diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-08-23 09:46:34 -0400 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-11-06 00:11:45 -0500 |
commit | 63eca94ca206e342bad4a06a86d8e7eda3053a4e (patch) | |
tree | 8037367b1c0a2d428ad5add62c842223eb290520 /arch/arc | |
parent | b6fe8e7c0189d017fdba90d1cd134337098c19c3 (diff) |
ARC: [SMP] ASID allocation
-Track a Per CPU ASID counter
-mm-per-cpu ASID (multiple threads, or mm migrated around)
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/include/asm/mmu.h | 2 | ||||
-rw-r--r-- | arch/arc/include/asm/mmu_context.h | 44 | ||||
-rw-r--r-- | arch/arc/mm/tlb.c | 14 |
3 files changed, 37 insertions, 23 deletions
diff --git a/arch/arc/include/asm/mmu.h b/arch/arc/include/asm/mmu.h index c2663b32866b..8c84ae98c337 100644 --- a/arch/arc/include/asm/mmu.h +++ b/arch/arc/include/asm/mmu.h | |||
@@ -48,7 +48,7 @@ | |||
48 | #ifndef __ASSEMBLY__ | 48 | #ifndef __ASSEMBLY__ |
49 | 49 | ||
50 | typedef struct { | 50 | typedef struct { |
51 | unsigned long asid; /* 8 bit MMU PID + Generation cycle */ | 51 | unsigned long asid[NR_CPUS]; /* 8 bit MMU PID + Generation cycle */ |
52 | } mm_context_t; | 52 | } mm_context_t; |
53 | 53 | ||
54 | #ifdef CONFIG_ARC_DBG_TLB_PARANOIA | 54 | #ifdef CONFIG_ARC_DBG_TLB_PARANOIA |
diff --git a/arch/arc/include/asm/mmu_context.h b/arch/arc/include/asm/mmu_context.h index 43a1b51bb8cc..45f06f566b02 100644 --- a/arch/arc/include/asm/mmu_context.h +++ b/arch/arc/include/asm/mmu_context.h | |||
@@ -30,13 +30,13 @@ | |||
30 | * "Fast Context Switch" i.e. no TLB flush on ctxt-switch | 30 | * "Fast Context Switch" i.e. no TLB flush on ctxt-switch |
31 | * | 31 | * |
32 | * Linux assigns each task a unique ASID. A simple round-robin allocation | 32 | * Linux assigns each task a unique ASID. A simple round-robin allocation |
33 | * of H/w ASID is done using software tracker @asid_cache. | 33 | * of H/w ASID is done using software tracker @asid_cpu. |
34 | * When it reaches max 255, the allocation cycle starts afresh by flushing | 34 | * When it reaches max 255, the allocation cycle starts afresh by flushing |
35 | * the entire TLB and wrapping ASID back to zero. | 35 | * the entire TLB and wrapping ASID back to zero. |
36 | * | 36 | * |
37 | * A new allocation cycle, post rollover, could potentially reassign an ASID | 37 | * A new allocation cycle, post rollover, could potentially reassign an ASID |
38 | * to a different task. Thus the rule is to refresh the ASID in a new cycle. | 38 | * to a different task. Thus the rule is to refresh the ASID in a new cycle. |
39 | * The 32 bit @asid_cache (and mm->asid) have 8 bits MMU PID and rest 24 bits | 39 | * The 32 bit @asid_cpu (and mm->asid) have 8 bits MMU PID and rest 24 bits |
40 | * serve as cycle/generation indicator and natural 32 bit unsigned math | 40 | * serve as cycle/generation indicator and natural 32 bit unsigned math |
41 | * automagically increments the generation when lower 8 bits rollover. | 41 | * automagically increments the generation when lower 8 bits rollover. |
42 | */ | 42 | */ |
@@ -47,9 +47,11 @@ | |||
47 | #define MM_CTXT_FIRST_CYCLE (MM_CTXT_ASID_MASK + 1) | 47 | #define MM_CTXT_FIRST_CYCLE (MM_CTXT_ASID_MASK + 1) |
48 | #define MM_CTXT_NO_ASID 0UL | 48 | #define MM_CTXT_NO_ASID 0UL |
49 | 49 | ||
50 | #define hw_pid(mm) (mm->context.asid & MM_CTXT_ASID_MASK) | 50 | #define asid_mm(mm, cpu) mm->context.asid[cpu] |
51 | #define hw_pid(mm, cpu) (asid_mm(mm, cpu) & MM_CTXT_ASID_MASK) | ||
51 | 52 | ||
52 | extern unsigned int asid_cache; | 53 | DECLARE_PER_CPU(unsigned int, asid_cache); |
54 | #define asid_cpu(cpu) per_cpu(asid_cache, cpu) | ||
53 | 55 | ||
54 | /* | 56 | /* |
55 | * Get a new ASID if task doesn't have a valid one (unalloc or from prev cycle) | 57 | * Get a new ASID if task doesn't have a valid one (unalloc or from prev cycle) |
@@ -57,6 +59,7 @@ extern unsigned int asid_cache; | |||
57 | */ | 59 | */ |
58 | static inline void get_new_mmu_context(struct mm_struct *mm) | 60 | static inline void get_new_mmu_context(struct mm_struct *mm) |
59 | { | 61 | { |
62 | const unsigned int cpu = smp_processor_id(); | ||
60 | unsigned long flags; | 63 | unsigned long flags; |
61 | 64 | ||
62 | local_irq_save(flags); | 65 | local_irq_save(flags); |
@@ -71,11 +74,11 @@ static inline void get_new_mmu_context(struct mm_struct *mm) | |||
71 | * first need to destroy the context, setting it to invalid | 74 | * first need to destroy the context, setting it to invalid |
72 | * value. | 75 | * value. |
73 | */ | 76 | */ |
74 | if (!((mm->context.asid ^ asid_cache) & MM_CTXT_CYCLE_MASK)) | 77 | if (!((asid_mm(mm, cpu) ^ asid_cpu(cpu)) & MM_CTXT_CYCLE_MASK)) |
75 | goto set_hw; | 78 | goto set_hw; |
76 | 79 | ||
77 | /* move to new ASID and handle rollover */ | 80 | /* move to new ASID and handle rollover */ |
78 | if (unlikely(!(++asid_cache & MM_CTXT_ASID_MASK))) { | 81 | if (unlikely(!(++asid_cpu(cpu) & MM_CTXT_ASID_MASK))) { |
79 | 82 | ||
80 | flush_tlb_all(); | 83 | flush_tlb_all(); |
81 | 84 | ||
@@ -84,15 +87,15 @@ static inline void get_new_mmu_context(struct mm_struct *mm) | |||
84 | * If the container itself wrapped around, set it to a non zero | 87 | * If the container itself wrapped around, set it to a non zero |
85 | * "generation" to distinguish from no context | 88 | * "generation" to distinguish from no context |
86 | */ | 89 | */ |
87 | if (!asid_cache) | 90 | if (!asid_cpu(cpu)) |
88 | asid_cache = MM_CTXT_FIRST_CYCLE; | 91 | asid_cpu(cpu) = MM_CTXT_FIRST_CYCLE; |
89 | } | 92 | } |
90 | 93 | ||
91 | /* Assign new ASID to tsk */ | 94 | /* Assign new ASID to tsk */ |
92 | mm->context.asid = asid_cache; | 95 | asid_mm(mm, cpu) = asid_cpu(cpu); |
93 | 96 | ||
94 | set_hw: | 97 | set_hw: |
95 | write_aux_reg(ARC_REG_PID, hw_pid(mm) | MMU_ENABLE); | 98 | write_aux_reg(ARC_REG_PID, hw_pid(mm, cpu) | MMU_ENABLE); |
96 | 99 | ||
97 | local_irq_restore(flags); | 100 | local_irq_restore(flags); |
98 | } | 101 | } |
@@ -104,10 +107,24 @@ set_hw: | |||
104 | static inline int | 107 | static inline int |
105 | init_new_context(struct task_struct *tsk, struct mm_struct *mm) | 108 | init_new_context(struct task_struct *tsk, struct mm_struct *mm) |
106 | { | 109 | { |
107 | mm->context.asid = MM_CTXT_NO_ASID; | 110 | int i; |
111 | |||
112 | for_each_possible_cpu(i) | ||
113 | asid_mm(mm, i) = MM_CTXT_NO_ASID; | ||
114 | |||
108 | return 0; | 115 | return 0; |
109 | } | 116 | } |
110 | 117 | ||
118 | static inline void destroy_context(struct mm_struct *mm) | ||
119 | { | ||
120 | unsigned long flags; | ||
121 | |||
122 | /* Needed to elide CONFIG_DEBUG_PREEMPT warning */ | ||
123 | local_irq_save(flags); | ||
124 | asid_mm(mm, smp_processor_id()) = MM_CTXT_NO_ASID; | ||
125 | local_irq_restore(flags); | ||
126 | } | ||
127 | |||
111 | /* Prepare the MMU for task: setup PID reg with allocated ASID | 128 | /* Prepare the MMU for task: setup PID reg with allocated ASID |
112 | If task doesn't have an ASID (never alloc or stolen, get a new ASID) | 129 | If task doesn't have an ASID (never alloc or stolen, get a new ASID) |
113 | */ | 130 | */ |
@@ -131,11 +148,6 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, | |||
131 | */ | 148 | */ |
132 | #define activate_mm(prev, next) switch_mm(prev, next, NULL) | 149 | #define activate_mm(prev, next) switch_mm(prev, next, NULL) |
133 | 150 | ||
134 | static inline void destroy_context(struct mm_struct *mm) | ||
135 | { | ||
136 | mm->context.asid = MM_CTXT_NO_ASID; | ||
137 | } | ||
138 | |||
139 | /* it seemed that deactivate_mm( ) is a reasonable place to do book-keeping | 151 | /* it seemed that deactivate_mm( ) is a reasonable place to do book-keeping |
140 | * for retiring-mm. However destroy_context( ) still needs to do that because | 152 | * for retiring-mm. However destroy_context( ) still needs to do that because |
141 | * between mm_release( ) = >deactive_mm( ) and | 153 | * between mm_release( ) = >deactive_mm( ) and |
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 5f53050abf60..db0f0f823980 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c | |||
@@ -100,7 +100,7 @@ | |||
100 | 100 | ||
101 | 101 | ||
102 | /* A copy of the ASID from the PID reg is kept in asid_cache */ | 102 | /* A copy of the ASID from the PID reg is kept in asid_cache */ |
103 | unsigned int asid_cache = MM_CTXT_FIRST_CYCLE; | 103 | DEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE; |
104 | 104 | ||
105 | /* | 105 | /* |
106 | * Utility Routine to erase a J-TLB entry | 106 | * Utility Routine to erase a J-TLB entry |
@@ -274,6 +274,7 @@ noinline void local_flush_tlb_mm(struct mm_struct *mm) | |||
274 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | 274 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, |
275 | unsigned long end) | 275 | unsigned long end) |
276 | { | 276 | { |
277 | const unsigned int cpu = smp_processor_id(); | ||
277 | unsigned long flags; | 278 | unsigned long flags; |
278 | 279 | ||
279 | /* If range @start to @end is more than 32 TLB entries deep, | 280 | /* If range @start to @end is more than 32 TLB entries deep, |
@@ -297,9 +298,9 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | |||
297 | 298 | ||
298 | local_irq_save(flags); | 299 | local_irq_save(flags); |
299 | 300 | ||
300 | if (vma->vm_mm->context.asid != MM_CTXT_NO_ASID) { | 301 | if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { |
301 | while (start < end) { | 302 | while (start < end) { |
302 | tlb_entry_erase(start | hw_pid(vma->vm_mm)); | 303 | tlb_entry_erase(start | hw_pid(vma->vm_mm, cpu)); |
303 | start += PAGE_SIZE; | 304 | start += PAGE_SIZE; |
304 | } | 305 | } |
305 | } | 306 | } |
@@ -346,6 +347,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | |||
346 | 347 | ||
347 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | 348 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) |
348 | { | 349 | { |
350 | const unsigned int cpu = smp_processor_id(); | ||
349 | unsigned long flags; | 351 | unsigned long flags; |
350 | 352 | ||
351 | /* Note that it is critical that interrupts are DISABLED between | 353 | /* Note that it is critical that interrupts are DISABLED between |
@@ -353,8 +355,8 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | |||
353 | */ | 355 | */ |
354 | local_irq_save(flags); | 356 | local_irq_save(flags); |
355 | 357 | ||
356 | if (vma->vm_mm->context.asid != MM_CTXT_NO_ASID) { | 358 | if (asid_mm(vma->vm_mm, cpu) != MM_CTXT_NO_ASID) { |
357 | tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm)); | 359 | tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm, cpu)); |
358 | utlb_invalidate(); | 360 | utlb_invalidate(); |
359 | } | 361 | } |
360 | 362 | ||
@@ -400,7 +402,7 @@ void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) | |||
400 | 402 | ||
401 | local_irq_save(flags); | 403 | local_irq_save(flags); |
402 | 404 | ||
403 | tlb_paranoid_check(vma->vm_mm->context.asid, address); | 405 | tlb_paranoid_check(asid_mm(vma->vm_mm, smp_processor_id()), address); |
404 | 406 | ||
405 | address &= PAGE_MASK; | 407 | address &= PAGE_MASK; |
406 | 408 | ||