Skip to content

Commit

Permalink
KVM: x86: fix deadlock for KVM_XEN_EVTCHN_RESET
Browse files Browse the repository at this point in the history
[ Upstream commit a79b53a ]

While KVM_XEN_EVTCHN_RESET is usually called with no vCPUs running,
if that happened it could cause a deadlock.  This is due to
kvm_xen_eventfd_reset() doing a synchronize_srcu() inside
a kvm->lock critical section.

To avoid this, first collect all the evtchnfd objects in an
array and free all of them once the kvm->lock critical section
is over and th SRCU grace period has expired.

Reported-by: Michal Luczaj <mhal@rbox.co>
Cc: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
bonzini authored and Sasha Levin committed Feb 24, 2023
1 parent 2c9e2b2 commit a2133d6
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
30 changes: 27 additions & 3 deletions arch/x86/kvm/xen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1757,18 +1757,42 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)

static int kvm_xen_eventfd_reset(struct kvm *kvm)
{
struct evtchnfd *evtchnfd;
struct evtchnfd *evtchnfd, **all_evtchnfds;
int i;
int n = 0;

mutex_lock(&kvm->lock);

/*
* Because synchronize_srcu() cannot be called inside the
* critical section, first collect all the evtchnfd objects
* in an array as they are removed from evtchn_ports.
*/
idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i)
n++;

all_evtchnfds = kmalloc_array(n, sizeof(struct evtchnfd *), GFP_KERNEL);
if (!all_evtchnfds) {
mutex_unlock(&kvm->lock);
return -ENOMEM;
}

n = 0;
idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
all_evtchnfds[n++] = evtchnfd;
idr_remove(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
synchronize_srcu(&kvm->srcu);
}
mutex_unlock(&kvm->lock);

synchronize_srcu(&kvm->srcu);

while (n--) {
evtchnfd = all_evtchnfds[n];
if (!evtchnfd->deliver.port.port)
eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
kfree(evtchnfd);
}
mutex_unlock(&kvm->lock);
kfree(all_evtchnfds);

return 0;
}
Expand Down
6 changes: 6 additions & 0 deletions tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,12 @@ int main(int argc, char *argv[])
}

done:
struct kvm_xen_hvm_attr evt_reset = {
.type = KVM_XEN_ATTR_TYPE_EVTCHN,
.u.evtchn.flags = KVM_XEN_EVTCHN_RESET,
};
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &evt_reset);

alarm(0);
clock_gettime(CLOCK_REALTIME, &max_ts);

Expand Down

0 comments on commit a2133d6

Please sign in to comment.