]>
git.proxmox.com Git - ceph.git/blob - ceph/src/osd/scrubber/scrub_machine.cc
7987559d0261f1cd399fd3fc5daa7ae1a0ca2d38
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 // ----------------------- ReservingReplicas ---------------------------------
109 ReservingReplicas::ReservingReplicas(my_context ctx
) : my_base(ctx
)
111 dout(10) << "-- state -->> ReservingReplicas" << dendl
;
112 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
114 // prevent the OSD from starting another scrub while we are trying to secure
115 // replicas resources
116 scrbr
->set_reserving_now();
117 scrbr
->reserve_replicas();
120 ReservingReplicas::~ReservingReplicas()
122 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
123 scrbr
->clear_reserving_now();
126 sc::result
ReservingReplicas::react(const ReservationFailure
&)
128 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
129 dout(10) << "ReservingReplicas::react(const ReservationFailure&)" << dendl
;
131 // the Scrubber must release all resources and abort the scrubbing
132 scrbr
->clear_pgscrub_state();
133 return transit
<NotActive
>();
137 * note: the event poster is handling the scrubber reset
139 sc::result
ReservingReplicas::react(const FullReset
&)
141 dout(10) << "ReservingReplicas::react(const FullReset&)" << dendl
;
142 return transit
<NotActive
>();
145 // ----------------------- ActiveScrubbing -----------------------------------
147 ActiveScrubbing::ActiveScrubbing(my_context ctx
) : my_base(ctx
)
149 dout(10) << "-- state -->> ActiveScrubbing" << dendl
;
150 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
155 * upon exiting the Active state
157 ActiveScrubbing::~ActiveScrubbing()
159 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
160 dout(15) << __func__
<< dendl
;
161 scrbr
->unreserve_replicas();
162 scrbr
->clear_queued_or_active();
166 * The only source of an InternalError event as of now is the BuildMap state,
167 * when encountering a backend error.
168 * We kill the scrub and reset the FSM.
170 sc::result
ActiveScrubbing::react(const InternalError
&)
172 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
173 dout(10) << __func__
<< dendl
;
174 scrbr
->clear_pgscrub_state();
175 return transit
<NotActive
>();
178 sc::result
ActiveScrubbing::react(const FullReset
&)
180 dout(10) << "ActiveScrubbing::react(const FullReset&)" << dendl
;
181 // caller takes care of clearing the scrubber & FSM states
182 return transit
<NotActive
>();
185 // ----------------------- RangeBlocked -----------------------------------
188 * Blocked. Will be released by kick_object_context_blocked() (or upon
191 * Note: we are never expected to be waiting for long for a blocked object.
192 * Unfortunately we know from experience that a bug elsewhere might result
193 * in an indefinite wait in this state, for an object that is never released.
194 * If that happens, all we can do is to issue a warning message to help
195 * with the debugging.
197 RangeBlocked::RangeBlocked(my_context ctx
) : my_base(ctx
)
199 dout(10) << "-- state -->> Act/RangeBlocked" << dendl
;
200 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
202 // arrange to have a warning message issued if we are stuck in this
203 // state for longer than some reasonable number of minutes.
204 m_timeout
= scrbr
->acquire_blocked_alarm();
207 // ----------------------- PendingTimer -----------------------------------
210 * Sleeping till timer reactivation - or just requeuing
212 PendingTimer::PendingTimer(my_context ctx
) : my_base(ctx
)
214 dout(10) << "-- state -->> Act/PendingTimer" << dendl
;
215 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
217 scrbr
->add_delayed_scheduling();
220 // ----------------------- NewChunk -----------------------------------
224 * - preemption data was set
225 * - epoch start was updated
227 NewChunk::NewChunk(my_context ctx
) : my_base(ctx
)
229 dout(10) << "-- state -->> Act/NewChunk" << dendl
;
230 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
232 scrbr
->get_preemptor().adjust_parameters();
234 // choose range to work on
235 // select_range_n_notify() will signal either SelectedChunkFree or
236 // ChunkIsBusy. If 'busy', we transition to Blocked, and wait for the
237 // range to become available.
238 scrbr
->select_range_n_notify();
241 sc::result
NewChunk::react(const SelectedChunkFree
&)
243 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
244 dout(10) << "NewChunk::react(const SelectedChunkFree&)" << dendl
;
246 scrbr
->set_subset_last_update(scrbr
->search_log_for_updates());
247 return transit
<WaitPushes
>();
250 // ----------------------- WaitPushes -----------------------------------
252 WaitPushes::WaitPushes(my_context ctx
) : my_base(ctx
)
254 dout(10) << " -- state -->> Act/WaitPushes" << dendl
;
255 post_event(ActivePushesUpd
{});
259 * Triggered externally, by the entity that had an update re pushes
261 sc::result
WaitPushes::react(const ActivePushesUpd
&)
263 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
264 dout(10) << "WaitPushes::react(const ActivePushesUpd&) pending_active_pushes: "
265 << scrbr
->pending_active_pushes() << dendl
;
267 if (!scrbr
->pending_active_pushes()) {
269 return transit
<WaitLastUpdate
>();
272 return discard_event();
275 // ----------------------- WaitLastUpdate -----------------------------------
277 WaitLastUpdate::WaitLastUpdate(my_context ctx
) : my_base(ctx
)
279 dout(10) << " -- state -->> Act/WaitLastUpdate" << dendl
;
280 post_event(UpdatesApplied
{});
285 * Updates are locally readable immediately. Thus, on the replicas we do need
286 * to wait for the update notifications before scrubbing. For the Primary it's
287 * a bit different: on EC (and only there) rmw operations have an additional
288 * read roundtrip. That means that on the Primary we need to wait for
289 * last_update_applied (the replica side, even on EC, is still safe
290 * since the actual transaction will already be readable by commit time.
292 void WaitLastUpdate::on_new_updates(const UpdatesApplied
&)
294 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
295 dout(10) << "WaitLastUpdate::on_new_updates(const UpdatesApplied&)" << dendl
;
297 if (scrbr
->has_pg_marked_new_updates()) {
298 post_event(InternalAllUpdates
{});
300 // will be requeued by op_applied
301 dout(10) << "wait for EC read/modify/writes to queue" << dendl
;
306 * request maps from the replicas in the acting set
308 sc::result
WaitLastUpdate::react(const InternalAllUpdates
&)
310 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
311 dout(10) << "WaitLastUpdate::react(const InternalAllUpdates&)" << dendl
;
313 scrbr
->get_replicas_maps(scrbr
->get_preemptor().is_preemptable());
314 return transit
<BuildMap
>();
317 // ----------------------- BuildMap -----------------------------------
319 BuildMap::BuildMap(my_context ctx
) : my_base(ctx
)
321 dout(10) << " -- state -->> Act/BuildMap" << dendl
;
322 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
324 // no need to check for an epoch change, as all possible flows that brought us here have
325 // a check_interval() verification of their final event.
327 if (scrbr
->get_preemptor().was_preempted()) {
329 // we were preempted, either directly or by a replica
330 dout(10) << __func__
<< " preempted!!!" << dendl
;
331 scrbr
->mark_local_map_ready();
332 post_event(IntBmPreempted
{});
336 auto ret
= scrbr
->build_primary_map_chunk();
338 if (ret
== -EINPROGRESS
) {
339 // must wait for the backend to finish. No specific event provided.
340 // build_primary_map_chunk() has already requeued us.
341 dout(20) << "waiting for the backend..." << dendl
;
343 } else if (ret
< 0) {
345 dout(10) << "BuildMap::BuildMap() Error! Aborting. Ret: " << ret
<< dendl
;
346 post_event(InternalError
{});
350 // the local map was created
351 post_event(IntLocalMapDone
{});
356 sc::result
BuildMap::react(const IntLocalMapDone
&)
358 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
359 dout(10) << "BuildMap::react(const IntLocalMapDone&)" << dendl
;
361 scrbr
->mark_local_map_ready();
362 return transit
<WaitReplicas
>();
365 // ----------------------- DrainReplMaps -----------------------------------
367 DrainReplMaps::DrainReplMaps(my_context ctx
) : my_base(ctx
)
369 dout(10) << "-- state -->> Act/DrainReplMaps" << dendl
;
370 // we may have received all maps already. Send the event that will make us check.
371 post_event(GotReplicas
{});
374 sc::result
DrainReplMaps::react(const GotReplicas
&)
376 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
377 dout(10) << "DrainReplMaps::react(const GotReplicas&)" << dendl
;
379 if (scrbr
->are_all_maps_available()) {
380 // NewChunk will handle the preemption that brought us to this state
381 return transit
<PendingTimer
>();
384 dout(15) << "DrainReplMaps::react(const GotReplicas&): still draining incoming maps: "
385 << scrbr
->dump_awaited_maps() << dendl
;
386 return discard_event();
389 // ----------------------- WaitReplicas -----------------------------------
391 WaitReplicas::WaitReplicas(my_context ctx
) : my_base(ctx
)
393 dout(10) << "-- state -->> Act/WaitReplicas" << dendl
;
394 post_event(GotReplicas
{});
398 * note: now that maps_compare_n_cleanup() is "futurized"(*), and we remain in this state
399 * for a while even after we got all our maps, we must prevent are_all_maps_available()
400 * (actually - the code after the if()) from being called more than once.
401 * This is basically a separate state, but it's too transitory and artificial to justify
402 * the cost of a separate state.
404 * (*) "futurized" - in Crimson, the call to maps_compare_n_cleanup() returns immediately
405 * after initiating the process. The actual termination of the maps comparing etc' is
406 * signalled via an event. As we share the code with "classic" OSD, here too
407 * maps_compare_n_cleanup() is responsible for signalling the completion of the
410 sc::result
WaitReplicas::react(const GotReplicas
&)
412 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
413 dout(10) << "WaitReplicas::react(const GotReplicas&)" << dendl
;
415 if (!all_maps_already_called
&& scrbr
->are_all_maps_available()) {
416 dout(10) << "WaitReplicas::react(const GotReplicas&) got all" << dendl
;
418 all_maps_already_called
= true;
420 // were we preempted?
421 if (scrbr
->get_preemptor().disable_and_test()) { // a test&set
424 dout(10) << "WaitReplicas::react(const GotReplicas&) PREEMPTED!" << dendl
;
425 return transit
<PendingTimer
>();
429 // maps_compare_n_cleanup() will arrange for MapsCompared event to be sent:
430 scrbr
->maps_compare_n_cleanup();
431 return discard_event();
434 return discard_event();
438 sc::result
WaitReplicas::react(const DigestUpdate
&)
440 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
441 std::string warn_msg
= "WaitReplicas::react(const DigestUpdate&): Unexpected DigestUpdate event";
442 dout(10) << warn_msg
<< dendl
;
443 scrbr
->log_cluster_warning(warn_msg
);
444 return discard_event();
447 // ----------------------- WaitDigestUpdate -----------------------------------
449 WaitDigestUpdate::WaitDigestUpdate(my_context ctx
) : my_base(ctx
)
451 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
452 dout(10) << "-- state -->> Act/WaitDigestUpdate" << dendl
;
454 // perform an initial check: maybe we already
455 // have all the updates we need:
456 // (note that DigestUpdate is usually an external event)
457 post_event(DigestUpdate
{});
460 sc::result
WaitDigestUpdate::react(const DigestUpdate
&)
462 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
463 dout(10) << "WaitDigestUpdate::react(const DigestUpdate&)" << dendl
;
465 // on_digest_updates() will either:
466 // - do nothing - if we are still waiting for updates, or
467 // - finish the scrubbing of the current chunk, and:
468 // - send NextChunk, or
469 // - send ScrubFinished
470 scrbr
->on_digest_updates();
471 return discard_event();
474 sc::result
WaitDigestUpdate::react(const ScrubFinished
&)
476 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
477 dout(10) << "WaitDigestUpdate::react(const ScrubFinished&)" << dendl
;
478 scrbr
->set_scrub_duration();
479 scrbr
->scrub_finish();
480 return transit
<NotActive
>();
483 ScrubMachine::ScrubMachine(PG
* pg
, ScrubMachineListener
* pg_scrub
)
484 : m_pg_id
{pg
->pg_id
}, m_scrbr
{pg_scrub
}
488 ScrubMachine::~ScrubMachine() = default;
490 // -------- for replicas -----------------------------------------------------
492 // ----------------------- ReplicaWaitUpdates --------------------------------
494 ReplicaWaitUpdates::ReplicaWaitUpdates(my_context ctx
) : my_base(ctx
)
496 dout(10) << "-- state -->> ReplicaWaitUpdates" << dendl
;
497 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
498 scrbr
->on_replica_init();
502 * Triggered externally, by the entity that had an update re pushes
504 sc::result
ReplicaWaitUpdates::react(const ReplicaPushesUpd
&)
506 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
507 dout(10) << "ReplicaWaitUpdates::react(const ReplicaPushesUpd&): "
508 << scrbr
->pending_active_pushes() << dendl
;
510 if (scrbr
->pending_active_pushes() == 0) {
513 return transit
<ActiveReplica
>();
516 return discard_event();
520 * the event poster is handling the scrubber reset
522 sc::result
ReplicaWaitUpdates::react(const FullReset
&)
524 dout(10) << "ReplicaWaitUpdates::react(const FullReset&)" << dendl
;
525 return transit
<NotActive
>();
528 // ----------------------- ActiveReplica -----------------------------------
530 ActiveReplica::ActiveReplica(my_context ctx
) : my_base(ctx
)
532 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
533 dout(10) << "-- state -->> ActiveReplica" << dendl
;
534 scrbr
->on_replica_init(); // as we might have skipped ReplicaWaitUpdates
535 post_event(SchedReplica
{});
538 sc::result
ActiveReplica::react(const SchedReplica
&)
540 DECLARE_LOCALS
; // 'scrbr' & 'pg_id' aliases
541 dout(10) << "ActiveReplica::react(const SchedReplica&). is_preemptable? "
542 << scrbr
->get_preemptor().is_preemptable() << dendl
;
544 if (scrbr
->get_preemptor().was_preempted()) {
545 dout(10) << "replica scrub job preempted" << dendl
;
547 scrbr
->send_preempted_replica();
548 scrbr
->replica_handling_done();
549 return transit
<NotActive
>();
552 // start or check progress of build_replica_map_chunk()
553 auto ret_init
= scrbr
->build_replica_map_chunk();
554 if (ret_init
!= -EINPROGRESS
) {
555 return transit
<NotActive
>();
558 return discard_event();
562 * the event poster is handling the scrubber reset
564 sc::result
ActiveReplica::react(const FullReset
&)
566 dout(10) << "ActiveReplica::react(const FullReset&)" << dendl
;
567 return transit
<NotActive
>();