]>
git.proxmox.com Git - ceph.git/blob - ceph/src/osd/scrubber/scrub_machine.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "scrub_machine.h"
9 #include <boost/core/demangle.hpp>
12 #include "osd/OpRequest.h"
13 #include "ScrubStore.h"
15 #define dout_context g_ceph_context
16 #define dout_subsys ceph_subsys_osd
18 #define dout_prefix *_dout << " scrubberFSM "
20 using namespace std::chrono
;
21 using namespace std::chrono_literals
;
22 namespace sc
= boost::statechart
;
24 #define DECLARE_LOCALS \
25 ScrubMachineListener* scrbr = context<ScrubMachine>().m_scrbr; \
26 std::ignore = scrbr; \
27 auto pg_id = context<ScrubMachine>().m_pg_id; \
32 // --------- trace/debug auxiliaries -------------------------------
34 void on_event_creation(std::string_view nm
)
36 dout(20) << " event: --vvvv---- " << nm
<< dendl
;
39 void on_event_discard(std::string_view nm
)
41 dout(20) << " event: --^^^^---- " << nm
<< dendl
;
44 std::string
ScrubMachine::current_states_desc() const
47 for (auto si
= state_begin(); si
!= state_end(); ++si
) {
48 const auto& siw
{ *si
}; // prevents a warning re side-effects
49 // the '7' is the size of the 'scrub::'
50 sts
+= boost::core::demangle(typeid(siw
).name()).substr(7, std::string::npos
) + "/";
55 void ScrubMachine::assert_not_active() const
57 ceph_assert(state_cast
<const NotActive
*>());
60 bool ScrubMachine::is_reserving() const
62 return state_cast
<const ReservingReplicas
*>();
65 bool ScrubMachine::is_accepting_updates() const
67 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
68 ceph_assert(scrbr
->is_primary());
70 return state_cast
<const WaitLastUpdate
*>();
73 // for the rest of the code in this file - we know what PG we are dealing with:
75 #define dout_prefix _prefix(_dout, this->context<ScrubMachine>())
78 static ostream
& _prefix(std::ostream
* _dout
, T
& t
)
80 return t
.gen_prefix(*_dout
);
83 std::ostream
& ScrubMachine::gen_prefix(std::ostream
& out
) const
85 return m_scrbr
->gen_prefix(out
) << "FSM: ";
88 // ////////////// the actual actions
90 // ----------------------- NotActive -----------------------------------------
92 NotActive::NotActive(my_context ctx
) : my_base(ctx
)
94 dout(10) << "-- state -->> NotActive" << dendl
;
95 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
96 scrbr
->clear_queued_or_active();
99 sc::result
NotActive::react(const StartScrub
&)
101 dout(10) << "NotActive::react(const StartScrub&)" << dendl
;
103 scrbr
->set_scrub_begin_time();
104 return transit
<ReservingReplicas
>();
107 sc::result
NotActive::react(const AfterRepairScrub
&)
109 dout(10) << "NotActive::react(const AfterRepairScrub&)" << dendl
;
111 scrbr
->set_scrub_begin_time();
112 return transit
<ReservingReplicas
>();
115 // ----------------------- ReservingReplicas ---------------------------------
117 ReservingReplicas::ReservingReplicas(my_context ctx
) : my_base(ctx
)
119 dout(10) << "-- state -->> ReservingReplicas" << dendl
;
120 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
122 // prevent the OSD from starting another scrub while we are trying to secure
123 // replicas resources
124 scrbr
->set_reserving_now();
125 scrbr
->reserve_replicas();
128 ReservingReplicas::~ReservingReplicas()
130 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
131 scrbr
->clear_reserving_now();
134 sc::result
ReservingReplicas::react(const ReservationFailure
&)
136 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
137 dout(10) << "ReservingReplicas::react(const ReservationFailure&)" << dendl
;
139 // the Scrubber must release all resources and abort the scrubbing
140 scrbr
->clear_pgscrub_state();
141 return transit
<NotActive
>();
145 * note: the event poster is handling the scrubber reset
147 sc::result
ReservingReplicas::react(const FullReset
&)
149 dout(10) << "ReservingReplicas::react(const FullReset&)" << dendl
;
150 return transit
<NotActive
>();
153 // ----------------------- ActiveScrubbing -----------------------------------
155 ActiveScrubbing::ActiveScrubbing(my_context ctx
) : my_base(ctx
)
157 dout(10) << "-- state -->> ActiveScrubbing" << dendl
;
158 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
163 * upon exiting the Active state
165 ActiveScrubbing::~ActiveScrubbing()
167 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
168 dout(15) << __func__
<< dendl
;
169 scrbr
->unreserve_replicas();
170 scrbr
->clear_queued_or_active();
174 * The only source of an InternalError event as of now is the BuildMap state,
175 * when encountering a backend error.
176 * We kill the scrub and reset the FSM.
178 sc::result
ActiveScrubbing::react(const InternalError
&)
180 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
181 dout(10) << __func__
<< dendl
;
182 scrbr
->clear_pgscrub_state();
183 return transit
<NotActive
>();
186 sc::result
ActiveScrubbing::react(const FullReset
&)
188 dout(10) << "ActiveScrubbing::react(const FullReset&)" << dendl
;
189 // caller takes care of clearing the scrubber & FSM states
190 return transit
<NotActive
>();
193 // ----------------------- RangeBlocked -----------------------------------
196 * Blocked. Will be released by kick_object_context_blocked() (or upon
199 * Note: we are never expected to be waiting for long for a blocked object.
200 * Unfortunately we know from experience that a bug elsewhere might result
201 * in an indefinite wait in this state, for an object that is never released.
202 * If that happens, all we can do is to issue a warning message to help
203 * with the debugging.
205 RangeBlocked::RangeBlocked(my_context ctx
) : my_base(ctx
)
207 dout(10) << "-- state -->> Act/RangeBlocked" << dendl
;
208 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
210 // arrange to have a warning message issued if we are stuck in this
211 // state for longer than some reasonable number of minutes.
212 m_timeout
= scrbr
->acquire_blocked_alarm();
215 // ----------------------- PendingTimer -----------------------------------
218 * Sleeping till timer reactivation - or just requeuing
220 PendingTimer::PendingTimer(my_context ctx
) : my_base(ctx
)
222 dout(10) << "-- state -->> Act/PendingTimer" << dendl
;
223 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
225 scrbr
->add_delayed_scheduling();
228 // ----------------------- NewChunk -----------------------------------
232 * - preemption data was set
233 * - epoch start was updated
235 NewChunk::NewChunk(my_context ctx
) : my_base(ctx
)
237 dout(10) << "-- state -->> Act/NewChunk" << dendl
;
238 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
240 scrbr
->get_preemptor().adjust_parameters();
242 // choose range to work on
243 // select_range_n_notify() will signal either SelectedChunkFree or
244 // ChunkIsBusy. If 'busy', we transition to Blocked, and wait for the
245 // range to become available.
246 scrbr
->select_range_n_notify();
249 sc::result
NewChunk::react(const SelectedChunkFree
&)
251 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
252 dout(10) << "NewChunk::react(const SelectedChunkFree&)" << dendl
;
254 scrbr
->set_subset_last_update(scrbr
->search_log_for_updates());
255 return transit
<WaitPushes
>();
258 // ----------------------- WaitPushes -----------------------------------
260 WaitPushes::WaitPushes(my_context ctx
) : my_base(ctx
)
262 dout(10) << " -- state -->> Act/WaitPushes" << dendl
;
263 post_event(ActivePushesUpd
{});
267 * Triggered externally, by the entity that had an update re pushes
269 sc::result
WaitPushes::react(const ActivePushesUpd
&)
271 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
272 dout(10) << "WaitPushes::react(const ActivePushesUpd&) pending_active_pushes: "
273 << scrbr
->pending_active_pushes() << dendl
;
275 if (!scrbr
->pending_active_pushes()) {
277 return transit
<WaitLastUpdate
>();
280 return discard_event();
283 // ----------------------- WaitLastUpdate -----------------------------------
285 WaitLastUpdate::WaitLastUpdate(my_context ctx
) : my_base(ctx
)
287 dout(10) << " -- state -->> Act/WaitLastUpdate" << dendl
;
288 post_event(UpdatesApplied
{});
293 * Updates are locally readable immediately. Thus, on the replicas we do need
294 * to wait for the update notifications before scrubbing. For the Primary it's
295 * a bit different: on EC (and only there) rmw operations have an additional
296 * read roundtrip. That means that on the Primary we need to wait for
297 * last_update_applied (the replica side, even on EC, is still safe
298 * since the actual transaction will already be readable by commit time.
300 void WaitLastUpdate::on_new_updates(const UpdatesApplied
&)
302 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
303 dout(10) << "WaitLastUpdate::on_new_updates(const UpdatesApplied&)" << dendl
;
305 if (scrbr
->has_pg_marked_new_updates()) {
306 post_event(InternalAllUpdates
{});
308 // will be requeued by op_applied
309 dout(10) << "wait for EC read/modify/writes to queue" << dendl
;
314 * request maps from the replicas in the acting set
316 sc::result
WaitLastUpdate::react(const InternalAllUpdates
&)
318 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
319 dout(10) << "WaitLastUpdate::react(const InternalAllUpdates&)" << dendl
;
321 scrbr
->get_replicas_maps(scrbr
->get_preemptor().is_preemptable());
322 return transit
<BuildMap
>();
325 // ----------------------- BuildMap -----------------------------------
327 BuildMap::BuildMap(my_context ctx
) : my_base(ctx
)
329 dout(10) << " -- state -->> Act/BuildMap" << dendl
;
330 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
332 // no need to check for an epoch change, as all possible flows that brought us here have
333 // a check_interval() verification of their final event.
335 if (scrbr
->get_preemptor().was_preempted()) {
337 // we were preempted, either directly or by a replica
338 dout(10) << __func__
<< " preempted!!!" << dendl
;
339 scrbr
->mark_local_map_ready();
340 post_event(IntBmPreempted
{});
344 auto ret
= scrbr
->build_primary_map_chunk();
346 if (ret
== -EINPROGRESS
) {
347 // must wait for the backend to finish. No specific event provided.
348 // build_primary_map_chunk() has already requeued us.
349 dout(20) << "waiting for the backend..." << dendl
;
351 } else if (ret
< 0) {
353 dout(10) << "BuildMap::BuildMap() Error! Aborting. Ret: " << ret
<< dendl
;
354 post_event(InternalError
{});
358 // the local map was created
359 post_event(IntLocalMapDone
{});
364 sc::result
BuildMap::react(const IntLocalMapDone
&)
366 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
367 dout(10) << "BuildMap::react(const IntLocalMapDone&)" << dendl
;
369 scrbr
->mark_local_map_ready();
370 return transit
<WaitReplicas
>();
373 // ----------------------- DrainReplMaps -----------------------------------
375 DrainReplMaps::DrainReplMaps(my_context ctx
) : my_base(ctx
)
377 dout(10) << "-- state -->> Act/DrainReplMaps" << dendl
;
378 // we may have received all maps already. Send the event that will make us check.
379 post_event(GotReplicas
{});
382 sc::result
DrainReplMaps::react(const GotReplicas
&)
384 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
385 dout(10) << "DrainReplMaps::react(const GotReplicas&)" << dendl
;
387 if (scrbr
->are_all_maps_available()) {
388 // NewChunk will handle the preemption that brought us to this state
389 return transit
<PendingTimer
>();
392 dout(15) << "DrainReplMaps::react(const GotReplicas&): still draining incoming maps: "
393 << scrbr
->dump_awaited_maps() << dendl
;
394 return discard_event();
397 // ----------------------- WaitReplicas -----------------------------------
399 WaitReplicas::WaitReplicas(my_context ctx
) : my_base(ctx
)
401 dout(10) << "-- state -->> Act/WaitReplicas" << dendl
;
402 post_event(GotReplicas
{});
406 * note: now that maps_compare_n_cleanup() is "futurized"(*), and we remain in this state
407 * for a while even after we got all our maps, we must prevent are_all_maps_available()
408 * (actually - the code after the if()) from being called more than once.
409 * This is basically a separate state, but it's too transitory and artificial to justify
410 * the cost of a separate state.
412 * (*) "futurized" - in Crimson, the call to maps_compare_n_cleanup() returns immediately
413 * after initiating the process. The actual termination of the maps comparing etc' is
414 * signalled via an event. As we share the code with "classic" OSD, here too
415 * maps_compare_n_cleanup() is responsible for signalling the completion of the
418 sc::result
WaitReplicas::react(const GotReplicas
&)
420 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
421 dout(10) << "WaitReplicas::react(const GotReplicas&)" << dendl
;
423 if (!all_maps_already_called
&& scrbr
->are_all_maps_available()) {
424 dout(10) << "WaitReplicas::react(const GotReplicas&) got all" << dendl
;
426 all_maps_already_called
= true;
428 // were we preempted?
429 if (scrbr
->get_preemptor().disable_and_test()) { // a test&set
432 dout(10) << "WaitReplicas::react(const GotReplicas&) PREEMPTED!" << dendl
;
433 return transit
<PendingTimer
>();
437 // maps_compare_n_cleanup() will arrange for MapsCompared event to be sent:
438 scrbr
->maps_compare_n_cleanup();
439 return discard_event();
442 return discard_event();
446 sc::result
WaitReplicas::react(const DigestUpdate
&)
448 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
449 std::string warn_msg
= "WaitReplicas::react(const DigestUpdate&): Unexpected DigestUpdate event";
450 dout(10) << warn_msg
<< dendl
;
451 scrbr
->log_cluster_warning(warn_msg
);
452 return discard_event();
455 // ----------------------- WaitDigestUpdate -----------------------------------
457 WaitDigestUpdate::WaitDigestUpdate(my_context ctx
) : my_base(ctx
)
459 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
460 dout(10) << "-- state -->> Act/WaitDigestUpdate" << dendl
;
462 // perform an initial check: maybe we already
463 // have all the updates we need:
464 // (note that DigestUpdate is usually an external event)
465 post_event(DigestUpdate
{});
468 sc::result
WaitDigestUpdate::react(const DigestUpdate
&)
470 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
471 dout(10) << "WaitDigestUpdate::react(const DigestUpdate&)" << dendl
;
473 // on_digest_updates() will either:
474 // - do nothing - if we are still waiting for updates, or
475 // - finish the scrubbing of the current chunk, and:
476 // - send NextChunk, or
477 // - send ScrubFinished
478 scrbr
->on_digest_updates();
479 return discard_event();
482 sc::result
WaitDigestUpdate::react(const ScrubFinished
&)
484 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
485 dout(10) << "WaitDigestUpdate::react(const ScrubFinished&)" << dendl
;
486 scrbr
->set_scrub_duration();
487 scrbr
->scrub_finish();
488 return transit
<NotActive
>();
491 ScrubMachine::ScrubMachine(PG
* pg
, ScrubMachineListener
* pg_scrub
)
492 : m_pg_id
{pg
->pg_id
}, m_scrbr
{pg_scrub
}
496 ScrubMachine::~ScrubMachine() = default;
498 // -------- for replicas -----------------------------------------------------
500 // ----------------------- ReplicaWaitUpdates --------------------------------
502 ReplicaWaitUpdates::ReplicaWaitUpdates(my_context ctx
) : my_base(ctx
)
504 dout(10) << "-- state -->> ReplicaWaitUpdates" << dendl
;
505 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
506 scrbr
->on_replica_init();
510 * Triggered externally, by the entity that had an update re pushes
512 sc::result
ReplicaWaitUpdates::react(const ReplicaPushesUpd
&)
514 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
515 dout(10) << "ReplicaWaitUpdates::react(const ReplicaPushesUpd&): "
516 << scrbr
->pending_active_pushes() << dendl
;
518 if (scrbr
->pending_active_pushes() == 0) {
521 return transit
<ActiveReplica
>();
524 return discard_event();
528 * the event poster is handling the scrubber reset
530 sc::result
ReplicaWaitUpdates::react(const FullReset
&)
532 dout(10) << "ReplicaWaitUpdates::react(const FullReset&)" << dendl
;
533 return transit
<NotActive
>();
536 // ----------------------- ActiveReplica -----------------------------------
538 ActiveReplica::ActiveReplica(my_context ctx
) : my_base(ctx
)
540 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
541 dout(10) << "-- state -->> ActiveReplica" << dendl
;
542 scrbr
->on_replica_init(); // as we might have skipped ReplicaWaitUpdates
543 post_event(SchedReplica
{});
546 sc::result
ActiveReplica::react(const SchedReplica
&)
548 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
549 dout(10) << "ActiveReplica::react(const SchedReplica&). is_preemptable? "
550 << scrbr
->get_preemptor().is_preemptable() << dendl
;
552 if (scrbr
->get_preemptor().was_preempted()) {
553 dout(10) << "replica scrub job preempted" << dendl
;
555 scrbr
->send_preempted_replica();
556 scrbr
->replica_handling_done();
557 return transit
<NotActive
>();
560 // start or check progress of build_replica_map_chunk()
561 auto ret_init
= scrbr
->build_replica_map_chunk();
562 if (ret_init
!= -EINPROGRESS
) {
563 return transit
<NotActive
>();
566 return discard_event();
570 * the event poster is handling the scrubber reset
572 sc::result
ActiveReplica::react(const FullReset
&)
574 dout(10) << "ActiveReplica::react(const FullReset&)" << dendl
;
575 return transit
<NotActive
>();