diff options
| author | Will Deacon <will.deacon@arm.com> | 2014-10-29 06:03:09 -0400 |
|---|---|---|
| committer | Will Deacon <will.deacon@arm.com> | 2014-11-17 05:12:42 -0500 |
| commit | fb7332a9fedfd62b1ba6530c86f39f0fa38afd49 (patch) | |
| tree | 5e77bd4944da750634c4438df64257cdeaa58888 /include/asm-generic | |
| parent | 63648dd20fa0780ab6c1e923b5c276d257422cb3 (diff) | |
mmu_gather: move minimal range calculations into generic code
On architectures with hardware broadcasting of TLB invalidation messages
, it makes sense to reduce the range of the mmu_gather structure when
unmapping page ranges based on the dirty address information passed to
tlb_remove_tlb_entry.
arm64 already does this by directly manipulating the start/end fields
of the gather structure, but this confuses the generic code which
does not expect these fields to change and can end up calculating
invalid, negative ranges when forcing a flush in zap_pte_range.
This patch moves the minimal range calculation out of the arm64 code
and into the generic implementation, simplifying zap_pte_range in the
process (which no longer needs to care about start/end, since they will
point to the appropriate ranges already). With the range being tracked
by core code, the need_flush flag is dropped in favour of checking that
the end of the range has actually been set.
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: Michal Simek <monstr@monstr.eu>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'include/asm-generic')
| -rw-r--r-- | include/asm-generic/tlb.h | 57 |
1 files changed, 47 insertions, 10 deletions
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index 5672d7ea1fa0..08848050922e 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h | |||
| @@ -96,10 +96,9 @@ struct mmu_gather { | |||
| 96 | #endif | 96 | #endif |
| 97 | unsigned long start; | 97 | unsigned long start; |
| 98 | unsigned long end; | 98 | unsigned long end; |
| 99 | unsigned int need_flush : 1, /* Did free PTEs */ | ||
| 100 | /* we are in the middle of an operation to clear | 99 | /* we are in the middle of an operation to clear |
| 101 | * a full mm and can make some optimizations */ | 100 | * a full mm and can make some optimizations */ |
| 102 | fullmm : 1, | 101 | unsigned int fullmm : 1, |
| 103 | /* we have performed an operation which | 102 | /* we have performed an operation which |
| 104 | * requires a complete flush of the tlb */ | 103 | * requires a complete flush of the tlb */ |
| 105 | need_flush_all : 1; | 104 | need_flush_all : 1; |
| @@ -128,16 +127,54 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) | |||
| 128 | tlb_flush_mmu(tlb); | 127 | tlb_flush_mmu(tlb); |
| 129 | } | 128 | } |
| 130 | 129 | ||
| 130 | static inline void __tlb_adjust_range(struct mmu_gather *tlb, | ||
| 131 | unsigned long address) | ||
| 132 | { | ||
| 133 | tlb->start = min(tlb->start, address); | ||
| 134 | tlb->end = max(tlb->end, address + PAGE_SIZE); | ||
| 135 | } | ||
| 136 | |||
| 137 | static inline void __tlb_reset_range(struct mmu_gather *tlb) | ||
| 138 | { | ||
| 139 | tlb->start = TASK_SIZE; | ||
| 140 | tlb->end = 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * In the case of tlb vma handling, we can optimise these away in the | ||
| 145 | * case where we're doing a full MM flush. When we're doing a munmap, | ||
| 146 | * the vmas are adjusted to only cover the region to be torn down. | ||
| 147 | */ | ||
| 148 | #ifndef tlb_start_vma | ||
| 149 | #define tlb_start_vma(tlb, vma) do { } while (0) | ||
| 150 | #endif | ||
| 151 | |||
| 152 | #define __tlb_end_vma(tlb, vma) \ | ||
| 153 | do { \ | ||
| 154 | if (!tlb->fullmm && tlb->end) { \ | ||
| 155 | tlb_flush(tlb); \ | ||
| 156 | __tlb_reset_range(tlb); \ | ||
| 157 | } \ | ||
| 158 | } while (0) | ||
| 159 | |||
| 160 | #ifndef tlb_end_vma | ||
| 161 | #define tlb_end_vma __tlb_end_vma | ||
| 162 | #endif | ||
| 163 | |||
| 164 | #ifndef __tlb_remove_tlb_entry | ||
| 165 | #define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) | ||
| 166 | #endif | ||
| 167 | |||
| 131 | /** | 168 | /** |
| 132 | * tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation. | 169 | * tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation. |
| 133 | * | 170 | * |
| 134 | * Record the fact that pte's were really umapped in ->need_flush, so we can | 171 | * Record the fact that pte's were really unmapped by updating the range, |
| 135 | * later optimise away the tlb invalidate. This helps when userspace is | 172 | * so we can later optimise away the tlb invalidate. This helps when |
| 136 | * unmapping already-unmapped pages, which happens quite a lot. | 173 | * userspace is unmapping already-unmapped pages, which happens quite a lot. |
| 137 | */ | 174 | */ |
| 138 | #define tlb_remove_tlb_entry(tlb, ptep, address) \ | 175 | #define tlb_remove_tlb_entry(tlb, ptep, address) \ |
| 139 | do { \ | 176 | do { \ |
| 140 | tlb->need_flush = 1; \ | 177 | __tlb_adjust_range(tlb, address); \ |
| 141 | __tlb_remove_tlb_entry(tlb, ptep, address); \ | 178 | __tlb_remove_tlb_entry(tlb, ptep, address); \ |
| 142 | } while (0) | 179 | } while (0) |
| 143 | 180 | ||
| @@ -151,27 +188,27 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) | |||
| 151 | 188 | ||
| 152 | #define tlb_remove_pmd_tlb_entry(tlb, pmdp, address) \ | 189 | #define tlb_remove_pmd_tlb_entry(tlb, pmdp, address) \ |
| 153 | do { \ | 190 | do { \ |
| 154 | tlb->need_flush = 1; \ | 191 | __tlb_adjust_range(tlb, address); \ |
| 155 | __tlb_remove_pmd_tlb_entry(tlb, pmdp, address); \ | 192 | __tlb_remove_pmd_tlb_entry(tlb, pmdp, address); \ |
| 156 | } while (0) | 193 | } while (0) |
| 157 | 194 | ||
| 158 | #define pte_free_tlb(tlb, ptep, address) \ | 195 | #define pte_free_tlb(tlb, ptep, address) \ |
| 159 | do { \ | 196 | do { \ |
| 160 | tlb->need_flush = 1; \ | 197 | __tlb_adjust_range(tlb, address); \ |
| 161 | __pte_free_tlb(tlb, ptep, address); \ | 198 | __pte_free_tlb(tlb, ptep, address); \ |
| 162 | } while (0) | 199 | } while (0) |
| 163 | 200 | ||
| 164 | #ifndef __ARCH_HAS_4LEVEL_HACK | 201 | #ifndef __ARCH_HAS_4LEVEL_HACK |
| 165 | #define pud_free_tlb(tlb, pudp, address) \ | 202 | #define pud_free_tlb(tlb, pudp, address) \ |
| 166 | do { \ | 203 | do { \ |
| 167 | tlb->need_flush = 1; \ | 204 | __tlb_adjust_range(tlb, address); \ |
| 168 | __pud_free_tlb(tlb, pudp, address); \ | 205 | __pud_free_tlb(tlb, pudp, address); \ |
| 169 | } while (0) | 206 | } while (0) |
| 170 | #endif | 207 | #endif |
| 171 | 208 | ||
| 172 | #define pmd_free_tlb(tlb, pmdp, address) \ | 209 | #define pmd_free_tlb(tlb, pmdp, address) \ |
| 173 | do { \ | 210 | do { \ |
| 174 | tlb->need_flush = 1; \ | 211 | __tlb_adjust_range(tlb, address); \ |
| 175 | __pmd_free_tlb(tlb, pmdp, address); \ | 212 | __pmd_free_tlb(tlb, pmdp, address); \ |
| 176 | } while (0) | 213 | } while (0) |
| 177 | 214 | ||
