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   */
get_next_state(const struct admin_state * state,const struct admin_state_code * operation)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   */
vdo_finish_operation(struct admin_state * state,int result)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(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   */
begin_operation(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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 = 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   */
start_operation(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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   */
check_code(bool valid,const struct admin_state_code * code,const char * what,struct vdo_completion * waiter)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 = 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   */
assert_vdo_drain_operation(const struct admin_state_code * operation,struct vdo_completion * waiter)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   */
vdo_start_draining(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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  		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   */
vdo_finish_draining(struct admin_state * state)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   */
vdo_finish_draining_with_result(struct admin_state * state,int result)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   */
vdo_assert_load_operation(const struct admin_state_code * operation,struct vdo_completion * waiter)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   */
vdo_start_loading(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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   */
vdo_finish_loading(struct admin_state * state)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   */
vdo_finish_loading_with_result(struct admin_state * state,int result)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   */
assert_vdo_resume_operation(const struct admin_state_code * operation,struct vdo_completion * waiter)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   */
vdo_start_resuming(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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   */
vdo_finish_resuming(struct admin_state * state)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   */
vdo_finish_resuming_with_result(struct admin_state * state,int result)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   */
vdo_resume_if_quiescent(struct admin_state * state)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   */
vdo_start_operation(struct admin_state * state,const struct admin_state_code * operation)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   */
vdo_start_operation_with_waiter(struct admin_state * state,const struct admin_state_code * operation,struct vdo_completion * waiter,vdo_admin_initiator_fn initiator)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