ChangeSet 1.785, 2002/12/05 14:20:47-08:00, baldrick@wanadoo.fr [PATCH] usbdevfs: finalize urbs on interface release Description: When an urb has been submitted via usbdevfs, and is still pending when the interface it was submitted to is released, force the urb to be completed. This is the correct behaviour. It fixes an oops on system shutdown when using the user space driver for the speedtouch modem. diff -Nru a/drivers/usb/devio.c b/drivers/usb/devio.c --- a/drivers/usb/devio.c Thu Dec 5 14:48:37 2002 +++ b/drivers/usb/devio.c Thu Dec 5 14:48:37 2002 @@ -50,6 +50,7 @@ struct dev_state *ps; struct task_struct *task; unsigned int signr; + unsigned int intf; void *userbuffer; void *userurb; struct urb urb; @@ -265,24 +266,50 @@ } } -static void destroy_all_async(struct dev_state *ps) +static void destroy_async (struct dev_state *ps, struct list_head *list) { - struct async *as; - unsigned long flags; + struct async *as; + unsigned long flags; - spin_lock_irqsave(&ps->lock, flags); - while (!list_empty(&ps->async_pending)) { - as = list_entry(ps->async_pending.next, struct async, asynclist); - list_del(&as->asynclist); - INIT_LIST_HEAD(&as->asynclist); - spin_unlock_irqrestore(&ps->lock, flags); - /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */ - usb_unlink_urb(&as->urb); - spin_lock_irqsave(&ps->lock, flags); - } - spin_unlock_irqrestore(&ps->lock, flags); - while ((as = async_getcompleted(ps))) - free_async(as); + spin_lock_irqsave(&ps->lock, flags); + while (!list_empty(list)) { + as = list_entry(list->next, struct async, asynclist); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */ + usb_unlink_urb(&as->urb); + spin_lock_irqsave(&ps->lock, flags); + } + spin_unlock_irqrestore(&ps->lock, flags); + while ((as = async_getcompleted(ps))) + free_async(as); +} + +static void destroy_async_on_interface (struct dev_state *ps, unsigned int intf) +{ + struct async *as; + struct list_head *p, hitlist; + unsigned long flags; + + INIT_LIST_HEAD(&hitlist); + spin_lock_irqsave(&ps->lock, flags); + for (p = ps->async_pending.next; p != &ps->async_pending; ) { + as = list_entry(p, struct async, asynclist); + p = p->next; + + if (as->intf == intf) { + list_del(&as->asynclist); + list_add_tail(&as->asynclist, &hitlist); + } + } + spin_unlock_irqrestore(&ps->lock, flags); + destroy_async(ps, &hitlist); +} + +static inline void destroy_all_async(struct dev_state *ps) +{ + destroy_async(ps, &ps->async_pending); } /* @@ -775,6 +802,7 @@ struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; int ret; + int intf = -1; if (copy_from_user(&uurb, arg, sizeof(uurb))) return -EFAULT; @@ -786,9 +814,9 @@ if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX)) return -EINVAL; if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { - if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0) - return ret; - if ((ret = checkintf(ps, ret))) + if ((intf = findintfep(ps->dev, uurb.endpoint)) < 0) + return intf; + if ((ret = checkintf(ps, intf))) return ret; } switch(uurb.type) { @@ -909,6 +937,7 @@ else as->userbuffer = NULL; as->signr = uurb.signr; + as->intf = intf; as->task = current; if (!(uurb.endpoint & USB_DIR_IN)) { if (copy_from_user(as->urb.transfer_buffer, uurb.buffer, as->urb.transfer_buffer_length)) { @@ -1054,7 +1083,10 @@ return -EFAULT; if ((ret = findintfif(ps->dev, intf)) < 0) return ret; - return releaseintf(ps, intf); + if ((ret = releaseintf(ps, intf)) < 0) + return ret; + destroy_async_on_interface (ps, intf); + return 0; } static int proc_ioctl (struct dev_state *ps, void *arg)