[PATCH 3/4] static_call: Restrict exporting of static call *key* to tracepoints

From: Sean Christopherson

Date: Thu May 28 2026 - 10:03:24 EST


Rename the export macros for static call trampolines and keys so that the
"default" EXPORT_STATIC_CALL{,_GPL}() exports only the trampoline, and full
exports of trampoline+key pairs is restricted to tracepoints (by naming
convention). Most developers are blissfully unaware of the gory details of
static calls, and so don't understand the implications of using the
innocuous-looking "vanilla" macros.

Effectively defaulting to exporting the key is undesirable as there is no
known use case for allowing a module to change an export static call's
target, outside of tracepoints.

Opportunistically massage the macro magic to deduplicate the
CONFIG_HAVE_STATIC_CALL_INLINE=y vs. CONFIG_HAVE_STATIC_CALL=y vs.
CONFIG_HAVE_STATIC_CALL=n implementations.

No functional change intended.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
arch/x86/events/amd/brs.c | 2 +-
arch/x86/kernel/apic/init.c | 4 +--
arch/x86/kernel/cpu/mshyperv.c | 2 +-
arch/x86/kernel/traps.c | 2 +-
arch/x86/kvm/x86.c | 6 ++--
arch/x86/xen/enlighten.c | 2 +-
include/linux/static_call.h | 59 +++++++++++++---------------------
include/linux/tracepoint.h | 4 +--
kernel/sched/core.c | 8 ++---
9 files changed, 38 insertions(+), 51 deletions(-)

diff --git a/arch/x86/events/amd/brs.c b/arch/x86/events/amd/brs.c
index 06f35a6b58a5..b9a246989bd4 100644
--- a/arch/x86/events/amd/brs.c
+++ b/arch/x86/events/amd/brs.c
@@ -424,7 +424,7 @@ void noinstr perf_amd_brs_lopwr_cb(bool lopwr_in)
}

DEFINE_STATIC_CALL_NULL(perf_lopwr_cb, perf_amd_brs_lopwr_cb);
-EXPORT_STATIC_CALL_TRAMP_GPL(perf_lopwr_cb);
+EXPORT_STATIC_CALL_GPL(perf_lopwr_cb);

void __init amd_brs_lopwr_init(void)
{
diff --git a/arch/x86/kernel/apic/init.c b/arch/x86/kernel/apic/init.c
index 821e2e536f19..933b8d2d3af5 100644
--- a/arch/x86/kernel/apic/init.c
+++ b/arch/x86/kernel/apic/init.c
@@ -30,8 +30,8 @@ DEFINE_APIC_CALL(wakeup_secondary_cpu);
DEFINE_APIC_CALL(wakeup_secondary_cpu_64);
DEFINE_APIC_CALL(write);

-EXPORT_STATIC_CALL_TRAMP_GPL(apic_call_send_IPI_mask);
-EXPORT_STATIC_CALL_TRAMP_GPL(apic_call_send_IPI_self);
+EXPORT_STATIC_CALL_GPL(apic_call_send_IPI_mask);
+EXPORT_STATIC_CALL_GPL(apic_call_send_IPI_self);

/* The container for function call overrides */
struct apic_override __x86_apic_override __initdata;
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index b5b6a58b67b0..9adfc12be1db 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -333,7 +333,7 @@ static void __init x86_setup_ops_for_tsc_pg_clock(void)

#ifdef CONFIG_X86_64
DEFINE_STATIC_CALL(hv_hypercall, hv_std_hypercall);
-EXPORT_STATIC_CALL_TRAMP_GPL(hv_hypercall);
+EXPORT_STATIC_CALL_GPL(hv_hypercall);
#define hypercall_update(hc) static_call_update(hv_hypercall, hc)
#endif
#endif /* CONFIG_HYPERV */
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 0ca3912ecb7f..df05ad454414 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -218,7 +218,7 @@ static inline unsigned long pt_regs_val(struct pt_regs *regs, int nr)

#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
DEFINE_STATIC_CALL(WARN_trap, __WARN_trap);
-EXPORT_STATIC_CALL_TRAMP(WARN_trap);
+EXPORT_STATIC_CALL(WARN_trap);

/*
* Create a va_list from an exception context.
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 792f402f493f..d9d51803b7b2 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -148,9 +148,9 @@ struct kvm_x86_ops kvm_x86_ops __read_mostly;
#define KVM_X86_OP_OPTIONAL KVM_X86_OP
#define KVM_X86_OP_OPTIONAL_RET0 KVM_X86_OP
#include <asm/kvm-x86-ops.h>
-EXPORT_STATIC_CALL_TRAMP_GPL(kvm_x86_get_cs_db_l_bits);
-EXPORT_STATIC_CALL_TRAMP_GPL(kvm_x86_cache_reg);
-EXPORT_STATIC_CALL_TRAMP_GPL(kvm_x86_get_cpl);
+EXPORT_STATIC_CALL_GPL(kvm_x86_get_cs_db_l_bits);
+EXPORT_STATIC_CALL_GPL(kvm_x86_cache_reg);
+EXPORT_STATIC_CALL_GPL(kvm_x86_get_cpl);

static bool __read_mostly ignore_msrs = 0;
module_param(ignore_msrs, bool, 0644);
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 23b91bf9b663..ec14d2017909 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -23,7 +23,7 @@
#include "xen-ops.h"

DEFINE_STATIC_CALL(xen_hypercall, xen_hypercall_hvm);
-EXPORT_STATIC_CALL_TRAMP(xen_hypercall);
+EXPORT_STATIC_CALL(xen_hypercall);

/*
* Pointer to the xen_vcpu_info structure or
diff --git a/include/linux/static_call.h b/include/linux/static_call.h
index 7539c82dd35f..c2c667baf8fe 100644
--- a/include/linux/static_call.h
+++ b/include/linux/static_call.h
@@ -26,7 +26,7 @@
* static_call_update(name, func);
* static_call_query(name);
*
- * EXPORT_STATIC_CALL{,_TRAMP}{,_GPL}()
+ * EXPORT_STATIC_CALL{,_GPL}()
*
* Usage example:
*
@@ -121,14 +121,6 @@
* completely eliding any function call overhead.
*
* Notably argument setup is unconditional.
- *
- *
- * EXPORT_STATIC_CALL() vs EXPORT_STATIC_CALL_TRAMP():
- *
- * The difference is that the _TRAMP variant tries to only export the
- * trampoline with the result that a module can use static_call{,_cond}() but
- * not static_call_update().
- *
*/

#include <linux/types.h>
@@ -214,19 +206,8 @@ extern long __static_call_return0(void);
#define ARCH_ADD_TRAMP_KEY(name)
#endif

-#define EXPORT_STATIC_CALL(name) \
- EXPORT_SYMBOL(STATIC_CALL_KEY(name)); \
- EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
-#define EXPORT_STATIC_CALL_GPL(name) \
- EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name)); \
- EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))
-
-/* Leave the key unexported, so modules can't change static call targets: */
-#define EXPORT_STATIC_CALL_TRAMP(name) \
- EXPORT_SYMBOL(STATIC_CALL_TRAMP(name)); \
- ARCH_ADD_TRAMP_KEY(name)
-#define EXPORT_STATIC_CALL_TRAMP_GPL(name) \
- EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name)); \
+#define __EXPORT_STATIC_CALL(name, scope) \
+ EXPORT_SYMBOL##scope(STATIC_CALL_TRAMP(name)); \
ARCH_ADD_TRAMP_KEY(name)

#elif defined(CONFIG_HAVE_STATIC_CALL)
@@ -274,18 +255,8 @@ static inline int static_call_text_reserved(void *start, void *end)

extern long __static_call_return0(void);

-#define EXPORT_STATIC_CALL(name) \
- EXPORT_SYMBOL(STATIC_CALL_KEY(name)); \
- EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
-#define EXPORT_STATIC_CALL_GPL(name) \
- EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name)); \
- EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))
-
-/* Leave the key unexported, so modules can't change static call targets: */
-#define EXPORT_STATIC_CALL_TRAMP(name) \
- EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
-#define EXPORT_STATIC_CALL_TRAMP_GPL(name) \
- EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name))
+#define __EXPORT_STATIC_CALL(name, scope) \
+ EXPORT_SYMBOL##scope(STATIC_CALL_TRAMP(name))

#else /* Generic implementation */

@@ -348,9 +319,25 @@ static inline int static_call_text_reserved(void *start, void *end)
return 0;
}

-#define EXPORT_STATIC_CALL(name) EXPORT_SYMBOL(STATIC_CALL_KEY(name))
-#define EXPORT_STATIC_CALL_GPL(name) EXPORT_SYMBOL_GPL(STATIC_CALL_KEY(name))
+#define __EXPORT_STATIC_CALL(name, scope)

#endif /* CONFIG_HAVE_STATIC_CALL */

+#define __EXPORT_TRACEPOINT_STATIC_CALL(name, scope) \
+ EXPORT_SYMBOL##scope(STATIC_CALL_KEY(name)); \
+ __EXPORT_STATIC_CALL(name, scope)
+#define EXPORT_TRACEPOINT_STATIC_CALL(name) \
+ __EXPORT_TRACEPOINT_STATIC_CALL(name, )
+#define EXPORT_TRACEPOINT_STATIC_CALL_GPL(name) \
+ __EXPORT_TRACEPOINT_STATIC_CALL(name, _GPL)
+
+/*
+ * For non-tracepoint usage, leave the key unexported, so modules can't change
+ * static call targets, i.e. can only invoke the static call.
+ */
+#define EXPORT_STATIC_CALL(name) \
+ __EXPORT_STATIC_CALL(name, )
+#define EXPORT_STATIC_CALL_GPL(name) \
+ __EXPORT_STATIC_CALL(name, _GPL)
+
#endif /* _LINUX_STATIC_CALL_H */
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 2d2b9f8cdda4..1b64dcaf683e 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -423,12 +423,12 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
TRACEPOINT_CHECK(name) \
EXPORT_SYMBOL_GPL(__tracepoint_##name); \
EXPORT_SYMBOL_GPL(__traceiter_##name); \
- EXPORT_STATIC_CALL_GPL(tp_func_##name)
+ EXPORT_TRACEPOINT_STATIC_CALL_GPL(tp_func_##name)
#define EXPORT_TRACEPOINT_SYMBOL(name) \
TRACEPOINT_CHECK(name) \
EXPORT_SYMBOL(__tracepoint_##name); \
EXPORT_SYMBOL(__traceiter_##name); \
- EXPORT_STATIC_CALL(tp_func_##name)
+ EXPORT_TRACEPOINT_STATIC_CALL(tp_func_##name)


#else /* !TRACEPOINTS_ENABLED */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index b8871449d3c6..c4d0db00d036 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7403,7 +7403,7 @@ EXPORT_SYMBOL(preempt_schedule);
# define preempt_schedule_dynamic_disabled NULL
# endif
DEFINE_STATIC_CALL(preempt_schedule, preempt_schedule_dynamic_enabled);
-EXPORT_STATIC_CALL_TRAMP(preempt_schedule);
+EXPORT_STATIC_CALL(preempt_schedule);
# elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY)
static DEFINE_STATIC_KEY_TRUE(sk_dynamic_preempt_schedule);
void __sched notrace dynamic_preempt_schedule(void)
@@ -7476,7 +7476,7 @@ EXPORT_SYMBOL_GPL(preempt_schedule_notrace);
# define preempt_schedule_notrace_dynamic_disabled NULL
# endif
DEFINE_STATIC_CALL(preempt_schedule_notrace, preempt_schedule_notrace_dynamic_enabled);
-EXPORT_STATIC_CALL_TRAMP(preempt_schedule_notrace);
+EXPORT_STATIC_CALL(preempt_schedule_notrace);
# elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY)
static DEFINE_STATIC_KEY_TRUE(sk_dynamic_preempt_schedule_notrace);
void __sched notrace dynamic_preempt_schedule_notrace(void)
@@ -7723,12 +7723,12 @@ EXPORT_SYMBOL(__cond_resched);
# define cond_resched_dynamic_enabled __cond_resched
# define cond_resched_dynamic_disabled ((void *)&__static_call_return0)
DEFINE_STATIC_CALL_RET0(cond_resched, __cond_resched);
-EXPORT_STATIC_CALL_TRAMP(cond_resched);
+EXPORT_STATIC_CALL(cond_resched);

# define might_resched_dynamic_enabled __cond_resched
# define might_resched_dynamic_disabled ((void *)&__static_call_return0)
DEFINE_STATIC_CALL_RET0(might_resched, __cond_resched);
-EXPORT_STATIC_CALL_TRAMP(might_resched);
+EXPORT_STATIC_CALL(might_resched);
# elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY)
static DEFINE_STATIC_KEY_FALSE(sk_dynamic_cond_resched);
int __sched dynamic_cond_resched(void)
--
2.55.0.rc0.799.gd6f94ed593-goog


--dnmNnJAO7OucAamW
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename=0004-static_call-Add-FOR_MODULES-static-call-exports-use-.patch