* [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage
@ 2026-10-05 18:57 Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:57 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
This series reduces the executions syzkaller spends on rechecking
spurious timer and scheduler coverage. On a Raspberry Pi 400 with
PREEMPT_RT, triage dropped from 26-34% of all executions to 10%, and
20-minute runs ended with a 26-82% larger corpus at equal execution
counts.
Documentation/dev-tools/kcov.rst says that KCOV aims at stable coverage
that is a function of syscall inputs, and that it does not collect
coverage from non-deterministic parts of the kernel such as the
scheduler. kernel/sched/ is built without KCOV instrumentation, but code
that it calls is instrumented, for example sched_clock() and the CPU
capacity helpers. Deferred hrtimer rearm runs instrumented code in task
context on interrupt return. All of that is recorded for whichever task
is in the kernel at that moment. Identical syscalls then report
different coverage, and the fuzzer re-runs programs to tell the spurious
part from real new coverage.
The code involved also runs on behalf of real syscalls, for example the
hrtimer and timekeeping ones, so building it without KCOV would lose
wanted coverage. Add a nestable KCOV_PAUSED bit and a kcov_pause guard
instead. Take the guard in deferred hrtimer rearm, __schedule(),
try_to_wake_up() and wake_up_new_task(). With KCOV=n the guard compiles
away.
Measurements with syzkaller 29fd395f4ab6, patched against base, every
run from an empty corpus. Each row summarizes three pairs of runs,
except the last, which summarizes five. Ranges show the minimum and
maximum across pairs. Corpus size and coverage (covered PCs) are
compared at the execution count of the kernel that executed fewer
programs. The triage column is the relative change in the share of
executions spent on triage, for example from 13.5% to 6% in the third
row.
executions corpus coverage triage
Pi 400 native, RT, 20 min 37-81k +26..82% +18..45% -71..-63%
Pi 500+ KVM, RT, 20 min 126-189k +11..12% +4..8% -59..-52%
Pi 500+ KVM, RT, 60 min 391-455k +5..10% -1..+1% -58..-53%
Pi 500+ KVM, 60 min 339-470k +0..2% -3..+4% -25..-16%
x86-64 QEMU, RT, 3 h 6-32k +40..81% +15..21% -71..-59%
x86-64 QEMU, 3 h 15-52k +29..74% +8..20% -75..-68%
The gain is largest early in a run and narrows as coverage saturates.
After one hour on the Pi 500+ with PREEMPT_RT the coverage is the same,
the corpus is 5-10% larger and the share of executions spent on triage
is still roughly halved.
The Pi rows are real hardware. "Native" means that the board fuzzes its
own kernel, without a hypervisor. arm64 has no deferred hrtimer rearm,
so the Pi rows measure patches 4-6 only. The x86-64 rows are emulated
with QEMU TCG and -icount shift=0, at HZ=100. x86-64 has not been
measured at real speed.
The effect also shows without a fuzzer. Of 1,000 identical fork() calls
on the Pi 400 with PREEMPT_RT, 218-233 still produce new syzkaller
signal on the base kernel and 45-53 with this series.
The spurious coverage also breaks KCOV_SELFTEST. On x86-64 defconfig
with KCOV_SELFTEST=y, with and without PREEMPT_RT, current mainline
crashed in the selftest in 3/3 boots per configuration and this series
passed in 10/10. The crash is a recursive fault that does not name the
offending PC. The diagnostic patch that reports it is in
https://lore.kernel.org/r/20261002181357.14293-1-kmehltretter@gmail.com
Known limits:
- Without PREEMPT_RT, arm64 showed no clear corpus improvement in these
runs. It also records its softirq stack switch on interrupt exit in
task coverage, which this series does not touch. That is addressed
by the softirq patch mentioned below together with
https://lore.kernel.org/r/20260926191325.79047-1-kmehltretter@gmail.com
- RISC-V showed no clear improvement from this series alone, because
its trap entry code records into task coverage on every interrupt.
A prototype fix looks promising. I will post it separately after more
testing.
- The KCOV selftest still fails on arm64 and RISC-V for these reasons.
Peter Zijlstra's IRQ-exit accounting idea from the v2 discussion is
complementary. I turned it into a separate patch, "softirq: Preserve
interrupt context during IRQ exit", which is at v5 and has been
reviewed:
https://lore.kernel.org/r/20260930191432.62760-1-kmehltretter@gmail.com
It only covers work dispatched directly from IRQ exit. Deferred rearm
outside that path and the task-context scheduler paths remain.
Changes in v4:
- Lead the cover letter with the fuzzing results, now including real
hardware, and retitle the series, which was "kcov: Suppress timer and
scheduler coverage leaks" (Andrew Morton).
- Say "instrumented with KCOV" in the scheduler comments and in the
changelogs of patches 4-6 (Alexander Potapenko).
- Rebase onto current mainline (551c722f4080). No code change. The
affected objects are identical to v3 rebased onto the same commit.
Other v4 testing:
- KCOV=n: in kernel/, the .text of sched/core.o, time/hrtimer.o,
softirq.o, entry/common.o and fork.o is identical to the base.
- The affected objects build with KCOV on ARM32, arm64, RISC-V32,
RISC-V64, s390 and LoongArch64, and on x86-64 with GCC 8.1 and
Clang 21. With PREEMPT_RT: ARM32, arm64, RISC-V64 and LoongArch64.
v1:
https://lore.kernel.org/r/20260807205027.31972-1-kmehltretter@gmail.com
v2:
https://lore.kernel.org/r/20260811154111.64669-1-kmehltretter@gmail.com
v3:
https://lore.kernel.org/r/20260914054632.12877-1-kmehltretter@gmail.com
Karl Mehltretter (6):
kcov: Use unsigned int for kcov_start() mode parameter
kcov: Add a kcov_pause guard
hrtimer: Pause KCOV during deferred rearm
sched/core: Pause KCOV in __schedule()
sched/core: Pause KCOV in try_to_wake_up()
sched/core: Pause KCOV in wake_up_new_task()
include/linux/hrtimer_rearm.h | 18 +++++++-
include/linux/kcov.h | 80 +++++++++++++++++++++++++++++++----
kernel/kcov.c | 7 +--
kernel/sched/core.c | 10 ++++-
4 files changed, 97 insertions(+), 18 deletions(-)
Range-diff:
1: cbbdcfe8483b = 1: d11d5a628248 kcov: Use unsigned int for kcov_start() mode parameter
2: 1ca1b221b7e6 = 2: d94ce0944f95 kcov: Add a kcov_pause guard
3: 238cf03aa786 = 3: b251939de11f hrtimer: Pause KCOV during deferred rearm
4: 55c364e62fb8 ! 4: 8910122b4d6d sched/core: Pause KCOV in __schedule()
@@ Metadata
## Commit message ##
sched/core: Pause KCOV in __schedule()
- kernel/sched/ is built without KCOV instrumentation, but callees such
- as sched_clock(), architecture CPU-capacity helpers and profile_hits() are.
+ kernel/sched/ is built without KCOV instrumentation, but it calls code
+ that is instrumented with KCOV, such as sched_clock(), architecture
+ CPU-capacity helpers and profile_hits().
- During preemption and schedule() calls, instrumented callees can add
+ During preemption and schedule() calls, these callees can add
nondeterministic scheduler coverage to current.
With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
@@ kernel/sched/core.c: static void __sched notrace __schedule(int sched_mode)
struct rq *rq;
int cpu;
-+ /* Instrumented callees would leak coverage into current. */
++ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
+
/* Trace preemptions consistently with task switches */
5: 2b6c6355917c ! 5: 5bb61ae630f7 sched/core: Pause KCOV in try_to_wake_up()
@@ Commit message
sched/core: Pause KCOV in try_to_wake_up()
try_to_wake_up() is built without KCOV instrumentation, but it calls
- instrumented helpers such as kthread_is_per_cpu(), CPU capacity helpers
- and SCHED_HRTICK arming. They can record into current while in_task() is
- true.
+ helpers instrumented with KCOV, such as kthread_is_per_cpu(), CPU
+ capacity helpers and SCHED_HRTICK arming. They can record into current
+ while in_task() is true.
CONFIG_KCOV_SELFTEST exposes this under PREEMPT_RT. The interrupt
selftest fails on x86_64 in kthread_is_per_cpu(): RT runs the timer
@@ kernel/sched/core.c: bool ttwu_state_match(struct task_struct *p, unsigned int s
*/
int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
-+ /* Instrumented callees would leak coverage into current. */
++ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
guard(preempt)();
int cpu, success = 0;
6: 417784f395a8 ! 6: 7c13bc0e78d5 sched/core: Pause KCOV in wake_up_new_task()
@@ Commit message
sched/core: Pause KCOV in wake_up_new_task()
wake_up_new_task() is built without KCOV instrumentation, but CPU
- selection and enqueue call instrumented helpers. During a KCOV-enabled
- fork, they can record scheduler, hrtimer and clockevent coverage into the
- parent.
+ selection and enqueue call helpers instrumented with KCOV. During a
+ KCOV-enabled fork, they can record scheduler, hrtimer and clockevent
+ coverage into the parent.
The paths depend on runqueue and CPU state, so coverage varies between
identical forks. Pause KCOV for the whole function, extending the
@@ kernel/sched/core.c: void wake_up_new_task(struct task_struct *p)
struct rq *rq;
int wake_flags = WF_FORK;
-+ /* Instrumented callees would leak coverage into current. */
++ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
+
raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
base-commit: 551c722f40809618230001baccf219193e22fc5a
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 1/6] kcov: Use unsigned int for kcov_start() mode parameter
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
@ 2026-10-05 18:57 ` Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:57 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
task_struct::kcov_mode usually holds an enum kcov_mode value, but it can
also carry flag bits such as KCOV_IN_CTXSW, so the field is unsigned int.
kcov_remote_softirq_stop() passes the saved raw value through
kcov_start()'s enum kcov_mode parameter before kcov_start() stores it
back into the unsigned int field. The parameter type therefore does not
match the values it receives.
Type the parameter unsigned int, like the field and the saved copy. No
functional change.
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
Reviewed-by: Alexander Potapenko <glider@google.com>
---
kernel/kcov.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 35420f0ac524..79dabbad5a38 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -350,7 +350,7 @@ EXPORT_SYMBOL(__sanitizer_cov_trace_switch);
#endif /* ifdef CONFIG_KCOV_ENABLE_COMPARISONS */
static void kcov_start(struct task_struct *t, struct kcov *kcov,
- unsigned int size, void *area, enum kcov_mode mode,
+ unsigned int size, void *area, unsigned int mode,
int sequence)
{
kcov_debug("t = %px, size = %u, area = %px\n", t, size, area);
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/6] kcov: Add a kcov_pause guard
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
@ 2026-10-05 18:57 ` Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:57 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
Interrupt-return work can run after HARDIRQ_OFFSET is dropped, when
in_task() is true. KCOV then attributes instrumented callees to the
interrupted task.
Add a KCOV_PAUSED bit next to KCOV_IN_CTXSW and mask both in
kcov_mode_enabled(). The coverage callbacks need no new check because
check_kcov_mode()'s exact comparison rejects modes with KCOV_PAUSED set.
The context switch suppression keeps its own bit: kcov_prepare_switch()
runs on the previous task and kcov_finish_switch() on the one switched
in, so its lifetime is not a pause section.
The shared setter no longer sets KCOV_IN_CTXSW on a disabled task,
since its mode already fails the coverage callbacks' exact comparison.
Provide a kcov_pause guard backed by flag helpers shared with context
switch suppression. The helpers access kcov_mode with READ_ONCE() and
WRITE_ONCE() and use compiler barriers to keep instrumented calls inside
the suppressed region. The guard saves the previous pause state and
restores it at scope exit, so sections nest. When KCOV is enabled for
current, remote softirq sections save and restore the complete mode,
preserving the pause state.
With CONFIG_KCOV=y, restoring a previously clear flag still writes
kcov_mode even when task coverage is disabled; nested guards also take
that path in this case.
The helpers are __always_inline. Guard users must be built without KCOV
instrumentation because inlining does not remove the caller's own coverage
callbacks.
Assisted-by: LLM
Suggested-by: Alexander Potapenko <glider@google.com>
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
include/linux/kcov.h | 80 +++++++++++++++++++++++++++++++++++++++-----
kernel/kcov.c | 5 ---
2 files changed, 71 insertions(+), 14 deletions(-)
diff --git a/include/linux/kcov.h b/include/linux/kcov.h
index 895b761b2db1..41833b434c40 100644
--- a/include/linux/kcov.h
+++ b/include/linux/kcov.h
@@ -2,6 +2,8 @@
#ifndef _LINUX_KCOV_H
#define _LINUX_KCOV_H
+#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/sched.h>
#include <uapi/linux/kcov.h>
@@ -23,20 +25,64 @@ enum kcov_mode {
KCOV_MODE_TRACE_CMP = 3,
};
-#define KCOV_IN_CTXSW (1 << 30)
+#define KCOV_IN_CTXSW BIT(30)
+#define KCOV_PAUSED BIT(29)
+
+static __always_inline bool kcov_mode_enabled(unsigned int mode)
+{
+ return (mode & ~(KCOV_IN_CTXSW | KCOV_PAUSED)) != KCOV_MODE_DISABLED;
+}
void kcov_task_init(struct task_struct *t);
void kcov_task_exit(struct task_struct *t);
-#define kcov_prepare_switch(t) \
-do { \
- (t)->kcov_mode |= KCOV_IN_CTXSW; \
-} while (0)
+static __always_inline unsigned int
+__kcov_set_flag(struct task_struct *t, unsigned int flag)
+{
+ unsigned int mode = READ_ONCE(t->kcov_mode);
+ unsigned int prev_flag = mode & flag;
+
+ if (!prev_flag && kcov_mode_enabled(mode)) {
+ WRITE_ONCE(t->kcov_mode, mode | flag);
+ barrier();
+ }
+ return prev_flag;
+}
-#define kcov_finish_switch(t) \
-do { \
- (t)->kcov_mode &= ~KCOV_IN_CTXSW; \
-} while (0)
+static __always_inline void
+__kcov_restore_flag(struct task_struct *t, unsigned int flag,
+ unsigned int prev_flag)
+{
+ if (!prev_flag) {
+ barrier();
+ WRITE_ONCE(t->kcov_mode, READ_ONCE(t->kcov_mode) & ~flag);
+ }
+}
+
+static __always_inline void kcov_prepare_switch(struct task_struct *t)
+{
+ __kcov_set_flag(t, KCOV_IN_CTXSW);
+}
+
+static __always_inline void kcov_finish_switch(struct task_struct *t)
+{
+ __kcov_restore_flag(t, KCOV_IN_CTXSW, 0);
+}
+
+/*
+ * Pause coverage for current. Callers must be built without KCOV
+ * instrumentation.
+ * Pass the returned state to __kcov_resume().
+ */
+static __always_inline unsigned int __kcov_pause(void)
+{
+ return __kcov_set_flag(current, KCOV_PAUSED);
+}
+
+static __always_inline void __kcov_resume(unsigned int paused)
+{
+ __kcov_restore_flag(current, KCOV_PAUSED, paused);
+}
/* See Documentation/dev-tools/kcov.rst for usage details. */
void kcov_remote_start(u64 handle);
@@ -93,6 +139,8 @@ void __sanitizer_cov_trace_switch(kcov_u64 val, void *cases);
static inline void kcov_task_init(struct task_struct *t) {}
static inline void kcov_task_exit(struct task_struct *t) {}
+static inline unsigned int __kcov_pause(void) { return 0; }
+static inline void __kcov_resume(unsigned int paused) {}
static inline void kcov_prepare_switch(struct task_struct *t) {}
static inline void kcov_finish_switch(struct task_struct *t) {}
static inline void kcov_remote_start(u64 handle) {}
@@ -107,4 +155,18 @@ static inline void kcov_remote_start_usb_softirq(u64 id) {}
static inline void kcov_remote_stop_softirq(void) {}
#endif /* CONFIG_KCOV */
+
+/*
+ * Scope-based KCOV pause:
+ *
+ * guard(kcov_pause)();
+ *
+ * pauses coverage for current until the end of the scope. Callers must be
+ * built without KCOV instrumentation.
+ */
+DEFINE_LOCK_GUARD_0(kcov_pause,
+ _T->paused = __kcov_pause(),
+ __kcov_resume(_T->paused),
+ unsigned int paused)
+
#endif /* _LINUX_KCOV_H */
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 79dabbad5a38..faccbd3bd2b8 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -830,11 +830,6 @@ static const struct file_operations kcov_fops = {
* collecting coverage and copies all collected coverage into the kcov area.
*/
-static inline bool kcov_mode_enabled(unsigned int mode)
-{
- return (mode & ~KCOV_IN_CTXSW) != KCOV_MODE_DISABLED;
-}
-
static void kcov_remote_softirq_start(struct task_struct *t)
__must_hold(&kcov_percpu_data.lock)
{
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 3/6] hrtimer: Pause KCOV during deferred rearm
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
@ 2026-10-05 18:58 ` Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:58 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
Deferred hrtimer rearm can run after HARDIRQ_OFFSET is dropped. in_task()
is then true, so KCOV attributes the instrumented timer-reprogramming
subtree to current.
With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
selftest fails under QEMU, detecting spurious coverage in
__hrtimer_rearm_deferred(). The same happens on s390, RISC-V and LoongArch,
which also enable HRTIMER_REARM_DEFERRED.
Excluding the involved files instead would cost their coverage on real
task-context paths, e.g. the hrtimer and timekeeping syscalls.
Take the kcov_pause guard in an __always_inline wrapper around
__hrtimer_rearm_deferred(). Use it at all call sites, including
hrtick_schedule_exit(). Callers that may run with KCOV enabled for current
are built without KCOV instrumentation or marked noinstr. HAVE_NOINSTR_HACK
covers pre-GCC-12 x86. The other affected architectures restrict KCOV to
GCC 12 or Clang through ARCH_WANTS_NO_INSTR. This avoids relying on
__no_sanitize_coverage, which is empty before GCC 12. Tested with GCC 8.1
and 15 on x86_64.
A later patch also pauses __schedule(); keeping hrtick_schedule_exit()
guarded here makes the deferred-rearm fix independent of that scheduler
change.
Fixes: 15dd3a948855 ("hrtimer: Push reprogramming timers into the interrupt return path")
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
This patch depends on patch 2, "kcov: Add a kcov_pause guard".
include/linux/hrtimer_rearm.h | 18 ++++++++++++++++--
kernel/sched/core.c | 2 +-
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/include/linux/hrtimer_rearm.h b/include/linux/hrtimer_rearm.h
index 17a81826bd9a..cd935bab3da3 100644
--- a/include/linux/hrtimer_rearm.h
+++ b/include/linux/hrtimer_rearm.h
@@ -6,12 +6,25 @@
#ifdef CONFIG_HRTIMER_REARM_DEFERRED
#include <linux/irqflags.h>
+#include <linux/kcov.h>
#include <linux/lockdep.h>
#include <linux/preempt.h>
#include <linux/thread_info.h>
void __hrtimer_rearm_deferred(void);
+/*
+ * KCOV: Pause outside __hrtimer_rearm_deferred() to suppress entry coverage.
+ * Callers with KCOV enabled for current must be built without KCOV
+ * instrumentation.
+ */
+static __always_inline void hrtimer_rearm_deferred_kcov_paused(void)
+{
+ guard(kcov_pause)();
+
+ __hrtimer_rearm_deferred();
+}
+
/*
* This is purely CPU local, so check the TIF bit first to avoid the overhead of
* the atomic test_and_clear_bit() operation for the common case where the bit
@@ -43,7 +56,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif
*/
if (unlikely((*tif_work & TIF_REARM_MASK) == _TIF_HRTIMER_REARM)) {
clear_thread_flag(TIF_HRTIMER_REARM);
- __hrtimer_rearm_deferred();
+ hrtimer_rearm_deferred_kcov_paused();
/* Don't go into the loop if HRTIMER_REARM was the only flag */
*tif_work &= ~TIF_HRTIMER_REARM;
return !*tif_work;
@@ -55,7 +68,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif
static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work)
{
if (hrtimer_test_and_clear_rearm_deferred_tif(tif_work))
- __hrtimer_rearm_deferred();
+ hrtimer_rearm_deferred_kcov_paused();
}
/*
@@ -78,6 +91,7 @@ static __always_inline bool hrtimer_test_and_clear_rearm_deferred(void)
#else /* CONFIG_HRTIMER_REARM_DEFERRED */
static __always_inline void __hrtimer_rearm_deferred(void) { }
+static __always_inline void hrtimer_rearm_deferred_kcov_paused(void) { }
static __always_inline void hrtimer_rearm_deferred(void) { }
static __always_inline void hrtimer_rearm_deferred_tif(unsigned long tif_work) { }
static __always_inline bool
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 1fe40de6ebe3..87cff2884980 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1020,7 +1020,7 @@ static inline void hrtick_schedule_exit(struct rq *rq)
}
if (rq->hrtick_sched & HRTICK_SCHED_REARM_HRTIMER)
- __hrtimer_rearm_deferred();
+ hrtimer_rearm_deferred_kcov_paused();
rq->hrtick_sched = HRTICK_SCHED_NONE;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 4/6] sched/core: Pause KCOV in __schedule()
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
` (2 preceding siblings ...)
2026-10-05 18:58 ` [PATCH v4 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
@ 2026-10-05 18:58 ` Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:58 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
kernel/sched/ is built without KCOV instrumentation, but it calls code
that is instrumented with KCOV, such as sched_clock(), architecture
CPU-capacity helpers and profile_hits().
During preemption and schedule() calls, these callees can add
nondeterministic scheduler coverage to current.
With CONFIG_KCOV_SELFTEST added to x86_64 defconfig, the interrupt
selftest fails under QEMU, detecting spurious coverage in
arch_scale_cpu_capacity().
Annotating each callee would spread exclusions across architectures.
Pause across __schedule() instead, extending the scheduler exclusion to
its callees.
KCOV_PAUSED remains set while a task is switched out. The guard in its
resumed __schedule() frame restores the prior state.
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
kernel/sched/core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 87cff2884980..419dd98645bd 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7123,6 +7123,9 @@ static void __sched notrace __schedule(int sched_mode)
struct rq *rq;
int cpu;
+ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
+
/* Trace preemptions consistently with task switches */
trace_sched_entry_tp(sched_mode == SM_PREEMPT);
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 5/6] sched/core: Pause KCOV in try_to_wake_up()
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
` (3 preceding siblings ...)
2026-10-05 18:58 ` [PATCH v4 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
@ 2026-10-05 18:58 ` Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:58 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
try_to_wake_up() is built without KCOV instrumentation, but it calls
helpers instrumented with KCOV, such as kthread_is_per_cpu(), CPU
capacity helpers and SCHED_HRTICK arming. They can record into current
while in_task() is true.
CONFIG_KCOV_SELFTEST exposes this under PREEMPT_RT. The interrupt
selftest fails on x86_64 in kthread_is_per_cpu(): RT runs the timer
softirq in a thread, so the wakeup runs in task context during the
selftest's spin. The same helpers leak into non-RT syscall wakeups such
as a pipe write waking a reader.
Pause the wakeup body with the kcov_pause guard. Take it before the
preemption guard so preemption is re-enabled while KCOV remains paused.
Wrapping only select_task_rq() would miss SCHED_HRTICK arming during
enqueue.
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
kernel/sched/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 419dd98645bd..f19221282278 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4273,6 +4273,8 @@ bool ttwu_state_match(struct task_struct *p, unsigned int state, int *success)
*/
int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
+ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
guard(preempt)();
int cpu, success = 0;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 6/6] sched/core: Pause KCOV in wake_up_new_task()
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
` (4 preceding siblings ...)
2026-10-05 18:58 ` [PATCH v4 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
@ 2026-10-05 18:58 ` Karl Mehltretter
5 siblings, 0 replies; 7+ messages in thread
From: Karl Mehltretter @ 2026-10-05 18:58 UTC (permalink / raw)
To: Andrew Morton
Cc: Karl Mehltretter, Andrey Konovalov, Alexander Potapenko,
Dmitry Vyukov, Marco Elver, Bradley Morgan, Anna-Maria Behnsen,
Frederic Weisbecker, Thomas Gleixner, Ingo Molnar,
Peter Zijlstra, Juri Lelli, Vincent Guittot, Dietmar Eggemann,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Sebastian Andrzej Siewior, Clark Williams,
linux-rt-devel, kasan-dev, linux-kernel
wake_up_new_task() is built without KCOV instrumentation, but CPU
selection and enqueue call helpers instrumented with KCOV. During a
KCOV-enabled fork, they can record scheduler, hrtimer and clockevent
coverage into the parent.
The paths depend on runqueue and CPU state, so coverage varies between
identical forks. Pause KCOV for the whole function, extending the
scheduler exclusion to new-task wakeups.
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
kernel/sched/core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index f19221282278..888460c97952 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4969,6 +4969,9 @@ void wake_up_new_task(struct task_struct *p)
struct rq *rq;
int wake_flags = WF_FORK;
+ /* Callees instrumented with KCOV would leak coverage into current. */
+ guard(kcov_pause)();
+
raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
WRITE_ONCE(p->__state, TASK_RUNNING);
/*
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-05 18:58 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-05 18:57 [PATCH v4 0/6] kcov: Keep timer and scheduler noise out of task coverage Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 1/6] kcov: Use unsigned int for kcov_start() mode parameter Karl Mehltretter
2026-10-05 18:57 ` [PATCH v4 2/6] kcov: Add a kcov_pause guard Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 3/6] hrtimer: Pause KCOV during deferred rearm Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 4/6] sched/core: Pause KCOV in __schedule() Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 5/6] sched/core: Pause KCOV in try_to_wake_up() Karl Mehltretter
2026-10-05 18:58 ` [PATCH v4 6/6] sched/core: Pause KCOV in wake_up_new_task() Karl Mehltretter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®