Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp2554610imu; Thu, 29 Nov 2018 06:49:49 -0800 (PST) X-Google-Smtp-Source: AFSGD/XsptsQ6EHs0W/SM2NFHDsmUUHb2p1RSNyDMZTEFb9hLymDVtVQYc1WgGpB+NpXw2TjSHya X-Received: by 2002:a63:d655:: with SMTP id d21mr293495pgj.280.1543502989363; Thu, 29 Nov 2018 06:49:49 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1543502989; cv=none; d=google.com; s=arc-20160816; b=zkQOXKUvXt13NdGtQIXMLxY5N0hJDqRJImB1jtMelN4XPKCsZSi8KwSrHOuncmMUC6 tIxtDg0YIYqr3Wtm6KYoGn8Y3JlXJGiYIULn3EXcPoGbKYyJiQzLpx0ncuhq4dsWLrtQ gJQGmqs0DcfCFPUOd9W9d+2B1egmGiD6ad+efCUiOhHJOvPOclTtOxiNhKNnEuMQLtT4 T6Q+StudT7lveeb6vh56MOd89CdI8v8RIodyTMD9kxHzHl3Ay3/EVoYtsw801n2R6gj+ EFtm9kg4Ys3JvlByzY9BsbBCjOK3R0dJLbn37p8E66TNEdgdTzimboT+/G++tPmTfr5y hvaw== 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 :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=Z5KF78BvoZrvdmUsjXuDNLciAc+28oVYALOaBVJxOOQ=; b=CdoVx2Hd57pquQlMZlq+klqP/sn6kDDNxpYp3BG21wpHRxUhsXGR4++qomuv2FG3aj 1glQ/84H6/fYpXrGPrD61SE+yE2E5Ugl0H6mO6SNPLb7q7mlUESMCIvjhc0WxTPNUgTV 1c0ROfvcWyTXaMipT6ooRLnYt0uQdiKTRb1ofTpezYceaxbUMndSBVrm9jMx5ntvwZVU wDWnfvo0D7BmExCGBytsU47xiZrNu8WM4yRy1pYvZ4LNBkEHDYiHlGT2VHsR1gbI1cR/ /XOWwhNmXiHExR3Y4l5golBTtL2ABlRocKMcC9Sah9fUFEWAfsTLnK5n4KVK9lDYRf3f 98Bw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=MVAsgUe5; 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 a11si1974013pga.198.2018.11.29.06.49.34; Thu, 29 Nov 2018 06:49:49 -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; dkim=pass header.i=@kernel.org header.s=default header.b=MVAsgUe5; 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 S1732972AbeK3Ba4 (ORCPT + 99 others); Thu, 29 Nov 2018 20:30:56 -0500 Received: from mail.kernel.org ([198.145.29.99]:58224 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731601AbeK3Baz (ORCPT ); Thu, 29 Nov 2018 20:30:55 -0500 Received: from localhost (5356596B.cm-6-7b.dynamic.ziggo.nl [83.86.89.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id C42A0205C9; Thu, 29 Nov 2018 14:25:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1543501523; bh=sa41xx+QN/cRI6ajeSa6LxtjAH26uIkBZAvQ1hQNDyg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MVAsgUe5tfvNXrhPukIrGXZD7IofIqWVx1te14tyJWkT9NehIJTdrE6TzHo7Gn+N0 6HrdjIlGBhNcSlS+gI6tNrv0hRtn6SgyFhpRC+wQ8lH2jXS76tGiA65yVFFQGdyqg/ OdaysiUhz4r6PzfuOwpT6L7DmH5Rer9cSrXY8LcI= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Mathias Nyman Subject: [PATCH 4.14 032/100] usb: xhci: Prevent bus suspend if a port connect change or polling state is detected Date: Thu, 29 Nov 2018 15:12:02 +0100 Message-Id: <20181129140101.578328748@linuxfoundation.org> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20181129140058.768942700@linuxfoundation.org> References: <20181129140058.768942700@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.14-stable review patch. If anyone has any objections, please let me know. ------------------ From: Mathias Nyman commit 2f31a67f01a8beb22cae754c53522cb61a005750 upstream. USB3 roothub might autosuspend before a plugged USB3 device is detected, causing USB3 device enumeration failure. USB3 devices don't show up as connected and enabled until USB3 link trainig completes. On a fast booting platform with a slow USB3 link training the link might reach the connected enabled state just as the bus is suspending. If this device is discovered first time by the xhci_bus_suspend() routine it will be put to U3 suspended state like the other ports which failed to suspend earlier. The hub thread will notice the connect change and resume the bus, moving the port back to U0 This U0 -> U3 -> U0 transition right after being connected seems to be too much for some devices, causing them to first go to SS.Inactive state, and finally end up stuck in a polling state with reset asserted Fix this by failing the bus suspend if a port has a connect change or is in a polling state in xhci_bus_suspend(). Don't do any port changes until all ports are checked, buffer all port changes and only write them in the end if suspend can proceed Cc: stable@vger.kernel.org Signed-off-by: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 60 +++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1481,13 +1481,16 @@ int xhci_bus_suspend(struct usb_hcd *hcd __le32 __iomem **port_array; struct xhci_bus_state *bus_state; unsigned long flags; + u32 portsc_buf[USB_MAXCHILDREN]; + bool wake_enabled; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; + wake_enabled = hcd->self.root_hub->do_remote_wakeup; spin_lock_irqsave(&xhci->lock, flags); - if (hcd->self.root_hub->do_remote_wakeup) { + if (wake_enabled) { if (bus_state->resuming_ports || /* USB2 */ bus_state->port_remote_wakeup) { /* USB3 */ spin_unlock_irqrestore(&xhci->lock, flags); @@ -1495,26 +1498,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd return -EBUSY; } } - - port_index = max_ports; + /* + * Prepare ports for suspend, but don't write anything before all ports + * are checked and we know bus suspend can proceed + */ bus_state->bus_suspended = 0; + port_index = max_ports; while (port_index--) { - /* suspend the port if the port is not suspended */ u32 t1, t2; - int slot_id; t1 = readl(port_array[port_index]); t2 = xhci_port_state_to_neutral(t1); + portsc_buf[port_index] = 0; - if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { - xhci_dbg(xhci, "port %d not suspended\n", port_index); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - port_index + 1); - if (slot_id) { + /* Bail out if a USB3 port has a new device in link training */ + if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) { + bus_state->bus_suspended = 0; + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_dbg(xhci, "Bus suspend bailout, port in polling\n"); + return -EBUSY; + } + + /* suspend ports in U0, or bail out for new connect changes */ + if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) { + if ((t1 & PORT_CSC) && wake_enabled) { + bus_state->bus_suspended = 0; spin_unlock_irqrestore(&xhci->lock, flags); - xhci_stop_device(xhci, slot_id, 1); - spin_lock_irqsave(&xhci->lock, flags); + xhci_dbg(xhci, "Bus suspend bailout, port connect change\n"); + return -EBUSY; } + xhci_dbg(xhci, "port %d not suspended\n", port_index); t2 &= ~PORT_PLS_MASK; t2 |= PORT_LINK_STROBE | XDEV_U3; set_bit(port_index, &bus_state->bus_suspended); @@ -1523,7 +1536,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd * including the USB 3.0 roothub, but only if CONFIG_PM * is enabled, so also enable remote wake here. */ - if (hcd->self.root_hub->do_remote_wakeup) { + if (wake_enabled) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; t2 &= ~PORT_WKCONN_E; @@ -1543,7 +1556,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) - writel(t2, port_array[port_index]); + portsc_buf[port_index] = t2; + } + + /* write port settings, stopping and suspending ports if needed */ + port_index = max_ports; + while (port_index--) { + if (!portsc_buf[port_index]) + continue; + if (test_bit(port_index, &bus_state->bus_suspended)) { + int slot_id; + + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + port_index + 1); + if (slot_id) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_stop_device(xhci, slot_id, 1); + spin_lock_irqsave(&xhci->lock, flags); + } + } + writel(portsc_buf[port_index], port_array[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10);