From: Magnus Kulke Date: Thu, 2 Oct 2025 07:50:12 +0000 (+0200) Subject: target/i386/mshv: Use preallocated page for hvcall X-Git-Tag: v10.2.0-rc1~71^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e4a20afce59937073298d716cc829bdc026542dc;p=thirdparty%2Fqemu.git target/i386/mshv: Use preallocated page for hvcall 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 Link: https://lore.kernel.org/r/20250916164847.77883-26-magnuskulke@linux.microsoft.com [Use standard MAX_CONST macro; mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini --- diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 5edfcbad9d..45174f7c4e 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -399,8 +399,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_int.h b/include/system/mshv_int.h index 6350c69e9d..490563c1ab 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -20,9 +20,16 @@ typedef struct hyperv_message hv_message; +typedef struct MshvHvCallArgs { + void *base; + void *input_page; + void *output_page; +} MshvHvCallArgs; + struct AccelCPUState { int cpufd; bool dirty; + MshvHvCallArgs hvcall_args; }; typedef struct MshvMemoryListener { diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index de87142bff..1f7b9cb37e 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -34,6 +34,10 @@ #include +#define MAX_REGISTER_COUNT (MAX_CONST(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \ + MAX_CONST(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, @@ -151,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; @@ -160,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); @@ -172,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; @@ -193,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}; @@ -204,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; @@ -224,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; @@ -243,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; } @@ -1696,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); } @@ -1704,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_clear_pointer(&env->emu_mmio_buf, g_free); }