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