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