[PATCH v12 00/11] riscv: add SBI Supervisor Software Events support

From: Zhanpeng Zhang

Date: Thu Oct 29 2026 - 03:49:16 EST


This revision follows Anup's v11 [1] and uses the same riscv/for-next
base, a5ae1bf65cee. It incorporates the entry-state fix identified by
Himanshu, replaces the temporary GUP workaround for perf user-stack
sampling, and brings together the outstanding preemption, fault-entry,
lockdep and PMU error-handling changes. Thanks to Anup for the rebase
and PMU testing, and to Paul for coordinating integration.

Why SSE
=======

RISC-V does not define an ordinary supervisor-mode non-maskable interrupt
(NMI). When Linux clears SIE, a normal supervisor interrupt remains pending
until interrupts are enabled again. That is appropriate for ordinary
interrupt handling, but leaves a blind spot for profiling and delays
notifications that need attention inside an interrupt-disabled region.

The SBI Supervisor Software Events (SSE) extension [2] provides a
firmware-to-supervisor event delivery interface that does not depend on
SIE. Linux registers an entry point, and firmware can transfer control to
it while ordinary supervisor interrupts are masked. After the handler
completes the event, firmware resumes the interrupted execution.

PMU overflow is the first client in this series. The same framework is
intended to support other clients, including high-priority RAS reports;
the RAS client implementation is separate from this series.

Delivery and implementation
===========================

For a hardware-triggered event, the flow is:

Linux kernel Firmware Hardware
interrupted SSE handler OpenSBI source
context | | |
| | | |
1. setup | |-----register-------> |
| |------enable--------> |
2. trigger | | <-----event------|
| | +-------------------+ |
3. save | | | context save | |
| | +-------------------+ |
| | | |
| | +-------------------+ |
4. inject | | | handler setup | |
| | +-------------------+ |
| <-------mret---------| |
| +-------------------+ | |
5. handle | | event handling | | |
| +-------------------+ | |
6. complete | |-----complete-------> |
| | +-------------------+ |
7. restore | | | context restore | |
| | +-------------------+ |
8. resume <---------------mret----------------| |
| | | |

Software-injected events do not need the hardware trigger. The diagram
shows a typical OpenSBI implementation of the firmware interface.

Linux enters on a dedicated event stack and establishes NMI context.
The entry wrapper reconstructs the interrupted pt_regs for the callback,
rather than treating the synthetic handler context as the sampled task.
It also preserves Linux-owned trap state, task stack metadata and the
temporary register slots used by ordinary exception entry.

Handlers must not sleep or rely on locks that the interrupted code may
hold. A nofault user access may still raise a synchronous exception: that
exception must return through its exception-table fixup without paging
memory in or opening an interrupt window. Preserving the interrupted
entry state across such nested exceptions is therefore essential.

The firmware driver provides a common client API for local and global
events. It tracks ownership across partial setup and teardown failures,
keeps callback resources alive while firmware may still reference them,
and integrates event handling with CPU hotplug, shutdown and kexec.

PMU overflow and perf
=====================

Ordinary PMU interrupts cannot sample an interrupt-disabled critical
section until Linux unmasks interrupts. The sample then often lands at
that boundary, obscuring the work that preceded it. SSE removes this
SIE-induced delivery delay. It does not promise zero skid or precise-event
sampling, but lets perf observe execution inside those critical sections.

No new perf command or perf.data format is required. The PMU driver
selects its delivery mechanism during setup. With PMU-SSE configured,
explicitly unsupported firmware or events can use ordinary interrupts;
other setup errors do not silently select that route. There is no runtime
SSE-to-IRQ transition after SSE has become active.

Kernel and user callchains use the interrupted context. Kernel unwinding
falls back to the interrupted PC where stack bounds cannot be established.
For DWARF sampling, perf copies the raw user stack with page faults
disabled and truncates it at an inaccessible address. V12 uses inatomic
uaccess and exception-table fixup instead of fast-only GUP. The additional
address-space checks reject a missing mm or mismatched active page-table
root; they are not a complete equivalent of x86's nmi_uaccess_okay().

The PMU path retains throttling and stopped-event semantics. It restores
counters and userpage state before enabling an event after CPU power
management, and stops affected counters if an SSE operation fails.

Hardware observations
=====================

The following measurements were reported with v10. They illustrate the
delivery mechanism, not a new benchmark of this v12 source tree. The IRQ
and SSE comparisons used the same kernel source and perf binary on a
RISC-V server platform.

For perf bench sched pipe, the mean of three runs of three million
single-CPU operations was:

rate IRQ ops/s SSE ops/s delta IRQ samples/s SSE samples/s
-F 99 337,707 339,555 +0.55% 98.0 98.6
-F 999 338,352 338,289 -0.02% 995.7 998.0
-F 5000 329,002 333,034 +1.23% 5001.7 5001.6

These runs had no lost samples. Both routes reached the requested rates,
with workload throughput differing by at most 1.23% in this comparison.

The same workload makes the irq-off sampling bias visible. In the -F 999
IRQ profile, finish_task_switch() and _raw_spin_unlock_irqrestore()
accounted for 54.99% of samples. Sampling via SSE instead exposed work
inside the critical sections, including scheduling, locking and address
space switching. Leading entries from the two reports were:

Ordinary PMU IRQ
overhead symbol
36.63% finish_task_switch.isra.0
18.36% _raw_spin_unlock_irqrestore
7.66% __internal_syscall_cancel
7.55% do_trap_ecall_u
4.19% mutex_lock
3.64% mutex_unlock
3.06% exit_to_user_mode_loop

PMU-SSE
overhead symbol
5.48% __kprobes_text_end
5.29% __schedule
5.10% ret_from_exception
4.71% do_raw_spin_lock
4.01% do_trap_ecall_u
3.99% mutex_lock
3.66% switch_mm
3.43% mutex_unlock
3.29% exit_to_user_mode_loop
3.28% psi_group_change

Symbols are reproduced as reported by perf. This is a change in what the
profile can observe, not evidence that SSE made those functions faster.

Entry-state fix and validation
==============================

Himanshu identified that TASK_TI_A0/A1/A2 were not preserved across SSE
handling [3]. Ordinary RV64 exception entry temporarily stores registers
in these slots. If SSE interrupts that window and its handler takes a
nested exception, the nested entry can overwrite slots still needed by
the interrupted entry path. Patch 2 preserves and restores them alongside
the task's kernel and user stack pointers.

Hardware A/B testing confirmed that preserving these scratch slots fixes
the reproduced corruption, allowing the temporary GUP workaround to be
replaced with nofault uaccess.

Guest SSE delivery, guest callchains and hibernation are outside the scope
of this series.

Firmware requirements
=====================

Firmware must implement SSE and the local PMU event. PMU testing also
requires the OpenSBI fixes that preserve pending overflows across event
enablement [4] and clear stale SPV for non-virtualized events [5].

Changes in v12
==============

Relative to Anup's v11 [1], on the same a5ae1bf65cee base:

- P2: Preserve TASK_TI_A0/A1/A2, fix per-CPU initialization preemption,
and address the kernel test robot's unused-function warning.
- P3/P4: Clarify the kexec restriction and retain v10 RESEND's CPU-hotplug
preemption guards.
- P5/P6: Order RV32 vmalloc synchronization before nofault fixup and add
NMI-safe lockdep irqflags tracking.
- P7: Replace GUP with nofault uaccess.
- P8: Fix early-error counter STOP, ordinary IRQ CPU-PM ordering and IRQ
cleanup tracking; carry Conor's Acked-by [6].
- P9: Split MAINTAINERS updates out; keep both selftests last.

Acknowledgements
================

The original feature patches were developed by Clément Léger and
Himanshu Chauhan. Thanks to Himanshu for identifying the missing scratch
slot preservation and reviewing the entry and lifecycle code; to Susheng
Yang for reporting the callchain failure and providing a reproducer; to
Conor Dooley for review and acknowledgements; and to Anup Patel, Paul
Walmsley and the kernel test robot for integration work and feedback.

[1] https://lore.kernel.org/r/20261028165531.940336-1-anup.patel@xxxxxxxxxxxxxxxx/
[2] https://docs.riscv.org/reference/sbi/ext-sse.html
[3] https://lore.kernel.org/r/as79MHtNYd-VbUug@xxxxxxxxxxxxxxxxxxxxxxxxxxx/
[4] https://github.com/riscv-software-src/opensbi/commit/f30a54f3b3a091c225a00476f4039bf399badd1f
[5] https://github.com/riscv-software-src/opensbi/commit/35511bc6ee1c9c17b6a89b44c52e2044bb51b979
[6] https://lore.kernel.org/r/20261002-reach-cloning-f1594729dbe3@wendy/

Clément Léger (5):
riscv: add SBI SSE extension definitions
riscv: add support for SBI Supervisor Software Events extension
drivers: firmware: add riscv SSE support
perf: RISC-V: add support for SSE event
selftests/riscv: add SSE test module

Zhanpeng Zhang (6):
riscv: sse: mask events during shutdown and kexec
riscv: mm: avoid enabling interrupts for nofault page faults
riscv: enable NMI-safe lockdep irqflags tracking
perf: RISC-V: support callchains with SSE delivery
MAINTAINERS: add RISC-V SSE maintainers and file coverage
selftests/riscv: add perf user-stack SSE copy regression test

Documentation/arch/riscv/index.rst | 1 +
Documentation/arch/riscv/pmu-sse.rst | 62 +
MAINTAINERS | 22 +
arch/riscv/Kconfig | 1 +
arch/riscv/include/asm/asm.h | 14 +-
arch/riscv/include/asm/perf_event.h | 10 +
arch/riscv/include/asm/sbi.h | 63 +
arch/riscv/include/asm/scs.h | 7 +
arch/riscv/include/asm/sse.h | 82 ++
arch/riscv/include/asm/thread_info.h | 1 +
arch/riscv/kernel/Makefile | 1 +
arch/riscv/kernel/asm-offsets.c | 14 +
arch/riscv/kernel/entry.S | 14 +
arch/riscv/kernel/machine_kexec.c | 11 +
arch/riscv/kernel/perf_callchain.c | 140 +++
arch/riscv/kernel/reset.c | 18 +
arch/riscv/kernel/sbi_sse.c | 252 ++++
arch/riscv/kernel/sbi_sse_entry.S | 237 ++++
arch/riscv/kernel/smp.c | 17 +
arch/riscv/mm/fault.c | 32 +-
drivers/firmware/Kconfig | 1 +
drivers/firmware/Makefile | 1 +
drivers/firmware/riscv/Kconfig | 18 +
drivers/firmware/riscv/Makefile | 3 +
drivers/firmware/riscv/riscv_sbi_sse.c | 1228 ++++++++++++++++++++
drivers/perf/Kconfig | 11 +
drivers/perf/riscv_pmu.c | 14 +-
drivers/perf/riscv_pmu_sbi.c | 561 +++++++--
include/linux/cpuhotplug.h | 1 +
include/linux/perf/riscv_pmu.h | 20 +-
include/linux/riscv_sbi_sse.h | 95 ++
tools/testing/selftests/riscv/Makefile | 2 +-
tools/testing/selftests/riscv/sse/Makefile | 10 +
tools/testing/selftests/riscv/sse/module/Makefile | 22 +
.../selftests/riscv/sse/module/riscv_sse_test.c | 1154 ++++++++++++++++++
tools/testing/selftests/riscv/sse/run_sse_test.sh | 59 +
.../testing/selftests/riscv/sse/sse_perf_ustack.c | 564 +++++++++
37 files changed, 4656 insertions(+), 107 deletions(-)

base-commit: a5ae1bf65cee84bcc121d659e7e22a767853f5f5
--
2.50.1