diff mbox series

[RFC,1/9] vl: Allow finer control in advancing machine through phases

Message ID 20210513082549.114275-2-mirela.grujic@greensocs.com
State New
Headers show
Series Initial support for machine creation via QMP | expand

Commit Message

Mirela Grujic May 13, 2021, 8:25 a.m. UTC
In addition to the existing preconfig approach, which allows configuring
the machine via QMP before the machine is initialized, we need the ability
to configure the machine right after it's initialized. This patch will
enable doing that in a scalable fashion as follows.

The basic idea is to group machine initialization steps into chunks
that need to be executed to advance machine initialization from one
phase to the next one. Then, we add a finer control to allow executing
only a chunk, i.e. to advance machine initialization to the next phase.
Between the phases, we would configure the machine properties via QMP,
add devices, etc. (depends on the phase). Advancing the machine to the
next initialization phase would also be controllable via a QMP command.

For example, the machine configuration could look like this:
1) Run QEMU with existing -preconfig command line option (this will
   cause QEMU to wait before the machine is initialized)
Then, using QMP commands from a client:
2) Perform configuration that needs to be done before the machine
   is initialized
3) Advance machine initialization phase to the next (the
   'next-machine-phase' command to be added)
4) Perform configuration that needs to be done after the machine
   is initialized
5) 'next-machine-phase' (same as in 3), etc.

Available QMP commands will be specific to each phase, but the command
'next-machine-phase' will be common. In the implementation of the
'next-machine-phase' command, we will call qemu_machine_enter_phase
function introduced in this patch.

Function qemu_machine_enter_phase introduced here allows advancing
the machine to the target phase. For the single-stepping configuration
described above, the target phase would be the current machine
phase + 1 (assuming some sanity checks on top).
When this function is called for the PHASE_MACHINE_READY target phase
(the final phase) it implements the same functionality as
qmp_x_exit_preconfig before this patch. In other words, the body of
this function implements the same initialization steps as before, but
just groups them into chunks (if branches) and allows stepping through
machine init phases.

For now, the relevant target machine phases are only
PHASE_MACHINE_INITIALIZED and PHASE_MACHINE_READY, but the approach
allows to easily add more or split existing phases if needed.

Signed-off-by: Mirela Grujic <mirela.grujic@greensocs.com>
---
 include/hw/qdev-core.h |  1 +
 hw/core/qdev.c         |  5 +++
 softmmu/vl.c           | 87 ++++++++++++++++++++++++++++--------------
 3 files changed, 64 insertions(+), 29 deletions(-)
diff mbox series

Patch

diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index bafc311bfa..6e52240d92 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -841,5 +841,6 @@  typedef enum MachineInitPhase {
 
 extern bool phase_check(MachineInitPhase phase);
 extern void phase_advance(MachineInitPhase phase);
+extern MachineInitPhase phase_get(void);
 
 #endif
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index cefc5eaa0a..4a4a4d8c52 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -1150,6 +1150,11 @@  void phase_advance(MachineInitPhase phase)
     machine_phase = phase;
 }
 
+MachineInitPhase phase_get(void)
+{
+    return machine_phase;
+}
+
 static const TypeInfo device_type_info = {
     .name = TYPE_DEVICE,
     .parent = TYPE_OBJECT,
diff --git a/softmmu/vl.c b/softmmu/vl.c
index aadb526138..cbf62abeb4 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -2578,6 +2578,62 @@  static void qemu_machine_creation_done(void)
     }
 }
 
+static void qemu_machine_enter_phase(MachineInitPhase target_phase,
+                                     Error **errp)
+{
+    /* target phases before initialization are not handled here */
+    if (target_phase < PHASE_MACHINE_INITIALIZED) {
+        error_setg(errp, "Target machine phase too early to enter this way");
+        return;
+    }
+
+    /* check if machine has already passed through the target phase */
+    if (phase_check(target_phase)) {
+        error_setg(errp, "Target machine phase already entered");
+        return;
+    }
+
+    /*
+     * if machine has not yet passed 'initialized' phase and according to the
+     * target_phase it should
+     */
+    if (target_phase >= PHASE_MACHINE_INITIALIZED &&
+        phase_get() < PHASE_MACHINE_INITIALIZED) {
+        qemu_init_board();
+        qemu_create_cli_devices();
+    }
+
+    if (target_phase >= PHASE_MACHINE_READY &&
+        phase_get() < PHASE_MACHINE_READY) {
+        qemu_machine_creation_done();
+
+        if (loadvm) {
+            Error *local_err = NULL;
+            if (!load_snapshot(loadvm, NULL, false, NULL, &local_err)) {
+                error_report_err(local_err);
+                autostart = 0;
+                exit(1);
+            }
+        }
+        if (replay_mode != REPLAY_MODE_NONE) {
+            replay_vmstate_init();
+        }
+
+        if (incoming) {
+            Error *local_err = NULL;
+            if (strcmp(incoming, "defer") != 0) {
+                qmp_migrate_incoming(incoming, &local_err);
+                if (local_err) {
+                    error_reportf_err(local_err, "-incoming %s: ", incoming);
+                    exit(1);
+                }
+            }
+        } else if (autostart) {
+            qmp_cont(NULL);
+        }
+    }
+}
+
 void qmp_x_exit_preconfig(Error **errp)
 {
     if (phase_check(PHASE_MACHINE_INITIALIZED)) {
@@ -2585,34 +2641,7 @@  void qmp_x_exit_preconfig(Error **errp)
         return;
     }
 
-    qemu_init_board();
-    qemu_create_cli_devices();
-    qemu_machine_creation_done();
-
-    if (loadvm) {
-        Error *local_err = NULL;
-        if (!load_snapshot(loadvm, NULL, false, NULL, &local_err)) {
-            error_report_err(local_err);
-            autostart = 0;
-            exit(1);
-        }
-    }
-    if (replay_mode != REPLAY_MODE_NONE) {
-        replay_vmstate_init();
-    }
-
-    if (incoming) {
-        Error *local_err = NULL;
-        if (strcmp(incoming, "defer") != 0) {
-            qmp_migrate_incoming(incoming, &local_err);
-            if (local_err) {
-                error_reportf_err(local_err, "-incoming %s: ", incoming);
-                exit(1);
-            }
-        }
-    } else if (autostart) {
-        qmp_cont(NULL);
-    }
+    qemu_machine_enter_phase(PHASE_MACHINE_READY, errp);
 }
 
 void qemu_init(int argc, char **argv, char **envp)
@@ -3608,7 +3637,7 @@  void qemu_init(int argc, char **argv, char **envp)
     }
 
     if (!preconfig_requested) {
-        qmp_x_exit_preconfig(&error_fatal);
+        qemu_machine_enter_phase(PHASE_MACHINE_READY, &error_fatal);
     }
     qemu_init_displays();
     accel_setup_post(current_machine);