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

Commit 67e03559 authored by David Mosberger's avatar David Mosberger Committed by TARKZiM
Browse files

drivers: usb: core: Don't disable irqs in usb_sg_wait() during URB submit.



commit 98b74b0ee57af1bcb6e8b2e76e707a71c5ef8ec9 upstream.

usb_submit_urb() may take quite long to execute.  For example, a
single sg list may have 30 or more entries, possibly leading to that
many calls to DMA-map pages.  This can cause interrupt latency of
several hundred micro-seconds.

Avoid the problem by releasing the io->lock spinlock and re-enabling
interrupts before calling usb_submit_urb().  This opens races with
usb_sg_cancel() and sg_complete().  Handle those races by using
usb_block_urb() to stop URBs from being submitted after
usb_sg_cancel() or sg_complete() with error.

Note that usb_unlink_urb() is guaranteed to return -ENODEV if
!io->urbs[i]->dev and since the -ENODEV case is already handled,
we don't have to check for !io->urbs[i]->dev explicitly.

Before this change, reading 512MB from an ext3 filesystem on a USB
memory stick showed a throughput of 12 MB/s with about 500 missed
deadlines.

With this change, reading the same file gave the same throughput but
only one or two missed deadlines.

Signed-off-by: default avatarDavid Mosberger <davidm@egauge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarKevin F. Haggerty <haggertk@lineageos.org>
Change-Id: I687e1101dc3393bc63e72ef917d7f5f04539f73c
parent 2696138d
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -303,9 +303,10 @@ static void sg_complete(struct urb *urb)
		 */
		spin_unlock(&io->lock);
		for (i = 0, found = 0; i < io->entries; i++) {
			if (!io->urbs[i] || !io->urbs[i]->dev)
			if (!io->urbs[i])
				continue;
			if (found) {
				usb_block_urb(io->urbs[i]);
				retval = usb_unlink_urb(io->urbs[i]);
				if (retval != -EINPROGRESS &&
				    retval != -ENODEV &&
@@ -516,12 +517,10 @@ void usb_sg_wait(struct usb_sg_request *io)
		int retval;

		io->urbs[i]->dev = io->dev;
		retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC);

		/* after we submit, let completions or cancelations fire;
		 * we handshake using io->status.
		 */
		spin_unlock_irq(&io->lock);

		retval = usb_submit_urb(io->urbs[i], GFP_NOIO);

		switch (retval) {
			/* maybe we retrying will recover */
		case -ENXIO:	/* hc didn't queue this one */
@@ -591,8 +590,8 @@ void usb_sg_cancel(struct usb_sg_request *io)
		for (i = 0; i < io->entries; i++) {
			int retval;

			if (!io->urbs[i]->dev)
				continue;
			usb_block_urb(io->urbs[i]);

			retval = usb_unlink_urb(io->urbs[i]);
			if (retval != -EINPROGRESS
					&& retval != -ENODEV