diff options
| -rw-r--r-- | arch/arm/include/asm/tlb.h | 12 | ||||
| -rw-r--r-- | arch/ia64/include/asm/tlb.h | 42 | ||||
| -rw-r--r-- | arch/s390/include/asm/tlb.h | 13 | ||||
| -rw-r--r-- | arch/sh/include/asm/tlb.h | 8 | ||||
| -rw-r--r-- | arch/um/include/asm/tlb.h | 16 | ||||
| -rw-r--r-- | mm/memory.c | 53 |
6 files changed, 111 insertions, 33 deletions
diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h index 0baf7f0d9394..f1a0dace3efe 100644 --- a/arch/arm/include/asm/tlb.h +++ b/arch/arm/include/asm/tlb.h | |||
| @@ -98,15 +98,25 @@ static inline void __tlb_alloc_page(struct mmu_gather *tlb) | |||
| 98 | } | 98 | } |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | 101 | static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) |
| 102 | { | 102 | { |
| 103 | tlb_flush(tlb); | 103 | tlb_flush(tlb); |
| 104 | } | ||
| 105 | |||
| 106 | static inline void tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 107 | { | ||
| 104 | free_pages_and_swap_cache(tlb->pages, tlb->nr); | 108 | free_pages_and_swap_cache(tlb->pages, tlb->nr); |
| 105 | tlb->nr = 0; | 109 | tlb->nr = 0; |
| 106 | if (tlb->pages == tlb->local) | 110 | if (tlb->pages == tlb->local) |
| 107 | __tlb_alloc_page(tlb); | 111 | __tlb_alloc_page(tlb); |
| 108 | } | 112 | } |
| 109 | 113 | ||
| 114 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | ||
| 115 | { | ||
| 116 | tlb_flush_mmu_tlbonly(tlb); | ||
| 117 | tlb_flush_mmu_free(tlb); | ||
| 118 | } | ||
| 119 | |||
| 110 | static inline void | 120 | static inline void |
| 111 | tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) | 121 | tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) |
| 112 | { | 122 | { |
diff --git a/arch/ia64/include/asm/tlb.h b/arch/ia64/include/asm/tlb.h index bc5efc7c3f3f..39d64e0df1de 100644 --- a/arch/ia64/include/asm/tlb.h +++ b/arch/ia64/include/asm/tlb.h | |||
| @@ -91,18 +91,9 @@ extern struct ia64_tr_entry *ia64_idtrs[NR_CPUS]; | |||
| 91 | #define RR_RID_MASK 0x00000000ffffff00L | 91 | #define RR_RID_MASK 0x00000000ffffff00L |
| 92 | #define RR_TO_RID(val) ((val >> 8) & 0xffffff) | 92 | #define RR_TO_RID(val) ((val >> 8) & 0xffffff) |
| 93 | 93 | ||
| 94 | /* | ||
| 95 | * Flush the TLB for address range START to END and, if not in fast mode, release the | ||
| 96 | * freed pages that where gathered up to this point. | ||
| 97 | */ | ||
| 98 | static inline void | 94 | static inline void |
| 99 | ia64_tlb_flush_mmu (struct mmu_gather *tlb, unsigned long start, unsigned long end) | 95 | ia64_tlb_flush_mmu_tlbonly(struct mmu_gather *tlb, unsigned long start, unsigned long end) |
| 100 | { | 96 | { |
| 101 | unsigned long i; | ||
| 102 | unsigned int nr; | ||
| 103 | |||
| 104 | if (!tlb->need_flush) | ||
| 105 | return; | ||
| 106 | tlb->need_flush = 0; | 97 | tlb->need_flush = 0; |
| 107 | 98 | ||
| 108 | if (tlb->fullmm) { | 99 | if (tlb->fullmm) { |
| @@ -135,6 +126,14 @@ ia64_tlb_flush_mmu (struct mmu_gather *tlb, unsigned long start, unsigned long e | |||
| 135 | flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end)); | 126 | flush_tlb_range(&vma, ia64_thash(start), ia64_thash(end)); |
| 136 | } | 127 | } |
| 137 | 128 | ||
| 129 | } | ||
| 130 | |||
| 131 | static inline void | ||
| 132 | ia64_tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 133 | { | ||
| 134 | unsigned long i; | ||
| 135 | unsigned int nr; | ||
| 136 | |||
| 138 | /* lastly, release the freed pages */ | 137 | /* lastly, release the freed pages */ |
| 139 | nr = tlb->nr; | 138 | nr = tlb->nr; |
| 140 | 139 | ||
| @@ -144,6 +143,19 @@ ia64_tlb_flush_mmu (struct mmu_gather *tlb, unsigned long start, unsigned long e | |||
| 144 | free_page_and_swap_cache(tlb->pages[i]); | 143 | free_page_and_swap_cache(tlb->pages[i]); |
| 145 | } | 144 | } |
| 146 | 145 | ||
| 146 | /* | ||
| 147 | * Flush the TLB for address range START to END and, if not in fast mode, release the | ||
| 148 | * freed pages that where gathered up to this point. | ||
| 149 | */ | ||
| 150 | static inline void | ||
| 151 | ia64_tlb_flush_mmu (struct mmu_gather *tlb, unsigned long start, unsigned long end) | ||
| 152 | { | ||
| 153 | if (!tlb->need_flush) | ||
| 154 | return; | ||
| 155 | ia64_tlb_flush_mmu_tlbonly(tlb, start, end); | ||
| 156 | ia64_tlb_flush_mmu_free(tlb); | ||
| 157 | } | ||
| 158 | |||
| 147 | static inline void __tlb_alloc_page(struct mmu_gather *tlb) | 159 | static inline void __tlb_alloc_page(struct mmu_gather *tlb) |
| 148 | { | 160 | { |
| 149 | unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0); | 161 | unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0); |
| @@ -206,6 +218,16 @@ static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) | |||
| 206 | return tlb->max - tlb->nr; | 218 | return tlb->max - tlb->nr; |
| 207 | } | 219 | } |
| 208 | 220 | ||
| 221 | static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) | ||
| 222 | { | ||
| 223 | ia64_tlb_flush_mmu_tlbonly(tlb, tlb->start_addr, tlb->end_addr); | ||
| 224 | } | ||
| 225 | |||
| 226 | static inline void tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 227 | { | ||
| 228 | ia64_tlb_flush_mmu_free(tlb); | ||
| 229 | } | ||
| 230 | |||
| 209 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | 231 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) |
| 210 | { | 232 | { |
| 211 | ia64_tlb_flush_mmu(tlb, tlb->start_addr, tlb->end_addr); | 233 | ia64_tlb_flush_mmu(tlb, tlb->start_addr, tlb->end_addr); |
diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h index c544b6f05d95..a25f09fbaf36 100644 --- a/arch/s390/include/asm/tlb.h +++ b/arch/s390/include/asm/tlb.h | |||
| @@ -59,12 +59,23 @@ static inline void tlb_gather_mmu(struct mmu_gather *tlb, | |||
| 59 | tlb->batch = NULL; | 59 | tlb->batch = NULL; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | 62 | static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) |
| 63 | { | 63 | { |
| 64 | __tlb_flush_mm_lazy(tlb->mm); | 64 | __tlb_flush_mm_lazy(tlb->mm); |
| 65 | } | ||
| 66 | |||
| 67 | static inline void tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 68 | { | ||
| 65 | tlb_table_flush(tlb); | 69 | tlb_table_flush(tlb); |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 72 | |||
| 73 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | ||
| 74 | { | ||
| 75 | tlb_flush_mmu_tlbonly(tlb); | ||
| 76 | tlb_flush_mmu_free(tlb); | ||
| 77 | } | ||
| 78 | |||
| 68 | static inline void tlb_finish_mmu(struct mmu_gather *tlb, | 79 | static inline void tlb_finish_mmu(struct mmu_gather *tlb, |
| 69 | unsigned long start, unsigned long end) | 80 | unsigned long start, unsigned long end) |
| 70 | { | 81 | { |
diff --git a/arch/sh/include/asm/tlb.h b/arch/sh/include/asm/tlb.h index 362192ed12fe..62f80d2a9df9 100644 --- a/arch/sh/include/asm/tlb.h +++ b/arch/sh/include/asm/tlb.h | |||
| @@ -86,6 +86,14 @@ tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) | |||
| 86 | } | 86 | } |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | static inline void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) | ||
| 90 | { | ||
| 91 | } | ||
| 92 | |||
| 93 | static inline void tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 94 | { | ||
| 95 | } | ||
| 96 | |||
| 89 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) | 97 | static inline void tlb_flush_mmu(struct mmu_gather *tlb) |
| 90 | { | 98 | { |
| 91 | } | 99 | } |
diff --git a/arch/um/include/asm/tlb.h b/arch/um/include/asm/tlb.h index 29b0301c18aa..16eb63fac57d 100644 --- a/arch/um/include/asm/tlb.h +++ b/arch/um/include/asm/tlb.h | |||
| @@ -59,13 +59,25 @@ extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, | |||
| 59 | unsigned long end); | 59 | unsigned long end); |
| 60 | 60 | ||
| 61 | static inline void | 61 | static inline void |
| 62 | tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) | ||
| 63 | { | ||
| 64 | flush_tlb_mm_range(tlb->mm, tlb->start, tlb->end); | ||
| 65 | } | ||
| 66 | |||
| 67 | static inline void | ||
| 68 | tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 69 | { | ||
| 70 | init_tlb_gather(tlb); | ||
| 71 | } | ||
| 72 | |||
| 73 | static inline void | ||
| 62 | tlb_flush_mmu(struct mmu_gather *tlb) | 74 | tlb_flush_mmu(struct mmu_gather *tlb) |
| 63 | { | 75 | { |
| 64 | if (!tlb->need_flush) | 76 | if (!tlb->need_flush) |
| 65 | return; | 77 | return; |
| 66 | 78 | ||
| 67 | flush_tlb_mm_range(tlb->mm, tlb->start, tlb->end); | 79 | tlb_flush_mmu_tlbonly(tlb); |
| 68 | init_tlb_gather(tlb); | 80 | tlb_flush_mmu_free(tlb); |
| 69 | } | 81 | } |
| 70 | 82 | ||
| 71 | /* tlb_finish_mmu | 83 | /* tlb_finish_mmu |
diff --git a/mm/memory.c b/mm/memory.c index 93e332d5ed77..037b812a9531 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
| @@ -232,17 +232,18 @@ void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long | |||
| 232 | #endif | 232 | #endif |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | void tlb_flush_mmu(struct mmu_gather *tlb) | 235 | static void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb) |
| 236 | { | 236 | { |
| 237 | struct mmu_gather_batch *batch; | ||
| 238 | |||
| 239 | if (!tlb->need_flush) | ||
| 240 | return; | ||
| 241 | tlb->need_flush = 0; | 237 | tlb->need_flush = 0; |
| 242 | tlb_flush(tlb); | 238 | tlb_flush(tlb); |
| 243 | #ifdef CONFIG_HAVE_RCU_TABLE_FREE | 239 | #ifdef CONFIG_HAVE_RCU_TABLE_FREE |
| 244 | tlb_table_flush(tlb); | 240 | tlb_table_flush(tlb); |
| 245 | #endif | 241 | #endif |
| 242 | } | ||
| 243 | |||
| 244 | static void tlb_flush_mmu_free(struct mmu_gather *tlb) | ||
| 245 | { | ||
| 246 | struct mmu_gather_batch *batch; | ||
| 246 | 247 | ||
| 247 | for (batch = &tlb->local; batch; batch = batch->next) { | 248 | for (batch = &tlb->local; batch; batch = batch->next) { |
| 248 | free_pages_and_swap_cache(batch->pages, batch->nr); | 249 | free_pages_and_swap_cache(batch->pages, batch->nr); |
| @@ -251,6 +252,14 @@ void tlb_flush_mmu(struct mmu_gather *tlb) | |||
| 251 | tlb->active = &tlb->local; | 252 | tlb->active = &tlb->local; |
| 252 | } | 253 | } |
| 253 | 254 | ||
| 255 | void tlb_flush_mmu(struct mmu_gather *tlb) | ||
| 256 | { | ||
| 257 | if (!tlb->need_flush) | ||
| 258 | return; | ||
| 259 | tlb_flush_mmu_tlbonly(tlb); | ||
| 260 | tlb_flush_mmu_free(tlb); | ||
| 261 | } | ||
| 262 | |||
| 254 | /* tlb_finish_mmu | 263 | /* tlb_finish_mmu |
| 255 | * Called at the end of the shootdown operation to free up any resources | 264 | * Called at the end of the shootdown operation to free up any resources |
| 256 | * that were required. | 265 | * that were required. |
| @@ -1127,8 +1136,10 @@ again: | |||
| 1127 | if (PageAnon(page)) | 1136 | if (PageAnon(page)) |
| 1128 | rss[MM_ANONPAGES]--; | 1137 | rss[MM_ANONPAGES]--; |
| 1129 | else { | 1138 | else { |
| 1130 | if (pte_dirty(ptent)) | 1139 | if (pte_dirty(ptent)) { |
| 1140 | force_flush = 1; | ||
| 1131 | set_page_dirty(page); | 1141 | set_page_dirty(page); |
| 1142 | } | ||
| 1132 | if (pte_young(ptent) && | 1143 | if (pte_young(ptent) && |
| 1133 | likely(!(vma->vm_flags & VM_SEQ_READ))) | 1144 | likely(!(vma->vm_flags & VM_SEQ_READ))) |
| 1134 | mark_page_accessed(page); | 1145 | mark_page_accessed(page); |
| @@ -1137,9 +1148,10 @@ again: | |||
| 1137 | page_remove_rmap(page); | 1148 | page_remove_rmap(page); |
| 1138 | if (unlikely(page_mapcount(page) < 0)) | 1149 | if (unlikely(page_mapcount(page) < 0)) |
| 1139 | print_bad_pte(vma, addr, ptent, page); | 1150 | print_bad_pte(vma, addr, ptent, page); |
| 1140 | force_flush = !__tlb_remove_page(tlb, page); | 1151 | if (unlikely(!__tlb_remove_page(tlb, page))) { |
| 1141 | if (force_flush) | 1152 | force_flush = 1; |
| 1142 | break; | 1153 | break; |
| 1154 | } | ||
| 1143 | continue; | 1155 | continue; |
| 1144 | } | 1156 | } |
| 1145 | /* | 1157 | /* |
| @@ -1174,18 +1186,11 @@ again: | |||
| 1174 | 1186 | ||
| 1175 | add_mm_rss_vec(mm, rss); | 1187 | add_mm_rss_vec(mm, rss); |
| 1176 | arch_leave_lazy_mmu_mode(); | 1188 | arch_leave_lazy_mmu_mode(); |
| 1177 | pte_unmap_unlock(start_pte, ptl); | ||
| 1178 | 1189 | ||
| 1179 | /* | 1190 | /* Do the actual TLB flush before dropping ptl */ |
| 1180 | * mmu_gather ran out of room to batch pages, we break out of | ||
| 1181 | * the PTE lock to avoid doing the potential expensive TLB invalidate | ||
| 1182 | * and page-free while holding it. | ||
| 1183 | */ | ||
| 1184 | if (force_flush) { | 1191 | if (force_flush) { |
| 1185 | unsigned long old_end; | 1192 | unsigned long old_end; |
| 1186 | 1193 | ||
| 1187 | force_flush = 0; | ||
| 1188 | |||
| 1189 | /* | 1194 | /* |
| 1190 | * Flush the TLB just for the previous segment, | 1195 | * Flush the TLB just for the previous segment, |
| 1191 | * then update the range to be the remaining | 1196 | * then update the range to be the remaining |
| @@ -1193,11 +1198,21 @@ again: | |||
| 1193 | */ | 1198 | */ |
| 1194 | old_end = tlb->end; | 1199 | old_end = tlb->end; |
| 1195 | tlb->end = addr; | 1200 | tlb->end = addr; |
| 1196 | 1201 | tlb_flush_mmu_tlbonly(tlb); | |
| 1197 | tlb_flush_mmu(tlb); | ||
| 1198 | |||
| 1199 | tlb->start = addr; | 1202 | tlb->start = addr; |
| 1200 | tlb->end = old_end; | 1203 | tlb->end = old_end; |
| 1204 | } | ||
| 1205 | pte_unmap_unlock(start_pte, ptl); | ||
| 1206 | |||
| 1207 | /* | ||
| 1208 | * If we forced a TLB flush (either due to running out of | ||
| 1209 | * batch buffers or because we needed to flush dirty TLB | ||
| 1210 | * entries before releasing the ptl), free the batched | ||
| 1211 | * memory too. Restart if we didn't do everything. | ||
| 1212 | */ | ||
| 1213 | if (force_flush) { | ||
| 1214 | force_flush = 0; | ||
| 1215 | tlb_flush_mmu_free(tlb); | ||
| 1201 | 1216 | ||
| 1202 | if (addr != end) | 1217 | if (addr != end) |
| 1203 | goto again; | 1218 | goto again; |
