summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDu Xing <duxing2007@gmail.com>2013-03-20 13:47:46 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-25 21:32:20 +0100
commitc79041a44045a40329d9ada3f8679c4b30c5b76b (patch)
treeade4c7d97b3569aa04ec5f48eaa4a22a99583291 /drivers/usb
parentUSB: cdc-wdm: implement IOCTL_WDM_MAX_COMMAND (diff)
downloadlinux-c79041a44045a40329d9ada3f8679c4b30c5b76b.tar.xz
linux-c79041a44045a40329d9ada3f8679c4b30c5b76b.zip
USB: usb-skeleton.c: fix blocked forever in skel_read
In skel_read,the reader blocked in wait_for_completion before submit bulk in urb. Using processed_urb is for retaining the completion in the case that previous interruptible wait in skel_read was interrupted and complete before next skel_read. Replacing completion with waitqueue can avoid working around the counting nature of completions and fix the bug. Signed-off-by: Du Xing duxing2007@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/usb-skeleton.c32
1 files changed, 8 insertions, 24 deletions
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index ce310170829f..7ed3b039dbe8 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -61,11 +61,10 @@ struct usb_skel {
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
int errors; /* the last request tanked */
bool ongoing_read; /* a read is going on */
- bool processed_urb; /* indicates we haven't processed the urb */
spinlock_t err_lock; /* lock for errors */
struct kref kref;
struct mutex io_mutex; /* synchronize I/O with disconnect */
- struct completion bulk_in_completion; /* to wait for an ongoing read */
+ wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
@@ -185,7 +184,7 @@ static void skel_read_bulk_callback(struct urb *urb)
dev->ongoing_read = 0;
spin_unlock(&dev->err_lock);
- complete(&dev->bulk_in_completion);
+ wake_up_interruptible(&dev->bulk_in_wait);
}
static int skel_do_read_io(struct usb_skel *dev, size_t count)
@@ -206,13 +205,16 @@ static int skel_do_read_io(struct usb_skel *dev, size_t count)
dev->ongoing_read = 1;
spin_unlock_irq(&dev->err_lock);
+ /* submit bulk in urb, which means no data to deliver */
+ dev->bulk_in_filled = 0;
+ dev->bulk_in_copied = 0;
+
/* do it */
rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
if (rv < 0) {
dev_err(&dev->interface->dev,
"%s - failed submitting read urb, error %d\n",
__func__, rv);
- dev->bulk_in_filled = 0;
rv = (rv == -ENOMEM) ? rv : -EIO;
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 0;
@@ -261,25 +263,9 @@ retry:
* IO may take forever
* hence wait in an interruptible state
*/
- rv = wait_for_completion_interruptible(&dev->bulk_in_completion);
+ rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
if (rv < 0)
goto exit;
- /*
- * by waiting we also semiprocessed the urb
- * we must finish now
- */
- dev->bulk_in_copied = 0;
- dev->processed_urb = 1;
- }
-
- if (!dev->processed_urb) {
- /*
- * the URB hasn't been processed
- * do it now
- */
- wait_for_completion(&dev->bulk_in_completion);
- dev->bulk_in_copied = 0;
- dev->processed_urb = 1;
}
/* errors must be reported */
@@ -289,8 +275,6 @@ retry:
dev->errors = 0;
/* to preserve notifications about reset */
rv = (rv == -EPIPE) ? rv : -EIO;
- /* no data to deliver */
- dev->bulk_in_filled = 0;
/* report it */
goto exit;
}
@@ -526,7 +510,7 @@ static int skel_probe(struct usb_interface *interface,
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
- init_completion(&dev->bulk_in_completion);
+ init_waitqueue_head(&dev->bulk_in_wait);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;