On Wed, 2 May 2001, Oliver Neukum wrote:

> as was recently discussed, sleep_on and derivates, which have a little race 
> problem are used 21 times in drivers/usb

and 12 more in drivers/usb/serial ;-)

> The attached patch should fix one such place in printer.

Don't want to be pedantic, but there are some more issues:

- current state should be set to TASK_INTERRUPTIBLE for each loop
  traversing. schedule() always returns RUNNABLE, so we would only
  sleep once.
- memory barriers required to make sure we are really on the wait_queue
  before testing whether to sleep - otherwise there would be a race in
  case the interrupt/wakeup is executed on other CPU
- better use set_current_state() - it provides the memory barrier when
  placed between add_wait_queue() and sleep/nosleep decision
- sleep/nosleep decision must be atomic. If this is enforced by a spinlock
  we may use __set_current_state() (which does not serialize).
- not only lpusb_read() uses sleep_on(), similar for lpusb_write() but
  with timeout

the updated patch below (vs. 2.4.4, -ac should be ok) addresses these 
issues.

> It even compiles.

and nothing appears to be broken - at least for my HL1250 (2 EP).

> 20 more to go. People let's kill them all.

Good idea anyway!

Martin

----------------------

--- linux-2.4.4/drivers/usb.orig/printer.c      Sat Apr 14 13:44:41 2001
+++ linux-2.4.4/drivers/usb/printer.c   Wed May  2 22:36:41 2001
@@ -331,22 +331,42 @@
 {
        struct usblp *usblp = file->private_data;
        int timeout, err = 0, writecount = 0;
+       unsigned long flags;
+       int status;
+       DECLARE_WAITQUEUE(wait,current);
 
        while (writecount < count) {
 
+               /* FIXME: explicitly atomic required? */
                if (usblp->writeurb.status == -EINPROGRESS) {
 
                        if (file->f_flags & O_NONBLOCK)
                                return -EAGAIN;
 
+                       add_wait_queue(&usblp->wait, &wait);
+
                        timeout = USBLP_WRITE_TIMEOUT;
-                       while (timeout && usblp->writeurb.status == -EINPROGRESS) {
+                       while (timeout) {
 
-                               if (signal_pending(current))
-                                       return writecount ? writecount : -EINTR;
+                               /* if wakeup arrives here we won't sleep! */
+                               __set_current_state(TASK_INTERRUPTIBLE);
 
-                               timeout = interruptible_sleep_on_timeout(&usblp->wait, 
timeout);
+                               /* spinlock for atomic access and serialization */
+                               spin_lock_irqsave(&usblp->writeurb.lock, flags);
+                               status = usblp->writeurb.status;
+                               spin_unlock_irqrestore(&usblp->writeurb.lock, flags);
+
+                               if (status != -EINPROGRESS)
+                                       break;
+                               if (signal_pending(current)) {
+                                       remove_wait_queue(&usblp->wait, &wait);
+                                       set_current_state(TASK_RUNNING);
+                                       return writecount ? writecount : -EINTR;
+                               }
+                               timeout = schedule_timeout(timeout);
                        }
+                       remove_wait_queue(&usblp->wait, &wait);
+                       set_current_state(TASK_RUNNING); /* provides barrier */
                }
 
                if (!usblp->dev)
@@ -388,6 +408,9 @@
 static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
        struct usblp *usblp = file->private_data;
+       unsigned long flags;
+       int status;
+       DECLARE_WAITQUEUE(wait,current);
 
        if (!usblp->bidir)
                return -EINVAL;
@@ -397,11 +420,23 @@
                if (file->f_flags & O_NONBLOCK)
                        return -EAGAIN;
 
-               while (usblp->readurb.status == -EINPROGRESS) {
-                       if (signal_pending(current))
+               add_wait_queue(&usblp->wait, &wait);
+               for (;;) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       spin_lock_irqsave(&usblp->readurb.lock, flags);
+                       status = usblp->readurb.status;
+                       spin_unlock_irqrestore(&usblp->readurb.lock, flags);
+                       if (status != -EINPROGRESS)
+                               break;
+                       if (signal_pending(current)) {
+                               remove_wait_queue(&usblp->wait, &wait);
+                               set_current_state(TASK_RUNNING);
                                return -EINTR;
-                       interruptible_sleep_on(&usblp->wait);
+                       }
+                       schedule();
                }
+               remove_wait_queue(&usblp->wait, &wait);
+               __set_current_state(TASK_RUNNING);
        }
 
        if (!usblp->dev)


_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
http://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to