Skip to content

Commit

Permalink
KVM: x86/xen: Fix potential deadlock in kvm_xen_update_runstate_guest()
Browse files Browse the repository at this point in the history
The kvm_xen_update_runstate_guest() function can be called when the vCPU
is being scheduled out, from a preempt notifier. It *opportunistically*
updates the runstate area in the guest memory, if the gfn_to_pfn_cache
which caches the appropriate address is still valid.

If there is *contention* when it attempts to obtain gpc->lock, then
locking inside the priority inheritance checks may cause a deadlock.
Lockdep reports:

[13890.148997] Chain exists of:
                 &gpc->lock --> &p->pi_lock --> &rq->__lock

[13890.149002]  Possible unsafe locking scenario:

[13890.149003]        CPU0                    CPU1
[13890.149004]        ----                    ----
[13890.149005]   lock(&rq->__lock);
[13890.149007]                                lock(&p->pi_lock);
[13890.149009]                                lock(&rq->__lock);
[13890.149011]   lock(&gpc->lock);
[13890.149013]
                *** DEADLOCK ***

In the general case, if there's contention for a read lock on gpc->lock,
that's going to be because something else is either invalidating or
revalidating the cache. Either way, we've raced with seeing it in an
invalid state, in which case we would have aborted the opportunistic
update anyway.

So in the 'atomic' case when called from the preempt notifier, just
switch to using read_trylock() and avoid the PI handling altogether.

Signed-off-by: David Woodhouse <[email protected]>
Message-Id: <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
  • Loading branch information
dwmw2 authored and bonzini committed Jan 11, 2023
1 parent 23e6025 commit bbe17c6
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions arch/x86/kvm/xen.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,15 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* Attempt to obtain the GPC lock on *both* (if there are two)
* gfn_to_pfn caches that cover the region.
*/
read_lock_irqsave(&gpc1->lock, flags);
if (atomic) {
local_irq_save(flags);
if (!read_trylock(&gpc1->lock)) {
local_irq_restore(flags);
return;
}
} else {
read_lock_irqsave(&gpc1->lock, flags);
}
while (!kvm_gpc_check(gpc1, user_len1)) {
read_unlock_irqrestore(&gpc1->lock, flags);

Expand Down Expand Up @@ -308,7 +316,14 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* gpc1 lock to make lockdep shut up about it.
*/
lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
read_lock(&gpc2->lock);
if (atomic) {
if (!read_trylock(&gpc2->lock)) {
read_unlock_irqrestore(&gpc1->lock, flags);
return;
}
} else {
read_lock(&gpc2->lock);
}

if (!kvm_gpc_check(gpc2, user_len2)) {
read_unlock(&gpc2->lock);
Expand Down

0 comments on commit bbe17c6

Please sign in to comment.