# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.540 -> 1.541 # drivers/usb/host/ehci-q.c 1.11 -> 1.12 # drivers/usb/host/ehci-sched.c 1.9 -> 1.10 # drivers/usb/host/ehci-hcd.c 1.13 -> 1.14 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/04/22 david-b@pacbell.net 1.541 # [PATCH] PATCH: 2.5.8 ehci, submit errors # # It fixes problems with interrupt transfers, which I think that # nobody else has run into (or I'd surely have heard of it :). # Looks like not many folk are using USB 2.0 hubs yet. # # - wasn't checking enough of the periodic schedule to # detect bandwidth overcommit (would BUG out) # - frames to uframes is rightshift 3, not 8 :) # - properly cleans up (no oops!) after certain rare errors # in the interrupt submit path (just my luck to hit one) # - use that cleanup to bypass some old implementation # shortcuts in the control and bulk submit paths # - there are also some other minor updates/cleanups # -------------------------------------------- # diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Mon Apr 22 14:28:12 2002 +++ b/drivers/usb/host/ehci-hcd.c Mon Apr 22 14:28:12 2002 @@ -70,6 +70,8 @@ * * HISTORY: * + * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on + * errors in submit path. Bugfixes to interrupt scheduling/processing. * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift * more checking to generic hcd framework (db). Make it work with * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt). @@ -81,7 +83,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "$Revision: 0.27 $" +#define DRIVER_VERSION "$Revision: 0.31 $" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -512,8 +514,7 @@ case PIPE_BULK: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; - submit_async (ehci, urb, &qtd_list, mem_flags); - break; + return submit_async (ehci, urb, &qtd_list, mem_flags); case PIPE_INTERRUPT: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) @@ -531,8 +532,9 @@ return -ENOSYS; #endif /* have_split_iso */ + default: /* can't happen */ + return -ENOSYS; } - return 0; } /* remove from hardware lists @@ -587,7 +589,7 @@ // itd or sitd ... // wait till next completion, do it then. - // completion irqs can wait up to 128 msec, + // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; return 0; } @@ -614,10 +616,7 @@ if (dev->ep [i]) { struct ehci_qh *qh; - // FIXME: this might be an itd/sitd too ... - // or an interrupt urb (not on async list) - // can use "union ehci_shadow" - + /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; vdbg ("free_config, ep 0x%02x qh %p", i, qh); if (!list_empty (&qh->qtd_list)) { diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Mon Apr 22 14:28:12 2002 +++ b/drivers/usb/host/ehci-q.c Mon Apr 22 14:28:12 2002 @@ -280,6 +280,8 @@ /* if these qtds were queued to the HC, some may be active. * else we're cleaning up after a failed URB submission. + * + * FIXME can simplify: cleanup case is gone now. */ if (likely (qh != 0)) { int qh_halted; @@ -406,6 +408,47 @@ /*-------------------------------------------------------------------------*/ /* + * reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list +) { + struct list_head *entry, *temp; + int unmapped = 0; + + list_for_each_safe (entry, temp, qtd_list) { + struct ehci_qtd *qtd; + + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + list_del (&qtd->qtd_list); + if (unmapped != 2) { + int direction; + + /* for ctrl unmap twice: SETUP and DATA; + * else (bulk, intr) just once: DATA + */ + if (!unmapped++ && usb_pipecontrol (urb->pipe)) + direction = PCI_DMA_TODEVICE; + else { + direction = usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE; + unmapped++; + } + if (qtd->buf_dma) + pci_unmap_single (ehci->hcd.pdev, + qtd->buf_dma, + qtd->urb->transfer_buffer_length, + direction); + } + ehci_qtd_free (ehci, qtd); + } +} + +/* * create a list of filled qtds for this URB; won't link into qh. */ static struct list_head * @@ -547,8 +590,7 @@ return head; cleanup: - urb->status = -ENOMEM; - qh_completions (ehci, head, 1); + qtd_list_free (ehci, urb, head); return 0; } @@ -713,7 +755,7 @@ /*-------------------------------------------------------------------------*/ -static void +static int submit_async ( struct ehci_hcd *ehci, struct urb *urb, @@ -807,8 +849,7 @@ if (likely (qh != 0)) { // dbg_qh ("new qh", ehci, qh); dev->ep [epnum] = qh; - } else - urb->status = -ENOMEM; + } } /* Control/bulk operations through TTs don't need scheduling, @@ -820,8 +861,11 @@ qh_link_async (ehci, qh_get (qh)); } spin_unlock_irqrestore (&ehci->lock, flags); - if (unlikely (!qh)) - qh_completions (ehci, qtd_list, 1); + if (unlikely (qh == 0)) { + qtd_list_free (ehci, urb, qtd_list); + return -ENOMEM; + } + return 0; } /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Mon Apr 22 14:28:12 2002 +++ b/drivers/usb/host/ehci-sched.c Mon Apr 22 14:28:12 2002 @@ -220,6 +220,8 @@ ) { unsigned long flags; + period >>= 3; // FIXME microframe periods not handled yet + spin_lock_irqsave (&ehci->lock, flags); do { @@ -256,6 +258,38 @@ atomic_read (&qh->refcount), ehci->periodic_urbs); } +static int check_period ( + struct ehci_hcd *ehci, + unsigned frame, + int uframe, + unsigned period, + unsigned usecs +) { + /* + * 80% periodic == 100 usec/uframe available + * convert "usecs we need" to "max already claimed" + */ + usecs = 100 - usecs; + + do { + int claimed; + +// FIXME delete when intr_submit handles non-empty queues +// this gives us a one intr/frame limit (vs N/uframe) + if (ehci->pshadow [frame].ptr) + return 0; + + claimed = periodic_usecs (ehci, frame, uframe); + if (claimed > usecs) + return 0; + +// FIXME update to handle sub-frame periods + } while ((frame += period) < ehci->periodic_size); + + // success! + return 1; +} + static int intr_submit ( struct ehci_hcd *ehci, struct urb *urb, @@ -263,7 +297,6 @@ int mem_flags ) { unsigned epnum, period; - unsigned temp; unsigned short usecs; unsigned long flags; struct ehci_qh *qh; @@ -272,11 +305,8 @@ /* get endpoint and transfer data */ epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) { - temp = urb->dev->epmaxpacketin [epnum]; + if (usb_pipein (urb->pipe)) epnum |= 0x10; - } else - temp = urb->dev->epmaxpacketout [epnum]; if (urb->dev->speed != USB_SPEED_HIGH) { dbg ("no intr/tt scheduling yet"); status = -ENOSYS; @@ -287,6 +317,13 @@ * NOTE: current completion/restart logic doesn't handle more than * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this. * such big requests need many periods to transfer. + * + * FIXME want to change hcd core submit model to expect queuing + * for all transfer types ... not just ISO and (with flag) BULK. + * that means: getting rid of this check; handling the "interrupt + * urb already queued" case below like bulk queuing is handled (no + * errors possible!); and completly getting rid of that annoying + * qh restart logic. simpler/smaller overall, and more flexible. */ if (unlikely (qtd_list->next != qtd_list->prev)) { dbg ("only one intr qtd per urb allowed"); @@ -297,11 +334,13 @@ usecs = HS_USECS (urb->transfer_buffer_length); /* FIXME handle HS periods of less than 1 frame. */ - if (urb->interval < 8) - period = 1; - else - period = urb->interval >> 8; - + period = urb->interval >> 3; + if (period < 1) { + dbg ("intr period %d uframes, NYET!", urb->interval); + status = -EINVAL; + goto done; + } + spin_lock_irqsave (&ehci->lock, flags); /* get the qh (must be empty and idle) */ @@ -326,21 +365,22 @@ list_splice (qtd_list, &qh->qtd_list); qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + qtd_list = &qh->qtd_list; } } else { /* can't sleep here, we have ehci->lock... */ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - qtd_list = &qh->qtd_list; if (likely (qh != 0)) { // dbg ("new INTR qh %p", qh); dev->ep [epnum] = qh; + qtd_list = &qh->qtd_list; } else status = -ENOMEM; } /* Schedule this periodic QH. */ if (likely (status == 0)) { - unsigned frame = urb->interval; + unsigned frame = period; qh->hw_next = EHCI_LIST_END; qh->usecs = usecs; @@ -352,28 +392,20 @@ do { int uframe; - /* Select some frame 0..(urb->interval - 1) with a - * microframe that can hold this transaction. + /* pick a set of slots such that all uframes have + * enough periodic bandwidth available. * * FIXME for TT splits, need uframes for start and end. * FSTNs can put end into next frame (uframes 0 or 1). */ frame--; for (uframe = 0; uframe < 8; uframe++) { - int claimed; - claimed = periodic_usecs (ehci, frame, uframe); - /* 80% periodic == 100 usec max committed */ - if ((claimed + usecs) <= 100) { - vdbg ("frame %d.%d: %d usecs, plus %d", - frame, uframe, claimed, usecs); + if (check_period (ehci, frame, uframe, + period, usecs) != 0) break; - } } if (uframe == 8) continue; -// FIXME delete when code below handles non-empty queues - if (ehci->pshadow [frame].ptr) - continue; /* QH will run once each period, starting there */ urb->start_frame = frame; @@ -389,8 +421,9 @@ qh, qh->usecs, period, frame, uframe); do { if (unlikely (ehci->pshadow [frame].ptr != 0)) { -// FIXME -- just link to the end, before any qh with a shorter period, +// FIXME -- just link toward the end, before any qh with a shorter period, // AND handle it already being (implicitly) linked into this frame +// AS WELL AS updating the check_period() logic BUG (); } else { ehci->pshadow [frame].qh = qh_get (qh); @@ -401,7 +434,7 @@ } while (frame < ehci->periodic_size); /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, usecs, 0); + usb_claim_bandwidth (urb->dev, urb, usecs/period, 0); /* maybe enable periodic schedule processing */ if (!ehci->periodic_urbs++) @@ -412,14 +445,9 @@ } spin_unlock_irqrestore (&ehci->lock, flags); done: - if (status) { - usb_complete_t complete = urb->complete; + if (status) + qtd_list_free (ehci, urb, qtd_list); - urb->complete = 0; - urb->status = status; - qh_completions (ehci, qtd_list, 1); - urb->complete = complete; - } return status; } @@ -438,6 +466,10 @@ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) != 0)) return flags; + if (unlikely (list_empty (&qh->qtd_list))) { + dbg ("intr qh %p no TDs?", qh); + return flags; + } qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); urb = qtd->urb;