Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dccf4a48 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

[PATCH] UHCI: use one QH per endpoint, not per URB



This patch (as623) changes the uhci-hcd driver to make it use one QH per
device endpoint, instead of a QH per URB as it does now.  Numerous areas
of the code are affected by this.  For example, the distinction between
"queued" URBs and non-"queued" URBs no longer exists; all URBs belong to
a queue and some just happen to be at the queue's head.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 499003e8
Loading
Loading
Loading
Loading
+92 −228
Original line number Diff line number Diff line
@@ -90,13 +90,60 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space)
	return out - buf;
}

static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
{
	char *out = buf;
	struct urb_priv *urbp;
	struct list_head *head, *tmp;
	struct uhci_td *td;
	int i = 0, checked = 0, prevactive = 0;
	int i, nactive, ninactive;

	if (len < 200)
		return 0;

	out += sprintf(out, "urb_priv [%p] ", urbp);
	out += sprintf(out, "urb [%p] ", urbp->urb);
	out += sprintf(out, "qh [%p] ", urbp->qh);
	out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
	out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe),
			(usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));

	switch (usb_pipetype(urbp->urb->pipe)) {
	case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO"); break;
	case PIPE_INTERRUPT: out += sprintf(out, "INT"); break;
	case PIPE_BULK: out += sprintf(out, "BLK"); break;
	case PIPE_CONTROL: out += sprintf(out, "CTL"); break;
	}

	out += sprintf(out, "%s", (urbp->fsbr ? " FSBR" : ""));
	out += sprintf(out, "%s", (urbp->fsbr_timeout ? " FSBR_TO" : ""));

	if (urbp->urb->status != -EINPROGRESS)
		out += sprintf(out, " Status=%d", urbp->urb->status);
	out += sprintf(out, "\n");

	i = nactive = ninactive = 0;
	list_for_each_entry(td, &urbp->td_list, list) {
		if (++i <= 10 || debug > 2) {
			out += sprintf(out, "%*s%d: ", space + 2, "", i);
			out += uhci_show_td(td, out, len - (out - buf), 0);
		} else {
			if (td_status(td) & TD_CTRL_ACTIVE)
				++nactive;
			else
				++ninactive;
		}
	}
	if (nactive + ninactive > 0)
		out += sprintf(out, "%*s[skipped %d inactive and %d active "
				"TDs]\n",
				space, "", ninactive, nactive);

	return out - buf;
}

static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
{
	char *out = buf;
	int i, nurbs;
	__le32 element = qh_element(qh);

	/* Try to make sure there's enough memory */
@@ -118,86 +165,36 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space)
	if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH)))
		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");

	if (!qh->urbp) {
		out += sprintf(out, "%*s  urbp == NULL\n", space, "");
		goto out;
	}

	urbp = qh->urbp;

	head = &urbp->td_list;
	tmp = head->next;

	td = list_entry(tmp, struct uhci_td, list);
	if (list_empty(&qh->queue)) {
		out += sprintf(out, "%*s  queue is empty\n", space, "");
	} else {
		struct urb_priv *urbp = list_entry(qh->queue.next,
				struct urb_priv, node);
		struct uhci_td *td = list_entry(urbp->td_list.next,
				struct uhci_td, list);

		if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS))
		out += sprintf(out, "%*s Element != First TD\n", space, "");

	while (tmp != head) {
		struct uhci_td *td = list_entry(tmp, struct uhci_td, list);

		tmp = tmp->next;

		out += sprintf(out, "%*s%d: ", space + 2, "", i++);
		out += uhci_show_td(td, out, len - (out - buf), 0);

		if (i > 10 && !checked && prevactive && tmp != head &&
		    debug <= 2) {
			struct list_head *ntmp = tmp;
			struct uhci_td *ntd = td;
			int active = 1, ni = i;

			checked = 1;

			while (ntmp != head && ntmp->next != head && active) {
				ntd = list_entry(ntmp, struct uhci_td, list);

				ntmp = ntmp->next;

				active = td_status(ntd) & TD_CTRL_ACTIVE;

				ni++;
			}

			if (active && ni > i) {
				out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i);
				tmp = ntmp;
				td = ntd;
				i = ni;
			}
			out += sprintf(out, "%*s Element != First TD\n",
					space, "");
		i = nurbs = 0;
		list_for_each_entry(urbp, &qh->queue, node) {
			if (++i <= 10)
				out += uhci_show_urbp(urbp, out,
						len - (out - buf), space + 2);
			else
				++nurbs;
		}

		prevactive = td_status(td) & TD_CTRL_ACTIVE;
		if (nurbs > 0)
			out += sprintf(out, "%*s Skipped %d URBs\n",
					space, "", nurbs);
	}

	if (list_empty(&urbp->queue_list) || urbp->queued)
		goto out;

	out += sprintf(out, "%*sQueued QHs:\n", -space, "--");

	head = &urbp->queue_list;
	tmp = head->next;

	while (tmp != head) {
		struct urb_priv *nurbp = list_entry(tmp, struct urb_priv,
						queue_list);
		tmp = tmp->next;

		out += uhci_show_qh(nurbp->qh, out, len - (out - buf), space);
	}

out:
	return out - buf;
}

#define show_frame_num()	\
	if (!shown) {		\
	  shown = 1;		\
	  out += sprintf(out, "- Frame %d\n", i); \
	}

#ifdef CONFIG_PROC_FS
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",
@@ -206,12 +203,6 @@ static const char * const qh_names[] = {
  "skel_bulk_qh", "skel_term_qh"
};

#define show_qh_name()		\
	if (!shown) {		\
	  shown = 1;		\
	  out += sprintf(out, "- %s\n", qh_names[i]); \
	}

static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
{
	char *out = buf;
@@ -321,139 +312,29 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
	return out - buf;
}

static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *buf, int len)
{
	struct list_head *tmp;
	char *out = buf;
	int count = 0;

	if (len < 200)
		return 0;

	out += sprintf(out, "urb_priv [%p] ", urbp);
	out += sprintf(out, "urb [%p] ", urbp->urb);
	out += sprintf(out, "qh [%p] ", urbp->qh);
	out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
	out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe), (usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));

	switch (usb_pipetype(urbp->urb->pipe)) {
	case PIPE_ISOCHRONOUS: out += sprintf(out, "ISO "); break;
	case PIPE_INTERRUPT: out += sprintf(out, "INT "); break;
	case PIPE_BULK: out += sprintf(out, "BLK "); break;
	case PIPE_CONTROL: out += sprintf(out, "CTL "); break;
	}

	out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : ""));
	out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : ""));

	if (urbp->urb->status != -EINPROGRESS)
		out += sprintf(out, "Status=%d ", urbp->urb->status);
	//out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);

	count = 0;
	list_for_each(tmp, &urbp->td_list)
		count++;
	out += sprintf(out, "TDs=%d ",count);

	if (urbp->queued)
		out += sprintf(out, "queued\n");
	else {
		count = 0;
		list_for_each(tmp, &urbp->queue_list)
			count++;
		out += sprintf(out, "queued URBs=%d\n", count);
	}

	return out - buf;
}

static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
{
	char *out = buf;
	struct list_head *head, *tmp;
	int count;

	out += sprintf(out, "Main list URBs:");
	if (list_empty(&uhci->urb_list))
		out += sprintf(out, " Empty\n");
	else {
		out += sprintf(out, "\n");
		count = 0;
		head = &uhci->urb_list;
		tmp = head->next;
		while (tmp != head) {
			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);

			out += sprintf(out, "  %d: ", ++count);
			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
			tmp = tmp->next;
		}
	}

	out += sprintf(out, "Remove list URBs:");
	if (list_empty(&uhci->urb_remove_list))
		out += sprintf(out, " Empty\n");
	else {
		out += sprintf(out, "\n");
		count = 0;
		head = &uhci->urb_remove_list;
		tmp = head->next;
		while (tmp != head) {
			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);

			out += sprintf(out, "  %d: ", ++count);
			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
			tmp = tmp->next;
		}
	}

	out += sprintf(out, "Complete list URBs:");
	if (list_empty(&uhci->complete_list))
		out += sprintf(out, " Empty\n");
	else {
		out += sprintf(out, "\n");
		count = 0;
		head = &uhci->complete_list;
		tmp = head->next;
		while (tmp != head) {
			struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);

			out += sprintf(out, "  %d: ", ++count);
			out += uhci_show_urbp(uhci, urbp, out, len - (out - buf));
			tmp = tmp->next;
		}
	}

	return out - buf;
}

static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
{
	unsigned long flags;
	char *out = buf;
	int i, j;
	struct uhci_qh *qh;
	struct uhci_td *td;
	struct list_head *tmp, *head;

	spin_lock_irqsave(&uhci->lock, flags);

	out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
	out += sprintf(out, "HC status\n");
	out += uhci_show_status(uhci, out, len - (out - buf));
	if (debug <= 1)
		return out - buf;

	out += sprintf(out, "Frame List\n");
	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
		int shown = 0;
		td = uhci->frame_cpu[i];
		if (!td)
			continue;

		if (td->dma_handle != (dma_addr_t)uhci->frame[i]) {
			show_frame_num();
		out += sprintf(out, "- Frame %d\n", i); \
		if (td->dma_handle != (dma_addr_t)uhci->frame[i])
			out += sprintf(out, "    frame list does not match td->dma_handle!\n");
		}
		show_frame_num();

		head = &td->fl_list;
		tmp = head;
@@ -467,14 +348,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
	out += sprintf(out, "Skeleton QHs\n");

	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
		int shown = 0;
		int cnt = 0;

		qh = uhci->skelqh[i];

		if (debug > 1) {
			show_qh_name();
		out += sprintf(out, "- %s\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) {
@@ -487,44 +365,27 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
			continue;
		}

		j = (i < 7) ? 7 : i+1;		/* Next skeleton */
		if (list_empty(&qh->list)) {
			if (i < UHCI_NUM_SKELQH - 1) {
				if (qh->link !=
				    (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) {
					show_qh_name();
					out += sprintf(out, "    skeleton QH not linked to next skeleton QH!\n");
				}
			}

			continue;
		}

		show_qh_name();

		head = &qh->list;
		j = (i < 9) ? 9 : i+1;		/* Next skeleton */
		head = &qh->node;
		tmp = head->next;

		while (tmp != head) {
			qh = list_entry(tmp, struct uhci_qh, list);

			qh = list_entry(tmp, struct uhci_qh, node);
			tmp = tmp->next;

			out += uhci_show_qh(qh, out, len - (out - buf), 4);
			if (++cnt <= 10)
				out += uhci_show_qh(qh, out,
						len - (out - buf), 4);
		}
		if ((cnt -= 10) > 0)
			out += sprintf(out, "    Skipped %d QHs\n", cnt);

		if (i < UHCI_NUM_SKELQH - 1) {
		if (i > 1 && i < UHCI_NUM_SKELQH - 1) {
			if (qh->link !=
			    (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH))
				out += sprintf(out, "    last QH not linked to next skeleton!\n");
		}
	}

	if (debug > 2)
		out += uhci_show_lists(uhci, out, len - (out - buf));

	spin_unlock_irqrestore(&uhci->lock, flags);

	return out - buf;
}

@@ -541,6 +402,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
	struct uhci_hcd *uhci = inode->u.generic_ip;
	struct uhci_debug *up;
	int ret = -ENOMEM;
	unsigned long flags;

	lock_kernel();
	up = kmalloc(sizeof(*up), GFP_KERNEL);
@@ -553,7 +415,9 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
		goto out;
	}

	spin_lock_irqsave(&uhci->lock, flags);
	up->size = uhci_sprint_schedule(uhci, up->data, MAX_OUTPUT);
	spin_unlock_irqrestore(&uhci->lock, flags);

	file->private_data = up;

+41 −24
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@
/*
 * Version Information
 */
#define DRIVER_VERSION "v2.3"
#define DRIVER_VERSION "v3.0"
#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \
Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \
Alan Stern"
@@ -489,15 +489,11 @@ static int uhci_start(struct usb_hcd *hcd)
	uhci->fsbrtimeout = 0;

	spin_lock_init(&uhci->lock);
	INIT_LIST_HEAD(&uhci->qh_remove_list);

	INIT_LIST_HEAD(&uhci->td_remove_list);

	INIT_LIST_HEAD(&uhci->urb_remove_list);

	INIT_LIST_HEAD(&uhci->urb_list);

	INIT_LIST_HEAD(&uhci->complete_list);
	INIT_LIST_HEAD(&uhci->idle_qh_list);

	init_waitqueue_head(&uhci->waitqh);

@@ -540,7 +536,7 @@ static int uhci_start(struct usb_hcd *hcd)
	}

	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
		uhci->skelqh[i] = uhci_alloc_qh(uhci);
		uhci->skelqh[i] = uhci_alloc_qh(uhci, NULL, NULL);
		if (!uhci->skelqh[i]) {
			dev_err(uhci_dev(uhci), "unable to allocate QH\n");
			goto err_alloc_skelqh;
@@ -557,13 +553,17 @@ static int uhci_start(struct usb_hcd *hcd)
			uhci->skel_int16_qh->link =
			uhci->skel_int8_qh->link =
			uhci->skel_int4_qh->link =
			uhci->skel_int2_qh->link =
			cpu_to_le32(uhci->skel_int1_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_int1_qh->link = cpu_to_le32(uhci->skel_ls_control_qh->dma_handle) | UHCI_PTR_QH;

	uhci->skel_ls_control_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_fs_control_qh->link = cpu_to_le32(uhci->skel_bulk_qh->dma_handle) | UHCI_PTR_QH;
	uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH;
			uhci->skel_int2_qh->link = UHCI_PTR_QH |
			cpu_to_le32(uhci->skel_int1_qh->dma_handle);

	uhci->skel_int1_qh->link = UHCI_PTR_QH |
			cpu_to_le32(uhci->skel_ls_control_qh->dma_handle);
	uhci->skel_ls_control_qh->link = UHCI_PTR_QH |
			cpu_to_le32(uhci->skel_fs_control_qh->dma_handle);
	uhci->skel_fs_control_qh->link = UHCI_PTR_QH |
			cpu_to_le32(uhci->skel_bulk_qh->dma_handle);
	uhci->skel_bulk_qh->link = UHCI_PTR_QH |
			cpu_to_le32(uhci->skel_term_qh->dma_handle);

	/* This dummy TD is to work around a bug in Intel PIIX controllers */
	uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
@@ -589,15 +589,15 @@ static int uhci_start(struct usb_hcd *hcd)

		/*
		 * ffs (Find First bit Set) does exactly what we need:
		 * 1,3,5,...  => ffs = 0 => use skel_int2_qh = skelqh[6],
		 * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[5], etc.
		 * ffs > 6 => not on any high-period queue, so use
		 *	skel_int1_qh = skelqh[7].
		 * 1,3,5,...  => ffs = 0 => use skel_int2_qh = skelqh[8],
		 * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc.
		 * ffs >= 7 => not on any high-period queue, so use
		 *	skel_int1_qh = skelqh[9].
		 * Add UHCI_NUMFRAMES to insure at least one bit is set.
		 */
		irq = 6 - (int) __ffs(i + UHCI_NUMFRAMES);
		if (irq < 0)
			irq = 7;
		irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES);
		if (irq <= 1)
			irq = 9;

		/* Only place we don't use the frame list routines */
		uhci->frame[i] = UHCI_PTR_QH |
@@ -767,13 +767,30 @@ static int uhci_resume(struct usb_hcd *hcd)
}
#endif

/* Wait until all the URBs for a particular device/endpoint are gone */
/* Wait until a particular device/endpoint's QH is idle, and free it */
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
		struct usb_host_endpoint *ep)
		struct usb_host_endpoint *hep)
{
	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
	struct uhci_qh *qh;

	spin_lock_irq(&uhci->lock);
	qh = (struct uhci_qh *) hep->hcpriv;
	if (qh == NULL)
		goto done;

	wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
	while (qh->state != QH_STATE_IDLE) {
		++uhci->num_waiting;
		spin_unlock_irq(&uhci->lock);
		wait_event_interruptible(uhci->waitqh,
				qh->state == QH_STATE_IDLE);
		spin_lock_irq(&uhci->lock);
		--uhci->num_waiting;
	}

	uhci_free_qh(uhci, qh);
done:
	spin_unlock_irq(&uhci->lock);
}

static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)