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

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

USB: EHCI: defer reclamation of siTDs



This patch (as1369) fixes a problem in ehci-hcd.  Some controllers
occasionally run into trouble when the driver reclaims siTDs too
quickly.  This can happen while streaming audio; it causes the
controller to crash.

The patch changes siTD reclamation to work the same way as iTD
reclamation: Completed siTDs are stored on a list and not reused until
at least one frame has passed.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarNate Case <ncase@xes-inc.com>
CC: <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 5f677f1d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -543,6 +543,7 @@ static int ehci_init(struct usb_hcd *hcd)
	 */
	ehci->periodic_size = DEFAULT_I_TDPS;
	INIT_LIST_HEAD(&ehci->cached_itd_list);
	INIT_LIST_HEAD(&ehci->cached_sitd_list);
	if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
		return retval;

+1 −1
Original line number Diff line number Diff line
@@ -136,7 +136,7 @@ static inline void qh_put (struct ehci_qh *qh)

static void ehci_mem_cleanup (struct ehci_hcd *ehci)
{
	free_cached_itd_list(ehci);
	free_cached_lists(ehci);
	if (ehci->async)
		qh_put (ehci->async);
	ehci->async = NULL;
+31 −9
Original line number Diff line number Diff line
@@ -510,7 +510,7 @@ static int disable_periodic (struct ehci_hcd *ehci)
	ehci_writel(ehci, cmd, &ehci->regs->command);
	/* posted write ... */

	free_cached_itd_list(ehci);
	free_cached_lists(ehci);

	ehci->next_uframe = -1;
	return 0;
@@ -2139,13 +2139,27 @@ sitd_complete (
			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
	}
	iso_stream_put (ehci, stream);
	/* OK to recycle this SITD now that its completion callback ran. */

done:
	sitd->urb = NULL;
	if (ehci->clock_frame != sitd->frame) {
		/* OK to recycle this SITD now. */
		sitd->stream = NULL;
		list_move(&sitd->sitd_list, &stream->free_list);
		iso_stream_put(ehci, stream);

	} else {
		/* HW might remember this SITD, so we can't recycle it yet.
		 * Move it to a safe place until a new frame starts.
		 */
		list_move(&sitd->sitd_list, &ehci->cached_sitd_list);
		if (stream->refcount == 2) {
			/* If iso_stream_put() were called here, stream
			 * would be freed.  Instead, just prevent reuse.
			 */
			stream->ep->hcpriv = NULL;
			stream->ep = NULL;
		}
	}
	return retval;
}

@@ -2211,9 +2225,10 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,

/*-------------------------------------------------------------------------*/

static void free_cached_itd_list(struct ehci_hcd *ehci)
static void free_cached_lists(struct ehci_hcd *ehci)
{
	struct ehci_itd *itd, *n;
	struct ehci_sitd *sitd, *sn;

	list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) {
		struct ehci_iso_stream	*stream = itd->stream;
@@ -2221,6 +2236,13 @@ static void free_cached_itd_list(struct ehci_hcd *ehci)
		list_move(&itd->itd_list, &stream->free_list);
		iso_stream_put(ehci, stream);
	}

	list_for_each_entry_safe(sitd, sn, &ehci->cached_sitd_list, sitd_list) {
		struct ehci_iso_stream	*stream = sitd->stream;
		sitd->stream = NULL;
		list_move(&sitd->sitd_list, &stream->free_list);
		iso_stream_put(ehci, stream);
	}
}

/*-------------------------------------------------------------------------*/
@@ -2247,7 +2269,7 @@ scan_periodic (struct ehci_hcd *ehci)
		clock_frame = -1;
	}
	if (ehci->clock_frame != clock_frame) {
		free_cached_itd_list(ehci);
		free_cached_lists(ehci);
		ehci->clock_frame = clock_frame;
	}
	clock %= mod;
@@ -2414,7 +2436,7 @@ scan_periodic (struct ehci_hcd *ehci)
			clock = now;
			clock_frame = clock >> 3;
			if (ehci->clock_frame != clock_frame) {
				free_cached_itd_list(ehci);
				free_cached_lists(ehci);
				ehci->clock_frame = clock_frame;
			}
		} else {
+3 −2
Original line number Diff line number Diff line
@@ -87,8 +87,9 @@ struct ehci_hcd { /* one per controller */
	int			next_uframe;	/* scan periodic, start here */
	unsigned		periodic_sched;	/* periodic activity count */

	/* list of itds completed while clock_frame was still active */
	/* list of itds & sitds completed while clock_frame was still active */
	struct list_head	cached_itd_list;
	struct list_head	cached_sitd_list;
	unsigned		clock_frame;

	/* per root hub port */
@@ -195,7 +196,7 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
	clear_bit (action, &ehci->actions);
}

static void free_cached_itd_list(struct ehci_hcd *ehci);
static void free_cached_lists(struct ehci_hcd *ehci);

/*-------------------------------------------------------------------------*/