153492b1dSGerald Schaefer /* 253492b1dSGerald Schaefer * IBM System z Huge TLB Page Support for Kernel. 353492b1dSGerald Schaefer * 4a53c8fabSHeiko Carstens * Copyright IBM Corp. 2007 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 11*e5098611SMartin Schwidefsky static inline pmd_t __pte_to_pmd(pte_t pte) 1253492b1dSGerald Schaefer { 13*e5098611SMartin Schwidefsky int none, prot; 14*e5098611SMartin Schwidefsky pmd_t pmd; 1553492b1dSGerald Schaefer 16*e5098611SMartin Schwidefsky /* 17*e5098611SMartin Schwidefsky * Convert encoding pte bits pmd bits 18*e5098611SMartin Schwidefsky * .IR.....wdtp ..R...I..... 19*e5098611SMartin Schwidefsky * empty .10.....0000 -> ..0...1..... 20*e5098611SMartin Schwidefsky * prot-none, clean .11.....0001 -> ..1...1..... 21*e5098611SMartin Schwidefsky * prot-none, dirty .10.....0101 -> ..1...1..... 22*e5098611SMartin Schwidefsky * read-only, clean .01.....0001 -> ..1...0..... 23*e5098611SMartin Schwidefsky * read-only, dirty .01.....0101 -> ..1...0..... 24*e5098611SMartin Schwidefsky * read-write, clean .01.....1001 -> ..0...0..... 25*e5098611SMartin Schwidefsky * read-write, dirty .00.....1101 -> ..0...0..... 26*e5098611SMartin Schwidefsky * Huge ptes are dirty by definition, a clean pte is made dirty 27*e5098611SMartin Schwidefsky * by the conversion. 28*e5098611SMartin Schwidefsky */ 29*e5098611SMartin Schwidefsky if (pte_present(pte)) { 30*e5098611SMartin Schwidefsky pmd_val(pmd) = pte_val(pte) & PAGE_MASK; 31*e5098611SMartin Schwidefsky if (pte_val(pte) & _PAGE_INVALID) 32*e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; 33*e5098611SMartin Schwidefsky none = (pte_val(pte) & _PAGE_PRESENT) && 34*e5098611SMartin Schwidefsky (pte_val(pte) & _PAGE_INVALID); 35*e5098611SMartin Schwidefsky prot = (pte_val(pte) & _PAGE_PROTECT); 36*e5098611SMartin Schwidefsky if (prot || none) 37*e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; 38*e5098611SMartin Schwidefsky } else 39*e5098611SMartin Schwidefsky pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; 40*e5098611SMartin Schwidefsky return pmd; 4153492b1dSGerald Schaefer } 4253492b1dSGerald Schaefer 43*e5098611SMartin Schwidefsky static inline pte_t __pmd_to_pte(pmd_t pmd) 44*e5098611SMartin Schwidefsky { 45*e5098611SMartin Schwidefsky pte_t pte; 46*e5098611SMartin Schwidefsky 47*e5098611SMartin Schwidefsky /* 48*e5098611SMartin Schwidefsky * Convert encoding pmd bits pte bits 49*e5098611SMartin Schwidefsky * ..R...I..... .IR.....wdtp 50*e5098611SMartin Schwidefsky * empty ..0...1..... -> .10.....0000 51*e5098611SMartin Schwidefsky * prot-none, young ..1...1..... -> .10.....0101 52*e5098611SMartin Schwidefsky * read-only, young ..1...0..... -> .01.....0101 53*e5098611SMartin Schwidefsky * read-write, young ..0...0..... -> .00.....1101 54*e5098611SMartin Schwidefsky * Huge ptes are dirty by definition 55*e5098611SMartin Schwidefsky */ 56*e5098611SMartin Schwidefsky if (pmd_present(pmd)) { 57*e5098611SMartin Schwidefsky pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY | 58*e5098611SMartin Schwidefsky (pmd_val(pmd) & PAGE_MASK); 59*e5098611SMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) 60*e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_INVALID; 61*e5098611SMartin Schwidefsky else { 62*e5098611SMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) 63*e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_PROTECT; 64*e5098611SMartin Schwidefsky else 65*e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_WRITE; 66*e5098611SMartin Schwidefsky } 67*e5098611SMartin Schwidefsky } else 68*e5098611SMartin Schwidefsky pte_val(pte) = _PAGE_INVALID; 69*e5098611SMartin Schwidefsky return pte; 70*e5098611SMartin Schwidefsky } 71*e5098611SMartin Schwidefsky 72*e5098611SMartin Schwidefsky void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 73*e5098611SMartin Schwidefsky pte_t *ptep, pte_t pte) 74*e5098611SMartin Schwidefsky { 75*e5098611SMartin Schwidefsky pmd_t pmd; 76*e5098611SMartin Schwidefsky 77*e5098611SMartin Schwidefsky pmd = __pte_to_pmd(pte); 78*e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE) { 79*e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 80*e5098611SMartin Schwidefsky pmd_val(pmd) |= pte_page(pte)[1].index; 81*e5098611SMartin Schwidefsky } else 82*e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO; 83*e5098611SMartin Schwidefsky *(pmd_t *) ptep = pmd; 84*e5098611SMartin Schwidefsky } 85*e5098611SMartin Schwidefsky 86*e5098611SMartin Schwidefsky pte_t huge_ptep_get(pte_t *ptep) 87*e5098611SMartin Schwidefsky { 88*e5098611SMartin Schwidefsky unsigned long origin; 89*e5098611SMartin Schwidefsky pmd_t pmd; 90*e5098611SMartin Schwidefsky 91*e5098611SMartin Schwidefsky pmd = *(pmd_t *) ptep; 92*e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE && pmd_present(pmd)) { 93*e5098611SMartin Schwidefsky origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN; 94*e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 95*e5098611SMartin Schwidefsky pmd_val(pmd) |= *(unsigned long *) origin; 96*e5098611SMartin Schwidefsky } 97*e5098611SMartin Schwidefsky return __pmd_to_pte(pmd); 98*e5098611SMartin Schwidefsky } 99*e5098611SMartin Schwidefsky 100*e5098611SMartin Schwidefsky pte_t huge_ptep_get_and_clear(struct mm_struct *mm, 101*e5098611SMartin Schwidefsky unsigned long addr, pte_t *ptep) 102*e5098611SMartin Schwidefsky { 103*e5098611SMartin Schwidefsky pmd_t *pmdp = (pmd_t *) ptep; 104*e5098611SMartin Schwidefsky pte_t pte = huge_ptep_get(ptep); 105*e5098611SMartin Schwidefsky 106*e5098611SMartin Schwidefsky if (MACHINE_HAS_IDTE) 107*e5098611SMartin Schwidefsky __pmd_idte(addr, pmdp); 108*e5098611SMartin Schwidefsky else 109*e5098611SMartin Schwidefsky __pmd_csp(pmdp); 110*e5098611SMartin Schwidefsky pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY; 111*e5098611SMartin Schwidefsky return pte; 11253492b1dSGerald Schaefer } 11353492b1dSGerald Schaefer 11453492b1dSGerald Schaefer int arch_prepare_hugepage(struct page *page) 11553492b1dSGerald Schaefer { 11653492b1dSGerald Schaefer unsigned long addr = page_to_phys(page); 11753492b1dSGerald Schaefer pte_t pte; 11853492b1dSGerald Schaefer pte_t *ptep; 11953492b1dSGerald Schaefer int i; 12053492b1dSGerald Schaefer 12153492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 12253492b1dSGerald Schaefer return 0; 12353492b1dSGerald Schaefer 124e5992f2eSMartin Schwidefsky ptep = (pte_t *) pte_alloc_one(&init_mm, addr); 12553492b1dSGerald Schaefer if (!ptep) 12653492b1dSGerald Schaefer return -ENOMEM; 12753492b1dSGerald Schaefer 128106c992aSGerald Schaefer pte_val(pte) = addr; 12953492b1dSGerald Schaefer for (i = 0; i < PTRS_PER_PTE; i++) { 13053492b1dSGerald Schaefer set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte); 13153492b1dSGerald Schaefer pte_val(pte) += PAGE_SIZE; 13253492b1dSGerald Schaefer } 13353492b1dSGerald Schaefer page[1].index = (unsigned long) ptep; 13453492b1dSGerald Schaefer return 0; 13553492b1dSGerald Schaefer } 13653492b1dSGerald Schaefer 13753492b1dSGerald Schaefer void arch_release_hugepage(struct page *page) 13853492b1dSGerald Schaefer { 13953492b1dSGerald Schaefer pte_t *ptep; 14053492b1dSGerald Schaefer 14153492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 14253492b1dSGerald Schaefer return; 14353492b1dSGerald Schaefer 14453492b1dSGerald Schaefer ptep = (pte_t *) page[1].index; 14553492b1dSGerald Schaefer if (!ptep) 14653492b1dSGerald Schaefer return; 147*e5098611SMartin Schwidefsky clear_table((unsigned long *) ptep, _PAGE_INVALID, 148a686425bSGerald Schaefer PTRS_PER_PTE * sizeof(pte_t)); 14980217147SMartin Schwidefsky page_table_free(&init_mm, (unsigned long *) ptep); 15053492b1dSGerald Schaefer page[1].index = 0; 15153492b1dSGerald Schaefer } 15253492b1dSGerald Schaefer 153a5516438SAndi Kleen pte_t *huge_pte_alloc(struct mm_struct *mm, 154a5516438SAndi Kleen unsigned long addr, unsigned long sz) 15553492b1dSGerald Schaefer { 15653492b1dSGerald Schaefer pgd_t *pgdp; 15753492b1dSGerald Schaefer pud_t *pudp; 15853492b1dSGerald Schaefer pmd_t *pmdp = NULL; 15953492b1dSGerald Schaefer 16053492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 16153492b1dSGerald Schaefer pudp = pud_alloc(mm, pgdp, addr); 16253492b1dSGerald Schaefer if (pudp) 16353492b1dSGerald Schaefer pmdp = pmd_alloc(mm, pudp, addr); 16453492b1dSGerald Schaefer return (pte_t *) pmdp; 16553492b1dSGerald Schaefer } 16653492b1dSGerald Schaefer 16753492b1dSGerald Schaefer pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) 16853492b1dSGerald Schaefer { 16953492b1dSGerald Schaefer pgd_t *pgdp; 17053492b1dSGerald Schaefer pud_t *pudp; 17153492b1dSGerald Schaefer pmd_t *pmdp = NULL; 17253492b1dSGerald Schaefer 17353492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 17453492b1dSGerald Schaefer if (pgd_present(*pgdp)) { 17553492b1dSGerald Schaefer pudp = pud_offset(pgdp, addr); 17653492b1dSGerald Schaefer if (pud_present(*pudp)) 17753492b1dSGerald Schaefer pmdp = pmd_offset(pudp, addr); 17853492b1dSGerald Schaefer } 17953492b1dSGerald Schaefer return (pte_t *) pmdp; 18053492b1dSGerald Schaefer } 18153492b1dSGerald Schaefer 18253492b1dSGerald Schaefer int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) 18353492b1dSGerald Schaefer { 18453492b1dSGerald Schaefer return 0; 18553492b1dSGerald Schaefer } 18653492b1dSGerald Schaefer 18753492b1dSGerald Schaefer struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, 18853492b1dSGerald Schaefer int write) 18953492b1dSGerald Schaefer { 19053492b1dSGerald Schaefer return ERR_PTR(-EINVAL); 19153492b1dSGerald Schaefer } 19253492b1dSGerald Schaefer 19353492b1dSGerald Schaefer int pmd_huge(pmd_t pmd) 19453492b1dSGerald Schaefer { 19553492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 19653492b1dSGerald Schaefer return 0; 19753492b1dSGerald Schaefer 19853492b1dSGerald Schaefer return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); 19953492b1dSGerald Schaefer } 20053492b1dSGerald Schaefer 201ceb86879SAndi Kleen int pud_huge(pud_t pud) 202ceb86879SAndi Kleen { 203ceb86879SAndi Kleen return 0; 204ceb86879SAndi Kleen } 205ceb86879SAndi Kleen 20653492b1dSGerald Schaefer struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, 20753492b1dSGerald Schaefer pmd_t *pmdp, int write) 20853492b1dSGerald Schaefer { 20953492b1dSGerald Schaefer struct page *page; 21053492b1dSGerald Schaefer 21153492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 21253492b1dSGerald Schaefer return NULL; 21353492b1dSGerald Schaefer 21453492b1dSGerald Schaefer page = pmd_page(*pmdp); 21553492b1dSGerald Schaefer if (page) 21653492b1dSGerald Schaefer page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); 21753492b1dSGerald Schaefer return page; 21853492b1dSGerald Schaefer } 219