[PATCH 1/5] kcov: add kcov_pause()/kcov_resume() helpers

From: Karl Mehltretter

Date: Fri Aug 07 2026 - 16:51:13 EST


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 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.

Sections nest by passing the state returned by kcov_pause() to
kcov_resume(). Both operate on current. When task KCOV is active, remote
softirq sections save and restore the complete mode, preserving the
pause state.

The helpers are __always_inline, and the caller must be uninstrumented:
inlining does not remove the caller's own coverage callbacks.

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Claude:claude-fable-5
Signed-off-by: Karl Mehltretter <kmehltretter@xxxxxxxxx>
---
include/linux/kcov.h | 25 ++++++++++++++++++++++++-
kernel/kcov.c | 2 +-
2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/include/linux/kcov.h b/include/linux/kcov.h
index 895b761b2db1..5a0a1a9bb7ef 100644
--- a/include/linux/kcov.h
+++ b/include/linux/kcov.h
@@ -2,6 +2,7 @@
#ifndef _LINUX_KCOV_H
#define _LINUX_KCOV_H

+#include <linux/bits.h>
#include <linux/sched.h>
#include <uapi/linux/kcov.h>

@@ -23,7 +24,8 @@ 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)

void kcov_task_init(struct task_struct *t);
void kcov_task_exit(struct task_struct *t);
@@ -38,6 +40,25 @@ do { \
(t)->kcov_mode &= ~KCOV_IN_CTXSW; \
} while (0)

+/*
+ * Pause coverage for current. Pass the returned state to kcov_resume().
+ * Callers must be uninstrumented.
+ */
+static __always_inline unsigned int kcov_pause(struct task_struct *t)
+{
+ unsigned int paused;
+
+ paused = t->kcov_mode & KCOV_PAUSED;
+ t->kcov_mode |= KCOV_PAUSED;
+ return paused;
+}
+
+static __always_inline void kcov_resume(struct task_struct *t, unsigned int paused)
+{
+ if (!paused)
+ t->kcov_mode &= ~KCOV_PAUSED;
+}
+
/* See Documentation/dev-tools/kcov.rst for usage details. */
void kcov_remote_start(u64 handle);
void kcov_remote_stop(void);
@@ -93,6 +114,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(struct task_struct *t) { return 0; }
+static inline void kcov_resume(struct task_struct *t, 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) {}
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 1df373fb562b..83d53e383822 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -830,7 +830,7 @@ static const struct file_operations kcov_fops = {

static inline bool kcov_mode_enabled(unsigned int mode)
{
- return (mode & ~KCOV_IN_CTXSW) != KCOV_MODE_DISABLED;
+ return (mode & ~(KCOV_IN_CTXSW | KCOV_PAUSED)) != KCOV_MODE_DISABLED;
}

static void kcov_remote_softirq_start(struct task_struct *t)
--
2.53.0