"Smcntrpmf": Privilege-Mode Filtering for cycle & instret

Part II Linux boot: optional Vol. II (Privileged) pp. 88–89 · ~4 min read

  • smcntrpmf

Smcntrpmf adds one inhibit bit per privilege mode to cycle and instret, so a counter can be made to tick only in the mode you are actually measuring.

Why this matters

Filtering is also what keeps a user-readable cycle counter safe to expose: with MINH set, privileged execution becomes invisible rather than merely uninteresting, and the fast unprivileged read stays allowable.

By default cycle and instret tick straight through page faults and interrupts — so a user program self-profiling with two counter reads sees kernel noise, and worse, measures privileged execution (a timing side channel). Smcntrpmf adds per-privilege inhibit bits.

mcyclecfg (0x321) / minstretcfg (0x322)

OF=063MINH62SINH61UINH60VSINH59VUINH58WPRI570
WPRI/RO WLRL WARL RW
Click a field for semantics, reset state, and the minimal-implementation note.

(Address trivia the spec admits: 0x320 would’ve been natural, but mcountinhibit got there first. RV32 gets mcyclecfgh/minstretcfgh; Smcdeleg/Ssccfg can expose these to S-mode.)

The counting rules

What counts where
Rule
cycleCounts CPU cycles only in non-inhibited modes; multi-cycle trap/return transitions attribute the switchover to ANY one transition cycle, implementation-defined and not necessarily consistent
instret — ordinary instructionsIncrement iff they retire in a non-inhibited mode
instret — exception-raising instructionsNever retire, never count (the u07 rule, unchanged)
instret — xRETCounts iff its ORIGINATING mode is non-inhibited
Software emulationAn emulator running in an inhibited mode must emulate the increment for the emulated instruction
Dotted-underlined cells have explanations — click one.

One faulting load, counted once

Take the configuration a profiler actually wants — count U-mode only, so MINH = SINH = 1 and UINH = 0 — and trace a single user load that takes a page fault:

  1. The load raises the fault, so it never retires: instret does not move (the u07 rule, unchanged).
  2. The trap enters S-mode, which is inhibited, so nothing the handler retires counts.
  3. The handler’s SRET originates in S-mode — inhibited — so the return does not count either.
  4. The load re-executes in U-mode and retires: instret += 1.

One user instruction, one increment. The xRET rule is what makes that arithmetic exact: had the return counted in its destination mode instead, every serviced fault would have inflated the user’s instruction count by one.

If you remember only three things
  • Inhibition is per mode, per counter: mcyclecfg and minstretcfg each carry MINH/SINH/UINH (plus VSINH/VUINH with H), and all-zero is the legacy count-everywhere behavior.
  • Instructions count where they retire — except xRET, which counts where it originated. That one exception is what keeps mode-filtered totals exact across traps.
  • Cycle attribution at trap boundaries is implementation-defined, so a filtered cycle count is statistically sound but never cycle-exact — nothing in the ISA fixes trap latency.

Hardware Designer Notes

Cost: two config registers and two AND gates on existing increment wires; the only subtlety is plumbing the xRET originating mode to the commit-stage counter logic — which your trap unit already knows. Worthwhile the moment your users run perf.

Minimal Linux-boot hart MUST

  • Gate the cycle increment with !xINH[current mode] and the instret increment with !xINH[retire mode] — except xRET, which uses its pre-return (originating) mode
  • Read-only-zero the INH bits of unimplemented modes and the OF position

MAY simplify / trap-and-emulate

  • Skip the extension (both cfg CSRs absent) — legacy counting is still conformant
  • Attribute transition cycles however your trap microsequence falls — no consistency required

Check yourself — counter filtering

1.minstretcfg inhibits every mode except U. A user load page-faults, the kernel fixes it, the load re-executes. How many instret increments?

2.During a trap's multi-cycle mode transition, which mode do the transition cycles count toward?

3.Why do mcyclecfg/minstretcfg mirror the Sscofpmf event-register format with bit 63 read-only zero?

3 questions