USB fixes for 4.17-rc6
Here are some USB driver fixes fro 4.17-rc6. They resolve some reported bugs in the musb driver, the xhci driver, and a number of small fixes for the usbip driver. All of these have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWv7roA8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yklSwCglRWdR/XPWovehZcFz1x/LHIbxAkAn0aV7mh/ k9WHsoAKijXbOIkMNswb =t6bD -----END PGP SIGNATURE----- Merge tag 'usb-4.17-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB fixes from Greg KH: "Here are some USB driver fixes fro 4.17-rc6. They resolve some reported bugs in the musb driver, the xhci driver, and a number of small fixes for the usbip driver. All of these have been in linux-next with no reported issues" * tag 'usb-4.17-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: usbip: usbip_host: fix bad unlock balance during stub_probe() usbip: usbip_host: fix NULL-ptr deref and use-after-free errors usbip: usbip_host: run rebind from exit when module is removed usbip: usbip_host: delete device from busid_table after rebind usbip: usbip_host: refine probe and disconnect debug msgs to be useful usb: musb: fix remote wakeup racing with suspend xhci: Fix USB3 NULL pointer dereference at logical disconnect.
This commit is contained in:
Коммит
0e273f9edc
|
@ -354,7 +354,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
|
|||
|
||||
slot_id = 0;
|
||||
for (i = 0; i < MAX_HC_SLOTS; i++) {
|
||||
if (!xhci->devs[i])
|
||||
if (!xhci->devs[i] || !xhci->devs[i]->udev)
|
||||
continue;
|
||||
speed = xhci->devs[i]->udev->speed;
|
||||
if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3))
|
||||
|
|
|
@ -2524,8 +2524,11 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
|
|||
{
|
||||
struct musb *musb = hcd_to_musb(hcd);
|
||||
u8 devctl;
|
||||
int ret;
|
||||
|
||||
musb_port_suspend(musb, true);
|
||||
ret = musb_port_suspend(musb, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!is_host_active(musb))
|
||||
return 0;
|
||||
|
|
|
@ -67,7 +67,7 @@ extern void musb_host_rx(struct musb *, u8);
|
|||
extern void musb_root_disconnect(struct musb *musb);
|
||||
extern void musb_host_resume_root_hub(struct musb *musb);
|
||||
extern void musb_host_poke_root_hub(struct musb *musb);
|
||||
extern void musb_port_suspend(struct musb *musb, bool do_suspend);
|
||||
extern int musb_port_suspend(struct musb *musb, bool do_suspend);
|
||||
extern void musb_port_reset(struct musb *musb, bool do_reset);
|
||||
extern void musb_host_finish_resume(struct work_struct *work);
|
||||
#else
|
||||
|
@ -99,7 +99,10 @@ static inline void musb_root_disconnect(struct musb *musb) {}
|
|||
static inline void musb_host_resume_root_hub(struct musb *musb) {}
|
||||
static inline void musb_host_poll_rh_status(struct musb *musb) {}
|
||||
static inline void musb_host_poke_root_hub(struct musb *musb) {}
|
||||
static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {}
|
||||
static inline int musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void musb_port_reset(struct musb *musb, bool do_reset) {}
|
||||
static inline void musb_host_finish_resume(struct work_struct *work) {}
|
||||
#endif
|
||||
|
|
|
@ -48,14 +48,14 @@ void musb_host_finish_resume(struct work_struct *work)
|
|||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
int musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
{
|
||||
struct usb_otg *otg = musb->xceiv->otg;
|
||||
u8 power;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
if (!is_host_active(musb))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
/* NOTE: this doesn't necessarily put PHY into low power mode,
|
||||
* turning off its clock; that's a function of PHY integration and
|
||||
|
@ -66,16 +66,20 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
|
|||
if (do_suspend) {
|
||||
int retries = 10000;
|
||||
|
||||
power &= ~MUSB_POWER_RESUME;
|
||||
power |= MUSB_POWER_SUSPENDM;
|
||||
musb_writeb(mbase, MUSB_POWER, power);
|
||||
if (power & MUSB_POWER_RESUME)
|
||||
return -EBUSY;
|
||||
|
||||
/* Needed for OPT A tests */
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
while (power & MUSB_POWER_SUSPENDM) {
|
||||
if (!(power & MUSB_POWER_SUSPENDM)) {
|
||||
power |= MUSB_POWER_SUSPENDM;
|
||||
musb_writeb(mbase, MUSB_POWER, power);
|
||||
|
||||
/* Needed for OPT A tests */
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (retries-- < 1)
|
||||
break;
|
||||
while (power & MUSB_POWER_SUSPENDM) {
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (retries-- < 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
musb_dbg(musb, "Root port suspended, power %02x", power);
|
||||
|
@ -111,6 +115,7 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
|
|||
schedule_delayed_work(&musb->finish_resume_work,
|
||||
msecs_to_jiffies(USB_RESUME_TIMEOUT));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
|
|
|
@ -73,6 +73,7 @@ struct bus_id_priv {
|
|||
struct stub_device *sdev;
|
||||
struct usb_device *udev;
|
||||
char shutdown_busid;
|
||||
spinlock_t busid_lock;
|
||||
};
|
||||
|
||||
/* stub_priv is allocated from stub_priv_cache */
|
||||
|
@ -83,6 +84,7 @@ extern struct usb_device_driver stub_driver;
|
|||
|
||||
/* stub_main.c */
|
||||
struct bus_id_priv *get_busid_priv(const char *busid);
|
||||
void put_busid_priv(struct bus_id_priv *bid);
|
||||
int del_match_busid(char *busid);
|
||||
void stub_device_cleanup_urbs(struct stub_device *sdev);
|
||||
|
||||
|
|
|
@ -300,9 +300,9 @@ static int stub_probe(struct usb_device *udev)
|
|||
struct stub_device *sdev = NULL;
|
||||
const char *udev_busid = dev_name(&udev->dev);
|
||||
struct bus_id_priv *busid_priv;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
dev_dbg(&udev->dev, "Enter\n");
|
||||
dev_dbg(&udev->dev, "Enter probe\n");
|
||||
|
||||
/* check we should claim or not by busid_table */
|
||||
busid_priv = get_busid_priv(udev_busid);
|
||||
|
@ -317,13 +317,15 @@ static int stub_probe(struct usb_device *udev)
|
|||
* other matched drivers by the driver core.
|
||||
* See driver_probe_device() in driver/base/dd.c
|
||||
*/
|
||||
return -ENODEV;
|
||||
rc = -ENODEV;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
|
||||
dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
|
||||
udev_busid);
|
||||
return -ENODEV;
|
||||
rc = -ENODEV;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
|
||||
|
@ -331,13 +333,16 @@ static int stub_probe(struct usb_device *udev)
|
|||
"%s is attached on vhci_hcd... skip!\n",
|
||||
udev_busid);
|
||||
|
||||
return -ENODEV;
|
||||
rc = -ENODEV;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
/* ok, this is my device */
|
||||
sdev = stub_device_alloc(udev);
|
||||
if (!sdev)
|
||||
return -ENOMEM;
|
||||
if (!sdev) {
|
||||
rc = -ENOMEM;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
dev_info(&udev->dev,
|
||||
"usbip-host: register new device (bus %u dev %u)\n",
|
||||
|
@ -369,7 +374,9 @@ static int stub_probe(struct usb_device *udev)
|
|||
}
|
||||
busid_priv->status = STUB_BUSID_ALLOC;
|
||||
|
||||
return 0;
|
||||
rc = 0;
|
||||
goto call_put_busid_priv;
|
||||
|
||||
err_files:
|
||||
usb_hub_release_port(udev->parent, udev->portnum,
|
||||
(struct usb_dev_state *) udev);
|
||||
|
@ -379,6 +386,9 @@ err_port:
|
|||
|
||||
busid_priv->sdev = NULL;
|
||||
stub_device_free(sdev);
|
||||
|
||||
call_put_busid_priv:
|
||||
put_busid_priv(busid_priv);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -404,7 +414,7 @@ static void stub_disconnect(struct usb_device *udev)
|
|||
struct bus_id_priv *busid_priv;
|
||||
int rc;
|
||||
|
||||
dev_dbg(&udev->dev, "Enter\n");
|
||||
dev_dbg(&udev->dev, "Enter disconnect\n");
|
||||
|
||||
busid_priv = get_busid_priv(udev_busid);
|
||||
if (!busid_priv) {
|
||||
|
@ -417,7 +427,7 @@ static void stub_disconnect(struct usb_device *udev)
|
|||
/* get stub_device */
|
||||
if (!sdev) {
|
||||
dev_err(&udev->dev, "could not get device");
|
||||
return;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&udev->dev, NULL);
|
||||
|
@ -432,12 +442,12 @@ static void stub_disconnect(struct usb_device *udev)
|
|||
(struct usb_dev_state *) udev);
|
||||
if (rc) {
|
||||
dev_dbg(&udev->dev, "unable to release port\n");
|
||||
return;
|
||||
goto call_put_busid_priv;
|
||||
}
|
||||
|
||||
/* If usb reset is called from event handler */
|
||||
if (usbip_in_eh(current))
|
||||
return;
|
||||
goto call_put_busid_priv;
|
||||
|
||||
/* shutdown the current connection */
|
||||
shutdown_busid(busid_priv);
|
||||
|
@ -448,12 +458,11 @@ static void stub_disconnect(struct usb_device *udev)
|
|||
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);
|
||||
}
|
||||
|
||||
call_put_busid_priv:
|
||||
put_busid_priv(busid_priv);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
|
|
@ -14,6 +14,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
|
||||
|
@ -25,6 +26,8 @@ static spinlock_t busid_table_lock;
|
|||
|
||||
static void init_busid_table(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* This also sets the bus_table[i].status to
|
||||
* STUB_BUSID_OTHER, which is 0.
|
||||
|
@ -32,6 +35,9 @@ static void init_busid_table(void)
|
|||
memset(busid_table, 0, sizeof(busid_table));
|
||||
|
||||
spin_lock_init(&busid_table_lock);
|
||||
|
||||
for (i = 0; i < MAX_BUSID; i++)
|
||||
spin_lock_init(&busid_table[i].busid_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -43,15 +49,20 @@ static int get_busid_idx(const char *busid)
|
|||
int i;
|
||||
int idx = -1;
|
||||
|
||||
for (i = 0; i < MAX_BUSID; i++)
|
||||
for (i = 0; i < MAX_BUSID; i++) {
|
||||
spin_lock(&busid_table[i].busid_lock);
|
||||
if (busid_table[i].name[0])
|
||||
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
|
||||
idx = i;
|
||||
spin_unlock(&busid_table[i].busid_lock);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&busid_table[i].busid_lock);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
|
||||
struct bus_id_priv *get_busid_priv(const char *busid)
|
||||
{
|
||||
int idx;
|
||||
|
@ -59,13 +70,22 @@ struct bus_id_priv *get_busid_priv(const char *busid)
|
|||
|
||||
spin_lock(&busid_table_lock);
|
||||
idx = get_busid_idx(busid);
|
||||
if (idx >= 0)
|
||||
if (idx >= 0) {
|
||||
bid = &(busid_table[idx]);
|
||||
/* get busid_lock before returning */
|
||||
spin_lock(&bid->busid_lock);
|
||||
}
|
||||
spin_unlock(&busid_table_lock);
|
||||
|
||||
return bid;
|
||||
}
|
||||
|
||||
void put_busid_priv(struct bus_id_priv *bid)
|
||||
{
|
||||
if (bid)
|
||||
spin_unlock(&bid->busid_lock);
|
||||
}
|
||||
|
||||
static int add_match_busid(char *busid)
|
||||
{
|
||||
int i;
|
||||
|
@ -78,15 +98,19 @@ static int add_match_busid(char *busid)
|
|||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_BUSID; i++)
|
||||
for (i = 0; i < MAX_BUSID; i++) {
|
||||
spin_lock(&busid_table[i].busid_lock);
|
||||
if (!busid_table[i].name[0]) {
|
||||
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
|
||||
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
|
||||
(busid_table[i].status != STUB_BUSID_REMOV))
|
||||
busid_table[i].status = STUB_BUSID_ADDED;
|
||||
ret = 0;
|
||||
spin_unlock(&busid_table[i].busid_lock);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&busid_table[i].busid_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&busid_table_lock);
|
||||
|
@ -107,6 +131,8 @@ int del_match_busid(char *busid)
|
|||
/* found */
|
||||
ret = 0;
|
||||
|
||||
spin_lock(&busid_table[idx].busid_lock);
|
||||
|
||||
if (busid_table[idx].status == STUB_BUSID_OTHER)
|
||||
memset(busid_table[idx].name, 0, BUSID_SIZE);
|
||||
|
||||
|
@ -114,6 +140,7 @@ int del_match_busid(char *busid)
|
|||
(busid_table[idx].status != STUB_BUSID_ADDED))
|
||||
busid_table[idx].status = STUB_BUSID_REMOV;
|
||||
|
||||
spin_unlock(&busid_table[idx].busid_lock);
|
||||
out:
|
||||
spin_unlock(&busid_table_lock);
|
||||
|
||||
|
@ -126,9 +153,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
|
|||
char *out = buf;
|
||||
|
||||
spin_lock(&busid_table_lock);
|
||||
for (i = 0; i < MAX_BUSID; i++)
|
||||
for (i = 0; i < MAX_BUSID; i++) {
|
||||
spin_lock(&busid_table[i].busid_lock);
|
||||
if (busid_table[i].name[0])
|
||||
out += sprintf(out, "%s ", busid_table[i].name);
|
||||
spin_unlock(&busid_table[i].busid_lock);
|
||||
}
|
||||
spin_unlock(&busid_table_lock);
|
||||
out += sprintf(out, "\n");
|
||||
|
||||
|
@ -169,6 +199,51 @@ static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
|
|||
}
|
||||
static DRIVER_ATTR_RW(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 - no need to hold locks. driver files are removed */
|
||||
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)
|
||||
{
|
||||
|
@ -186,16 +261,17 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|||
if (!bid)
|
||||
return -ENODEV;
|
||||
|
||||
/* 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");
|
||||
/* mark the device for deletion so probe ignores it during rescan */
|
||||
bid->status = STUB_BUSID_OTHER;
|
||||
/* release the busid lock */
|
||||
put_busid_priv(bid);
|
||||
|
||||
ret = do_rebind((char *) buf, bid);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* delete device from busid_table */
|
||||
del_match_busid((char *) buf);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
@ -317,6 +393,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);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче