Skip to content

sched/clock: Keep the perf overflow state per CPU. - #21458

Open
casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:armv7-perf-overflow-smp-up
Open

casaroli wants to merge 1 commit into
apache:masterfrom
casaroli:armv7-perf-overflow-smp-up

Conversation

@casaroli

@casaroli casaroli commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Summary

With PERF_OVERFLOW_CORRECTION, perf_gettime() compares the counter with one global "last" value. On SMP each CPU has its own counter (PMCCNTR on ARMv7-A), and the counters do not hold the same value. A read on a CPU with a lower counter than the last read on another CPU counts as an overflow, and adds 2^32 cycles to all later values. On qemu-armv7a:smp a thread that runs for 500 ms shows 4795 ms of CPU time.

This PR keeps the overflow state for each CPU. The state belongs to one CPU, so perf_gettime() disables the local interrupts and needs no spinlock. The watchdog that reads the counter before it can wrap twice runs on one CPU only, so each CPU's watchdog now sends a SMP call to the CPU of its state.

Impact

SMP builds with PERF_OVERFLOW_CORRECTION only. A value from perf_gettime() compares only with a value from the same CPU, as in an SMP build without the option. With SMP off the object has the same size (304 bytes of text, 48 of bss on qemu-armv7a:nsh, before and after). The option is off by default. No API change, no documentation change (Documentation/ does not name the option).

Testing

Test program perfover.c: a thread pinned to each CPU in turn runs a busy loop (CLOCK_MONOTONIC) and reads CLOCK_THREAD_CPUTIME_ID, while a second thread on the next CPU sleeps 1 ms in a loop. qemu-armv7a:smp, 4 CPUs, QEMU TCG, SCHED_CRITMONITOR, ARCH_PERF_EVENTS, PERF_OVERFLOW_CORRECTION. CPU time in ms, CPU 0 to 3:

Busy time master this PR
500 ms (no second thread) 500, 500, 4795, 500 500, 500, 500, 500
1000 ms 5297, 9590, 5295, 5296 1000, 1000, 1000, 1000
10000 ms (2 wraps at 1 GHz) 35769, 27188, 35770, 14295 10000, 10000, 10000, 10000

To check the SMP call, the same 10000 ms test with perf_gettime() in the watchdog in place of the SMP call gives 10000, 1410, 1410, 1410: the CPUs that do not run the watchdog miss the wraps. The same test with 2 CPUs on this PR gives 10000, 10000. ostest exits with status 0 on master and with this PR.

No SMP (qemu-armv7a:nsh, same options): 10000 ms before and after.

Builds with -Werror and the option on: qemu-armv7a:smp, :nsh, :knsh_smp (kernel, SMP), :knsh (kernel), mps2-an500:knsh (protected), raspberrypi-pico-2:smp (ARMv8-M, SMP), rv-virt:smp (RISC-V, SMP). arty_a7:knsh-tickless (the defconfig in the tree that sets the option) also builds. The code applies only when unsigned long is not 64 bits, as before. The RISC-V and RP2350 configurations were built only, not run.

The runs and builds are on 39a1f27. After the rebase on 3d57048, the 4-CPU run gives 500 ms on each CPU for the 500 ms test and 10000 ms on each CPU for the 10000 ms test.

The branch is now rebased on f19dbe6 (no change in the touched files).

With CONFIG_PERF_OVERFLOW_CORRECTION, perf_gettime() compares the counter
with one global "last" value.  On SMP each CPU has its own counter (for
example PMCCNTR on ARMv7-A), and these counters do not hold the same
value.  A read on a CPU with a lower counter than the last read on another
CPU counts as an overflow.  Each such read adds 2^32 cycles to all later
values: on qemu-armv7a:smp a thread that runs 500 ms shows 4795 ms of CPU
time.

Keep "last" and "overflow" for each CPU.  The state belongs to one CPU, so
disabling the local interrupts is sufficient and the spinlock goes away.
This also removes the wd_start() call with a spinlock held.

The watchdog runs on one CPU only, so it cannot read the counter of another
CPU.  Use a watchdog for each CPU that sends a SMP call to the CPU of its
state.  A CPU that runs one thread for a long time then does not miss an
overflow.

With SMP off, the array has one element and the size of the code does not
change: 304 bytes of text and 48 of bss on qemu-armv7a:nsh, before and after.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@casaroli
casaroli requested a review from GUIDINGLI as a code owner November 4, 2026 16:52
@casaroli casaroli added the Area: OS Components OS Components issues label Nov 4, 2026
@casaroli
casaroli requested a review from jerpelea as a code owner November 4, 2026 16:52
@github-actions github-actions Bot added the Size: S The size of the change in this PR is small label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Size: S The size of the change in this PR is small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants