mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC
@ 2026-10-28 11:59 Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
                   ` (19 more replies)
  0 siblings, 20 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Adrian Hunter, Alexander Potapenko, Alexander Shishkin,
	Andrew Morton, Andrey Konovalov, Andy Lutomirski, Anton Ivanov,
	Arnaldo Carvalho de Melo, Arnd Bergmann, Ben Segall,
	Bill Wendling, Boqun Feng, Borislav Petkov, Chuck Wolber,
	Dave Hansen, Dietmar Eggemann, Dmitry Vyukov, Frantisek Hrbata,
	Frederic Weisbecker, H. Peter Anvin, Ian Rogers, Ingo Molnar,
	James Clark, Jiri Olsa, Johannes Berg, Jonathan Corbet,
	Josh Poimboeuf, Julian Braha, Juri Lelli, Justin Stitt,
	kasan-dev, K Prateek Nayak, linux-doc, linux-kbuild, llvm,
	Marco Elver, Mark Rutland, Masami Hiramatsu, Mathieu Desnoyers,
	Mauro Carvalho Chehab, Mel Gorman, Miguel Ojeda, Namhyung Kim,
	Nathan Chancellor, Nick Desaulniers, Nicolas Schier,
	Paolo Bonzini, Paul E. McKenney, Peter Oberparleiter,
	Peter Zijlstra, Randy Dunlap, Richard Weinberger,
	Sean Christopherson, Shuah Khan, Steven Rostedt, Thomas Gleixner,
	Valentin Schneider, Vincent Guittot, Waiman Long, Will Deacon,
	x86

The previous postings of Clang's source-based code coverage [1][2] drew
objections [3][4]: rather than a third coverage implementation with its
own interface next to gcov and KCOV, reviewers asked for a unified
coverage interface, and for coverage instrumentation to honour noinstr.
This series first restructures the existing coverage support into one
framework, then adds source-based coverage and MC/DC as part of it.

Architecture
============

Today, each coverage implementation brings its own plumbing. gcov has its
compiler flags in the top-level Makefile, the GCOV_PROFILE annotations,
its options under "General architecture-dependent options" and
/sys/kernel/debug/gcov/. KCOV has scripts/Makefile.kcov, KCOV_INSTRUMENT,
its options under "Kernel hacking" and /sys/kernel/debug/kcov. Anything
that depends on the kernel being built with coverage instrumentation
spells it GCOV_KERNEL, and the earlier source-based coverage postings
added a third copy of all of it.

This series turns that into one framework, kernel/coverage/:

  kernel/coverage/
    core.c, coverage.h   debugfs root, backend registration, common reset
    gcov/                gcov backend (moved from kernel/gcov/)
    llvm/                Clang source-based coverage backend (new)
    kcov.c               KCOV (moved from kernel/kcov.c)

A code coverage measurement implementation, or backend, only provides
what is specific to its toolchain:

- its compiler flags, as CFLAGS_COVERAGE in scripts/Makefile.<backend>,
  including the ones for MC/DC;
- a struct coverage_backend registered with the core: its name, its
  enabled features and how to reset its data;
- its data, reachable under /sys/kernel/debug/coverage/;
- its capabilities: the architectures it supports, and whether it can
  instrument modules.

Everything else is shared by all backends:

- Configuration: one "Code coverage" menu with a choice of backend (none,
  gcov or Clang source-based coverage, as each data format is defined by
  its toolchain), CONFIG_COVERAGE_KERNEL for anything that depends on
  coverage instrumentation (LTO, KCFI, ...), CONFIG_COVERAGE_PROFILE_ALL,
  the per-subsystem CONFIG_COVERAGE_PROFILE_<subsystem> options and
  CONFIG_COVERAGE_MCDC. Existing configurations are migrated.
- Build control: COVERAGE_PROFILE[_obj.o] decides what gets instrumented,
  whichever backend is selected. Kbuild applies the backend's flags,
  leaves modules alone for backends that can't export their data, and
  sets up objtool accordingly.
- Runtime interface: /sys/kernel/debug/coverage/, with "backend" naming
  the backend and its features, "reset" resetting its data, and the
  backend's data files. The existing gcov and kcov entries stay where
  they are, for lcov and syzkaller, and are linked from there.
- noinstr: one mechanism for all backends, enforced by objtool, see
  below.

With that in place, adding source-based coverage (patch 14) only takes
the backend specific parts.

KCOV is a per-task tracing interface for fuzzers with its own UAPI, so it
is not a backend: it moves into kernel/coverage/, the same menu, the same
debugfs root and the same documentation, and its noinstr handling is
unified, but it keeps its UAPI and KCOV_INSTRUMENT, whose exclusions serve
the fuzzing signal and the KCOV runtime rather than marking code that
can't be instrumented.

noinstr
=======

gcov and source-based coverage instrument the code before inlining, so
__always_inline helpers carried their counters into noinstr code: in an
x86 build, 2833 relocations in .noinstr.text and .cpuidle.text referenced
coverage counters. objtool didn't notice, as these are plain memory
updates rather than calls. KCOV instruments after inlining and was not
affected.

- Patch 4 stops instrumenting __always_inline functions in coverage
  builds, which costs about 1-2% of the coverage data.
- Patches 5-10 make the empty stubs that noinstr code calls
  __always_inline, and switch the GHCB accessors to the arch_ bitops.
- Patch 11 makes objtool report coverage counter updates in noinstr code.
- Patches 12 and 13 drop the coverage exclusions that only existed for
  noinstr: arch/x86/coco/sev/noinstr.o, added by 9efb74f84ba8
  ("x86/sev: Disable GCOV on noinstr object"), and the KCOV exclusion of
  the x86 entry code. The generic entry code stays excluded from KCOV for
  a different reason, which patch 13 documents.

Source-based coverage and MC/DC
===============================

Patches 14-20 add Clang's source-based code coverage for code built into
the kernel image (raw profile formats of Clang 19-22 and 23+), enable it
on x86_64, add MC/DC measurement for it and for gcov with GCC 14+ [5],
including the gcov merge fix MC/DC needs, and document the result.

Testing
=======

- x86_64 with CONFIG_DEBUG_ENTRY, gcov with GCC 16 and Clang 22, and
  source-based coverage with Clang 19, 22 and 24, including MC/DC, on
  configurations toggling lockdep, context tracking, SEV and rseq:
  objtool is clean and no coverage counter is left in noinstr code.
- KCOV with GCC 16 and Clang 22: the noinstr functions of the x86 entry
  code have no KCOV calls, and system call traces don't pick up
  interrupt code.
- Booted in a VM: gcov and source-based coverage data, reset, MC/DC
  reports from llvm-cov and gcov --conditions, gcov data persistence
  across module reloads, and KCOV.
- Every patch builds, kernels without coverage produce identical
  compiler invocations, and arm64, i386 and UML build with coverage.

[1] https://lore.kernel.org/all/20240905043245.1389509-1-wentaoz5@illinois.edu/
[2] https://lore.kernel.org/all/20251014232639.673260-1-sashal@kernel.org/
[3] https://lore.kernel.org/all/20251015073701.GZ3419281@noisy.programming.kicks-ass.net/
[4] https://lore.kernel.org/all/20251218092439.GL3707891@noisy.programming.kicks-ass.net/
[5] https://lore.kernel.org/all/20260314141749.3382679-1-sashal@kernel.org/

Sasha Levin (20):
  coverage: move gcov and kcov under kernel/coverage/
  kbuild: unify static coverage instrumentation as COVERAGE_PROFILE
  coverage: add a common debugfs interface
  compiler_types.h: Don't instrument __always_inline functions for
    coverage
  context_tracking: Always inline the empty stubs called from noinstr
    code
  lockdep: Always inline the empty stubs called from noinstr code
  perf: Always inline the empty perf_lopwr_cb() stub
  rseq: Always inline the empty rseq_note_user_irq_entry() stub
  x86: Always inline empty stubs called from noinstr code
  x86/sev: Use non-instrumented bitops in the GHCB accessors
  objtool: Detect code coverage instrumentation in noinstr code
  x86/sev: Stop excluding noinstr.o from code coverage
  x86/entry: Stop excluding the entry code from KCOV
  coverage: add Clang's source-based code coverage
  x86: Enable Clang's source-based code coverage
  coverage: add MC/DC measurement
  gcov: fix gcov_info_add() merge semantics for IOR counters
  kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection
  gcov: add MC/DC condition coverage support
  Documentation: dev-tools: document the common code coverage interface

 Documentation/dev-tools/coverage.rst          | 158 ++++++++++++
 Documentation/dev-tools/gcov.rst              |  46 +++-
 Documentation/dev-tools/index.rst             |   1 +
 Documentation/dev-tools/kcov.rst              |   3 +
 .../translations/zh_CN/dev-tools/gcov.rst     |  16 +-
 .../translations/zh_TW/dev-tools/gcov.rst     |  16 +-
 MAINTAINERS                                   |  20 +-
 Makefile                                      |  23 +-
 arch/Kconfig                                  |   8 +-
 arch/alpha/boot/compressed/Makefile           |   2 +-
 arch/parisc/kernel/vdso32/Makefile            |   2 +-
 arch/parisc/kernel/vdso64/Makefile            |   2 +-
 arch/powerpc/kernel/Makefile                  |   6 +-
 arch/powerpc/kernel/trace/Makefile            |   4 +-
 arch/powerpc/kexec/Makefile                   |   2 +-
 arch/powerpc/xmon/Makefile                    |   2 +-
 arch/s390/boot/Makefile                       |   2 +-
 arch/s390/kernel/Makefile                     |   2 +-
 arch/sh/mm/Makefile                           |   2 +-
 arch/x86/Kconfig                              |   2 +
 arch/x86/coco/sev/Makefile                    |   2 -
 arch/x86/entry/Makefile                       |   1 -
 arch/x86/include/asm/processor.h              |   2 +-
 arch/x86/include/asm/sev.h                    |   6 +-
 arch/x86/include/asm/svm.h                    |   8 +-
 arch/x86/kernel/vmlinux.lds.S                 |   2 +
 arch/x86/platform/efi/Makefile                |   2 +-
 arch/x86/um/vdso/Makefile                     |   4 +-
 arch/xtensa/boot/lib/Makefile                 |   2 +-
 drivers/gpu/drm/amd/amdgpu/Kconfig            |  12 +-
 drivers/gpu/drm/amd/amdgpu/Makefile           |   4 +-
 .../gpu/drm/amd/display/amdgpu_dm/Makefile    |   2 +-
 drivers/gpu/drm/i915/Makefile                 |   2 +-
 drivers/gpu/drm/xe/Makefile                   |   2 +-
 drivers/s390/char/Makefile                    |   2 +-
 include/asm-generic/vmlinux.lds.h             |  36 +++
 include/linux/compiler_types.h                |  13 +
 include/linux/context_tracking.h              |   8 +-
 include/linux/irqflags.h                      |   6 +-
 include/linux/lockdep.h                       |   4 +-
 include/linux/perf_event.h                    |   2 +-
 include/linux/rseq_entry.h                    |   2 +-
 include/linux/sched.h                         |   2 +-
 init/Kconfig                                  |   9 +-
 io_uring/Makefile                             |   4 +-
 kernel/Makefile                               |  15 +-
 kernel/coverage/Kconfig                       | 226 ++++++++++++++++++
 kernel/coverage/Makefile                      |  18 ++
 kernel/coverage/core.c                        |  71 ++++++
 kernel/coverage/coverage.h                    |  22 ++
 kernel/{ => coverage}/gcov/Makefile           |   0
 kernel/{ => coverage}/gcov/base.c             |   0
 kernel/{ => coverage}/gcov/clang.c            |   2 +-
 kernel/{ => coverage}/gcov/fs.c               |  22 +-
 kernel/{ => coverage}/gcov/gcc_4_7.c          |  25 +-
 kernel/{ => coverage}/gcov/gcc_base.c         |   0
 kernel/{ => coverage}/gcov/gcov.h             |   0
 kernel/{ => coverage}/kcov.c                  |   3 +
 kernel/coverage/llvm/Makefile                 |   4 +
 kernel/coverage/llvm/fs.c                     | 202 ++++++++++++++++
 kernel/coverage/llvm/llvm-cov.h               | 139 +++++++++++
 kernel/entry/Makefile                         |   3 +
 kernel/gcov/Kconfig                           |  55 -----
 kernel/trace/Kconfig                          |   9 +-
 kernel/trace/Makefile                         |   4 +-
 lib/Kconfig.debug                             |  61 +----
 lib/Makefile                                  |   2 +-
 lib/crypto/Makefile                           |   2 +-
 net/bridge/Makefile                           |   8 +-
 net/bridge/netfilter/Makefile                 |   4 +-
 net/ipv4/Makefile                             |   4 +-
 net/ipv4/netfilter/Makefile                   |   4 +-
 net/ipv6/Makefile                             |   4 +-
 net/ipv6/netfilter/Makefile                   |   4 +-
 net/netfilter/Kconfig                         |   9 +-
 net/netfilter/Makefile                        |   4 +-
 net/netfilter/ipset/Kconfig                   |   9 +-
 net/netfilter/ipset/Makefile                  |   4 +-
 net/netfilter/ipvs/Kconfig                    |   9 +-
 net/netfilter/ipvs/Makefile                   |   4 +-
 net/rds/Kconfig                               |   9 +-
 net/rds/Makefile                              |   4 +-
 scripts/Kconfig.toolchain                     |   3 +
 scripts/Makefile.gcov                         |  25 ++
 scripts/Makefile.lib                          |  19 +-
 scripts/Makefile.llvm-cov                     |  11 +
 scripts/Makefile.modfinal                     |   2 +-
 scripts/sbom/sbom/environment.py              |   2 +-
 tools/objtool/check.c                         |  53 ++++
 tools/testing/selftests/net/rds/config.sh     |   9 +-
 tools/testing/selftests/net/rds/rds_run.sh    |   8 +-
 tools/unittests/test_cmatch.py                |   2 +-
 92 files changed, 1245 insertions(+), 312 deletions(-)
 create mode 100644 Documentation/dev-tools/coverage.rst
 create mode 100644 kernel/coverage/Kconfig
 create mode 100644 kernel/coverage/Makefile
 create mode 100644 kernel/coverage/core.c
 create mode 100644 kernel/coverage/coverage.h
 rename kernel/{ => coverage}/gcov/Makefile (100%)
 rename kernel/{ => coverage}/gcov/base.c (100%)
 rename kernel/{ => coverage}/gcov/clang.c (99%)
 rename kernel/{ => coverage}/gcov/fs.c (97%)
 rename kernel/{ => coverage}/gcov/gcc_4_7.c (94%)
 rename kernel/{ => coverage}/gcov/gcc_base.c (100%)
 rename kernel/{ => coverage}/gcov/gcov.h (100%)
 rename kernel/{ => coverage}/kcov.c (99%)
 create mode 100644 kernel/coverage/llvm/Makefile
 create mode 100644 kernel/coverage/llvm/fs.c
 create mode 100644 kernel/coverage/llvm/llvm-cov.h
 delete mode 100644 kernel/gcov/Kconfig
 create mode 100644 scripts/Makefile.gcov
 create mode 100644 scripts/Makefile.llvm-cov


base-commit: 83f32c773255720a661aa45d83e36278135f23f5
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE Sasha Levin
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexander Potapenko, Andrew Morton, Andrey Konovalov, Ben Segall,
	Bill Wendling, Chuck Wolber, Dietmar Eggemann, Dmitry Vyukov,
	Ingo Molnar, Juri Lelli, Justin Stitt, kasan-dev,
	K Prateek Nayak, linux-doc, llvm, Marco Elver, Masami Hiramatsu,
	Mauro Carvalho Chehab, Mel Gorman, Nathan Chancellor,
	Nick Desaulniers, Peter Oberparleiter, Peter Zijlstra,
	Steven Rostedt, Thomas Gleixner, Valentin Schneider,
	Vincent Guittot

The kernel carries more than one code coverage implementation, each
with its own directory, Kconfig menu, build plumbing and debugfs
interface. Start consolidating them by moving the existing
implementations next to each other:

  kernel/gcov/  -> kernel/coverage/gcov/
  kernel/kcov.c -> kernel/coverage/kcov.c

Both are configured from a new "Code coverage" menu in
kernel/coverage/Kconfig, which is sourced from "Kernel Testing and
Coverage" where the KCOV options used to live. The gcov options move
there from "General architecture-dependent options".

No functional change: the Kconfig symbols and their dependencies are
unchanged, and so are the per-object compiler flags of kcov.o, which
move to kernel/coverage/Makefile.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 MAINTAINERS                           |  8 +++-
 arch/Kconfig                          |  2 -
 include/linux/sched.h                 |  2 +-
 kernel/Makefile                       | 15 +------
 kernel/coverage/Kconfig               | 65 +++++++++++++++++++++++++++
 kernel/coverage/Makefile              | 16 +++++++
 kernel/{ => coverage}/gcov/Kconfig    |  0
 kernel/{ => coverage}/gcov/Makefile   |  0
 kernel/{ => coverage}/gcov/base.c     |  0
 kernel/{ => coverage}/gcov/clang.c    |  2 +-
 kernel/{ => coverage}/gcov/fs.c       |  0
 kernel/{ => coverage}/gcov/gcc_4_7.c  |  0
 kernel/{ => coverage}/gcov/gcc_base.c |  0
 kernel/{ => coverage}/gcov/gcov.h     |  0
 kernel/{ => coverage}/kcov.c          |  0
 lib/Kconfig.debug                     | 59 +-----------------------
 tools/unittests/test_cmatch.py        |  2 +-
 17 files changed, 92 insertions(+), 79 deletions(-)
 create mode 100644 kernel/coverage/Kconfig
 create mode 100644 kernel/coverage/Makefile
 rename kernel/{ => coverage}/gcov/Kconfig (100%)
 rename kernel/{ => coverage}/gcov/Makefile (100%)
 rename kernel/{ => coverage}/gcov/base.c (100%)
 rename kernel/{ => coverage}/gcov/clang.c (99%)
 rename kernel/{ => coverage}/gcov/fs.c (100%)
 rename kernel/{ => coverage}/gcov/gcc_4_7.c (100%)
 rename kernel/{ => coverage}/gcov/gcc_base.c (100%)
 rename kernel/{ => coverage}/gcov/gcov.h (100%)
 rename kernel/{ => coverage}/kcov.c (100%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 1ed6d81ed2f6b..2e0a98ceed3e7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11129,7 +11129,9 @@ GCOV BASED KERNEL PROFILING
 M:	Peter Oberparleiter <oberpar@linux.ibm.com>
 S:	Maintained
 F:	Documentation/dev-tools/gcov.rst
-F:	kernel/gcov/
+F:	kernel/coverage/Kconfig
+F:	kernel/coverage/Makefile
+F:	kernel/coverage/gcov/
 
 GDB KERNEL DEBUGGING HELPER SCRIPTS
 M:	Jan Kiszka <jan.kiszka@siemens.com>
@@ -14350,7 +14352,9 @@ B:	https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%2
 F:	Documentation/dev-tools/kcov.rst
 F:	include/linux/kcov.h
 F:	include/uapi/linux/kcov.h
-F:	kernel/kcov.c
+F:	kernel/coverage/Kconfig
+F:	kernel/coverage/Makefile
+F:	kernel/coverage/kcov.c
 F:	scripts/Makefile.kcov
 
 KCSAN
diff --git a/arch/Kconfig b/arch/Kconfig
index 50601c0301e84..f4b24649f039c 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -1777,8 +1777,6 @@ config ARCH_VMLINUX_NEEDS_RELOCS
 config HAVE_GENERIC_TIF_BITS
        bool
 
-source "kernel/gcov/Kconfig"
-
 source "scripts/gcc-plugins/Kconfig"
 
 config FUNCTION_ALIGNMENT_4B
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 1c93da75226e3..aa71fe45a6ae1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1536,7 +1536,7 @@ struct task_struct {
 #endif /* CONFIG_TRACING */
 
 #ifdef CONFIG_KCOV
-	/* See kernel/kcov.c for more details. */
+	/* See kernel/coverage/kcov.c for more details. */
 
 	/* Coverage collection mode enabled for this task (0 if disabled): */
 	unsigned int			kcov_mode;
diff --git a/kernel/Makefile b/kernel/Makefile
index 4ff92e24680c5..50bf37a26bae3 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -35,18 +35,6 @@ KCSAN_SANITIZE_softirq.o = n
 # and produce insane amounts of uninteresting coverage.
 KCOV_INSTRUMENT_extable.o := n
 KCOV_INSTRUMENT_stacktrace.o := n
-# Don't self-instrument.
-KCOV_INSTRUMENT_kcov.o := n
-# If sanitizers detect any issues in kcov, it may lead to recursion
-# via printk, etc.
-KASAN_SANITIZE_kcov.o := n
-KCSAN_SANITIZE_kcov.o := n
-UBSAN_SANITIZE_kcov.o := n
-KMSAN_SANITIZE_kcov.o := n
-
-CONTEXT_ANALYSIS_kcov.o := y
-CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
-
 obj-y += sched/
 obj-y += locking/
 obj-y += power/
@@ -96,8 +84,7 @@ obj-$(CONFIG_IKHEADERS) += kheaders.o
 obj-$(CONFIG_SMP) += stop_machine.o
 obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
 obj-$(CONFIG_AUDITSYSCALL) += auditsc.o audit_watch.o audit_fsnotify.o audit_tree.o
-obj-$(CONFIG_GCOV_KERNEL) += gcov/
-obj-$(CONFIG_KCOV) += kcov.o
+obj-y += coverage/
 obj-$(CONFIG_KPROBES) += kprobes.o
 obj-$(CONFIG_FAIL_FUNCTION) += fail_function.o
 obj-$(CONFIG_KGDB) += debug/
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
new file mode 100644
index 0000000000000..dd90b6a61ae65
--- /dev/null
+++ b/kernel/coverage/Kconfig
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Code coverage"
+
+source "kernel/coverage/gcov/Kconfig"
+
+config ARCH_HAS_KCOV
+	bool
+	help
+	  An architecture should select this when it can successfully
+	  build and run with CONFIG_KCOV. This typically requires
+	  disabling instrumentation for some early boot code.
+
+config KCOV
+	bool "Code coverage for fuzzing"
+	depends on ARCH_HAS_KCOV
+	depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
+		   GCC_VERSION >= 120000 || CC_IS_CLANG
+	select DEBUG_FS
+	select OBJTOOL if HAVE_NOINSTR_HACK
+	help
+	  KCOV exposes kernel code coverage information in a form suitable
+	  for coverage-guided fuzzing (randomized testing).
+
+	  For more details, see Documentation/dev-tools/kcov.rst.
+
+config KCOV_ENABLE_COMPARISONS
+	bool "Enable comparison operands collection by KCOV"
+	depends on KCOV
+	depends on $(cc-option,-fsanitize-coverage=trace-cmp)
+	help
+	  KCOV also exposes operands of every comparison in the instrumented
+	  code along with operand sizes and PCs of the comparison instructions.
+	  These operands can be used by fuzzing engines to improve the quality
+	  of fuzzing coverage.
+
+config KCOV_INSTRUMENT_ALL
+	bool "Instrument all code by default"
+	depends on KCOV
+	default y
+	help
+	  If you are doing generic system call fuzzing (like e.g. syzkaller),
+	  then you will want to instrument the whole kernel and you should
+	  say y here. If you are doing more targeted fuzzing (like e.g.
+	  filesystem fuzzing with AFL) then you will want to enable coverage
+	  for more specific subsets of files, and should say n here.
+
+config KCOV_IRQ_AREA_SIZE
+	hex "Size of interrupt coverage collection area in words"
+	depends on KCOV
+	range 0x80 0x1000000
+	default 0x40000
+	help
+	  KCOV uses preallocated areas to collect coverage from soft
+	  interrupts. This specifies the size of those areas in the
+	  number of unsigned long words.
+
+config KCOV_SELFTEST
+	bool "Perform short selftests on boot"
+	depends on KCOV
+	help
+	  Run short KCOV coverage collection selftests on boot.
+	  On test failure, causes the kernel to panic. Recommended to be
+	  enabled, ensuring critical functionality works as intended.
+
+endmenu
diff --git a/kernel/coverage/Makefile b/kernel/coverage/Makefile
new file mode 100644
index 0000000000000..a4cd8cc18d9bc
--- /dev/null
+++ b/kernel/coverage/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_GCOV_KERNEL) += gcov/
+obj-$(CONFIG_KCOV) += kcov.o
+
+# Don't self-instrument.
+KCOV_INSTRUMENT_kcov.o := n
+# If sanitizers detect any issues in kcov, it may lead to recursion
+# via printk, etc.
+KASAN_SANITIZE_kcov.o := n
+KCSAN_SANITIZE_kcov.o := n
+UBSAN_SANITIZE_kcov.o := n
+KMSAN_SANITIZE_kcov.o := n
+
+CONTEXT_ANALYSIS_kcov.o := y
+CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
diff --git a/kernel/gcov/Kconfig b/kernel/coverage/gcov/Kconfig
similarity index 100%
rename from kernel/gcov/Kconfig
rename to kernel/coverage/gcov/Kconfig
diff --git a/kernel/gcov/Makefile b/kernel/coverage/gcov/Makefile
similarity index 100%
rename from kernel/gcov/Makefile
rename to kernel/coverage/gcov/Makefile
diff --git a/kernel/gcov/base.c b/kernel/coverage/gcov/base.c
similarity index 100%
rename from kernel/gcov/base.c
rename to kernel/coverage/gcov/base.c
diff --git a/kernel/gcov/clang.c b/kernel/coverage/gcov/clang.c
similarity index 99%
rename from kernel/gcov/clang.c
rename to kernel/coverage/gcov/clang.c
index fd98ced0e51d2..e979e8983dce8 100644
--- a/kernel/gcov/clang.c
+++ b/kernel/coverage/gcov/clang.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2019 Google, Inc.
- * modified from kernel/gcov/gcc_4_7.c
+ * modified from kernel/coverage/gcov/gcc_4_7.c
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
diff --git a/kernel/gcov/fs.c b/kernel/coverage/gcov/fs.c
similarity index 100%
rename from kernel/gcov/fs.c
rename to kernel/coverage/gcov/fs.c
diff --git a/kernel/gcov/gcc_4_7.c b/kernel/coverage/gcov/gcc_4_7.c
similarity index 100%
rename from kernel/gcov/gcc_4_7.c
rename to kernel/coverage/gcov/gcc_4_7.c
diff --git a/kernel/gcov/gcc_base.c b/kernel/coverage/gcov/gcc_base.c
similarity index 100%
rename from kernel/gcov/gcc_base.c
rename to kernel/coverage/gcov/gcc_base.c
diff --git a/kernel/gcov/gcov.h b/kernel/coverage/gcov/gcov.h
similarity index 100%
rename from kernel/gcov/gcov.h
rename to kernel/coverage/gcov/gcov.h
diff --git a/kernel/kcov.c b/kernel/coverage/kcov.c
similarity index 100%
rename from kernel/kcov.c
rename to kernel/coverage/kcov.c
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6759087154edd..41a5925e4568b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2192,64 +2192,7 @@ config FAULT_INJECTION_STACKTRACE_FILTER
 	help
 	  Provide stacktrace filter for fault-injection capabilities
 
-config ARCH_HAS_KCOV
-	bool
-	help
-	  An architecture should select this when it can successfully
-	  build and run with CONFIG_KCOV. This typically requires
-	  disabling instrumentation for some early boot code.
-
-config KCOV
-	bool "Code coverage for fuzzing"
-	depends on ARCH_HAS_KCOV
-	depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
-		   GCC_VERSION >= 120000 || CC_IS_CLANG
-	select DEBUG_FS
-	select OBJTOOL if HAVE_NOINSTR_HACK
-	help
-	  KCOV exposes kernel code coverage information in a form suitable
-	  for coverage-guided fuzzing (randomized testing).
-
-	  For more details, see Documentation/dev-tools/kcov.rst.
-
-config KCOV_ENABLE_COMPARISONS
-	bool "Enable comparison operands collection by KCOV"
-	depends on KCOV
-	depends on $(cc-option,-fsanitize-coverage=trace-cmp)
-	help
-	  KCOV also exposes operands of every comparison in the instrumented
-	  code along with operand sizes and PCs of the comparison instructions.
-	  These operands can be used by fuzzing engines to improve the quality
-	  of fuzzing coverage.
-
-config KCOV_INSTRUMENT_ALL
-	bool "Instrument all code by default"
-	depends on KCOV
-	default y
-	help
-	  If you are doing generic system call fuzzing (like e.g. syzkaller),
-	  then you will want to instrument the whole kernel and you should
-	  say y here. If you are doing more targeted fuzzing (like e.g.
-	  filesystem fuzzing with AFL) then you will want to enable coverage
-	  for more specific subsets of files, and should say n here.
-
-config KCOV_IRQ_AREA_SIZE
-	hex "Size of interrupt coverage collection area in words"
-	depends on KCOV
-	range 0x80 0x1000000
-	default 0x40000
-	help
-	  KCOV uses preallocated areas to collect coverage from soft
-	  interrupts. This specifies the size of those areas in the
-	  number of unsigned long words.
-
-config KCOV_SELFTEST
-	bool "Perform short selftests on boot"
-	depends on KCOV
-	help
-	  Run short KCOV coverage collection selftests on boot.
-	  On test failure, causes the kernel to panic. Recommended to be
-	  enabled, ensuring critical functionality works as intended.
+source "kernel/coverage/Kconfig"
 
 menuconfig RUNTIME_TESTING_MENU
 	bool "Runtime Testing"
diff --git a/tools/unittests/test_cmatch.py b/tools/unittests/test_cmatch.py
index 109141cd2ab8d..06f9c61572e2e 100755
--- a/tools/unittests/test_cmatch.py
+++ b/tools/unittests/test_cmatch.py
@@ -706,7 +706,7 @@ class TestSubWithLocalXforms(TestCaseDiff):
 
     def test_struct_kcov(self):
         """
-        Test a struct from kernel/kcov.c.
+        Test a struct from kernel/coverage/kcov.c.
         """
         line = """
             struct kcov {
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 03/20] coverage: add a common debugfs interface Sasha Levin
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexander Potapenko, Andrew Morton, Andrey Konovalov,
	Chuck Wolber, Dmitry Vyukov, Jonathan Corbet, kasan-dev,
	linux-doc, linux-kbuild, Marco Elver, Masami Hiramatsu,
	Nathan Chancellor, Nicolas Schier, Peter Oberparleiter,
	Peter Zijlstra, Randy Dunlap, Shuah Khan, Thomas Gleixner,
	workflows

The gcov plumbing is written for a single coverage implementation: the
per-object and per-directory knobs are called GCOV_PROFILE, the flags
are CFLAGS_GCOV, and every dependency on "coverage instrumentation is
present" spells it as GCOV_KERNEL. Make this generic so that further
coverage implementations can share it instead of growing parallel
copies:

 - The coverage implementation becomes a choice ("Code coverage
   measurement"), with gcov as its only member besides "None" for now.
   COVERAGE_KERNEL is set whenever one is selected. allyesconfig and
   allmodconfig keep selecting gcov through a COMPILE_TEST default.

 - GCOV_PROFILE_ALL becomes COVERAGE_PROFILE_ALL. The old symbol is
   kept as a transitional one so that existing configurations keep
   profiling the entire kernel.

 - GCOV_PROFILE[_obj.o] becomes COVERAGE_PROFILE[_obj.o] tree-wide,
   with unchanged precedence (object, then directory, then
   COVERAGE_PROFILE_ALL). Out-of-tree modules that use GCOV_PROFILE
   need the same rename.

 - The compiler flags move from the top-level Makefile to
   scripts/Makefile.gcov, which sets CFLAGS_COVERAGE and is only
   included when gcov is enabled. This also stops running the
   -fprofile-update=prefer-atomic probe on every GCC build.

 - The per-subsystem opt-ins GCOV_PROFILE_{NETFILTER,IPSET,IPVS,RDS,
   AMDGPU,URING,FTRACE} become COVERAGE_PROFILE_*, again with the old
   names kept as transitional symbols. The rds selftest is updated.

 - Dependencies that are about instrumented code rather than about
   gcov (LTO, the KCFI integer normalization workaround, the longest
   symbol KUnit test, the per-subsystem opt-ins and objtool's
   --no-unreachable) now use COVERAGE_KERNEL.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 Documentation/dev-tools/gcov.rst              |  18 +--
 .../translations/zh_CN/dev-tools/gcov.rst     |  16 +--
 .../translations/zh_TW/dev-tools/gcov.rst     |  16 +--
 MAINTAINERS                                   |   1 +
 Makefile                                      |  22 +---
 arch/Kconfig                                  |   6 +-
 arch/alpha/boot/compressed/Makefile           |   2 +-
 arch/parisc/kernel/vdso32/Makefile            |   2 +-
 arch/parisc/kernel/vdso64/Makefile            |   2 +-
 arch/powerpc/kernel/Makefile                  |   6 +-
 arch/powerpc/kernel/trace/Makefile            |   4 +-
 arch/powerpc/kexec/Makefile                   |   2 +-
 arch/powerpc/xmon/Makefile                    |   2 +-
 arch/s390/boot/Makefile                       |   2 +-
 arch/s390/kernel/Makefile                     |   2 +-
 arch/sh/mm/Makefile                           |   2 +-
 arch/x86/coco/sev/Makefile                    |   2 +-
 arch/x86/platform/efi/Makefile                |   2 +-
 arch/xtensa/boot/lib/Makefile                 |   2 +-
 drivers/gpu/drm/amd/amdgpu/Kconfig            |  12 +-
 drivers/gpu/drm/amd/amdgpu/Makefile           |   4 +-
 .../gpu/drm/amd/display/amdgpu_dm/Makefile    |   2 +-
 drivers/gpu/drm/i915/Makefile                 |   2 +-
 drivers/gpu/drm/xe/Makefile                   |   2 +-
 drivers/s390/char/Makefile                    |   2 +-
 init/Kconfig                                  |   9 +-
 io_uring/Makefile                             |   4 +-
 kernel/coverage/Kconfig                       | 103 +++++++++++++++++-
 kernel/coverage/gcov/Kconfig                  |  55 ----------
 kernel/trace/Kconfig                          |   9 +-
 kernel/trace/Makefile                         |   4 +-
 lib/Kconfig.debug                             |   2 +-
 lib/Makefile                                  |   2 +-
 lib/crypto/Makefile                           |   2 +-
 net/bridge/Makefile                           |   8 +-
 net/bridge/netfilter/Makefile                 |   4 +-
 net/ipv4/Makefile                             |   4 +-
 net/ipv4/netfilter/Makefile                   |   4 +-
 net/ipv6/Makefile                             |   4 +-
 net/ipv6/netfilter/Makefile                   |   4 +-
 net/netfilter/Kconfig                         |   9 +-
 net/netfilter/Makefile                        |   4 +-
 net/netfilter/ipset/Kconfig                   |   9 +-
 net/netfilter/ipset/Makefile                  |   4 +-
 net/netfilter/ipvs/Kconfig                    |   9 +-
 net/netfilter/ipvs/Makefile                   |   4 +-
 net/rds/Kconfig                               |   9 +-
 net/rds/Makefile                              |   4 +-
 scripts/Makefile.gcov                         |  22 ++++
 scripts/Makefile.lib                          |  14 +--
 scripts/Makefile.modfinal                     |   2 +-
 scripts/sbom/sbom/environment.py              |   2 +-
 tools/testing/selftests/net/rds/config.sh     |   9 +-
 tools/testing/selftests/net/rds/rds_run.sh    |   8 +-
 54 files changed, 259 insertions(+), 203 deletions(-)
 delete mode 100644 kernel/coverage/gcov/Kconfig
 create mode 100644 scripts/Makefile.gcov

diff --git a/Documentation/dev-tools/gcov.rst b/Documentation/dev-tools/gcov.rst
index 075df6a4598d8..b6910c3fc00dd 100644
--- a/Documentation/dev-tools/gcov.rst
+++ b/Documentation/dev-tools/gcov.rst
@@ -36,10 +36,10 @@ Configure the kernel with::
 
 and to get coverage data for the entire kernel::
 
-        CONFIG_GCOV_PROFILE_ALL=y
+        CONFIG_COVERAGE_PROFILE_ALL=y
 
 Note that kernels compiled with profiling flags will be significantly
-larger and run slower. Also CONFIG_GCOV_PROFILE_ALL may not be supported
+larger and run slower. Also CONFIG_COVERAGE_PROFILE_ALL may not be supported
 on all architectures.
 
 Profiling data will only become accessible once debugfs has been
@@ -56,20 +56,20 @@ similar to the following to the respective kernel Makefile:
 
 - For a single file (e.g. main.o)::
 
-	GCOV_PROFILE_main.o := y
+	COVERAGE_PROFILE_main.o := y
 
 - For all files in one directory::
 
-	GCOV_PROFILE := y
+	COVERAGE_PROFILE := y
 
-To exclude files from being profiled even when CONFIG_GCOV_PROFILE_ALL
+To exclude files from being profiled even when CONFIG_COVERAGE_PROFILE_ALL
 is specified, use::
 
-	GCOV_PROFILE_main.o := n
+	COVERAGE_PROFILE_main.o := n
 
 and::
 
-	GCOV_PROFILE := n
+	COVERAGE_PROFILE := n
 
 Only files which are linked to the main kernel image or are compiled as
 kernel modules are supported by this mechanism.
@@ -80,7 +80,7 @@ Module specific configs
 
 Gcov kernel configs for specific modules are described below:
 
-CONFIG_GCOV_PROFILE_RDS:
+CONFIG_COVERAGE_PROFILE_RDS:
         Enables GCOV profiling on RDS for checking which functions or
         lines are executed. This config is used by the rds selftest to
         generate coverage reports. If left unset the report is omitted.
@@ -207,7 +207,7 @@ Cause
 
 Solution
     Exclude affected source files from profiling by specifying
-    ``GCOV_PROFILE := n`` or ``GCOV_PROFILE_basename.o := n`` in the
+    ``COVERAGE_PROFILE := n`` or ``COVERAGE_PROFILE_basename.o := n`` in the
     corresponding Makefile.
 
 Problem
diff --git a/Documentation/translations/zh_CN/dev-tools/gcov.rst b/Documentation/translations/zh_CN/dev-tools/gcov.rst
index ea8f94852f417..47064c4d5ff98 100644
--- a/Documentation/translations/zh_CN/dev-tools/gcov.rst
+++ b/Documentation/translations/zh_CN/dev-tools/gcov.rst
@@ -39,7 +39,7 @@ gcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 gcov_ 
 
 获取整个内核的覆盖率数据,还需要打开::
 
-        CONFIG_GCOV_PROFILE_ALL=y
+        CONFIG_COVERAGE_PROFILE_ALL=y
 
 需要注意的是,整个内核开启覆盖率统计会造成内核镜像文件尺寸的增大,
 同时内核运行也会变慢一些。
@@ -58,20 +58,20 @@ gcov分析核心支持在Linux内核中启用GCC的覆盖率测试工具 gcov_ 
 
 - 单独统计单个文件(例如main.o)::
 
-    GCOV_PROFILE_main.o := y
+    COVERAGE_PROFILE_main.o := y
 
 - 单独统计某一个路径::
 
-    GCOV_PROFILE := y
+    COVERAGE_PROFILE := y
 
-如果要在整个内核的覆盖率统计(开启CONFIG_GCOV_PROFILE_ALL)中单独排除
+如果要在整个内核的覆盖率统计(开启CONFIG_COVERAGE_PROFILE_ALL)中单独排除
 某一个文件或者路径,可以使用如下的方法::
 
-    GCOV_PROFILE_main.o := n
+    COVERAGE_PROFILE_main.o := n
 
 和::
 
-    GCOV_PROFILE := n
+    COVERAGE_PROFILE := n
 
 此机制仅支持链接到内核镜像或编译为内核模块的文件。
 
@@ -186,8 +186,8 @@ kconfig会根据编译工具链的检查自动选择合适的gcov格式。
     分析标志指定在了源文件但是没有链接到主内核,或者客制化了链接程序
 
 解决方法
-    通过在相应的Makefile中使用 ``GCOV_PROFILE := n``
-    或者 ``GCOV_PROFILE_basename.o := n`` 来将链接报错的文件排除掉
+    通过在相应的Makefile中使用 ``COVERAGE_PROFILE := n``
+    或者 ``COVERAGE_PROFILE_basename.o := n`` 来将链接报错的文件排除掉
 
 可能出现的问题2
     从sysfs复制的文件显示为空或不完整
diff --git a/Documentation/translations/zh_TW/dev-tools/gcov.rst b/Documentation/translations/zh_TW/dev-tools/gcov.rst
index 39ac3fff44cdb..7d36a5460ce12 100644
--- a/Documentation/translations/zh_TW/dev-tools/gcov.rst
+++ b/Documentation/translations/zh_TW/dev-tools/gcov.rst
@@ -39,7 +39,7 @@ gcov分析核心支持在Linux內核中啓用GCC的覆蓋率測試工具 gcov_ 
 
 獲取整個內核的覆蓋率數據,還需要打開::
 
-        CONFIG_GCOV_PROFILE_ALL=y
+        CONFIG_COVERAGE_PROFILE_ALL=y
 
 需要注意的是,整個內核開啓覆蓋率統計會造成內核鏡像文件尺寸的增大,
 同時內核運行也會變慢一些。
@@ -58,20 +58,20 @@ gcov分析核心支持在Linux內核中啓用GCC的覆蓋率測試工具 gcov_ 
 
 - 單獨統計單個文件(例如main.o)::
 
-    GCOV_PROFILE_main.o := y
+    COVERAGE_PROFILE_main.o := y
 
 - 單獨統計某一個路徑::
 
-    GCOV_PROFILE := y
+    COVERAGE_PROFILE := y
 
-如果要在整個內核的覆蓋率統計(開啓CONFIG_GCOV_PROFILE_ALL)中單獨排除
+如果要在整個內核的覆蓋率統計(開啓CONFIG_COVERAGE_PROFILE_ALL)中單獨排除
 某一個文件或者路徑,可以使用如下的方法::
 
-    GCOV_PROFILE_main.o := n
+    COVERAGE_PROFILE_main.o := n
 
 和::
 
-    GCOV_PROFILE := n
+    COVERAGE_PROFILE := n
 
 此機制僅支持鏈接到內核鏡像或編譯爲內核模塊的文件。
 
@@ -186,8 +186,8 @@ kconfig會根據編譯工具鏈的檢查自動選擇合適的gcov格式。
     分析標誌指定在了源文件但是沒有鏈接到主內核,或者客製化了鏈接程序
 
 解決方法
-    通過在相應的Makefile中使用 ``GCOV_PROFILE := n``
-    或者 ``GCOV_PROFILE_basename.o := n`` 來將鏈接報錯的文件排除掉
+    通過在相應的Makefile中使用 ``COVERAGE_PROFILE := n``
+    或者 ``COVERAGE_PROFILE_basename.o := n`` 來將鏈接報錯的文件排除掉
 
 可能出現的問題2
     從sysfs複製的文件顯示爲空或不完整
diff --git a/MAINTAINERS b/MAINTAINERS
index 2e0a98ceed3e7..3cedde9ecaac3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11132,6 +11132,7 @@ F:	Documentation/dev-tools/gcov.rst
 F:	kernel/coverage/Kconfig
 F:	kernel/coverage/Makefile
 F:	kernel/coverage/gcov/
+F:	scripts/Makefile.gcov
 
 GDB KERNEL DEBUGGING HELPER SCRIPTS
 M:	Jan Kiszka <jan.kiszka@siemens.com>
diff --git a/Makefile b/Makefile
index 77caa3b0e9925..cb34eb7e1b790 100644
--- a/Makefile
+++ b/Makefile
@@ -1183,27 +1183,6 @@ endif
 # Ensure compilers do not transform certain loops into calls to wcslen()
 KBUILD_CFLAGS += -fno-builtin-wcslen
 
-CFLAGS_GCOV	:= -fprofile-arcs -ftest-coverage
-ifdef CONFIG_CC_IS_GCC
-CFLAGS_GCOV	+= -fno-tree-loop-im
-# Use atomic counter updates to avoid concurrent-access crashes in GCOV.
-# Only enable if -fprofile-update=prefer-atomic does not introduce new
-# undefined symbols (e.g. libatomic calls that the kernel cannot link).
-CFLAGS_GCOV	+= $(call try-run,\
-	echo 'long long x; void f(void){x++;}' | \
-	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
-	-ftest-coverage -x c - -c -o "$$TMP.base" && \
-	echo 'long long x; void f(void){x++;}' | \
-	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
-	-ftest-coverage -fprofile-update=prefer-atomic \
-	-x c - -c -o "$$TMP" && \
-	$(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \
-	$(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \
-	cmp -s "$$TMP.ubase" "$$TMP.utest",\
-	-fprofile-update=prefer-atomic)
-endif
-export CFLAGS_GCOV
-
 # change __FILE__ to the relative path to the source directory
 ifdef building_out_of_srctree
 CFLAGS_PREFIX_MAP := -fmacro-prefix-map=$(srcroot)/=
@@ -1223,6 +1202,7 @@ include-$(CONFIG_KASAN)		+= scripts/Makefile.kasan
 include-$(CONFIG_KCSAN)		+= scripts/Makefile.kcsan
 include-$(CONFIG_KMSAN)		+= scripts/Makefile.kmsan
 include-$(CONFIG_UBSAN)		+= scripts/Makefile.ubsan
+include-$(CONFIG_GCOV_KERNEL)	+= scripts/Makefile.gcov
 include-$(CONFIG_KCOV)		+= scripts/Makefile.kcov
 include-$(CONFIG_RANDSTRUCT)	+= scripts/Makefile.randstruct
 include-$(CONFIG_KSTACK_ERASE)	+= scripts/Makefile.kstack_erase
diff --git a/arch/Kconfig b/arch/Kconfig
index f4b24649f039c..5fc66af346eda 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -819,7 +819,7 @@ config HAS_LTO_CLANG
 	depends on $(success,$(AR) --help | head -n 1 | grep -qi llvm)
 	depends on ARCH_SUPPORTS_LTO_CLANG
 	depends on !FTRACE_MCOUNT_USE_RECORDMCOUNT
-	depends on !GCOV_KERNEL
+	depends on !COVERAGE_KERNEL
 	help
 	  The compiler and Kconfig options support building with Clang's
 	  LTO.
@@ -985,7 +985,7 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS
 	def_bool y
 	depends on $(cc-option,-fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers)
 	# With GCOV/KASAN we need this fix: https://github.com/llvm/llvm-project/pull/104826
-	depends on CLANG_VERSION >= 190103 || (!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS)
+	depends on CLANG_VERSION >= 190103 || (!COVERAGE_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS)
 
 config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC
 	def_bool y
@@ -993,7 +993,7 @@ config HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC
 	depends on ARM64 || X86_64
 	# With GCOV/KASAN we need this fix: https://github.com/rust-lang/rust/pull/129373
 	depends on RUSTC_LLVM_VERSION >= 190103 || \
-		(!GCOV_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS)
+		(!COVERAGE_KERNEL && !KASAN_GENERIC && !KASAN_SW_TAGS)
 
 config CFI_PERMISSIVE
 	bool "Use CFI in permissive mode"
diff --git a/arch/alpha/boot/compressed/Makefile b/arch/alpha/boot/compressed/Makefile
index eb930f01b4be4..26c3f62075a90 100644
--- a/arch/alpha/boot/compressed/Makefile
+++ b/arch/alpha/boot/compressed/Makefile
@@ -28,7 +28,7 @@ KBUILD_AFLAGS := -D__ASSEMBLY__ $(KBUILD_CFLAGS)
 # built with the kernel's instrumentation and -mcpu=, so build private
 # copies of the library routines the stub needs instead of linking it.
 UBSAN_SANITIZE := n
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 
 $(obj)/memcpy.o: $(srctree)/arch/alpha/lib/memcpy.c FORCE
 	$(call if_changed_dep,cc_o_c)
diff --git a/arch/parisc/kernel/vdso32/Makefile b/arch/parisc/kernel/vdso32/Makefile
index 4ee8d17da229b..e884795da088d 100644
--- a/arch/parisc/kernel/vdso32/Makefile
+++ b/arch/parisc/kernel/vdso32/Makefile
@@ -4,7 +4,7 @@ include $(srctree)/lib/vdso/Makefile.include
 KCOV_INSTRUMENT := n
 
 # Disable gcov profiling, ubsan and kasan for VDSO code
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 UBSAN_SANITIZE := n
 KASAN_SANITIZE := n
 KCSAN_SANITIZE := n
diff --git a/arch/parisc/kernel/vdso64/Makefile b/arch/parisc/kernel/vdso64/Makefile
index c63f4069170f4..42b8b0d167e1a 100644
--- a/arch/parisc/kernel/vdso64/Makefile
+++ b/arch/parisc/kernel/vdso64/Makefile
@@ -4,7 +4,7 @@ include $(srctree)/lib/vdso/Makefile.include
 KCOV_INSTRUMENT := n
 
 # Disable gcov profiling, ubsan and kasan for VDSO code
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 UBSAN_SANITIZE := n
 KASAN_SANITIZE := n
 KCSAN_SANITIZE := n
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 7bf6b16b2d93c..a38e82894130c 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -169,15 +169,15 @@ obj-$(CONFIG_PPC_SECURE_BOOT)	+= secure_boot.o ima_arch.o secvar-ops.o
 obj-$(CONFIG_PPC_SECVAR_SYSFS)	+= secvar-sysfs.o
 
 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
-GCOV_PROFILE_prom_init.o := n
+COVERAGE_PROFILE_prom_init.o := n
 KCOV_INSTRUMENT_prom_init.o := n
 KCSAN_SANITIZE_prom_init.o := n
 UBSAN_SANITIZE_prom_init.o := n
-GCOV_PROFILE_kprobes.o := n
+COVERAGE_PROFILE_kprobes.o := n
 KCOV_INSTRUMENT_kprobes.o := n
 KCSAN_SANITIZE_kprobes.o := n
 UBSAN_SANITIZE_kprobes.o := n
-GCOV_PROFILE_kprobes-ftrace.o := n
+COVERAGE_PROFILE_kprobes-ftrace.o := n
 KCOV_INSTRUMENT_kprobes-ftrace.o := n
 KCSAN_SANITIZE_kprobes-ftrace.o := n
 UBSAN_SANITIZE_kprobes-ftrace.o := n
diff --git a/arch/powerpc/kernel/trace/Makefile b/arch/powerpc/kernel/trace/Makefile
index d6c3885453bda..e5d3b15f82018 100644
--- a/arch/powerpc/kernel/trace/Makefile
+++ b/arch/powerpc/kernel/trace/Makefile
@@ -24,11 +24,11 @@ obj-$(CONFIG_PPC64)			+= $(obj64-y)
 obj-$(CONFIG_PPC32)			+= $(obj32-y)
 
 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
-GCOV_PROFILE_ftrace.o := n
+COVERAGE_PROFILE_ftrace.o := n
 KCOV_INSTRUMENT_ftrace.o := n
 KCSAN_SANITIZE_ftrace.o := n
 UBSAN_SANITIZE_ftrace.o := n
-GCOV_PROFILE_ftrace_64_pg.o := n
+COVERAGE_PROFILE_ftrace_64_pg.o := n
 KCOV_INSTRUMENT_ftrace_64_pg.o := n
 KCSAN_SANITIZE_ftrace_64_pg.o := n
 UBSAN_SANITIZE_ftrace_64_pg.o := n
diff --git a/arch/powerpc/kexec/Makefile b/arch/powerpc/kexec/Makefile
index ec7a0eed75dc5..d9e7a387d058c 100644
--- a/arch/powerpc/kexec/Makefile
+++ b/arch/powerpc/kexec/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_VMCORE_INFO)	+= vmcore_info.o
 obj-$(CONFIG_CRASH_DUMP)	+= crash.o
 
 # Disable GCOV, KCOV & sanitizers in odd or sensitive code
-GCOV_PROFILE_core_$(BITS).o := n
+COVERAGE_PROFILE_core_$(BITS).o := n
 KCOV_INSTRUMENT_core_$(BITS).o := n
 UBSAN_SANITIZE_core_$(BITS).o := n
 KASAN_SANITIZE_core.o := n
diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile
index d74b147126b7a..b2ad38d5ec8d8 100644
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for xmon
 
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 KCOV_INSTRUMENT := n
 UBSAN_SANITIZE := n
 KASAN_SANITIZE := n
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
index e8e43ae80dd52..8dea682626462 100644
--- a/arch/s390/boot/Makefile
+++ b/arch/s390/boot/Makefile
@@ -5,7 +5,7 @@
 
 # Tooling runtimes are unavailable and cannot be linked for early boot code
 KCOV_INSTRUMENT := n
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 UBSAN_SANITIZE := n
 KASAN_SANITIZE := n
 KCSAN_SANITIZE := n
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index f84b8fb41331b..8642db1c1f234 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -18,7 +18,7 @@ CFLAGS_REMOVE_unwind_bc.o	= $(CC_FLAGS_FTRACE)
 
 endif
 
-GCOV_PROFILE_early.o		:= n
+COVERAGE_PROFILE_early.o		:= n
 KCOV_INSTRUMENT_early.o		:= n
 UBSAN_SANITIZE_early.o		:= n
 KASAN_SANITIZE_ipl.o		:= n
diff --git a/arch/sh/mm/Makefile b/arch/sh/mm/Makefile
index f69ddc70b1465..8752d3a0c1ea5 100644
--- a/arch/sh/mm/Makefile
+++ b/arch/sh/mm/Makefile
@@ -42,4 +42,4 @@ obj-$(CONFIG_IOREMAP_FIXED)	+= ioremap_fixed.o
 obj-$(CONFIG_UNCACHED_MAPPING)	+= uncached.o
 obj-$(CONFIG_HAVE_SRAM_POOL)	+= sram.o
 
-GCOV_PROFILE_pmb.o := n
+COVERAGE_PROFILE_pmb.o := n
diff --git a/arch/x86/coco/sev/Makefile b/arch/x86/coco/sev/Makefile
index 3111a2ee67221..be4785d766ec1 100644
--- a/arch/x86/coco/sev/Makefile
+++ b/arch/x86/coco/sev/Makefile
@@ -9,4 +9,4 @@ UBSAN_SANITIZE_noinstr.o	:= n
 KASAN_SANITIZE_noinstr.o	:= n
 KCSAN_SANITIZE_noinstr.o	:= n
 
-GCOV_PROFILE_noinstr.o		:= n
+COVERAGE_PROFILE_noinstr.o		:= n
diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile
index 500cab4a7f7c6..b0b891f776bfb 100644
--- a/arch/x86/platform/efi/Makefile
+++ b/arch/x86/platform/efi/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 KASAN_SANITIZE := n
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 
 obj-$(CONFIG_EFI) 		+= memmap.o quirks.o efi.o efi_$(BITS).o \
 				   efi_stub_$(BITS).o
diff --git a/arch/xtensa/boot/lib/Makefile b/arch/xtensa/boot/lib/Makefile
index 0378a22a08e33..b42c7fab56a5e 100644
--- a/arch/xtensa/boot/lib/Makefile
+++ b/arch/xtensa/boot/lib/Makefile
@@ -18,7 +18,7 @@ endif
 KASAN_SANITIZE := n
 KCSAN_SANITIZE := n
 KCOV_INSTRUMENT := n
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 
 CFLAGS_REMOVE_inflate.o += -fstack-protector -fstack-protector-strong
 CFLAGS_REMOVE_zmem.o += -fstack-protector -fstack-protector-strong
diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig
index 12e4a41bf1f04..aaebfdfc31a12 100644
--- a/drivers/gpu/drm/amd/amdgpu/Kconfig
+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
@@ -103,16 +103,16 @@ config DRM_AMDGPU_WERROR
 	  Only enable this if you are warning code for amdgpu.ko.
 
 
-config GCOV_PROFILE_AMDGPU
-	bool "Enable GCOV profiling on amdgpu"
+config COVERAGE_PROFILE_AMDGPU
+	bool "Enable code coverage profiling on amdgpu"
 	depends on DRM_AMDGPU
-	depends on GCOV_KERNEL
-	default n
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_AMDGPU
 	help
-	  Enable GCOV profiling on the amdgpu driver for checking which
+	  Enable code coverage profiling on the amdgpu driver for checking which
 	  functions/lines are executed during testing. This adds compiler
 	  instrumentation flags to all amdgpu source files, producing
-	  .gcda/.gcno coverage data accessible via debugfs.
+	  coverage data accessible via debugfs.
 
 	  This increases the amdgpu module size by ~50% and adds ~2-5%
 	  runtime overhead on GPU submission paths.
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index 55b5d11f19382..dc408e24f5bc6 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -27,8 +27,8 @@ FULL_AMD_PATH=$(src)/..
 DISPLAY_FOLDER_NAME=display
 FULL_AMD_DISPLAY_PATH = $(FULL_AMD_PATH)/$(DISPLAY_FOLDER_NAME)
 
-ifdef CONFIG_GCOV_PROFILE_AMDGPU
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_AMDGPU
+COVERAGE_PROFILE := y
 endif
 
 ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index d1faf2d8370ff..3cd62d0be68e7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -71,5 +71,5 @@ ifneq ($(CONFIG_DRM_AMD_DC_KUNIT_TEST),)
 obj-y += $(AMDDALPATH)/amdgpu_dm/tests/
 
 # Enable gcov for amdgpu_dm KUnit builds
-GCOV_PROFILE := y
+COVERAGE_PROFILE := y
 endif
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d4e7764621e18..20675723fee4a 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -456,7 +456,7 @@ always-$(CONFIG_DRM_I915_WERROR) += \
 		$(shell cd $(src) && find * -name '*.h')))
 
 quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@)
-      cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; \
+      cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_COVERAGE), $(c_flags)) -S -o /dev/null -x c /dev/null -include $<; \
 		$(KERNELDOC) -none -Werror $<; touch $@
 
 $(obj)/%.hdrtest: $(src)/%.h FORCE
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 563c2f859361f..c0f4e684268cf 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -392,7 +392,7 @@ always-$(CONFIG_DRM_XE_WERROR) += \
 	$(patsubst %.h,%.hdrtest, $(shell cd $(src) && find * -name '*.h' $(hdrtest_find_args)))
 
 quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@)
-      cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_GCOV), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \
+      cmd_hdrtest = $(CC) $(filter-out $(CFLAGS_COVERAGE), $(c_flags)) -S -o /dev/null -x c /dev/null -include $< -include $<; \
 		$(srctree)/scripts/kernel-doc -none -Werror $<; touch $@
 
 $(obj)/%.hdrtest: $(src)/%.h FORCE
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 0ad960d000a2f..0d4c144cbd994 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -10,7 +10,7 @@ ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_sclp_early_core.o	= $(CC_FLAGS_FTRACE)
 endif
 
-GCOV_PROFILE_sclp_early_core.o		:= n
+COVERAGE_PROFILE_sclp_early_core.o		:= n
 KCOV_INSTRUMENT_sclp_early_core.o	:= n
 UBSAN_SANITIZE_sclp_early_core.o	:= n
 KASAN_SANITIZE_sclp_early_core.o	:= n
diff --git a/init/Kconfig b/init/Kconfig
index b31aa298c4fe5..9ec5def26d1b8 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1780,11 +1780,12 @@ config IO_URING
 	  applications to submit and complete IO through submission and
 	  completion rings that are shared between the kernel and application.
 
-config GCOV_PROFILE_URING
-	bool "Enable GCOV profiling on the io_uring subsystem"
-	depends on IO_URING && GCOV_KERNEL
+config COVERAGE_PROFILE_URING
+	bool "Enable code coverage profiling on the io_uring subsystem"
+	depends on IO_URING && COVERAGE_KERNEL
+	default GCOV_PROFILE_URING
 	help
-	  Enable GCOV profiling on the io_uring subsystem, to facilitate
+	  Enable code coverage profiling on the io_uring subsystem, to facilitate
 	  code coverage testing.
 
 	  If unsure, say N.
diff --git a/io_uring/Makefile b/io_uring/Makefile
index 965bb60e9f7b1..cb27f1f8b59fb 100644
--- a/io_uring/Makefile
+++ b/io_uring/Makefile
@@ -2,8 +2,8 @@
 #
 # Makefile for io_uring
 
-ifdef CONFIG_GCOV_PROFILE_URING
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_URING
+COVERAGE_PROFILE := y
 endif
 
 obj-$(CONFIG_IO_URING)		+= io_uring.o opdef.o kbuf.o rsrc.o notif.o \
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index dd90b6a61ae65..62a6fa94bd308 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -1,7 +1,108 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "Code coverage"
 
-source "kernel/coverage/gcov/Kconfig"
+config ARCH_HAS_GCOV_PROFILE_ALL
+	def_bool n
+
+choice
+	prompt "Code coverage measurement"
+	default GCOV_KERNEL if COMPILE_TEST
+	default COVERAGE_NONE
+	help
+	  Instrument the kernel to measure which parts of it get executed.
+	  Each mechanism produces data in the format of its toolchain, so
+	  only one of them can be enabled at a time.
+
+	  KCOV, which collects coverage for fuzzers, is configured
+	  separately and can be combined with any of them.
+
+	  The instrumentation of a single file or of all files in a
+	  directory can be controlled from the respective Makefile:
+
+	  For a single file (e.g. main.o):
+	          COVERAGE_PROFILE_main.o := y
+
+	  For all files in one directory:
+	          COVERAGE_PROFILE := y
+
+	  To exclude files from being profiled even when
+	  CONFIG_COVERAGE_PROFILE_ALL is specified, use:
+
+	          COVERAGE_PROFILE_main.o := n
+	  and:
+	          COVERAGE_PROFILE := n
+
+	  Note that the debugfs filesystem has to be mounted to access
+	  the coverage data.
+
+config COVERAGE_NONE
+	bool "None"
+
+config GCOV_KERNEL
+	bool "gcov-based kernel profiling"
+	depends on DEBUG_FS
+	depends on !ARCH_WANTS_NO_INSTR || CC_HAS_NO_PROFILE_FN_ATTR
+	select CONSTRUCTORS
+	help
+	  This option enables gcov-based code profiling (e.g. for code coverage
+	  measurements), with GCC or Clang. The coverage data of the kernel
+	  and its modules is exported in gcov's format in /sys/kernel/debug/gcov,
+	  see Documentation/dev-tools/gcov.rst.
+
+endchoice
+
+config COVERAGE_KERNEL
+	def_bool GCOV_KERNEL
+
+config GCOV_PROFILE_ALL
+	bool
+	transitional
+	help
+	  Transitional config for GCOV_PROFILE_ALL to COVERAGE_PROFILE_ALL
+	  migration.
+
+config COVERAGE_PROFILE_ALL
+	bool "Profile entire kernel"
+	depends on !COMPILE_TEST
+	depends on GCOV_KERNEL && ARCH_HAS_GCOV_PROFILE_ALL
+	default GCOV_PROFILE_ALL
+	help
+	  This option activates coverage instrumentation for the entire kernel.
+
+	  If unsure, say N.
+
+	  Note that a kernel compiled with profiling flags will be significantly
+	  larger and run slower. Also be sure to exclude files from profiling
+	  which are not linked to the kernel image to prevent linker errors.
+
+# The per-subsystem COVERAGE_PROFILE_* options used to be GCOV_PROFILE_*.
+config GCOV_PROFILE_AMDGPU
+	bool
+	transitional
+
+config GCOV_PROFILE_URING
+	bool
+	transitional
+
+config GCOV_PROFILE_FTRACE
+	bool
+	transitional
+
+config GCOV_PROFILE_NETFILTER
+	bool
+	transitional
+
+config GCOV_PROFILE_IPSET
+	bool
+	transitional
+
+config GCOV_PROFILE_IPVS
+	bool
+	transitional
+
+config GCOV_PROFILE_RDS
+	bool
+	transitional
 
 config ARCH_HAS_KCOV
 	bool
diff --git a/kernel/coverage/gcov/Kconfig b/kernel/coverage/gcov/Kconfig
deleted file mode 100644
index 04f4ebdc3cf59..0000000000000
--- a/kernel/coverage/gcov/Kconfig
+++ /dev/null
@@ -1,55 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-menu "GCOV-based kernel profiling"
-
-config GCOV_KERNEL
-	bool "Enable gcov-based kernel profiling"
-	depends on DEBUG_FS
-	depends on !ARCH_WANTS_NO_INSTR || CC_HAS_NO_PROFILE_FN_ATTR
-	select CONSTRUCTORS
-	default n
-	help
-	This option enables gcov-based code profiling (e.g. for code coverage
-	measurements).
-
-	If unsure, say N.
-
-	Additionally specify CONFIG_GCOV_PROFILE_ALL=y to get profiling data
-	for the entire kernel. To enable profiling for specific files or
-	directories, add a line similar to the following to the respective
-	Makefile:
-
-	For a single file (e.g. main.o):
-	        GCOV_PROFILE_main.o := y
-
-	For all files in one directory:
-	        GCOV_PROFILE := y
-
-	To exclude files from being profiled even when CONFIG_GCOV_PROFILE_ALL
-	is specified, use:
-
-	        GCOV_PROFILE_main.o := n
-	and:
-	        GCOV_PROFILE := n
-
-	Note that the debugfs filesystem has to be mounted to access
-	profiling data.
-
-config ARCH_HAS_GCOV_PROFILE_ALL
-	def_bool n
-
-config GCOV_PROFILE_ALL
-	bool "Profile entire Kernel"
-	depends on !COMPILE_TEST
-	depends on GCOV_KERNEL
-	depends on ARCH_HAS_GCOV_PROFILE_ALL
-	default n
-	help
-	This options activates profiling for the entire kernel.
-
-	If unsure, say N.
-
-	Note that a kernel compiled with profiling flags will be significantly
-	larger and run slower. Also be sure to exclude files from profiling
-	which are not linked to the kernel image to prevent linker errors.
-
-endmenu
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index d8faeb659cfc9..4d88deda4ef8a 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -1110,11 +1110,12 @@ config RING_BUFFER_RECORD_RECURSION
 
 	  This will add more overhead to cases that have recursion.
 
-config GCOV_PROFILE_FTRACE
-	bool "Enable GCOV profiling on ftrace subsystem"
-	depends on GCOV_KERNEL
+config COVERAGE_PROFILE_FTRACE
+	bool "Enable code coverage profiling on ftrace subsystem"
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_FTRACE
 	help
-	  Enable GCOV profiling on ftrace subsystem for checking
+	  Enable code coverage profiling on ftrace subsystem for checking
 	  which functions/lines are tested.
 
 	  If unsure, say N.
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 1f925cd1b9f89..02d57906f13f5 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -44,8 +44,8 @@ KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
 endif
 
 # for GCOV coverage profiling
-ifdef CONFIG_GCOV_PROFILE_FTRACE
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_FTRACE
+COVERAGE_PROFILE := y
 endif
 
 # Functions in these files can run from IRQ entry before hardirq context
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 41a5925e4568b..fa68fc92e543c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3048,7 +3048,7 @@ config FORTIFY_KUNIT_TEST
 config LONGEST_SYM_KUNIT_TEST
 	tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS
 	depends on KUNIT && KPROBES
-	depends on !CALL_PADDING && !CFI && !GCOV_KERNEL
+	depends on !CALL_PADDING && !CFI && !COVERAGE_KERNEL
 	default KUNIT_ALL_TESTS
 	help
 	  Tests the longest symbol possible
diff --git a/lib/Makefile b/lib/Makefile
index a2afe954805cb..6e80cd0023e8e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -88,7 +88,7 @@ obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
 obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
 ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy)
 # FIXME: Clang breaks test_bitmap_const_eval when KASAN and GCOV are enabled
-GCOV_PROFILE_test_bitmap.o := n
+COVERAGE_PROFILE_test_bitmap.o := n
 endif
 
 obj-$(CONFIG_TEST_XARRAY) += test_xarray.o
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index 832d170cab941..4d7b265c023a1 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -143,7 +143,7 @@ obj-$(CONFIG_CRYPTO_LIB_CURVE25519) += libcurve25519.o
 libcurve25519-y := curve25519.o
 
 # Disable GCOV in odd or sensitive code
-GCOV_PROFILE_curve25519.o := n
+COVERAGE_PROFILE_curve25519.o := n
 
 ifeq ($(CONFIG_ARCH_SUPPORTS_INT128),y)
 libcurve25519-$(CONFIG_CRYPTO_LIB_CURVE25519_GENERIC) += curve25519-hacl64.o
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 1203dc19e15c9..79e07925c9322 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -30,8 +30,8 @@ bridge-$(CONFIG_BRIDGE_MRP)	+= br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
 
 bridge-$(CONFIG_BRIDGE_CFM)	+= br_cfm.o br_cfm_netlink.o
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE_br_nf_core.o := y
-GCOV_PROFILE_br_netfilter_hooks.o := y
-GCOV_PROFILE_br_netfilter_ipv6.o := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE_br_nf_core.o := y
+COVERAGE_PROFILE_br_netfilter_hooks.o := y
+COVERAGE_PROFILE_br_netfilter_ipv6.o := y
 endif
diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile
index af0c903aa4ace..46af78735a5c5 100644
--- a/net/bridge/netfilter/Makefile
+++ b/net/bridge/netfilter/Makefile
@@ -39,6 +39,6 @@ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
 obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
 obj-$(CONFIG_BRIDGE_EBT_NFLOG) += ebt_nflog.o
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE := y
 endif
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index e8a0e9605b901..23f68b7fb3498 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -74,6 +74,6 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o
 obj-$(CONFIG_CGROUP_BPF) += bpf_tcp_ops.o
 endif
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE_netfilter.o := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE_netfilter.o := y
 endif
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index dbfb1c4739a87..c4c0dfeef382c 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -52,6 +52,6 @@ obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
 
 obj-$(CONFIG_NF_DUP_IPV4) += nf_dup_ipv4.o
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE := y
 endif
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index cf5e01f83ce3d..3c7e993fefcc1 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -55,6 +55,6 @@ obj-y += mcast_snoop.o ndisc_snoop.o
 obj-$(CONFIG_TCP_AO) += tcp_ao.o
 endif
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE_netfilter.o := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE_netfilter.o := y
 endif
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index 72902d8005ad9..e49f0dd9526ac 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -44,6 +44,6 @@ obj-$(CONFIG_IP6_NF_TARGET_NPT) += ip6t_NPT.o
 obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
 obj-$(CONFIG_IP6_NF_TARGET_SYNPROXY) += ip6t_SYNPROXY.o
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE := y
 endif
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 09874c26fd135..9470494ee760a 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1649,11 +1649,12 @@ config NETFILTER_XT_MATCH_U32
 
 endif # NETFILTER_XTABLES
 
-config GCOV_PROFILE_NETFILTER
-	bool "Enable GCOV profiling for netfilter"
-	depends on GCOV_KERNEL
+config COVERAGE_PROFILE_NETFILTER
+	bool "Enable code coverage profiling for netfilter"
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_NETFILTER
 	help
-	  Enable GCOV profiling for netfilter to check which functions/lines
+	  Enable code coverage profiling for netfilter to check which functions/lines
 	  are executed.
 
 	  If unsure, say N.
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 6bf74d488a292..f84c622e3a70d 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -241,6 +241,6 @@ obj-$(CONFIG_IP_VS) += ipvs/
 # lwtunnel
 obj-$(CONFIG_LWTUNNEL) += nf_hooks_lwtunnel.o
 
-ifdef CONFIG_GCOV_PROFILE_NETFILTER
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_NETFILTER
+COVERAGE_PROFILE := y
 endif
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index 6c4d547581067..e8f05c1df8e0f 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -175,11 +175,12 @@ config IP_SET_LIST_SET
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
-config GCOV_PROFILE_IPSET
-	bool "Enable GCOV profiling for ipset"
-	depends on GCOV_KERNEL
+config COVERAGE_PROFILE_IPSET
+	bool "Enable code coverage profiling for ipset"
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_IPSET
 	help
-	  Enable GCOV profiling for ipset to check which functions/lines
+	  Enable code coverage profiling for ipset to check which functions/lines
 	  are executed.
 
 	  If unsure, say N.
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile
index 4f48df5406cd3..b4991815f44bb 100644
--- a/net/netfilter/ipset/Makefile
+++ b/net/netfilter/ipset/Makefile
@@ -29,6 +29,6 @@ obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o
 
 # list types
 obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o
-ifdef CONFIG_GCOV_PROFILE_IPSET
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_IPSET
+COVERAGE_PROFILE := y
 endif
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig
index 7724cb44e6dea..959e5832be874 100644
--- a/net/netfilter/ipvs/Kconfig
+++ b/net/netfilter/ipvs/Kconfig
@@ -349,11 +349,12 @@ config	IP_VS_PE_SIP
 	help
 	  Allow persistence based on the SIP Call-ID
 
-config GCOV_PROFILE_IPVS
-	bool "Enable GCOV profiling for IPVS"
-	depends on GCOV_KERNEL
+config COVERAGE_PROFILE_IPVS
+	bool "Enable code coverage profiling for IPVS"
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_IPVS
 	help
-	  Enable GCOV profiling for IPVS to check which functions/lines
+	  Enable code coverage profiling for IPVS to check which functions/lines
 	  are executed.
 
 	  If unsure, say N.
diff --git a/net/netfilter/ipvs/Makefile b/net/netfilter/ipvs/Makefile
index 8e4cc67ad39d5..6cf42a7350904 100644
--- a/net/netfilter/ipvs/Makefile
+++ b/net/netfilter/ipvs/Makefile
@@ -43,6 +43,6 @@ obj-$(CONFIG_IP_VS_FTP) += ip_vs_ftp.o
 
 # IPVS connection template retrievers
 obj-$(CONFIG_IP_VS_PE_SIP) += ip_vs_pe_sip.o
-ifdef CONFIG_GCOV_PROFILE_IPVS
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_IPVS
+COVERAGE_PROFILE := y
 endif
diff --git a/net/rds/Kconfig b/net/rds/Kconfig
index f007730aa2bb6..9c35cb18a3f93 100644
--- a/net/rds/Kconfig
+++ b/net/rds/Kconfig
@@ -27,11 +27,12 @@ config RDS_DEBUG
 	depends on RDS
 	default n
 
-config GCOV_PROFILE_RDS
-	bool "Enable GCOV profiling on RDS"
-	depends on GCOV_KERNEL
+config COVERAGE_PROFILE_RDS
+	bool "Enable code coverage profiling on RDS"
+	depends on COVERAGE_KERNEL
+	default GCOV_PROFILE_RDS
 	help
-	  Enable GCOV profiling on RDS for checking which functions/lines
+	  Enable code coverage profiling on RDS for checking which functions/lines
 	  are executed.
 
 	  If unsure, say N.
diff --git a/net/rds/Makefile b/net/rds/Makefile
index 3af1ca1d965ce..84c1eb77369f5 100644
--- a/net/rds/Makefile
+++ b/net/rds/Makefile
@@ -17,6 +17,6 @@ rds_tcp-y :=		tcp.o tcp_connect.o tcp_listen.o tcp_recv.o \
 ccflags-$(CONFIG_RDS_DEBUG)	:=	-DRDS_DEBUG
 
 # for GCOV coverage profiling
-ifdef CONFIG_GCOV_PROFILE_RDS
-GCOV_PROFILE := y
+ifdef CONFIG_COVERAGE_PROFILE_RDS
+COVERAGE_PROFILE := y
 endif
diff --git a/scripts/Makefile.gcov b/scripts/Makefile.gcov
new file mode 100644
index 0000000000000..1620b81398eaa
--- /dev/null
+++ b/scripts/Makefile.gcov
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS_COVERAGE	:= -fprofile-arcs -ftest-coverage
+ifdef CONFIG_CC_IS_GCC
+CFLAGS_COVERAGE	+= -fno-tree-loop-im
+# Use atomic counter updates to avoid concurrent-access crashes in GCOV.
+# Only enable if -fprofile-update=prefer-atomic does not introduce new
+# undefined symbols (e.g. libatomic calls that the kernel cannot link).
+CFLAGS_COVERAGE	+= $(call try-run,\
+	echo 'long long x; void f(void){x++;}' | \
+	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
+	-ftest-coverage -x c - -c -o "$$TMP.base" && \
+	echo 'long long x; void f(void){x++;}' | \
+	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \
+	-ftest-coverage -fprofile-update=prefer-atomic \
+	-x c - -c -o "$$TMP" && \
+	$(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \
+	$(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \
+	cmp -s "$$TMP.ubase" "$$TMP.utest",\
+	-fprofile-update=prefer-atomic)
+endif
+export CFLAGS_COVERAGE
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 19e96b30a122f..e0dc21e009522 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -38,14 +38,14 @@ _a_flags       = $(filter-out $(AFLAGS_REMOVE_$(target-stem).o), \
 _cpp_flags     = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(target-stem).lds)
 
 #
-# Enable gcov profiling flags for a file, directory or for all files depending
-# on variables GCOV_PROFILE_obj.o, GCOV_PROFILE and CONFIG_GCOV_PROFILE_ALL
-# (in this order)
+# Enable code coverage instrumentation for a file, directory or for all files
+# depending on variables COVERAGE_PROFILE_obj.o, COVERAGE_PROFILE and
+# CONFIG_COVERAGE_PROFILE_ALL (in this order)
 #
-ifeq ($(CONFIG_GCOV_KERNEL),y)
+ifeq ($(CONFIG_COVERAGE_KERNEL),y)
 _c_flags += $(if $(patsubst n%,, \
-		$(GCOV_PROFILE_$(target-stem).o)$(GCOV_PROFILE)$(if $(is-kernel-object),$(CONFIG_GCOV_PROFILE_ALL))), \
-		$(CFLAGS_GCOV))
+		$(COVERAGE_PROFILE_$(target-stem).o)$(COVERAGE_PROFILE)$(if $(is-kernel-object),$(CONFIG_COVERAGE_PROFILE_ALL))), \
+		$(CFLAGS_COVERAGE))
 endif
 
 #
@@ -206,7 +206,7 @@ objtool-args-$(CONFIG_MITIGATION_SLS)			+= --sls
 objtool-args-$(CONFIG_STACK_VALIDATION)			+= --stackval
 objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE)		+= --static-call
 objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION)		+= --uaccess
-objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV))	+= --no-unreachable
+objtool-args-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV))	+= --no-unreachable
 objtool-args-$(CONFIG_OBJTOOL_WERROR)			+= --werror
 
 objtool-args = $(objtool-args-y)					\
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 01a37ec872b90..2ca03eccbe291 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -19,7 +19,7 @@ __modfinal: $(modules:%.o=%.ko)
 # modname and part-of-module are set to make c_flags define proper module flags
 modname = $(notdir $(@:.mod.o=))
 part-of-module = y
-GCOV_PROFILE := n
+COVERAGE_PROFILE := n
 ccflags-remove-y := $(CC_FLAGS_CFI)
 
 %.mod.o: %.mod.c FORCE
diff --git a/scripts/sbom/sbom/environment.py b/scripts/sbom/sbom/environment.py
index 4304066fe974e..a74c96ddfe593 100644
--- a/scripts/sbom/sbom/environment.py
+++ b/scripts/sbom/sbom/environment.py
@@ -18,7 +18,7 @@ KERNEL_BUILD_VARIABLES_ALLOWLIST = [
     "CC",
     "CC_FLAGS_FPU",
     "CC_FLAGS_NO_FPU",
-    "CFLAGS_GCOV",
+    "CFLAGS_COVERAGE",
     "CFLAGS_KERNEL",
     "CFLAGS_MODULE",
     "CHECK",
diff --git a/tools/testing/selftests/net/rds/config.sh b/tools/testing/selftests/net/rds/config.sh
index 2df2226310efe..bb854f49e464d 100755
--- a/tools/testing/selftests/net/rds/config.sh
+++ b/tools/testing/selftests/net/rds/config.sh
@@ -43,13 +43,14 @@ scripts/config "${FLAGS[@]}" --enable CONFIG_RDS_TCP
 
 if [ "$GENERATE_GCOV_REPORT" -eq 1 ]; then
 	# instrument RDS and only RDS
+	scripts/config "${FLAGS[@]}" --disable CONFIG_COVERAGE_NONE
 	scripts/config "${FLAGS[@]}" --enable CONFIG_GCOV_KERNEL
-	scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_ALL
-	scripts/config "${FLAGS[@]}" --enable GCOV_PROFILE_RDS
+	scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_ALL
+	scripts/config "${FLAGS[@]}" --enable COVERAGE_PROFILE_RDS
 else
 	scripts/config "${FLAGS[@]}" --disable CONFIG_GCOV_KERNEL
-	scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_ALL
-	scripts/config "${FLAGS[@]}" --disable GCOV_PROFILE_RDS
+	scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_ALL
+	scripts/config "${FLAGS[@]}" --disable COVERAGE_PROFILE_RDS
 fi
 
 # need network namespaces to run tests with veth network interfaces
diff --git a/tools/testing/selftests/net/rds/rds_run.sh b/tools/testing/selftests/net/rds/rds_run.sh
index cdf487ec97dcc..25bb65d048abe 100755
--- a/tools/testing/selftests/net/rds/rds_run.sh
+++ b/tools/testing/selftests/net/rds/rds_run.sh
@@ -70,16 +70,16 @@ check_gcov_env()
 # Check to see if the kconfig has the required configs to generate a coverage report
 check_gcov_conf()
 {
-	if ! grep -x "CONFIG_GCOV_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then
-		echo "# INFO: CONFIG_GCOV_PROFILE_RDS should be enabled for coverage reports"
+	if ! grep -x "CONFIG_COVERAGE_PROFILE_RDS=y" "$kconfig" > /dev/null 2>&1; then
+		echo "# INFO: CONFIG_COVERAGE_PROFILE_RDS should be enabled for coverage reports"
 		GENERATE_GCOV_REPORT=0
 	fi
 	if ! grep -x "CONFIG_GCOV_KERNEL=y" "$kconfig" > /dev/null 2>&1; then
 		echo "# INFO: CONFIG_GCOV_KERNEL should be enabled for coverage reports"
 		GENERATE_GCOV_REPORT=0
 	fi
-	if grep -x "CONFIG_GCOV_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then
-		echo "# INFO: CONFIG_GCOV_PROFILE_ALL should be disabled for coverage reports"
+	if grep -x "CONFIG_COVERAGE_PROFILE_ALL=y" "$kconfig" > /dev/null 2>&1; then
+		echo "# INFO: CONFIG_COVERAGE_PROFILE_ALL should be disabled for coverage reports"
 		GENERATE_GCOV_REPORT=0
 	fi
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 03/20] coverage: add a common debugfs interface
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexander Potapenko, Andrey Konovalov, Chuck Wolber,
	Dmitry Vyukov, kasan-dev, Marco Elver, Masami Hiramatsu,
	Peter Oberparleiter, Peter Zijlstra, Thomas Gleixner

Each coverage implementation creates its own top-level debugfs entry
with its own conventions: /sys/kernel/debug/gcov/ and
/sys/kernel/debug/kcov. Give them a common root,
/sys/kernel/debug/coverage/, which contains:

  backend  name of the selected code coverage measurement implementation
  reset    write to reset all coverage data of that implementation
  gcov     symlink to ../gcov when gcov is enabled
  kcov     symlink to ../kcov when KCOV is enabled

backend and reset are only present when a code coverage measurement
implementation is selected, so a kernel with only KCOV just has the kcov
symlink. The data formats are defined by the respective toolchains and
stay implementation specific. The existing paths are kept as they are,
since lcov, distribution tooling and syzkaller rely on them; the gcov
reset file now shares its implementation with the common one.

The common reset never touches KCOV, whose coverage buffers are owned
by the tasks that set them up.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 MAINTAINERS                |  8 +++++
 kernel/coverage/Makefile   |  1 +
 kernel/coverage/core.c     | 71 ++++++++++++++++++++++++++++++++++++++
 kernel/coverage/coverage.h | 22 ++++++++++++
 kernel/coverage/gcov/fs.c  | 21 ++++++++---
 kernel/coverage/kcov.c     |  3 ++
 6 files changed, 122 insertions(+), 4 deletions(-)
 create mode 100644 kernel/coverage/core.c
 create mode 100644 kernel/coverage/coverage.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 3cedde9ecaac3..573ac65304f7c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6589,6 +6589,14 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/media/coda.yaml
 F:	drivers/media/platform/chips-media/coda
 
+CODE COVERAGE
+M:	Sasha Levin <sashal@kernel.org>
+S:	Maintained
+F:	kernel/coverage/Kconfig
+F:	kernel/coverage/Makefile
+F:	kernel/coverage/core.c
+F:	kernel/coverage/coverage.h
+
 CODE OF CONDUCT
 M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 S:	Supported
diff --git a/kernel/coverage/Makefile b/kernel/coverage/Makefile
index a4cd8cc18d9bc..881fada442bd1 100644
--- a/kernel/coverage/Makefile
+++ b/kernel/coverage/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 
+obj-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV)) += core.o
 obj-$(CONFIG_GCOV_KERNEL) += gcov/
 obj-$(CONFIG_KCOV) += kcov.o
 
diff --git a/kernel/coverage/core.c b/kernel/coverage/core.c
new file mode 100644
index 0000000000000..f90dd78fce557
--- /dev/null
+++ b/kernel/coverage/core.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common debugfs interface of the kernel's code coverage implementations.
+ *
+ * /sys/kernel/debug/coverage/ is the root for all of them. The selected
+ * code coverage measurement implementation registers itself as the
+ * backend, which provides the "backend" and "reset" files.
+ */
+
+#include <linux/bug.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+
+#include "coverage.h"
+
+static struct dentry *coverage_root;
+static const struct coverage_backend *coverage_backend;
+
+struct dentry *coverage_debugfs_root(void)
+{
+	return coverage_root;
+}
+
+static int backend_show(struct seq_file *m, void *unused)
+{
+	seq_puts(m, coverage_backend->name);
+	if (coverage_backend->features)
+		seq_printf(m, " %s", coverage_backend->features);
+	seq_putc(m, '\n');
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(backend);
+
+static ssize_t reset_write(struct file *file, const char __user *buf,
+			   size_t len, loff_t *pos)
+{
+	coverage_backend->reset();
+	return len;
+}
+
+/* Allow reads so that a recursive copy of the directory doesn't fail. */
+static ssize_t reset_read(struct file *file, char __user *buf, size_t len,
+			  loff_t *pos)
+{
+	return 0;
+}
+
+static const struct file_operations reset_fops = {
+	.write	= reset_write,
+	.read	= reset_read,
+	.llseek	= noop_llseek,
+};
+
+void __init coverage_register_backend(const struct coverage_backend *backend)
+{
+	if (WARN_ON(coverage_backend))
+		return;
+
+	coverage_backend = backend;
+	debugfs_create_file("backend", 0444, coverage_root, NULL, &backend_fops);
+	debugfs_create_file("reset", 0600, coverage_root, NULL, &reset_fops);
+}
+
+static int __init coverage_init(void)
+{
+	coverage_root = debugfs_create_dir("coverage", NULL);
+	return 0;
+}
+fs_initcall(coverage_init);
diff --git a/kernel/coverage/coverage.h b/kernel/coverage/coverage.h
new file mode 100644
index 0000000000000..1b7e3ce25b686
--- /dev/null
+++ b/kernel/coverage/coverage.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _KERNEL_COVERAGE_H
+#define _KERNEL_COVERAGE_H
+
+struct dentry;
+
+/**
+ * struct coverage_backend - code coverage measurement implementation
+ * @name: name reported by /sys/kernel/debug/coverage/backend
+ * @features: optional space separated list of enabled features
+ * @reset: reset all coverage data collected so far
+ */
+struct coverage_backend {
+	const char *name;
+	const char *features;
+	void (*reset)(void);
+};
+
+struct dentry *coverage_debugfs_root(void);
+void coverage_register_backend(const struct coverage_backend *backend);
+
+#endif /* _KERNEL_COVERAGE_H */
diff --git a/kernel/coverage/gcov/fs.c b/kernel/coverage/gcov/fs.c
index 1d19b1be207a7..23d366dcfc2f7 100644
--- a/kernel/coverage/gcov/fs.c
+++ b/kernel/coverage/gcov/fs.c
@@ -28,6 +28,7 @@
 #include <linux/seq_file.h>
 #include <linux/mm.h>
 #include "gcov.h"
+#include "../coverage.h"
 
 /**
  * struct gcov_node - represents a debugfs entry
@@ -628,11 +629,10 @@ static struct gcov_node *get_child_by_name(struct gcov_node *parent,
 }
 
 /*
- * write() implementation for reset file. Reset all profiling data to zero
- * and remove nodes for which all associated object files are unloaded.
+ * Reset all profiling data to zero and remove nodes for which all associated
+ * object files are unloaded.
  */
-static ssize_t reset_write(struct file *file, const char __user *addr,
-			   size_t len, loff_t *pos)
+static void gcov_reset_all(void)
 {
 	struct gcov_node *node;
 
@@ -648,7 +648,13 @@ static ssize_t reset_write(struct file *file, const char __user *addr,
 		}
 	}
 	mutex_unlock(&node_lock);
+}
 
+/* write() implementation for reset file. */
+static ssize_t reset_write(struct file *file, const char __user *addr,
+			   size_t len, loff_t *pos)
+{
+	gcov_reset_all();
 	return len;
 }
 
@@ -859,6 +865,11 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
 	mutex_unlock(&node_lock);
 }
 
+static const struct coverage_backend gcov_backend = {
+	.name	= "gcov",
+	.reset	= gcov_reset_all,
+};
+
 /* Create debugfs entries. */
 static __init int gcov_fs_init(void)
 {
@@ -874,6 +885,8 @@ static __init int gcov_fs_init(void)
 	 */
 	debugfs_create_file("reset", 0600, root_node.dentry, NULL,
 			    &gcov_reset_fops);
+	debugfs_create_symlink("gcov", coverage_debugfs_root(), "../gcov");
+	coverage_register_backend(&gcov_backend);
 	/* Replay previous events to get our fs hierarchy up-to-date. */
 	gcov_enable_events();
 	return 0;
diff --git a/kernel/coverage/kcov.c b/kernel/coverage/kcov.c
index 35420f0ac524d..b6707f478f3d8 100644
--- a/kernel/coverage/kcov.c
+++ b/kernel/coverage/kcov.c
@@ -27,6 +27,8 @@
 #include <linux/log2.h>
 #include <asm/setup.h>
 
+#include "coverage.h"
+
 #define kcov_debug(fmt, ...) pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)
 
 /* Number of 64-bit words written per one comparison: */
@@ -1149,6 +1151,7 @@ static int __init kcov_init(void)
 	 * use of debugfs_create_file_unsafe() is actually safe here.
 	 */
 	debugfs_create_file_unsafe("kcov", 0600, NULL, NULL, &kcov_fops);
+	debugfs_create_symlink("kcov", coverage_debugfs_root(), "../kcov");
 
 #ifdef CONFIG_KCOV_SELFTEST
 	selftest();
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (2 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 03/20] coverage: add a common debugfs interface Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 13:51   ` Peter Zijlstra
  2026-10-28 11:59 ` [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
                   ` (15 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Bill Wendling, Chuck Wolber, Justin Stitt, llvm, Marco Elver,
	Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
	Nick Desaulniers, Peter Zijlstra, Thomas Gleixner

noinstr code may only call other noinstr functions and __always_inline
functions. gcov and Clang's source-based code coverage insert their
instrumentation before inlining, so the instrumentation of an
__always_inline function ends up in every noinstr function it gets
inlined into. With Clang's source-based coverage on x86, 2500 of the
3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
does the same. Since these are plain memory updates rather than calls,
objtool's noinstr validation doesn't notice them.

KCOV doesn't have this problem, as it instruments the code after
inlining and checks the attributes of the function that the code ends
up in.

Make __always_inline imply __no_profile when a code coverage
measurement implementation is enabled, so that no noinstr code carries
coverage instrumentation. The cost is that __always_inline functions
don't show up in the coverage data at all, including at their callers
outside of noinstr code. For an x86_64 configuration built with Clang 22
and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
conditions (1.2%) from the coverage data.

Leave unoptimized objects alone: GCC refuses to inline an
__always_inline function across a no_profile_instrument_function
mismatch at -O0, and those objects don't contain noinstr code.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/compiler_types.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f1390079..c3298d29a9425 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -246,6 +246,19 @@ struct ftrace_likely_data {
  */
 #define __inline__ inline
 
+/*
+ * noinstr code may only call noinstr and __always_inline functions. Code
+ * coverage instrumentation is added before inlining, so the counters of an
+ * __always_inline function would end up in the noinstr code it gets inlined
+ * into: don't instrument __always_inline functions. GCC refuses to inline
+ * across a no_profile_instrument_function mismatch without optimization, so
+ * leave the rare unoptimized objects alone; they don't contain noinstr code.
+ */
+#if defined(CONFIG_COVERAGE_KERNEL) && defined(__OPTIMIZE__)
+#undef __always_inline
+#define __always_inline inline __attribute__((__always_inline__)) __no_profile
+#endif
+
 /*
  * GCC does not warn about unused static inline functions for -Wunused-function.
  * Suppress the warning in clang as well by using __maybe_unused, but enable it
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (3 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 13:52   ` Peter Zijlstra
  2026-10-28 11:59 ` [RFC PATCH 06/20] lockdep: " Sasha Levin
                   ` (14 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Chuck Wolber, Frederic Weisbecker, Marco Elver, Masami Hiramatsu,
	Paul E. McKenney, Peter Zijlstra, Thomas Gleixner

The !CONFIG_CONTEXT_TRACKING_USER versions of user_enter_irqoff() and
user_exit_irqoff(), and the !CONFIG_CONTEXT_TRACKING_IDLE versions of
ct_idle_enter() and ct_idle_exit(), are plain static inline functions,
while their real counterparts are __always_inline (user_*_irqoff()) or
noinstr (ct_idle_*()) functions. They are called from noinstr code, so
when they aren't inlined that's a call leaving the noinstr section. This
happens with code coverage instrumentation, which makes the compiler
refuse to inline an instrumented function into a noinstr one:

  vmlinux.o: warning: objtool: do_syscall_64+0x81: call to user_exit_irqoff() leaves .noinstr.text section

Make the stubs __always_inline as well.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/context_tracking.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h
index af9fe87a09225..d0672916fc1d3 100644
--- a/include/linux/context_tracking.h
+++ b/include/linux/context_tracking.h
@@ -93,8 +93,8 @@ static __always_inline bool context_tracking_guest_exit(void)
 #else
 static inline void user_enter(void) { }
 static inline void user_exit(void) { }
-static inline void user_enter_irqoff(void) { }
-static inline void user_exit_irqoff(void) { }
+static __always_inline void user_enter_irqoff(void) { }
+static __always_inline void user_exit_irqoff(void) { }
 static inline int exception_enter(void) { return 0; }
 static inline void exception_exit(enum ctx_state prev_ctx) { }
 static inline int ct_state(void) { return -1; }
@@ -162,8 +162,8 @@ static __always_inline void warn_rcu_exit(bool rcu)
 }
 
 #else
-static inline void ct_idle_enter(void) { }
-static inline void ct_idle_exit(void) { }
+static __always_inline void ct_idle_enter(void) { }
+static __always_inline void ct_idle_exit(void) { }
 
 static __always_inline bool warn_rcu_enter(void) { return false; }
 static __always_inline void warn_rcu_exit(bool rcu) { }
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 06/20] lockdep: Always inline the empty stubs called from noinstr code
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (4 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 07/20] perf: Always inline the empty perf_lopwr_cb() stub Sasha Levin
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Boqun Feng, Chuck Wolber, Ingo Molnar, Marco Elver,
	Masami Hiramatsu, Peter Zijlstra, Thomas Gleixner, Waiman Long,
	Will Deacon

With !CONFIG_PROVE_LOCKING, lockdep_hardirqs_on() and
lockdep_hardirqs_off() are empty static inline functions, and so are
lockdep_off() and lockdep_on() with !CONFIG_LOCKDEP, while the real
versions are noinstr functions and macros respectively. They are called
from noinstr code, which the compiler won't inline them into when
building with code coverage instrumentation:

  vmlinux.o: warning: objtool: do_syscall_64+0x7b: call to lockdep_hardirqs_off() leaves .noinstr.text section
  vmlinux.o: warning: objtool: irqentry_nmi_enter+0x4: call to lockdep_off() leaves .noinstr.text section

Make the stubs __always_inline, along with the neighbouring
lockdep_hardirqs_on_prepare() stub for consistency.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/irqflags.h | 6 +++---
 include/linux/lockdep.h  | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
index 57b074e0cfbbb..e06004255e3d8 100644
--- a/include/linux/irqflags.h
+++ b/include/linux/irqflags.h
@@ -32,9 +32,9 @@ struct task_struct;
 #else
   static inline void lockdep_softirqs_on(unsigned long ip) { }
   static inline void lockdep_softirqs_off(unsigned long ip) { }
-  static inline void lockdep_hardirqs_on_prepare(void) { }
-  static inline void lockdep_hardirqs_on(unsigned long ip) { }
-  static inline void lockdep_hardirqs_off(unsigned long ip) { }
+  static __always_inline void lockdep_hardirqs_on_prepare(void) { }
+  static __always_inline void lockdep_hardirqs_on(unsigned long ip) { }
+  static __always_inline void lockdep_hardirqs_off(unsigned long ip) { }
   static inline void lockdep_cleanup_dead_cpu(unsigned int cpu,
 					      struct task_struct *idle) {}
 #endif
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index a6451ecbbe9a0..85b864880d069 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -324,11 +324,11 @@ static inline void lockdep_init_task(struct task_struct *task)
 {
 }
 
-static inline void lockdep_off(void)
+static __always_inline void lockdep_off(void)
 {
 }
 
-static inline void lockdep_on(void)
+static __always_inline void lockdep_on(void)
 {
 }
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 07/20] perf: Always inline the empty perf_lopwr_cb() stub
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (5 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 06/20] lockdep: " Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 08/20] rseq: Always inline the empty rseq_note_user_irq_entry() stub Sasha Levin
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Adrian Hunter, Alexander Shishkin, Arnaldo Carvalho de Melo,
	Chuck Wolber, Ian Rogers, Ingo Molnar, James Clark, Jiri Olsa,
	linux-perf-users, Marco Elver, Mark Rutland, Masami Hiramatsu,
	Namhyung Kim, Peter Zijlstra, Thomas Gleixner

perf_lopwr_cb() is called from the noinstr acpi_idle_do_entry(). The
x86 implementation is __always_inline, but the empty fallback is a
plain static inline function, which the compiler won't inline into
noinstr code when building with code coverage instrumentation:

  vmlinux.o: warning: objtool: acpi_idle_do_entry+0x4: call to perf_lopwr_cb() leaves .noinstr.text section

Make the stub __always_inline as well.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/perf_event.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index eb3f7bab498ca..dcda92526b78c 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -2137,7 +2137,7 @@ typedef int (perf_snapshot_branch_stack_t)(struct perf_branch_entry *entries,
 DECLARE_STATIC_CALL(perf_snapshot_branch_stack, perf_snapshot_branch_stack_t);
 
 #ifndef PERF_NEEDS_LOPWR_CB
-static inline void perf_lopwr_cb(bool mode)
+static __always_inline void perf_lopwr_cb(bool mode)
 {
 }
 #endif
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 08/20] rseq: Always inline the empty rseq_note_user_irq_entry() stub
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (6 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 07/20] perf: Always inline the empty perf_lopwr_cb() stub Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 09/20] x86: Always inline empty stubs called from noinstr code Sasha Levin
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Chuck Wolber, Marco Elver, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Thomas Gleixner

rseq_note_user_irq_entry() is called from noinstr entry code. The
CONFIG_RSEQ version is __always_inline, but the !CONFIG_RSEQ stub is a
plain static inline function, which the compiler won't inline into
noinstr code when building with code coverage instrumentation.

Make the stub __always_inline as well.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/linux/rseq_entry.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h
index 31ce349ed42ce..e31f9e625d03d 100644
--- a/include/linux/rseq_entry.h
+++ b/include/linux/rseq_entry.h
@@ -759,7 +759,7 @@ static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs)
 		__rseq_debug_syscall_return(regs);
 }
 #else /* CONFIG_RSEQ */
-static inline void rseq_note_user_irq_entry(void) { }
+static __always_inline void rseq_note_user_irq_entry(void) { }
 static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
 {
 	return false;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 09/20] x86: Always inline empty stubs called from noinstr code
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (7 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 08/20] rseq: Always inline the empty rseq_note_user_irq_entry() stub Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors Sasha Levin
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Borislav Petkov, Chuck Wolber, Dave Hansen, H. Peter Anvin,
	Ingo Molnar, Marco Elver, Masami Hiramatsu, Peter Zijlstra,
	Thomas Gleixner, x86

The !CONFIG_AMD_MEM_ENCRYPT stubs of sev_es_ist_enter(),
sev_es_ist_exit() and sev_es_nmi_complete(), and the !CONFIG_CPU_SUP_AMD
stub of amd_clear_divider(), are plain static inline functions, while
the real implementations are __always_inline. They are called from
noinstr code, which the compiler won't inline them into when building
with code coverage instrumentation:

  vmlinux.o: warning: objtool: exc_nmi+0x7: call to sev_es_nmi_complete() leaves .noinstr.text section

Make the stubs __always_inline as well.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/include/asm/processor.h | 2 +-
 arch/x86/include/asm/sev.h       | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 77488ff5eb8c7..f7f28c8a09ed8 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -735,7 +735,7 @@ static __always_inline void amd_clear_divider(void)
 
 extern void amd_check_microcode(void);
 #else
-static inline void amd_clear_divider(void)		{ }
+static __always_inline void amd_clear_divider(void)	{ }
 static inline void amd_check_microcode(void)		{ }
 #endif
 
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index c7ea9845351e3..52251ce42b666 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -601,10 +601,10 @@ static inline void sev_evict_cache(void *va, int npages)
 #else	/* !CONFIG_AMD_MEM_ENCRYPT */
 
 #define snp_vmpl 0
-static inline void sev_es_ist_enter(struct pt_regs *regs) { }
-static inline void sev_es_ist_exit(void) { }
+static __always_inline void sev_es_ist_enter(struct pt_regs *regs) { }
+static __always_inline void sev_es_ist_exit(void) { }
 static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; }
-static inline void sev_es_nmi_complete(void) { }
+static __always_inline void sev_es_nmi_complete(void) { }
 static inline int sev_es_efi_map_ghcbs_cas(pgd_t *pgd) { return 0; }
 static inline void sev_enable(struct boot_params *bp) { }
 static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate) { return 0; }
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (8 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 09/20] x86: Always inline empty stubs called from noinstr code Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Borislav Petkov, Chuck Wolber, Dave Hansen, H. Peter Anvin,
	Ingo Molnar, kvm, Marco Elver, Masami Hiramatsu, Paolo Bonzini,
	Peter Zijlstra, Sean Christopherson, Thomas Gleixner, x86

The GHCB accessors are used from noinstr code, such as
__sev_es_nmi_complete(), but use test_bit() and __set_bit(), which
call the KASAN and KCSAN checks. arch/x86/coco/sev/Makefile disables
KASAN and KCSAN for noinstr.o, which turns those checks into empty
stubs, but they still have to be inlined. Instrumentation such as gcov
can prevent that and leave calls to kasan_check_write() behind in
noinstr code.

Use the arch_ bitops, which aren't instrumented, as is done elsewhere
for noinstr code.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/include/asm/svm.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 83fd49b26a4b1..9881c702a8f01 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -674,8 +674,8 @@ static inline void __unused_size_checks(void)
 #define DEFINE_GHCB_ACCESSORS(field)						\
 	static __always_inline bool ghcb_##field##_is_valid(const struct ghcb *ghcb) \
 	{									\
-		return test_bit(GHCB_BITMAP_IDX(field),				\
-				(unsigned long *)&ghcb->save.valid_bitmap);	\
+		return arch_test_bit(GHCB_BITMAP_IDX(field),			\
+				     (unsigned long *)&ghcb->save.valid_bitmap); \
 	}									\
 										\
 	static __always_inline u64 ghcb_get_##field(struct ghcb *ghcb)		\
@@ -690,8 +690,8 @@ static inline void __unused_size_checks(void)
 										\
 	static __always_inline void ghcb_set_##field(struct ghcb *ghcb, u64 value) \
 	{									\
-		__set_bit(GHCB_BITMAP_IDX(field),				\
-			  (unsigned long *)&ghcb->save.valid_bitmap);		\
+		arch___set_bit(GHCB_BITMAP_IDX(field),				\
+			       (unsigned long *)&ghcb->save.valid_bitmap);	\
 		ghcb->save.field = value;					\
 	}
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (9 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 14:11   ` Peter Zijlstra
  2026-10-28 11:59 ` [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Bill Wendling, Chuck Wolber, Josh Poimboeuf, Justin Stitt, llvm,
	Marco Elver, Masami Hiramatsu, Nathan Chancellor,
	Nick Desaulniers, Peter Zijlstra, Thomas Gleixner

gcov and Clang's source-based code coverage update their counters
inline, rather than by calling into a runtime like KCOV does, so
coverage instrumentation in noinstr code goes unnoticed by the noinstr
validation of call destinations.

Flag instructions in noinstr code, outside of instrumentation_begin()
and instrumentation_end() sections, that reference coverage counters:
GCC's __gcov<N>.* counters (including the MC/DC counters), Clang's gcov
__llvm_gcov_ctr* counters, and the __llvm_prf_cnts and __llvm_prf_bits
sections of Clang's source-based code coverage.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 464f6c9d9ff0b..6382d2a44ef2b 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4,6 +4,7 @@
  */
 
 #define _GNU_SOURCE /* memmem() */
+#include <ctype.h>
 #include <fnmatch.h>
 #include <string.h>
 #include <stdlib.h>
@@ -3507,6 +3508,52 @@ static inline bool noinstr_call_dest(struct objtool_file *file,
 	return false;
 }
 
+/*
+ * Code coverage instrumentation (gcov, Clang's source-based coverage) updates
+ * its counters inline rather than through calls, so it can't be caught by
+ * checking the call destinations of noinstr code.
+ */
+static bool is_coverage_counter(struct symbol *sym)
+{
+	if (!strcmp(sym->sec->name, "__llvm_prf_cnts") ||
+	    !strcmp(sym->sec->name, "__llvm_prf_bits"))
+		return true;
+
+	return (strstarts(sym->name, "__gcov") && isdigit((unsigned char)sym->name[6])) ||
+	       strstarts(sym->name, "__llvm_gcov_ctr");
+}
+
+static bool insn_updates_coverage(struct objtool_file *file,
+				  struct instruction *insn)
+{
+	struct symbol *sym;
+	struct reloc *reloc;
+
+	for (reloc = insn_reloc(file, insn);
+	     reloc;
+	     reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+					      reloc_offset(reloc) + 1,
+					      insn->offset + insn->len -
+					      (reloc_offset(reloc) + 1))) {
+
+		sym = reloc->sym;
+		if (!sym->sec)
+			continue;
+
+		if (is_sec_sym(sym)) {
+			sym = find_symbol_containing(sym->sec,
+						     arch_insn_adjusted_addend(insn, reloc));
+			if (!sym)
+				sym = reloc->sym;
+		}
+
+		if (is_coverage_counter(sym))
+			return true;
+	}
+
+	return false;
+}
+
 static int validate_call(struct objtool_file *file,
 			 struct instruction *insn,
 			 struct insn_state *state)
@@ -3680,6 +3727,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	if (statep->noinstr)
 		statep->instr += insn->instr;
 
+	if (statep->noinstr && statep->instr <= 0 &&
+	    insn_updates_coverage(file, insn)) {
+		WARN_INSN(insn, "coverage counter update in .noinstr.text section");
+		return 1;
+	}
+
 	if (insn->hint) {
 		if (insn->restore) {
 			struct instruction *save_insn, *i;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (10 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 13/20] x86/entry: Stop excluding the entry code from KCOV Sasha Levin
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Bill Wendling, Borislav Petkov, Chuck Wolber, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, Justin Stitt, llvm, Marco Elver,
	Masami Hiramatsu, Nathan Chancellor, Nick Desaulniers,
	Peter Zijlstra, Thomas Gleixner, x86

noinstr.o was excluded from gcov after Clang's gcov instrumentation
left calls to kasan_check_write() in __sev_es_nmi_complete(), and
because any coverage instrumentation would violate noinstr.

The GHCB accessors no longer call the KASAN and KCSAN checks, and the
noinstr attribute is now sufficient to keep coverage instrumentation
out of noinstr code, which objtool verifies. Drop the now redundant
exclusion and rely on the attributes, like the rest of the kernel's
noinstr code does.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/coco/sev/Makefile | 2 --
 1 file changed, 2 deletions(-)

diff --git a/arch/x86/coco/sev/Makefile b/arch/x86/coco/sev/Makefile
index be4785d766ec1..fb8ffedfc8b0c 100644
--- a/arch/x86/coco/sev/Makefile
+++ b/arch/x86/coco/sev/Makefile
@@ -8,5 +8,3 @@ UBSAN_SANITIZE_noinstr.o	:= n
 # GCC may fail to respect __no_sanitize_address or __no_kcsan when inlining
 KASAN_SANITIZE_noinstr.o	:= n
 KCSAN_SANITIZE_noinstr.o	:= n
-
-COVERAGE_PROFILE_noinstr.o		:= n
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 13/20] x86/entry: Stop excluding the entry code from KCOV
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (11 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 14/20] coverage: add Clang's source-based code coverage Sasha Levin
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Andy Lutomirski, Borislav Petkov, Chuck Wolber, Dave Hansen,
	H. Peter Anvin, Ingo Molnar, Marco Elver, Masami Hiramatsu,
	Peter Zijlstra, Thomas Gleixner, x86

The x86 entry code has been excluded from KCOV since commit 20355e5f73a7
("x86/entry: Exclude low level entry code from sanitizing"), when
noinstr couldn't keep KCOV out of noinstr functions.

Since commit 540540d06e9d ("kcov: add __no_sanitize_coverage to fix
noinstr for all architectures"), noinstr implies __no_sanitize_coverage,
and on x86 objtool removes any KCOV call left in noinstr code. KCOV
instruments the code after inlining, so __always_inline helpers don't
carry it into noinstr functions either. What remains instrumentable in
arch/x86/entry/ is the system call dispatch and the ptrace system call
hooks, which run in task context.

Drop the exclusion, so that noinstr alone keeps KCOV out of the x86
entry code, like it does for the other coverage implementations. A
traced read(-1) now reports 18 instead of 12 PCs, the additional ones
being the system call dispatch.

The generic entry code has to stay excluded, but for a different reason
than its Makefile gives: parts of the interrupt exit code, such as
irqentry_exit_cond_resched(), run after leaving hardirq context, where
KCOV adds their coverage to the trace of the interrupted task. Without
the exclusion, they show up in the traces of system calls interrupted
by the timer. Document that instead.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/entry/Makefile | 1 -
 kernel/entry/Makefile   | 3 +++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/x86/entry/Makefile b/arch/x86/entry/Makefile
index 83b4762d6ecba..cd24c28358f97 100644
--- a/arch/x86/entry/Makefile
+++ b/arch/x86/entry/Makefile
@@ -5,7 +5,6 @@
 
 KASAN_SANITIZE := n
 UBSAN_SANITIZE := n
-KCOV_INSTRUMENT := n
 
 CFLAGS_REMOVE_syscall_32.o	= $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_syscall_64.o	= $(CC_FLAGS_FTRACE)
diff --git a/kernel/entry/Makefile b/kernel/entry/Makefile
index f220bae86b123..2c837b7513c71 100644
--- a/kernel/entry/Makefile
+++ b/kernel/entry/Makefile
@@ -4,6 +4,9 @@
 # as long as these things cannot be disabled per function.
 KASAN_SANITIZE := n
 UBSAN_SANITIZE := n
+
+# Parts of the interrupt exit code run after leaving hardirq context, where KCOV
+# would add their coverage to the trace of the interrupted task.
 KCOV_INSTRUMENT := n
 
 # Branch profiling isn't noinstr-safe
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 14/20] coverage: add Clang's source-based code coverage
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (12 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 13/20] x86/entry: Stop excluding the entry code from KCOV Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 15/20] x86: Enable " Sasha Levin
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
	Arnd Bergmann, Bill Wendling, Chuck Wolber, Dmitry Vyukov,
	Justin Stitt, kasan-dev, linux-arch, linux-kbuild, llvm,
	Marco Elver, Masami Hiramatsu, Nathan Chancellor,
	Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
	Peter Zijlstra, Thomas Gleixner

Add Clang's Source-based Code Coverage [1] as a second code coverage
measurement implementation, next to gcov.

gcov works at the object code level, which may better reflect actual
execution, but its measurements can lose source level precision when
compiler optimizations are enabled, as they always are for the kernel.
In the following example from drivers/firmware/dmi_scan.c, measured on
v6.11, gcov reports six branch outcomes for an expression with four
conditions:

        5: 1068:	if (s == e || *e != '/' || !month || month > 12) {
branch  0 taken 5 (fallthrough)
branch  1 taken 0
branch  2 taken 5 (fallthrough)
branch  3 taken 0
branch  4 taken 0 (fallthrough)
branch  5 taken 5

Clang's Source-based Code Coverage instruments the code in the compiler
front end, so the coverage data maps back to the source code exactly,
regardless of optimizations, and reports each condition:

 1068|      5|	if (s == e || *e != '/' || !month || month > 12) {
  ------------------
  |  Branch (1068:6): [True: 0, False: 5]
  |  Branch (1068:16): [True: 0, False: 5]
  |  Branch (1068:29): [True: 0, False: 5]
  |  Branch (1068:39): [True: 0, False: 5]
  ------------------

It is selected with CONFIG_LLVM_COV_KERNEL in the "Code coverage
measurement" choice, uses the common COVERAGE_PROFILE annotations,
noinstr handling and reset, and exports the raw profile of the kernel
image in /sys/kernel/debug/coverage/llvm/profraw. The raw profile
formats of Clang 19 to 22 (version 10) and of Clang 23 and later
(version 11) are supported. Counters are updated atomically, like gcov
does when the compiler allows it.

The data is exported from sections that the architecture's linker script
places with LLVM_COV_DATA, so only code built into the kernel image is
instrumented; modules are left alone.

This reuses the debugfs interface and raw profile serialization of
earlier work by Sami Tolvanen et al. [2], without the parts specific to
profile guided optimization.

Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html [1]
Link: https://lore.kernel.org/lkml/20210407211704.367039-1-morbo@google.com/ [2]
Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 MAINTAINERS                       |   2 +
 Makefile                          |   1 +
 include/asm-generic/vmlinux.lds.h |  36 ++++++
 kernel/coverage/Kconfig           |  33 ++++-
 kernel/coverage/Makefile          |   1 +
 kernel/coverage/llvm/Makefile     |   4 +
 kernel/coverage/llvm/fs.c         | 201 ++++++++++++++++++++++++++++++
 kernel/coverage/llvm/llvm-cov.h   | 139 +++++++++++++++++++++
 scripts/Makefile.lib              |   7 +-
 scripts/Makefile.llvm-cov         |   6 +
 10 files changed, 427 insertions(+), 3 deletions(-)
 create mode 100644 kernel/coverage/llvm/Makefile
 create mode 100644 kernel/coverage/llvm/fs.c
 create mode 100644 kernel/coverage/llvm/llvm-cov.h
 create mode 100644 scripts/Makefile.llvm-cov

diff --git a/MAINTAINERS b/MAINTAINERS
index 573ac65304f7c..506f25cfc3e85 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6596,6 +6596,8 @@ F:	kernel/coverage/Kconfig
 F:	kernel/coverage/Makefile
 F:	kernel/coverage/core.c
 F:	kernel/coverage/coverage.h
+F:	kernel/coverage/llvm/
+F:	scripts/Makefile.llvm-cov
 
 CODE OF CONDUCT
 M:	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Makefile b/Makefile
index cb34eb7e1b790..8f60f7b99b67a 100644
--- a/Makefile
+++ b/Makefile
@@ -1203,6 +1203,7 @@ include-$(CONFIG_KCSAN)		+= scripts/Makefile.kcsan
 include-$(CONFIG_KMSAN)		+= scripts/Makefile.kmsan
 include-$(CONFIG_UBSAN)		+= scripts/Makefile.ubsan
 include-$(CONFIG_GCOV_KERNEL)	+= scripts/Makefile.gcov
+include-$(CONFIG_LLVM_COV_KERNEL) += scripts/Makefile.llvm-cov
 include-$(CONFIG_KCOV)		+= scripts/Makefile.kcov
 include-$(CONFIG_RANDSTRUCT)	+= scripts/Makefile.randstruct
 include-$(CONFIG_KSTACK_ERASE)	+= scripts/Makefile.kstack_erase
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3eb732942d699..840899b604336 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -359,6 +359,42 @@
 #define THERMAL_TABLE(name)
 #endif
 
+#ifdef CONFIG_LLVM_COV_KERNEL
+#define LLVM_COV_DATA							\
+	__llvm_prf_data : AT(ADDR(__llvm_prf_data) - LOAD_OFFSET) {	\
+		BOUNDED_SECTION_POST_LABEL(__llvm_prf_data,		\
+					   __llvm_prf_data,		\
+					   _start, _end)		\
+	}								\
+	__llvm_prf_cnts : AT(ADDR(__llvm_prf_cnts) - LOAD_OFFSET) {	\
+		BOUNDED_SECTION_POST_LABEL(__llvm_prf_cnts,		\
+					   __llvm_prf_cnts,		\
+					   _start, _end)		\
+	}								\
+	__llvm_prf_names : AT(ADDR(__llvm_prf_names) - LOAD_OFFSET) {	\
+		BOUNDED_SECTION_POST_LABEL(__llvm_prf_names,		\
+					   __llvm_prf_names,		\
+					   _start, _end)		\
+	}								\
+	__llvm_prf_bits : AT(ADDR(__llvm_prf_bits) - LOAD_OFFSET) {	\
+		BOUNDED_SECTION_POST_LABEL(__llvm_prf_bits,		\
+					   __llvm_prf_bits,		\
+					   _start, _end)		\
+	}								\
+	__llvm_covfun : AT(ADDR(__llvm_covfun) - LOAD_OFFSET) {		\
+		BOUNDED_SECTION_POST_LABEL(__llvm_covfun,		\
+					   __llvm_covfun,		\
+					   _start, _end)		\
+	}								\
+	__llvm_covmap : AT(ADDR(__llvm_covmap) - LOAD_OFFSET) {		\
+		BOUNDED_SECTION_POST_LABEL(__llvm_covmap,		\
+					   __llvm_covmap,		\
+					   _start, _end)		\
+	}
+#else
+#define LLVM_COV_DATA
+#endif
+
 #define KERNEL_DTB()							\
 	STRUCT_ALIGN();							\
 	__dtb_start = .;						\
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index 62a6fa94bd308..acfcce23e4c97 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -4,6 +4,15 @@ menu "Code coverage"
 config ARCH_HAS_GCOV_PROFILE_ALL
 	def_bool n
 
+config ARCH_HAS_LLVM_COV
+	bool
+	help
+	  An architecture should select this when its linker script places
+	  the sections of Clang's source-based code coverage (LLVM_COV_DATA).
+
+config ARCH_HAS_LLVM_COV_PROFILE_ALL
+	bool
+
 choice
 	prompt "Code coverage measurement"
 	default GCOV_KERNEL if COMPILE_TEST
@@ -49,9 +58,30 @@ config GCOV_KERNEL
 	  and its modules is exported in gcov's format in /sys/kernel/debug/gcov,
 	  see Documentation/dev-tools/gcov.rst.
 
+config LLVM_COV_KERNEL
+	bool "Clang's source-based code coverage"
+	depends on CC_IS_CLANG && CLANG_VERSION >= 190000
+	depends on DEBUG_FS
+	depends on ARCH_HAS_LLVM_COV
+	depends on !ARCH_WANTS_NO_INSTR || CC_HAS_NO_PROFILE_FN_ATTR
+	help
+	  This option enables Clang's Source-based Code Coverage. It
+	  instruments the code in the compiler's front end, so that coverage
+	  data maps back to the source code exactly, regardless of the
+	  optimizations applied.
+
+	  The raw profile is exported in /sys/kernel/debug/coverage/llvm/profraw.
+	  Convert it with "llvm-profdata merge" and analyze it with "llvm-cov"
+	  against vmlinux, using the LLVM version that built the kernel.
+
+	  Only code built into the kernel image is instrumented.
+
 endchoice
 
 config COVERAGE_KERNEL
+	def_bool GCOV_KERNEL || LLVM_COV_KERNEL
+
+config COVERAGE_MODULES
 	def_bool GCOV_KERNEL
 
 config GCOV_PROFILE_ALL
@@ -64,7 +94,8 @@ config GCOV_PROFILE_ALL
 config COVERAGE_PROFILE_ALL
 	bool "Profile entire kernel"
 	depends on !COMPILE_TEST
-	depends on GCOV_KERNEL && ARCH_HAS_GCOV_PROFILE_ALL
+	depends on (GCOV_KERNEL && ARCH_HAS_GCOV_PROFILE_ALL) || \
+		   (LLVM_COV_KERNEL && ARCH_HAS_LLVM_COV_PROFILE_ALL)
 	default GCOV_PROFILE_ALL
 	help
 	  This option activates coverage instrumentation for the entire kernel.
diff --git a/kernel/coverage/Makefile b/kernel/coverage/Makefile
index 881fada442bd1..d783272d9974d 100644
--- a/kernel/coverage/Makefile
+++ b/kernel/coverage/Makefile
@@ -2,6 +2,7 @@
 
 obj-$(or $(CONFIG_COVERAGE_KERNEL),$(CONFIG_KCOV)) += core.o
 obj-$(CONFIG_GCOV_KERNEL) += gcov/
+obj-$(CONFIG_LLVM_COV_KERNEL) += llvm/
 obj-$(CONFIG_KCOV) += kcov.o
 
 # Don't self-instrument.
diff --git a/kernel/coverage/llvm/Makefile b/kernel/coverage/llvm/Makefile
new file mode 100644
index 0000000000000..7090171b070eb
--- /dev/null
+++ b/kernel/coverage/llvm/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+COVERAGE_PROFILE := n
+
+obj-y := fs.o
diff --git a/kernel/coverage/llvm/fs.c b/kernel/coverage/llvm/fs.c
new file mode 100644
index 0000000000000..e90c325daeba3
--- /dev/null
+++ b/kernel/coverage/llvm/fs.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019	Sami Tolvanen <samitolvanen@google.com>, Google, Inc.
+ * Copyright (C) 2024	Jinghao Jia   <jinghao7@illinois.edu>,   UIUC
+ * Copyright (C) 2024	Wentao Zhang  <wentaoz5@illinois.edu>,   UIUC
+ */
+
+#define pr_fmt(fmt)	"llvm-cov: " fmt
+
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+
+#include "../coverage.h"
+#include "llvm-cov.h"
+
+/*
+ * Serializes taking a snapshot of the profile data against resetting it. The
+ * instrumented code updates the counters and bitmaps without taking it, so a
+ * snapshot is not atomic with respect to code running on other CPUs.
+ */
+static DEFINE_MUTEX(llvm_cov_mutex);
+
+struct llvm_cov_private_data {
+	char *buffer;
+	unsigned long size;
+};
+
+/*
+ * Raw profile data format:
+ * https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format. Only the
+ * information relevant to Source-based Code Coverage is populated. Binary IDs,
+ * continuous mode, single-byte mode, value profiling, type profiling and
+ * uniform counters are not implemented.
+ */
+
+static void llvm_cov_fill_raw_profile_header(void **buffer)
+{
+	struct __llvm_profile_header *header = *buffer;
+
+	header->magic = INSTR_PROF_RAW_MAGIC_64;
+	header->version = INSTR_PROF_RAW_VERSION;
+	header->binary_ids_size = 0;
+	header->num_data = __llvm_prf_data_count();
+	header->padding_bytes_before_counters = 0;
+	header->num_counters = __llvm_prf_cnts_count();
+	header->padding_bytes_after_counters =
+		__llvm_prf_get_padding(__llvm_prf_cnts_size());
+	header->num_bitmap_bytes = __llvm_prf_bits_size();
+	header->padding_bytes_after_bitmap_bytes =
+		__llvm_prf_get_padding(__llvm_prf_bits_size());
+#if CONFIG_CLANG_VERSION >= 230000
+	header->num_uniform_counters = 0;
+	header->padding_bytes_after_uniform_counters = 0;
+	header->uniform_counters_delta = 0;
+#endif
+	header->names_size = __llvm_prf_names_size();
+	header->counters_delta = (u64)__llvm_prf_cnts_start -
+				 (u64)__llvm_prf_data_start;
+	header->bitmap_delta   = (u64)__llvm_prf_bits_start -
+				 (u64)__llvm_prf_data_start;
+	header->names_delta    = (u64)__llvm_prf_names_start;
+	header->num_v_tables = 0;
+	header->v_names_size = 0;
+	header->value_kind_last = IPVK_LAST;
+
+	*buffer += sizeof(*header);
+}
+
+/*
+ * Copy the source into the buffer, incrementing the pointer into buffer in the
+ * process.
+ */
+static void llvm_cov_copy_section_to_buffer(void **buffer, void *src,
+					    unsigned long size)
+{
+	memcpy(*buffer, src, size);
+	*buffer += size;
+}
+
+static unsigned long llvm_cov_get_raw_profile_size(void)
+{
+	return sizeof(struct __llvm_profile_header) +
+	       __llvm_prf_data_size() +
+	       __llvm_prf_cnts_size() +
+	       __llvm_prf_get_padding(__llvm_prf_cnts_size()) +
+	       __llvm_prf_bits_size() +
+	       __llvm_prf_get_padding(__llvm_prf_bits_size()) +
+	       __llvm_prf_names_size() +
+	       __llvm_prf_get_padding(__llvm_prf_names_size());
+}
+
+/*
+ * Serialize in-memory data into a format LLVM tools can understand
+ * (https://llvm.org/docs/InstrProfileFormat.html#raw-profile-format)
+ */
+static int llvm_cov_serialize_raw_profile(struct llvm_cov_private_data *p)
+{
+	void *buffer;
+
+	p->size = llvm_cov_get_raw_profile_size();
+	p->buffer = vzalloc(p->size);
+	if (!p->buffer)
+		return -ENOMEM;
+
+	buffer = p->buffer;
+
+	llvm_cov_fill_raw_profile_header(&buffer);
+	llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_data_start,
+					__llvm_prf_data_size());
+	llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_cnts_start,
+					__llvm_prf_cnts_size());
+	buffer += __llvm_prf_get_padding(__llvm_prf_cnts_size());
+	llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_bits_start,
+					__llvm_prf_bits_size());
+	buffer += __llvm_prf_get_padding(__llvm_prf_bits_size());
+	llvm_cov_copy_section_to_buffer(&buffer, __llvm_prf_names_start,
+					__llvm_prf_names_size());
+
+	return 0;
+}
+
+/* open() implementation for llvm-cov data file. */
+static int llvm_cov_open(struct inode *inode, struct file *file)
+{
+	struct llvm_cov_private_data *data;
+	int err;
+
+	data = kzalloc_obj(*data);
+	if (!data)
+		return -ENOMEM;
+
+	mutex_lock(&llvm_cov_mutex);
+	err = llvm_cov_serialize_raw_profile(data);
+	mutex_unlock(&llvm_cov_mutex);
+
+	if (err) {
+		kfree(data);
+		return err;
+	}
+
+	file->private_data = data;
+	return 0;
+}
+
+/* read() implementation for llvm-cov data file. */
+static ssize_t llvm_cov_read(struct file *file, char __user *buf, size_t count,
+			     loff_t *ppos)
+{
+	struct llvm_cov_private_data *data = file->private_data;
+
+	return simple_read_from_buffer(buf, count, ppos, data->buffer,
+				       data->size);
+}
+
+/* release() implementation for llvm-cov data file. */
+static int llvm_cov_release(struct inode *inode, struct file *file)
+{
+	struct llvm_cov_private_data *data = file->private_data;
+
+	vfree(data->buffer);
+	kfree(data);
+	return 0;
+}
+
+static const struct file_operations llvm_cov_data_fops = {
+	.owner		= THIS_MODULE,
+	.open		= llvm_cov_open,
+	.read		= llvm_cov_read,
+	.llseek		= default_llseek,
+	.release	= llvm_cov_release,
+};
+
+static void llvm_cov_reset(void)
+{
+	mutex_lock(&llvm_cov_mutex);
+	memset(__llvm_prf_cnts_start, 0, __llvm_prf_cnts_size());
+	memset(__llvm_prf_bits_start, 0, __llvm_prf_bits_size());
+	mutex_unlock(&llvm_cov_mutex);
+}
+
+static const struct coverage_backend llvm_cov_backend = {
+	.name	= "llvm",
+	.reset	= llvm_cov_reset,
+};
+
+static int __init llvm_cov_init(void)
+{
+	struct dentry *dir;
+
+	dir = debugfs_create_dir("llvm", coverage_debugfs_root());
+	debugfs_create_file("profraw", 0400, dir, NULL, &llvm_cov_data_fops);
+	coverage_register_backend(&llvm_cov_backend);
+
+	return 0;
+}
+device_initcall(llvm_cov_init);
diff --git a/kernel/coverage/llvm/llvm-cov.h b/kernel/coverage/llvm/llvm-cov.h
new file mode 100644
index 0000000000000..61c76e803f88b
--- /dev/null
+++ b/kernel/coverage/llvm/llvm-cov.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019	Sami Tolvanen <samitolvanen@google.com>, Google, Inc.
+ * Copyright (C) 2024	Jinghao Jia   <jinghao7@illinois.edu>,   UIUC
+ * Copyright (C) 2024	Wentao Zhang  <wentaoz5@illinois.edu>,   UIUC
+ */
+
+#ifndef _LLVM_COV_H
+#define _LLVM_COV_H
+
+#include <linux/types.h>
+
+/*
+ * These internal LLVM definitions must match the compiler version. See
+ * llvm/include/llvm/ProfileData/InstrProfData.inc in LLVM's source code.
+ */
+
+#define INSTR_PROF_RAW_MAGIC_64		\
+		((u64)255 << 56 |	\
+		 (u64)'l' << 48 |	\
+		 (u64)'p' << 40 |	\
+		 (u64)'r' << 32 |	\
+		 (u64)'o' << 24 |	\
+		 (u64)'f' << 16 |	\
+		 (u64)'r' << 8  |	\
+		 (u64)129)
+
+#if CONFIG_CLANG_VERSION >= 230000
+#define INSTR_PROF_RAW_VERSION		11
+#else
+#define INSTR_PROF_RAW_VERSION		10
+#endif
+#define INSTR_PROF_DATA_ALIGNMENT	8
+#define IPVK_LAST			2
+
+/**
+ * struct __llvm_profile_header - represents the raw profile header data
+ * structure. Description of each member can be found here:
+ * https://llvm.org/docs/InstrProfileFormat.html#header.
+ */
+struct __llvm_profile_header {
+	u64 magic;
+	u64 version;
+	u64 binary_ids_size;
+	u64 num_data;
+	u64 padding_bytes_before_counters;
+	u64 num_counters;
+	u64 padding_bytes_after_counters;
+	u64 num_bitmap_bytes;
+	u64 padding_bytes_after_bitmap_bytes;
+#if CONFIG_CLANG_VERSION >= 230000
+	u64 num_uniform_counters;
+	u64 padding_bytes_after_uniform_counters;
+	u64 uniform_counters_delta;
+#endif
+	u64 names_size;
+	u64 counters_delta;
+	u64 bitmap_delta;
+	u64 names_delta;
+	u64 num_v_tables;
+	u64 v_names_size;
+	u64 value_kind_last;
+};
+
+/**
+ * struct __llvm_profile_data - represents the per-function control structure.
+ * Description of each member can be found here:
+ * https://llvm.org/docs/InstrProfileFormat.html#profile-metadata. To measure
+ * Source-based Code Coverage, the internals of this struct don't matter at run
+ * time. The only purpose of the definition below is to run sizeof() against it
+ * so that we can calculate the "num_data" field in header.
+ */
+struct __llvm_profile_data {
+	const u64 name_ref;
+	const u64 func_hash;
+	const void *counter_ptr;
+#if CONFIG_CLANG_VERSION >= 230000
+	const void *uniform_counter_ptr;
+#endif
+	const void *bitmap_ptr;
+	const void *function_pointer;
+	void *values;
+	const u32 num_counters;
+	const u16 num_value_sites[IPVK_LAST + 1];
+#if CONFIG_CLANG_VERSION >= 230000
+	const u16 offload_device_wave_size;
+#endif
+	const u32 num_bitmap_bytes;
+} __aligned(INSTR_PROF_DATA_ALIGNMENT);
+
+/* Payload sections */
+
+extern struct __llvm_profile_data __llvm_prf_data_start[];
+extern struct __llvm_profile_data __llvm_prf_data_end[];
+
+extern u64 __llvm_prf_cnts_start[];
+extern u64 __llvm_prf_cnts_end[];
+
+extern char __llvm_prf_names_start[];
+extern char __llvm_prf_names_end[];
+
+extern char __llvm_prf_bits_start[];
+extern char __llvm_prf_bits_end[];
+
+#define __DEFINE_SECTION_SIZE(s)					\
+	static inline unsigned long __llvm_prf_ ## s ## _size(void)	\
+	{								\
+		unsigned long start =					\
+			(unsigned long)__llvm_prf_ ## s ## _start;	\
+		unsigned long end =					\
+			(unsigned long)__llvm_prf_ ## s ## _end;	\
+		return end - start;					\
+	}
+#define __DEFINE_SECTION_COUNT(s)					\
+	static inline unsigned long __llvm_prf_ ## s ## _count(void)	\
+	{								\
+		return __llvm_prf_ ## s ## _size() /			\
+			sizeof(__llvm_prf_ ## s ## _start[0]);		\
+	}
+
+__DEFINE_SECTION_SIZE(data)
+__DEFINE_SECTION_SIZE(cnts)
+__DEFINE_SECTION_SIZE(names)
+__DEFINE_SECTION_SIZE(bits)
+
+__DEFINE_SECTION_COUNT(data)
+__DEFINE_SECTION_COUNT(cnts)
+__DEFINE_SECTION_COUNT(names)
+__DEFINE_SECTION_COUNT(bits)
+
+#undef __DEFINE_SECTION_SIZE
+#undef __DEFINE_SECTION_COUNT
+
+static inline unsigned long __llvm_prf_get_padding(unsigned long size)
+{
+	return 7 & (sizeof(u64) - size % sizeof(u64));
+}
+
+#endif /* _LLVM_COV_H */
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index e0dc21e009522..77d1ab47125be 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -42,10 +42,13 @@ _cpp_flags     = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(target-stem).lds)
 # depending on variables COVERAGE_PROFILE_obj.o, COVERAGE_PROFILE and
 # CONFIG_COVERAGE_PROFILE_ALL (in this order)
 #
+# Modules are only instrumented if the coverage implementation supports them.
+#
 ifeq ($(CONFIG_COVERAGE_KERNEL),y)
-_c_flags += $(if $(patsubst n%,, \
+_c_flags += $(if $(if $(part-of-module),$(CONFIG_COVERAGE_MODULES),y), \
+		$(if $(patsubst n%,, \
 		$(COVERAGE_PROFILE_$(target-stem).o)$(COVERAGE_PROFILE)$(if $(is-kernel-object),$(CONFIG_COVERAGE_PROFILE_ALL))), \
-		$(CFLAGS_COVERAGE))
+		$(CFLAGS_COVERAGE)))
 endif
 
 #
diff --git a/scripts/Makefile.llvm-cov b/scripts/Makefile.llvm-cov
new file mode 100644
index 0000000000000..5babb09b6d3e6
--- /dev/null
+++ b/scripts/Makefile.llvm-cov
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CFLAGS_COVERAGE	:= -fprofile-instr-generate -fcoverage-mapping \
+		   -fprofile-update=atomic
+
+export CFLAGS_COVERAGE
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 15/20] x86: Enable Clang's source-based code coverage
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (13 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 14/20] coverage: add Clang's source-based code coverage Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Wentao Zhang, Bill Wendling, Borislav Petkov, Chuck Wolber,
	Dave Hansen, H. Peter Anvin, Ingo Molnar, Justin Stitt, llvm,
	Marco Elver, Masami Hiramatsu, Nathan Chancellor,
	Nick Desaulniers, Peter Zijlstra, Thomas Gleixner, x86

Place the sections of Clang's source-based code coverage in the x86
linker script and allow it to be used, including for the entire
kernel, on x86_64.

Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/Kconfig              | 2 ++
 arch/x86/kernel/vmlinux.lds.S | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index a5e29ffd5ddc3..0c26d61350553 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -89,6 +89,8 @@ config X86
 	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
 	select ARCH_HAS_KCOV			if X86_64
+	select ARCH_HAS_LLVM_COV		if X86_64
+	select ARCH_HAS_LLVM_COV_PROFILE_ALL	if X86_64
 	select ARCH_HAS_KERNEL_FPU_SUPPORT
 	select ARCH_HAS_MEM_ENCRYPT
 	select ARCH_HAS_MEMBARRIER_SYNC_CORE
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 2438b89a4620a..98f41b943fe63 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -212,6 +212,8 @@ SECTIONS
 
 	BUG_TABLE
 
+	LLVM_COV_DATA
+
 	ORC_UNWIND_TABLE
 
 	/* Init code and data - will be freed after init */
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 16/20] coverage: add MC/DC measurement
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (14 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 15/20] x86: Enable " Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 19:14   ` Nathan Chancellor
  2026-10-28 11:59 ` [RFC PATCH 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters Sasha Levin
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Wentao Zhang, Alexander Potapenko, Andrey Konovalov,
	Bill Wendling, Chuck Wolber, Dmitry Vyukov, Justin Stitt,
	kasan-dev, linux-kbuild, llvm, Marco Elver, Masami Hiramatsu,
	Nathan Chancellor, Nick Desaulniers, Nicolas Schier,
	Peter Oberparleiter, Peter Zijlstra, Thomas Gleixner

Add CONFIG_COVERAGE_MCDC to measure Modified Condition/Decision
Coverage (MC/DC) [1], which is required by several automotive and
aviation standards for certifying safety critical software [2]. Start
with Clang's source-based code coverage, which supports it since Clang
18 [3].

In the following example from arch/x86/events/probe.c, measured on
v6.11, llvm-cov gives the MC/DC measurement for the compound logic
decision at line 43:

   43|     12|			if (msr[bit].test && !msr[bit].test(bit, data))
  ------------------
  |---> MC/DC Decision Region (43:8) to (43:50)
  |
  |  Number of Conditions: 2
  |     Condition C1 --> (43:8)
  |     Condition C2 --> (43:25)
  |
  |  Executed MC/DC Test Vectors:
  |
  |     C1, C2    Result
  |  1 { T,  F  = F      }
  |  2 { T,  T  = T      }
  |
  |  C1-Pair: not covered
  |  C2-Pair: covered: (1,2)
  |  MC/DC Coverage for Decision: 50.00%
  |
  ------------------
   44|      5|				continue;

Only condition C2 was shown to independently affect the outcome of the
decision, so the MC/DC of this decision is 1 out of 2.

MC/DC uses bitmaps to track the executed test vectors, which grow
quickly with the number of conditions. CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS
sets the largest decision to instrument, through -fmcdc-max-conditions.
Larger decisions, and decisions the compiler can't instrument, produce
warnings. Starting with Clang 22 they are kept from becoming errors
with CONFIG_WERROR; older versions can't be used together with it.

Link: https://en.wikipedia.org/wiki/Modified_condition%2Fdecision_coverage [1]
Link: https://digital-library.theiet.org/content/journals/10.1049/sej.1994.0025 [2]
Link: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#mc-dc-instrumentation [3]
Originally-by: Wentao Zhang <wentaoz5@illinois.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 kernel/coverage/Kconfig   | 26 ++++++++++++++++++++++++++
 kernel/coverage/llvm/fs.c |  5 +++--
 scripts/Makefile.llvm-cov |  5 +++++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index acfcce23e4c97..fab3da51305bb 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -106,6 +106,32 @@ config COVERAGE_PROFILE_ALL
 	  larger and run slower. Also be sure to exclude files from profiling
 	  which are not linked to the kernel image to prevent linker errors.
 
+config COVERAGE_MCDC
+	bool "Measure modified condition/decision coverage (MC/DC)"
+	depends on LLVM_COV_KERNEL
+	depends on CLANG_VERSION >= 220000 || !WERROR
+	help
+	  MC/DC verifies that each condition of a boolean decision has been
+	  shown to independently affect the outcome of the decision. It is
+	  required for certification by safety standards such as DO-178C
+	  (avionics) and ISO 26262 (automotive).
+
+	  Decisions that the compiler can't instrument produce build warnings
+	  and are not measured.
+
+	  If unsure, say N.
+
+config LLVM_COV_MCDC_MAX_CONDITIONS
+	int "Maximum number of conditions in a decision to instrument"
+	range 6 32767
+	depends on COVERAGE_MCDC && LLVM_COV_KERNEL
+	default 6
+	help
+	  This value is passed to the "-fmcdc-max-conditions" option of Clang.
+	  Decisions with more conditions produce warnings and are not
+	  instrumented. MC/DC uses bitmaps to track the executed test vectors,
+	  which grow quickly with the number of conditions.
+
 # The per-subsystem COVERAGE_PROFILE_* options used to be GCOV_PROFILE_*.
 config GCOV_PROFILE_AMDGPU
 	bool
diff --git a/kernel/coverage/llvm/fs.c b/kernel/coverage/llvm/fs.c
index e90c325daeba3..16ada19650d07 100644
--- a/kernel/coverage/llvm/fs.c
+++ b/kernel/coverage/llvm/fs.c
@@ -184,8 +184,9 @@ static void llvm_cov_reset(void)
 }
 
 static const struct coverage_backend llvm_cov_backend = {
-	.name	= "llvm",
-	.reset	= llvm_cov_reset,
+	.name		= "llvm",
+	.features	= IS_ENABLED(CONFIG_COVERAGE_MCDC) ? "mcdc" : NULL,
+	.reset		= llvm_cov_reset,
 };
 
 static int __init llvm_cov_init(void)
diff --git a/scripts/Makefile.llvm-cov b/scripts/Makefile.llvm-cov
index 5babb09b6d3e6..771a033a92669 100644
--- a/scripts/Makefile.llvm-cov
+++ b/scripts/Makefile.llvm-cov
@@ -3,4 +3,9 @@
 CFLAGS_COVERAGE	:= -fprofile-instr-generate -fcoverage-mapping \
 		   -fprofile-update=atomic
 
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE	+= -fcoverage-mcdc $(call cc-option,-Wno-error=pgo-coverage) \
+		   -Xclang -fmcdc-max-conditions=$(CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS)
+endif
+
 export CFLAGS_COVERAGE
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (15 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection Sasha Levin
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Andrew Morton, Chuck Wolber, Frantisek Hrbata, Marco Elver,
	Masami Hiramatsu, Peter Oberparleiter, Peter Zijlstra,
	Thomas Gleixner

gcov_info_add() unconditionally uses += to merge all counter types.
This is wrong for counters that use IOR merge semantics (bitwise OR),
such as GCOV_COUNTER_IOR and GCC 14's GCOV_COUNTER_CONDS (MC/DC
condition coverage). These counters store bitsets that must be merged
with |=, not accumulated with +=.

Detect IOR merge semantics by comparing the merge function pointer
against __gcov_merge_ior, matching how GCC's own libgcov identifies
merge semantics. This fixes the pre-existing bug for GCOV_COUNTER_IOR
and also enables correct merging for MC/DC condition coverage data.

Fixes: 5f41ea0386a5 ("gcov: add support for gcc 4.7 gcov format")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 kernel/coverage/gcov/gcc_4_7.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/kernel/coverage/gcov/gcc_4_7.c b/kernel/coverage/gcov/gcc_4_7.c
index 8fa22ababd943..923cfb34966b2 100644
--- a/kernel/coverage/gcov/gcc_4_7.c
+++ b/kernel/coverage/gcov/gcc_4_7.c
@@ -18,6 +18,8 @@
 #include <linux/mm.h>
 #include "gcov.h"
 
+extern void __gcov_merge_ior(gcov_type *, unsigned int);
+
 #if (__GNUC__ >= 15)
 #define GCOV_COUNTERS			10
 #elif (__GNUC__ >= 14)
@@ -187,6 +189,15 @@ static int counter_active(struct gcov_info *info, unsigned int type)
 	return info->merge[type] ? 1 : 0;
 }
 
+/*
+ * Determine whether a counter uses IOR merge semantics (bitwise OR of
+ * bitsets). Used for condition coverage (MC/DC) and other IOR-based counters.
+ */
+static bool counter_is_ior(struct gcov_info *info, unsigned int type)
+{
+	return info->merge[type] == __gcov_merge_ior;
+}
+
 /* Determine number of active counters. Based on gcc magic. */
 static unsigned int num_counter_active(struct gcov_info *info)
 {
@@ -259,9 +270,17 @@ void gcov_info_add(struct gcov_info *dst, struct gcov_info *src)
 			if (!counter_active(src, ct_idx))
 				continue;
 
-			for (val_idx = 0; val_idx < sci_ptr->num; val_idx++)
-				dci_ptr->values[val_idx] +=
-					sci_ptr->values[val_idx];
+			if (counter_is_ior(src, ct_idx)) {
+				for (val_idx = 0; val_idx < sci_ptr->num;
+				     val_idx++)
+					dci_ptr->values[val_idx] |=
+						sci_ptr->values[val_idx];
+			} else {
+				for (val_idx = 0; val_idx < sci_ptr->num;
+				     val_idx++)
+					dci_ptr->values[val_idx] +=
+						sci_ptr->values[val_idx];
+			}
 
 			dci_ptr++;
 			sci_ptr++;
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (16 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 19/20] gcov: add MC/DC condition coverage support Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Chuck Wolber, Julian Braha, linux-kbuild, Marco Elver,
	Masami Hiramatsu, Nathan Chancellor, Nicolas Schier,
	Peter Zijlstra, Thomas Gleixner

Add a Kconfig symbol to detect compiler support for -fcondition-coverage,
which enables MC/DC (Modified Condition/Decision Coverage) instrumentation.
This flag is available since GCC 14.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 scripts/Kconfig.toolchain | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/scripts/Kconfig.toolchain b/scripts/Kconfig.toolchain
index 4b962831d62af..f66b6efacee6c 100644
--- a/scripts/Kconfig.toolchain
+++ b/scripts/Kconfig.toolchain
@@ -144,6 +144,9 @@ config CC_HAS_ASSUME
 config CC_HAS_NO_PROFILE_FN_ATTR
 	def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
 
+config CC_HAS_CONDITION_COVERAGE
+	def_bool $(cc-option,-fcondition-coverage)
+
 config CC_OPT_ZERO_INIT_PADDING_BITS
 	def_string "$(cc-option-str,-fzero-init-padding-bits=all)"
 
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 19/20] gcov: add MC/DC condition coverage support
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (17 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  2026-10-28 11:59 ` [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexander Potapenko, Andrey Konovalov, Anton Ivanov,
	Bill Wendling, Borislav Petkov, Chuck Wolber, Dave Hansen,
	Dmitry Vyukov, H. Peter Anvin, Ingo Molnar, Johannes Berg,
	Justin Stitt, kasan-dev, linux-kbuild, linux-um, llvm,
	Marco Elver, Masami Hiramatsu, Nathan Chancellor,
	Nick Desaulniers, Nicolas Schier, Peter Oberparleiter,
	Peter Zijlstra, Richard Weinberger, Thomas Gleixner, x86

Support CONFIG_COVERAGE_MCDC with gcov, using GCC's MC/DC condition
coverage instrumentation (-fcondition-coverage), which is available
since GCC 14. The data is shown by "gcov --conditions".

Add -fcondition-coverage and -Wno-error=coverage-too-many-conditions to
the gcov compiler flags when enabled. Both flags are gated on the config
option to avoid Clang warnings about unknown options.

Also add -fcondition-coverage to CFLAGS_REMOVE in the x86 UML vDSO
Makefile to prevent instrumentation of userspace vDSO code.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 arch/x86/um/vdso/Makefile | 4 ++--
 kernel/coverage/Kconfig   | 7 +++++--
 kernel/coverage/gcov/fs.c | 5 +++--
 scripts/Makefile.gcov     | 3 +++
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/x86/um/vdso/Makefile b/arch/x86/um/vdso/Makefile
index 7664cbedbe30f..adfc57c1a726d 100644
--- a/arch/x86/um/vdso/Makefile
+++ b/arch/x86/um/vdso/Makefile
@@ -42,8 +42,8 @@ $(vobjs): KBUILD_CFLAGS += $(CFL)
 #
 # vDSO code runs in userspace and -pg doesn't help with profiling anyway.
 #
-CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage
-CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage
+CFLAGS_REMOVE_vdso-note.o = -pg -fprofile-arcs -ftest-coverage -fcondition-coverage
+CFLAGS_REMOVE_um_vdso.o = -pg -fprofile-arcs -ftest-coverage -fcondition-coverage
 
 #
 # The DSO images are built using a special linker script.
diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index fab3da51305bb..5990e366db994 100644
--- a/kernel/coverage/Kconfig
+++ b/kernel/coverage/Kconfig
@@ -108,8 +108,8 @@ config COVERAGE_PROFILE_ALL
 
 config COVERAGE_MCDC
 	bool "Measure modified condition/decision coverage (MC/DC)"
-	depends on LLVM_COV_KERNEL
-	depends on CLANG_VERSION >= 220000 || !WERROR
+	depends on LLVM_COV_KERNEL || (GCOV_KERNEL && CC_HAS_CONDITION_COVERAGE)
+	depends on !LLVM_COV_KERNEL || CLANG_VERSION >= 220000 || !WERROR
 	help
 	  MC/DC verifies that each condition of a boolean decision has been
 	  shown to independently affect the outcome of the decision. It is
@@ -119,6 +119,9 @@ config COVERAGE_MCDC
 	  Decisions that the compiler can't instrument produce build warnings
 	  and are not measured.
 
+	  With gcov this requires GCC 14 or later, and the data is shown by
+	  "gcov --conditions".
+
 	  If unsure, say N.
 
 config LLVM_COV_MCDC_MAX_CONDITIONS
diff --git a/kernel/coverage/gcov/fs.c b/kernel/coverage/gcov/fs.c
index 23d366dcfc2f7..dfaccc01e06f3 100644
--- a/kernel/coverage/gcov/fs.c
+++ b/kernel/coverage/gcov/fs.c
@@ -866,8 +866,9 @@ void gcov_event(enum gcov_action action, struct gcov_info *info)
 }
 
 static const struct coverage_backend gcov_backend = {
-	.name	= "gcov",
-	.reset	= gcov_reset_all,
+	.name		= "gcov",
+	.features	= IS_ENABLED(CONFIG_COVERAGE_MCDC) ? "mcdc" : NULL,
+	.reset		= gcov_reset_all,
 };
 
 /* Create debugfs entries. */
diff --git a/scripts/Makefile.gcov b/scripts/Makefile.gcov
index 1620b81398eaa..b00dd40f2167d 100644
--- a/scripts/Makefile.gcov
+++ b/scripts/Makefile.gcov
@@ -19,4 +19,7 @@ CFLAGS_COVERAGE	+= $(call try-run,\
 	cmp -s "$$TMP.ubase" "$$TMP.utest",\
 	-fprofile-update=prefer-atomic)
 endif
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE	+= -fcondition-coverage -Wno-error=coverage-too-many-conditions
+endif
 export CFLAGS_COVERAGE
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface
  2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (18 preceding siblings ...)
  2026-10-28 11:59 ` [RFC PATCH 19/20] gcov: add MC/DC condition coverage support Sasha Levin
@ 2026-10-28 11:59 ` Sasha Levin
  19 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 11:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Alexander Potapenko, Andrey Konovalov, Bill Wendling,
	Chuck Wolber, Dmitry Vyukov, Jonathan Corbet, Justin Stitt,
	kasan-dev, linux-doc, llvm, Marco Elver, Masami Hiramatsu,
	Nathan Chancellor, Nick Desaulniers, Peter Oberparleiter,
	Peter Zijlstra, Randy Dunlap, Shuah Khan, Thomas Gleixner,
	workflows

Describe the code coverage implementations and what they share: the
Kconfig options, the COVERAGE_PROFILE annotations, the
/sys/kernel/debug/coverage/ interface, MC/DC measurement, and how they
deal with noinstr code. Point to it from the gcov and KCOV
documentation, and document gcov's MC/DC support.

Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 Documentation/dev-tools/coverage.rst | 158 +++++++++++++++++++++++++++
 Documentation/dev-tools/gcov.rst     |  28 +++++
 Documentation/dev-tools/index.rst    |   1 +
 Documentation/dev-tools/kcov.rst     |   3 +
 MAINTAINERS                          |   1 +
 5 files changed, 191 insertions(+)
 create mode 100644 Documentation/dev-tools/coverage.rst

diff --git a/Documentation/dev-tools/coverage.rst b/Documentation/dev-tools/coverage.rst
new file mode 100644
index 0000000000000..547863b2afd6d
--- /dev/null
+++ b/Documentation/dev-tools/coverage.rst
@@ -0,0 +1,158 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+Code coverage
+=============
+
+The kernel can be instrumented to find out which parts of it get executed.
+This serves two different purposes:
+
+* Measuring code coverage, e.g. to assess and improve a test suite, or to
+  show the structural coverage required by safety standards. This is done by
+  one of the code coverage measurement implementations described below.
+
+* Guiding fuzzers. This is done by KCOV, which collects coverage per task;
+  see Documentation/dev-tools/kcov.rst.
+
+All of them are configured in the "Kernel hacking -> Kernel Testing and
+Coverage -> Code coverage" menu, live in ``kernel/coverage/``, and share the
+``/sys/kernel/debug/coverage/`` debugfs directory.
+
+
+Code coverage measurement
+=========================
+
+The implementation is selected by the "Code coverage measurement" choice:
+
+============================  ==============  ================================
+Option                        Compilers       Data
+============================  ==============  ================================
+``CONFIG_GCOV_KERNEL``        GCC, Clang      gcov ``.gcda`` files, see
+                                              Documentation/dev-tools/gcov.rst
+``CONFIG_LLVM_COV_KERNEL``    Clang 19 and    LLVM raw profile of the kernel
+                              later           image (``.profraw``)
+============================  ==============  ================================
+
+The instrumentation and the format of the data are defined by the toolchain,
+so only one implementation can be used at a time. Both only instrument C
+code.
+
+Clang's source-based code coverage (``CONFIG_LLVM_COV_KERNEL``) instruments
+the code in the compiler front end. Its data therefore maps back to the source
+code exactly, regardless of the optimizations applied, and it can show the
+coverage of every condition of a decision. It only instruments code built into
+the kernel image. gcov also supports modules.
+
+
+Selecting the code to instrument
+--------------------------------
+
+``CONFIG_COVERAGE_PROFILE_ALL`` instruments the entire kernel. On architectures
+that don't support it, or to instrument only parts of the kernel, add a line
+like the following to the respective Makefile.
+
+For a single file (e.g. main.o)::
+
+	COVERAGE_PROFILE_main.o := y
+
+For all files in one directory::
+
+	COVERAGE_PROFILE := y
+
+To exclude files from being instrumented even when
+``CONFIG_COVERAGE_PROFILE_ALL`` is enabled, use::
+
+	COVERAGE_PROFILE_main.o := n
+
+and::
+
+	COVERAGE_PROFILE := n
+
+The per-file setting takes precedence over the per-directory one, which takes
+precedence over ``CONFIG_COVERAGE_PROFILE_ALL``.
+
+Only exclude code that can't work when instrumented, for example because it
+runs before the kernel is relocated or is copied elsewhere. Code that must not
+be instrumented for correctness reasons, such as the entry code, is annotated
+``noinstr`` instead; see `noinstr`_.
+
+
+Interface
+---------
+
+``/sys/kernel/debug/coverage/`` contains:
+
+``backend``
+	The name of the selected implementation, ``gcov`` or ``llvm``, followed by
+	``mcdc`` when MC/DC is measured.
+
+``reset``
+	Writing to this file resets all coverage data of the selected
+	implementation. It does not affect KCOV.
+
+``gcov``
+	A symlink to ``/sys/kernel/debug/gcov``, where gcov keeps its data (see
+	Documentation/dev-tools/gcov.rst).
+
+``llvm/profraw``
+	The raw profile of the kernel image, for Clang's source-based code
+	coverage.
+
+``kcov``
+	A symlink to ``/sys/kernel/debug/kcov`` (see
+	Documentation/dev-tools/kcov.rst).
+
+The instrumented code updates the coverage data without synchronizing with
+readers, so reading the data while other CPUs execute instrumented code
+doesn't give an atomic snapshot, and the same applies to a reset.
+
+With Clang's source-based code coverage, convert the raw profile and create a
+report with the ``llvm-profdata`` and ``llvm-cov`` tools of the LLVM version
+that built the kernel, since the raw profile format changes between releases::
+
+	# cp /sys/kernel/debug/coverage/llvm/profraw vmlinux.profraw
+	$ llvm-profdata merge vmlinux.profraw -o vmlinux.profdata
+	$ llvm-cov report -instr-profile=vmlinux.profdata vmlinux
+	$ llvm-cov show -instr-profile=vmlinux.profdata vmlinux kernel/fork.c
+
+
+MC/DC
+-----
+
+Modified condition/decision coverage (MC/DC) verifies that each condition of a
+boolean decision has been shown to independently affect the outcome of the
+decision. It is required by safety standards such as DO-178C (avionics) and
+ISO 26262 (automotive). Enable it with ``CONFIG_COVERAGE_MCDC``:
+
+* With Clang's source-based code coverage, decisions with more than
+  ``CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS`` conditions are not measured. Show
+  the results with ``llvm-cov show -show-mcdc`` and
+  ``llvm-cov report -show-mcdc-summary``.
+
+* With gcov, GCC 14 or later is required, and decisions with more than 64
+  conditions are not measured. Show the results with ``gcov --conditions``.
+
+The compiler warns about every decision it doesn't instrument.
+
+
+noinstr
+=======
+
+Code annotated ``noinstr`` must not be instrumented; this includes the code
+running on kernel entry and exit, and in NMI context. Outside of
+``instrumentation_begin()``/``instrumentation_end()`` sections, ``noinstr``
+functions may only call other ``noinstr`` functions and ``__always_inline``
+functions.
+
+The code coverage measurement implementations insert their instrumentation
+before functions get inlined, which would carry the instrumentation of an
+``__always_inline`` function into the ``noinstr`` code it is inlined into. To
+avoid that, ``__always_inline`` functions are not instrumented when one of them
+is enabled, and as a consequence they don't show up in the coverage data.
+
+With ``CONFIG_NOINSTR_VALIDATION``, objtool reports coverage counters that get
+updated by ``noinstr`` code outside of such sections.
+
+KCOV instruments the code after inlining and honours ``noinstr`` through the
+``no_sanitize_coverage`` attribute with Clang and GCC 12 or later; with older
+GCC, objtool removes its calls from ``noinstr`` code.
diff --git a/Documentation/dev-tools/gcov.rst b/Documentation/dev-tools/gcov.rst
index b6910c3fc00dd..d2950070f3ca4 100644
--- a/Documentation/dev-tools/gcov.rst
+++ b/Documentation/dev-tools/gcov.rst
@@ -22,6 +22,9 @@ Possible uses:
 * minimizing kernel configurations (do I need this option if the
   associated code is never run?)
 
+See Documentation/dev-tools/coverage.rst for the options and interfaces shared
+with the other code coverage implementations.
+
 .. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html
 .. _lcov: https://github.com/linux-test-project/lcov
 
@@ -180,6 +183,31 @@ b) gcov is run on the BUILD machine
       [user@build] gcov -o /tmp/coverage/tmp/out/init main.c
 
 
+MC/DC Coverage
+--------------
+
+When using GCC 14 or later with ``CONFIG_COVERAGE_MCDC=y``, Modified
+Condition/Decision Coverage (MC/DC) data is collected alongside standard
+branch coverage. MC/DC verifies that each condition in a decision
+independently affects the decision's outcome.
+
+To view MC/DC data, use::
+
+    gcov --conditions -o /sys/kernel/debug/gcov/path/to/dir file.c
+
+MC/DC coverage is required by safety standards such as DO-178C
+(avionics) and ISO 26262 (automotive).
+
+GCC's condition coverage implementation has a limit of 64 conditions per
+boolean expression, due to the use of 64-bit bitmasks internally.
+Expressions exceeding this limit cannot be instrumented and will produce
+a compiler warning. This does not affect the correctness of the kernel;
+those expressions simply will not have MC/DC coverage data.
+
+Note that MC/DC instrumentation increases binary size and execution
+overhead compared to standard gcov profiling.
+
+
 Note on compilers
 -----------------
 
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst
index 59cbb77b33ff4..d670c1398e495 100644
--- a/Documentation/dev-tools/index.rst
+++ b/Documentation/dev-tools/index.rst
@@ -23,6 +23,7 @@ Documentation/process/debugging/index.rst
    coccinelle
    context-analysis
    sparse
+   coverage
    kcov
    gcov
    kasan
diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst
index 1a739290c8ecc..98a30a1a3f88a 100644
--- a/Documentation/dev-tools/kcov.rst
+++ b/Documentation/dev-tools/kcov.rst
@@ -12,6 +12,9 @@ To achieve this goal, it does not collect coverage in soft/hard interrupts
 (unless remove coverage collection is enabled, see below) and from some
 inherently non-deterministic parts of the kernel (e.g. scheduler, locking).
 
+KCOV is configured next to the other code coverage implementations, see
+Documentation/dev-tools/coverage.rst.
+
 Besides collecting code coverage, KCOV can also collect comparison operands.
 See the "Comparison operands collection" section for details.
 
diff --git a/MAINTAINERS b/MAINTAINERS
index 506f25cfc3e85..ff9b355c2fc0d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6592,6 +6592,7 @@ F:	drivers/media/platform/chips-media/coda
 CODE COVERAGE
 M:	Sasha Levin <sashal@kernel.org>
 S:	Maintained
+F:	Documentation/dev-tools/coverage.rst
 F:	kernel/coverage/Kconfig
 F:	kernel/coverage/Makefile
 F:	kernel/coverage/core.c
-- 
2.53.0


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
  2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
@ 2026-10-28 13:51   ` Peter Zijlstra
  2026-10-28 14:01     ` Peter Zijlstra
  0 siblings, 1 reply; 28+ messages in thread
From: Peter Zijlstra @ 2026-10-28 13:51 UTC (permalink / raw)
  To: Sasha Levin
  Cc: linux-kernel, Bill Wendling, Chuck Wolber, Justin Stitt, llvm,
	Marco Elver, Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
	Nick Desaulniers, Thomas Gleixner, wentaoz5, chuck.wolber,
	steven.h.vanderleest

On Wed, Oct 28, 2026 at 07:59:28AM -0400, Sasha Levin wrote:
> noinstr code may only call other noinstr functions and __always_inline
> functions. gcov and Clang's source-based code coverage insert their
> instrumentation before inlining, so the instrumentation of an
> __always_inline function ends up in every noinstr function it gets
> inlined into. With Clang's source-based coverage on x86, 2500 of the
> 3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
> of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
> does the same. Since these are plain memory updates rather than calls,
> objtool's noinstr validation doesn't notice them.
> 
> KCOV doesn't have this problem, as it instruments the code after
> inlining and checks the attributes of the function that the code ends
> up in.
> 
> Make __always_inline imply __no_profile when a code coverage
> measurement implementation is enabled, so that no noinstr code carries
> coverage instrumentation. The cost is that __always_inline functions
> don't show up in the coverage data at all, including at their callers
> outside of noinstr code. For an x86_64 configuration built with Clang 22
> and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
> regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
> conditions (1.2%) from the coverage data.

This is pretty vile. But also, since people were trying to use MC/DC for
validation efforts and the like, how does this impact them? If I
understand correctly their previous emails on the topic, this means they
have to go manually audit every single __always_inline function.

Now; I understand this is a tool limitation and there really must not be
instrumentation in noinstr code, since that can run before things like
page-tables are complete (consider the PTI case). So this is the best
option.

Still, I'm left wondering if these tools should be fixed. For instance,
while the front-end emits this instrumentation, the backend could easily
remove it again when it finds itself in the context of a noinstr
function after inlining.

> Leave unoptimized objects alone: GCC refuses to inline an
> __always_inline function across a no_profile_instrument_function
> mismatch at -O0, and those objects don't contain noinstr code.

We have -O0 code? I thought we hard relied on building with at least
-O1, we often get 'fixes' to enable code to build with -O0.

> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
>  include/linux/compiler_types.h | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
> index c5921f1390079..c3298d29a9425 100644
> --- a/include/linux/compiler_types.h
> +++ b/include/linux/compiler_types.h
> @@ -246,6 +246,19 @@ struct ftrace_likely_data {
>   */
>  #define __inline__ inline
>  
> +/*
> + * noinstr code may only call noinstr and __always_inline functions. Code
> + * coverage instrumentation is added before inlining, so the counters of an
> + * __always_inline function would end up in the noinstr code it gets inlined
> + * into: don't instrument __always_inline functions. GCC refuses to inline
> + * across a no_profile_instrument_function mismatch without optimization, so
> + * leave the rare unoptimized objects alone; they don't contain noinstr code.
> + */
> +#if defined(CONFIG_COVERAGE_KERNEL) && defined(__OPTIMIZE__)
> +#undef __always_inline
> +#define __always_inline inline __attribute__((__always_inline__)) __no_profile
> +#endif
> +
>  /*
>   * GCC does not warn about unused static inline functions for -Wunused-function.
>   * Suppress the warning in clang as well by using __maybe_unused, but enable it
> -- 
> 2.53.0
> 

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code
  2026-10-28 11:59 ` [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
@ 2026-10-28 13:52   ` Peter Zijlstra
  0 siblings, 0 replies; 28+ messages in thread
From: Peter Zijlstra @ 2026-10-28 13:52 UTC (permalink / raw)
  To: Sasha Levin
  Cc: linux-kernel, Chuck Wolber, Frederic Weisbecker, Marco Elver,
	Masami Hiramatsu, Paul E. McKenney, Thomas Gleixner

On Wed, Oct 28, 2026 at 07:59:29AM -0400, Sasha Levin wrote:
> The !CONFIG_CONTEXT_TRACKING_USER versions of user_enter_irqoff() and
> user_exit_irqoff(), and the !CONFIG_CONTEXT_TRACKING_IDLE versions of
> ct_idle_enter() and ct_idle_exit(), are plain static inline functions,
> while their real counterparts are __always_inline (user_*_irqoff()) or
> noinstr (ct_idle_*()) functions. They are called from noinstr code, so
> when they aren't inlined that's a call leaving the noinstr section. This
> happens with code coverage instrumentation, which makes the compiler
> refuse to inline an instrumented function into a noinstr one:
> 
>   vmlinux.o: warning: objtool: do_syscall_64+0x81: call to user_exit_irqoff() leaves .noinstr.text section
> 
> Make the stubs __always_inline as well.

Ah yes, nothing beats adding instrumentation to empty functions :-)

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
  2026-10-28 13:51   ` Peter Zijlstra
@ 2026-10-28 14:01     ` Peter Zijlstra
  2026-10-28 16:45       ` Sasha Levin
  0 siblings, 1 reply; 28+ messages in thread
From: Peter Zijlstra @ 2026-10-28 14:01 UTC (permalink / raw)
  To: Sasha Levin
  Cc: linux-kernel, Bill Wendling, Chuck Wolber, Justin Stitt, llvm,
	Marco Elver, Masami Hiramatsu, Miguel Ojeda, Nathan Chancellor,
	Nick Desaulniers, Thomas Gleixner, wentaoz5, chuck.wolber,
	steven.h.vanderleest, Josh Poimboeuf

On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
> On Wed, Oct 28, 2026 at 07:59:28AM -0400, Sasha Levin wrote:
> > noinstr code may only call other noinstr functions and __always_inline
> > functions. gcov and Clang's source-based code coverage insert their
> > instrumentation before inlining, so the instrumentation of an
> > __always_inline function ends up in every noinstr function it gets
> > inlined into. With Clang's source-based coverage on x86, 2500 of the
> > 3449 relocations in .noinstr.text and 333 of the 451 in .cpuidle.text
> > of vmlinux.o point to coverage counters or MC/DC bitmaps. GCC's gcov
> > does the same. Since these are plain memory updates rather than calls,
> > objtool's noinstr validation doesn't notice them.
> > 
> > KCOV doesn't have this problem, as it instruments the code after
> > inlining and checks the attributes of the function that the code ends
> > up in.
> > 
> > Make __always_inline imply __no_profile when a code coverage
> > measurement implementation is enabled, so that no noinstr code carries
> > coverage instrumentation. The cost is that __always_inline functions
> > don't show up in the coverage data at all, including at their callers
> > outside of noinstr code. For an x86_64 configuration built with Clang 22
> > and MC/DC, that removes 556 of 60788 functions (0.9%), 29261 of 1598305
> > regions (1.8%), 11067 of 591108 branches (1.9%) and 445 of 36561 MC/DC
> > conditions (1.2%) from the coverage data.
> 
> This is pretty vile. But also, since people were trying to use MC/DC for
> validation efforts and the like, how does this impact them? If I
> understand correctly their previous emails on the topic, this means they
> have to go manually audit every single __always_inline function.
> 
> Now; I understand this is a tool limitation and there really must not be
> instrumentation in noinstr code, since that can run before things like
> page-tables are complete (consider the PTI case). So this is the best
> option.
> 
> Still, I'm left wondering if these tools should be fixed. For instance,
> while the front-end emits this instrumentation, the backend could easily
> remove it again when it finds itself in the context of a noinstr
> function after inlining.

Ah, also, we have objtool's hack_noinstr :/ But sssh, don't tell Josh
about that.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
  2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
@ 2026-10-28 14:11   ` Peter Zijlstra
  2026-10-28 16:45     ` Sasha Levin
  0 siblings, 1 reply; 28+ messages in thread
From: Peter Zijlstra @ 2026-10-28 14:11 UTC (permalink / raw)
  To: Sasha Levin
  Cc: linux-kernel, Bill Wendling, Chuck Wolber, Josh Poimboeuf,
	Justin Stitt, llvm, Marco Elver, Masami Hiramatsu,
	Nathan Chancellor, Nick Desaulniers, Thomas Gleixner

On Wed, Oct 28, 2026 at 07:59:35AM -0400, Sasha Levin wrote:
> gcov and Clang's source-based code coverage update their counters
> inline, rather than by calling into a runtime like KCOV does, so
> coverage instrumentation in noinstr code goes unnoticed by the noinstr
> validation of call destinations.
> 
> Flag instructions in noinstr code, outside of instrumentation_begin()
> and instrumentation_end() sections, that reference coverage counters:
> GCC's __gcov<N>.* counters (including the MC/DC counters), Clang's gcov
> __llvm_gcov_ctr* counters, and the __llvm_prf_cnts and __llvm_prf_bits
> sections of Clang's source-based code coverage.

Nice!

> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
>  tools/objtool/check.c | 53 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 53 insertions(+)
> 
> diff --git a/tools/objtool/check.c b/tools/objtool/check.c
> index 464f6c9d9ff0b..6382d2a44ef2b 100644
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -4,6 +4,7 @@
>   */
>  
>  #define _GNU_SOURCE /* memmem() */
> +#include <ctype.h>
>  #include <fnmatch.h>
>  #include <string.h>
>  #include <stdlib.h>
> @@ -3507,6 +3508,52 @@ static inline bool noinstr_call_dest(struct objtool_file *file,
>  	return false;
>  }
>  
> +/*
> + * Code coverage instrumentation (gcov, Clang's source-based coverage) updates
> + * its counters inline rather than through calls, so it can't be caught by
> + * checking the call destinations of noinstr code.
> + */
> +static bool is_coverage_counter(struct symbol *sym)
> +{
> +	if (!strcmp(sym->sec->name, "__llvm_prf_cnts") ||
> +	    !strcmp(sym->sec->name, "__llvm_prf_bits"))
> +		return true;
> +
> +	return (strstarts(sym->name, "__gcov") && isdigit((unsigned char)sym->name[6])) ||
> +	       strstarts(sym->name, "__llvm_gcov_ctr");
> +}
> +
> +static bool insn_updates_coverage(struct objtool_file *file,
> +				  struct instruction *insn)
> +{
> +	struct symbol *sym;
> +	struct reloc *reloc;
> +
> +	for (reloc = insn_reloc(file, insn);
> +	     reloc;
> +	     reloc = find_reloc_by_dest_range(file->elf, insn->sec,
> +					      reloc_offset(reloc) + 1,
> +					      insn->offset + insn->len -
> +					      (reloc_offset(reloc) + 1))) {
> +
> +		sym = reloc->sym;
> +		if (!sym->sec)
> +			continue;
> +
> +		if (is_sec_sym(sym)) {
> +			sym = find_symbol_containing(sym->sec,
> +						     arch_insn_adjusted_addend(insn, reloc));
> +			if (!sym)
> +				sym = reloc->sym;
> +		}
> +
> +		if (is_coverage_counter(sym))
> +			return true;
> +	}
> +
> +	return false;
> +}

Since you can have many relocations to the same symbol; does it make
sense to cache the results of those many strcmp()s in symbol::coverage
or something? We have plenty bits left in struct symbol.

> +
>  static int validate_call(struct objtool_file *file,
>  			 struct instruction *insn,
>  			 struct insn_state *state)
> @@ -3680,6 +3727,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
>  	if (statep->noinstr)
>  		statep->instr += insn->instr;
>  
> +	if (statep->noinstr && statep->instr <= 0 &&
> +	    insn_updates_coverage(file, insn)) {
> +		WARN_INSN(insn, "coverage counter update in .noinstr.text section");
> +		return 1;
> +	}

Perhaps merge with the previous conditional?

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
  2026-10-28 14:01     ` Peter Zijlstra
@ 2026-10-28 16:45       ` Sasha Levin
  0 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 16:45 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Sasha Levin, linux-kernel, Bill Wendling, Chuck Wolber,
	Justin Stitt, llvm, Marco Elver, Masami Hiramatsu, Miguel Ojeda,
	Nathan Chancellor, Nick Desaulniers, Thomas Gleixner, wentaoz5,
	chuck.wolber, steven.h.vanderleest, Josh Poimboeuf

On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
>This is pretty vile. But also, since people were trying to use MC/DC for
>validation efforts and the like, how does this impact them? If I
>understand correctly their previous emails on the topic, this means they
>have to go manually audit every single __always_inline function.

Yes, the __always_inline helpers join noinstr code as something that
has to be justified separately rather than covered by tests. Steve's
answer to your noinstr question in 2024 [1] was that this is workable
as long as the number of distinct categories stays small, and this
adds one (556 functions and 445 MC/DC conditions for the config in the
changelog).

Chuck, Steve, does that match what you'd expect on your side?

>Still, I'm left wondering if these tools should be fixed. For instance,
>while the front-end emits this instrumentation, the backend could easily
>remove it again when it finds itself in the context of a noinstr
>function after inlining.

Yup, we should do that. Both GCC and Clang add the counters before
inlining and let always_inline win over the attribute mismatch, so
neither can do it today. We can look at improving them, but it's probably
outside of what this series can do.

On Wed, Oct 28, 2026 at 03:01:18PM +0100, Peter Zijlstra wrote:
>Ah, also, we have objtool's hack_noinstr :/ But sssh, don't tell Josh
>about that.

I looked at that. NOPing the gcov counter updates would be easy, as
they're all single locked instructions, but Clang's MC/DC bitmap
updates go through registers and would need dataflow analysis. It
would also only help x86, while gcov and noinstr are on arm64, riscv
and s390 too. So I'd rather keep this as the guarantee, with objtool
(patch 11) checking it.

On Wed, Oct 28, 2026 at 02:51:40PM +0100, Peter Zijlstra wrote:
>We have -O0 code? I thought we hard relied on building with at least
>-O1, we often get 'fixes' to enable code to build with -O0.

Only crypto/jitterentropy.c, and lpfc when built with GCOV=y. The check
makes no difference for either (lpfc doesn't build at -O0 regardless),
so I'll drop it in v2.

[1] https://lore.kernel.org/all/20240905180758.1396234-1-wentaoz5@illinois.edu/

-- 
Thanks,
Sasha

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code
  2026-10-28 14:11   ` Peter Zijlstra
@ 2026-10-28 16:45     ` Sasha Levin
  0 siblings, 0 replies; 28+ messages in thread
From: Sasha Levin @ 2026-10-28 16:45 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Sasha Levin, linux-kernel, Bill Wendling, Chuck Wolber,
	Josh Poimboeuf, Justin Stitt, llvm, Marco Elver,
	Masami Hiramatsu, Nathan Chancellor, Nick Desaulniers,
	Thomas Gleixner

On Wed, Oct 28, 2026 at 03:11:48PM +0100, Peter Zijlstra wrote:
>Since you can have many relocations to the same symbol; does it make
>sense to cache the results of those many strcmp()s in symbol::coverage
>or something? We have plenty bits left in struct symbol.

Sure, I'll throw in a symbol::coverage_counter bit in classify_symbols(). It
has to happen before the is_global_sym() check there, since the counters, and
the section symbols that relocations often point at, are local.

>Perhaps merge with the previous conditional?

Will do.

-- 
Thanks,
Sasha

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [RFC PATCH 16/20] coverage: add MC/DC measurement
  2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
@ 2026-10-28 19:14   ` Nathan Chancellor
  0 siblings, 0 replies; 28+ messages in thread
From: Nathan Chancellor @ 2026-10-28 19:14 UTC (permalink / raw)
  To: Sasha Levin
  Cc: linux-kernel, Wentao Zhang, Alexander Potapenko,
	Andrey Konovalov, Bill Wendling, Chuck Wolber, Dmitry Vyukov,
	Justin Stitt, kasan-dev, linux-kbuild, llvm, Marco Elver,
	Masami Hiramatsu, Nick Desaulniers, Nicolas Schier,
	Peter Oberparleiter, Peter Zijlstra, Thomas Gleixner

On Wed, Oct 28, 2026 at 07:59:40AM -0400, Sasha Levin wrote:
> Larger decisions, and decisions the compiler can't instrument, produce
> warnings. Starting with Clang 22 they are kept from becoming errors
> with CONFIG_WERROR; older versions can't be used together with it.

What is the reason for keeping these warnings in the build as opposed to
just disabling them altogether? Are developers expected to address them
and if so, how? It seems odd to keep them in the build but prevent them
from breaking the build unless they are not intended to be acted on, in
which case they should just be disabled outright.

-- 
Cheers,
Nathan

^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2026-10-28 19:14 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-28 11:59 [RFC PATCH 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 03/20] coverage: add a common debugfs interface Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
2026-10-28 13:51   ` Peter Zijlstra
2026-10-28 14:01     ` Peter Zijlstra
2026-10-28 16:45       ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
2026-10-28 13:52   ` Peter Zijlstra
2026-10-28 11:59 ` [RFC PATCH 06/20] lockdep: " Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 07/20] perf: Always inline the empty perf_lopwr_cb() stub Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 08/20] rseq: Always inline the empty rseq_note_user_irq_entry() stub Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 09/20] x86: Always inline empty stubs called from noinstr code Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
2026-10-28 14:11   ` Peter Zijlstra
2026-10-28 16:45     ` Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 13/20] x86/entry: Stop excluding the entry code from KCOV Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 14/20] coverage: add Clang's source-based code coverage Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 15/20] x86: Enable " Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 16/20] coverage: add MC/DC measurement Sasha Levin
2026-10-28 19:14   ` Nathan Chancellor
2026-10-28 11:59 ` [RFC PATCH 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 19/20] gcov: add MC/DC condition coverage support Sasha Levin
2026-10-28 11:59 ` [RFC PATCH 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin

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®