From: Greg Kroah-Hartman Date: Thu, 1 Aug 2013 23:27:48 +0000 (+0800) Subject: 3.0-stable patches X-Git-Tag: v3.0.89~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=18d42a920bfed33e96f244c1df215928c2dab51c;p=thirdparty%2Fkernel%2Fstable-queue.git 3.0-stable patches added patches: xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch --- diff --git a/queue-3.0/series b/queue-3.0/series index 82f560540cd..03313464a2b 100644 --- a/queue-3.0/series +++ b/queue-3.0/series @@ -17,3 +17,4 @@ drm-radeon-improve-dac-adjust-heuristics-for-legacy-pdac.patch drm-radeon-atom-initialize-more-atom-interpretor-elements-to-0.patch usb-serial-ftdi_sio-add-more-rt-systems-ftdi-devices.patch livelock-avoidance-in-sget.patch +xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch diff --git a/queue-3.0/xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch b/queue-3.0/xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch new file mode 100644 index 00000000000..3d85b9e8c87 --- /dev/null +++ b/queue-3.0/xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch @@ -0,0 +1,103 @@ +From 179fbd5a45f0d4034cc6fd37b8d367a3b79663c4 Mon Sep 17 00:00:00 2001 +From: David Vrabel +Date: Fri, 19 Jul 2013 15:51:58 +0100 +Subject: xen/evtchn: avoid a deadlock when unbinding an event channel + +From: David Vrabel + +commit 179fbd5a45f0d4034cc6fd37b8d367a3b79663c4 upstream. + +Unbinding an event channel (either with the ioctl or when the evtchn +device is closed) may deadlock because disable_irq() is called with +port_user_lock held which is also locked by the interrupt handler. + +Think of the IOCTL_EVTCHN_UNBIND is being serviced, the routine has +just taken the lock, and an interrupt happens. The evtchn_interrupt +is invoked, tries to take the lock and spins forever. + +A quick glance at the code shows that the spinlock is a local IRQ +variant. Unfortunately that does not help as "disable_irq() waits for +the interrupt handler on all CPUs to stop running. If the irq occurs +on another VCPU, it tries to take port_user_lock and can't because +the unbind ioctl is holding it." (from David). Hence we cannot +depend on the said spinlock to protect us. We could make it a system +wide IRQ disable spinlock but there is a better way. + +We can piggyback on the fact that the existence of the spinlock is +to make get_port_user() checks be up-to-date. And we can alter those +checks to not depend on the spin lock (as it's protected by u->bind_mutex +in the ioctl) and can remove the unnecessary locking (this is +IOCTL_EVTCHN_UNBIND) path. + +In the interrupt handler we cannot use the mutex, but we do not +need it. + +"The unbind disables the irq before making the port user stale, so when +you clear it you are guaranteed that the interrupt handler that might +use that port cannot be running." (from David). + +Hence this patch removes the spinlock usage on the teardown path +and piggybacks on disable_irq happening before we muck with the +get_port_user() data. This ensures that the interrupt handler will +never run on stale data. + +Signed-off-by: David Vrabel +Signed-off-by: Konrad Rzeszutek Wilk +[v1: Expanded the commit description a bit] +Signed-off-by: Jonghwan Choi +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/xen/evtchn.c | 21 ++------------------- + 1 file changed, 2 insertions(+), 19 deletions(-) + +--- a/drivers/xen/evtchn.c ++++ b/drivers/xen/evtchn.c +@@ -367,18 +367,12 @@ static long evtchn_ioctl(struct file *fi + if (unbind.port >= NR_EVENT_CHANNELS) + break; + +- spin_lock_irq(&port_user_lock); +- + rc = -ENOTCONN; +- if (get_port_user(unbind.port) != u) { +- spin_unlock_irq(&port_user_lock); ++ if (get_port_user(unbind.port) != u) + break; +- } + + disable_irq(irq_from_evtchn(unbind.port)); + +- spin_unlock_irq(&port_user_lock); +- + evtchn_unbind_from_user(u, unbind.port); + + rc = 0; +@@ -478,26 +472,15 @@ static int evtchn_release(struct inode * + int i; + struct per_user_data *u = filp->private_data; + +- spin_lock_irq(&port_user_lock); +- +- free_page((unsigned long)u->ring); +- + for (i = 0; i < NR_EVENT_CHANNELS; i++) { + if (get_port_user(i) != u) + continue; + + disable_irq(irq_from_evtchn(i)); +- } +- +- spin_unlock_irq(&port_user_lock); +- +- for (i = 0; i < NR_EVENT_CHANNELS; i++) { +- if (get_port_user(i) != u) +- continue; +- + evtchn_unbind_from_user(get_port_user(i), i); + } + ++ free_page((unsigned long)u->ring); + kfree(u->name); + kfree(u); +