This patch does two main things:

 - add a new <linux/completion.h> primitive, wait_timeout(),
   that's like wait_for_completion() but it takes a timeout

 - uses it to replace some dubious code at the core of the
   usb synchronous messaging primitives.

The wait_timeout() code replaces most of wait_for_completion(),
avoiding some code duplication at the cost of a few instructions.
That'd be easily tweaked, if there's evidence it matters.

Please merge, and let me know if that kernel/sched.c change
(or exporting that declared-but-unexported routine) is trouble.

- Dave
--- ./drivers/usb-dist/core/message.c   Sun Dec  8 10:57:46 2002
+++ ./drivers/usb/core/message.c        Mon Dec  9 16:02:56 2002
@@ -11,72 +11,41 @@
 
 #include "hcd.h"       /* for usbcore internals */
 
-struct usb_api_data {
-       wait_queue_head_t wqh;
-       int done;
-};
 
 static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
 {
-       struct usb_api_data *awd = (struct usb_api_data *)urb->context;
-
-       awd->done = 1;
-       wmb();
-       wake_up(&awd->wqh);
+       complete ((struct completion *) urb->context);
 }
 
 // Starts urb and waits for completion or timeout
 static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
-{ 
-       DECLARE_WAITQUEUE(wait, current);
-       struct usb_api_data awd;
-       int status;
-
-       init_waitqueue_head(&awd.wqh);  
-       awd.done = 0;
+{
+       struct completion       complete;
+       int                     status;
 
-       set_current_state(TASK_UNINTERRUPTIBLE);
-       add_wait_queue(&awd.wqh, &wait);
+       init_completion (&complete);
+       urb->context = &complete;
+       urb->complete = usb_api_blocking_completion;
+       urb->transfer_flags |= URB_ASYNC_UNLINK;
 
-       urb->context = &awd;
-       status = usb_submit_urb(urb, GFP_ATOMIC);
-       if (status) {
-               // something went wrong
-               usb_free_urb(urb);
-               set_current_state(TASK_RUNNING);
-               remove_wait_queue(&awd.wqh, &wait);
+       // NOTE:  watchdogs that unlink would race this submit ...
+       // else this routine could usefully be exported.
+       status = usb_submit_urb (urb, SLAB_KERNEL);
+       if (status < 0)
                return status;
-       }
 
-       while (timeout && !awd.done)
-       {
-               timeout = schedule_timeout(timeout);
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               rmb();
+       if (wait_timeout (&complete, timeout)) {
+               dev_warn (urb->dev->dev, "timeout urb %p dev%d ep%d-%s\n",
+                       urb, usb_pipedevice (urb->pipe),
+                       usb_pipeendpoint (urb->pipe),
+                       usb_pipein (urb->pipe) ? "in" : "out");
+               usb_unlink_urb (urb);
+               wait_for_completion (&complete);
+               urb->status = -ETIMEDOUT;
        }
-
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&awd.wqh, &wait);
-
-       if (!timeout && !awd.done) {
-               if (urb->status != -EINPROGRESS) {      /* No callback?!! */
-                       printk(KERN_ERR "usb: raced timeout, "
-                           "pipe 0x%x status %d time left %d\n",
-                           urb->pipe, urb->status, timeout);
-                       status = urb->status;
-               } else {
-                       warn("usb_control/bulk_msg: timeout");
-                       usb_unlink_urb(urb);  // remove urb safely
-                       status = -ETIMEDOUT;
-               }
-       } else
-               status = urb->status;
-
        if (actual_length)
                *actual_length = urb->actual_length;
-
-       usb_free_urb(urb);
-       return status;
+       return urb->status;
 }
 
 /*-------------------------------------------------------------------*/
--- ./include/linux-dist/completion.h   Sun Dec  8 10:57:47 2002
+++ ./include/linux/completion.h        Mon Dec  9 15:11:51 2002
@@ -28,6 +28,7 @@
 }
 
 extern void FASTCALL(wait_for_completion(struct completion *));
+extern int FASTCALL(wait_timeout(struct completion *, signed long jiffies));
 extern void FASTCALL(complete(struct completion *));
 extern void FASTCALL(complete_all(struct completion *));
 
--- ./kernel-dist/ksyms.c       Sun Dec  8 10:57:48 2002
+++ ./kernel/ksyms.c    Mon Dec  9 15:13:47 2002
@@ -404,7 +404,9 @@ EXPORT_SYMBOL(autoremove_wake_function);
 
 /* completion handling */
 EXPORT_SYMBOL(wait_for_completion);
+EXPORT_SYMBOL(wait_timeout);
 EXPORT_SYMBOL(complete);
+EXPORT_SYMBOL(complete_all);
 
 /* The notion of irq probe/assignment is foreign to S/390 */
 
--- ./kernel-dist/sched.c       Sun Dec  8 10:57:48 2002
+++ ./kernel/sched.c    Mon Dec  9 15:34:36 2002
@@ -1204,6 +1204,11 @@ void complete_all(struct completion *x)
 
 void wait_for_completion(struct completion *x)
 {
+       wait_timeout (x, MAX_SCHEDULE_TIMEOUT);
+}
+
+int wait_timeout(struct completion *x, signed long timeout_jiffies)
+{
        might_sleep();
        spin_lock_irq(&x->wait.lock);
        if (!x->done) {
@@ -1214,13 +1219,19 @@ void wait_for_completion(struct completi
                do {
                        __set_current_state(TASK_UNINTERRUPTIBLE);
                        spin_unlock_irq(&x->wait.lock);
-                       schedule();
+                       timeout_jiffies = schedule_timeout(timeout_jiffies);
+                       if (timeout_jiffies == 0) {
+                               __remove_wait_queue(&x->wait, &wait);
+                               /* caller should wait again */
+                               return 1;
+                       }
                        spin_lock_irq(&x->wait.lock);
                } while (!x->done);
                __remove_wait_queue(&x->wait, &wait);
        }
        x->done--;
        spin_unlock_irq(&x->wait.lock);
+       return 0;
 }
 
 #define        SLEEP_ON_VAR                            \

Reply via email to