USB: prevent char device open/deregister race

This patch (as908) adds central protection in usbcore for the
prototypical race between opening and unregistering a char device.
The spinlock used to protect the minor-numbers array is replaced with
an rwsem, which can remain locked across a call to a driver's open()
method.  This guarantees that open() and deregister() will be mutually
exclusive.

The private locks currently used in several individual drivers for
this purpose are no longer necessary, and the patch removes them.  The
following USB drivers are affected: usblcd, idmouse, auerswald,
legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and
usb-skeleton.

As a side effect of this change, usb_deregister_dev() must not be
called while holding a lock that is acquired by open().  Unfortunately
a number of drivers do this, but luckily the solution is simple: call
usb_deregister_dev() before acquiring the lock.

In addition to these changes (and their consequent code
simplifications), the patch fixes a use-after-free bug in adutux and a
race between open() and release() in iowarrior.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Stern 2007-05-22 11:46:41 -04:00 committed by Greg Kroah-Hartman
parent 55e5fdfa54
commit d4ead16f50
12 changed files with 75 additions and 228 deletions

View File

@ -16,15 +16,15 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/rwsem.h>
#include <linux/usb.h> #include <linux/usb.h>
#include "usb.h" #include "usb.h"
#define MAX_USB_MINORS 256 #define MAX_USB_MINORS 256
static const struct file_operations *usb_minors[MAX_USB_MINORS]; static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DEFINE_SPINLOCK(minor_lock); static DECLARE_RWSEM(minor_rwsem);
static int usb_open(struct inode * inode, struct file * file) static int usb_open(struct inode * inode, struct file * file)
{ {
@ -33,14 +33,11 @@ static int usb_open(struct inode * inode, struct file * file)
int err = -ENODEV; int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL; const struct file_operations *old_fops, *new_fops = NULL;
spin_lock (&minor_lock); down_read(&minor_rwsem);
c = usb_minors[minor]; c = usb_minors[minor];
if (!c || !(new_fops = fops_get(c))) { if (!c || !(new_fops = fops_get(c)))
spin_unlock(&minor_lock); goto done;
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;
@ -52,6 +49,8 @@ static int usb_open(struct inode * inode, struct file * file)
file->f_op = fops_get(old_fops); file->f_op = fops_get(old_fops);
} }
fops_put(old_fops); fops_put(old_fops);
done:
up_read(&minor_rwsem);
return err; return err;
} }
@ -166,7 +165,7 @@ int usb_register_dev(struct usb_interface *intf,
if (class_driver->fops == NULL) if (class_driver->fops == NULL)
goto exit; goto exit;
spin_lock (&minor_lock); down_write(&minor_rwsem);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor]) if (usb_minors[minor])
continue; continue;
@ -176,7 +175,7 @@ int usb_register_dev(struct usb_interface *intf,
retval = 0; retval = 0;
break; break;
} }
spin_unlock (&minor_lock); up_write(&minor_rwsem);
if (retval) if (retval)
goto exit; goto exit;
@ -197,9 +196,9 @@ int usb_register_dev(struct usb_interface *intf,
intf->usb_dev = device_create(usb_class->class, &intf->dev, intf->usb_dev = device_create(usb_class->class, &intf->dev,
MKDEV(USB_MAJOR, minor), "%s", temp); MKDEV(USB_MAJOR, minor), "%s", temp);
if (IS_ERR(intf->usb_dev)) { if (IS_ERR(intf->usb_dev)) {
spin_lock (&minor_lock); down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL; usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock); up_write(&minor_rwsem);
retval = PTR_ERR(intf->usb_dev); retval = PTR_ERR(intf->usb_dev);
} }
exit: exit:
@ -236,9 +235,9 @@ void usb_deregister_dev(struct usb_interface *intf,
dbg ("removing %d minor", intf->minor); dbg ("removing %d minor", intf->minor);
spin_lock (&minor_lock); down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL; usb_minors[intf->minor] = NULL;
spin_unlock (&minor_lock); up_write(&minor_rwsem);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
@ -247,5 +246,3 @@ void usb_deregister_dev(struct usb_interface *intf,
destroy_usb_class(); destroy_usb_class();
} }
EXPORT_SYMBOL(usb_deregister_dev); EXPORT_SYMBOL(usb_deregister_dev);

View File

@ -108,8 +108,6 @@ struct adu_device {
struct urb* interrupt_out_urb; struct urb* interrupt_out_urb;
}; };
/* prevent races between open() and disconnect */
static DEFINE_MUTEX(disconnect_mutex);
static struct usb_driver adu_driver; static struct usb_driver adu_driver;
static void adu_debug_data(int level, const char *function, int size, static void adu_debug_data(int level, const char *function, int size,
@ -256,8 +254,6 @@ static int adu_open(struct inode *inode, struct file *file)
subminor = iminor(inode); subminor = iminor(inode);
mutex_lock(&disconnect_mutex);
interface = usb_find_interface(&adu_driver, subminor); interface = usb_find_interface(&adu_driver, subminor);
if (!interface) { if (!interface) {
err("%s - error, can't find device for minor %d", err("%s - error, can't find device for minor %d",
@ -306,7 +302,6 @@ static int adu_open(struct inode *inode, struct file *file)
up(&dev->sem); up(&dev->sem);
exit_no_device: exit_no_device:
mutex_unlock(&disconnect_mutex);
dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
return retval; return retval;
@ -318,12 +313,6 @@ static int adu_release_internal(struct adu_device *dev)
dbg(2," %s : enter", __FUNCTION__); dbg(2," %s : enter", __FUNCTION__);
if (dev->udev == NULL) {
/* the device was unplugged before the file was released */
adu_delete(dev);
goto exit;
}
/* decrement our usage count for the device */ /* decrement our usage count for the device */
--dev->open_count; --dev->open_count;
dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
@ -332,7 +321,6 @@ static int adu_release_internal(struct adu_device *dev)
dev->open_count = 0; dev->open_count = 0;
} }
exit:
dbg(2," %s : leave", __FUNCTION__); dbg(2," %s : leave", __FUNCTION__);
return retval; return retval;
} }
@ -367,8 +355,15 @@ static int adu_release(struct inode *inode, struct file *file)
goto exit; goto exit;
} }
/* do the work */ if (dev->udev == NULL) {
retval = adu_release_internal(dev); /* the device was unplugged before the file was released */
up(&dev->sem);
adu_delete(dev);
dev = NULL;
} else {
/* do the work */
retval = adu_release_internal(dev);
}
exit: exit:
if (dev) if (dev)
@ -831,19 +826,17 @@ static void adu_disconnect(struct usb_interface *interface)
dbg(2," %s : enter", __FUNCTION__); dbg(2," %s : enter", __FUNCTION__);
mutex_lock(&disconnect_mutex); /* not interruptible */
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
down(&dev->sem); /* not interruptible */
minor = dev->minor; minor = dev->minor;
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &adu_class); usb_deregister_dev(interface, &adu_class);
dev->minor = 0; dev->minor = 0;
down(&dev->sem); /* not interruptible */
/* if the device is not opened, then we clean up right now */ /* if the device is not opened, then we clean up right now */
dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
if (!dev->open_count) { if (!dev->open_count) {
@ -854,8 +847,6 @@ static void adu_disconnect(struct usb_interface *interface)
up(&dev->sem); up(&dev->sem);
} }
mutex_unlock(&disconnect_mutex);
dev_info(&interface->dev, "ADU device adutux%d now disconnected", dev_info(&interface->dev, "ADU device adutux%d now disconnected",
(minor - ADU_MINOR_BASE)); (minor - ADU_MINOR_BASE));

View File

@ -2034,12 +2034,12 @@ static void auerswald_disconnect (struct usb_interface *intf)
if (!cp) if (!cp)
return; return;
down (&cp->mutex);
info ("device /dev/%s now disconnecting", cp->name);
/* give back our USB minor number */ /* give back our USB minor number */
usb_deregister_dev(intf, &auerswald_class); usb_deregister_dev(intf, &auerswald_class);
down (&cp->mutex);
info ("device /dev/%s now disconnecting", cp->name);
/* Stop the interrupt endpoint */ /* Stop the interrupt endpoint */
auerswald_int_release (cp); auerswald_int_release (cp);

View File

@ -119,9 +119,6 @@ static struct usb_driver idmouse_driver = {
.id_table = idmouse_table, .id_table = idmouse_table,
}; };
/* prevent races between open() and disconnect() */
static DEFINE_MUTEX(disconnect_mutex);
static int idmouse_create_image(struct usb_idmouse *dev) static int idmouse_create_image(struct usb_idmouse *dev)
{ {
int bytes_read; int bytes_read;
@ -211,21 +208,15 @@ static int idmouse_open(struct inode *inode, struct file *file)
struct usb_interface *interface; struct usb_interface *interface;
int result; int result;
/* prevent disconnects */
mutex_lock(&disconnect_mutex);
/* get the interface from minor number and driver information */ /* get the interface from minor number and driver information */
interface = usb_find_interface (&idmouse_driver, iminor (inode)); interface = usb_find_interface (&idmouse_driver, iminor (inode));
if (!interface) { if (!interface)
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
}
/* get the device information block from the interface */ /* get the device information block from the interface */
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev)
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
}
/* lock this device */ /* lock this device */
down(&dev->sem); down(&dev->sem);
@ -255,9 +246,6 @@ error:
/* unlock this device */ /* unlock this device */
up(&dev->sem); up(&dev->sem);
/* unlock the disconnect semaphore */
mutex_unlock(&disconnect_mutex);
return result; return result;
} }
@ -265,15 +253,10 @@ static int idmouse_release(struct inode *inode, struct file *file)
{ {
struct usb_idmouse *dev; struct usb_idmouse *dev;
/* prevent a race condition with open() */
mutex_lock(&disconnect_mutex);
dev = file->private_data; dev = file->private_data;
if (dev == NULL) { if (dev == NULL)
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
}
/* lock our device */ /* lock our device */
down(&dev->sem); down(&dev->sem);
@ -281,7 +264,6 @@ static int idmouse_release(struct inode *inode, struct file *file)
/* are we really open? */ /* are we really open? */
if (dev->open <= 0) { if (dev->open <= 0) {
up(&dev->sem); up(&dev->sem);
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
} }
@ -291,12 +273,9 @@ static int idmouse_release(struct inode *inode, struct file *file)
/* the device was unplugged before the file was released */ /* the device was unplugged before the file was released */
up(&dev->sem); up(&dev->sem);
idmouse_delete(dev); idmouse_delete(dev);
mutex_unlock(&disconnect_mutex); } else {
return 0; up(&dev->sem);
} }
up(&dev->sem);
mutex_unlock(&disconnect_mutex);
return 0; return 0;
} }
@ -391,30 +370,27 @@ static void idmouse_disconnect(struct usb_interface *interface)
{ {
struct usb_idmouse *dev; struct usb_idmouse *dev;
/* prevent races with open() */
mutex_lock(&disconnect_mutex);
/* get device structure */ /* get device structure */
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
/* lock it */
down(&dev->sem);
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &idmouse_class); usb_deregister_dev(interface, &idmouse_class);
/* lock it */
down(&dev->sem);
/* prevent device read, write and ioctl */ /* prevent device read, write and ioctl */
dev->present = 0; dev->present = 0;
/* unlock */
up(&dev->sem);
/* if the device is opened, idmouse_release will clean this up */ /* if the device is opened, idmouse_release will clean this up */
if (!dev->open) if (!dev->open) {
up(&dev->sem);
idmouse_delete(dev); idmouse_delete(dev);
} else {
mutex_unlock(&disconnect_mutex); /* unlock */
up(&dev->sem);
}
info("%s disconnected", DRIVER_DESC); info("%s disconnected", DRIVER_DESC);
} }

View File

@ -100,8 +100,6 @@ struct iowarrior {
/*--------------*/ /*--------------*/
/* globals */ /* globals */
/*--------------*/ /*--------------*/
/* prevent races between open() and disconnect() */
static DECLARE_MUTEX(disconnect_sem);
/* /*
* USB spec identifies 5 second timeouts. * USB spec identifies 5 second timeouts.
@ -600,22 +598,18 @@ static int iowarrior_open(struct inode *inode, struct file *file)
subminor = iminor(inode); subminor = iminor(inode);
/* prevent disconnects */
down(&disconnect_sem);
interface = usb_find_interface(&iowarrior_driver, subminor); interface = usb_find_interface(&iowarrior_driver, subminor);
if (!interface) { if (!interface) {
err("%s - error, can't find device for minor %d", __FUNCTION__, err("%s - error, can't find device for minor %d", __FUNCTION__,
subminor); subminor);
retval = -ENODEV; return -ENODEV;
goto out;
} }
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev)
retval = -ENODEV; return -ENODEV;
goto out;
} mutex_lock(&dev->mutex);
/* Only one process can open each device, no sharing. */ /* Only one process can open each device, no sharing. */
if (dev->opened) { if (dev->opened) {
@ -636,7 +630,7 @@ static int iowarrior_open(struct inode *inode, struct file *file)
retval = 0; retval = 0;
out: out:
up(&disconnect_sem); mutex_unlock(&dev->mutex);
return retval; return retval;
} }
@ -868,19 +862,16 @@ static void iowarrior_disconnect(struct usb_interface *interface)
struct iowarrior *dev; struct iowarrior *dev;
int minor; int minor;
/* prevent races with open() */
down(&disconnect_sem);
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
mutex_lock(&dev->mutex);
minor = dev->minor; minor = dev->minor;
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &iowarrior_class); usb_deregister_dev(interface, &iowarrior_class);
mutex_lock(&dev->mutex);
/* prevent device read, write and ioctl */ /* prevent device read, write and ioctl */
dev->present = 0; dev->present = 0;
@ -898,7 +889,6 @@ static void iowarrior_disconnect(struct usb_interface *interface)
/* no process is using the device, cleanup now */ /* no process is using the device, cleanup now */
iowarrior_delete(dev); iowarrior_delete(dev);
} }
up(&disconnect_sem);
dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n",
minor - IOWARRIOR_MINOR_BASE); minor - IOWARRIOR_MINOR_BASE);

View File

@ -176,9 +176,6 @@ struct ld_usb {
int interrupt_out_busy; int interrupt_out_busy;
}; };
/* prevent races between open() and disconnect() */
static DEFINE_MUTEX(disconnect_mutex);
static struct usb_driver ld_usb_driver; static struct usb_driver ld_usb_driver;
/** /**
@ -298,35 +295,28 @@ static int ld_usb_open(struct inode *inode, struct file *file)
{ {
struct ld_usb *dev; struct ld_usb *dev;
int subminor; int subminor;
int retval = 0; int retval;
struct usb_interface *interface; struct usb_interface *interface;
nonseekable_open(inode, file); nonseekable_open(inode, file);
subminor = iminor(inode); subminor = iminor(inode);
mutex_lock(&disconnect_mutex);
interface = usb_find_interface(&ld_usb_driver, subminor); interface = usb_find_interface(&ld_usb_driver, subminor);
if (!interface) { if (!interface) {
err("%s - error, can't find device for minor %d\n", err("%s - error, can't find device for minor %d\n",
__FUNCTION__, subminor); __FUNCTION__, subminor);
retval = -ENODEV; return -ENODEV;
goto unlock_disconnect_exit;
} }
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev)
retval = -ENODEV; return -ENODEV;
goto unlock_disconnect_exit;
}
/* lock this device */ /* lock this device */
if (down_interruptible(&dev->sem)) { if (down_interruptible(&dev->sem))
retval = -ERESTARTSYS; return -ERESTARTSYS;
goto unlock_disconnect_exit;
}
/* allow opening only once */ /* allow opening only once */
if (dev->open_count) { if (dev->open_count) {
@ -366,9 +356,6 @@ static int ld_usb_open(struct inode *inode, struct file *file)
unlock_exit: unlock_exit:
up(&dev->sem); up(&dev->sem);
unlock_disconnect_exit:
mutex_unlock(&disconnect_mutex);
return retval; return retval;
} }
@ -766,18 +753,16 @@ static void ld_usb_disconnect(struct usb_interface *intf)
struct ld_usb *dev; struct ld_usb *dev;
int minor; int minor;
mutex_lock(&disconnect_mutex);
dev = usb_get_intfdata(intf); dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
down(&dev->sem);
minor = intf->minor; minor = intf->minor;
/* give back our minor */ /* give back our minor */
usb_deregister_dev(intf, &ld_usb_class); usb_deregister_dev(intf, &ld_usb_class);
down(&dev->sem);
/* if the device is not opened, then we clean up right now */ /* if the device is not opened, then we clean up right now */
if (!dev->open_count) { if (!dev->open_count) {
up(&dev->sem); up(&dev->sem);
@ -787,8 +772,6 @@ static void ld_usb_disconnect(struct usb_interface *intf)
up(&dev->sem); up(&dev->sem);
} }
mutex_unlock(&disconnect_mutex);
dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", dev_info(&intf->dev, "LD USB Device #%d now disconnected\n",
(minor - USB_LD_MINOR_BASE)); (minor - USB_LD_MINOR_BASE));
} }

View File

@ -254,9 +254,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_devic
static void tower_disconnect (struct usb_interface *interface); static void tower_disconnect (struct usb_interface *interface);
/* prevent races between open() and disconnect */
static DEFINE_MUTEX (disconnect_mutex);
/* file operations needed when we register this driver */ /* file operations needed when we register this driver */
static const struct file_operations tower_fops = { static const struct file_operations tower_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -344,28 +341,26 @@ static int tower_open (struct inode *inode, struct file *file)
nonseekable_open(inode, file); nonseekable_open(inode, file);
subminor = iminor(inode); subminor = iminor(inode);
mutex_lock (&disconnect_mutex);
interface = usb_find_interface (&tower_driver, subminor); interface = usb_find_interface (&tower_driver, subminor);
if (!interface) { if (!interface) {
err ("%s - error, can't find device for minor %d", err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor); __FUNCTION__, subminor);
retval = -ENODEV; retval = -ENODEV;
goto unlock_disconnect_exit; goto exit;
} }
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev) {
retval = -ENODEV; retval = -ENODEV;
goto unlock_disconnect_exit; goto exit;
} }
/* lock this device */ /* lock this device */
if (down_interruptible (&dev->sem)) { if (down_interruptible (&dev->sem)) {
retval = -ERESTARTSYS; retval = -ERESTARTSYS;
goto unlock_disconnect_exit; goto exit;
} }
/* allow opening only once */ /* allow opening only once */
@ -421,9 +416,7 @@ static int tower_open (struct inode *inode, struct file *file)
unlock_exit: unlock_exit:
up (&dev->sem); up (&dev->sem);
unlock_disconnect_exit: exit:
mutex_unlock (&disconnect_mutex);
dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
return retval; return retval;
@ -993,19 +986,16 @@ static void tower_disconnect (struct usb_interface *interface)
dbg(2, "%s: enter", __FUNCTION__); dbg(2, "%s: enter", __FUNCTION__);
mutex_lock (&disconnect_mutex);
dev = usb_get_intfdata (interface); dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL); usb_set_intfdata (interface, NULL);
down (&dev->sem);
minor = dev->minor; minor = dev->minor;
/* give back our minor */ /* give back our minor */
usb_deregister_dev (interface, &tower_class); usb_deregister_dev (interface, &tower_class);
down (&dev->sem);
/* if the device is not opened, then we clean up right now */ /* if the device is not opened, then we clean up right now */
if (!dev->open_count) { if (!dev->open_count) {
up (&dev->sem); up (&dev->sem);
@ -1015,8 +1005,6 @@ static void tower_disconnect (struct usb_interface *interface)
up (&dev->sem); up (&dev->sem);
} }
mutex_unlock (&disconnect_mutex);
info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
dbg(2, "%s: leave", __FUNCTION__); dbg(2, "%s: leave", __FUNCTION__);

View File

@ -72,8 +72,6 @@ MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES
static struct usb_driver sisusb_driver; static struct usb_driver sisusb_driver;
DEFINE_MUTEX(disconnect_mutex);
static void static void
sisusb_free_buffers(struct sisusb_usb_data *sisusb) sisusb_free_buffers(struct sisusb_usb_data *sisusb)
{ {
@ -2511,31 +2509,24 @@ sisusb_open(struct inode *inode, struct file *file)
struct usb_interface *interface; struct usb_interface *interface;
int subminor = iminor(inode); int subminor = iminor(inode);
mutex_lock(&disconnect_mutex);
if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
printk(KERN_ERR "sisusb[%d]: Failed to find interface\n", printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
subminor); subminor);
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
} }
if (!(sisusb = usb_get_intfdata(interface))) { if (!(sisusb = usb_get_intfdata(interface)))
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
}
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
if (!sisusb->present || !sisusb->ready) { if (!sisusb->present || !sisusb->ready) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
} }
if (sisusb->isopen) { if (sisusb->isopen) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return -EBUSY; return -EBUSY;
} }
@ -2543,7 +2534,6 @@ sisusb_open(struct inode *inode, struct file *file)
if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) { if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
if (sisusb_init_gfxdevice(sisusb, 0)) { if (sisusb_init_gfxdevice(sisusb, 0)) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
printk(KERN_ERR printk(KERN_ERR
"sisusbvga[%d]: Failed to initialize " "sisusbvga[%d]: Failed to initialize "
"device\n", "device\n",
@ -2552,7 +2542,6 @@ sisusb_open(struct inode *inode, struct file *file)
} }
} else { } else {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
printk(KERN_ERR printk(KERN_ERR
"sisusbvga[%d]: Device not attached to " "sisusbvga[%d]: Device not attached to "
"USB 2.0 hub\n", "USB 2.0 hub\n",
@ -2570,8 +2559,6 @@ sisusb_open(struct inode *inode, struct file *file)
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return 0; return 0;
} }
@ -2601,12 +2588,8 @@ sisusb_release(struct inode *inode, struct file *file)
struct sisusb_usb_data *sisusb; struct sisusb_usb_data *sisusb;
int myminor; int myminor;
mutex_lock(&disconnect_mutex); if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
mutex_unlock(&disconnect_mutex);
return -ENODEV; return -ENODEV;
}
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
@ -2626,8 +2609,6 @@ sisusb_release(struct inode *inode, struct file *file)
/* decrement the usage count on our device */ /* decrement the usage count on our device */
kref_put(&sisusb->kref, sisusb_delete); kref_put(&sisusb->kref, sisusb_delete);
mutex_unlock(&disconnect_mutex);
return 0; return 0;
} }
@ -3383,12 +3364,9 @@ static void sisusb_disconnect(struct usb_interface *intf)
sisusb_console_exit(sisusb); sisusb_console_exit(sisusb);
#endif #endif
/* The above code doesn't need the disconnect minor = sisusb->minor;
* semaphore to be down; its meaning is to
* protect all other routines from the disconnect usb_deregister_dev(intf, &usb_sisusb_class);
* case, not the other way round.
*/
mutex_lock(&disconnect_mutex);
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
@ -3396,12 +3374,8 @@ static void sisusb_disconnect(struct usb_interface *intf)
if (!sisusb_wait_all_out_complete(sisusb)) if (!sisusb_wait_all_out_complete(sisusb))
sisusb_kill_all_busy(sisusb); sisusb_kill_all_busy(sisusb);
minor = sisusb->minor;
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
usb_deregister_dev(intf, &usb_sisusb_class);
#ifdef SISUSB_OLD_CONFIG_COMPAT #ifdef SISUSB_OLD_CONFIG_COMPAT
if (sisusb->ioctl32registered) { if (sisusb->ioctl32registered) {
int ret; int ret;
@ -3426,8 +3400,6 @@ static void sisusb_disconnect(struct usb_interface *intf)
/* decrement our usage count */ /* decrement our usage count */
kref_put(&sisusb->kref, sisusb_delete); kref_put(&sisusb->kref, sisusb_delete);
mutex_unlock(&disconnect_mutex);
printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor); printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
} }

View File

@ -214,18 +214,13 @@ sisusbcon_init(struct vc_data *c, int init)
* are set up/restored. * are set up/restored.
*/ */
mutex_lock(&disconnect_mutex); if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
mutex_unlock(&disconnect_mutex);
return; return;
}
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
if (!sisusb_sisusb_valid(sisusb)) { if (!sisusb_sisusb_valid(sisusb)) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return; return;
} }
@ -264,8 +259,6 @@ sisusbcon_init(struct vc_data *c, int init)
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
if (init) { if (init) {
c->vc_cols = cols; c->vc_cols = cols;
c->vc_rows = rows; c->vc_rows = rows;
@ -284,12 +277,8 @@ sisusbcon_deinit(struct vc_data *c)
* and others, ie not under our control. * and others, ie not under our control.
*/ */
mutex_lock(&disconnect_mutex); if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
mutex_unlock(&disconnect_mutex);
return; return;
}
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
@ -314,8 +303,6 @@ sisusbcon_deinit(struct vc_data *c)
/* decrement the usage count on our sisusb */ /* decrement the usage count on our sisusb */
kref_put(&sisusb->kref, sisusb_delete); kref_put(&sisusb->kref, sisusb_delete);
mutex_unlock(&disconnect_mutex);
} }
/* interface routine */ /* interface routine */
@ -1490,14 +1477,11 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
{ {
int i, ret, minor = sisusb->minor; int i, ret, minor = sisusb->minor;
mutex_lock(&disconnect_mutex);
mutex_lock(&sisusb->lock); mutex_lock(&sisusb->lock);
/* Erm.. that should not happen */ /* Erm.. that should not happen */
if (sisusb->haveconsole || !sisusb->SiS_Pr) { if (sisusb->haveconsole || !sisusb->SiS_Pr) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return 1; return 1;
} }
@ -1508,14 +1492,12 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
first > MAX_NR_CONSOLES || first > MAX_NR_CONSOLES ||
last > MAX_NR_CONSOLES) { last > MAX_NR_CONSOLES) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return 1; return 1;
} }
/* If gfxcore not initialized or no consoles given, quit graciously */ /* If gfxcore not initialized or no consoles given, quit graciously */
if (!sisusb->gfxinit || first < 1 || last < 1) { if (!sisusb->gfxinit || first < 1 || last < 1) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
return 0; return 0;
} }
@ -1526,7 +1508,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
/* Set up text mode (and upload default font) */ /* Set up text mode (and upload default font) */
if (sisusb_reset_text_mode(sisusb, 1)) { if (sisusb_reset_text_mode(sisusb, 1)) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
printk(KERN_ERR printk(KERN_ERR
"sisusbvga[%d]: Failed to set up text mode\n", "sisusbvga[%d]: Failed to set up text mode\n",
minor); minor);
@ -1550,7 +1531,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
/* Allocate screen buffer */ /* Allocate screen buffer */
if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
printk(KERN_ERR printk(KERN_ERR
"sisusbvga[%d]: Failed to allocate screen buffer\n", "sisusbvga[%d]: Failed to allocate screen buffer\n",
minor); minor);
@ -1558,7 +1538,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
} }
mutex_unlock(&sisusb->lock); mutex_unlock(&sisusb->lock);
mutex_unlock(&disconnect_mutex);
/* Now grab the desired console(s) */ /* Now grab the desired console(s) */
ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);

View File

@ -808,8 +808,6 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] =
{ 0x2b,0xc2, 35} /* 0x71 768@576@60 */ { 0x2b,0xc2, 35} /* 0x71 768@576@60 */
}; };
extern struct mutex disconnect_mutex;
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);

View File

@ -51,7 +51,6 @@ struct usb_lcd {
#define USB_LCD_CONCURRENT_WRITES 5 #define USB_LCD_CONCURRENT_WRITES 5
static struct usb_driver lcd_driver; static struct usb_driver lcd_driver;
static DEFINE_MUTEX(usb_lcd_open_mutex);
static void lcd_delete(struct kref *kref) static void lcd_delete(struct kref *kref)
@ -69,24 +68,19 @@ static int lcd_open(struct inode *inode, struct file *file)
struct usb_lcd *dev; struct usb_lcd *dev;
struct usb_interface *interface; struct usb_interface *interface;
int subminor; int subminor;
int retval = 0;
subminor = iminor(inode); subminor = iminor(inode);
mutex_lock(&usb_lcd_open_mutex);
interface = usb_find_interface(&lcd_driver, subminor); interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) { if (!interface) {
err ("USBLCD: %s - error, can't find device for minor %d", err ("USBLCD: %s - error, can't find device for minor %d",
__FUNCTION__, subminor); __FUNCTION__, subminor);
retval = -ENODEV; return -ENODEV;
goto exit;
} }
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev)
retval = -ENODEV; return -ENODEV;
goto exit;
}
/* increment our usage count for the device */ /* increment our usage count for the device */
kref_get(&dev->kref); kref_get(&dev->kref);
@ -94,9 +88,7 @@ static int lcd_open(struct inode *inode, struct file *file)
/* save our object in the file's private structure */ /* save our object in the file's private structure */
file->private_data = dev; file->private_data = dev;
exit: return 0;
mutex_unlock(&usb_lcd_open_mutex);
return retval;
} }
static int lcd_release(struct inode *inode, struct file *file) static int lcd_release(struct inode *inode, struct file *file)
@ -363,17 +355,12 @@ static void lcd_disconnect(struct usb_interface *interface)
struct usb_lcd *dev; struct usb_lcd *dev;
int minor = interface->minor; int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
mutex_lock(&usb_lcd_open_mutex);
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &lcd_class); usb_deregister_dev(interface, &lcd_class);
mutex_unlock(&usb_lcd_open_mutex);
/* decrement our usage count */ /* decrement our usage count */
kref_put(&dev->kref, lcd_delete); kref_put(&dev->kref, lcd_delete);

View File

@ -34,9 +34,6 @@ static struct usb_device_id skel_table [] = {
}; };
MODULE_DEVICE_TABLE(usb, skel_table); MODULE_DEVICE_TABLE(usb, skel_table);
/* to prevent a race between open and disconnect */
static DEFINE_MUTEX(skel_open_lock);
/* Get a minor range for your devices from the usb maintainer */ /* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192 #define USB_SKEL_MINOR_BASE 192
@ -83,10 +80,8 @@ static int skel_open(struct inode *inode, struct file *file)
subminor = iminor(inode); subminor = iminor(inode);
mutex_lock(&skel_open_lock);
interface = usb_find_interface(&skel_driver, subminor); interface = usb_find_interface(&skel_driver, subminor);
if (!interface) { if (!interface) {
mutex_unlock(&skel_open_lock);
err ("%s - error, can't find device for minor %d", err ("%s - error, can't find device for minor %d",
__FUNCTION__, subminor); __FUNCTION__, subminor);
retval = -ENODEV; retval = -ENODEV;
@ -95,15 +90,12 @@ static int skel_open(struct inode *inode, struct file *file)
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
if (!dev) { if (!dev) {
mutex_unlock(&skel_open_lock);
retval = -ENODEV; retval = -ENODEV;
goto exit; goto exit;
} }
/* increment our usage count for the device */ /* increment our usage count for the device */
kref_get(&dev->kref); kref_get(&dev->kref);
/* now we can drop the lock */
mutex_unlock(&skel_open_lock);
/* prevent the device from being autosuspended */ /* prevent the device from being autosuspended */
retval = usb_autopm_get_interface(interface); retval = usb_autopm_get_interface(interface);
@ -368,23 +360,17 @@ static void skel_disconnect(struct usb_interface *interface)
struct usb_skel *dev; struct usb_skel *dev;
int minor = interface->minor; int minor = interface->minor;
/* prevent skel_open() from racing skel_disconnect() */
mutex_lock(&skel_open_lock);
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL); usb_set_intfdata(interface, NULL);
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &skel_class); usb_deregister_dev(interface, &skel_class);
mutex_unlock(&skel_open_lock);
/* prevent more I/O from starting */ /* prevent more I/O from starting */
mutex_lock(&dev->io_mutex); mutex_lock(&dev->io_mutex);
dev->interface = NULL; dev->interface = NULL;
mutex_unlock(&dev->io_mutex); mutex_unlock(&dev->io_mutex);
/* decrement our usage count */ /* decrement our usage count */
kref_put(&dev->kref, skel_delete); kref_put(&dev->kref, skel_delete);