[PATCH v4 25/27] target/i386/mshv: Use preallocated page for hvcall

Magnus Kulke posted 27 patches 1 month, 4 weeks ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Magnus Kulke <magnus.kulke@linux.microsoft.com>, Wei Liu <wei.liu@kernel.org>, "Alex Bennée" <alex.bennee@linaro.org>, "Daniel P. Berrangé" <berrange@redhat.com>, Thomas Huth <thuth@redhat.com>, Markus Armbruster <armbru@redhat.com>, "Dr. David Alan Gilbert" <dave@treblig.org>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, Yanan Wang <wangyanan55@huawei.com>, Zhao Liu <zhao1.liu@intel.com>, "Michael S. Tsirkin" <mst@redhat.com>, Cornelia Huck <cohuck@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, Eric Blake <eblake@redhat.com>, Cameron Esfahani <dirty@apple.com>, Roman Bolshakov <rbolshakov@ddn.com>, Phil Dennis-Jordan <phil@philjordan.eu>
There is a newer version of this series
[PATCH v4 25/27] target/i386/mshv: Use preallocated page for hvcall
Posted by Magnus Kulke 1 month, 4 weeks ago
There are hvcalls that are invoked during MMIO exits, the payload is of
dynamic size. To avoid heap allocations we can use preallocated pages as
in/out buffer for those calls. A page is reserved per vCPU and used for
set/get register hv calls.

Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
---
 accel/mshv/mshv-all.c       |  2 +-
 include/system/mshv.h       |  7 +++++++
 target/i386/mshv/mshv-cpu.c | 39 +++++++++++++++++++++++++------------
 3 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 2f7b325985..35a10f7a78 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -398,8 +398,8 @@ static int mshv_init_vcpu(CPUState *cpu)
     uint8_t vp_index = cpu->cpu_index;
     int ret;
 
-    mshv_arch_init_vcpu(cpu);
     cpu->accel = g_new0(AccelCPUState, 1);
+    mshv_arch_init_vcpu(cpu);
 
     ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
     if (ret < 0) {
diff --git a/include/system/mshv.h b/include/system/mshv.h
index fc0a2829c1..c57a4058bc 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -74,9 +74,16 @@ typedef struct MshvState {
 } MshvState;
 extern MshvState *mshv_state;
 
+typedef struct MshvHvCallArgs {
+    void *base;
+    void *input_page;
+    void *output_page;
+} MshvHvCallArgs;
+
 struct AccelCPUState {
     int cpufd;
     bool dirty;
+    MshvHvCallArgs hvcall_args;
 };
 
 typedef struct MshvMsiControl {
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 6b7e795a36..52afc6b303 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -33,6 +33,11 @@
 
 #include <sys/ioctl.h>
 
+#define MAX_SIZE(a, b) ((a) > (b) ? (a) : (b))
+#define MAX_REGISTER_COUNT (MAX_SIZE(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \
+                            MAX_SIZE(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \
+                                     ARRAY_SIZE(FPU_REGISTER_NAMES))))
+
 static enum hv_register_name STANDARD_REGISTER_NAMES[18] = {
     HV_X64_REGISTER_RAX,
     HV_X64_REGISTER_RBX,
@@ -150,7 +155,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
     int cpu_fd = mshv_vcpufd(cpu);
     int vp_index = cpu->cpu_index;
     size_t in_sz, assocs_sz;
-    hv_input_set_vp_registers *in;
+    hv_input_set_vp_registers *in = cpu->accel->hvcall_args.input_page;
     struct mshv_root_hvcall args = {0};
     int ret;
 
@@ -159,7 +164,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
     in_sz = sizeof(hv_input_set_vp_registers) + assocs_sz;
 
     /* fill the input struct */
-    in = g_malloc0(in_sz);
+    memset(in, 0, sizeof(hv_input_set_vp_registers));
     in->vp_index = vp_index;
     memcpy(in->elements, assocs, assocs_sz);
 
@@ -171,7 +176,6 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
 
     /* perform the call */
     ret = mshv_hvcall(cpu_fd, &args);
-    g_free(in);
     if (ret < 0) {
         error_report("Failed to set registers");
         return -1;
@@ -192,8 +196,8 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
 {
     int cpu_fd = mshv_vcpufd(cpu);
     int vp_index = cpu->cpu_index;
-    hv_input_get_vp_registers *in;
-    hv_register_value *values;
+    hv_input_get_vp_registers *in = cpu->accel->hvcall_args.input_page;
+    hv_register_value *values = cpu->accel->hvcall_args.output_page;
     size_t in_sz, names_sz, values_sz;
     int i, ret;
     struct mshv_root_hvcall args = {0};
@@ -203,15 +207,14 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
     in_sz = sizeof(hv_input_get_vp_registers) + names_sz;
 
     /* fill the input struct */
-    in = g_malloc0(in_sz);
+    memset(in, 0, sizeof(hv_input_get_vp_registers));
     in->vp_index = vp_index;
     for (i = 0; i < n_regs; i++) {
         in->names[i] = assocs[i].name;
     }
 
-    /* allocate value output buffer */
+    /* determine size of value output buffer */
     values_sz = n_regs * sizeof(union hv_register_value);
-    values = g_malloc0(values_sz);
 
     /* create the hvcall envelope */
     args.code = HVCALL_GET_VP_REGISTERS;
@@ -223,16 +226,13 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
 
     /* perform the call */
     ret = mshv_hvcall(cpu_fd, &args);
-    g_free(in);
     if (ret < 0) {
-        g_free(values);
         error_report("Failed to retrieve registers");
         return -1;
     }
 
     /* assert we got all registers */
     if (args.reps != n_regs) {
-        g_free(values);
         error_report("Failed to retrieve registers: expected %zu elements"
                      ", got %u", n_regs, args.reps);
         return -1;
@@ -242,7 +242,6 @@ static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
     for (i = 0; i < n_regs; i++) {
         assocs[i].value = values[i];
     }
-    g_free(values);
 
     return 0;
 }
@@ -1695,6 +1694,19 @@ void mshv_arch_init_vcpu(CPUState *cpu)
 {
     X86CPU *x86_cpu = X86_CPU(cpu);
     CPUX86State *env = &x86_cpu->env;
+    AccelCPUState *state = cpu->accel;
+    size_t page = HV_HYP_PAGE_SIZE;
+    void *mem = qemu_memalign(page, 2 * page);
+
+    /* sanity check, to make sure we don't overflow the page */
+    QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
+                      * sizeof(hv_register_assoc)
+                      + sizeof(hv_input_get_vp_registers)
+                      > HV_HYP_PAGE_SIZE));
+
+    state->hvcall_args.base = mem;
+    state->hvcall_args.input_page = mem;
+    state->hvcall_args.output_page = (uint8_t *)mem + page;
 
     env->emu_mmio_buf = g_new(char, 4096);
 }
@@ -1703,7 +1715,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
 {
     X86CPU *x86_cpu = X86_CPU(cpu);
     CPUX86State *env = &x86_cpu->env;
+    AccelCPUState *state = cpu->accel;
 
+    g_free(state->hvcall_args.base);
+    state->hvcall_args = (MshvHvCallArgs){0};
     g_free(env->emu_mmio_buf);
     env->emu_mmio_buf = NULL;
 }
-- 
2.34.1
Re: [PATCH v4 25/27] target/i386/mshv: Use preallocated page for hvcall
Posted by Daniel P. Berrangé 1 month, 2 weeks ago
On Tue, Sep 16, 2025 at 06:48:45PM +0200, Magnus Kulke wrote:
> There are hvcalls that are invoked during MMIO exits, the payload is of
> dynamic size. To avoid heap allocations we can use preallocated pages as
> in/out buffer for those calls. A page is reserved per vCPU and used for
> set/get register hv calls.
> 
> Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
> ---
>  accel/mshv/mshv-all.c       |  2 +-
>  include/system/mshv.h       |  7 +++++++
>  target/i386/mshv/mshv-cpu.c | 39 +++++++++++++++++++++++++------------
>  3 files changed, 35 insertions(+), 13 deletions(-)
> 
> diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
> index 2f7b325985..35a10f7a78 100644
> --- a/accel/mshv/mshv-all.c
> +++ b/accel/mshv/mshv-all.c
> @@ -398,8 +398,8 @@ static int mshv_init_vcpu(CPUState *cpu)
>      uint8_t vp_index = cpu->cpu_index;
>      int ret;
>  
> -    mshv_arch_init_vcpu(cpu);
>      cpu->accel = g_new0(AccelCPUState, 1);
> +    mshv_arch_init_vcpu(cpu);

This change can either be dropped, or squashed into the
earlier patch in this series that introduced this method.

>  
>      ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
>      if (ret < 0) {

> diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
> index 6b7e795a36..52afc6b303 100644
> --- a/target/i386/mshv/mshv-cpu.c
> +++ b/target/i386/mshv/mshv-cpu.c
> @@ -33,6 +33,11 @@
>  
>  #include <sys/ioctl.h>
>  
> +#define MAX_SIZE(a, b) ((a) > (b) ? (a) : (b))

Isn't this the same as the existing 'MAX(a, b)' macro in osdep.h ?

> +#define MAX_REGISTER_COUNT (MAX_SIZE(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \
> +                            MAX_SIZE(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \
> +                                     ARRAY_SIZE(FPU_REGISTER_NAMES))))
> +




With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
Re: [PATCH v4 25/27] target/i386/mshv: Use preallocated page for hvcall
Posted by Magnus Kulke 1 month, 1 week ago
On Wed, Oct 01, 2025 at 01:17:44PM +0100, Daniel P. Berrangé wrote:
> On Tue, Sep 16, 2025 at 06:48:45PM +0200, Magnus Kulke wrote:
> > -    mshv_arch_init_vcpu(cpu);
> >      cpu->accel = g_new0(AccelCPUState, 1);
> > +    mshv_arch_init_vcpu(cpu);
>
> This change can either be dropped, or squashed into the
> earlier patch in this series that introduced this method.
>

right, this makes sense.

> > +#define MAX_SIZE(a, b) ((a) > (b) ? (a) : (b))
>
> Isn't this the same as the existing 'MAX(a, b)' macro in osdep.h ?
>

not quite, I don't think MAX can be used in a static assertion, but
there's a MAX_CONST sibling that we can use.
Re: [PATCH v4 25/27] target/i386/mshv: Use preallocated page for hvcall
Posted by Daniel P. Berrangé 1 month, 1 week ago
On Thu, Oct 02, 2025 at 10:05:52AM +0200, Magnus Kulke wrote:
> On Wed, Oct 01, 2025 at 01:17:44PM +0100, Daniel P. Berrangé wrote:
> > On Tue, Sep 16, 2025 at 06:48:45PM +0200, Magnus Kulke wrote:
> > > -    mshv_arch_init_vcpu(cpu);
> > >      cpu->accel = g_new0(AccelCPUState, 1);
> > > +    mshv_arch_init_vcpu(cpu);
> >
> > This change can either be dropped, or squashed into the
> > earlier patch in this series that introduced this method.
> >
> 
> right, this makes sense.
> 
> > > +#define MAX_SIZE(a, b) ((a) > (b) ? (a) : (b))
> >
> > Isn't this the same as the existing 'MAX(a, b)' macro in osdep.h ?
> >
> 
> not quite, I don't think MAX can be used in a static assertion, but
> there's a MAX_CONST sibling that we can use.

Ah yes, that sounds good.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|