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 15 static const struct admin_state_code VDO_CODE_NORMAL_OPERATION = { 16 .name = "VDO_ADMIN_STATE_NORMAL_OPERATION", 17 .normal = true, 18 }; 19 const struct admin_state_code *VDO_ADMIN_STATE_NORMAL_OPERATION = &VDO_CODE_NORMAL_OPERATION; 20 static const struct admin_state_code VDO_CODE_OPERATING = { 21 .name = "VDO_ADMIN_STATE_OPERATING", 22 .normal = true, 23 .operating = true, 24 }; 25 const struct admin_state_code *VDO_ADMIN_STATE_OPERATING = &VDO_CODE_OPERATING; 26 static const struct admin_state_code VDO_CODE_FORMATTING = { 27 .name = "VDO_ADMIN_STATE_FORMATTING", 28 .operating = true, 29 .loading = true, 30 }; 31 const struct admin_state_code *VDO_ADMIN_STATE_FORMATTING = &VDO_CODE_FORMATTING; 32 static const struct admin_state_code VDO_CODE_PRE_LOADING = { 33 .name = "VDO_ADMIN_STATE_PRE_LOADING", 34 .operating = true, 35 .loading = true, 36 }; 37 const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADING = &VDO_CODE_PRE_LOADING; 38 static const struct admin_state_code VDO_CODE_PRE_LOADED = { 39 .name = "VDO_ADMIN_STATE_PRE_LOADED", 40 }; 41 const struct admin_state_code *VDO_ADMIN_STATE_PRE_LOADED = &VDO_CODE_PRE_LOADED; 42 static const struct admin_state_code VDO_CODE_LOADING = { 43 .name = "VDO_ADMIN_STATE_LOADING", 44 .normal = true, 45 .operating = true, 46 .loading = true, 47 }; 48 const struct admin_state_code *VDO_ADMIN_STATE_LOADING = &VDO_CODE_LOADING; 49 static 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 }; 54 const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_RECOVERY = 55 &VDO_CODE_LOADING_FOR_RECOVERY; 56 static 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 }; 61 const struct admin_state_code *VDO_ADMIN_STATE_LOADING_FOR_REBUILD = &VDO_CODE_LOADING_FOR_REBUILD; 62 static const struct admin_state_code VDO_CODE_WAITING_FOR_RECOVERY = { 63 .name = "VDO_ADMIN_STATE_WAITING_FOR_RECOVERY", 64 .operating = true, 65 }; 66 const struct admin_state_code *VDO_ADMIN_STATE_WAITING_FOR_RECOVERY = 67 &VDO_CODE_WAITING_FOR_RECOVERY; 68 static const struct admin_state_code VDO_CODE_NEW = { 69 .name = "VDO_ADMIN_STATE_NEW", 70 .quiescent = true, 71 }; 72 const struct admin_state_code *VDO_ADMIN_STATE_NEW = &VDO_CODE_NEW; 73 static const struct admin_state_code VDO_CODE_INITIALIZED = { 74 .name = "VDO_ADMIN_STATE_INITIALIZED", 75 }; 76 const struct admin_state_code *VDO_ADMIN_STATE_INITIALIZED = &VDO_CODE_INITIALIZED; 77 static const struct admin_state_code VDO_CODE_RECOVERING = { 78 .name = "VDO_ADMIN_STATE_RECOVERING", 79 .draining = true, 80 .operating = true, 81 }; 82 const struct admin_state_code *VDO_ADMIN_STATE_RECOVERING = &VDO_CODE_RECOVERING; 83 static const struct admin_state_code VDO_CODE_REBUILDING = { 84 .name = "VDO_ADMIN_STATE_REBUILDING", 85 .draining = true, 86 .operating = true, 87 }; 88 const struct admin_state_code *VDO_ADMIN_STATE_REBUILDING = &VDO_CODE_REBUILDING; 89 static const struct admin_state_code VDO_CODE_SAVING = { 90 .name = "VDO_ADMIN_STATE_SAVING", 91 .draining = true, 92 .quiescing = true, 93 .operating = true, 94 }; 95 const struct admin_state_code *VDO_ADMIN_STATE_SAVING = &VDO_CODE_SAVING; 96 static const struct admin_state_code VDO_CODE_SAVED = { 97 .name = "VDO_ADMIN_STATE_SAVED", 98 .quiescent = true, 99 }; 100 const struct admin_state_code *VDO_ADMIN_STATE_SAVED = &VDO_CODE_SAVED; 101 static const struct admin_state_code VDO_CODE_SCRUBBING = { 102 .name = "VDO_ADMIN_STATE_SCRUBBING", 103 .draining = true, 104 .loading = true, 105 .operating = true, 106 }; 107 const struct admin_state_code *VDO_ADMIN_STATE_SCRUBBING = &VDO_CODE_SCRUBBING; 108 static 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 }; 113 const struct admin_state_code *VDO_ADMIN_STATE_SAVE_FOR_SCRUBBING = &VDO_CODE_SAVE_FOR_SCRUBBING; 114 static const struct admin_state_code VDO_CODE_STOPPING = { 115 .name = "VDO_ADMIN_STATE_STOPPING", 116 .draining = true, 117 .quiescing = true, 118 .operating = true, 119 }; 120 const struct admin_state_code *VDO_ADMIN_STATE_STOPPING = &VDO_CODE_STOPPING; 121 static const struct admin_state_code VDO_CODE_STOPPED = { 122 .name = "VDO_ADMIN_STATE_STOPPED", 123 .quiescent = true, 124 }; 125 const struct admin_state_code *VDO_ADMIN_STATE_STOPPED = &VDO_CODE_STOPPED; 126 static const struct admin_state_code VDO_CODE_SUSPENDING = { 127 .name = "VDO_ADMIN_STATE_SUSPENDING", 128 .draining = true, 129 .quiescing = true, 130 .operating = true, 131 }; 132 const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDING = &VDO_CODE_SUSPENDING; 133 static const struct admin_state_code VDO_CODE_SUSPENDED = { 134 .name = "VDO_ADMIN_STATE_SUSPENDED", 135 .quiescent = true, 136 }; 137 const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED = &VDO_CODE_SUSPENDED; 138 static const struct admin_state_code VDO_CODE_SUSPENDED_OPERATION = { 139 .name = "VDO_ADMIN_STATE_SUSPENDED_OPERATION", 140 .operating = true, 141 }; 142 const struct admin_state_code *VDO_ADMIN_STATE_SUSPENDED_OPERATION = &VDO_CODE_SUSPENDED_OPERATION; 143 static const struct admin_state_code VDO_CODE_RESUMING = { 144 .name = "VDO_ADMIN_STATE_RESUMING", 145 .operating = true, 146 }; 147 const 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 */ 157 static 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 */ 197 bool 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(uds_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 */ 222 static 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 = uds_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 = uds_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 */ 267 static 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 */ 286 static 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 = uds_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 */ 308 static 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 */ 322 bool 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 uds_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 */ 351 bool 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 */ 361 bool 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 */ 372 bool 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 */ 386 bool 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 */ 399 bool 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 */ 410 bool 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 */ 421 static 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 */ 436 bool 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 */ 449 bool 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 */ 460 bool 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 */ 471 int 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 */ 485 int 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 */ 498 int 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