]> git.proxmox.com Git - ceph.git/blame - ceph/src/crimson/os/seastore/segment_cleaner.cc
buildsys: change download over to reef release
[ceph.git] / ceph / src / crimson / os / seastore / segment_cleaner.cc
CommitLineData
f67539c2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "crimson/common/log.h"
20effc67 5#include "crimson/os/seastore/logging.h"
f67539c2 6
f67539c2 7#include "crimson/os/seastore/segment_cleaner.h"
20effc67 8#include "crimson/os/seastore/transaction_manager.h"
f67539c2
TL
9
10namespace {
11 seastar::logger& logger() {
20effc67 12 return crimson::get_logger(ceph_subsys_seastore_cleaner);
f67539c2
TL
13 }
14}
15
20effc67
TL
16SET_SUBSYS(seastore_cleaner);
17
f67539c2
TL
18namespace crimson::os::seastore {
19
20effc67
TL
20void segment_info_set_t::segment_info_t::set_open() {
21 assert(state == Segment::segment_state_t::EMPTY);
22 state = Segment::segment_state_t::OPEN;
23}
24
25void segment_info_set_t::segment_info_t::set_empty() {
26 assert(state == Segment::segment_state_t::CLOSED);
27 state = Segment::segment_state_t::EMPTY;
28}
29
30void segment_info_set_t::segment_info_t::set_closed() {
31 state = Segment::segment_state_t::CLOSED;
32}
33
f67539c2
TL
34bool SpaceTrackerSimple::equals(const SpaceTrackerI &_other) const
35{
36 const auto &other = static_cast<const SpaceTrackerSimple&>(_other);
37
38 if (other.live_bytes_by_segment.size() != live_bytes_by_segment.size()) {
39 logger().error("{}: different segment counts, bug in test");
40 assert(0 == "segment counts should match");
41 return false;
42 }
43
44 bool all_match = true;
20effc67
TL
45 for (auto i = live_bytes_by_segment.begin(), j = other.live_bytes_by_segment.begin();
46 i != live_bytes_by_segment.end(); ++i, ++j) {
47 if (i->second != j->second) {
f67539c2
TL
48 all_match = false;
49 logger().debug(
50 "{}: segment_id {} live bytes mismatch *this: {}, other: {}",
51 __func__,
20effc67
TL
52 i->first,
53 i->second,
54 j->second);
f67539c2
TL
55 }
56 }
57 return all_match;
58}
59
60int64_t SpaceTrackerDetailed::SegmentMap::allocate(
20effc67 61 device_segment_id_t segment,
f67539c2
TL
62 segment_off_t offset,
63 extent_len_t len,
64 const extent_len_t block_size)
65{
66 assert(offset % block_size == 0);
67 assert(len % block_size == 0);
68
69 const auto b = (offset / block_size);
70 const auto e = (offset + len) / block_size;
71
72 bool error = false;
73 for (auto i = b; i < e; ++i) {
74 if (bitmap[i]) {
75 if (!error) {
76 logger().error(
77 "SegmentMap::allocate found allocated in {}, {} ~ {}",
78 segment,
79 offset,
80 len);
81 error = true;
82 }
83 logger().debug(
84 "SegmentMap::allocate block {} allocated",
85 i * block_size);
86 }
87 bitmap[i] = true;
88 }
20effc67 89 return update_usage(len);
f67539c2
TL
90}
91
92int64_t SpaceTrackerDetailed::SegmentMap::release(
20effc67 93 device_segment_id_t segment,
f67539c2
TL
94 segment_off_t offset,
95 extent_len_t len,
96 const extent_len_t block_size)
97{
98 assert(offset % block_size == 0);
99 assert(len % block_size == 0);
100
101 const auto b = (offset / block_size);
102 const auto e = (offset + len) / block_size;
103
104 bool error = false;
105 for (auto i = b; i < e; ++i) {
106 if (!bitmap[i]) {
107 if (!error) {
108 logger().error(
109 "SegmentMap::release found unallocated in {}, {} ~ {}",
110 segment,
111 offset,
112 len);
113 error = true;
114 }
115 logger().debug(
116 "SegmentMap::release block {} unallocated",
117 i * block_size);
118 }
119 bitmap[i] = false;
120 }
20effc67 121 return update_usage(-(int64_t)len);
f67539c2
TL
122}
123
124bool SpaceTrackerDetailed::equals(const SpaceTrackerI &_other) const
125{
126 const auto &other = static_cast<const SpaceTrackerDetailed&>(_other);
127
128 if (other.segment_usage.size() != segment_usage.size()) {
129 logger().error("{}: different segment counts, bug in test");
130 assert(0 == "segment counts should match");
131 return false;
132 }
133
134 bool all_match = true;
20effc67
TL
135 for (auto i = segment_usage.begin(), j = other.segment_usage.begin();
136 i != segment_usage.end(); ++i, ++j) {
137 if (i->second.get_usage() != j->second.get_usage()) {
f67539c2
TL
138 all_match = false;
139 logger().error(
140 "{}: segment_id {} live bytes mismatch *this: {}, other: {}",
141 __func__,
20effc67
TL
142 i->first,
143 i->second.get_usage(),
144 j->second.get_usage());
f67539c2
TL
145 }
146 }
147 return all_match;
148}
149
150void SpaceTrackerDetailed::SegmentMap::dump_usage(extent_len_t block_size) const
151{
152 for (unsigned i = 0; i < bitmap.size(); ++i) {
153 if (bitmap[i]) {
154 logger().debug(" {} still live", i * block_size);
155 }
156 }
157}
158
159void SpaceTrackerDetailed::dump_usage(segment_id_t id) const
160{
161 logger().debug("SpaceTrackerDetailed::dump_usage {}", id);
20effc67
TL
162 segment_usage[id].dump_usage(
163 block_size_by_segment_manager[id.device_id()]);
f67539c2
TL
164}
165
20effc67
TL
166SegmentCleaner::SegmentCleaner(
167 config_t config,
168 ExtentReaderRef&& scr,
169 bool detailed)
170 : detailed(detailed),
171 config(config),
172 scanner(std::move(scr)),
173 gc_process(*this)
f67539c2 174{
20effc67
TL
175 register_metrics();
176}
177
178void SegmentCleaner::register_metrics()
179{
180 namespace sm = seastar::metrics;
181 metrics.add_group("segment_cleaner", {
182 sm::make_counter("segments_released", stats.segments_released,
183 sm::description("total number of extents released by SegmentCleaner")),
184 sm::make_counter("accumulated_blocked_ios", stats.accumulated_blocked_ios,
185 sm::description("accumulated total number of ios that were blocked by gc")),
186 sm::make_derive("empty_segments", stats.empty_segments,
187 sm::description("current empty segments")),
188 sm::make_derive("ios_blocking", stats.ios_blocking,
189 sm::description("IOs that are blocking on space usage")),
190 sm::make_derive("used_bytes", stats.used_bytes,
191 sm::description("the size of the space occupied by live extents")),
192 sm::make_derive("projected_used_bytes", stats.projected_used_bytes,
193 sm::description("the size of the space going to be occupied by new extents")),
194 sm::make_derive("avail_bytes",
195 [this] {
196 return segments.get_available_bytes();
197 },
198 sm::description("the size of the space not occupied")),
199 sm::make_derive("opened_segments",
200 [this] {
201 return segments.get_opened_segments();
202 },
203 sm::description("the number of segments whose state is open"))
204 });
205}
206
207SegmentCleaner::get_segment_ret SegmentCleaner::get_segment(device_id_t id)
208{
209 for (auto it = segments.device_begin(id);
210 it != segments.device_end(id);
211 ++it) {
212 auto id = it->first;
213 auto& segment_info = it->second;
214 if (segment_info.is_empty()) {
215 mark_open(id);
216 logger().debug("{}: returning segment {}", __func__, id);
f67539c2
TL
217 return get_segment_ret(
218 get_segment_ertr::ready_future_marker{},
20effc67 219 id);
f67539c2
TL
220 }
221 }
222 assert(0 == "out of space handling todo");
223 return get_segment_ret(
224 get_segment_ertr::ready_future_marker{},
20effc67 225 NULL_SEG_ID);
f67539c2
TL
226}
227
228void SegmentCleaner::update_journal_tail_target(journal_seq_t target)
229{
230 logger().debug(
20effc67 231 "{}: {}, current tail target {}",
f67539c2 232 __func__,
20effc67
TL
233 target,
234 journal_tail_target);
f67539c2
TL
235 assert(journal_tail_target == journal_seq_t() || target >= journal_tail_target);
236 if (journal_tail_target == journal_seq_t() || target > journal_tail_target) {
237 journal_tail_target = target;
238 }
20effc67
TL
239 gc_process.maybe_wake_on_space_used();
240 maybe_wake_gc_blocked_io();
f67539c2
TL
241}
242
243void SegmentCleaner::update_journal_tail_committed(journal_seq_t committed)
244{
245 if (journal_tail_committed == journal_seq_t() ||
246 committed > journal_tail_committed) {
247 logger().debug(
248 "{}: update journal_tail_committed {}",
249 __func__,
250 committed);
251 journal_tail_committed = committed;
252 }
253 if (journal_tail_target == journal_seq_t() ||
254 committed > journal_tail_target) {
255 logger().debug(
256 "{}: update journal_tail_target {}",
257 __func__,
258 committed);
259 journal_tail_target = committed;
260 }
261}
262
263void SegmentCleaner::close_segment(segment_id_t segment)
264{
265 mark_closed(segment);
266}
267
f67539c2
TL
268SegmentCleaner::rewrite_dirty_ret SegmentCleaner::rewrite_dirty(
269 Transaction &t,
270 journal_seq_t limit)
271{
20effc67 272 LOG_PREFIX(SegmentCleaner::rewrite_dirty);
f67539c2 273 return ecb->get_next_dirty_extents(
20effc67
TL
274 t,
275 limit,
276 config.journal_rewrite_per_cycle
277 ).si_then([=, &t](auto dirty_list) {
f67539c2
TL
278 return seastar::do_with(
279 std::move(dirty_list),
20effc67
TL
280 [FNAME, this, &t](auto &dirty_list) {
281 return trans_intr::do_for_each(
f67539c2 282 dirty_list,
20effc67
TL
283 [FNAME, this, &t](auto &e) {
284 DEBUGT("cleaning {}", t, *e);
f67539c2
TL
285 return ecb->rewrite_extent(t, e);
286 });
287 });
288 });
289}
290
20effc67 291SegmentCleaner::gc_cycle_ret SegmentCleaner::GCProcess::run()
f67539c2 292{
20effc67
TL
293 return seastar::do_until(
294 [this] { return stopping; },
295 [this] {
296 return maybe_wait_should_run(
297 ).then([this] {
298 cleaner.log_gc_state("GCProcess::run");
299
300 if (stopping) {
301 return seastar::now();
302 } else {
303 return cleaner.do_gc_cycle();
304 }
305 });
306 });
307}
308
309SegmentCleaner::gc_cycle_ret SegmentCleaner::do_gc_cycle()
310{
311 if (gc_should_trim_journal()) {
312 return gc_trim_journal(
313 ).handle_error(
314 crimson::ct_error::assert_all{
315 "GCProcess::run encountered invalid error in gc_trim_journal"
316 }
317 );
318 } else if (gc_should_reclaim_space()) {
319 return gc_reclaim_space(
320 ).handle_error(
321 crimson::ct_error::assert_all{
322 "GCProcess::run encountered invalid error in gc_reclaim_space"
323 }
324 );
325 } else {
326 return seastar::now();
f67539c2 327 }
20effc67 328}
f67539c2 329
20effc67
TL
330SegmentCleaner::gc_trim_journal_ret SegmentCleaner::gc_trim_journal()
331{
332 return repeat_eagain([this] {
333 return ecb->with_transaction_intr(
334 Transaction::src_t::CLEANER_TRIM,
335 "trim_journal",
336 [this](auto& t)
337 {
338 return rewrite_dirty(t, get_dirty_tail()
339 ).si_then([this, &t] {
340 return ecb->submit_transaction_direct(t);
341 });
342 });
343 });
344}
345
346SegmentCleaner::gc_reclaim_space_ret SegmentCleaner::gc_reclaim_space()
347{
f67539c2 348 if (!scan_cursor) {
20effc67
TL
349 journal_seq_t next = get_next_gc_target();
350 if (next == journal_seq_t()) {
f67539c2
TL
351 logger().debug(
352 "SegmentCleaner::do_gc: no segments to gc");
20effc67 353 return seastar::now();
f67539c2 354 }
f67539c2 355 scan_cursor =
20effc67 356 std::make_unique<ExtentReader::scan_extents_cursor>(
f67539c2
TL
357 next);
358 logger().debug(
359 "SegmentCleaner::do_gc: starting gc on segment {}",
20effc67
TL
360 scan_cursor->seq);
361 } else {
362 ceph_assert(!scan_cursor->is_complete());
f67539c2
TL
363 }
364
20effc67 365 return scanner->scan_extents(
f67539c2 366 *scan_cursor,
20effc67
TL
367 config.reclaim_bytes_stride
368 ).safe_then([this](auto &&_extents) {
f67539c2 369 return seastar::do_with(
20effc67
TL
370 std::move(_extents),
371 [this](auto &extents) {
372 return repeat_eagain([this, &extents]() mutable {
373 logger().debug(
374 "SegmentCleaner::gc_reclaim_space: processing {} extents",
375 extents.size());
376 return ecb->with_transaction_intr(
377 Transaction::src_t::CLEANER_RECLAIM,
378 "reclaim_space",
379 [this, &extents](auto& t)
380 {
381 return trans_intr::do_for_each(
382 extents,
383 [this, &t](auto &extent) {
384 auto &[addr, info] = extent;
385 logger().debug(
386 "SegmentCleaner::gc_reclaim_space: checking extent {}",
387 info);
388 return ecb->get_extent_if_live(
389 t,
390 info.type,
391 addr,
392 info.addr,
393 info.len
394 ).si_then([addr=addr, &t, this](CachedExtentRef ext) {
395 if (!ext) {
396 logger().debug(
397 "SegmentCleaner::gc_reclaim_space: addr {} dead, skipping",
398 addr);
399 return ExtentCallbackInterface::rewrite_extent_iertr::now();
400 } else {
401 logger().debug(
402 "SegmentCleaner::gc_reclaim_space: addr {} alive, gc'ing {}",
403 addr,
404 *ext);
405 return ecb->rewrite_extent(
406 t,
407 ext);
408 }
409 });
410 }).si_then([this, &t] {
411 if (scan_cursor->is_complete()) {
412 t.mark_segment_to_release(scan_cursor->get_segment_id());
413 }
414 return ecb->submit_transaction_direct(t);
415 });
416 });
f67539c2 417 });
20effc67
TL
418 });
419 }).safe_then([this] {
420 if (scan_cursor->is_complete()) {
421 scan_cursor.reset();
422 }
f67539c2
TL
423 });
424}
425
20effc67
TL
426SegmentCleaner::init_segments_ret SegmentCleaner::init_segments() {
427 logger().debug("SegmentCleaner::init_segments: {} segments", segments.size());
428 return seastar::do_with(
429 std::vector<std::pair<segment_id_t, segment_header_t>>(),
430 [this](auto& segment_set) {
431 return crimson::do_for_each(
432 segments.begin(),
433 segments.end(),
434 [this, &segment_set](auto& it) {
435 auto segment_id = it.first;
436 return scanner->read_segment_header(
437 segment_id
438 ).safe_then([&segment_set, segment_id, this](auto header) {
439 if (header.out_of_line) {
440 logger().debug(
441 "ExtentReader::init_segments: out-of-line segment {}",
442 segment_id);
443 init_mark_segment_closed(
444 segment_id,
445 header.journal_segment_seq,
446 true);
447 } else {
448 logger().debug(
449 "ExtentReader::init_segments: journal segment {}",
450 segment_id);
451 segment_set.emplace_back(std::make_pair(segment_id, std::move(header)));
452 }
453 return seastar::now();
454 }).handle_error(
455 crimson::ct_error::enoent::handle([](auto) {
456 return init_segments_ertr::now();
457 }),
458 crimson::ct_error::enodata::handle([](auto) {
459 return init_segments_ertr::now();
460 }),
461 crimson::ct_error::input_output_error::pass_further{}
462 );
463 }).safe_then([&segment_set] {
464 return seastar::make_ready_future<
465 std::vector<std::pair<segment_id_t, segment_header_t>>>(
466 std::move(segment_set));
467 });
468 });
469}
470
f67539c2 471}