[PATCH 1/3] ehci-hcd: complete ISO URBs sooner

Karsten Wiese <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
This patch lets some ISO URBs complete sooner, by scanning ITDs up
to the current microframe and then reporting completions immediately.

Previously some completions for high speed transfers wouldn't be reported
right away, because the code waited until a complete frame was scanned and
such a scan might not happen immediately.  Soundcard drivers have used a
partial work around for this, transferring ISO urbs using entire frames.

itd_complete() is split into 3 functions:
itd_scan() updates the urb's iso_frame_desc and returns a bitfield
indicating if the ITD's frame has elapsed (uframe % 8 == 0) or the urb can
be completed.
itd_complete() completes the urb and sets itd->urb = NULL.
itd_recycle() puts the ITD on the freelist.
If itd->urb == NULL, it also updates ehci->periodic_sched and appropiately
calls disable_periodic(ehci).

scan_periodic() is changed for "case Q_TYPE_ITD:" to always call
itd_scan() and itd_complete() or itd_recycle() based on the value itd_scan()
returned.

Signed-off-by: Karsten Wiese <[email protected]>
---
 drivers/usb/host/ehci-sched.c |  133 ++++++++++++++++++++++++-----------------
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 80 insertions(+), 54 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 80d99bc..2d4fda5 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1563,25 +1563,29 @@ itd_link_urb (
 	return 0;
 }
 
+/* mask for return values used by itd_scan() and sitd_scan() */
+#define XITD_UNLINK	(1 << 0)	/* needs unlink from hw schedule */
+#define URB_COMPLETE	(1 << 1)	/* schedule may have new entries */
+
 #define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
 
 static unsigned
-itd_complete (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd
-) {
+itd_scan(struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned uframe_after)
+{
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
 	unsigned				uframe;
 	int					urb_index = -1;
 	struct ehci_iso_stream			*stream = itd->stream;
-	struct usb_device			*dev;
+	unsigned				complete = 0;
 
 	/* for each uframe with a packet */
-	for (uframe = 0; uframe < 8; uframe++) {
+	for (uframe = itd->uframe_scan; uframe < uframe_after; uframe++) {
+
 		if (likely (itd->index[uframe] == -1))
 			continue;
+
 		urb_index = itd->index[uframe];
 		desc = &urb->iso_frame_desc [urb_index];
 
@@ -1607,46 +1611,75 @@ itd_complete (
 		} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
 			desc->status = 0;
 			desc->actual_length = EHCI_ITD_LENGTH (t);
+		} else {
+			/* SHOULD NOT HAPPEN!! */
+			WARN_ON(1);
 		}
+
+		/* handle completion now? */
+		if (urb_index + 1 != urb->number_of_packets)
+			continue;
+
+#if 1
+		/* ASSERT: it's really the last itd for this urb */
+		{
+			struct ehci_itd	*tmp;
+
+			list_for_each_entry(tmp, &stream->td_list, itd_list)
+				BUG_ON(tmp != itd && tmp->urb == urb);
+		}
+#endif
+
+		urb = NULL;
+		complete = URB_COMPLETE;
 	}
 
-	usb_put_urb (urb);
-	itd->urb = NULL;
-	itd->stream = NULL;
-	list_move (&itd->itd_list, &stream->free_list);
-	iso_stream_put (ehci, stream);
+	if (uframe < 8) {
+		itd->uframe_scan = uframe;
+		return complete;
+	}
 
-	/* handle completion now? */
-	if (likely ((urb_index + 1) != urb->number_of_packets))
-		return 0;
+	return complete | XITD_UNLINK;
+}
 
-	/* ASSERT: it's really the last itd for this urb
-	list_for_each_entry (itd, &stream->td_list, itd_list)
-		BUG_ON (itd->urb == urb);
-	 */
+/* give urb back to the driver ... can be out-of-order */
+static void
+itd_complete(struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+	usb_put_urb(itd->urb);
 
-	/* give urb back to the driver ... can be out-of-order */
-	dev = urb->dev;
-	ehci_urb_done(ehci, urb, 0);
-	urb = NULL;
+	ehci_urb_done(ehci, itd->urb, 0);
+	itd->urb = NULL;
 
-	/* defer stopping schedule; completion can submit */
-	ehci->periodic_sched--;
-	if (unlikely (!ehci->periodic_sched))
-		(void) disable_periodic (ehci);
 	ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+	iso_stream_put(ehci, itd->stream);
+}
 
-	if (unlikely (list_empty (&stream->td_list))) {
+static void
+itd_recycle(struct ehci_hcd *ehci, struct ehci_itd *itd)
+{
+	struct ehci_iso_stream	*stream = itd->stream;
+
+	usb_put_urb(itd->urb);
+	if (unlikely(!itd->urb)) {
+		/* deferred schedule disable; completion often submits */
+		ehci->periodic_sched--;
+		if (unlikely(!ehci->periodic_sched))
+			disable_periodic(ehci);
+	}
+	itd->stream = NULL;
+	list_move(&itd->itd_list, &stream->free_list);
+	if (unlikely(list_empty(&stream->td_list))) {
 		ehci_to_hcd(ehci)->self.bandwidth_allocated
 				-= stream->bandwidth;
-		ehci_vdbg (ehci,
-			"deschedule devp %s ep%d%s-iso\n",
-			dev->devpath, stream->bEndpointAddress & 0x0f,
-			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+		ehci_vdbg(ehci,
+			  "deschedule devp %s ep%d%s-iso\n",
+			  stream->udev->devpath,
+			  stream->bEndpointAddress & 0x0f,
+			  (stream->bEndpointAddress & USB_DIR_IN)
+			  ?  "in" : "out");
 	}
 	iso_stream_put (ehci, stream);
-
-	return 1;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2148,7 +2181,6 @@ restart:
 		modified = 0;
 
 		while (q.ptr != NULL) {
-			unsigned		uf;
 			union ehci_shadow	temp;
 			int			live;
 
@@ -2175,30 +2207,23 @@ restart:
 				q = q.fstn->fstn_next;
 				break;
 			case Q_TYPE_ITD:
-				/* skip itds for later in the frame */
 				rmb ();
-				for (uf = live ? uframes : 8; uf < 8; uf++) {
-					if (0 == (q.itd->hw_transaction [uf]
-							& ITD_ACTIVE(ehci)))
-						continue;
+				type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
+				modified = itd_scan(ehci, q.itd,
+						    live ? uframes : 8);
+				if (modified & URB_COMPLETE)
+					itd_complete(ehci, q.itd);
+
+				if (modified & XITD_UNLINK) {
+					modified &= ~XITD_UNLINK;
+					*q_p = q.itd->itd_next;
+					*hw_p = q.itd->hw_next;
+					wmb();
+					itd_recycle(ehci, q.itd);
+				} else {
 					q_p = &q.itd->itd_next;
 					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE(ehci,
-							q.itd->hw_next);
-					q = *q_p;
-					break;
 				}
-				if (uf != 8)
-					break;
-
-				/* this one's ready ... HC won't cache the
-				 * pointer for much longer, if at all.
-				 */
-				*q_p = q.itd->itd_next;
-				*hw_p = q.itd->hw_next;
-				type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
-				wmb();
-				modified = itd_complete (ehci, q.itd);
 				q = *q_p;
 				break;
 			case Q_TYPE_SITD:
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 951d69f..dcd031a 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -581,6 +581,7 @@ struct ehci_itd {
 	struct urb		*urb;
 	struct ehci_iso_stream	*stream;	/* endpoint's queue */
 	struct list_head	itd_list;	/* list of stream's itds */
+	unsigned		uframe_scan;
 
 	/* any/all hw_transactions here may be used by that urb */
 	unsigned		frame;		/* where scheduled */
-- 
1.5.3.3


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.