staging: usbip: use interrupt safe spinlock to avoid potential deadlock.
authorHarvey Yang <harvey.huawei.yang@gmail.com>
Tue, 22 Jan 2013 05:31:30 +0000 (13:31 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Jan 2013 17:00:10 +0000 (09:00 -0800)
The function 'usbip_event_add()' may be called in interrupt context on
the stub side:
'stub_complete'->'stub_enqueue_ret_unlink'->'usbip_event_add'.
In this function it tries to get the lock 'ud->lock', so we should
disable irq when we get this lock in process context.

Signed-off-by: Harvey Yang <harvey.huawei.yang@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/usbip/stub_dev.c
drivers/staging/usbip/stub_rx.c
drivers/staging/usbip/usbip_event.c

index ee36415eb26db02a543f9a5d75642b21bfe07712..d36c69e16d366aabfc9cc2bb82769bda7a11d192 100644 (file)
@@ -67,9 +67,9 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
                return -ENODEV;
        }
 
-       spin_lock(&sdev->ud.lock);
+       spin_lock_irq(&sdev->ud.lock);
        status = sdev->ud.status;
-       spin_unlock(&sdev->ud.lock);
+       spin_unlock_irq(&sdev->ud.lock);
 
        return snprintf(buf, PAGE_SIZE, "%d\n", status);
 }
@@ -97,39 +97,39 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
        if (sockfd != -1) {
                dev_info(dev, "stub up\n");
 
-               spin_lock(&sdev->ud.lock);
+               spin_lock_irq(&sdev->ud.lock);
 
                if (sdev->ud.status != SDEV_ST_AVAILABLE) {
                        dev_err(dev, "not ready\n");
-                       spin_unlock(&sdev->ud.lock);
+                       spin_unlock_irq(&sdev->ud.lock);
                        return -EINVAL;
                }
 
                socket = sockfd_to_socket(sockfd);
                if (!socket) {
-                       spin_unlock(&sdev->ud.lock);
+                       spin_unlock_irq(&sdev->ud.lock);
                        return -EINVAL;
                }
                sdev->ud.tcp_socket = socket;
 
-               spin_unlock(&sdev->ud.lock);
+               spin_unlock_irq(&sdev->ud.lock);
 
                sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, "stub_rx");
                sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, "stub_tx");
 
-               spin_lock(&sdev->ud.lock);
+               spin_lock_irq(&sdev->ud.lock);
                sdev->ud.status = SDEV_ST_USED;
-               spin_unlock(&sdev->ud.lock);
+               spin_unlock_irq(&sdev->ud.lock);
 
        } else {
                dev_info(dev, "stub down\n");
 
-               spin_lock(&sdev->ud.lock);
+               spin_lock_irq(&sdev->ud.lock);
                if (sdev->ud.status != SDEV_ST_USED) {
-                       spin_unlock(&sdev->ud.lock);
+                       spin_unlock_irq(&sdev->ud.lock);
                        return -EINVAL;
                }
-               spin_unlock(&sdev->ud.lock);
+               spin_unlock_irq(&sdev->ud.lock);
 
                usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
        }
@@ -241,9 +241,9 @@ static void stub_device_reset(struct usbip_device *ud)
        ret = usb_lock_device_for_reset(udev, sdev->interface);
        if (ret < 0) {
                dev_err(&udev->dev, "lock for reset\n");
-               spin_lock(&ud->lock);
+               spin_lock_irq(&ud->lock);
                ud->status = SDEV_ST_ERROR;
-               spin_unlock(&ud->lock);
+               spin_unlock_irq(&ud->lock);
                return;
        }
 
@@ -251,7 +251,7 @@ static void stub_device_reset(struct usbip_device *ud)
        ret = usb_reset_device(udev);
        usb_unlock_device(udev);
 
-       spin_lock(&ud->lock);
+       spin_lock_irq(&ud->lock);
        if (ret) {
                dev_err(&udev->dev, "device reset\n");
                ud->status = SDEV_ST_ERROR;
@@ -259,14 +259,14 @@ static void stub_device_reset(struct usbip_device *ud)
                dev_info(&udev->dev, "device reset\n");
                ud->status = SDEV_ST_AVAILABLE;
        }
-       spin_unlock(&ud->lock);
+       spin_unlock_irq(&ud->lock);
 }
 
 static void stub_device_unusable(struct usbip_device *ud)
 {
-       spin_lock(&ud->lock);
+       spin_lock_irq(&ud->lock);
        ud->status = SDEV_ST_ERROR;
-       spin_unlock(&ud->lock);
+       spin_unlock_irq(&ud->lock);
 }
 
 /**
index 0572a15242b5a7c85efb5dafff64645e6693d6da..e7458e1ad15118f13ccd49c798c132f01cb709d5 100644 (file)
@@ -307,12 +307,12 @@ static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
        int valid = 0;
 
        if (pdu->base.devid == sdev->devid) {
-               spin_lock(&ud->lock);
+               spin_lock_irq(&ud->lock);
                if (ud->status == SDEV_ST_USED) {
                        /* A request is valid. */
                        valid = 1;
                }
-               spin_unlock(&ud->lock);
+               spin_unlock_irq(&ud->lock);
        }
 
        return valid;
index d332a34ddb6d125f7e737d8abe146f12750f8faa..82123be8732d49861db86d11c8f6de895978b62d 100644 (file)
@@ -105,10 +105,12 @@ EXPORT_SYMBOL_GPL(usbip_stop_eh);
 
 void usbip_event_add(struct usbip_device *ud, unsigned long event)
 {
-       spin_lock(&ud->lock);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ud->lock, flags);
        ud->event |= event;
        wake_up(&ud->eh_waitq);
-       spin_unlock(&ud->lock);
+       spin_unlock_irqrestore(&ud->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usbip_event_add);