File Index
Files and directories
Sign in
FILE

admin-state.c

File preview with syntax highlighting.

linux-6.19.14/drivers/md/dm-vdo/admin-state.c C
  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 * @state: The current admin state.
153 * @operation: The operation to be started.
154 *
155 * Return: The state to set when the operation completes or NULL if the operation can not be
156 *         started in the current state.
157 */
158static const struct admin_state_code *get_next_state(const struct admin_state *state,
159						     const struct admin_state_code *operation)
160{
161	const struct admin_state_code *code = vdo_get_admin_state_code(state);
162
163	if (code->operating)
164		return NULL;
165
166	if (operation == VDO_ADMIN_STATE_SAVING)
167		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_SAVED : NULL);
168
169	if (operation == VDO_ADMIN_STATE_SUSPENDING) {
170		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION
171			? VDO_ADMIN_STATE_SUSPENDED
172			: NULL);
173	}
174
175	if (operation == VDO_ADMIN_STATE_STOPPING)
176		return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_STOPPED : NULL);
177
178	if (operation == VDO_ADMIN_STATE_PRE_LOADING)
179		return (code == VDO_ADMIN_STATE_INITIALIZED ? VDO_ADMIN_STATE_PRE_LOADED : NULL);
180
181	if (operation == VDO_ADMIN_STATE_SUSPENDED_OPERATION) {
182		return (((code == VDO_ADMIN_STATE_SUSPENDED) ||
183			 (code == VDO_ADMIN_STATE_SAVED)) ? code : NULL);
184	}
185
186	return VDO_ADMIN_STATE_NORMAL_OPERATION;
187}
188
189/**
190 * vdo_finish_operation() - Finish the current operation.
191 * @state: The current admin state.
192 * @result: The result of the operation.
193 *
194 * Will notify the operation waiter if there is one. This method should be used for operations
195 * started with vdo_start_operation(). For operations which were started with vdo_start_draining(),
196 * use vdo_finish_draining() instead.
197 *
198 * Return: true if there was an operation to finish.
199 */
200bool vdo_finish_operation(struct admin_state *state, int result)
201{
202	if (!vdo_get_admin_state_code(state)->operating)
203		return false;
204
205	state->complete = state->starting;
206	if (state->waiter != NULL)
207		vdo_set_completion_result(state->waiter, result);
208
209	if (!state->starting) {
210		vdo_set_admin_state_code(state, state->next_state);
211		if (state->waiter != NULL)
212			vdo_launch_completion(vdo_forget(state->waiter));
213	}
214
215	return true;
216}
217
218/**
219 * begin_operation() - Begin an operation if it may be started given the current state.
220 * @state: The current admin state.
221 * @operation: The operation to be started.
222 * @waiter: A completion to notify when the operation is complete; may be NULL.
223 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
224 *
225 * Return: VDO_SUCCESS or an error.
226 */
227static int __must_check begin_operation(struct admin_state *state,
228					const struct admin_state_code *operation,
229					struct vdo_completion *waiter,
230					vdo_admin_initiator_fn initiator)
231{
232	int result;
233	const struct admin_state_code *next_state = get_next_state(state, operation);
234
235	if (next_state == NULL) {
236		result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
237						"Can't start %s from %s",
238						operation->name,
239						vdo_get_admin_state_code(state)->name);
240	} else if (state->waiter != NULL) {
241		result = vdo_log_error_strerror(VDO_COMPONENT_BUSY,
242						"Can't start %s with extant waiter",
243						operation->name);
244	} else {
245		state->waiter = waiter;
246		state->next_state = next_state;
247		vdo_set_admin_state_code(state, operation);
248		if (initiator != NULL) {
249			state->starting = true;
250			initiator(state);
251			state->starting = false;
252			if (state->complete)
253				vdo_finish_operation(state, VDO_SUCCESS);
254		}
255
256		return VDO_SUCCESS;
257	}
258
259	if (waiter != NULL)
260		vdo_continue_completion(waiter, result);
261
262	return result;
263}
264
265/**
266 * start_operation() - Start an operation if it may be started given the current state.
267 * @state: The current admin state.
268 * @operation: The operation to be started.
269 * @waiter: A completion to notify when the operation is complete; may be NULL.
270 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
271 *
272 * Return: true if the operation was started.
273 */
274static inline bool __must_check start_operation(struct admin_state *state,
275						const struct admin_state_code *operation,
276						struct vdo_completion *waiter,
277						vdo_admin_initiator_fn initiator)
278{
279	return (begin_operation(state, operation, waiter, initiator) == VDO_SUCCESS);
280}
281
282/**
283 * check_code() - Check the result of a state validation.
284 * @valid: True if the code is of an appropriate type.
285 * @code: The code which failed to be of the correct type.
286 * @what: What the code failed to be, for logging.
287 * @waiter: The completion to notify of the error; may be NULL.
288 *
289 * If the result failed, log an invalid state error and, if there is a waiter, notify it.
290 *
291 * Return: The result of the check.
292 */
293static bool check_code(bool valid, const struct admin_state_code *code, const char *what,
294		       struct vdo_completion *waiter)
295{
296	int result;
297
298	if (valid)
299		return true;
300
301	result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
302					"%s is not a %s", code->name, what);
303	if (waiter != NULL)
304		vdo_continue_completion(waiter, result);
305
306	return false;
307}
308
309/**
310 * assert_vdo_drain_operation() - Check that an operation is a drain.
311 * @operation: The operation to check.
312 * @waiter: The completion to finish with an error if the operation is not a drain.
313 *
314 * Return: true if the specified operation is a drain.
315 */
316static bool __must_check assert_vdo_drain_operation(const struct admin_state_code *operation,
317						    struct vdo_completion *waiter)
318{
319	return check_code(operation->draining, operation, "drain operation", waiter);
320}
321
322/**
323 * vdo_start_draining() - Initiate a drain operation if the current state permits it.
324 * @state: The current admin state.
325 * @operation: The type of drain to initiate.
326 * @waiter: The completion to notify when the drain is complete.
327 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
328 *
329 * Return: true if the drain was initiated, if not the waiter will be notified.
330 */
331bool vdo_start_draining(struct admin_state *state,
332			const struct admin_state_code *operation,
333			struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
334{
335	const struct admin_state_code *code = vdo_get_admin_state_code(state);
336
337	if (!assert_vdo_drain_operation(operation, waiter))
338		return false;
339
340	if (code->quiescent) {
341		vdo_launch_completion(waiter);
342		return false;
343	}
344
345	if (!code->normal) {
346		vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE, "can't start %s from %s",
347				       operation->name, code->name);
348		vdo_continue_completion(waiter, VDO_INVALID_ADMIN_STATE);
349		return false;
350	}
351
352	return start_operation(state, operation, waiter, initiator);
353}
354
355/**
356 * vdo_finish_draining() - Finish a drain operation if one was in progress.
357 * @state: The current admin state.
358 *
359 * Return: true if the state was draining; will notify the waiter if so.
360 */
361bool vdo_finish_draining(struct admin_state *state)
362{
363	return vdo_finish_draining_with_result(state, VDO_SUCCESS);
364}
365
366/**
367 * vdo_finish_draining_with_result() - Finish a drain operation with a status code.
368 * @state: The current admin state.
369 * @result: The result of the drain operation.
370 *
371 * Return: true if the state was draining; will notify the waiter if so.
372 */
373bool vdo_finish_draining_with_result(struct admin_state *state, int result)
374{
375	return (vdo_is_state_draining(state) && vdo_finish_operation(state, result));
376}
377
378/**
379 * vdo_assert_load_operation() - Check that an operation is a load.
380 * @operation: The operation to check.
381 * @waiter: The completion to finish with an error if the operation is not a load.
382 *
383 * Return: true if the specified operation is a load.
384 */
385bool vdo_assert_load_operation(const struct admin_state_code *operation,
386			       struct vdo_completion *waiter)
387{
388	return check_code(operation->loading, operation, "load operation", waiter);
389}
390
391/**
392 * vdo_start_loading() - Initiate a load operation if the current state permits it.
393 * @state: The current admin state.
394 * @operation: The type of load to initiate.
395 * @waiter: The completion to notify when the load is complete; may be NULL.
396 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
397 *
398 * Return: true if the load was initiated, if not the waiter will be notified.
399 */
400bool vdo_start_loading(struct admin_state *state,
401		       const struct admin_state_code *operation,
402		       struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
403{
404	return (vdo_assert_load_operation(operation, waiter) &&
405		start_operation(state, operation, waiter, initiator));
406}
407
408/**
409 * vdo_finish_loading() - Finish a load operation if one was in progress.
410 * @state: The current admin state.
411 *
412 * Return: true if the state was loading; will notify the waiter if so.
413 */
414bool vdo_finish_loading(struct admin_state *state)
415{
416	return vdo_finish_loading_with_result(state, VDO_SUCCESS);
417}
418
419/**
420 * vdo_finish_loading_with_result() - Finish a load operation with a status code.
421 * @state: The current admin state.
422 * @result: The result of the load operation.
423 *
424 * Return: true if the state was loading; will notify the waiter if so.
425 */
426bool vdo_finish_loading_with_result(struct admin_state *state, int result)
427{
428	return (vdo_is_state_loading(state) && vdo_finish_operation(state, result));
429}
430
431/**
432 * assert_vdo_resume_operation() - Check whether an admin_state_code is a resume operation.
433 * @operation: The operation to check.
434 * @waiter: The completion to notify if the operation is not a resume operation; may be NULL.
435 *
436 * Return: true if the code is a resume operation.
437 */
438static bool __must_check assert_vdo_resume_operation(const struct admin_state_code *operation,
439						     struct vdo_completion *waiter)
440{
441	return check_code(operation == VDO_ADMIN_STATE_RESUMING, operation,
442			  "resume operation", waiter);
443}
444
445/**
446 * vdo_start_resuming() - Initiate a resume operation if the current state permits it.
447 * @state: The current admin state.
448 * @operation: The type of resume to start.
449 * @waiter: The completion to notify when the resume is complete; may be NULL.
450 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
451 *
452 * Return: true if the resume was initiated, if not the waiter will be notified.
453 */
454bool vdo_start_resuming(struct admin_state *state,
455			const struct admin_state_code *operation,
456			struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
457{
458	return (assert_vdo_resume_operation(operation, waiter) &&
459		start_operation(state, operation, waiter, initiator));
460}
461
462/**
463 * vdo_finish_resuming() - Finish a resume operation if one was in progress.
464 * @state: The current admin state.
465 *
466 * Return: true if the state was resuming; will notify the waiter if so.
467 */
468bool vdo_finish_resuming(struct admin_state *state)
469{
470	return vdo_finish_resuming_with_result(state, VDO_SUCCESS);
471}
472
473/**
474 * vdo_finish_resuming_with_result() - Finish a resume operation with a status code.
475 * @state: The current admin state.
476 * @result: The result of the resume operation.
477 *
478 * Return: true if the state was resuming; will notify the waiter if so.
479 */
480bool vdo_finish_resuming_with_result(struct admin_state *state, int result)
481{
482	return (vdo_is_state_resuming(state) && vdo_finish_operation(state, result));
483}
484
485/**
486 * vdo_resume_if_quiescent() - Change the state to normal operation if the current state is
487 *                             quiescent.
488 * @state: The current admin state.
489 *
490 * Return: VDO_SUCCESS if the state resumed, VDO_INVALID_ADMIN_STATE otherwise.
491 */
492int vdo_resume_if_quiescent(struct admin_state *state)
493{
494	if (!vdo_is_state_quiescent(state))
495		return VDO_INVALID_ADMIN_STATE;
496
497	vdo_set_admin_state_code(state, VDO_ADMIN_STATE_NORMAL_OPERATION);
498	return VDO_SUCCESS;
499}
500
501/**
502 * vdo_start_operation() - Attempt to start an operation.
503 * @state: The current admin state.
504 * @operation: The operation to attempt to start.
505 *
506 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
507 */
508int vdo_start_operation(struct admin_state *state,
509			const struct admin_state_code *operation)
510{
511	return vdo_start_operation_with_waiter(state, operation, NULL, NULL);
512}
513
514/**
515 * vdo_start_operation_with_waiter() - Attempt to start an operation.
516 * @state: The current admin state.
517 * @operation: The operation to attempt to start.
518 * @waiter: The completion to notify when the operation completes or fails to start; may be NULL.
519 * @initiator: The vdo_admin_initiator_fn to call if the operation may begin; may be NULL.
520 *
521 * Return: VDO_SUCCESS if the operation was started, VDO_INVALID_ADMIN_STATE if not
522 */
523int vdo_start_operation_with_waiter(struct admin_state *state,
524				    const struct admin_state_code *operation,
525				    struct vdo_completion *waiter,
526				    vdo_admin_initiator_fn initiator)
527{
528	return (check_code(operation->operating, operation, "operation", waiter) ?
529		begin_operation(state, operation, waiter, initiator) :
530		VDO_INVALID_ADMIN_STATE);
531}
~/ Read-only file preview.