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>
This commit is contained in:
		
					parent
					
						
							
								cead24c118
							
						
					
				
			
			
				commit
				
					
						4fe9f8e203
					
				
			
		
					 1 changed files with 26 additions and 43 deletions
				
			
		|  | @ -42,6 +42,7 @@ 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_MUTEX(minors_lock); | static DEFINE_MUTEX(minors_lock); | ||||||
|  | static void drop_ref(struct hidraw *hid, int exists_bit); | ||||||
| 
 | 
 | ||||||
| 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) | ||||||
| { | { | ||||||
|  | @ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, | ||||||
| 	__u8 *buf; | 	__u8 *buf; | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
| 	if (!hidraw_table[minor]) { | 	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { | ||||||
| 		ret = -ENODEV; | 		ret = -ENODEV; | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
|  | @ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&minors_lock); | 	mutex_lock(&minors_lock); | ||||||
| 	if (!hidraw_table[minor]) { | 	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { | ||||||
| 		err = -ENODEV; | 		err = -ENODEV; | ||||||
| 		goto out_unlock; | 		goto out_unlock; | ||||||
| 	} | 	} | ||||||
|  | @ -298,36 +299,12 @@ out: | ||||||
| static int hidraw_release(struct inode * inode, struct file * file) | static int hidraw_release(struct inode * inode, struct file * file) | ||||||
| { | { | ||||||
| 	unsigned int minor = iminor(inode); | 	unsigned int minor = iminor(inode); | ||||||
| 	struct hidraw *dev; |  | ||||||
| 	struct hidraw_list *list = file->private_data; | 	struct hidraw_list *list = file->private_data; | ||||||
| 	int ret; |  | ||||||
| 	int i; |  | ||||||
| 
 |  | ||||||
| 	mutex_lock(&minors_lock); |  | ||||||
| 	if (!hidraw_table[minor]) { |  | ||||||
| 		ret = -ENODEV; |  | ||||||
| 		goto unlock; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
|  | 	drop_ref(hidraw_table[minor], 0); | ||||||
| 	list_del(&list->node); | 	list_del(&list->node); | ||||||
| 	dev = hidraw_table[minor]; |  | ||||||
| 	if (!--dev->open) { |  | ||||||
| 		if (list->hidraw->exist) { |  | ||||||
| 			hid_hw_power(dev->hid, PM_HINT_NORMAL); |  | ||||||
| 			hid_hw_close(dev->hid); |  | ||||||
| 		} else { |  | ||||||
| 			kfree(list->hidraw); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) |  | ||||||
| 		kfree(list->buffer[i].value); |  | ||||||
| 	kfree(list); | 	kfree(list); | ||||||
| 	ret = 0; | 	return 0; | ||||||
| unlock: |  | ||||||
| 	mutex_unlock(&minors_lock); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static long hidraw_ioctl(struct file *file, unsigned int cmd, | static long hidraw_ioctl(struct file *file, unsigned int cmd, | ||||||
|  | @ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect); | ||||||
| void hidraw_disconnect(struct hid_device *hid) | void hidraw_disconnect(struct hid_device *hid) | ||||||
| { | { | ||||||
| 	struct hidraw *hidraw = hid->hidraw; | 	struct hidraw *hidraw = hid->hidraw; | ||||||
| 
 | 	drop_ref(hidraw, 1); | ||||||
| 	mutex_lock(&minors_lock); |  | ||||||
| 	hidraw->exist = 0; |  | ||||||
| 
 |  | ||||||
| 	device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); |  | ||||||
| 
 |  | ||||||
| 	hidraw_table[hidraw->minor] = NULL; |  | ||||||
| 
 |  | ||||||
| 	if (hidraw->open) { |  | ||||||
| 		hid_hw_close(hid); |  | ||||||
| 		wake_up_interruptible(&hidraw->wait); |  | ||||||
| 	} else { |  | ||||||
| 		kfree(hidraw); |  | ||||||
| 	} |  | ||||||
| 	mutex_unlock(&minors_lock); |  | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(hidraw_disconnect); | EXPORT_SYMBOL_GPL(hidraw_disconnect); | ||||||
| 
 | 
 | ||||||
|  | @ -585,3 +548,23 @@ void hidraw_exit(void) | ||||||
| 	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | 	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static void drop_ref(struct hidraw *hidraw, int exists_bit) | ||||||
|  | { | ||||||
|  | 	mutex_lock(&minors_lock); | ||||||
|  | 	if (exists_bit) { | ||||||
|  | 		hid_hw_close(hidraw->hid); | ||||||
|  | 		hidraw->exist = 0; | ||||||
|  | 		if (hidraw->open) | ||||||
|  | 			wake_up_interruptible(&hidraw->wait); | ||||||
|  | 	} else { | ||||||
|  | 		--hidraw->open; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!hidraw->open && !hidraw->exist) { | ||||||
|  | 		device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); | ||||||
|  | 		hidraw_table[hidraw->minor] = NULL; | ||||||
|  | 		kfree(hidraw); | ||||||
|  | 	} | ||||||
|  | 	mutex_unlock(&minors_lock); | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ratan Nalumasu
				Ratan Nalumasu