153492b1dSGerald Schaefer /* 253492b1dSGerald Schaefer * IBM System z Huge TLB Page Support for Kernel. 353492b1dSGerald Schaefer * 453492b1dSGerald Schaefer * Copyright 2007 IBM Corp. 553492b1dSGerald Schaefer * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com> 653492b1dSGerald Schaefer */ 753492b1dSGerald Schaefer 853492b1dSGerald Schaefer #include <linux/mm.h> 953492b1dSGerald Schaefer #include <linux/hugetlb.h> 1053492b1dSGerald Schaefer 1153492b1dSGerald Schaefer 1253492b1dSGerald Schaefer void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 1353492b1dSGerald Schaefer pte_t *pteptr, pte_t pteval) 1453492b1dSGerald Schaefer { 1553492b1dSGerald Schaefer pmd_t *pmdp = (pmd_t *) pteptr; 1653492b1dSGerald Schaefer unsigned long mask; 1753492b1dSGerald Schaefer 1853492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) { 1953492b1dSGerald Schaefer pteptr = (pte_t *) pte_page(pteval)[1].index; 2053492b1dSGerald Schaefer mask = pte_val(pteval) & 2153492b1dSGerald Schaefer (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO); 2253492b1dSGerald Schaefer pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask; 2353492b1dSGerald Schaefer } 2453492b1dSGerald Schaefer 2553492b1dSGerald Schaefer pmd_val(*pmdp) = pte_val(pteval); 2653492b1dSGerald Schaefer } 2753492b1dSGerald Schaefer 2853492b1dSGerald Schaefer int arch_prepare_hugepage(struct page *page) 2953492b1dSGerald Schaefer { 3053492b1dSGerald Schaefer unsigned long addr = page_to_phys(page); 3153492b1dSGerald Schaefer pte_t pte; 3253492b1dSGerald Schaefer pte_t *ptep; 3353492b1dSGerald Schaefer int i; 3453492b1dSGerald Schaefer 3553492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 3653492b1dSGerald Schaefer return 0; 3753492b1dSGerald Schaefer 38*e5992f2eSMartin Schwidefsky ptep = (pte_t *) pte_alloc_one(&init_mm, addr); 3953492b1dSGerald Schaefer if (!ptep) 4053492b1dSGerald Schaefer return -ENOMEM; 4153492b1dSGerald Schaefer 4253492b1dSGerald Schaefer pte = mk_pte(page, PAGE_RW); 4353492b1dSGerald Schaefer for (i = 0; i < PTRS_PER_PTE; i++) { 4453492b1dSGerald Schaefer set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte); 4553492b1dSGerald Schaefer pte_val(pte) += PAGE_SIZE; 4653492b1dSGerald Schaefer } 4753492b1dSGerald Schaefer page[1].index = (unsigned long) ptep; 4853492b1dSGerald Schaefer return 0; 4953492b1dSGerald Schaefer } 5053492b1dSGerald Schaefer 5153492b1dSGerald Schaefer void arch_release_hugepage(struct page *page) 5253492b1dSGerald Schaefer { 5353492b1dSGerald Schaefer pte_t *ptep; 5453492b1dSGerald Schaefer 5553492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 5653492b1dSGerald Schaefer return; 5753492b1dSGerald Schaefer 5853492b1dSGerald Schaefer ptep = (pte_t *) page[1].index; 5953492b1dSGerald Schaefer if (!ptep) 6053492b1dSGerald Schaefer return; 6180217147SMartin Schwidefsky page_table_free(&init_mm, (unsigned long *) ptep); 6253492b1dSGerald Schaefer page[1].index = 0; 6353492b1dSGerald Schaefer } 6453492b1dSGerald Schaefer 65a5516438SAndi Kleen pte_t *huge_pte_alloc(struct mm_struct *mm, 66a5516438SAndi Kleen unsigned long addr, unsigned long sz) 6753492b1dSGerald Schaefer { 6853492b1dSGerald Schaefer pgd_t *pgdp; 6953492b1dSGerald Schaefer pud_t *pudp; 7053492b1dSGerald Schaefer pmd_t *pmdp = NULL; 7153492b1dSGerald Schaefer 7253492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 7353492b1dSGerald Schaefer pudp = pud_alloc(mm, pgdp, addr); 7453492b1dSGerald Schaefer if (pudp) 7553492b1dSGerald Schaefer pmdp = pmd_alloc(mm, pudp, addr); 7653492b1dSGerald Schaefer return (pte_t *) pmdp; 7753492b1dSGerald Schaefer } 7853492b1dSGerald Schaefer 7953492b1dSGerald Schaefer pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) 8053492b1dSGerald Schaefer { 8153492b1dSGerald Schaefer pgd_t *pgdp; 8253492b1dSGerald Schaefer pud_t *pudp; 8353492b1dSGerald Schaefer pmd_t *pmdp = NULL; 8453492b1dSGerald Schaefer 8553492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 8653492b1dSGerald Schaefer if (pgd_present(*pgdp)) { 8753492b1dSGerald Schaefer pudp = pud_offset(pgdp, addr); 8853492b1dSGerald Schaefer if (pud_present(*pudp)) 8953492b1dSGerald Schaefer pmdp = pmd_offset(pudp, addr); 9053492b1dSGerald Schaefer } 9153492b1dSGerald Schaefer return (pte_t *) pmdp; 9253492b1dSGerald Schaefer } 9353492b1dSGerald Schaefer 9453492b1dSGerald Schaefer int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) 9553492b1dSGerald Schaefer { 9653492b1dSGerald Schaefer return 0; 9753492b1dSGerald Schaefer } 9853492b1dSGerald Schaefer 9953492b1dSGerald Schaefer struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, 10053492b1dSGerald Schaefer int write) 10153492b1dSGerald Schaefer { 10253492b1dSGerald Schaefer return ERR_PTR(-EINVAL); 10353492b1dSGerald Schaefer } 10453492b1dSGerald Schaefer 10553492b1dSGerald Schaefer int pmd_huge(pmd_t pmd) 10653492b1dSGerald Schaefer { 10753492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 10853492b1dSGerald Schaefer return 0; 10953492b1dSGerald Schaefer 11053492b1dSGerald Schaefer return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); 11153492b1dSGerald Schaefer } 11253492b1dSGerald Schaefer 113ceb86879SAndi Kleen int pud_huge(pud_t pud) 114ceb86879SAndi Kleen { 115ceb86879SAndi Kleen return 0; 116ceb86879SAndi Kleen } 117ceb86879SAndi Kleen 11853492b1dSGerald Schaefer struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, 11953492b1dSGerald Schaefer pmd_t *pmdp, int write) 12053492b1dSGerald Schaefer { 12153492b1dSGerald Schaefer struct page *page; 12253492b1dSGerald Schaefer 12353492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 12453492b1dSGerald Schaefer return NULL; 12553492b1dSGerald Schaefer 12653492b1dSGerald Schaefer page = pmd_page(*pmdp); 12753492b1dSGerald Schaefer if (page) 12853492b1dSGerald Schaefer page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); 12953492b1dSGerald Schaefer return page; 13053492b1dSGerald Schaefer } 131