summaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-wdm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r--drivers/usb/class/cdc-wdm.c53
1 files changed, 34 insertions, 19 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 23cf9d38eb5..f63601a2054 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -105,7 +105,8 @@ struct wdm_device {
int count;
dma_addr_t shandle;
dma_addr_t ihandle;
- struct mutex lock;
+ struct mutex wlock;
+ struct mutex rlock;
wait_queue_head_t wait;
struct work_struct rxwork;
int werr;
@@ -314,7 +315,7 @@ static ssize_t wdm_write
}
/* concurrent writes and disconnect */
- r = mutex_lock_interruptible(&desc->lock);
+ r = mutex_lock_interruptible(&desc->wlock);
rv = -ERESTARTSYS;
if (r) {
kfree(buf);
@@ -377,7 +378,7 @@ static ssize_t wdm_write
out:
usb_autopm_put_interface(desc->intf);
outnp:
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
outnl:
return rv < 0 ? rv : count;
}
@@ -390,7 +391,7 @@ static ssize_t wdm_read
struct wdm_device *desc = file->private_data;
- rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */
+ rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */
if (rv < 0)
return -ERESTARTSYS;
@@ -458,14 +459,16 @@ retry:
for (i = 0; i < desc->length - cntr; i++)
desc->ubuf[i] = desc->ubuf[i + cntr];
+ spin_lock_irq(&desc->iuspin);
desc->length -= cntr;
+ spin_unlock_irq(&desc->iuspin);
/* in case we had outstanding data */
if (!desc->length)
clear_bit(WDM_READ, &desc->flags);
rv = cntr;
err:
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->rlock);
return rv;
}
@@ -531,7 +534,8 @@ static int wdm_open(struct inode *inode, struct file *file)
}
intf->needs_remote_wakeup = 1;
- mutex_lock(&desc->lock);
+ /* using write lock to protect desc->count */
+ mutex_lock(&desc->wlock);
if (!desc->count++) {
desc->werr = 0;
desc->rerr = 0;
@@ -544,7 +548,7 @@ static int wdm_open(struct inode *inode, struct file *file)
} else {
rv = 0;
}
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
usb_autopm_put_interface(desc->intf);
out:
mutex_unlock(&wdm_mutex);
@@ -556,9 +560,11 @@ static int wdm_release(struct inode *inode, struct file *file)
struct wdm_device *desc = file->private_data;
mutex_lock(&wdm_mutex);
- mutex_lock(&desc->lock);
+
+ /* using write lock to protect desc->count */
+ mutex_lock(&desc->wlock);
desc->count--;
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
if (!desc->count) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
@@ -655,7 +661,8 @@ next_desc:
desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL);
if (!desc)
goto out;
- mutex_init(&desc->lock);
+ mutex_init(&desc->rlock);
+ mutex_init(&desc->wlock);
spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait);
desc->wMaxCommand = maxcom;
@@ -771,11 +778,13 @@ static void wdm_disconnect(struct usb_interface *intf)
/* to terminate pending flushes */
clear_bit(WDM_IN_USE, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
- mutex_lock(&desc->lock);
+ wake_up_all(&desc->wait);
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
- mutex_unlock(&desc->lock);
- wake_up_all(&desc->wait);
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
if (!desc->count)
cleanup(desc);
mutex_unlock(&wdm_mutex);
@@ -790,8 +799,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
/* if this is an autosuspend the caller does the locking */
- if (!PMSG_IS_AUTO(message))
- mutex_lock(&desc->lock);
+ if (!PMSG_IS_AUTO(message)) {
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
+ }
spin_lock_irq(&desc->iuspin);
if (PMSG_IS_AUTO(message) &&
@@ -807,8 +818,10 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
kill_urbs(desc);
cancel_work_sync(&desc->rxwork);
}
- if (!PMSG_IS_AUTO(message))
- mutex_unlock(&desc->lock);
+ if (!PMSG_IS_AUTO(message)) {
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
+ }
return rv;
}
@@ -846,7 +859,8 @@ static int wdm_pre_reset(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
- mutex_lock(&desc->lock);
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
kill_urbs(desc);
/*
@@ -868,7 +882,8 @@ static int wdm_post_reset(struct usb_interface *intf)
int rv;
rv = recover_from_urb_loss(desc);
- mutex_unlock(&desc->lock);
+ mutex_unlock(&desc->wlock);
+ mutex_unlock(&desc->rlock);
return 0;
}