We need to move the plugin vcpu_init callback to the reset phase so
all register information is available to plugins. However so as not to
complicate the initialisation code we still want the call once to do
our housekeeping.

Signed-off-by: Alex Bennée <[email protected]>
---
 include/qemu/plugin.h      | 19 ++++++++++++++-----
 include/qemu/qemu-plugin.h |  4 +++-
 hw/core/cpu-common.c       | 21 +++++++++++++++------
 plugins/core.c             | 31 +++++++++++++++++++++++--------
 4 files changed, 55 insertions(+), 20 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 91df1e78d2c..6d38b923c73 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -150,14 +150,23 @@ struct CPUPluginState {
 };
 
 /**
- * qemu_plugin_create_vcpu_state: allocate plugin state
+ * qemu_plugin_vcpu_create_hook() - called on vcpu creation
+ * @cpu: vCPU CPUState
  *
- * The returned data must be released with g_free()
- * when no longer required.
+ * We don't expose creation to plugins but we need to do internal
+ * housekeeping of stuff. Called once per vCPU creation.
  */
-CPUPluginState *qemu_plugin_create_vcpu_state(void);
+void qemu_plugin_vcpu_create_hook(CPUState *cpu);
+
+/**
+ * qemu_plugin_vcpu_reset_hook() - called on vcpu reset
+ * @cpu: vCPU CPUState
+ *
+ * Called each time the vcpu is reset. This can happen multiple times
+ * for a given vCPU. We expose this to plugins as vcpu_init.
+ */
+void qemu_plugin_vcpu_reset_hook(CPUState *cpu);
 
-void qemu_plugin_vcpu_init_hook(CPUState *cpu);
 void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
 void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb);
 void qemu_plugin_vcpu_idle_cb(CPUState *cpu);
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index e8db7a72545..a75e323f31d 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -238,7 +238,9 @@ void qemu_plugin_reset(qemu_plugin_id_t id, 
qemu_plugin_simple_cb_t cb);
  * @id: plugin ID
  * @cb: callback function
  *
- * The @cb function is called every time a vCPU is initialized.
+ * The @cb function is called every time a vCPU is initialised. This
+ * includes resets. NOTE: not all register information may be
+ * available the first time this is called.
  *
  * See also: qemu_plugin_register_vcpu_exit_cb()
  */
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 8c306c89e45..8113d83713f 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -100,8 +100,18 @@ void cpu_dump_state(CPUState *cpu, FILE *f, int flags)
 void cpu_reset(CPUState *cpu)
 {
     device_cold_reset(DEVICE(cpu));
-
     trace_cpu_reset(cpu->cpu_index);
+
+#ifdef CONFIG_TCG
+    /*
+     * Because vCPUs get "started" before the machine is ready we often
+     * can't provide all the information plugins need during
+     * cpu_common_initfn. However the vCPU will be reset a few times
+     * before we eventually set things going giving plugins an
+     * opportunity to update things.
+     */
+    qemu_plugin_vcpu_reset_hook(cpu);
+#endif
 }
 
 static void cpu_common_reset_hold(Object *obj, ResetType type)
@@ -328,14 +338,13 @@ static void cpu_common_initfn(Object *obj)
     cpu_exec_initfn(cpu);
 
     /*
-     * Plugin initialization must wait until the cpu start executing
-     * code, but we must queue this work before the threads are
-     * created to ensure we don't race.
+     * Called once at vCPU creation so the plugin subsystem can do its
+     * housekeeping. See also cpu_reset() and
+     * qemu_plugin_vcpu_reset_hook() above.
      */
 #ifdef CONFIG_PLUGIN
     if (tcg_enabled()) {
-        cpu->plugin_state = qemu_plugin_create_vcpu_state();
-        qemu_plugin_vcpu_init_hook(cpu);
+        qemu_plugin_vcpu_create_hook(cpu);
     }
 #endif
 }
diff --git a/plugins/core.c b/plugins/core.c
index 85fabf9ec81..d303256c851 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -226,7 +226,7 @@ plugin_register_cb_udata(qemu_plugin_id_t id, enum 
qemu_plugin_event ev,
     do_plugin_register_cb(id, ev, func, udata);
 }
 
-CPUPluginState *qemu_plugin_create_vcpu_state(void)
+static CPUPluginState *qemu_plugin_create_vcpu_state(void)
 {
     return g_new0(CPUPluginState, 1);
 }
@@ -279,7 +279,25 @@ static void plugin_grow_scoreboards__locked(CPUState *cpu)
     end_exclusive();
 }
 
-static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
+static void qemu_plugin_vcpu_reset__async(CPUState *cpu, run_on_cpu_data 
unused)
+{
+    assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
+
+    qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
+    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
+    qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
+}
+
+void qemu_plugin_vcpu_reset_hook(CPUState *cpu)
+{
+    async_run_on_cpu(cpu, qemu_plugin_vcpu_reset__async, RUN_ON_CPU_NULL);
+}
+
+/*
+ * This is purely internal to plugins as we need to expand scoreboards
+ * each time a new vCPU is created.
+ */
+static void qemu_plugin_vcpu_create__async(CPUState *cpu, run_on_cpu_data 
unused)
 {
     bool success;
 
@@ -292,16 +310,13 @@ static void qemu_plugin_vcpu_init__async(CPUState *cpu, 
run_on_cpu_data unused)
     g_assert(success);
     plugin_grow_scoreboards__locked(cpu);
     qemu_rec_mutex_unlock(&plugin.lock);
-
-    qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS);
-    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
-    qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS);
 }
 
-void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+void qemu_plugin_vcpu_create_hook(CPUState *cpu)
 {
+    cpu->plugin_state = qemu_plugin_create_vcpu_state();
     /* Plugin initialization must wait until the cpu start executing code */
-    async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
+    async_run_on_cpu(cpu, qemu_plugin_vcpu_create__async, RUN_ON_CPU_NULL);
 }
 
 void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
-- 
2.47.3


Reply via email to