Commit b35c5009 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: create per-TT bandwidth tables

This patch continues the scheduling changes in ehci-hcd by adding a
table to store the bandwidth allocation below each TT.  This will
speed up the scheduling code, as it will no longer need to read
through the entire schedule to compute the bandwidth currently in use.

Properly speaking, the FS/LS budget calculations should be done in
terms of full-speed bytes per microframe, as described in the USB-2
spec.  However the driver currently uses microseconds per microframe,
and the scheduling code isn't robust enough at this point to change
over.  For the time being, we leave the calculations as they are.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ca1ad0ff
...@@ -536,10 +536,14 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) ...@@ -536,10 +536,14 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf) static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
{ {
struct ehci_hcd *ehci; struct ehci_hcd *ehci;
struct ehci_tt *tt;
struct ehci_per_sched *ps;
unsigned temp, size; unsigned temp, size;
char *next; char *next;
unsigned i; unsigned i;
u8 *bw; u8 *bw;
u16 *bf;
u8 budget[EHCI_BANDWIDTH_SIZE];
ehci = hcd_to_ehci(bus_to_hcd(buf->bus)); ehci = hcd_to_ehci(bus_to_hcd(buf->bus));
next = buf->output_buf; next = buf->output_buf;
...@@ -563,6 +567,50 @@ static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf) ...@@ -563,6 +567,50 @@ static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
size -= temp; size -= temp;
next += temp; next += temp;
} }
/* Dump all the FS/LS tables */
list_for_each_entry(tt, &ehci->tt_list, tt_list) {
temp = scnprintf(next, size,
"\nTT %s port %d FS/LS bandwidth allocation (us per frame)\n",
dev_name(&tt->usb_tt->hub->dev),
tt->tt_port + !!tt->usb_tt->multi);
size -= temp;
next += temp;
bf = tt->bandwidth;
temp = scnprintf(next, size,
" %5u%5u%5u%5u%5u%5u%5u%5u\n",
bf[0], bf[1], bf[2], bf[3],
bf[4], bf[5], bf[6], bf[7]);
size -= temp;
next += temp;
temp = scnprintf(next, size,
"FS/LS budget (us per microframe)\n");
size -= temp;
next += temp;
compute_tt_budget(budget, tt);
for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
bw = &budget[i];
temp = scnprintf(next, size,
"%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
i, bw[0], bw[1], bw[2], bw[3],
bw[4], bw[5], bw[6], bw[7]);
size -= temp;
next += temp;
}
list_for_each_entry(ps, &tt->ps_list, ps_list) {
temp = scnprintf(next, size,
"%s ep %02x: %4u @ %2u.%u+%u mask %04x\n",
dev_name(&ps->udev->dev),
ps->ep->desc.bEndpointAddress,
ps->tt_usecs,
ps->bw_phase, ps->phase_uf,
ps->bw_period, ps->cs_mask);
size -= temp;
next += temp;
}
}
spin_unlock_irq(&ehci->lock); spin_unlock_irq(&ehci->lock);
return next - buf->output_buf; return next - buf->output_buf;
......
...@@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); ...@@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
#include "ehci.h" #include "ehci.h"
#include "pci-quirks.h" #include "pci-quirks.h"
static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
struct ehci_tt *tt);
/* /*
* The MosChip MCS9990 controller updates its microframe counter * The MosChip MCS9990 controller updates its microframe counter
* a little before the frame counter, and occasionally we will read * a little before the frame counter, and occasionally we will read
...@@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd) ...@@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
INIT_LIST_HEAD(&ehci->intr_qh_list); INIT_LIST_HEAD(&ehci->intr_qh_list);
INIT_LIST_HEAD(&ehci->cached_itd_list); INIT_LIST_HEAD(&ehci->cached_itd_list);
INIT_LIST_HEAD(&ehci->cached_sitd_list); INIT_LIST_HEAD(&ehci->cached_sitd_list);
INIT_LIST_HEAD(&ehci->tt_list);
if (HCC_PGM_FRAMELISTLEN(hcc_params)) { if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
/* periodic schedule size can be smaller than default */ /* periodic schedule size can be smaller than default */
...@@ -1051,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd) ...@@ -1051,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* Device addition and removal */
static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
spin_lock_irq(&ehci->lock);
drop_tt(udev);
spin_unlock_irq(&ehci->lock);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* suspend/resume, section 4.3 */ /* suspend/resume, section 4.3 */
...@@ -1194,6 +1211,11 @@ static const struct hc_driver ehci_hc_driver = { ...@@ -1194,6 +1211,11 @@ static const struct hc_driver ehci_hc_driver = {
.bus_resume = ehci_bus_resume, .bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port, .relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over, .port_handed_over = ehci_port_handed_over,
/*
* device support
*/
.free_dev = ehci_remove_device,
}; };
void ehci_init_driver(struct hc_driver *drv, void ehci_init_driver(struct hc_driver *drv,
......
This diff is collapsed.
...@@ -61,6 +61,7 @@ struct ehci_stats { ...@@ -61,6 +61,7 @@ struct ehci_stats {
struct ehci_per_sched { struct ehci_per_sched {
struct usb_device *udev; /* access to the TT */ struct usb_device *udev; /* access to the TT */
struct usb_host_endpoint *ep; struct usb_host_endpoint *ep;
struct list_head ps_list; /* node on ehci_tt's ps_list */
u16 tt_usecs; /* time on the FS/LS bus */ u16 tt_usecs; /* time on the FS/LS bus */
u16 cs_mask; /* C-mask and S-mask bytes */ u16 cs_mask; /* C-mask and S-mask bytes */
u16 period; /* actual period in frames */ u16 period; /* actual period in frames */
...@@ -256,6 +257,9 @@ struct ehci_hcd { /* one per controller */ ...@@ -256,6 +257,9 @@ struct ehci_hcd { /* one per controller */
#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3) #define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3)
u8 bandwidth[EHCI_BANDWIDTH_SIZE]; u8 bandwidth[EHCI_BANDWIDTH_SIZE];
/* us allocated per uframe */ /* us allocated per uframe */
u8 tt_budget[EHCI_BANDWIDTH_SIZE];
/* us budgeted per uframe */
struct list_head tt_list;
/* platform-specific data -- must come last */ /* platform-specific data -- must come last */
unsigned long priv[0] __aligned(sizeof(s64)); unsigned long priv[0] __aligned(sizeof(s64));
...@@ -595,6 +599,35 @@ struct ehci_fstn { ...@@ -595,6 +599,35 @@ struct ehci_fstn {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/*
* USB-2.0 Specification Sections 11.14 and 11.18
* Scheduling and budgeting split transactions using TTs
*
* A hub can have a single TT for all its ports, or multiple TTs (one for each
* port). The bandwidth and budgeting information for the full/low-speed bus
* below each TT is self-contained and independent of the other TTs or the
* high-speed bus.
*
* "Bandwidth" refers to the number of microseconds on the FS/LS bus allocated
* to an interrupt or isochronous endpoint for each frame. "Budget" refers to
* the best-case estimate of the number of full-speed bytes allocated to an
* endpoint for each microframe within an allocated frame.
*
* Removal of an endpoint invalidates a TT's budget. Instead of trying to
* keep an up-to-date record, we recompute the budget when it is needed.
*/
struct ehci_tt {
u16 bandwidth[EHCI_BANDWIDTH_FRAMES];
struct list_head tt_list; /* List of all ehci_tt's */
struct list_head ps_list; /* Items using this TT */
struct usb_tt *usb_tt;
int tt_port; /* TT port number */
};
/*-------------------------------------------------------------------------*/
/* Prepare the PORTSC wakeup flags during controller suspend/resume */ /* Prepare the PORTSC wakeup flags during controller suspend/resume */
#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \ #define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment