diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index afffea894021..c12417cddab4 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ struct hid_sensor_custom_field { struct hid_sensor_custom { struct mutex mutex; + struct kref ref; + bool removed; struct platform_device *pdev; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_callbacks callbacks; @@ -636,6 +639,15 @@ static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * kfree(sensor_inst->fields); } +static void hid_sensor_custom_free(struct kref *ref) +{ + struct hid_sensor_custom *sensor_inst = + container_of(ref, struct hid_sensor_custom, ref); + + kfifo_free(&sensor_inst->data_fifo); + kfree(sensor_inst); +} + static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, size_t count, loff_t *f_ps) { @@ -649,22 +661,35 @@ static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, if (count < sizeof(struct hid_sensor_sample)) return -EINVAL; - do { - if (kfifo_is_empty(&sensor_inst->data_fifo)) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + for (;;) { + ret = mutex_lock_interruptible(&sensor_inst->mutex); + if (ret) + return ret; - ret = wait_event_interruptible(sensor_inst->wait, - !kfifo_is_empty(&sensor_inst->data_fifo)); - if (ret) - return ret; + if (sensor_inst->removed) { + ret = -ENODEV; + goto unlock; } ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count, &copied); + if (ret || copied) + goto unlock; + mutex_unlock(&sensor_inst->mutex); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(sensor_inst->wait, + READ_ONCE(sensor_inst->removed) || + !kfifo_is_empty(&sensor_inst->data_fifo)); if (ret) return ret; + } - } while (copied == 0); +unlock: + mutex_unlock(&sensor_inst->mutex); + if (ret) + return ret; return copied; } @@ -677,6 +702,7 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) struct hid_sensor_custom, custom_dev); clear_bit(0, &sensor_inst->misc_opened); + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return 0; } @@ -684,14 +710,22 @@ static int hid_sensor_custom_release(struct inode *inode, struct file *file) static int hid_sensor_custom_open(struct inode *inode, struct file *file) { struct hid_sensor_custom *sensor_inst; + int ret; sensor_inst = container_of(file->private_data, struct hid_sensor_custom, custom_dev); - /* We essentially have single reader and writer */ - if (test_and_set_bit(0, &sensor_inst->misc_opened)) - return -EBUSY; + mutex_lock(&sensor_inst->mutex); + if (sensor_inst->removed) { + ret = -ENODEV; + } else if (test_and_set_bit(0, &sensor_inst->misc_opened)) { + ret = -EBUSY; + } else { + kref_get(&sensor_inst->ref); + ret = stream_open(inode, file); + } + mutex_unlock(&sensor_inst->mutex); - return stream_open(inode, file); + return ret; } static __poll_t hid_sensor_custom_poll(struct file *file, @@ -705,6 +739,9 @@ static __poll_t hid_sensor_custom_poll(struct file *file, poll_wait(file, &sensor_inst->wait, wait); + if (READ_ONCE(sensor_inst->removed)) + return EPOLLHUP | EPOLLERR; + if (!kfifo_is_empty(&sensor_inst->data_fifo)) mask = EPOLLIN | EPOLLRDNORM; @@ -712,6 +749,7 @@ static __poll_t hid_sensor_custom_poll(struct file *file, } static const struct file_operations hid_sensor_custom_fops = { + .owner = THIS_MODULE, .open = hid_sensor_custom_open, .read = hid_sensor_custom_read, .release = hid_sensor_custom_release, @@ -734,20 +772,17 @@ static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst) sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev); sensor_inst->custom_dev.fops = &hid_sensor_custom_fops; ret = misc_register(&sensor_inst->custom_dev); - if (ret) { - kfifo_free(&sensor_inst->data_fifo); - return ret; - } - return 0; + return ret; } static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom *sensor_inst) { - wake_up(&sensor_inst->wait); + mutex_lock(&sensor_inst->mutex); + WRITE_ONCE(sensor_inst->removed, true); + mutex_unlock(&sensor_inst->mutex); + wake_up_interruptible_all(&sensor_inst->wait); misc_deregister(&sensor_inst->custom_dev); - kfifo_free(&sensor_inst->data_fifo); - } /* @@ -970,8 +1005,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) int ret; const struct hid_sensor_custom_match *match; - sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), - GFP_KERNEL); + sensor_inst = kzalloc_obj(*sensor_inst); if (!sensor_inst) return -ENOMEM; @@ -981,6 +1015,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) sensor_inst->hsdev = hsdev; sensor_inst->pdev = pdev; mutex_init(&sensor_inst->mutex); + kref_init(&sensor_inst->ref); platform_set_drvdata(pdev, sensor_inst); ret = hid_sensor_custom_get_known(hsdev, &match); @@ -992,7 +1027,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "register_platform_device failed\n"); - return ret; + goto err_free; } return 0; @@ -1002,7 +1037,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) &sensor_inst->callbacks); if (ret < 0) { dev_err(&pdev->dev, "callback reg failed\n"); - return ret; + goto err_free; } ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj, @@ -1027,6 +1062,8 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) &enable_sensor_attr_group); err_remove_callback: sensor_hub_remove_callback(hsdev, hsdev->usage); +err_free: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); return ret; } @@ -1038,14 +1075,18 @@ static void hid_sensor_custom_remove(struct platform_device *pdev) if (sensor_inst->custom_pdev) { platform_device_unregister(sensor_inst->custom_pdev); - return; + goto out; } hid_sensor_custom_dev_if_remove(sensor_inst); hid_sensor_custom_remove_attributes(sensor_inst); sysfs_remove_group(&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); + if (sensor_inst->enable) + sensor_hub_device_close(hsdev); sensor_hub_remove_callback(hsdev, hsdev->usage); +out: + kref_put(&sensor_inst->ref, hid_sensor_custom_free); } static const struct platform_device_id hid_sensor_custom_ids[] = { diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 90666ff629de..2bac8b82ce37 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -173,7 +173,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); unsigned long flags; - spin_lock_irqsave(&pdata->dyn_callback_lock, flags); + spin_lock_irqsave(&pdata->lock, flags); + spin_lock(&pdata->dyn_callback_lock); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && callback->hsdev == hsdev) { @@ -181,7 +182,8 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, kfree(callback); break; } - spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); + spin_unlock(&pdata->dyn_callback_lock); + spin_unlock_irqrestore(&pdata->lock, flags); return 0; }