arm64: Implement parallel CPU onlining with PSCI v0.2+
Subsystems (6)
Will Deacon
maintainerv2 cover arm64: Implement parallel CPU onlining with PSCI v0.2+
Hi everyone,
This is version two of the patches I previously posted here:
https://lore.kernel.org/all/[email protected]/
There have been quite a few changes since last time, thanks to all the feedback from humans and robots alike. These include:
* Added tags (but I didn't carry over Pankaj's T-b, since the series has changed a fair bit).
* Fix secondary entry assembly code to handle timeouts correctly.
* Fix stashing of MPIDR so that we don't clobber STACK_END_MAGIC.
* Fix NULL dereference if PSCI fails to initialise.
* Enable RCU as soon as a secondary CPU is alive.
* Drop pointless (unreachable) optimisation to
arch_cpuhp_sync_state_poll().
* Include cpufeature patches from Fuad so that 'cpu_data' is populated with register values taking into account any cmdline overrides. This is necessary for correct handling of 'boot_cpu_data' later on, where the previous version of this series stashed a stale copy of mpamidr.
As before, the first four patches are small changes to the generic code to make it a little more "Arm-shaped" but the rest of the series is largely confined to the arm64 architecture and PSCI driver code.
The series is based on -rc2, as it otherwise conflicts with Fuad's GMID fix that was recently merged upstream.
Cheers,
Will
--->8
Fuad Tabba (4):
arm64: cpufeature: Check arm64_ftr_regs[] before the first store
arm64: cpufeature: Add read_cpuid_with_overrides()
arm64: cpufeature: Read MPAMIDR_EL1 in __cpuinfo_store_cpu()
arm64: cpufeature: Store every ID register with its overrides applied
Will Deacon (19):
cpu/hotplug: Clean up cmpxchg() logic in cpuhp_can_boot_ap() cpu/hotplug: Avoid trying to bring up CPUs that are already online cpu/hotplug: Avoid busy-polling on archs where cpu_relax() is a no-op cpu/hotplug: Propagate bring-up status to
arch_cpuhp_cleanup_kick_cpu()
arm64: smp: Tidy up smp_prepare_cpus()
arm64: smp: Tidy up cpuinfo init and cpufeature updates
arm64: smp: Defer update of secondary CPU capabilities
arm64: smp: Don't bother printing the I-cache policy for each CPU
arm64: smp: Defer RCU registration during secondary CPU bringup
arm64: smp: Use generic HOTPLUG_CORE_SYNC_FULL machinery for CPU
onlining
arm64: smp: Use generic HOTPLUG_SPLIT_STARTUP machinery for CPU
onlining
arm64: cpu_ops: Make 'cpu_operations' pointer global instead of
per-cpu
arm64: cpu_ops: Introduce get_secondary_cpu_ops() firmware/psci: Cache PSCI v0.2+ version number to avoid redundant SMCs firmware/psci: Extend ->cpu_on() callback to take an additional
argument
arm64: cpu_ops: Expose optional argument to target cpu in ->cpu_boot()
arm64: smp: Pass secondary CPU boot parameters via firmware if
possible
arm64: smp: Use generic HOTPLUG_PARALLEL machinery for CPU onlining
arm64: smp: Harden parallel CPU bringup against broken PSCI firmware
arch/arm/kernel/psci_smp.c | 4 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cpu.h | 7 +- arch/arm64/include/asm/cpu_ops.h | 7 +- arch/arm64/include/asm/cpufeature.h | 11 + arch/arm64/include/asm/smp.h | 40 ++- arch/arm64/include/asm/topology.h | 2 + arch/arm64/kernel/acpi_parking_protocol.c | 3 +- arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kernel/cpu_ops.c | 34 +- arch/arm64/kernel/cpufeature.c | 397 +++++++++++----------- arch/arm64/kernel/cpuinfo.c | 122 +++---- arch/arm64/kernel/head.S | 60 +++- arch/arm64/kernel/psci.c | 14 +- arch/arm64/kernel/smp.c | 183 +++++----- arch/arm64/kernel/smp_spin_table.c | 2 +- arch/arm64/mm/mmu.c | 2 +- arch/x86/kernel/smpboot.c | 4 +- drivers/firmware/psci/psci.c | 27 +- include/linux/cpuhotplug.h | 2 +- include/linux/psci.h | 3 +- include/linux/rcutree.h | 2 +- kernel/cpu.c | 18 +- 23 files changed, 508 insertions(+), 439 deletions(-)
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 1/23 cpu/hotplug: Clean up cmpxchg() logic in cpuhp_can_boot_ap()
cpuhp_can_boot_ap() uses atomic_try_cmpxchg() to transition the sync state of the incoming CPU to SYNC_STATE_KICKED. However, this is unnecessary if the state is SYNC_STATE_DEAD, since there will not be any concurrent state modifications, and also if the state is already set to SYNC_STATE_KICKED.
Restrict the use of cmpxchg() to the case where the existing state is SYNC_STATE_ALIVE.
--- kernel/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
kernel/cpu.c +4 -4
@@ -408,22 +408,22 @@ static bool cpuhp_can_boot_ap(unsigned int cpu)switch (sync) {case SYNC_STATE_DEAD :/* CPU is properly dead */ +atomic_set (st,SYNC_STATE_KICKED );break ;case SYNC_STATE_KICKED :/* CPU did not come up in previous attempt */ break ;case SYNC_STATE_ALIVE :/* CPU is stuck cpuhp_ap_sync_alive(). */ +if (!atomic_try_cmpxchg_relaxed (st, &sync,SYNC_STATE_KICKED )) + goto again;break ;default :/* CPU failed to report online or dead and is in limbo state. */ return false; } -/* Prepare for booting */ -if (!atomic_try_cmpxchg (st, &sync,SYNC_STATE_KICKED )) - goto again; - +/* Continue with booting */ return true; }
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 2/23 cpu/hotplug: Avoid trying to bring up CPUs that are already online
In reply to Will DeaconThere's little point trying to bring up a CPU that is already online. Although _cpu_up() handles this case by doing nothing (because the target state has already been reached), it's wasted effort when we can easily elide the call to cpu_up() in the first place.
Check that the target CPU isn't already online before invoking cpu_up() from cpuhp_bringup_mask().
--- kernel/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/cpu.c +2 -1
@@ -1769,7 +1769,8 @@ static void __init cpuhp_bringup_mask(const struct cpumask *mask, unsigned int nfor_each_cpu (cpu, mask) {struct cpuhp_cpu_state *st =per_cpu_ptr (&cpuhp_state, cpu); -if (cpu_up (cpu, target) &&can_rollback_cpu (st)) { +if (!cpu_online (cpu) &&cpu_up (cpu, target) && +can_rollback_cpu (st)) {/* * If this failed then cpu_up() might have only * rolled back to CPUHP_BP_KICK_AP for the final
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Thomas Gleixner
maintainerRe: [PATCH v2 02/23] cpu/hotplug: Avoid trying to bring up CPUs that are already online
Quoted 4 lines · Will Deacon
There's little point trying to bring up a CPU that is already online. Although
_cpu_up()handles this case by doing nothing (because the target state has already been reached), it's wasted effort when we can easily elide the call tocpu_up()in the first place.
s/we can ../the call can be .../
Quoted 2 lines
Check that the target CPU isn't already online before invoking
cpu_up()fromcpuhp_bringup_mask().
Confused.
cpuhp_bringup_mask() is only invoked from bringup_nonboot_cpus() during init. So there is only _ONE_ CPU which can be online and in the present mask, i.e. the boot CPU, right? Any other CPU than that being online would be a bug, no?
Can you spell that out please and add a comment to that effect?
I'm not opposed to the change per se, but I doubt that the "win" is actually measurable as you trade that one pointless call against a conditional which has to be executed NR_CPUS times, where NR_CPUS-1 times the conditinal is pointless. There is ZERO contention at that point, so what's the actual benefit?
Thanks,
tglx
Will Deacon
maintainerPATCH v2 3/23 cpu/hotplug: Avoid busy-polling on archs where cpu_relax() is a no-op
In reply to Will DeaconOn some architectures (such as arm64), cpu_relax() is effectively a NOP and so isn't particularly efficient when used in a tight polling loop such as the CPU state synchronisation in cpuhp_ap_sync_alive().
Once an incoming CPU has reached the SYNC_STATE_ALIVE state, we know that it is executing within the kernel and so we can use the more efficient polling mechanism provided by the atomic_cond_read* API, allowing the use of WFE on arm64 and Zawrs on RISC-V.
Change cpuhp_ap_sync_alive() to poll using atomic_cond_read_relaxed() instead of cpu_relax(). No change on x86.
--- kernel/cpu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/cpu.c +1 -2
@@ -395,8 +395,7 @@ void cpuhp_ap_sync_alive(void)cpuhp_ap_update_sync_state (SYNC_STATE_ALIVE );/* Wait for the control CPU to release it. */ -while (atomic_read (st) !=SYNC_STATE_SHOULD_ONLINE ) -cpu_relax (); +atomic_cond_read_acquire (st,VAL ==SYNC_STATE_SHOULD_ONLINE ); }static bool cpuhp_can_boot_ap (unsigned int cpu)
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Re: [PATCH v2 03/23] cpu/hotplug: Avoid busy-polling on archs where cpu_relax() is a no-op
Quoted 1 line · Will Deacon
Change
cpuhp_ap_sync_alive()to poll usingatomic_cond_read_relaxed()
relaxed.
...
Quoted 1 line
+
atomic_cond_read_acquire(st, VAL == SYNC_STATE_SHOULD_ONLINE);
acquire.
Will Deacon
maintainerRe: [PATCH v2 03/23] cpu/hotplug: Avoid busy-polling on archs where cpu_relax() is a no-op
Quoted 10 lines · David Woodhouse
Quoted 1 line · Will Deacon
Change
cpuhp_ap_sync_alive()to poll usingatomic_cond_read_relaxed()relaxed.
...
Quoted 1 line
+
atomic_cond_read_acquire(st, VAL == SYNC_STATE_SHOULD_ONLINE);acquire.
Oops, I'll fix the commit message. Thanks. I think we need acquire semantics here so that the AP sees anything published by the BP prior to advancing the state.
Will
Will Deacon
maintainerPATCH v2 4/23 cpu/hotplug: Propagate bring-up status to arch_cpuhp_cleanup_kick_cpu()
In reply to Will DeaconIn preparation for enabling the generic CPU hotplug machinery on arm64, which has architecture-specific handling of early bringup failures, extend arch_cpuhp_cleanup_kick_cpu() to take an additional argument indicating whether or not the target AP reached the alive state.
--- arch/x86/kernel/smpboot.c | 4 ++-- include/linux/cpuhotplug.h | 2 +- kernel/cpu.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-)
arch/x86/kernel/smpboot.c +2 -2
@@ -1074,7 +1074,7 @@ static int do_boot_cpu(u32 apicid, unsigned int cpu, struct task_struct *idle)/* If the wakeup mechanism failed, cleanup the warm reset vector */ if (ret) -arch_cpuhp_cleanup_kick_cpu (cpu); +arch_cpuhp_cleanup_kick_cpu (cpu, false);return ret; }
@@ -1122,7 +1122,7 @@ int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle)return smp_ops.kick_ap_alive (cpu, tidle); } -void arch_cpuhp_cleanup_kick_cpu (unsigned int cpu) +void arch_cpuhp_cleanup_kick_cpu (unsigned int cpu,bool is_alive) {/* Cleanup possible dangling ends... */ if (smp_ops.kick_ap_alive == native_kick_ap && x86_platform.legacy.warm_reset)
include/linux/cpuhotplug.h +1 -1
@@ -510,7 +510,7 @@ struct task_struct;void cpuhp_ap_sync_alive (void );void arch_cpuhp_sync_state_poll (void ); -void arch_cpuhp_cleanup_kick_cpu (unsigned int cpu); +void arch_cpuhp_cleanup_kick_cpu (unsigned int cpu,bool is_alive);int arch_cpuhp_kick_ap_alive (unsigned int cpu,struct task_struct *tidle);bool arch_cpuhp_init_parallel_bringup (void );
kernel/cpu.c +2 -2
@@ -426,7 +426,7 @@ static bool cpuhp_can_boot_ap(unsigned int cpu)return true; } -void __weak arch_cpuhp_cleanup_kick_cpu (unsigned int cpu) { } +void __weak arch_cpuhp_cleanup_kick_cpu (unsigned int cpu,bool is_alive) { }/* * Early CPU bringup synchronization point. Cannot use cpuhp_state::done_up
@@ -445,7 +445,7 @@ static int cpuhp_bp_sync_alive(unsigned int cpu) }/* Let the architecture cleanup the kick alive mechanics. */ -arch_cpuhp_cleanup_kick_cpu (cpu); +arch_cpuhp_cleanup_kick_cpu (cpu, !ret);return ret; }#else /* CONFIG_HOTPLUG_CORE_SYNC_FULL */
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 5/23 arm64: cpufeature: Check arm64_ftr_regs[] before the first store
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
sort_ftr_regs() sorts nothing. It checks that arm64_ftr_regs[] is in sys_id order, which the binary search in get_arm64_ftr_reg() relies on, and that no register's fields overlap. It runs at the top of init_cpu_features(), after the boot CPU's __cpuinfo_store_cpu(). A subsequent patch makes __cpuinfo_store_cpu() look registers up in the table.
Rename it validate_ftr_regs() and call it from smp_prepare_boot_cpu(), ahead of cpuinfo_store_boot_cpu().
--- arch/arm64/include/asm/cpu.h | 1 + arch/arm64/kernel/cpufeature.c | 5 +---- arch/arm64/kernel/smp.c | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/include/asm/cpu.h +1 -0
@@ -75,6 +75,7 @@ DECLARE_PER_CPU(struct cpuinfo_arm64, cpu_data);void cpuinfo_store_cpu (void );void __init cpuinfo_store_boot_cpu (void ); +void __init validate_ftr_regs (void );void __init init_cpu_features (struct cpuinfo_arm64 *info);void update_cpu_features (int cpu,struct cpuinfo_arm64 *info,struct cpuinfo_arm64 *boot);
arch/arm64/kernel/cpufeature.c +1 -4
@@ -962,7 +962,7 @@ s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,return ret; } -static void __init sort_ftr_regs (void ) +void __init validate_ftr_regs (void ) {unsigned int i;
@@ -1207,9 +1207,6 @@ bool gmid_el1_accessible(const struct cpuinfo_arm64 *info)void __init init_cpu_features (struct cpuinfo_arm64 *info) { -/* Before we start using the tables, make sure it is sorted */ -sort_ftr_regs (); -init_cpu_ftr_reg (SYS_CTR_EL0 , info->reg_ctr);init_cpu_ftr_reg (SYS_DCZID_EL0 , info->reg_dczid);init_cpu_ftr_reg (SYS_CNTFRQ_EL0 , info->reg_cntfrq);
arch/arm64/kernel/smp.c +2 -0
@@ -455,6 +455,8 @@ void __init smp_prepare_boot_cpu(void)*/ set_my_cpu_offset (per_cpu_offset (smp_processor_id ())); +/* Check arm64_ftr_regs[] before anything looks registers up in it. */ +validate_ftr_regs ();cpuinfo_store_boot_cpu ();setup_boot_cpu_features ();
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Re: [PATCH v2 05/23] arm64: cpufeature: Check arm64_ftr_regs[] before the first store
Quoted 5 lines · Will Deacon
From: Fuad Tabba <[email protected]>
sort_ftr_regs()sorts nothing. It checks that arm64_ftr_regs[] is insys_idorder, which the binary search inget_arm64_ftr_reg()relies on, and that no register's fields overlap. It runs at the top of
It also checks whether the register field extends beyond 64 bits
Quoted 56 lines
init_cpu_features(), after the boot CPU's__cpuinfo_store_cpu(). A subsequent patch makes__cpuinfo_store_cpu()look registers up in the table.Rename it
validate_ftr_regs()and call it fromsmp_prepare_boot_cpu(), ahead ofcpuinfo_store_boot_cpu().Suggested-by Will Deacon <[email protected]>Signed-off-by Fuad Tabba <[email protected]>Signed-off-by Will Deacon <[email protected]>--- arch/arm64/include/asm/cpu.h | 1 + arch/arm64/kernel/cpufeature.c | 5 +---- arch/arm64/kernel/smp.c | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-)arch/arm64/include/asm/cpu.h +1
-0@@ -75,6 +75,7 @@ DECLARE_PER_CPU(struct cpuinfo_arm64, cpu_data);void cpuinfo_store_cpu (void );void __init cpuinfo_store_boot_cpu (void ); +void __init validate_ftr_regs (void );void __init init_cpu_features (struct cpuinfo_arm64 *info);void update_cpu_features (int cpu,struct cpuinfo_arm64 *info,struct cpuinfo_arm64 *boot);arch/arm64/kernel/cpufeature.c +1
-4@@ -962,7 +962,7 @@ s64 arm64_ftr_safe_value(const struct arm64_ftr_bits *ftrp, s64 new,return ret; } -static void __init sort_ftr_regs (void ) +void __init validate_ftr_regs (void ) {unsigned int i;@@ -1207,9 +1207,6 @@ bool gmid_el1_accessible(const struct cpuinfo_arm64 *info)void __init init_cpu_features (struct cpuinfo_arm64 *info) { -/* Before we start using the tables, make sure it is sorted */ -sort_ftr_regs (); -init_cpu_ftr_reg (SYS_CTR_EL0 , info->reg_ctr);init_cpu_ftr_reg (SYS_DCZID_EL0 , info->reg_dczid);init_cpu_ftr_reg (SYS_CNTFRQ_EL0 , info->reg_cntfrq);arch/arm64/kernel/smp.c +0
-0
arch/arm64/kernel/smp.c void __init smp_prepare_boot_cpu(void) · line 459
Quoted 5 lines
arch/arm64/kernel/smp.c +2
-0@@ -455,6 +455,8 @@ void __init smp_prepare_boot_cpu(void)*/ set_my_cpu_offset (per_cpu_offset (smp_processor_id ())); +/* Check arm64_ftr_regs[] before anything looks registers up in it. */ +validate_ftr_regs ();
Quoted 3 lines
arch/arm64/kernel/smp.c +0
-0cpuinfo_store_boot_cpu ();setup_boot_cpu_features ();
Signature · 2 lines
Best regards, Jinjie
Will Deacon
maintainerPATCH v2 6/23 arm64: cpufeature: Add read_cpuid_with_overrides()
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
__cpuinfo_store_cpu() stores the ID registers raw. Its gates on reading a feature's register test those raw values. The exception is gmid_el1_accessible(), which open-codes MTE's command-line override. It checks the override with arm64_ftr_safe_value(), since init_cpu_features() hasn't yet removed an unsafe one. The other gates still read a feature's register when the command line disabled it. That's the read that trapped on GMID_EL1.
Add read_cpuid_with_overrides(), which applies every valid override to any register in arm64_ftr_regs[]. An override is valid when arm64_ftr_safe_value() returns it unchanged. validate_ftr_regs() has checked the table by the first store, so the lookup is safe there. arm64_apply_feature_override() can't do this: it takes one field and no descriptor to check the override against.
Store ID_AA64PFR1_EL1 through it, and replace gmid_el1_accessible() with a test on the stored value at its three callers, as for SME.
Other readers of the stored value see the override too. With arm64.nosme, __cpuinfo_store_cpu() no longer reads SMIDR_EL1. CPUs that differ only in an ID_AA64PFR1_EL1 field the command line turned off no longer taint the kernel. init_cpu_ftr_reg() logs a valid override as "already set" rather than "forced".
The helper is Suzuki's override_cpu_ftr_reg(), without its other two call sites.
--- arch/arm64/include/asm/cpu.h | 1 - arch/arm64/include/asm/cpufeature.h | 11 ++++++ arch/arm64/kernel/cpufeature.c | 57 ++++++++++++++--------------- arch/arm64/kernel/cpuinfo.c | 4 +- 4 files changed, 41 insertions(+), 32 deletions(-)
arch/arm64/include/asm/cpu.h +0 -1
@@ -79,6 +79,5 @@ void __init validate_ftr_regs(void);void __init init_cpu_features (struct cpuinfo_arm64 *info);void update_cpu_features (int cpu,struct cpuinfo_arm64 *info,struct cpuinfo_arm64 *boot); -bool gmid_el1_accessible (const struct cpuinfo_arm64 *info);#endif /* __ASM_CPU_H */
arch/arm64/include/asm/cpufeature.h +11 -0
@@ -627,6 +627,13 @@ static inline bool id_aa64pfr1_mpamfrac(u64 pfr1)return val >0 ; } +static inline bool id_aa64pfr1_mte (u64 pfr1) +{ +u32 val =cpuid_feature_extract_unsigned_field (pfr1,ID_AA64PFR1_EL1_MTE_SHIFT ); + +return val >=ID_AA64PFR1_EL1_MTE_MTE2 ; +} +void __init setup_boot_cpu_features (void );void __init setup_system_features (void );void __init setup_user_features (void );
@@ -957,6 +964,10 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)s64 arm64_ftr_safe_value (const struct arm64_ftr_bits *ftrp,s64 new,s64 cur);struct arm64_ftr_reg *get_arm64_ftr_reg (u32 sys_id); +u64 arm64_ftr_reg_override (u32 sys_id,u64 val); + +/* Read this CPU's ID register with each valid command-line override applied. */ +#define read_cpuid_with_overrides (reg) arm64_ftr_reg_override(SYS_##reg, read_cpuid(reg))extern struct arm64_ftr_override id_aa64mmfr0_override;extern struct arm64_ftr_override id_aa64mmfr1_override;
arch/arm64/kernel/cpufeature.c +28 -29
81 lines not on this page · raw message
arch/arm64/kernel/cpuinfo.c +2 -2
@@ -496,13 +496,13 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64mmfr3 =read_cpuid (ID_AA64MMFR3_EL1 ); info->reg_id_aa64mmfr4 =read_cpuid (ID_AA64MMFR4_EL1 ); info->reg_id_aa64pfr0 =read_cpuid (ID_AA64PFR0_EL1 ); - info->reg_id_aa64pfr1 =read_cpuid (ID_AA64PFR1_EL1 ); + info->reg_id_aa64pfr1 =read_cpuid_with_overrides (ID_AA64PFR1_EL1 ); info->reg_id_aa64pfr2 =read_cpuid (ID_AA64PFR2_EL1 ); info->reg_id_aa64zfr0 =read_cpuid (ID_AA64ZFR0_EL1 ); info->reg_id_aa64smfr0 =read_cpuid (ID_AA64SMFR0_EL1 ); info->reg_id_aa64fpfr0 =read_cpuid (ID_AA64FPFR0_EL1 ); -if (gmid_el1_accessible (info)) +if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) info->reg_gmid =read_cpuid (GMID_EL1 );if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0))
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 7/23 arm64: cpufeature: Read MPAMIDR_EL1 in __cpuinfo_store_cpu()
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
MPAMIDR_EL1 is the one ID register __cpuinfo_store_cpu() doesn't read. Its read was deferred to init_cpu_features() and update_cpu_features() because it traps on firmware that fails to enable MPAM. Only the sanitised ID_AA64PFR0_EL1 and ID_AA64PFR1_EL1 carried the arm64.nompam override for such firmware.
Store ID_AA64PFR0_EL1 through read_cpuid_with_overrides() as well. Then read MPAMIDR_EL1 in __cpuinfo_store_cpu() again, beside GMID_EL1, gated on this CPU's overridden ID_AA64PFR0_EL1 and ID_AA64PFR1_EL1. cpu_data is then complete before init_cpu_features() runs.
Other readers of the stored ID_AA64PFR0_EL1 see the override too. With arm64.no32bit_el0, __cpuinfo_store_cpu() no longer reads the AArch32 ID registers. Adding allow_mismatched_32bit_el0 no longer enables 32-bit EL0 either.
detect_ftr_has_mpam(), a system-wide test on the sanitised values, stays for the ARM64_MPAM capability.
--- arch/arm64/kernel/cpufeature.c | 10 +++------- arch/arm64/kernel/cpuinfo.c | 11 ++++------- 2 files changed, 7 insertions(+), 14 deletions(-)
arch/arm64/kernel/cpufeature.c +3 -7
@@ -1248,10 +1248,8 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)cpacr_restore (cpacr); } -if (detect_ftr_has_mpam ()) { - info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1))init_cpu_ftr_reg (SYS_MPAMIDR_EL1 , info->reg_mpamidr); - }if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1))init_cpu_ftr_reg (SYS_GMID_EL1 , info->reg_gmid);
@@ -1504,11 +1502,9 @@ void update_cpu_features(int cpu,cpacr_restore (cpacr); } -if (detect_ftr_has_mpam ()) { - info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) taint |=check_update_ftr_reg (SYS_MPAMIDR_EL1 , cpu, - info->reg_mpamidr, boot->reg_mpamidr); - } + info->reg_mpamidr, boot->reg_mpamidr);/* * The kernel uses the LDGM/STGM instructions and the number of tags
arch/arm64/kernel/cpuinfo.c +4 -7
@@ -495,7 +495,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64mmfr2 =read_cpuid (ID_AA64MMFR2_EL1 ); info->reg_id_aa64mmfr3 =read_cpuid (ID_AA64MMFR3_EL1 ); info->reg_id_aa64mmfr4 =read_cpuid (ID_AA64MMFR4_EL1 ); - info->reg_id_aa64pfr0 =read_cpuid (ID_AA64PFR0_EL1 ); + info->reg_id_aa64pfr0 =read_cpuid_with_overrides (ID_AA64PFR0_EL1 ); info->reg_id_aa64pfr1 =read_cpuid_with_overrides (ID_AA64PFR1_EL1 ); info->reg_id_aa64pfr2 =read_cpuid (ID_AA64PFR2_EL1 ); info->reg_id_aa64zfr0 =read_cpuid (ID_AA64ZFR0_EL1 );
@@ -505,15 +505,12 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) info->reg_gmid =read_cpuid (GMID_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) + info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0))__cpuinfo_store_cpu_32bit (&info->aarch32); -/* -* info->reg_mpamidr deferred to {init,update}_cpu_features because we -* don't want to read it (and trigger a trap on buggy firmware) if -* using an aa64pfr0_el1 override to unconditionally disable MPAM. -*/ -if (IS_ENABLED (CONFIG_ARM64_SME ) &&id_aa64pfr1_sme (info->reg_id_aa64pfr1)) {/*
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Re: [PATCH v2 07/23] arm64: cpufeature: Read MPAMIDR_EL1 in __cpuinfo_store_cpu()
Hi Will,
Quoted 33 lines · Will Deacon
From: Fuad Tabba <[email protected]>
MPAMIDR_EL1is the one ID register__cpuinfo_store_cpu()doesn't read. Its read was deferred toinit_cpu_features()andupdate_cpu_features()because it traps on firmware that fails to enable MPAM. Only the sanitisedID_AA64PFR0_EL1andID_AA64PFR1_EL1carried the arm64.nompam override for such firmware.Store
ID_AA64PFR0_EL1throughread_cpuid_with_overrides()as well. Then readMPAMIDR_EL1in__cpuinfo_store_cpu()again, besideGMID_EL1, gated on this CPU's overriddenID_AA64PFR0_EL1andID_AA64PFR1_EL1.cpu_datais then complete beforeinit_cpu_features()runs.Other readers of the stored
ID_AA64PFR0_EL1see the override too. With arm64.no32bit_el0,__cpuinfo_store_cpu()no longer reads the AArch32 ID registers. Addingallow_mismatched_32bit_el0no longer enables 32-bit EL0 either.
detect_ftr_has_mpam(), a system-wide test on the sanitised values, stays for theARM64_MPAMcapability.Suggested-by Will Deacon <[email protected]>Signed-off-by Fuad Tabba <[email protected]>Signed-off-by Will Deacon <[email protected]>--->
arch/arm64/kernel/cpufeature.c| 10 +++-------arch/arm64/kernel/cpuinfo.c | 11 ++++------- 2 files changed, 7 insertions(+), 14 deletions(-)arch/arm64/kernel/cpufeature.c +0
-0
arch/arm64/kernel/cpufeature.c void __init init_cpu_features(struct cpuinfo_arm64 *info) · line 1252
Quoted 7 lines
arch/arm64/kernel/cpufeature.c +1
-2@@ -1248,10 +1248,8 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)cpacr_restore (cpacr); } -if (detect_ftr_has_mpam ()) { - info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1))init_cpu_ftr_reg (SYS_MPAMIDR_EL1 , info->reg_mpamidr);
info->reg_mpamidr isn't useful if we aren't going to use MPAM so I suppose all the info->reg_mpamidr accesses should be additionally guarded with IS_ENABLED(CONFIG_ARM64_MPAM) as already done for MTE. That's a separate change though and what you've got here looks good to me. So, FWIW,
Thanks,
Ben
Quoted 50 lines
arch/arm64/kernel/cpufeature.c +2
-5- }if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1))init_cpu_ftr_reg (SYS_GMID_EL1 , info->reg_gmid);@@ -1504,11 +1502,9 @@ void update_cpu_features(int cpu,cpacr_restore (cpacr); } -if (detect_ftr_has_mpam ()) { - info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) taint |=check_update_ftr_reg (SYS_MPAMIDR_EL1 , cpu, - info->reg_mpamidr, boot->reg_mpamidr); - } + info->reg_mpamidr, boot->reg_mpamidr);/* * The kernel uses the LDGM/STGM instructions and the number of tags arch/arm64/kernel/cpuinfo.c +4
-7@@ -495,7 +495,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64mmfr2 =read_cpuid (ID_AA64MMFR2_EL1 ); info->reg_id_aa64mmfr3 =read_cpuid (ID_AA64MMFR3_EL1 ); info->reg_id_aa64mmfr4 =read_cpuid (ID_AA64MMFR4_EL1 ); - info->reg_id_aa64pfr0 =read_cpuid (ID_AA64PFR0_EL1 ); + info->reg_id_aa64pfr0 =read_cpuid_with_overrides (ID_AA64PFR0_EL1 ); info->reg_id_aa64pfr1 =read_cpuid_with_overrides (ID_AA64PFR1_EL1 ); info->reg_id_aa64pfr2 =read_cpuid (ID_AA64PFR2_EL1 ); info->reg_id_aa64zfr0 =read_cpuid (ID_AA64ZFR0_EL1 );@@ -505,15 +505,12 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) info->reg_gmid =read_cpuid (GMID_EL1 ); +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) + info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); +if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0))__cpuinfo_store_cpu_32bit (&info->aarch32); -/* -* info->reg_mpamidr deferred to {init,update}_cpu_features because we -* don't want to read it (and trigger a trap on buggy firmware) if -* using an aa64pfr0_el1 override to unconditionally disable MPAM. -*/ -if (IS_ENABLED (CONFIG_ARM64_SME ) &&id_aa64pfr1_sme (info->reg_id_aa64pfr1)) {/*
Will Deacon
maintainerPATCH v2 8/23 arm64: cpufeature: Store every ID register with its overrides applied
In reply to Will DeaconFrom: Fuad Tabba <[email protected]>
__cpuinfo_store_cpu() stores ID_AA64PFR0_EL1 and ID_AA64PFR1_EL1 with their overrides applied and every other register raw. Store the rest the same way. Everything read with read_cpuid() goes through read_cpuid_with_overrides(). CTR_EL0 and CNTFRQ_EL0, which are read otherwise, go through arm64_ftr_reg_override(). It returns a register with no override unchanged, so an override added to one later takes effect without a change here.
init_cpu_ftr_reg() then gets values that already carry every valid override, so its code to apply one is dead. Remove that code. init_cpu_ftr_reg() still removes an unsafe override, so the same overrides are rejected and sys_val is unchanged. Only its log changes. The raw value is gone, so it logs an override equal to that value as "forced" rather than "already set".
Secondaries are compared with the boot CPU on their stored values, so CPUs that differ only in a field the command line turned off no longer taint the kernel.
--- arch/arm64/kernel/cpufeature.c | 17 +++---- arch/arm64/kernel/cpuinfo.c | 82 +++++++++++++++++----------------- 2 files changed, 46 insertions(+), 53 deletions(-)
arch/arm64/kernel/cpufeature.c +5 -12
@@ -1043,6 +1043,8 @@ void __init validate_ftr_regs(void)* Also initialises the strict_mask for the register. * Any bits that are not covered by an arm64_ftr_bits entry are considered * RES0 for the system-wide value, and must strictly match. +* @new has been through arm64_ftr_reg_override(), so a valid override is +* already applied to it. Only an unsafe one is left to remove here. */ static void init_cpu_ftr_reg (u32 sys_reg,u64 new) {
@@ -1063,29 +1065,20 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new)s64 ftr_ovr =arm64_ftr_value (ftrp, reg->override->val);if ((ftr_mask & reg->override->mask) == ftr_mask) { -s64 tmp =arm64_ftr_safe_value (ftrp, ftr_ovr, ftr_new); -char *str =NULL ; +char *str ="forced" ; -if (ftr_ovr != tmp) { +if (arm64_ftr_safe_value (ftrp, ftr_ovr, ftr_new) != ftr_ovr) {/* Unsafe, remove the override */ reg->override->mask &= ~ftr_mask; reg->override->val &= ~ftr_mask; - tmp = ftr_ovr; str ="ignoring override" ; - }else if (ftr_new != tmp) { -/* Override was valid */ - ftr_new = tmp; - str ="forced" ; - }else { -/* Override was the safe value */ - str ="already set" ; }pr_warn ("%s[%d:%d]: %s to %llx\n" , reg->name, ftrp->shift + ftrp->width -1 , ftrp->shift, str, - tmp & (BIT (ftrp->width) -1 )); + ftr_ovr & (BIT (ftrp->width) -1 )); }else if ((ftr_mask & reg->override->val) == ftr_mask) { reg->override->val &= ~ftr_mask; pr_warn("%s[%d:%d]: impossible override, ignored\n",
arch/arm64/kernel/cpuinfo.c +41 -41
109 lines not on this page · raw message
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Re: [PATCH v2 08/23] arm64: cpufeature: Store every ID register with its overrides applied
Hi Will,
Quoted 180 lines · Will Deacon
From: Fuad Tabba <[email protected]>
__cpuinfo_store_cpu()storesID_AA64PFR0_EL1andID_AA64PFR1_EL1with their overrides applied and every other register raw. Store the rest the same way. Everything read withread_cpuid()goes throughread_cpuid_with_overrides().CTR_EL0andCNTFRQ_EL0, which are read otherwise, go througharm64_ftr_reg_override(). It returns a register with no override unchanged, so an override added to one later takes effect without a change here.
init_cpu_ftr_reg()then gets values that already carry every valid override, so its code to apply one is dead. Remove that code.init_cpu_ftr_reg()still removes an unsafe override, so the same overrides are rejected andsys_valis unchanged. Only its log changes. The raw value is gone, so it logs an override equal to that value as "forced" rather than "already set".Secondaries are compared with the boot CPU on their stored values, so CPUs that differ only in a field the command line turned off no longer taint the kernel.
Suggested-by Will Deacon <[email protected]>Signed-off-by Fuad Tabba <[email protected]>Signed-off-by Will Deacon <[email protected]>--- arch/arm64/kernel/cpufeature.c | 17 +++---- arch/arm64/kernel/cpuinfo.c | 82 +++++++++++++++++----------------- 2 files changed, 46 insertions(+), 53 deletions(-)arch/arm64/kernel/cpufeature.c +5
-12@@ -1043,6 +1043,8 @@ void __init validate_ftr_regs(void)* Also initialises the strict_mask for the register. * Any bits that are not covered by an arm64_ftr_bits entry are considered * RES0 for the system-wide value, and must strictly match. +* @new has been through arm64_ftr_reg_override(), so a valid override is +* already applied to it. Only an unsafe one is left to remove here. */ static void init_cpu_ftr_reg (u32 sys_reg,u64 new) {@@ -1063,29 +1065,20 @@ static void init_cpu_ftr_reg(u32 sys_reg, u64 new)s64 ftr_ovr =arm64_ftr_value (ftrp, reg->override->val);if ((ftr_mask & reg->override->mask) == ftr_mask) { -s64 tmp =arm64_ftr_safe_value (ftrp, ftr_ovr, ftr_new); -char *str =NULL ; +char *str ="forced" ; -if (ftr_ovr != tmp) { +if (arm64_ftr_safe_value (ftrp, ftr_ovr, ftr_new) != ftr_ovr) {/* Unsafe, remove the override */ reg->override->mask &= ~ftr_mask; reg->override->val &= ~ftr_mask; - tmp = ftr_ovr; str ="ignoring override" ; - }else if (ftr_new != tmp) { -/* Override was valid */ - ftr_new = tmp; - str ="forced" ; - }else { -/* Override was the safe value */ - str ="already set" ; }pr_warn ("%s[%d:%d]: %s to %llx\n" , reg->name, ftrp->shift + ftrp->width -1 , ftrp->shift, str, - tmp & (BIT (ftrp->width) -1 )); + ftr_ovr & (BIT (ftrp->width) -1 )); }else if ((ftr_mask & reg->override->val) == ftr_mask) { reg->override->val &= ~ftr_mask; pr_warn("%s[%d:%d]: impossible override, ignored\n",arch/arm64/kernel/cpuinfo.c +39
-39@@ -443,33 +443,33 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)static void __cpuinfo_store_cpu_32bit (struct cpuinfo_32bit *info) { - info->reg_id_dfr0 =read_cpuid (ID_DFR0_EL1 ); - info->reg_id_dfr1 =read_cpuid (ID_DFR1_EL1 ); - info->reg_id_isar0 =read_cpuid (ID_ISAR0_EL1 ); - info->reg_id_isar1 =read_cpuid (ID_ISAR1_EL1 ); - info->reg_id_isar2 =read_cpuid (ID_ISAR2_EL1 ); - info->reg_id_isar3 =read_cpuid (ID_ISAR3_EL1 ); - info->reg_id_isar4 =read_cpuid (ID_ISAR4_EL1 ); - info->reg_id_isar5 =read_cpuid (ID_ISAR5_EL1 ); - info->reg_id_isar6 =read_cpuid (ID_ISAR6_EL1 ); - info->reg_id_mmfr0 =read_cpuid (ID_MMFR0_EL1 ); - info->reg_id_mmfr1 =read_cpuid (ID_MMFR1_EL1 ); - info->reg_id_mmfr2 =read_cpuid (ID_MMFR2_EL1 ); - info->reg_id_mmfr3 =read_cpuid (ID_MMFR3_EL1 ); - info->reg_id_mmfr4 =read_cpuid (ID_MMFR4_EL1 ); - info->reg_id_mmfr5 =read_cpuid (ID_MMFR5_EL1 ); - info->reg_id_pfr0 =read_cpuid (ID_PFR0_EL1 ); - info->reg_id_pfr1 =read_cpuid (ID_PFR1_EL1 ); - info->reg_id_pfr2 =read_cpuid (ID_PFR2_EL1 ); + info->reg_id_dfr0 =read_cpuid_with_overrides (ID_DFR0_EL1 ); + info->reg_id_dfr1 =read_cpuid_with_overrides (ID_DFR1_EL1 ); + info->reg_id_isar0 =read_cpuid_with_overrides (ID_ISAR0_EL1 ); + info->reg_id_isar1 =read_cpuid_with_overrides (ID_ISAR1_EL1 ); + info->reg_id_isar2 =read_cpuid_with_overrides (ID_ISAR2_EL1 ); + info->reg_id_isar3 =read_cpuid_with_overrides (ID_ISAR3_EL1 ); + info->reg_id_isar4 =read_cpuid_with_overrides (ID_ISAR4_EL1 ); + info->reg_id_isar5 =read_cpuid_with_overrides (ID_ISAR5_EL1 ); + info->reg_id_isar6 =read_cpuid_with_overrides (ID_ISAR6_EL1 ); + info->reg_id_mmfr0 =read_cpuid_with_overrides (ID_MMFR0_EL1 ); + info->reg_id_mmfr1 =read_cpuid_with_overrides (ID_MMFR1_EL1 ); + info->reg_id_mmfr2 =read_cpuid_with_overrides (ID_MMFR2_EL1 ); + info->reg_id_mmfr3 =read_cpuid_with_overrides (ID_MMFR3_EL1 ); + info->reg_id_mmfr4 =read_cpuid_with_overrides (ID_MMFR4_EL1 ); + info->reg_id_mmfr5 =read_cpuid_with_overrides (ID_MMFR5_EL1 ); + info->reg_id_pfr0 =read_cpuid_with_overrides (ID_PFR0_EL1 ); + info->reg_id_pfr1 =read_cpuid_with_overrides (ID_PFR1_EL1 ); + info->reg_id_pfr2 =read_cpuid_with_overrides (ID_PFR2_EL1 ); - info->reg_mvfr0 =read_cpuid (MVFR0_EL1 ); - info->reg_mvfr1 =read_cpuid (MVFR1_EL1 ); - info->reg_mvfr2 =read_cpuid (MVFR2_EL1 ); + info->reg_mvfr0 =read_cpuid_with_overrides (MVFR0_EL1 ); + info->reg_mvfr1 =read_cpuid_with_overrides (MVFR1_EL1 ); + info->reg_mvfr2 =read_cpuid_with_overrides (MVFR2_EL1 ); }static void __cpuinfo_store_cpu (struct cpuinfo_arm64 *info) { - info->reg_cntfrq =arch_timer_get_cntfrq (); + info->reg_cntfrq =arm64_ftr_reg_override (SYS_CNTFRQ_EL0 ,arch_timer_get_cntfrq ());/* * Use the effective value of the CTR_EL0 than the raw value * exposed by the CPU. CTR_EL0.IDC field value must be interpreted @@ -478,35 +478,35 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)* effective value of the CTR_EL0 in our internal records for * accurate sanity check and feature enablement. */ - info->reg_ctr =read_cpuid_effective_cachetype (); - info->reg_dczid =read_cpuid (DCZID_EL0 ); + info->reg_ctr =arm64_ftr_reg_override (SYS_CTR_EL0 ,read_cpuid_effective_cachetype ()); + info->reg_dczid =read_cpuid_with_overrides (DCZID_EL0 ); info->reg_midr =read_cpuid_id (); info->reg_revidr =read_cpuid (REVIDR_EL1 ); info->reg_aidr =read_cpuid (AIDR_EL1 ); - info->reg_id_aa64dfr0 =read_cpuid (ID_AA64DFR0_EL1 ); - info->reg_id_aa64dfr1 =read_cpuid (ID_AA64DFR1_EL1 ); - info->reg_id_aa64isar0 =read_cpuid (ID_AA64ISAR0_EL1 ); - info->reg_id_aa64isar1 =read_cpuid (ID_AA64ISAR1_EL1 ); - info->reg_id_aa64isar2 =read_cpuid (ID_AA64ISAR2_EL1 ); - info->reg_id_aa64isar3 =read_cpuid (ID_AA64ISAR3_EL1 ); - info->reg_id_aa64mmfr0 =read_cpuid (ID_AA64MMFR0_EL1 ); - info->reg_id_aa64mmfr1 =read_cpuid (ID_AA64MMFR1_EL1 ); - info->reg_id_aa64mmfr2 =read_cpuid (ID_AA64MMFR2_EL1 ); - info->reg_id_aa64mmfr3 =read_cpuid (ID_AA64MMFR3_EL1 ); - info->reg_id_aa64mmfr4 =read_cpuid (ID_AA64MMFR4_EL1 ); + info->reg_id_aa64dfr0 =read_cpuid_with_overrides (ID_AA64DFR0_EL1 ); + info->reg_id_aa64dfr1 =read_cpuid_with_overrides (ID_AA64DFR1_EL1 ); + info->reg_id_aa64isar0 =read_cpuid_with_overrides (ID_AA64ISAR0_EL1 ); + info->reg_id_aa64isar1 =read_cpuid_with_overrides (ID_AA64ISAR1_EL1 ); + info->reg_id_aa64isar2 =read_cpuid_with_overrides (ID_AA64ISAR2_EL1 ); + info->reg_id_aa64isar3 =read_cpuid_with_overrides (ID_AA64ISAR3_EL1 ); + info->reg_id_aa64mmfr0 =read_cpuid_with_overrides (ID_AA64MMFR0_EL1 ); + info->reg_id_aa64mmfr1 =read_cpuid_with_overrides (ID_AA64MMFR1_EL1 ); + info->reg_id_aa64mmfr2 =read_cpuid_with_overrides (ID_AA64MMFR2_EL1 ); + info->reg_id_aa64mmfr3 =read_cpuid_with_overrides (ID_AA64MMFR3_EL1 ); + info->reg_id_aa64mmfr4 =read_cpuid_with_overrides (ID_AA64MMFR4_EL1 ); info->reg_id_aa64pfr0 =read_cpuid_with_overrides (ID_AA64PFR0_EL1 ); info->reg_id_aa64pfr1 =read_cpuid_with_overrides (ID_AA64PFR1_EL1 ); - info->reg_id_aa64pfr2 =read_cpuid (ID_AA64PFR2_EL1 ); - info->reg_id_aa64zfr0 =read_cpuid (ID_AA64ZFR0_EL1 ); - info->reg_id_aa64smfr0 =read_cpuid (ID_AA64SMFR0_EL1 ); - info->reg_id_aa64fpfr0 =read_cpuid (ID_AA64FPFR0_EL1 ); + info->reg_id_aa64pfr2 =read_cpuid_with_overrides (ID_AA64PFR2_EL1 ); + info->reg_id_aa64zfr0 =read_cpuid_with_overrides (ID_AA64ZFR0_EL1 ); + info->reg_id_aa64smfr0 =read_cpuid_with_overrides (ID_AA64SMFR0_EL1 ); + info->reg_id_aa64fpfr0 =read_cpuid_with_overrides (ID_AA64FPFR0_EL1 );
arch/arm64/kernel/cpuinfo.c static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) · line 512
Quoted 10 lines
arch/arm64/kernel/cpuinfo.c +2
-2if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) - info->reg_gmid =read_cpuid (GMID_EL1 ); + info->reg_gmid =read_cpuid_with_overrides (GMID_EL1 );if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) - info->reg_mpamidr =read_cpuid (MPAMIDR_EL1 ); + info->reg_mpamidr =read_cpuid_with_overrides (MPAMIDR_EL1 );if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0))__cpuinfo_store_cpu_32bit (&info->aarch32);
For completeness shouldn't read_cpuid(SMIDR_EL1) change to read_cpuid_with_overrides(SMIDR_EL1)?
Thanks,
Ben
Re: [PATCH v2 08/23] arm64: cpufeature: Store every ID register with its overrides applied
Hi Ben,
On Fri, 09 Oct 2026 16:51:14 +0100, Ben Horgan <[email protected]> wrote:
[...]
Quoted 2 lines
Quoted 2 lines
the same way. Everything read with
read_cpuid()goes throughread_cpuid_with_overrides().CTR_EL0andCNTFRQ_EL0, which are read
[...]
Quoted 2 lines
For completeness shouldn't
read_cpuid(SMIDR_EL1)change toread_cpuid_with_overrides(SMIDR_EL1)?
"Everything" should have said every register in arm64_ftr_regs[], and SMIDR_EL1 isn't one of them, so read_cpuid_with_overrides() would hit the WARN_ON() in get_arm64_ftr_reg() whenever SME is enabled.
Thanks,
/fuad
Re: [PATCH v2 08/23] arm64: cpufeature: Store every ID register with its overrides applied
Hi Fuad,
Quoted 6 lines · Fuad Tabba
Hi Ben,
On Fri, 09 Oct 2026 16:51:14 +0100, Ben Horgan <[email protected]> wrote:
[...]Quoted 2 lines
Quoted 2 lines
the same way. Everything read with
read_cpuid()goes throughread_cpuid_with_overrides().CTR_EL0andCNTFRQ_EL0, which are read
Quoted 7 lines
[...]
Quoted 2 lines
For completeness shouldn't
read_cpuid(SMIDR_EL1)change toread_cpuid_with_overrides(SMIDR_EL1)?"Everything" should have said every register in arm64_ftr_regs[], and
SMIDR_EL1isn't one of them, soread_cpuid_with_overrides()would hit theWARN_ON()inget_arm64_ftr_reg()whenever SME is enabled.
Thanks for the explanation. If I understand correctly, SMIDR_EL1 isn't in arm64_ftr_regs[] because there is no need to sanitize SMIDR_EL1 to give a consistent view across cpus. If and when support for sme priority is added then a unified view of the SMIDR_EL1.HIP, highest implement priority, would be needed and so at that point deserve a place in arm64_ftr_regs[].
Cheers,
Ben
Quoted 3 lines
Thanks,
/fuad
Will Deacon
maintainerPATCH v2 9/23 arm64: smp: Tidy up smp_prepare_cpus()
In reply to Will Deaconsmp_prepare_cpus() is always run on the boot CPU (i.e. CPU 0) but goes to great lengths to support running on a CPU where smp_processor_id() is non-zero.
Clean up the code a little by hardcoding zero for the boot CPU ID.
--- arch/arm64/kernel/smp.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
arch/arm64/kernel/smp.c +5 -8
@@ -781,16 +781,14 @@ void __init smp_init_cpus(void)void __init smp_prepare_cpus (unsigned int max_cpus) {const struct cpu_operations *ops; -int err;unsigned int cpu; -unsigned int this_cpu; +int err;init_cpu_topology (); - this_cpu =smp_processor_id (); -store_cpu_topology (this_cpu); -numa_store_cpu_info (this_cpu); -numa_add_cpu (this_cpu); +store_cpu_topology (0 ); +numa_store_cpu_info (0 ); +numa_add_cpu (0 );/* * If UP is mandated by "nosmp" (which implies "maxcpus=0"), don't set
@@ -805,8 +803,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)* secondaries from the bootloader. */ for_each_possible_cpu (cpu) { - -if (cpu ==smp_processor_id ()) +if (cpu ==0 )continue ; ops =get_cpu_ops (cpu);
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 10/23 arm64: smp: Tidy up cpuinfo init and cpufeature updates
In reply to Will DeaconPopulating the 'cpuinfo_arm64' structure during CPU bringup and subsequently checking/updating cpufeature structures is slightly convoluted and differs unnecessarily between the boot CPU and secondary CPUs.
Rework the code so that cpuinfo_store_cpu() is used to populate the 'cpuinfo_arm64' structure for each CPU, with secondary CPUs then calling update_cpu_features() to update the global view of the available features. This allows us to internalise init_cpu_features() and the 'boot_cpu_data' in cpufeature.c and paves the way for parallelising the ID register probing during bring-up of secondary CPUs.
--- arch/arm64/include/asm/cpu.h | 5 +---- arch/arm64/kernel/cpufeature.c | 21 +++++++++++++++++---- arch/arm64/kernel/cpuinfo.c | 11 ----------- arch/arm64/kernel/smp.c | 3 ++- 4 files changed, 20 insertions(+), 20 deletions(-)
arch/arm64/include/asm/cpu.h +1 -4
@@ -73,11 +73,8 @@ struct cpuinfo_arm64 { DECLARE_PER_CPU(struct cpuinfo_arm64, cpu_data);void cpuinfo_store_cpu (void ); -void __init cpuinfo_store_boot_cpu (void ); +void update_cpu_features (int cpu);void __init validate_ftr_regs (void ); -void __init init_cpu_features (struct cpuinfo_arm64 *info); -void update_cpu_features (int cpu,struct cpuinfo_arm64 *info, -struct cpuinfo_arm64 *boot);#endif /* __ASM_CPU_H */
arch/arm64/kernel/cpufeature.c +17 -4
@@ -117,6 +117,7 @@ EXPORT_SYMBOL(system_cpucaps);static struct arm64_cpu_capabilities const __ro_after_init *cpucap_ptrs[ARM64_NCAPS ];DECLARE_BITMAP (boot_cpucaps,ARM64_NCAPS ); +static struct cpuinfo_arm64 boot_cpu_data;/* * arm64_use_ng_mappings must be placed in the .data section, otherwise it
@@ -1197,8 +1198,16 @@ static bool detect_ftr_has_mpam(void)return id_aa64pfr0_mpam (pfr0) ||id_aa64pfr1_mpamfrac (pfr1); } -void __init init_cpu_features (struct cpuinfo_arm64 *info) +static void __init init_cpu_features (void ) { +struct cpuinfo_arm64 *info = &per_cpu (cpu_data,0 ); + +/* +* We keep a copy of the boot CPU registers so that physical hotplug +* of CPU 0 can still be properly checked. +*/ + boot_cpu_data = *info; +init_cpu_ftr_reg (SYS_CTR_EL0 , info->reg_ctr);init_cpu_ftr_reg (SYS_DCZID_EL0 , info->reg_dczid);init_cpu_ftr_reg (SYS_CNTFRQ_EL0 , info->reg_cntfrq);
@@ -1391,12 +1400,14 @@ static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,* non-boot CPU. Also performs SANITY checks to make sure that there * aren't any insane variations from that of the boot CPU. */ -void update_cpu_features (int cpu, -struct cpuinfo_arm64 *info, -struct cpuinfo_arm64 *boot) +void update_cpu_features (int cpu) { +struct cpuinfo_arm64 *boot, *info;int taint =0 ; + boot = &boot_cpu_data; + info =per_cpu_ptr (&cpu_data, cpu); +/* * The kernel can handle differing I-cache policies, but otherwise * caches should look identical. Userspace JITs will make use of
@@ -3963,6 +3974,8 @@ static void __init setup_boot_cpu_capabilities(void)void __init setup_boot_cpu_features (void ) { +init_cpu_features (); +/* * Initialize the indirect array of CPU capabilities pointers before we * handle the boot CPU.
arch/arm64/kernel/cpuinfo.c +0 -11
@@ -31,7 +31,6 @@* values depending on configuration at or after reset. */ DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data); -static struct cpuinfo_arm64 boot_cpu_data;static inline const char *icache_policy_str (int l1ip) {
@@ -528,14 +527,4 @@ void cpuinfo_store_cpu(void) {struct cpuinfo_arm64 *info =this_cpu_ptr (&cpu_data);__cpuinfo_store_cpu (info); -update_cpu_features (smp_processor_id (), info, &boot_cpu_data); -} - -void __init cpuinfo_store_boot_cpu (void ) -{ -struct cpuinfo_arm64 *info = &per_cpu (cpu_data,0 ); -__cpuinfo_store_cpu (info); - - boot_cpu_data = *info; -init_cpu_features (&boot_cpu_data); }
arch/arm64/kernel/smp.c +2 -1
@@ -235,6 +235,7 @@ asmlinkage notrace void secondary_start_kernel(void)* Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu (); +update_cpu_features (cpu);store_cpu_topology (cpu);/*
@@ -457,7 +458,7 @@ void __init smp_prepare_boot_cpu(void)/* Check arm64_ftr_regs[] before anything looks registers up in it. */ validate_ftr_regs (); -cpuinfo_store_boot_cpu (); +cpuinfo_store_cpu ();setup_boot_cpu_features ();/* Conditionally switch to GIC PMR for interrupt masking */
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 11/23 arm64: smp: Defer update of secondary CPU capabilities
In reply to Will Deaconcheck_local_cpu_capabilities() runs relatively early during the boot of each secondary CPU and, despite its name, calls update_cpu_capabilities() to manipulate the global 'system_cpucaps' based on the features detected by the incoming CPU.
In preparation for parallel bringup of secondary CPUs, move the call to update_cpu_capabilities() into update_cpu_features(), allowing check_local_cpu_capabilities() to run concurrently in future, as it now only performs local verification of the incoming CPU.
--- arch/arm64/kernel/cpufeature.c | 307 +++++++++++++++++---------------- 1 file changed, 155 insertions(+), 152 deletions(-)
arch/arm64/kernel/cpufeature.c +155 -152
@@ -1395,154 +1395,6 @@ static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info,return taint; } -/* -* Update system wide CPU feature registers with the values from a -* non-boot CPU. Also performs SANITY checks to make sure that there -* aren't any insane variations from that of the boot CPU. -*/ -void update_cpu_features (int cpu) -{ -struct cpuinfo_arm64 *boot, *info; -int taint =0 ; - - boot = &boot_cpu_data; - info =per_cpu_ptr (&cpu_data, cpu); - -/* -* The kernel can handle differing I-cache policies, but otherwise -* caches should look identical. Userspace JITs will make use of -* *minLine. -*/ - taint |=check_update_ftr_reg (SYS_CTR_EL0 , cpu, - info->reg_ctr, boot->reg_ctr); - -/* -* Userspace may perform DC ZVA instructions. Mismatched block sizes -* could result in too much or too little memory being zeroed if a -* process is preempted and migrated between CPUs. -*/ - taint |=check_update_ftr_reg (SYS_DCZID_EL0 , cpu, - info->reg_dczid, boot->reg_dczid); - -/* If different, timekeeping will be broken (especially with KVM) */ - taint |=check_update_ftr_reg (SYS_CNTFRQ_EL0 , cpu, - info->reg_cntfrq, boot->reg_cntfrq); - -/* -* The kernel uses self-hosted debug features and expects CPUs to -* support identical debug features. We presently need CTX_CMPs, WRPs, -* and BRPs to be identical. -* ID_AA64DFR1 is currently RES0. -*/ - taint |=check_update_ftr_reg (SYS_ID_AA64DFR0_EL1 , cpu, - info->reg_id_aa64dfr0, boot->reg_id_aa64dfr0); - taint |=check_update_ftr_reg (SYS_ID_AA64DFR1_EL1 , cpu, - info->reg_id_aa64dfr1, boot->reg_id_aa64dfr1); -/* -* Even in big.LITTLE, processors should be identical instruction-set -* wise. -*/ - taint |=check_update_ftr_reg (SYS_ID_AA64ISAR0_EL1 , cpu, - info->reg_id_aa64isar0, boot->reg_id_aa64isar0); - taint |=check_update_ftr_reg (SYS_ID_AA64ISAR1_EL1 , cpu, - info->reg_id_aa64isar1, boot->reg_id_aa64isar1); - taint |=check_update_ftr_reg (SYS_ID_AA64ISAR2_EL1 , cpu, - info->reg_id_aa64isar2, boot->reg_id_aa64isar2); - taint |=check_update_ftr_reg (SYS_ID_AA64ISAR3_EL1 , cpu, - info->reg_id_aa64isar3, boot->reg_id_aa64isar3); - -/* -* Differing PARange support is fine as long as all peripherals and -* memory are mapped within the minimum PARange of all CPUs. -* Linux should not care about secure memory. -*/ - taint |=check_update_ftr_reg (SYS_ID_AA64MMFR0_EL1 , cpu, - info->reg_id_aa64mmfr0, boot->reg_id_aa64mmfr0); - taint |=check_update_ftr_reg (SYS_ID_AA64MMFR1_EL1 , cpu, - info->reg_id_aa64mmfr1, boot->reg_id_aa64mmfr1); - taint |=check_update_ftr_reg (SYS_ID_AA64MMFR2_EL1 , cpu, - info->reg_id_aa64mmfr2, boot->reg_id_aa64mmfr2); - taint |=check_update_ftr_reg (SYS_ID_AA64MMFR3_EL1 , cpu, - info->reg_id_aa64mmfr3, boot->reg_id_aa64mmfr3); - taint |=check_update_ftr_reg (SYS_ID_AA64MMFR4_EL1 , cpu, - info->reg_id_aa64mmfr4, boot->reg_id_aa64mmfr4); - - taint |=check_update_ftr_reg (SYS_ID_AA64PFR0_EL1 , cpu, - info->reg_id_aa64pfr0, boot->reg_id_aa64pfr0); - taint |=check_update_ftr_reg (SYS_ID_AA64PFR1_EL1 , cpu, - info->reg_id_aa64pfr1, boot->reg_id_aa64pfr1); - taint |=check_update_ftr_reg (SYS_ID_AA64PFR2_EL1 , cpu, - info->reg_id_aa64pfr2, boot->reg_id_aa64pfr2); - - taint |=check_update_ftr_reg (SYS_ID_AA64ZFR0_EL1 , cpu, - info->reg_id_aa64zfr0, boot->reg_id_aa64zfr0); - - taint |=check_update_ftr_reg (SYS_ID_AA64SMFR0_EL1 , cpu, - info->reg_id_aa64smfr0, boot->reg_id_aa64smfr0); - - taint |=check_update_ftr_reg (SYS_ID_AA64FPFR0_EL1 , cpu, - info->reg_id_aa64fpfr0, boot->reg_id_aa64fpfr0); - -/* Probe vector lengths */ -if (IS_ENABLED (CONFIG_ARM64_SVE ) && -id_aa64pfr0_sve (read_sanitised_ftr_reg (SYS_ID_AA64PFR0_EL1 ))) { -if (!system_capabilities_finalized ()) { -unsigned long cpacr =cpacr_save_enable_kernel_sve (); - -vec_update_vq_map (ARM64_VEC_SVE ); - -cpacr_restore (cpacr); - } - } - -if (IS_ENABLED (CONFIG_ARM64_SME ) && -id_aa64pfr1_sme (read_sanitised_ftr_reg (SYS_ID_AA64PFR1_EL1 ))) { -unsigned long cpacr =cpacr_save_enable_kernel_sme (); - -/* Probe vector lengths */ -if (!system_capabilities_finalized ()) -vec_update_vq_map (ARM64_VEC_SME ); - -cpacr_restore (cpacr); - } - -if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) - taint |=check_update_ftr_reg (SYS_MPAMIDR_EL1 , cpu, - info->reg_mpamidr, boot->reg_mpamidr); - -/* -* The kernel uses the LDGM/STGM instructions and the number of tags -* they read/write depends on the GMID_EL1.BS field. Check that the -* value is the same on all CPUs. -*/ -if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) - taint |=check_update_ftr_reg (SYS_GMID_EL1 , cpu, - info->reg_gmid, boot->reg_gmid); - -/* -* If we don't have AArch32 at all then skip the checks entirely -* as the register values may be UNKNOWN and we're not going to be -* using them for anything. -* -* This relies on a sanitised view of the AArch64 ID registers -* (e.g. SYS_ID_AA64PFR0_EL1), so we call it last. -*/ -if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0)) { -lazy_init_32bit_cpu_features (info, boot); - taint |=update_32bit_cpu_features (cpu, &info->aarch32, - &boot->aarch32); - } - -/* -* Mismatched CPU features are a recipe for disaster. Don't even -* pretend to support them. -*/ -if (taint) { -pr_warn_once ("Unsupported CPU feature variation detected.\n" ); -add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); - } -} -u64 read_sanitised_ftr_reg (u32 id) {struct arm64_ftr_reg *regp =get_arm64_ftr_reg (id);
@@ -3887,16 +3739,167 @@ void check_local_cpu_capabilities(void)*/ check_early_cpu_features (); +/* +* Verify that this CPU has all the system advertised +* capabilities. +*/ +if (system_capabilities_finalized ()) +verify_local_cpu_capabilities (); +} + +/* +* Update system wide CPU feature registers with the values from a +* non-boot CPU. Also performs SANITY checks to make sure that there +* aren't any insane variations from that of the boot CPU. +*/ +void update_cpu_features (int cpu) +{ +struct cpuinfo_arm64 *boot, *info; +int taint =0 ; +/* * If we haven't finalised the system capabilities, this CPU gets * a chance to update the errata work arounds and local features. -* Otherwise, this CPU should verify that it has all the system -* advertised capabilities. */ if (!system_capabilities_finalized ())update_cpu_capabilities (SCOPE_LOCAL_CPU ); -else -verify_local_cpu_capabilities (); + + boot = &boot_cpu_data; + info =per_cpu_ptr (&cpu_data, cpu); + +/* +* The kernel can handle differing I-cache policies, but otherwise +* caches should look identical. Userspace JITs will make use of +* *minLine. +*/ + taint |=check_update_ftr_reg (SYS_CTR_EL0 , cpu, + info->reg_ctr, boot->reg_ctr); + +/* +* Userspace may perform DC ZVA instructions. Mismatched block sizes +* could result in too much or too little memory being zeroed if a +* process is preempted and migrated between CPUs. +*/ + taint |=check_update_ftr_reg (SYS_DCZID_EL0 , cpu, + info->reg_dczid, boot->reg_dczid); + +/* If different, timekeeping will be broken (especially with KVM) */ + taint |=check_update_ftr_reg (SYS_CNTFRQ_EL0 , cpu, + info->reg_cntfrq, boot->reg_cntfrq); + +/* +* The kernel uses self-hosted debug features and expects CPUs to +* support identical debug features. We presently need CTX_CMPs, WRPs, +* and BRPs to be identical. +* ID_AA64DFR1 is currently RES0. +*/ + taint |=check_update_ftr_reg (SYS_ID_AA64DFR0_EL1 , cpu, + info->reg_id_aa64dfr0, boot->reg_id_aa64dfr0); + taint |=check_update_ftr_reg (SYS_ID_AA64DFR1_EL1 , cpu, + info->reg_id_aa64dfr1, boot->reg_id_aa64dfr1); +/* +* Even in big.LITTLE, processors should be identical instruction-set +* wise. +*/ + taint |=check_update_ftr_reg (SYS_ID_AA64ISAR0_EL1 , cpu, + info->reg_id_aa64isar0, boot->reg_id_aa64isar0); + taint |=check_update_ftr_reg (SYS_ID_AA64ISAR1_EL1 , cpu, + info->reg_id_aa64isar1, boot->reg_id_aa64isar1); + taint |=check_update_ftr_reg (SYS_ID_AA64ISAR2_EL1 , cpu, + info->reg_id_aa64isar2, boot->reg_id_aa64isar2); + taint |=check_update_ftr_reg (SYS_ID_AA64ISAR3_EL1 , cpu, + info->reg_id_aa64isar3, boot->reg_id_aa64isar3); + +/* +* Differing PARange support is fine as long as all peripherals and +* memory are mapped within the minimum PARange of all CPUs. +* Linux should not care about secure memory. +*/ + taint |=check_update_ftr_reg (SYS_ID_AA64MMFR0_EL1 , cpu, + info->reg_id_aa64mmfr0, boot->reg_id_aa64mmfr0); + taint |=check_update_ftr_reg (SYS_ID_AA64MMFR1_EL1 , cpu, + info->reg_id_aa64mmfr1, boot->reg_id_aa64mmfr1); + taint |=check_update_ftr_reg (SYS_ID_AA64MMFR2_EL1 , cpu, + info->reg_id_aa64mmfr2, boot->reg_id_aa64mmfr2); + taint |=check_update_ftr_reg (SYS_ID_AA64MMFR3_EL1 , cpu, + info->reg_id_aa64mmfr3, boot->reg_id_aa64mmfr3); + taint |=check_update_ftr_reg (SYS_ID_AA64MMFR4_EL1 , cpu, + info->reg_id_aa64mmfr4, boot->reg_id_aa64mmfr4); + + taint |=check_update_ftr_reg (SYS_ID_AA64PFR0_EL1 , cpu, + info->reg_id_aa64pfr0, boot->reg_id_aa64pfr0); + taint |=check_update_ftr_reg (SYS_ID_AA64PFR1_EL1 , cpu, + info->reg_id_aa64pfr1, boot->reg_id_aa64pfr1); + taint |=check_update_ftr_reg (SYS_ID_AA64PFR2_EL1 , cpu, + info->reg_id_aa64pfr2, boot->reg_id_aa64pfr2); + + taint |=check_update_ftr_reg (SYS_ID_AA64ZFR0_EL1 , cpu, + info->reg_id_aa64zfr0, boot->reg_id_aa64zfr0); + + taint |=check_update_ftr_reg (SYS_ID_AA64SMFR0_EL1 , cpu, + info->reg_id_aa64smfr0, boot->reg_id_aa64smfr0); + + taint |=check_update_ftr_reg (SYS_ID_AA64FPFR0_EL1 , cpu, + info->reg_id_aa64fpfr0, boot->reg_id_aa64fpfr0); + +/* Probe vector lengths */ +if (IS_ENABLED (CONFIG_ARM64_SVE ) && +id_aa64pfr0_sve (read_sanitised_ftr_reg (SYS_ID_AA64PFR0_EL1 ))) { +if (!system_capabilities_finalized ()) { +unsigned long cpacr =cpacr_save_enable_kernel_sve (); + +vec_update_vq_map (ARM64_VEC_SVE ); + +cpacr_restore (cpacr); + } + } + +if (IS_ENABLED (CONFIG_ARM64_SME ) && +id_aa64pfr1_sme (read_sanitised_ftr_reg (SYS_ID_AA64PFR1_EL1 ))) { +unsigned long cpacr =cpacr_save_enable_kernel_sme (); + +/* Probe vector lengths */ +if (!system_capabilities_finalized ()) +vec_update_vq_map (ARM64_VEC_SME ); + +cpacr_restore (cpacr); + } + +if (id_aa64pfr0_mpam (info->reg_id_aa64pfr0) ||id_aa64pfr1_mpamfrac (info->reg_id_aa64pfr1)) + taint |=check_update_ftr_reg (SYS_MPAMIDR_EL1 , cpu, + info->reg_mpamidr, boot->reg_mpamidr); + +/* +* The kernel uses the LDGM/STGM instructions and the number of tags +* they read/write depends on the GMID_EL1.BS field. Check that the +* value is the same on all CPUs. +*/ +if (IS_ENABLED (CONFIG_ARM64_MTE ) &&id_aa64pfr1_mte (info->reg_id_aa64pfr1)) + taint |=check_update_ftr_reg (SYS_GMID_EL1 , cpu, + info->reg_gmid, boot->reg_gmid); + +/* +* If we don't have AArch32 at all then skip the checks entirely +* as the register values may be UNKNOWN and we're not going to be +* using them for anything. +* +* This relies on a sanitised view of the AArch64 ID registers +* (e.g. SYS_ID_AA64PFR0_EL1), so we call it last. +*/ +if (id_aa64pfr0_32bit_el0 (info->reg_id_aa64pfr0)) { +lazy_init_32bit_cpu_features (info, boot); + taint |=update_32bit_cpu_features (cpu, &info->aarch32, + &boot->aarch32); + } + +/* +* Mismatched CPU features are a recipe for disaster. Don't even +* pretend to support them. +*/ +if (taint) { +pr_warn_once ("Unsupported CPU feature variation detected.\n" ); +add_taint (TAINT_CPU_OUT_OF_SPEC ,LOCKDEP_STILL_OK ); + } }bool this_cpu_has_cap (unsigned int n)
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 12/23 arm64: smp: Don't bother printing the I-cache policy for each CPU
In reply to Will DeaconThe I-cache policy isn't particularly interesting but printing it early can result in unnecessary serialisation of onlining CPUs.
Remove the pointless print.
--- arch/arm64/kernel/cpuinfo.c | 16 ---------------- 1 file changed, 16 deletions(-)
arch/arm64/kernel/cpuinfo.c +0 -16
@@ -31,19 +31,6 @@* values depending on configuration at or after reset. */ DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data); - -static inline const char *icache_policy_str (int l1ip) -{ -switch (l1ip) { -case CTR_EL0_L1Ip_VIPT: -return "VIPT" ; -case CTR_EL0_L1Ip_PIPT: -return "PIPT" ; -default : -return "RESERVED/UNKNOWN" ; - } -} -unsigned long __icache_flags;static const char *const hwcap_str[] = {
@@ -424,7 +411,6 @@ device_initcall(cpuinfo_regs_init);static void cpuinfo_detect_icache_policy (struct cpuinfo_arm64 *info) { -unsigned int cpu =smp_processor_id ();u32 l1ip =CTR_L1IP (info->reg_ctr);switch (l1ip) {
@@ -436,8 +422,6 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)set_bit (ICACHEF_ALIASING , &__icache_flags);break ; } - -pr_info ("Detected %s I-cache on CPU%d\n" ,icache_policy_str (l1ip), cpu); }static void __cpuinfo_store_cpu_32bit (struct cpuinfo_32bit *info)
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 13/23 arm64: smp: Defer RCU registration during secondary CPU bringup
In reply to Will DeaconCalling rcutree_report_cpu_starting() early during boot can lead to livelocks with the generic CPU hotplug mechanism if the boot CPU blocks on an RCU grace period while the CPU being onlined is spinning in cpuhp_ap_sync_alive().
In preparation for enabling the generic CPU hotplug code on arm64, split up the trace_hardirqs_off() call during secondary CPU bringup so that we update lockdep early but defer the tracing updates until after the new CPU has been registered with RCU, which itself is deferred until after the early CPU compatability checks, allowing us to drop the explicit call to rcutree_report_cpu_dead() entirely.
--- arch/arm64/kernel/smp.c | 6 +++--- include/linux/rcutree.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kernel/smp.c +3 -3
@@ -217,8 +217,7 @@ asmlinkage notrace void secondary_start_kernel(void)if (system_uses_irq_prio_masking ())init_gic_priority_masking (); -rcutree_report_cpu_starting (cpu); -trace_hardirqs_off (); +lockdep_hardirqs_off (CALLER_ADDR0 );/* * If the system has established the capabilities, make sure
@@ -235,6 +234,7 @@ asmlinkage notrace void secondary_start_kernel(void)* Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu (); +rcutree_report_cpu_starting (cpu);update_cpu_features (cpu);store_cpu_topology (cpu);
@@ -242,6 +242,7 @@ asmlinkage notrace void secondary_start_kernel(void)* Enable GIC and timers. */ notify_cpu_starting (cpu); +trace_hardirqs_off_finish ();ipi_setup (cpu);
@@ -411,7 +412,6 @@ void __noreturn cpu_die_early(void)/* Mark this CPU absent */ set_cpu_present (cpu,0 ); -rcutree_report_cpu_dead ();if (IS_ENABLED (CONFIG_HOTPLUG_CPU )) {update_cpu_boot_status (CPU_KILL_ME );
include/linux/rcutree.h +1 -1
@@ -116,7 +116,7 @@ int rcutree_offline_cpu(unsigned int cpu);void rcutree_migrate_callbacks (int cpu); -/* Called from hotplug and also arm64 early secondary boot failure */ +/* Called from hotplug */ void rcutree_report_cpu_dead (void );#endif /* __LINUX_RCUTREE_H */
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 14/23 arm64: smp: Use generic HOTPLUG_CORE_SYNC_FULL machinery for CPU onlining
In reply to Will DeaconSelect HOTPLUG_CORE_SYNC_FULL on arm64 to replace the 'cpu_running' completion with the generic code for synchronising with secondary CPUs during boot.
--- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/smp.h | 1 - arch/arm64/kernel/smp.c | 39 +++++++++++++++++------------------- 3 files changed, 19 insertions(+), 23 deletions(-)
arch/arm64/Kconfig +1 -1
@@ -230,7 +230,7 @@ config ARM64 select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES select HAVE_KRETPROBES - select HOTPLUG_CORE_SYNC_DEAD if HOTPLUG_CPU + select HOTPLUG_CORE_SYNC_FULL select HOTPLUG_SMT if HOTPLUG_CPU select IRQ_DOMAIN select IRQ_FORCED_THREADING
arch/arm64/include/asm/smp.h +0 -1
@@ -12,7 +12,6 @@#define CPU_BOOT_STATUS_MASK ((UL(1) << CPU_STUCK_REASON_SHIFT) - 1)#define CPU_MMU_OFF (-1) -#define CPU_BOOT_SUCCESS (0)/* The cpu invoked ops->cpu_die, synchronise it with cpu_kill */ #define CPU_KILL_ME (1)/* The cpu couldn't die gracefully and is looping in the kernel */
arch/arm64/kernel/smp.c +18 -21
@@ -108,12 +108,9 @@ static int boot_secondary(unsigned int cpu, struct task_struct *idle)return -EOPNOTSUPP ; } -static DECLARE_COMPLETION (cpu_running); -int __cpu_up (unsigned int cpu,struct task_struct *idle) {int ret; -long status;/* * We need to tell the secondary core where to find its stack and the
@@ -124,27 +121,24 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)/* Now bring the CPU into our world */ ret =boot_secondary (cpu, idle); -if (ret) { -if (ret != -EPERM ) -pr_err ("CPU%u: failed to boot: %d\n" , cpu, ret); -return ret; - } +if (ret && ret != -EPERM ) +pr_err ("CPU%u: failed to boot: %d\n" , cpu, ret); +return ret; +} -/* -* CPU was successfully started, wait for it to come online or -* time out. -*/ -wait_for_completion_timeout (&cpu_running, -msecs_to_jiffies (5000 )); -if (cpu_online (cpu)) -return 0 ; +void arch_cpuhp_cleanup_kick_cpu (unsigned int cpu,bool is_alive) +{ +long status; + +if (is_alive) +return ; -pr_crit ("CPU%u: failed to come online\n" , cpu); secondary_data.task =NULL ; status =READ_ONCE (secondary_data.status);if (status ==CPU_MMU_OFF ) status =READ_ONCE (__early_cpu_boot_status); +/* A CPU has failed to boot. Try to figure out what happened. */ switch (status &CPU_BOOT_STATUS_MASK ) {default : pr_err("CPU%u: failed in unknown state : 0x%lx\n",
@@ -171,8 +165,6 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)case CPU_PANIC_KERNEL :panic ("CPU%u detected unsupported configuration\n" , cpu); } - -return -EIO ; }static void init_gic_priority_masking (void )
@@ -234,6 +226,13 @@ asmlinkage notrace void secondary_start_kernel(void)* Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu (); + +/* +* Synchronise with the core bringing us online so that it knows +* we made it into the kernel. We're still not 'online'. +*/ +cpuhp_ap_sync_alive (); +rcutree_report_cpu_starting (cpu);update_cpu_features (cpu);store_cpu_topology (cpu);
@@ -256,9 +255,7 @@ asmlinkage notrace void secondary_start_kernel(void)pr_info ("CPU%u: Booted secondary processor 0x%010lx [0x%08x]\n" , cpu, (unsigned long )mpidr,read_cpuid_id ()); -update_cpu_boot_status (CPU_BOOT_SUCCESS );set_cpu_online (cpu, true); -complete (&cpu_running);/* * Secondary CPUs enter the kernel with all DAIF exceptions masked.
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 15/23 arm64: smp: Use generic HOTPLUG_SPLIT_STARTUP machinery for CPU onlining
In reply to Will DeaconIn preparation for enabling parallel bringup of secondary CPUs on arm64, take the baby step of moving from HOTPLUG_CORE_SYNC_FULL to HOTPLUG_SPLIT_STARTUP.
Rework the cpu_die_early() path to use a private cpumask, otherwise clearing the incoming CPU from the present mask in the 'kick' stage will prevent the hotplug stage machine from progressing and arch_cpuhp_cleanup_kick_cpu() will not be called.
--- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/smp.h | 1 + arch/arm64/kernel/smp.c | 10 ++++++---- 3 files changed, 8 insertions(+), 5 deletions(-)
arch/arm64/Kconfig +1 -1
@@ -230,7 +230,7 @@ config ARM64 select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES select HAVE_KRETPROBES - select HOTPLUG_CORE_SYNC_FULL + select HOTPLUG_SPLIT_STARTUP select HOTPLUG_SMT if HOTPLUG_CPU select IRQ_DOMAIN select IRQ_FORCED_THREADING
arch/arm64/include/asm/smp.h +1 -0
@@ -89,6 +89,7 @@ asmlinkage void secondary_start_kernel(void);struct secondary_data {struct task_struct *task;long status; +cpumask_t cpu_died_early_mask; };extern struct secondary_data secondary_data;
arch/arm64/kernel/smp.c +6 -4
@@ -62,7 +62,7 @@* so we need some other way of telling a new secondary core * where to place its SVC stack */ -struct secondary_data secondary_data; +struct secondary_data secondary_data = {};/* Number of CPUs which aren't online, but looping in kernel text. */ static int cpus_stuck_in_kernel;
@@ -108,7 +108,7 @@ static int boot_secondary(unsigned int cpu, struct task_struct *idle)return -EOPNOTSUPP ; } -int __cpu_up (unsigned int cpu,struct task_struct *idle) +int arch_cpuhp_kick_ap_alive (unsigned int cpu,struct task_struct *idle) {int ret;
@@ -138,6 +138,9 @@ void arch_cpuhp_cleanup_kick_cpu(unsigned int cpu, bool is_alive)if (status ==CPU_MMU_OFF ) status =READ_ONCE (__early_cpu_boot_status); +if (cpumask_test_and_clear_cpu (cpu, &secondary_data.cpu_died_early_mask)) +set_cpu_present (cpu, false); +/* A CPU has failed to boot. Try to figure out what happened. */ switch (status &CPU_BOOT_STATUS_MASK ) {default :
@@ -407,8 +410,7 @@ void __noreturn cpu_die_early(void)pr_crit ("CPU%d: will not boot\n" , cpu); -/* Mark this CPU absent */ -set_cpu_present (cpu,0 ); +cpumask_set_cpu (cpu, &secondary_data.cpu_died_early_mask);if (IS_ENABLED (CONFIG_HOTPLUG_CPU )) {update_cpu_boot_status (CPU_KILL_ME );
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 16/23 arm64: cpu_ops: Make 'cpu_operations' pointer global instead of per-cpu
In reply to Will Deacon'cpu_ops' is an NR_CPUS-length array of 'cpu_operations' pointers, which theoretically allows for different CPUs to have different bringup and hotplug backends.
In reality, this complexity exists only to deal with the case where CPU0 is not hotpluggable, so replace the array with a single, global pointer and record separately whether or not they apply to the boot CPU. Update the logic in init_cpu_ops() to enforce that only a single set of 'cpu_ops' is required.
--- arch/arm64/kernel/cpu_ops.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-)
arch/arm64/kernel/cpu_ops.c +20 -9
@@ -20,7 +20,8 @@ extern const struct cpu_operations acpi_parking_protocol_ops;#endif extern const struct cpu_operations cpu_psci_ops; -static const struct cpu_operations *cpu_ops[NR_CPUS ]__ro_after_init ; +static const struct cpu_operations *cpu_ops__ro_after_init ; +static bool boot_cpu_has_enable_method__ro_after_init ;static const struct cpu_operations *const dt_supported_cpu_ops[]__initconst = { &smp_spin_table_ops,
@@ -40,6 +41,9 @@ static const struct cpu_operations * __init cpu_get_ops(const char *name) {const struct cpu_operations *const *ops; +if (!name) +return NULL ; + ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops;while (*ops) {
@@ -49,6 +53,7 @@ static const struct cpu_operations * __init cpu_get_ops(const char *name) ops++; } +pr_warn ("Unsupported enable-method: %s\n" , name);return NULL ; }
@@ -94,25 +99,31 @@ static const char *__init cpu_read_enable_method(int cpu)return enable_method; }/* -* Read a cpu's enable method and record it in cpu_ops. +* Read a cpu's enable method and update/check cpu_ops. */ int __init init_cpu_ops (int cpu) {const char *enable_method =cpu_read_enable_method (cpu); +const struct cpu_operations *ops =cpu_get_ops (enable_method); -if (!enable_method) +if (!ops)return -ENODEV ; - cpu_ops[cpu] =cpu_get_ops (enable_method); -if (!cpu_ops[cpu]) { -pr_warn ("Unsupported enable-method: %s\n" , enable_method); -return -EOPNOTSUPP ; - } +if (!cpu_ops) + cpu_ops = ops; +else if (cpu_ops != ops) +return -EBUSY ; + +if (cpu ==0 ) + boot_cpu_has_enable_method = true;return 0 ; }const struct cpu_operations *get_cpu_ops (int cpu) { -return cpu_ops[cpu]; +if (cpu || boot_cpu_has_enable_method) +return cpu_ops; + +return NULL ; }
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 17/23 arm64: cpu_ops: Introduce get_secondary_cpu_ops()
In reply to Will DeaconIntroduce get_secondary_cpu_ops() to retrieve a pointer to the 'cpu_operations' structure for the non-boot CPUs and use it instead of get_cpu_ops() where we are dealing with secondary CPUs.
This is a pre-requisite for enabling parallel CPU bring-up.
--- arch/arm64/include/asm/cpu_ops.h | 1 + arch/arm64/kernel/cpu_ops.c | 5 +++++ arch/arm64/kernel/smp.c | 19 +++++++------------ 3 files changed, 13 insertions(+), 12 deletions(-)
arch/arm64/include/asm/cpu_ops.h +1 -0
@@ -48,6 +48,7 @@ struct cpu_operations {int __init init_cpu_ops (int cpu);extern const struct cpu_operations *get_cpu_ops (int cpu); +extern const struct cpu_operations *get_secondary_cpu_ops (void );static inline void __init init_bootcpu_ops (void ) {
arch/arm64/kernel/cpu_ops.c +5 -0
@@ -127,3 +127,8 @@ const struct cpu_operations *get_cpu_ops(int cpu)return NULL ; } + +const struct cpu_operations *get_secondary_cpu_ops (void ) +{ +return cpu_ops; +}
arch/arm64/kernel/smp.c +7 -12
@@ -100,7 +100,7 @@ static inline int op_cpu_kill(unsigned int cpu)*/ static int boot_secondary (unsigned int cpu,struct task_struct *idle) { -const struct cpu_operations *ops =get_cpu_ops (cpu); +const struct cpu_operations *ops =get_secondary_cpu_ops ();if (ops->cpu_boot)return ops->cpu_boot (cpu);
@@ -221,7 +221,7 @@ asmlinkage notrace void secondary_start_kernel(void)*/ check_local_cpu_capabilities (); - ops =get_cpu_ops (cpu); + ops =get_secondary_cpu_ops ();if (ops->cpu_postboot) ops->cpu_postboot ();
@@ -329,7 +329,7 @@ int __cpu_disable(void)static int op_cpu_kill (unsigned int cpu) { -const struct cpu_operations *ops =get_cpu_ops (cpu); +const struct cpu_operations *ops =get_secondary_cpu_ops ();/* * If we have no means of synchronising with the dying CPU, then assume
@@ -370,7 +370,7 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu)void __noreturn cpu_die (void ) {unsigned int cpu =smp_processor_id (); -const struct cpu_operations *ops =get_cpu_ops (cpu); +const struct cpu_operations *ops =get_secondary_cpu_ops ();idle_task_exit ();
@@ -496,7 +496,7 @@ static int __init smp_cpu_setup(int cpu)if (init_cpu_ops (cpu))return -ENODEV ; - ops =get_cpu_ops (cpu); + ops =get_secondary_cpu_ops ();if (ops->cpu_init (cpu))return -ENODEV ;
@@ -780,7 +780,7 @@ void __init smp_init_cpus(void)void __init smp_prepare_cpus (unsigned int max_cpus) { -const struct cpu_operations *ops; +const struct cpu_operations *ops =get_secondary_cpu_ops ();unsigned int cpu;int err;
@@ -806,10 +806,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)if (cpu ==0 )continue ; - ops =get_cpu_ops (cpu); -if (!ops) -continue ; - err = ops->cpu_prepare (cpu);if (err)continue ;
@@ -1345,8 +1341,7 @@ bool smp_crash_stop_failed(void)static bool have_cpu_die (void ) {#ifdef CONFIG_HOTPLUG_CPU -int any_cpu =raw_smp_processor_id (); -const struct cpu_operations *ops =get_cpu_ops (any_cpu); +const struct cpu_operations *ops =get_secondary_cpu_ops ();if (ops && ops->cpu_die)return true;
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 18/23 firmware/psci: Cache PSCI v0.2+ version number to avoid redundant SMCs
In reply to Will DeaconSecure monitor calls to EL3 aren't necessarily cheap, so cache the PSCI version number in memory to avoid asking firmware the same question over and over again.
--- drivers/firmware/psci/psci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/firmware/psci/psci.c +6 -1
@@ -155,7 +155,12 @@ static u32 psci_0_1_get_version(void)static u32 psci_0_2_get_version (void ) { -return invoke_psci_fn (PSCI_0_2_FN_PSCI_VERSION ,0 ,0 ,0 ); +static u32 version; + +if (unlikely (!version)) + version =invoke_psci_fn (PSCI_0_2_FN_PSCI_VERSION ,0 ,0 ,0 ); + +return version; }int psci_set_osi_mode (bool enable)
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 19/23 firmware/psci: Extend ->cpu_on() callback to take an additional argument
In reply to Will DeaconIn preparation for hooking up the 'context ID' parameter introduced by PSCI v0.2 to the CPU_ON call, extend the ->cpu_on() callback to take an additional argument and modify all callers to pass 0 for now.
--- arch/arm/kernel/psci_smp.c | 4 ++-- arch/arm64/kernel/psci.c | 2 +- drivers/firmware/psci/psci.c | 20 ++++++++++++++------ include/linux/psci.h | 3 ++- 4 files changed, 19 insertions(+), 10 deletions(-)
arch/arm/kernel/psci_smp.c +2 -2
@@ -49,10 +49,10 @@ static int psci_boot_secondary(unsigned int cpu, struct task_struct *idle)return psci_ops.cpu_on (cpu_logical_map (cpu), ((phys_addr_t)(&secondary_startup) -XIP_VIRT_ADDR (CONFIG_XIP_PHYS_ADDR ) - +CONFIG_XIP_PHYS_ADDR )); + +CONFIG_XIP_PHYS_ADDR ),0 );#else return psci_ops.cpu_on (cpu_logical_map (cpu), -virt_to_idmap (&secondary_startup)); +virt_to_idmap (&secondary_startup),0 );#endif return -ENODEV ; }
arch/arm64/kernel/psci.c +1 -1
@@ -39,7 +39,7 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu)static int cpu_psci_cpu_boot (unsigned int cpu) {phys_addr_t pa_secondary_entry =__pa_symbol (secondary_entry); -int err = psci_ops.cpu_on (cpu_logical_map (cpu), pa_secondary_entry); +int err = psci_ops.cpu_on (cpu_logical_map (cpu), pa_secondary_entry,0 );if (err && err != -EPERM )pr_err ("failed to boot CPU%d (%d)\n" , cpu, err);
drivers/firmware/psci/psci.c +14 -6
@@ -219,22 +219,30 @@ static int psci_0_2_cpu_off(u32 state)return __psci_cpu_off (PSCI_0_2_FN_CPU_OFF , state); } -static int __psci_cpu_on (u32 fn,unsigned long cpuid,unsigned long entry_point) +static int __psci_cpu_on (u32 fn,unsigned long cpuid,unsigned long entry_point, +unsigned long context) {int err; - err =invoke_psci_fn (fn, cpuid, entry_point,0 ); + err =invoke_psci_fn (fn, cpuid, entry_point, context);return psci_to_linux_errno (err); } -static int psci_0_1_cpu_on (unsigned long cpuid,unsigned long entry_point) +static int psci_0_1_cpu_on (unsigned long cpuid,unsigned long entry_point, +unsigned long mbz) { -return __psci_cpu_on (psci_0_1_function_ids.cpu_on, cpuid, entry_point); +if (mbz) +return -EINVAL ; + +return __psci_cpu_on (psci_0_1_function_ids.cpu_on, cpuid, entry_point, +0 ); } -static int psci_0_2_cpu_on (unsigned long cpuid,unsigned long entry_point) +static int psci_0_2_cpu_on (unsigned long cpuid,unsigned long entry_point, +unsigned long context) { - return __psci_cpu_on(PSCI_FN_NATIVE(0_2, CPU_ON), cpuid, entry_point); + return __psci_cpu_on(PSCI_FN_NATIVE(0_2, CPU_ON), cpuid, entry_point, + context); }static int __psci_migrate (u32 fn,unsigned long cpuid)
include/linux/psci.h +2 -1
@@ -25,7 +25,8 @@ struct psci_operations {u32 (*get_version)(void);int (*cpu_suspend)(u32 state,unsigned long entry_point);int (*cpu_off)(u32 state); -int (*cpu_on)(unsigned long cpuid,unsigned long entry_point); +int (*cpu_on)(unsigned long cpuid,unsigned long entry_point, +unsigned long context);int (*migrate)(unsigned long cpuid);int (*affinity_info)(unsigned long target_affinity,unsigned long lowest_affinity_level);
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 20/23 arm64: cpu_ops: Expose optional argument to target cpu in ->cpu_boot()
In reply to Will DeaconSome backend implementations of 'struct cpu_ops', notably PSCI v0.2+, allow an optional argument to be passed in register X0 to the target CPU during boot.
Expose this functionality by extending the ->cpu_boot() CPU operation to take an additional argument which is ignored unless the new optional ->cpu_boot_has_arg() callback is present and returns 'true'. For now, we continue to pass zero.
--- arch/arm64/include/asm/cpu_ops.h | 6 +++++- arch/arm64/kernel/acpi_parking_protocol.c | 3 ++- arch/arm64/kernel/psci.c | 12 ++++++++++-- arch/arm64/kernel/smp.c | 2 +- arch/arm64/kernel/smp_spin_table.c | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-)
arch/arm64/include/asm/cpu_ops.h +5 -1
@@ -21,6 +21,9 @@* mechanism for doing so, tests whether it is possible to boot * the given CPU. * @cpu_boot: Boots a cpu into the kernel. +* @cpu_boot_has_arg: Optionally determines whether @cpu_boot passes its +* (non-zero) second argument to the booting CPU in +* register x0. * @cpu_postboot: Optionally, perform any post-boot cleanup or necessary * synchronisation. Called from the cpu being booted. * @cpu_can_disable: Determines whether a CPU can be disabled based on
@@ -36,7 +39,8 @@ struct cpu_operations {const char *name;int (*cpu_init)(unsigned int );int (*cpu_prepare)(unsigned int ); -int (*cpu_boot)(unsigned int ); +int (*cpu_boot)(unsigned int ,unsigned long ); +bool (*cpu_boot_has_arg)(void );void (*cpu_postboot)(void );#ifdef CONFIG_HOTPLUG_CPU bool (*cpu_can_disable)(unsigned int cpu);
arch/arm64/kernel/acpi_parking_protocol.c +2 -1
@@ -56,7 +56,8 @@ static int acpi_parking_protocol_cpu_prepare(unsigned int cpu)return 0 ; } -static int acpi_parking_protocol_cpu_boot (unsigned int cpu) +static int acpi_parking_protocol_cpu_boot (unsigned int cpu, +unsigned long ignored) {struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu];struct parking_protocol_mailbox __iomem *mailbox;
arch/arm64/kernel/psci.c +10 -2
@@ -36,16 +36,23 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu)return 0 ; } -static int cpu_psci_cpu_boot (unsigned int cpu) +static int cpu_psci_cpu_boot (unsigned int cpu,unsigned long context) {phys_addr_t pa_secondary_entry =__pa_symbol (secondary_entry); -int err = psci_ops.cpu_on (cpu_logical_map (cpu), pa_secondary_entry,0 ); +int err = psci_ops.cpu_on (cpu_logical_map (cpu), pa_secondary_entry, + context);if (err && err != -EPERM )pr_err ("failed to boot CPU%d (%d)\n" , cpu, err);return err; } +static bool cpu_psci_cpu_boot_has_context (void ) +{ +return psci_ops.get_version && + psci_ops.get_version () >=PSCI_VERSION (0 ,2 ); +} +#ifdef CONFIG_HOTPLUG_CPU static bool cpu_psci_cpu_can_disable (unsigned int cpu) {
@@ -114,6 +121,7 @@ const struct cpu_operations cpu_psci_ops = { .cpu_init = cpu_psci_cpu_init, .cpu_prepare = cpu_psci_cpu_prepare, .cpu_boot = cpu_psci_cpu_boot, + .cpu_boot_has_arg = cpu_psci_cpu_boot_has_context, #ifdef CONFIG_HOTPLUG_CPU .cpu_can_disable = cpu_psci_cpu_can_disable, .cpu_disable = cpu_psci_cpu_disable,
arch/arm64/kernel/smp.c +1 -1
@@ -103,7 +103,7 @@ static int boot_secondary(unsigned int cpu, struct task_struct *idle)const struct cpu_operations *ops =get_secondary_cpu_ops ();if (ops->cpu_boot) -return ops->cpu_boot (cpu); +return ops->cpu_boot (cpu,0 );return -EOPNOTSUPP ; }
arch/arm64/kernel/smp_spin_table.c +1 -1
@@ -104,7 +104,7 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)return 0 ; } -static int smp_spin_table_cpu_boot (unsigned int cpu) +static int smp_spin_table_cpu_boot (unsigned int cpu,unsigned long ignored) {/* * Update the pen release flag.
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 21/23 arm64: smp: Pass secondary CPU boot parameters via firmware if possible
In reply to Will DeaconIn preparation for parallel bringup of secondary CPUs, the global 'secondary_data' structure used for initial paramater passing must be localised.
Pass the idle 'task_struct' pointer for secondary CPUs directly to ->cpu_boot() if the backend supports it.
--- arch/arm64/include/asm/smp.h | 1 + arch/arm64/kernel/head.S | 12 +++++++++++- arch/arm64/kernel/psci.c | 4 ++-- arch/arm64/kernel/smp.c | 25 +++++++++++-------------- 4 files changed, 25 insertions(+), 17 deletions(-)
arch/arm64/include/asm/smp.h +1 -0
@@ -95,6 +95,7 @@ struct secondary_data {extern struct secondary_data secondary_data;extern long __early_cpu_boot_status;extern void secondary_entry (void ); +extern void secondary_entry_with_arg (void );extern void arch_send_call_function_single_ipi (int cpu);extern void arch_send_call_function_ipi_mask (const struct cpumask *mask);
arch/arm64/kernel/head.S +11 -1
@@ -335,6 +335,7 @@ SYM_FUNC_END(init_kernel_el) * cores are held until we're ready for them to initialise. */ SYM_FUNC_START(secondary_holding_pen) + mov x19, xzr mov x0, xzr bl init_kernel_el // w0=cpu_boot_mode mrs x2, mpidr_el1
@@ -353,10 +354,16 @@ SYM_FUNC_END(secondary_holding_pen) * be used where CPUs are brought online dynamically by the kernel. */ SYM_FUNC_START(secondary_entry) + mov x0, xzr + b secondary_entry_with_arg +SYM_FUNC_END(secondary_entry) + +SYM_FUNC_START(secondary_entry_with_arg) + mov x19, x0 mov x0, xzr bl init_kernel_el // w0=cpu_boot_mode b secondary_startup -SYM_FUNC_END(secondary_entry) +SYM_FUNC_END(secondary_entry_with_arg) SYM_FUNC_START_LOCAL(secondary_startup) /*
@@ -391,10 +398,13 @@ SYM_FUNC_START_LOCAL(__secondary_switched) msr vbar_el1, x5 isb + mov x2, x19 + cbnz x2, 1f adr_l x0, secondary_data ldr x2, [x0, #CPU_BOOT_TASK] cbz x2, __secondary_too_slow +1: init_cpu_task x2, x1, x3 #ifdef CONFIG_ARM64_PTR_AUTH
arch/arm64/kernel/psci.c +2 -2
@@ -38,8 +38,8 @@ static int __init cpu_psci_cpu_prepare(unsigned int cpu)static int cpu_psci_cpu_boot (unsigned int cpu,unsigned long context) { -phys_addr_t pa_secondary_entry =__pa_symbol (secondary_entry); -int err = psci_ops.cpu_on (cpu_logical_map (cpu), pa_secondary_entry, +void *entry_va = context ? secondary_entry_with_arg : secondary_entry; +int err = psci_ops.cpu_on (cpu_logical_map (cpu),__pa_symbol (entry_va), context);if (err && err != -EPERM )pr_err ("failed to boot CPU%d (%d)\n" , cpu, err);
arch/arm64/kernel/smp.c +11 -14
@@ -93,34 +93,31 @@ static inline int op_cpu_kill(unsigned int cpu) }#endif -/* * Boot a secondary CPU, and assign it the specified idle task. * This also gives us the initial stack to use for this CPU. */ -static int boot_secondary (unsigned int cpu,struct task_struct *idle) -{ -const struct cpu_operations *ops =get_secondary_cpu_ops (); - -if (ops->cpu_boot) -return ops->cpu_boot (cpu,0 ); - -return -EOPNOTSUPP ; -} -int arch_cpuhp_kick_ap_alive (unsigned int cpu,struct task_struct *idle) { -int ret; +const struct cpu_operations *ops =get_secondary_cpu_ops (); +int ret = -EOPNOTSUPP ; +void *arg =NULL ;/* * We need to tell the secondary core where to find its stack and the * page tables. */ - secondary_data.task = idle; +if (ops->cpu_boot_has_arg && ops->cpu_boot_has_arg ()) + arg = idle; +else + secondary_data.task = idle; +update_cpu_boot_status (CPU_MMU_OFF );/* Now bring the CPU into our world */ - ret =boot_secondary (cpu, idle); +if (ops->cpu_boot) + ret = ops->cpu_boot (cpu, (unsigned long )arg); +if (ret && ret != -EPERM )pr_err ("CPU%u: failed to boot: %d\n" , cpu, ret);return ret;
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 22/23 arm64: smp: Use generic HOTPLUG_PARALLEL machinery for CPU onlining
In reply to Will DeaconMake the move from HOTPLUG_SPLIT_STARTUP to HOTPLUG_PARALLEL and enable parallel CPU bringup on systems with PSCI v0.2 or later.
The fiddly part of all this is the error handling if a CPU fails to come up, as we can no longer rely on a single global 'status' flag to capture the details. Instead, the secondary_data::status field is replaced with a zero-initialised byte array, with each byte representing an error reason, so the total set of failures can be accurately captured by the primary CPU.
--- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/smp.h | 35 +++++++------ arch/arm64/include/asm/topology.h | 2 + arch/arm64/kernel/head.S | 13 ++--- arch/arm64/kernel/smp.c | 82 ++++++++++++++++--------------- arch/arm64/mm/mmu.c | 2 +- 6 files changed, 71 insertions(+), 65 deletions(-)
arch/arm64/Kconfig +1 -1
@@ -230,7 +230,7 @@ config ARM64 select HAVE_SYSCALL_TRACEPOINTS select HAVE_KPROBES select HAVE_KRETPROBES - select HOTPLUG_SPLIT_STARTUP + select HOTPLUG_PARALLEL select HOTPLUG_SMT if HOTPLUG_CPU select IRQ_DOMAIN select IRQ_FORCED_THREADING
arch/arm64/include/asm/smp.h +20 -15
@@ -7,20 +7,15 @@#include <linux/const.h> -/* Values for secondary_data.status */ -#define CPU_STUCK_REASON_SHIFT (8) -#define CPU_BOOT_STATUS_MASK ((UL(1) << CPU_STUCK_REASON_SHIFT) - 1) +/* Offsets for early CPU boot reasons */ +#define EARLY_CPU_STUCK_REASON_52_BIT_VA (0) +#define EARLY_CPU_STUCK_REASON_NO_GRAN (1) +#define EARLY_CPU_STUCK_REASON_MAX (2) -#define CPU_MMU_OFF (-1) -/* The cpu invoked ops->cpu_die, synchronise it with cpu_kill */ -#define CPU_KILL_ME (1) -/* The cpu couldn't die gracefully and is looping in the kernel */ -#define CPU_STUCK_IN_KERNEL (2) +/* Offsets for late (i.e. MMU-enabled) CPU boot reasons */ /* Fatal system error detected by secondary CPU, crash the system */ -#define CPU_PANIC_KERNEL (3) - -#define CPU_STUCK_REASON_52_BIT_VA (UL(1) << CPU_STUCK_REASON_SHIFT) -#define CPU_STUCK_REASON_NO_GRAN (UL(2) << CPU_STUCK_REASON_SHIFT) +#define CPU_PANIC_KERNEL (0) +#define CPU_STATUS_FLAGS_MAX (1)#ifndef __ASSEMBLER__
@@ -81,6 +76,14 @@ static inline void set_smp_ipi_range(int ipi_base, int n)*/ asmlinkage void secondary_start_kernel(void); +union secondary_status { +u64 val; +union { +u8 flags[CPU_STATUS_FLAGS_MAX ]; +u8 early_flags[EARLY_CPU_STUCK_REASON_MAX ]; + }; +}; +/* * Initial data for bringing up a secondary CPU. * @status - Result passed back from the secondary CPU to
@@ -88,7 +91,7 @@ asmlinkage void secondary_start_kernel(void);*/ struct secondary_data {struct task_struct *task; -long status; +union secondary_status status;cpumask_t cpu_died_early_mask; };
@@ -123,9 +126,11 @@ static inline void __noreturn cpu_park_loop(void) } } -static inline void update_cpu_boot_status (int val) +static inline void update_cpu_boot_status (const unsigned int val) { -WRITE_ONCE (secondary_data.status, val); +BUILD_BUG_ON (val >=CPU_STATUS_FLAGS_MAX ); + +WRITE_ONCE (secondary_data.status.flags[val],1 );/* Ensure the visibility of the status update */ dsb (ishst); }
arch/arm64/include/asm/topology.h +2 -0
@@ -41,4 +41,6 @@ void update_freq_counters_refs(void);#include <asm-generic/topology.h> +#define cpu_primary_thread_mask cpu_none_mask +#endif /* _ASM_ARM_TOPOLOGY_H */
arch/arm64/kernel/head.S +5 -8
@@ -393,7 +393,6 @@ SYM_FUNC_START_LOCAL(__secondary_switched) mov x0, x20 bl finalise_el2 - str_l xzr, __early_cpu_boot_status, x3 adr_l x5, vectors msr vbar_el1, x5 isb
@@ -440,14 +439,14 @@ SYM_FUNC_END(set_cpu_boot_mode_flag) * * update_early_cpu_boot_status tmp, status * - Corrupts tmp1, tmp2 - * - Writes 'status' to __early_cpu_boot_status and makes sure + * - Writes 1 to the 'status' field of __early_cpu_boot_status and makes sure * it is committed to memory. */ .macro update_early_cpu_boot_status status, tmp1, tmp2 - mov \tmp2, #\status adr_l \tmp1, __early_cpu_boot_status - str \tmp2, [\tmp1] + mov \tmp2, #1 + strb w\tmp2, [\tmp1, #\status] dmb sy dc ivac, \tmp1 // Invalidate potentially stale cache line .endm
@@ -495,8 +494,7 @@ SYM_FUNC_START(__cpu_secondary_check52bitva) b.ge 2f #endif - update_early_cpu_boot_status \ - CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_52_BIT_VA, x0, x1 + update_early_cpu_boot_status EARLY_CPU_STUCK_REASON_52_BIT_VA, x0, x1 1: wfe wfi b 1b
@@ -507,8 +505,7 @@ SYM_FUNC_END(__cpu_secondary_check52bitva) SYM_FUNC_START_LOCAL(__no_granule_support) /* Indicate that this CPU can't boot and is stuck in the kernel */ - update_early_cpu_boot_status \ - CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_NO_GRAN, x1, x2 + update_early_cpu_boot_status EARLY_CPU_STUCK_REASON_NO_GRAN, x1, x2 1: wfe wfi
arch/arm64/kernel/smp.c +42 -40
126 lines not on this page · raw message
arch/arm64/mm/mmu.c +1 -1
@@ -61,7 +61,7 @@ static bool rodata_is_rw __ro_after_init = true;* The booting CPU updates the failed status @__early_cpu_boot_status, * with MMU turned off. */ -long __section(".mmuoff.data.write") __early_cpu_boot_status; +long __section(".mmuoff.data.write") __early_cpu_boot_status = 0;static DEFINE_SPINLOCK (swapper_pgdir_lock);static DEFINE_MUTEX (fixmap_lock);
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
Will Deacon
maintainerPATCH v2 23/23 arm64: smp: Harden parallel CPU bringup against broken PSCI firmware
In reply to Will DeaconSince firmware has occasionally been known to get things wrong, harden our parallel CPU bringup code against a PSCI implementation that passes the CPU_ON argument to an incorrect CPU.
The primary CPU writes the MPIDR of the incoming CPU to the penultimate slot of its task stack and this is then checked against the MPIDR_EL1 register during early kernel entry. A mismatch is reported via the existing failure reporting mechanism and the CPU is not brought online.
--- arch/arm64/include/asm/smp.h | 4 +++- arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kernel/head.S | 39 ++++++++++++++++++++++++++++----- arch/arm64/kernel/smp.c | 12 ++++++++-- 4 files changed, 47 insertions(+), 9 deletions(-)
arch/arm64/include/asm/smp.h +3 -1
@@ -15,7 +15,9 @@/* Offsets for late (i.e. MMU-enabled) CPU boot reasons */ /* Fatal system error detected by secondary CPU, crash the system */ #define CPU_PANIC_KERNEL (0) -#define CPU_STATUS_FLAGS_MAX (1) +/* The PSCI v0.2+ implementation passed the wrong argument */ +#define CPU_BROKEN_PSCI_ARG (1) +#define CPU_STATUS_FLAGS_MAX (2)#ifndef __ASSEMBLER__
arch/arm64/kernel/asm-offsets.c +1 -0
@@ -97,6 +97,7 @@ int main(void)BLANK ();#endif DEFINE (CPU_BOOT_TASK , offsetof(struct secondary_data , task)); + DEFINE(CPU_BOOT_STATUS_FLAGS, offsetof(struct secondary_data, status.flags));BLANK ();DEFINE (FTR_OVR_VAL_OFFSET , offsetof(struct arm64_ftr_override , val));DEFINE (FTR_OVR_MASK_OFFSET , offsetof(struct arm64_ftr_override , mask));
arch/arm64/kernel/head.S +33 -6
@@ -192,11 +192,21 @@ SYM_CODE_END(preserve_boot_args) * its location in the task stack. We reserve the entire pt_regs space * for consistency with user tasks and kthreads. */ - .macro init_cpu_task tsk, tmp1, tmp2 + .macro init_cpu_task tsk, tmp1, tmp2, check_mpidr= msr sp_el0, \tsk ldr \tmp1, [\tsk, #TSK_STACK] - add sp, \tmp1, #THREAD_SIZE + mov sp, \tmp1 + .ifnb \check_mpidr + mov_q \tmp1, MPIDR_HWID_BITMASK + mrs \tmp2, mpidr_el1 + and \tmp2, \tmp2, \tmp1 + ldr \tmp1, [sp, #8] + sub \tmp1, \tmp1, \tmp2 + cbnz \tmp1, __cpu_secondary_broken_psci_arg + .endif + + add sp, sp, #THREAD_SIZE sub sp, sp, #PT_REGS_SIZE stp xzr, xzr, [sp, #S_STACKFRAME]
@@ -397,15 +407,16 @@ SYM_FUNC_START_LOCAL(__secondary_switched) msr vbar_el1, x5 isb + cbz x19, 1f mov x2, x19 - cbnz x2, 1f + init_cpu_task x2, x1, x3, check_mpidr=1 + b 2f +1: adr_l x0, secondary_data ldr x2, [x0, #CPU_BOOT_TASK] cbz x2, __secondary_too_slow - -1: init_cpu_task x2, x1, x3 - +2: #ifdef CONFIG_ARM64_PTR_AUTH ptrauth_keys_init_cpu x2, x3, x4, x5 #endif
@@ -451,6 +462,22 @@ SYM_FUNC_END(set_cpu_boot_mode_flag) dc ivac, \tmp1 // Invalidate potentially stale cache line .endm + .macro update_cpu_boot_status status, tmp1, tmp2 + adr_l \tmp1, secondary_data + add \tmp1, \tmp1, #CPU_BOOT_STATUS_FLAGS + \status + mov \tmp2, #1 + strb w\tmp2, [\tmp1] + .endm + +SYM_FUNC_START_LOCAL(__cpu_secondary_broken_psci_arg) + update_cpu_boot_status CPU_BROKEN_PSCI_ARG, x0, x1 +1: + wfe + wfi + b 1b +SYM_FUNC_END(__cpu_secondary_broken_psci_arg) + + /* * Enable the MMU. *
arch/arm64/kernel/smp.c +10 -2
@@ -119,10 +119,13 @@ int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *idle)* We need to tell the secondary core where to find its stack and the * page tables. */ -if (smp_parallel_bringup) +if (smp_parallel_bringup) { +/* Avoid clobbering STACK_END_MAGIC */ + *(end_of_stack (idle) +1 ) =cpu_logical_map (cpu); arg = idle; -else + }else { secondary_data.task = idle; + }/* Now bring the CPU into our world */ if (ops->cpu_boot)
@@ -158,6 +161,11 @@ void arch_cpuhp_cleanup_kick_cpu(unsigned int cpu, bool is_alive) } status =READ_ONCE (secondary_data.status); +if (status.flags[CPU_BROKEN_PSCI_ARG ]) { +pr_crit_once ("CPU%u detected broken PSCI v0.2+ CPU_ON argument passing\n" , + cpu); + } +if (status.flags[CPU_PANIC_KERNEL ])panic ("CPU%u detected unsupported configuration\n" , cpu);
Signature · 1 line
2.56.0.385.gd3acb90ef8-goog
From: Will Deacon <[email protected]> Sent: Friday, October 9, 2026 3:07 AM
Quoted 29 lines
Hi everyone,
This is version two of the patches I previously posted here:
https://lore.kernel.org/all/[email protected]/
There have been quite a few changes since last time, thanks to all the feedback from humans and robots alike. These include:
* Added tags (but I didn't carry over Pankaj's T-b, since the series has changed a fair bit).
* Fix secondary entry assembly code to handle timeouts correctly.
* Fix stashing of MPIDR so that we don't clobber
STACK_END_MAGIC.* Fix NULL dereference if PSCI fails to initialise.
* Enable RCU as soon as a secondary CPU is alive.
* Drop pointless (unreachable) optimisation to
arch_cpuhp_sync_state_poll().* Include cpufeature patches from Fuad so that '
cpu_data' is populated with register values taking into account any cmdline overrides. This is necessary for correct handling of 'boot_cpu_data' later on, where the previous version of this series stashed a stale copy of mpamidr.
Quoted 7 lines
As before, the first four patches are small changes to the generic code to make it a little more "Arm-shaped" but the rest of the series is largely confined to the arm64 architecture and PSCI driver code.
The series is based on -rc2, as it otherwise conflicts with Fuad's GMID fix that was recently merged upstream.
As I reported [1] for Jinjie Ruan's original series, I tested this series on an ARM64 VM running on Hyper-V in the Azure cloud. Tested with 16 vCPUs in the VM and with 96 vCPUs in the VM. No issues. I mainly wanted to make sure nothing got crosswise with Hyper-V's implementation of PSCI.
Quoted 82 lines
Cheers,
Will
Cc Thomas Gleixner <[email protected]>Cc Ben Horgan <[email protected]>Cc Ștefania Ion <[email protected]>Cc Catalin Marinas <[email protected]>Cc Pankaj Patil <[email protected]>Cc Borislav Petkov <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Cc Jinjie Ruan <[email protected]>Cc Mark Rutland <[email protected]>Cc Tarun Sahu <[email protected]>Cc Fuad Tabba <[email protected]>Cc David Woodhouse <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Marc Zyngier <[email protected]>--->8
Fuad Tabba (4):
arm64: cpufeature: Check arm64_ftr_regs[] before the first store
arm64: cpufeature: Addread_cpuid_with_overrides()
arm64: cpufeature: ReadMPAMIDR_EL1in__cpuinfo_store_cpu()
arm64: cpufeature: Store every ID register with its overrides appliedWill Deacon (19):
cpu/hotplug: Clean upcmpxchg()logic incpuhp_can_boot_ap()cpu/hotplug: Avoid trying to bring up CPUs that are already online cpu/hotplug: Avoid busy-polling on archs wherecpu_relax()is a no-op cpu/hotplug: Propagate bring-up status to
arch_cpuhp_cleanup_kick_cpu()
arm64: smp: Tidy upsmp_prepare_cpus()
arm64: smp: Tidy up cpuinfo init and cpufeature updates
arm64: smp: Defer update of secondary CPU capabilities
arm64: smp: Don't bother printing the I-cache policy for each CPU
arm64: smp: Defer RCU registration during secondary CPU bringup
arm64: smp: Use genericHOTPLUG_CORE_SYNC_FULLmachinery for CPU
onlining
arm64: smp: Use genericHOTPLUG_SPLIT_STARTUPmachinery for CPU
onlining
arm64:cpu_ops: Make 'cpu_operations' pointer global instead of
per-cpu
arm64:cpu_ops: Introduceget_secondary_cpu_ops()firmware/psci: Cache PSCI v0.2+ version number to avoid redundant SMCsfirmware/psci: Extend ->cpu_on() callback to take an additional
argument
arm64:cpu_ops: Expose optional argument to target cpu in ->cpu_boot()
arm64: smp: Pass secondary CPU boot parameters via firmware if
possible
arm64: smp: Use genericHOTPLUG_PARALLELmachinery for CPU onlining
arm64: smp: Harden parallel CPU bringup against broken PSCI firmwarearch/arm/kernel/psci_smp.c | 4 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cpu.h | 7 +- arch/arm64/include/asm/cpu_ops.h | 7 +- arch/arm64/include/asm/cpufeature.h | 11 + arch/arm64/include/asm/smp.h | 40 ++- arch/arm64/include/asm/topology.h | 2 + arch/arm64/kernel/acpi_parking_protocol.c | 3 +- arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kernel/cpu_ops.c | 34 +- arch/arm64/kernel/cpufeature.c | 397 +++++++++++----------- arch/arm64/kernel/cpuinfo.c | 122 +++---- arch/arm64/kernel/head.S | 60 +++- arch/arm64/kernel/psci.c | 14 +- arch/arm64/kernel/smp.c | 183 +++++----- arch/arm64/kernel/smp_spin_table.c | 2 +- arch/arm64/mm/mmu.c | 2 +- arch/x86/kernel/smpboot.c | 4 +- drivers/firmware/psci/psci.c | 27 +- include/linux/cpuhotplug.h | 2 +- include/linux/psci.h | 3 +- include/linux/rcutree.h | 2 +- kernel/cpu.c | 18 +- 23 files changed, 508 insertions(+), 439 deletions(-)--
2.56.0.385.gd3acb90ef8-goog
Quoted 28 lines · Will Deacon
Hi everyone,
This is version two of the patches I previously posted here:
https://lore.kernel.org/all/[email protected]/
There have been quite a few changes since last time, thanks to all the feedback from humans and robots alike. These include:
* Added tags (but I didn't carry over Pankaj's T-b, since the series has changed a fair bit).
* Fix secondary entry assembly code to handle timeouts correctly.
* Fix stashing of MPIDR so that we don't clobber
STACK_END_MAGIC.* Fix NULL dereference if PSCI fails to initialise.
* Enable RCU as soon as a secondary CPU is alive.
* Drop pointless (unreachable) optimisation to
arch_cpuhp_sync_state_poll().* Include cpufeature patches from Fuad so that '
cpu_data' is populated with register values taking into account any cmdline overrides. This is necessary for correct handling of 'boot_cpu_data' later on, where the previous version of this series stashed a stale copy of mpamidr.
Quoted 6 lines
As before, the first four patches are small changes to the generic code to make it a little more "Arm-shaped" but the rest of the series is largely confined to the arm64 architecture and PSCI driver code.
The series is based on -rc2, as it otherwise conflicts with Fuad's GMID fix that was recently merged upstream.
Hi Will,
I tested this series on v7.3 rc6.
On Qemu (ms):
| Platform | baseline| P=0 | P=1 | Delta(%)| | ---------------- | ------- | ------- | ------ | ------- | | 128 vCPUs KVM | 77.45 | 81.59 | 487.5 | -529.5% | | 64-core ATF QEMU | 878.63 | 884.69 | 657.43 | 25.18% |
The parallel boot time under KVM has become much slower, and it is unclear whether there is a concurrency bottleneck inside KVM.
And the parallel boot time of ATF-based QEMU is reduced by about 25%.
On Kunpeng 192-core HIP12 server (ms):
| v7.3 rc6 | P=0 | P=1 | Delta(%)| | ------- | ------- | ------- | ------ | | 14573.17 | 14573.17 | 8581.10 | 41.21% |
But, similar to the test results of the previous version, the secondary cores boot in parallel, reducing the boot time by 41%.
Quoted 80 lines
Cheers,
Will
Cc Thomas Gleixner <[email protected]>Cc Ben Horgan <[email protected]>Cc Ștefania Ion <[email protected]>Cc Catalin Marinas <[email protected]>Cc Pankaj Patil <[email protected]>Cc Borislav Petkov <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Cc Jinjie Ruan <[email protected]>Cc Mark Rutland <[email protected]>Cc Tarun Sahu <[email protected]>Cc Fuad Tabba <[email protected]>Cc David Woodhouse <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Marc Zyngier <[email protected]>--->8
Fuad Tabba (4):
arm64: cpufeature: Check arm64_ftr_regs[] before the first store
arm64: cpufeature: Addread_cpuid_with_overrides()
arm64: cpufeature: ReadMPAMIDR_EL1in__cpuinfo_store_cpu()
arm64: cpufeature: Store every ID register with its overrides appliedWill Deacon (19):
cpu/hotplug: Clean upcmpxchg()logic incpuhp_can_boot_ap()cpu/hotplug: Avoid trying to bring up CPUs that are already online cpu/hotplug: Avoid busy-polling on archs wherecpu_relax()is a no-op cpu/hotplug: Propagate bring-up status to
arch_cpuhp_cleanup_kick_cpu()
arm64: smp: Tidy upsmp_prepare_cpus()
arm64: smp: Tidy up cpuinfo init and cpufeature updates
arm64: smp: Defer update of secondary CPU capabilities
arm64: smp: Don't bother printing the I-cache policy for each CPU
arm64: smp: Defer RCU registration during secondary CPU bringup
arm64: smp: Use genericHOTPLUG_CORE_SYNC_FULLmachinery for CPU
onlining
arm64: smp: Use genericHOTPLUG_SPLIT_STARTUPmachinery for CPU
onlining
arm64:cpu_ops: Make 'cpu_operations' pointer global instead of
per-cpu
arm64:cpu_ops: Introduceget_secondary_cpu_ops()firmware/psci: Cache PSCI v0.2+ version number to avoid redundant SMCsfirmware/psci: Extend ->cpu_on() callback to take an additional
argument
arm64:cpu_ops: Expose optional argument to target cpu in ->cpu_boot()
arm64: smp: Pass secondary CPU boot parameters via firmware if
possible
arm64: smp: Use genericHOTPLUG_PARALLELmachinery for CPU onlining
arm64: smp: Harden parallel CPU bringup against broken PSCI firmwarearch/arm/kernel/psci_smp.c | 4 +- arch/arm64/Kconfig | 2 +- arch/arm64/include/asm/cpu.h | 7 +- arch/arm64/include/asm/cpu_ops.h | 7 +- arch/arm64/include/asm/cpufeature.h | 11 + arch/arm64/include/asm/smp.h | 40 ++- arch/arm64/include/asm/topology.h | 2 + arch/arm64/kernel/acpi_parking_protocol.c | 3 +- arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kernel/cpu_ops.c | 34 +- arch/arm64/kernel/cpufeature.c | 397 +++++++++++----------- arch/arm64/kernel/cpuinfo.c | 122 +++---- arch/arm64/kernel/head.S | 60 +++- arch/arm64/kernel/psci.c | 14 +- arch/arm64/kernel/smp.c | 183 +++++----- arch/arm64/kernel/smp_spin_table.c | 2 +- arch/arm64/mm/mmu.c | 2 +- arch/x86/kernel/smpboot.c | 4 +- drivers/firmware/psci/psci.c | 27 +- include/linux/cpuhotplug.h | 2 +- include/linux/psci.h | 3 +- include/linux/rcutree.h | 2 +- kernel/cpu.c | 18 +- 23 files changed, 508 insertions(+), 439 deletions(-)
Signature · 2 lines
Best regards, Jinjie
Catalin Marinas
maintainerRe: (subset) [PATCH v2 00/23] arm64: Implement parallel CPU onlining with PSCI v0.2+
In reply to Will DeaconQuoted 8 lines · Will Deacon
This is version two of the patches I previously posted here:
https://lore.kernel.org/all/[email protected]/
There have been quite a few changes since last time, thanks to all the feedback from humans and robots alike. These include:
[...]
Applied to arm64 (for-next/secondary-boot-prep), thanks!
[05/23] arm64: cpufeature: Check arm64_ftr_regs[] before the first store
https://git.kernel.org/arm64/c/72add8bf4d57 [06/23] arm64: cpufeature: Add read_cpuid_with_overrides() https://git.kernel.org/arm64/c/cd9df8b01977 [07/23] arm64: cpufeature: Read MPAMIDR_EL1 in __cpuinfo_store_cpu() https://git.kernel.org/arm64/c/342e62ded77c [08/23] arm64: cpufeature: Store every ID register with its overrides applied https://git.kernel.org/arm64/c/fbb6e5708311 [09/23] arm64: smp: Tidy up smp_prepare_cpus() https://git.kernel.org/arm64/c/9ddba588450f [10/23] arm64: smp: Tidy up cpuinfo init and cpufeature updates https://git.kernel.org/arm64/c/f0a14972b5b5 [11/23] arm64: smp: Defer update of secondary CPU capabilities https://git.kernel.org/arm64/c/c8491bbf35e5 [12/23] arm64: smp: Don't bother printing the I-cache policy for each CPU https://git.kernel.org/arm64/c/6a52689f698a [13/23] arm64: smp: Defer RCU registration during secondary CPU bringup https://git.kernel.org/arm64/c/e21f4de88aca