gpio: cdev: wake up chardev poll() on device unbind
authorBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Thu, 17 Aug 2023 12:42:24 +0000 (14:42 +0200)
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Mon, 21 Aug 2023 13:57:05 +0000 (15:57 +0200)
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>
drivers/gpio/gpiolib-cdev.c

index fec446510028ea9a493a65374c57d796bf371f4e..bc1542544e49cb09241de56ab5f9f9dbb6b0e334 100644 (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);