xref: /openbmc/linux/arch/s390/mm/hugetlbpage.c (revision 1b948d6caec4f28e3524244ca0f77c6ae8ddceef)
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