* [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space
@ 2026-10-29 11:29 Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel
Removing a GPIO chip while user space holds a line request, as when a
hot-pluggable GPIO controller goes away, leaks the line labels (patch 2)
and IRQ names (patch 5), frees IRQs a second time (patches 3, 7) and
lets a blocked read() stall the removal (patch 4). Patch 1 makes
gpiodev_release() wait for pending label frees; without it,
cleanup_srcu_struct() can warn, more often with patch 2.
Patch 7 changes gpiochip_remove() for in-kernel users too: their
remaining IRQs are freed after the chip pointer is cleared, when
gpiod_*() calls fail. Patch 6 drops edge events when the line read
fails, so an IRQ thread in that window does not report -ENODEV as
a rising edge.
User-visible change: a read() blocked when the chip is removed returns
the queued events, then fails with -ENODEV.
For stable: patch 2 needs 1; patches 3, 5 and 7 fix a 6.13 commit also
in 6.12.y; patch 7 needs 6; patch 4 applies to 6.12.y.
Tested under QEMU (x86_64, 4 vCPUs) with gpio-sim and a test chip on
the hierarchical irqchip, with lockdep, kmemleak and KASAN. An LLM
assistant helped with the analysis, reproducers and drafts, hence the
Assisted-by tags; I reviewed and tested every change.
Changes in v4:
- Rebased on gpio/for-current.
- New patch 6: drop edge events when a line read fails, before patch 7
frees the character device IRQs after gdev->chip is cleared (Sashiko).
- Patch 2: for_each_hwgpio() seeing a requested line with a NULL label
is on mainline's request path too; applied separately (Sashiko):
https://lore.kernel.org/r/20261022083343.3905973-3-aambrozewicz@nvidia.com
- Patch 5: moving irq_label above irq in struct line moves the 4-byte
hole rather than removing it (Andy).
- Link to v3: https://lore.kernel.org/r/20261020-gpiolib-chip-removal-v3-0-aambrozewicz@nvidia.com
Changes in v3:
- Rebased on gpio/for-current; shorter commit messages (Kent, Bartosz).
- Patch 6 (now 7): return early when there is no ->to_irq(); drop the
lineevent_free() comment (Kent).
- Sashiko's v2 6/6 finding on gpiochip_setup_dev() is on mainline too;
I plan a separate fix on top of this series.
- Link to v2: https://lore.kernel.org/r/20261016-gpiolib-chip-removal-v2-0-aambrozewicz@nvidia.com
- Link to v1: https://lore.kernel.org/r/20261012-gpiolib-chip-removal-v1-0-aambrozewicz@nvidia.com
Adrian Ambrozewicz (7):
gpiolib: wait for pending label frees before cleaning up desc_srcu
gpiolib: free the label of lines released after their chip was removed
gpiolib: cdev: do not free line IRQs again after the chip is removed
gpiolib: cdev: don't sleep in the SRCU read section in read()
gpiolib: cdev: don't leak the IRQ name when the chip is removed
gpiolib: cdev: don't report a failed line read as an edge
gpiolib: let the character device free its IRQs first on chip removal
drivers/gpio/gpiolib-cdev.c | 155 +++++++++++++++++++++++++++---------
drivers/gpio/gpiolib.c | 38 +++++++--
2 files changed, 147 insertions(+), 46 deletions(-)
base-commit: baa14758afb3e2828de1f76511edc1de50216f39
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
` (5 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
desc_set_label() frees the old label with call_srcu() on
gdev->desc_srcu, and gpiodev_release() calls synchronize_srcu() before
cleanup_srcu_struct(). synchronize_srcu() waits for a grace period, not
for queued callbacks. Once the srcu_struct uses per-CPU callback lists,
a label freed by gpiochip_free_hogs() can still be pending, and
cleanup_srcu_struct() warns:
WARNING: kernel/rcu/srcutree.c:706 at cleanup_srcu_struct+0x239/0x330, CPU#0: init/1
Call Trace:
gpiodev_release
device_release
kobject_put
devres_release_all
device_release_driver_internal
platform_device_unregister
gpio_sim_device_deactivate
Use srcu_barrier(), which waits for them.
Reproduced with a hogged gpio-sim line, setting the chip live and back
on a 4-CPU guest with srcutree.convert_to_big=1: it warns within 60
cycles; with this change, 1000 cycles pass.
Fixes: a86d27693066 ("gpiolib: fix the speed of descriptor label setting with SRCU")
Cc: stable@vger.kernel.org
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 90731a547ed6..faf4a8507dd6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -895,7 +895,7 @@ static void gpiodev_release(struct device *dev)
struct gpio_device *gdev = to_gpio_device(dev);
/* Call pending kfree()s for descriptor labels. */
- synchronize_srcu(&gdev->desc_srcu);
+ srcu_barrier(&gdev->desc_srcu);
cleanup_srcu_struct(&gdev->desc_srcu);
ida_free(&gpio_ida, gdev->id);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 2/7] gpiolib: free the label of lines released after their chip was removed
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
gpiod_free_commit() drops the consumer label only while the chip is
still there. A line that user space keeps requested across
gpiochip_remove() is released when gdev->chip is already NULL, and its
label leaks:
unreferenced object (size 32):
comm "gpio-hold"
backtrace (crc 7ff222d9):
desc_set_label
gpiod_request_commit
gpiod_request
linereq_create
gpio_ioctl
Clear the label before the chip check. The ->free() callback, the flags
update and the line state notification need the chip and stay under it.
Reproduced with gpio-sim: request a line, set the chip's "live"
attribute to 0, then close the request. kmemleak reports up to 30 such
objects after 30 cycles; with this change, none after 100.
Fixes: 35b545332b80 ("gpio: remove gpio_lock")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index faf4a8507dd6..49b2823dab0c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2669,7 +2669,12 @@ void gpiod_free_commit(struct gpio_desc *desc)
flags = READ_ONCE(desc->flags);
- if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
+ if (guard.gc && !test_bit(GPIOD_FLAG_REQUESTED, &flags))
+ return;
+
+ desc_set_label(desc, NULL);
+
+ if (guard.gc) {
if (guard.gc->free)
guard.gc->free(guard.gc, gpiod_hwgpio(desc));
@@ -2688,7 +2693,6 @@ void gpiod_free_commit(struct gpio_desc *desc)
#ifdef CONFIG_OF_DYNAMIC
WRITE_ONCE(desc->hog, NULL);
#endif
- desc_set_label(desc, NULL);
WRITE_ONCE(desc->flags, flags);
#ifdef CONFIG_GPIO_CDEV
WRITE_ONCE(desc->debounce_period_us, 0);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
Since commit ec8b6f55b981 ("gpio: free irqs that are still requested
when the chip is being removed"), gpiochip_remove() frees the IRQs
still installed on the chip's lines. A line request or v1 line event
kept open across the removal frees its IRQ again on release. If the
mapping is gone, that can touch freed memory, as reported with
virtio-gpio; if the number was reused, free_irq() warns:
Trying to free already-free IRQ 28
WARNING: kernel/irq/manage.c:1881 at free_irq+0x193/0x2d0, CPU#2: gpio-hold/111
Call Trace:
linereq_free
linereq_release
__fput
Stop edge detection from the device notifier that gpiochip_remove()
calls, under config_mutex so that it does not race a reconfiguration.
Free the IRQ there only if it still has an action, as gpiochip_remove()
frees only those on lines flagged as used for IRQs. The release path
then finds nothing left to free.
Reproduced with gpio-sim: remove the chip under an open request with
edge detection, set it up again and request the same line, then close
the first request. Every cycle warns; with this change, 100 cycles pass.
Fixes: ec8b6f55b981 ("gpio: free irqs that are still requested when the chip is being removed")
Reported-by: Jia Jia <physicalmtea@gmail.com>
Closes: https://lore.kernel.org/all/20260817070429.157993-1-physicalmtea@gmail.com/
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib-cdev.c | 57 ++++++++++++++++++++++++++-----------
1 file changed, 40 insertions(+), 17 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index f4d78113ade2..6350b2fca8a7 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -544,17 +544,6 @@ struct linereq {
GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
GPIO_V2_LINE_EDGE_FLAGS)
-static int linereq_unregistered_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct linereq *lr = container_of(nb, struct linereq,
- device_unregistered_nb);
-
- wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR);
-
- return NOTIFY_OK;
-}
-
static void linereq_put_event(struct linereq *lr,
struct gpio_v2_line_event *le)
{
@@ -609,6 +598,21 @@ static inline void free_irq_label(const char *label)
kfree(label);
}
+/*
+ * gpiochip_remove() frees the interrupts still requested on the lines of the
+ * chip being removed, so once the chip is going away the IRQ may no longer be
+ * ours to free.
+ */
+static void free_line_irq(unsigned int irq, void *dev_id)
+{
+ const char *name = NULL;
+
+ if (irq && irq_has_action(irq))
+ name = free_irq(irq, dev_id);
+
+ free_irq_label(name);
+}
+
#ifdef CONFIG_HTE
static enum hte_return process_hw_ts_thread(void *p)
@@ -974,10 +978,8 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
static void edge_detector_stop(struct line *line)
{
- if (line->irq) {
- free_irq_label(free_irq(line->irq, line));
- line->irq = 0;
- }
+ free_line_irq(line->irq, line);
+ line->irq = 0;
#ifdef CONFIG_HTE
if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
@@ -992,6 +994,25 @@ static void edge_detector_stop(struct line *line)
/* do not change line->level - see comment in debounced_value() */
}
+static int linereq_unregistered_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct linereq *lr = container_of(nb, struct linereq, device_unregistered_nb);
+
+ /*
+ * Stop edge detection now rather than when the request is released:
+ * by then the IRQ numbers of the removed chip may be in use again.
+ */
+ scoped_guard(mutex, &lr->config_mutex) {
+ for (unsigned int i = 0; i < lr->num_lines; i++)
+ edge_detector_stop(&lr->lines[i]);
+ }
+
+ wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR);
+
+ return NOTIFY_OK;
+}
+
static int edge_detector_fifo_init(struct linereq *req)
{
if (kfifo_initialized(&req->events))
@@ -1787,6 +1808,9 @@ static int lineevent_unregistered_notify(struct notifier_block *nb,
struct lineevent_state *le = container_of(nb, struct lineevent_state,
device_unregistered_nb);
+ free_line_irq(le->irq, le);
+ le->irq = 0;
+
wake_up_poll(&le->wait, EPOLLIN | EPOLLERR);
return NOTIFY_OK;
@@ -1866,8 +1890,7 @@ static void lineevent_free(struct lineevent_state *le)
if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb);
- if (le->irq)
- free_irq_label(free_irq(le->irq, le));
+ free_line_irq(le->irq, le);
if (le->desc)
gpiod_free(le->desc);
kfree(le->label);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read()
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (2 preceding siblings ...)
2026-10-29 11:29 ` [PATCH v4 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
linereq_read(), lineevent_read() and lineinfo_watch_read() stay in the
gdev->srcu read section while they sleep waiting for an event, so
gpiochip_remove() blocks in synchronize_srcu() until an event arrives
or the reader is signalled. The device lock is held meanwhile, which
also blocks device_shutdown():
task:init state:D stack:0 pid:1 tgid:1 ppid:0
Call Trace:
__synchronize_srcu
gpiochip_remove
devres_release_all
device_release_driver_internal
platform_device_unregister
gpio_sim_device_deactivate
Check the chip in a scoped SRCU section only; nothing after the check
dereferences it. End the wait once the chip is gone, and return -ENODEV
if there is no event to report.
Reproduced with gpio-sim: setting "live" to 0 hangs while a reader is
blocked in read() on a line request; with this change, the write
returns at once and read() fails with -ENODEV.
Fixes: bdbbae241a04 ("gpiolib: protect the GPIO device against being dropped while in use by user-space")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib-cdev.c | 50 ++++++++++++++++++++++++++-----------
1 file changed, 35 insertions(+), 15 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 6350b2fca8a7..c5869e3f7767 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1507,6 +1507,20 @@ static __poll_t linereq_poll(struct file *file,
return events;
}
+/*
+ * For the read() paths, which must not stay in the SRCU read section while
+ * sleeping: that would stall gpiochip_remove() until an event arrives.
+ */
+static int gpiodev_check_chip(struct gpio_device *gdev)
+{
+ guard(srcu)(&gdev->srcu);
+
+ if (!rcu_access_pointer(gdev->chip))
+ return -ENODEV;
+
+ return 0;
+}
+
static ssize_t linereq_read(struct file *file, char __user *buf,
size_t count, loff_t *f_ps)
{
@@ -1515,10 +1529,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
int ret;
- guard(srcu)(&lr->gdev->srcu);
-
- if (!rcu_access_pointer(lr->gdev->chip))
- return -ENODEV;
+ ret = gpiodev_check_chip(lr->gdev);
+ if (ret)
+ return ret;
if (count < sizeof(le))
return -EINVAL;
@@ -1533,9 +1546,12 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(lr->wait,
- !kfifo_is_empty(&lr->events));
+ !kfifo_is_empty(&lr->events) ||
+ !rcu_access_pointer(lr->gdev->chip));
if (ret)
return ret;
+ if (kfifo_is_empty(&lr->events))
+ return -ENODEV;
}
if (kfifo_out(&lr->events, &le, 1) != 1) {
@@ -1830,10 +1846,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
ssize_t ge_size;
int ret;
- guard(srcu)(&le->gdev->srcu);
-
- if (!rcu_access_pointer(le->gdev->chip))
- return -ENODEV;
+ ret = gpiodev_check_chip(le->gdev);
+ if (ret)
+ return ret;
/*
* When compatible system call is being used the struct gpioevent_data,
@@ -1861,9 +1876,12 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(le->wait,
- !kfifo_is_empty(&le->events));
+ !kfifo_is_empty(&le->events) ||
+ !rcu_access_pointer(le->gdev->chip));
if (ret)
return ret;
+ if (kfifo_is_empty(&le->events))
+ return -ENODEV;
}
if (kfifo_out(&le->events, &ge, 1) != 1) {
@@ -2607,10 +2625,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
int ret;
size_t event_size;
- guard(srcu)(&cdev->gdev->srcu);
-
- if (!rcu_access_pointer(cdev->gdev->chip))
- return -ENODEV;
+ ret = gpiodev_check_chip(cdev->gdev);
+ if (ret)
+ return ret;
#ifndef CONFIG_GPIO_CDEV_V1
event_size = sizeof(struct gpio_v2_line_info_changed);
@@ -2628,9 +2645,12 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(cdev->wait,
- !kfifo_is_empty(&cdev->events));
+ !kfifo_is_empty(&cdev->events) ||
+ !rcu_access_pointer(cdev->gdev->chip));
if (ret)
return ret;
+ if (kfifo_is_empty(&cdev->events))
+ return -ENODEV;
}
#ifdef CONFIG_GPIO_CDEV_V1
/* must be after kfifo check so watch_abi_version is set */
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (3 preceding siblings ...)
2026-10-29 11:29 ` [PATCH v4 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
The character device requests line IRQs with a copy of the consumer
label as the name, and frees the name that free_irq() returns. When
gpiochip_remove() frees the IRQ first, it drops that name and the copy
leaks:
unreferenced object (size 8):
comm "gpio-hold"
backtrace (crc dc8eac94):
kstrdup
kstrdup_and_replace
edge_detector_setup
linereq_create
gpio_ioctl
Keep the name with the line or v1 line event, and free it there,
whoever freed the IRQ.
Reproduced with gpio-sim: remove the chip under an open request with
edge detection and a consumer label. kmemleak reports up to 23 such
objects after 30 cycles; with this change, none after 100.
Fixes: ec8b6f55b981 ("gpio: free irqs that are still requested when the chip is being removed")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib-cdev.c | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index c5869e3f7767..cf23b5296fd1 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -399,6 +399,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
* @desc: the GPIO descriptor for this line.
* @req: the corresponding line request
* @irq: the interrupt triggered in response to events on this GPIO
+ * @irq_label: the name @irq was requested with
* @edflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
* GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied
* @timestamp_ns: cache for the timestamp storing it between hardirq and
@@ -422,6 +423,7 @@ struct line {
*/
struct linereq *req;
unsigned int irq;
+ char *irq_label;
/*
* The flags for the active edge detector configuration.
*
@@ -601,16 +603,18 @@ static inline void free_irq_label(const char *label)
/*
* gpiochip_remove() frees the interrupts still requested on the lines of the
* chip being removed, so once the chip is going away the IRQ may no longer be
- * ours to free.
+ * ours to free. The label is kept by the caller as free_irq() is then not the
+ * one returning it.
*/
-static void free_line_irq(unsigned int irq, void *dev_id)
+static void free_line_irq(unsigned int irq, void *dev_id, char **label)
{
- const char *name = NULL;
+ const char *name = *label;
if (irq && irq_has_action(irq))
- name = free_irq(irq, dev_id);
+ free_irq(irq, dev_id);
free_irq_label(name);
+ *label = NULL;
}
#ifdef CONFIG_HTE
@@ -936,6 +940,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
return ret;
}
line->irq = irq;
+ line->irq_label = label;
} else {
ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH);
if (ret)
@@ -978,7 +983,7 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
static void edge_detector_stop(struct line *line)
{
- free_line_irq(line->irq, line);
+ free_line_irq(line->irq, line, &line->irq_label);
line->irq = 0;
#ifdef CONFIG_HTE
@@ -1078,6 +1083,7 @@ static int edge_detector_setup(struct line *line,
}
line->irq = irq;
+ line->irq_label = label;
return 0;
}
@@ -1776,6 +1782,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
* @desc: the GPIO descriptor held by this event
* @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO
+ * @irq_label: the name @irq was requested with
* @wait: wait queue that handles blocking reads of events
* @device_unregistered_nb: notifier block for receiving gdev unregister events
* @events: KFIFO for the GPIO events
@@ -1789,6 +1796,7 @@ struct lineevent_state {
struct gpio_desc *desc;
u32 eflags;
int irq;
+ char *irq_label;
wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
DECLARE_KFIFO(events, struct gpioevent_data, 16);
@@ -1824,7 +1832,7 @@ static int lineevent_unregistered_notify(struct notifier_block *nb,
struct lineevent_state *le = container_of(nb, struct lineevent_state,
device_unregistered_nb);
- free_line_irq(le->irq, le);
+ free_line_irq(le->irq, le, &le->irq_label);
le->irq = 0;
wake_up_poll(&le->wait, EPOLLIN | EPOLLERR);
@@ -1908,7 +1916,7 @@ static void lineevent_free(struct lineevent_state *le)
if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb);
- free_line_irq(le->irq, le);
+ free_line_irq(le->irq, le, &le->irq_label);
if (le->desc)
gpiod_free(le->desc);
kfree(le->label);
@@ -2147,6 +2155,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
}
le->irq = irq;
+ le->irq_label = label;
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
anon_inode_getfile("gpio-event", &lineevent_fileops, le,
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (4 preceding siblings ...)
2026-10-29 11:29 ` [PATCH v4 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
2026-10-30 11:14 ` Kent Gibson
2026-10-29 11:29 ` [PATCH v4 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
6 siblings, 1 reply; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable,
Sashiko
With both edges requested, the IRQ threads read the line to tell a
rising edge from a falling one, and report any non-zero result as
rising, an error included. A read fails when a hot-pluggable chip is
going away, or when gpiochip_remove() has cleared gdev->chip before the
IRQ is freed.
Drop the event when the read fails, as debounce_work_func() does.
Reproduced with gpio-sim and a test delay in the IRQ threads: a falling
edge during chip removal is read as rising; with this change, it is
dropped and read() returns -ENODEV. The HTE path was compile-tested.
Fixes: 61f922db7221 ("gpio: userspace ABI for reading GPIO line events")
Fixes: 73e0341992b6 ("gpiolib: cdev: support edge detection for uAPI v2")
Fixes: 2068339a6c35 ("gpiolib: cdev: Add hardware timestamp clock type")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20261020-gpiolib-chip-removal-v3-0-aambrozewicz@nvidia.com?part=6
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib-cdev.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index cf23b5296fd1..0d6853751e6e 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -643,6 +643,8 @@ static enum hte_return process_hw_ts_thread(void *p)
level = (line->raw_level >= 0) ?
line->raw_level :
gpiod_get_raw_value_cansleep(line->desc);
+ if (level < 0)
+ return HTE_CB_HANDLED;
if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
level = !level;
@@ -743,6 +745,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
struct line *line = p;
struct linereq *lr = line->req;
struct gpio_v2_line_event le;
+ int level;
/* Do not leak kernel stack to userspace */
memset(&le, 0, sizeof(le));
@@ -763,7 +766,10 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) {
case GPIO_V2_LINE_FLAG_EDGE_BOTH:
- le.id = line_event_id(gpiod_get_value_cansleep(line->desc));
+ level = gpiod_get_value_cansleep(line->desc);
+ if (level < 0)
+ return IRQ_HANDLED;
+ le.id = line_event_id(level);
break;
case GPIO_V2_LINE_FLAG_EDGE_RISING:
le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
@@ -2006,6 +2012,8 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
&& le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
int level = gpiod_get_value_cansleep(le->desc);
+ if (level < 0)
+ return IRQ_HANDLED;
if (level)
/* Emit low-to-high event */
ge.id = GPIOEVENT_EVENT_RISING_EDGE;
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 7/7] gpiolib: let the character device free its IRQs first on chip removal
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (5 preceding siblings ...)
2026-10-29 11:29 ` [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
@ 2026-10-29 11:29 ` Adrian Ambrozewicz
6 siblings, 0 replies; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-29 11:29 UTC (permalink / raw)
To: Bartosz Golaszewski, Linus Walleij
Cc: Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Herve Codina,
Jia Jia, Andy Shevchenko, linux-gpio, linux-kernel, stable
gpiochip_remove() force-frees the remaining IRQs, removes the irqchip,
and only then calls the device notifier on which the character device
frees its own IRQs. The character device can then free an IRQ it no
longer owns: after its mapping was disposed of and the number reused, or
from a release that races gpiod_free_irqs() and
gpiochip_irqchip_remove(). free_irq() then warns "Trying to free
already-free IRQ" or uses a disposed descriptor.
Call the notifier once the chip pointer is cleared and the SRCU grace
period has elapsed, when no ioctl can request IRQs any more, and free
the remaining IRQs after it. Have the release paths free their IRQs
before unregistering from the notifier, under the lock its callback
takes (config_mutex, or a new mutex for v1 line events). Herve Codina
first proposed calling the notifier earlier.
IRQs that in-kernel users leave requested are now freed after the chip
pointer is cleared, when gpiod_*() calls on the chip fail.
Reproduced with a test-only chip on the hierarchical irqchip: both races
warn in all 30 cycles; with this change, none does.
Link: https://lore.kernel.org/all/20240227113426.253232-1-herve.codina@bootlin.com/
Fixes: ec8b6f55b981 ("gpio: free irqs that are still requested when the chip is being removed")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
---
drivers/gpio/gpiolib-cdev.c | 35 +++++++++++++++++++++++++++--------
drivers/gpio/gpiolib.c | 28 +++++++++++++++++++++++-----
2 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 0d6853751e6e..ed00bd3205ac 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1589,19 +1589,29 @@ static void linereq_free(struct linereq *lr)
{
unsigned int i;
+ /*
+ * Stop edge detection while still registered for the notification, so
+ * that gpiochip_remove() does not get past it until the IRQs are freed.
+ */
+ scoped_guard(mutex, &lr->config_mutex) {
+ for (i = 0; i < lr->num_lines; i++) {
+ if (lr->lines[i].desc)
+ edge_detector_stop(&lr->lines[i]);
+ }
+ }
+
if (lr->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&lr->gdev->device_notifier,
&lr->device_unregistered_nb);
for (i = 0; i < lr->num_lines; i++) {
- if (lr->lines[i].desc) {
- edge_detector_stop(&lr->lines[i]);
+ if (lr->lines[i].desc)
gpiod_free(lr->lines[i].desc);
- }
}
kfifo_free(&lr->events);
kfree(lr->label);
gpio_device_put(lr->gdev);
+ mutex_destroy(&lr->config_mutex);
kvfree(lr);
}
@@ -1672,6 +1682,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
if (!lr)
return -ENOMEM;
lr->num_lines = ulr.num_lines;
+ mutex_init(&lr->config_mutex);
lr->gdev = gpio_device_get(gdev);
@@ -1689,7 +1700,6 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
return -ENOMEM;
}
- mutex_init(&lr->config_mutex);
init_waitqueue_head(&lr->wait);
INIT_KFIFO(lr->events);
lr->event_buffer_size = ulr.event_buffer_size;
@@ -1789,6 +1799,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
* @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO
* @irq_label: the name @irq was requested with
+ * @irq_lock: serializes freeing @irq on release and on chip removal
* @wait: wait queue that handles blocking reads of events
* @device_unregistered_nb: notifier block for receiving gdev unregister events
* @events: KFIFO for the GPIO events
@@ -1803,6 +1814,8 @@ struct lineevent_state {
u32 eflags;
int irq;
char *irq_label;
+ /* release vs. chip removal notifier */
+ struct mutex irq_lock;
wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
DECLARE_KFIFO(events, struct gpioevent_data, 16);
@@ -1838,8 +1851,10 @@ static int lineevent_unregistered_notify(struct notifier_block *nb,
struct lineevent_state *le = container_of(nb, struct lineevent_state,
device_unregistered_nb);
- free_line_irq(le->irq, le, &le->irq_label);
- le->irq = 0;
+ scoped_guard(mutex, &le->irq_lock) {
+ free_line_irq(le->irq, le, &le->irq_label);
+ le->irq = 0;
+ }
wake_up_poll(&le->wait, EPOLLIN | EPOLLERR);
@@ -1919,14 +1934,18 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
static void lineevent_free(struct lineevent_state *le)
{
+ scoped_guard(mutex, &le->irq_lock) {
+ free_line_irq(le->irq, le, &le->irq_label);
+ le->irq = 0;
+ }
if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb);
- free_line_irq(le->irq, le, &le->irq_label);
if (le->desc)
gpiod_free(le->desc);
kfree(le->label);
gpio_device_put(le->gdev);
+ mutex_destroy(&le->irq_lock);
kfree(le);
}
@@ -2100,6 +2119,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
le = kzalloc_obj(*le);
if (!le)
return -ENOMEM;
+ mutex_init(&le->irq_lock);
le->gdev = gpio_device_get(gdev);
if (eventreq.consumer_label[0] != '\0') {
@@ -2835,5 +2855,4 @@ void gpiolib_cdev_unregister(struct gpio_device *gdev)
{
destroy_workqueue(gdev->line_state_wq);
cdev_device_del(&gdev->chrdev, &gdev->dev);
- blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL);
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 49b2823dab0c..673265c9d95c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -851,11 +851,22 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc,
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
-static void gpiod_free_irqs(struct gpio_desc *desc)
+static void gpiod_free_irqs(struct gpio_chip *gc, struct gpio_desc *desc)
{
- int irq = gpiod_to_irq(desc);
- struct irq_desc *irqd = irq_to_desc(irq);
+ struct irq_desc *irqd;
void *cookie;
+ int irq;
+
+ /* Called with gdev->chip already NULL, so gpiod_to_irq() can't be used. */
+ if (!gc->to_irq)
+ return;
+ irq = gc->to_irq(gc, gpiod_hwgpio(desc));
+ if (irq <= 0)
+ return;
+
+ irqd = irq_to_desc(irq);
+ if (!irqd)
+ return;
for (;;) {
/*
@@ -887,7 +898,7 @@ static void gpiochip_free_remaining_irqs(struct gpio_chip *gc)
struct gpio_desc *desc;
for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_USED_AS_IRQ)
- gpiod_free_irqs(desc);
+ gpiod_free_irqs(gc, desc);
}
static void gpiodev_release(struct device *dev)
@@ -1422,7 +1433,6 @@ void gpiochip_remove(struct gpio_chip *gc)
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gc);
gpiochip_free_hogs(gc);
- gpiochip_free_remaining_irqs(gc);
scoped_guard(mutex, &gpio_devices_lock)
list_del_rcu(&gdev->list);
@@ -1431,6 +1441,14 @@ void gpiochip_remove(struct gpio_chip *gc)
/* Numb the device, cancelling all outstanding operations */
rcu_assign_pointer(gdev->chip, NULL);
synchronize_srcu(&gdev->srcu);
+
+ /*
+ * No new interrupts can be requested through the character device now.
+ * Have it free the ones it holds before forcibly freeing the rest, and
+ * both before the irqchip and its mappings go away.
+ */
+ blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL);
+ gpiochip_free_remaining_irqs(gc);
gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge
2026-10-29 11:29 ` [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
@ 2026-10-30 11:14 ` Kent Gibson
2026-10-30 12:31 ` Adrian Ambrozewicz
0 siblings, 1 reply; 11+ messages in thread
From: Kent Gibson @ 2026-10-30 11:14 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Paul E. McKenney, Herve Codina, Jia Jia, Andy Shevchenko,
linux-gpio, linux-kernel, stable, Sashiko
On Thu, Oct 29, 2026 at 01:29:21PM +0200, Adrian Ambrozewicz wrote:
> With both edges requested, the IRQ threads read the line to tell a
> rising edge from a falling one, and report any non-zero result as
> rising, an error included. A read fails when a hot-pluggable chip is
> going away, or when gpiochip_remove() has cleared gdev->chip before the
> IRQ is freed.
>
> Drop the event when the read fails, as debounce_work_func() does.
>
> Reproduced with gpio-sim and a test delay in the IRQ threads: a falling
> edge during chip removal is read as rising; with this change, it is
> dropped and read() returns -ENODEV. The HTE path was compile-tested.
>
> Fixes: 61f922db7221 ("gpio: userspace ABI for reading GPIO line events")
> Fixes: 73e0341992b6 ("gpiolib: cdev: support edge detection for uAPI v2")
> Fixes: 2068339a6c35 ("gpiolib: cdev: Add hardware timestamp clock type")
How did you determine that? Those commits are where the associated blocks
of code were first added - not where the problem first exhibited.
The failure mode you are addressing was added by
commit d83cee3d ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU").
Prior to that gpiod_get_raw_value_commit(), and gpiod_get_value_cansleep()
and gpiod_get_raw_value_cansleep() that wrap it, wouldn't return the error
that you are handling.
Could you bisect to confirm?
The code change itself looks good.
Cheers,
Kent.
> Cc: stable@vger.kernel.org
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://sashiko.dev/#/patchset/20261020-gpiolib-chip-removal-v3-0-aambrozewicz@nvidia.com?part=6
> Assisted-by: LLM
> Signed-off-by: Adrian Ambrozewicz <aambrozewicz@nvidia.com>
> ---
> drivers/gpio/gpiolib-cdev.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
> index cf23b5296fd1..0d6853751e6e 100644
> --- a/drivers/gpio/gpiolib-cdev.c
> +++ b/drivers/gpio/gpiolib-cdev.c
> @@ -643,6 +643,8 @@ static enum hte_return process_hw_ts_thread(void *p)
> level = (line->raw_level >= 0) ?
> line->raw_level :
> gpiod_get_raw_value_cansleep(line->desc);
> + if (level < 0)
> + return HTE_CB_HANDLED;
>
> if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
> level = !level;
> @@ -743,6 +745,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
> struct line *line = p;
> struct linereq *lr = line->req;
> struct gpio_v2_line_event le;
> + int level;
>
> /* Do not leak kernel stack to userspace */
> memset(&le, 0, sizeof(le));
> @@ -763,7 +766,10 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
>
> switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) {
> case GPIO_V2_LINE_FLAG_EDGE_BOTH:
> - le.id = line_event_id(gpiod_get_value_cansleep(line->desc));
> + level = gpiod_get_value_cansleep(line->desc);
> + if (level < 0)
> + return IRQ_HANDLED;
> + le.id = line_event_id(level);
> break;
> case GPIO_V2_LINE_FLAG_EDGE_RISING:
> le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
> @@ -2006,6 +2012,8 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
> && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
> int level = gpiod_get_value_cansleep(le->desc);
>
> + if (level < 0)
> + return IRQ_HANDLED;
> if (level)
> /* Emit low-to-high event */
> ge.id = GPIOEVENT_EVENT_RISING_EDGE;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge
2026-10-30 11:14 ` Kent Gibson
@ 2026-10-30 12:31 ` Adrian Ambrozewicz
2026-10-30 13:46 ` Kent Gibson
0 siblings, 1 reply; 11+ messages in thread
From: Adrian Ambrozewicz @ 2026-10-30 12:31 UTC (permalink / raw)
To: Kent Gibson
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Paul E. McKenney, Herve Codina, Jia Jia, Andy Shevchenko,
linux-gpio, linux-kernel, stable, Sashiko
On Fri, Oct 30, 2026 at 07:14:35PM +0800, Kent Gibson wrote:
> > Fixes: 61f922db7221 ("gpio: userspace ABI for reading GPIO line events")
> > Fixes: 73e0341992b6 ("gpiolib: cdev: support edge detection for uAPI v2")
> > Fixes: 2068339a6c35 ("gpiolib: cdev: Add hardware timestamp clock type")
>
> How did you determine that? Those commits are where the associated blocks
> of code were first added - not where the problem first exhibited.
From the hot-plug case in the message: a driver's ->get() error has
reached these checks since each was added. Removal is newer, as you
say.
> The failure mode you are addressing was added by
> commit d83cee3d ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU").
> Prior to that gpiod_get_raw_value_commit(), and gpiod_get_value_cansleep()
> and gpiod_get_raw_value_cansleep() that wrap it, wouldn't return the error
> that you are handling.
>
> Could you bisect to confirm?
Confirmed: with gpio-sim and delayed IRQ threads, a falling edge
during removal reads as falling on the parent of commit d83cee3d2bb1
("gpio: protect the pointer to gpio_chip in gpio_device with SRCU")
and as rising on it, 10 of 10 each.
A test chip whose ->get() returns -EIO gives a rising edge on that
parent and on gpio/for-current, none with this patch. pcf857x and dln2
already returned such errors at commit 61f922db7221
("gpio: userspace ABI for reading GPIO line events").
I would use Fixes: 61f922db7221 for ->get() errors and d83cee3d2bb1
for removal. Is that OK?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge
2026-10-30 12:31 ` Adrian Ambrozewicz
@ 2026-10-30 13:46 ` Kent Gibson
0 siblings, 0 replies; 11+ messages in thread
From: Kent Gibson @ 2026-10-30 13:46 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Paul E. McKenney, Herve Codina, Jia Jia, Andy Shevchenko,
linux-gpio, linux-kernel, stable, Sashiko
On Fri, Oct 30, 2026 at 02:31:10PM +0200, Adrian Ambrozewicz wrote:
> On Fri, Oct 30, 2026 at 07:14:35PM +0800, Kent Gibson wrote:
> > > Fixes: 61f922db7221 ("gpio: userspace ABI for reading GPIO line events")
> > > Fixes: 73e0341992b6 ("gpiolib: cdev: support edge detection for uAPI v2")
> > > Fixes: 2068339a6c35 ("gpiolib: cdev: Add hardware timestamp clock type")
> >
> > How did you determine that? Those commits are where the associated blocks
> > of code were first added - not where the problem first exhibited.
>
> From the hot-plug case in the message: a driver's ->get() error has
> reached these checks since each was added. Removal is newer, as you
> say.
>
> > The failure mode you are addressing was added by
> > commit d83cee3d ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU").
> > Prior to that gpiod_get_raw_value_commit(), and gpiod_get_value_cansleep()
> > and gpiod_get_raw_value_cansleep() that wrap it, wouldn't return the error
> > that you are handling.
> >
> > Could you bisect to confirm?
>
> Confirmed: with gpio-sim and delayed IRQ threads, a falling edge
> during removal reads as falling on the parent of commit d83cee3d2bb1
> ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU")
> and as rising on it, 10 of 10 each.
>
> A test chip whose ->get() returns -EIO gives a rising edge on that
> parent and on gpio/for-current, none with this patch. pcf857x and dln2
> already returned such errors at commit 61f922db7221
> ("gpio: userspace ABI for reading GPIO line events").
>
Ok, so the driver ->get() itself may return an error if the physical chip
has gone away. Hadn't considered that path.
Could you update the commit message to better describe that case?
> I would use Fixes: 61f922db7221 for ->get() errors and d83cee3d2bb1
> for removal. Is that OK?
Not sure - I'll defer to Bart on that.
Cheers,
Kent.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-30 13:46 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 11:29 [PATCH v4 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
2026-10-29 11:29 ` [PATCH v4 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
2026-10-30 11:14 ` Kent Gibson
2026-10-30 12:31 ` Adrian Ambrozewicz
2026-10-30 13:46 ` Kent Gibson
2026-10-29 11:29 ` [PATCH v4 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.