Skip to content

Commit

Permalink
HID: fix locking in hidraw_open()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Oliver Neukum authored and Jiri Kosina committed Nov 13, 2008
1 parent 4ffaf86 commit 7d672cd
Showing 1 changed file with 16 additions and 14 deletions.
30 changes: 16 additions & 14 deletions drivers/hid/hidraw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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;

}
Expand Down Expand Up @@ -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])
Expand All @@ -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;
}
Expand All @@ -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);

Expand All @@ -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));

Expand Down

0 comments on commit 7d672cd

Please sign in to comment.