On Fri, May 04, 2001, Johannes Erdfelt <[EMAIL PROTECTED]> wrote:
> On Fri, May 04, 2001, Martin Diehl <[EMAIL PROTECTED]> wrote:
> > However, now uhci looses in performance compared to usb-uhci: With my
> > usual 2 queued bulk transfers, len=2112, both to same endpoint,
> > usb-uhci provides >1MB/sec av. throughput - for uhci this number was the
> > same without this patch (until the race happened) and has now dropped to
> > about 700KB/sec.
> 
> 1MB/sec is very impressive. I forget exactly what your device did, does
> it connect 2 hosts together?
> 
> I can get 805KB/sec with this patch using the usbnet driver which is
> as fast as usb-uhci. I'm still testing and I'll send an email later
> with more tests.

Ok, I've done some more testing now and I found some problems. First, I
sent the wrong patch to the list. Doesn't matter since there were some
dumb bugs in the "correct" version, some of them fatal.

Here's a new updated version. Remove the previous patch first.

JE

diff -ur -X dontdiff linux-2.4.5-pre1.orig/drivers/usb/uhci-debug.h 
linux-2.4.5-pre1/drivers/usb/uhci-debug.h
--- linux-2.4.5-pre1.orig/drivers/usb/uhci-debug.h      Fri Apr 27 15:50:01 2001
+++ linux-2.4.5-pre1/drivers/usb/uhci-debug.h   Fri May  4 15:08:23 2001
@@ -197,7 +197,7 @@
                        qh, qh->link, qh->element);
 
        if (qh->element & UHCI_PTR_QH)
-               out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
+               out += sprintf(out, "%*s  Element points to QH\n", space, "");
 
        if (qh->element & UHCI_PTR_DEPTH)
                out += sprintf(out, "%*s  Depth traverse\n", space, "");
@@ -215,13 +215,16 @@
 
        urbp = qh->urbp;
 
+       if (urbp->bqh == qh)
+               goto out;
+
        head = &urbp->td_list;
        tmp = head->next;
 
        td = list_entry(tmp, struct uhci_td, list);
 
        if (td->dma_handle != (qh->element & ~UHCI_PTR_BITS))
-               out += sprintf(out, "%*s Element != First TD\n", space, "");
+               out += sprintf(out, "%*s  Element != First TD\n", space, "");
 
        while (tmp != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
@@ -258,6 +261,11 @@
                }
 
                prevactive = td->status & TD_CTRL_ACTIVE;
+       }
+
+       if (urbp->bqh) {
+               out += sprintf(out, "%*s Bottom QH:\n", space, "");
+               out += uhci_show_qh(urbp->bqh, out, len - (out - buf), space + 2);
        }
 
        if (list_empty(&urbp->queue_list) || urbp->queued)
diff -ur -X dontdiff linux-2.4.5-pre1.orig/drivers/usb/uhci.c 
linux-2.4.5-pre1/drivers/usb/uhci.c
--- linux-2.4.5-pre1.orig/drivers/usb/uhci.c    Wed May  2 12:25:47 2001
+++ linux-2.4.5-pre1/drivers/usb/uhci.c Fri May  4 15:01:48 2001
@@ -147,12 +147,17 @@
        return 0;
 }
 
+static inline void _uhci_set_next_interrupt(struct uhci *uhci)
+{
+       uhci->skel_term_td->status |= TD_CTRL_IOC;
+}
+
 static inline void uhci_set_next_interrupt(struct uhci *uhci)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&uhci->frame_list_lock, flags);
-       uhci->skel_term_td->status |= TD_CTRL_IOC;
+       _uhci_set_next_interrupt(uhci);
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
@@ -390,26 +395,31 @@
        pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
 }
 
+static void _uhci_insert_qh(struct uhci_qh *eqh, struct uhci_qh *qh)
+{
+       qh->link = eqh->link;
+       mb();                           /* Ordering is important */
+       eqh->link = qh->dma_handle | UHCI_PTR_QH;
+
+       list_add(&qh->list, &eqh->list);
+}
+
 static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh 
*qh)
 {
-       struct uhci_qh *lqh;
        unsigned long flags;
+       struct uhci_qh *lqh;
 
        spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
        /* Grab the last QH */
        lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
 
-       qh->link = lqh->link;
-       mb();                           /* Ordering is important */
-       lqh->link = qh->dma_handle | UHCI_PTR_QH;
-
-       list_add_tail(&qh->list, &skelqh->list);
+       _uhci_insert_qh(lqh, qh);
 
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 }
 
-static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
+static void _uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
        unsigned long flags;
        struct uhci_qh *prevqh;
@@ -422,8 +432,6 @@
 
        qh->urbp = NULL;
 
-       spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
        prevqh = list_entry(qh->list.prev, struct uhci_qh, list);
 
        prevqh->link = qh->link;
@@ -433,20 +441,29 @@
        list_del(&qh->list);
        INIT_LIST_HEAD(&qh->list);
 
-       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-
        spin_lock_irqsave(&uhci->qh_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 QH */
        if (list_empty(&uhci->qh_remove_list))
-               uhci_set_next_interrupt(uhci);
+               _uhci_set_next_interrupt(uhci);
 
        list_add(&qh->remove_list, &uhci->qh_remove_list);
 
        spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
 }
 
+static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+
+       _uhci_remove_qh(uhci, qh);
+
+       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+}
+
 static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
 {
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
@@ -472,17 +489,59 @@
 /* This function will append one URB's QH to another URB's QH. This is for */
 /*  USB_QUEUE_BULK support for bulk transfers and soon implicitily for */
 /*  control transfers */
-static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb 
*urb)
+static int uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb 
+*urb)
 {
        struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
        struct list_head *tmp;
-       struct uhci_td *ftd, *lltd;
+       struct uhci_td *ftd, *lltd, *ltd;
        unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       eurbp = eurb->hcpriv;
        urbp = urb->hcpriv;
 
-       spin_lock_irqsave(&uhci->frame_list_lock, flags);
+       /* Bottom qh which will be stolen by the next URB */
+       urbp->bqh = uhci_alloc_qh(uhci, urb->dev);
+       if (!urbp->bqh) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       urbp->bqh->urbp = urbp;
+
+       ltd = list_entry(urbp->td_list.prev, struct uhci_td, list);
+       ltd->link = urbp->bqh->dma_handle | UHCI_PTR_QH;
+
+       if (!eurb) {
+               struct uhci_qh *lqh;
+
+               /* Next qh, basically a helper qh to get to continue */
+               /*  processing the schedule */
+               urbp->nqh = uhci_alloc_qh(uhci, urb->dev);
+               if (!urbp->nqh) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               urbp->bqh->link = urbp->nqh->dma_handle | UHCI_PTR_QH;
+
+               /* Grab the last QH */
+               lqh = list_entry(uhci->skel_bulk_qh->list.prev, struct uhci_qh, list);
+
+               /* We need to make sure we insert these at the same time */
+               _uhci_insert_qh(lqh, urbp->qh);
+               _uhci_insert_qh(urbp->qh, urbp->nqh);
+
+               goto out;
+       }
+
+       eurbp = eurb->hcpriv;
+
+       /* Each group of queued URB's has it's own nqh */
+       urbp->nqh = eurbp->nqh;
+
+       urbp->bqh->link = urbp->nqh->dma_handle | UHCI_PTR_QH;
 
        /* Find the first URB in the queue */
        if (eurbp->queued) {
@@ -511,13 +570,16 @@
 
        mb();                   /* Make sure we flush everything */
        /* Only support bulk right now, so no depth */
-       lltd->link = ftd->dma_handle;
+       lurbp->bqh->element = ftd->dma_handle;
 
        list_add_tail(&urbp->queue_list, &furbp->queue_list);
 
        urbp->queued = 1;
 
+out:
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
+
+       return ret;
 }
 
 static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
@@ -533,8 +595,14 @@
 
        spin_lock_irqsave(&uhci->frame_list_lock, flags);
 
-       if (list_empty(&urbp->queue_list))
+       if (list_empty(&urbp->queue_list)) {
+               if (urbp->nqh) {
+                       _uhci_remove_qh(uhci, urbp->nqh);
+                       urbp->nqh = NULL;
+               }
+
                goto out;
+       }
 
        nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
 
@@ -571,28 +639,19 @@
                usb_pipeout(urb->pipe), toggle);
 
        if (!urbp->queued) {
-               int status;
+               struct uhci_qh *pqh;
 
-               /*  The HC may continue using the current QH if it finished */
-               /* all of the TD's in this URB and may have started on the */
-               /* next URB's TD's already, so we'll take over ownership */
-               /* of this QH and use it instead. Don't forget to delete */
-               /* the old QH first */
+               /* The next URB gets our bottom QH. We don't need the QH */
+               /*  we allocated earlier anymore */
                uhci_free_qh(uhci, nurbp->qh);
-
-               nurbp->qh = urbp->qh;
+               nurbp->qh = urbp->bqh;
                nurbp->qh->urbp = nurbp;
-               urbp->qh = NULL;
+               urbp->bqh = NULL;
 
-               /* If the last TD from the first (this) urb didn't */
-               /*  complete, reset qh->element to the first TD in the */
-               /*  next urb */
-               pltd = list_entry(urbp->td_list.prev, struct uhci_td, list);
-               status = uhci_status_bits(pltd->status);
-               if ((status & TD_CTRL_ACTIVE) || uhci_actual_length(pltd->status) < 
uhci_expected_length(pltd->info)) {
-                       struct uhci_td *ftd = list_entry(nurbp->td_list.next, struct 
uhci_td, list);
-                       nurbp->qh->element = ftd->dma_handle;
-               }
+               /* Grab the previous QH */
+               pqh = list_entry(nurbp->nqh->list.prev, struct uhci_qh, list);
+
+               _uhci_insert_qh(pqh, nurbp->qh);
 
                nurbp->queued = 0;
        } else {
@@ -1280,9 +1339,13 @@
        /* Always assume breadth first */
        uhci_insert_tds_in_qh(qh, urb, 1);
 
-       if (urb->transfer_flags & USB_QUEUE_BULK && eurb)
-               uhci_append_queued_urb(uhci, eurb, urb);
-       else
+       if (urb->transfer_flags & USB_QUEUE_BULK) {
+               int ret;
+
+               ret = uhci_append_queued_urb(uhci, eurb, urb);
+               if (ret < 0)
+                       return ret;
+       } else
                uhci_insert_qh(uhci, uhci->skel_bulk_qh, qh);
 
        uhci_inc_fsbr(uhci, urb);
@@ -1674,9 +1737,12 @@
 
        uhci_delete_queued_urb(uhci, urb);
 
+       /* The interrupt loop will reclaim the QH's */
        if (urbp->qh)
-               /* The interrupt loop will reclaim the QH's */
                uhci_remove_qh(uhci, urbp->qh);
+
+       if (urbp->bqh)
+               uhci_remove_qh(uhci, urbp->bqh);
 }
 
 /* FIXME: If we forcefully unlink an urb, we should reset the toggle for */
diff -ur -X dontdiff linux-2.4.5-pre1.orig/drivers/usb/uhci.h 
linux-2.4.5-pre1/drivers/usb/uhci.h
--- linux-2.4.5-pre1.orig/drivers/usb/uhci.h    Fri Apr 27 15:49:56 2001
+++ linux-2.4.5-pre1/drivers/usb/uhci.h Fri May  4 11:39:11 2001
@@ -331,7 +331,8 @@
        dma_addr_t setup_packet_dma_handle;
        dma_addr_t transfer_buffer_dma_handle;
 
-       struct uhci_qh *qh;             /* QH for this URB */
+       struct uhci_qh *qh;
+       struct uhci_qh *bqh, *nqh;      /* For queued URB's */
        struct list_head td_list;
 
        int fsbr : 1;                   /* URB turned on FSBR */

_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
http://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to