Commit b55a0266 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

[PATCH] USB: lots of locking and other SMP race fixes

This is a merge of a bunch of SMP and locking fixes for the USB code
that Oliver has sent me (greg k-h) over the past few weeks.
parent 68a13be8
...@@ -655,7 +655,10 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, ...@@ -655,7 +655,10 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count,
(count - writecount) : USBLP_BUF_SIZE; (count - writecount) : USBLP_BUF_SIZE;
if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount, if (copy_from_user(usblp->writeurb->transfer_buffer, buffer + writecount,
usblp->writeurb->transfer_buffer_length)) return -EFAULT; usblp->writeurb->transfer_buffer_length)) {
up(&usblp->sem);
return writecount ? writecount : -EFAULT;
}
usblp->writeurb->dev = usblp->dev; usblp->writeurb->dev = usblp->dev;
usblp->wcomplete = 0; usblp->wcomplete = 0;
......
...@@ -152,8 +152,8 @@ static const struct class_info clas_info[] = ...@@ -152,8 +152,8 @@ static const struct class_info clas_info[] =
void usbdevfs_conn_disc_event(void) void usbdevfs_conn_disc_event(void)
{ {
wake_up(&deviceconndiscwq);
conndiscevcnt++; conndiscevcnt++;
wake_up(&deviceconndiscwq);
} }
static const char *class_decode(const int class) static const char *class_decode(const int class)
...@@ -239,6 +239,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct ...@@ -239,6 +239,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct
if (start > end) if (start > end)
return start; return start;
lock_kernel(); /* driver might be unloaded */
start += sprintf(start, format_iface, start += sprintf(start, format_iface,
desc->bInterfaceNumber, desc->bInterfaceNumber,
desc->bAlternateSetting, desc->bAlternateSetting,
...@@ -248,6 +249,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct ...@@ -248,6 +249,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct
desc->bInterfaceSubClass, desc->bInterfaceSubClass,
desc->bInterfaceProtocol, desc->bInterfaceProtocol,
iface->driver ? iface->driver->name : "(none)"); iface->driver ? iface->driver->name : "(none)");
unlock_kernel();
return start; return start;
} }
...@@ -597,6 +599,13 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct ...@@ -597,6 +599,13 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
unlock_kernel(); unlock_kernel();
return POLLIN; return POLLIN;
} }
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
kfree(st);
goto lost_race;
}
/* /*
* need to prevent the module from being unloaded, since * need to prevent the module from being unloaded, since
* proc_unregister does not call the release method and * proc_unregister does not call the release method and
...@@ -606,6 +615,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct ...@@ -606,6 +615,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
file->private_data = st; file->private_data = st;
mask = POLLIN; mask = POLLIN;
} }
lost_race:
if (file->f_mode & FMODE_READ) if (file->f_mode & FMODE_READ)
poll_wait(file, &deviceconndiscwq, wait); poll_wait(file, &deviceconndiscwq, wait);
if (st->lastev != conndiscevcnt) if (st->lastev != conndiscevcnt)
......
...@@ -361,14 +361,14 @@ static int releaseintf(struct dev_state *ps, unsigned int intf) ...@@ -361,14 +361,14 @@ static int releaseintf(struct dev_state *ps, unsigned int intf)
if (intf >= 8*sizeof(ps->ifclaimed)) if (intf >= 8*sizeof(ps->ifclaimed))
return -EINVAL; return -EINVAL;
err = -EINVAL; err = -EINVAL;
lock_kernel();
dev = ps->dev; dev = ps->dev;
down(&dev->serialize);
if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) {
iface = &dev->actconfig->interface[intf]; iface = &dev->actconfig->interface[intf];
usb_driver_release_interface(&usbdevfs_driver, iface); usb_driver_release_interface(&usbdevfs_driver, iface);
err = 0; err = 0;
} }
unlock_kernel(); up(&dev->serialize);
return err; return err;
} }
...@@ -722,14 +722,11 @@ static int proc_resetdevice(struct dev_state *ps) ...@@ -722,14 +722,11 @@ static int proc_resetdevice(struct dev_state *ps)
if (test_bit(i, &ps->ifclaimed)) if (test_bit(i, &ps->ifclaimed))
continue; continue;
if (intf->driver) { lock_kernel();
const struct usb_device_id *id; if (intf->driver && ps->dev) {
down(&intf->driver->serialize); usb_bind_driver(intf->driver,ps->dev, i);
intf->driver->disconnect(ps->dev, intf->private_data);
id = usb_match_id(ps->dev,intf,intf->driver->id_table);
intf->driver->probe(ps->dev, i, id);
up(&intf->driver->serialize);
} }
unlock_kernel();
} }
return 0; return 0;
...@@ -1092,16 +1089,17 @@ static int proc_ioctl (struct dev_state *ps, void *arg) ...@@ -1092,16 +1089,17 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
/* disconnect kernel driver from interface, leaving it unbound. */ /* disconnect kernel driver from interface, leaving it unbound. */
case USBDEVFS_DISCONNECT: case USBDEVFS_DISCONNECT:
/* this function is voodoo. without locking it is a maybe thing */
lock_kernel();
driver = ifp->driver; driver = ifp->driver;
if (driver) { if (driver) {
down (&driver->serialize);
dbg ("disconnect '%s' from dev %d interface %d", dbg ("disconnect '%s' from dev %d interface %d",
driver->name, ps->dev->devnum, ctrl.ifno); driver->name, ps->dev->devnum, ctrl.ifno);
driver->disconnect (ps->dev, ifp->private_data); usb_unbind_driver(ps->dev, ifp);
usb_driver_release_interface (driver, ifp); usb_driver_release_interface (driver, ifp);
up (&driver->serialize);
} else } else
retval = -EINVAL; retval = -EINVAL;
unlock_kernel();
break; break;
/* let kernel drivers try to (re)bind to the interface */ /* let kernel drivers try to (re)bind to the interface */
...@@ -1111,18 +1109,28 @@ static int proc_ioctl (struct dev_state *ps, void *arg) ...@@ -1111,18 +1109,28 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
/* talk directly to the interface's driver */ /* talk directly to the interface's driver */
default: default:
lock_kernel(); /* against module unload */
driver = ifp->driver; driver = ifp->driver;
if (driver == 0 || driver->ioctl == 0) if (driver == 0 || driver->ioctl == 0) {
retval = -ENOSYS; unlock_kernel();
else { retval = -ENOSYS;
if (ifp->driver->owner) } else {
if (ifp->driver->owner) {
__MOD_INC_USE_COUNT(ifp->driver->owner); __MOD_INC_USE_COUNT(ifp->driver->owner);
unlock_kernel();
}
/* ifno might usefully be passed ... */ /* ifno might usefully be passed ... */
retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf); retval = driver->ioctl (ps->dev, ctrl.ioctl_code, buf);
/* size = min_t(int, size, retval)? */ /* size = min_t(int, size, retval)? */
if (ifp->driver->owner) if (ifp->driver->owner) {
__MOD_DEC_USE_COUNT(ifp->driver->owner); __MOD_DEC_USE_COUNT(ifp->driver->owner);
} else {
unlock_kernel();
}
} }
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
} }
/* cleanup and return */ /* cleanup and return */
...@@ -1139,7 +1147,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg) ...@@ -1139,7 +1147,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg)
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{ {
struct dev_state *ps = (struct dev_state *)file->private_data; struct dev_state *ps = (struct dev_state *)file->private_data;
int ret = -ENOIOCTLCMD; int ret = -ENOTTY;
if (!(file->f_mode & FMODE_WRITE)) if (!(file->f_mode & FMODE_WRITE))
return -EPERM; return -EPERM;
......
...@@ -66,6 +66,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff ...@@ -66,6 +66,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff
start = page; start = page;
end = page + (PAGE_SIZE - 100); end = page + (PAGE_SIZE - 100);
pos = *ppos; pos = *ppos;
lock_kernel(); /* else drivers might be unloaded */
for (; tmp != &usb_driver_list; tmp = tmp->next) { for (; tmp != &usb_driver_list; tmp = tmp->next) {
struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
int minor = driver->fops ? driver->minor : -1; int minor = driver->fops ? driver->minor : -1;
...@@ -80,6 +81,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff ...@@ -80,6 +81,7 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff
break; break;
} }
} }
unlock_kernel();
if (start == page) if (start == page)
start += sprintf(start, "(none)\n"); start += sprintf(start, "(none)\n");
len = start - page; len = start - page;
......
...@@ -44,10 +44,13 @@ static int usb_open(struct inode * inode, struct file * file) ...@@ -44,10 +44,13 @@ static int usb_open(struct inode * inode, struct file * file)
spin_lock (&minor_lock); spin_lock (&minor_lock);
c = usb_minors[minor]; c = usb_minors[minor];
spin_unlock (&minor_lock);
if (!c || !(new_fops = fops_get(c))) if (!c || !(new_fops = fops_get(c))) {
spin_unlock(&minor_lock);
return err; return err;
}
spin_unlock(&minor_lock);
old_fops = file->f_op; old_fops = file->f_op;
file->f_op = new_fops; file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */ /* Curiouser and curiouser... NULL ->open() as "no device" ? */
......
...@@ -1046,8 +1046,6 @@ static void usb_hub_events(void) ...@@ -1046,8 +1046,6 @@ static void usb_hub_events(void)
static int usb_hub_thread(void *__hub) static int usb_hub_thread(void *__hub)
{ {
lock_kernel();
/* /*
* This thread doesn't need any user-level access, * This thread doesn't need any user-level access,
* so get rid of all our resources * so get rid of all our resources
...@@ -1067,8 +1065,6 @@ static int usb_hub_thread(void *__hub) ...@@ -1067,8 +1065,6 @@ static int usb_hub_thread(void *__hub)
} while (!signal_pending(current)); } while (!signal_pending(current));
dbg("usb_hub_thread exiting"); dbg("usb_hub_thread exiting");
unlock_kernel();
complete_and_exit(&khubd_exited, 0); complete_and_exit(&khubd_exited, 0);
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/smp_lock.h>
#ifdef CONFIG_USB_DEBUG #ifdef CONFIG_USB_DEBUG
#define DEBUG #define DEBUG
...@@ -117,6 +118,108 @@ void usb_scan_devices(void) ...@@ -117,6 +118,108 @@ void usb_scan_devices(void)
up (&usb_bus_list_lock); up (&usb_bus_list_lock);
} }
/**
* usb_unbind_driver - disconnects a driver from a device
* @device: usb device to be disconnected
* @intf: interface of the device to be disconnected
* Context: BKL held
*
* Handles module usage count correctly
*/
void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf)
{
struct usb_driver *driver;
void *priv;
int m;
driver = intf->driver;
priv = intf->private_data;
if (!driver)
return;
/* as soon as we increase the module use count we drop the BKL
before that we must not sleep */
if (driver->owner) {
m = try_inc_mod_count(driver->owner);
if (m == 0) {
err("Dieing driver still bound to device.\n");
return;
}
unlock_kernel();
}
down(&driver->serialize); /* if we sleep here on an umanaged driver
the holder of the lock guards against
module unload */
driver->disconnect(device, priv);
up(&driver->serialize);
if (driver->owner) {
lock_kernel();
__MOD_DEC_USE_COUNT(driver->owner);
}
}
/**
* usb_bind_driver - connect a driver to a device's interface
* @driver: device driver to be bound to a devices interface
* @dev: device to be bound
* @ifnum: index number of the interface to be used
*
* Does a save binding of a driver to a device's interface
* Returns a pointer to the drivers private description of the binding
*/
void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum)
{
int i,m;
void *private = NULL;
const struct usb_device_id *id;
struct usb_interface *interface;
if (driver->owner) {
m = try_inc_mod_count(driver->owner);
if (m == 0)
return NULL; /* this horse is dead - don't ride*/
unlock_kernel();
}
interface = &dev->actconfig->interface[ifnum];
id = driver->id_table;
/* new style driver? */
if (id) {
for (i = 0; i < interface->num_altsetting; i++) {
interface->act_altsetting = i;
id = usb_match_id(dev, interface, id);
if (id) {
down(&driver->serialize);
private = driver->probe(dev,ifnum,id);
up(&driver->serialize);
if (private != NULL)
break;
}
}
/* if driver not bound, leave defaults unchanged */
if (private == NULL)
interface->act_altsetting = 0;
} else { /* "old style" driver */
down(&driver->serialize);
private = driver->probe(dev, ifnum, NULL);
up(&driver->serialize);
}
if (driver->owner) {
lock_kernel();
__MOD_DEC_USE_COUNT(driver->owner);
}
return private;
}
/* /*
* This function is part of a depth-first search down the device tree, * This function is part of a depth-first search down the device tree,
* removing any instances of a device driver. * removing any instances of a device driver.
...@@ -136,18 +239,12 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) ...@@ -136,18 +239,12 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
if (!dev->actconfig) if (!dev->actconfig)
return; return;
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_interface *interface = &dev->actconfig->interface[i];
if (interface->driver == driver) { if (interface->driver == driver) {
if (driver->owner) usb_unbind_driver(dev, interface);
__MOD_INC_USE_COUNT(driver->owner);
down(&driver->serialize);
driver->disconnect(dev, interface->private_data);
up(&driver->serialize);
if (driver->owner)
__MOD_DEC_USE_COUNT(driver->owner);
/* if driver->disconnect didn't release the interface */ /* if driver->disconnect didn't release the interface */
if (interface->driver) if (interface->driver)
usb_driver_release_interface(driver, interface); usb_driver_release_interface(driver, interface);
...@@ -163,7 +260,7 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) ...@@ -163,7 +260,7 @@ static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev)
/** /**
* usb_deregister - unregister a USB driver * usb_deregister - unregister a USB driver
* @driver: USB operations of the driver to unregister * @driver: USB operations of the driver to unregister
* Context: !in_interrupt () * Context: !in_interrupt (), must be called with BKL held
* *
* Unlinks the specified driver from the internal USB driver list. * Unlinks the specified driver from the internal USB driver list.
* *
...@@ -528,9 +625,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) ...@@ -528,9 +625,7 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
struct list_head *tmp; struct list_head *tmp;
struct usb_interface *interface; struct usb_interface *interface;
void *private; void *private;
const struct usb_device_id *id;
struct usb_driver *driver; struct usb_driver *driver;
int i;
if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) {
err("bad find_interface_driver params"); err("bad find_interface_driver params");
...@@ -545,37 +640,12 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) ...@@ -545,37 +640,12 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
goto out_err; goto out_err;
private = NULL; private = NULL;
lock_kernel();
for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) { for (tmp = usb_driver_list.next; tmp != &usb_driver_list;) {
driver = list_entry(tmp, struct usb_driver, driver_list); driver = list_entry(tmp, struct usb_driver, driver_list);
tmp = tmp->next; tmp = tmp->next;
if (driver->owner) private = usb_bind_driver(driver, dev, ifnum);
__MOD_INC_USE_COUNT(driver->owner);
id = driver->id_table;
/* new style driver? */
if (id) {
for (i = 0; i < interface->num_altsetting; i++) {
interface->act_altsetting = i;
id = usb_match_id(dev, interface, id);
if (id) {
down(&driver->serialize);
private = driver->probe(dev,ifnum,id);
up(&driver->serialize);
if (private != NULL)
break;
}
}
/* if driver not bound, leave defaults unchanged */
if (private == NULL)
interface->act_altsetting = 0;
} else { /* "old style" driver */
down(&driver->serialize);
private = driver->probe(dev, ifnum, NULL);
up(&driver->serialize);
}
if (driver->owner)
__MOD_DEC_USE_COUNT(driver->owner);
/* probe() may have changed the config on us */ /* probe() may have changed the config on us */
interface = dev->actconfig->interface + ifnum; interface = dev->actconfig->interface + ifnum;
...@@ -583,9 +653,11 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) ...@@ -583,9 +653,11 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum)
if (private) { if (private) {
usb_driver_claim_interface(driver, interface, private); usb_driver_claim_interface(driver, interface, private);
up(&dev->serialize); up(&dev->serialize);
unlock_kernel();
return 0; return 0;
} }
} }
unlock_kernel();
out_err: out_err:
up(&dev->serialize); up(&dev->serialize);
...@@ -1121,27 +1193,22 @@ void usb_disconnect(struct usb_device **pdev) ...@@ -1121,27 +1193,22 @@ void usb_disconnect(struct usb_device **pdev)
info("USB disconnect on device %d", dev->devnum); info("USB disconnect on device %d", dev->devnum);
lock_kernel();
if (dev->actconfig) { if (dev->actconfig) {
for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_interface *interface = &dev->actconfig->interface[i];
struct usb_driver *driver = interface->driver; struct usb_driver *driver = interface->driver;
if (driver) { if (driver) {
if (driver->owner) usb_unbind_driver(dev, interface);
__MOD_INC_USE_COUNT(driver->owner);
down(&driver->serialize);
driver->disconnect(dev, interface->private_data);
up(&driver->serialize);
/* if driver->disconnect didn't release the interface */ /* if driver->disconnect didn't release the interface */
if (interface->driver) if (interface->driver)
usb_driver_release_interface(driver, interface); usb_driver_release_interface(driver, interface);
/* we don't need the driver any longer */
if (driver->owner)
__MOD_DEC_USE_COUNT(driver->owner);
} }
/* remove our device node for this interface */ /* remove our device node for this interface */
put_device(&interface->dev); put_device(&interface->dev);
} }
} }
unlock_kernel();
/* Free up all the children.. */ /* Free up all the children.. */
for (i = 0; i < USB_MAXCHILDREN; i++) { for (i = 0; i < USB_MAXCHILDREN; i++) {
...@@ -1475,6 +1542,8 @@ EXPORT_SYMBOL(usb_new_device); ...@@ -1475,6 +1542,8 @@ EXPORT_SYMBOL(usb_new_device);
EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect); EXPORT_SYMBOL(usb_disconnect);
EXPORT_SYMBOL(usb_bind_driver);
EXPORT_SYMBOL(usb_unbind_driver);
EXPORT_SYMBOL(__usb_get_extra_descriptor); EXPORT_SYMBOL(__usb_get_extra_descriptor);
......
...@@ -1756,40 +1756,40 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) ...@@ -1756,40 +1756,40 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
pdev = (struct pwc_device *)ptr; pdev = (struct pwc_device *)ptr;
if (pdev == NULL) { if (pdev == NULL) {
Err("pwc_disconnect() Called without private pointer.\n"); Err("pwc_disconnect() Called without private pointer.\n");
return; goto out_err;
} }
if (pdev->udev == NULL) { if (pdev->udev == NULL) {
Err("pwc_disconnect() already called for %p\n", pdev); Err("pwc_disconnect() already called for %p\n", pdev);
return; goto out_err;
} }
if (pdev->udev != udev) { if (pdev->udev != udev) {
Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
return; goto out_err;
} }
#ifdef PWC_MAGIC #ifdef PWC_MAGIC
if (pdev->magic != PWC_MAGIC) { if (pdev->magic != PWC_MAGIC) {
Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
return; goto out_err;
} }
#endif #endif
pdev->unplugged = 1; pdev->unplugged = 1;
if (pdev->vdev != NULL) { if (pdev->vdev != NULL) {
video_unregister_device(pdev->vdev); video_unregister_device(pdev->vdev);
if (pdev->vopen) { if (pdev->vopen) {
Info("Disconnected while device/video is open!\n"); Info("Disconnected while device/video is open!\n");
/* Wake up any processes that might be waiting for /* Wake up any processes that might be waiting for
a frame, let them return an error condition a frame, let them return an error condition
*/ */
wake_up(&pdev->frameq); wake_up(&pdev->frameq);
/* Wait until we get a 'go' from _close(). This used /* Wait until we get a 'go' from _close(). This used
to have a gigantic race condition, since we kfree() to have a gigantic race condition, since we kfree()
stuff here, but we have to wait until close() stuff here, but we have to wait until close()
is finished. is finished.
*/ */
Trace(TRACE_PROBE, "Sleeping on remove_ok.\n"); Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
add_wait_queue(&pdev->remove_ok, &wait); add_wait_queue(&pdev->remove_ok, &wait);
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
...@@ -1815,6 +1815,7 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr) ...@@ -1815,6 +1815,7 @@ static void usb_pwc_disconnect(struct usb_device *udev, void *ptr)
device_hint[hint].pdev = NULL; device_hint[hint].pdev = NULL;
pdev->udev = NULL; pdev->udev = NULL;
out_err:
unlock_kernel(); unlock_kernel();
kfree(pdev); kfree(pdev);
} }
......
...@@ -431,6 +431,10 @@ extern void usb_free_dev(struct usb_device *); ...@@ -431,6 +431,10 @@ extern void usb_free_dev(struct usb_device *);
/* for when layers above USB add new non-USB drivers */ /* for when layers above USB add new non-USB drivers */
extern void usb_scan_devices(void); extern void usb_scan_devices(void);
/* for probe/disconnect with correct module usage counting */
void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum);
void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf);
/* mostly for devices emulating SCSI over USB */ /* mostly for devices emulating SCSI over USB */
extern int usb_reset_device(struct usb_device *dev); extern int usb_reset_device(struct usb_device *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment