From xiphmont@gmail.com Thu Sep 28 00:19:01 2006 Message-ID: <806dafc20609280018o45bcdd42x6533f25425b6833a@mail.gmail.com> Date: Thu, 28 Sep 2006 03:18:55 -0400 From: "Christopher \"Monty\" Montgomery" To: linux-usb-devel@lists.sourceforge.net Subject: [PATCH 9/15] USB: ehci-hcd: unify interval granularity and limit depth of interrupt tree Cc: greg@kroah.com, david-b@pacbell.net, xiphmont@gmail.com Content-Disposition: inline patch 9: Standardize/unify 'period' and 'interval' values on uFrame granularity instead of mixing frame and uframe depending on endpoint type (there's no reason to use urb->interval directly). Implement QH tree depth limit such that the lowest level of the QH tree can be smaller than the width of the full periodic schedule; useful for saving memory upon implementation of save-state FSTNs. Signed-off-by: Christopher "Monty" Montgomery Cc: David Brownell Signed-off-by: Greg Kroah-Hartman --- --- drivers/usb/host/ehci-q.c | 16 ++++---- drivers/usb/host/ehci-sched.c | 80 ++++++++++++++++++++++-------------------- drivers/usb/host/ehci.h | 8 +++- 3 files changed, 58 insertions(+), 46 deletions(-) --- gregkh-2.6.orig/drivers/usb/host/ehci-q.c +++ gregkh-2.6/drivers/usb/host/ehci-q.c @@ -667,17 +667,19 @@ qh_make ( if (urb->dev->speed == USB_SPEED_HIGH) { qh->c_usecs = 0; qh->gap_uf = 0; + qh->period = urb->interval; /* uFrame */ - qh->period = urb->interval >> 3; - if (qh->period == 0 && urb->interval != 1) { + /* XXX: remove once new budgeting code */ + if (urb->interval<8 && urb->interval != 1) { /* NOTE interval 2 or 4 uframes could work. * But interval 1 scheduling is simpler, and * includes high bandwidth. */ - dbg ("intr period %d uframes, NYET!", - urb->interval); - goto done; - } + dbg ("intr period %d uframes, NYET!", + urb->interval); + goto done; + } + } else { struct usb_tt *tt = urb->dev->tt; int think_time; @@ -699,7 +701,7 @@ qh_make ( qh->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time (urb->dev->speed, is_input, 0, max_packet (maxp))); - qh->period = urb->interval; + qh->period = urb->interval << 3; /* uFrame */ } } --- gregkh-2.6.orig/drivers/usb/host/ehci-sched.c +++ gregkh-2.6/drivers/usb/host/ehci-sched.c @@ -96,6 +96,19 @@ periodic_find_entry(struct ehci_hcd *ehc return here; } +/* covert a QH's period [expressed in uFrames] to tree level. + * + * @period: period (uFrames) of a QH + */ +static int _period_to_level(int period) +{ + if(period<8)return 1; + if(period > PERIODIC_QH_MAX_PERIOD){ + return (PERIODIC_QH_MAX_PERIOD >> 3); + } + return period>>3; +} + /* how many of the uframe's 125 usecs are allocated? */ static unsigned short periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) @@ -596,9 +609,8 @@ static void periodic_qh_deschedule(struc periodic_qh_unlink_frame (ehci, i, qh); /* update per-qh bandwidth for usbfs */ - ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); + ehci_to_hcd(ehci)->self.bandwidth_allocated -= + (qh->usecs + qh->c_usecs) / qh->period; dev_dbg (&qh->dev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", @@ -642,17 +654,13 @@ static void periodic_qh_deschedule(struc static int periodic_qh_link (struct ehci_hcd *ehci, struct ehci_qh *qh) { unsigned i; - unsigned period = qh->period; + unsigned period = _period_to_level(qh->period); dev_dbg (&qh->dev->dev, "link qh%d-%04x/%p start %d [%d/%d us]\n", period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); - /* high bandwidth, or otherwise every microframe */ - if (period == 0) - period = 1; - for (i = qh->start; i < ehci->periodic_size; i += period) { union ehci_shadow *prev = &ehci->pshadow [i]; __le32 *hw_p = &ehci->periodic [i]; @@ -673,7 +681,7 @@ static int periodic_qh_link (struct ehci * enables sharing interior tree nodes */ while (here.ptr && qh != here.qh) { - if (qh->period > here.qh->period) + if (period > _period_to_level(here.qh->period)) break; prev = &here.qh->qh_next; hw_p = &here.qh->hw_next; @@ -693,9 +701,8 @@ static int periodic_qh_link (struct ehci qh_get (qh); /* update per-qh bandwidth for usbfs */ - ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); + ehci_to_hcd(ehci)->self.bandwidth_allocated += + (qh->usecs + qh->c_usecs) / qh->period; /* maybe enable periodic schedule processing */ return enable_periodic (ehci); @@ -711,6 +718,7 @@ static int check_period ( unsigned usecs ) { int claimed; + int level = _period_to_level(period); /* complete split running into next frame? * given FSTN support, we could sometimes check... @@ -725,9 +733,9 @@ static int check_period ( usecs = 100 - usecs; /* we "know" 2 and 4 uframe intervals were rejected; so - * for period 0, check _every_ microframe in the schedule. + * for period 1, check _every_ microframe in the schedule. */ - if (unlikely (period == 0)) { + if (unlikely (period == 1)) { do { for (uframe = 0; uframe < 7; uframe++) { claimed = periodic_usecs (ehci, frame, uframe); @@ -742,7 +750,7 @@ static int check_period ( claimed = periodic_usecs (ehci, frame, uframe); if (claimed > usecs) return 0; - } while ((frame += period) < ehci->periodic_size); + } while ((frame += level) < ehci->periodic_size); } // success! @@ -772,8 +780,8 @@ static int check_intr_schedule ( } #ifdef CONFIG_USB_EHCI_TT_NEWSCHED - if (tt_available (ehci, qh->period, qh->dev, frame, uframe, - qh->tt_usecs)) { + if (tt_available (ehci, _period_to_level(qh->period), + qh->dev, frame, uframe, qh->tt_usecs)) { unsigned i; /* TODO : this may need FSTN for SSPLIT in uframe 5. */ @@ -820,13 +828,14 @@ static int periodic_qh_schedule (struct unsigned uframe; __le32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + int period = _period_to_level(qh->period); qh_refresh(ehci, qh); qh->hw_next = EHCI_LIST_END; frame = qh->start; /* reuse the previous schedule slots, if we can */ - if (frame < qh->period) { + if (frame < period) { uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); @@ -841,8 +850,8 @@ static int periodic_qh_schedule (struct */ if (status) { /* "normal" case, uframing flexible except with splits */ - if (qh->period) { - frame = qh->period - 1; + if (qh->period > 1) { + frame = period - 1; do { for (uframe = 0; uframe < 8; uframe++) { status = check_intr_schedule (ehci, @@ -853,7 +862,7 @@ static int periodic_qh_schedule (struct } } while (status && frame--); - /* qh->period == 0 means every uframe */ + /* qh->period == 1 means every uframe */ } else { frame = 0; status = check_intr_schedule (ehci, 0, 0, qh, &c_mask); @@ -864,7 +873,7 @@ static int periodic_qh_schedule (struct /* reset S-frame and (maybe) C-frame masks */ qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); - qh->hw_info2 |= qh->period + qh->hw_info2 |= (qh->period>1) ? cpu_to_le32 (1 << uframe) : __constant_cpu_to_le32 (QH_SMASK); qh->hw_info2 |= c_mask; @@ -1048,6 +1057,7 @@ iso_stream_init ( * when transfers on this endpoint are scheduled ... */ stream->usecs = HS_USECS_ISO (maxp); + stream->interval = interval; bandwidth = stream->usecs * 8; bandwidth /= 1 << (interval - 1); @@ -1086,14 +1096,12 @@ iso_stream_init ( /* stream->splits gets created from raw_mask later */ stream->address = cpu_to_le32 (addr); + stream->interval = interval<<3; } - stream->bandwidth = bandwidth; + stream->bandwidth = bandwidth; stream->udev = dev; - stream->bEndpointAddress = is_input | epnum; - stream->interval = interval; - stream->maxp = maxp; } /* iso_stream_put - decrement refcount of passed in ehci_iso_stream @@ -1286,14 +1294,15 @@ sitd_slot_ok ( /* The tt's fullspeed bus bandwidth must be available. * tt_available scheduling guarantees 10+% for control/bulk. */ - if (!tt_available (ehci, period_uframes << 3, + /* period_uframes << 3 had always been wrong */ + if (!tt_available (ehci, period_uframes >> 3, stream->udev, frame, uf, stream->tt_usecs)) return 0; #else /* tt must be idle for start(s), any gap, and csplit. * assume scheduling slop leaves 10+% for control/bulk. */ - if (!tt_no_collision (ehci, period_uframes << 3, + if (!tt_no_collision (ehci, period_uframes >> 3, stream->udev, frame, mask)) return 0; #endif @@ -1413,10 +1422,7 @@ static int iso_stream_schedule ( stream->next_uframe = start; /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ - - period = urb->interval; - if (!stream->highspeed) - period <<= 3; + period = stream->interval; /* find a uframe slot with enough bandwidth */ for (; start < (stream->next_uframe + period); start++) { @@ -2129,8 +2135,8 @@ sitd_link_urb ( for (packet = 0; packet < urb->number_of_packets; packet++) { sitd_link (ehci, urb, ((next_uframe & (mod-1)) >> 3), stream, sched, packet); - next_uframe += stream->interval << 3; - stream->depth += stream->interval << 3; + next_uframe += stream->interval; + stream->depth += stream->interval; } stream->next_uframe = next_uframe % mod; @@ -2192,7 +2198,7 @@ sitd_complete ( sitd->urb = NULL; sitd->stream = NULL; list_move (&sitd->sitd_list, &stream->free_list); - stream->depth -= stream->interval << 3; + stream->depth -= stream->interval; iso_stream_put (ehci, stream); /* handle completion now? */ @@ -2245,9 +2251,9 @@ static int sitd_submit (struct ehci_hcd ehci_dbg (ehci, "can't get iso stream\n"); return -ENOMEM; } - if (urb->interval != stream->interval) { + if (urb->interval != ((stream->interval+7)>>3)) { ehci_dbg (ehci, "can't change iso interval %d --> %d\n", - stream->interval, urb->interval); + ((stream->interval+7)>>3), urb->interval); goto done; } --- gregkh-2.6.orig/drivers/usb/host/ehci.h +++ gregkh-2.6/drivers/usb/host/ehci.h @@ -385,6 +385,11 @@ union ehci_shadow { * These appear in both the async and (for interrupt) periodic schedules. */ +/* as per ehci spec guidelines, replicate QH tree linkage of a maximum + size that is less than the full periodic schedule size. This is + also necessary to limit memory usage of FSTN support. */ +#define PERIODIC_QH_MAX_PERIOD 256 // 32 frames * 8 uFrames/Frame + struct ehci_qh { /* first part defined by EHCI spec */ __le32 hw_next; /* see EHCI 3.6.1 */ @@ -485,10 +490,9 @@ struct ehci_iso_stream { * trusting urb->interval == f(epdesc->bInterval) and * including the extra info for hw_bufp[0..2] */ - u8 interval; + u16 interval; u8 usecs, c_usecs; u16 tt_usecs; - u16 maxp; u16 raw_mask; unsigned bandwidth;