1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
6 #include "admin-state.h"
9 #include "memory-alloc.h"
10 #include "permassert.h"
12 #include "completion.h"
15 static const struct admin_state_code VDO_CODE_NORMAL_OPERATION
= {
16 .name
= "VDO_ADMIN_STATE_NORMAL_OPERATION",
19 const struct admin_state_code
*VDO_ADMIN_STATE_NORMAL_OPERATION
= &VDO_CODE_NORMAL_OPERATION
;
20 static const struct admin_state_code VDO_CODE_OPERATING
= {
21 .name
= "VDO_ADMIN_STATE_OPERATING",
25 const struct admin_state_code
*VDO_ADMIN_STATE_OPERATING
= &VDO_CODE_OPERATING
;
26 static const struct admin_state_code VDO_CODE_FORMATTING
= {
27 .name
= "VDO_ADMIN_STATE_FORMATTING",
31 const struct admin_state_code
*VDO_ADMIN_STATE_FORMATTING
= &VDO_CODE_FORMATTING
;
32 static const struct admin_state_code VDO_CODE_PRE_LOADING
= {
33 .name
= "VDO_ADMIN_STATE_PRE_LOADING",
37 const struct admin_state_code
*VDO_ADMIN_STATE_PRE_LOADING
= &VDO_CODE_PRE_LOADING
;
38 static const struct admin_state_code VDO_CODE_PRE_LOADED
= {
39 .name
= "VDO_ADMIN_STATE_PRE_LOADED",
41 const struct admin_state_code
*VDO_ADMIN_STATE_PRE_LOADED
= &VDO_CODE_PRE_LOADED
;
42 static const struct admin_state_code VDO_CODE_LOADING
= {
43 .name
= "VDO_ADMIN_STATE_LOADING",
48 const struct admin_state_code
*VDO_ADMIN_STATE_LOADING
= &VDO_CODE_LOADING
;
49 static const struct admin_state_code VDO_CODE_LOADING_FOR_RECOVERY
= {
50 .name
= "VDO_ADMIN_STATE_LOADING_FOR_RECOVERY",
54 const struct admin_state_code
*VDO_ADMIN_STATE_LOADING_FOR_RECOVERY
=
55 &VDO_CODE_LOADING_FOR_RECOVERY
;
56 static const struct admin_state_code VDO_CODE_LOADING_FOR_REBUILD
= {
57 .name
= "VDO_ADMIN_STATE_LOADING_FOR_REBUILD",
61 const struct admin_state_code
*VDO_ADMIN_STATE_LOADING_FOR_REBUILD
= &VDO_CODE_LOADING_FOR_REBUILD
;
62 static const struct admin_state_code VDO_CODE_WAITING_FOR_RECOVERY
= {
63 .name
= "VDO_ADMIN_STATE_WAITING_FOR_RECOVERY",
66 const struct admin_state_code
*VDO_ADMIN_STATE_WAITING_FOR_RECOVERY
=
67 &VDO_CODE_WAITING_FOR_RECOVERY
;
68 static const struct admin_state_code VDO_CODE_NEW
= {
69 .name
= "VDO_ADMIN_STATE_NEW",
72 const struct admin_state_code
*VDO_ADMIN_STATE_NEW
= &VDO_CODE_NEW
;
73 static const struct admin_state_code VDO_CODE_INITIALIZED
= {
74 .name
= "VDO_ADMIN_STATE_INITIALIZED",
76 const struct admin_state_code
*VDO_ADMIN_STATE_INITIALIZED
= &VDO_CODE_INITIALIZED
;
77 static const struct admin_state_code VDO_CODE_RECOVERING
= {
78 .name
= "VDO_ADMIN_STATE_RECOVERING",
82 const struct admin_state_code
*VDO_ADMIN_STATE_RECOVERING
= &VDO_CODE_RECOVERING
;
83 static const struct admin_state_code VDO_CODE_REBUILDING
= {
84 .name
= "VDO_ADMIN_STATE_REBUILDING",
88 const struct admin_state_code
*VDO_ADMIN_STATE_REBUILDING
= &VDO_CODE_REBUILDING
;
89 static const struct admin_state_code VDO_CODE_SAVING
= {
90 .name
= "VDO_ADMIN_STATE_SAVING",
95 const struct admin_state_code
*VDO_ADMIN_STATE_SAVING
= &VDO_CODE_SAVING
;
96 static const struct admin_state_code VDO_CODE_SAVED
= {
97 .name
= "VDO_ADMIN_STATE_SAVED",
100 const struct admin_state_code
*VDO_ADMIN_STATE_SAVED
= &VDO_CODE_SAVED
;
101 static const struct admin_state_code VDO_CODE_SCRUBBING
= {
102 .name
= "VDO_ADMIN_STATE_SCRUBBING",
107 const struct admin_state_code
*VDO_ADMIN_STATE_SCRUBBING
= &VDO_CODE_SCRUBBING
;
108 static const struct admin_state_code VDO_CODE_SAVE_FOR_SCRUBBING
= {
109 .name
= "VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING",
113 const struct admin_state_code
*VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING
= &VDO_CODE_SAVE_FOR_SCRUBBING
;
114 static const struct admin_state_code VDO_CODE_STOPPING
= {
115 .name
= "VDO_ADMIN_STATE_STOPPING",
120 const struct admin_state_code
*VDO_ADMIN_STATE_STOPPING
= &VDO_CODE_STOPPING
;
121 static const struct admin_state_code VDO_CODE_STOPPED
= {
122 .name
= "VDO_ADMIN_STATE_STOPPED",
125 const struct admin_state_code
*VDO_ADMIN_STATE_STOPPED
= &VDO_CODE_STOPPED
;
126 static const struct admin_state_code VDO_CODE_SUSPENDING
= {
127 .name
= "VDO_ADMIN_STATE_SUSPENDING",
132 const struct admin_state_code
*VDO_ADMIN_STATE_SUSPENDING
= &VDO_CODE_SUSPENDING
;
133 static const struct admin_state_code VDO_CODE_SUSPENDED
= {
134 .name
= "VDO_ADMIN_STATE_SUSPENDED",
137 const struct admin_state_code
*VDO_ADMIN_STATE_SUSPENDED
= &VDO_CODE_SUSPENDED
;
138 static const struct admin_state_code VDO_CODE_SUSPENDED_OPERATION
= {
139 .name
= "VDO_ADMIN_STATE_SUSPENDED_OPERATION",
142 const struct admin_state_code
*VDO_ADMIN_STATE_SUSPENDED_OPERATION
= &VDO_CODE_SUSPENDED_OPERATION
;
143 static const struct admin_state_code VDO_CODE_RESUMING
= {
144 .name
= "VDO_ADMIN_STATE_RESUMING",
147 const struct admin_state_code
*VDO_ADMIN_STATE_RESUMING
= &VDO_CODE_RESUMING
;
150 * get_next_state() - Determine the state which should be set after a given operation completes
151 * based on the operation and the current state.
152 * @operation The operation to be started.
154 * Return: The state to set when the operation completes or NULL if the operation can not be
155 * started in the current state.
157 static const struct admin_state_code
*get_next_state(const struct admin_state
*state
,
158 const struct admin_state_code
*operation
)
160 const struct admin_state_code
*code
= vdo_get_admin_state_code(state
);
165 if (operation
== VDO_ADMIN_STATE_SAVING
)
166 return (code
== VDO_ADMIN_STATE_NORMAL_OPERATION
? VDO_ADMIN_STATE_SAVED
: NULL
);
168 if (operation
== VDO_ADMIN_STATE_SUSPENDING
) {
169 return (code
== VDO_ADMIN_STATE_NORMAL_OPERATION
170 ? VDO_ADMIN_STATE_SUSPENDED
174 if (operation
== VDO_ADMIN_STATE_STOPPING
)
175 return (code
== VDO_ADMIN_STATE_NORMAL_OPERATION
? VDO_ADMIN_STATE_STOPPED
: NULL
);
177 if (operation
== VDO_ADMIN_STATE_PRE_LOADING
)
178 return (code
== VDO_ADMIN_STATE_INITIALIZED
? VDO_ADMIN_STATE_PRE_LOADED
: NULL
);
180 if (operation
== VDO_ADMIN_STATE_SUSPENDED_OPERATION
) {
181 return (((code
== VDO_ADMIN_STATE_SUSPENDED
) ||
182 (code
== VDO_ADMIN_STATE_SAVED
)) ? code
: NULL
);
185 return VDO_ADMIN_STATE_NORMAL_OPERATION
;
189 * vdo_finish_operation() - Finish the current operation.
191 * Will notify the operation waiter if there is one. This method should be used for operations
192 * started with vdo_start_operation(). For operations which were started with vdo_start_draining(),
193 * use vdo_finish_draining() instead.
195 * Return: true if there was an operation to finish.
197 bool vdo_finish_operation(struct admin_state
*state
, int result
)
199 if (!vdo_get_admin_state_code(state
)->operating
)
202 state
->complete
= state
->starting
;
203 if (state
->waiter
!= NULL
)
204 vdo_set_completion_result(state
->waiter
, result
);
206 if (!state
->starting
) {
207 vdo_set_admin_state_code(state
, state
->next_state
);
208 if (state
->waiter
!= NULL
)
209 vdo_launch_completion(vdo_forget(state
->waiter
));
216 * begin_operation() - Begin an operation if it may be started given the current state.
217 * @waiter A completion to notify when the operation is complete; may be NULL.
218 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
220 * Return: VDO_SUCCESS or an error.
222 static int __must_check
begin_operation(struct admin_state
*state
,
223 const struct admin_state_code
*operation
,
224 struct vdo_completion
*waiter
,
225 vdo_admin_initiator_fn initiator
)
228 const struct admin_state_code
*next_state
= get_next_state(state
, operation
);
230 if (next_state
== NULL
) {
231 result
= vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE
,
232 "Can't start %s from %s",
234 vdo_get_admin_state_code(state
)->name
);
235 } else if (state
->waiter
!= NULL
) {
236 result
= vdo_log_error_strerror(VDO_COMPONENT_BUSY
,
237 "Can't start %s with extant waiter",
240 state
->waiter
= waiter
;
241 state
->next_state
= next_state
;
242 vdo_set_admin_state_code(state
, operation
);
243 if (initiator
!= NULL
) {
244 state
->starting
= true;
246 state
->starting
= false;
248 vdo_finish_operation(state
, VDO_SUCCESS
);
255 vdo_continue_completion(waiter
, result
);
261 * start_operation() - Start an operation if it may be started given the current state.
262 * @waiter A completion to notify when the operation is complete.
263 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
265 * Return: true if the operation was started.
267 static inline bool __must_check
start_operation(struct admin_state
*state
,
268 const struct admin_state_code
*operation
,
269 struct vdo_completion
*waiter
,
270 vdo_admin_initiator_fn initiator
)
272 return (begin_operation(state
, operation
, waiter
, initiator
) == VDO_SUCCESS
);
276 * check_code() - Check the result of a state validation.
277 * @valid true if the code is of an appropriate type.
278 * @code The code which failed to be of the correct type.
279 * @what What the code failed to be, for logging.
280 * @waiter The completion to notify of the error; may be NULL.
282 * If the result failed, log an invalid state error and, if there is a waiter, notify it.
284 * Return: The result of the check.
286 static bool check_code(bool valid
, const struct admin_state_code
*code
, const char *what
,
287 struct vdo_completion
*waiter
)
294 result
= vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE
,
295 "%s is not a %s", code
->name
, what
);
297 vdo_continue_completion(waiter
, result
);
303 * assert_vdo_drain_operation() - Check that an operation is a drain.
304 * @waiter The completion to finish with an error if the operation is not a drain.
306 * Return: true if the specified operation is a drain.
308 static bool __must_check
assert_vdo_drain_operation(const struct admin_state_code
*operation
,
309 struct vdo_completion
*waiter
)
311 return check_code(operation
->draining
, operation
, "drain operation", waiter
);
315 * vdo_start_draining() - Initiate a drain operation if the current state permits it.
316 * @operation The type of drain to initiate.
317 * @waiter The completion to notify when the drain is complete.
318 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
320 * Return: true if the drain was initiated, if not the waiter will be notified.
322 bool vdo_start_draining(struct admin_state
*state
,
323 const struct admin_state_code
*operation
,
324 struct vdo_completion
*waiter
, vdo_admin_initiator_fn initiator
)
326 const struct admin_state_code
*code
= vdo_get_admin_state_code(state
);
328 if (!assert_vdo_drain_operation(operation
, waiter
))
331 if (code
->quiescent
) {
332 vdo_launch_completion(waiter
);
337 vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE
, "can't start %s from %s",
338 operation
->name
, code
->name
);
339 vdo_continue_completion(waiter
, VDO_INVALID_ADMIN_STATE
);
343 return start_operation(state
, operation
, waiter
, initiator
);
347 * vdo_finish_draining() - Finish a drain operation if one was in progress.
349 * Return: true if the state was draining; will notify the waiter if so.
351 bool vdo_finish_draining(struct admin_state
*state
)
353 return vdo_finish_draining_with_result(state
, VDO_SUCCESS
);
357 * vdo_finish_draining_with_result() - Finish a drain operation with a status code.
359 * Return: true if the state was draining; will notify the waiter if so.
361 bool vdo_finish_draining_with_result(struct admin_state
*state
, int result
)
363 return (vdo_is_state_draining(state
) && vdo_finish_operation(state
, result
));
367 * vdo_assert_load_operation() - Check that an operation is a load.
368 * @waiter The completion to finish with an error if the operation is not a load.
370 * Return: true if the specified operation is a load.
372 bool vdo_assert_load_operation(const struct admin_state_code
*operation
,
373 struct vdo_completion
*waiter
)
375 return check_code(operation
->loading
, operation
, "load operation", waiter
);
379 * vdo_start_loading() - Initiate a load operation if the current state permits it.
380 * @operation The type of load to initiate.
381 * @waiter The completion to notify when the load is complete (may be NULL).
382 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
384 * Return: true if the load was initiated, if not the waiter will be notified.
386 bool vdo_start_loading(struct admin_state
*state
,
387 const struct admin_state_code
*operation
,
388 struct vdo_completion
*waiter
, vdo_admin_initiator_fn initiator
)
390 return (vdo_assert_load_operation(operation
, waiter
) &&
391 start_operation(state
, operation
, waiter
, initiator
));
395 * vdo_finish_loading() - Finish a load operation if one was in progress.
397 * Return: true if the state was loading; will notify the waiter if so.
399 bool vdo_finish_loading(struct admin_state
*state
)
401 return vdo_finish_loading_with_result(state
, VDO_SUCCESS
);
405 * vdo_finish_loading_with_result() - Finish a load operation with a status code.
406 * @result The result of the load operation.
408 * Return: true if the state was loading; will notify the waiter if so.
410 bool vdo_finish_loading_with_result(struct admin_state
*state
, int result
)
412 return (vdo_is_state_loading(state
) && vdo_finish_operation(state
, result
));
416 * assert_vdo_resume_operation() - Check whether an admin_state_code is a resume operation.
417 * @waiter The completion to notify if the operation is not a resume operation; may be NULL.
419 * Return: true if the code is a resume operation.
421 static bool __must_check
assert_vdo_resume_operation(const struct admin_state_code
*operation
,
422 struct vdo_completion
*waiter
)
424 return check_code(operation
== VDO_ADMIN_STATE_RESUMING
, operation
,
425 "resume operation", waiter
);
429 * vdo_start_resuming() - Initiate a resume operation if the current state permits it.
430 * @operation The type of resume to start.
431 * @waiter The completion to notify when the resume is complete (may be NULL).
432 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
434 * Return: true if the resume was initiated, if not the waiter will be notified.
436 bool vdo_start_resuming(struct admin_state
*state
,
437 const struct admin_state_code
*operation
,
438 struct vdo_completion
*waiter
, vdo_admin_initiator_fn initiator
)
440 return (assert_vdo_resume_operation(operation
, waiter
) &&
441 start_operation(state
, operation
, waiter
, initiator
));
445 * vdo_finish_resuming() - Finish a resume operation if one was in progress.
447 * Return: true if the state was resuming; will notify the waiter if so.
449 bool vdo_finish_resuming(struct admin_state
*state
)
451 return vdo_finish_resuming_with_result(state
, VDO_SUCCESS
);
455 * vdo_finish_resuming_with_result() - Finish a resume operation with a status code.
456 * @result The result of the resume operation.
458 * Return: true if the state was resuming; will notify the waiter if so.
460 bool vdo_finish_resuming_with_result(struct admin_state
*state
, int result
)
462 return (vdo_is_state_resuming(state
) && vdo_finish_operation(state
, result
));
466 * vdo_resume_if_quiescent() - Change the state to normal operation if the current state is
469 * Return: VDO_SUCCESS if the state resumed, VDO_INVALID_ADMIN_STATE otherwise.
471 int vdo_resume_if_quiescent(struct admin_state
*state
)
473 if (!vdo_is_state_quiescent(state
))
474 return VDO_INVALID_ADMIN_STATE
;
476 vdo_set_admin_state_code(state
, VDO_ADMIN_STATE_NORMAL_OPERATION
);
481 * vdo_start_operation() - Attempt to start an operation.
483 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
485 int vdo_start_operation(struct admin_state
*state
,
486 const struct admin_state_code
*operation
)
488 return vdo_start_operation_with_waiter(state
, operation
, NULL
, NULL
);
492 * vdo_start_operation_with_waiter() - Attempt to start an operation.
493 * @waiter the completion to notify when the operation completes or fails to start; may be NULL.
494 * @initiator The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
496 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
498 int vdo_start_operation_with_waiter(struct admin_state
*state
,
499 const struct admin_state_code
*operation
,
500 struct vdo_completion
*waiter
,
501 vdo_admin_initiator_fn initiator
)
503 return (check_code(operation
->operating
, operation
, "operation", waiter
) ?
504 begin_operation(state
, operation
, waiter
, initiator
) :
505 VDO_INVALID_ADMIN_STATE
);