diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 4204ffa2d104..7c4e1e4ba77b 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -238,7 +238,7 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) { unsigned long flags; unsigned int cpu = smp_processor_id(); - u64 asid; + u64 asid, old_active_asid; check_vmalloc_seq(mm); @@ -250,8 +250,17 @@ void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) cpu_set_reserved_ttbr0(); asid = atomic64_read(&mm->context.id); - if (!((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) - && atomic64_xchg(&per_cpu(active_asids, cpu), asid)) + + /* + * If our active_asids is zero, we are racing with an ASID roll-over + * on a different CPU, so skip the update (using cmpxchg if non-zero) + * and take the slow path. + */ + old_active_asid = atomic64_read(&per_cpu(active_asids, cpu)); + if (old_active_asid && + !((asid ^ atomic64_read(&asid_generation)) >> ASID_BITS) && + atomic64_cmpxchg(&per_cpu(active_asids, cpu), + old_active_asid, asid)) goto switch_mm_fastpath; raw_spin_lock_irqsave(&cpu_asid_lock, flags);