diff options
| author | David Mosberger-Tang <David.Mosberger@acm.org> | 2005-07-26 01:23:00 -0400 |
|---|---|---|
| committer | Tony Luck <tony.luck@intel.com> | 2005-08-12 18:05:21 -0400 |
| commit | badea125d7cbd93f1678a95cf009b3bdfe6065cd (patch) | |
| tree | c9cd47cfc5f7474fdf60735548734e647a4f7a9d | |
| parent | 7d69fa6266770eeb6317eddd46b64456e8a515bf (diff) | |
[IA64] Fix race in mm-context wrap-around logic.
The patch below should fix a race which could cause stale TLB entries.
Specifically, when 2 CPUs ended up racing for entrance to
wrap_mmu_context(). The losing CPU would find that by the time it
acquired ctx.lock, mm->context already had a valid value, but then it
failed to (re-)check the delayed TLB flushing logic and hence could
end up using a context number when there were still stale entries in
its TLB. The fix is to check for delayed TLB flushes only after
mm->context is valid (non-zero). The patch also makes GCC v4.x
happier by defining a non-volatile variant of mm_context_t called
nv_mm_context_t.
Signed-off-by: David Mosberger-Tang <David.Mosberger@acm.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>
| -rw-r--r-- | include/asm-ia64/mmu.h | 8 | ||||
| -rw-r--r-- | include/asm-ia64/mmu_context.h | 54 |
2 files changed, 37 insertions, 25 deletions
diff --git a/include/asm-ia64/mmu.h b/include/asm-ia64/mmu.h index ae1525352a25..611432ba579c 100644 --- a/include/asm-ia64/mmu.h +++ b/include/asm-ia64/mmu.h | |||
| @@ -2,10 +2,12 @@ | |||
| 2 | #define __MMU_H | 2 | #define __MMU_H |
| 3 | 3 | ||
| 4 | /* | 4 | /* |
| 5 | * Type for a context number. We declare it volatile to ensure proper ordering when it's | 5 | * Type for a context number. We declare it volatile to ensure proper |
| 6 | * accessed outside of spinlock'd critical sections (e.g., as done in activate_mm() and | 6 | * ordering when it's accessed outside of spinlock'd critical sections |
| 7 | * init_new_context()). | 7 | * (e.g., as done in activate_mm() and init_new_context()). |
| 8 | */ | 8 | */ |
| 9 | typedef volatile unsigned long mm_context_t; | 9 | typedef volatile unsigned long mm_context_t; |
| 10 | 10 | ||
| 11 | typedef unsigned long nv_mm_context_t; | ||
| 12 | |||
| 11 | #endif | 13 | #endif |
diff --git a/include/asm-ia64/mmu_context.h b/include/asm-ia64/mmu_context.h index e3e5fededb04..0680d163be97 100644 --- a/include/asm-ia64/mmu_context.h +++ b/include/asm-ia64/mmu_context.h | |||
| @@ -55,34 +55,46 @@ static inline void | |||
| 55 | delayed_tlb_flush (void) | 55 | delayed_tlb_flush (void) |
| 56 | { | 56 | { |
| 57 | extern void local_flush_tlb_all (void); | 57 | extern void local_flush_tlb_all (void); |
| 58 | unsigned long flags; | ||
| 58 | 59 | ||
| 59 | if (unlikely(__ia64_per_cpu_var(ia64_need_tlb_flush))) { | 60 | if (unlikely(__ia64_per_cpu_var(ia64_need_tlb_flush))) { |
| 60 | local_flush_tlb_all(); | 61 | spin_lock_irqsave(&ia64_ctx.lock, flags); |
| 61 | __ia64_per_cpu_var(ia64_need_tlb_flush) = 0; | 62 | { |
| 63 | if (__ia64_per_cpu_var(ia64_need_tlb_flush)) { | ||
| 64 | local_flush_tlb_all(); | ||
| 65 | __ia64_per_cpu_var(ia64_need_tlb_flush) = 0; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | spin_unlock_irqrestore(&ia64_ctx.lock, flags); | ||
| 62 | } | 69 | } |
| 63 | } | 70 | } |
| 64 | 71 | ||
| 65 | static inline mm_context_t | 72 | static inline nv_mm_context_t |
| 66 | get_mmu_context (struct mm_struct *mm) | 73 | get_mmu_context (struct mm_struct *mm) |
| 67 | { | 74 | { |
| 68 | unsigned long flags; | 75 | unsigned long flags; |
| 69 | mm_context_t context = mm->context; | 76 | nv_mm_context_t context = mm->context; |
| 70 | 77 | ||
| 71 | if (context) | 78 | if (unlikely(!context)) { |
| 72 | return context; | 79 | spin_lock_irqsave(&ia64_ctx.lock, flags); |
| 73 | 80 | { | |
| 74 | spin_lock_irqsave(&ia64_ctx.lock, flags); | 81 | /* re-check, now that we've got the lock: */ |
| 75 | { | 82 | context = mm->context; |
| 76 | /* re-check, now that we've got the lock: */ | 83 | if (context == 0) { |
| 77 | context = mm->context; | 84 | cpus_clear(mm->cpu_vm_mask); |
| 78 | if (context == 0) { | 85 | if (ia64_ctx.next >= ia64_ctx.limit) |
| 79 | cpus_clear(mm->cpu_vm_mask); | 86 | wrap_mmu_context(mm); |
| 80 | if (ia64_ctx.next >= ia64_ctx.limit) | 87 | mm->context = context = ia64_ctx.next++; |
| 81 | wrap_mmu_context(mm); | 88 | } |
| 82 | mm->context = context = ia64_ctx.next++; | ||
| 83 | } | 89 | } |
| 90 | spin_unlock_irqrestore(&ia64_ctx.lock, flags); | ||
| 84 | } | 91 | } |
| 85 | spin_unlock_irqrestore(&ia64_ctx.lock, flags); | 92 | /* |
| 93 | * Ensure we're not starting to use "context" before any old | ||
| 94 | * uses of it are gone from our TLB. | ||
| 95 | */ | ||
| 96 | delayed_tlb_flush(); | ||
| 97 | |||
| 86 | return context; | 98 | return context; |
| 87 | } | 99 | } |
| 88 | 100 | ||
| @@ -104,7 +116,7 @@ destroy_context (struct mm_struct *mm) | |||
| 104 | } | 116 | } |
| 105 | 117 | ||
| 106 | static inline void | 118 | static inline void |
| 107 | reload_context (mm_context_t context) | 119 | reload_context (nv_mm_context_t context) |
| 108 | { | 120 | { |
| 109 | unsigned long rid; | 121 | unsigned long rid; |
| 110 | unsigned long rid_incr = 0; | 122 | unsigned long rid_incr = 0; |
| @@ -138,7 +150,7 @@ reload_context (mm_context_t context) | |||
| 138 | static inline void | 150 | static inline void |
| 139 | activate_context (struct mm_struct *mm) | 151 | activate_context (struct mm_struct *mm) |
| 140 | { | 152 | { |
| 141 | mm_context_t context; | 153 | nv_mm_context_t context; |
| 142 | 154 | ||
| 143 | do { | 155 | do { |
| 144 | context = get_mmu_context(mm); | 156 | context = get_mmu_context(mm); |
| @@ -157,8 +169,6 @@ activate_context (struct mm_struct *mm) | |||
| 157 | static inline void | 169 | static inline void |
| 158 | activate_mm (struct mm_struct *prev, struct mm_struct *next) | 170 | activate_mm (struct mm_struct *prev, struct mm_struct *next) |
| 159 | { | 171 | { |
| 160 | delayed_tlb_flush(); | ||
| 161 | |||
| 162 | /* | 172 | /* |
| 163 | * We may get interrupts here, but that's OK because interrupt handlers cannot | 173 | * We may get interrupts here, but that's OK because interrupt handlers cannot |
| 164 | * touch user-space. | 174 | * touch user-space. |
