[PATCH v5 6/7] gpiolib: cdev: don't report a failed line read as an edge
From: Adrian Ambrozewicz
Date: Tue Nov 03 2026 - 06:14:14 EST
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@xxxxxxxxxxxxxxx
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://sashiko.dev/#/patchset/20261020-gpiolib-chip-removal-v3-0-aambrozewicz@xxxxxxxxxx?part=6
Assisted-by: LLM
Signed-off-by: Adrian Ambrozewicz <aambrozewicz@xxxxxxxxxx>
---
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