UHCI: Eliminate asynchronous skeleton Queue Headers

This patch (as856) attempts to improve the performance of uhci-hcd by
removing the asynchronous skeleton Queue Headers.  They don't contain
any useful information but the controller has to read through them at
least once every millisecond, incurring a non-zero DMA overhead.

Now all the asynchronous queues are combined, along with the period-1
interrupt queue, into a single list with a single skeleton QH.  The
start of the low-speed control, full-speed control, and bulk sublists
is determined by linear search.  Since there should rarely be more
than a couple of QHs in the list, the searches should incur a much
smaller total load than keeping the skeleton QHs.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index a067713..8d24d3d 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -220,16 +220,6 @@
 	return out - buf;
 }
 
-static const char * const qh_names[] = {
-  "skel_unlink_qh", "skel_iso_qh",
-  "skel_int128_qh", "skel_int64_qh",
-  "skel_int32_qh", "skel_int16_qh",
-  "skel_int8_qh", "skel_int4_qh",
-  "skel_int2_qh", "skel_int1_qh",
-  "skel_ls_control_qh", "skel_fs_control_qh",
-  "skel_bulk_qh", "skel_term_qh"
-};
-
 static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
 {
 	char *out = buf;
@@ -352,6 +342,12 @@
 	struct uhci_td *td;
 	struct list_head *tmp, *head;
 	int nframes, nerrs;
+	__le32 link;
+
+	static const char * const qh_names[] = {
+		"unlink", "iso", "int128", "int64", "int32", "int16",
+		"int8", "int4", "int2", "async", "term"
+	};
 
 	out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
 	out += sprintf(out, "HC status\n");
@@ -374,7 +370,7 @@
 	nframes = 10;
 	nerrs = 0;
 	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
-		__le32 link, qh_dma;
+		__le32 qh_dma;
 
 		j = 0;
 		td = uhci->frame_cpu[i];
@@ -430,23 +426,21 @@
 
 	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
 		int cnt = 0;
+		__le32 fsbr_link = 0;
 
 		qh = uhci->skelqh[i];
-		out += sprintf(out, "- %s\n", qh_names[i]); \
+		out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \
 		out += uhci_show_qh(qh, out, len - (out - buf), 4);
 
 		/* Last QH is the Terminating QH, it's different */
-		if (i == UHCI_NUM_SKELQH - 1) {
-			if (qh->link != UHCI_PTR_TERM)
-				out += sprintf(out, "    bandwidth reclamation on!\n");
-
+		if (i == SKEL_TERM) {
 			if (qh_element(qh) != LINK_TO_TD(uhci->term_td))
 				out += sprintf(out, "    skel_term_qh element is not set to term_td!\n");
-
+			if (link == LINK_TO_QH(uhci->skel_term_qh))
+				goto check_qh_link;
 			continue;
 		}
 
-		j = (i < 9) ? 9 : i+1;		/* Next skeleton */
 		head = &qh->node;
 		tmp = head->next;
 
@@ -456,14 +450,26 @@
 			if (++cnt <= 10)
 				out += uhci_show_qh(qh, out,
 						len - (out - buf), 4);
+			if (!fsbr_link && qh->skel >= SKEL_FSBR)
+				fsbr_link = LINK_TO_QH(qh);
 		}
 		if ((cnt -= 10) > 0)
 			out += sprintf(out, "    Skipped %d QHs\n", cnt);
 
-		if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
-			if (qh->link != LINK_TO_QH(uhci->skelqh[j]))
-				out += sprintf(out, "    last QH not linked to next skeleton!\n");
-		}
+		link = UHCI_PTR_TERM;
+		if (i <= SKEL_ISO)
+			;
+		else if (i < SKEL_ASYNC)
+			link = LINK_TO_QH(uhci->skel_async_qh);
+		else if (!uhci->fsbr_is_on)
+			;
+		else if (fsbr_link)
+			link = fsbr_link;
+		else
+			link = LINK_TO_QH(uhci->skel_term_qh);
+check_qh_link:
+		if (qh->link != link)
+			out += sprintf(out, "    last QH not linked to next skeleton!\n");
 	}
 
 	return out - buf;