HID: fix locking in hidraw_open()

As open needs to sleep hidraw was wrong to call it with a spinlock held.
Furthermore, open can of course fail which needs to be handled.

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
Oliver Neukum 2008-10-31 00:07:23 +01:00 committed by Jiri Kosina
parent 4ffaf869c7
commit 7d672cd750

View File

@ -38,7 +38,7 @@ static int hidraw_major;
static struct cdev hidraw_cdev; static struct cdev hidraw_cdev;
static struct class *hidraw_class; static struct class *hidraw_class;
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DEFINE_SPINLOCK(minors_lock); static DEFINE_MUTEX(minors_lock);
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{ {
@ -159,13 +159,13 @@ static int hidraw_open(struct inode *inode, struct file *file)
struct hidraw_list *list; struct hidraw_list *list;
int err = 0; int err = 0;
lock_kernel();
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
spin_lock(&minors_lock); lock_kernel();
mutex_lock(&minors_lock);
if (!hidraw_table[minor]) { if (!hidraw_table[minor]) {
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
minor); minor);
@ -180,13 +180,16 @@ static int hidraw_open(struct inode *inode, struct file *file)
file->private_data = list; file->private_data = list;
dev = hidraw_table[minor]; dev = hidraw_table[minor];
if (!dev->open++) if (!dev->open++) {
dev->hid->ll_driver->open(dev->hid); err = dev->hid->ll_driver->open(dev->hid);
if (err < 0)
dev->open--;
}
out_unlock: out_unlock:
spin_unlock(&minors_lock); mutex_unlock(&minors_lock);
out:
unlock_kernel(); unlock_kernel();
out:
return err; return err;
} }
@ -310,7 +313,7 @@ int hidraw_connect(struct hid_device *hid)
result = -EINVAL; result = -EINVAL;
spin_lock(&minors_lock); mutex_lock(&minors_lock);
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) { for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
if (hidraw_table[minor]) if (hidraw_table[minor])
@ -320,9 +323,8 @@ int hidraw_connect(struct hid_device *hid)
break; break;
} }
spin_unlock(&minors_lock);
if (result) { if (result) {
mutex_unlock(&minors_lock);
kfree(dev); kfree(dev);
goto out; goto out;
} }
@ -331,14 +333,14 @@ int hidraw_connect(struct hid_device *hid)
NULL, "%s%d", "hidraw", minor); NULL, "%s%d", "hidraw", minor);
if (IS_ERR(dev->dev)) { if (IS_ERR(dev->dev)) {
spin_lock(&minors_lock);
hidraw_table[minor] = NULL; hidraw_table[minor] = NULL;
spin_unlock(&minors_lock); mutex_unlock(&minors_lock);
result = PTR_ERR(dev->dev); result = PTR_ERR(dev->dev);
kfree(dev); kfree(dev);
goto out; goto out;
} }
mutex_unlock(&minors_lock);
init_waitqueue_head(&dev->wait); init_waitqueue_head(&dev->wait);
INIT_LIST_HEAD(&dev->list); INIT_LIST_HEAD(&dev->list);
@ -360,9 +362,9 @@ void hidraw_disconnect(struct hid_device *hid)
hidraw->exist = 0; hidraw->exist = 0;
spin_lock(&minors_lock); mutex_lock(&minors_lock);
hidraw_table[hidraw->minor] = NULL; hidraw_table[hidraw->minor] = NULL;
spin_unlock(&minors_lock); mutex_unlock(&minors_lock);
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));