From 00ea2b26510c225977609620800c1989c4fefd8a Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 6 Jun 2026 09:25:20 +0300 Subject: test(priority): add end-to-end coverage for getpriority/setpriority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getpriority/setpriority (FamilyProcess, KindNull enter, UNCLASSIFIED ret) were untested end-to-end: no ioworkload scenario invoked them. Add a safe, unprivileged, non-disruptive priority-basic scenario that reads the current nice value via getpriority(PRIO_PROCESS, 0) and re-applies that exact value via setpriority(PRIO_PROCESS, 0, currentNice) — a byte-for-byte no-op, mirroring the existing schedRoundtripAffinity round-trip. Note getpriority returns 20-nice, so the value is converted back before re-applying. Register the scenario in scenarios.go and add TestPriorityBasic, which asserts enter_getpriority and enter_setpriority appear as null_event enters attributed to the ioworkload process. Enter-tracepoint presence is the right check given KindNull/UNCLASSIFIED (no fd/path/bytes to assert). Coverage only — classification verified correct in audits 6u and pz. Co-Authored-By: Claude Opus 4.8 --- cmd/ioworkload/scenario_priority.go | 57 +++++++++++++++++++++++++++++++++++++ cmd/ioworkload/scenarios.go | 1 + 2 files changed, 58 insertions(+) create mode 100644 cmd/ioworkload/scenario_priority.go (limited to 'cmd') diff --git a/cmd/ioworkload/scenario_priority.go b/cmd/ioworkload/scenario_priority.go new file mode 100644 index 0000000..1383376 --- /dev/null +++ b/cmd/ioworkload/scenario_priority.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +// prioProcess is PRIO_PROCESS (value 0): the "which" selector telling +// get/setpriority that "who" identifies a process (rather than a process group +// or user). Paired with who == 0 it means "the calling process", so the calls +// are entirely self-targeted and need no privilege. +const prioProcess = 0 + +// niceOffset is the bias the kernel applies to getpriority's return value. The +// raw getpriority(2) syscall never returns a negative number (that range is +// reserved for -errno), so instead of returning the nice value directly +// (-20..19) it returns 20 - nice (i.e. 1..40). To recover the actual nice value +// we must subtract the return value from this offset. setpriority(2), by +// contrast, takes the real nice value (-20..19) directly, so we convert before +// re-applying it. +const niceOffset = 20 + +// priorityBasic exercises the SAFE, NON-DISRUPTIVE members of the priority pair +// (getpriority/setpriority), entirely self-targeted (PRIO_PROCESS, who 0 == the +// calling process), so it changes no other process and leaves this process's +// nice value byte-for-byte unchanged: +// +// - getpriority(PRIO_PROCESS, 0) reads the current nice value (a pure read). +// - setpriority(PRIO_PROCESS, 0, currentNice) re-applies the EXACT nice value +// just read back, so the priority is left unchanged. +// +// Re-applying the current nice value needs no privilege: lowering the priority +// (a larger nice) is always allowed, and writing back the unchanged value is a +// no-op the kernel permits regardless of RLIMIT_NICE. This mirrors the safe +// sched_setaffinity round-trip in schedRoundtripAffinity (scenario_sched.go), +// where we likewise read a value and write the identical value straight back. +// +// Both syscalls classify as FamilyProcess with a KindNull enter (PRIO_PROCESS is +// an opcode, not an fd) and an UNCLASSIFIED return (getpriority returns a nice +// value, NOT a byte count), so the scenario exists purely to fire the enter +// tracepoints end-to-end. +func priorityBasic() error { + // getpriority returns 20 - nice (see niceOffset); recover the real nice. + prio, err := unix.Getpriority(prioProcess, 0) + if err != nil { + return fmt.Errorf("getpriority(PRIO_PROCESS, 0): %w", err) + } + currentNice := niceOffset - prio + + // Re-apply the IDENTICAL nice value we just read: a no-op change that needs + // no privilege. + if err := unix.Setpriority(prioProcess, 0, currentNice); err != nil { + return fmt.Errorf("setpriority(PRIO_PROCESS, 0, %d) (restore same nice): %w", currentNice, err) + } + return nil +} diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index 131e5f0..7e03b3d 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -148,6 +148,7 @@ var scenarios = map[string]func() error{ "signals-basic": signalsBasic, "misc-basic": miscBasic, "sched-basic": schedBasic, + "priority-basic": priorityBasic, } func makeTempDir(prefix string) (string, func(), error) { -- cgit v1.2.3