td->dev = dev;
INIT_LIST_HEAD(&td->list);
+ INIT_LIST_HEAD(&td->remove_list);
INIT_LIST_HEAD(&td->fl_list);
usb_get_dev(dev);
{
if (!list_empty(&td->list))
dbg("td %p is still in list!", td);
+ if (!list_empty(&td->remove_list))
+ dbg("td %p still in remove_list!", td);
if (!list_empty(&td->fl_list))
dbg("td %p is still in fl_list!", td);
{
struct list_head *head, *tmp;
struct urb_priv *urbp;
+ unsigned long flags;
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
if (!list_empty(&urbp->complete_list))
warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb);
+ spin_lock_irqsave(&uhci->td_remove_list_lock, flags);
+
+ /* Check to see if the remove list is empty. Set the IOC bit */
+ /* to force an interrupt so we can remove the TD's*/
+ if (list_empty(&uhci->td_remove_list))
+ uhci_set_next_interrupt(uhci);
+
head = &urbp->td_list;
tmp = head->next;
while (tmp != head) {
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
- uhci_free_td(uhci, td);
+ list_add(&td->remove_list, &uhci->td_remove_list);
}
+ spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags);
+
urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
}
+static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+{
+ struct list_head *tmp, *head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->td_remove_list_lock, flags);
+ head = &uhci->td_remove_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list);
+
+ tmp = tmp->next;
+
+ list_del_init(&td->remove_list);
+
+ uhci_free_td(uhci, td);
+ }
+ spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags);
+}
+
static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
uhci_free_pending_qhs(uhci);
+ uhci_free_pending_tds(uhci);
+
uhci_remove_pending_qhs(uhci);
uhci_clear_next_interrupt(uhci);
spin_lock_init(&uhci->qh_remove_list_lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
+ spin_lock_init(&uhci->td_remove_list_lock);
+ INIT_LIST_HEAD(&uhci->td_remove_list);
+
spin_lock_init(&uhci->urb_remove_list_lock);
INIT_LIST_HEAD(&uhci->urb_remove_list);
* to this bus since there are no more parents
*/
uhci_free_pending_qhs(uhci);
+ uhci_free_pending_tds(uhci);
uhci_remove_pending_qhs(uhci);
reset_hc(uhci);
uhci_free_pending_qhs(uhci);
+ uhci_free_pending_tds(uhci);
release_uhci(uhci);
}
struct urb *urb;
struct list_head list; /* P: urb->lock */
+ struct list_head remove_list; /* P: uhci->td_remove_list_lock */
int frame; /* for iso: what frame? */
struct list_head fl_list; /* P: uhci->frame_list_lock */
spinlock_t qh_remove_list_lock;
struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */
+ /* List of TD's that are done, but waiting to be freed (race) */
+ spinlock_t td_remove_list_lock;
+ struct list_head td_remove_list; /* P: uhci->td_remove_list_lock */
+
/* List of asynchronously unlinked URB's */
spinlock_t urb_remove_list_lock;
struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */