2
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
5
- //! A task to handle requests to change a VM's state or configuration.
5
+ //! Structures and tasks that handle VM state and configuration change requests.
6
+ //!
7
+ //! This module handles the second high-level phase of a VM's lifecycle: once a
8
+ //! VM's components and services exist, it enters an event loop that changes the
9
+ //! VM's state in response to external API requests and signals arriving from
10
+ //! within the VM. See the [`ensure`] module for more information about the
11
+ //! initialization phase.
12
+ //!
13
+ //! This module's main struct is the [`StateDriver`], which holds references to
14
+ //! an active VM's components, the VM's event queues, and the sender side of a
15
+ //! channel that publishes instance state updates. External API requests are
16
+ //! routed to the driver's event queue and handled by the driver task. This
17
+ //! model ensures that only one task handles VM events and updates VM state; the
18
+ //! idea is to minimize the number of different tasks and threads one has to
19
+ //! consider when reasoning about concurrency in the VM state machine.
20
+ //!
21
+ //! On a migration in, the state driver implicitly starts the VM before entering
22
+ //! the main event loop:
23
+ //!
24
+ //! ```text
25
+ //! +-----------------------+
26
+ //! | VM components created |
27
+ //! +-----------+-----------+
28
+ //! |
29
+ //! |
30
+ //! Yes +--------------v--------------+ No
31
+ //! +-+ Initialized via migration? +-+
32
+ //! | +-----------------------------+ |
33
+ //! | |
34
+ //! +------v--------+ |
35
+ //! | Auto-start VM | |
36
+ //! +------+--------+ |
37
+ //! | |
38
+ //! +------------v------------+ |
39
+ //! | Start devices and vCPUs | |
40
+ //! +------------+------------+ |
41
+ //! | |
42
+ //! | |
43
+ //! | +-----------------------+ |
44
+ //! +----> Enter main event loop <----+
45
+ //! +-----------------------+
46
+ //! ```
47
+ //!
48
+ //! Once in the main event loop, a VM generally remains active until it receives
49
+ //! a signal telling it to do something else:
50
+ //!
51
+ //! ```text
52
+ //! +-----------------+ +-----------------+ error during startup
53
+ //! | Not yet started | | Not yet started | +--------+
54
+ //! | (migrating in) | | (Creating) +-------> Failed |
55
+ //! +-------+---------+ +--------+--------+ +--------+
56
+ //! | |
57
+ //! | | Successful start request
58
+ //! +-----------+ |
59
+ //! +v----------v-----------+ API/chipset request
60
+ //! +---------+ Running +------+
61
+ //! | +---^-------+--------^--+ +--v--------+
62
+ //! | | | +------+ Rebooting |
63
+ //! | | | +-----------+
64
+ //! +--------v------+ | |
65
+ //! | Migrating out +------+ | API/chipset request
66
+ //! +--------+------+ |
67
+ //! | +----v-----+
68
+ //! | | Stopping |
69
+ //! | +----+-----+
70
+ //! | |
71
+ //! | | +-----------------+
72
+ //! | +----v-----+ | Destroyed |
73
+ //! +----------------> Stopped +------> (after rundown) |
74
+ //! +----------+ +-----------------+
75
+ //! ```
76
+ //!
77
+ //! The state driver's [`InputQueue`] receives events that can push a running VM
78
+ //! out of its steady "running" state. These can come either from the external
79
+ //! API or from events happening in the guest (e.g. a vCPU asserting a pin on
80
+ //! the virtual chipset that should reset or halt the VM). The policy that
81
+ //! determines what API requests can be accepted in which states is implemented
82
+ //! in the [`request_queue`] module.
83
+ //!
84
+ //! The "stopped" and "failed" states are terminal states. When the state driver
85
+ //! reaches one of these states, it exits the event loop, returning its final
86
+ //! state to the wrapper function that launched the driver. The wrapper task is
87
+ //! responsible for running down the VM objects and structures and resetting the
88
+ //! server so that it can start another VM.
89
+ //!
90
+ //! [`ensure`]: crate::vm::ensure
6
91
7
92
use std:: {
8
93
sync:: { Arc , Mutex } ,
@@ -248,7 +333,10 @@ struct StateDriver {
248
333
migration_src_state : crate :: migrate:: source:: PersistentState ,
249
334
}
250
335
251
- /// The values returned by a state driver task when it exits.
336
+ /// Contains a state driver's terminal state and the channel it used to publish
337
+ /// state updates to the rest of the server. The driver's owner can use these to
338
+ /// publish the VM's terminal state after running down all of its objects and
339
+ /// services.
252
340
pub ( super ) struct StateDriverOutput {
253
341
/// The channel this driver used to publish external instance state changes.
254
342
pub state_publisher : StatePublisher ,
@@ -258,9 +346,14 @@ pub(super) struct StateDriverOutput {
258
346
pub final_state : InstanceState ,
259
347
}
260
348
261
- /// Creates a new set of VM objects in response to an `ensure_request` directed
262
- /// to the supplied `vm`.
263
- pub ( super ) async fn run_state_driver (
349
+ /// Given an instance ensure request, processes the request and hands the
350
+ /// resulting activated VM off to a [`StateDriver`] that will drive the main VM
351
+ /// event loop.
352
+ ///
353
+ /// Returns the final state driver disposition. Note that this routine does not
354
+ /// return a `Result`; if the VM fails to start, the returned
355
+ /// [`StateDriverOutput`] contains appropriate state for a failed VM.
356
+ pub ( super ) async fn ensure_vm_and_launch_driver (
264
357
log : slog:: Logger ,
265
358
vm : Arc < super :: Vm > ,
266
359
mut state_publisher : StatePublisher ,
@@ -269,7 +362,7 @@ pub(super) async fn run_state_driver(
269
362
ensure_options : super :: EnsureOptions ,
270
363
) -> StateDriverOutput {
271
364
let ensure_options = Arc :: new ( ensure_options) ;
272
- let activated_vm = match create_and_activate_vm (
365
+ let activated_vm = match ensure_active_vm (
273
366
& log,
274
367
& vm,
275
368
& mut state_publisher,
@@ -318,7 +411,7 @@ pub(super) async fn run_state_driver(
318
411
319
412
/// Processes the supplied `ensure_request` to create a set of VM objects that
320
413
/// can be moved into a new `StateDriver`.
321
- async fn create_and_activate_vm < ' a > (
414
+ async fn ensure_active_vm < ' a > (
322
415
log : & ' a slog:: Logger ,
323
416
vm : & ' a Arc < super :: Vm > ,
324
417
state_publisher : & ' a mut StatePublisher ,
@@ -371,23 +464,27 @@ async fn create_and_activate_vm<'a>(
371
464
}
372
465
373
466
impl StateDriver {
467
+ /// Directs this state driver to enter its main event loop. The driver may
468
+ /// perform additional tasks (e.g. automatically starting a migration
469
+ /// target) before it begins processing events from its queues.
374
470
pub ( super ) async fn run ( mut self , migrated_in : bool ) -> StateDriverOutput {
375
471
info ! ( self . log, "state driver launched" ) ;
376
472
377
473
let final_state = if migrated_in {
378
474
if self . start_vm ( VmStartReason :: MigratedIn ) . await . is_ok ( ) {
379
- self . run_loop ( ) . await
475
+ self . event_loop ( ) . await
380
476
} else {
381
477
InstanceState :: Failed
382
478
}
383
479
} else {
384
- self . run_loop ( ) . await
480
+ self . event_loop ( ) . await
385
481
} ;
386
482
387
483
StateDriverOutput { state_publisher : self . external_state , final_state }
388
484
}
389
485
390
- async fn run_loop ( & mut self ) -> InstanceState {
486
+ /// Runs the state driver's main event loop.
487
+ async fn event_loop ( & mut self ) -> InstanceState {
391
488
info ! ( self . log, "state driver entered main loop" ) ;
392
489
loop {
393
490
let event = self . input_queue . wait_for_next_event ( ) . await ;
@@ -415,6 +512,8 @@ impl StateDriver {
415
512
}
416
513
}
417
514
515
+ /// Starts the driver's VM by sending start commands to its devices and
516
+ /// vCPUs.
418
517
async fn start_vm (
419
518
& mut self ,
420
519
start_reason : VmStartReason ,
0 commit comments