···24242525#ifdef CONFIG_CPU_HAS_ASID26262727-/*2828- * On ARMv6, we have the following structure in the Context ID:2929- *3030- * 31 7 03131- * +-------------------------+-----------+3232- * | process ID | ASID |3333- * +-------------------------+-----------+3434- * | context ID |3535- * +-------------------------------------+3636- *3737- * The ASID is used to tag entries in the CPU caches and TLBs.3838- * The context ID is used by debuggers and trace logic, and3939- * should be unique within all running processes.4040- */4141-#define ASID_BITS 84242-#define ASID_MASK ((~0) << ASID_BITS)4343-#define ASID_FIRST_VERSION (1 << ASID_BITS)4444-4545-extern unsigned int cpu_last_asid;4646-4747-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm);4848-void __new_context(struct mm_struct *mm);4949-void cpu_set_reserved_ttbr0(void);5050-5151-static inline void switch_new_context(struct mm_struct *mm)5252-{5353- unsigned long flags;5454-5555- __new_context(mm);5656-5757- local_irq_save(flags);5858- cpu_switch_mm(mm->pgd, mm);5959- local_irq_restore(flags);6060-}6161-6262-static inline void check_and_switch_context(struct mm_struct *mm,6363- struct task_struct *tsk)6464-{6565- if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))6666- __check_kvm_seq(mm);6767-6868- /*6969- * Required during context switch to avoid speculative page table7070- * walking with the wrong TTBR.7171- */7272- cpu_set_reserved_ttbr0();7373-7474- if (!((mm->context.id ^ cpu_last_asid) >> ASID_BITS))7575- /*7676- * The ASID is from the current generation, just switch to the7777- * new pgd. This condition is only true for calls from7878- * context_switch() and interrupts are already disabled.7979- */8080- cpu_switch_mm(mm->pgd, mm);8181- else if (irqs_disabled())8282- /*8383- * Defer the new ASID allocation until after the context8484- * switch critical region since __new_context() cannot be8585- * called with interrupts disabled (it sends IPIs).8686- */8787- set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM);8888- else8989- /*9090- * That is a direct call to switch_mm() or activate_mm() with9191- * interrupts enabled and a new context.9292- */9393- switch_new_context(mm);9494-}9595-9696-#define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0)9797-9898-#define finish_arch_post_lock_switch \9999- finish_arch_post_lock_switch100100-static inline void finish_arch_post_lock_switch(void)101101-{102102- if (test_and_clear_thread_flag(TIF_SWITCH_MM))103103- switch_new_context(current->mm);104104-}2727+void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk);2828+#define init_new_context(tsk,mm) ({ mm->context.id = 0; })1052910630#else /* !CONFIG_CPU_HAS_ASID */10731···67143#endif /* CONFIG_CPU_HAS_ASID */6814469145#define destroy_context(mm) do { } while(0)146146+#define activate_mm(prev,next) switch_mm(prev, next, NULL)7014771148/*72149 * This is called when "tsk" is about to enter lazy TLB mode.···111186}112187113188#define deactivate_mm(tsk,mm) do { } while (0)114114-#define activate_mm(prev,next) switch_mm(prev, next, NULL)115189116190#endif
+111-102
arch/arm/mm/context.c
···22 * linux/arch/arm/mm/context.c33 *44 * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.55+ * Copyright (C) 2012 ARM Limited66+ *77+ * Author: Will Deacon <will.deacon@arm.com>58 *69 * This program is free software; you can redistribute it and/or modify710 * it under the terms of the GNU General Public License version 2 as···1714#include <linux/percpu.h>18151916#include <asm/mmu_context.h>1717+#include <asm/smp_plat.h>2018#include <asm/thread_notify.h>2119#include <asm/tlbflush.h>22202121+/*2222+ * On ARMv6, we have the following structure in the Context ID:2323+ *2424+ * 31 7 02525+ * +-------------------------+-----------+2626+ * | process ID | ASID |2727+ * +-------------------------+-----------+2828+ * | context ID |2929+ * +-------------------------------------+3030+ *3131+ * The ASID is used to tag entries in the CPU caches and TLBs.3232+ * The context ID is used by debuggers and trace logic, and3333+ * should be unique within all running processes.3434+ */3535+#define ASID_FIRST_VERSION (1ULL << ASID_BITS)3636+#define NUM_USER_ASIDS (ASID_FIRST_VERSION - 1)3737+3838+#define ASID_TO_IDX(asid) ((asid & ~ASID_MASK) - 1)3939+#define IDX_TO_ASID(idx) ((idx + 1) & ~ASID_MASK)4040+2341static DEFINE_RAW_SPINLOCK(cpu_asid_lock);2424-unsigned int cpu_last_asid = ASID_FIRST_VERSION;4242+static atomic64_t asid_generation = ATOMIC64_INIT(ASID_FIRST_VERSION);4343+static DECLARE_BITMAP(asid_map, NUM_USER_ASIDS);4444+4545+static DEFINE_PER_CPU(atomic64_t, active_asids);4646+static DEFINE_PER_CPU(u64, reserved_asids);4747+static cpumask_t tlb_flush_pending;25482649#ifdef CONFIG_ARM_LPAE2727-void cpu_set_reserved_ttbr0(void)5050+static void cpu_set_reserved_ttbr0(void)2851{2952 unsigned long ttbl = __pa(swapper_pg_dir);3053 unsigned long ttbh = 0;···6637 isb();6738}6839#else6969-void cpu_set_reserved_ttbr0(void)4040+static void cpu_set_reserved_ttbr0(void)7041{7142 u32 ttb;7243 /* Copy TTBR1 into TTBR0 */···11384arch_initcall(contextidr_notifier_init);11485#endif11586116116-/*117117- * We fork()ed a process, and we need a new context for the child118118- * to run in.119119- */120120-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)8787+static void flush_context(unsigned int cpu)12188{122122- mm->context.id = 0;123123- raw_spin_lock_init(&mm->context.id_lock);124124-}8989+ int i;9090+ u64 asid;12591126126-static void flush_context(void)127127-{128128- cpu_set_reserved_ttbr0();129129- local_flush_tlb_all();130130- if (icache_is_vivt_asid_tagged()) {131131- __flush_icache_all();132132- dsb();9292+ /* Update the list of reserved ASIDs and the ASID bitmap. */9393+ bitmap_clear(asid_map, 0, NUM_USER_ASIDS);9494+ for_each_possible_cpu(i) {9595+ if (i == cpu) {9696+ asid = 0;9797+ } else {9898+ asid = atomic64_xchg(&per_cpu(active_asids, i), 0);9999+ __set_bit(ASID_TO_IDX(asid), asid_map);100100+ }101101+ per_cpu(reserved_asids, i) = asid;133102 }103103+104104+ /* Queue a TLB invalidate and flush the I-cache if necessary. */105105+ if (!tlb_ops_need_broadcast())106106+ cpumask_set_cpu(cpu, &tlb_flush_pending);107107+ else108108+ cpumask_setall(&tlb_flush_pending);109109+110110+ if (icache_is_vivt_asid_tagged())111111+ __flush_icache_all();134112}135113136136-#ifdef CONFIG_SMP137137-138138-static void set_mm_context(struct mm_struct *mm, unsigned int asid)114114+static int is_reserved_asid(u64 asid)139115{140140- unsigned long flags;116116+ int cpu;117117+ for_each_possible_cpu(cpu)118118+ if (per_cpu(reserved_asids, cpu) == asid)119119+ return 1;120120+ return 0;121121+}141122142142- /*143143- * Locking needed for multi-threaded applications where the144144- * same mm->context.id could be set from different CPUs during145145- * the broadcast. This function is also called via IPI so the146146- * mm->context.id_lock has to be IRQ-safe.147147- */148148- raw_spin_lock_irqsave(&mm->context.id_lock, flags);149149- if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) {123123+static void new_context(struct mm_struct *mm, unsigned int cpu)124124+{125125+ u64 asid = mm->context.id;126126+ u64 generation = atomic64_read(&asid_generation);127127+128128+ if (asid != 0 && is_reserved_asid(asid)) {150129 /*151151- * Old version of ASID found. Set the new one and152152- * reset mm_cpumask(mm).130130+ * Our current ASID was active during a rollover, we can131131+ * continue to use it and this was just a false alarm.153132 */154154- mm->context.id = asid;133133+ asid = generation | (asid & ~ASID_MASK);134134+ } else {135135+ /*136136+ * Allocate a free ASID. If we can't find one, take a137137+ * note of the currently active ASIDs and mark the TLBs138138+ * as requiring flushes.139139+ */140140+ asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS);141141+ if (asid == NUM_USER_ASIDS) {142142+ generation = atomic64_add_return(ASID_FIRST_VERSION,143143+ &asid_generation);144144+ flush_context(cpu);145145+ asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS);146146+ }147147+ __set_bit(asid, asid_map);148148+ asid = generation | IDX_TO_ASID(asid);155149 cpumask_clear(mm_cpumask(mm));156150 }157157- raw_spin_unlock_irqrestore(&mm->context.id_lock, flags);158151159159- /*160160- * Set the mm_cpumask(mm) bit for the current CPU.161161- */162162- cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));163163-}164164-165165-/*166166- * Reset the ASID on the current CPU. This function call is broadcast167167- * from the CPU handling the ASID rollover and holding cpu_asid_lock.168168- */169169-static void reset_context(void *info)170170-{171171- unsigned int asid;172172- unsigned int cpu = smp_processor_id();173173- struct mm_struct *mm = current->active_mm;174174-175175- smp_rmb();176176- asid = cpu_last_asid + cpu + 1;177177-178178- flush_context();179179- set_mm_context(mm, asid);180180-181181- /* set the new ASID */182182- cpu_switch_mm(mm->pgd, mm);183183-}184184-185185-#else186186-187187-static inline void set_mm_context(struct mm_struct *mm, unsigned int asid)188188-{189152 mm->context.id = asid;190190- cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id()));191153}192154193193-#endif194194-195195-void __new_context(struct mm_struct *mm)155155+void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)196156{197197- unsigned int asid;157157+ unsigned long flags;158158+ unsigned int cpu = smp_processor_id();198159199199- raw_spin_lock(&cpu_asid_lock);200200-#ifdef CONFIG_SMP201201- /*202202- * Check the ASID again, in case the change was broadcast from203203- * another CPU before we acquired the lock.204204- */205205- if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) {206206- cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));207207- raw_spin_unlock(&cpu_asid_lock);208208- return;209209- }210210-#endif211211- /*212212- * At this point, it is guaranteed that the current mm (with213213- * an old ASID) isn't active on any other CPU since the ASIDs214214- * are changed simultaneously via IPI.215215- */216216- asid = ++cpu_last_asid;217217- if (asid == 0)218218- asid = cpu_last_asid = ASID_FIRST_VERSION;160160+ if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))161161+ __check_kvm_seq(mm);219162220163 /*221221- * If we've used up all our ASIDs, we need222222- * to start a new version and flush the TLB.164164+ * Required during context switch to avoid speculative page table165165+ * walking with the wrong TTBR.223166 */224224- if (unlikely((asid & ~ASID_MASK) == 0)) {225225- asid = cpu_last_asid + smp_processor_id() + 1;226226- flush_context();227227-#ifdef CONFIG_SMP228228- smp_wmb();229229- smp_call_function(reset_context, NULL, 1);230230-#endif231231- cpu_last_asid += NR_CPUS;232232- }167167+ cpu_set_reserved_ttbr0();233168234234- set_mm_context(mm, asid);235235- raw_spin_unlock(&cpu_asid_lock);169169+ if (!((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS)170170+ && atomic64_xchg(&per_cpu(active_asids, cpu), mm->context.id))171171+ goto switch_mm_fastpath;172172+173173+ raw_spin_lock_irqsave(&cpu_asid_lock, flags);174174+ /* Check that our ASID belongs to the current generation. */175175+ if ((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS)176176+ new_context(mm, cpu);177177+178178+ atomic64_set(&per_cpu(active_asids, cpu), mm->context.id);179179+ cpumask_set_cpu(cpu, mm_cpumask(mm));180180+181181+ if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending))182182+ local_flush_tlb_all();183183+ raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);184184+185185+switch_mm_fastpath:186186+ cpu_switch_mm(mm->pgd, mm);236187}