diff options
Diffstat (limited to 'arch/powerpc/mm/hugetlbpage.c')
| -rw-r--r-- | arch/powerpc/mm/hugetlbpage.c | 95 |
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 | ||
| 151 | struct slb_flush_info { | ||
| 152 | struct mm_struct *mm; | ||
| 153 | u16 newareas; | ||
| 154 | }; | ||
| 155 | |||
| 151 | static void flush_low_segments(void *parm) | 156 | static 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 | ||
| 170 | static void flush_high_segments(void *parm) | 182 | static 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) | |||
| 229 | static int open_low_hpage_areas(struct mm_struct *mm, u16 newareas) | 249 | static 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 | ||
| 258 | static int open_high_hpage_areas(struct mm_struct *mm, u16 newareas) | 279 | static 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 | */ | ||
| 670 | static 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 | |||
| 642 | int hash_huge_page(struct mm_struct *mm, unsigned long access, | 694 | int 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 | ||
