[PATCH] usb: Skip U1/U2 LPM disable if device is disconnected.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Occasionally when a USB 3.0 device is disconnected, the roothub port
goes into the SS.Inactive state, rather than reporting a disconnect.  A
warm reset is the only way to get out of this state.  khubd notices the
link state in hub_port_events(), and since a udev is active on that
port, it calls into usb_reset_and_verify_device().

USB 3.0 Link PM is disabled before the device is reset, in order to
balance out the LPM ref counts.  That code will also issue two control
transfers to disable the U1 and U2 timeouts.  On some USB host
controllers (at least the Intel ones, possibly others), issuing a
control transfer to a disconnected port causes the transfer to timeout,
rather than immediately returning with a transfer error.  Each control
transfer takes five seconds to timeout and be canceled.

[   89.551350] hub 2-0:1.0: state 7 ports 4 chg 0000 evt 0004
[   89.551642] hub 2-0:1.0: warm reset port 2
[   94.549887] usb 2-2: Disable of device-initiated U1 failed.
[   99.548027] usb 2-2: Disable of device-initiated U2 failed.

The end result is that USB device disconnect is delayed by ten seconds,
and khubd won't be able to service other ports until the disconnect is
handled.  Work around this by checking the status of the device's port,
and not sending the U1/U2 disable control transfers if the device is
disconnected.

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Tested-by: Girish Nandibasappa <girishx.nandibasappa@xxxxxxxxx>
Tested-by: Hemanth Venkatesh Murthy <hemanthx.venkatesh.murthy@xxxxxxxxx>
---
 drivers/usb/core/hub.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 92e052db27ac..5eea4a5475a4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2521,6 +2521,22 @@ static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
 		  USB_SS_PORT_LS_COMP_MOD)) ;
 }
 
+static bool hub_is_device_disconnected(struct usb_device *udev)
+{
+	u16 portstatus;
+	u16 portchange;
+	int ret;
+	struct usb_hub *hub;
+
+	hub = usb_hub_to_struct_hub(udev->parent);
+	ret = hub_port_status(hub, udev->portnum, &portstatus, &portchange);
+	if (ret < 0)
+		return true;
+	if ((portstatus & USB_PORT_STAT_CONNECTION))
+		return false;
+	return true;
+}
+
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 			struct usb_device *udev, unsigned int delay, bool warm)
 {
@@ -3513,6 +3529,13 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
 				usb3_lpm_names[state]);
 		return 0;
 	}
+	if (hub_is_device_disconnected(udev)) {
+		dev_dbg(&udev->dev, "%s: Can't %s %s state "
+				"for disconnected device.\n",
+				__func__, enable ? "enable" : "disable",
+				usb3_lpm_names[state]);
+		return 0;
+	}
 
 	if (enable) {
 		/*
-- 
1.8.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux