xref: /openbmc/linux/arch/riscv/mm/hugetlbpage.c (revision e0316069)
19e953cdaSAlexandre Ghiti // SPDX-License-Identifier: GPL-2.0
29e953cdaSAlexandre Ghiti #include <linux/hugetlb.h>
39e953cdaSAlexandre Ghiti #include <linux/err.h>
49e953cdaSAlexandre Ghiti 
582a1a1f3SQinglin Pan #ifdef CONFIG_RISCV_ISA_SVNAPOT
huge_ptep_get(pte_t * ptep)66966d798SAlexandre Ghiti pte_t huge_ptep_get(pte_t *ptep)
76966d798SAlexandre Ghiti {
86966d798SAlexandre Ghiti 	unsigned long pte_num;
96966d798SAlexandre Ghiti 	int i;
106966d798SAlexandre Ghiti 	pte_t orig_pte = ptep_get(ptep);
116966d798SAlexandre Ghiti 
126966d798SAlexandre Ghiti 	if (!pte_present(orig_pte) || !pte_napot(orig_pte))
136966d798SAlexandre Ghiti 		return orig_pte;
146966d798SAlexandre Ghiti 
156966d798SAlexandre Ghiti 	pte_num = napot_pte_num(napot_cont_order(orig_pte));
166966d798SAlexandre Ghiti 
176966d798SAlexandre Ghiti 	for (i = 0; i < pte_num; i++, ptep++) {
186966d798SAlexandre Ghiti 		pte_t pte = ptep_get(ptep);
196966d798SAlexandre Ghiti 
206966d798SAlexandre Ghiti 		if (pte_dirty(pte))
216966d798SAlexandre Ghiti 			orig_pte = pte_mkdirty(orig_pte);
226966d798SAlexandre Ghiti 
236966d798SAlexandre Ghiti 		if (pte_young(pte))
246966d798SAlexandre Ghiti 			orig_pte = pte_mkyoung(orig_pte);
256966d798SAlexandre Ghiti 	}
266966d798SAlexandre Ghiti 
276966d798SAlexandre Ghiti 	return orig_pte;
286966d798SAlexandre Ghiti }
296966d798SAlexandre Ghiti 
huge_pte_alloc(struct mm_struct * mm,struct vm_area_struct * vma,unsigned long addr,unsigned long sz)3082a1a1f3SQinglin Pan pte_t *huge_pte_alloc(struct mm_struct *mm,
3182a1a1f3SQinglin Pan 		      struct vm_area_struct *vma,
3282a1a1f3SQinglin Pan 		      unsigned long addr,
3382a1a1f3SQinglin Pan 		      unsigned long sz)
3482a1a1f3SQinglin Pan {
3582a1a1f3SQinglin Pan 	unsigned long order;
3682a1a1f3SQinglin Pan 	pte_t *pte = NULL;
3782a1a1f3SQinglin Pan 	pgd_t *pgd;
3882a1a1f3SQinglin Pan 	p4d_t *p4d;
3982a1a1f3SQinglin Pan 	pud_t *pud;
4082a1a1f3SQinglin Pan 	pmd_t *pmd;
4182a1a1f3SQinglin Pan 
4282a1a1f3SQinglin Pan 	pgd = pgd_offset(mm, addr);
4382a1a1f3SQinglin Pan 	p4d = p4d_alloc(mm, pgd, addr);
4482a1a1f3SQinglin Pan 	if (!p4d)
4582a1a1f3SQinglin Pan 		return NULL;
4682a1a1f3SQinglin Pan 
4782a1a1f3SQinglin Pan 	pud = pud_alloc(mm, p4d, addr);
4882a1a1f3SQinglin Pan 	if (!pud)
4982a1a1f3SQinglin Pan 		return NULL;
5082a1a1f3SQinglin Pan 
5182a1a1f3SQinglin Pan 	if (sz == PUD_SIZE) {
5282a1a1f3SQinglin Pan 		pte = (pte_t *)pud;
5382a1a1f3SQinglin Pan 		goto out;
5482a1a1f3SQinglin Pan 	}
5582a1a1f3SQinglin Pan 
5682a1a1f3SQinglin Pan 	if (sz == PMD_SIZE) {
57*e0316069SAlexandre Ghiti 		if (want_pmd_share(vma, addr) && pud_none(pudp_get(pud)))
5882a1a1f3SQinglin Pan 			pte = huge_pmd_share(mm, vma, addr, pud);
5982a1a1f3SQinglin Pan 		else
6082a1a1f3SQinglin Pan 			pte = (pte_t *)pmd_alloc(mm, pud, addr);
6182a1a1f3SQinglin Pan 		goto out;
6282a1a1f3SQinglin Pan 	}
6382a1a1f3SQinglin Pan 
6482a1a1f3SQinglin Pan 	pmd = pmd_alloc(mm, pud, addr);
6582a1a1f3SQinglin Pan 	if (!pmd)
6682a1a1f3SQinglin Pan 		return NULL;
6782a1a1f3SQinglin Pan 
6882a1a1f3SQinglin Pan 	for_each_napot_order(order) {
6982a1a1f3SQinglin Pan 		if (napot_cont_size(order) == sz) {
70893f667fSHugh Dickins 			pte = pte_alloc_huge(mm, pmd, addr & napot_cont_mask(order));
7182a1a1f3SQinglin Pan 			break;
7282a1a1f3SQinglin Pan 		}
7382a1a1f3SQinglin Pan 	}
7482a1a1f3SQinglin Pan 
7582a1a1f3SQinglin Pan out:
7662ba41d2SJohn Hubbard 	if (pte) {
7762ba41d2SJohn Hubbard 		pte_t pteval = ptep_get_lockless(pte);
7862ba41d2SJohn Hubbard 
7962ba41d2SJohn Hubbard 		WARN_ON_ONCE(pte_present(pteval) && !pte_huge(pteval));
8062ba41d2SJohn Hubbard 	}
8182a1a1f3SQinglin Pan 	return pte;
8282a1a1f3SQinglin Pan }
8382a1a1f3SQinglin Pan 
huge_pte_offset(struct mm_struct * mm,unsigned long addr,unsigned long sz)8482a1a1f3SQinglin Pan pte_t *huge_pte_offset(struct mm_struct *mm,
8582a1a1f3SQinglin Pan 		       unsigned long addr,
8682a1a1f3SQinglin Pan 		       unsigned long sz)
8782a1a1f3SQinglin Pan {
8882a1a1f3SQinglin Pan 	unsigned long order;
8982a1a1f3SQinglin Pan 	pte_t *pte = NULL;
9082a1a1f3SQinglin Pan 	pgd_t *pgd;
9182a1a1f3SQinglin Pan 	p4d_t *p4d;
9282a1a1f3SQinglin Pan 	pud_t *pud;
9382a1a1f3SQinglin Pan 	pmd_t *pmd;
9482a1a1f3SQinglin Pan 
9582a1a1f3SQinglin Pan 	pgd = pgd_offset(mm, addr);
96*e0316069SAlexandre Ghiti 	if (!pgd_present(pgdp_get(pgd)))
9782a1a1f3SQinglin Pan 		return NULL;
9882a1a1f3SQinglin Pan 
9982a1a1f3SQinglin Pan 	p4d = p4d_offset(pgd, addr);
100*e0316069SAlexandre Ghiti 	if (!p4d_present(p4dp_get(p4d)))
10182a1a1f3SQinglin Pan 		return NULL;
10282a1a1f3SQinglin Pan 
10382a1a1f3SQinglin Pan 	pud = pud_offset(p4d, addr);
10482a1a1f3SQinglin Pan 	if (sz == PUD_SIZE)
10582a1a1f3SQinglin Pan 		/* must be pud huge, non-present or none */
10682a1a1f3SQinglin Pan 		return (pte_t *)pud;
10782a1a1f3SQinglin Pan 
108*e0316069SAlexandre Ghiti 	if (!pud_present(pudp_get(pud)))
10982a1a1f3SQinglin Pan 		return NULL;
11082a1a1f3SQinglin Pan 
11182a1a1f3SQinglin Pan 	pmd = pmd_offset(pud, addr);
11282a1a1f3SQinglin Pan 	if (sz == PMD_SIZE)
11382a1a1f3SQinglin Pan 		/* must be pmd huge, non-present or none */
11482a1a1f3SQinglin Pan 		return (pte_t *)pmd;
11582a1a1f3SQinglin Pan 
116*e0316069SAlexandre Ghiti 	if (!pmd_present(pmdp_get(pmd)))
11782a1a1f3SQinglin Pan 		return NULL;
11882a1a1f3SQinglin Pan 
11982a1a1f3SQinglin Pan 	for_each_napot_order(order) {
12082a1a1f3SQinglin Pan 		if (napot_cont_size(order) == sz) {
121893f667fSHugh Dickins 			pte = pte_offset_huge(pmd, addr & napot_cont_mask(order));
12282a1a1f3SQinglin Pan 			break;
12382a1a1f3SQinglin Pan 		}
12482a1a1f3SQinglin Pan 	}
12582a1a1f3SQinglin Pan 	return pte;
12682a1a1f3SQinglin Pan }
12782a1a1f3SQinglin Pan 
hugetlb_mask_last_page(struct hstate * h)128d0d1f9a9SAlexandre Ghiti unsigned long hugetlb_mask_last_page(struct hstate *h)
129d0d1f9a9SAlexandre Ghiti {
130d0d1f9a9SAlexandre Ghiti 	unsigned long hp_size = huge_page_size(h);
131d0d1f9a9SAlexandre Ghiti 
132d0d1f9a9SAlexandre Ghiti 	switch (hp_size) {
133d0d1f9a9SAlexandre Ghiti #ifndef __PAGETABLE_PMD_FOLDED
134d0d1f9a9SAlexandre Ghiti 	case PUD_SIZE:
135d0d1f9a9SAlexandre Ghiti 		return P4D_SIZE - PUD_SIZE;
136d0d1f9a9SAlexandre Ghiti #endif
137d0d1f9a9SAlexandre Ghiti 	case PMD_SIZE:
138d0d1f9a9SAlexandre Ghiti 		return PUD_SIZE - PMD_SIZE;
139d0d1f9a9SAlexandre Ghiti 	case napot_cont_size(NAPOT_CONT64KB_ORDER):
140d0d1f9a9SAlexandre Ghiti 		return PMD_SIZE - napot_cont_size(NAPOT_CONT64KB_ORDER);
141d0d1f9a9SAlexandre Ghiti 	default:
142d0d1f9a9SAlexandre Ghiti 		break;
143d0d1f9a9SAlexandre Ghiti 	}
144d0d1f9a9SAlexandre Ghiti 
145d0d1f9a9SAlexandre Ghiti 	return 0UL;
146d0d1f9a9SAlexandre Ghiti }
147d0d1f9a9SAlexandre Ghiti 
get_clear_contig(struct mm_struct * mm,unsigned long addr,pte_t * ptep,unsigned long pte_num)14882a1a1f3SQinglin Pan static pte_t get_clear_contig(struct mm_struct *mm,
14982a1a1f3SQinglin Pan 			      unsigned long addr,
15082a1a1f3SQinglin Pan 			      pte_t *ptep,
15182a1a1f3SQinglin Pan 			      unsigned long pte_num)
15282a1a1f3SQinglin Pan {
15382a1a1f3SQinglin Pan 	pte_t orig_pte = ptep_get(ptep);
15482a1a1f3SQinglin Pan 	unsigned long i;
15582a1a1f3SQinglin Pan 
15682a1a1f3SQinglin Pan 	for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) {
15782a1a1f3SQinglin Pan 		pte_t pte = ptep_get_and_clear(mm, addr, ptep);
15882a1a1f3SQinglin Pan 
15982a1a1f3SQinglin Pan 		if (pte_dirty(pte))
16082a1a1f3SQinglin Pan 			orig_pte = pte_mkdirty(orig_pte);
16182a1a1f3SQinglin Pan 
16282a1a1f3SQinglin Pan 		if (pte_young(pte))
16382a1a1f3SQinglin Pan 			orig_pte = pte_mkyoung(orig_pte);
16482a1a1f3SQinglin Pan 	}
16582a1a1f3SQinglin Pan 
16682a1a1f3SQinglin Pan 	return orig_pte;
16782a1a1f3SQinglin Pan }
16882a1a1f3SQinglin Pan 
get_clear_contig_flush(struct mm_struct * mm,unsigned long addr,pte_t * ptep,unsigned long pte_num)16982a1a1f3SQinglin Pan static pte_t get_clear_contig_flush(struct mm_struct *mm,
17082a1a1f3SQinglin Pan 				    unsigned long addr,
17182a1a1f3SQinglin Pan 				    pte_t *ptep,
17282a1a1f3SQinglin Pan 				    unsigned long pte_num)
17382a1a1f3SQinglin Pan {
17482a1a1f3SQinglin Pan 	pte_t orig_pte = get_clear_contig(mm, addr, ptep, pte_num);
17582a1a1f3SQinglin Pan 	struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
17682a1a1f3SQinglin Pan 	bool valid = !pte_none(orig_pte);
17782a1a1f3SQinglin Pan 
17882a1a1f3SQinglin Pan 	if (valid)
17982a1a1f3SQinglin Pan 		flush_tlb_range(&vma, addr, addr + (PAGE_SIZE * pte_num));
18082a1a1f3SQinglin Pan 
18182a1a1f3SQinglin Pan 	return orig_pte;
18282a1a1f3SQinglin Pan }
18382a1a1f3SQinglin Pan 
arch_make_huge_pte(pte_t entry,unsigned int shift,vm_flags_t flags)18482a1a1f3SQinglin Pan pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
18582a1a1f3SQinglin Pan {
18682a1a1f3SQinglin Pan 	unsigned long order;
18782a1a1f3SQinglin Pan 
18882a1a1f3SQinglin Pan 	for_each_napot_order(order) {
18982a1a1f3SQinglin Pan 		if (shift == napot_cont_shift(order)) {
19082a1a1f3SQinglin Pan 			entry = pte_mknapot(entry, order);
19182a1a1f3SQinglin Pan 			break;
19282a1a1f3SQinglin Pan 		}
19382a1a1f3SQinglin Pan 	}
19482a1a1f3SQinglin Pan 	if (order == NAPOT_ORDER_MAX)
19582a1a1f3SQinglin Pan 		entry = pte_mkhuge(entry);
19682a1a1f3SQinglin Pan 
19782a1a1f3SQinglin Pan 	return entry;
19882a1a1f3SQinglin Pan }
19982a1a1f3SQinglin Pan 
clear_flush(struct mm_struct * mm,unsigned long addr,pte_t * ptep,unsigned long pgsize,unsigned long ncontig)20021dba4e4SAlexandre Ghiti static void clear_flush(struct mm_struct *mm,
20121dba4e4SAlexandre Ghiti 			unsigned long addr,
20221dba4e4SAlexandre Ghiti 			pte_t *ptep,
20321dba4e4SAlexandre Ghiti 			unsigned long pgsize,
20421dba4e4SAlexandre Ghiti 			unsigned long ncontig)
20521dba4e4SAlexandre Ghiti {
20621dba4e4SAlexandre Ghiti 	struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
20721dba4e4SAlexandre Ghiti 	unsigned long i, saddr = addr;
20821dba4e4SAlexandre Ghiti 
20921dba4e4SAlexandre Ghiti 	for (i = 0; i < ncontig; i++, addr += pgsize, ptep++)
21021dba4e4SAlexandre Ghiti 		ptep_get_and_clear(mm, addr, ptep);
21121dba4e4SAlexandre Ghiti 
21221dba4e4SAlexandre Ghiti 	flush_tlb_range(&vma, saddr, addr);
21321dba4e4SAlexandre Ghiti }
21421dba4e4SAlexandre Ghiti 
21521dba4e4SAlexandre Ghiti /*
21621dba4e4SAlexandre Ghiti  * When dealing with NAPOT mappings, the privileged specification indicates that
21721dba4e4SAlexandre Ghiti  * "if an update needs to be made, the OS generally should first mark all of the
21821dba4e4SAlexandre Ghiti  * PTEs invalid, then issue SFENCE.VMA instruction(s) covering all 4 KiB regions
21921dba4e4SAlexandre Ghiti  * within the range, [...] then update the PTE(s), as described in Section
22021dba4e4SAlexandre Ghiti  * 4.2.1.". That's the equivalent of the Break-Before-Make approach used by
22121dba4e4SAlexandre Ghiti  * arm64.
22221dba4e4SAlexandre Ghiti  */
set_huge_pte_at(struct mm_struct * mm,unsigned long addr,pte_t * ptep,pte_t pte,unsigned long sz)22382a1a1f3SQinglin Pan void set_huge_pte_at(struct mm_struct *mm,
22482a1a1f3SQinglin Pan 		     unsigned long addr,
22582a1a1f3SQinglin Pan 		     pte_t *ptep,
226935d4f0cSRyan Roberts 		     pte_t pte,
227935d4f0cSRyan Roberts 		     unsigned long sz)
22882a1a1f3SQinglin Pan {
22921dba4e4SAlexandre Ghiti 	unsigned long hugepage_shift, pgsize;
23082a1a1f3SQinglin Pan 	int i, pte_num;
23182a1a1f3SQinglin Pan 
2321de195ddSAlexandre Ghiti 	if (sz >= PGDIR_SIZE)
2331de195ddSAlexandre Ghiti 		hugepage_shift = PGDIR_SHIFT;
2341de195ddSAlexandre Ghiti 	else if (sz >= P4D_SIZE)
2351de195ddSAlexandre Ghiti 		hugepage_shift = P4D_SHIFT;
2361de195ddSAlexandre Ghiti 	else if (sz >= PUD_SIZE)
2371de195ddSAlexandre Ghiti 		hugepage_shift = PUD_SHIFT;
2381de195ddSAlexandre Ghiti 	else if (sz >= PMD_SIZE)
2391de195ddSAlexandre Ghiti 		hugepage_shift = PMD_SHIFT;
2401de195ddSAlexandre Ghiti 	else
2411de195ddSAlexandre Ghiti 		hugepage_shift = PAGE_SHIFT;
24282a1a1f3SQinglin Pan 
2431de195ddSAlexandre Ghiti 	pte_num = sz >> hugepage_shift;
24421dba4e4SAlexandre Ghiti 	pgsize = 1 << hugepage_shift;
24521dba4e4SAlexandre Ghiti 
24621dba4e4SAlexandre Ghiti 	if (!pte_present(pte)) {
24721dba4e4SAlexandre Ghiti 		for (i = 0; i < pte_num; i++, ptep++, addr += pgsize)
24821dba4e4SAlexandre Ghiti 			set_ptes(mm, addr, ptep, pte, 1);
24921dba4e4SAlexandre Ghiti 		return;
25021dba4e4SAlexandre Ghiti 	}
25121dba4e4SAlexandre Ghiti 
25221dba4e4SAlexandre Ghiti 	if (!pte_napot(pte)) {
25321dba4e4SAlexandre Ghiti 		set_ptes(mm, addr, ptep, pte, 1);
25421dba4e4SAlexandre Ghiti 		return;
25521dba4e4SAlexandre Ghiti 	}
25621dba4e4SAlexandre Ghiti 
25721dba4e4SAlexandre Ghiti 	clear_flush(mm, addr, ptep, pgsize, pte_num);
25821dba4e4SAlexandre Ghiti 
25921dba4e4SAlexandre Ghiti 	for (i = 0; i < pte_num; i++, ptep++, addr += pgsize)
26082a1a1f3SQinglin Pan 		set_pte_at(mm, addr, ptep, pte);
26182a1a1f3SQinglin Pan }
26282a1a1f3SQinglin Pan 
huge_ptep_set_access_flags(struct vm_area_struct * vma,unsigned long addr,pte_t * ptep,pte_t pte,int dirty)26382a1a1f3SQinglin Pan int huge_ptep_set_access_flags(struct vm_area_struct *vma,
26482a1a1f3SQinglin Pan 			       unsigned long addr,
26582a1a1f3SQinglin Pan 			       pte_t *ptep,
26682a1a1f3SQinglin Pan 			       pte_t pte,
26782a1a1f3SQinglin Pan 			       int dirty)
26882a1a1f3SQinglin Pan {
26982a1a1f3SQinglin Pan 	struct mm_struct *mm = vma->vm_mm;
27082a1a1f3SQinglin Pan 	unsigned long order;
27182a1a1f3SQinglin Pan 	pte_t orig_pte;
27282a1a1f3SQinglin Pan 	int i, pte_num;
27382a1a1f3SQinglin Pan 
27482a1a1f3SQinglin Pan 	if (!pte_napot(pte))
27582a1a1f3SQinglin Pan 		return ptep_set_access_flags(vma, addr, ptep, pte, dirty);
27682a1a1f3SQinglin Pan 
27782a1a1f3SQinglin Pan 	order = napot_cont_order(pte);
27882a1a1f3SQinglin Pan 	pte_num = napot_pte_num(order);
27982a1a1f3SQinglin Pan 	ptep = huge_pte_offset(mm, addr, napot_cont_size(order));
28082a1a1f3SQinglin Pan 	orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num);
28182a1a1f3SQinglin Pan 
28282a1a1f3SQinglin Pan 	if (pte_dirty(orig_pte))
28382a1a1f3SQinglin Pan 		pte = pte_mkdirty(pte);
28482a1a1f3SQinglin Pan 
28582a1a1f3SQinglin Pan 	if (pte_young(orig_pte))
28682a1a1f3SQinglin Pan 		pte = pte_mkyoung(pte);
28782a1a1f3SQinglin Pan 
28882a1a1f3SQinglin Pan 	for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
28982a1a1f3SQinglin Pan 		set_pte_at(mm, addr, ptep, pte);
29082a1a1f3SQinglin Pan 
29182a1a1f3SQinglin Pan 	return true;
29282a1a1f3SQinglin Pan }
29382a1a1f3SQinglin Pan 
huge_ptep_get_and_clear(struct mm_struct * mm,unsigned long addr,pte_t * ptep)29482a1a1f3SQinglin Pan pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
29582a1a1f3SQinglin Pan 			      unsigned long addr,
29682a1a1f3SQinglin Pan 			      pte_t *ptep)
29782a1a1f3SQinglin Pan {
29882a1a1f3SQinglin Pan 	pte_t orig_pte = ptep_get(ptep);
29982a1a1f3SQinglin Pan 	int pte_num;
30082a1a1f3SQinglin Pan 
30182a1a1f3SQinglin Pan 	if (!pte_napot(orig_pte))
30282a1a1f3SQinglin Pan 		return ptep_get_and_clear(mm, addr, ptep);
30382a1a1f3SQinglin Pan 
30482a1a1f3SQinglin Pan 	pte_num = napot_pte_num(napot_cont_order(orig_pte));
30582a1a1f3SQinglin Pan 
30682a1a1f3SQinglin Pan 	return get_clear_contig(mm, addr, ptep, pte_num);
30782a1a1f3SQinglin Pan }
30882a1a1f3SQinglin Pan 
huge_ptep_set_wrprotect(struct mm_struct * mm,unsigned long addr,pte_t * ptep)30982a1a1f3SQinglin Pan void huge_ptep_set_wrprotect(struct mm_struct *mm,
31082a1a1f3SQinglin Pan 			     unsigned long addr,
31182a1a1f3SQinglin Pan 			     pte_t *ptep)
31282a1a1f3SQinglin Pan {
31382a1a1f3SQinglin Pan 	pte_t pte = ptep_get(ptep);
31482a1a1f3SQinglin Pan 	unsigned long order;
315835e5ac3SAlexandre Ghiti 	pte_t orig_pte;
31682a1a1f3SQinglin Pan 	int i, pte_num;
31782a1a1f3SQinglin Pan 
31882a1a1f3SQinglin Pan 	if (!pte_napot(pte)) {
31982a1a1f3SQinglin Pan 		ptep_set_wrprotect(mm, addr, ptep);
32082a1a1f3SQinglin Pan 		return;
32182a1a1f3SQinglin Pan 	}
32282a1a1f3SQinglin Pan 
32382a1a1f3SQinglin Pan 	order = napot_cont_order(pte);
32482a1a1f3SQinglin Pan 	pte_num = napot_pte_num(order);
32582a1a1f3SQinglin Pan 	ptep = huge_pte_offset(mm, addr, napot_cont_size(order));
326835e5ac3SAlexandre Ghiti 	orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num);
327835e5ac3SAlexandre Ghiti 
328835e5ac3SAlexandre Ghiti 	orig_pte = pte_wrprotect(orig_pte);
32982a1a1f3SQinglin Pan 
33082a1a1f3SQinglin Pan 	for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
331835e5ac3SAlexandre Ghiti 		set_pte_at(mm, addr, ptep, orig_pte);
33282a1a1f3SQinglin Pan }
33382a1a1f3SQinglin Pan 
huge_ptep_clear_flush(struct vm_area_struct * vma,unsigned long addr,pte_t * ptep)33482a1a1f3SQinglin Pan pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
33582a1a1f3SQinglin Pan 			    unsigned long addr,
33682a1a1f3SQinglin Pan 			    pte_t *ptep)
33782a1a1f3SQinglin Pan {
33882a1a1f3SQinglin Pan 	pte_t pte = ptep_get(ptep);
33982a1a1f3SQinglin Pan 	int pte_num;
34082a1a1f3SQinglin Pan 
34182a1a1f3SQinglin Pan 	if (!pte_napot(pte))
34282a1a1f3SQinglin Pan 		return ptep_clear_flush(vma, addr, ptep);
34382a1a1f3SQinglin Pan 
34482a1a1f3SQinglin Pan 	pte_num = napot_pte_num(napot_cont_order(pte));
34582a1a1f3SQinglin Pan 
34682a1a1f3SQinglin Pan 	return get_clear_contig_flush(vma->vm_mm, addr, ptep, pte_num);
34782a1a1f3SQinglin Pan }
34882a1a1f3SQinglin Pan 
huge_pte_clear(struct mm_struct * mm,unsigned long addr,pte_t * ptep,unsigned long sz)34982a1a1f3SQinglin Pan void huge_pte_clear(struct mm_struct *mm,
35082a1a1f3SQinglin Pan 		    unsigned long addr,
35182a1a1f3SQinglin Pan 		    pte_t *ptep,
35282a1a1f3SQinglin Pan 		    unsigned long sz)
35382a1a1f3SQinglin Pan {
354*e0316069SAlexandre Ghiti 	pte_t pte = ptep_get(ptep);
35582a1a1f3SQinglin Pan 	int i, pte_num;
35682a1a1f3SQinglin Pan 
35782a1a1f3SQinglin Pan 	if (!pte_napot(pte)) {
35882a1a1f3SQinglin Pan 		pte_clear(mm, addr, ptep);
35982a1a1f3SQinglin Pan 		return;
36082a1a1f3SQinglin Pan 	}
36182a1a1f3SQinglin Pan 
36282a1a1f3SQinglin Pan 	pte_num = napot_pte_num(napot_cont_order(pte));
36382a1a1f3SQinglin Pan 	for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
36482a1a1f3SQinglin Pan 		pte_clear(mm, addr, ptep);
36582a1a1f3SQinglin Pan }
36682a1a1f3SQinglin Pan 
is_napot_size(unsigned long size)367bc401f79SAlexandre Ghiti static bool is_napot_size(unsigned long size)
36882a1a1f3SQinglin Pan {
36982a1a1f3SQinglin Pan 	unsigned long order;
37082a1a1f3SQinglin Pan 
37182a1a1f3SQinglin Pan 	if (!has_svnapot())
37282a1a1f3SQinglin Pan 		return false;
37382a1a1f3SQinglin Pan 
37482a1a1f3SQinglin Pan 	for_each_napot_order(order) {
37582a1a1f3SQinglin Pan 		if (size == napot_cont_size(order))
37682a1a1f3SQinglin Pan 			return true;
37782a1a1f3SQinglin Pan 	}
37882a1a1f3SQinglin Pan 	return false;
37982a1a1f3SQinglin Pan }
38082a1a1f3SQinglin Pan 
napot_hugetlbpages_init(void)38182a1a1f3SQinglin Pan static __init int napot_hugetlbpages_init(void)
38282a1a1f3SQinglin Pan {
38382a1a1f3SQinglin Pan 	if (has_svnapot()) {
38482a1a1f3SQinglin Pan 		unsigned long order;
38582a1a1f3SQinglin Pan 
38682a1a1f3SQinglin Pan 		for_each_napot_order(order)
38782a1a1f3SQinglin Pan 			hugetlb_add_hstate(order);
38882a1a1f3SQinglin Pan 	}
38982a1a1f3SQinglin Pan 	return 0;
39082a1a1f3SQinglin Pan }
39182a1a1f3SQinglin Pan arch_initcall(napot_hugetlbpages_init);
39282a1a1f3SQinglin Pan 
39382a1a1f3SQinglin Pan #else
39482a1a1f3SQinglin Pan 
is_napot_size(unsigned long size)395bc401f79SAlexandre Ghiti static bool is_napot_size(unsigned long size)
39682a1a1f3SQinglin Pan {
39782a1a1f3SQinglin Pan 	return false;
39882a1a1f3SQinglin Pan }
39982a1a1f3SQinglin Pan 
40082a1a1f3SQinglin Pan #endif /*CONFIG_RISCV_ISA_SVNAPOT*/
40182a1a1f3SQinglin Pan 
pud_huge(pud_t pud)4029e953cdaSAlexandre Ghiti int pud_huge(pud_t pud)
4039e953cdaSAlexandre Ghiti {
4043133287bSAlexandre Ghiti 	return pud_leaf(pud);
4059e953cdaSAlexandre Ghiti }
4069e953cdaSAlexandre Ghiti 
pmd_huge(pmd_t pmd)4079e953cdaSAlexandre Ghiti int pmd_huge(pmd_t pmd)
4089e953cdaSAlexandre Ghiti {
4093133287bSAlexandre Ghiti 	return pmd_leaf(pmd);
4109e953cdaSAlexandre Ghiti }
4119e953cdaSAlexandre Ghiti 
__hugetlb_valid_size(unsigned long size)412bc401f79SAlexandre Ghiti static bool __hugetlb_valid_size(unsigned long size)
413ae94da89SMike Kravetz {
414ae94da89SMike Kravetz 	if (size == HPAGE_SIZE)
415ae94da89SMike Kravetz 		return true;
416ae94da89SMike Kravetz 	else if (IS_ENABLED(CONFIG_64BIT) && size == PUD_SIZE)
417ae94da89SMike Kravetz 		return true;
41882a1a1f3SQinglin Pan 	else if (is_napot_size(size))
41982a1a1f3SQinglin Pan 		return true;
420ae94da89SMike Kravetz 	else
421ae94da89SMike Kravetz 		return false;
422ae94da89SMike Kravetz }
423ae94da89SMike Kravetz 
arch_hugetlb_valid_size(unsigned long size)424bc401f79SAlexandre Ghiti bool __init arch_hugetlb_valid_size(unsigned long size)
425bc401f79SAlexandre Ghiti {
426bc401f79SAlexandre Ghiti 	return __hugetlb_valid_size(size);
427bc401f79SAlexandre Ghiti }
428bc401f79SAlexandre Ghiti 
429cc698db4SAlexandre Ghiti #ifdef CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION
arch_hugetlb_migration_supported(struct hstate * h)430bc401f79SAlexandre Ghiti bool arch_hugetlb_migration_supported(struct hstate *h)
431bc401f79SAlexandre Ghiti {
432bc401f79SAlexandre Ghiti 	return __hugetlb_valid_size(huge_page_size(h));
433bc401f79SAlexandre Ghiti }
434cc698db4SAlexandre Ghiti #endif
435bc401f79SAlexandre Ghiti 
4369e953cdaSAlexandre Ghiti #ifdef CONFIG_CONTIG_ALLOC
gigantic_pages_init(void)4379e953cdaSAlexandre Ghiti static __init int gigantic_pages_init(void)
4389e953cdaSAlexandre Ghiti {
4399e953cdaSAlexandre Ghiti 	/* With CONTIG_ALLOC, we can allocate gigantic pages at runtime */
44038237830SMike Kravetz 	if (IS_ENABLED(CONFIG_64BIT))
4419e953cdaSAlexandre Ghiti 		hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
4429e953cdaSAlexandre Ghiti 	return 0;
4439e953cdaSAlexandre Ghiti }
4449e953cdaSAlexandre Ghiti arch_initcall(gigantic_pages_init);
4459e953cdaSAlexandre Ghiti #endif
446