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 11e5098611SMartin Schwidefsky static inline pmd_t __pte_to_pmd(pte_t pte) 1253492b1dSGerald Schaefer { 130944fe3fSMartin Schwidefsky int none, young, prot; 14e5098611SMartin Schwidefsky pmd_t pmd; 1553492b1dSGerald Schaefer 16e5098611SMartin Schwidefsky /* 17e5098611SMartin Schwidefsky * Convert encoding pte bits pmd bits 180944fe3fSMartin Schwidefsky * .IR...wrdytp ..R...I...y. 190944fe3fSMartin Schwidefsky * empty .10...000000 -> ..0...1...0. 200944fe3fSMartin Schwidefsky * prot-none, clean, old .11...000001 -> ..0...1...1. 210944fe3fSMartin Schwidefsky * prot-none, clean, young .11...000101 -> ..1...1...1. 220944fe3fSMartin Schwidefsky * prot-none, dirty, old .10...001001 -> ..0...1...1. 230944fe3fSMartin Schwidefsky * prot-none, dirty, young .10...001101 -> ..1...1...1. 240944fe3fSMartin Schwidefsky * read-only, clean, old .11...010001 -> ..1...1...0. 250944fe3fSMartin Schwidefsky * read-only, clean, young .01...010101 -> ..1...0...1. 260944fe3fSMartin Schwidefsky * read-only, dirty, old .11...011001 -> ..1...1...0. 270944fe3fSMartin Schwidefsky * read-only, dirty, young .01...011101 -> ..1...0...1. 280944fe3fSMartin Schwidefsky * read-write, clean, old .11...110001 -> ..0...1...0. 290944fe3fSMartin Schwidefsky * read-write, clean, young .01...110101 -> ..0...0...1. 300944fe3fSMartin Schwidefsky * read-write, dirty, old .10...111001 -> ..0...1...0. 310944fe3fSMartin Schwidefsky * read-write, dirty, young .00...111101 -> ..0...0...1. 32e5098611SMartin Schwidefsky * Huge ptes are dirty by definition, a clean pte is made dirty 33e5098611SMartin Schwidefsky * by the conversion. 34e5098611SMartin Schwidefsky */ 35e5098611SMartin Schwidefsky if (pte_present(pte)) { 36e5098611SMartin Schwidefsky pmd_val(pmd) = pte_val(pte) & PAGE_MASK; 37e5098611SMartin Schwidefsky if (pte_val(pte) & _PAGE_INVALID) 38e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; 39e5098611SMartin Schwidefsky none = (pte_val(pte) & _PAGE_PRESENT) && 400944fe3fSMartin Schwidefsky !(pte_val(pte) & _PAGE_READ) && 410944fe3fSMartin Schwidefsky !(pte_val(pte) & _PAGE_WRITE); 420944fe3fSMartin Schwidefsky prot = (pte_val(pte) & _PAGE_PROTECT) && 430944fe3fSMartin Schwidefsky !(pte_val(pte) & _PAGE_WRITE); 440944fe3fSMartin Schwidefsky young = pte_val(pte) & _PAGE_YOUNG; 450944fe3fSMartin Schwidefsky if (none || young) 460944fe3fSMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG; 470944fe3fSMartin Schwidefsky if (prot || (none && young)) 48e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; 49e5098611SMartin Schwidefsky } else 50e5098611SMartin Schwidefsky pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; 51e5098611SMartin Schwidefsky return pmd; 5253492b1dSGerald Schaefer } 5353492b1dSGerald Schaefer 54e5098611SMartin Schwidefsky static inline pte_t __pmd_to_pte(pmd_t pmd) 55e5098611SMartin Schwidefsky { 56e5098611SMartin Schwidefsky pte_t pte; 57e5098611SMartin Schwidefsky 58e5098611SMartin Schwidefsky /* 59e5098611SMartin Schwidefsky * Convert encoding pmd bits pte bits 600944fe3fSMartin Schwidefsky * ..R...I...y. .IR...wrdytp 610944fe3fSMartin Schwidefsky * empty ..0...1...0. -> .10...000000 620944fe3fSMartin Schwidefsky * prot-none, old ..0...1...1. -> .10...001001 630944fe3fSMartin Schwidefsky * prot-none, young ..1...1...1. -> .10...001101 640944fe3fSMartin Schwidefsky * read-only, old ..1...1...0. -> .11...011001 650944fe3fSMartin Schwidefsky * read-only, young ..1...0...1. -> .01...011101 660944fe3fSMartin Schwidefsky * read-write, old ..0...1...0. -> .10...111001 670944fe3fSMartin Schwidefsky * read-write, young ..0...0...1. -> .00...111101 68e5098611SMartin Schwidefsky * Huge ptes are dirty by definition 69e5098611SMartin Schwidefsky */ 70e5098611SMartin Schwidefsky if (pmd_present(pmd)) { 71e5098611SMartin Schwidefsky pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY | 72e5098611SMartin Schwidefsky (pmd_val(pmd) & PAGE_MASK); 73e5098611SMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) 74e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_INVALID; 750944fe3fSMartin Schwidefsky if (pmd_prot_none(pmd)) { 760944fe3fSMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) 770944fe3fSMartin Schwidefsky pte_val(pte) |= _PAGE_YOUNG; 780944fe3fSMartin Schwidefsky } else { 790944fe3fSMartin Schwidefsky pte_val(pte) |= _PAGE_READ; 80e5098611SMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) 81e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_PROTECT; 82e5098611SMartin Schwidefsky else 83e5098611SMartin Schwidefsky pte_val(pte) |= _PAGE_WRITE; 840944fe3fSMartin Schwidefsky if (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) 850944fe3fSMartin Schwidefsky pte_val(pte) |= _PAGE_YOUNG; 86e5098611SMartin Schwidefsky } 87e5098611SMartin Schwidefsky } else 88e5098611SMartin Schwidefsky pte_val(pte) = _PAGE_INVALID; 89e5098611SMartin Schwidefsky return pte; 90e5098611SMartin Schwidefsky } 91e5098611SMartin Schwidefsky 92e5098611SMartin Schwidefsky void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, 93e5098611SMartin Schwidefsky pte_t *ptep, pte_t pte) 94e5098611SMartin Schwidefsky { 95e5098611SMartin Schwidefsky pmd_t pmd; 96e5098611SMartin Schwidefsky 97e5098611SMartin Schwidefsky pmd = __pte_to_pmd(pte); 98e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE) { 99e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 100e5098611SMartin Schwidefsky pmd_val(pmd) |= pte_page(pte)[1].index; 101e5098611SMartin Schwidefsky } else 102e5098611SMartin Schwidefsky pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO; 103e5098611SMartin Schwidefsky *(pmd_t *) ptep = pmd; 104e5098611SMartin Schwidefsky } 105e5098611SMartin Schwidefsky 106e5098611SMartin Schwidefsky pte_t huge_ptep_get(pte_t *ptep) 107e5098611SMartin Schwidefsky { 108e5098611SMartin Schwidefsky unsigned long origin; 109e5098611SMartin Schwidefsky pmd_t pmd; 110e5098611SMartin Schwidefsky 111e5098611SMartin Schwidefsky pmd = *(pmd_t *) ptep; 112e5098611SMartin Schwidefsky if (!MACHINE_HAS_HPAGE && pmd_present(pmd)) { 113e5098611SMartin Schwidefsky origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN; 114e5098611SMartin Schwidefsky pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; 115e5098611SMartin Schwidefsky pmd_val(pmd) |= *(unsigned long *) origin; 116e5098611SMartin Schwidefsky } 117e5098611SMartin Schwidefsky return __pmd_to_pte(pmd); 118e5098611SMartin Schwidefsky } 119e5098611SMartin Schwidefsky 120e5098611SMartin Schwidefsky pte_t huge_ptep_get_and_clear(struct mm_struct *mm, 121e5098611SMartin Schwidefsky unsigned long addr, pte_t *ptep) 122e5098611SMartin Schwidefsky { 123e5098611SMartin Schwidefsky pmd_t *pmdp = (pmd_t *) ptep; 124e5098611SMartin Schwidefsky pte_t pte = huge_ptep_get(ptep); 125e5098611SMartin Schwidefsky 126*1b948d6cSMartin Schwidefsky pmdp_flush_direct(mm, addr, pmdp); 127e5098611SMartin Schwidefsky pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY; 128e5098611SMartin Schwidefsky return pte; 12953492b1dSGerald Schaefer } 13053492b1dSGerald Schaefer 13153492b1dSGerald Schaefer int arch_prepare_hugepage(struct page *page) 13253492b1dSGerald Schaefer { 13353492b1dSGerald Schaefer unsigned long addr = page_to_phys(page); 13453492b1dSGerald Schaefer pte_t pte; 13553492b1dSGerald Schaefer pte_t *ptep; 13653492b1dSGerald Schaefer int i; 13753492b1dSGerald Schaefer 13853492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 13953492b1dSGerald Schaefer return 0; 14053492b1dSGerald Schaefer 141e5992f2eSMartin Schwidefsky ptep = (pte_t *) pte_alloc_one(&init_mm, addr); 14253492b1dSGerald Schaefer if (!ptep) 14353492b1dSGerald Schaefer return -ENOMEM; 14453492b1dSGerald Schaefer 145106c992aSGerald Schaefer pte_val(pte) = addr; 14653492b1dSGerald Schaefer for (i = 0; i < PTRS_PER_PTE; i++) { 14753492b1dSGerald Schaefer set_pte_at(&init_mm, addr + i * PAGE_SIZE, ptep + i, pte); 14853492b1dSGerald Schaefer pte_val(pte) += PAGE_SIZE; 14953492b1dSGerald Schaefer } 15053492b1dSGerald Schaefer page[1].index = (unsigned long) ptep; 15153492b1dSGerald Schaefer return 0; 15253492b1dSGerald Schaefer } 15353492b1dSGerald Schaefer 15453492b1dSGerald Schaefer void arch_release_hugepage(struct page *page) 15553492b1dSGerald Schaefer { 15653492b1dSGerald Schaefer pte_t *ptep; 15753492b1dSGerald Schaefer 15853492b1dSGerald Schaefer if (MACHINE_HAS_HPAGE) 15953492b1dSGerald Schaefer return; 16053492b1dSGerald Schaefer 16153492b1dSGerald Schaefer ptep = (pte_t *) page[1].index; 16253492b1dSGerald Schaefer if (!ptep) 16353492b1dSGerald Schaefer return; 164e5098611SMartin Schwidefsky clear_table((unsigned long *) ptep, _PAGE_INVALID, 165a686425bSGerald Schaefer PTRS_PER_PTE * sizeof(pte_t)); 16680217147SMartin Schwidefsky page_table_free(&init_mm, (unsigned long *) ptep); 16753492b1dSGerald Schaefer page[1].index = 0; 16853492b1dSGerald Schaefer } 16953492b1dSGerald Schaefer 170a5516438SAndi Kleen pte_t *huge_pte_alloc(struct mm_struct *mm, 171a5516438SAndi Kleen unsigned long addr, unsigned long sz) 17253492b1dSGerald Schaefer { 17353492b1dSGerald Schaefer pgd_t *pgdp; 17453492b1dSGerald Schaefer pud_t *pudp; 17553492b1dSGerald Schaefer pmd_t *pmdp = NULL; 17653492b1dSGerald Schaefer 17753492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 17853492b1dSGerald Schaefer pudp = pud_alloc(mm, pgdp, addr); 17953492b1dSGerald Schaefer if (pudp) 18053492b1dSGerald Schaefer pmdp = pmd_alloc(mm, pudp, addr); 18153492b1dSGerald Schaefer return (pte_t *) pmdp; 18253492b1dSGerald Schaefer } 18353492b1dSGerald Schaefer 18453492b1dSGerald Schaefer pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) 18553492b1dSGerald Schaefer { 18653492b1dSGerald Schaefer pgd_t *pgdp; 18753492b1dSGerald Schaefer pud_t *pudp; 18853492b1dSGerald Schaefer pmd_t *pmdp = NULL; 18953492b1dSGerald Schaefer 19053492b1dSGerald Schaefer pgdp = pgd_offset(mm, addr); 19153492b1dSGerald Schaefer if (pgd_present(*pgdp)) { 19253492b1dSGerald Schaefer pudp = pud_offset(pgdp, addr); 19353492b1dSGerald Schaefer if (pud_present(*pudp)) 19453492b1dSGerald Schaefer pmdp = pmd_offset(pudp, addr); 19553492b1dSGerald Schaefer } 19653492b1dSGerald Schaefer return (pte_t *) pmdp; 19753492b1dSGerald Schaefer } 19853492b1dSGerald Schaefer 19953492b1dSGerald Schaefer int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) 20053492b1dSGerald Schaefer { 20153492b1dSGerald Schaefer return 0; 20253492b1dSGerald Schaefer } 20353492b1dSGerald Schaefer 20453492b1dSGerald Schaefer struct page *follow_huge_addr(struct mm_struct *mm, unsigned long address, 20553492b1dSGerald Schaefer int write) 20653492b1dSGerald Schaefer { 20753492b1dSGerald Schaefer return ERR_PTR(-EINVAL); 20853492b1dSGerald Schaefer } 20953492b1dSGerald Schaefer 21053492b1dSGerald Schaefer int pmd_huge(pmd_t pmd) 21153492b1dSGerald Schaefer { 21253492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 21353492b1dSGerald Schaefer return 0; 21453492b1dSGerald Schaefer 21553492b1dSGerald Schaefer return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE); 21653492b1dSGerald Schaefer } 21753492b1dSGerald Schaefer 218ceb86879SAndi Kleen int pud_huge(pud_t pud) 219ceb86879SAndi Kleen { 220ceb86879SAndi Kleen return 0; 221ceb86879SAndi Kleen } 222ceb86879SAndi Kleen 22383467efbSNaoya Horiguchi int pmd_huge_support(void) 22483467efbSNaoya Horiguchi { 22583467efbSNaoya Horiguchi return 1; 22683467efbSNaoya Horiguchi } 22783467efbSNaoya Horiguchi 22853492b1dSGerald Schaefer struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address, 22953492b1dSGerald Schaefer pmd_t *pmdp, int write) 23053492b1dSGerald Schaefer { 23153492b1dSGerald Schaefer struct page *page; 23253492b1dSGerald Schaefer 23353492b1dSGerald Schaefer if (!MACHINE_HAS_HPAGE) 23453492b1dSGerald Schaefer return NULL; 23553492b1dSGerald Schaefer 23653492b1dSGerald Schaefer page = pmd_page(*pmdp); 23753492b1dSGerald Schaefer if (page) 23853492b1dSGerald Schaefer page += ((address & ~HPAGE_MASK) >> PAGE_SHIFT); 23953492b1dSGerald Schaefer return page; 24053492b1dSGerald Schaefer } 241