linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
To: nsaaenzjulienne@suse.de, linux-kernel@vger.kernel.org
Cc: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	linux-usb@vger.kernel.org
Subject: [PATCH] usb: hub: add I/O error retry & reset routine
Date: Thu, 15 Nov 2018 18:14:18 +0100	[thread overview]
Message-ID: <20181115171418.23522-1-nsaenzjulienne@suse.de> (raw)

An URB submission error in the HUB's endpoint completion function
renders the whole HUB device unresponsive. This patch introduces a
routine that retries the submission for 1s to then, as a last resort,
reset the whole device.

The implementation is based on usbhid/hid_core.c's, which implements the
same functionality. It was tested with the help of BCC's error injection
tool (inject.py).

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---
 drivers/usb/core/hub.c | 43 +++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/core/hub.h |  3 +++
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d9bd7576786a..1447bdba59ec 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -607,6 +607,44 @@ static int hub_port_status(struct usb_hub *hub, int port1,
 				   status, change, NULL);
 }
 
+static void hub_io_error(struct usb_hub *hub)
+{
+	/*
+	 * If it has been a while since the last error, we'll assume
+	 * this a brand new error and reset the retry timeout.
+	 */
+	if (time_after(jiffies, hub->stop_retry + HZ/2))
+		hub->retry_delay = 0;
+
+	/* When an error occurs, retry at increasing intervals */
+	if (hub->retry_delay == 0) {
+		hub->retry_delay = 13;	/* Then 26, 52, 104, 104, ... */
+		hub->stop_retry = jiffies + msecs_to_jiffies(1000);
+	} else if (hub->retry_delay < 100)
+		hub->retry_delay *= 2;
+
+	if (time_after(jiffies, hub->stop_retry)) {
+		/* Retries failed, so do a port reset */
+		usb_queue_reset_device(to_usb_interface(hub->intfdev));
+		return;
+	}
+
+	mod_timer(&hub->io_retry,
+			jiffies + msecs_to_jiffies(hub->retry_delay));
+}
+
+static void hub_retry_timeout(struct timer_list *t)
+{
+	struct usb_hub *hub = from_timer(hub, t, io_retry);
+
+	if (hub->disconnected || hub->quiescing)
+		return;
+
+	dev_err(hub->intfdev, "retrying int urb\n");
+	if (usb_submit_urb(hub->urb, GFP_ATOMIC))
+		hub_io_error(hub);
+}
+
 static void kick_hub_wq(struct usb_hub *hub)
 {
 	struct usb_interface *intf;
@@ -713,8 +751,10 @@ static void hub_irq(struct urb *urb)
 		return;
 
 	status = usb_submit_urb(hub->urb, GFP_ATOMIC);
-	if (status != 0 && status != -ENODEV && status != -EPERM)
+	if (status != 0 && status != -ENODEV && status != -EPERM) {
 		dev_err(hub->intfdev, "resubmit --> %d\n", status);
+		hub_io_error(hub);
+	}
 }
 
 /* USB 2.0 spec Section 11.24.2.3 */
@@ -1800,6 +1840,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	INIT_DELAYED_WORK(&hub->leds, led_work);
 	INIT_DELAYED_WORK(&hub->init_work, NULL);
 	INIT_WORK(&hub->events, hub_event);
+	timer_setup(&hub->io_retry, hub_retry_timeout, 0);
 	usb_get_intf(intf);
 	usb_get_dev(hdev);
 
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 4accfb63f7dc..7dbd19c2c8d9 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -69,6 +69,9 @@ struct usb_hub {
 	struct delayed_work	leds;
 	struct delayed_work	init_work;
 	struct work_struct      events;
+	struct timer_list	io_retry;
+	unsigned long		stop_retry;
+	unsigned int		retry_delay;
 	struct usb_port		**ports;
 };
 
-- 
2.19.1


             reply	other threads:[~2018-11-15 17:14 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-15 17:14 Nicolas Saenz Julienne [this message]
2018-11-15 19:24 ` [PATCH] usb: hub: add I/O error retry & reset routine Alan Stern
2018-11-16 10:49   ` Nicolas Saenz Julienne
2018-11-16 16:21     ` Alan Stern
2018-11-19  9:20 ` Oliver Neukum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181115171418.23522-1-nsaenzjulienne@suse.de \
    --to=nsaenzjulienne@suse.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=nsaaenzjulienne@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).