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 \