brintos

brintos / linux-shallow public Read only

0
0
Text · 16.9 KiB · 3f9dba5 Raw
507 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2023 Red Hat4 */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 completes151 *                    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 be155 *         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_OPERATION170			? VDO_ADMIN_STATE_SUSPENDED171			: 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 operations192 * 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 is467 *                             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 not484 */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 not497 */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