USB: fix locking in idmouse

Pete caused me to lock at buggy drivers in this respect. The idmouse has
a race between open and disconnect. This patch

- solves the open/disconnect race
- switches locking to mutexes

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Oliver Neukum 2007-10-23 14:23:13 +02:00 committed by Greg Kroah-Hartman
parent 439a903a96
commit 54d2bc068f

View file

@ -66,6 +66,7 @@ static struct usb_device_id idmouse_table[] = {
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000) USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)
MODULE_DEVICE_TABLE(usb, idmouse_table); MODULE_DEVICE_TABLE(usb, idmouse_table);
static DEFINE_MUTEX(open_disc_mutex);
/* structure to hold all of our device specific stuff */ /* structure to hold all of our device specific stuff */
struct usb_idmouse { struct usb_idmouse {
@ -80,7 +81,7 @@ struct usb_idmouse {
int open; /* if the port is open or not */ int open; /* if the port is open or not */
int present; /* if the device is not disconnected */ int present; /* if the device is not disconnected */
struct semaphore sem; /* locks this structure */ struct mutex lock; /* locks this structure */
}; };
@ -213,13 +214,17 @@ static int idmouse_open(struct inode *inode, struct file *file)
if (!interface) if (!interface)
return -ENODEV; return -ENODEV;
mutex_lock(&open_disc_mutex);
/* 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(&open_disc_mutex);
return -ENODEV; return -ENODEV;
}
/* lock this device */ /* lock this device */
down(&dev->sem); mutex_lock(&dev->lock);
mutex_unlock(&open_disc_mutex);
/* check if already open */ /* check if already open */
if (dev->open) { if (dev->open) {
@ -245,7 +250,7 @@ static int idmouse_open(struct inode *inode, struct file *file)
error: error:
/* unlock this device */ /* unlock this device */
up(&dev->sem); mutex_unlock(&dev->lock);
return result; return result;
} }
@ -258,12 +263,14 @@ static int idmouse_release(struct inode *inode, struct file *file)
if (dev == NULL) if (dev == NULL)
return -ENODEV; return -ENODEV;
mutex_lock(&open_disc_mutex);
/* lock our device */ /* lock our device */
down(&dev->sem); mutex_lock(&dev->lock);
/* are we really open? */ /* are we really open? */
if (dev->open <= 0) { if (dev->open <= 0) {
up(&dev->sem); mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
return -ENODEV; return -ENODEV;
} }
@ -271,10 +278,12 @@ static int idmouse_release(struct inode *inode, struct file *file)
if (!dev->present) { if (!dev->present) {
/* the device was unplugged before the file was released */ /* the device was unplugged before the file was released */
up(&dev->sem); mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
idmouse_delete(dev); idmouse_delete(dev);
} else { } else {
up(&dev->sem); mutex_unlock(&dev->lock);
mutex_unlock(&open_disc_mutex);
} }
return 0; return 0;
} }
@ -286,18 +295,18 @@ static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count
int result; int result;
/* lock this object */ /* lock this object */
down(&dev->sem); mutex_lock(&dev->lock);
/* verify that the device wasn't unplugged */ /* verify that the device wasn't unplugged */
if (!dev->present) { if (!dev->present) {
up(&dev->sem); mutex_unlock(&dev->lock);
return -ENODEV; return -ENODEV;
} }
result = simple_read_from_buffer(buffer, count, ppos, result = simple_read_from_buffer(buffer, count, ppos,
dev->bulk_in_buffer, IMGSIZE); dev->bulk_in_buffer, IMGSIZE);
/* unlock the device */ /* unlock the device */
up(&dev->sem); mutex_unlock(&dev->lock);
return result; return result;
} }
@ -320,7 +329,7 @@ static int idmouse_probe(struct usb_interface *interface,
if (dev == NULL) if (dev == NULL)
return -ENOMEM; return -ENOMEM;
init_MUTEX(&dev->sem); mutex_init(&dev->lock);
dev->udev = udev; dev->udev = udev;
dev->interface = interface; dev->interface = interface;
@ -372,24 +381,26 @@ static void idmouse_disconnect(struct usb_interface *interface)
/* get device structure */ /* get device structure */
dev = usb_get_intfdata(interface); dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* give back our minor */ /* give back our minor */
usb_deregister_dev(interface, &idmouse_class); usb_deregister_dev(interface, &idmouse_class);
/* lock it */ mutex_lock(&open_disc_mutex);
down(&dev->sem); usb_set_intfdata(interface, NULL);
/* lock the device */
mutex_lock(&dev->lock);
mutex_unlock(&open_disc_mutex);
/* prevent device read, write and ioctl */ /* prevent device read, write and ioctl */
dev->present = 0; dev->present = 0;
/* 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); mutex_unlock(&dev->lock);
idmouse_delete(dev); idmouse_delete(dev);
} else { } else {
/* unlock */ /* unlock */
up(&dev->sem); mutex_unlock(&dev->lock);
} }
info("%s disconnected", DRIVER_DESC); info("%s disconnected", DRIVER_DESC);