These patches are against kernels 2.6.18 through at least 2.6.18-git7.
patch 14: Adds low-level scheduler mechanisms for linking, unlinking,
manipulating and maintaining spanning ('dummy') sITDs such that
split-iso transactions may span H-frames. Turns on shadow budget
logic to allow/use sITD spanning in budgeting.
Signed-off-by: Christopher "Monty" Montgomery <[EMAIL PROTECTED]>
---
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci.h
b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h 2006-09-26 22:34:23.000000000 -0400
+++ b/drivers/usb/host/ehci.h 2006-09-26 22:34:40.000000000 -0400
@@ -627,6 +627,7 @@ struct ehci_sitd {
#define SITD_STS_STS (1 << 1) /* split transaction state */
#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE)
+#define SITD_DOCOMPLETE __constant_cpu_to_le32(SITD_STS_STS)
__le32 hw_buf [2]; /* EHCI table 3-12 */
__le32 hw_backpointer; /* EHCI table 3-13 */
diff -X b/Documentation/dontdiff -upr a/drivers/usb/host/ehci-sched.c
b/drivers/usb/host/ehci-sched.c
--- a/drivers/usb/host/ehci-sched.c 2006-09-26 22:34:24.000000000 -0400
+++ b/drivers/usb/host/ehci-sched.c 2006-09-26 22:34:40.000000000 -0400
@@ -61,10 +61,10 @@
* For isochronous, an "iso_stream" head serves the same role as a QH.
* It keeps track of every ITD (or SITD) that's scheduled.
*
- * This scheduler supports and aggressively uses both FSTNs in order
- * to allow interrupt QH transfers to span H-frame boundaries. This
- * support is necessary for efficient FS bus bandwidth usage through a
- * 2.0 hub's TT mechanism.
+ * This scheduler supports and aggressively uses both FSTNs and sITD
+ * backpointers in order to allow interrupt QH and sITD transfers to
+ * span H-frame boundaries. This support is necessary for efficient
+ * FS bus bandwidth usage through a 2.0 hub's TT mechanism.
*
* The transfer ordering in the shadow schedule looks like:
*
@@ -105,7 +105,21 @@
* schedule in the order specified by the budget. FSTN positioning is
* static; it follows the recommendations of the EHCI 1.0 spec and
* places one save-state FSTN at the lowest level in each branch of
- * the INTR tree and one restore FSTN at the head of level one.
+ * the INTR tree and one restore FSTN at the head of level one. Dummy
+ * sITDs are positioned in the order of the original sITDs of the
+ * preceeding frame.
+ *
+ * The scheduler does not currently implement any inter- or
+ * intra-frame rebalancing; that is, the budget is not adjusted to
+ * fill in 'holes' or shuffle transfer slots to optimize remaining
+ * bandwidth when endpoints are removed. QHs will reuse holes at the
+ * moment, isoch transfers will not. This can also lead to starving
+ * QHs depending on the order in which the QHs are budgeted because
+ * the tree arrangement of the QHs places ordering restrictions upon
+ * QHs that do not exist for isoch transfers. Starvation is unlikely
+ * (and far less likely than for the old scheduler), but it's still
+ * possible to be unable to schedule a new QH before actually running
+ * out of FS bandwidth.
*
*/
@@ -124,6 +138,14 @@ MODULE_PARM_DESC (use_fstn,
"use FSTNs: allow use of FSTN to schedule full speed INTR "
"transfers across frame boundaries; default is 1 (on)");
+/* enable/disable use of dummy sITDs */
+static unsigned use_dummy_sitds = 1;
+module_param (use_dummy_sitds, uint, S_IRUGO);
+MODULE_PARM_DESC (use_dummy_sitds,
+ "use dummy sITDs: allow use of 'dummy' sITDs to "
+ "schedule schedule full speed ISOCH transfers across frame "
+ "boundaries; default is 1 (on)");
+
/* set limit on periodic load per TT uframe */
static unsigned fs_bytes_per_uframe = 188;
module_param (fs_bytes_per_uframe, uint, S_IRUGO);
@@ -480,6 +502,68 @@ static void *periodic_shadow_owner(struc
}
}
+static union ehci_shadow *
+periodic_translate_budget_dummy (struct ehci_hcd *ehci,
+ unsigned frame,
+ struct ehci_shadow_budget *translate,
+ __le32 **hw_p)
+{
+ struct ehci_shadow_budget *budget_prev =
+ ehci->budget [((frame-1) & BUDGET_SLOT_MASK)];
+ union ehci_shadow *shadow =
+ &ehci->pshadow [frame & (ehci->periodic_size-1)];
+ __le32 type;
+
+ *hw_p = &ehci->periodic [frame & (ehci->periodic_size-1)];
+ type = Q_NEXT_TYPE (**hw_p);
+
+ /* Everything in the shadow schedule will be there in the budget
+ [except for FSTNs] */
+ while (shadow->ptr){
+ struct ehci_sitd *sitd;
+ if(type != Q_TYPE_SITD) return shadow;
+
+ sitd = shadow->sitd;
+ if(sitd->hw_backpointer != EHCI_LIST_END) return shadow;
+
+ /* advance budget until the current budget
+ placeholder is the one belonging to current
+ shadow entry or until we hit the stopping
+ point. */
+
+ while(budget_prev &&
+ budget_prev != translate &&
+ budget_prev->owner.ptr !=
+ periodic_shadow_owner(ehci,shadow->ptr,type)){
+ budget_prev = budget_prev->next;
+ }
+ if(budget_prev == translate){
+ return shadow; /* a match! */
+ }
+
+ /* falling off the end of the budget indicates
+ * an internal logic error */
+ if(budget_prev == NULL){
+ ehci_err(ehci,
+ "schedule contains "
+ "unbudgeted transaction\n");
+ return NULL;
+ }
+
+ /* advance shadow and hardware schedule by one, then
+ loop to test this position */
+ *hw_p = shadow->hw_next;
+ shadow = periodic_next_shadow (shadow, type);
+ type = Q_NEXT_TYPE (**hw_p);
+
+ }
+
+ /* nothing queued or fell off the end looking for stopping point.
+ Append to end. */
+ return shadow;
+
+}
+
/* periodic_translate_budget - Finds the position in the
* hardware/shadow schedule to insert a new hardware schedule entry
* based on the position in the shadow budget of the passed in budget
@@ -969,10 +1053,11 @@ static int budget_calc_fs_frame(struct e
cmask = 0;
}
- /* don't allow spanning yet */
- if(cmask&(~0xff))
+ /* have we disallowed frame spanning? */
+ if(cmask&(~0xff)){
+ if(use_dummy_sitds==0)
return -1;
-
+ }
}
/* FS/LS ssplit in uFrame 6 or 7 is illegal */
@@ -2785,7 +2870,7 @@ sitd_sched_init (
unsigned i;
dma_addr_t dma = urb->transfer_dma;
- /* how many frames are needed for these transfers */
+ /* how many uframes are needed for these transfers */
iso_sched->span = urb->number_of_packets * stream->interval;
/* figure out per-frame sitd fields that we'll need later
@@ -2854,12 +2939,10 @@ sitd_urb_transaction (
/* allocate/init sITDs */
spin_lock_irqsave (&ehci->lock, flags);
- for (i = 0; i < urb->number_of_packets; i++) {
- /* NOTE: for now, we don't try to handle wraparound cases
- * for IN (using sitd->hw_backpointer, like a FSTN), which
- * means we never need two sitds for full speed packets.
- */
+ /* note: frame spanning may require two SITDs per transaction;
+ allocate all we may need, and free the ones we don't later. */
+ for (i = 0; i < urb->number_of_packets * 2; i++) {
/* free_list.next might be cache-hot ... but maybe
* the HC caches it too. avoid that issue for now.
@@ -2908,6 +2991,8 @@ sitd_urb_transaction (
* information
* @sitd: New sITD that is to be added to the shadow/hardware schedule
* @uf: iso stream packet
+ * @back: hw pointer (cpu order) pointing back to original sITD
+ * if this is a spanning dummy sITD
*/
static inline void
sitd_patch (
@@ -2924,8 +3009,17 @@ sitd_patch (
sitd->hw_fullspeed_ep = stream->address;
sitd->hw_results = uf->transaction;
+ if(back){
+ /* spanning completion only */
+ sitd->hw_uframe = cpu_to_le32( ((b->cmask>>8)&0xff)<<8 );
+ /* set dummy's completion state to DoComplete (ehci
+ 4.12.3.3.x) */
+ sitd->hw_results |= SITD_DOCOMPLETE;
+ sitd->hw_backpointer = cpu_to_le32 (back);
+ }else{
sitd->hw_uframe = cpu_to_le32(b->smask | ((b->cmask&0xff)<<8));
sitd->hw_backpointer = EHCI_LIST_END;
+ }
sitd->hw_buf [0] = cpu_to_le32 (bufp);
sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32);
@@ -2937,7 +3031,8 @@ sitd_patch (
}
/* sitd_link - fill in and link one sITD into the shadow/hardware
- * schedule
+ * schedule; if this sITD requires a spanning dummy in the next frame,
+ * a spanning dummy is implicitly filled in and linked as well.
*
* @ehci: pointer to ehci host controller device structure
* @urb: new USB request
@@ -2953,7 +3048,7 @@ static inline void sitd_link (struct ehc
struct ehci_iso_sched *sched,
int index)
{
- struct ehci_sitd *sitd;
+ struct ehci_sitd *sitd, *dummy;
__le32 *hw_p;
struct ehci_shadow_budget *b;
union ehci_shadow *here;
@@ -2988,6 +3083,40 @@ static inline void sitd_link (struct ehc
here->sitd = sitd;
wmb ();
+ /* is this a wrap case? if so, have the dummy SITD fully in place
+ before the real SITD is reachable from the hardware schedule */
+
+ if(BUDGET_WRAP_P(b)){
+ __le32 *d_hw_p;
+ union ehci_shadow *d_here;
+
+ /* get SITD from already allocated list */
+ BUG_ON (list_empty (&sched->td_list));
+ dummy = list_entry (sched->td_list.next,
+ struct ehci_sitd, sitd_list);
+ list_move_tail (&dummy->sitd_list, &stream->td_list);
+ dummy->stream = iso_stream_get (stream);
+ dummy->urb = usb_get_urb (urb);
+ dummy->index = index;
+ dummy->frame = frame+1;
+
+ /* find the proper insertion point */
+ d_here = periodic_translate_budget_dummy (ehci, frame+1, b,
&d_hw_p);
+
+ /* set the sitd fields */
+ sitd_patch (stream, b, dummy, &sched->packet [index],
+ sitd->sitd_dma);
+ dummy->budget = b;
+
+ dummy->sitd_next = *d_here;
+ dummy->hw_next = *d_hw_p;
+
+ d_here->sitd = dummy;
+ wmb ();
+ *d_hw_p = cpu_to_le32 (dummy->sitd_dma) | Q_TYPE_SITD;
+
+ }
+
/* make sitd reacahable from periodic schedule */
*hw_p = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD;
}
@@ -3177,6 +3306,41 @@ done:
/*-------------------------------------------------------------------------*/
+static void unlink_dummy(struct ehci_hcd *ehci, struct ehci_sitd *sitd,
+ struct pt_regs *regs, int frame){
+
+ /* there is a dummy; search for it */
+
+ __le32 *hw_p =
+ &ehci->periodic [frame & (ehci->periodic_size-1)];
+ union ehci_shadow *here =
+ &ehci->pshadow [frame & (ehci->periodic_size-1)];
+ __le32 type = Q_NEXT_TYPE (*hw_p);
+
+ while (here->ptr) {
+ if(type == Q_TYPE_SITD){
+ if(here->sitd->budget == sitd->budget){
+ struct ehci_sitd *temp = here->sitd;
+ *hw_p = here->sitd->hw_next;
+ *here = here->sitd->sitd_next;
+ wmb();
+
+ iso_stream_put (ehci, temp->stream);
+
+ break; /* only one dummy will match */
+ }
+ if(here->sitd->hw_backpointer == EHCI_LIST_END)
+ break; /* all dummies come first */
+
+ }else
+ break; /* all dummies come first */
+
+ hw_p = here->hw_next;
+ here = periodic_next_shadow (here, type);
+ type = Q_NEXT_TYPE (*hw_p);
+ }
+}
+
static int scan_frame(struct ehci_hcd *ehci, struct pt_regs *regs,
int frame, int uframes, int rescan){
@@ -3307,9 +3471,9 @@ static int scan_frame(struct ehci_hcd *e
type = Q_NEXT_TYPE (*hw_p);
wmb();
- /* when spanning sITD support is
- * added, the spanning dummy will be
- * unlinked here as well. */
+ /* unlink the dummy (if any) */
+ if(BUDGET_WRAP_P(q.sitd->budget))
+ unlink_dummy(ehci,q.sitd,regs,frame+1);
modified = sitd_complete (ehci, q.sitd, regs);
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel