[PATCH v2 16/16] compiler_types: Move notrace from inline to __always_inline

From: Song Liu

Date: Thu Oct 22 2026 - 21:17:21 EST


Commit 45959ee7aa64 ("ftrace: Do not function trace inlined functions")
added notrace to the inline macro. At the time, inline was only a hint
with CONFIG_OPTIMIZE_INLINING, and whether a given inline function got
an mcount call depended on the gcc version. Some functions that must
not be traced, such as the paravirt native_*() helpers, were marked
inline and relied on being inlined, so a different gcc could silently
break the function tracer. Making all inline functions notrace removed
that dependency on the compiler.

That trade-off no longer makes sense. CONFIG_OPTIMIZE_INLINING was made
always on by commit ac7c3e4ff401 ("compiler: enable
CONFIG_OPTIMIZE_INLINING forcibly") and removed by commit 889b3c1245de
("compiler: remove CONFIG_OPTIMIZE_INLINING entirely"). The compiler
also inlines static functions that are not marked inline, and creates
.constprop, .isra and .part clones, so the set of traceable functions
already depends on the compiler. notrace on inline does not make it
consistent. It only hides the inline functions that the compiler chose
not to inline. Hidden functions cannot be traced with ftrace or used
as BPF fentry/fexit targets. They also cannot be livepatched, because
livepatch redirects functions through ftrace, so a fix to one of them
has to be shipped by livepatching its callers instead.

A function that must not be traced should say so. The previous patches
mark notrace the inline helpers that relied on inline implying notrace:
the paravirt native helpers, the helpers used by resume and kexec, the
helpers used by bpf_spin_lock, the BPF trampoline and bpf_mem_alloc,
and the helpers used by ftrace recursion protection, tracepoints, SRCU,
the trace clocks, core_kernel_text() and the nofault accessors.

Keep notrace on __always_inline. Such functions are meant to be inlined
into their callers. An out-of-line copy exists only when the address of
the function is taken or when it has external linkage, and those copies
are mostly low-level helpers used by entry, context switch and tracing
code.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@xxxxxxxxxx>
---
include/linux/compiler_attributes.h | 6 +++++-
include/linux/compiler_types.h | 3 +--
2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h
index 476c4c560d17..5fdc07b3e380 100644
--- a/include/linux/compiler_attributes.h
+++ b/include/linux/compiler_attributes.h
@@ -49,10 +49,14 @@
* to its docs (and also "warning: always_inline function might not be
* inlinable [-Wattributes]" is emitted).
*
+ * The compiler still emits an out-of-line copy of an __always_inline function
+ * when its address is taken or when it has external linkage. These functions
+ * are often used by code that must not be traced, so mark them notrace.
+ *
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-always_005finline-function-attribute
* clang: mentioned
*/
-#define __always_inline inline __attribute__((__always_inline__))
+#define __always_inline inline __attribute__((__always_inline__)) notrace

/*
* The second argument is optional (default 0), so we use a variadic macro
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f139007..ea7f2a928b4d 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -230,9 +230,8 @@ struct ftrace_likely_data {
* externally visible function. This makes extern inline behave as per gnu89
* semantics rather than c99. This prevents multiple symbol definition errors
* of extern inline functions at link time.
- * A lot of inline functions can cause havoc with function tracing.
*/
-#define inline inline __gnu_inline __inline_maybe_unused notrace
+#define inline inline __gnu_inline __inline_maybe_unused

/*
* gcc provides both __inline__ and __inline as alternate spellings of
--
2.53.0-Meta