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 <[email protected]> --- 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
