// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Red Hat
*/
#include "admin-state.h"
#include "logger.h"
#include "memory-alloc.h"
#include "permassert.h"
#include "completion.h"
#include "types.h"
static const struct admin_state_code VDO_CODE_NORMAL_OPERATION = {
.name = "VDO_ADMIN_STATE_NORMAL_OPERATION" ,
.normal = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_NORMAL_OPERATION = &VDO_CODE_NORMAL_OPERATION;
static const struct admin_state_code VDO_CODE_OPERATING = {
.name = "VDO_ADMIN_STATE_OPERATING" ,
.normal = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_OPERATING = &VDO_CODE_OPERATING;
static const struct admin_state_code VDO_CODE_FORMATTING = {
.name = "VDO_ADMIN_STATE_FORMATTING" ,
.operating = true ,
.loading = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_FORMATTING = &VDO_CODE_FORMATTING;
static const struct admin_state_code VDO_CODE_PRE_LOADING = {
.name = "VDO_ADMIN_STATE_PRE_LOADING" ,
.operating = true ,
.loading = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADING = &VDO_CODE_PRE_LOADING;
static const struct admin_state_code VDO_CODE_PRE_LOADED = {
.name = "VDO_ADMIN_STATE_PRE_LOADED" ,
};
const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADED = &VDO_CODE_PRE_LOADED;
static const struct admin_state_code VDO_CODE_LOADING = {
.name = "VDO_ADMIN_STATE_LOADING" ,
.normal = true ,
.operating = true ,
.loading = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_LOADING = &VDO_CODE_LOADING;
static const struct admin_state_code VDO_CODE_LOADING_FOR_RECOVERY = {
.name = "VDO_ADMIN_STATE_LOADING_FOR_RECOVERY" ,
.operating = true ,
.loading = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_RECOVERY =
&VDO_CODE_LOADING_FOR_RECOVERY;
static const struct admin_state_code VDO_CODE_LOADING_FOR_REBUILD = {
.name = "VDO_ADMIN_STATE_LOADING_FOR_REBUILD" ,
.operating = true ,
.loading = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_REBUILD = &VDO_CODE_LOADING_FOR_REBUILD;
static const struct admin_state_code VDO_CODE_WAITING_FOR_RECOVERY = {
.name = "VDO_ADMIN_STATE_WAITING_FOR_RECOVERY" ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_WAITING_FOR_RECOVERY =
&VDO_CODE_WAITING_FOR_RECOVERY;
static const struct admin_state_code VDO_CODE_NEW = {
.name = "VDO_ADMIN_STATE_NEW" ,
.quiescent = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_NEW = &VDO_CODE_NEW;
static const struct admin_state_code VDO_CODE_INITIALIZED = {
.name = "VDO_ADMIN_STATE_INITIALIZED" ,
};
const struct admin_state_code *VDO_ADMIN_STATE_INITIALIZED = &VDO_CODE_INITIALIZED;
static const struct admin_state_code VDO_CODE_RECOVERING = {
.name = "VDO_ADMIN_STATE_RECOVERING" ,
.draining = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_RECOVERING = &VDO_CODE_RECOVERING;
static const struct admin_state_code VDO_CODE_REBUILDING = {
.name = "VDO_ADMIN_STATE_REBUILDING" ,
.draining = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_REBUILDING = &VDO_CODE_REBUILDING;
static const struct admin_state_code VDO_CODE_SAVING = {
.name = "VDO_ADMIN_STATE_SAVING" ,
.draining = true ,
.quiescing = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SAVING = &VDO_CODE_SAVING;
static const struct admin_state_code VDO_CODE_SAVED = {
.name = "VDO_ADMIN_STATE_SAVED" ,
.quiescent = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SAVED = &VDO_CODE_SAVED;
static const struct admin_state_code VDO_CODE_SCRUBBING = {
.name = "VDO_ADMIN_STATE_SCRUBBING" ,
.draining = true ,
.loading = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SCRUBBING = &VDO_CODE_SCRUBBING;
static const struct admin_state_code VDO_CODE_SAVE_FOR_SCRUBBING = {
.name = "VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING" ,
.draining = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING = &VDO_CODE_SAVE_FOR_SCRUBBING;
static const struct admin_state_code VDO_CODE_STOPPING = {
.name = "VDO_ADMIN_STATE_STOPPING" ,
.draining = true ,
.quiescing = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_STOPPING = &VDO_CODE_STOPPING;
static const struct admin_state_code VDO_CODE_STOPPED = {
.name = "VDO_ADMIN_STATE_STOPPED" ,
.quiescent = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_STOPPED = &VDO_CODE_STOPPED;
static const struct admin_state_code VDO_CODE_SUSPENDING = {
.name = "VDO_ADMIN_STATE_SUSPENDING" ,
.draining = true ,
.quiescing = true ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDING = &VDO_CODE_SUSPENDING;
static const struct admin_state_code VDO_CODE_SUSPENDED = {
.name = "VDO_ADMIN_STATE_SUSPENDED" ,
.quiescent = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED = &VDO_CODE_SUSPENDED;
static const struct admin_state_code VDO_CODE_SUSPENDED_OPERATION = {
.name = "VDO_ADMIN_STATE_SUSPENDED_OPERATION" ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED_OPERATION = &VDO_CODE_SUSPENDED_OPERATION;
static const struct admin_state_code VDO_CODE_RESUMING = {
.name = "VDO_ADMIN_STATE_RESUMING" ,
.operating = true ,
};
const struct admin_state_code *VDO_ADMIN_STATE_RESUMING = &VDO_CODE_RESUMING;
/**
* get_next_state ( ) - Determine the state which should be set after a given operation completes
* based on the operation and the current state .
* @ operation The operation to be started .
*
* Return : The state to set when the operation completes or NULL if the operation can not be
* started in the current state .
*/
static const struct admin_state_code *get_next_state(const struct admin_state *state,
const struct admin_state_code *operation)
{
const struct admin_state_code *code = vdo_get_admin_state_code(state);
if (code->operating)
return NULL;
if (operation == VDO_ADMIN_STATE_SAVING)
return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_SAVED : NULL);
if (operation == VDO_ADMIN_STATE_SUSPENDING) {
return (code == VDO_ADMIN_STATE_NORMAL_OPERATION
? VDO_ADMIN_STATE_SUSPENDED
: NULL);
}
if (operation == VDO_ADMIN_STATE_STOPPING)
return (code == VDO_ADMIN_STATE_NORMAL_OPERATION ? VDO_ADMIN_STATE_STOPPED : NULL);
if (operation == VDO_ADMIN_STATE_PRE_LOADING)
return (code == VDO_ADMIN_STATE_INITIALIZED ? VDO_ADMIN_STATE_PRE_LOADED : NULL);
if (operation == VDO_ADMIN_STATE_SUSPENDED_OPERATION) {
return (((code == VDO_ADMIN_STATE_SUSPENDED) ||
(code == VDO_ADMIN_STATE_SAVED)) ? code : NULL);
}
return VDO_ADMIN_STATE_NORMAL_OPERATION;
}
/**
* vdo_finish_operation ( ) - Finish the current operation .
*
* Will notify the operation waiter if there is one . This method should be used for operations
* started with vdo_start_operation ( ) . For operations which were started with vdo_start_draining ( ) ,
* use vdo_finish_draining ( ) instead .
*
* Return : true if there was an operation to finish .
*/
bool vdo_finish_operation(struct admin_state *state, int result)
{
if (!vdo_get_admin_state_code(state)->operating)
return false ;
state->complete = state->starting;
if (state->waiter != NULL)
vdo_set_completion_result(state->waiter, result);
if (!state->starting) {
vdo_set_admin_state_code(state, state->next_state);
if (state->waiter != NULL)
vdo_launch_completion(vdo_forget(state->waiter));
}
return true ;
}
/**
* begin_operation ( ) - Begin an operation if it may be started given the current state .
* @ waiter A completion to notify when the operation is complete ; may be NULL .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : VDO_SUCCESS or an error .
*/
static int __must_check begin_operation(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter,
vdo_admin_initiator_fn initiator)
{
int result;
const struct admin_state_code *next_state = get_next_state(state, operation);
if (next_state == NULL) {
result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
"Can't start %s from %s" ,
operation->name,
vdo_get_admin_state_code(state)->name);
} else if (state->waiter != NULL) {
result = vdo_log_error_strerror(VDO_COMPONENT_BUSY,
"Can't start %s with extant waiter" ,
operation->name);
} else {
state->waiter = waiter;
state->next_state = next_state;
vdo_set_admin_state_code(state, operation);
if (initiator != NULL) {
state->starting = true ;
initiator(state);
state->starting = false ;
if (state->complete)
vdo_finish_operation(state, VDO_SUCCESS);
}
return VDO_SUCCESS;
}
if (waiter != NULL)
vdo_continue_completion(waiter, result);
return result;
}
/**
* start_operation ( ) - Start an operation if it may be started given the current state .
* @ waiter A completion to notify when the operation is complete .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : true if the operation was started .
*/
static inline bool __must_check start_operation(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter,
vdo_admin_initiator_fn initiator)
{
return (begin_operation(state, operation, waiter, initiator) == VDO_SUCCESS);
}
/**
* check_code ( ) - Check the result of a state validation .
* @ valid true if the code is of an appropriate type .
* @ code The code which failed to be of the correct type .
* @ what What the code failed to be , for logging .
* @ waiter The completion to notify of the error ; may be NULL .
*
* If the result failed , log an invalid state error and , if there is a waiter , notify it .
*
* Return : The result of the check .
*/
static bool check_code(bool valid, const struct admin_state_code *code, const char *what,
struct vdo_completion *waiter)
{
int result;
if (valid)
return true ;
result = vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE,
"%s is not a %s" , code->name, what);
if (waiter != NULL)
vdo_continue_completion(waiter, result);
return false ;
}
/**
* assert_vdo_drain_operation ( ) - Check that an operation is a drain .
* @ waiter The completion to finish with an error if the operation is not a drain .
*
* Return : true if the specified operation is a drain .
*/
static bool __must_check assert_vdo_drain_operation(const struct admin_state_code *operation,
struct vdo_completion *waiter)
{
return check_code(operation->draining, operation, "drain operation" , waiter);
}
/**
* vdo_start_draining ( ) - Initiate a drain operation if the current state permits it .
* @ operation The type of drain to initiate .
* @ waiter The completion to notify when the drain is complete .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : true if the drain was initiated , if not the waiter will be notified .
*/
bool vdo_start_draining(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
{
const struct admin_state_code *code = vdo_get_admin_state_code(state);
if (!assert_vdo_drain_operation(operation, waiter))
return false ;
if (code->quiescent) {
vdo_launch_completion(waiter);
return false ;
}
if (!code->normal) {
vdo_log_error_strerror(VDO_INVALID_ADMIN_STATE, "can't start %s from %s" ,
operation->name, code->name);
vdo_continue_completion(waiter, VDO_INVALID_ADMIN_STATE);
return false ;
}
return start_operation(state, operation, waiter, initiator);
}
/**
* vdo_finish_draining ( ) - Finish a drain operation if one was in progress .
*
* Return : true if the state was draining ; will notify the waiter if so .
*/
bool vdo_finish_draining(struct admin_state *state)
{
return vdo_finish_draining_with_result(state, VDO_SUCCESS);
}
/**
* vdo_finish_draining_with_result ( ) - Finish a drain operation with a status code .
*
* Return : true if the state was draining ; will notify the waiter if so .
*/
bool vdo_finish_draining_with_result(struct admin_state *state, int result)
{
return (vdo_is_state_draining(state) && vdo_finish_operation(state, result));
}
/**
* vdo_assert_load_operation ( ) - Check that an operation is a load .
* @ waiter The completion to finish with an error if the operation is not a load .
*
* Return : true if the specified operation is a load .
*/
bool vdo_assert_load_operation(const struct admin_state_code *operation,
struct vdo_completion *waiter)
{
return check_code(operation->loading, operation, "load operation" , waiter);
}
/**
* vdo_start_loading ( ) - Initiate a load operation if the current state permits it .
* @ operation The type of load to initiate .
* @ waiter The completion to notify when the load is complete ( may be NULL ) .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : true if the load was initiated , if not the waiter will be notified .
*/
bool vdo_start_loading(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
{
return (vdo_assert_load_operation(operation, waiter) &&
start_operation(state, operation, waiter, initiator));
}
/**
* vdo_finish_loading ( ) - Finish a load operation if one was in progress .
*
* Return : true if the state was loading ; will notify the waiter if so .
*/
bool vdo_finish_loading(struct admin_state *state)
{
return vdo_finish_loading_with_result(state, VDO_SUCCESS);
}
/**
* vdo_finish_loading_with_result ( ) - Finish a load operation with a status code .
* @ result The result of the load operation .
*
* Return : true if the state was loading ; will notify the waiter if so .
*/
bool vdo_finish_loading_with_result(struct admin_state *state, int result)
{
return (vdo_is_state_loading(state) && vdo_finish_operation(state, result));
}
/**
* assert_vdo_resume_operation ( ) - Check whether an admin_state_code is a resume operation .
* @ waiter The completion to notify if the operation is not a resume operation ; may be NULL .
*
* Return : true if the code is a resume operation .
*/
static bool __must_check assert_vdo_resume_operation(const struct admin_state_code *operation,
struct vdo_completion *waiter)
{
return check_code(operation == VDO_ADMIN_STATE_RESUMING, operation,
"resume operation" , waiter);
}
/**
* vdo_start_resuming ( ) - Initiate a resume operation if the current state permits it .
* @ operation The type of resume to start .
* @ waiter The completion to notify when the resume is complete ( may be NULL ) .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : true if the resume was initiated , if not the waiter will be notified .
*/
bool vdo_start_resuming(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter, vdo_admin_initiator_fn initiator)
{
return (assert_vdo_resume_operation(operation, waiter) &&
start_operation(state, operation, waiter, initiator));
}
/**
* vdo_finish_resuming ( ) - Finish a resume operation if one was in progress .
*
* Return : true if the state was resuming ; will notify the waiter if so .
*/
bool vdo_finish_resuming(struct admin_state *state)
{
return vdo_finish_resuming_with_result(state, VDO_SUCCESS);
}
/**
* vdo_finish_resuming_with_result ( ) - Finish a resume operation with a status code .
* @ result The result of the resume operation .
*
* Return : true if the state was resuming ; will notify the waiter if so .
*/
bool vdo_finish_resuming_with_result(struct admin_state *state, int result)
{
return (vdo_is_state_resuming(state) && vdo_finish_operation(state, result));
}
/**
* vdo_resume_if_quiescent ( ) - Change the state to normal operation if the current state is
* quiescent .
*
* Return : VDO_SUCCESS if the state resumed , VDO_INVALID_ADMIN_STATE otherwise .
*/
int vdo_resume_if_quiescent(struct admin_state *state)
{
if (!vdo_is_state_quiescent(state))
return VDO_INVALID_ADMIN_STATE;
vdo_set_admin_state_code(state, VDO_ADMIN_STATE_NORMAL_OPERATION);
return VDO_SUCCESS;
}
/**
* vdo_start_operation ( ) - Attempt to start an operation .
*
* Return : VDO_SUCCESS if the operation was started , VDO_INVALID_ADMIN_STATE if not
*/
int vdo_start_operation(struct admin_state *state,
const struct admin_state_code *operation)
{
return vdo_start_operation_with_waiter(state, operation, NULL, NULL);
}
/**
* vdo_start_operation_with_waiter ( ) - Attempt to start an operation .
* @ waiter the completion to notify when the operation completes or fails to start ; may be NULL .
* @ initiator The vdo_admin_initiator_fn to call if the operation may begin ; may be NULL .
*
* Return : VDO_SUCCESS if the operation was started , VDO_INVALID_ADMIN_STATE if not
*/
int vdo_start_operation_with_waiter(struct admin_state *state,
const struct admin_state_code *operation,
struct vdo_completion *waiter,
vdo_admin_initiator_fn initiator)
{
return (check_code(operation->operating, operation, "operation" , waiter) ?
begin_operation(state, operation, waiter, initiator) :
VDO_INVALID_ADMIN_STATE);
}
Messung V0.5 in Prozent C=96 H=100 G=97
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland