diff options
author | Ratan Nalumasu <ratan@google.com> | 2012-09-22 14:46:30 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-10-01 04:27:36 -0400 |
commit | 4fe9f8e203fdad1524c04beb390f3c6099781ed9 (patch) | |
tree | b3f6e92fb068c718fd2f6a9b345b03f3dda970ed /drivers/hid | |
parent | cead24c1181ad199354bd08013d0f9d08b66691b (diff) |
HID: hidraw: don't deallocate memory when it is in use
When a device is unplugged, wait for all processes that have opened the device
to close before deallocating the device.
Signed-off-by: Ratan Nalumasu <ratan@google.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/hidraw.c | 69 |
1 files changed, 26 insertions, 43 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 3b6f7bf5a77e..c46c5f1037f4 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c | |||
@@ -42,6 +42,7 @@ static struct cdev hidraw_cdev; | |||
42 | static struct class *hidraw_class; | 42 | static struct class *hidraw_class; |
43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; | 43 | static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; |
44 | static DEFINE_MUTEX(minors_lock); | 44 | static DEFINE_MUTEX(minors_lock); |
45 | static void drop_ref(struct hidraw *hid, int exists_bit); | ||
45 | 46 | ||
46 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | 47 | static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) |
47 | { | 48 | { |
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, | |||
113 | __u8 *buf; | 114 | __u8 *buf; |
114 | int ret = 0; | 115 | int ret = 0; |
115 | 116 | ||
116 | if (!hidraw_table[minor]) { | 117 | if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { |
117 | ret = -ENODEV; | 118 | ret = -ENODEV; |
118 | goto out; | 119 | goto out; |
119 | } | 120 | } |
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file) | |||
261 | } | 262 | } |
262 | 263 | ||
263 | mutex_lock(&minors_lock); | 264 | mutex_lock(&minors_lock); |
264 | if (!hidraw_table[minor]) { | 265 | if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { |
265 | err = -ENODEV; | 266 | err = -ENODEV; |
266 | goto out_unlock; | 267 | goto out_unlock; |
267 | } | 268 | } |
@@ -298,36 +299,12 @@ out: | |||
298 | static int hidraw_release(struct inode * inode, struct file * file) | 299 | static int hidraw_release(struct inode * inode, struct file * file) |
299 | { | 300 | { |
300 | unsigned int minor = iminor(inode); | 301 | unsigned int minor = iminor(inode); |
301 | struct hidraw *dev; | ||
302 | struct hidraw_list *list = file->private_data; | 302 | struct hidraw_list *list = file->private_data; |
303 | int ret; | ||
304 | int i; | ||
305 | |||
306 | mutex_lock(&minors_lock); | ||
307 | if (!hidraw_table[minor]) { | ||
308 | ret = -ENODEV; | ||
309 | goto unlock; | ||
310 | } | ||
311 | 303 | ||
304 | drop_ref(hidraw_table[minor], 0); | ||
312 | list_del(&list->node); | 305 | list_del(&list->node); |
313 | dev = hidraw_table[minor]; | ||
314 | if (!--dev->open) { | ||
315 | if (list->hidraw->exist) { | ||
316 | hid_hw_power(dev->hid, PM_HINT_NORMAL); | ||
317 | hid_hw_close(dev->hid); | ||
318 | } else { | ||
319 | kfree(list->hidraw); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) | ||
324 | kfree(list->buffer[i].value); | ||
325 | kfree(list); | 306 | kfree(list); |
326 | ret = 0; | 307 | return 0; |
327 | unlock: | ||
328 | mutex_unlock(&minors_lock); | ||
329 | |||
330 | return ret; | ||
331 | } | 308 | } |
332 | 309 | ||
333 | static long hidraw_ioctl(struct file *file, unsigned int cmd, | 310 | static long hidraw_ioctl(struct file *file, unsigned int cmd, |
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect); | |||
529 | void hidraw_disconnect(struct hid_device *hid) | 506 | void hidraw_disconnect(struct hid_device *hid) |
530 | { | 507 | { |
531 | struct hidraw *hidraw = hid->hidraw; | 508 | struct hidraw *hidraw = hid->hidraw; |
532 | 509 | drop_ref(hidraw, 1); | |
533 | mutex_lock(&minors_lock); | ||
534 | hidraw->exist = 0; | ||
535 | |||
536 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
537 | |||
538 | hidraw_table[hidraw->minor] = NULL; | ||
539 | |||
540 | if (hidraw->open) { | ||
541 | hid_hw_close(hid); | ||
542 | wake_up_interruptible(&hidraw->wait); | ||
543 | } else { | ||
544 | kfree(hidraw); | ||
545 | } | ||
546 | mutex_unlock(&minors_lock); | ||
547 | } | 510 | } |
548 | EXPORT_SYMBOL_GPL(hidraw_disconnect); | 511 | EXPORT_SYMBOL_GPL(hidraw_disconnect); |
549 | 512 | ||
@@ -585,3 +548,23 @@ void hidraw_exit(void) | |||
585 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | 548 | unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); |
586 | 549 | ||
587 | } | 550 | } |
551 | |||
552 | static void drop_ref(struct hidraw *hidraw, int exists_bit) | ||
553 | { | ||
554 | mutex_lock(&minors_lock); | ||
555 | if (exists_bit) { | ||
556 | hid_hw_close(hidraw->hid); | ||
557 | hidraw->exist = 0; | ||
558 | if (hidraw->open) | ||
559 | wake_up_interruptible(&hidraw->wait); | ||
560 | } else { | ||
561 | --hidraw->open; | ||
562 | } | ||
563 | |||
564 | if (!hidraw->open && !hidraw->exist) { | ||
565 | device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||
566 | hidraw_table[hidraw->minor] = NULL; | ||
567 | kfree(hidraw); | ||
568 | } | ||
569 | mutex_unlock(&minors_lock); | ||
570 | } | ||