]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.0-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 1 Aug 2013 23:27:48 +0000 (07:27 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 1 Aug 2013 23:27:48 +0000 (07:27 +0800)
added patches:
xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch

queue-3.0/series
queue-3.0/xen-evtchn-avoid-a-deadlock-when-unbinding-an-event-channel.patch [new file with mode: 0644]

index 82f560540cddcfe78a035584e2c50e702e90edd2..03313464a2b6abe408f447b1514293a1af1194d2 100644 (file)
@@ -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 (file)
index 0000000..3d85b9e
--- /dev/null
@@ -0,0 +1,103 @@
+From 179fbd5a45f0d4034cc6fd37b8d367a3b79663c4 Mon Sep 17 00:00:00 2001
+From: David Vrabel <david.vrabel@citrix.com>
+Date: Fri, 19 Jul 2013 15:51:58 +0100
+Subject: xen/evtchn: avoid a deadlock when unbinding an event channel
+
+From: David Vrabel <david.vrabel@citrix.com>
+
+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 <david.vrabel@citrix.com>
+Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+[v1: Expanded the commit description a bit]
+Signed-off-by: Jonghwan Choi <jhbird.choi@samsung.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);