From: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxx> Extend struct linereq with a notifier block and use it to receive the GPIO device unregister event. Upon reception, 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@xxxxxxxxxx> --- drivers/gpio/gpiolib-cdev.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index eb8c0cb71da4..0b21ea04fa52 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -563,6 +563,7 @@ struct line { * @wait: wait queue that handles blocking reads of events * @event_buffer_size: the number of elements allocated in @events * @events: KFIFO for the GPIO events + * @nb: notifier block for receiving gpio_device notifications * @seqno: the sequence number for edge events generated on all lines in * this line request. Note that this is not used when @num_lines is 1, as * the line_seqno is then the same and is cheaper to calculate. @@ -577,11 +578,17 @@ struct linereq { wait_queue_head_t wait; u32 event_buffer_size; DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); + struct notifier_block nb; atomic_t seqno; struct mutex config_mutex; struct line lines[]; }; +static struct linereq *to_linereq(struct notifier_block *nb) +{ + return container_of(nb, struct linereq, nb); +} + #define GPIO_V2_LINE_BIAS_FLAGS \ (GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \ GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \ @@ -1573,6 +1580,10 @@ static void linereq_free(struct linereq *lr) { unsigned int i; + if (lr->nb.notifier_call) + blocking_notifier_chain_unregister(&lr->gdev->notifier, + &lr->nb); + for (i = 0; i < lr->num_lines; i++) { if (lr->lines[i].desc) { edge_detector_stop(&lr->lines[i]); @@ -1623,6 +1634,22 @@ static const struct file_operations line_fileops = { #endif }; +static int linereq_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct linereq *lr = to_linereq(nb); + + switch (action) { + case GPIO_CDEV_UNREGISTERED: + wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + static int linereq_create(struct gpio_device *gdev, void __user *ip) { struct gpio_v2_line_request ulr; @@ -1733,6 +1760,11 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) offset); } + lr->nb.notifier_call = linereq_notify; + ret = blocking_notifier_chain_register(&gdev->notifier, &lr->nb); + if (ret) + goto out_free_linereq; + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { ret = fd; -- 2.39.2