Re: [PATCH v4 14/25] memory: Add Error** argument to the global_dirty_log routines

2024-03-15 Thread Yong Huang
On Wed, Mar 6, 2024 at 9:35 PM Cédric Le Goater  wrote:

> Now that the log_global*() handlers take an Error** parameter and
> return a bool, do the same for memory_global_dirty_log_start() and
> memory_global_dirty_log_stop(). The error is reported in the callers
> for now and it will be propagated in the call stack in the next
> changes.


> To be noted a functional change in ram_init_bitmaps(), if the dirty
>
Hi, Cédric Le Goater. Could the functional modification be made
separately from the patch? And my "Reviewed-by" is attached
to the first patch that refines memory_global_dirty_log_start's
function declaration.


> pages logger fails to start, there is no need to synchronize the dirty
> pages bitmaps. colo_incoming_start_dirty_log() could be modified in a
> similar way.
>
> Cc: Stefano Stabellini 
> Cc: Anthony Perard 
> Cc: Paul Durrant 
> Cc: "Michael S. Tsirkin" 
> Cc: Paolo Bonzini 
> Cc: David Hildenbrand 
> Cc: Hyman Huang 
> Reviewed-by: Hyman Huang 
> Signed-off-by: Cédric Le Goater 
> ---
>
>  Changes in v4:
>
>  - Dropped log_global_stop() and log_global_sync() changes
>
>  include/exec/memory.h |  5 -
>  hw/i386/xen/xen-hvm.c |  2 +-
>  migration/dirtyrate.c | 13 +++--
>  migration/ram.c   | 22 --
>  system/memory.c   | 11 +--
>  5 files changed, 41 insertions(+), 12 deletions(-)
>
> diff --git a/include/exec/memory.h b/include/exec/memory.h
> index
> 567bc4c9fdb53e8f63487f1400980275687d..c129ee6db7162504bd72d4cfc69b5affb2cd87e8
> 100644
> --- a/include/exec/memory.h
> +++ b/include/exec/memory.h
> @@ -2570,8 +2570,11 @@ void memory_listener_unregister(MemoryListener
> *listener);
>   * memory_global_dirty_log_start: begin dirty logging for all regions
>   *
>   * @flags: purpose of starting dirty log, migration or dirty rate
> + * @errp: pointer to Error*, to store an error if it happens.
> + *
> + * Return: true on success, else false setting @errp with error.
>   */
> -void memory_global_dirty_log_start(unsigned int flags);
> +bool memory_global_dirty_log_start(unsigned int flags, Error **errp);
>
>  /**
>   * memory_global_dirty_log_stop: end dirty logging for all regions
> diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
> index
> 0608ca99f5166fd6379ee674442484e805eff9c0..57cb7df50788a6c31eff68c95e8eaa856fdebede
> 100644
> --- a/hw/i386/xen/xen-hvm.c
> +++ b/hw/i386/xen/xen-hvm.c
> @@ -654,7 +654,7 @@ void xen_hvm_modified_memory(ram_addr_t start,
> ram_addr_t length)
>  void qmp_xen_set_global_dirty_log(bool enable, Error **errp)
>  {
>  if (enable) {
> -memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION);
> +memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp);
>  } else {
>  memory_global_dirty_log_stop(GLOBAL_DIRTY_MIGRATION);
>  }
> diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c
> index
> 1d2e85746fb7b10eb7f149976970f9a92125af8a..d02d70b7b4b86a29d4d5540ded416543536d8f98
> 100644
> --- a/migration/dirtyrate.c
> +++ b/migration/dirtyrate.c
> @@ -90,9 +90,15 @@ static int64_t do_calculate_dirtyrate(DirtyPageRecord
> dirty_pages,
>
>  void global_dirty_log_change(unsigned int flag, bool start)
>  {
> +Error *local_err = NULL;
> +bool ret;
> +
>  bql_lock();
>  if (start) {
> -memory_global_dirty_log_start(flag);
> +ret = memory_global_dirty_log_start(flag, _err);
> +if (!ret) {
> +error_report_err(local_err);
> +}
>  } else {
>  memory_global_dirty_log_stop(flag);
>  }
> @@ -608,9 +614,12 @@ static void calculate_dirtyrate_dirty_bitmap(struct
> DirtyRateConfig config)
>  {
>  int64_t start_time;
>  DirtyPageRecord dirty_pages;
> +Error *local_err = NULL;
>
>  bql_lock();
> -memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
> +if (!memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE,
> _err)) {
> +error_report_err(local_err);
> +}
>
>  /*
>   * 1'round of log sync may return all 1 bits with
> diff --git a/migration/ram.c b/migration/ram.c
> index
> c5149b7d717aefad7f590422af0ea4a40e7507be..397b4c0f218a66d194e44f9c5f9fe8e9885c48b6
> 100644
> --- a/migration/ram.c
> +++ b/migration/ram.c
> @@ -2836,18 +2836,31 @@ static void
> migration_bitmap_clear_discarded_pages(RAMState *rs)
>
>  static void ram_init_bitmaps(RAMState *rs)
>  {
> +Error *local_err = NULL;
> +bool ret = true;
> +
>  qemu_mutex_lock_ramlist();
>
>  WITH_RCU_READ_LOCK_GUARD() {
>  ram_list_init_bitmaps();
>  /* We don't use dirty log with background snapshots */
>  if (!migrate_background_snapshot()) {
> -memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION);
> +ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION,
> +_err);
> +if (!ret) {
> +error_report_err(local_err);
> +goto out_unlock;
> +}
> 

[PATCH 20/22] plugins: Move qemu_plugin_insn_cleanup_fn to tcg.c

2024-03-15 Thread Richard Henderson
This is only used in one place, and usage requires an
out-of-line function.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h | 12 
 tcg/tcg.c | 12 
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 07b1755990..201889cbee 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -116,18 +116,6 @@ struct qemu_plugin_scoreboard {
 QLIST_ENTRY(qemu_plugin_scoreboard) entry;
 };
 
-/*
- * qemu_plugin_insn allocate and cleanup functions. We don't expect to
- * cleanup many of these structures. They are reused for each fresh
- * translation.
- */
-
-static inline void qemu_plugin_insn_cleanup_fn(gpointer data)
-{
-struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
-g_byte_array_free(insn->data, true);
-}
-
 /* Internal context for this TranslationBlock */
 struct qemu_plugin_tb {
 GPtrArray *insns;
diff --git a/tcg/tcg.c b/tcg/tcg.c
index d248c52e96..d7abc514c4 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -761,6 +761,18 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, 
tlb.f[0]) -
   < MIN_TLB_MASK_TABLE_OFS);
 #endif
 
+#ifdef CONFIG_PLUGIN
+/*
+ * We don't expect to cleanup many of these structures.
+ * They are reused for each fresh translation.
+ */
+static void qemu_plugin_insn_cleanup_fn(gpointer data)
+{
+struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
+g_byte_array_free(insn->data, true);
+}
+#endif
+
 static void alloc_tcg_plugin_context(TCGContext *s)
 {
 #ifdef CONFIG_PLUGIN
-- 
2.34.1




[PATCH 06/22] plugins: Create TCGHelperInfo for all out-of-line callbacks

2024-03-15 Thread Richard Henderson
TCGHelperInfo includes the ABI for every function call.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h |  1 +
 plugins/core.c| 51 ++-
 2 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 143262dca8..793c44f1f2 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -92,6 +92,7 @@ struct qemu_plugin_dyn_cb {
 union {
 struct {
 union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
 } regular;
 struct {
 qemu_plugin_u64 entry;
diff --git a/plugins/core.c b/plugins/core.c
index 837c373690..b0a2e80874 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -338,12 +338,26 @@ void plugin_register_dyn_cb__udata(GArray **arr,
enum qemu_plugin_cb_flags flags,
void *udata)
 {
-struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+static TCGHelperInfo info[3] = {
+[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN,
+/*
+ * Match qemu_plugin_vcpu_udata_cb_t:
+ *   void (*)(uint32_t, void *)
+ */
+[0 ... 2].typemask = (dh_typemask(void, 0) |
+  dh_typemask(i32, 1) |
+  dh_typemask(ptr, 2))
+};
 
+struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = udata;
-/* Note flags are discarded as unused. */
-dyn_cb->regular.f.vcpu_udata = cb;
 dyn_cb->type = PLUGIN_CB_REGULAR;
+dyn_cb->regular.f.vcpu_udata = cb;
+
+assert((unsigned)flags < ARRAY_SIZE(info));
+dyn_cb->regular.info = [flags];
 }
 
 void plugin_register_vcpu_mem_cb(GArray **arr,
@@ -352,14 +366,39 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata)
 {
-struct qemu_plugin_dyn_cb *dyn_cb;
+/*
+ * Expect that the underlying type for enum qemu_plugin_meminfo_t
+ * is either int32_t or uint32_t, aka int or unsigned int.
+ */
+QEMU_BUILD_BUG_ON(
+!__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) &&
+!__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t));
 
-dyn_cb = plugin_get_dyn_cb(arr);
+static TCGHelperInfo info[3] = {
+[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN,
+/*
+ * Match qemu_plugin_vcpu_mem_cb_t:
+ *   void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
+ */
+[0 ... 2].typemask =
+(dh_typemask(void, 0) |
+ dh_typemask(i32, 1) |
+ (__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t)
+  ? dh_typemask(i32, 2) : dh_typemask(s32, 2)) |
+ dh_typemask(i64, 3) |
+ dh_typemask(ptr, 4))
+};
+
+struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = udata;
-/* Note flags are discarded as unused. */
 dyn_cb->type = PLUGIN_CB_REGULAR;
 dyn_cb->rw = rw;
 dyn_cb->regular.f.vcpu_mem = cb;
+
+assert((unsigned)flags < ARRAY_SIZE(info));
+dyn_cb->regular.info = [flags];
 }
 
 /*
-- 
2.34.1




[PATCH 02/22] tcg: Make tcg/helper-info.h self-contained

2024-03-15 Thread Richard Henderson
Move MAX_CALL_IARGS from tcg.h and include for
the define of TCG_TARGET_REG_BITS.

Signed-off-by: Richard Henderson 
---
 include/tcg/helper-info.h | 3 +++
 include/tcg/tcg.h | 2 --
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h
index 7c27d6164a..909fe73afa 100644
--- a/include/tcg/helper-info.h
+++ b/include/tcg/helper-info.h
@@ -12,6 +12,9 @@
 #ifdef CONFIG_TCG_INTERPRETER
 #include 
 #endif
+#include "tcg-target-reg-bits.h"
+
+#define MAX_CALL_IARGS  7
 
 /*
  * Describe the calling convention of a given argument type.
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index e9d05f40b0..a6e7df146a 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -39,8 +39,6 @@
 /* XXX: make safe guess about sizes */
 #define MAX_OP_PER_INSTR 266
 
-#define MAX_CALL_IARGS  7
-
 #define CPU_TEMP_BUF_NLONGS 128
 #define TCG_STATIC_FRAME_SIZE  (CPU_TEMP_BUF_NLONGS * sizeof(long))
 
-- 
2.34.1




[PATCH 05/22] plugins: Move function pointer in qemu_plugin_dyn_cb

2024-03-15 Thread Richard Henderson
The out-of-line function pointer is mutually exclusive
with inline expansion, so move it into the union.
Wrap the pointer in a structure named 'regular' to match
PLUGIN_CB_REGULAR.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h  | 4 +++-
 accel/tcg/plugin-gen.c | 4 ++--
 plugins/core.c | 8 
 3 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 12a96cea2a..143262dca8 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -84,13 +84,15 @@ enum plugin_dyn_cb_subtype {
  * instance of a callback to be called upon the execution of a particular TB.
  */
 struct qemu_plugin_dyn_cb {
-union qemu_plugin_cb_sig f;
 void *userp;
 enum plugin_dyn_cb_subtype type;
 /* @rw applies to mem callbacks only (both regular and inline) */
 enum qemu_plugin_mem_rw rw;
 /* fields specific to each dyn_cb type go here */
 union {
+struct {
+union qemu_plugin_cb_sig f;
+} regular;
 struct {
 qemu_plugin_u64 entry;
 enum qemu_plugin_op op;
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8028786c7b..c56f104aee 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -431,7 +431,7 @@ static TCGOp *append_udata_cb(const struct 
qemu_plugin_dyn_cb *cb,
 }
 
 /* call */
-op = copy_call(_op, op, cb->f.vcpu_udata, cb_idx);
+op = copy_call(_op, op, cb->regular.f.vcpu_udata, cb_idx);
 
 return op;
 }
@@ -479,7 +479,7 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb 
*cb,
 
 if (type == PLUGIN_GEN_CB_MEM) {
 /* call */
-op = copy_call(_op, op, cb->f.vcpu_udata, cb_idx);
+op = copy_call(_op, op, cb->regular.f.vcpu_udata, cb_idx);
 }
 
 return op;
diff --git a/plugins/core.c b/plugins/core.c
index 4487cb7c48..837c373690 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -342,7 +342,7 @@ void plugin_register_dyn_cb__udata(GArray **arr,
 
 dyn_cb->userp = udata;
 /* Note flags are discarded as unused. */
-dyn_cb->f.vcpu_udata = cb;
+dyn_cb->regular.f.vcpu_udata = cb;
 dyn_cb->type = PLUGIN_CB_REGULAR;
 }
 
@@ -359,7 +359,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
 /* Note flags are discarded as unused. */
 dyn_cb->type = PLUGIN_CB_REGULAR;
 dyn_cb->rw = rw;
-dyn_cb->f.generic = cb;
+dyn_cb->regular.f.vcpu_mem = cb;
 }
 
 /*
@@ -511,8 +511,8 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 }
 switch (cb->type) {
 case PLUGIN_CB_REGULAR:
-cb->f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
-   vaddr, cb->userp);
+cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
+   vaddr, cb->userp);
 break;
 case PLUGIN_CB_INLINE:
 exec_inline_op(cb, cpu->cpu_index);
-- 
2.34.1




[PATCH 17/22] plugins: Replace pr_ops with a proper debug dump flag

2024-03-15 Thread Richard Henderson
The DEBUG_PLUGIN_GEN_OPS ifdef is replaced with "-d op_plugin".
The second pr_ops call can be obtained with "-d op".

Signed-off-by: Richard Henderson 
---
 include/qemu/log.h |  1 +
 include/tcg/tcg.h  |  1 +
 accel/tcg/plugin-gen.c | 68 --
 tcg/tcg.c  | 29 +-
 util/log.c |  4 +++
 5 files changed, 46 insertions(+), 57 deletions(-)

diff --git a/include/qemu/log.h b/include/qemu/log.h
index df59bfabcd..e10e24cd4f 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -36,6 +36,7 @@ bool qemu_log_separate(void);
 #define LOG_STRACE (1 << 19)
 #define LOG_PER_THREAD (1 << 20)
 #define CPU_LOG_TB_VPU (1 << 21)
+#define LOG_TB_OP_PLUGIN   (1 << 22)
 
 /* Lock/unlock output. */
 
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index df66e8f012..753d7ca3e0 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -1065,5 +1065,6 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const 
TCGOpcode *n)
 }
 
 bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned);
+void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs);
 
 #endif /* TCG_H */
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 6f0731b479..10d917abd3 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -44,6 +44,7 @@
  */
 #include "qemu/osdep.h"
 #include "qemu/plugin.h"
+#include "qemu/log.h"
 #include "cpu.h"
 #include "tcg/tcg.h"
 #include "tcg/tcg-temp-internal.h"
@@ -58,6 +59,7 @@
 # define CONFIG_SOFTMMU_GATE 0
 #endif
 
+/* Update plugin_from_name in tcg.c. */
 enum plugin_gen_from {
 PLUGIN_GEN_FROM_TB,
 PLUGIN_GEN_FROM_INSN,
@@ -192,66 +194,21 @@ static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
 tcg_temp_free_i32(cpu_index);
 }
 
-/* #define DEBUG_PLUGIN_GEN_OPS */
-static void pr_ops(void)
-{
-#ifdef DEBUG_PLUGIN_GEN_OPS
-TCGOp *op;
-int i = 0;
-
-QTAILQ_FOREACH(op, _ctx->ops, link) {
-const char *name = "";
-const char *type = "";
-
-if (op->opc == INDEX_op_plugin_cb_start) {
-switch (op->args[0]) {
-case PLUGIN_GEN_FROM_TB:
-name = "tb";
-break;
-case PLUGIN_GEN_FROM_INSN:
-name = "insn";
-break;
-case PLUGIN_GEN_FROM_MEM:
-name = "mem";
-break;
-case PLUGIN_GEN_AFTER_INSN:
-name = "after insn";
-break;
-default:
-break;
-}
-switch (op->args[1]) {
-case PLUGIN_GEN_CB_UDATA:
-type = "udata";
-break;
-case PLUGIN_GEN_CB_INLINE:
-type = "inline";
-break;
-case PLUGIN_GEN_CB_MEM:
-type = "mem";
-break;
-case PLUGIN_GEN_ENABLE_MEM_HELPER:
-type = "enable mem helper";
-break;
-case PLUGIN_GEN_DISABLE_MEM_HELPER:
-type = "disable mem helper";
-break;
-default:
-break;
-}
-}
-printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, 
type);
-i++;
-}
-#endif
-}
-
 static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
 {
 TCGOp *op, *next;
 int insn_idx = -1;
 
-pr_ops();
+if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN)
+ && qemu_log_in_addr_range(plugin_tb->vaddr))) {
+FILE *logfile = qemu_log_trylock();
+if (logfile) {
+fprintf(logfile, "OP before plugin injection:\n");
+tcg_dump_ops(tcg_ctx, logfile, false);
+fprintf(logfile, "\n");
+qemu_log_unlock(logfile);
+}
+}
 
 /*
  * While injecting code, we cannot afford to reuse any ebb temps
@@ -389,7 +346,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 break;
 }
 }
-pr_ops();
 }
 
 bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 363a065e28..d248c52e96 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -2540,6 +2540,15 @@ static const char bswap_flag_name[][6] = {
 [TCG_BSWAP_IZ | TCG_BSWAP_OS] = "iz,os",
 };
 
+#ifdef CONFIG_PLUGIN
+static const char * const plugin_from_name[] = {
+"from-tb",
+"from-insn",
+"after-insn",
+"after-tb",
+};
+#endif
+
 static inline bool tcg_regset_single(TCGRegSet d)
 {
 return (d & (d - 1)) == 0;
@@ -2558,7 +2567,7 @@ static inline TCGReg tcg_regset_first(TCGRegSet d)
 #define ne_fprintf(...) \
 ({ int ret_ = fprintf(__VA_ARGS__); ret_ >= 0 ? ret_ : 0; })
 
-static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
+void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs)
 {
 char buf[128];
 TCGOp *op;
@@ -2714,6 +2723,24 @@ static void tcg_dump_ops(TCGContext 

[PATCH 19/22] plugins: Merge qemu_plugin_tb_insn_get to plugin-gen.c

2024-03-15 Thread Richard Henderson
Merge qemu_plugin_insn_alloc and qemu_plugin_tb_insn_get into
plugin_gen_insn_start, since it is used nowhere else.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h  | 39 ---
 accel/tcg/plugin-gen.c | 39 ---
 2 files changed, 32 insertions(+), 46 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 34498da717..07b1755990 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -128,14 +128,6 @@ static inline void qemu_plugin_insn_cleanup_fn(gpointer 
data)
 g_byte_array_free(insn->data, true);
 }
 
-static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void)
-{
-struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
-
-insn->data = g_byte_array_sized_new(4);
-return insn;
-}
-
 /* Internal context for this TranslationBlock */
 struct qemu_plugin_tb {
 GPtrArray *insns;
@@ -152,37 +144,6 @@ struct qemu_plugin_tb {
 GArray *cbs;
 };
 
-/**
- * qemu_plugin_tb_insn_get(): get next plugin record for translation.
- * @tb: the internal tb context
- * @pc: address of instruction
- */
-static inline
-struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb,
- uint64_t pc)
-{
-struct qemu_plugin_insn *insn;
-
-if (unlikely(tb->n == tb->insns->len)) {
-struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
-g_ptr_array_add(tb->insns, new_insn);
-}
-
-insn = g_ptr_array_index(tb->insns, tb->n++);
-g_byte_array_set_size(insn->data, 0);
-insn->calls_helpers = false;
-insn->mem_helper = false;
-insn->vaddr = pc;
-if (insn->insn_cbs) {
-g_array_set_size(insn->insn_cbs, 0);
-}
-if (insn->mem_cbs) {
-g_array_set_size(insn->mem_cbs, 0);
-}
-
-return insn;
-}
-
 /**
  * struct CPUPluginState - per-CPU state for plugins
  * @event_mask: plugin event bitmap. Modified only via async work.
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 28414c4ff1..70914c3bf8 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -380,11 +380,34 @@ bool plugin_gen_tb_start(CPUState *cpu, const 
DisasContextBase *db,
 void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
 {
 struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
-struct qemu_plugin_insn *pinsn;
+struct qemu_plugin_insn *insn;
+size_t n = db->num_insns;
+vaddr pc;
 
-pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next);
-tcg_ctx->plugin_insn = pinsn;
-plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
+assert(n >= 1);
+ptb->n = n;
+if (n <= ptb->insns->len) {
+insn = g_ptr_array_index(ptb->insns, n - 1);
+g_byte_array_set_size(insn->data, 0);
+} else {
+assert(n - 1 == ptb->insns->len);
+insn = g_new0(struct qemu_plugin_insn, 1);
+insn->data = g_byte_array_sized_new(4);
+g_ptr_array_add(ptb->insns, insn);
+}
+
+tcg_ctx->plugin_insn = insn;
+insn->calls_helpers = false;
+insn->mem_helper = false;
+if (insn->insn_cbs) {
+g_array_set_size(insn->insn_cbs, 0);
+}
+if (insn->mem_cbs) {
+g_array_set_size(insn->mem_cbs, 0);
+}
+
+pc = db->pc_next;
+insn->vaddr = pc;
 
 /*
  * Detect page crossing to get the new host address.
@@ -392,16 +415,18 @@ void plugin_gen_insn_start(CPUState *cpu, const 
DisasContextBase *db)
  * fetching instructions from a region not backed by RAM.
  */
 if (ptb->haddr1 == NULL) {
-pinsn->haddr = NULL;
+insn->haddr = NULL;
 } else if (is_same_page(db, db->pc_next)) {
-pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
+insn->haddr = ptb->haddr1 + pc - ptb->vaddr;
 } else {
 if (ptb->vaddr2 == -1) {
 ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
 get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, >haddr2);
 }
-pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
+insn->haddr = ptb->haddr2 + pc - ptb->vaddr2;
 }
+
+plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
 }
 
 void plugin_gen_insn_end(void)
-- 
2.34.1




[PATCH 04/22] plugins: Zero new qemu_plugin_dyn_cb entries

2024-03-15 Thread Richard Henderson
Signed-off-by: Richard Henderson 
---
 plugins/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/plugins/core.c b/plugins/core.c
index 11ca20e626..4487cb7c48 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -307,7 +307,7 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray 
**arr)
 GArray *cbs = *arr;
 
 if (!cbs) {
-cbs = g_array_sized_new(false, false,
+cbs = g_array_sized_new(false, true,
 sizeof(struct qemu_plugin_dyn_cb), 1);
 *arr = cbs;
 }
-- 
2.34.1




[PATCH 21/22] plugins: Inline plugin_gen_empty_callback

2024-03-15 Thread Richard Henderson
Each caller can use tcg_gen_plugin_cb directly.

Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-gen.c | 19 +++
 1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 70914c3bf8..fd52ea3987 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -67,19 +67,6 @@ enum plugin_gen_from {
 PLUGIN_GEN_AFTER_TB,
 };
 
-static void plugin_gen_empty_callback(enum plugin_gen_from from)
-{
-switch (from) {
-case PLUGIN_GEN_AFTER_INSN:
-case PLUGIN_GEN_FROM_TB:
-case PLUGIN_GEN_FROM_INSN:
-tcg_gen_plugin_cb(from);
-break;
-default:
-g_assert_not_reached();
-}
-}
-
 /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
 void plugin_gen_disable_mem_helpers(void)
 {
@@ -369,7 +356,7 @@ bool plugin_gen_tb_start(CPUState *cpu, const 
DisasContextBase *db,
 ptb->mem_only = mem_only;
 ptb->mem_helper = false;
 
-plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
+tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB);
 }
 
 tcg_ctx->plugin_insn = NULL;
@@ -426,12 +413,12 @@ void plugin_gen_insn_start(CPUState *cpu, const 
DisasContextBase *db)
 insn->haddr = ptb->haddr2 + pc - ptb->vaddr2;
 }
 
-plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
+tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN);
 }
 
 void plugin_gen_insn_end(void)
 {
-plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
+tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_INSN);
 }
 
 /*
-- 
2.34.1




[PATCH 16/22] plugins: Introduce PLUGIN_CB_MEM_REGULAR

2024-03-15 Thread Richard Henderson
Use different enumerators for vcpu_udata and vcpu_mem callbacks.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h  | 1 +
 accel/tcg/plugin-gen.c | 2 +-
 plugins/core.c | 4 ++--
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index cf9758be55..34498da717 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -67,6 +67,7 @@ union qemu_plugin_cb_sig {
 
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
+PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE,
 };
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 669e343cfb..6f0731b479 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -367,7 +367,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 
 if (cb->rw & rw) {
 switch (cb->type) {
-case PLUGIN_CB_REGULAR:
+case PLUGIN_CB_MEM_REGULAR:
 gen_mem_cb(cb, meminfo, addr);
 break;
 case PLUGIN_CB_INLINE:
diff --git a/plugins/core.c b/plugins/core.c
index b0615f1e7f..0213513ec6 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -391,7 +391,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
 
 struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = udata;
-dyn_cb->type = PLUGIN_CB_REGULAR;
+dyn_cb->type = PLUGIN_CB_MEM_REGULAR;
 dyn_cb->rw = rw;
 dyn_cb->regular.f.vcpu_mem = cb;
 
@@ -547,7 +547,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 break;
 }
 switch (cb->type) {
-case PLUGIN_CB_REGULAR:
+case PLUGIN_CB_MEM_REGULAR:
 cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
vaddr, cb->userp);
 break;
-- 
2.34.1




[PATCH 18/22] plugins: Split out common cb expanders

2024-03-15 Thread Richard Henderson
Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-gen.c | 84 +-
 1 file changed, 41 insertions(+), 43 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 10d917abd3..28414c4ff1 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -194,6 +194,37 @@ static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
 tcg_temp_free_i32(cpu_index);
 }
 
+static void inject_cb(struct qemu_plugin_dyn_cb *cb)
+
+{
+switch (cb->type) {
+case PLUGIN_CB_REGULAR:
+gen_udata_cb(cb);
+break;
+case PLUGIN_CB_INLINE:
+gen_inline_cb(cb);
+break;
+default:
+g_assert_not_reached();
+}
+}
+
+static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb,
+  enum qemu_plugin_mem_rw rw,
+  qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
+{
+if (cb->rw & rw) {
+switch (cb->type) {
+case PLUGIN_CB_MEM_REGULAR:
+gen_mem_cb(cb, meminfo, addr);
+break;
+default:
+inject_cb(cb);
+break;
+}
+}
+}
+
 static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
 {
 TCGOp *op, *next;
@@ -255,19 +286,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 
 cbs = plugin_tb->cbs;
 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
-struct qemu_plugin_dyn_cb *cb =
-_array_index(cbs, struct qemu_plugin_dyn_cb, i);
-
-switch (cb->type) {
-case PLUGIN_CB_REGULAR:
-gen_udata_cb(cb);
-break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
-break;
-default:
-g_assert_not_reached();
-}
+inject_cb(
+_array_index(cbs, struct qemu_plugin_dyn_cb, i));
 }
 break;
 
@@ -278,19 +298,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 
 cbs = insn->insn_cbs;
 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
-struct qemu_plugin_dyn_cb *cb =
-_array_index(cbs, struct qemu_plugin_dyn_cb, i);
-
-switch (cb->type) {
-case PLUGIN_CB_REGULAR:
-gen_udata_cb(cb);
-break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
-break;
-default:
-g_assert_not_reached();
-}
+inject_cb(
+_array_index(cbs, struct qemu_plugin_dyn_cb, i));
 }
 break;
 
@@ -307,33 +316,22 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 {
 TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0]));
 qemu_plugin_meminfo_t meminfo = op->args[1];
+enum qemu_plugin_mem_rw rw =
+(qemu_plugin_mem_is_store(meminfo)
+ ? QEMU_PLUGIN_MEM_W : QEMU_PLUGIN_MEM_R);
 struct qemu_plugin_insn *insn;
 const GArray *cbs;
-int i, n, rw;
+int i, n;
 
 assert(insn_idx >= 0);
 insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
-rw = qemu_plugin_mem_is_store(meminfo) ? 2 : 1;
 
 tcg_ctx->emit_before_op = op;
 
 cbs = insn->mem_cbs;
 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
-struct qemu_plugin_dyn_cb *cb =
-_array_index(cbs, struct qemu_plugin_dyn_cb, i);
-
-if (cb->rw & rw) {
-switch (cb->type) {
-case PLUGIN_CB_MEM_REGULAR:
-gen_mem_cb(cb, meminfo, addr);
-break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
-break;
-default:
-g_assert_not_reached();
-}
-}
+inject_mem_cb(_array_index(cbs, struct qemu_plugin_dyn_cb, 
i),
+  rw, meminfo, addr);
 }
 
 tcg_ctx->emit_before_op = NULL;
-- 
2.34.1




[PATCH 22/22] plugins: Update the documentation block for plugin-gen.c

2024-03-15 Thread Richard Henderson
Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-gen.c | 31 ---
 1 file changed, 4 insertions(+), 27 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index fd52ea3987..c354825779 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -14,33 +14,10 @@
  * Injecting the desired instrumentation could be done with a second
  * translation pass that combined the instrumentation requests, but that
  * would be ugly and inefficient since we would decode the guest code twice.
- * Instead, during TB translation we add "empty" instrumentation calls for all
- * possible instrumentation events, and then once we collect the 
instrumentation
- * requests from plugins, we either "fill in" those empty events or remove them
- * if they have no requests.
- *
- * When "filling in" an event we first copy the empty callback's TCG ops. This
- * might seem unnecessary, but it is done to support an arbitrary number
- * of callbacks per event. Take for example a regular instruction callback.
- * We first generate a callback to an empty helper function. Then, if two
- * plugins register one callback each for this instruction, we make two copies
- * of the TCG ops generated for the empty callback, substituting the function
- * pointer that points to the empty helper function with the plugins' desired
- * callback functions. After that we remove the empty callback's ops.
- *
- * Note that the location in TCGOp.args[] of the pointer to a helper function
- * varies across different guest and host architectures. Instead of duplicating
- * the logic that figures this out, we rely on the fact that the empty
- * callbacks point to empty functions that are unique pointers in the program.
- * Thus, to find the right location we just have to look for a match in
- * TCGOp.args[]. This is the main reason why we first copy an empty callback's
- * TCG ops and then fill them in; regardless of whether we have one or many
- * callbacks for that event, the logic to add all of them is the same.
- *
- * When generating more than one callback per event, we make a small
- * optimization to avoid generating redundant operations. For instance, for the
- * second and all subsequent callbacks of an event, we do not need to reload 
the
- * CPU's index into a TCG temp, since the first callback did it already.
+ * Instead, during TB translation we add "plugin_cb" marker opcodes
+ * for all possible instrumentation events, and then once we collect the
+ * instrumentation requests from plugins, we generate code for those markers
+ * or remove them if they have no requests.
  */
 #include "qemu/osdep.h"
 #include "qemu/plugin.h"
-- 
2.34.1




[PATCH 13/22] tcg: Remove TCG_CALL_PLUGIN

2024-03-15 Thread Richard Henderson
Since we no longer emit plugin helpers during the initial code
translation phase, we don't need to specially mark plugin helpers.

Signed-off-by: Richard Henderson 
---
 include/tcg/tcg.h |  2 --
 plugins/core.c| 10 --
 tcg/tcg.c |  4 +---
 3 files changed, 5 insertions(+), 11 deletions(-)

diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 95a7f4d010..df66e8f012 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -353,8 +353,6 @@ typedef TCGv_ptr TCGv_env;
 #define TCG_CALL_NO_SIDE_EFFECTS0x0004
 /* Helper is G_NORETURN.  */
 #define TCG_CALL_NO_RETURN  0x0008
-/* Helper is part of Plugins.  */
-#define TCG_CALL_PLUGIN 0x0010
 
 /* convenience version of most used call flags */
 #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS
diff --git a/plugins/core.c b/plugins/core.c
index b0a2e80874..b0615f1e7f 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -339,9 +339,8 @@ void plugin_register_dyn_cb__udata(GArray **arr,
void *udata)
 {
 static TCGHelperInfo info[3] = {
-[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN,
-[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN,
-[QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
+[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
 /*
  * Match qemu_plugin_vcpu_udata_cb_t:
  *   void (*)(uint32_t, void *)
@@ -375,9 +374,8 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
 !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t));
 
 static TCGHelperInfo info[3] = {
-[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG | TCG_CALL_PLUGIN,
-[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG | TCG_CALL_PLUGIN,
-[QEMU_PLUGIN_CB_RW_REGS].flags = TCG_CALL_PLUGIN,
+[QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG,
+[QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG,
 /*
  * Match qemu_plugin_vcpu_mem_cb_t:
  *   void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *)
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 0bf218314b..363a065e28 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -2269,9 +2269,7 @@ static void tcg_gen_callN(void *func, TCGHelperInfo *info,
 
 #ifdef CONFIG_PLUGIN
 /* Flag helpers that may affect guest state */
-if (tcg_ctx->plugin_insn &&
-!(info->flags & TCG_CALL_PLUGIN) &&
-!(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) {
+if (tcg_ctx->plugin_insn && !(info->flags & TCG_CALL_NO_SIDE_EFFECTS)) {
 tcg_ctx->plugin_insn->calls_helpers = true;
 }
 #endif
-- 
2.34.1




[PATCH 15/22] plugins: Simplify callback queues

2024-03-15 Thread Richard Henderson
We have qemu_plugin_dyn_cb.type to differentiate the various
callback types, so we do not need to keep them in separate queues.

Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h  | 35 ++--
 accel/tcg/plugin-gen.c | 90 ++
 plugins/api.c  | 18 +++--
 3 files changed, 65 insertions(+), 78 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index ee1c1b174a..cf9758be55 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -66,15 +66,8 @@ union qemu_plugin_cb_sig {
 };
 
 enum plugin_dyn_cb_type {
-PLUGIN_CB_INSN,
-PLUGIN_CB_MEM,
-PLUGIN_N_CB_TYPES,
-};
-
-enum plugin_dyn_cb_subtype {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_INLINE,
-PLUGIN_N_CB_SUBTYPES,
 };
 
 /*
@@ -84,7 +77,7 @@ enum plugin_dyn_cb_subtype {
  */
 struct qemu_plugin_dyn_cb {
 void *userp;
-enum plugin_dyn_cb_subtype type;
+enum plugin_dyn_cb_type type;
 /* @rw applies to mem callbacks only (both regular and inline) */
 enum qemu_plugin_mem_rw rw;
 /* fields specific to each dyn_cb type go here */
@@ -106,7 +99,8 @@ struct qemu_plugin_insn {
 GByteArray *data;
 uint64_t vaddr;
 void *haddr;
-GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
+GArray *insn_cbs;
+GArray *mem_cbs;
 bool calls_helpers;
 
 /* if set, the instruction calls helpers that might access guest memory */
@@ -135,16 +129,9 @@ static inline void qemu_plugin_insn_cleanup_fn(gpointer 
data)
 
 static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void)
 {
-int i, j;
 struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
-insn->data = g_byte_array_sized_new(4);
 
-for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
-for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
-insn->cbs[i][j] = g_array_new(false, false,
-  sizeof(struct qemu_plugin_dyn_cb));
-}
-}
+insn->data = g_byte_array_sized_new(4);
 return insn;
 }
 
@@ -161,7 +148,7 @@ struct qemu_plugin_tb {
 /* if set, the TB calls helpers that might access guest memory */
 bool mem_helper;
 
-GArray *cbs[PLUGIN_N_CB_SUBTYPES];
+GArray *cbs;
 };
 
 /**
@@ -174,22 +161,22 @@ struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct 
qemu_plugin_tb *tb,
  uint64_t pc)
 {
 struct qemu_plugin_insn *insn;
-int i, j;
 
 if (unlikely(tb->n == tb->insns->len)) {
 struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
 g_ptr_array_add(tb->insns, new_insn);
 }
+
 insn = g_ptr_array_index(tb->insns, tb->n++);
 g_byte_array_set_size(insn->data, 0);
 insn->calls_helpers = false;
 insn->mem_helper = false;
 insn->vaddr = pc;
-
-for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
-for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
-g_array_set_size(insn->cbs[i][j], 0);
-}
+if (insn->insn_cbs) {
+g_array_set_size(insn->insn_cbs, 0);
+}
+if (insn->mem_cbs) {
+g_array_set_size(insn->mem_cbs, 0);
 }
 
 return insn;
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index c8f0e0ecaa..669e343cfb 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -89,9 +89,8 @@ void plugin_gen_disable_mem_helpers(void)
 static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
   struct qemu_plugin_insn *insn)
 {
-GArray *cbs[2];
 GArray *arr;
-size_t n_cbs;
+size_t len;
 
 /*
  * Tracking memory accesses performed from helpers requires extra work.
@@ -110,22 +109,25 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb 
*ptb,
 return;
 }
 
-cbs[0] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
-cbs[1] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
-n_cbs = cbs[0]->len + cbs[1]->len;
-
-if (n_cbs == 0) {
+if (!insn->mem_cbs || !insn->mem_cbs->len) {
 insn->mem_helper = false;
 return;
 }
 insn->mem_helper = true;
 ptb->mem_helper = true;
 
+/*
+ * TODO: It seems like we should be able to use ref/unref
+ * to avoid needing to actually copy this array.
+ * Alternately, perhaps we could allocate new memory adjacent
+ * to the TranslationBlock itself, so that we do not have to
+ * actively manage the lifetime after this.
+ */
+len = insn->mem_cbs->len;
 arr = g_array_sized_new(false, false,
-sizeof(struct qemu_plugin_dyn_cb), n_cbs);
-g_array_append_vals(arr, cbs[0]->data, cbs[0]->len);
-g_array_append_vals(arr, cbs[1]->data, cbs[1]->len);
-
+sizeof(struct qemu_plugin_dyn_cb), len);
+memcpy(arr->data, insn->mem_cbs->data,
+   len * sizeof(struct qemu_plugin_dyn_cb));
 qemu_plugin_add_dyn_cb_arr(arr);
 
 tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), 

[PATCH 10/22] plugins: Use emit_before_op for PLUGIN_GEN_FROM_INSN

2024-03-15 Thread Richard Henderson
Signed-off-by: Richard Henderson 
---
 include/qemu/plugin.h  |   1 -
 accel/tcg/plugin-gen.c | 286 ++---
 plugins/api.c  |   8 +-
 3 files changed, 67 insertions(+), 228 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 793c44f1f2..ee1c1b174a 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -73,7 +73,6 @@ enum plugin_dyn_cb_type {
 
 enum plugin_dyn_cb_subtype {
 PLUGIN_CB_REGULAR,
-PLUGIN_CB_REGULAR_R,
 PLUGIN_CB_INLINE,
 PLUGIN_N_CB_SUBTYPES,
 };
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index aa74e580bd..4785838eca 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -104,30 +104,6 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
 void *userdata)
 { }
 
-static void gen_empty_udata_cb(void (*gen_helper)(TCGv_i32, TCGv_ptr))
-{
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_ptr udata = tcg_temp_ebb_new_ptr();
-
-tcg_gen_movi_ptr(udata, 0);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-gen_helper(cpu_index, udata);
-
-tcg_temp_free_ptr(udata);
-tcg_temp_free_i32(cpu_index);
-}
-
-static void gen_empty_udata_cb_no_wg(void)
-{
-gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_wg);
-}
-
-static void gen_empty_udata_cb_no_rwg(void)
-{
-gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_rwg);
-}
-
 /*
  * For now we only support addi_i64.
  * When we support more ops, we can generate one empty inline cb for each.
@@ -176,51 +152,19 @@ static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
 tcg_temp_free_i32(cpu_index);
 }
 
-/*
- * Share the same function for enable/disable. When enabling, the NULL
- * pointer will be overwritten later.
- */
-static void gen_empty_mem_helper(void)
-{
-TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
-
-tcg_gen_movi_ptr(ptr, 0);
-tcg_gen_st_ptr(ptr, tcg_env, offsetof(CPUState, plugin_mem_cbs) -
- offsetof(ArchCPU, env));
-tcg_temp_free_ptr(ptr);
-}
-
 static void gen_plugin_cb_start(enum plugin_gen_from from,
 enum plugin_gen_cb type, unsigned wr)
 {
 tcg_gen_plugin_cb_start(from, type, wr);
 }
 
-static void gen_wrapped(enum plugin_gen_from from,
-enum plugin_gen_cb type, void (*func)(void))
-{
-gen_plugin_cb_start(from, type, 0);
-func();
-tcg_gen_plugin_cb_end();
-}
-
 static void plugin_gen_empty_callback(enum plugin_gen_from from)
 {
 switch (from) {
 case PLUGIN_GEN_AFTER_INSN:
 case PLUGIN_GEN_FROM_TB:
-tcg_gen_plugin_cb(from);
-break;
 case PLUGIN_GEN_FROM_INSN:
-/*
- * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being
- * the first callback of an instruction
- */
-gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
-gen_empty_mem_helper);
-gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg);
-gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg);
-gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
+tcg_gen_plugin_cb(from);
 break;
 default:
 g_assert_not_reached();
@@ -374,18 +318,6 @@ static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, 
uint32_t v)
 return op;
 }
 
-static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
-{
-if (UINTPTR_MAX == UINT32_MAX) {
-/* st_i32 */
-op = copy_op(begin_op, op, INDEX_op_st_i32);
-} else {
-/* st_i64 */
-op = copy_st_i64(begin_op, op);
-}
-return op;
-}
-
 static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *func, int *cb_idx)
 {
 TCGOp *old_op;
@@ -409,32 +341,6 @@ static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void 
*func, int *cb_idx)
 return op;
 }
 
-/*
- * When we append/replace ops here we are sensitive to changing patterns of
- * TCGOps generated by the tcg_gen_FOO calls when we generated the
- * empty callbacks. This will assert very quickly in a debug build as
- * we assert the ops we are replacing are the correct ones.
- */
-static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
-  TCGOp *begin_op, TCGOp *op, int *cb_idx)
-{
-/* const_ptr */
-op = copy_const_ptr(_op, op, cb->userp);
-
-/* copy the ld_i32, but note that we only have to copy it once */
-if (*cb_idx == -1) {
-op = copy_op(_op, op, INDEX_op_ld_i32);
-} else {
-begin_op = QTAILQ_NEXT(begin_op, link);
-tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
-}
-
-/* call */
-op = copy_call(_op, op, cb->regular.f.vcpu_udata, cb_idx);
-
-return op;
-}
-
 static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
TCGOp *begin_op, TCGOp *op,
  

[PATCH 09/22] plugins: Add PLUGIN_GEN_AFTER_TB

2024-03-15 Thread Richard Henderson
Delay test of plugin_tb->mem_helper until the inject pass.

Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-gen.c | 37 -
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index f92aa80510..aa74e580bd 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -75,6 +75,7 @@ enum plugin_gen_from {
 PLUGIN_GEN_FROM_INSN,
 PLUGIN_GEN_FROM_MEM,
 PLUGIN_GEN_AFTER_INSN,
+PLUGIN_GEN_AFTER_TB,
 PLUGIN_GEN_N_FROMS,
 };
 
@@ -615,20 +616,9 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb 
*ptb,
 /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
 void plugin_gen_disable_mem_helpers(void)
 {
-/*
- * We could emit the clearing unconditionally and be done. However, this 
can
- * be wasteful if for instance plugins don't track memory accesses, or if
- * most TBs don't use helpers. Instead, emit the clearing iff the TB calls
- * helpers that might access guest memory.
- *
- * Note: we do not reset plugin_tb->mem_helper here; a TB might have 
several
- * exit points, and we want to emit the clearing from all of them.
- */
-if (!tcg_ctx->plugin_tb->mem_helper) {
-return;
+if (tcg_ctx->plugin_insn) {
+tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB);
 }
-tcg_gen_st_ptr(tcg_constant_ptr(NULL), tcg_env,
-   offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, 
env));
 }
 
 static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
@@ -679,14 +669,11 @@ static void plugin_gen_enable_mem_helper(struct 
qemu_plugin_tb *ptb,
 inject_mem_enable_helper(ptb, insn, begin_op);
 }
 
-static void gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
-   struct qemu_plugin_insn *insn)
+static void gen_disable_mem_helper(void)
 {
-if (insn->mem_helper) {
-tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
-   offsetof(CPUState, plugin_mem_cbs) -
-   offsetof(ArchCPU, env));
-}
+tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
+   offsetof(CPUState, plugin_mem_cbs) -
+   offsetof(ArchCPU, env));
 }
 
 static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
@@ -812,9 +799,17 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 tcg_ctx->emit_before_op = op;
 
 switch (from) {
+case PLUGIN_GEN_AFTER_TB:
+if (plugin_tb->mem_helper) {
+gen_disable_mem_helper();
+}
+break;
+
 case PLUGIN_GEN_AFTER_INSN:
 assert(insn != NULL);
-gen_disable_mem_helper(plugin_tb, insn);
+if (insn->mem_helper) {
+gen_disable_mem_helper();
+}
 break;
 
 case PLUGIN_GEN_FROM_TB:
-- 
2.34.1




[PATCH 14/22] tcg: Remove INDEX_op_plugin_cb_{start,end}

2024-03-15 Thread Richard Henderson
These opcodes are no longer used.

Signed-off-by: Richard Henderson 
---
 include/tcg/tcg-op-common.h |  2 --
 include/tcg/tcg-opc.h   |  2 --
 accel/tcg/plugin-gen.c  | 18 --
 tcg/tcg-op.c| 10 --
 4 files changed, 32 deletions(-)

diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index 72b80b20d0..009e2778c5 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -76,8 +76,6 @@ void tcg_gen_lookup_and_goto_ptr(void);
 
 void tcg_gen_plugin_cb(unsigned from);
 void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo);
-void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr);
-void tcg_gen_plugin_cb_end(void);
 
 /* 32 bit ops */
 
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index be9e36e386..546eb49c11 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -199,8 +199,6 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 
 DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)
 DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT)
-DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT)
-DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT)
 
 /* Replicate ld/st ops for 32 and 64-bit guest addresses. */
 DEF(qemu_ld_a32_i32, 1, 1, 1,
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index b5261edc38..c8f0e0ecaa 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -58,29 +58,11 @@
 # define CONFIG_SOFTMMU_GATE 0
 #endif
 
-/*
- * plugin_cb_start TCG op args[]:
- * 0: enum plugin_gen_from
- * 1: enum plugin_gen_cb
- * 2: set to 1 for mem callback that is a write, 0 otherwise.
- */
-
 enum plugin_gen_from {
 PLUGIN_GEN_FROM_TB,
 PLUGIN_GEN_FROM_INSN,
 PLUGIN_GEN_AFTER_INSN,
 PLUGIN_GEN_AFTER_TB,
-PLUGIN_GEN_N_FROMS,
-};
-
-enum plugin_gen_cb {
-PLUGIN_GEN_CB_UDATA,
-PLUGIN_GEN_CB_UDATA_R,
-PLUGIN_GEN_CB_INLINE,
-PLUGIN_GEN_CB_MEM,
-PLUGIN_GEN_ENABLE_MEM_HELPER,
-PLUGIN_GEN_DISABLE_MEM_HELPER,
-PLUGIN_GEN_N_CBS,
 };
 
 static void plugin_gen_empty_callback(enum plugin_gen_from from)
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 0ae12fa49d..eff3728622 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -322,16 +322,6 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo)
 tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo);
 }
 
-void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr)
-{
-tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr);
-}
-
-void tcg_gen_plugin_cb_end(void)
-{
-tcg_emit_op(INDEX_op_plugin_cb_end, 0);
-}
-
 /* 32 bit ops */
 
 void tcg_gen_discard_i32(TCGv_i32 arg)
-- 
2.34.1




[PATCH 01/22] tcg: Add TCGContext.emit_before_op

2024-03-15 Thread Richard Henderson
Allow operations to be emitted via normal expanders
into the middle of the opcode stream.

Signed-off-by: Richard Henderson 
---
 include/tcg/tcg.h |  1 +
 tcg/tcg.c | 14 --
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index 451f3fec41..e9d05f40b0 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -552,6 +552,7 @@ struct TCGContext {
 
 QTAILQ_HEAD(, TCGOp) ops, free_ops;
 QSIMPLEQ_HEAD(, TCGLabel) labels;
+TCGOp *emit_before_op;
 
 /* Tells which temporary holds a given register.
It does not take into account fixed registers */
diff --git a/tcg/tcg.c b/tcg/tcg.c
index d6670237fb..0c0bb9d169 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1521,6 +1521,7 @@ void tcg_func_start(TCGContext *s)
 
 QTAILQ_INIT(>ops);
 QTAILQ_INIT(>free_ops);
+s->emit_before_op = NULL;
 QSIMPLEQ_INIT(>labels);
 
 tcg_debug_assert(s->addr_type == TCG_TYPE_I32 ||
@@ -2332,7 +2333,11 @@ static void tcg_gen_callN(TCGHelperInfo *info, TCGTemp 
*ret, TCGTemp **args)
 op->args[pi++] = (uintptr_t)info;
 tcg_debug_assert(pi == total_args);
 
-QTAILQ_INSERT_TAIL(_ctx->ops, op, link);
+if (tcg_ctx->emit_before_op) {
+QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link);
+} else {
+QTAILQ_INSERT_TAIL(_ctx->ops, op, link);
+}
 
 tcg_debug_assert(n_extend < ARRAY_SIZE(extend_free));
 for (i = 0; i < n_extend; ++i) {
@@ -3215,7 +3220,12 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs)
 TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs)
 {
 TCGOp *op = tcg_op_alloc(opc, nargs);
-QTAILQ_INSERT_TAIL(_ctx->ops, op, link);
+
+if (tcg_ctx->emit_before_op) {
+QTAILQ_INSERT_BEFORE(tcg_ctx->emit_before_op, op, link);
+} else {
+QTAILQ_INSERT_TAIL(_ctx->ops, op, link);
+}
 return op;
 }
 
-- 
2.34.1




[PATCH 11/22] plugins: Use emit_before_op for PLUGIN_GEN_FROM_MEM

2024-03-15 Thread Richard Henderson
Introduce a new plugin_mem_cb op to hold the address temp
and meminfo computed by tcg-op-ldst.c.  Because this now
has its own opcode, we no longer need PLUGIN_GEN_FROM_MEM.

Signed-off-by: Richard Henderson 
---
 include/exec/plugin-gen.h   |   4 -
 include/tcg/tcg-op-common.h |   1 +
 include/tcg/tcg-opc.h   |   1 +
 accel/tcg/plugin-gen.c  | 408 
 tcg/tcg-op-ldst.c   |   6 +-
 tcg/tcg-op.c|   5 +
 6 files changed, 54 insertions(+), 371 deletions(-)

diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
index c4552b5061..f333f33198 100644
--- a/include/exec/plugin-gen.h
+++ b/include/exec/plugin-gen.h
@@ -25,7 +25,6 @@ void plugin_gen_insn_start(CPUState *cpu, const struct 
DisasContextBase *db);
 void plugin_gen_insn_end(void);
 
 void plugin_gen_disable_mem_helpers(void);
-void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info);
 
 #else /* !CONFIG_PLUGIN */
 
@@ -48,9 +47,6 @@ static inline void plugin_gen_tb_end(CPUState *cpu, size_t 
num_insns)
 static inline void plugin_gen_disable_mem_helpers(void)
 { }
 
-static inline void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
-{ }
-
 #endif /* CONFIG_PLUGIN */
 
 #endif /* QEMU_PLUGIN_GEN_H */
diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index 9de5a7f280..72b80b20d0 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -75,6 +75,7 @@ void tcg_gen_goto_tb(unsigned idx);
 void tcg_gen_lookup_and_goto_ptr(void);
 
 void tcg_gen_plugin_cb(unsigned from);
+void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo);
 void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr);
 void tcg_gen_plugin_cb_end(void);
 
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index 3b7cb2bce1..be9e36e386 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -198,6 +198,7 @@ DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 
 DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)
+DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT)
 DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT)
 DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT)
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 4785838eca..be7fd548cc 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -73,7 +73,6 @@
 enum plugin_gen_from {
 PLUGIN_GEN_FROM_TB,
 PLUGIN_GEN_FROM_INSN,
-PLUGIN_GEN_FROM_MEM,
 PLUGIN_GEN_AFTER_INSN,
 PLUGIN_GEN_AFTER_TB,
 PLUGIN_GEN_N_FROMS,
@@ -104,60 +103,6 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
 void *userdata)
 { }
 
-/*
- * For now we only support addi_i64.
- * When we support more ops, we can generate one empty inline cb for each.
- */
-static void gen_empty_inline_cb(void)
-{
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr();
-TCGv_i64 val = tcg_temp_ebb_new_i64();
-TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-/* second operand will be replaced by immediate value */
-tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index);
-tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index);
-
-tcg_gen_movi_ptr(ptr, 0);
-tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr);
-tcg_gen_ld_i64(val, ptr, 0);
-/* second operand will be replaced by immediate value */
-tcg_gen_add_i64(val, val, val);
-
-tcg_gen_st_i64(val, ptr, 0);
-tcg_temp_free_ptr(ptr);
-tcg_temp_free_i64(val);
-tcg_temp_free_ptr(cpu_index_as_ptr);
-tcg_temp_free_i32(cpu_index);
-}
-
-static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
-{
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_i32 meminfo = tcg_temp_ebb_new_i32();
-TCGv_ptr udata = tcg_temp_ebb_new_ptr();
-
-tcg_gen_movi_i32(meminfo, info);
-tcg_gen_movi_ptr(udata, 0);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-
-gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata);
-
-tcg_temp_free_ptr(udata);
-tcg_temp_free_i32(meminfo);
-tcg_temp_free_i32(cpu_index);
-}
-
-static void gen_plugin_cb_start(enum plugin_gen_from from,
-enum plugin_gen_cb type, unsigned wr)
-{
-tcg_gen_plugin_cb_start(from, type, wr);
-}
-
 static void plugin_gen_empty_callback(enum plugin_gen_from from)
 {
 switch (from) {
@@ -171,278 +116,6 @@ static void plugin_gen_empty_callback(enum 
plugin_gen_from from)
 }
 }
 
-void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
-{
-enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
-
-gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw);
-gen_empty_mem_cb(addr, info);
-tcg_gen_plugin_cb_end();
-
-

[PATCH 12/22] plugins: Remove plugin helpers

2024-03-15 Thread Richard Henderson
These placeholder helpers are no longer required.

Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-helpers.h |  5 -
 include/exec/helper-gen-common.h   |  4 
 include/exec/helper-proto-common.h |  4 
 accel/tcg/plugin-gen.c | 20 
 4 files changed, 33 deletions(-)
 delete mode 100644 accel/tcg/plugin-helpers.h

diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h
deleted file mode 100644
index 11796436f3..00
--- a/accel/tcg/plugin-helpers.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifdef CONFIG_PLUGIN
-DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_wg, TCG_CALL_NO_WG | 
TCG_CALL_PLUGIN, void, i32, ptr)
-DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_rwg, TCG_CALL_NO_RWG | 
TCG_CALL_PLUGIN, void, i32, ptr)
-DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, 
void, i32, i32, i64, ptr)
-#endif
diff --git a/include/exec/helper-gen-common.h b/include/exec/helper-gen-common.h
index 5d6d78a625..834590dc4e 100644
--- a/include/exec/helper-gen-common.h
+++ b/include/exec/helper-gen-common.h
@@ -11,8 +11,4 @@
 #include "exec/helper-gen.h.inc"
 #undef  HELPER_H
 
-#define HELPER_H "accel/tcg/plugin-helpers.h"
-#include "exec/helper-gen.h.inc"
-#undef  HELPER_H
-
 #endif /* HELPER_GEN_COMMON_H */
diff --git a/include/exec/helper-proto-common.h 
b/include/exec/helper-proto-common.h
index 8b67170a22..16782ef46c 100644
--- a/include/exec/helper-proto-common.h
+++ b/include/exec/helper-proto-common.h
@@ -13,8 +13,4 @@
 #include "exec/helper-proto.h.inc"
 #undef  HELPER_H
 
-#define HELPER_H "accel/tcg/plugin-helpers.h"
-#include "exec/helper-proto.h.inc"
-#undef  HELPER_H
-
 #endif /* HELPER_PROTO_COMMON_H */
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index be7fd548cc..b5261edc38 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -51,11 +51,6 @@
 #include "exec/exec-all.h"
 #include "exec/plugin-gen.h"
 #include "exec/translator.h"
-#include "exec/helper-proto-common.h"
-
-#define HELPER_H  "accel/tcg/plugin-helpers.h"
-#include "exec/helper-info.c.inc"
-#undef  HELPER_H
 
 #ifdef CONFIG_SOFTMMU
 # define CONFIG_SOFTMMU_GATE 1
@@ -88,21 +83,6 @@ enum plugin_gen_cb {
 PLUGIN_GEN_N_CBS,
 };
 
-/*
- * These helpers are stubs that get dynamically switched out for calls
- * direct to the plugin if they are subscribed to.
- */
-void HELPER(plugin_vcpu_udata_cb_no_wg)(uint32_t cpu_index, void *udata)
-{ }
-
-void HELPER(plugin_vcpu_udata_cb_no_rwg)(uint32_t cpu_index, void *udata)
-{ }
-
-void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
-qemu_plugin_meminfo_t info, uint64_t vaddr,
-void *userdata)
-{ }
-
 static void plugin_gen_empty_callback(enum plugin_gen_from from)
 {
 switch (from) {
-- 
2.34.1




[PATCH 03/22] tcg: Pass function pointer to tcg_gen_call*

2024-03-15 Thread Richard Henderson
For normal helpers, read the function pointer from the
structure earlier.  For plugins, this will allow the
function pointer to come from elsewhere.

Signed-off-by: Richard Henderson 
---
 include/tcg/tcg.h | 21 +---
 include/exec/helper-gen.h.inc | 24 ---
 tcg/tcg.c | 45 +++
 3 files changed, 52 insertions(+), 38 deletions(-)

diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
index a6e7df146a..95a7f4d010 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -847,19 +847,22 @@ typedef struct TCGTargetOpDef {
 
 bool tcg_op_supported(TCGOpcode op);
 
-void tcg_gen_call0(TCGHelperInfo *, TCGTemp *ret);
-void tcg_gen_call1(TCGHelperInfo *, TCGTemp *ret, TCGTemp *);
-void tcg_gen_call2(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *);
-void tcg_gen_call3(TCGHelperInfo *, TCGTemp *ret, TCGTemp *,
+void tcg_gen_call0(void *func, TCGHelperInfo *, TCGTemp *ret);
+void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *);
+void tcg_gen_call2(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *);
-void tcg_gen_call4(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
-   TCGTemp *, TCGTemp *);
-void tcg_gen_call5(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call3(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *);
-void tcg_gen_call6(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call4(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *);
-void tcg_gen_call7(TCGHelperInfo *, TCGTemp *ret, TCGTemp *, TCGTemp *,
+void tcg_gen_call5(void *func, TCGHelperInfo *, TCGTemp *ret,
TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *);
+void tcg_gen_call6(void *func, TCGHelperInfo *, TCGTemp *ret,
+   TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *,
+   TCGTemp *, TCGTemp *);
+void tcg_gen_call7(void *func, TCGHelperInfo *, TCGTemp *ret,
+   TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *,
+   TCGTemp *, TCGTemp *, TCGTemp *);
 
 TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs);
 void tcg_op_remove(TCGContext *s, TCGOp *op);
diff --git a/include/exec/helper-gen.h.inc b/include/exec/helper-gen.h.inc
index c009641517..f7eb59b6c1 100644
--- a/include/exec/helper-gen.h.inc
+++ b/include/exec/helper-gen.h.inc
@@ -14,7 +14,8 @@
 extern TCGHelperInfo glue(helper_info_, name);  \
 static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret))\
 {   \
-tcg_gen_call0((helper_info_, name), dh_retvar(ret));   \
+tcg_gen_call0(glue(helper_info_,name).func, \
+  (helper_info_,name), dh_retvar(ret));\
 }
 
 #define DEF_HELPER_FLAGS_1(name, flags, ret, t1)\
@@ -22,7 +23,8 @@ extern TCGHelperInfo glue(helper_info_, name);
  \
 static inline void glue(gen_helper_, name)(dh_retvar_decl(ret)  \
 dh_arg_decl(t1, 1)) \
 {   \
-tcg_gen_call1((helper_info_, name), dh_retvar(ret),\
+tcg_gen_call1(glue(helper_info_,name).func, \
+  (helper_info_,name), dh_retvar(ret), \
   dh_arg(t1, 1));   \
 }
 
@@ -31,7 +33,8 @@ extern TCGHelperInfo glue(helper_info_, name);
  \
 static inline void glue(gen_helper_, name)(dh_retvar_decl(ret)  \
 dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \
 {   \
-tcg_gen_call2((helper_info_, name), dh_retvar(ret),\
+tcg_gen_call2(glue(helper_info_,name).func, \
+  (helper_info_,name), dh_retvar(ret), \
   dh_arg(t1, 1), dh_arg(t2, 2));\
 }
 
@@ -40,7 +43,8 @@ extern TCGHelperInfo glue(helper_info_, name);
  \
 static inline void glue(gen_helper_, name)(dh_retvar_decl(ret)  \
 dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \
 {   \
-tcg_gen_call3((helper_info_, name), dh_retvar(ret),\
+tcg_gen_call3(glue(helper_info_,name).func, \
+  (helper_info_,name), dh_retvar(ret), \
   dh_arg(t1, 1), dh_arg(t2, 2), dh_arg(t3, 3)); \
 }
 
@@ -50,7 +54,8 @@ static inline void glue(gen_helper_, 
name)(dh_retvar_decl(ret)  \
 dh_arg_decl(t1, 1), 

[PATCH 08/22] plugins: Use emit_before_op for PLUGIN_GEN_FROM_TB

2024-03-15 Thread Richard Henderson
By having the qemu_plugin_cb_flags be recorded in the TCGHelperInfo,
we no longer need to distinguish PLUGIN_CB_REGULAR from
PLUGIN_CB_REGULAR_R, so place all TB callbacks in the same queue.

Signed-off-by: Richard Henderson 
---
 accel/tcg/plugin-gen.c | 96 +-
 plugins/api.c  |  6 +--
 2 files changed, 58 insertions(+), 44 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8fa342b425..f92aa80510 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -207,6 +207,7 @@ static void plugin_gen_empty_callback(enum plugin_gen_from 
from)
 {
 switch (from) {
 case PLUGIN_GEN_AFTER_INSN:
+case PLUGIN_GEN_FROM_TB:
 tcg_gen_plugin_cb(from);
 break;
 case PLUGIN_GEN_FROM_INSN:
@@ -216,8 +217,6 @@ static void plugin_gen_empty_callback(enum plugin_gen_from 
from)
  */
 gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
 gen_empty_mem_helper);
-/* fall through */
-case PLUGIN_GEN_FROM_TB:
 gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg);
 gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg);
 gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
@@ -632,24 +631,6 @@ void plugin_gen_disable_mem_helpers(void)
offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, 
env));
 }
 
-static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
-TCGOp *begin_op)
-{
-inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
-}
-
-static void plugin_gen_tb_udata_r(const struct qemu_plugin_tb *ptb,
-  TCGOp *begin_op)
-{
-inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR_R], begin_op);
-}
-
-static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
- TCGOp *begin_op)
-{
-inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok);
-}
-
 static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
   TCGOp *begin_op, int insn_idx)
 {
@@ -708,6 +689,41 @@ static void gen_disable_mem_helper(struct qemu_plugin_tb 
*ptb,
 }
 }
 
+static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL,
+  tcgv_i32_temp(cpu_index),
+  tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+tcg_temp_free_i32(cpu_index);
+}
+
+static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
+{
+GArray *arr = cb->inline_insn.entry.score->data;
+size_t offset = cb->inline_insn.entry.offset;
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
+
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
+tcg_gen_ext_i32_ptr(ptr, cpu_index);
+tcg_temp_free_i32(cpu_index);
+
+tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
+tcg_gen_ld_i64(val, ptr, offset);
+tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
+tcg_gen_st_i64(val, ptr, offset);
+
+tcg_temp_free_i64(val);
+tcg_temp_free_ptr(ptr);
+}
+
 /* #define DEBUG_PLUGIN_GEN_OPS */
 static void pr_ops(void)
 {
@@ -786,6 +802,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 {
 enum plugin_gen_from from = op->args[0];
 struct qemu_plugin_insn *insn = NULL;
+const GArray *cbs;
+int i, n;
 
 if (insn_idx >= 0) {
 insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
@@ -798,6 +816,25 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 assert(insn != NULL);
 gen_disable_mem_helper(plugin_tb, insn);
 break;
+
+case PLUGIN_GEN_FROM_TB:
+assert(insn == NULL);
+
+cbs = plugin_tb->cbs[PLUGIN_CB_REGULAR];
+for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
+struct qemu_plugin_dyn_cb *cb =
+_array_index(cbs, struct qemu_plugin_dyn_cb, i);
+gen_udata_cb(cb);
+}
+
+cbs = plugin_tb->cbs[PLUGIN_CB_INLINE];
+for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
+struct qemu_plugin_dyn_cb *cb =
+_array_index(cbs, struct qemu_plugin_dyn_cb, i);
+gen_inline_cb(cb);
+}
+break;
+
 default:
 g_assert_not_reached();
 }
@@ -813,25 +850,6 @@ static void 

[PATCH 07/22] plugins: Use emit_before_op for PLUGIN_GEN_AFTER_INSN

2024-03-15 Thread Richard Henderson
Introduce a new plugin_cb op and migrate one operation.
By using emit_before_op, we do not need to emit opcodes
early and modify them later -- we can simply emit the
final set of opcodes once.

Signed-off-by: Richard Henderson 
---
 include/tcg/tcg-op-common.h |  1 +
 include/tcg/tcg-opc.h   |  1 +
 accel/tcg/plugin-gen.c  | 74 +
 tcg/tcg-op.c|  5 +++
 4 files changed, 50 insertions(+), 31 deletions(-)

diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index 2d932a515e..9de5a7f280 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -74,6 +74,7 @@ void tcg_gen_goto_tb(unsigned idx);
  */
 void tcg_gen_lookup_and_goto_ptr(void);
 
+void tcg_gen_plugin_cb(unsigned from);
 void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr);
 void tcg_gen_plugin_cb_end(void);
 
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
index b80227fa1c..3b7cb2bce1 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -197,6 +197,7 @@ DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 
+DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)
 DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT)
 DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT)
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index c56f104aee..8fa342b425 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -207,8 +207,7 @@ static void plugin_gen_empty_callback(enum plugin_gen_from 
from)
 {
 switch (from) {
 case PLUGIN_GEN_AFTER_INSN:
-gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
-gen_empty_mem_helper);
+tcg_gen_plugin_cb(from);
 break;
 case PLUGIN_GEN_FROM_INSN:
 /*
@@ -614,16 +613,6 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb 
*ptb,
 inject_mem_helper(begin_op, arr);
 }
 
-static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
-  TCGOp *begin_op)
-{
-if (likely(!plugin_insn->mem_helper)) {
-rm_ops(begin_op);
-return;
-}
-inject_mem_helper(begin_op, NULL);
-}
-
 /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
 void plugin_gen_disable_mem_helpers(void)
 {
@@ -709,11 +698,14 @@ static void plugin_gen_enable_mem_helper(struct 
qemu_plugin_tb *ptb,
 inject_mem_enable_helper(ptb, insn, begin_op);
 }
 
-static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
-  TCGOp *begin_op, int insn_idx)
+static void gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
+   struct qemu_plugin_insn *insn)
 {
-struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
-inject_mem_disable_helper(insn, begin_op);
+if (insn->mem_helper) {
+tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
+   offsetof(CPUState, plugin_mem_cbs) -
+   offsetof(ArchCPU, env));
+}
 }
 
 /* #define DEBUG_PLUGIN_GEN_OPS */
@@ -772,16 +764,49 @@ static void pr_ops(void)
 
 static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
 {
-TCGOp *op;
+TCGOp *op, *next;
 int insn_idx = -1;
 
 pr_ops();
 
-QTAILQ_FOREACH(op, _ctx->ops, link) {
+/*
+ * While injecting code, we cannot afford to reuse any ebb temps
+ * that might be live within the existing opcode stream.
+ * The simplest solution is to release them all and create new.
+ */
+memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps));
+
+QTAILQ_FOREACH_SAFE(op, _ctx->ops, link, next) {
 switch (op->opc) {
 case INDEX_op_insn_start:
 insn_idx++;
 break;
+
+case INDEX_op_plugin_cb:
+{
+enum plugin_gen_from from = op->args[0];
+struct qemu_plugin_insn *insn = NULL;
+
+if (insn_idx >= 0) {
+insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
+}
+
+tcg_ctx->emit_before_op = op;
+
+switch (from) {
+case PLUGIN_GEN_AFTER_INSN:
+assert(insn != NULL);
+gen_disable_mem_helper(plugin_tb, insn);
+break;
+default:
+g_assert_not_reached();
+}
+
+tcg_ctx->emit_before_op = NULL;
+tcg_op_remove(tcg_ctx, op);
+break;
+}
+
 case INDEX_op_plugin_cb_start:
 {
 enum plugin_gen_from from = op->args[0];
@@ -846,19 +871,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
 
 break;
 }
-case PLUGIN_GEN_AFTER_INSN:
-{
-g_assert(insn_idx >= 0);
-
-switch (type) {
-case 

[PATCH 00/22] plugins: Rewrite plugin code generation

2024-03-15 Thread Richard Henderson
As I mooted with Pierrick earlier this week:

(1) Add a (trivial) mechanism for emitting code into
the middle of the opcode sequence: tcg_ctx->emit_before_op.

(2) Rip out all of the "empty" generation and "copy"
to modify those sequences.  Replace with regular code
generation once we know what values to place.

There's probably still more cleanup that could be done:

There seems to be a lot of artificial separation between
plugins and the rest of the code base, even between
plugins/api.c and plugins/core.c.

I suspect that all of plugins could be moved into the
build-once buckets.


r~


Richard Henderson (22):
  tcg: Add TCGContext.emit_before_op
  tcg: Make tcg/helper-info.h self-contained
  tcg: Pass function pointer to tcg_gen_call*
  plugins: Zero new qemu_plugin_dyn_cb entries
  plugins: Move function pointer in qemu_plugin_dyn_cb
  plugins: Create TCGHelperInfo for all out-of-line callbacks
  plugins: Use emit_before_op for PLUGIN_GEN_AFTER_INSN
  plugins: Use emit_before_op for PLUGIN_GEN_FROM_TB
  plugins: Add PLUGIN_GEN_AFTER_TB
  plugins: Use emit_before_op for PLUGIN_GEN_FROM_INSN
  plugins: Use emit_before_op for PLUGIN_GEN_FROM_MEM
  plugins: Remove plugin helpers
  tcg: Remove TCG_CALL_PLUGIN
  tcg: Remove INDEX_op_plugin_cb_{start,end}
  plugins: Simplify callback queues
  plugins: Introduce PLUGIN_CB_MEM_REGULAR
  plugins: Replace pr_ops with a proper debug dump flag
  plugins: Split out common cb expanders
  plugins: Merge qemu_plugin_tb_insn_get to plugin-gen.c
  plugins: Move qemu_plugin_insn_cleanup_fn to tcg.c
  plugins: Inline plugin_gen_empty_callback
  plugins: Update the documentation block for plugin-gen.c

 accel/tcg/plugin-helpers.h |5 -
 include/exec/helper-gen-common.h   |4 -
 include/exec/helper-proto-common.h |4 -
 include/exec/plugin-gen.h  |4 -
 include/qemu/log.h |1 +
 include/qemu/plugin.h  |   79 +--
 include/tcg/helper-info.h  |3 +
 include/tcg/tcg-op-common.h|4 +-
 include/tcg/tcg-opc.h  |4 +-
 include/tcg/tcg.h  |   27 +-
 include/exec/helper-gen.h.inc  |   24 +-
 accel/tcg/plugin-gen.c | 1008 +++-
 plugins/api.c  |   26 +-
 plugins/core.c |   61 +-
 tcg/tcg-op-ldst.c  |6 +-
 tcg/tcg-op.c   |8 +-
 tcg/tcg.c  |  104 ++-
 util/log.c |4 +
 18 files changed, 424 insertions(+), 952 deletions(-)
 delete mode 100644 accel/tcg/plugin-helpers.h

-- 
2.34.1




Re: [PATCH V4 1/1] target/loongarch: Fixed tlb huge page loading issue

2024-03-15 Thread lixianglai



在 2024/3/16 上午1:06, Richard Henderson 写道:
> On 3/14/24 23:01, lixianglai wrote:
>> Hi Richard :
>>
>>> On 3/14/24 20:22, lixianglai wrote:
 Hi Richard:
> On 3/13/24 15:33, Xianglai Li wrote:
>> +    if (unlikely((level == 0) || (level > 4))) {
>> +    return base;
>> +    }
>>> ...
> Perhaps it would be worthwhile to add another for the level==0 or > 4 
> case above?
>
 A normal level 4 page table should not print an error log,

 only if a level 4 page is large, so we should put it in

  if (FIELD_EX64(base, TLBENTRY, HUGE)) {
  if (unlikely(level == 4)) {
  qemu_log_mask(LOG_GUEST_ERROR,
    "Attempted use of level %lu huge page\n", 
 level);
  }

  if (FIELD_EX64(base, TLBENTRY, LEVEL)) {
  return base;
  } else {
  return  FIELD_DP64(base, TLBENTRY, LEVEL, level);
  }
  }
>>>
>>> A level 5 page table is not normal, nor is a level 0 lddir.
>>>
>>
>> We communicate with the hardware guys that the behavior above level 4 and 
>> lddir 0 is undefined behavior.
>>
>> The result of our test on 3A5000 is that it has no any effect on "base",
>>
>> however in future chips the behavior may change since it may support 5-level 
>> page table and width for level[13:14] may change also.
>>
>>
>> So in this context,I am not sure which level to use to print logs,
>>
>> which content to print, and where to add these prints,
>>
>> any more detailed advice?
>
> Yes, right there in the IF that I quoted at the top.
> What I was trying to spell out is
>
>     if (unlikely(level == 0 || level > 4)) {
>     qemu_log_mask(LOG_GUEST_ERROR,
>   "Attepted LDDIR with level %"PRId64"\n", level);
>     return base;
>     }
>
Thank you very much, I will modify it in V6 version of patch.

Thanks,
Xianglai.
>
> r~




Re: [PATCH-for-9.1 12/12] exec/poison: Poison CONFIG_SOFTMMU again

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Now that the confusion around SOFTMMU vs SYSTEM emulation
was clarified, we can restore the CONFIG_SOFTMMU poison
pragma.

This reverts commit d31b84041d4353ef310ffde23c87b78c2aa32ead
("exec/poison: Do not poison CONFIG_SOFTMMU").

Signed-off-by: Philippe Mathieu-Daudé 
---
  include/exec/poison.h | 1 +
  scripts/make-config-poison.sh | 1 -
  2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/exec/poison.h b/include/exec/poison.h
index 1ea5633eb3..fbec710f6c 100644
--- a/include/exec/poison.h
+++ b/include/exec/poison.h
@@ -84,6 +84,7 @@
  #pragma GCC poison CONFIG_HVF
  #pragma GCC poison CONFIG_LINUX_USER
  #pragma GCC poison CONFIG_KVM
+#pragma GCC poison CONFIG_SOFTMMU
  #pragma GCC poison CONFIG_WHPX
  #pragma GCC poison CONFIG_XEN
  
diff --git a/scripts/make-config-poison.sh b/scripts/make-config-poison.sh

index 2b36907e23..6ef5580f84 100755
--- a/scripts/make-config-poison.sh
+++ b/scripts/make-config-poison.sh
@@ -9,7 +9,6 @@ fi
  exec sed -n \
-e' /CONFIG_TCG/d' \
-e '/CONFIG_USER_ONLY/d' \
-  -e '/CONFIG_SOFTMMU/d' \
-e '/^#define / {' \
-e's///' \
-e's/ .*//' \


Reviewed-by: Richard Henderson 

r~



Re: [PATCH-for-9.1 06/12] tcg/sparc64: Check for USER_ONLY definition instead of SOFTMMU one

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Since we *might* have user emulation with softmmu,
replace the system emulation check by !user emulation one.

Signed-off-by: Philippe Mathieu-Daudé 
---
  tcg/sparc64/tcg-target.c.inc | 8 
  1 file changed, 4 insertions(+), 4 deletions(-)


Having read further, the ultimate goal is worthwhile.
It'll be easy to turn these into runtime tests when we get there.

Reviewed-by: Richard Henderson 


r~



Re: [PATCH-for-9.1 11/12] tcg: Remove unused CONFIG_SOFTMMU definition from libtcg_system.fa

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Signed-off-by: Philippe Mathieu-Daudé 
---
  tcg/meson.build | 1 -
  1 file changed, 1 deletion(-)

diff --git a/tcg/meson.build b/tcg/meson.build
index 8251589fd4..b5246676c6 100644
--- a/tcg/meson.build
+++ b/tcg/meson.build
@@ -42,7 +42,6 @@ user_ss.add(tcg_user)
  libtcg_system = static_library('tcg_system',
  tcg_ss.sources() + genh,
  name_suffix: 'fa',
-c_args: '-DCONFIG_SOFTMMU',
  build_by_default: false)
  
  tcg_system = declare_dependency(link_with: libtcg_system,


Reviewed-by: Richard Henderson 

r~



Re: [PATCH-for-9.1 10/12] exec/cpu-defs: Restrict SOFTMMU specific definitions to accel/tcg/

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

CPU_TLB_foo definitions are specific to SoftMMU and
only used in accel/tcg/.

Signed-off-by: Philippe Mathieu-Daudé 
---
  accel/tcg/internal-target.h | 26 ++
  include/exec/cpu-defs.h | 26 --
  2 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h
index b22b29c461..9b5cc9168b 100644
--- a/accel/tcg/internal-target.h
+++ b/accel/tcg/internal-target.h
@@ -12,6 +12,32 @@
  #include "exec/exec-all.h"
  #include "exec/translate-all.h"
  
+#if defined(CONFIG_SOFTMMU) && defined(CONFIG_TCG)


I see this is moved intact, but drop the CONFIG_TCG ifdef within accel/tcg/.
With that change,

Reviewed-by: Richard Henderson 

r~



Re: [PATCH-for-9.1 09/12] accel/tcg/internal: Check for USER_ONLY definition instead of SOFTMMU

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Since we *might* have user emulation with softmmu,
replace the system emulation check by !user emulation one.

Signed-off-by: Philippe Mathieu-Daudé 
---
  accel/tcg/internal-target.h | 6 +++---
  accel/tcg/tb-hash.h | 4 ++--
  accel/tcg/tcg-all.c | 2 +-
  3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h
index 4e36cf858e..b22b29c461 100644
--- a/accel/tcg/internal-target.h
+++ b/accel/tcg/internal-target.h
@@ -24,7 +24,7 @@
  #define assert_memory_lock()
  #endif
  
-#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)

+#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG)
  void assert_no_pages_locked(void);
  #else
  static inline void assert_no_pages_locked(void) { }
@@ -62,12 +62,12 @@ void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t);
  void tb_unlock_pages(TranslationBlock *);
  #endif
  
-#ifdef CONFIG_SOFTMMU

+#ifndef CONFIG_USER_ONLY
  void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
 unsigned size,
 uintptr_t retaddr);
  G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
-#endif /* CONFIG_SOFTMMU */
+#endif /* !CONFIG_USER_ONLY */
  
  TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc,

uint64_t cs_base, uint32_t flags,


Ok.



diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h
index a0c61f25cd..45a484ce82 100644
--- a/accel/tcg/tb-hash.h
+++ b/accel/tcg/tb-hash.h
@@ -25,7 +25,7 @@
  #include "qemu/xxhash.h"
  #include "tb-jmp-cache.h"
  
-#ifdef CONFIG_SOFTMMU

+#ifndef CONFIG_USER_ONLY
  
  /* Only the bottom TB_JMP_PAGE_BITS of the jump cache hash bits vary for

 addresses on the same page.  The top bits are the same.  This allows
@@ -58,7 +58,7 @@ static inline unsigned int tb_jmp_cache_hash_func(vaddr pc)
  return (pc ^ (pc >> TB_JMP_CACHE_BITS)) & (TB_JMP_CACHE_SIZE - 1);
  }
  
-#endif /* CONFIG_SOFTMMU */

+#endif /* CONFIG_USER_ONLY */
  
  static inline

  uint32_t tb_hash_func(tb_page_addr_t phys_pc, vaddr pc,


Not ok, this is really softmmu related.  If we have user-only softmmu, then we'll need to 
take multiple mappings into account, just like this.


Perhaps add a comment so it's easy to see this (and whichever else) have 
already been audited?



diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
index c6619f5b98..929af1f64c 100644
--- a/accel/tcg/tcg-all.c
+++ b/accel/tcg/tcg-all.c
@@ -116,7 +116,7 @@ static int tcg_init_machine(MachineState *ms)
  tb_htable_init();
  tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus);
  
-#if defined(CONFIG_SOFTMMU)

+#if !defined(CONFIG_USER_ONLY)
  /*
   * There's no guest base to take into account, so go ahead and
   * initialize the prologue now.


With system, we *know* softmmu must be used.
With user-only, we will want to wait until after command-line processing.

Ok with comment change to "never a guest base".


r~




Re: [PATCH-for-9.1 07/12] plugins/api: Check for USER_ONLY definition instead of SOFTMMU one

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Since we*might*  have user emulation with softmmu,
replace the system emulation check by !user emulation one.

Signed-off-by: Philippe Mathieu-Daudé
---
  plugins/api.c | 10 +-
  1 file changed, 5 insertions(+), 5 deletions(-)


Reviewed-by: Richard Henderson 

r~



Re: [PATCH-for-9.1 06/12] tcg/sparc64: Check for USER_ONLY definition instead of SOFTMMU one

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Since we *might* have user emulation with softmmu,
replace the system emulation check by !user emulation one.

Signed-off-by: Philippe Mathieu-Daudé 
---
  tcg/sparc64/tcg-target.c.inc | 8 
  1 file changed, 4 insertions(+), 4 deletions(-)


This is all really softmmu.

If we ever grow user-only softmmu support, these will have to be runtime tests.  But until 
then the ifdefs are really pointing out softmmu uses.



r~



diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc
index 176c98740b..56915a913b 100644
--- a/tcg/sparc64/tcg-target.c.inc
+++ b/tcg/sparc64/tcg-target.c.inc
@@ -78,7 +78,7 @@ static const char * const 
tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
  #define TCG_REG_T2  TCG_REG_G2
  #define TCG_REG_T3  TCG_REG_O7
  
-#ifndef CONFIG_SOFTMMU

+#ifdef CONFIG_USER_ONLY
  # define TCG_GUEST_BASE_REG TCG_REG_I5
  #endif
  
@@ -961,7 +961,7 @@ static void tcg_target_qemu_prologue(TCGContext *s)

  tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) |
INSN_IMM13(-frame_size));
  
-#ifndef CONFIG_SOFTMMU

+#ifdef CONFIG_USER_ONLY
  if (guest_base != 0) {
  tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG,
   guest_base, true, TCG_REG_T1);
@@ -1075,7 +1075,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, 
HostAddress *h,
  h->aa.align = MAX(h->aa.align, s_bits);
  a_mask = (1u << h->aa.align) - 1;
  
-#ifdef CONFIG_SOFTMMU

+#ifndef CONFIG_USER_ONLY
  int mem_index = get_mmuidx(oi);
  int fast_off = tlb_mask_table_ofs(s, mem_index);
  int mask_off = fast_off + offsetof(CPUTLBDescFast, mask);
@@ -1147,7 +1147,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, 
HostAddress *h,
  tcg_out_bpcc0(s, COND_NE, BPCC_PN | BPCC_ICC, 0);
  }
  h->base = guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0;
-#endif
+#endif /* CONFIG_USER_ONLY */
  
  /* If the guest address must be zero-extended, do in the delay slot.  */

  if (addr_type == TCG_TYPE_I32) {





Re: [PATCH-for-9.0? 03/12] gdbstub: Correct invalid mentions of 'softmmu' by 'system'

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Signed-off-by: Philippe Mathieu-Daudé
---
  gdbstub/internals.h | 20 ++--
  gdbstub/system.c|  2 +-
  2 files changed, 11 insertions(+), 11 deletions(-)


Reviewed-by: Richard Henderson 

r~



Re: [PATCH-for-9.0? 04/12] gdbstub/system: Rename 'user_ctx' argument as 'ctx'

2024-03-15 Thread Richard Henderson

On 3/13/24 11:33, Philippe Mathieu-Daudé wrote:

Signed-off-by: Philippe Mathieu-Daudé
---
  gdbstub/internals.h | 8 
  gdbstub/system.c| 8 
  2 files changed, 8 insertions(+), 8 deletions(-)


Reviewed-by: Richard Henderson 

r~



Re: [RFC PATCH v3 3/3] migration: Add fd to FileMigrationArgs

2024-03-15 Thread Peter Xu
On Fri, Mar 15, 2024 at 03:01:09PM -0300, Fabiano Rosas wrote:
> Peter Xu  writes:
> 
> > [I queued patch 1-2 into -stable, leaving this patch for further
> >  discussions]
> >
> > On Fri, Mar 15, 2024 at 08:55:42AM +, Daniel P. Berrangé wrote:
> >> The 'file:' protocol eventually calls into qemu_open, and this
> >> transparently allows for FD passing using /dev/fdset/NNN syntax
> >> to pass in FDs. 
> >
> > If it always use /dev/fdsets for files, does it mean that the newly added
> > SOCKET_ADDRESS_TYPE_FD support on mapped-ram will never be used (so we can
> > drop them)?
> 
> We already have SOCKET_ADDRESS_TYPE_FD + file since 8.2 when the
> MigrationAddress was added. So this:
> 
> 'channels': [ { 'channel-type': 'main',
> 'addr': { 'transport': 'socket',
> 'type': 'fd',
> 'str': 'fdname' } } ]
> 
> works without multifd and without mapped-ram if the fd is a file or
> socket.
> 
> So yes, you're correct, but given we already have this^ it would be
> perhaps more confusing for users to allow it, but not allow the very
> same JSON when multifd=true, mapped-ram=true.

I don't think the fd: protocol (no matter the old "fd:", or the new JSON
format) is trivial to use. If libvirt didn't use it I won't be surprised to
see nobody using it.  I want us to avoid working on things that nobody is
using, or has a better replacement.

So even if Libvirt supports both, I'm wondering whether /dev/fdset/ works
for all the cases that libvirt needs.  I am aware that the old getfd has
the monitor limitation so that if the QMP disconnected and reconnect, the
fd can be gone.  However I'm not sure whether that's the only reason to
have add-fd, and also not sure whether it means add-fd is always preferred,
so that maybe we can consider obsolete getfd?

> 
> That's the only reason I didn't propose reverting commit decdc76772
> ("migration/multifd: Add mapped-ram support to fd: URI").
> 
> For mapped-ram in libvirt, we'll definitely not use
> SOCKET_ADDRESS_TYPE_FD (as in the JSON), because I don't think libvirt
> supports the new API.
> 
> As for SOCKET_ADDRESS_TYPE_FD as in "fd:", we could use it when
> direct-io is disabled. With direct-io, the fdset will be required.
> 
> >
> > What about the old getfd?  Is it obsolete because it relies on monitor
> > object?  Or maybe it's still in use?
> >
> > It would be greatly helpful if there can be a summary of how libvirt uses
> > fd for migration purpose.
> >
> > Thanks,
> 

-- 
Peter Xu




Re: [PATCH v7 3/8] tests/qtest/migration: Replace migrate_get_connect_uri inplace of migrate_get_socket_address

2024-03-15 Thread Het Gala


On 15/03/24 6:28 pm, Fabiano Rosas wrote:

Het Gala  writes:


Refactor migrate_get_socket_address to internally utilize 'socket-address'
parameter, reducing redundancy in the function definition.

migrate_get_socket_address implicitly converts SocketAddress into str.
Move migrate_get_socket_address inside migrate_get_connect_uri which
should return the uri string instead.

Signed-off-by: Het Gala
Suggested-by: Fabiano Rosas
Reviewed-by: Fabiano Rosas
---
  tests/qtest/migration-helpers.c | 29 +++--
  1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index 3e8c19c4de..8806dc841e 100644
--- a/tests/qtest/migration-helpers.c
+++ b/tests/qtest/migration-helpers.c
@@ -48,28 +48,37 @@ static char *SocketAddress_to_str(SocketAddress *addr)
  }
  }
  
-static char *

-migrate_get_socket_address(QTestState *who, const char *parameter)
+static SocketAddress *migrate_get_socket_address(QTestState *who)
  {
  QDict *rsp;
-char *result;
  SocketAddressList *addrs;
+SocketAddress *addr;
  Visitor *iv = NULL;
  QObject *object;
  
  rsp = migrate_query(who);

-object = qdict_get(rsp, parameter);
+object = qdict_get(rsp, "socket-address");

Just a heads up, none of what I'm about to say applies to current
master.

This can return NULL if there is no socket-address, such as with a file
migration. Then the visitor code below just barfs. It would be nice if
we touched this up eventually.


Yes. I agree this is not full proof solution and covers for all the cases.
It would only for 'socket-address'. Could you elaborate on what other than
socket-address the QObject can have ?


I only noticed this because I was fiddling with the file migration API
and this series helped me a lot to test my changes. So thanks for that,
Het.

Another point is: we really need to encourage people to write tests
using the new channels API. I added the FileMigrationArgs with the
'offset' as a required parameter, not even knowing optional parameters
were a thing. So it's obviously not enough to write support for the new
API if no tests ever touch it.

Yes, definitely we need more tests with the new channels API to test other
than just tcp connection. I could give a try for vsock and unix with the
new QAPI syntax, and add some tests.

I also wanted to bring in attention that, this solution I what i feel is 
also

not complete. If we are using new channel syntax for migrate_qmp, then the
same syntax should also be used for migrate_qmp_incoming. But we haven't
touch that, and it still prints the old syntax. We might need a lot changes
in design maybe to incorporate that too in new tests totally with the new
syntax.

Another thing that you also noted down while discussing on the patches that
we should have a standard pattern on how to define the migration tests. Even
that would be helpful for the users, on how to add new tests, where to add
new tests in the file, and which test is needed to run if a specific change
needs to be tested.

  
  iv = qobject_input_visitor_new(object);

  visit_type_SocketAddressList(iv, NULL, , _abort);
+addr = addrs->value;
  visit_free(iv);
  
-/* we are only using a single address */

-result = SocketAddress_to_str(addrs->value);
-
-qapi_free_SocketAddressList(addrs);
  qobject_unref(rsp);
-return result;
+return addr;
+}
+
+static char *
+migrate_get_connect_uri(QTestState *who)
+{
+SocketAddress *addrs;
+char *connect_uri;
+
+addrs = migrate_get_socket_address(who);
+connect_uri = SocketAddress_to_str(addrs);
+
+qapi_free_SocketAddress(addrs);
+return connect_uri;
  }
  
  bool migrate_watch_for_events(QTestState *who, const char *name,

@@ -129,7 +138,7 @@ void migrate_qmp(QTestState *who, QTestState *to, const 
char *uri,
  
  g_assert(!qdict_haskey(args, "uri"));

  if (!uri) {
-connect_uri = migrate_get_socket_address(to, "socket-address");
+connect_uri = migrate_get_connect_uri(to);
  }
  qdict_put_str(args, "uri", uri ? uri : connect_uri);


Regards,
Het Gala


Re: [PATCH v4 2/2] vhost: Perform memory section dirty scans once per iteration

2024-03-15 Thread Si-Wei Liu




On 3/14/2024 9:03 PM, Jason Wang wrote:

On Fri, Mar 15, 2024 at 5:39 AM Si-Wei Liu  wrote:

On setups with one or more virtio-net devices with vhost on,
dirty tracking iteration increases cost the bigger the number
amount of queues are set up e.g. on idle guests migration the
following is observed with virtio-net with vhost=on:

48 queues -> 78.11%  [.] vhost_dev_sync_region.isra.13
8 queues -> 40.50%   [.] vhost_dev_sync_region.isra.13
1 queue -> 6.89% [.] vhost_dev_sync_region.isra.13
2 devices, 1 queue -> 18.60%  [.] vhost_dev_sync_region.isra.14

With high memory rates the symptom is lack of convergence as soon
as it has a vhost device with a sufficiently high number of queues,
the sufficient number of vhost devices.

On every migration iteration (every 100msecs) it will redundantly
query the *shared log* the number of queues configured with vhost
that exist in the guest. For the virtqueue data, this is necessary,
but not for the memory sections which are the same. So essentially
we end up scanning the dirty log too often.

To fix that, select a vhost device responsible for scanning the
log with regards to memory sections dirty tracking. It is selected
when we enable the logger (during migration) and cleared when we
disable the logger. If the vhost logger device goes away for some
reason, the logger will be re-selected from the rest of vhost
devices.

After making mem-section logger a singleton instance, constant cost
of 7%-9% (like the 1 queue report) will be seen, no matter how many
queues or how many vhost devices are configured:

48 queues -> 8.71%[.] vhost_dev_sync_region.isra.13
2 devices, 8 queues -> 7.97%   [.] vhost_dev_sync_region.isra.14

Co-developed-by: Joao Martins 
Signed-off-by: Joao Martins 
Signed-off-by: Si-Wei Liu 

---
v3 -> v4:
   - add comment to clarify effect on cache locality and
 performance

v2 -> v3:
   - add after-fix benchmark to commit log
   - rename vhost_log_dev_enabled to vhost_dev_should_log
   - remove unneeded comparisons for backend_type
   - use QLIST array instead of single flat list to store vhost
 logger devices
   - simplify logger election logic
---
  hw/virtio/vhost.c | 67 ++-
  include/hw/virtio/vhost.h |  1 +
  2 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 612f4db..58522f1 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -45,6 +45,7 @@

  static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX];
  static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX];
+static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX];

  /* Memslots used by backends that support private memslots (without an fd). */
  static unsigned int used_memslots;
@@ -149,6 +150,47 @@ bool vhost_dev_has_iommu(struct vhost_dev *dev)
  }
  }

+static inline bool vhost_dev_should_log(struct vhost_dev *dev)
+{
+assert(dev->vhost_ops);
+assert(dev->vhost_ops->backend_type > VHOST_BACKEND_TYPE_NONE);
+assert(dev->vhost_ops->backend_type < VHOST_BACKEND_TYPE_MAX);
+
+return dev == QLIST_FIRST(_log_devs[dev->vhost_ops->backend_type]);

A dumb question, why not simple check

dev->log == vhost_log_shm[dev->vhost_ops->backend_type]
Because we are not sure if the logger comes from vhost_log_shm[] or 
vhost_log[]. Don't want to complicate the check here by calling into 
vhost_dev_log_is_shared() everytime when the .log_sync() is called.


-Siwei

?

Thanks






Re: [PATCH v4 1/2] vhost: dirty log should be per backend type

2024-03-15 Thread Si-Wei Liu




On 3/14/2024 8:50 PM, Jason Wang wrote:

On Fri, Mar 15, 2024 at 5:39 AM Si-Wei Liu  wrote:

There could be a mix of both vhost-user and vhost-kernel clients
in the same QEMU process, where separate vhost loggers for the
specific vhost type have to be used. Make the vhost logger per
backend type, and have them properly reference counted.

It's better to describe what's the advantage of doing this.
Yes, I can add that to the log. Although it's a niche use case, it was 
actually a long standing limitation / bug that vhost-user and 
vhost-kernel loggers can't co-exist per QEMU process, but today it's 
just silent failure that may be ended up with. This bug fix removes that 
implicit limitation in the code.



Suggested-by: Michael S. Tsirkin 
Signed-off-by: Si-Wei Liu 

---
v3->v4:
   - remove checking NULL return value from vhost_log_get

v2->v3:
   - remove non-effective assertion that never be reached
   - do not return NULL from vhost_log_get()
   - add neccessary assertions to vhost_log_get()
---
  hw/virtio/vhost.c | 45 +
  1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 2c9ac79..612f4db 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -43,8 +43,8 @@
  do { } while (0)
  #endif

-static struct vhost_log *vhost_log;
-static struct vhost_log *vhost_log_shm;
+static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX];
+static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX];

  /* Memslots used by backends that support private memslots (without an fd). */
  static unsigned int used_memslots;
@@ -287,6 +287,10 @@ static int vhost_set_backend_type(struct vhost_dev *dev,
  r = -1;
  }

+if (r == 0) {
+assert(dev->vhost_ops->backend_type == backend_type);
+}
+

Under which condition could we hit this?
Just in case some other function inadvertently corrupted this earlier, 
we have to capture discrepancy in the first place... On the other hand, 
it will be helpful for other vhost backend writers to diagnose day-one 
bug in the code. I feel just code comment here will not be 
sufficient/helpful.



  It seems not good to assert a local logic.
It seems to me quite a few local asserts are in the same file already, 
vhost_save_backend_state, vhost_load_backend_state, 
vhost_virtqueue_mask, vhost_config_mask, just to name a few. Why local 
assert a problem?


Thanks,
-Siwei


Thanks






Re: [PATCH v3 for 9.1 1/6] virtio/virtio-pci: Handle extra notification data

2024-03-15 Thread Eugenio Perez Martin
On Fri, Mar 15, 2024 at 5:57 PM Jonah Palmer  wrote:
>
> Add support to virtio-pci devices for handling the extra data sent
> from the driver to the device when the VIRTIO_F_NOTIFICATION_DATA
> transport feature has been negotiated.
>
> The extra data that's passed to the virtio-pci device when this
> feature is enabled varies depending on the device's virtqueue
> layout.
>
> In a split virtqueue layout, this data includes:
>  - upper 16 bits: shadow_avail_idx
>  - lower 16 bits: virtqueue index
>
> In a packed virtqueue layout, this data includes:
>  - upper 16 bits: 1-bit wrap counter & 15-bit shadow_avail_idx
>  - lower 16 bits: virtqueue index
>

Reviewed-by: Eugenio Pérez 

Thanks!

> Signed-off-by: Jonah Palmer 
> ---
>  hw/virtio/virtio-pci.c | 11 ---
>  hw/virtio/virtio.c | 18 ++
>  include/hw/virtio/virtio.h |  2 ++
>  3 files changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index cb6940fc0e..f3e0a08f53 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -384,7 +384,7 @@ static void virtio_ioport_write(void *opaque, uint32_t 
> addr, uint32_t val)
>  {
>  VirtIOPCIProxy *proxy = opaque;
>  VirtIODevice *vdev = virtio_bus_get_device(>bus);
> -uint16_t vector;
> +uint16_t vector, vq_idx;
>  hwaddr pa;
>
>  switch (addr) {
> @@ -408,8 +408,13 @@ static void virtio_ioport_write(void *opaque, uint32_t 
> addr, uint32_t val)
>  vdev->queue_sel = val;
>  break;
>  case VIRTIO_PCI_QUEUE_NOTIFY:
> -if (val < VIRTIO_QUEUE_MAX) {
> -virtio_queue_notify(vdev, val);
> +vq_idx = val;
> +if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) 
> {
> +if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
> +virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, 
> vq_idx),
> +  val >> 16);
> +}
> +virtio_queue_notify(vdev, vq_idx);
>  }
>  break;
>  case VIRTIO_PCI_STATUS:
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index d229755eae..463426ca92 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -2255,6 +2255,24 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, 
> int align)
>  }
>  }
>
> +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t 
> shadow_avail_idx)
> +{
> +if (!vq->vring.desc) {
> +return;
> +}
> +
> +/*
> + * 16-bit data for packed VQs include 1-bit wrap counter and
> + * 15-bit shadow_avail_idx.
> + */
> +if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
> +vq->shadow_avail_wrap_counter = (shadow_avail_idx >> 15) & 0x1;
> +vq->shadow_avail_idx = shadow_avail_idx & 0x7FFF;
> +} else {
> +vq->shadow_avail_idx = shadow_avail_idx;
> +}
> +}
> +
>  static void virtio_queue_notify_vq(VirtQueue *vq)
>  {
>  if (vq->vring.desc && vq->handle_output) {
> diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
> index c8f72850bc..cdd4f86b61 100644
> --- a/include/hw/virtio/virtio.h
> +++ b/include/hw/virtio/virtio.h
> @@ -306,6 +306,8 @@ int virtio_queue_ready(VirtQueue *vq);
>
>  int virtio_queue_empty(VirtQueue *vq);
>
> +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t idx);
> +
>  /* Host binding interface.  */
>
>  uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr);
> --
> 2.39.3
>




Re: [RFC PATCH v3 3/3] migration: Add fd to FileMigrationArgs

2024-03-15 Thread Fabiano Rosas
Peter Xu  writes:

> [I queued patch 1-2 into -stable, leaving this patch for further
>  discussions]
>
> On Fri, Mar 15, 2024 at 08:55:42AM +, Daniel P. Berrangé wrote:
>> The 'file:' protocol eventually calls into qemu_open, and this
>> transparently allows for FD passing using /dev/fdset/NNN syntax
>> to pass in FDs. 
>
> If it always use /dev/fdsets for files, does it mean that the newly added
> SOCKET_ADDRESS_TYPE_FD support on mapped-ram will never be used (so we can
> drop them)?

We already have SOCKET_ADDRESS_TYPE_FD + file since 8.2 when the
MigrationAddress was added. So this:

'channels': [ { 'channel-type': 'main',
'addr': { 'transport': 'socket',
'type': 'fd',
'str': 'fdname' } } ]

works without multifd and without mapped-ram if the fd is a file or
socket.

So yes, you're correct, but given we already have this^ it would be
perhaps more confusing for users to allow it, but not allow the very
same JSON when multifd=true, mapped-ram=true.

That's the only reason I didn't propose reverting commit decdc76772
("migration/multifd: Add mapped-ram support to fd: URI").

For mapped-ram in libvirt, we'll definitely not use
SOCKET_ADDRESS_TYPE_FD (as in the JSON), because I don't think libvirt
supports the new API.

As for SOCKET_ADDRESS_TYPE_FD as in "fd:", we could use it when
direct-io is disabled. With direct-io, the fdset will be required.

>
> What about the old getfd?  Is it obsolete because it relies on monitor
> object?  Or maybe it's still in use?
>
> It would be greatly helpful if there can be a summary of how libvirt uses
> fd for migration purpose.
>
> Thanks,



Re: [PATCH v2] vfio/pci: migration: Skip config space check for vendor specific capability during restore/load

2024-03-15 Thread Vinayak Kale




On 11/03/24 8:32 pm, Alex Williamson wrote:

External email: Use caution opening links or attachments


On Mon, 11 Mar 2024 17:45:19 +0530
Vinayak Kale  wrote:


In case of migration, during restore operation, qemu checks config space of the
pci device with the config space in the migration stream captured during save
operation. In case of config space data mismatch, restore operation is failed.

config space check is done in function get_pci_config_device(). By default VSC
(vendor-specific-capability) in config space is checked.

Ideally qemu should not check VSC for VFIO-PCI device during restore/load as
qemu is not aware of VSC ABI.


It's disappointing that we can't seem to have a discussion about why
it's not the responsibility of the underlying migration support in the
vfio-pci variant driver to make the vendor specific capability
consistent across migration.


I think it is device vendor driver's responsibility to ensure that VSC 
is consistent across migration. Here consistency could mean that VSC 
format should be same on source and destination, however actual VSC 
contents may not be byte-to-byte identical.


If a vfio-pci device is migration capable and if vfio-pci vendor driver 
is OK with volatile VSC contents as long as consistency is maintained 
for VSC format then QEMU should exempt config space check for VSC contents.




Also, for future maintenance, specifically what device is currently
broken by this and under what conditions?


Under certain conditions VSC contents vary for NVIDIA vGPU devices in 
case of live migration. Due to QEMU's current config space check for 
VSC, live migration is broken across NVIDIA vGPU devices.






This patch skips the check for VFIO-PCI device by clearing pdev->cmask[] for VSC
offsets. If cmask[] is not set for an offset, then qemu skips config space check
for that offset.

Signed-off-by: Vinayak Kale 
---
Version History
v1->v2:
 - Limited scope of change to vfio-pci devices instead of all pci devices.

  hw/vfio/pci.c | 19 +++
  1 file changed, 19 insertions(+)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index d7fe06715c..9edaff4b37 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2132,6 +2132,22 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, 
uint8_t pos)
  }
  }

+static int vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos,
+uint8_t size, Error **errp)
+{
+PCIDevice *pdev = >pdev;
+
+pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp);
+if (pos < 0) {
+return pos;
+}
+
+/* Exempt config space check for VSC during restore/load  */
+memset(pdev->cmask + pos, 0, size);


This excludes the entire capability from comparison, including the
capability ID, next pointer, and capability length.  Even if the
contents of the capability are considered volatile vendor information,
the header is spec defined ABI which must be consistent.  Thanks,


This makes sense, I'll address this in V3. Thanks.



Alex


+
+return pos;
+}
+
  static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
  {
  PCIDevice *pdev = >pdev;
@@ -2199,6 +2215,9 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t 
pos, Error **errp)
  vfio_check_af_flr(vdev, pos);
  ret = pci_add_capability(pdev, cap_id, pos, size, errp);
  break;
+case PCI_CAP_ID_VNDR:
+ret = vfio_add_vendor_specific_cap(vdev, pos, size, errp);
+break;
  default:
  ret = pci_add_capability(pdev, cap_id, pos, size, errp);
  break;






Re: [PATCH 1/1] cxl/mem: Fix for the index of Clear Event Record Handle

2024-03-15 Thread Dan Williams
Yuquan Wang wrote:
> The dev_dbg info for Clear Event Records mailbox command would report
> the handle of the next record to clear not the current one.
> 
> This was because the index 'i' had incremented before printing the
> current handle value.
> 
> This fix also adjusts the index variable name from 'i' to 'clear_cnt'
> which can be easier for developers to distinguish and understand.
> 
> Signed-off-by: Yuquan Wang 
> ---
>  drivers/cxl/core/mbox.c | 21 +++--
>  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 9adda4795eb7..3ca2845ae6aa 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -881,7 +881,7 @@ static int cxl_clear_event_record(struct cxl_memdev_state 
> *mds,
>   struct cxl_mbox_cmd mbox_cmd;
>   u16 cnt;
>   int rc = 0;
> - int i;
> + int clear_cnt;
>  
>   /* Payload size may limit the max handles */
>   if (pl_size > mds->payload_size) {
> @@ -908,28 +908,29 @@ static int cxl_clear_event_record(struct 
> cxl_memdev_state *mds,
>* Clear Event Records uses u8 for the handle cnt while Get Event
>* Record can return up to 0x records.
>*/
> - i = 0;
> + clear_cnt = 0;
>   for (cnt = 0; cnt < total; cnt++) {
>   struct cxl_event_record_raw *raw = _pl->records[cnt];
>   struct cxl_event_generic *gen = >event.generic;
>  
> - payload->handles[i++] = gen->hdr.handle;
> + payload->handles[clear_cnt] = gen->hdr.handle;
>   dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log,
> - le16_to_cpu(payload->handles[i]));

Couldn't this patch be much smaller by just changing this to "i - 1",
because from the description the only problem is the dev_dbg()
statement, so just fix that.



Re: [PATCH v4 21/23] qapi/schema: add type hints

2024-03-15 Thread John Snow
On Fri, Mar 15, 2024, 10:03 AM Markus Armbruster  wrote:

> John Snow  writes:
>
> > This patch only adds type hints, which aren't utilized at runtime and
> > don't change the behavior of this module in any way.
> >
> > In a scant few locations, type hints are removed where no longer
> > necessary due to inference power from typing all of the rest of
> > creation; and any type hints that no longer need string quotes are
> > changed.
> >
> > Signed-off-by: John Snow 
> > ---
> >  scripts/qapi/schema.py | 568 -
> >  1 file changed, 396 insertions(+), 172 deletions(-)
> >
> > diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> > index 3b8c2ebbb5f..d2faaea6eac 100644
> > --- a/scripts/qapi/schema.py
> > +++ b/scripts/qapi/schema.py
>
> [...]
>
> > @@ -1006,18 +1181,27 @@ def _def_definition(self, defn):
> >  defn.info, "%s is already defined" %
> other_defn.describe())
> >  self._entity_dict[defn.name] = defn
> >
> > -def lookup_entity(self, name, typ=None):
> > +def lookup_entity(
> > +self,
> > +name: str,
> > +typ: Optional[type] = None,
> > +) -> Optional[QAPISchemaEntity]:
>
> Optional[QAPISchemaDefinition], actually.
>

Ah! Very good catch.


> >  ent = self._entity_dict.get(name)
> >  if typ and not isinstance(ent, typ):
> >  return None
> >  return ent
>
> [...]
>
>


Re: [PATCH v2] target/s390x: improve cpu compatibility check error message

2024-03-15 Thread Claudio Fontana
On 3/15/24 17:59, Nina Schoetterl-Glausch wrote:
> On Thu, 2024-03-14 at 22:37 +0100, Claudio Fontana wrote:
>> some users were confused by this message showing under TCG:
>>
>> Selected CPU generation is too new. Maximum supported model
>> in the configuration: 'xyz'
>>
>> Clarify that the maximum can depend on the accel, and add a
>> hint to try a different one.
>>
>> Also add a hint for features mismatch to suggest trying
>> different accel, QEMU and kernel versions.
>>
>> Signed-off-by: Claudio Fontana 
> 
> Reviewed-by: Nina Schoetterl-Glausch 
> 
>> ---
>>  target/s390x/cpu_models.c | 22 +++---
>>  1 file changed, 15 insertions(+), 7 deletions(-)
>>
>> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
>> index 1a1c096122..8ed3bb6a27 100644
>> --- a/target/s390x/cpu_models.c
>> +++ b/target/s390x/cpu_models.c
>> @@ -500,6 +500,16 @@ static void error_prepend_missing_feat(const char 
>> *name, void *opaque)
>>  error_prepend((Error **) opaque, "%s ", name);
>>  }
>>  
>> +static void check_compat_model_failed(Error **errp,
>> +  const S390CPUModel *max_model,
>> +  const char *msg)
>> +{
>> +error_setg(errp, "%s. Maximum supported model in the current 
>> configuration: \'%s\'",
>> +   msg, max_model->def->name);
>> +error_append_hint(errp, "Consider a different accelerator, try \"-accel 
>> help\"\n");
>> +return;
>> +}
>> +
>>  static void check_compatibility(const S390CPUModel *max_model,
>>  const S390CPUModel *model, Error **errp)
>>  {
>> @@ -507,15 +517,11 @@ static void check_compatibility(const S390CPUModel 
>> *max_model,
>>  S390FeatBitmap missing;
>>  
>>  if (model->def->gen > max_model->def->gen) {
>> -error_setg(errp, "Selected CPU generation is too new. Maximum "
>> -   "supported model in the configuration: \'%s\'",
>> -   max_model->def->name);
>> +check_compat_model_failed(errp, max_model, "Selected CPU generation 
>> is too new");
>>  return;
>>  } else if (model->def->gen == max_model->def->gen &&
>> model->def->ec_ga > max_model->def->ec_ga) {
>> -error_setg(errp, "Selected CPU GA level is too new. Maximum "
>> -   "supported model in the configuration: \'%s\'",
>> -   max_model->def->name);
>> +check_compat_model_failed(errp, max_model, "Selected CPU GA level 
>> is too new");
>>  return;
>>  }
>>  
>> @@ -537,7 +543,9 @@ static void check_compatibility(const S390CPUModel 
>> *max_model,
>>  error_setg(errp, " ");
>>  s390_feat_bitmap_to_ascii(missing, errp, error_prepend_missing_feat);
>>  error_prepend(errp, "Some features requested in the CPU model are not "
>> -  "available in the configuration: ");
>> +  "available in the current configuration: ");
>> +error_append_hint(errp,
>> +  "Consider a different accelerator, QEMU, or kernel 
>> version\n");
> 
> If I'm reading the regex right, this ^, a string literal on a separate line, 
> is excluded
> from the line length check.

Thanks, it's very good to know!

Claudio
> 
>>  }
>>  
>>  S390CPUModel *get_max_cpu_model(Error **errp)
> 




Re: [PATCH V4 1/1] target/loongarch: Fixed tlb huge page loading issue

2024-03-15 Thread Richard Henderson

On 3/14/24 23:01, lixianglai wrote:

Hi Richard :


On 3/14/24 20:22, lixianglai wrote:

Hi Richard:

On 3/13/24 15:33, Xianglai Li wrote:

+    if (unlikely((level == 0) || (level > 4))) {
+    return base;
+    }

...

Perhaps it would be worthwhile to add another for the level==0 or > 4 case 
above?


A normal level 4 page table should not print an error log,

only if a level 4 page is large, so we should put it in

 if (FIELD_EX64(base, TLBENTRY, HUGE)) {
 if (unlikely(level == 4)) {
 qemu_log_mask(LOG_GUEST_ERROR,
   "Attempted use of level %lu huge page\n", level);
 }

 if (FIELD_EX64(base, TLBENTRY, LEVEL)) {
 return base;
 } else {
 return  FIELD_DP64(base, TLBENTRY, LEVEL, level);
 }
 }


A level 5 page table is not normal, nor is a level 0 lddir.



We communicate with the hardware guys that the behavior above level 4 and lddir 0 is 
undefined behavior.


The result of our test on 3A5000 is that it has no any effect on "base",

however in future chips the behavior may change since it may support 5-level page table 
and width for level[13:14] may change also.



So in this context,I am not sure which level to use to print logs,

which content to print, and where to add these prints,

any more detailed advice?


Yes, right there in the IF that I quoted at the top.
What I was trying to spell out is

if (unlikely(level == 0 || level > 4)) {
qemu_log_mask(LOG_GUEST_ERROR,
  "Attepted LDDIR with level %"PRId64"\n", level);
return base;
}


r~



Re: [PATCH for-9.0 v3] vdpa-dev: Fix initialisation order to restore VDUSE compatibility

2024-03-15 Thread Stefano Garzarella

On Fri, Mar 15, 2024 at 04:59:49PM +0100, Kevin Wolf wrote:

VDUSE requires that virtqueues are first enabled before the DRIVER_OK
status flag is set; with the current API of the kernel module, it is
impossible to enable the opposite order in our block export code because
userspace is not notified when a virtqueue is enabled.

This requirement also mathces the normal initialisation order as done by
the generic vhost code in QEMU. However, commit 6c482547 accidentally
changed the order for vdpa-dev and broke access to VDUSE devices with
this.

This changes vdpa-dev to use the normal order again and use the standard
vhost callback .vhost_set_vring_enable for this. VDUSE devices can be
used with vdpa-dev again after this fix.

vhost_net intentionally avoided enabling the vrings for vdpa and does
this manually later while it does enable them for other vhost backends.
Reflect this in the vhost_net code and return early for vdpa, so that
the behaviour doesn't change for this device.

Cc: qemu-sta...@nongnu.org
Fixes: 6c4825476a4351530bcac17abab72295b75ffe98
Signed-off-by: Kevin Wolf 
---
v2:
- Actually make use of the @enable parameter
- Change vhost_net to preserve the current behaviour

v3:
- Updated trace point [Stefano]
- Fixed typo in comment [Stefano]

hw/net/vhost_net.c | 10 ++
hw/virtio/vdpa-dev.c   |  5 +
hw/virtio/vhost-vdpa.c | 29 ++---
hw/virtio/vhost.c  |  8 +++-
hw/virtio/trace-events |  2 +-
5 files changed, 45 insertions(+), 9 deletions(-)


LGTM!

Reviewed-by: Stefano Garzarella 

Thanks,
Stefano



diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e8e1661646..fd1a93701a 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -541,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int enable)
VHostNetState *net = get_vhost_net(nc);
const VhostOps *vhost_ops = net->dev.vhost_ops;

+/*
+ * vhost-vdpa network devices need to enable dataplane virtqueues after
+ * DRIVER_OK, so they can recover device state before starting dataplane.
+ * Because of that, we don't enable virtqueues here and leave it to
+ * net/vhost-vdpa.c.
+ */
+if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+return 0;
+}
+
nc->vring_enable = enable;

if (vhost_ops && vhost_ops->vhost_set_vring_enable) {
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index eb9ecea83b..13e87f06f6 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -253,14 +253,11 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, 
Error **errp)

s->dev.acked_features = vdev->guest_features;

-ret = vhost_dev_start(>dev, vdev, false);
+ret = vhost_dev_start(>dev, vdev, true);
if (ret < 0) {
error_setg_errno(errp, -ret, "Error starting vhost");
goto err_guest_notifiers;
}
-for (i = 0; i < s->dev.nvqs; ++i) {
-vhost_vdpa_set_vring_ready(>vdpa, i);
-}
s->started = true;

/*
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index ddae494ca8..31453466af 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -886,19 +886,41 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, 
int idx)
return idx;
}

-int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned idx,
+   int enable)
{
struct vhost_dev *dev = v->dev;
struct vhost_vring_state state = {
.index = idx,
-.num = 1,
+.num = enable,
};
int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, );

-trace_vhost_vdpa_set_vring_ready(dev, idx, r);
+trace_vhost_vdpa_set_vring_enable_one(dev, idx, enable, r);
return r;
}

+static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+struct vhost_vdpa *v = dev->opaque;
+unsigned int i;
+int ret;
+
+for (i = 0; i < dev->nvqs; ++i) {
+ret = vhost_vdpa_set_vring_enable_one(v, i, enable);
+if (ret < 0) {
+return ret;
+}
+}
+
+return 0;
+}
+
+int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+{
+return vhost_vdpa_set_vring_enable_one(v, idx, 1);
+}
+
static int vhost_vdpa_set_config_call(struct vhost_dev *dev,
   int fd)
{
@@ -1514,6 +1536,7 @@ const VhostOps vdpa_ops = {
.vhost_set_features = vhost_vdpa_set_features,
.vhost_reset_device = vhost_vdpa_reset_device,
.vhost_get_vq_index = vhost_vdpa_get_vq_index,
+.vhost_set_vring_enable = vhost_vdpa_set_vring_enable,
.vhost_get_config  = vhost_vdpa_get_config,
.vhost_set_config = vhost_vdpa_set_config,
.vhost_requires_shm_log = NULL,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 2c9ac79468..a66186 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1984,7 +1984,13 @@ static int 

Re: [PATCH v2] target/s390x: improve cpu compatibility check error message

2024-03-15 Thread Nina Schoetterl-Glausch
On Thu, 2024-03-14 at 22:37 +0100, Claudio Fontana wrote:
> some users were confused by this message showing under TCG:
> 
> Selected CPU generation is too new. Maximum supported model
> in the configuration: 'xyz'
> 
> Clarify that the maximum can depend on the accel, and add a
> hint to try a different one.
> 
> Also add a hint for features mismatch to suggest trying
> different accel, QEMU and kernel versions.
> 
> Signed-off-by: Claudio Fontana 

Reviewed-by: Nina Schoetterl-Glausch 

> ---
>  target/s390x/cpu_models.c | 22 +++---
>  1 file changed, 15 insertions(+), 7 deletions(-)
> 
> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> index 1a1c096122..8ed3bb6a27 100644
> --- a/target/s390x/cpu_models.c
> +++ b/target/s390x/cpu_models.c
> @@ -500,6 +500,16 @@ static void error_prepend_missing_feat(const char *name, 
> void *opaque)
>  error_prepend((Error **) opaque, "%s ", name);
>  }
>  
> +static void check_compat_model_failed(Error **errp,
> +  const S390CPUModel *max_model,
> +  const char *msg)
> +{
> +error_setg(errp, "%s. Maximum supported model in the current 
> configuration: \'%s\'",
> +   msg, max_model->def->name);
> +error_append_hint(errp, "Consider a different accelerator, try \"-accel 
> help\"\n");
> +return;
> +}
> +
>  static void check_compatibility(const S390CPUModel *max_model,
>  const S390CPUModel *model, Error **errp)
>  {
> @@ -507,15 +517,11 @@ static void check_compatibility(const S390CPUModel 
> *max_model,
>  S390FeatBitmap missing;
>  
>  if (model->def->gen > max_model->def->gen) {
> -error_setg(errp, "Selected CPU generation is too new. Maximum "
> -   "supported model in the configuration: \'%s\'",
> -   max_model->def->name);
> +check_compat_model_failed(errp, max_model, "Selected CPU generation 
> is too new");
>  return;
>  } else if (model->def->gen == max_model->def->gen &&
> model->def->ec_ga > max_model->def->ec_ga) {
> -error_setg(errp, "Selected CPU GA level is too new. Maximum "
> -   "supported model in the configuration: \'%s\'",
> -   max_model->def->name);
> +check_compat_model_failed(errp, max_model, "Selected CPU GA level is 
> too new");
>  return;
>  }
>  
> @@ -537,7 +543,9 @@ static void check_compatibility(const S390CPUModel 
> *max_model,
>  error_setg(errp, " ");
>  s390_feat_bitmap_to_ascii(missing, errp, error_prepend_missing_feat);
>  error_prepend(errp, "Some features requested in the CPU model are not "
> -  "available in the configuration: ");
> +  "available in the current configuration: ");
> +error_append_hint(errp,
> +  "Consider a different accelerator, QEMU, or kernel 
> version\n");

If I'm reading the regex right, this ^, a string literal on a separate line, is 
excluded
from the line length check.

>  }
>  
>  S390CPUModel *get_max_cpu_model(Error **errp)




[PATCH v3 for 9.1 6/6] virtio: Add VIRTIO_F_NOTIFICATION_DATA property definition

2024-03-15 Thread Jonah Palmer
Extend the virtio device property definitions to include the
VIRTIO_F_NOTIFICATION_DATA feature.

The default state of this feature is disabled, allowing it to be
explicitly enabled where it's supported.

Tested-by: Lei Yang 
Reviewed-by: Eugenio Pérez 
Signed-off-by: Jonah Palmer 
---
 include/hw/virtio/virtio.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index cdd4f86b61..14858c0924 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -370,7 +370,9 @@ typedef struct VirtIORNGConf VirtIORNGConf;
 DEFINE_PROP_BIT64("packed", _state, _field, \
   VIRTIO_F_RING_PACKED, false), \
 DEFINE_PROP_BIT64("queue_reset", _state, _field, \
-  VIRTIO_F_RING_RESET, true)
+  VIRTIO_F_RING_RESET, true), \
+DEFINE_PROP_BIT64("notification_data", _state, _field, \
+  VIRTIO_F_NOTIFICATION_DATA, false)
 
 hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
 bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n);
-- 
2.39.3




[PATCH v3 for 9.1 5/6] vhost/vhost-user: Add VIRTIO_F_NOTIFICATION_DATA to vhost feature bits

2024-03-15 Thread Jonah Palmer
Add support for the VIRTIO_F_NOTIFICATION_DATA feature across a variety
of vhost devices.

The inclusion of VIRTIO_F_NOTIFICATION_DATA in the feature bits arrays
for these devices ensures that the backend is capable of offering and
providing support for this feature, and that it can be disabled if the
backend does not support it.

Tested-by: Lei Yang 
Reviewed-by: Eugenio Pérez 
Signed-off-by: Jonah Palmer 
---
 hw/block/vhost-user-blk.c| 1 +
 hw/net/vhost_net.c   | 2 ++
 hw/scsi/vhost-scsi.c | 1 +
 hw/scsi/vhost-user-scsi.c| 1 +
 hw/virtio/vhost-user-fs.c| 2 +-
 hw/virtio/vhost-user-vsock.c | 1 +
 net/vhost-vdpa.c | 1 +
 7 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 6a856ad51a..983c0657da 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -51,6 +51,7 @@ static const int user_feature_bits[] = {
 VIRTIO_F_RING_PACKED,
 VIRTIO_F_IOMMU_PLATFORM,
 VIRTIO_F_RING_RESET,
+VIRTIO_F_NOTIFICATION_DATA,
 VHOST_INVALID_FEATURE_BIT
 };
 
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e8e1661646..bb1f975b39 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -48,6 +48,7 @@ static const int kernel_feature_bits[] = {
 VIRTIO_F_IOMMU_PLATFORM,
 VIRTIO_F_RING_PACKED,
 VIRTIO_F_RING_RESET,
+VIRTIO_F_NOTIFICATION_DATA,
 VIRTIO_NET_F_HASH_REPORT,
 VHOST_INVALID_FEATURE_BIT
 };
@@ -55,6 +56,7 @@ static const int kernel_feature_bits[] = {
 /* Features supported by others. */
 static const int user_feature_bits[] = {
 VIRTIO_F_NOTIFY_ON_EMPTY,
+VIRTIO_F_NOTIFICATION_DATA,
 VIRTIO_RING_F_INDIRECT_DESC,
 VIRTIO_RING_F_EVENT_IDX,
 
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index ae26bc19a4..3d5fe0994d 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -38,6 +38,7 @@ static const int kernel_feature_bits[] = {
 VIRTIO_RING_F_EVENT_IDX,
 VIRTIO_SCSI_F_HOTPLUG,
 VIRTIO_F_RING_RESET,
+VIRTIO_F_NOTIFICATION_DATA,
 VHOST_INVALID_FEATURE_BIT
 };
 
diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c
index a63b1f4948..0b050805a8 100644
--- a/hw/scsi/vhost-user-scsi.c
+++ b/hw/scsi/vhost-user-scsi.c
@@ -36,6 +36,7 @@ static const int user_feature_bits[] = {
 VIRTIO_RING_F_EVENT_IDX,
 VIRTIO_SCSI_F_HOTPLUG,
 VIRTIO_F_RING_RESET,
+VIRTIO_F_NOTIFICATION_DATA,
 VHOST_INVALID_FEATURE_BIT
 };
 
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
index cca2cd41be..ae48cc1c96 100644
--- a/hw/virtio/vhost-user-fs.c
+++ b/hw/virtio/vhost-user-fs.c
@@ -33,7 +33,7 @@ static const int user_feature_bits[] = {
 VIRTIO_F_RING_PACKED,
 VIRTIO_F_IOMMU_PLATFORM,
 VIRTIO_F_RING_RESET,
-
+VIRTIO_F_NOTIFICATION_DATA,
 VHOST_INVALID_FEATURE_BIT
 };
 
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
index 9431b9792c..802b44a07d 100644
--- a/hw/virtio/vhost-user-vsock.c
+++ b/hw/virtio/vhost-user-vsock.c
@@ -21,6 +21,7 @@ static const int user_feature_bits[] = {
 VIRTIO_RING_F_INDIRECT_DESC,
 VIRTIO_RING_F_EVENT_IDX,
 VIRTIO_F_NOTIFY_ON_EMPTY,
+VIRTIO_F_NOTIFICATION_DATA,
 VHOST_INVALID_FEATURE_BIT
 };
 
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index 2a9ddb4552..5583ce5279 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -61,6 +61,7 @@ const int vdpa_feature_bits[] = {
 VIRTIO_F_RING_PACKED,
 VIRTIO_F_RING_RESET,
 VIRTIO_F_VERSION_1,
+VIRTIO_F_NOTIFICATION_DATA,
 VIRTIO_NET_F_CSUM,
 VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
 VIRTIO_NET_F_CTRL_MAC_ADDR,
-- 
2.39.3




[PATCH v3 for 9.1 0/6] virtio, vhost: Add VIRTIO_F_NOTIFICATION_DATA support

2024-03-15 Thread Jonah Palmer
The goal of these patches are to add support to a variety of virtio and
vhost devices for the VIRTIO_F_NOTIFICATION_DATA transport feature. This
feature indicates that a driver will pass extra data (instead of just a
virtqueue's index) when notifying the corresponding device.

The data passed in by the driver when this feature is enabled varies in
format depending on if the device is using a split or packed virtqueue
layout:

 Split VQ
  - Upper 16 bits: shadow_avail_idx
  - Lower 16 bits: virtqueue index

 Packed VQ
  - Upper 16 bits: 1-bit wrap counter & 15-bit shadow_avail_idx
  - Lower 16 bits: virtqueue index

Also, due to the limitations of ioeventfd not being able to carry the
extra provided by the driver, having both VIRTIO_F_NOTIFICATION_DATA
feature and ioeventfd enabled is a functional mismatch. The user must
explicitly disable ioeventfd for the device in the Qemu arguments when
using this feature, else the device will fail to complete realization.

For example, a device must explicitly enable notification_data as well
as disable ioeventfd:

-device virtio-scsi-pci,...,ioeventfd=off,notification_data=on

A significant aspect of this effort has been to maintain compatibility
across different backends. As such, the feature is offered by backend
devices only when supported, with fallback mechanisms where backend
support is absent.

v3: Validate VQ idx via. virtio_queue_get_num() (pci, mmio, ccw)
Rename virtio_queue_set_shadow_avail_data
Only pass in upper 16 bits of 32-bit extra data (was redundant)
Make notification compatibility check function static
Drop tags on patches 1/6, 3/6, and 4/6

v2: Don't disable ioeventfd by default, user must disable it
Drop tags on patch 2/6

Jonah Palmer (6):
  virtio/virtio-pci: Handle extra notification data
  virtio: Prevent creation of device using notification-data with ioeventfd
  virtio-mmio: Handle extra notification data
  virtio-ccw: Handle extra notification data
  vhost/vhost-user: Add VIRTIO_F_NOTIFICATION_DATA to vhost feature bits
  virtio: Add VIRTIO_F_NOTIFICATION_DATA property definition

 hw/block/vhost-user-blk.c|  1 +
 hw/net/vhost_net.c   |  2 ++
 hw/s390x/s390-virtio-ccw.c   | 17 +++
 hw/scsi/vhost-scsi.c |  1 +
 hw/scsi/vhost-user-scsi.c|  1 +
 hw/virtio/vhost-user-fs.c|  2 +-
 hw/virtio/vhost-user-vsock.c |  1 +
 hw/virtio/virtio-mmio.c  | 10 +++--
 hw/virtio/virtio-pci.c   | 11 +++---
 hw/virtio/virtio.c   | 40 
 include/hw/virtio/virtio.h   |  6 +-
 net/vhost-vdpa.c |  1 +
 12 files changed, 82 insertions(+), 11 deletions(-)

-- 
2.39.3




[PATCH v3 for 9.1 4/6] virtio-ccw: Handle extra notification data

2024-03-15 Thread Jonah Palmer
Add support to virtio-ccw devices for handling the extra data sent from
the driver to the device when the VIRTIO_F_NOTIFICATION_DATA transport
feature has been negotiated.

The extra data that's passed to the virtio-ccw device when this feature
is enabled varies depending on the device's virtqueue layout.

That data passed to the virtio-ccw device is in the same format as the
data passed to virtio-pci devices.

Signed-off-by: Jonah Palmer 
---
 hw/s390x/s390-virtio-ccw.c | 17 +
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index b1dcb3857f..b550adfc68 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -140,9 +140,11 @@ static void subsystem_reset(void)
 static int virtio_ccw_hcall_notify(const uint64_t *args)
 {
 uint64_t subch_id = args[0];
-uint64_t queue = args[1];
+uint64_t data = args[1];
 SubchDev *sch;
+VirtIODevice *vdev;
 int cssid, ssid, schid, m;
+uint16_t vq_idx = data;
 
 if (ioinst_disassemble_sch_ident(subch_id, , , , )) {
 return -EINVAL;
@@ -151,12 +153,19 @@ static int virtio_ccw_hcall_notify(const uint64_t *args)
 if (!sch || !css_subch_visible(sch)) {
 return -EINVAL;
 }
-if (queue >= VIRTIO_QUEUE_MAX) {
+
+vdev = virtio_ccw_get_vdev(sch);
+if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) {
 return -EINVAL;
 }
-virtio_queue_notify(virtio_ccw_get_vdev(sch), queue);
-return 0;
 
+if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
+virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx),
+  (data >> 16) & 0x);
+}
+
+virtio_queue_notify(vdev, vq_idx);
+return 0;
 }
 
 static int virtio_ccw_hcall_early_printk(const uint64_t *args)
-- 
2.39.3




[PATCH v3 for 9.1 1/6] virtio/virtio-pci: Handle extra notification data

2024-03-15 Thread Jonah Palmer
Add support to virtio-pci devices for handling the extra data sent
from the driver to the device when the VIRTIO_F_NOTIFICATION_DATA
transport feature has been negotiated.

The extra data that's passed to the virtio-pci device when this
feature is enabled varies depending on the device's virtqueue
layout.

In a split virtqueue layout, this data includes:
 - upper 16 bits: shadow_avail_idx
 - lower 16 bits: virtqueue index

In a packed virtqueue layout, this data includes:
 - upper 16 bits: 1-bit wrap counter & 15-bit shadow_avail_idx
 - lower 16 bits: virtqueue index

Signed-off-by: Jonah Palmer 
---
 hw/virtio/virtio-pci.c | 11 ---
 hw/virtio/virtio.c | 18 ++
 include/hw/virtio/virtio.h |  2 ++
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index cb6940fc0e..f3e0a08f53 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -384,7 +384,7 @@ static void virtio_ioport_write(void *opaque, uint32_t 
addr, uint32_t val)
 {
 VirtIOPCIProxy *proxy = opaque;
 VirtIODevice *vdev = virtio_bus_get_device(>bus);
-uint16_t vector;
+uint16_t vector, vq_idx;
 hwaddr pa;
 
 switch (addr) {
@@ -408,8 +408,13 @@ static void virtio_ioport_write(void *opaque, uint32_t 
addr, uint32_t val)
 vdev->queue_sel = val;
 break;
 case VIRTIO_PCI_QUEUE_NOTIFY:
-if (val < VIRTIO_QUEUE_MAX) {
-virtio_queue_notify(vdev, val);
+vq_idx = val;
+if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) {
+if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
+virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, 
vq_idx),
+  val >> 16);
+}
+virtio_queue_notify(vdev, vq_idx);
 }
 break;
 case VIRTIO_PCI_STATUS:
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index d229755eae..463426ca92 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2255,6 +2255,24 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, 
int align)
 }
 }
 
+void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t 
shadow_avail_idx)
+{
+if (!vq->vring.desc) {
+return;
+}
+
+/*
+ * 16-bit data for packed VQs include 1-bit wrap counter and
+ * 15-bit shadow_avail_idx.
+ */
+if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
+vq->shadow_avail_wrap_counter = (shadow_avail_idx >> 15) & 0x1;
+vq->shadow_avail_idx = shadow_avail_idx & 0x7FFF;
+} else {
+vq->shadow_avail_idx = shadow_avail_idx;
+}
+}
+
 static void virtio_queue_notify_vq(VirtQueue *vq)
 {
 if (vq->vring.desc && vq->handle_output) {
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index c8f72850bc..cdd4f86b61 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -306,6 +306,8 @@ int virtio_queue_ready(VirtQueue *vq);
 
 int virtio_queue_empty(VirtQueue *vq);
 
+void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t idx);
+
 /* Host binding interface.  */
 
 uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr);
-- 
2.39.3




[PATCH v3 for 9.1 2/6] virtio: Prevent creation of device using notification-data with ioeventfd

2024-03-15 Thread Jonah Palmer
Prevent the realization of a virtio device that attempts to use the
VIRTIO_F_NOTIFICATION_DATA transport feature without disabling
ioeventfd.

Due to ioeventfd not being able to carry the extra data associated with
this feature, having both enabled is a functional mismatch and therefore
Qemu should not continue the device's realization process.

Although the device does not yet know if the feature will be
successfully negotiated, many devices using this feature wont actually
work without this extra data and would fail FEATURES_OK anyway.

If ioeventfd is able to work with the extra notification data in the
future, this compatibility check can be removed.

Signed-off-by: Jonah Palmer 
---
 hw/virtio/virtio.c | 22 ++
 1 file changed, 22 insertions(+)

diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 463426ca92..f9cb8d1e5c 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2971,6 +2971,20 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val)
 return ret;
 }
 
+static void virtio_device_check_notification_compatibility(VirtIODevice *vdev,
+   Error **errp)
+{
+VirtioBusState *bus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev)));
+VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus);
+DeviceState *proxy = DEVICE(BUS(bus)->parent);
+
+if (virtio_host_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA) &&
+k->ioeventfd_enabled(proxy)) {
+error_setg(errp,
+   "notification_data=on without ioeventfd=off is not 
supported");
+}
+}
+
 size_t virtio_get_config_size(const VirtIOConfigSizeParams *params,
   uint64_t host_features)
 {
@@ -3731,6 +3745,14 @@ static void virtio_device_realize(DeviceState *dev, 
Error **errp)
 }
 }
 
+/* Devices should not use both ioeventfd and notification data feature */
+virtio_device_check_notification_compatibility(vdev, );
+if (err != NULL) {
+error_propagate(errp, err);
+vdc->unrealize(dev);
+return;
+}
+
 virtio_bus_device_plugged(vdev, );
 if (err != NULL) {
 error_propagate(errp, err);
-- 
2.39.3




[PATCH v3 for 9.1 3/6] virtio-mmio: Handle extra notification data

2024-03-15 Thread Jonah Palmer
Add support to virtio-mmio devices for handling the extra data sent from
the driver to the device when the VIRTIO_F_NOTIFICATION_DATA transport
feature has been negotiated.

The extra data that's passed to the virtio-mmio device when this feature
is enabled varies depending on the device's virtqueue layout.

The data passed to the virtio-mmio device is in the same format as the
data passed to virtio-pci devices.

Signed-off-by: Jonah Palmer 
---
 hw/virtio/virtio-mmio.c | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 22f9fbcf5a..003c363f0b 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -248,6 +248,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, 
uint64_t value,
 {
 VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
 VirtIODevice *vdev = virtio_bus_get_device(>bus);
+uint16_t vq_idx;
 
 trace_virtio_mmio_write_offset(offset, value);
 
@@ -407,8 +408,13 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, 
uint64_t value,
 }
 break;
 case VIRTIO_MMIO_QUEUE_NOTIFY:
-if (value < VIRTIO_QUEUE_MAX) {
-virtio_queue_notify(vdev, value);
+vq_idx = value;
+if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) {
+if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
+virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, 
vq_idx),
+  (value >> 16) & 0x);
+}
+virtio_queue_notify(vdev, vq_idx);
 }
 break;
 case VIRTIO_MMIO_INTERRUPT_ACK:
-- 
2.39.3




Re: [PATCH] scsi-generic: fix io_timeout property not applying

2024-03-15 Thread Alex Bennée
Lorenz Brun  writes:

> The io_timeout property, introduced in c9b6609 (part of 6.0) is
> silently overwritten by the hardcoded default value of 30 seconds
> (DEFAULT_IO_TIMEOUT) in scsi_generic_realize because that function is
> being called after the properties have already been applied.
>
> The property definition already has a default value which is applied
> correctly when no value is explicitly set, so we can just remove the
> code which overrides the io_timeout completely.
>
> This has been tested by stracing SG_IO operations with the io_timeout
> property set and unset and now sets the timeout field in the ioctl
> request to the proper value.
>
> Fixes: c9b6609b69facad ("scsi: make io_timeout configurable")
> Signed-off-by: Lorenz Brun 
> ---
>  hw/scsi/scsi-generic.c | 1 -
>  1 file changed, 1 deletion(-)
>
> diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
> index b7b04e1d63..ee945f87e3 100644
> --- a/hw/scsi/scsi-generic.c
> +++ b/hw/scsi/scsi-generic.c
> @@ -752,7 +752,6 @@ static void scsi_generic_realize(SCSIDevice *s, Error 
> **errp)
>  
>  /* Only used by scsi-block, but initialize it nevertheless to be clean.  
> */
>  s->default_scsi_version = -1;
> -s->io_timeout = DEFAULT_IO_TIMEOUT;
>  scsi_generic_read_device_inquiry(s);
>  }

Reviewed-by: Alex Bennée 


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



Re: [PATCH v5 24/25] qapi: Tighten check whether implicit object type already exists

2024-03-15 Thread Philippe Mathieu-Daudé

On 15/3/24 16:23, Markus Armbruster wrote:

Entities with names starting with q_obj_ are implicit object types.
Therefore, QAPISchema._make_implicit_object_type()'s .lookup_entity()
can only return a QAPISchemaObjectType.  Assert that.

Signed-off-by: Markus Armbruster 
---
  scripts/qapi/schema.py | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)


Reviewed-by: Philippe Mathieu-Daudé 





[PATCH] scsi-generic: fix io_timeout property not applying

2024-03-15 Thread Lorenz Brun
The io_timeout property, introduced in c9b6609 (part of 6.0) is
silently overwritten by the hardcoded default value of 30 seconds
(DEFAULT_IO_TIMEOUT) in scsi_generic_realize because that function is
being called after the properties have already been applied.

The property definition already has a default value which is applied
correctly when no value is explicitly set, so we can just remove the
code which overrides the io_timeout completely.

This has been tested by stracing SG_IO operations with the io_timeout
property set and unset and now sets the timeout field in the ioctl
request to the proper value.

Fixes: c9b6609b69facad ("scsi: make io_timeout configurable")
Signed-off-by: Lorenz Brun 
---
 hw/scsi/scsi-generic.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index b7b04e1d63..ee945f87e3 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -752,7 +752,6 @@ static void scsi_generic_realize(SCSIDevice *s, Error 
**errp)
 
 /* Only used by scsi-block, but initialize it nevertheless to be clean.  */
 s->default_scsi_version = -1;
-s->io_timeout = DEFAULT_IO_TIMEOUT;
 scsi_generic_read_device_inquiry(s);
 }
 
-- 
2.42.0




Re: [RFC PATCH v3 3/3] migration: Add fd to FileMigrationArgs

2024-03-15 Thread Peter Xu
[I queued patch 1-2 into -stable, leaving this patch for further
 discussions]

On Fri, Mar 15, 2024 at 08:55:42AM +, Daniel P. Berrangé wrote:
> The 'file:' protocol eventually calls into qemu_open, and this
> transparently allows for FD passing using /dev/fdset/NNN syntax
> to pass in FDs. 

If it always use /dev/fdsets for files, does it mean that the newly added
SOCKET_ADDRESS_TYPE_FD support on mapped-ram will never be used (so we can
drop them)?

What about the old getfd?  Is it obsolete because it relies on monitor
object?  Or maybe it's still in use?

It would be greatly helpful if there can be a summary of how libvirt uses
fd for migration purpose.

Thanks,

-- 
Peter Xu




[PATCH for-9.0 v3] vdpa-dev: Fix initialisation order to restore VDUSE compatibility

2024-03-15 Thread Kevin Wolf
VDUSE requires that virtqueues are first enabled before the DRIVER_OK
status flag is set; with the current API of the kernel module, it is
impossible to enable the opposite order in our block export code because
userspace is not notified when a virtqueue is enabled.

This requirement also mathces the normal initialisation order as done by
the generic vhost code in QEMU. However, commit 6c482547 accidentally
changed the order for vdpa-dev and broke access to VDUSE devices with
this.

This changes vdpa-dev to use the normal order again and use the standard
vhost callback .vhost_set_vring_enable for this. VDUSE devices can be
used with vdpa-dev again after this fix.

vhost_net intentionally avoided enabling the vrings for vdpa and does
this manually later while it does enable them for other vhost backends.
Reflect this in the vhost_net code and return early for vdpa, so that
the behaviour doesn't change for this device.

Cc: qemu-sta...@nongnu.org
Fixes: 6c4825476a4351530bcac17abab72295b75ffe98
Signed-off-by: Kevin Wolf 
---
v2:
- Actually make use of the @enable parameter
- Change vhost_net to preserve the current behaviour

v3:
- Updated trace point [Stefano]
- Fixed typo in comment [Stefano]

 hw/net/vhost_net.c | 10 ++
 hw/virtio/vdpa-dev.c   |  5 +
 hw/virtio/vhost-vdpa.c | 29 ++---
 hw/virtio/vhost.c  |  8 +++-
 hw/virtio/trace-events |  2 +-
 5 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e8e1661646..fd1a93701a 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -541,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int enable)
 VHostNetState *net = get_vhost_net(nc);
 const VhostOps *vhost_ops = net->dev.vhost_ops;
 
+/*
+ * vhost-vdpa network devices need to enable dataplane virtqueues after
+ * DRIVER_OK, so they can recover device state before starting dataplane.
+ * Because of that, we don't enable virtqueues here and leave it to
+ * net/vhost-vdpa.c.
+ */
+if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+return 0;
+}
+
 nc->vring_enable = enable;
 
 if (vhost_ops && vhost_ops->vhost_set_vring_enable) {
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index eb9ecea83b..13e87f06f6 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -253,14 +253,11 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, 
Error **errp)
 
 s->dev.acked_features = vdev->guest_features;
 
-ret = vhost_dev_start(>dev, vdev, false);
+ret = vhost_dev_start(>dev, vdev, true);
 if (ret < 0) {
 error_setg_errno(errp, -ret, "Error starting vhost");
 goto err_guest_notifiers;
 }
-for (i = 0; i < s->dev.nvqs; ++i) {
-vhost_vdpa_set_vring_ready(>vdpa, i);
-}
 s->started = true;
 
 /*
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index ddae494ca8..31453466af 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -886,19 +886,41 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, 
int idx)
 return idx;
 }
 
-int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned idx,
+   int enable)
 {
 struct vhost_dev *dev = v->dev;
 struct vhost_vring_state state = {
 .index = idx,
-.num = 1,
+.num = enable,
 };
 int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, );
 
-trace_vhost_vdpa_set_vring_ready(dev, idx, r);
+trace_vhost_vdpa_set_vring_enable_one(dev, idx, enable, r);
 return r;
 }
 
+static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+struct vhost_vdpa *v = dev->opaque;
+unsigned int i;
+int ret;
+
+for (i = 0; i < dev->nvqs; ++i) {
+ret = vhost_vdpa_set_vring_enable_one(v, i, enable);
+if (ret < 0) {
+return ret;
+}
+}
+
+return 0;
+}
+
+int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+{
+return vhost_vdpa_set_vring_enable_one(v, idx, 1);
+}
+
 static int vhost_vdpa_set_config_call(struct vhost_dev *dev,
int fd)
 {
@@ -1514,6 +1536,7 @@ const VhostOps vdpa_ops = {
 .vhost_set_features = vhost_vdpa_set_features,
 .vhost_reset_device = vhost_vdpa_reset_device,
 .vhost_get_vq_index = vhost_vdpa_get_vq_index,
+.vhost_set_vring_enable = vhost_vdpa_set_vring_enable,
 .vhost_get_config  = vhost_vdpa_get_config,
 .vhost_set_config = vhost_vdpa_set_config,
 .vhost_requires_shm_log = NULL,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 2c9ac79468..a66186 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1984,7 +1984,13 @@ static int vhost_dev_set_vring_enable(struct vhost_dev 
*hdev, int enable)
 return 

Re: [PATCH for-9.0 v2] vdpa-dev: Fix initialisation order to restore VDUSE compatibility

2024-03-15 Thread Kevin Wolf
Am 15.03.2024 um 16:07 hat Stefano Garzarella geschrieben:
> On Fri, Mar 15, 2024 at 03:03:31PM +0100, Kevin Wolf wrote:
> > VDUSE requires that virtqueues are first enabled before the DRIVER_OK
> > status flag is set; with the current API of the kernel module, it is
> > impossible to enable the opposite order in our block export code because
> > userspace is not notified when a virtqueue is enabled.
> > 
> > This requirement also mathces the normal initialisation order as done by
> > the generic vhost code in QEMU. However, commit 6c482547 accidentally
> > changed the order for vdpa-dev and broke access to VDUSE devices with
> > this.
> > 
> > This changes vdpa-dev to use the normal order again and use the standard
> > vhost callback .vhost_set_vring_enable for this. VDUSE devices can be
> > used with vdpa-dev again after this fix.
> > 
> > vhost_net intentionally avoided enabling the vrings for vdpa and does
> > this manually later while it does enable them for other vhost backends.
> > Reflect this in the vhost_net code and return early for vdpa, so that
> > the behaviour doesn't change for this device.
> > 
> > Cc: qemu-sta...@nongnu.org
> > Fixes: 6c4825476a4351530bcac17abab72295b75ffe98
> > Signed-off-by: Kevin Wolf 
> > ---
> > v2:
> > - Actually make use of the @enable parameter
> > - Change vhost_net to preserve the current behaviour
> > 
> > hw/net/vhost_net.c | 10 ++
> > hw/virtio/vdpa-dev.c   |  5 +
> > hw/virtio/vhost-vdpa.c | 27 +--
> > hw/virtio/vhost.c  |  8 +++-
> > 4 files changed, 43 insertions(+), 7 deletions(-)
> > 
> > diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
> > index e8e1661646..fd1a93701a 100644
> > --- a/hw/net/vhost_net.c
> > +++ b/hw/net/vhost_net.c
> > @@ -541,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int 
> > enable)
> > VHostNetState *net = get_vhost_net(nc);
> > const VhostOps *vhost_ops = net->dev.vhost_ops;
> > 
> > +/*
> > + * vhost-vdpa network devices need to enable dataplane virtqueues after
> > + * DRIVER_OK, so they can recover device state before starting 
> > dataplane.
> > + * Because of that, we don't enable virtqueues here and leave it to
> > + * net/vhost-vdpa.c.
> > + */
> > +if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
> > +return 0;
> > +}
> > +
> > nc->vring_enable = enable;
> > 
> > if (vhost_ops && vhost_ops->vhost_set_vring_enable) {
> > diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
> > index eb9ecea83b..13e87f06f6 100644
> > --- a/hw/virtio/vdpa-dev.c
> > +++ b/hw/virtio/vdpa-dev.c
> > @@ -253,14 +253,11 @@ static int vhost_vdpa_device_start(VirtIODevice 
> > *vdev, Error **errp)
> > 
> > s->dev.acked_features = vdev->guest_features;
> > 
> > -ret = vhost_dev_start(>dev, vdev, false);
> > +ret = vhost_dev_start(>dev, vdev, true);
> > if (ret < 0) {
> > error_setg_errno(errp, -ret, "Error starting vhost");
> > goto err_guest_notifiers;
> > }
> > -for (i = 0; i < s->dev.nvqs; ++i) {
> > -vhost_vdpa_set_vring_ready(>vdpa, i);
> > -}
> > s->started = true;
> > 
> > /*
> > diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
> > index ddae494ca8..401afac2f5 100644
> > --- a/hw/virtio/vhost-vdpa.c
> > +++ b/hw/virtio/vhost-vdpa.c
> > @@ -886,12 +886,13 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev 
> > *dev, int idx)
> > return idx;
> > }
> > 
> > -int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
> > +static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned 
> > idx,
> > +   int enable)
> > {
> > struct vhost_dev *dev = v->dev;
> > struct vhost_vring_state state = {
> > .index = idx,
> > -.num = 1,
> > +.num = enable,
> > };
> > int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, );
> > 
> 
> After this line we now have:
> 
>   trace_vhost_vdpa_set_vring_ready(dev, idx, r);
> 
> Should we rename it or move it to the new function?
> 
> If we rename it, we should trace also `enable`.

I think renaming is better so that we cover all code paths that enable a
vring. I'll change this and send a v3.

> > @@ -899,6 +900,27 @@ int vhost_vdpa_set_vring_ready(struct vhost_vdpa
> > *v, unsigned idx)
> > return r;
> > }
> > 
> > +static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable)
> > +{
> > +struct vhost_vdpa *v = dev->opaque;
> > +unsigned int i;
> > +int ret;
> > +
> > +for (i = 0; i < dev->nvqs; ++i) {
> > +ret = vhost_vdpa_set_vring_enable_one(v, i, enable);
> > +if (ret < 0) {
> > +return ret;
> > +}
> > +}
> > +
> > +return 0;
> > +}
> > +
> > +int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
> > +{
> > +return vhost_vdpa_set_vring_enable_one(v, idx, 1);
> > +}
> > +
> > static int 

[PATCH v5 11/25] qapi/schema: assert resolve_type has 'info' and 'what' args on error

2024-03-15 Thread Markus Armbruster
From: John Snow 

resolve_type() is generally used to resolve configuration-provided type
names into type objects, and generally requires valid 'info' and 'what'
parameters.

In some cases, such as with QAPISchemaArrayType.check(), resolve_type
may be used to resolve built-in types and as such will not have an
'info' argument, but also must not fail in this scenario.

Use an assertion to sate mypy that we will indeed have 'info' and 'what'
parameters for the error pathway in resolve_type.

Note: there are only three callsites to resolve_type at present where
"info" is perceived by mypy to be possibly None:

1) QAPISchemaArrayType.check()
2) QAPISchemaObjectTypeMember.check()
3) QAPISchemaEvent.check()

Of those three, only the first actually ever passes None; the other two
are limited by their base class initializers which accept info=None, but
neither subclass actually use a None value in practice, currently.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 1034825415..0ef9b3398a 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -996,6 +996,7 @@ def lookup_type(self, name):
 def resolve_type(self, name, info, what):
 typ = self.lookup_type(name)
 if not typ:
+assert info and what  # built-in types must not fail lookup
 if callable(what):
 what = what(info)
 raise QAPISemError(
-- 
2.44.0




[PATCH v5 05/25] qapi: create QAPISchemaDefinition

2024-03-15 Thread Markus Armbruster
From: John Snow 

Include entities don't have names, but we generally expect "entities" to
have names. Reclassify all entities with names as *definitions*, leaving
the nameless include entities as QAPISchemaEntity instances.

This is primarily to help simplify typing around expectations of what
callers expect for properties of an "entity".

Suggested-by: Markus Armbruster 
Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 144 +++--
 1 file changed, 82 insertions(+), 62 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 117f0f78f0..b298c8b4f9 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -55,14 +55,13 @@ def is_present(self):
 
 
 class QAPISchemaEntity:
-meta: Optional[str] = None
+"""
+A schema entity.
 
-def __init__(self, name: str, info, doc, ifcond=None, features=None):
-assert name is None or isinstance(name, str)
-for f in features or []:
-assert isinstance(f, QAPISchemaFeature)
-f.set_defined_in(name)
-self.name = name
+This is either a directive, such as include, or a definition.
+The latter uses sub-class `QAPISchemaDefinition`.
+"""
+def __init__(self, info):
 self._module = None
 # For explicitly defined entities, info points to the (explicit)
 # definition.  For builtins (and their arrays), info is None.
@@ -70,33 +69,17 @@ def __init__(self, name: str, info, doc, ifcond=None, 
features=None):
 # triggered the implicit definition (there may be more than one
 # such place).
 self.info = info
-self.doc = doc
-self._ifcond = ifcond or QAPISchemaIfCond()
-self.features = features or []
 self._checked = False
 
 def __repr__(self):
-if self.name is None:
-return "<%s at 0x%x>" % (type(self).__name__, id(self))
-return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
-id(self))
-
-def c_name(self):
-return c_name(self.name)
+return "<%s at 0x%x>" % (type(self).__name__, id(self))
 
 def check(self, schema):
 # pylint: disable=unused-argument
-assert not self._checked
-seen = {}
-for f in self.features:
-f.check_clash(self.info, seen)
 self._checked = True
 
 def connect_doc(self, doc=None):
-doc = doc or self.doc
-if doc:
-for f in self.features:
-doc.connect_feature(f)
+pass
 
 def _set_module(self, schema, info):
 assert self._checked
@@ -107,6 +90,46 @@ def _set_module(self, schema, info):
 def set_module(self, schema):
 self._set_module(schema, self.info)
 
+def visit(self, visitor):
+# pylint: disable=unused-argument
+assert self._checked
+
+
+class QAPISchemaDefinition(QAPISchemaEntity):
+meta: Optional[str] = None
+
+def __init__(self, name: str, info, doc, ifcond=None, features=None):
+assert isinstance(name, str)
+super().__init__(info)
+for f in features or []:
+assert isinstance(f, QAPISchemaFeature)
+f.set_defined_in(name)
+self.name = name
+self.doc = doc
+self._ifcond = ifcond or QAPISchemaIfCond()
+self.features = features or []
+
+def __repr__(self):
+return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
+id(self))
+
+def c_name(self):
+return c_name(self.name)
+
+def check(self, schema):
+assert not self._checked
+super().check(schema)
+seen = {}
+for f in self.features:
+f.check_clash(self.info, seen)
+
+def connect_doc(self, doc=None):
+super().connect_doc(doc)
+doc = doc or self.doc
+if doc:
+for f in self.features:
+doc.connect_feature(f)
+
 @property
 def ifcond(self):
 assert self._checked
@@ -115,10 +138,6 @@ def ifcond(self):
 def is_implicit(self):
 return not self.info
 
-def visit(self, visitor):
-# pylint: disable=unused-argument
-assert self._checked
-
 def describe(self):
 assert self.meta
 return "%s '%s'" % (self.meta, self.name)
@@ -218,7 +237,7 @@ def visit(self, visitor):
 
 class QAPISchemaInclude(QAPISchemaEntity):
 def __init__(self, sub_module, info):
-super().__init__(None, info, None)
+super().__init__(info)
 self._sub_module = sub_module
 
 def visit(self, visitor):
@@ -226,7 +245,7 @@ def visit(self, visitor):
 visitor.visit_include(self._sub_module.name, self.info)
 
 
-class QAPISchemaType(QAPISchemaEntity):
+class QAPISchemaType(QAPISchemaDefinition):
 # Return the C type for common use.
 # For the types we commonly box, this is 

[PATCH v5 23/25] qapi/schema: remove unnecessary asserts

2024-03-15 Thread Markus Armbruster
From: John Snow 

With strict typing enabled, these runtime statements aren't necessary
anymore; we can prove them statically.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 25 -
 1 file changed, 25 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index ef4d6a7def..e52930a48a 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -123,10 +123,8 @@ def __init__(
 ifcond: Optional[QAPISchemaIfCond] = None,
 features: Optional[List[QAPISchemaFeature]] = None,
 ):
-assert isinstance(name, str)
 super().__init__(info)
 for f in features or []:
-assert isinstance(f, QAPISchemaFeature)
 f.set_defined_in(name)
 self.name = name
 self.doc = doc
@@ -163,7 +161,6 @@ def is_implicit(self) -> bool:
 return not self.info
 
 def describe(self) -> str:
-assert self.meta
 return "%s '%s'" % (self.meta, self.name)
 
 
@@ -377,7 +374,6 @@ def check(self, schema: QAPISchema) -> None:
 f"feature '{feat.name}' is not supported for types")
 
 def describe(self) -> str:
-assert self.meta
 return "%s type '%s'" % (self.meta, self.name)
 
 
@@ -386,7 +382,6 @@ class QAPISchemaBuiltinType(QAPISchemaType):
 
 def __init__(self, name: str, json_type: str, c_type: str):
 super().__init__(name, None, None)
-assert not c_type or isinstance(c_type, str)
 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
  'value')
 self._json_type_name = json_type
@@ -429,9 +424,7 @@ def __init__(
 ):
 super().__init__(name, info, doc, ifcond, features)
 for m in members:
-assert isinstance(m, QAPISchemaEnumMember)
 m.set_defined_in(name)
-assert prefix is None or isinstance(prefix, str)
 self.members = members
 self.prefix = prefix
 
@@ -474,7 +467,6 @@ def __init__(
 self, name: str, info: Optional[QAPISourceInfo], element_type: str
 ):
 super().__init__(name, info, None)
-assert isinstance(element_type, str)
 self._element_type_name = element_type
 self.element_type: QAPISchemaType
 
@@ -519,7 +511,6 @@ def visit(self, visitor: QAPISchemaVisitor) -> None:
  self.element_type)
 
 def describe(self) -> str:
-assert self.meta
 return "%s type ['%s']" % (self.meta, self._element_type_name)
 
 
@@ -539,12 +530,9 @@ def __init__(
 # union has base, variants, and no local_members
 super().__init__(name, info, doc, ifcond, features)
 self.meta = 'union' if variants else 'struct'
-assert base is None or isinstance(base, str)
 for m in local_members:
-assert isinstance(m, QAPISchemaObjectTypeMember)
 m.set_defined_in(name)
 if variants is not None:
-assert isinstance(variants, QAPISchemaVariants)
 variants.set_defined_in(name)
 self._base_name = base
 self.base = None
@@ -666,7 +654,6 @@ def __init__(
 variants: QAPISchemaVariants,
 ):
 super().__init__(name, info, doc, ifcond, features)
-assert isinstance(variants, QAPISchemaVariants)
 assert variants.tag_member
 variants.set_defined_in(name)
 variants.tag_member.set_defined_in(self.name)
@@ -742,8 +729,6 @@ def __init__(
 assert bool(tag_member) != bool(tag_name)
 assert (isinstance(tag_name, str) or
 isinstance(tag_member, QAPISchemaObjectTypeMember))
-for v in variants:
-assert isinstance(v, QAPISchemaVariant)
 self._tag_name = tag_name
 self.info = info
 self._tag_member = tag_member
@@ -858,7 +843,6 @@ def __init__(
 info: Optional[QAPISourceInfo],
 ifcond: Optional[QAPISchemaIfCond] = None,
 ):
-assert isinstance(name, str)
 self.name = name
 self.info = info
 self.ifcond = ifcond or QAPISchemaIfCond()
@@ -926,7 +910,6 @@ def __init__(
 ):
 super().__init__(name, info, ifcond)
 for f in features or []:
-assert isinstance(f, QAPISchemaFeature)
 f.set_defined_in(name)
 self.features = features or []
 
@@ -955,10 +938,7 @@ def __init__(
 features: Optional[List[QAPISchemaFeature]] = None,
 ):
 super().__init__(name, info, ifcond)
-assert isinstance(typ, str)
-assert isinstance(optional, bool)
 for f in features or []:
-assert isinstance(f, QAPISchemaFeature)
 f.set_defined_in(name)
 self._type_name = typ
 self.type: QAPISchemaType  # set during check()
@@ -966,7 +946,6 @@ def __init__(
 self.features = features or []
 
 def need_has(self) -> bool:
-  

[PATCH v5 20/25] qapi/parser.py: assert member.info is present in connect_member

2024-03-15 Thread Markus Armbruster
From: John Snow 

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/parser.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index 2f3c704fa2..7b13a583ac 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -707,6 +707,7 @@ def append_line(self, line: str) -> None:
 
 def connect_member(self, member: 'QAPISchemaMember') -> None:
 if member.name not in self.args:
+assert member.info
 if self.symbol not in member.info.pragma.documentation_exceptions:
 raise QAPISemError(member.info,
"%s '%s' lacks documentation"
-- 
2.44.0




[PATCH v5 17/25] qapi/schema: fix typing for QAPISchemaVariants.tag_member

2024-03-15 Thread Markus Armbruster
From: John Snow 

There are two related changes here:

(1) We need to perform type narrowing for resolving the type of
tag_member during check(), and

(2) tag_member is a delayed initialization field, but we can hide it
behind a property that raises an Exception if it's called too
early. This simplifies the typing in quite a few places and avoids
needing to assert that the "tag_member is not None" at a dozen
callsites, which can be confusing and suggest the wrong thing to a
drive-by contributor.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 22 +++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 74b0d7b007..9c138badb0 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -627,25 +627,39 @@ def __init__(self, tag_name, info, tag_member, variants):
 assert isinstance(v, QAPISchemaVariant)
 self._tag_name = tag_name
 self.info = info
-self.tag_member = tag_member
+self._tag_member: Optional[QAPISchemaObjectTypeMember] = tag_member
 self.variants = variants
 
+@property
+def tag_member(self) -> 'QAPISchemaObjectTypeMember':
+if self._tag_member is None:
+raise RuntimeError(
+"QAPISchemaVariants has no tag_member property until "
+"after check() has been run."
+)
+return self._tag_member
+
 def set_defined_in(self, name):
 for v in self.variants:
 v.set_defined_in(name)
 
 def check(self, schema, seen):
 if self._tag_name:  # union
-self.tag_member = seen.get(c_name(self._tag_name))
+# We need to narrow the member type:
+tmp = seen.get(c_name(self._tag_name))
+assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember)
+self._tag_member = tmp
+
 base = "'base'"
 # Pointing to the base type when not implicit would be
 # nice, but we don't know it here
-if not self.tag_member or self._tag_name != self.tag_member.name:
+if not self._tag_member or self._tag_name != self._tag_member.name:
 raise QAPISemError(
 self.info,
 "discriminator '%s' is not a member of %s"
 % (self._tag_name, base))
 # Here we do:
+assert self.tag_member.defined_in
 base_type = schema.lookup_type(self.tag_member.defined_in)
 assert base_type
 if not base_type.is_implicit():
@@ -666,11 +680,13 @@ def check(self, schema, seen):
 "discriminator member '%s' of %s must not be conditional"
 % (self._tag_name, base))
 else:   # alternate
+assert self._tag_member
 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
 assert not self.tag_member.optional
 assert not self.tag_member.ifcond.is_present()
 if self._tag_name:  # union
 # branches that are not explicitly covered get an empty type
+assert self.tag_member.defined_in
 cases = {v.name for v in self.variants}
 for m in self.tag_member.type.members:
 if m.name not in cases:
-- 
2.44.0




[PATCH v5 22/25] qapi/schema: turn on mypy strictness

2024-03-15 Thread Markus Armbruster
From: John Snow 

This patch can be rolled in with the previous one once the series is
ready for merge, but for work-in-progress' sake, it's separate here.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/mypy.ini | 5 -
 1 file changed, 5 deletions(-)

diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini
index 56e0dfb132..8109470a03 100644
--- a/scripts/qapi/mypy.ini
+++ b/scripts/qapi/mypy.ini
@@ -2,8 +2,3 @@
 strict = True
 disallow_untyped_calls = False
 python_version = 3.8
-
-[mypy-qapi.schema]
-disallow_untyped_defs = False
-disallow_incomplete_defs = False
-check_untyped_defs = False
-- 
2.44.0




[PATCH v5 08/25] qapi/schema: make c_type() and json_type() abstract methods

2024-03-15 Thread Markus Armbruster
From: John Snow 

These methods should always return a str, it's only the default abstract
implementation that doesn't. They can be marked "abstract", which
requires subclasses to override the method with the proper return type.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 48f157fb91..0b01c841ff 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -16,6 +16,7 @@
 
 # TODO catching name collisions in generated code would be nice
 
+from abc import ABC, abstractmethod
 from collections import OrderedDict
 import os
 import re
@@ -245,9 +246,10 @@ def visit(self, visitor):
 visitor.visit_include(self._sub_module.name, self.info)
 
 
-class QAPISchemaType(QAPISchemaDefinition):
+class QAPISchemaType(QAPISchemaDefinition, ABC):
 # Return the C type for common use.
 # For the types we commonly box, this is a pointer type.
+@abstractmethod
 def c_type(self):
 pass
 
@@ -259,6 +261,7 @@ def c_param_type(self):
 def c_unboxed_type(self):
 return self.c_type()
 
+@abstractmethod
 def json_type(self):
 pass
 
-- 
2.44.0




[PATCH v5 03/25] qapi: sort pylint suppressions

2024-03-15 Thread Markus Armbruster
From: John Snow 

Suggested-by: Markus Armbruster 
Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/pylintrc | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc
index 90546df534..1342412c3c 100644
--- a/scripts/qapi/pylintrc
+++ b/scripts/qapi/pylintrc
@@ -16,13 +16,13 @@ ignore-patterns=schema.py,
 # --enable=similarities". If you want to run only the classes checker, but have
 # no Warning level messages displayed, use "--disable=all --enable=classes
 # --disable=W".
-disable=fixme,
+disable=consider-using-f-string,
+fixme,
 missing-docstring,
 too-many-arguments,
 too-many-branches,
-too-many-statements,
 too-many-instance-attributes,
-consider-using-f-string,
+too-many-statements,
 useless-option-value,
 
 [REPORTS]
-- 
2.44.0




[PATCH v5 21/25] qapi/schema: add type hints

2024-03-15 Thread Markus Armbruster
From: John Snow 

This patch only adds type hints, which aren't utilized at runtime and
don't change the behavior of this module in any way.

In a scant few locations, type hints are removed where no longer
necessary due to inference power from typing all of the rest of
creation; and any type hints that no longer need string quotes are
changed.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 568 -
 1 file changed, 396 insertions(+), 172 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 177bfa0d11..ef4d6a7def 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -16,11 +16,21 @@
 
 # TODO catching name collisions in generated code would be nice
 
+from __future__ import annotations
+
 from abc import ABC, abstractmethod
 from collections import OrderedDict
 import os
 import re
-from typing import List, Optional, cast
+from typing import (
+Any,
+Callable,
+Dict,
+List,
+Optional,
+Union,
+cast,
+)
 
 from .common import (
 POINTER_SUFFIX,
@@ -32,26 +42,30 @@
 )
 from .error import QAPIError, QAPISemError, QAPISourceError
 from .expr import check_exprs
-from .parser import QAPIExpression, QAPISchemaParser
+from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser
+from .source import QAPISourceInfo
 
 
 class QAPISchemaIfCond:
-def __init__(self, ifcond=None):
+def __init__(
+self,
+ifcond: Optional[Union[str, Dict[str, object]]] = None,
+) -> None:
 self.ifcond = ifcond
 
-def _cgen(self):
+def _cgen(self) -> str:
 return cgen_ifcond(self.ifcond)
 
-def gen_if(self):
+def gen_if(self) -> str:
 return gen_if(self._cgen())
 
-def gen_endif(self):
+def gen_endif(self) -> str:
 return gen_endif(self._cgen())
 
-def docgen(self):
+def docgen(self) -> str:
 return docgen_ifcond(self.ifcond)
 
-def is_present(self):
+def is_present(self) -> bool:
 return bool(self.ifcond)
 
 
@@ -62,8 +76,8 @@ class QAPISchemaEntity:
 This is either a directive, such as include, or a definition.
 The latter uses sub-class `QAPISchemaDefinition`.
 """
-def __init__(self, info):
-self._module = None
+def __init__(self, info: Optional[QAPISourceInfo]):
+self._module: Optional[QAPISchemaModule] = None
 # For explicitly defined entities, info points to the (explicit)
 # definition.  For builtins (and their arrays), info is None.
 # For implicitly defined entities, info points to a place that
@@ -72,34 +86,43 @@ def __init__(self, info):
 self.info = info
 self._checked = False
 
-def __repr__(self):
+def __repr__(self) -> str:
 return "<%s at 0x%x>" % (type(self).__name__, id(self))
 
-def check(self, schema):
+def check(self, schema: QAPISchema) -> None:
 # pylint: disable=unused-argument
 self._checked = True
 
-def connect_doc(self, doc=None):
+def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
 pass
 
-def _set_module(self, schema, info):
+def _set_module(
+self, schema: QAPISchema, info: Optional[QAPISourceInfo]
+) -> None:
 assert self._checked
 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
 self._module = schema.module_by_fname(fname)
 self._module.add_entity(self)
 
-def set_module(self, schema):
+def set_module(self, schema: QAPISchema) -> None:
 self._set_module(schema, self.info)
 
-def visit(self, visitor):
+def visit(self, visitor: QAPISchemaVisitor) -> None:
 # pylint: disable=unused-argument
 assert self._checked
 
 
 class QAPISchemaDefinition(QAPISchemaEntity):
-meta: Optional[str] = None
+meta: str
 
-def __init__(self, name: str, info, doc, ifcond=None, features=None):
+def __init__(
+self,
+name: str,
+info: Optional[QAPISourceInfo],
+doc: Optional[QAPIDoc],
+ifcond: Optional[QAPISchemaIfCond] = None,
+features: Optional[List[QAPISchemaFeature]] = None,
+):
 assert isinstance(name, str)
 super().__init__(info)
 for f in features or []:
@@ -110,21 +133,21 @@ def __init__(self, name: str, info, doc, ifcond=None, 
features=None):
 self._ifcond = ifcond or QAPISchemaIfCond()
 self.features = features or []
 
-def __repr__(self):
+def __repr__(self) -> str:
 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
 id(self))
 
-def c_name(self):
+def c_name(self) -> str:
 return c_name(self.name)
 
-def check(self, schema):
+def check(self, schema: QAPISchema) -> None:
 assert not self._checked
 super().check(schema)
-seen = {}
+seen: Dict[str, 

[PATCH v5 25/25] qapi: Dumb down QAPISchema.lookup_entity()

2024-03-15 Thread Markus Armbruster
QAPISchema.lookup_entity() takes an optional type argument, a subtype
of QAPISchemaDefinition, and returns that type or None.  Callers can
use this to save themselves an isinstance() test.

The only remaining user of this convenience feature is .lookup_type().
But we don't actually save anything anymore there: we still the
isinstance() to help mypy over the hump.

Drop the .lookup_entity() argument, and adjust .lookup_type().

Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 18 ++
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index a6180f93c6..5924947fc3 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -1157,20 +1157,14 @@ def _def_definition(self, defn: QAPISchemaDefinition) 
-> None:
 defn.info, "%s is already defined" % other_defn.describe())
 self._entity_dict[defn.name] = defn
 
-def lookup_entity(
-self,
-name: str,
-typ: Optional[type] = None,
-) -> Optional[QAPISchemaDefinition]:
-ent = self._entity_dict.get(name)
-if typ and not isinstance(ent, typ):
-return None
-return ent
+def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]:
+return self._entity_dict.get(name)
 
 def lookup_type(self, name: str) -> Optional[QAPISchemaType]:
-typ = self.lookup_entity(name, QAPISchemaType)
-assert typ is None or isinstance(typ, QAPISchemaType)
-return typ
+typ = self.lookup_entity(name)
+if isinstance(typ, QAPISchemaType):
+return typ
+return None
 
 def resolve_type(
 self,
-- 
2.44.0




[PATCH v5 14/25] qapi/schema: assert info is present when necessary

2024-03-15 Thread Markus Armbruster
From: John Snow 

QAPISchemaInfo arguments can often be None because built-in definitions
don't have such information.  The type hint can only be
Optional[QAPISchemaInfo] then.  But, mypy gets upset about all the
places where we exploit that it can't actually be None there.  Add
assertions that will help mypy over the hump, to enable adding type
hints in a forthcoming commit.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 087c6e9366..173e27d9e2 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -751,6 +751,7 @@ def describe(self, info):
 else:
 assert False
 
+assert info is not None
 if defined_in != info.defn_name:
 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
 return "%s '%s'" % (role, self.name)
@@ -841,6 +842,7 @@ def __init__(self, name, info, doc, ifcond, features,
 self.coroutine = coroutine
 
 def check(self, schema):
+assert self.info is not None
 super().check(schema)
 if self._arg_type_name:
 arg_type = schema.resolve_type(
-- 
2.44.0




[PATCH v5 07/25] qapi/schema: declare type for QAPISchemaArrayType.element_type

2024-03-15 Thread Markus Armbruster
From: John Snow 

A QAPISchemaArrayType's element type gets resolved only during .check().
We have QAPISchemaArrayType.__init__() initialize self.element_type =
None, and .check() assign the actual type.  Using .element_type before
.check() is wrong, and hopefully crashes due to the value being None.
Works.

However, it makes for awkward typing.  With .element_type:
Optional[QAPISchemaType], mypy is of course unable to see that it's None
before .check(), and a QAPISchemaType after.  To help it over the hump,
we'd have to assert self.element_type is not None before all the (valid)
uses.  The assertion catches invalid uses, but only at run time; mypy
can't flag them.

Instead, declare .element_type in .__init__() as QAPISchemaType
*without* initializing it.  Using .element_type before .check() now
certainly crashes, which is an improvement.  Mypy still can't flag
invalid uses, but that's okay.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 307f8af01a..48f157fb91 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -381,7 +381,7 @@ def __init__(self, name, info, element_type):
 super().__init__(name, info, None)
 assert isinstance(element_type, str)
 self._element_type_name = element_type
-self.element_type = None
+self.element_type: QAPISchemaType
 
 def need_has_if_optional(self):
 # When FOO is an array, we still need has_FOO to distinguish
-- 
2.44.0




[PATCH v5 09/25] qapi/schema: adjust type narrowing for mypy's benefit

2024-03-15 Thread Markus Armbruster
From: John Snow 

We already take care to perform some type narrowing for arg_type and
ret_type, but not in a way where mypy can utilize the result once we add
type hints, e.g.:

qapi/schema.py:833: error: Incompatible types in assignment (expression
has type "QAPISchemaType", variable has type
"Optional[QAPISchemaObjectType]") [assignment]

qapi/schema.py:893: error: Incompatible types in assignment (expression
has type "QAPISchemaType", variable has type
"Optional[QAPISchemaObjectType]") [assignment]

A simple change to use a temporary variable helps the medicine go down.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 0b01c841ff..e44802369d 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -843,13 +843,14 @@ def __init__(self, name, info, doc, ifcond, features,
 def check(self, schema):
 super().check(schema)
 if self._arg_type_name:
-self.arg_type = schema.resolve_type(
+arg_type = schema.resolve_type(
 self._arg_type_name, self.info, "command's 'data'")
-if not isinstance(self.arg_type, QAPISchemaObjectType):
+if not isinstance(arg_type, QAPISchemaObjectType):
 raise QAPISemError(
 self.info,
 "command's 'data' cannot take %s"
-% self.arg_type.describe())
+% arg_type.describe())
+self.arg_type = arg_type
 if self.arg_type.variants and not self.boxed:
 raise QAPISemError(
 self.info,
@@ -866,8 +867,8 @@ def check(self, schema):
 if self.name not in self.info.pragma.command_returns_exceptions:
 typ = self.ret_type
 if isinstance(typ, QAPISchemaArrayType):
-typ = self.ret_type.element_type
 assert typ
+typ = typ.element_type
 if not isinstance(typ, QAPISchemaObjectType):
 raise QAPISemError(
 self.info,
@@ -903,13 +904,14 @@ def __init__(self, name, info, doc, ifcond, features, 
arg_type, boxed):
 def check(self, schema):
 super().check(schema)
 if self._arg_type_name:
-self.arg_type = schema.resolve_type(
+typ = schema.resolve_type(
 self._arg_type_name, self.info, "event's 'data'")
-if not isinstance(self.arg_type, QAPISchemaObjectType):
+if not isinstance(typ, QAPISchemaObjectType):
 raise QAPISemError(
 self.info,
 "event's 'data' cannot take %s"
-% self.arg_type.describe())
+% typ.describe())
+self.arg_type = typ
 if self.arg_type.variants and not self.boxed:
 raise QAPISemError(
 self.info,
-- 
2.44.0




[PATCH v5 13/25] qapi/schema: fix QAPISchemaArrayType.check's call to resolve_type

2024-03-15 Thread Markus Armbruster
From: John Snow 

Adjust the expression at the callsite to work around mypy's weak type
introspection that believes this expression can resolve to
QAPISourceInfo; it cannot.

(Fundamentally: self.info only resolves to false in a boolean expression
when it is None; therefore this expression may only ever produce
Optional[str]. mypy does not know that 'info', when it is a
QAPISourceInfo object, cannot ever be false.)

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 0ef9b3398a..087c6e9366 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -395,7 +395,7 @@ def check(self, schema):
 super().check(schema)
 self.element_type = schema.resolve_type(
 self._element_type_name, self.info,
-self.info and self.info.defn_meta)
+self.info.defn_meta if self.info else None)
 assert not isinstance(self.element_type, QAPISchemaArrayType)
 
 def set_module(self, schema):
-- 
2.44.0




[PATCH v5 00/25] qapi: statically type schema.py

2024-03-15 Thread Markus Armbruster
v5:
* PATCH 05: Move QAPISchemaDefinition.check()'s
  super().check() back to where it was in v3
* PATCH 12: Replaced, necessitating minor adjustments in PATCH 17+22
* PATCH 16: Tweak comment
* PATCH 22: Tighten QAPISchema.lookup_entity()'s type hint
* PATCH 24+25: New

John Snow (22):
  qapi/parser: fix typo - self.returns.info => self.errors.info
  qapi/parser: shush up pylint
  qapi: sort pylint suppressions
  qapi/schema: add pylint suppressions
  qapi: create QAPISchemaDefinition
  qapi/schema: declare type for QAPISchemaObjectTypeMember.type
  qapi/schema: declare type for QAPISchemaArrayType.element_type
  qapi/schema: make c_type() and json_type() abstract methods
  qapi/schema: adjust type narrowing for mypy's benefit
  qapi/schema: add type narrowing to lookup_type()
  qapi/schema: assert resolve_type has 'info' and 'what' args on error
  qapi/schema: fix QAPISchemaArrayType.check's call to resolve_type
  qapi/schema: assert info is present when necessary
  qapi/schema: add _check_complete flag
  qapi/schema: Don't initialize "members" with `None`
  qapi/schema: fix typing for QAPISchemaVariants.tag_member
  qapi/schema: assert inner type of QAPISchemaVariants in check_clash()
  qapi/parser: demote QAPIExpression to Dict[str, Any]
  qapi/parser.py: assert member.info is present in connect_member
  qapi/schema: add type hints
  qapi/schema: turn on mypy strictness
  qapi/schema: remove unnecessary asserts

Markus Armbruster (3):
  qapi: Assert built-in types exist
  qapi: Tighten check whether implicit object type already exists
  qapi: Dumb down QAPISchema.lookup_entity()

 scripts/qapi/introspect.py |   8 +-
 scripts/qapi/mypy.ini  |   5 -
 scripts/qapi/parser.py |   7 +-
 scripts/qapi/pylintrc  |  11 +-
 scripts/qapi/schema.py | 794 -
 5 files changed, 537 insertions(+), 288 deletions(-)

-- 
2.44.0




[PATCH v5 18/25] qapi/schema: assert inner type of QAPISchemaVariants in check_clash()

2024-03-15 Thread Markus Armbruster
From: John Snow 

QAPISchemaVariant's "variants" field is typed as
List[QAPISchemaVariant], where the typing for QAPISchemaVariant allows
its type field to be any QAPISchemaType.

However, QAPISchemaVariant expects that all of its variants contain the
narrower QAPISchemaObjectType. This relationship is enforced at runtime
in QAPISchemaVariants.check(). This relationship is not embedded in the
type system though, so QAPISchemaVariants.check_clash() needs to
re-assert this property in order to call
QAPISchemaVariant.type.check_clash().

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 9c138badb0..177bfa0d11 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -716,7 +716,10 @@ def check(self, schema, seen):
 def check_clash(self, info, seen):
 for v in self.variants:
 # Reset seen map for each variant, since qapi names from one
-# branch do not affect another branch
+# branch do not affect another branch.
+#
+# v.type's typing is enforced in check() above.
+assert isinstance(v.type, QAPISchemaObjectType)
 v.type.check_clash(info, dict(seen))
 
 
-- 
2.44.0




[PATCH v5 01/25] qapi/parser: fix typo - self.returns.info => self.errors.info

2024-03-15 Thread Markus Armbruster
From: John Snow 

Small copy-pasto. The correct info field to use in this conditional
block is self.errors.info.

Fixes: 3a025d3d1ffa
Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/parser.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index d8f76060b8..fed88e9074 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -733,7 +733,7 @@ def check_expr(self, expr: QAPIExpression) -> None:
 "'Returns' section is only valid for commands")
 if self.errors:
 raise QAPISemError(
-self.returns.info,
+self.errors.info,
 "'Errors' section is only valid for commands")
 
 def check(self) -> None:
-- 
2.44.0




[PATCH v5 15/25] qapi/schema: add _check_complete flag

2024-03-15 Thread Markus Armbruster
From: John Snow 

Instead of using the None value for the members field, use a dedicated
flag to detect recursive misconfigurations.

This is intended to assist with subsequent patches that seek to remove
the "None" value from the members field (which can never hold that value
after the final call to check()) in order to simplify the static typing
of that field; avoiding the need of assertions littered at many
callsites to eliminate the possibility of the None value.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 173e27d9e2..2c3de72ae6 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -450,12 +450,13 @@ def __init__(self, name, info, doc, ifcond, features,
 self.local_members = local_members
 self.variants = variants
 self.members = None
+self._check_complete = False
 
 def check(self, schema):
 # This calls another type T's .check() exactly when the C
 # struct emitted by gen_object() contains that T's C struct
 # (pointers don't count).
-if self.members is not None:
+if self._check_complete:
 # A previous .check() completed: nothing to do
 return
 if self._checked:
@@ -464,7 +465,7 @@ def check(self, schema):
"object %s contains itself" % self.name)
 
 super().check(schema)
-assert self._checked and self.members is None
+assert self._checked and not self._check_complete
 
 seen = OrderedDict()
 if self._base_name:
@@ -487,7 +488,8 @@ def check(self, schema):
 self.variants.check(schema, seen)
 self.variants.check_clash(self.info, seen)
 
-self.members = members  # mark completed
+self.members = members
+self._check_complete = True  # mark completed
 
 # Check that the members of this type do not cause duplicate JSON members,
 # and update seen to track the members seen so far. Report any errors
-- 
2.44.0




[PATCH v5 06/25] qapi/schema: declare type for QAPISchemaObjectTypeMember.type

2024-03-15 Thread Markus Armbruster
From: John Snow 

A QAPISchemaObjectTypeMember's type gets resolved only during .check().
We have QAPISchemaObjectTypeMember.__init__() initialize self.type =
None, and .check() assign the actual type.  Using .type before .check()
is wrong, and hopefully crashes due to the value being None.  Works.

However, it makes for awkward typing.  With .type:
Optional[QAPISchemaType], mypy is of course unable to see that it's None
before .check(), and a QAPISchemaType after.  To help it over the hump,
we'd have to assert self.type is not None before all the (valid) uses.
The assertion catches invalid uses, but only at run time; mypy can't
flag them.

Instead, declare .type in .__init__() as QAPISchemaType *without*
initializing it.  Using .type before .check() now certainly crashes,
which is an improvement.  Mypy still can't flag invalid uses, but that's
okay.

Addresses typing errors such as these:

qapi/schema.py:657: error: "None" has no attribute "alternate_qtype"  
[attr-defined]
qapi/schema.py:662: error: "None" has no attribute "describe"  [attr-defined]

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index b298c8b4f9..307f8af01a 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -786,7 +786,7 @@ def __init__(self, name, info, typ, optional, ifcond=None, 
features=None):
 assert isinstance(f, QAPISchemaFeature)
 f.set_defined_in(name)
 self._type_name = typ
-self.type = None
+self.type: QAPISchemaType  # set during check()
 self.optional = optional
 self.features = features or []
 
-- 
2.44.0




[PATCH v5 19/25] qapi/parser: demote QAPIExpression to Dict[str, Any]

2024-03-15 Thread Markus Armbruster
From: John Snow 

Dict[str, object] is a stricter type, but with the way that code is
currently arranged, it is infeasible to enforce this strictness.

In particular, although expr.py's entire raison d'être is normalization
and type-checking of QAPI Expressions, that type information is not
"remembered" in any meaningful way by mypy because each individual
expression is not downcast to a specific expression type that holds all
the details of each expression's unique form.

As a result, all of the code in schema.py that deals with actually
creating type-safe specialized structures has no guarantee (myopically)
that the data it is being passed is correct.

There are two ways to solve this:

(1) Re-assert that the incoming data is in the shape we expect it to be, or
(2) Disable type checking for this data.

(1) is appealing to my sense of strictness, but I gotta concede that it
is asinine to re-check the shape of a QAPIExpression in schema.py when
expr.py has just completed that work at length. The duplication of code
and the nightmare thought of needing to update both locations if and
when we change the shape of these structures makes me extremely
reluctant to go down this route.

(2) allows us the chance to miss updating types in the case that types
are updated in expr.py, but it *is* an awful lot simpler and,
importantly, gets us closer to type checking schema.py *at
all*. Something is better than nothing, I'd argue.

So, do the simpler dumber thing and worry about future strictness
improvements later.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/parser.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index ec4ebef4e3..2f3c704fa2 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -19,6 +19,7 @@
 import re
 from typing import (
 TYPE_CHECKING,
+Any,
 Dict,
 List,
 Mapping,
@@ -43,7 +44,7 @@
 _ExprValue = Union[List[object], Dict[str, object], str, bool]
 
 
-class QAPIExpression(Dict[str, object]):
+class QAPIExpression(Dict[str, Any]):
 # pylint: disable=too-few-public-methods
 def __init__(self,
  data: Mapping[str, object],
-- 
2.44.0




[PATCH v5 02/25] qapi/parser: shush up pylint

2024-03-15 Thread Markus Armbruster
From: John Snow 

Shhh!

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/parser.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index fed88e9074..ec4ebef4e3 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -607,6 +607,7 @@ class QAPIDoc:
 """
 
 class Section:
+# pylint: disable=too-few-public-methods
 def __init__(self, info: QAPISourceInfo,
  tag: Optional[str] = None):
 # section source info, i.e. where it begins
-- 
2.44.0




[PATCH v5 16/25] qapi/schema: Don't initialize "members" with `None`

2024-03-15 Thread Markus Armbruster
From: John Snow 

Declare, but don't initialize the "members" field with type
List[QAPISchemaObjectTypeMember].

This simplifies the typing from what would otherwise be
Optional[List[T]] to merely List[T]. This removes the need to add
assertions to several callsites that this value is not None - which it
never will be after the delayed initialization in check() anyway.

The type declaration without initialization trick will cause accidental
uses of this field prior to full initialization to raise an
AttributeError.

(Note that it is valid to have an empty members list, see the internal
q_empty object as an example. For this reason, we cannot use the empty
list as a replacement test for full initialization and instead rely on
the _checked/_check_complete fields.)

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 12 +++-
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 2c3de72ae6..74b0d7b007 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -20,7 +20,7 @@
 from collections import OrderedDict
 import os
 import re
-from typing import List, Optional
+from typing import List, Optional, cast
 
 from .common import (
 POINTER_SUFFIX,
@@ -449,7 +449,7 @@ def __init__(self, name, info, doc, ifcond, features,
 self.base = None
 self.local_members = local_members
 self.variants = variants
-self.members = None
+self.members: List[QAPISchemaObjectTypeMember]
 self._check_complete = False
 
 def check(self, schema):
@@ -482,7 +482,11 @@ def check(self, schema):
 for m in self.local_members:
 m.check(schema)
 m.check_clash(self.info, seen)
-members = seen.values()
+
+# self.check_clash() works in terms of the supertype, but
+# self.members is declared List[QAPISchemaObjectTypeMember].
+# Cast down to the subtype.
+members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
 
 if self.variants:
 self.variants.check(schema, seen)
@@ -515,11 +519,9 @@ def is_implicit(self):
 return self.name.startswith('q_')
 
 def is_empty(self):
-assert self.members is not None
 return not self.members and not self.variants
 
 def has_conditional_members(self):
-assert self.members is not None
 return any(m.ifcond.is_present() for m in self.members)
 
 def c_name(self):
-- 
2.44.0




[PATCH v5 24/25] qapi: Tighten check whether implicit object type already exists

2024-03-15 Thread Markus Armbruster
Entities with names starting with q_obj_ are implicit object types.
Therefore, QAPISchema._make_implicit_object_type()'s .lookup_entity()
can only return a QAPISchemaObjectType.  Assert that.

Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index e52930a48a..a6180f93c6 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -1297,8 +1297,9 @@ def _make_implicit_object_type(
 return None
 # See also QAPISchemaObjectTypeMember.describe()
 name = 'q_obj_%s-%s' % (name, role)
-typ = self.lookup_entity(name, QAPISchemaObjectType)
+typ = self.lookup_entity(name)
 if typ:
+assert(isinstance(typ, QAPISchemaObjectType))
 # The implicit object type has multiple users.  This can
 # only be a duplicate definition, which will be flagged
 # later.
-- 
2.44.0




[PATCH v5 04/25] qapi/schema: add pylint suppressions

2024-03-15 Thread Markus Armbruster
From: John Snow 

With this patch, pylint is happy with the file, so enable it in the
configuration.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/pylintrc  | 5 -
 scripts/qapi/schema.py | 5 +
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc
index 1342412c3c..c028a1f9f5 100644
--- a/scripts/qapi/pylintrc
+++ b/scripts/qapi/pylintrc
@@ -1,10 +1,5 @@
 [MASTER]
 
-# Add files or directories matching the regex patterns to the ignore list.
-# The regex matches against base names, not paths.
-ignore-patterns=schema.py,
-
-
 [MESSAGES CONTROL]
 
 # Disable the message, report, category or checker with the given id(s). You
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 8ba5665bc6..117f0f78f0 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -12,6 +12,8 @@
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
+# pylint: disable=too-many-lines
+
 # TODO catching name collisions in generated code would be nice
 
 from collections import OrderedDict
@@ -83,6 +85,7 @@ def c_name(self):
 return c_name(self.name)
 
 def check(self, schema):
+# pylint: disable=unused-argument
 assert not self._checked
 seen = {}
 for f in self.features:
@@ -113,6 +116,7 @@ def is_implicit(self):
 return not self.info
 
 def visit(self, visitor):
+# pylint: disable=unused-argument
 assert self._checked
 
 def describe(self):
@@ -131,6 +135,7 @@ def visit_module(self, name):
 pass
 
 def visit_needed(self, entity):
+# pylint: disable=unused-argument
 # Default to visiting everything
 return True
 
-- 
2.44.0




[PATCH v5 12/25] qapi: Assert built-in types exist

2024-03-15 Thread Markus Armbruster
QAPISchema.lookup_type('FOO') returns a QAPISchemaType when type 'FOO'
exists, else None.  It won't return None for built-in types like
'int'.

Since mypy can't see that, it'll complain that we assign the
Optional[QAPISchemaType] returned by .lookup_type() to QAPISchemaType
variables.

Add assertions to help it over the hump.

Signed-off-by: Markus Armbruster 
---
 scripts/qapi/introspect.py | 8 ++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 67c7d89aae..4679b1bc2c 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -227,10 +227,14 @@ def _use_type(self, typ: QAPISchemaType) -> str:
 
 # Map the various integer types to plain int
 if typ.json_type() == 'int':
-typ = self._schema.lookup_type('int')
+type_int = self._schema.lookup_type('int')
+assert type_int
+typ = type_int
 elif (isinstance(typ, QAPISchemaArrayType) and
   typ.element_type.json_type() == 'int'):
-typ = self._schema.lookup_type('intList')
+type_intList = self._schema.lookup_type('intList')
+assert type_intList
+typ = type_intList
 # Add type to work queue if new
 if typ not in self._used_types:
 self._used_types.append(typ)
-- 
2.44.0




[PATCH v5 10/25] qapi/schema: add type narrowing to lookup_type()

2024-03-15 Thread Markus Armbruster
From: John Snow 

This function is a bit hard to type as-is; mypy needs some assertions to
assist with the type narrowing.

Signed-off-by: John Snow 
Reviewed-by: Markus Armbruster 
Signed-off-by: Markus Armbruster 
---
 scripts/qapi/schema.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index e44802369d..1034825415 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -989,7 +989,9 @@ def lookup_entity(self, name, typ=None):
 return ent
 
 def lookup_type(self, name):
-return self.lookup_entity(name, QAPISchemaType)
+typ = self.lookup_entity(name, QAPISchemaType)
+assert typ is None or isinstance(typ, QAPISchemaType)
+return typ
 
 def resolve_type(self, name, info, what):
 typ = self.lookup_type(name)
-- 
2.44.0




Re: [PATCH for-9.0 v2] vdpa-dev: Fix initialisation order to restore VDUSE compatibility

2024-03-15 Thread Stefano Garzarella

On Fri, Mar 15, 2024 at 03:03:31PM +0100, Kevin Wolf wrote:

VDUSE requires that virtqueues are first enabled before the DRIVER_OK
status flag is set; with the current API of the kernel module, it is
impossible to enable the opposite order in our block export code because
userspace is not notified when a virtqueue is enabled.

This requirement also mathces the normal initialisation order as done by
the generic vhost code in QEMU. However, commit 6c482547 accidentally
changed the order for vdpa-dev and broke access to VDUSE devices with
this.

This changes vdpa-dev to use the normal order again and use the standard
vhost callback .vhost_set_vring_enable for this. VDUSE devices can be
used with vdpa-dev again after this fix.

vhost_net intentionally avoided enabling the vrings for vdpa and does
this manually later while it does enable them for other vhost backends.
Reflect this in the vhost_net code and return early for vdpa, so that
the behaviour doesn't change for this device.

Cc: qemu-sta...@nongnu.org
Fixes: 6c4825476a4351530bcac17abab72295b75ffe98
Signed-off-by: Kevin Wolf 
---
v2:
- Actually make use of the @enable parameter
- Change vhost_net to preserve the current behaviour

hw/net/vhost_net.c | 10 ++
hw/virtio/vdpa-dev.c   |  5 +
hw/virtio/vhost-vdpa.c | 27 +--
hw/virtio/vhost.c  |  8 +++-
4 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e8e1661646..fd1a93701a 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -541,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int enable)
VHostNetState *net = get_vhost_net(nc);
const VhostOps *vhost_ops = net->dev.vhost_ops;

+/*
+ * vhost-vdpa network devices need to enable dataplane virtqueues after
+ * DRIVER_OK, so they can recover device state before starting dataplane.
+ * Because of that, we don't enable virtqueues here and leave it to
+ * net/vhost-vdpa.c.
+ */
+if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+return 0;
+}
+
nc->vring_enable = enable;

if (vhost_ops && vhost_ops->vhost_set_vring_enable) {
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index eb9ecea83b..13e87f06f6 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -253,14 +253,11 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, 
Error **errp)

s->dev.acked_features = vdev->guest_features;

-ret = vhost_dev_start(>dev, vdev, false);
+ret = vhost_dev_start(>dev, vdev, true);
if (ret < 0) {
error_setg_errno(errp, -ret, "Error starting vhost");
goto err_guest_notifiers;
}
-for (i = 0; i < s->dev.nvqs; ++i) {
-vhost_vdpa_set_vring_ready(>vdpa, i);
-}
s->started = true;

/*
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index ddae494ca8..401afac2f5 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -886,12 +886,13 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, 
int idx)
return idx;
}

-int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned idx,
+   int enable)
{
struct vhost_dev *dev = v->dev;
struct vhost_vring_state state = {
.index = idx,
-.num = 1,
+.num = enable,
};
int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, );



After this line we now have:

  trace_vhost_vdpa_set_vring_ready(dev, idx, r);

Should we rename it or move it to the new function?

If we rename it, we should trace also `enable`.

@@ -899,6 +900,27 @@ int vhost_vdpa_set_vring_ready(struct vhost_vdpa 
*v, unsigned idx)

return r;
}

+static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+struct vhost_vdpa *v = dev->opaque;
+unsigned int i;
+int ret;
+
+for (i = 0; i < dev->nvqs; ++i) {
+ret = vhost_vdpa_set_vring_enable_one(v, i, enable);
+if (ret < 0) {
+return ret;
+}
+}
+
+return 0;
+}
+
+int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+{
+return vhost_vdpa_set_vring_enable_one(v, idx, 1);
+}
+
static int vhost_vdpa_set_config_call(struct vhost_dev *dev,
   int fd)
{
@@ -1514,6 +1536,7 @@ const VhostOps vdpa_ops = {
.vhost_set_features = vhost_vdpa_set_features,
.vhost_reset_device = vhost_vdpa_reset_device,
.vhost_get_vq_index = vhost_vdpa_get_vq_index,
+.vhost_set_vring_enable = vhost_vdpa_set_vring_enable,
.vhost_get_config  = vhost_vdpa_get_config,
.vhost_set_config = vhost_vdpa_set_config,
.vhost_requires_shm_log = NULL,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 2c9ac79468..decfb85184 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1984,7 +1984,13 @@ static int 

Re: [PATCH v4 22/25] memory: Add Error** argument to memory_get_xlat_addr()

2024-03-15 Thread Peter Xu
On Wed, Mar 06, 2024 at 02:34:37PM +0100, Cédric Le Goater wrote:
> Let the callers do the reporting. This will be useful in
> vfio_iommu_map_dirty_notify().
> 
> Cc: "Michael S. Tsirkin" 
> Cc: Paolo Bonzini 
> Cc: David Hildenbrand 
> Signed-off-by: Cédric Le Goater 

Reviewed-by: Peter Xu 

-- 
Peter Xu




Re: Another CXL/MMIO tcg tlb corner case

2024-03-15 Thread Jørgen Hansen

> On 15 Mar 2024, at 13.25, Alex Bennée  wrote:
> 
> Jørgen Hansen  writes:
> 
>> Hi,
>> 
>> While doing some testing using numactl-based interleaving of application 
>> memory
>> across regular memory and CXL-based memory using QEMU with tcg, I ran into an
>> issue similar to what we saw a while back - link to old issue:
>> https://lore.kernel.org/qemu-devel/CAFEAcA_a_AyQ=epz3_+cheat8crsk9mou894wbnw_fywamk...@mail.gmail.com/#t.
>> 
>> When running:
>> 
>> numactl --interleave 0,1 ./cachebench …
>> 
>> I hit the following:
>> 
>> numactl --interleave 0,1 ./cachebench --json_test_config 
>> ../test_configs/hit_ratio/graph_cache_follower_assocs/config.json
>> qemu: fatal: cpu_io_recompile: could not find TB for pc=0x7fffc3926dd4
> 
> Ok so this will be because we (by design) don't cache TB's for MMIO
> regions. But as you say:
>> 
>> Looking at the tb being executed, it looks like it is a single instruction 
>> tb,
>> so with my _very_ limited understanding of tcg, it shouldn’t be necessary to
>> do the IO recompile:
>> 
>> (gdb) up 13
>> 
>> #13 0x55ca13ac in cpu_loop_exec_tb (tb_exit=0x749ff6d8, 
>> last_tb=, pc=, tb=0x7fffc3926cc0 
>> , cpu=0x578c19c0) at 
>> ../accel/tcg/cpu-exec.c:904
>> 904 tb = cpu_tb_exec(cpu, tb, tb_exit);
>> (gdb) print *tb
>> $1 = {pc = 0, cs_base = 0, flags = 415285939, cflags = 4278321152, size = 7, 
>> icount = 1, tc = {ptr = 0x7fffc3926d80 , size = 
>> 176}, page_next = {0, 0}, page_addr = {18446744073709551615,
>>18446744073709551615}, jmp_lock = {value = 0}, jmp_reset_offset =
>> {65535, 65535}, jmp_insn_offset = {65535, 65535}, jmp_target_addr =
>> {0, 0}, jmp_list_head = 140736474540928, jmp_list_next = {0, 0},
>> jmp_dest = {0, 0}}
> 
> with an icount of 1 we by definition can do io. This is done in the
> translator_loop:
> 
>/*
> * Disassemble one instruction.  The translate_insn hook should
> * update db->pc_next and db->is_jmp to indicate what should be
> * done next -- either exiting this loop or locate the start of
> * the next instruction.
> */
>if (db->num_insns == db->max_insns) {
>/* Accept I/O on the last instruction.  */
>set_can_do_io(db, true);
>}
> 
> and we see later on:
> 
>tb->icount = db->num_insns;
> 
> So I guess we must have gone into the translator loop expecting to
> translate more? (side note having int8_t type for the saved bool value
> seems weird).
> 
> Could you confirm by doing something like:
> 
>  if (tb->icount == 1 &&  db->max_insns > 1) {
> fprintf(stderr, "ended up doing one insn (%d, %d)", db->max_insns, 
> db->saved_can_do_io);
>  }
> 
> after we set tb->icount?
> 

Thanks for looking into this - I tried what you suggest above and it looks
like that is a case that happens quite often (I see 100s of these just when
booting the VM), so it is hard to tell whether it is related directly to the
issue, e.g.,:

ended up doing one insn (512, 0)ended up doing one insn (512, 0)ended up doing 
one insn (512, 0)ended up doing one insn (512, 0)ended up doing one insn (512, 
0)ended up doing one insn (512, 0)ended up doing one insn (512, 0)qemu: fatal: 
cpu_io_recompile: could not find TB for pc=0x7f4264da3d48
RAX= RBX=7f6798e69040 RCX=0205f958 
RDX=0205f8f0
RSI= RDI=7ffd5663c720 RBP=7ffd5663c720 
RSP=7ffd5663c6c0
R8 =001e R9 =0205f920 R10=0004 
R11=7f67984b56b0
R12=7ffd5663c7e0 R13=7f6798e5d0b8 R14=7f6798e5d588 
R15=7ffd5663c700
RIP=7f6798e39ffd RFL=0246 [---Z-P-] CPL=3 II=0 A20=1 SMM=0 HLT=0


>> If the application is run entirely out of MMIO memory, things work fine (the
>> previous patches related to this is in Jonathans branch), so one thought is 
>> that
>> it is related to having the code on a mix of regular and CXL memory. Since we
>> previously had issues with code crossing page boundaries where only the 
>> second
>> page is MMIO, I tried out the following change to the fix introduced for that
>> issue thinking that reverting to the slow path in the middle of the 
>> translation
>> might not correctly update can_do_io:
>> 
>> diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
>> index 38c34009a5..db6ea360e0 100644
>> --- a/accel/tcg/translator.c
>> +++ b/accel/tcg/translator.c
>> @@ -258,6 +258,7 @@ static void *translator_access(CPUArchState *env, 
>> DisasContextBase *db,
>> if (unlikely(new_page1 == -1)) {
>> tb_unlock_pages(tb);
>> tb_set_page_addr0(tb, -1);
>> +set_can_do_io(db, true);
>> return NULL;
>> }
>> 
>> With that change, things work fine. Not saying that this is a valid fix for 
>> the
>> issue, but just trying to provide as much information as possible :)
> 
> I can see why this works, my worry is if we address all the early exit
> cases here.
> 
>> 
>> Thanks,
>> Jorgen
> 
> --
> 

Re: [PATCH v4 10/25] migration: Add Error** argument to qemu_savevm_state_setup()

2024-03-15 Thread Peter Xu
On Fri, Mar 15, 2024 at 03:31:28PM +0100, Cédric Le Goater wrote:
> On 3/15/24 14:11, Peter Xu wrote:
> > On Fri, Mar 15, 2024 at 01:20:49PM +0100, Cédric Le Goater wrote:
> > > +static void qemu_savevm_wait_unplug(MigrationState *s, int state)
> > 
> > One more trivial comment: I'd even consider dropping "state" altogether, as
> > this should be the only state this function should be invoked.  So we can
> > perhaps assert it instead of passing it over?
> 
> Yes. If you prefer this implementation I will change.

I am fine with either approach, we can wait for 1-2 days to see whether
others want to say.  Otherwise the other approach actually looks better to
me in that it avoids SETUP->UNPLUG->SETUP jumps.

And then we wait to see whether UNPLUG can be dropped for either way to go,
perhaps starting from adding it into deprecation list if no objections from
the relevant folks.

Thanks,

-- 
Peter Xu




Re: [PATCH v4 10/25] migration: Add Error** argument to qemu_savevm_state_setup()

2024-03-15 Thread Peter Xu
On Fri, Mar 15, 2024 at 03:21:27PM +0100, Cédric Le Goater wrote:
> On 3/15/24 13:20, Cédric Le Goater wrote:
> > On 3/15/24 12:01, Peter Xu wrote:
> > > On Fri, Mar 15, 2024 at 11:17:45AM +0100, Cédric Le Goater wrote:
> > > > > migrate_set_state is also unintuitive because it ignores invalid state
> > > > > transitions and we've been using that property to deal with special
> > > > > states such as POSTCOPY_PAUSED and FAILED:
> > > > > 
> > > > > - After the migration goes into POSTCOPY_PAUSED, the resumed 
> > > > > migration's
> > > > >     migrate_init() will try to set the state NONE->SETUP, which is not
> > > > >     valid.
> > > > > 
> > > > > - After save_setup fails, the migration goes into FAILED, but 
> > > > > wait_unplug
> > > > >     will try to transition SETUP->ACTIVE, which is also not valid.
> > > > > 
> > > > 
> > > > I am not sure I understand what the plan is. Both solutions are 
> > > > problematic
> > > > regarding the state transitions.
> > > > 
> > > > Should we consider that waiting for failover devices to unplug is an 
> > > > internal
> > > > step of the SETUP phase not transitioning to ACTIVE ?
> > > 
> > > If to unblock this series, IIUC the simplest solution is to do what 
> > > Fabiano
> > > suggested, that we move qemu_savevm_wait_unplug() to be before the check 
> > > of
> > > setup() ret.
> > 
> > The simplest is IMHO moving qemu_savevm_wait_unplug() before
> > qemu_savevm_state_setup() and leave patch 10 is unchanged. See
> > below the extra patch. It looks much cleaner than what we have
> > today.
> > 
> > > In that case, the state change in qemu_savevm_wait_unplug()
> > > should be benign and we should see a super small window it became ACTIVE
> > > but then it should be FAILED (and IIUC the patch itself will need to use
> > > ACTIVE as "old_state", not SETUP anymore).
> > 
> > OK. I will give it a try to compare.
> 
> Here's the alternative solution. SETUP state failures are handled after
> transitioning to ACTIVE state, which is unfortunate but probably harmless.
> I guess it's OK.

This also looks good to me, thanks.

One trivial early comment is in this case we can introduce a helper to
cover both setup() calls and UNPLUG waits and dedup the two paths.

> 
> Thanks,
> 
> C.
> 
> 
> 
> Signed-off-by: Cédric Le Goater 
> ---
>  migration/savevm.h|  2 +-
>  migration/migration.c | 29 +++--
>  migration/savevm.c| 26 +++---
>  3 files changed, 43 insertions(+), 14 deletions(-)
> 
> diff --git a/migration/savevm.h b/migration/savevm.h
> index 
> 74669733dd63a080b765866c703234a5c4939223..9ec96a995c93a42aad621595f0ed58596c532328
>  100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -32,7 +32,7 @@
>  bool qemu_savevm_state_blocked(Error **errp);
>  void qemu_savevm_non_migratable_list(strList **reasons);
>  int qemu_savevm_state_prepare(Error **errp);
> -void qemu_savevm_state_setup(QEMUFile *f);
> +int qemu_savevm_state_setup(QEMUFile *f, Error **errp);
>  bool qemu_savevm_state_guest_unplug_pending(void);
>  int qemu_savevm_state_resume_prepare(MigrationState *s);
>  void qemu_savevm_state_header(QEMUFile *f);
> diff --git a/migration/migration.c b/migration/migration.c
> index 
> 644e073b7dcc70cb2bdaa9c975ba478952465ff4..0704ad6226df61f2f15bd81a2897f9946d601ca7
>  100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -3427,6 +3427,8 @@ static void *migration_thread(void *opaque)
>  int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST);
>  MigThrError thr_error;
>  bool urgent = false;
> +Error *local_err = NULL;
> +int ret;
>  thread = migration_threads_add("live_migration", qemu_get_thread_id());
> @@ -3470,12 +3472,24 @@ static void *migration_thread(void *opaque)
>  }
>  bql_lock();
> -qemu_savevm_state_setup(s->to_dst_file);
> +ret = qemu_savevm_state_setup(s->to_dst_file, _err);
>  bql_unlock();
>  qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,
> MIGRATION_STATUS_ACTIVE);
> +/*
> + * Handle SETUP failures after waiting for virtio-net-failover
> + * devices to unplug. This to preserve migration state transitions.
> + */
> +if (ret) {
> +migrate_set_error(s, local_err);
> +error_free(local_err);
> +migrate_set_state(>state, MIGRATION_STATUS_ACTIVE,
> +  MIGRATION_STATUS_FAILED);
> +goto out;
> +}
> +
>  s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
>  trace_migration_thread_setup_complete();
> @@ -3549,6 +3563,8 @@ static void *bg_migration_thread(void *opaque)
>  MigThrError thr_error;
>  QEMUFile *fb;
>  bool early_fail = true;
> +Error *local_err = NULL;
> +int ret;
>  rcu_register_thread();
>  object_ref(OBJECT(s));
> @@ -3582,12 +3598,20 @@ static void *bg_migration_thread(void *opaque)
>  bql_lock();
>  qemu_savevm_state_header(s->to_dst_file);
> -   

[RFC] Is there a bug in pause_all_vcpus()

2024-03-15 Thread zhukeqian via


During we waited on qemu_pause_cond the bql was unlocked,
the vcpu's state may has been changed by other thread, so
we must request the pause state on all vcpus again.

For example:

Both main loop thread and vCPU thread are allowed to call
pause_all_vcpus(), and in general resume_all_vcpus() is called
after it.

Thus there is possibility that during thread T1 waits on
qemu_pause_cond, other thread has called pause_all_vcpus() and
resume_all_vcpus(), then thread T1 will stuck, because the condition
all_vcpus_paused() is always false.

PS:
I hit this bug when I test the RFC patch of ARM vCPU hotplug feature.
This patch is not verified!!! just for RFC.

Signed-off-by: Keqian Zhu 
---
 system/cpus.c | 22 +-
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/system/cpus.c b/system/cpus.c
index 68d161d96b..24ac8085cd 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -571,12 +571,14 @@ static bool all_vcpus_paused(void)
 return true;
 }

-void pause_all_vcpus(void)
+static void request_pause_all_vcpus(void)
 {
 CPUState *cpu;

-qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
 CPU_FOREACH(cpu) {
+if (cpu->stopped) {
+continue;
+}
 if (qemu_cpu_is_self(cpu)) {
 qemu_cpu_stop(cpu, true);
 } else {
@@ -584,6 +586,14 @@ void pause_all_vcpus(void)
 qemu_cpu_kick(cpu);
 }
 }
+}
+
+void pause_all_vcpus(void)
+{
+CPUState *cpu;
+
+qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
+request_pause_all_vcpus();

 /* We need to drop the replay_lock so any vCPU threads woken up
  * can finish their replay tasks
@@ -592,9 +602,11 @@ void pause_all_vcpus(void)

 while (!all_vcpus_paused()) {
 qemu_cond_wait(_pause_cond, );
-CPU_FOREACH(cpu) {
-qemu_cpu_kick(cpu);
-}
+/* During we waited on qemu_pause_cond the bql was unlocked,
+ * the vcpu's state may has been changed by other thread, so
+ * we must request the pause state on all vcpus again.
+ */
+request_pause_all_vcpus();
 }

 bql_unlock();
--
2.36.1





Re: [PATCH v4 10/25] migration: Add Error** argument to qemu_savevm_state_setup()

2024-03-15 Thread Cédric Le Goater

On 3/15/24 14:11, Peter Xu wrote:

On Fri, Mar 15, 2024 at 01:20:49PM +0100, Cédric Le Goater wrote:

+static void qemu_savevm_wait_unplug(MigrationState *s, int state)


One more trivial comment: I'd even consider dropping "state" altogether, as
this should be the only state this function should be invoked.  So we can
perhaps assert it instead of passing it over?


Yes. If you prefer this implementation I will change.


Thanks,

C.






Re: [PATCH v4 10/25] migration: Add Error** argument to qemu_savevm_state_setup()

2024-03-15 Thread Cédric Le Goater

On 3/15/24 14:09, Peter Xu wrote:

On Fri, Mar 15, 2024 at 01:20:49PM +0100, Cédric Le Goater wrote:

On 3/15/24 12:01, Peter Xu wrote:

On Fri, Mar 15, 2024 at 11:17:45AM +0100, Cédric Le Goater wrote:

migrate_set_state is also unintuitive because it ignores invalid state
transitions and we've been using that property to deal with special
states such as POSTCOPY_PAUSED and FAILED:

- After the migration goes into POSTCOPY_PAUSED, the resumed migration's
 migrate_init() will try to set the state NONE->SETUP, which is not
 valid.

- After save_setup fails, the migration goes into FAILED, but wait_unplug
 will try to transition SETUP->ACTIVE, which is also not valid.



I am not sure I understand what the plan is. Both solutions are problematic
regarding the state transitions.

Should we consider that waiting for failover devices to unplug is an internal
step of the SETUP phase not transitioning to ACTIVE ?


If to unblock this series, IIUC the simplest solution is to do what Fabiano
suggested, that we move qemu_savevm_wait_unplug() to be before the check of
setup() ret.


The simplest is IMHO moving qemu_savevm_wait_unplug() before
qemu_savevm_state_setup() and leave patch 10 is unchanged. See
below the extra patch. It looks much cleaner than what we have
today.


Yes it looks cleaner indeed, it's just that then we'll have one more
possible state conversions like SETUP->UNPLUG->SETUP.  I'd say it's fine,
but let's also copy Laruent and Laine if it's going to be posted formally.


OK. I just sent the alternative implementation. The code looks a little
ugly  :

bql_lock();   |
qemu_savevm_state_header(s->to_dst_file); |
ret = qemu_savevm_state_setup(s->to_dst_file, _err);| in 
SETUP state
bql_unlock(); |
  |
qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,| SETUP 
-> ACTIVE transition
   MIGRATION_STATUS_ACTIVE);  |
  |
/*|
 * Handle SETUP failures after waiting for virtio-net-failover|
 * devices to unplug. This to preserve migration state transitions.   |
 */   |
if (ret) {|
migrate_set_error(s, local_err);  | 
handling SETUP errors in ACTIVE
error_free(local_err);|
migrate_set_state(>state, MIGRATION_STATUS_ACTIVE, |
  MIGRATION_STATUS_FAILED);   |
goto fail_setup;  |
} |
  |
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; | 
SETUP duration
  |
trace_migration_thread_setup_complete();  | 
SETUP trace event



Thanks,

C.




Re: [PATCH v3 1/7] qga: guest-get-fsinfo: add optional 'total-bytes-root' field

2024-03-15 Thread Andrey Drobyshev
On 3/15/24 15:44, Markus Armbruster wrote:
> [?? ??? ? ?? ?? arm...@redhat.com. ???, ?? ??? ?, 
> ?? ?? https://aka.ms/LearnAboutSenderIdentification ]
> 
> Andrey Drobyshev  writes:
> 
>> Since the commit 25b5ff1a86 ("qga: add mountpoint usage info to
>> GuestFilesystemInfo") we have 2 values reported in guest-get-fsinfo:
>> used = (f_blocks - f_bfree), total = (f_blocks - f_bfree + f_bavail) as
>> returned by statvfs(3).  While on Windows guests that's all we can get
>> with GetDiskFreeSpaceExA(), on POSIX guests we might also be interested in
>> total file system size, as it's visible for root user.  Let's add an
>> optional field 'total-bytes-root' to GuestFilesystemInfo struct, which'd
>> only be reported on POSIX and represent f_blocks value as returned by
>> statvfs(3).
>>
>> While here, let's document better where those values come from in both
>> POSIX and Windows.
>>
>> Signed-off-by: Andrey Drobyshev 
> 
> [...]
> 
>> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
>> index b8efe31897..093a5ab602 100644
>> --- a/qga/qapi-schema.json
>> +++ b/qga/qapi-schema.json
>> @@ -1031,8 +1031,18 @@
>>  # @type: file system type string
>>  #
>>  # @used-bytes: file system used bytes (since 3.0)
>> +# * POSIX: (f_blocks - f_bfree) * f_frsize, as returned by statvfs(3)
>> +# * Windows: (TotalNumberOfBytes - TotalNumberOfFreeBytes), as returned
>> +#   by GetDiskFreeSpaceEx()
>>  #
>>  # @total-bytes: non-root file system total bytes (since 3.0)
>> +# * POSIX: (f_blocks - f_bfree + f_bavail) * f_frsize, as returned by
>> +#   statvfs(3)
>> +# * Windows: TotalNumberOfBytes, as returned by GetDiskFreeSpaceEx()
>> +#
>> +# @total-bytes-root: total file system size in bytes (as visible for a
>> +# priviledged user) (since 8.3)
> 
> privileged
> 
>> +# * POSIX only: (f_blocks * f_frsize), returned by statvfs(3)
>>  #
>>  # @disk: an array of disk hardware information that the volume lies
>>  # on, which may be empty if the disk type is not supported
>> @@ -1042,7 +1052,7 @@
>>  { 'struct': 'GuestFilesystemInfo',
>>'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
>> '*used-bytes': 'uint64', '*total-bytes': 'uint64',
>> -   'disk': ['GuestDiskAddress']} }
>> +   '*total-bytes-root': 'uint64', 'disk': ['GuestDiskAddress']} }
>>
>>  ##
>>  # @guest-get-fsinfo:
> 
> Fails to build the manual:
> 
> qga/qapi-schema.json:1019:Unexpected indentation.
> 
> To fix, add blank lines before the lists, like this:
> 
># @used-bytes: file system used bytes (since 3.0)
>#
># * POSIX: (f_blocks - f_bfree) * f_frsize, as returned by
>#   statvfs(3)
># * Windows: (TotalNumberOfBytes - TotalNumberOfFreeBytes), as
>#   returned by GetDiskFreeSpaceEx()
>#
># @total-bytes: non-root file system total bytes (since 3.0)
>#
># * POSIX: (f_blocks - f_bfree + f_bavail) * f_frsize, as returned by
>#   statvfs(3)
># * Windows: TotalNumberOfBytes, as returned by GetDiskFreeSpaceEx()
>#
># @total-bytes-root: total file system size in bytes (as visible for a
># privileged user) (since 8.3)
>#
># * POSIX only: (f_blocks * f_frsize), returned by statvfs(3)
>#
> 
> Yes, reST can be quite annoying.
> 

For some reason in my environment build doesn't fail and file
build/docs/qemu-ga-ref.7 is produced.  However lists aren't indeed
formatted properly.  I'll wait for other patches to be reviewed and fix
that in v4.  Thank you for noticing.



Re: [PATCH v4 10/25] migration: Add Error** argument to qemu_savevm_state_setup()

2024-03-15 Thread Cédric Le Goater

On 3/15/24 13:20, Cédric Le Goater wrote:

On 3/15/24 12:01, Peter Xu wrote:

On Fri, Mar 15, 2024 at 11:17:45AM +0100, Cédric Le Goater wrote:

migrate_set_state is also unintuitive because it ignores invalid state
transitions and we've been using that property to deal with special
states such as POSTCOPY_PAUSED and FAILED:

- After the migration goes into POSTCOPY_PAUSED, the resumed migration's
    migrate_init() will try to set the state NONE->SETUP, which is not
    valid.

- After save_setup fails, the migration goes into FAILED, but wait_unplug
    will try to transition SETUP->ACTIVE, which is also not valid.



I am not sure I understand what the plan is. Both solutions are problematic
regarding the state transitions.

Should we consider that waiting for failover devices to unplug is an internal
step of the SETUP phase not transitioning to ACTIVE ?


If to unblock this series, IIUC the simplest solution is to do what Fabiano
suggested, that we move qemu_savevm_wait_unplug() to be before the check of
setup() ret. 


The simplest is IMHO moving qemu_savevm_wait_unplug() before
qemu_savevm_state_setup() and leave patch 10 is unchanged. See
below the extra patch. It looks much cleaner than what we have
today.


In that case, the state change in qemu_savevm_wait_unplug()
should be benign and we should see a super small window it became ACTIVE
but then it should be FAILED (and IIUC the patch itself will need to use
ACTIVE as "old_state", not SETUP anymore).


OK. I will give it a try to compare.


Here's the alternative solution. SETUP state failures are handled after
transitioning to ACTIVE state, which is unfortunate but probably harmless.
I guess it's OK.

Thanks,

C.



Signed-off-by: Cédric Le Goater 
---
 migration/savevm.h|  2 +-
 migration/migration.c | 29 +++--
 migration/savevm.c| 26 +++---
 3 files changed, 43 insertions(+), 14 deletions(-)

diff --git a/migration/savevm.h b/migration/savevm.h
index 
74669733dd63a080b765866c703234a5c4939223..9ec96a995c93a42aad621595f0ed58596c532328
 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -32,7 +32,7 @@
 bool qemu_savevm_state_blocked(Error **errp);
 void qemu_savevm_non_migratable_list(strList **reasons);
 int qemu_savevm_state_prepare(Error **errp);
-void qemu_savevm_state_setup(QEMUFile *f);
+int qemu_savevm_state_setup(QEMUFile *f, Error **errp);
 bool qemu_savevm_state_guest_unplug_pending(void);
 int qemu_savevm_state_resume_prepare(MigrationState *s);
 void qemu_savevm_state_header(QEMUFile *f);
diff --git a/migration/migration.c b/migration/migration.c
index 
644e073b7dcc70cb2bdaa9c975ba478952465ff4..0704ad6226df61f2f15bd81a2897f9946d601ca7
 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -3427,6 +3427,8 @@ static void *migration_thread(void *opaque)
 int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST);
 MigThrError thr_error;
 bool urgent = false;
+Error *local_err = NULL;
+int ret;
 
 thread = migration_threads_add("live_migration", qemu_get_thread_id());
 
@@ -3470,12 +3472,24 @@ static void *migration_thread(void *opaque)

 }
 
 bql_lock();

-qemu_savevm_state_setup(s->to_dst_file);
+ret = qemu_savevm_state_setup(s->to_dst_file, _err);
 bql_unlock();
 
 qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,

MIGRATION_STATUS_ACTIVE);
 
+/*

+ * Handle SETUP failures after waiting for virtio-net-failover
+ * devices to unplug. This to preserve migration state transitions.
+ */
+if (ret) {
+migrate_set_error(s, local_err);
+error_free(local_err);
+migrate_set_state(>state, MIGRATION_STATUS_ACTIVE,
+  MIGRATION_STATUS_FAILED);
+goto out;
+}
+
 s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
 
 trace_migration_thread_setup_complete();

@@ -3549,6 +3563,8 @@ static void *bg_migration_thread(void *opaque)
 MigThrError thr_error;
 QEMUFile *fb;
 bool early_fail = true;
+Error *local_err = NULL;
+int ret;
 
 rcu_register_thread();

 object_ref(OBJECT(s));
@@ -3582,12 +3598,20 @@ static void *bg_migration_thread(void *opaque)
 
 bql_lock();

 qemu_savevm_state_header(s->to_dst_file);
-qemu_savevm_state_setup(s->to_dst_file);
+ret = qemu_savevm_state_setup(s->to_dst_file, _err);
 bql_unlock();
 
 qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,

MIGRATION_STATUS_ACTIVE);
 
+if (ret) {

+migrate_set_error(s, local_err);
+error_free(local_err);
+migrate_set_state(>state, MIGRATION_STATUS_ACTIVE,
+  MIGRATION_STATUS_FAILED);
+goto fail_setup;
+}
+
 s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
 
 trace_migration_thread_setup_complete();

@@ -3656,6 +3680,7 @@ fail:
 bql_unlock();
 }
 
+fail_setup:

  

Re: [PATCH 03/12] uefi-test-tools: Add support for python based build script

2024-03-15 Thread Gerd Hoffmann
> +Build/bios-tables-test.%.efi:
> + $(PYTHON) ../../roms/edk2-build.py --config uefi-test-build.config

Adding '--match $*' will build one arch instead of all.




Re: [PATCH 02/12] uefi-test-tools/UefiTestToolsPkg: Add RISC-V support

2024-03-15 Thread Gerd Hoffmann
On Fri, Mar 15, 2024 at 06:35:09PM +0530, Sunil V L wrote:
> Enable building the test application for RISC-V with appropriate
> dependencies updated.
> 
> Signed-off-by: Sunil V L 
> ---
>  tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc | 6 +-
>  1 file changed, 5 insertions(+), 1 deletion(-)

Acked-by: Gerd Hoffmann 




Re: [PATCH 01/12] roms/edk2-build.py: Add --module support

2024-03-15 Thread Gerd Hoffmann
On Fri, Mar 15, 2024 at 06:35:08PM +0530, Sunil V L wrote:
> UefiTestToolsPkg which should use edk2-build.py needs --module parameter
> support. Add this optional parameter handling.

I don't think this is needed.  By default everything listed in
[Components] should be built, which is just that one module we
have ;)

take care,
  Gerd




[PATCH for-9.0 v2] vdpa-dev: Fix initialisation order to restore VDUSE compatibility

2024-03-15 Thread Kevin Wolf
VDUSE requires that virtqueues are first enabled before the DRIVER_OK
status flag is set; with the current API of the kernel module, it is
impossible to enable the opposite order in our block export code because
userspace is not notified when a virtqueue is enabled.

This requirement also mathces the normal initialisation order as done by
the generic vhost code in QEMU. However, commit 6c482547 accidentally
changed the order for vdpa-dev and broke access to VDUSE devices with
this.

This changes vdpa-dev to use the normal order again and use the standard
vhost callback .vhost_set_vring_enable for this. VDUSE devices can be
used with vdpa-dev again after this fix.

vhost_net intentionally avoided enabling the vrings for vdpa and does
this manually later while it does enable them for other vhost backends.
Reflect this in the vhost_net code and return early for vdpa, so that
the behaviour doesn't change for this device.

Cc: qemu-sta...@nongnu.org
Fixes: 6c4825476a4351530bcac17abab72295b75ffe98
Signed-off-by: Kevin Wolf 
---
v2:
- Actually make use of the @enable parameter
- Change vhost_net to preserve the current behaviour

 hw/net/vhost_net.c | 10 ++
 hw/virtio/vdpa-dev.c   |  5 +
 hw/virtio/vhost-vdpa.c | 27 +--
 hw/virtio/vhost.c  |  8 +++-
 4 files changed, 43 insertions(+), 7 deletions(-)

diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index e8e1661646..fd1a93701a 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -541,6 +541,16 @@ int vhost_set_vring_enable(NetClientState *nc, int enable)
 VHostNetState *net = get_vhost_net(nc);
 const VhostOps *vhost_ops = net->dev.vhost_ops;
 
+/*
+ * vhost-vdpa network devices need to enable dataplane virtqueues after
+ * DRIVER_OK, so they can recover device state before starting dataplane.
+ * Because of that, we don't enable virtqueues here and leave it to
+ * net/vhost-vdpa.c.
+ */
+if (nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA) {
+return 0;
+}
+
 nc->vring_enable = enable;
 
 if (vhost_ops && vhost_ops->vhost_set_vring_enable) {
diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c
index eb9ecea83b..13e87f06f6 100644
--- a/hw/virtio/vdpa-dev.c
+++ b/hw/virtio/vdpa-dev.c
@@ -253,14 +253,11 @@ static int vhost_vdpa_device_start(VirtIODevice *vdev, 
Error **errp)
 
 s->dev.acked_features = vdev->guest_features;
 
-ret = vhost_dev_start(>dev, vdev, false);
+ret = vhost_dev_start(>dev, vdev, true);
 if (ret < 0) {
 error_setg_errno(errp, -ret, "Error starting vhost");
 goto err_guest_notifiers;
 }
-for (i = 0; i < s->dev.nvqs; ++i) {
-vhost_vdpa_set_vring_ready(>vdpa, i);
-}
 s->started = true;
 
 /*
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index ddae494ca8..401afac2f5 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -886,12 +886,13 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, 
int idx)
 return idx;
 }
 
-int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+static int vhost_vdpa_set_vring_enable_one(struct vhost_vdpa *v, unsigned idx,
+   int enable)
 {
 struct vhost_dev *dev = v->dev;
 struct vhost_vring_state state = {
 .index = idx,
-.num = 1,
+.num = enable,
 };
 int r = vhost_vdpa_call(dev, VHOST_VDPA_SET_VRING_ENABLE, );
 
@@ -899,6 +900,27 @@ int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, 
unsigned idx)
 return r;
 }
 
+static int vhost_vdpa_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+struct vhost_vdpa *v = dev->opaque;
+unsigned int i;
+int ret;
+
+for (i = 0; i < dev->nvqs; ++i) {
+ret = vhost_vdpa_set_vring_enable_one(v, i, enable);
+if (ret < 0) {
+return ret;
+}
+}
+
+return 0;
+}
+
+int vhost_vdpa_set_vring_ready(struct vhost_vdpa *v, unsigned idx)
+{
+return vhost_vdpa_set_vring_enable_one(v, idx, 1);
+}
+
 static int vhost_vdpa_set_config_call(struct vhost_dev *dev,
int fd)
 {
@@ -1514,6 +1536,7 @@ const VhostOps vdpa_ops = {
 .vhost_set_features = vhost_vdpa_set_features,
 .vhost_reset_device = vhost_vdpa_reset_device,
 .vhost_get_vq_index = vhost_vdpa_get_vq_index,
+.vhost_set_vring_enable = vhost_vdpa_set_vring_enable,
 .vhost_get_config  = vhost_vdpa_get_config,
 .vhost_set_config = vhost_vdpa_set_config,
 .vhost_requires_shm_log = NULL,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 2c9ac79468..decfb85184 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1984,7 +1984,13 @@ static int vhost_dev_set_vring_enable(struct vhost_dev 
*hdev, int enable)
 return hdev->vhost_ops->vhost_set_vring_enable(hdev, enable);
 }
 
-/* Host notifiers must be enabled at this point. */
+/*
+ * Host notifiers must be enabled at 

  1   2   3   >