diff options
author | Jiri Kosina <jkosina@suse.cz> | 2010-03-25 14:15:11 +0100 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-03-25 14:29:14 +0100 |
commit | 2e57480b2a717916510b0c23b2851398a4cbd958 (patch) | |
tree | 69491b58b4d661186e4925089d88d49fab8b02f6 | |
parent | 39710479303fd3affb3e204e9a7a75cc676977b5 (diff) | |
download | lwn-2e57480b2a717916510b0c23b2851398a4cbd958.tar.gz lwn-2e57480b2a717916510b0c23b2851398a4cbd958.zip |
HID: remove BKL from hidraw
Remove BKL from hidraw, which is possible through fixing the
locking of minors_lock mutex properly -- it is now used to
guard all accessess to hidraw_table[], preventing it to becoming
NULL unexpectedly by unregistering the device.
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/hidraw.c | 41 |
1 files changed, 25 insertions, 16 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index d04476700b7b..589dac5b5f56 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -105,38 +105,48 @@ out: static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { unsigned int minor = iminor(file->f_path.dentry->d_inode); - /* FIXME: What stops hidraw_table going NULL */ - struct hid_device *dev = hidraw_table[minor]->hid; + struct hid_device *dev; __u8 *buf; int ret = 0; - if (!dev->hid_output_raw_report) - return -ENODEV; + mutex_lock(&minors_lock); + dev = hidraw_table[minor]->hid; + + if (!dev->hid_output_raw_report) { + ret = -ENODEV; + goto out; + } if (count > HID_MAX_BUFFER_SIZE) { printk(KERN_WARNING "hidraw: pid %d passed too large report\n", task_pid_nr(current)); - return -EINVAL; + ret = -EINVAL; + goto out; } if (count < 2) { printk(KERN_WARNING "hidraw: pid %d passed too short report\n", task_pid_nr(current)); - return -EINVAL; + ret = -EINVAL; + goto out; } buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + ret = -ENOMEM; + goto out; + } if (copy_from_user(buf, buffer, count)) { ret = -EFAULT; - goto out; + goto out_free; } ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); -out: +out_free: kfree(buf); +out: + mutex_unlock(&minors_lock); return ret; } @@ -164,7 +174,6 @@ static int hidraw_open(struct inode *inode, struct file *file) goto out; } - lock_kernel(); mutex_lock(&minors_lock); if (!hidraw_table[minor]) { printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", @@ -196,7 +205,6 @@ static int hidraw_open(struct inode *inode, struct file *file) out_unlock: mutex_unlock(&minors_lock); - unlock_kernel(); out: return err; @@ -237,11 +245,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, struct inode *inode = file->f_path.dentry->d_inode; unsigned int minor = iminor(inode); long ret = 0; - /* FIXME: What stops hidraw_table going NULL */ - struct hidraw *dev = hidraw_table[minor]; + struct hidraw *dev; void __user *user_arg = (void __user*) arg; - lock_kernel(); + mutex_lock(&minors_lock); + dev = hidraw_table[minor]; + switch (cmd) { case HIDIOCGRDESCSIZE: if (put_user(dev->hid->rsize, (int __user *)arg)) @@ -314,7 +323,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, ret = -ENOTTY; } - unlock_kernel(); + mutex_unlock(&minors_lock); return ret; } |