On Wed, May 19, 2004 at 11:02:52PM +1000, herbert wrote: > On Thu, May 13, 2004 at 10:26:25PM -0400, Alan Stern wrote: > > > > Nothing guarantees it! That's one of the other things that needs to be > > fixed. The way I eventually want it to work is that there will be a list > > of urbp's that have been removed from the schedule and are waiting for the > > hardware to let them go (like we have now) together with a record of the > > frame number at the time they were removed. There will also be another > > OK, how about this patch?
Let's fix QHs/TDs too. Plus I forgot to set the age when the current pending list is removed... -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmV>HI~} <[EMAIL PROTECTED]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
===== drivers/usb/host/uhci-hcd.c 1.77 vs edited ===== --- 1.77/drivers/usb/host/uhci-hcd.c Wed Apr 14 20:19:54 2004 +++ edited/drivers/usb/host/uhci-hcd.c Wed May 19 23:41:50 2004 @@ -95,6 +95,10 @@ static int uhci_get_current_frame_number(struct uhci_hcd *uhci); static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); +static void uhci_remove_pending_urbps(struct uhci_hcd *uhci); +static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs); +static void uhci_free_pending_qhs(struct uhci_hcd *uhci); +static void uhci_free_pending_tds(struct uhci_hcd *uhci); static void hc_state_transitions(struct uhci_hcd *uhci); @@ -373,6 +377,7 @@ { struct uhci_qh *pqh; u32 newlink; + unsigned int age; if (!qh) return; @@ -425,6 +430,12 @@ list_del_init(&qh->urbp->queue_list); qh->urbp = NULL; + age = uhci_get_current_frame_number(uhci); + if (age != uhci->qh_remove_age) { + uhci_free_pending_qhs(uhci); + uhci->qh_remove_age = age; + } + /* Check to see if the remove list is empty. Set the IOC bit */ /* to force an interrupt so we can remove the QH */ if (list_empty(&uhci->qh_remove_list)) @@ -628,6 +639,7 @@ { struct list_head *head, *tmp; struct urb_priv *urbp; + unsigned int age; urbp = (struct urb_priv *)urb->hcpriv; if (!urbp) @@ -637,6 +649,12 @@ dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " "or uhci->remove_list!\n", urb); + age = uhci_get_current_frame_number(uhci); + if (age != uhci->td_remove_age) { + uhci_free_pending_tds(uhci); + uhci->td_remove_age = age; + } + /* 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)) @@ -1512,6 +1530,7 @@ struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; struct urb_priv *urbp; + unsigned int age; spin_lock_irqsave(&uhci->schedule_lock, flags); urbp = urb->hcpriv; @@ -1521,6 +1540,12 @@ uhci_unlink_generic(uhci, urb); + age = uhci_get_current_frame_number(uhci); + if (age != uhci->urb_remove_age) { + uhci_remove_pending_urbps(uhci); + uhci->urb_remove_age = age; + } + /* If we're the first, set the next interrupt bit */ if (list_empty(&uhci->urb_remove_list)) uhci_set_next_interrupt(uhci); @@ -1590,6 +1615,12 @@ INIT_LIST_HEAD(&list); spin_lock_irqsave(&uhci->schedule_lock, flags); + if (!list_empty(&uhci->urb_remove_list) && + uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) { + uhci_remove_pending_urbps(uhci); + uhci_finish_completion(hcd, NULL); + } + head = &uhci->urb_list; tmp = head->next; while (tmp != head) { @@ -1728,6 +1759,7 @@ unsigned int io_addr = uhci->io_addr; unsigned short status; struct list_head *tmp, *head; + unsigned int age; /* * Read the interrupt status, and write it back to clear the @@ -1758,11 +1790,20 @@ spin_lock(&uhci->schedule_lock); - uhci_free_pending_qhs(uhci); - uhci_free_pending_tds(uhci); - uhci_remove_pending_urbps(uhci); - - uhci_clear_next_interrupt(uhci); + age = uhci_get_current_frame_number(uhci); + if (age != uhci->qh_remove_age) + uhci_free_pending_qhs(uhci); + if (age != uhci->td_remove_age) + uhci_free_pending_tds(uhci); + if (age != uhci->urb_remove_age) + uhci_remove_pending_urbps(uhci); + + if (list_empty(&uhci->urb_remove_list) && + list_empty(&uhci->td_remove_list) && + list_empty(&uhci->qh_remove_list)) + uhci_clear_next_interrupt(uhci); + else + uhci_set_next_interrupt(uhci); /* Walk the list of pending URB's to see which ones completed */ head = &uhci->urb_list; ===== drivers/usb/host/uhci-hcd.h 1.23 vs edited ===== --- 1.23/drivers/usb/host/uhci-hcd.h Mon Apr 19 21:05:56 2004 +++ edited/drivers/usb/host/uhci-hcd.h Wed May 19 23:25:26 2004 @@ -357,12 +357,15 @@ /* List of QH's that are done, but waiting to be unlinked (race) */ struct list_head qh_remove_list; /* P: uhci->schedule_lock */ + unsigned int qh_remove_age; /* Age in frames */ /* List of TD's that are done, but waiting to be freed (race) */ struct list_head td_remove_list; /* P: uhci->schedule_lock */ + unsigned int td_remove_age; /* Age in frames */ /* List of asynchronously unlinked URB's */ struct list_head urb_remove_list; /* P: uhci->schedule_lock */ + unsigned int urb_remove_age; /* Age in frames */ /* List of URB's awaiting completion callback */ struct list_head complete_list; /* P: uhci->schedule_lock */