| From: "Shuah Khan (Samsung OSG)" <shuah@kernel.org> |
| Date: Mon, 30 Apr 2018 16:17:20 -0600 |
| Subject: usbip: usbip_host: run rebind from exit when module is removed |
| |
| commit 7510df3f29d44685bab7b1918b61a8ccd57126a9 upstream. |
| |
| After removing usbip_host module, devices it releases are left without |
| a driver. For example, when a keyboard or a mass storage device are |
| bound to usbip_host when it is removed, these devices are no longer |
| bound to any driver. |
| |
| Fix it to run device_attach() from the module exit routine to restore |
| the devices to their original drivers. This includes cleanup changes |
| and moving device_attach() code to a common routine to be called from |
| rebind_store() and usbip_host_exit(). |
| |
| Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| [bwh: Backported to 3.16: adjust filenames] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/staging/usbip/stub_dev.c | 6 +--- |
| drivers/staging/usbip/stub_main.c | 60 +++++++++++++++++++++++++++++------ |
| 2 files changed, 52 insertions(+), 14 deletions(-) |
| |
| --- a/drivers/staging/usbip/stub_dev.c |
| +++ b/drivers/staging/usbip/stub_dev.c |
| @@ -490,12 +490,8 @@ static void stub_disconnect(struct usb_d |
| busid_priv->sdev = NULL; |
| stub_device_free(sdev); |
| |
| - if (busid_priv->status == STUB_BUSID_ALLOC) { |
| + if (busid_priv->status == STUB_BUSID_ALLOC) |
| busid_priv->status = STUB_BUSID_ADDED; |
| - } else { |
| - busid_priv->status = STUB_BUSID_OTHER; |
| - del_match_busid((char *)udev_busid); |
| - } |
| } |
| |
| #ifdef CONFIG_PM |
| --- a/drivers/staging/usbip/stub_main.c |
| +++ b/drivers/staging/usbip/stub_main.c |
| @@ -28,6 +28,7 @@ |
| #define DRIVER_DESC "USB/IP Host Driver" |
| |
| struct kmem_cache *stub_priv_cache; |
| + |
| /* |
| * busid_tables defines matching busids that usbip can grab. A user can change |
| * dynamically what device is locally used and what device is exported to a |
| @@ -188,6 +189,51 @@ static ssize_t store_match_busid(struct |
| static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, |
| store_match_busid); |
| |
| +static int do_rebind(char *busid, struct bus_id_priv *busid_priv) |
| +{ |
| + int ret; |
| + |
| + /* device_attach() callers should hold parent lock for USB */ |
| + if (busid_priv->udev->dev.parent) |
| + device_lock(busid_priv->udev->dev.parent); |
| + ret = device_attach(&busid_priv->udev->dev); |
| + if (busid_priv->udev->dev.parent) |
| + device_unlock(busid_priv->udev->dev.parent); |
| + if (ret < 0) { |
| + dev_err(&busid_priv->udev->dev, "rebind failed\n"); |
| + return ret; |
| + } |
| + return 0; |
| +} |
| + |
| +static void stub_device_rebind(void) |
| +{ |
| +#if IS_MODULE(CONFIG_USBIP_HOST) |
| + struct bus_id_priv *busid_priv; |
| + int i; |
| + |
| + /* update status to STUB_BUSID_OTHER so probe ignores the device */ |
| + spin_lock(&busid_table_lock); |
| + for (i = 0; i < MAX_BUSID; i++) { |
| + if (busid_table[i].name[0] && |
| + busid_table[i].shutdown_busid) { |
| + busid_priv = &(busid_table[i]); |
| + busid_priv->status = STUB_BUSID_OTHER; |
| + } |
| + } |
| + spin_unlock(&busid_table_lock); |
| + |
| + /* now run rebind */ |
| + for (i = 0; i < MAX_BUSID; i++) { |
| + if (busid_table[i].name[0] && |
| + busid_table[i].shutdown_busid) { |
| + busid_priv = &(busid_table[i]); |
| + do_rebind(busid_table[i].name, busid_priv); |
| + } |
| + } |
| +#endif |
| +} |
| + |
| static ssize_t rebind_store(struct device_driver *dev, const char *buf, |
| size_t count) |
| { |
| @@ -208,16 +254,9 @@ static ssize_t rebind_store(struct devic |
| /* mark the device for deletion so probe ignores it during rescan */ |
| bid->status = STUB_BUSID_OTHER; |
| |
| - /* device_attach() callers should hold parent lock for USB */ |
| - if (bid->udev->dev.parent) |
| - device_lock(bid->udev->dev.parent); |
| - ret = device_attach(&bid->udev->dev); |
| - if (bid->udev->dev.parent) |
| - device_unlock(bid->udev->dev.parent); |
| - if (ret < 0) { |
| - dev_err(&bid->udev->dev, "rebind failed\n"); |
| + ret = do_rebind((char *) buf, bid); |
| + if (ret < 0) |
| return ret; |
| - } |
| |
| /* delete device from busid_table */ |
| del_match_busid((char *) buf); |
| @@ -343,6 +382,9 @@ static void __exit usbip_host_exit(void) |
| */ |
| usb_deregister_device_driver(&stub_driver); |
| |
| + /* initiate scan to attach devices */ |
| + stub_device_rebind(); |
| + |
| kmem_cache_destroy(stub_priv_cache); |
| } |
| |