Commit a79b53aa authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

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: default avatarMichal Luczaj <mhal@rbox.co>
Cc: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent af280890
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -1942,18 +1942,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);
	}
	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;
}
+6 −0
Original line number Diff line number Diff line
@@ -962,6 +962,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);