Hi Alan, thanks for the patch. Yes, it works. Comments below.
On Thursday 24 July 2003 20:58, Alan Stern wrote:
> On Fri, 18 Jul 2003, Duncan Sands wrote:
> > Hi Alan, Johannes, how about this? I tried to make as few changes to the
> > existing code as possible. In my opinion it would make more sense to
> > have the list of TDs belong to the QH and not the urb_priv, and free them
> > when the QH is freed, but that would mean making many more and more
> > invasive changes. By the way, does the last
> > + uhci_free_pending_tds(uhci);
> > make sense? If so, should there be a
> > + uhci_remove_pending_qhs(uhci);
> > as well?
>
> Duncan:
>
> I looked at your suggested patch; it seems like it ought to work.
:)
> I'm trying a different approach. It's a little more complicated, but it
> doesn't need an extra list header added to each TD. The idea is not to
> free the urbp as soon as the URB is given back, but to wait until
> uhci_free_pending_qhs() has released the QH as well.
My patch also doesn't need to add a list header to each TD: you can just
reuse the list entry. I added the new list head to keep things symmetric
with QHs.
I guess you preferred to keep the TDs as part of the urbp (rather than
making them part of the QH) because isochronous transfers have no QH?
Fair enough. However, wouldn't a cleaner approach be to do the same for
QHs as you do for TDs? I mean: just have a list of urbp's to be removed
(rather than a list of QHs), and when the urbp goes down destroy the TDs
and any QH? That way you can remove a list_head (remove_list) from the
QH as well :)
Comments on the patch are below.
> Here's the patch. Please try it and let me know if it fixes that
> Controller Halted problem. (You will probably have to use a little fuzz
> when applying it, because I wrote it on top of a couple of other patches.)
>
> Alan Stern
>
>
> ===== uhci-hcd.c 1.55 vs edited =====
> --- 1.55/drivers/usb/host/uhci-hcd.c Tue Jul 22 11:19:19 2003
> +++ edited/drivers/usb/host/uhci-hcd.c Wed Jul 23 16:45:35 2003
> @@ -403,7 +403,6 @@
>
> /*
> * Start removal of QH from schedule; it finishes next frame.
> - * TDs should be unlinked before this is called.
> */
> static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
> {
> @@ -413,8 +412,6 @@
> if (!qh)
> return;
>
> - qh->urbp = NULL;
> -
> /*
> * Only go through the hoops if it's actually linked in
> * Queued QHs are removed in uhci_delete_queued_urb,
> @@ -666,6 +663,7 @@
> list_add_tail(&urbp->urb_list, &uhci->urb_list);
>
> urb->hcpriv = urbp;
> + atomic_set(&urbp->refcount, 1);
>
> return urbp;
> }
> @@ -696,22 +694,20 @@
> }
>
> /*
> - * MUST be called with urb->lock acquired
> + * MUST be called with urbp->urb == NULL
> */
> -static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
> +static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb_priv
> *urbp) {
> struct list_head *head, *tmp;
> - struct urb_priv *urbp;
>
> - urbp = (struct urb_priv *)urb->hcpriv;
> if (!urbp)
> return;
>
> if (!list_empty(&urbp->urb_list))
> - warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or
> uhci->remove_list", urb); + warn("uhci_destroy_urb_priv: urbp %p still on
> uhci->urb_list or uhci->remove_list", urbp);
>
> if (!list_empty(&urbp->complete_list))
> - warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list",
> urb);
> + warn("uhci_destroy_urb_priv: urbp %p still on uhci->complete_list",
> urbp);
>
> head = &urbp->td_list;
> tmp = head->next;
> @@ -725,7 +721,6 @@
> uhci_free_td(uhci, td);
> }
>
> - urb->hcpriv = NULL;
> kmem_cache_free(uhci_up_cachep, urbp);
> }
>
> @@ -891,6 +886,7 @@
>
> urbp->qh = qh;
> qh->urbp = urbp;
> + atomic_inc(&urbp->refcount);
Why not define uhci_get_urbp and uhci_put_urbp methods?
Especially uhci_put_urbp, which would call uhci_destroy_urb_priv
when the urbp goes down. At the moment you do this by hand
each time.
> uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
>
> @@ -926,6 +922,7 @@
> urbp->short_control_packet = 1;
>
> /* Create a new QH to avoid pointer overwriting problems */
> + urbp->qh->urbp = NULL;
> uhci_remove_qh(uhci, urbp->qh);
>
> /* Delete all of the TD's except for the status TD at the end */
> @@ -943,6 +940,7 @@
>
> urbp->qh = uhci_alloc_qh(uhci, urb->dev);
> if (!urbp->qh) {
> + atomic_dec(&urbp->refcount);
So no destroy_urb_priv here...
> err("unable to allocate new QH for control retrigger");
> return -ENOMEM;
> }
> @@ -1150,6 +1148,7 @@
>
> urbp->qh = qh;
> qh->urbp = urbp;
> + atomic_inc(&urbp->refcount);
>
> /* Always breadth first */
> uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
> @@ -1491,7 +1490,9 @@
>
> list_del_init(&urbp->urb_list);
> spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> - uhci_destroy_urb_priv (uhci, urb);
> + urb->hcpriv = NULL;
> + urbp->urb = NULL;
> + uhci_destroy_urb_priv (uhci, urbp);
>
> return ret;
> }
> @@ -1630,9 +1631,8 @@
>
> uhci_delete_queued_urb(uhci, urb);
>
> - /* The interrupt loop will reclaim the QH's */
> + /* The interrupt loop will reclaim the QH's and TD's */
> uhci_remove_qh(uhci, urbp->qh);
> - urbp->qh = NULL;
> }
>
> static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
> @@ -1781,19 +1781,24 @@
>
> static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
> {
> - struct list_head *tmp, *head;
> + struct list_head *head;
> unsigned long flags;
>
> spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
> head = &uhci->qh_remove_list;
> - tmp = head->next;
> - while (tmp != head) {
> - struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
> -
> - tmp = tmp->next;
> + while (!list_empty(head)) {
> + struct uhci_qh *qh = list_entry(head->next,
> + struct uhci_qh, remove_list);
> + struct urb_priv *urbp = qh->urbp;
>
> list_del_init(&qh->remove_list);
> -
> + if (urbp && atomic_dec_and_test(&urbp->refcount)) {
> + qh->urbp = NULL;
> + urbp->qh = NULL;
> + spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
> + uhci_destroy_urb_priv(uhci, urbp);
Why release the spinlock before calling this?
> + spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
> + }
> uhci_free_qh(uhci, qh);
> }
> spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
> @@ -1802,10 +1807,14 @@
> static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct
> pt_regs *regs) {
> struct uhci_hcd *uhci = hcd_to_uhci(hcd);
> + struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
> unsigned long flags;
>
> spin_lock_irqsave(&urb->lock, flags);
> - uhci_destroy_urb_priv(uhci, urb);
> + urbp->urb = NULL;
> + urb->hcpriv = NULL;
> + if (atomic_dec_and_test(&urbp->refcount))
> + uhci_destroy_urb_priv(uhci, urbp);
> spin_unlock_irqrestore(&urb->lock, flags);
>
> usb_hcd_giveback_urb(hcd, urb, regs);
> @@ -1814,24 +1823,21 @@
> static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs
> *regs) {
> struct uhci_hcd *uhci = hcd_to_uhci(hcd);
> - struct list_head *tmp, *head;
> + struct list_head *head;
> unsigned long flags;
>
> spin_lock_irqsave(&uhci->complete_list_lock, flags);
> head = &uhci->complete_list;
> - tmp = head->next;
> - while (tmp != head) {
> - struct urb_priv *urbp = list_entry(tmp, struct urb_priv,
> complete_list);
> - struct urb *urb = urbp->urb;
> + while (!list_empty(head)) {
> + struct urb_priv *urbp = list_entry(head->next,
> + struct urb_priv, complete_list);
>
> list_del_init(&urbp->complete_list);
> spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
>
> - uhci_finish_urb(hcd, urb, regs);
> + uhci_finish_urb(hcd, urbp->urb, regs);
>
> spin_lock_irqsave(&uhci->complete_list_lock, flags);
> - head = &uhci->complete_list;
> - tmp = head->next;
> }
> spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
> }
> ===== uhci-hcd.h 1.15 vs edited =====
> --- 1.15/drivers/usb/host/uhci-hcd.h Tue Jul 22 11:26:32 2003
> +++ edited/drivers/usb/host/uhci-hcd.h Wed Jul 23 16:29:16 2003
> @@ -382,6 +382,7 @@
>
> unsigned long inserttime; /* In jiffies */
> unsigned long fsbrtime; /* In jiffies */
> + atomic_t refcount; /* 1 for URB, 1 for QH */
>
> struct list_head queue_list; /* P: uhci->frame_list_lock */
> struct list_head complete_list; /* P: uhci->complete_list_lock */
By the way, looks like much of the list-handling in uhci-hcd could be done
more cleanly using the macros from list.h.
Ciao,
Duncan.
-------------------------------------------------------
This SF.Net email sponsored by: Free pre-built ASP.NET sites including
Data Reports, E-commerce, Portals, and Forums are available now.
Download today and enter to win an XBOX or Visual Studio .NET.
http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/01
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel