hv: pm: support shutting down multiple VMs when pCPUs are shared

More than one VM may request shutdown on the same pCPU before
shutdown_vm_from_idle() is called in the idle thread when pCPUs are
shared among VMs.

Use a per-pCPU bitmap to store all the VMIDs requesting shutdown.

v1 -> v2:
- use vm_lock to avoid a race on shutdown

Tracked-On: #5411
Signed-off-by: Peter Fang <peter.fang@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Peter Fang 2020-10-22 02:25:27 -07:00 committed by wenlingz
parent 63fd853df3
commit 70b1218952
4 changed files with 38 additions and 19 deletions

View File

@ -150,20 +150,25 @@ static bool pm1ab_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width)
return true;
}
static inline void enter_s5(struct acrn_vm *vm, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val)
static inline void enter_s5(struct acrn_vcpu *vcpu, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val)
{
struct acrn_vm *vm = vcpu->vm;
uint16_t pcpu_id = pcpuid_from_vcpu(vcpu);
get_vm_lock(vm);
/*
* It's possible that ACRN come here from SOS and pre-launched VM. Currently, we
* assume SOS has full ACPI power management stack. That means the value from SOS
* should be saved and used to shutdown the system.
* Currently, we assume SOS has full ACPI power management stack.
* That means the value from SOS should be saved and used to shut
* down the system.
*/
if (is_sos_vm(vm)) {
save_s5_reg_val(pm1a_cnt_val, pm1b_cnt_val);
}
pause_vm(vm);
(void)shutdown_vm(vm);
put_vm_lock(vm);
bitmap_set_nolock(vm->vm_id, &per_cpu(shutdown_vm_bitmap, pcpu_id));
make_shutdown_vm_request(pcpu_id);
}
static inline void enter_s3(struct acrn_vm *vm, uint32_t pm1a_cnt_val, uint32_t pm1b_cnt_val)
@ -206,7 +211,7 @@ static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width,
if (vm->pm.sx_state_data->s3_pkg.val_pm1a == val) {
enter_s3(vm, v, 0U);
} else if (vm->pm.sx_state_data->s5_pkg.val_pm1a == val) {
enter_s5(vm, v, 0U);
enter_s5(vcpu, v, 0U);
}
}
@ -220,7 +225,7 @@ static bool pm1ab_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width,
if (vm->pm.sx_state_data->s3_pkg.val_pm1b == val) {
enter_s3(vm, pm1a_cnt_val, v);
} else if (vm->pm.sx_state_data->s5_pkg.val_pm1b == val) {
enter_s5(vm, pm1a_cnt_val, v);
enter_s5(vcpu, pm1a_cnt_val, v);
}
} else {
/* the case broke ACPI spec */

View File

@ -52,7 +52,8 @@ void triple_fault_shutdown_vm(struct acrn_vcpu *vcpu)
pause_vm(vm);
put_vm_lock(vm);
per_cpu(shutdown_vm_id, pcpuid_from_vcpu(vcpu)) = vm->vm_id;
bitmap_set_nolock(vm->vm_id,
&per_cpu(shutdown_vm_bitmap, pcpuid_from_vcpu(vcpu)));
make_shutdown_vm_request(pcpuid_from_vcpu(vcpu));
}
}
@ -83,8 +84,9 @@ static bool handle_reset_reg_read(struct acrn_vcpu *vcpu, __unused uint16_t addr
/**
* @pre vm != NULL
*/
static bool handle_common_reset_reg_write(struct acrn_vm *vm, bool reset)
static bool handle_common_reset_reg_write(struct acrn_vcpu *vcpu, bool reset)
{
struct acrn_vm *vm = vcpu->vm;
bool ret = true;
get_vm_lock(vm);
@ -104,11 +106,10 @@ static bool handle_common_reset_reg_write(struct acrn_vm *vm, bool reset)
* or pre-launched VM reset,
* ACRN doesn't support re-launch, just shutdown the guest.
*/
const struct acrn_vcpu *bsp = vcpu_from_vid(vm, BSP_CPU_ID);
pause_vm(vm);
per_cpu(shutdown_vm_id, pcpuid_from_vcpu(bsp)) = vm->vm_id;
make_shutdown_vm_request(pcpuid_from_vcpu(bsp));
bitmap_set_nolock(vm->vm_id,
&per_cpu(shutdown_vm_bitmap, pcpuid_from_vcpu(vcpu)));
make_shutdown_vm_request(pcpuid_from_vcpu(vcpu));
}
} else {
if (is_postlaunched_vm(vm)) {
@ -132,7 +133,7 @@ static bool handle_common_reset_reg_write(struct acrn_vm *vm, bool reset)
static bool handle_kb_write(struct acrn_vcpu *vcpu, __unused uint16_t addr, size_t bytes, uint32_t val)
{
/* ignore commands other than system reset */
return handle_common_reset_reg_write(vcpu->vm, ((bytes == 1U) && (val == 0xfeU)));
return handle_common_reset_reg_write(vcpu, ((bytes == 1U) && (val == 0xfeU)));
}
static bool handle_kb_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes)
@ -163,7 +164,7 @@ static bool handle_kb_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t bytes)
static bool handle_cf9_write(struct acrn_vcpu *vcpu, __unused uint16_t addr, size_t bytes, uint32_t val)
{
/* We don't differentiate among hard/soft/warm/cold reset */
return handle_common_reset_reg_write(vcpu->vm,
return handle_common_reset_reg_write(vcpu,
((bytes == 1U) && ((val & 0x4U) == 0x4U) && ((val & 0xaU) != 0U)));
}
@ -179,7 +180,7 @@ static bool handle_reset_reg_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t
struct acpi_reset_reg *reset_reg = get_host_reset_reg_data();
if (val == reset_reg->val) {
ret = handle_common_reset_reg_write(vcpu->vm, true);
ret = handle_common_reset_reg_write(vcpu, true);
} else {
/*
* ACPI defines the reset value but doesn't specify the meaning of other values.
@ -234,7 +235,17 @@ void register_reset_port_handler(struct acrn_vm *vm)
void shutdown_vm_from_idle(uint16_t pcpu_id)
{
struct acrn_vm *vm = get_vm_from_vmid(per_cpu(shutdown_vm_id, pcpu_id));
uint16_t vm_id;
uint64_t *vms = &per_cpu(shutdown_vm_bitmap, pcpu_id);
struct acrn_vm *vm;
(void)shutdown_vm(vm);
for (vm_id = fls64(*vms); vm_id < CONFIG_MAX_VM_NUM; vm_id = fls64(*vms)) {
vm = get_vm_from_vmid(vm_id);
get_vm_lock(vm);
if (is_paused_vm(vm)) {
(void)shutdown_vm(vm);
}
put_vm_lock(vm);
bitmap_clear_nolock(vm_id, vms);
}
}

View File

@ -256,10 +256,13 @@ int32_t hcall_destroy_vm(__unused struct acrn_vm *vm, struct acrn_vm *target_vm,
{
int32_t ret = -1;
get_vm_lock(target_vm);
if (is_paused_vm(target_vm)) {
/* TODO: check target_vm guest_flags */
ret = shutdown_vm(target_vm);
}
put_vm_lock(target_vm);
return ret;
}

View File

@ -57,7 +57,7 @@ struct per_cpu_region {
#ifdef PROFILING_ON
struct profiling_info_wrapper profiling_info;
#endif
uint16_t shutdown_vm_id;
uint64_t shutdown_vm_bitmap;
uint64_t tsc_suspend;
/*
* We maintain a per-pCPU array of vCPUs. vCPUs of a VM won't