aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm/hugetlbpage.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/mm/hugetlbpage.c')
-rw-r--r--arch/powerpc/mm/hugetlbpage.c95
1 files changed, 77 insertions, 18 deletions
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index 6bc9dbad7dea..54131b877da3 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -148,43 +148,63 @@ int is_aligned_hugepage_range(unsigned long addr, unsigned long len)
148 return 0; 148 return 0;
149} 149}
150 150
151struct slb_flush_info {
152 struct mm_struct *mm;
153 u16 newareas;
154};
155
151static void flush_low_segments(void *parm) 156static void flush_low_segments(void *parm)
152{ 157{
153 u16 areas = (unsigned long) parm; 158 struct slb_flush_info *fi = parm;
154 unsigned long i; 159 unsigned long i;
155 160
156 asm volatile("isync" : : : "memory"); 161 BUILD_BUG_ON((sizeof(fi->newareas)*8) != NUM_LOW_AREAS);
162
163 if (current->active_mm != fi->mm)
164 return;
165
166 /* Only need to do anything if this CPU is working in the same
167 * mm as the one which has changed */
157 168
158 BUILD_BUG_ON((sizeof(areas)*8) != NUM_LOW_AREAS); 169 /* update the paca copy of the context struct */
170 get_paca()->context = current->active_mm->context;
159 171
172 asm volatile("isync" : : : "memory");
160 for (i = 0; i < NUM_LOW_AREAS; i++) { 173 for (i = 0; i < NUM_LOW_AREAS; i++) {
161 if (! (areas & (1U << i))) 174 if (! (fi->newareas & (1U << i)))
162 continue; 175 continue;
163 asm volatile("slbie %0" 176 asm volatile("slbie %0"
164 : : "r" ((i << SID_SHIFT) | SLBIE_C)); 177 : : "r" ((i << SID_SHIFT) | SLBIE_C));
165 } 178 }
166
167 asm volatile("isync" : : : "memory"); 179 asm volatile("isync" : : : "memory");
168} 180}
169 181
170static void flush_high_segments(void *parm) 182static void flush_high_segments(void *parm)
171{ 183{
172 u16 areas = (unsigned long) parm; 184 struct slb_flush_info *fi = parm;
173 unsigned long i, j; 185 unsigned long i, j;
174 186
175 asm volatile("isync" : : : "memory");
176 187
177 BUILD_BUG_ON((sizeof(areas)*8) != NUM_HIGH_AREAS); 188 BUILD_BUG_ON((sizeof(fi->newareas)*8) != NUM_HIGH_AREAS);
178 189
190 if (current->active_mm != fi->mm)
191 return;
192
193 /* Only need to do anything if this CPU is working in the same
194 * mm as the one which has changed */
195
196 /* update the paca copy of the context struct */
197 get_paca()->context = current->active_mm->context;
198
199 asm volatile("isync" : : : "memory");
179 for (i = 0; i < NUM_HIGH_AREAS; i++) { 200 for (i = 0; i < NUM_HIGH_AREAS; i++) {
180 if (! (areas & (1U << i))) 201 if (! (fi->newareas & (1U << i)))
181 continue; 202 continue;
182 for (j = 0; j < (1UL << (HTLB_AREA_SHIFT-SID_SHIFT)); j++) 203 for (j = 0; j < (1UL << (HTLB_AREA_SHIFT-SID_SHIFT)); j++)
183 asm volatile("slbie %0" 204 asm volatile("slbie %0"
184 :: "r" (((i << HTLB_AREA_SHIFT) 205 :: "r" (((i << HTLB_AREA_SHIFT)
185 + (j << SID_SHIFT)) | SLBIE_C)); 206 + (j << SID_SHIFT)) | SLBIE_C));
186 } 207 }
187
188 asm volatile("isync" : : : "memory"); 208 asm volatile("isync" : : : "memory");
189} 209}
190 210
@@ -229,6 +249,7 @@ static int prepare_high_area_for_htlb(struct mm_struct *mm, unsigned long area)
229static int open_low_hpage_areas(struct mm_struct *mm, u16 newareas) 249static int open_low_hpage_areas(struct mm_struct *mm, u16 newareas)
230{ 250{
231 unsigned long i; 251 unsigned long i;
252 struct slb_flush_info fi;
232 253
233 BUILD_BUG_ON((sizeof(newareas)*8) != NUM_LOW_AREAS); 254 BUILD_BUG_ON((sizeof(newareas)*8) != NUM_LOW_AREAS);
234 BUILD_BUG_ON((sizeof(mm->context.low_htlb_areas)*8) != NUM_LOW_AREAS); 255 BUILD_BUG_ON((sizeof(mm->context.low_htlb_areas)*8) != NUM_LOW_AREAS);
@@ -244,19 +265,20 @@ static int open_low_hpage_areas(struct mm_struct *mm, u16 newareas)
244 265
245 mm->context.low_htlb_areas |= newareas; 266 mm->context.low_htlb_areas |= newareas;
246 267
247 /* update the paca copy of the context struct */
248 get_paca()->context = mm->context;
249
250 /* the context change must make it to memory before the flush, 268 /* the context change must make it to memory before the flush,
251 * so that further SLB misses do the right thing. */ 269 * so that further SLB misses do the right thing. */
252 mb(); 270 mb();
253 on_each_cpu(flush_low_segments, (void *)(unsigned long)newareas, 0, 1); 271
272 fi.mm = mm;
273 fi.newareas = newareas;
274 on_each_cpu(flush_low_segments, &fi, 0, 1);
254 275
255 return 0; 276 return 0;
256} 277}
257 278
258static int open_high_hpage_areas(struct mm_struct *mm, u16 newareas) 279static int open_high_hpage_areas(struct mm_struct *mm, u16 newareas)
259{ 280{
281 struct slb_flush_info fi;
260 unsigned long i; 282 unsigned long i;
261 283
262 BUILD_BUG_ON((sizeof(newareas)*8) != NUM_HIGH_AREAS); 284 BUILD_BUG_ON((sizeof(newareas)*8) != NUM_HIGH_AREAS);
@@ -280,7 +302,10 @@ static int open_high_hpage_areas(struct mm_struct *mm, u16 newareas)
280 /* the context change must make it to memory before the flush, 302 /* the context change must make it to memory before the flush,
281 * so that further SLB misses do the right thing. */ 303 * so that further SLB misses do the right thing. */
282 mb(); 304 mb();
283 on_each_cpu(flush_high_segments, (void *)(unsigned long)newareas, 0, 1); 305
306 fi.mm = mm;
307 fi.newareas = newareas;
308 on_each_cpu(flush_high_segments, &fi, 0, 1);
284 309
285 return 0; 310 return 0;
286} 311}
@@ -639,8 +664,36 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr,
639 return -ENOMEM; 664 return -ENOMEM;
640} 665}
641 666
667/*
668 * Called by asm hashtable.S for doing lazy icache flush
669 */
670static unsigned int hash_huge_page_do_lazy_icache(unsigned long rflags,
671 pte_t pte, int trap)
672{
673 struct page *page;
674 int i;
675
676 if (!pfn_valid(pte_pfn(pte)))
677 return rflags;
678
679 page = pte_page(pte);
680
681 /* page is dirty */
682 if (!test_bit(PG_arch_1, &page->flags) && !PageReserved(page)) {
683 if (trap == 0x400) {
684 for (i = 0; i < (HPAGE_SIZE / PAGE_SIZE); i++)
685 __flush_dcache_icache(page_address(page+i));
686 set_bit(PG_arch_1, &page->flags);
687 } else {
688 rflags |= HPTE_R_N;
689 }
690 }
691 return rflags;
692}
693
642int hash_huge_page(struct mm_struct *mm, unsigned long access, 694int hash_huge_page(struct mm_struct *mm, unsigned long access,
643 unsigned long ea, unsigned long vsid, int local) 695 unsigned long ea, unsigned long vsid, int local,
696 unsigned long trap)
644{ 697{
645 pte_t *ptep; 698 pte_t *ptep;
646 unsigned long old_pte, new_pte; 699 unsigned long old_pte, new_pte;
@@ -691,6 +744,11 @@ int hash_huge_page(struct mm_struct *mm, unsigned long access,
691 rflags = 0x2 | (!(new_pte & _PAGE_RW)); 744 rflags = 0x2 | (!(new_pte & _PAGE_RW));
692 /* _PAGE_EXEC -> HW_NO_EXEC since it's inverted */ 745 /* _PAGE_EXEC -> HW_NO_EXEC since it's inverted */
693 rflags |= ((new_pte & _PAGE_EXEC) ? 0 : HPTE_R_N); 746 rflags |= ((new_pte & _PAGE_EXEC) ? 0 : HPTE_R_N);
747 if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE))
748 /* No CPU has hugepages but lacks no execute, so we
749 * don't need to worry about that case */
750 rflags = hash_huge_page_do_lazy_icache(rflags, __pte(old_pte),
751 trap);
694 752
695 /* Check if pte already has an hpte (case 2) */ 753 /* Check if pte already has an hpte (case 2) */
696 if (unlikely(old_pte & _PAGE_HASHPTE)) { 754 if (unlikely(old_pte & _PAGE_HASHPTE)) {
@@ -703,7 +761,8 @@ int hash_huge_page(struct mm_struct *mm, unsigned long access,
703 slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; 761 slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
704 slot += (old_pte & _PAGE_F_GIX) >> 12; 762 slot += (old_pte & _PAGE_F_GIX) >> 12;
705 763
706 if (ppc_md.hpte_updatepp(slot, rflags, va, 1, local) == -1) 764 if (ppc_md.hpte_updatepp(slot, rflags, va, mmu_huge_psize,
765 local) == -1)
707 old_pte &= ~_PAGE_HPTEFLAGS; 766 old_pte &= ~_PAGE_HPTEFLAGS;
708 } 767 }
709 768