]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
USB: usbcore: Introduce usb_bulk_msg_killable()
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 18 Feb 2026 03:07:47 +0000 (22:07 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 11 Mar 2026 15:16:56 +0000 (16:16 +0100)
The synchronous message API in usbcore (usb_control_msg(),
usb_bulk_msg(), and so on) uses uninterruptible waits.  However,
drivers may call these routines in the context of a user thread, which
means it ought to be possible to at least kill them.

For this reason, introduce a new usb_bulk_msg_killable() function
which behaves the same as usb_bulk_msg() except for using
wait_for_completion_killable_timeout() instead of
wait_for_completion_timeout().  The same can be done later for
usb_control_msg() later on, if it turns out to be needed.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Suggested-by: Oliver Neukum <oneukum@suse.com>
Link: https://lore.kernel.org/linux-usb/3acfe838-6334-4f6d-be7c-4bb01704b33d@rowland.harvard.edu/
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
CC: stable@vger.kernel.org
Link: https://patch.msgid.link/248628b4-cc83-4e81-a620-3ce4e0376d41@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/message.c
include/linux/usb.h

index ea970ddf8879fa5b372410e910e49205dc124254..d97ec7e8c280b2879b1b6d5f138bdb49cf6a34a9 100644 (file)
@@ -42,16 +42,17 @@ static void usb_api_blocking_completion(struct urb *urb)
 
 
 /*
- * Starts urb and waits for completion or timeout. Note that this call
- * is NOT interruptible. Many device driver i/o requests should be
- * interruptible and therefore these drivers should implement their
- * own interruptible routines.
+ * Starts urb and waits for completion or timeout.
+ * Whether or not the wait is killable depends on the flag passed in.
+ * For example, compare usb_bulk_msg() and usb_bulk_msg_killable().
  */
-static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length,
+               bool killable)
 {
        struct api_context ctx;
        unsigned long expire;
        int retval;
+       long rc;
 
        init_completion(&ctx.done);
        urb->context = &ctx;
@@ -61,12 +62,21 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
                goto out;
 
        expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
-       if (!wait_for_completion_timeout(&ctx.done, expire)) {
+       if (killable)
+               rc = wait_for_completion_killable_timeout(&ctx.done, expire);
+       else
+               rc = wait_for_completion_timeout(&ctx.done, expire);
+       if (rc <= 0) {
                usb_kill_urb(urb);
-               retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
+               if (ctx.status != -ENOENT)
+                       retval = ctx.status;
+               else if (rc == 0)
+                       retval = -ETIMEDOUT;
+               else
+                       retval = rc;
 
                dev_dbg(&urb->dev->dev,
-                       "%s timed out on ep%d%s len=%u/%u\n",
+                       "%s timed out or killed on ep%d%s len=%u/%u\n",
                        current->comm,
                        usb_endpoint_num(&urb->ep->desc),
                        usb_urb_dir_in(urb) ? "in" : "out",
@@ -100,7 +110,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
        usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
                             len, usb_api_blocking_completion, NULL);
 
-       retv = usb_start_wait_urb(urb, timeout, &length);
+       retv = usb_start_wait_urb(urb, timeout, &length, false);
        if (retv < 0)
                return retv;
        else
@@ -385,10 +395,59 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
                usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                                usb_api_blocking_completion, NULL);
 
-       return usb_start_wait_urb(urb, timeout, actual_length);
+       return usb_start_wait_urb(urb, timeout, actual_length, false);
 }
 EXPORT_SYMBOL_GPL(usb_bulk_msg);
 
+/**
+ * usb_bulk_msg_killable - Builds a bulk urb, sends it off and waits for completion in a killable state
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred
+ *     in bytes
+ * @timeout: time in msecs to wait for the message to complete before
+ *     timing out (if 0 the wait is forever)
+ *
+ * Context: task context, might sleep.
+ *
+ * This function is just like usb_blk_msg() except that it waits in a
+ * killable state.
+ *
+ * Return:
+ * If successful, 0. Otherwise a negative error number. The number of actual
+ * bytes transferred will be stored in the @actual_length parameter.
+ *
+ */
+int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
+                void *data, int len, int *actual_length, int timeout)
+{
+       struct urb *urb;
+       struct usb_host_endpoint *ep;
+
+       ep = usb_pipe_endpoint(usb_dev, pipe);
+       if (!ep || len < 0)
+               return -EINVAL;
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!urb)
+               return -ENOMEM;
+
+       if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                       USB_ENDPOINT_XFER_INT) {
+               pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
+               usb_fill_int_urb(urb, usb_dev, pipe, data, len,
+                               usb_api_blocking_completion, NULL,
+                               ep->desc.bInterval);
+       } else
+               usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
+                               usb_api_blocking_completion, NULL);
+
+       return usb_start_wait_urb(urb, timeout, actual_length, true);
+}
+EXPORT_SYMBOL_GPL(usb_bulk_msg_killable);
+
 /*-------------------------------------------------------------------*/
 
 static void sg_clean(struct usb_sg_request *io)
index fbfcc70b07fbe5dbaa1ca1be55af127c62294cf7..57ceeb02a7cbfea3df6f1624aa66bb9786535a7d 100644 (file)
@@ -1868,8 +1868,9 @@ extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
 extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
        void *data, int len, int *actual_length, int timeout);
 extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
-       void *data, int len, int *actual_length,
-       int timeout);
+       void *data, int len, int *actual_length, int timeout);
+extern int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
+       void *data, int len, int *actual_length, int timeout);
 
 /* wrappers around usb_control_msg() for the most common standard requests */
 int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,