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:
parent
4ffaf869c7
commit
7d672cd750
1 changed files with 16 additions and 14 deletions
|
@ -38,7 +38,7 @@ static int hidraw_major;
|
|||
static struct cdev hidraw_cdev;
|
||||
static struct class *hidraw_class;
|
||||
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)
|
||||
{
|
||||
|
@ -159,13 +159,13 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||
struct hidraw_list *list;
|
||||
int err = 0;
|
||||
|
||||
lock_kernel();
|
||||
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
lock_kernel();
|
||||
mutex_lock(&minors_lock);
|
||||
if (!hidraw_table[minor]) {
|
||||
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
|
||||
minor);
|
||||
|
@ -180,13 +180,16 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||
file->private_data = list;
|
||||
|
||||
dev = hidraw_table[minor];
|
||||
if (!dev->open++)
|
||||
dev->hid->ll_driver->open(dev->hid);
|
||||
if (!dev->open++) {
|
||||
err = dev->hid->ll_driver->open(dev->hid);
|
||||
if (err < 0)
|
||||
dev->open--;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&minors_lock);
|
||||
out:
|
||||
mutex_unlock(&minors_lock);
|
||||
unlock_kernel();
|
||||
out:
|
||||
return err;
|
||||
|
||||
}
|
||||
|
@ -310,7 +313,7 @@ int hidraw_connect(struct hid_device *hid)
|
|||
|
||||
result = -EINVAL;
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
mutex_lock(&minors_lock);
|
||||
|
||||
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
|
||||
if (hidraw_table[minor])
|
||||
|
@ -320,9 +323,8 @@ int hidraw_connect(struct hid_device *hid)
|
|||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&minors_lock);
|
||||
|
||||
if (result) {
|
||||
mutex_unlock(&minors_lock);
|
||||
kfree(dev);
|
||||
goto out;
|
||||
}
|
||||
|
@ -331,14 +333,14 @@ int hidraw_connect(struct hid_device *hid)
|
|||
NULL, "%s%d", "hidraw", minor);
|
||||
|
||||
if (IS_ERR(dev->dev)) {
|
||||
spin_lock(&minors_lock);
|
||||
hidraw_table[minor] = NULL;
|
||||
spin_unlock(&minors_lock);
|
||||
mutex_unlock(&minors_lock);
|
||||
result = PTR_ERR(dev->dev);
|
||||
kfree(dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_unlock(&minors_lock);
|
||||
init_waitqueue_head(&dev->wait);
|
||||
INIT_LIST_HEAD(&dev->list);
|
||||
|
||||
|
@ -360,9 +362,9 @@ void hidraw_disconnect(struct hid_device *hid)
|
|||
|
||||
hidraw->exist = 0;
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
mutex_lock(&minors_lock);
|
||||
hidraw_table[hidraw->minor] = NULL;
|
||||
spin_unlock(&minors_lock);
|
||||
mutex_unlock(&minors_lock);
|
||||
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
|
||||
|
|
Loading…
Reference in a new issue