[PATCH v2] x86/cpu: Use SERIALIZE in sync_core() when available
From: Ricardo Neri
Date: Tue Aug 04 2020 - 22:11:29 EST
The SERIALIZE instruction gives software a way to force the processor to
complete all modifications to flags, registers and memory from previous
instructions and drain all buffered writes to memory before the next
instruction is fetched and executed. Thus, it serves the purpose of
sync_core(). Use it when available.
Commit 7117f16bf460 ("objtool: Fix ORC vs alternatives") enforced stack
invariance in alternatives. The iret-to-self does not comply with such
invariance. Thus, it cannot be used inside alternative code. Instead, use
an alternative that jumps to SERIALIZE when available.
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
Cc: Cathy Zhang <cathy.zhang@xxxxxxxxx>
Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Cc: Fenghua Yu <fenghua.yu@xxxxxxxxx>
Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
Cc: Kyung Min Park <kyung.min.park@xxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: "Ravi V. Shankar" <ravi.v.shankar@xxxxxxxxx>
Cc: Sean Christopherson <sean.j.christopherson@xxxxxxxxx>
Cc: linux-edac@xxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Suggested-by: Andy Lutomirski <luto@xxxxxxxxxx>
Signed-off-by: Ricardo Neri <ricardo.neri-calderon@xxxxxxxxxxxxxxx>
---
This is a v2 from my initial submission [1]. The first three patches of
the series have been merged in Linus' tree. Hence, I am submitting only
this patch for review.
[1]. https://lkml.org/lkml/2020/7/27/8
Changes since v1:
* Support SERIALIZE using alternative runtime patching.
(Peter Zijlstra, H. Peter Anvin)
* Added a note to specify which version of binutils supports SERIALIZE.
(Peter Zijlstra)
* Verified that (::: "memory") is used. (H. Peter Anvin)
---
arch/x86/include/asm/special_insns.h | 2 ++
arch/x86/include/asm/sync_core.h | 10 +++++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h
index 59a3e13204c3..25cd67801dda 100644
--- a/arch/x86/include/asm/special_insns.h
+++ b/arch/x86/include/asm/special_insns.h
@@ -10,6 +10,8 @@
#include <linux/irqflags.h>
#include <linux/jump_label.h>
+/* Instruction opcode for SERIALIZE; supported in binutils >= 2.35. */
+#define __ASM_SERIALIZE ".byte 0xf, 0x1, 0xe8"
/*
* Volatile isn't enough to prevent the compiler from reordering the
* read/write functions for the control registers and messing everything up.
diff --git a/arch/x86/include/asm/sync_core.h b/arch/x86/include/asm/sync_core.h
index fdb5b356e59b..201ea3d9a6bd 100644
--- a/arch/x86/include/asm/sync_core.h
+++ b/arch/x86/include/asm/sync_core.h
@@ -5,15 +5,19 @@
#include <linux/preempt.h>
#include <asm/processor.h>
#include <asm/cpufeature.h>
+#include <asm/special_insns.h>
#ifdef CONFIG_X86_32
static inline void iret_to_self(void)
{
asm volatile (
+ ALTERNATIVE("", "jmp 2f", X86_FEATURE_SERIALIZE)
"pushfl\n\t"
"pushl %%cs\n\t"
"pushl $1f\n\t"
"iret\n\t"
+ "2:\n\t"
+ __ASM_SERIALIZE "\n"
"1:"
: ASM_CALL_CONSTRAINT : : "memory");
}
@@ -23,6 +27,7 @@ static inline void iret_to_self(void)
unsigned int tmp;
asm volatile (
+ ALTERNATIVE("", "jmp 2f", X86_FEATURE_SERIALIZE)
"mov %%ss, %0\n\t"
"pushq %q0\n\t"
"pushq %%rsp\n\t"
@@ -32,6 +37,8 @@ static inline void iret_to_self(void)
"pushq %q0\n\t"
"pushq $1f\n\t"
"iretq\n\t"
+ "2:\n\t"
+ __ASM_SERIALIZE "\n"
"1:"
: "=&r" (tmp), ASM_CALL_CONSTRAINT : : "cc", "memory");
}
@@ -54,7 +61,8 @@ static inline void iret_to_self(void)
static inline void sync_core(void)
{
/*
- * There are quite a few ways to do this. IRET-to-self is nice
+ * Hardware can do this for us if SERIALIZE is available. Otherwise,
+ * there are quite a few ways to do this. IRET-to-self is nice
* because it works on every CPU, at any CPL (so it's compatible
* with paravirtualization), and it never exits to a hypervisor.
* The only down sides are that it's a bit slow (it seems to be
--
2.17.1