mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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 @@ static int hidraw_open(struct inode *inode, struct file *file)
 | 
				
			||||||
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…
	
		Reference in a new issue