Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] powerpc: Add missing icache flushes for hugepages

On most powerpc CPUs, the dcache and icache are not coherent so
between writing and executing a page, the caches must be flushed.
Userspace programs assume pages given to them by the kernel are icache
clean, so we must do this flush between the kernel clearing a page and
it being mapped into userspace for execute. We were not doing this
for hugepages, this patch corrects the situation.

We use the same lazy mechanism as we use for normal pages, delaying
the flush until userspace actually attempts to execute from the page
in question.

Tested on G5.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

David Gibson and committed by
Paul Mackerras
cbf52afd dabcafd3

+37 -3
+1 -1
arch/powerpc/mm/hash_utils_64.c
··· 601 601 /* Handle hugepage regions */ 602 602 if (unlikely(in_hugepage_area(mm->context, ea))) { 603 603 DBG_LOW(" -> huge page !\n"); 604 - return hash_huge_page(mm, access, ea, vsid, local); 604 + return hash_huge_page(mm, access, ea, vsid, local, trap); 605 605 } 606 606 607 607 /* Get PTE and page size from page tables */
+34 -1
arch/powerpc/mm/hugetlbpage.c
··· 639 639 return -ENOMEM; 640 640 } 641 641 642 + /* 643 + * Called by asm hashtable.S for doing lazy icache flush 644 + */ 645 + static unsigned int hash_huge_page_do_lazy_icache(unsigned long rflags, 646 + pte_t pte, int trap) 647 + { 648 + struct page *page; 649 + int i; 650 + 651 + if (!pfn_valid(pte_pfn(pte))) 652 + return rflags; 653 + 654 + page = pte_page(pte); 655 + 656 + /* page is dirty */ 657 + if (!test_bit(PG_arch_1, &page->flags) && !PageReserved(page)) { 658 + if (trap == 0x400) { 659 + for (i = 0; i < (HPAGE_SIZE / PAGE_SIZE); i++) 660 + __flush_dcache_icache(page_address(page+i)); 661 + set_bit(PG_arch_1, &page->flags); 662 + } else { 663 + rflags |= HPTE_R_N; 664 + } 665 + } 666 + return rflags; 667 + } 668 + 642 669 int hash_huge_page(struct mm_struct *mm, unsigned long access, 643 - unsigned long ea, unsigned long vsid, int local) 670 + unsigned long ea, unsigned long vsid, int local, 671 + unsigned long trap) 644 672 { 645 673 pte_t *ptep; 646 674 unsigned long old_pte, new_pte; ··· 719 691 rflags = 0x2 | (!(new_pte & _PAGE_RW)); 720 692 /* _PAGE_EXEC -> HW_NO_EXEC since it's inverted */ 721 693 rflags |= ((new_pte & _PAGE_EXEC) ? 0 : HPTE_R_N); 694 + if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) 695 + /* No CPU has hugepages but lacks no execute, so we 696 + * don't need to worry about that case */ 697 + rflags = hash_huge_page_do_lazy_icache(rflags, __pte(old_pte), 698 + trap); 722 699 723 700 /* Check if pte already has an hpte (case 2) */ 724 701 if (unlikely(old_pte & _PAGE_HASHPTE)) {
+2 -1
include/asm-powerpc/mmu.h
··· 220 220 unsigned int local); 221 221 struct mm_struct; 222 222 extern int hash_huge_page(struct mm_struct *mm, unsigned long access, 223 - unsigned long ea, unsigned long vsid, int local); 223 + unsigned long ea, unsigned long vsid, int local, 224 + unsigned long trap); 224 225 225 226 extern void htab_finish_init(void); 226 227 extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend,