Commit dcf14779 authored by Harvey Yang's avatar Harvey Yang Committed by Greg Kroah-Hartman

staging: usbip: use interrupt safe spinlock to avoid potential deadlock.

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: default avatarHarvey Yang <harvey.huawei.yang@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 26ef1021
...@@ -67,9 +67,9 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr, ...@@ -67,9 +67,9 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr,
return -ENODEV; return -ENODEV;
} }
spin_lock(&sdev->ud.lock); spin_lock_irq(&sdev->ud.lock);
status = sdev->ud.status; status = sdev->ud.status;
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
return snprintf(buf, PAGE_SIZE, "%d\n", status); return snprintf(buf, PAGE_SIZE, "%d\n", status);
} }
...@@ -97,39 +97,39 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, ...@@ -97,39 +97,39 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr,
if (sockfd != -1) { if (sockfd != -1) {
dev_info(dev, "stub up\n"); dev_info(dev, "stub up\n");
spin_lock(&sdev->ud.lock); spin_lock_irq(&sdev->ud.lock);
if (sdev->ud.status != SDEV_ST_AVAILABLE) { if (sdev->ud.status != SDEV_ST_AVAILABLE) {
dev_err(dev, "not ready\n"); dev_err(dev, "not ready\n");
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
return -EINVAL; return -EINVAL;
} }
socket = sockfd_to_socket(sockfd); socket = sockfd_to_socket(sockfd);
if (!socket) { if (!socket) {
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
return -EINVAL; return -EINVAL;
} }
sdev->ud.tcp_socket = socket; 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_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"); 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; sdev->ud.status = SDEV_ST_USED;
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
} else { } else {
dev_info(dev, "stub down\n"); dev_info(dev, "stub down\n");
spin_lock(&sdev->ud.lock); spin_lock_irq(&sdev->ud.lock);
if (sdev->ud.status != SDEV_ST_USED) { if (sdev->ud.status != SDEV_ST_USED) {
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
return -EINVAL; return -EINVAL;
} }
spin_unlock(&sdev->ud.lock); spin_unlock_irq(&sdev->ud.lock);
usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN);
} }
...@@ -241,9 +241,9 @@ static void stub_device_reset(struct usbip_device *ud) ...@@ -241,9 +241,9 @@ static void stub_device_reset(struct usbip_device *ud)
ret = usb_lock_device_for_reset(udev, sdev->interface); ret = usb_lock_device_for_reset(udev, sdev->interface);
if (ret < 0) { if (ret < 0) {
dev_err(&udev->dev, "lock for reset\n"); dev_err(&udev->dev, "lock for reset\n");
spin_lock(&ud->lock); spin_lock_irq(&ud->lock);
ud->status = SDEV_ST_ERROR; ud->status = SDEV_ST_ERROR;
spin_unlock(&ud->lock); spin_unlock_irq(&ud->lock);
return; return;
} }
...@@ -251,7 +251,7 @@ static void stub_device_reset(struct usbip_device *ud) ...@@ -251,7 +251,7 @@ static void stub_device_reset(struct usbip_device *ud)
ret = usb_reset_device(udev); ret = usb_reset_device(udev);
usb_unlock_device(udev); usb_unlock_device(udev);
spin_lock(&ud->lock); spin_lock_irq(&ud->lock);
if (ret) { if (ret) {
dev_err(&udev->dev, "device reset\n"); dev_err(&udev->dev, "device reset\n");
ud->status = SDEV_ST_ERROR; ud->status = SDEV_ST_ERROR;
...@@ -259,14 +259,14 @@ static void stub_device_reset(struct usbip_device *ud) ...@@ -259,14 +259,14 @@ static void stub_device_reset(struct usbip_device *ud)
dev_info(&udev->dev, "device reset\n"); dev_info(&udev->dev, "device reset\n");
ud->status = SDEV_ST_AVAILABLE; ud->status = SDEV_ST_AVAILABLE;
} }
spin_unlock(&ud->lock); spin_unlock_irq(&ud->lock);
} }
static void stub_device_unusable(struct usbip_device *ud) static void stub_device_unusable(struct usbip_device *ud)
{ {
spin_lock(&ud->lock); spin_lock_irq(&ud->lock);
ud->status = SDEV_ST_ERROR; ud->status = SDEV_ST_ERROR;
spin_unlock(&ud->lock); spin_unlock_irq(&ud->lock);
} }
/** /**
......
...@@ -307,12 +307,12 @@ static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) ...@@ -307,12 +307,12 @@ static int valid_request(struct stub_device *sdev, struct usbip_header *pdu)
int valid = 0; int valid = 0;
if (pdu->base.devid == sdev->devid) { if (pdu->base.devid == sdev->devid) {
spin_lock(&ud->lock); spin_lock_irq(&ud->lock);
if (ud->status == SDEV_ST_USED) { if (ud->status == SDEV_ST_USED) {
/* A request is valid. */ /* A request is valid. */
valid = 1; valid = 1;
} }
spin_unlock(&ud->lock); spin_unlock_irq(&ud->lock);
} }
return valid; return valid;
......
...@@ -105,10 +105,12 @@ EXPORT_SYMBOL_GPL(usbip_stop_eh); ...@@ -105,10 +105,12 @@ EXPORT_SYMBOL_GPL(usbip_stop_eh);
void usbip_event_add(struct usbip_device *ud, unsigned long event) 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; ud->event |= event;
wake_up(&ud->eh_waitq); wake_up(&ud->eh_waitq);
spin_unlock(&ud->lock); spin_unlock_irqrestore(&ud->lock, flags);
} }
EXPORT_SYMBOL_GPL(usbip_event_add); EXPORT_SYMBOL_GPL(usbip_event_add);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment