Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp4970266imu; Tue, 8 Jan 2019 09:15:38 -0800 (PST) X-Google-Smtp-Source: ALg8bN5jllVz02BI7O7idY1QJIZpQApKNMkRc+KPnhlBYaOaFnzMyNqNjPcpYpQJoYoeVD3UleUw X-Received: by 2002:a62:d2c1:: with SMTP id c184mr2553706pfg.248.1546967738193; Tue, 08 Jan 2019 09:15:38 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1546967738; cv=none; d=google.com; s=arc-20160816; b=WV2vFnpH6yKh6r1nq+8soHvEqiW/qMs2qVTi1X/m//h9l/ZgSzkNVNMJYQtcpxZUwm lYQH8e9q6lO0phZdpc/MRFhgu9jHHXu3jLGuTGsqbRdsz4Vjp1BKq4f78CDX9bK/+D+n /0WX7ODJ0rn7di9j5xXS6yFATrg+MGKFFOk08OgPErakECwZRC0bwtn/iFU2jjvtS9/N 0ByJBoqX6t4ONUvoB5pcsZGIJma/juGosgioy3Th1wPFL3y73GAYOam5x38FVyLhcFnS ZuCuF671zYNUEWygURDytmWajHazJKR8UirMyWMay/iEWEtGWnSP653c1Z/OaDsmLJ5o wdFQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=hm1pj0e7zGVRLcvAu9jZDiUra28oUNbctq1WdcQ/cxg=; b=GFwBcilnVsxo+4v7P5DAbb7MauWTG7tXMNSEOmunj+m1q62nPcOLL4SnQtRXyEx/1u o/KiSnh63hOjLiyV4Owb8N8gjihYqLm9mpsHBetEbf0LECCxB9oDAcvmFpf3TYlIcADK +pYbWK5p9LWDLVpreyhYFxyjxK7U7vy2x8f8W3eTeh/OarBgmCWMFt7gYmn6chEQ5wtj K3oEbi4d/hxW1eNJhydbIAAHWBHYiS4QNejIbZ5SxEJnD2SXRFq77XkDclT86v29FYBQ fHhk4rCsoRdgPyURww5fl+OG0Qx5TIuKfSoKWIbotWngBmKdhhTyccKI03dn9Kj8heFT JXvg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 33si28897828plg.62.2019.01.08.09.15.23; Tue, 08 Jan 2019 09:15:38 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728569AbfAHPpa (ORCPT + 99 others); Tue, 8 Jan 2019 10:45:30 -0500 Received: from mx2.suse.de ([195.135.220.15]:35908 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728552AbfAHPp3 (ORCPT ); Tue, 8 Jan 2019 10:45:29 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id E0D44AFFC; Tue, 8 Jan 2019 15:45:26 +0000 (UTC) From: Nicolas Saenz Julienne To: stern@rowland.harvard.edu, oneukum@suse.com, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Cc: gregkh@linuxfoundation.org, nsaenzjulienne@suse.de Subject: [PATCH v4] usb: hub: add retry routine after intr URB submit error Date: Tue, 8 Jan 2019 16:45:22 +0100 Message-Id: <20190108154522.20221-1-nsaenzjulienne@suse.de> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The hub sends hot-plug events to the host trough it's interrupt URB. The driver takes care of completing the URB and re-submitting it. Completion errors are handled in the hub_event() work, yet submission errors are ignored, rendering the device unresponsive. All further events are lost. It is fairly hard to find this issue in the wild, since you have to time the USB hot-plug event with the URB submission failure. For instance it could be the system running out of memory or some malfunction in the USB controller driver. Nevertheless, it's pretty reasonable to think it'll happen sometime. One can trigger this issue using eBPF's function override feature (see BCC's inject.py script). This patch adds a retry routine to the event of a submission error. The HUB driver will try to re-submit the URB once every second until it's successful or the HUB is disconnected. As some USB subsystems already take care of this issue, the implementation was inspired from usbhid/hid_core.c's. Signed-off-by: Nicolas Saenz Julienne Reviewed-by: Oliver Neukum --- v4: - Add Oliver's Reviewed-by - Make timeout calculation simpler v3: As per Oliver's request: - Take care of race condition between disconnect and irq v2: as per Alan's and Oliver's comments: - Rename timer - Delete the timer on disconnect - Don't reset HUB nor exponential slowdown the timer, 1s fixed retry period - Check for -ESHUTDOWN prior kicking the timer drivers/usb/core/hub.c | 43 ++++++++++++++++++++++++++++++++++++------ drivers/usb/core/hub.h | 2 ++ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1d1e61e980f3..713ab85332f8 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -607,6 +607,36 @@ static int hub_port_status(struct usb_hub *hub, int port1, status, change, NULL); } +static void hub_resubmit_irq_urb(struct usb_hub *hub) +{ + unsigned long flags; + int status; + + spin_lock_irqsave(&hub->irq_urb_lock, flags); + + if (hub->quiescing) { + spin_unlock_irqrestore(&hub->irq_urb_lock, flags); + return; + } + + status = usb_submit_urb(hub->urb, GFP_ATOMIC); + if (status && status != -ENODEV && status != -EPERM && + status != -ESHUTDOWN) { + dev_err(hub->intfdev, "resubmit --> %d\n", status); + mod_timer(&hub->irq_urb_retry, jiffies + HZ); + } + + spin_unlock_irqrestore(&hub->irq_urb_lock, flags); +} + +static void hub_retry_irq_urb(struct timer_list *t) +{ + struct usb_hub *hub = from_timer(hub, t, irq_urb_retry); + + hub_resubmit_irq_urb(hub); +} + + static void kick_hub_wq(struct usb_hub *hub) { struct usb_interface *intf; @@ -709,12 +739,7 @@ static void hub_irq(struct urb *urb) kick_hub_wq(hub); resubmit: - if (hub->quiescing) - return; - - status = usb_submit_urb(hub->urb, GFP_ATOMIC); - if (status != 0 && status != -ENODEV && status != -EPERM) - dev_err(hub->intfdev, "resubmit --> %d\n", status); + hub_resubmit_irq_urb(hub); } /* USB 2.0 spec Section 11.24.2.3 */ @@ -1264,10 +1289,13 @@ enum hub_quiescing_type { static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) { struct usb_device *hdev = hub->hdev; + unsigned long flags; int i; /* hub_wq and related activity won't re-trigger */ + spin_lock_irqsave(&hub->irq_urb_lock, flags); hub->quiescing = 1; + spin_unlock_irqrestore(&hub->irq_urb_lock, flags); if (type != HUB_SUSPEND) { /* Disconnect all the children */ @@ -1278,6 +1306,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) } /* Stop hub_wq and related activity */ + del_timer_sync(&hub->irq_urb_retry); usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); @@ -1810,6 +1839,8 @@ 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); + spin_lock_init(&hub->irq_urb_lock); + timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 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..a9e24e4b8df1 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -69,6 +69,8 @@ struct usb_hub { struct delayed_work leds; struct delayed_work init_work; struct work_struct events; + spinlock_t irq_urb_lock; + struct timer_list irq_urb_retry; struct usb_port **ports; }; -- 2.20.1