* [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space
@ 2026-11-03 10:32 Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
` (6 more replies)
0 siblings, 7 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 twice (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 also affects in-kernel users: their remaining IRQs are freed
after the chip pointer is cleared, when gpiod_*() calls fail. Patch 6
drops edge events whose line read fails instead of reporting an 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 and patch 7 needs 6; patches 3, 5 and 7 fix
a 6.13 commit also in 6.12.y; patch 4 applies to 6.12.y.
Tested under QEMU (x86_64) 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; I reviewed and tested
every change.
Changes in v5:
- Rebased on gpio/for-current.
- Patch 6: describe driver ->get() errors; new Fixes tags (Kent,
Bartosz).
- Link to v4: https://lore.kernel.org/r/20261029-gpiolib-chip-removal-v4-0-aambrozewicz@nvidia.com
Changes in v4:
- Rebased on gpio/for-current.
- New patch 6: drop edge events when a line read fails (Sashiko).
- Patch 2: the NULL label fix was 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(): separate fix
planned.
- 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: 9e4b35c95fede87bad36f68b7ff1eae94ea6b910
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:48 ` Andy Shevchenko
2026-11-03 10:32 ` [PATCH v5 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
` (5 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 fbdeda2f2787..0439e016f246 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 [flat|nested] 13+ messages in thread
* [PATCH v5 2/7] gpiolib: free the label of lines released after their chip was removed
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
` (4 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 0439e016f246..ec5f81f4fd9e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2690,7 +2690,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));
@@ -2709,7 +2714,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 [flat|nested] 13+ messages in thread
* [PATCH v5 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
` (3 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 3f1f8c9acfc3..28cb63375d40 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)
@@ -976,10 +980,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)
@@ -994,6 +996,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))
@@ -1789,6 +1810,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;
@@ -1868,8 +1892,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 [flat|nested] 13+ messages in thread
* [PATCH v5 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read()
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (2 preceding siblings ...)
2026-11-03 10:32 ` [PATCH v5 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
` (2 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 28cb63375d40..610628d0b475 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1509,6 +1509,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)
{
@@ -1517,10 +1531,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;
@@ -1535,9 +1548,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) {
@@ -1832,10 +1848,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,
@@ -1863,9 +1878,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) {
@@ -2609,10 +2627,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);
@@ -2630,9 +2647,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 [flat|nested] 13+ messages in thread
* [PATCH v5 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (3 preceding siblings ...)
2026-11-03 10:32 ` [PATCH v5 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
6 siblings, 0 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 610628d0b475..3623b05fef60 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
@@ -938,6 +942,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)
@@ -980,7 +985,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
@@ -1080,6 +1085,7 @@ static int edge_detector_setup(struct line *line,
}
line->irq = irq;
+ line->irq_label = label;
return 0;
}
@@ -1778,6 +1784,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
@@ -1791,6 +1798,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);
@@ -1826,7 +1834,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);
@@ -1910,7 +1918,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);
@@ -2149,6 +2157,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 [flat|nested] 13+ messages in thread
* [PATCH v5 6/7] gpiolib: cdev: don't report a failed line read as an edge
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (4 preceding siblings ...)
2026-11-03 10:32 ` [PATCH v5 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
6 siblings, 0 replies; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 treat an error as a high level.
The read fails when the chip is gone. A driver's ->get() returns an
error once its device stops answering: pcf857x when the I2C transfer
fails, dln2 when the USB adapter is unplugged. Since gdev->chip became
SRCU-protected, the read also returns -ENODEV between gpiochip_remove()
and the IRQ being freed.
Drop the event when the read fails, as debounce_work_func() does.
Tested with gpio-sim removal (test delay in the IRQ threads) and a
test chip whose ->get() returns -EIO: both reported a rising edge, none
with this change. The HTE path was compile-tested.
Fixes: 61f922db7221 ("gpio: userspace ABI for reading GPIO line events")
Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU")
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 3623b05fef60..50b1a77ef0f1 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;
@@ -745,6 +747,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));
@@ -765,7 +768,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;
@@ -2008,6 +2014,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] 13+ messages in thread
* [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
` (5 preceding siblings ...)
2026-11-03 10:32 ` [PATCH v5 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
@ 2026-11-03 10:32 ` Adrian Ambrozewicz
2026-11-03 10:52 ` Andy Shevchenko
6 siblings, 1 reply; 13+ messages in thread
From: Adrian Ambrozewicz @ 2026-11-03 10:32 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 50b1a77ef0f1..e6f1da943532 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1591,19 +1591,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);
}
@@ -1674,6 +1684,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);
@@ -1691,7 +1702,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;
@@ -1791,6 +1801,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
@@ -1805,6 +1816,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);
@@ -1840,8 +1853,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);
@@ -1921,14 +1936,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);
}
@@ -2102,6 +2121,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') {
@@ -2837,5 +2857,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 ec5f81f4fd9e..2aba76f79fd6 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 [flat|nested] 13+ messages in thread
* Re: [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu
2026-11-03 10:32 ` [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
@ 2026-11-03 10:48 ` Andy Shevchenko
2026-11-03 10:50 ` Andy Shevchenko
0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-11-03 10:48 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Kent Gibson, Paul E. McKenney, Herve Codina, Jia Jia, linux-gpio,
linux-kernel, stable
On Tue, Nov 03, 2026 at 12:32:34PM +0200, Adrian Ambrozewicz wrote:
> 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.
...
> Assisted-by: LLM
This tag is obsoleted. Instead explain under '---' how AI has been used to
generate this contribution.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu
2026-11-03 10:48 ` Andy Shevchenko
@ 2026-11-03 10:50 ` Andy Shevchenko
0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2026-11-03 10:50 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Kent Gibson, Paul E. McKenney, Herve Codina, Jia Jia, linux-gpio,
linux-kernel, stable
On Tue, Nov 03, 2026 at 12:48:51PM +0200, Andy Shevchenko wrote:
> On Tue, Nov 03, 2026 at 12:32:34PM +0200, Adrian Ambrozewicz wrote:
> > 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.
...
> > Assisted-by: LLM
>
> This tag is obsoleted. Instead explain under '---' how AI has been used to
> generate this contribution.
Actually do it in the cover letter, as seems the whole series tagged with it.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal
2026-11-03 10:32 ` [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
@ 2026-11-03 10:52 ` Andy Shevchenko
2026-11-03 11:26 ` Herve Codina
0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-11-03 10:52 UTC (permalink / raw)
To: Adrian Ambrozewicz
Cc: Bartosz Golaszewski, Linus Walleij, Bartosz Golaszewski,
Kent Gibson, Paul E. McKenney, Herve Codina, Jia Jia, linux-gpio,
linux-kernel, stable
On Tue, Nov 03, 2026 at 12:32:40PM +0200, Adrian Ambrozewicz wrote:
> 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/
No. Either make it Closes tag (if appropriate) or add a reference like
Link: $URL [1]
and mention [1] in the text, otherwise it makes Linus T. grumpy. I think
I already told that.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal
2026-11-03 10:52 ` Andy Shevchenko
@ 2026-11-03 11:26 ` Herve Codina
2026-11-03 11:35 ` Andy Shevchenko
0 siblings, 1 reply; 13+ messages in thread
From: Herve Codina @ 2026-11-03 11:26 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Adrian Ambrozewicz, Bartosz Golaszewski, Linus Walleij,
Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Jia Jia,
linux-gpio, linux-kernel, stable
Hi Andy, Adrian,
On Tue, 3 Nov 2026 12:52:10 +0200
Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:
> On Tue, Nov 03, 2026 at 12:32:40PM +0200, Adrian Ambrozewicz wrote:
> > 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/
>
> No. Either make it Closes tag (if appropriate) or add a reference like
>
> Link: $URL [1]
>
> and mention [1] in the text, otherwise it makes Linus T. grumpy. I think
> I already told that.
>
IHMO, "Link:" instead of "Closes:" according to this reply in the series
pointed out [2].
[2]: https://lore.kernel.org/all/20240923150522.66b418d1@bootlin.com/
The history was:
- [1] (the link in this patch) proposed a fix
- It has been rejected and a improved one has been done [2]
- [2] has been accepted and led to the commit ec8b6f55b981 ("gpio: free irqs
that are still requested when the chip is being removed"). This commit
is already mentioned as 'Fixes:" in this patch.
So, IMHO, in this patch:
...
takes (config_mutex, or a new mutex for v1 line events). Herve Codina
first proposed calling the notifier earlier [1].
...
Link: https://lore.kernel.org/all/20240227113426.253232-1-herve.codina@bootlin.com/ [1]
Fixes: ec8b6f55b981 ("gpio: free irqs that are still requested when the chip is being removed")
...
Best regards,
Hervé
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal
2026-11-03 11:26 ` Herve Codina
@ 2026-11-03 11:35 ` Andy Shevchenko
0 siblings, 0 replies; 13+ messages in thread
From: Andy Shevchenko @ 2026-11-03 11:35 UTC (permalink / raw)
To: Herve Codina
Cc: Adrian Ambrozewicz, Bartosz Golaszewski, Linus Walleij,
Bartosz Golaszewski, Kent Gibson, Paul E. McKenney, Jia Jia,
linux-gpio, linux-kernel, stable
On Tue, Nov 03, 2026 at 12:26:34PM +0100, Herve Codina wrote:
> On Tue, 3 Nov 2026 12:52:10 +0200
> Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote:
> > On Tue, Nov 03, 2026 at 12:32:40PM +0200, Adrian Ambrozewicz wrote:
...
> > > Link: https://lore.kernel.org/all/20240227113426.253232-1-herve.codina@bootlin.com/
> >
> > No. Either make it Closes tag (if appropriate) or add a reference like
> >
> > Link: $URL [1]
> >
> > and mention [1] in the text, otherwise it makes Linus T. grumpy. I think
> > I already told that.
> >
>
> IHMO, "Link:" instead of "Closes:" according to this reply in the series
> pointed out [2].
>
> [2]: https://lore.kernel.org/all/20240923150522.66b418d1@bootlin.com/
>
> The history was:
> - [1] (the link in this patch) proposed a fix
> - It has been rejected and a improved one has been done [2]
> - [2] has been accepted and led to the commit ec8b6f55b981 ("gpio: free irqs
> that are still requested when the chip is being removed"). This commit
> is already mentioned as 'Fixes:" in this patch.
>
> So, IMHO, in this patch:
> ...
> takes (config_mutex, or a new mutex for v1 line events). Herve Codina
> first proposed calling the notifier earlier [1].
> ...
> Link: https://lore.kernel.org/all/20240227113426.253232-1-herve.codina@bootlin.com/ [1]
> Fixes: ec8b6f55b981 ("gpio: free irqs that are still requested when the chip is being removed")
Thanks, WFM!
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-11-03 11:35 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 10:32 [PATCH v5 0/7] gpiolib: fix chip removal with lines still requested from user space Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 1/7] gpiolib: wait for pending label frees before cleaning up desc_srcu Adrian Ambrozewicz
2026-11-03 10:48 ` Andy Shevchenko
2026-11-03 10:50 ` Andy Shevchenko
2026-11-03 10:32 ` [PATCH v5 2/7] gpiolib: free the label of lines released after their chip was removed Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 3/7] gpiolib: cdev: do not free line IRQs again after the chip is removed Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 4/7] gpiolib: cdev: don't sleep in the SRCU read section in read() Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 5/7] gpiolib: cdev: don't leak the IRQ name when the chip is removed Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 6/7] gpiolib: cdev: don't report a failed line read as an edge Adrian Ambrozewicz
2026-11-03 10:32 ` [PATCH v5 7/7] gpiolib: let the character device free its IRQs first on chip removal Adrian Ambrozewicz
2026-11-03 10:52 ` Andy Shevchenko
2026-11-03 11:26 ` Herve Codina
2026-11-03 11:35 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®