All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC] x86/syscall: Dispatch via sys_call_table when Spectre v2 and BHI are off
@ 2026-10-29 10:27 Breno Leitao
  2026-10-29 10:49 ` Peter Zijlstra
  0 siblings, 1 reply; 4+ messages in thread
From: Breno Leitao @ 2026-10-29 10:27 UTC (permalink / raw)
  To: Andy Lutomirski, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Peter Zijlstra, Josh Poimboeuf,
	Pawan Gupta, Nathan Chancellor, Nick Desaulniers, Bill Wendling,
	Justin Stitt, torvalds
  Cc: linux-kernel, llvm, kernel-team, Breno Leitao

Linus' commit 1e3ad78334a6 ("x86/syscall: Don't force use of indirect
calls for system calls") dispatches system calls with a switch
statement, so that syscall entry has no indirect branch for BHI to
target.

As noted by Linus in the commit above, the compiler can either
decide to generate an indirect jump, or just a series of conditional
branches (which is more expensive).

With CONFIG_MITIGATION_RETPOLINE, clang emits no indirect jump, and the
switch becomes a tree of compares and conditional branches. That code is
built in, so a kernel booted with mitigations=off still walks the tree
on every system call.

When neither the Spectre v2 nor the BHI mitigation is enabled, nothing in
the kernel protects indirect branches, and the switch buys nothing. Use a
static key to call through sys_call_table[] in that case.

The compiler emits that call through a retpoline thunk, which boot-time
patching turns into a plain indirect call when retpolines are off.

On next-20261028 built with clang and booted with mitigations=off on a
Xeon Gold 6138 at a fixed 2.0 GHz, perf bench syscall basic (getppid)
and getpgid. Mean and standard deviation of 30 to 45 runs over 2 to 3
boots, and kernel instructions and cycles per call from perf stat:

                         CPU_MITIGATIONS=n  =y              =y + patch
  getppid()  ns          131.2 +- 0.2       134.1 +- 0.1    131.7 +- 0.4
             insns/cyc   310 / 212.1        331 / 218.1     314 / 213.1
  getpgid()  ns          129.8 +- 0.3       132.5 +- 0.2    130.0 +- 0.5
             insns/cyc   302 / 209.4        323 / 214.1     306 / 209.8

Kernels that enable either mitigation keep the switch, behind one extra
jump.

Signed-off-by: Breno Leitao <leitao@debian.org>
---
The reason behind this change is to make the mitigations a boot command
line option rather than a compilation option.

It is common to find different kernel configurations where the major
difference is CONFIG_CPU_MITIGATIONS.

With this patch, CONFIG_CPU_MITIGATIONS can be enabled at build time, and
when the mitigations are disabled at boot, system call dispatch has no
performance impact.
---
 arch/x86/entry/syscall_64.c    | 15 +++++++++++----
 arch/x86/include/asm/syscall.h |  3 ++-
 arch/x86/kernel/cpu/bugs.c     | 12 ++++++++++++
 3 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/arch/x86/entry/syscall_64.c b/arch/x86/entry/syscall_64.c
index 966d3d1b75868..1da8b6c90171e 100644
--- a/arch/x86/entry/syscall_64.c
+++ b/arch/x86/entry/syscall_64.c
@@ -21,9 +21,8 @@
 #define __SYSCALL_NORETURN __SYSCALL
 
 /*
- * The sys_call_table[] is no longer used for system calls, but
- * kernel/trace/trace_syscalls.c still wants to know the system
- * call address.
+ * Used for dispatch only when x64_syscall_use_table is enabled;
+ * kernel/trace/trace_syscalls.c also wants the system call addresses.
  */
 #define __SYSCALL(nr, sym) __x64_##sym,
 const sys_call_ptr_t sys_call_table[] = {
@@ -31,6 +30,11 @@ const sys_call_ptr_t sys_call_table[] = {
 };
 #undef  __SYSCALL
 
+/*
+ * Decide about syscall dispatch method: x64_sys_call() or sys_call_table
+ */
+DEFINE_STATIC_KEY_FALSE(x64_syscall_use_table);
+
 #define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);
 
 /* The unsigned int @nr argument is intentional as it creates denser code */
@@ -58,7 +62,10 @@ static __always_inline bool do_syscall_x64(struct pt_regs *regs, unsigned long n
 {
 	if (likely(nr < NR_syscalls)) {
 		nr = array_index_nospec(nr, NR_syscalls);
-		regs->ax = x64_sys_call(regs, (unsigned int)nr);
+		if (static_branch_likely(&x64_syscall_use_table))
+			regs->ax = sys_call_table[nr](regs);
+		else
+			regs->ax = x64_sys_call(regs, (unsigned int)nr);
 		return true;
 	}
 	return false;
diff --git a/arch/x86/include/asm/syscall.h b/arch/x86/include/asm/syscall.h
index cfe4251fc1fe3..509449a70febc 100644
--- a/arch/x86/include/asm/syscall.h
+++ b/arch/x86/include/asm/syscall.h
@@ -13,12 +13,13 @@
 #include <uapi/linux/audit.h>
 #include <linux/sched.h>
 #include <linux/err.h>
+#include <linux/jump_label.h>
 #include <asm/thread_info.h>	/* for TS_COMPAT */
 #include <asm/unistd.h>
 
-/* This is used purely for kernel/trace/trace_syscalls.c */
 typedef long (*sys_call_ptr_t)(const struct pt_regs *);
 extern const sys_call_ptr_t sys_call_table[];
+DECLARE_STATIC_KEY_FALSE(x64_syscall_use_table);
 
 /*
  * Only the low 32 bits of orig_ax are meaningful, so we return int.
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 000ebee2d247b..1823b4d963de3 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -32,6 +32,7 @@
 #include <asm/hypervisor.h>
 #include <asm/tlbflush.h>
 #include <asm/cpu.h>
+#include <asm/syscall.h>
 
 #include "cpu.h"
 
@@ -2134,6 +2135,16 @@ static void __init bhi_apply_mitigation(void)
 	setup_force_cpu_cap(X86_FEATURE_CLEAR_BHB_VMEXIT);
 }
 
+static void __init syscall_dispatch_apply_mitigation(void)
+{
+	if (!IS_ENABLED(CONFIG_X86_64) || spectre_v2_enabled != SPECTRE_V2_NONE ||
+	    bhi_mitigation != BHI_MITIGATION_OFF)
+		return;
+
+	pr_info("BHI mitigation off, dispatching syscalls through sys_call_table\n");
+	static_branch_enable(&x64_syscall_use_table);
+}
+
 static void __init spectre_v2_select_mitigation(void)
 {
 	if ((spectre_v2_cmd == SPECTRE_V2_CMD_RETPOLINE ||
@@ -3371,6 +3382,7 @@ void __init cpu_select_mitigations(void)
 	bhi_apply_mitigation();
 	tsa_apply_mitigation();
 	vmscape_apply_mitigation();
+	syscall_dispatch_apply_mitigation();
 }
 
 #ifdef CONFIG_SYSFS

---
base-commit: 6d0933ae9d46674a6ea63a87e9e9ab4e662670c9
base-tree: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master
change-id: 20261028-syscall-table-dispatch-03d30189dc5b

Best regards,
--  
Breno Leitao <leitao@debian.org>


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

end of thread, other threads:[~2026-10-29 12:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 10:27 [PATCH RFC] x86/syscall: Dispatch via sys_call_table when Spectre v2 and BHI are off Breno Leitao
2026-10-29 10:49 ` Peter Zijlstra
2026-10-29 12:30   ` Breno Leitao
2026-10-29 12:39     ` Peter Zijlstra

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.