Skip to content

Commit e04796c

Browse files
ardbiesheuvelwilldeacon
authored andcommitted
arm64/fpsimd: Avoid unnecessary per-CPU buffers for EFI runtime calls
The EFI specification has some elaborate rules about which runtime services may be called while another runtime service call is already in progress. In Linux, however, for simplicity, all EFI runtime service invocations are serialized via the efi_runtime_lock semaphore. This implies that calls to the helper pair arch_efi_call_virt_setup() and arch_efi_call_virt_teardown() are serialized too, and are guaranteed not to nest. Furthermore, the arm64 arch code has its own spinlock to serialize use of the EFI runtime stack, of which only a single instance exists. This all means that the FP/SIMD and SVE state preserve/restore logic in __efi_fpsimd_begin() and __efi_fpsimd_end() are also serialized, and only a single instance of the associated per-CPU variables can ever be in use at the same time. There is therefore no need at all for per-CPU variables here, and they can all be replaced with singleton instances. This saves a non-trivial amount of memory on systems with many CPUs. To be more robust against potential future changes in the core EFI code that may invalidate the reasoning above, move the invocations of __efi_fpsimd_begin() and __efi_fpsimd_end() into the critical section covered by the efi_rt_lock spinlock. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20250318132421.3155799-2-ardb+git@google.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 0af2f6b commit e04796c

File tree

2 files changed

+27
-31
lines changed

2 files changed

+27
-31
lines changed

arch/arm64/kernel/efi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,14 @@ static DEFINE_RAW_SPINLOCK(efi_rt_lock);
169169
void arch_efi_call_virt_setup(void)
170170
{
171171
efi_virtmap_load();
172-
__efi_fpsimd_begin();
173172
raw_spin_lock(&efi_rt_lock);
173+
__efi_fpsimd_begin();
174174
}
175175

176176
void arch_efi_call_virt_teardown(void)
177177
{
178-
raw_spin_unlock(&efi_rt_lock);
179178
__efi_fpsimd_end();
179+
raw_spin_unlock(&efi_rt_lock);
180180
efi_virtmap_unload();
181181
}
182182

arch/arm64/kernel/fpsimd.c

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ static inline void set_sve_default_vl(int val)
180180
set_default_vl(ARM64_VEC_SVE, val);
181181
}
182182

183-
static void __percpu *efi_sve_state;
183+
static u8 *efi_sve_state;
184184

185185
#else /* ! CONFIG_ARM64_SVE */
186186

187187
/* Dummy declaration for code that will be optimised out: */
188-
extern void __percpu *efi_sve_state;
188+
extern u8 *efi_sve_state;
189189

190190
#endif /* ! CONFIG_ARM64_SVE */
191191

@@ -1131,15 +1131,15 @@ static void __init sve_efi_setup(void)
11311131
if (!sve_vl_valid(max_vl))
11321132
goto fail;
11331133

1134-
efi_sve_state = __alloc_percpu(
1135-
SVE_SIG_REGS_SIZE(sve_vq_from_vl(max_vl)), SVE_VQ_BYTES);
1134+
efi_sve_state = kmalloc(SVE_SIG_REGS_SIZE(sve_vq_from_vl(max_vl)),
1135+
GFP_KERNEL);
11361136
if (!efi_sve_state)
11371137
goto fail;
11381138

11391139
return;
11401140

11411141
fail:
1142-
panic("Cannot allocate percpu memory for EFI SVE save/restore");
1142+
panic("Cannot allocate memory for EFI SVE save/restore");
11431143
}
11441144

11451145
void cpu_enable_sve(const struct arm64_cpu_capabilities *__always_unused p)
@@ -1948,10 +1948,10 @@ EXPORT_SYMBOL_GPL(kernel_neon_end);
19481948

19491949
#ifdef CONFIG_EFI
19501950

1951-
static DEFINE_PER_CPU(struct user_fpsimd_state, efi_fpsimd_state);
1952-
static DEFINE_PER_CPU(bool, efi_fpsimd_state_used);
1953-
static DEFINE_PER_CPU(bool, efi_sve_state_used);
1954-
static DEFINE_PER_CPU(bool, efi_sm_state);
1951+
static struct user_fpsimd_state efi_fpsimd_state;
1952+
static bool efi_fpsimd_state_used;
1953+
static bool efi_sve_state_used;
1954+
static bool efi_sm_state;
19551955

19561956
/*
19571957
* EFI runtime services support functions
@@ -1984,18 +1984,16 @@ void __efi_fpsimd_begin(void)
19841984
* If !efi_sve_state, SVE can't be in use yet and doesn't need
19851985
* preserving:
19861986
*/
1987-
if (system_supports_sve() && likely(efi_sve_state)) {
1988-
char *sve_state = this_cpu_ptr(efi_sve_state);
1987+
if (system_supports_sve() && efi_sve_state != NULL) {
19891988
bool ffr = true;
19901989
u64 svcr;
19911990

1992-
__this_cpu_write(efi_sve_state_used, true);
1991+
efi_sve_state_used = true;
19931992

19941993
if (system_supports_sme()) {
19951994
svcr = read_sysreg_s(SYS_SVCR);
19961995

1997-
__this_cpu_write(efi_sm_state,
1998-
svcr & SVCR_SM_MASK);
1996+
efi_sm_state = svcr & SVCR_SM_MASK;
19991997

20001998
/*
20011999
* Unless we have FA64 FFR does not
@@ -2005,19 +2003,18 @@ void __efi_fpsimd_begin(void)
20052003
ffr = !(svcr & SVCR_SM_MASK);
20062004
}
20072005

2008-
sve_save_state(sve_state + sve_ffr_offset(sve_max_vl()),
2009-
&this_cpu_ptr(&efi_fpsimd_state)->fpsr,
2010-
ffr);
2006+
sve_save_state(efi_sve_state + sve_ffr_offset(sve_max_vl()),
2007+
&efi_fpsimd_state.fpsr, ffr);
20112008

20122009
if (system_supports_sme())
20132010
sysreg_clear_set_s(SYS_SVCR,
20142011
SVCR_SM_MASK, 0);
20152012

20162013
} else {
2017-
fpsimd_save_state(this_cpu_ptr(&efi_fpsimd_state));
2014+
fpsimd_save_state(&efi_fpsimd_state);
20182015
}
20192016

2020-
__this_cpu_write(efi_fpsimd_state_used, true);
2017+
efi_fpsimd_state_used = true;
20212018
}
20222019
}
20232020

@@ -2029,12 +2026,10 @@ void __efi_fpsimd_end(void)
20292026
if (!system_supports_fpsimd())
20302027
return;
20312028

2032-
if (!__this_cpu_xchg(efi_fpsimd_state_used, false)) {
2029+
if (!efi_fpsimd_state_used) {
20332030
kernel_neon_end();
20342031
} else {
2035-
if (system_supports_sve() &&
2036-
likely(__this_cpu_read(efi_sve_state_used))) {
2037-
char const *sve_state = this_cpu_ptr(efi_sve_state);
2032+
if (system_supports_sve() && efi_sve_state_used) {
20382033
bool ffr = true;
20392034

20402035
/*
@@ -2043,7 +2038,7 @@ void __efi_fpsimd_end(void)
20432038
* streaming mode.
20442039
*/
20452040
if (system_supports_sme()) {
2046-
if (__this_cpu_read(efi_sm_state)) {
2041+
if (efi_sm_state) {
20472042
sysreg_clear_set_s(SYS_SVCR,
20482043
0,
20492044
SVCR_SM_MASK);
@@ -2057,14 +2052,15 @@ void __efi_fpsimd_end(void)
20572052
}
20582053
}
20592054

2060-
sve_load_state(sve_state + sve_ffr_offset(sve_max_vl()),
2061-
&this_cpu_ptr(&efi_fpsimd_state)->fpsr,
2062-
ffr);
2055+
sve_load_state(efi_sve_state + sve_ffr_offset(sve_max_vl()),
2056+
&efi_fpsimd_state.fpsr, ffr);
20632057

2064-
__this_cpu_write(efi_sve_state_used, false);
2058+
efi_sve_state_used = false;
20652059
} else {
2066-
fpsimd_load_state(this_cpu_ptr(&efi_fpsimd_state));
2060+
fpsimd_load_state(&efi_fpsimd_state);
20672061
}
2062+
2063+
efi_fpsimd_state_used = false;
20682064
}
20692065
}
20702066

0 commit comments

Comments
 (0)