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:
Родитель
cead24c118
Коммит
4fe9f8e203
|
@ -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);
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче