| From 17401e36f7b1a96d6202e50c19c2f738b0068aad Mon Sep 17 00:00:00 2001 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Wed, 22 Apr 2020 16:09:51 -0400 |
| Subject: [PATCH] USB: hub: Fix handling of connect changes during sleep |
| |
| commit 9f952e26295d977dbfc6fedeaf8c4f112c818d37 upstream. |
| |
| Commit 8099f58f1ecd ("USB: hub: Don't record a connect-change event |
| during reset-resume") wasn't very well conceived. The problem it |
| tried to fix was that if a connect-change event occurred while the |
| system was asleep (such as a device disconnecting itself from the bus |
| when it is suspended and then reconnecting when it resumes) |
| requiring a reset-resume during the system wakeup transition, the hub |
| port's change_bit entry would remain set afterward. This would cause |
| the hub driver to believe another connect-change event had occurred |
| after the reset-resume, which was wrong and would lead the driver to |
| send unnecessary requests to the device (which could interfere with a |
| firmware update). |
| |
| The commit tried to fix this by not setting the change_bit during the |
| wakeup. But this was the wrong thing to do; it means that when a |
| device is unplugged while the system is asleep, the hub driver doesn't |
| realize anything has happened: The change_bit flag which would tell it |
| to handle the disconnect event is clear. |
| |
| The commit needs to be reverted and the problem fixed in a different |
| way. Fortunately an alternative solution was noted in the commit's |
| Changelog: We can continue to set the change_bit entry in |
| hub_activate() but then clear it when a reset-resume occurs. That way |
| the the hub driver will see the change_bit when a device is |
| disconnected but won't see it when the device is still present. |
| |
| That's what this patch does. |
| |
| Reported-and-tested-by: Peter Chen <peter.chen@nxp.com> |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Fixes: 8099f58f1ecd ("USB: hub: Don't record a connect-change event during reset-resume") |
| Tested-by: Paul Zimmerman <pauldzim@gmail.com> |
| CC: <stable@vger.kernel.org> |
| |
| Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.2004221602480.11262-100000@iolanthe.rowland.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c |
| index a54d0c8971e6..b5b36f5d2203 100644 |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -1218,6 +1218,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) |
| #ifdef CONFIG_PM |
| udev->reset_resume = 1; |
| #endif |
| + /* Don't set the change_bits when the device |
| + * was powered off. |
| + */ |
| + if (test_bit(port1, hub->power_bits)) |
| + set_bit(port1, hub->change_bits); |
| |
| } else { |
| /* The power session is gone; tell hub_wq */ |
| @@ -3081,6 +3086,15 @@ static int check_port_resume_type(struct usb_device *udev, |
| if (portchange & USB_PORT_STAT_C_ENABLE) |
| usb_clear_port_feature(hub->hdev, port1, |
| USB_PORT_FEAT_C_ENABLE); |
| + |
| + /* |
| + * Whatever made this reset-resume necessary may have |
| + * turned on the port1 bit in hub->change_bits. But after |
| + * a successful reset-resume we want the bit to be clear; |
| + * if it was on it would indicate that something happened |
| + * following the reset-resume. |
| + */ |
| + clear_bit(port1, hub->change_bits); |
| } |
| |
| return status; |
| -- |
| 2.7.4 |
| |