]> git.proxmox.com Git - ceph.git/blame - ceph/src/osd/scrubber/scrub_machine.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / osd / scrubber / scrub_machine.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
f67539c2
TL
4#include <chrono>
5#include <typeinfo>
6
7#include <boost/core/demangle.hpp>
8
20effc67
TL
9#include "osd/OSD.h"
10#include "osd/OpRequest.h"
1e59de90 11
f67539c2 12#include "ScrubStore.h"
1e59de90 13#include "scrub_machine.h"
f67539c2
TL
14
15#define dout_context g_ceph_context
16#define dout_subsys ceph_subsys_osd
17#undef dout_prefix
18#define dout_prefix *_dout << " scrubberFSM "
19
20using namespace std::chrono;
21using namespace std::chrono_literals;
f67539c2
TL
22
23#define DECLARE_LOCALS \
24 ScrubMachineListener* scrbr = context<ScrubMachine>().m_scrbr; \
25 std::ignore = scrbr; \
26 auto pg_id = context<ScrubMachine>().m_pg_id; \
27 std::ignore = pg_id;
28
1e59de90
TL
29NamedSimply::NamedSimply(ScrubMachineListener* scrubber, const char* name)
30{
31 scrubber->set_state_name(name);
32}
33
f67539c2
TL
34namespace Scrub {
35
36// --------- trace/debug auxiliaries -------------------------------
37
38void on_event_creation(std::string_view nm)
39{
40 dout(20) << " event: --vvvv---- " << nm << dendl;
41}
42
43void on_event_discard(std::string_view nm)
44{
45 dout(20) << " event: --^^^^---- " << nm << dendl;
46}
47
f67539c2
TL
48void ScrubMachine::assert_not_active() const
49{
50 ceph_assert(state_cast<const NotActive*>());
51}
52
53bool ScrubMachine::is_reserving() const
54{
55 return state_cast<const ReservingReplicas*>();
56}
57
20effc67
TL
58bool ScrubMachine::is_accepting_updates() const
59{
60 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
61 ceph_assert(scrbr->is_primary());
62
63 return state_cast<const WaitLastUpdate*>();
64}
65
f67539c2
TL
66// for the rest of the code in this file - we know what PG we are dealing with:
67#undef dout_prefix
20effc67
TL
68#define dout_prefix _prefix(_dout, this->context<ScrubMachine>())
69
70template <class T>
71static ostream& _prefix(std::ostream* _dout, T& t)
f67539c2 72{
20effc67
TL
73 return t.gen_prefix(*_dout);
74}
75
76std::ostream& ScrubMachine::gen_prefix(std::ostream& out) const
77{
78 return m_scrbr->gen_prefix(out) << "FSM: ";
f67539c2
TL
79}
80
81// ////////////// the actual actions
82
83// ----------------------- NotActive -----------------------------------------
84
1e59de90
TL
85NotActive::NotActive(my_context ctx)
86 : my_base(ctx)
87 , NamedSimply(context<ScrubMachine>().m_scrbr, "NotActive")
f67539c2
TL
88{
89 dout(10) << "-- state -->> NotActive" << dendl;
20effc67
TL
90 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
91 scrbr->clear_queued_or_active();
92}
93
94sc::result NotActive::react(const StartScrub&)
95{
96 dout(10) << "NotActive::react(const StartScrub&)" << dendl;
97 DECLARE_LOCALS;
98 scrbr->set_scrub_begin_time();
99 return transit<ReservingReplicas>();
f67539c2
TL
100}
101
39ae355f
TL
102sc::result NotActive::react(const AfterRepairScrub&)
103{
104 dout(10) << "NotActive::react(const AfterRepairScrub&)" << dendl;
105 DECLARE_LOCALS;
106 scrbr->set_scrub_begin_time();
107 return transit<ReservingReplicas>();
108}
109
f67539c2
TL
110// ----------------------- ReservingReplicas ---------------------------------
111
1e59de90
TL
112ReservingReplicas::ReservingReplicas(my_context ctx)
113 : my_base(ctx)
114 , NamedSimply(context<ScrubMachine>().m_scrbr, "ReservingReplicas")
f67539c2
TL
115{
116 dout(10) << "-- state -->> ReservingReplicas" << dendl;
117 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
20effc67
TL
118
119 // prevent the OSD from starting another scrub while we are trying to secure
120 // replicas resources
121 scrbr->set_reserving_now();
f67539c2
TL
122 scrbr->reserve_replicas();
123}
124
20effc67
TL
125ReservingReplicas::~ReservingReplicas()
126{
127 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
128 scrbr->clear_reserving_now();
129}
130
f67539c2
TL
131sc::result ReservingReplicas::react(const ReservationFailure&)
132{
133 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
134 dout(10) << "ReservingReplicas::react(const ReservationFailure&)" << dendl;
135
136 // the Scrubber must release all resources and abort the scrubbing
137 scrbr->clear_pgscrub_state();
138 return transit<NotActive>();
139}
140
141/**
142 * note: the event poster is handling the scrubber reset
143 */
144sc::result ReservingReplicas::react(const FullReset&)
145{
146 dout(10) << "ReservingReplicas::react(const FullReset&)" << dendl;
147 return transit<NotActive>();
148}
149
150// ----------------------- ActiveScrubbing -----------------------------------
151
1e59de90
TL
152ActiveScrubbing::ActiveScrubbing(my_context ctx)
153 : my_base(ctx)
154 , NamedSimply(context<ScrubMachine>().m_scrbr, "ActiveScrubbing")
f67539c2
TL
155{
156 dout(10) << "-- state -->> ActiveScrubbing" << dendl;
157 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
158 scrbr->on_init();
159}
160
161/**
162 * upon exiting the Active state
163 */
164ActiveScrubbing::~ActiveScrubbing()
165{
166 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
167 dout(15) << __func__ << dendl;
168 scrbr->unreserve_replicas();
20effc67 169 scrbr->clear_queued_or_active();
f67539c2
TL
170}
171
172/*
173 * The only source of an InternalError event as of now is the BuildMap state,
174 * when encountering a backend error.
175 * We kill the scrub and reset the FSM.
176 */
177sc::result ActiveScrubbing::react(const InternalError&)
178{
179 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
180 dout(10) << __func__ << dendl;
181 scrbr->clear_pgscrub_state();
182 return transit<NotActive>();
183}
184
185sc::result ActiveScrubbing::react(const FullReset&)
186{
187 dout(10) << "ActiveScrubbing::react(const FullReset&)" << dendl;
188 // caller takes care of clearing the scrubber & FSM states
189 return transit<NotActive>();
190}
191
192// ----------------------- RangeBlocked -----------------------------------
193
194/*
195 * Blocked. Will be released by kick_object_context_blocked() (or upon
196 * an abort)
20effc67
TL
197 *
198 * Note: we are never expected to be waiting for long for a blocked object.
199 * Unfortunately we know from experience that a bug elsewhere might result
200 * in an indefinite wait in this state, for an object that is never released.
201 * If that happens, all we can do is to issue a warning message to help
202 * with the debugging.
f67539c2 203 */
1e59de90
TL
204RangeBlocked::RangeBlocked(my_context ctx)
205 : my_base(ctx)
206 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/RangeBlocked")
f67539c2
TL
207{
208 dout(10) << "-- state -->> Act/RangeBlocked" << dendl;
20effc67
TL
209 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
210
211 // arrange to have a warning message issued if we are stuck in this
212 // state for longer than some reasonable number of minutes.
213 m_timeout = scrbr->acquire_blocked_alarm();
f67539c2
TL
214}
215
216// ----------------------- PendingTimer -----------------------------------
217
218/**
219 * Sleeping till timer reactivation - or just requeuing
220 */
1e59de90
TL
221PendingTimer::PendingTimer(my_context ctx)
222 : my_base(ctx)
223 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/PendingTimer")
f67539c2
TL
224{
225 dout(10) << "-- state -->> Act/PendingTimer" << dendl;
226 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
227
228 scrbr->add_delayed_scheduling();
229}
230
231// ----------------------- NewChunk -----------------------------------
232
233/**
234 * Preconditions:
235 * - preemption data was set
236 * - epoch start was updated
237 */
1e59de90
TL
238NewChunk::NewChunk(my_context ctx)
239 : my_base(ctx)
240 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/NewChunk")
f67539c2
TL
241{
242 dout(10) << "-- state -->> Act/NewChunk" << dendl;
243 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
244
245 scrbr->get_preemptor().adjust_parameters();
246
247 // choose range to work on
20effc67
TL
248 // select_range_n_notify() will signal either SelectedChunkFree or
249 // ChunkIsBusy. If 'busy', we transition to Blocked, and wait for the
250 // range to become available.
251 scrbr->select_range_n_notify();
f67539c2
TL
252}
253
254sc::result NewChunk::react(const SelectedChunkFree&)
255{
256 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
257 dout(10) << "NewChunk::react(const SelectedChunkFree&)" << dendl;
258
259 scrbr->set_subset_last_update(scrbr->search_log_for_updates());
260 return transit<WaitPushes>();
261}
262
263// ----------------------- WaitPushes -----------------------------------
264
1e59de90
TL
265WaitPushes::WaitPushes(my_context ctx)
266 : my_base(ctx)
267 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/WaitPushes")
f67539c2
TL
268{
269 dout(10) << " -- state -->> Act/WaitPushes" << dendl;
270 post_event(ActivePushesUpd{});
271}
272
273/*
274 * Triggered externally, by the entity that had an update re pushes
275 */
276sc::result WaitPushes::react(const ActivePushesUpd&)
277{
278 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
1e59de90
TL
279 dout(10)
280 << "WaitPushes::react(const ActivePushesUpd&) pending_active_pushes: "
281 << scrbr->pending_active_pushes() << dendl;
f67539c2
TL
282
283 if (!scrbr->pending_active_pushes()) {
284 // done waiting
285 return transit<WaitLastUpdate>();
286 }
287
288 return discard_event();
289}
290
291// ----------------------- WaitLastUpdate -----------------------------------
292
1e59de90
TL
293WaitLastUpdate::WaitLastUpdate(my_context ctx)
294 : my_base(ctx)
295 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/WaitLastUpdate")
f67539c2
TL
296{
297 dout(10) << " -- state -->> Act/WaitLastUpdate" << dendl;
298 post_event(UpdatesApplied{});
299}
300
20effc67
TL
301/**
302 * Note:
303 * Updates are locally readable immediately. Thus, on the replicas we do need
304 * to wait for the update notifications before scrubbing. For the Primary it's
305 * a bit different: on EC (and only there) rmw operations have an additional
306 * read roundtrip. That means that on the Primary we need to wait for
307 * last_update_applied (the replica side, even on EC, is still safe
308 * since the actual transaction will already be readable by commit time.
309 */
f67539c2
TL
310void WaitLastUpdate::on_new_updates(const UpdatesApplied&)
311{
312 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
313 dout(10) << "WaitLastUpdate::on_new_updates(const UpdatesApplied&)" << dendl;
314
315 if (scrbr->has_pg_marked_new_updates()) {
316 post_event(InternalAllUpdates{});
317 } else {
318 // will be requeued by op_applied
319 dout(10) << "wait for EC read/modify/writes to queue" << dendl;
320 }
321}
322
323/*
324 * request maps from the replicas in the acting set
325 */
326sc::result WaitLastUpdate::react(const InternalAllUpdates&)
327{
328 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
329 dout(10) << "WaitLastUpdate::react(const InternalAllUpdates&)" << dendl;
330
331 scrbr->get_replicas_maps(scrbr->get_preemptor().is_preemptable());
332 return transit<BuildMap>();
333}
334
335// ----------------------- BuildMap -----------------------------------
336
1e59de90
TL
337BuildMap::BuildMap(my_context ctx)
338 : my_base(ctx)
339 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/BuildMap")
f67539c2
TL
340{
341 dout(10) << " -- state -->> Act/BuildMap" << dendl;
342 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
343
1e59de90
TL
344 // no need to check for an epoch change, as all possible flows that brought
345 // us here have a check_interval() verification of their final event.
f67539c2
TL
346
347 if (scrbr->get_preemptor().was_preempted()) {
348
349 // we were preempted, either directly or by a replica
350 dout(10) << __func__ << " preempted!!!" << dendl;
351 scrbr->mark_local_map_ready();
352 post_event(IntBmPreempted{});
353
354 } else {
355
356 auto ret = scrbr->build_primary_map_chunk();
357
358 if (ret == -EINPROGRESS) {
359 // must wait for the backend to finish. No specific event provided.
360 // build_primary_map_chunk() has already requeued us.
361 dout(20) << "waiting for the backend..." << dendl;
362
363 } else if (ret < 0) {
364
365 dout(10) << "BuildMap::BuildMap() Error! Aborting. Ret: " << ret << dendl;
f67539c2
TL
366 post_event(InternalError{});
367
368 } else {
369
370 // the local map was created
371 post_event(IntLocalMapDone{});
372 }
373 }
374}
375
376sc::result BuildMap::react(const IntLocalMapDone&)
377{
378 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
379 dout(10) << "BuildMap::react(const IntLocalMapDone&)" << dendl;
380
381 scrbr->mark_local_map_ready();
382 return transit<WaitReplicas>();
383}
384
385// ----------------------- DrainReplMaps -----------------------------------
386
1e59de90
TL
387DrainReplMaps::DrainReplMaps(my_context ctx)
388 : my_base(ctx)
389 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/DrainReplMaps")
f67539c2
TL
390{
391 dout(10) << "-- state -->> Act/DrainReplMaps" << dendl;
1e59de90 392 // we may have got all maps already. Send the event that will make us check.
f67539c2
TL
393 post_event(GotReplicas{});
394}
395
396sc::result DrainReplMaps::react(const GotReplicas&)
397{
398 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
399 dout(10) << "DrainReplMaps::react(const GotReplicas&)" << dendl;
400
401 if (scrbr->are_all_maps_available()) {
402 // NewChunk will handle the preemption that brought us to this state
403 return transit<PendingTimer>();
404 }
405
1e59de90
TL
406 dout(15) << "DrainReplMaps::react(const GotReplicas&): still draining "
407 "incoming maps: "
f67539c2
TL
408 << scrbr->dump_awaited_maps() << dendl;
409 return discard_event();
410}
411
412// ----------------------- WaitReplicas -----------------------------------
413
1e59de90
TL
414WaitReplicas::WaitReplicas(my_context ctx)
415 : my_base(ctx)
416 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/WaitReplicas")
f67539c2
TL
417{
418 dout(10) << "-- state -->> Act/WaitReplicas" << dendl;
419 post_event(GotReplicas{});
420}
421
20effc67 422/**
1e59de90
TL
423 * note: now that maps_compare_n_cleanup() is "futurized"(*), and we remain in
424 * this state for a while even after we got all our maps, we must prevent
425 * are_all_maps_available() (actually - the code after the if()) from being
426 * called more than once.
427 * This is basically a separate state, but it's too transitory and artificial
428 * to justify the cost of a separate state.
429
430 * (*) "futurized" - in Crimson, the call to maps_compare_n_cleanup() returns
431 * immediately after initiating the process. The actual termination of the
432 * maps comparing etc' is signalled via an event. As we share the code with
433 * "classic" OSD, here too maps_compare_n_cleanup() is responsible for
434 * signalling the completion of the processing.
20effc67 435 */
f67539c2
TL
436sc::result WaitReplicas::react(const GotReplicas&)
437{
438 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
439 dout(10) << "WaitReplicas::react(const GotReplicas&)" << dendl;
440
20effc67 441 if (!all_maps_already_called && scrbr->are_all_maps_available()) {
f67539c2
TL
442 dout(10) << "WaitReplicas::react(const GotReplicas&) got all" << dendl;
443
20effc67
TL
444 all_maps_already_called = true;
445
f67539c2
TL
446 // were we preempted?
447 if (scrbr->get_preemptor().disable_and_test()) { // a test&set
448
449
450 dout(10) << "WaitReplicas::react(const GotReplicas&) PREEMPTED!" << dendl;
451 return transit<PendingTimer>();
452
453 } else {
f67539c2 454 scrbr->maps_compare_n_cleanup();
1e59de90 455 return transit<WaitDigestUpdate>();
f67539c2
TL
456 }
457 } else {
458 return discard_event();
459 }
460}
461
33c7a0ef
TL
462sc::result WaitReplicas::react(const DigestUpdate&)
463{
464 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
1e59de90
TL
465 auto warn_msg =
466 "WaitReplicas::react(const DigestUpdate&): Unexpected DigestUpdate event"s;
33c7a0ef
TL
467 dout(10) << warn_msg << dendl;
468 scrbr->log_cluster_warning(warn_msg);
469 return discard_event();
470}
471
f67539c2
TL
472// ----------------------- WaitDigestUpdate -----------------------------------
473
1e59de90
TL
474WaitDigestUpdate::WaitDigestUpdate(my_context ctx)
475 : my_base(ctx)
476 , NamedSimply(context<ScrubMachine>().m_scrbr, "Act/WaitDigestUpdate")
f67539c2 477{
20effc67 478 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
f67539c2 479 dout(10) << "-- state -->> Act/WaitDigestUpdate" << dendl;
20effc67 480
f67539c2
TL
481 // perform an initial check: maybe we already
482 // have all the updates we need:
483 // (note that DigestUpdate is usually an external event)
484 post_event(DigestUpdate{});
485}
486
487sc::result WaitDigestUpdate::react(const DigestUpdate&)
488{
489 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
490 dout(10) << "WaitDigestUpdate::react(const DigestUpdate&)" << dendl;
491
20effc67
TL
492 // on_digest_updates() will either:
493 // - do nothing - if we are still waiting for updates, or
494 // - finish the scrubbing of the current chunk, and:
495 // - send NextChunk, or
496 // - send ScrubFinished
497 scrbr->on_digest_updates();
498 return discard_event();
499}
f67539c2 500
20effc67
TL
501sc::result WaitDigestUpdate::react(const ScrubFinished&)
502{
503 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
504 dout(10) << "WaitDigestUpdate::react(const ScrubFinished&)" << dendl;
505 scrbr->set_scrub_duration();
506 scrbr->scrub_finish();
507 return transit<NotActive>();
f67539c2
TL
508}
509
510ScrubMachine::ScrubMachine(PG* pg, ScrubMachineListener* pg_scrub)
1e59de90
TL
511 : m_pg_id{pg->pg_id}
512 , m_scrbr{pg_scrub}
513{}
f67539c2
TL
514
515ScrubMachine::~ScrubMachine() = default;
516
517// -------- for replicas -----------------------------------------------------
518
519// ----------------------- ReplicaWaitUpdates --------------------------------
520
1e59de90
TL
521ReplicaWaitUpdates::ReplicaWaitUpdates(my_context ctx)
522 : my_base(ctx)
523 , NamedSimply(context<ScrubMachine>().m_scrbr, "ReplicaWaitUpdates")
f67539c2
TL
524{
525 dout(10) << "-- state -->> ReplicaWaitUpdates" << dendl;
526 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
527 scrbr->on_replica_init();
528}
529
530/*
531 * Triggered externally, by the entity that had an update re pushes
532 */
533sc::result ReplicaWaitUpdates::react(const ReplicaPushesUpd&)
534{
535 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
536 dout(10) << "ReplicaWaitUpdates::react(const ReplicaPushesUpd&): "
537 << scrbr->pending_active_pushes() << dendl;
538
539 if (scrbr->pending_active_pushes() == 0) {
540
541 // done waiting
542 return transit<ActiveReplica>();
543 }
544
545 return discard_event();
546}
547
548/**
549 * the event poster is handling the scrubber reset
550 */
551sc::result ReplicaWaitUpdates::react(const FullReset&)
552{
553 dout(10) << "ReplicaWaitUpdates::react(const FullReset&)" << dendl;
554 return transit<NotActive>();
555}
556
557// ----------------------- ActiveReplica -----------------------------------
558
1e59de90
TL
559ActiveReplica::ActiveReplica(my_context ctx)
560 : my_base(ctx)
561 , NamedSimply(context<ScrubMachine>().m_scrbr, "ActiveReplica")
f67539c2
TL
562{
563 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
564 dout(10) << "-- state -->> ActiveReplica" << dendl;
1e59de90
TL
565 // and as we might have skipped ReplicaWaitUpdates:
566 scrbr->on_replica_init();
f67539c2
TL
567 post_event(SchedReplica{});
568}
569
570sc::result ActiveReplica::react(const SchedReplica&)
571{
572 DECLARE_LOCALS; // 'scrbr' & 'pg_id' aliases
573 dout(10) << "ActiveReplica::react(const SchedReplica&). is_preemptable? "
574 << scrbr->get_preemptor().is_preemptable() << dendl;
575
576 if (scrbr->get_preemptor().was_preempted()) {
577 dout(10) << "replica scrub job preempted" << dendl;
578
20effc67 579 scrbr->send_preempted_replica();
f67539c2
TL
580 scrbr->replica_handling_done();
581 return transit<NotActive>();
582 }
583
584 // start or check progress of build_replica_map_chunk()
20effc67
TL
585 auto ret_init = scrbr->build_replica_map_chunk();
586 if (ret_init != -EINPROGRESS) {
f67539c2
TL
587 return transit<NotActive>();
588 }
589
20effc67 590 return discard_event();
f67539c2
TL
591}
592
593/**
594 * the event poster is handling the scrubber reset
595 */
596sc::result ActiveReplica::react(const FullReset&)
597{
598 dout(10) << "ActiveReplica::react(const FullReset&)" << dendl;
599 return transit<NotActive>();
600}
601
602} // namespace Scrub