[PATCH AUTOSEL 6.19-5.15] iio: Use IRQF_NO_THREAD

From: Sasha Levin

Date: Wed Feb 18 2026 - 21:17:15 EST


From: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>

[ Upstream commit 04d390af97f2c28166f7ddfe1a6bda622e3a4766 ]

The interrupt handler iio_trigger_generic_data_rdy_poll() will invoke
other interrupt handler and this supposed to happen from within the
hardirq.

Use IRQF_NO_THREAD to forbid forced-threading.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxx>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

`IRQF_NO_THREAD` has been available for a long time (it's a well-
established flag).

### Classification

This is a **bug fix for PREEMPT_RT kernels**. Without `IRQF_NO_THREAD`:
1. On PREEMPT_RT, the interrupt handler gets force-threaded
2. The handler runs in thread context instead of hardirq context
3. `iio_trigger_poll()` calls `generic_handle_irq()` which checks
`in_hardirq()`
4. The check fails → `WARN_ON_ONCE` fires → sub-IRQ is not dispatched →
**IIO trigger doesn't work**

This is not a theoretical issue — it's a complete functional breakage of
IIO triggers on PREEMPT_RT kernels.

### Scope and Risk Assessment

- **4 files changed**, each with a one-line flag addition
- No behavioral change on non-RT kernels (the flag is ignored when not
force-threading)
- No risk of regression — `IRQF_NO_THREAD` is a well-understood, widely-
used flag
- The fix is purely additive (adding a flag to existing IRQ registration
calls)

### Risk vs Benefit

- **Risk**: Extremely low. Adding `IRQF_NO_THREAD` only prevents force-
threading on RT kernels. On non-RT kernels, this flag is a no-op.
- **Benefit**: Fixes complete IIO trigger breakage for these four sensor
drivers on PREEMPT_RT kernels.

### Concerns

1. **Scope**: This only fixes 4 of 19+ drivers that use
`iio_trigger_generic_data_rdy_poll`. There may be other drivers
needing the same fix, but each fix is self-contained.
2. **Dependencies**: None. This is a simple flag addition that applies
cleanly.
3. **Affected versions**: Any kernel with PREEMPT_RT support and these
IIO drivers.

### Verification

- Read `iio_trigger_generic_data_rdy_poll()` at
`drivers/iio/industrialio-trigger.c:212` — confirmed it calls
`iio_trigger_poll()` which calls `generic_handle_irq()` at line 204
- Read `iio_trigger_poll()` comment at line 193: "This function should
only be called from a hard IRQ context" — confirmed the hardirq
requirement
- Read `kernel/irq/irqdesc.c:666`: confirmed `WARN_ON_ONCE(!in_hardirq()
...)` check that would trigger if called from thread context
- Read `ad7766_irq()` at `drivers/iio/adc/ad7766.c:187-191`: confirmed
it also calls `iio_trigger_poll()` directly, same issue
- Confirmed `IRQF_NO_THREAD` is defined in
`include/linux/interrupt.h:83` and has been available for a long time
- Confirmed author Sebastian Andrzej Siewior is the PREEMPT_RT
maintainer — this is from someone with deep expertise
- Confirmed reviewer Andy Shevchenko and IIO maintainer Jonathan Cameron
signed off
- `PREEMPT_RT` config option exists since at least v5.x (2019), so this
affects stable trees
- Confirmed no behavioral change on non-RT kernels (IRQF_NO_THREAD is
ignored when not force-threading)

### Conclusion

This is a small, surgical, obviously correct fix for a real bug that
causes complete IIO trigger failure on PREEMPT_RT kernels. It adds
`IRQF_NO_THREAD` to four IRQ registrations where the handler must run in
hardirq context (because it calls `generic_handle_irq()`). The fix has
zero risk on non-RT kernels and is essential for RT kernel users. It
meets all stable kernel criteria: fixes a real bug, is small and
contained, is obviously correct, and doesn't introduce new features.

**YES**

drivers/iio/accel/bma180.c | 5 +++--
drivers/iio/adc/ad7766.c | 2 +-
drivers/iio/gyro/itg3200_buffer.c | 8 +++-----
drivers/iio/light/si1145.c | 2 +-
4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index 8925f5279e627..7bc6761f51354 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -986,8 +986,9 @@ static int bma180_probe(struct i2c_client *client)
}

ret = devm_request_irq(dev, client->irq,
- iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
- "bma180_event", data->trig);
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "bma180_event", data->trig);
if (ret) {
dev_err(dev, "unable to request IRQ\n");
goto err_trigger_free;
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 4d570383ef025..1e6bfe8765ab3 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -261,7 +261,7 @@ static int ad7766_probe(struct spi_device *spi)
* don't enable the interrupt to avoid extra load on the system
*/
ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
- IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN | IRQF_NO_THREAD,
dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index a624400a239cb..cf97adfa97274 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -118,11 +118,9 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev)
if (!st->trig)
return -ENOMEM;

- ret = request_irq(st->i2c->irq,
- &iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_RISING,
- "itg3200_data_rdy",
- st->trig);
+ ret = request_irq(st->i2c->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "itg3200_data_rdy", st->trig);
if (ret)
goto error_free_trig;

diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index f8eb251eca8dc..ef0abc4499b74 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -1248,7 +1248,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)

ret = devm_request_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"si1145_irq",
trig);
if (ret < 0) {
--
2.51.0