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 */