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: Harvey Yang <harvey.huawei.yang@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
26ef1021c1
commit
dcf1477928
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue