Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: Fix persist resume of some SS USB devices

Problem Summary: Problem has been observed generally with PM states
where VBUS goes off during suspend. There are some SS USB devices which
take longer time for link training compared to many others. Such
devices fail to reconnect with same old address which was associated
with it before suspend.

When system resumes, at some point of time (dpm_run_callback->
usb_dev_resume->usb_resume->usb_resume_both->usb_resume_device->
usb_port_resume) SW reads hub status. If device is present,
then it finishes port resume and re-enumerates device with same
address. If device is not present then, SW thinks that device was
removed during suspend and therefore does logical disconnection
and removes all the resource allocated for this device.

Now, if I put sufficient delay just before root hub status read in
usb_resume_device then, SW sees always that device is present. In normal
course(without any delay) SW sees that no device is present and then SW
removes all resource associated with the device at this port. In the
latter case, after sometime, device says that hey I am here, now host
enumerates it, but with new address.

Problem had been reproduced when I connect verbatim USB3.0 hard disc
with my STiH407 XHCI host running with 3.10 kernel.

I see that similar problem has been reported here.
https://bugzilla.kernel.org/show_bug.cgi?id=53211
Reading above it seems that bug was not in 3.6.6 and was present in 3.8
and again it was not present for some in 3.12.6, while it was present
for few others. I tested with 3.13-FC19 running at i686 desktop, problem
was still there. However, I was failed to reproduce it with 3.16-RC4
running at same i686 machine. I would say it is just a random
observation. Problem for few devices is always there, as I am unable to
find a proper fix for the issue.

So, now question is what should be the amount of delay so that host is
always able to recognize suspended device after resume.

XHCI specs 4.19.4 says that when Link training is successful, port sets
CSC bit to 1. So if SW reads port status before successful link
training, then it will not find device to be present. USB Analyzer log
with such buggy devices show that in some cases device switch on the
RX termination after long delay of host enabling the VBUS. In few other
cases it has been seen that device fails to negotiate link training in
first attempt. It has been reported till now that few devices take as
long as 2000 ms to train the link after host enabling its VBUS and
RX termination. This patch implements a 2000 ms timeout for CSC bit to set
ie for link training. If in a case link trains before timeout, loop will
exit earlier.

This patch implements above delay, but only for SS device and when
persist is enabled.

So, for the good device overhead is almost none. While for the bad
devices penalty could be the time which it take for link training.
But, If a device was connected before suspend, and was removed
while system was asleep, then the penalty would be the timeout ie
2000 ms.

Results:

Verbatim USB SS hard disk connected with STiH407 USB host running 3.10
Kernel resumes in 461 msecs without this patch, but hard disk is
assigned a new device address. Same system resumes in 790 msecs with
this patch, but with old device address.

Cc: <stable@vger.kernel.org>
Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Pratyush Anand and committed by
Greg Kroah-Hartman
a40178b2 1c094728

+41
+41
drivers/usb/core/hub.c
··· 3259 3259 } 3260 3260 3261 3261 /* 3262 + * There are some SS USB devices which take longer time for link training. 3263 + * XHCI specs 4.19.4 says that when Link training is successful, port 3264 + * sets CSC bit to 1. So if SW reads port status before successful link 3265 + * training, then it will not find device to be present. 3266 + * USB Analyzer log with such buggy devices show that in some cases 3267 + * device switch on the RX termination after long delay of host enabling 3268 + * the VBUS. In few other cases it has been seen that device fails to 3269 + * negotiate link training in first attempt. It has been 3270 + * reported till now that few devices take as long as 2000 ms to train 3271 + * the link after host enabling its VBUS and termination. Following 3272 + * routine implements a 2000 ms timeout for link training. If in a case 3273 + * link trains before timeout, loop will exit earlier. 3274 + * 3275 + * FIXME: If a device was connected before suspend, but was removed 3276 + * while system was asleep, then the loop in the following routine will 3277 + * only exit at timeout. 3278 + * 3279 + * This routine should only be called when persist is enabled for a SS 3280 + * device. 3281 + */ 3282 + static int wait_for_ss_port_enable(struct usb_device *udev, 3283 + struct usb_hub *hub, int *port1, 3284 + u16 *portchange, u16 *portstatus) 3285 + { 3286 + int status = 0, delay_ms = 0; 3287 + 3288 + while (delay_ms < 2000) { 3289 + if (status || *portstatus & USB_PORT_STAT_CONNECTION) 3290 + break; 3291 + msleep(20); 3292 + delay_ms += 20; 3293 + status = hub_port_status(hub, *port1, portstatus, portchange); 3294 + } 3295 + return status; 3296 + } 3297 + 3298 + /* 3262 3299 * usb_port_resume - re-activate a suspended usb device's upstream port 3263 3300 * @udev: device to re-activate, not a root hub 3264 3301 * Context: must be able to sleep; device not locked; pm locks held ··· 3390 3353 USB_PORT_FEAT_C_SUSPEND); 3391 3354 } 3392 3355 } 3356 + 3357 + if (udev->persist_enabled && hub_is_superspeed(hub->hdev)) 3358 + status = wait_for_ss_port_enable(udev, hub, &port1, &portchange, 3359 + &portstatus); 3393 3360 3394 3361 status = check_port_resume_type(udev, 3395 3362 hub, port1, status, portchange, portstatus);