Skip to content

Commit a79b53a

Browse files
committed
KVM: x86: fix deadlock for KVM_XEN_EVTCHN_RESET
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 <[email protected]> Cc: David Woodhouse <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent af28089 commit a79b53a

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

arch/x86/kvm/xen.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,18 +1942,42 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
19421942

19431943
static int kvm_xen_eventfd_reset(struct kvm *kvm)
19441944
{
1945-
struct evtchnfd *evtchnfd;
1945+
struct evtchnfd *evtchnfd, **all_evtchnfds;
19461946
int i;
1947+
int n = 0;
19471948

19481949
mutex_lock(&kvm->lock);
1950+
1951+
/*
1952+
* Because synchronize_srcu() cannot be called inside the
1953+
* critical section, first collect all the evtchnfd objects
1954+
* in an array as they are removed from evtchn_ports.
1955+
*/
1956+
idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i)
1957+
n++;
1958+
1959+
all_evtchnfds = kmalloc_array(n, sizeof(struct evtchnfd *), GFP_KERNEL);
1960+
if (!all_evtchnfds) {
1961+
mutex_unlock(&kvm->lock);
1962+
return -ENOMEM;
1963+
}
1964+
1965+
n = 0;
19491966
idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
1967+
all_evtchnfds[n++] = evtchnfd;
19501968
idr_remove(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
1951-
synchronize_srcu(&kvm->srcu);
1969+
}
1970+
mutex_unlock(&kvm->lock);
1971+
1972+
synchronize_srcu(&kvm->srcu);
1973+
1974+
while (n--) {
1975+
evtchnfd = all_evtchnfds[n];
19521976
if (!evtchnfd->deliver.port.port)
19531977
eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
19541978
kfree(evtchnfd);
19551979
}
1956-
mutex_unlock(&kvm->lock);
1980+
kfree(all_evtchnfds);
19571981

19581982
return 0;
19591983
}

tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,12 @@ int main(int argc, char *argv[])
962962
}
963963

964964
done:
965+
struct kvm_xen_hvm_attr evt_reset = {
966+
.type = KVM_XEN_ATTR_TYPE_EVTCHN,
967+
.u.evtchn.flags = KVM_XEN_EVTCHN_RESET,
968+
};
969+
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &evt_reset);
970+
965971
alarm(0);
966972
clock_gettime(CLOCK_REALTIME, &max_ts);
967973

0 commit comments

Comments
 (0)