mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
Merge branch kvm-arm64/cross-vcpu-sync into kvmarm-master/fixes
* kvm-arm64/cross-vcpu-sync: : \ : Fix a number of issues related to publication of state when a vcpu : is forced out of the guest, address other problems related to the : nesting and overhead of pausing the VM, and apply all of that to : some of the nasty corners of the VGIC handling. : : This eventually simplifies the handling of operations that are : inherently racy by pausing the involved vcpus. : / KVM: arm64: vgic-its: Remove the pending state of a discarded LPI KVM: arm64: vgic-its: Don't map an LPI from more than one event KVM: arm64: vgic-its: Stop the VM when handling MOVALL KVM: arm64: vgic: Stop the target vcpu when disabling LPIs KVM: arm64: Add helpers to halt a vCPU KVM: arm64: Turn vcpu->arch.pause into a counter KVM: arm64: Move IN_GUEST_MODE ahead of state being loaded KVM: arm64: Move OUTSIDE_GUEST_MODE publication past context being saved KVM: arm64: nv: Move L2->L1 switch on interrupt early Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
af3a65d20d
|
|
@ -965,11 +965,10 @@ struct kvm_vcpu_arch {
|
|||
/*
|
||||
* Don't run the guest (internal implementation need).
|
||||
*
|
||||
* Contrary to the flags above, this is set/cleared outside of
|
||||
* a vcpu context, and thus cannot be mixed with the flags
|
||||
* themselves (or the flag accesses need to be made atomic).
|
||||
* Contrary to the flags above, this is updated outside of
|
||||
* a vcpu context, and thus cannot be mixed with the flags.
|
||||
*/
|
||||
bool pause;
|
||||
atomic_t pause;
|
||||
|
||||
/*
|
||||
* We maintain more than a single set of debug registers to support
|
||||
|
|
@ -1353,6 +1352,8 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
|
|||
|
||||
void kvm_arm_halt_guest(struct kvm *kvm);
|
||||
void kvm_arm_resume_guest(struct kvm *kvm);
|
||||
void kvm_pause_vcpu(struct kvm_vcpu *vcpu);
|
||||
void kvm_resume_vcpu(struct kvm_vcpu *vcpu);
|
||||
|
||||
#define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid))
|
||||
|
||||
|
|
|
|||
|
|
@ -590,6 +590,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
kvm_arm_pvtime_vcpu_init(&vcpu->arch);
|
||||
|
||||
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
|
||||
atomic_set(&vcpu->arch.pause, 0);
|
||||
|
||||
/*
|
||||
* This vCPU may have been created after mpidr_data was initialized.
|
||||
|
|
@ -954,6 +955,11 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool vcpu_can_run(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return !kvm_arm_vcpu_stopped(vcpu) && !atomic_read(&vcpu->arch.pause);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled
|
||||
* @v: The VCPU pointer
|
||||
|
|
@ -969,8 +975,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
|
|||
(kvm_timer_should_notify_user(v) ||
|
||||
kvm_pmu_should_notify_user(v)));
|
||||
|
||||
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v))
|
||||
&& !kvm_arm_vcpu_stopped(v) && !v->arch.pause);
|
||||
return ((irq_lines || kvm_vgic_vcpu_pending_irq(v)) && vcpu_can_run(v));
|
||||
}
|
||||
|
||||
bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
|
||||
|
|
@ -1127,13 +1132,28 @@ bool kvm_arch_intc_initialized(struct kvm *kvm)
|
|||
return vgic_initialized(kvm);
|
||||
}
|
||||
|
||||
void kvm_pause_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
DECLARE_BITMAP(vcpu_bitmap, KVM_MAX_VCPUS) = { };
|
||||
|
||||
atomic_inc(&vcpu->arch.pause);
|
||||
__set_bit(vcpu->vcpu_idx, vcpu_bitmap);
|
||||
kvm_make_vcpus_request_mask(vcpu->kvm, KVM_REQ_SLEEP, vcpu_bitmap);
|
||||
}
|
||||
|
||||
void kvm_resume_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (atomic_dec_and_test(&vcpu->arch.pause))
|
||||
__kvm_vcpu_wake_up(vcpu);
|
||||
}
|
||||
|
||||
void kvm_arm_halt_guest(struct kvm *kvm)
|
||||
{
|
||||
unsigned long i;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
vcpu->arch.pause = true;
|
||||
atomic_inc(&vcpu->arch.pause);
|
||||
kvm_make_all_cpus_request(kvm, KVM_REQ_SLEEP);
|
||||
}
|
||||
|
||||
|
|
@ -1142,21 +1162,17 @@ void kvm_arm_resume_guest(struct kvm *kvm)
|
|||
unsigned long i;
|
||||
struct kvm_vcpu *vcpu;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||
vcpu->arch.pause = false;
|
||||
__kvm_vcpu_wake_up(vcpu);
|
||||
}
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
kvm_resume_vcpu(vcpu);
|
||||
}
|
||||
|
||||
static void kvm_vcpu_sleep(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct rcuwait *wait = kvm_arch_vcpu_get_wait(vcpu);
|
||||
|
||||
rcuwait_wait_event(wait,
|
||||
(!kvm_arm_vcpu_stopped(vcpu)) && (!vcpu->arch.pause),
|
||||
TASK_INTERRUPTIBLE);
|
||||
rcuwait_wait_event(wait, vcpu_can_run(vcpu), TASK_INTERRUPTIBLE);
|
||||
|
||||
if (kvm_arm_vcpu_stopped(vcpu) || vcpu->arch.pause) {
|
||||
if (!vcpu_can_run(vcpu)) {
|
||||
/* Awaken to handle a signal, request we sleep again later. */
|
||||
kvm_make_request(KVM_REQ_SLEEP, vcpu);
|
||||
}
|
||||
|
|
@ -1437,38 +1453,40 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
*/
|
||||
preempt_disable();
|
||||
|
||||
kvm_nested_flush_hwstate(vcpu);
|
||||
|
||||
if (kvm_vcpu_has_pmu(vcpu))
|
||||
kvm_pmu_flush_hwstate(vcpu);
|
||||
|
||||
kvm_nested_flush_hwstate(vcpu);
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
kvm_vgic_flush_hwstate(vcpu);
|
||||
|
||||
kvm_pmu_update_vcpu_events(vcpu);
|
||||
|
||||
/*
|
||||
* Ensure we set mode to IN_GUEST_MODE after we disable
|
||||
* interrupts and before the final VCPU requests check.
|
||||
* See the comment in kvm_vcpu_exiting_guest_mode() and
|
||||
* Documentation/virt/kvm/vcpu-requests.rst
|
||||
*
|
||||
* Requests to self must be posted before this point, or
|
||||
* won't be taken into account until the next exit. No state
|
||||
* loading other than what happens at vcpu_load() time can
|
||||
* be done before we have checked for pending requests.
|
||||
*/
|
||||
smp_store_mb(vcpu->mode, IN_GUEST_MODE);
|
||||
|
||||
if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) {
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
isb(); /* Ensure work in x_flush_hwstate is committed */
|
||||
if (kvm_vcpu_has_pmu(vcpu))
|
||||
kvm_pmu_sync_hwstate(vcpu);
|
||||
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
|
||||
kvm_timer_sync_user(vcpu);
|
||||
kvm_vgic_sync_hwstate(vcpu);
|
||||
smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* From here onwards, we are commited to entering the guest */
|
||||
kvm_vgic_flush_hwstate(vcpu);
|
||||
|
||||
kvm_pmu_update_vcpu_events(vcpu);
|
||||
|
||||
kvm_arch_vcpu_ctxflush_fp(vcpu);
|
||||
|
||||
/**************************************************************
|
||||
|
|
@ -1479,7 +1497,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
|
||||
ret = kvm_arm_vcpu_enter_exit(vcpu);
|
||||
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
vcpu->stat.exits++;
|
||||
/*
|
||||
* Back from guest
|
||||
|
|
@ -1513,6 +1530,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
|
||||
kvm_arch_vcpu_ctxsync_fp(vcpu);
|
||||
|
||||
/*
|
||||
* The state that was pushed into the CPU on entering
|
||||
* the guest has been synchronised back, let's
|
||||
* advertise we're outside of the guest.
|
||||
*/
|
||||
smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
|
||||
|
||||
/*
|
||||
* We must ensure that any pending interrupts are taken before
|
||||
* we exit guest timing so that timer ticks are accounted as
|
||||
|
|
|
|||
|
|
@ -2181,6 +2181,9 @@ void check_nested_vcpu_requests(struct kvm_vcpu *vcpu)
|
|||
*
|
||||
* What follows is some slop to (1) react to exception routing / masking and (2)
|
||||
* preserve the pending SError state across translation regimes.
|
||||
*
|
||||
* Also generate the maintenance interrupt state, and finally check that if
|
||||
* we need to take an interrupt targetting the guest hypervisor.
|
||||
*/
|
||||
void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
|
@ -2189,6 +2192,11 @@ void kvm_nested_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
|
||||
if (unlikely(vcpu_test_and_clear_flag(vcpu, NESTED_SERROR_PENDING)))
|
||||
kvm_inject_serror_esr(vcpu, vcpu_get_vsesr(vcpu));
|
||||
|
||||
vgic_v3_nested_update_mi(vcpu);
|
||||
|
||||
if (vgic_state_is_nested(vcpu) && kvm_vgic_vcpu_pending_irq(vcpu))
|
||||
kvm_make_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu);
|
||||
}
|
||||
|
||||
void kvm_nested_sync_hwstate(struct kvm_vcpu *vcpu)
|
||||
|
|
|
|||
|
|
@ -776,6 +776,7 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite)
|
|||
its_unmap_vlpi(ite->irq->host_irq);
|
||||
|
||||
irq->hw = false;
|
||||
irq->mapped = false;
|
||||
}
|
||||
|
||||
vgic_put_irq(kvm, ite->irq);
|
||||
|
|
@ -784,6 +785,23 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite)
|
|||
kfree(ite);
|
||||
}
|
||||
|
||||
/* Mapping one LPI from two events is UNPREDICTABLE: the first ITE keeps it. */
|
||||
static bool vgic_its_map_lpi(struct kvm *kvm, struct its_ite *ite,
|
||||
struct vgic_irq *irq, struct kvm_vcpu *vcpu)
|
||||
{
|
||||
scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) {
|
||||
if (!irq->mapped) {
|
||||
irq->mapped = true;
|
||||
irq->target_vcpu = vcpu;
|
||||
ite->irq = irq;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
vgic_put_irq(kvm, irq);
|
||||
return false;
|
||||
}
|
||||
|
||||
static u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size)
|
||||
{
|
||||
return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1);
|
||||
|
|
@ -815,13 +833,36 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its,
|
|||
struct its_device *device = find_its_device(its, device_id);
|
||||
int ite_esz = vgic_its_get_abi(its)->ite_esz;
|
||||
gpa_t gpa = device->itt_addr + ite->event_id * ite_esz;
|
||||
/*
|
||||
* Though the spec talks about removing the pending state, we
|
||||
* don't bother here since we clear the ITTE anyway and the
|
||||
* pending state is a property of the ITTE struct.
|
||||
*/
|
||||
struct vgic_irq *irq = ite->irq;
|
||||
struct kvm_vcpu *vcpu, *target;
|
||||
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
/*
|
||||
* Only an LPI on a vCPU's AP list can be in its list registers,
|
||||
* which the exit path folds back, and that exit moves the LPI
|
||||
* to its target vCPU if the target has changed: pause both.
|
||||
*/
|
||||
scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) {
|
||||
vcpu = irq->vcpu;
|
||||
target = irq->target_vcpu;
|
||||
}
|
||||
|
||||
if (vcpu) {
|
||||
if (target != vcpu)
|
||||
kvm_pause_vcpu(target);
|
||||
kvm_pause_vcpu(vcpu);
|
||||
}
|
||||
|
||||
scoped_guard(raw_spinlock_irqsave, &irq->irq_lock)
|
||||
irq->pending_latch = false;
|
||||
|
||||
if (vcpu) {
|
||||
kvm_resume_vcpu(vcpu);
|
||||
if (target != vcpu)
|
||||
kvm_resume_vcpu(target);
|
||||
}
|
||||
|
||||
its_free_ite(kvm, ite);
|
||||
|
||||
return vgic_its_write_entry_lock(its, gpa, 0ULL, ite);
|
||||
|
|
@ -1093,13 +1134,14 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its,
|
|||
vcpu = collection_to_vcpu(kvm, collection);
|
||||
|
||||
irq = vgic_add_lpi(kvm, lpi_nr, vcpu);
|
||||
if (!IS_ERR(irq) && !vgic_its_map_lpi(kvm, ite, irq, vcpu))
|
||||
irq = ERR_PTR(-EEXIST);
|
||||
if (IS_ERR(irq)) {
|
||||
if (new_coll)
|
||||
vgic_its_free_collection(its, coll_id);
|
||||
its_free_ite(kvm, ite);
|
||||
return PTR_ERR(irq);
|
||||
}
|
||||
ite->irq = irq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1382,6 +1424,15 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
|||
if (vcpu1 == vcpu2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Bulk operations such as MOVALL are a pain, as they can clash
|
||||
* badly locking-wise with other vcpus entering and exiting the
|
||||
* guest, should they be affected by it. Stopping the guest is a
|
||||
* safer bet to ensure uncontended access and ultimately forward
|
||||
* progress. Yeah...
|
||||
*/
|
||||
kvm_arm_halt_guest(kvm);
|
||||
|
||||
xa_for_each(&dist->lpi_xa, intid, irq) {
|
||||
irq = vgic_get_irq(kvm, intid);
|
||||
if (!irq)
|
||||
|
|
@ -1394,6 +1445,8 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
|
|||
|
||||
vgic_its_invalidate_cache(its);
|
||||
|
||||
kvm_arm_resume_guest(kvm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2221,7 +2274,10 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id,
|
|||
its_free_ite(kvm, ite);
|
||||
return PTR_ERR(irq);
|
||||
}
|
||||
ite->irq = irq;
|
||||
|
||||
/* Drop an ITE whose LPI another ITE maps, as MAPTI does. */
|
||||
if (!vgic_its_map_lpi(kvm, ite, irq, vcpu))
|
||||
its_free_ite(kvm, ite);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,9 +300,19 @@ static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
|
|||
if (ctlr != GICR_CTLR_ENABLE_LPIS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Yes, disabling LPIs is painful, since it can be done from
|
||||
* a *remote* vcpu! So let's not take any chance, and make
|
||||
* sure that the target vCPU has written their LRs back to the
|
||||
* irq structures, and release any reference they would have.
|
||||
*
|
||||
* If it hurts, don't do it.
|
||||
*/
|
||||
kvm_pause_vcpu(vcpu);
|
||||
vgic_flush_pending_lpis(vcpu);
|
||||
vgic_its_invalidate_all_caches(vcpu->kvm);
|
||||
atomic_set_release(&vgic_cpu->ctlr, 0);
|
||||
kvm_resume_vcpu(vcpu);
|
||||
} else {
|
||||
ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 0,
|
||||
GICR_CTLR_ENABLE_LPIS);
|
||||
|
|
|
|||
|
|
@ -1167,7 +1167,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
*
|
||||
* We do that by placing a request to ourselves which will
|
||||
* abort the entry procedure and inject the exception at the
|
||||
* beginning of the run loop.
|
||||
* beginning of the run loop (see kvm_nested_flush_hwstate()).
|
||||
*
|
||||
* - Otherwise, do exactly *NOTHING* apart from enabling the virtual
|
||||
* CPU interface. The guest state is already loaded, and we can
|
||||
|
|
@ -1177,16 +1177,10 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
* maintenance interrupt state, as it may fire.
|
||||
*/
|
||||
if (vgic_state_is_nested(vcpu)) {
|
||||
if (kvm_vgic_vcpu_pending_irq(vcpu))
|
||||
kvm_make_request(KVM_REQ_GUEST_HYP_IRQ_PENDING, vcpu);
|
||||
|
||||
vgic_v3_flush_nested(vcpu);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vcpu_has_nv(vcpu))
|
||||
vgic_v3_nested_update_mi(vcpu);
|
||||
|
||||
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
|
||||
|
||||
vgic_flush_state(vcpu);
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ struct vgic_irq {
|
|||
bool active:1;
|
||||
bool hw:1; /* Tied to HW IRQ */
|
||||
bool on_lr:1; /* Present in a CPU LR */
|
||||
bool mapped:1; /* LPIs: mapped by an ITE */
|
||||
refcount_t refcount; /* Used for LPIs */
|
||||
u32 hwintid; /* HW INTID number */
|
||||
unsigned int host_irq; /* linux irq corresponding to hwintid */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user