[PATCH AUTOSEL 6.19-6.6] perf/cxlpmu: Replace IRQF_ONESHOT with IRQF_NO_THREAD

Sasha Levin sashal at kernel.org
Wed Feb 11 04:30:41 PST 2026


From: Sebastian Andrzej Siewior <bigeasy at linutronix.de>

[ Upstream commit ab26d9c85554c4ff1d95ca8341522880ed9219d6 ]

Passing IRQF_ONESHOT ensures that the interrupt source is masked until
the secondary (threaded) handler is done. If only a primary handler is
used then the flag makes no sense because the interrupt can not fire
(again) while its handler is running.
The flag also disallows force-threading of the primary handler and the
irq-core will warn about this.

The intention here was probably not allowing forced-threading.

Replace IRQF_ONESHOT with IRQF_NO_THREAD.

Reviewed-by: Jonathan Cameron <jonathan.cameron at huawei.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
Signed-off-by: Will Deacon <will at kernel.org>
Signed-off-by: Sasha Levin <sashal at kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have gathered all the information I need for a thorough analysis.
Let me compile my findings.

---

## Complete Analysis: `perf/cxlpmu: Replace IRQF_ONESHOT with
IRQF_NO_THREAD`

### 1. COMMIT MESSAGE ANALYSIS

The commit, authored by Sebastian Andrzej Siewior
(bigeasy at linutronix.de) — one of the PREEMPT_RT core maintainers —
replaces `IRQF_ONESHOT` with `IRQF_NO_THREAD` in the CXL PMU driver's
interrupt registration. The commit message explains:

- `IRQF_ONESHOT` ensures the interrupt source is masked until the
  threaded (secondary) handler finishes
- This driver only has a **primary** handler (`cxl_pmu_irq`) and no
  threaded handler
- Therefore `IRQF_ONESHOT` makes no sense here — the interrupt can't
  fire while its hardirq handler is running anyway
- The flag also **disables force-threading** of the primary handler
- The "irq-core will warn about this" (via lockdep assertions)
- The **intended** semantics were to prevent forced-threading, so
  `IRQF_NO_THREAD` is the correct replacement

### 2. CODE CHANGE ANALYSIS

The change is a **single flag swap** on one line:

```880:881:drivers/perf/cxl_pmu.c
        rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED |
IRQF_ONESHOT,
                              irq_name, info);
```

becomes:

```880:881:drivers/perf/cxl_pmu.c
        rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED |
IRQF_NO_THREAD,
                              irq_name, info);
```

#### What `IRQF_ONESHOT` does here (incorrectly):
Looking at `irq_setup_forced_threading()` in `kernel/irq/manage.c`:

```1291:1296:kernel/irq/manage.c
static int irq_setup_forced_threading(struct irqaction *new)
{
        if (!force_irqthreads())
                return 0;
        if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
                return 0;
```

Both `IRQF_NO_THREAD` and `IRQF_ONESHOT` cause
`irq_setup_forced_threading()` to bail out early, preventing the
interrupt from being force-threaded. However, `IRQF_ONESHOT` has an
**additional side effect**: it tells the IRQ core to mask the interrupt
line until a threaded handler completes. Since there is no threaded
handler here, this masking behavior is semantically wrong.

#### Why `IRQF_NO_THREAD` is the correct flag:
The `cxl_pmu_irq` handler is a PMU overflow interrupt handler that:
1. Reads the overflow register via `readq()`
2. Processes each overflowed counter via `__cxl_pmu_read()`
3. Clears the overflow status via `writeq()`

This handler interacts with perf core internals. As the arm-ccn PMU fix
(commit `0811ef7e2f54`) established, **PMU interrupt handlers must not
be force-threaded** because the perf core relies on strict CPU affinity
and interrupt disabling for mutual exclusion. Force-threading a PMU
interrupt handler would break these synchronization guarantees.

#### The actual bugs:

**Bug 1 — PREEMPT_RT / `threadirqs` warning:** When the kernel is booted
with `threadirqs` command-line parameter or PREEMPT_RT is enabled, the
IRQ core's lockdep infrastructure marks handlers as `hardirq_threaded`
if they can be force-threaded. In `kernel/irq/handle.c`:

```199:201:kernel/irq/handle.c
                if (irq_settings_can_thread(desc) &&
                    !(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU |
IRQF_ONESHOT)))
                        lockdep_hardirq_threaded();
```

Because `IRQF_ONESHOT` is already set, this particular path won't
trigger the threaded annotation, but the **masking semantics** of
ONESHOT are incorrect for a primary-only handler. In the `IRQF_ONESHOT`
path, the IRQ core does:

```1726:1727:kernel/irq/manage.c
                if (new->flags & IRQF_ONESHOT)
                        desc->istate |= IRQS_ONESHOT;
```

This causes the interrupt line to be masked during the handler and
unmask logic depends on `desc->threads_oneshot` — but there's no thread
to clear this mask, so it depends on the
`cond_unmask_irq`/`cond_unmask_eoi_irq` fallback path.

**Bug 2 — IRQF_SHARED conflict potential:** The interrupt uses
`IRQF_SHARED`. When sharing interrupts, all handlers on the same line
must agree on `IRQF_ONESHOT`. If another driver on the shared line
doesn't use `IRQF_ONESHOT`, `request_threaded_irq()` will fail with
`-EINVAL` at the mismatch check:

```1606:1607:kernel/irq/manage.c
                else if ((old->flags ^ new->flags) & IRQF_ONESHOT)
                        goto mismatch;
```

This is a real failure mode for shared interrupts — CXL PMU's incorrect
use of `IRQF_ONESHOT` could prevent other handlers from sharing the same
IRQ line.

### 3. CLASSIFICATION

This is a **bug fix** — it corrects incorrect IRQ flag usage that:
1. Applies semantically wrong masking behavior (ONESHOT without a
   thread)
2. Can trigger warnings/assertions under PREEMPT_RT or `threadirqs`
3. Could cause shared IRQ registration failures
4. Prevents force-threading in the wrong way (the intention is correct,
   but the mechanism is wrong)

### 4. SCOPE AND RISK ASSESSMENT

- **Lines changed:** 1 line (single flag change)
- **Files touched:** 1 (`drivers/perf/cxl_pmu.c`)
- **Complexity:** Minimal — straightforward flag replacement
- **Risk:** Extremely low. `IRQF_NO_THREAD` is the semantically correct
  flag for the intended purpose. The interrupt handler is a hardirq-only
  handler that performs only register I/O and atomic operations — it
  should never be threaded.
- **Subsystem:** CXL PMU performance monitoring driver (PCI-based)

### 5. USER IMPACT

- **PREEMPT_RT users:** On PREEMPT_RT kernels, `IRQF_ONESHOT` without a
  thread handler causes the interrupt line to be masked with incorrect
  oneshot semantics. This could lead to lockdep warnings or subtle
  interrupt masking issues.
- **Shared IRQ users:** CXL devices sharing PCI MSI/MSI-X vectors with
  other devices could experience IRQ registration failures due to
  ONESHOT flag mismatch.
- **General users:** The CXL PMU driver is used for performance
  monitoring on CXL 3.0 memory devices — an increasingly important
  hardware platform for servers and data centers.

### 6. STABILITY INDICATORS

- **Author:** Sebastian Andrzej Siewior — PREEMPT_RT core maintainer,
  expert on IRQ threading
- **Reviewer:** Jonathan Cameron — CXL maintainer
- **Signed-off-by:** Will Deacon — ARM architecture and perf subsystem
  maintainer
- **Pattern:** This is the same fix Sebastian has applied to multiple
  drivers (i2c-exynos5, i2c-hix5hd2) with proven track record
- **Precedent:** The arm-ccn PMU used exactly this fix
  (`IRQF_NO_THREAD`) for the same class of issue

### 7. DEPENDENCY CHECK

- No dependencies on other commits
- The driver exists in stable kernels 6.6.y and later (introduced in
  v6.5-rc1)
- The change is self-contained — just a flag change in
  `devm_request_irq()`

### 8. CONCLUSION

This is a small, surgical, well-understood bug fix that corrects
incorrect IRQ flag usage in the CXL PMU driver. The `IRQF_ONESHOT` flag
was semantically wrong for a primary-only interrupt handler and could
cause:
- Incorrect interrupt line masking behavior
- Shared IRQ registration failures
- Issues on PREEMPT_RT kernels

The replacement with `IRQF_NO_THREAD` correctly expresses the intended
behavior (prevent force-threading of this PMU interrupt handler) without
the incorrect side effects. The fix is trivial, low-risk, authored by a
world-class IRQ threading expert, and reviewed by CXL and ARM
maintainers.

**YES**

 drivers/perf/cxl_pmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c
index d094030220bf2..68a54d97d2a8a 100644
--- a/drivers/perf/cxl_pmu.c
+++ b/drivers/perf/cxl_pmu.c
@@ -877,7 +877,7 @@ static int cxl_pmu_probe(struct device *dev)
 	if (!irq_name)
 		return -ENOMEM;
 
-	rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED | IRQF_ONESHOT,
+	rc = devm_request_irq(dev, irq, cxl_pmu_irq, IRQF_SHARED | IRQF_NO_THREAD,
 			      irq_name, info);
 	if (rc)
 		return rc;
-- 
2.51.0




More information about the linux-arm-kernel mailing list