mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC
@ 2026-11-03  1:52 Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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, Konstantin Khorenko

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 scripts/Makefile.gcov, 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, 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. Neither GCC nor
  Clang can drop instrumentation that inlining moves into a noinstr
  function, so this is needed until they can.
- 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 commit 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.

objtool only validates noinstr code on x86. On other architectures,
patch 4 removes the instrumentation that comes from __always_inline
functions, but nothing checks for plain static inline helpers that
noinstr code uses. On arm64 with gcov profiling the whole kernel, Clang
22 leaves no coverage counters in .noinstr.text (4129 without this
series), and calls from noinstr code to out of line copies of trivial
helpers go from 35 to 27. GCC inlines helpers such as
__kern_my_cpu_offset() and preempt_count() after instrumenting them, so
about 190 counters remain there, as without this series. Making those
helpers __always_inline, as on x86, is left to follow-up work.

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.

Merging
=======

The series touches kbuild, x86, objtool, gcov and KCOV. gcov and KCOV
changes usually go through Andrew's tree, so I'd suggest taking it there,
with acks from the x86, objtool and kbuild maintainers.

Testing
=======

On v7.4-rc1:

- x86_64 with CONFIG_DEBUG_ENTRY: every patch builds with GCC 16 (gcov,
  KCOV and CONFIG_WERROR; gcov with MC/DC, lockdep and CONFIG_RSEQ=n from
  patch 8 on) and, from patch 14 on, with Clang 24 (source-based coverage
  with MC/DC, and KCOV). gcov with GCC 16 and source-based coverage with
  Clang 19, 22 and 24, with MC/DC, lockdep and CONFIG_RSEQ on and off:
  objtool is clean and no coverage counter is left in noinstr code.
  Without patch 4, objtool reports coverage counter updates in 60 noinstr
  functions.
- arm64 builds with gcov (GCC 15 and Clang 22), and so do i386 and UML.
- Booted in a VM: gcov and source-based coverage data, reset, MC/DC
  reports from llvm-cov and gcov --conditions, gcov data of a module
  across reloads, and KCOV through both /sys/kernel/debug/kcov and
  /sys/kernel/debug/coverage/kcov.
- Kernels without coverage produce identical compiler invocations.

Changes since the RFC [6]
=========================

- Rebased onto v7.4-rc1. Patch 2 builds on scripts/Makefile.gcov from
  commit f541906e89f9 ("kbuild: move GCOV flags to scripts/Makefile.gcov")
  and only renames the variable there.
- Patch 4: no exception for unoptimized objects, none of them needs one
  (Peter).
- Patch 8: make all the !CONFIG_RSEQ stubs, and the !CONFIG_GENERIC_ENTRY
  rseq_exit_to_user_mode_restart() stub, __always_inline (Sashiko).
- Patch 11: cache the counter classification in struct symbol, and fold
  the check into the existing noinstr conditional (Peter).
- Patches 16, 19 and 20: disable the warnings about decisions that MC/DC
  leaves out instead of keeping them from becoming errors; building with
  KCFLAGS=-Wpgo-coverage or -Wcoverage-too-many-conditions lists them
  (Nathan).

[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/
[6] https://lore.kernel.org/all/20261028120034.871983-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 stubs called from noinstr code
  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          | 161 +++++++++++++
 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                                   |  19 +-
 Makefile                                      |   1 +
 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                |  11 +
 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                    |  14 +-
 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                         |  17 +-
 scripts/Makefile.lib                          |  19 +-
 scripts/Makefile.llvm-cov                     |  15 ++
 scripts/Makefile.modfinal                     |   2 +-
 scripts/sbom/sbom/environment.py              |   2 +-
 tools/objtool/check.c                         |  60 ++++-
 tools/objtool/include/objtool/elf.h           |   1 +
 tools/testing/selftests/net/rds/config.sh     |   9 +-
 tools/testing/selftests/net/rds/rds_run.sh    |   8 +-
 tools/unittests/test_cmatch.py                |   2 +-
 93 files changed, 1247 insertions(+), 304 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.llvm-cov


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.53.0


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

* [PATCH v2 01/20] coverage: move gcov and kcov under kernel/coverage/
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE Sasha Levin
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 93abbb315065d..79de9a6964152 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11173,7 +11173,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/
 F:	scripts/Makefile.gcov
 
 GDB KERNEL DEBUGGING HELPER SCRIPTS
@@ -14395,7 +14397,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 1163736de1f99..057ada4747387 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -1769,8 +1769,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 2d22638ec13a6..bb939219f4cb4 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] 21+ messages in thread

* [PATCH v2 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 03/20] coverage: add a common debugfs interface Sasha Levin
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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, Konstantin Khorenko

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.

 - scripts/Makefile.gcov sets CFLAGS_COVERAGE instead of CFLAGS_GCOV.

 - 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 +--
 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                         |  12 +-
 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 +-
 52 files changed, 241 insertions(+), 188 deletions(-)
 delete mode 100644 kernel/coverage/gcov/Kconfig

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/arch/Kconfig b/arch/Kconfig
index 057ada4747387..b443a227e9d47 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 7033947955d60..3911cf5e9849d 100644
--- a/arch/sh/mm/Makefile
+++ b/arch/sh/mm/Makefile
@@ -41,4 +41,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 50305630f5335..2328a049f2816 100644
--- a/drivers/gpu/drm/amd/amdgpu/Kconfig
+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
@@ -101,16 +101,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 4013781e0a029..648106476e62f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1773,11 +1773,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 35d043b19332f..e54ed136ce4ac 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -1118,11 +1118,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 b0c44f438999e..ab3242265a7a8 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 bb939219f4cb4..8c80f3776f87a 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3062,7 +3062,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
index 16c5b876440c2..1cfe04d89b0a2 100644
--- a/scripts/Makefile.gcov
+++ b/scripts/Makefile.gcov
@@ -1,16 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0-only
-CFLAGS_GCOV	:= -fprofile-arcs -ftest-coverage
+CFLAGS_COVERAGE	:= -fprofile-arcs -ftest-coverage
 ifdef CONFIG_CC_IS_GCC
-CFLAGS_GCOV	+= -fno-tree-loop-im
+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_GCOV	+= $(call try-run,\
+CFLAGS_COVERAGE	+= $(call try-run,\
 	echo 'long long x; void f(void){x++;}' | \
-	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_GCOV) \
+	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_COVERAGE) \
 	-x c - -c -o "$$TMP.base" && \
 	echo 'long long x; void f(void){x++;}' | \
-	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_GCOV) \
+	$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w $(CFLAGS_COVERAGE) \
 	-fprofile-update=prefer-atomic \
 	-x c - -c -o "$$TMP" && \
 	$(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \
@@ -18,4 +18,4 @@ CFLAGS_GCOV	+= $(call try-run,\
 	cmp -s "$$TMP.ubase" "$$TMP.utest",\
 	-fprofile-update=prefer-atomic)
 endif
-export CFLAGS_GCOV
+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] 21+ messages in thread

* [PATCH v2 03/20] coverage: add a common debugfs interface
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 01/20] coverage: move gcov and kcov under kernel/coverage/ Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 02/20] kbuild: unify static coverage instrumentation as COVERAGE_PROFILE Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 79de9a6964152..01bdf404bfde8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6632,6 +6632,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 764918570de13..a761cfb837414 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 9402227bd7ba8..54071af990eec 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: */
@@ -1180,6 +1182,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] 21+ messages in thread

* [PATCH v2 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (2 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 03/20] coverage: add a common debugfs interface Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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.

This works around a limitation shared by both compilers: they lower
the coverage instrumentation before inlining, and let __always_inline
override a no_profile_instrument_function mismatch, so they can't drop
the instrumentation that ends up in a noinstr function. Once they can,
this can be limited to compilers that don't.

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

diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f1390079..5c8fff84004b4 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -246,6 +246,17 @@ 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.
+ */
+#ifdef CONFIG_COVERAGE_KERNEL
+#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] 21+ messages in thread

* [PATCH v2 05/20] context_tracking: Always inline the empty stubs called from noinstr code
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (3 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 04/20] compiler_types.h: Don't instrument __always_inline functions for coverage Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 06/20] lockdep: " Sasha Levin
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 06/20] lockdep: Always inline the empty stubs called from noinstr code
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (4 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 05/20] context_tracking: Always inline the empty stubs called from noinstr code Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 07/20] perf: Always inline the empty perf_lopwr_cb() stub Sasha Levin
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 07/20] perf: Always inline the empty perf_lopwr_cb() stub
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (5 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 06/20] lockdep: " Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 08/20] rseq: Always inline the empty stubs called from noinstr code Sasha Levin
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

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

The !CONFIG_RSEQ stubs in rseq_entry.h, and the !CONFIG_GENERIC_ENTRY
version of rseq_exit_to_user_mode_restart(), are plain static inline
functions, while their counterparts are __always_inline. They are called
from noinstr entry code, directly or through the __always_inline exit to
user mode helpers, and the compiler won't inline them there when
building with code coverage instrumentation.

On x86 that's rseq_note_user_irq_entry(). arm64 calls the exit to user
mode helpers from noinstr code outside of instrumentation_begin()
sections, so with Clang, asm_exit_to_user_mode() and the el0 exception
handlers end up calling rseq_syscall_exit_to_user_mode() and
rseq_irqentry_exit_to_user_mode() out of line.

Make the stubs __always_inline as well.

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

diff --git a/include/linux/rseq_entry.h b/include/linux/rseq_entry.h
index 31ce349ed42ce..7f9f1590d73fd 100644
--- a/include/linux/rseq_entry.h
+++ b/include/linux/rseq_entry.h
@@ -716,7 +716,7 @@ rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
 }
 
 #else /* CONFIG_GENERIC_ENTRY */
-static inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
+static __always_inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
 {
 	return false;
 }
@@ -759,15 +759,15 @@ 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 inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
+static __always_inline void rseq_note_user_irq_entry(void) { }
+static __always_inline bool rseq_exit_to_user_mode_restart(struct pt_regs *regs, unsigned long ti_work)
 {
 	return false;
 }
-static inline void rseq_syscall_exit_to_user_mode(void) { }
-static inline void rseq_irqentry_exit_to_user_mode(void) { }
-static inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
-static inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
+static __always_inline void rseq_syscall_exit_to_user_mode(void) { }
+static __always_inline void rseq_irqentry_exit_to_user_mode(void) { }
+static __always_inline void rseq_debug_syscall_return(struct pt_regs *regs) { }
+static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
 #endif /* !CONFIG_RSEQ */
 
 #endif /* _LINUX_RSEQ_ENTRY_H */
-- 
2.53.0


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

* [PATCH v2 09/20] x86: Always inline empty stubs called from noinstr code
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (7 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 08/20] rseq: Always inline the empty stubs called from noinstr code Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors Sasha Levin
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (8 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 09/20] x86: Always inline " Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 11/20] objtool: Detect code coverage instrumentation in noinstr code
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (9 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 10/20] x86/sev: Use non-instrumented bitops in the GHCB accessors Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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               | 60 ++++++++++++++++++++++++++++-
 tools/objtool/include/objtool/elf.h |  1 +
 2 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 931c5c412f15d..b0ea6e42c4fda 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>
@@ -2490,6 +2491,21 @@ static bool is_profiling_func(const char *name)
 	return false;
 }
 
+/*
+ * Return true if sym is a code coverage counter: GCC's __gcov<N>.* and
+ * Clang's __llvm_gcov_ctr* gcov counters, or the counters and MC/DC bitmaps
+ * of Clang's source-based code coverage.
+ */
+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 int classify_symbols(struct objtool_file *file)
 {
 	struct symbol *func;
@@ -2499,6 +2515,9 @@ static int classify_symbols(struct objtool_file *file)
 		if (is_notype_sym(func) && strstarts(func->name, ".L"))
 			func->local_label = true;
 
+		if (is_coverage_counter(func))
+			func->coverage_counter = true;
+
 		if (!is_global_sym(func))
 			continue;
 
@@ -3508,6 +3527,39 @@ 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 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 (is_sec_sym(sym)) {
+			sym = find_symbol_containing(sym->sec,
+						     arch_insn_adjusted_addend(insn, reloc));
+			if (!sym)
+				sym = reloc->sym;
+		}
+
+		if (sym->coverage_counter)
+			return true;
+	}
+
+	return false;
+}
+
 static int validate_call(struct objtool_file *file,
 			 struct instruction *insn,
 			 struct insn_state *state)
@@ -3678,9 +3730,15 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		nr_insns_visited++;
 	}
 
-	if (statep->noinstr)
+	if (statep->noinstr) {
 		statep->instr += insn->instr;
 
+		if (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;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index a82517a76a0f6..202e77f26785a 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -84,6 +84,7 @@ struct symbol {
 	u8 return_thunk      : 1;
 	u8 fentry            : 1;
 	u8 profiling_func    : 1;
+	u8 coverage_counter  : 1;
 	u8 warned	     : 1;
 	u8 embedded_insn     : 1;
 	u8 local_label       : 1;
-- 
2.53.0


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

* [PATCH v2 12/20] x86/sev: Stop excluding noinstr.o from code coverage
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (10 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 11/20] objtool: Detect code coverage instrumentation in noinstr code Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 13/20] x86/entry: Stop excluding the entry code from KCOV Sasha Levin
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 13/20] x86/entry: Stop excluding the entry code from KCOV
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (11 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 12/20] x86/sev: Stop excluding noinstr.o from code coverage Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 14/20] coverage: add Clang's source-based code coverage Sasha Levin
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 14/20] coverage: add Clang's source-based code coverage
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (12 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 13/20] x86/entry: Stop excluding the entry code from KCOV Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 15/20] x86: Enable " Sasha Levin
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 01bdf404bfde8..d4523a5fef33e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6639,6 +6639,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 f895752d4b05f..5b2da7e8ab22d 100644
--- a/Makefile
+++ b/Makefile
@@ -1205,6 +1205,7 @@ include-$(CONFIG_KMSAN)		+= scripts/Makefile.kmsan
 include-$(CONFIG_UBSAN)		+= scripts/Makefile.ubsan
 include-$(CONFIG_KCOV)		+= scripts/Makefile.kcov
 include-$(CONFIG_GCOV_KERNEL)	+= scripts/Makefile.gcov
+include-$(CONFIG_LLVM_COV_KERNEL) += scripts/Makefile.llvm-cov
 include-$(CONFIG_RANDSTRUCT)	+= scripts/Makefile.randstruct
 include-$(CONFIG_KSTACK_ERASE)	+= scripts/Makefile.kstack_erase
 include-$(CONFIG_AUTOFDO_CLANG)	+= scripts/Makefile.autofdo
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] 21+ messages in thread

* [PATCH v2 15/20] x86: Enable Clang's source-based code coverage
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (13 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 14/20] coverage: add Clang's source-based code coverage Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 16/20] coverage: add MC/DC measurement Sasha Levin
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 58dce0b66179b..988add4916411 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] 21+ messages in thread

* [PATCH v2 16/20] coverage: add MC/DC measurement
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (14 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 15/20] x86: Enable " Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters Sasha Levin
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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, are left
out of the MC/DC data and only get branch coverage. The compiler warns
about each of them, which is nothing to act on in the code, so disable
those warnings; building with KCFLAGS=-Wpgo-coverage lists them. Clang
only allows disabling them since version 22: older versions keep
warning, and can't be used together with CONFIG_WERROR.

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 |  9 +++++++++
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/kernel/coverage/Kconfig b/kernel/coverage/Kconfig
index acfcce23e4c97..8d9d5f063aea6 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 are not measured, and
+	  only get branch coverage.
+
+	  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 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..e2875836291b8 100644
--- a/scripts/Makefile.llvm-cov
+++ b/scripts/Makefile.llvm-cov
@@ -3,4 +3,13 @@
 CFLAGS_COVERAGE	:= -fprofile-instr-generate -fcoverage-mapping \
 		   -fprofile-update=atomic
 
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE	+= -fcoverage-mcdc \
+		   -Xclang -fmcdc-max-conditions=$(CONFIG_LLVM_COV_MCDC_MAX_CONDITIONS)
+# Decisions that MC/DC leaves out are nothing to fix in the code. Disable the
+# warnings here rather than in CFLAGS_COVERAGE, so that KCFLAGS=-Wpgo-coverage
+# can list them. Clang only allows this since version 22.
+KBUILD_CFLAGS	+= $(call cc-disable-warning, pgo-coverage)
+endif
+
 export CFLAGS_COVERAGE
-- 
2.53.0


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

* [PATCH v2 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (15 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 16/20] coverage: add MC/DC measurement Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection Sasha Levin
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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] 21+ messages in thread

* [PATCH v2 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (16 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 17/20] gcov: fix gcov_info_add() merge semantics for IOR counters Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 19/20] gcov: add MC/DC condition coverage support Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 2acbd40a38f16..76c1e794d1157 100644
--- a/scripts/Kconfig.toolchain
+++ b/scripts/Kconfig.toolchain
@@ -188,6 +188,9 @@ config CC_OPT_NO_ALLOW_STORE_DATA_RACES
 config CC_HAS_MNOP_MCOUNT
 	def_bool $(cc-option,-mnop-mcount)
 
+config CC_HAS_CONDITION_COVERAGE
+	def_bool $(cc-option,-fcondition-coverage)
+
 config LD_OPT_FORCE_GROUP_ALLOCATION
 	def_string "$(ld-option-str,--force-group-allocation)"
 
-- 
2.53.0


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

* [PATCH v2 19/20] gcov: add MC/DC condition coverage support
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (17 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 18/20] kconfig: add CC_HAS_CONDITION_COVERAGE for MC/DC support detection Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  2026-11-03  1:52 ` [PATCH v2 20/20] Documentation: dev-tools: document the common code coverage interface Sasha Levin
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 to the gcov compiler flags when enabled, and
disable the warnings about decisions it leaves out, as is done for
Clang. Both 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     | 5 +++++
 4 files changed, 15 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 8d9d5f063aea6..3429bc902c934 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 are not measured, and
 	  only get branch coverage.
 
+	  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 a761cfb837414..689b8b9a0d16a 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 1cfe04d89b0a2..977c8ee023f68 100644
--- a/scripts/Makefile.gcov
+++ b/scripts/Makefile.gcov
@@ -18,4 +18,9 @@ CFLAGS_COVERAGE	+= $(call try-run,\
 	cmp -s "$$TMP.ubase" "$$TMP.utest",\
 	-fprofile-update=prefer-atomic)
 endif
+ifdef CONFIG_COVERAGE_MCDC
+CFLAGS_COVERAGE	+= -fcondition-coverage
+# As for Clang: KCFLAGS=-Wcoverage-too-many-conditions lists what's left out.
+KBUILD_CFLAGS	+= -Wno-coverage-too-many-conditions
+endif
 export CFLAGS_COVERAGE
-- 
2.53.0


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

* [PATCH v2 20/20] Documentation: dev-tools: document the common code coverage interface
  2026-11-03  1:52 [PATCH v2 00/20] Unify code coverage and add Clang source-based coverage with MC/DC Sasha Levin
                   ` (18 preceding siblings ...)
  2026-11-03  1:52 ` [PATCH v2 19/20] gcov: add MC/DC condition coverage support Sasha Levin
@ 2026-11-03  1:52 ` Sasha Levin
  19 siblings, 0 replies; 21+ messages in thread
From: Sasha Levin @ 2026-11-03  1:52 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 | 161 +++++++++++++++++++++++++++
 Documentation/dev-tools/gcov.rst     |  28 +++++
 Documentation/dev-tools/index.rst    |   1 +
 Documentation/dev-tools/kcov.rst     |   3 +
 MAINTAINERS                          |   1 +
 5 files changed, 194 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..f45c6c160379f
--- /dev/null
+++ b/Documentation/dev-tools/coverage.rst
@@ -0,0 +1,161 @@
+.. 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``.
+
+Decisions that aren't measured still get branch coverage. To list them, build
+with ``KCFLAGS=-Wpgo-coverage`` (Clang) or
+``KCFLAGS=-Wcoverage-too-many-conditions`` (GCC). Clang only warns about them
+by default before version 22, as it can't disable these warnings.
+
+
+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 d4523a5fef33e..48ba7abd8951c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6635,6 +6635,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] 21+ messages in thread

end of thread, other threads:[~2026-11-03  1:53 UTC | newest]

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