Re: [RFC 3/3] EHCI: handle late isochronous submissions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 26 Aug 2013 14:50:53 -0400 (EDT)
Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote:

> This patch does the real work.  It fixes up ehci-hcd so that an URB 
> submitted by a completion handler will keep the isochronous stream 
> alive, even if the handler was delayed by running in a tasklet and the 
> queue has emptied out.
> 
> As I mentioned earlier, this is not a simple change.
> 
> Alan Stern
> 
> 
> 
>  drivers/usb/host/ehci-sched.c |  164 +++++++++++++++++++++++++-----------------
>  drivers/usb/host/ehci.h       |    1 
>  2 files changed, 101 insertions(+), 64 deletions(-)

Below is the approach I proposed(mentioned in another thread), which should be
simper than this one, any comments?

 drivers/usb/host/ehci-sched.c |   53 ++++++++++++++++++++++++++++++++++++++---
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 83be03f..80ef95d 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1370,6 +1370,33 @@ sitd_slot_ok (
 	return 1;
 }
 
+/* rebase in case of underun without ISO_ASAP together */
+static int iso_stream_rebase(struct ehci_hcd *ehci, struct urb *urb,
+		struct ehci_iso_stream	*stream, u32 span, u32 period)
+{
+	u32 i, end;
+	u32 mod = ehci->periodic_size;
+
+	if (ehci->last_base == -1)
+		return 0;
+
+	end = ((stream->next_uframe + span - period) >> 3) & (mod - 1);
+	for (i = ehci->last_base; i != ehci->last_iso_frame;
+			i = (i + 1) & (mod - 1)) {
+		/* don't schedule URB which is behind base totally */
+		if (end == i) {
+			for (i = 0; i < urb->number_of_packets; i++) {
+				urb->iso_frame_desc[i].length = 0;
+				urb->iso_frame_desc[i].status = 0;
+			}
+			return 1;
+		}
+		if (((stream->next_uframe >> 3) & (mod - 1)) == i)
+			ehci->last_iso_frame = i;
+	}
+	return 0;
+}
+
 /*
  * This scheduler plans almost as far into the future as it has actual
  * periodic schedule slots.  (Affected by TUNE_FLS, which defaults to
@@ -1409,7 +1436,9 @@ iso_stream_schedule (
 	 * (irq delays etc).  If there are, the behavior depends on
 	 * whether URB_ISO_ASAP is set.
 	 */
-	if (likely (!list_empty (&stream->td_list))) {
+	if (likely (!list_empty (&stream->td_list) ||
+				hcd_complete_in_progress(
+				ehci_to_hcd(ehci), urb))) {
 
 		/* Take the isochronous scheduling threshold into account */
 		if (ehci->i_thresh)
@@ -1417,6 +1446,14 @@ iso_stream_schedule (
 		else
 			next = (now + 2 + 7) & ~0x07;	/* full frame cache */
 
+		if (list_empty(&stream->td_list) &&
+				!(urb->transfer_flags & URB_ISO_ASAP))
+			if (iso_stream_rebase(ehci, urb, stream, span,
+					period)) {
+				status = 1;
+				goto fail;
+			}
+
 		/*
 		 * Use ehci->last_iso_frame as the base.  There can't be any
 		 * TDs scheduled for earlier than that.
@@ -1517,6 +1554,7 @@ iso_stream_schedule (
 	/* Make sure scan_isoc() sees these */
 	if (ehci->isoc_count == 0)
 		ehci->last_iso_frame = now >> 3;
+	ehci->last_base = -1;
 	return 0;
 
  fail:
@@ -1838,7 +1876,10 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
 	status = iso_stream_schedule(ehci, urb, stream);
 	if (likely (status == 0))
 		itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-	else
+	else if (status > 0) {
+		ehci_urb_done(ehci, urb, 0);
+		status = 0;
+	} else
 		usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
  done_not_linked:
 	spin_unlock_irqrestore (&ehci->lock, flags);
@@ -2224,7 +2265,10 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
 	status = iso_stream_schedule(ehci, urb, stream);
 	if (status == 0)
 		sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-	else
+	else if (status > 0) {
+		ehci_urb_done(ehci, urb, 0);
+		status = 0;
+	} else
 		usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
  done_not_linked:
 	spin_unlock_irqrestore (&ehci->lock, flags);
@@ -2255,6 +2299,9 @@ static void scan_isoc(struct ehci_hcd *ehci)
 	}
 	ehci->now_frame = now_frame;
 
+	if (ehci->last_base == -1)
+		ehci->last_base = ehci->last_iso_frame;
+
 	frame = ehci->last_iso_frame;
 	for (;;) {
 		union ehci_shadow	q, *q_p;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 2822e79..279e182 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -149,6 +149,7 @@ struct ehci_hcd {			/* one per controller */
 	unsigned		intr_unlink_wait_cycle;
 	unsigned		intr_unlink_cycle;
 	unsigned		now_frame;	/* frame from HC hardware */
+	unsigned		last_base;	/* previous 'last_iso_frame' */
 	unsigned		last_iso_frame;	/* last frame scanned for iso */
 	unsigned		intr_count;	/* intr activity count */
 	unsigned		isoc_count;	/* isoc activity count */



Thanks,
-- 
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux