gpio: cdev: wake up chardev poll() on device unbind

Add a notifier block to the gpio_chardev_data structure and register it
with the gpio_device's device notifier. Upon reception of an event, wake
up the wait queue so that the user-space be forced out of poll() and need
to go into a new system call which will then fail due to the chip being
gone.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Kent Gibson <warthog618@gmail.com>
This commit is contained in:
Bartosz Golaszewski 2023-08-17 14:42:24 +02:00
parent a067419ba7
commit d2e2586a32

View file

@ -2320,6 +2320,7 @@ struct gpio_chardev_data {
wait_queue_head_t wait;
DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
struct notifier_block lineinfo_changed_nb;
struct notifier_block device_unregistered_nb;
unsigned long *watched_lines;
#ifdef CONFIG_GPIO_CDEV_V1
atomic_t watch_abi_version;
@ -2517,6 +2518,18 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
return NOTIFY_OK;
}
static int gpio_device_unregistered_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct gpio_chardev_data *cdev = container_of(nb,
struct gpio_chardev_data,
device_unregistered_nb);
wake_up_poll(&cdev->wait, EPOLLIN | EPOLLERR);
return NOTIFY_OK;
}
static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
struct poll_table_struct *pollt)
{
@ -2671,17 +2684,27 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
if (ret)
goto out_free_bitmap;
cdev->device_unregistered_nb.notifier_call =
gpio_device_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&cdev->device_unregistered_nb);
if (ret)
goto out_unregister_line_notifier;
file->private_data = cdev;
ret = nonseekable_open(inode, file);
if (ret)
goto out_unregister_notifier;
goto out_unregister_device_notifier;
up_read(&gdev->sem);
return ret;
out_unregister_notifier:
out_unregister_device_notifier:
blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
out_unregister_line_notifier:
blocking_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
out_free_bitmap:
@ -2706,6 +2729,8 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
struct gpio_device *gdev = cdev->gdev;
bitmap_free(cdev->watched_lines);
blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
blocking_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb);
gpio_device_put(gdev);