Re: [PATCH v3 09/12] arm64: cpufeature: Ensure atomic updates to system_cpucaps bitmap

From: Jinjie Ruan

Date: Wed Jul 08 2026 - 06:08:36 EST




On 7/7/2026 1:40 AM, Will Deacon wrote:
> On Wed, Jun 24, 2026 at 05:25:34PM +0800, Jinjie Ruan wrote:
>> Parallel CPU bringup allows multiple secondary CPUs to concurrently
>> execute update_cpu_capabilities() during early boot.
>>
>> The current non-atomic __set_bit() and __clear_bit() helpers perform
>> unserialized updates on the shared global bitmap, risking data races
>> and feature flag erasure.
>>
>> Upgrade these operations to set_bit() and clear_bit() to ensure all
>> concurrent modifications are properly serialized via arm64 atomics.
>>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
>> ---
>> arch/arm64/kernel/cpufeature.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
>> index be75e60d56ca..a1a13f3e01ed 100644
>> --- a/arch/arm64/kernel/cpufeature.c
>> +++ b/arch/arm64/kernel/cpufeature.c
>> @@ -3548,7 +3548,7 @@ static void update_cpu_capabilities(u16 scope_mask)
>>
>> if (!caps->matches(caps, cpucap_default_scope(caps))) {
>> if (match_all)
>> - __clear_bit(caps->capability, system_cpucaps);
>> + clear_bit(caps->capability, system_cpucaps);
>> continue;
>> }
>>
>> @@ -3559,7 +3559,7 @@ static void update_cpu_capabilities(u16 scope_mask)
>> if (!match_all && caps->desc && !caps->cpus)
>> pr_info("detected: %s\n", caps->desc);
>>
>> - __set_bit(caps->capability, system_cpucaps);
>> + set_bit(caps->capability, system_cpucaps);
>>
>> if (boot_cpu && (caps->type & SCOPE_BOOT_CPU))
>> set_bit(caps->capability, boot_cpucaps);
>
> I don't think this is sufficient. Even if we use atomic updates for the
> low-level bits, the logic here which tries to avoid re-probing features
> that have already been detected isn't going to work correctly if it's
> running concurrently with itself.
>
> I think the best bet is probably to move update_cpu_capabilities() out
> of check_local_cpu_capabilities() and call it after cpuhp_ap_sync_alive()
> when the system capabilities are not yet finalised. WDYT? That means
> we'd have something akin to setup_boot_cpu_features() for the secondary
> CPUs.

This approach most thoroughly avoids concurrency issues, as after
cpuhp_ap_sync_alive(), the secondary CPUs are woken up serially by the
boot CPU.

It also eliminates the problem of boot CPUs being stuck in deadlock wait
for the secondary CPUs, because update_cpu_capabilities() does not call
cpu_die_early() or cpu_panic_kernel().

Is it similar to the following code?

diff --git a/arch/arm64/include/asm/cpufeature.h
b/arch/arm64/include/asm/cpufeature.h
index 6c95227032b0..33185527aea9 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -651,6 +651,7 @@ void __init setup_system_features(void);
void __init setup_user_features(void);

void check_local_cpu_capabilities(void);
+void setup_secondary_cpu_capabilities(void);

u64 read_sanitised_ftr_reg(u32 id);
u64 __read_sysreg_by_encoding(u32 sys_id);
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index e3110a572bc8..4d106d90adb0 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3859,6 +3859,12 @@ void check_local_cpu_capabilities(void)
*/
check_early_cpu_features();

+ if (system_capabilities_finalized())
+ verify_local_cpu_capabilities();
+}
+
+void setup_secondary_cpu_capabilities(void)
+{
/*
* If we haven't finalised the system capabilities, this CPU gets
* a chance to update the errata work arounds and local features.
@@ -3867,8 +3873,6 @@ void check_local_cpu_capabilities(void)
*/
if (!system_capabilities_finalized())
update_cpu_capabilities(SCOPE_LOCAL_CPU);
- else
- verify_local_cpu_capabilities();
}

bool this_cpu_has_cap(unsigned int n)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index fd824b96ab19..7d4c369fa090 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -243,6 +243,7 @@ asmlinkage notrace void secondary_start_kernel(void)
* we made it into the kernel. We're still not 'online'.
*/
cpuhp_ap_sync_alive();
+ setup_secondary_cpu_capabilities();

update_cpu_features(cpu);
store_cpu_topology(cpu);


>
> Will