Commit fdd64df7 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: usbfs: Always unlink URBs in reverse order

When the kernel unlinks a bunch of URBs for a single endpoint, it
should always unlink them in reverse order.  This eliminates any
possibility that some URB x will be unlinked before it can execute but
the following URB x+1 will execute before it can be unlinked.  Such an
event would be bad, for obvious reasons.

Chris Dickens pointed out that usbfs doesn't behave this way when it
is unbound from an interface.  All pending URBs are cancelled, but in
the order of submission.  This patch changes the behavior to make the
unlinks occur in reverse order.  It similarly changes the behavior
when usbfs cancels the continuation URBs for a BULK endpoint.
Suggested-by: default avatarChris Dickens <christopher.a.dickens@gmail.com>
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>

Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.2001171045380.1571-100000@iolanthe.rowland.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 50f737ab
...@@ -574,7 +574,7 @@ __acquires(ps->lock) ...@@ -574,7 +574,7 @@ __acquires(ps->lock)
/* Now carefully unlink all the marked pending URBs */ /* Now carefully unlink all the marked pending URBs */
rescan: rescan:
list_for_each_entry(as, &ps->async_pending, asynclist) { list_for_each_entry_reverse(as, &ps->async_pending, asynclist) {
if (as->bulk_status == AS_UNLINK) { if (as->bulk_status == AS_UNLINK) {
as->bulk_status = 0; /* Only once */ as->bulk_status = 0; /* Only once */
urb = as->urb; urb = as->urb;
...@@ -636,7 +636,7 @@ static void destroy_async(struct usb_dev_state *ps, struct list_head *list) ...@@ -636,7 +636,7 @@ static void destroy_async(struct usb_dev_state *ps, struct list_head *list)
spin_lock_irqsave(&ps->lock, flags); spin_lock_irqsave(&ps->lock, flags);
while (!list_empty(list)) { while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist); as = list_last_entry(list, struct async, asynclist);
list_del_init(&as->asynclist); list_del_init(&as->asynclist);
urb = as->urb; urb = as->urb;
usb_get_urb(urb); usb_get_urb(urb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment