Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'asid-allocation' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into devel-stable

+118 -188
+4 -7
arch/arm/include/asm/mmu.h
··· 5 5 6 6 typedef struct { 7 7 #ifdef CONFIG_CPU_HAS_ASID 8 - unsigned int id; 9 - raw_spinlock_t id_lock; 8 + u64 id; 10 9 #endif 11 10 unsigned int kvm_seq; 12 11 } mm_context_t; 13 12 14 13 #ifdef CONFIG_CPU_HAS_ASID 15 - #define ASID(mm) ((mm)->context.id & 255) 16 - 17 - /* init_mm.context.id_lock should be initialized. */ 18 - #define INIT_MM_CONTEXT(name) \ 19 - .context.id_lock = __RAW_SPIN_LOCK_UNLOCKED(name.context.id_lock), 14 + #define ASID_BITS 8 15 + #define ASID_MASK ((~0ULL) << ASID_BITS) 16 + #define ASID(mm) ((mm)->context.id & ~ASID_MASK) 20 17 #else 21 18 #define ASID(mm) (0) 22 19 #endif
+3 -79
arch/arm/include/asm/mmu_context.h
··· 24 24 25 25 #ifdef CONFIG_CPU_HAS_ASID 26 26 27 - /* 28 - * On ARMv6, we have the following structure in the Context ID: 29 - * 30 - * 31 7 0 31 - * +-------------------------+-----------+ 32 - * | process ID | ASID | 33 - * +-------------------------+-----------+ 34 - * | context ID | 35 - * +-------------------------------------+ 36 - * 37 - * The ASID is used to tag entries in the CPU caches and TLBs. 38 - * The context ID is used by debuggers and trace logic, and 39 - * should be unique within all running processes. 40 - */ 41 - #define ASID_BITS 8 42 - #define ASID_MASK ((~0) << ASID_BITS) 43 - #define ASID_FIRST_VERSION (1 << ASID_BITS) 44 - 45 - extern unsigned int cpu_last_asid; 46 - 47 - void __init_new_context(struct task_struct *tsk, struct mm_struct *mm); 48 - void __new_context(struct mm_struct *mm); 49 - void cpu_set_reserved_ttbr0(void); 50 - 51 - static inline void switch_new_context(struct mm_struct *mm) 52 - { 53 - unsigned long flags; 54 - 55 - __new_context(mm); 56 - 57 - local_irq_save(flags); 58 - cpu_switch_mm(mm->pgd, mm); 59 - local_irq_restore(flags); 60 - } 61 - 62 - static inline void check_and_switch_context(struct mm_struct *mm, 63 - struct task_struct *tsk) 64 - { 65 - if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) 66 - __check_kvm_seq(mm); 67 - 68 - /* 69 - * Required during context switch to avoid speculative page table 70 - * walking with the wrong TTBR. 71 - */ 72 - cpu_set_reserved_ttbr0(); 73 - 74 - if (!((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) 75 - /* 76 - * The ASID is from the current generation, just switch to the 77 - * new pgd. This condition is only true for calls from 78 - * context_switch() and interrupts are already disabled. 79 - */ 80 - cpu_switch_mm(mm->pgd, mm); 81 - else if (irqs_disabled()) 82 - /* 83 - * Defer the new ASID allocation until after the context 84 - * switch critical region since __new_context() cannot be 85 - * called with interrupts disabled (it sends IPIs). 86 - */ 87 - set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM); 88 - else 89 - /* 90 - * That is a direct call to switch_mm() or activate_mm() with 91 - * interrupts enabled and a new context. 92 - */ 93 - switch_new_context(mm); 94 - } 95 - 96 - #define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0) 97 - 98 - #define finish_arch_post_lock_switch \ 99 - finish_arch_post_lock_switch 100 - static inline void finish_arch_post_lock_switch(void) 101 - { 102 - if (test_and_clear_thread_flag(TIF_SWITCH_MM)) 103 - switch_new_context(current->mm); 104 - } 27 + void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk); 28 + #define init_new_context(tsk,mm) ({ mm->context.id = 0; }) 105 29 106 30 #else /* !CONFIG_CPU_HAS_ASID */ 107 31 ··· 67 143 #endif /* CONFIG_CPU_HAS_ASID */ 68 144 69 145 #define destroy_context(mm) do { } while(0) 146 + #define activate_mm(prev,next) switch_mm(prev, next, NULL) 70 147 71 148 /* 72 149 * This is called when "tsk" is about to enter lazy TLB mode. ··· 111 186 } 112 187 113 188 #define deactivate_mm(tsk,mm) do { } while (0) 114 - #define activate_mm(prev,next) switch_mm(prev, next, NULL) 115 189 116 190 #endif
+111 -102
arch/arm/mm/context.c
··· 2 2 * linux/arch/arm/mm/context.c 3 3 * 4 4 * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved. 5 + * Copyright (C) 2012 ARM Limited 6 + * 7 + * Author: Will Deacon <will.deacon@arm.com> 5 8 * 6 9 * This program is free software; you can redistribute it and/or modify 7 10 * it under the terms of the GNU General Public License version 2 as ··· 17 14 #include <linux/percpu.h> 18 15 19 16 #include <asm/mmu_context.h> 17 + #include <asm/smp_plat.h> 20 18 #include <asm/thread_notify.h> 21 19 #include <asm/tlbflush.h> 22 20 21 + /* 22 + * On ARMv6, we have the following structure in the Context ID: 23 + * 24 + * 31 7 0 25 + * +-------------------------+-----------+ 26 + * | process ID | ASID | 27 + * +-------------------------+-----------+ 28 + * | context ID | 29 + * +-------------------------------------+ 30 + * 31 + * The ASID is used to tag entries in the CPU caches and TLBs. 32 + * The context ID is used by debuggers and trace logic, and 33 + * should be unique within all running processes. 34 + */ 35 + #define ASID_FIRST_VERSION (1ULL << ASID_BITS) 36 + #define NUM_USER_ASIDS (ASID_FIRST_VERSION - 1) 37 + 38 + #define ASID_TO_IDX(asid) ((asid & ~ASID_MASK) - 1) 39 + #define IDX_TO_ASID(idx) ((idx + 1) & ~ASID_MASK) 40 + 23 41 static DEFINE_RAW_SPINLOCK(cpu_asid_lock); 24 - unsigned int cpu_last_asid = ASID_FIRST_VERSION; 42 + static atomic64_t asid_generation = ATOMIC64_INIT(ASID_FIRST_VERSION); 43 + static DECLARE_BITMAP(asid_map, NUM_USER_ASIDS); 44 + 45 + static DEFINE_PER_CPU(atomic64_t, active_asids); 46 + static DEFINE_PER_CPU(u64, reserved_asids); 47 + static cpumask_t tlb_flush_pending; 25 48 26 49 #ifdef CONFIG_ARM_LPAE 27 - void cpu_set_reserved_ttbr0(void) 50 + static void cpu_set_reserved_ttbr0(void) 28 51 { 29 52 unsigned long ttbl = __pa(swapper_pg_dir); 30 53 unsigned long ttbh = 0; ··· 66 37 isb(); 67 38 } 68 39 #else 69 - void cpu_set_reserved_ttbr0(void) 40 + static void cpu_set_reserved_ttbr0(void) 70 41 { 71 42 u32 ttb; 72 43 /* Copy TTBR1 into TTBR0 */ ··· 113 84 arch_initcall(contextidr_notifier_init); 114 85 #endif 115 86 116 - /* 117 - * We fork()ed a process, and we need a new context for the child 118 - * to run in. 119 - */ 120 - void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) 87 + static void flush_context(unsigned int cpu) 121 88 { 122 - mm->context.id = 0; 123 - raw_spin_lock_init(&mm->context.id_lock); 124 - } 89 + int i; 90 + u64 asid; 125 91 126 - static void flush_context(void) 127 - { 128 - cpu_set_reserved_ttbr0(); 129 - local_flush_tlb_all(); 130 - if (icache_is_vivt_asid_tagged()) { 131 - __flush_icache_all(); 132 - dsb(); 92 + /* Update the list of reserved ASIDs and the ASID bitmap. */ 93 + bitmap_clear(asid_map, 0, NUM_USER_ASIDS); 94 + for_each_possible_cpu(i) { 95 + if (i == cpu) { 96 + asid = 0; 97 + } else { 98 + asid = atomic64_xchg(&per_cpu(active_asids, i), 0); 99 + __set_bit(ASID_TO_IDX(asid), asid_map); 100 + } 101 + per_cpu(reserved_asids, i) = asid; 133 102 } 103 + 104 + /* Queue a TLB invalidate and flush the I-cache if necessary. */ 105 + if (!tlb_ops_need_broadcast()) 106 + cpumask_set_cpu(cpu, &tlb_flush_pending); 107 + else 108 + cpumask_setall(&tlb_flush_pending); 109 + 110 + if (icache_is_vivt_asid_tagged()) 111 + __flush_icache_all(); 134 112 } 135 113 136 - #ifdef CONFIG_SMP 137 - 138 - static void set_mm_context(struct mm_struct *mm, unsigned int asid) 114 + static int is_reserved_asid(u64 asid) 139 115 { 140 - unsigned long flags; 116 + int cpu; 117 + for_each_possible_cpu(cpu) 118 + if (per_cpu(reserved_asids, cpu) == asid) 119 + return 1; 120 + return 0; 121 + } 141 122 142 - /* 143 - * Locking needed for multi-threaded applications where the 144 - * same mm->context.id could be set from different CPUs during 145 - * the broadcast. This function is also called via IPI so the 146 - * mm->context.id_lock has to be IRQ-safe. 147 - */ 148 - raw_spin_lock_irqsave(&mm->context.id_lock, flags); 149 - if (likely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) { 123 + static void new_context(struct mm_struct *mm, unsigned int cpu) 124 + { 125 + u64 asid = mm->context.id; 126 + u64 generation = atomic64_read(&asid_generation); 127 + 128 + if (asid != 0 && is_reserved_asid(asid)) { 150 129 /* 151 - * Old version of ASID found. Set the new one and 152 - * reset mm_cpumask(mm). 130 + * Our current ASID was active during a rollover, we can 131 + * continue to use it and this was just a false alarm. 153 132 */ 154 - mm->context.id = asid; 133 + asid = generation | (asid & ~ASID_MASK); 134 + } else { 135 + /* 136 + * Allocate a free ASID. If we can't find one, take a 137 + * note of the currently active ASIDs and mark the TLBs 138 + * as requiring flushes. 139 + */ 140 + asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS); 141 + if (asid == NUM_USER_ASIDS) { 142 + generation = atomic64_add_return(ASID_FIRST_VERSION, 143 + &asid_generation); 144 + flush_context(cpu); 145 + asid = find_first_zero_bit(asid_map, NUM_USER_ASIDS); 146 + } 147 + __set_bit(asid, asid_map); 148 + asid = generation | IDX_TO_ASID(asid); 155 149 cpumask_clear(mm_cpumask(mm)); 156 150 } 157 - raw_spin_unlock_irqrestore(&mm->context.id_lock, flags); 158 151 159 - /* 160 - * Set the mm_cpumask(mm) bit for the current CPU. 161 - */ 162 - cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm)); 163 - } 164 - 165 - /* 166 - * Reset the ASID on the current CPU. This function call is broadcast 167 - * from the CPU handling the ASID rollover and holding cpu_asid_lock. 168 - */ 169 - static void reset_context(void *info) 170 - { 171 - unsigned int asid; 172 - unsigned int cpu = smp_processor_id(); 173 - struct mm_struct *mm = current->active_mm; 174 - 175 - smp_rmb(); 176 - asid = cpu_last_asid + cpu + 1; 177 - 178 - flush_context(); 179 - set_mm_context(mm, asid); 180 - 181 - /* set the new ASID */ 182 - cpu_switch_mm(mm->pgd, mm); 183 - } 184 - 185 - #else 186 - 187 - static inline void set_mm_context(struct mm_struct *mm, unsigned int asid) 188 - { 189 152 mm->context.id = asid; 190 - cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id())); 191 153 } 192 154 193 - #endif 194 - 195 - void __new_context(struct mm_struct *mm) 155 + void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk) 196 156 { 197 - unsigned int asid; 157 + unsigned long flags; 158 + unsigned int cpu = smp_processor_id(); 198 159 199 - raw_spin_lock(&cpu_asid_lock); 200 - #ifdef CONFIG_SMP 201 - /* 202 - * Check the ASID again, in case the change was broadcast from 203 - * another CPU before we acquired the lock. 204 - */ 205 - if (unlikely(((mm->context.id ^ cpu_last_asid) >> ASID_BITS) == 0)) { 206 - cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm)); 207 - raw_spin_unlock(&cpu_asid_lock); 208 - return; 209 - } 210 - #endif 211 - /* 212 - * At this point, it is guaranteed that the current mm (with 213 - * an old ASID) isn't active on any other CPU since the ASIDs 214 - * are changed simultaneously via IPI. 215 - */ 216 - asid = ++cpu_last_asid; 217 - if (asid == 0) 218 - asid = cpu_last_asid = ASID_FIRST_VERSION; 160 + if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) 161 + __check_kvm_seq(mm); 219 162 220 163 /* 221 - * If we've used up all our ASIDs, we need 222 - * to start a new version and flush the TLB. 164 + * Required during context switch to avoid speculative page table 165 + * walking with the wrong TTBR. 223 166 */ 224 - if (unlikely((asid & ~ASID_MASK) == 0)) { 225 - asid = cpu_last_asid + smp_processor_id() + 1; 226 - flush_context(); 227 - #ifdef CONFIG_SMP 228 - smp_wmb(); 229 - smp_call_function(reset_context, NULL, 1); 230 - #endif 231 - cpu_last_asid += NR_CPUS; 232 - } 167 + cpu_set_reserved_ttbr0(); 233 168 234 - set_mm_context(mm, asid); 235 - raw_spin_unlock(&cpu_asid_lock); 169 + if (!((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS) 170 + && atomic64_xchg(&per_cpu(active_asids, cpu), mm->context.id)) 171 + goto switch_mm_fastpath; 172 + 173 + raw_spin_lock_irqsave(&cpu_asid_lock, flags); 174 + /* Check that our ASID belongs to the current generation. */ 175 + if ((mm->context.id ^ atomic64_read(&asid_generation)) >> ASID_BITS) 176 + new_context(mm, cpu); 177 + 178 + atomic64_set(&per_cpu(active_asids, cpu), mm->context.id); 179 + cpumask_set_cpu(cpu, mm_cpumask(mm)); 180 + 181 + if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) 182 + local_flush_tlb_all(); 183 + raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); 184 + 185 + switch_mm_fastpath: 186 + cpu_switch_mm(mm->pgd, mm); 236 187 }