1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright 2023 Red Hat
4 */
5
6#include "admin-state.h"
7
8#include "logger.h"
9#include "memory-alloc.h"
10#include "permassert.h"
11
12#include "completion.h"
13#include "types.h"
14
15static const struct admin_state_code VDO_CODE_NORMAL_OPERATION = {
16	.name = "VDO_ADMIN_STATE_NORMAL_OPERATION",
17	.normal = true,
18};
19const struct admin_state_code *VDO_ADMIN_STATE_NORMAL_OPERATION = &VDO_CODE_NORMAL_OPERATION;
20static const struct admin_state_code VDO_CODE_OPERATING = {
21	.name = "VDO_ADMIN_STATE_OPERATING",
22	.normal = true,
23	.operating = true,
24};
25const struct admin_state_code *VDO_ADMIN_STATE_OPERATING = &VDO_CODE_OPERATING;
26static const struct admin_state_code VDO_CODE_FORMATTING = {
27	.name = "VDO_ADMIN_STATE_FORMATTING",
28	.operating = true,
29	.loading = true,
30};
31const struct admin_state_code *VDO_ADMIN_STATE_FORMATTING = &VDO_CODE_FORMATTING;
32static const struct admin_state_code VDO_CODE_PRE_LOADING = {
33	.name = "VDO_ADMIN_STATE_PRE_LOADING",
34	.operating = true,
35	.loading = true,
36};
37const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADING = &VDO_CODE_PRE_LOADING;
38static const struct admin_state_code VDO_CODE_PRE_LOADED = {
39	.name = "VDO_ADMIN_STATE_PRE_LOADED",
40};
41const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADED = &VDO_CODE_PRE_LOADED;
42static const struct admin_state_code VDO_CODE_LOADING = {
43	.name = "VDO_ADMIN_STATE_LOADING",
44	.normal = true,
45	.operating = true,
46	.loading = true,
47};
48const struct admin_state_code *VDO_ADMIN_STATE_LOADING = &VDO_CODE_LOADING;
49static const struct admin_state_code VDO_CODE_LOADING_FOR_RECOVERY = {
50	.name = "VDO_ADMIN_STATE_LOADING_FOR_RECOVERY",
51	.operating = true,
52	.loading = true,
53};
54const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_RECOVERY =
55	&VDO_CODE_LOADING_FOR_RECOVERY;
56static const struct admin_state_code VDO_CODE_LOADING_FOR_REBUILD = {
57	.name = "VDO_ADMIN_STATE_LOADING_FOR_REBUILD",
58	.operating = true,
59	.loading = true,
60};
61const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_REBUILD = &VDO_CODE_LOADING_FOR_REBUILD;
62static const struct admin_state_code VDO_CODE_WAITING_FOR_RECOVERY = {
63	.name = "VDO_ADMIN_STATE_WAITING_FOR_RECOVERY",
64	.operating = true,
65};
66const struct admin_state_code *VDO_ADMIN_STATE_WAITING_FOR_RECOVERY =
67	&VDO_CODE_WAITING_FOR_RECOVERY;
68static const struct admin_state_code VDO_CODE_NEW = {
69	.name = "VDO_ADMIN_STATE_NEW",
70	.quiescent = true,
71};
72const struct admin_state_code *VDO_ADMIN_STATE_NEW = &VDO_CODE_NEW;
73static const struct admin_state_code VDO_CODE_INITIALIZED = {
74	.name = "VDO_ADMIN_STATE_INITIALIZED",
75};
76const struct admin_state_code *VDO_ADMIN_STATE_INITIALIZED = &VDO_CODE_INITIALIZED;
77static const struct admin_state_code VDO_CODE_RECOVERING = {
78	.name = "VDO_ADMIN_STATE_RECOVERING",
79	.draining = true,
80	.operating = true,
81};
82const struct admin_state_code *VDO_ADMIN_STATE_RECOVERING = &VDO_CODE_RECOVERING;
83static const struct admin_state_code VDO_CODE_REBUILDING = {
84	.name = "VDO_ADMIN_STATE_REBUILDING",
85	.draining = true,
86	.operating = true,
87};
88const struct admin_state_code *VDO_ADMIN_STATE_REBUILDING = &VDO_CODE_REBUILDING;
89static const struct admin_state_code VDO_CODE_SAVING = {
90	.name = "VDO_ADMIN_STATE_SAVING",
91	.draining = true,
92	.quiescing = true,
93	.operating = true,
94};
95const struct admin_state_code *VDO_ADMIN_STATE_SAVING = &VDO_CODE_SAVING;
96static const struct admin_state_code VDO_CODE_SAVED = {
97	.name = "VDO_ADMIN_STATE_SAVED",
98	.quiescent = true,
99};
100const struct admin_state_code *VDO_ADMIN_STATE_SAVED = &VDO_CODE_SAVED;
101static const struct admin_state_code VDO_CODE_SCRUBBING = {
102	.name = "VDO_ADMIN_STATE_SCRUBBING",
103	.draining = true,
104	.loading = true,
105	.operating = true,
106};
107const struct admin_state_code *VDO_ADMIN_STATE_SCRUBBING = &VDO_CODE_SCRUBBING;
108static const struct admin_state_code VDO_CODE_SAVE_FOR_SCRUBBING = {
109	.name = "VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING",
110	.draining = true,
111	.operating = true,
112};
113const struct admin_state_code *VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING = &VDO_CODE_SAVE_FOR_SCRUBBING;
114static const struct admin_state_code VDO_CODE_STOPPING = {
115	.name = "VDO_ADMIN_STATE_STOPPING",
116	.draining = true,
117	.quiescing = true,
118	.operating = true,
119};
120const struct admin_state_code *VDO_ADMIN_STATE_STOPPING = &VDO_CODE_STOPPING;
121static const struct admin_state_code VDO_CODE_STOPPED = {
122	.name = "VDO_ADMIN_STATE_STOPPED",
123	.quiescent = true,
124};
125const struct admin_state_code *VDO_ADMIN_STATE_STOPPED = &VDO_CODE_STOPPED;
126static const struct admin_state_code VDO_CODE_SUSPENDING = {
127	.name = "VDO_ADMIN_STATE_SUSPENDING",
128	.draining = true,
129	.quiescing = true,
130	.operating = true,
131};
132const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDING = &VDO_CODE_SUSPENDING;
133static const struct admin_state_code VDO_CODE_SUSPENDED = {
134	.name = "VDO_ADMIN_STATE_SUSPENDED",
135	.quiescent = true,
136};
137const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED = &VDO_CODE_SUSPENDED;
138static const struct admin_state_code VDO_CODE_SUSPENDED_OPERATION = {
139	.name = "VDO_ADMIN_STATE_SUSPENDED_OPERATION",
140	.operating = true,
141};
142const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED_OPERATION = &VDO_CODE_SUSPENDED_OPERATION;
143static const struct admin_state_code VDO_CODE_RESUMING = {
144	.name = "VDO_ADMIN_STATE_RESUMING",
145	.operating = true,
146};
147const struct admin_state_code *VDO_ADMIN_STATE_RESUMING = &VDO_CODE_RESUMING;
148
149/**
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.
153 *
154 * Return: The state to set when the operation completes or NULL if the operation can not be
155 *         started in the current state.
156 */
157static const struct admin_state_code *get_next_state(const struct admin_state *state,
158						     const struct admin_state_code *operation)
159{
160	const struct admin_state_code *code = vdo_get_admin_state_code(state);
161
162	if (code->operating)
163		return NULL;
164
165	if (operation == VDO_ADMIN_STATE_SAVING)
166		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_SAVED : NULL);
167
168	if (operation == VDO_ADMIN_STATE_SUSPENDING) {
169		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION
170			? VDO_ADMIN_STATE_SUSPENDED
171			: NULL);
172	}
173
174	if (operation == VDO_ADMIN_STATE_STOPPING)
175		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_STOPPED : NULL);
176
177	if (operation == VDO_ADMIN_STATE_PRE_LOADING)
178		return (code == VDO_ADMIN_STATE_INITIALIZED ? VDO_ADMIN_STATE_PRE_LOADED : NULL);
179
180	if (operation == VDO_ADMIN_STATE_SUSPENDED_OPERATION) {
181		return (((code == VDO_ADMIN_STATE_SUSPENDED) ||
182			 (code == VDO_ADMIN_STATE_SAVED)) ? code : NULL);
183	}
184
185	return VDO_ADMIN_STATE_NORMAL_OPERATION;
186}
187
188/**
189 * vdo_finish_operation() - Finish the current operation.
190 *
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.
194 *
195 * Return: true if there was an operation to finish.
196 */
197bool vdo_finish_operation(struct admin_state *state, int result)
198{
199	if (!vdo_get_admin_state_code(state)->operating)
200		return false;
201
202	state->complete = state->starting;
203	if (state->waiter != NULL)
204		vdo_set_completion_result(state->waiter, result);
205
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));
210	}
211
212	return true;
213}
214
215/**
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.
219 *
220 * Return: VDO_SUCCESS or an error.
221 */
222static 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)
226{
227	int result;
228	const struct admin_state_code *next_state = get_next_state(state, operation);
229
230	if (next_state == NULL) {
231		result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
232						"Can't start %s from %s",
233						operation->name,
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",
238						operation->name);
239	} else {
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;
245			initiator(state);
246			state->starting = false;
247			if (state->complete)
248				vdo_finish_operation(state, VDO_SUCCESS);
249		}
250
251		return VDO_SUCCESS;
252	}
253
254	if (waiter != NULL)
255		vdo_continue_completion(waiter, result);
256
257	return result;
258}
259
260/**
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.
264 *
265 * Return: true if the operation was started.
266 */
267static 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)
271{
272	return (begin_operation(state, operation, waiter, initiator) == VDO_SUCCESS);
273}
274
275/**
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.
281 *
282 * If the result failed, log an invalid state error and, if there is a waiter, notify it.
283 *
284 * Return: The result of the check.
285 */
286static bool check_code(bool valid, const struct admin_state_code *code, const char *what,
287		       struct vdo_completion *waiter)
288{
289	int result;
290
291	if (valid)
292		return true;
293
294	result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
295					"%s is not a %s", code->name, what);
296	if (waiter != NULL)
297		vdo_continue_completion(waiter, result);
298
299	return false;
300}
301
302/**
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.
305 *
306 * Return: true if the specified operation is a drain.
307 */
308static bool __must_check assert_vdo_drain_operation(const struct admin_state_code *operation,
309						    struct vdo_completion *waiter)
310{
311	return check_code(operation->draining, operation, "drain operation", waiter);
312}
313
314/**
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.
319 *
320 * Return: true if the drain was initiated, if not the waiter will be notified.
321 */
322bool vdo_start_draining(struct admin_state *state,
323			const struct admin_state_code *operation,
324			struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
325{
326	const struct admin_state_code *code = vdo_get_admin_state_code(state);
327
328	if (!assert_vdo_drain_operation(operation, waiter))
329		return false;
330
331	if (code->quiescent) {
332		vdo_launch_completion(waiter);
333		return false;
334	}
335
336	if (!code->normal) {
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);
340		return false;
341	}
342
343	return start_operation(state, operation, waiter, initiator);
344}
345
346/**
347 * vdo_finish_draining() - Finish a drain operation if one was in progress.
348 *
349 * Return: true if the state was draining; will notify the waiter if so.
350 */
351bool vdo_finish_draining(struct admin_state *state)
352{
353	return vdo_finish_draining_with_result(state, VDO_SUCCESS);
354}
355
356/**
357 * vdo_finish_draining_with_result() - Finish a drain operation with a status code.
358 *
359 * Return: true if the state was draining; will notify the waiter if so.
360 */
361bool vdo_finish_draining_with_result(struct admin_state *state, int result)
362{
363	return (vdo_is_state_draining(state) && vdo_finish_operation(state, result));
364}
365
366/**
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.
369 *
370 * Return: true if the specified operation is a load.
371 */
372bool vdo_assert_load_operation(const struct admin_state_code *operation,
373			       struct vdo_completion *waiter)
374{
375	return check_code(operation->loading, operation, "load operation", waiter);
376}
377
378/**
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.
383 *
384 * Return: true if the load was initiated, if not the waiter will be notified.
385 */
386bool vdo_start_loading(struct admin_state *state,
387		       const struct admin_state_code *operation,
388		       struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
389{
390	return (vdo_assert_load_operation(operation, waiter) &&
391		start_operation(state, operation, waiter, initiator));
392}
393
394/**
395 * vdo_finish_loading() - Finish a load operation if one was in progress.
396 *
397 * Return: true if the state was loading; will notify the waiter if so.
398 */
399bool vdo_finish_loading(struct admin_state *state)
400{
401	return vdo_finish_loading_with_result(state, VDO_SUCCESS);
402}
403
404/**
405 * vdo_finish_loading_with_result() - Finish a load operation with a status code.
406 * @result The result of the load operation.
407 *
408 * Return: true if the state was loading; will notify the waiter if so.
409 */
410bool vdo_finish_loading_with_result(struct admin_state *state, int result)
411{
412	return (vdo_is_state_loading(state) && vdo_finish_operation(state, result));
413}
414
415/**
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.
418 *
419 * Return: true if the code is a resume operation.
420 */
421static bool __must_check assert_vdo_resume_operation(const struct admin_state_code *operation,
422						     struct vdo_completion *waiter)
423{
424	return check_code(operation == VDO_ADMIN_STATE_RESUMING, operation,
425			  "resume operation", waiter);
426}
427
428/**
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.
433 *
434 * Return: true if the resume was initiated, if not the waiter will be notified.
435 */
436bool vdo_start_resuming(struct admin_state *state,
437			const struct admin_state_code *operation,
438			struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
439{
440	return (assert_vdo_resume_operation(operation, waiter) &&
441		start_operation(state, operation, waiter, initiator));
442}
443
444/**
445 * vdo_finish_resuming() - Finish a resume operation if one was in progress.
446 *
447 * Return: true if the state was resuming; will notify the waiter if so.
448 */
449bool vdo_finish_resuming(struct admin_state *state)
450{
451	return vdo_finish_resuming_with_result(state, VDO_SUCCESS);
452}
453
454/**
455 * vdo_finish_resuming_with_result() - Finish a resume operation with a status code.
456 * @result The result of the resume operation.
457 *
458 * Return: true if the state was resuming; will notify the waiter if so.
459 */
460bool vdo_finish_resuming_with_result(struct admin_state *state, int result)
461{
462	return (vdo_is_state_resuming(state) && vdo_finish_operation(state, result));
463}
464
465/**
466 * vdo_resume_if_quiescent() - Change the state to normal operation if the current state is
467 *                             quiescent.
468 *
469 * Return: VDO_SUCCESS if the state resumed, VDO_INVALID_ADMIN_STATE otherwise.
470 */
471int vdo_resume_if_quiescent(struct admin_state *state)
472{
473	if (!vdo_is_state_quiescent(state))
474		return VDO_INVALID_ADMIN_STATE;
475
476	vdo_set_admin_state_code(state, VDO_ADMIN_STATE_NORMAL_OPERATION);
477	return VDO_SUCCESS;
478}
479
480/**
481 * vdo_start_operation() - Attempt to start an operation.
482 *
483 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
484 */
485int vdo_start_operation(struct admin_state *state,
486			const struct admin_state_code *operation)
487{
488	return vdo_start_operation_with_waiter(state, operation, NULL, NULL);
489}
490
491/**
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.
495 *
496 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
497 */
498int 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)
502{
503	return (check_code(operation->operating, operation, "operation", waiter) ?
504		begin_operation(state, operation, waiter, initiator) :
505		VDO_INVALID_ADMIN_STATE);
506}
507