]>
Commit | Line | Data |
---|---|---|
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 | |
10 | namespace { | |
11 | seastar::logger& logger() { | |
20effc67 | 12 | return crimson::get_logger(ceph_subsys_seastore_cleaner); |
f67539c2 TL |
13 | } |
14 | } | |
15 | ||
20effc67 TL |
16 | SET_SUBSYS(seastore_cleaner); |
17 | ||
f67539c2 TL |
18 | namespace crimson::os::seastore { |
19 | ||
20effc67 TL |
20 | void 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 | ||
25 | void 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 | ||
30 | void segment_info_set_t::segment_info_t::set_closed() { | |
31 | state = Segment::segment_state_t::CLOSED; | |
32 | } | |
33 | ||
f67539c2 TL |
34 | bool 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 | ||
60 | int64_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 | ||
92 | int64_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 | ||
124 | bool 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 | ||
150 | void 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 | ||
159 | void 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 |
166 | SegmentCleaner::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 | ||
178 | void 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 | ||
207 | SegmentCleaner::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 | ||
228 | void 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 | ||
243 | void 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 | ||
263 | void SegmentCleaner::close_segment(segment_id_t segment) | |
264 | { | |
265 | mark_closed(segment); | |
266 | } | |
267 | ||
f67539c2 TL |
268 | SegmentCleaner::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 | 291 | SegmentCleaner::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 | ||
309 | SegmentCleaner::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 |
330 | SegmentCleaner::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 | ||
346 | SegmentCleaner::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 |
426 | SegmentCleaner::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 | } |