Merge 6.4-rc5 into usb-next
[platform/kernel/linux-starfive.git] / arch / riscv / mm / hugetlbpage.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/hugetlb.h>
3 #include <linux/err.h>
4
5 #ifdef CONFIG_RISCV_ISA_SVNAPOT
6 pte_t huge_ptep_get(pte_t *ptep)
7 {
8         unsigned long pte_num;
9         int i;
10         pte_t orig_pte = ptep_get(ptep);
11
12         if (!pte_present(orig_pte) || !pte_napot(orig_pte))
13                 return orig_pte;
14
15         pte_num = napot_pte_num(napot_cont_order(orig_pte));
16
17         for (i = 0; i < pte_num; i++, ptep++) {
18                 pte_t pte = ptep_get(ptep);
19
20                 if (pte_dirty(pte))
21                         orig_pte = pte_mkdirty(orig_pte);
22
23                 if (pte_young(pte))
24                         orig_pte = pte_mkyoung(orig_pte);
25         }
26
27         return orig_pte;
28 }
29
30 pte_t *huge_pte_alloc(struct mm_struct *mm,
31                       struct vm_area_struct *vma,
32                       unsigned long addr,
33                       unsigned long sz)
34 {
35         unsigned long order;
36         pte_t *pte = NULL;
37         pgd_t *pgd;
38         p4d_t *p4d;
39         pud_t *pud;
40         pmd_t *pmd;
41
42         pgd = pgd_offset(mm, addr);
43         p4d = p4d_alloc(mm, pgd, addr);
44         if (!p4d)
45                 return NULL;
46
47         pud = pud_alloc(mm, p4d, addr);
48         if (!pud)
49                 return NULL;
50
51         if (sz == PUD_SIZE) {
52                 pte = (pte_t *)pud;
53                 goto out;
54         }
55
56         if (sz == PMD_SIZE) {
57                 if (want_pmd_share(vma, addr) && pud_none(*pud))
58                         pte = huge_pmd_share(mm, vma, addr, pud);
59                 else
60                         pte = (pte_t *)pmd_alloc(mm, pud, addr);
61                 goto out;
62         }
63
64         pmd = pmd_alloc(mm, pud, addr);
65         if (!pmd)
66                 return NULL;
67
68         for_each_napot_order(order) {
69                 if (napot_cont_size(order) == sz) {
70                         pte = pte_alloc_map(mm, pmd, addr & napot_cont_mask(order));
71                         break;
72                 }
73         }
74
75 out:
76         WARN_ON_ONCE(pte && pte_present(*pte) && !pte_huge(*pte));
77         return pte;
78 }
79
80 pte_t *huge_pte_offset(struct mm_struct *mm,
81                        unsigned long addr,
82                        unsigned long sz)
83 {
84         unsigned long order;
85         pte_t *pte = NULL;
86         pgd_t *pgd;
87         p4d_t *p4d;
88         pud_t *pud;
89         pmd_t *pmd;
90
91         pgd = pgd_offset(mm, addr);
92         if (!pgd_present(*pgd))
93                 return NULL;
94
95         p4d = p4d_offset(pgd, addr);
96         if (!p4d_present(*p4d))
97                 return NULL;
98
99         pud = pud_offset(p4d, addr);
100         if (sz == PUD_SIZE)
101                 /* must be pud huge, non-present or none */
102                 return (pte_t *)pud;
103
104         if (!pud_present(*pud))
105                 return NULL;
106
107         pmd = pmd_offset(pud, addr);
108         if (sz == PMD_SIZE)
109                 /* must be pmd huge, non-present or none */
110                 return (pte_t *)pmd;
111
112         if (!pmd_present(*pmd))
113                 return NULL;
114
115         for_each_napot_order(order) {
116                 if (napot_cont_size(order) == sz) {
117                         pte = pte_offset_kernel(pmd, addr & napot_cont_mask(order));
118                         break;
119                 }
120         }
121         return pte;
122 }
123
124 static pte_t get_clear_contig(struct mm_struct *mm,
125                               unsigned long addr,
126                               pte_t *ptep,
127                               unsigned long pte_num)
128 {
129         pte_t orig_pte = ptep_get(ptep);
130         unsigned long i;
131
132         for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) {
133                 pte_t pte = ptep_get_and_clear(mm, addr, ptep);
134
135                 if (pte_dirty(pte))
136                         orig_pte = pte_mkdirty(orig_pte);
137
138                 if (pte_young(pte))
139                         orig_pte = pte_mkyoung(orig_pte);
140         }
141
142         return orig_pte;
143 }
144
145 static pte_t get_clear_contig_flush(struct mm_struct *mm,
146                                     unsigned long addr,
147                                     pte_t *ptep,
148                                     unsigned long pte_num)
149 {
150         pte_t orig_pte = get_clear_contig(mm, addr, ptep, pte_num);
151         struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0);
152         bool valid = !pte_none(orig_pte);
153
154         if (valid)
155                 flush_tlb_range(&vma, addr, addr + (PAGE_SIZE * pte_num));
156
157         return orig_pte;
158 }
159
160 pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
161 {
162         unsigned long order;
163
164         for_each_napot_order(order) {
165                 if (shift == napot_cont_shift(order)) {
166                         entry = pte_mknapot(entry, order);
167                         break;
168                 }
169         }
170         if (order == NAPOT_ORDER_MAX)
171                 entry = pte_mkhuge(entry);
172
173         return entry;
174 }
175
176 void set_huge_pte_at(struct mm_struct *mm,
177                      unsigned long addr,
178                      pte_t *ptep,
179                      pte_t pte)
180 {
181         int i, pte_num;
182
183         if (!pte_napot(pte)) {
184                 set_pte_at(mm, addr, ptep, pte);
185                 return;
186         }
187
188         pte_num = napot_pte_num(napot_cont_order(pte));
189         for (i = 0; i < pte_num; i++, ptep++, addr += PAGE_SIZE)
190                 set_pte_at(mm, addr, ptep, pte);
191 }
192
193 int huge_ptep_set_access_flags(struct vm_area_struct *vma,
194                                unsigned long addr,
195                                pte_t *ptep,
196                                pte_t pte,
197                                int dirty)
198 {
199         struct mm_struct *mm = vma->vm_mm;
200         unsigned long order;
201         pte_t orig_pte;
202         int i, pte_num;
203
204         if (!pte_napot(pte))
205                 return ptep_set_access_flags(vma, addr, ptep, pte, dirty);
206
207         order = napot_cont_order(pte);
208         pte_num = napot_pte_num(order);
209         ptep = huge_pte_offset(mm, addr, napot_cont_size(order));
210         orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num);
211
212         if (pte_dirty(orig_pte))
213                 pte = pte_mkdirty(pte);
214
215         if (pte_young(orig_pte))
216                 pte = pte_mkyoung(pte);
217
218         for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
219                 set_pte_at(mm, addr, ptep, pte);
220
221         return true;
222 }
223
224 pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
225                               unsigned long addr,
226                               pte_t *ptep)
227 {
228         pte_t orig_pte = ptep_get(ptep);
229         int pte_num;
230
231         if (!pte_napot(orig_pte))
232                 return ptep_get_and_clear(mm, addr, ptep);
233
234         pte_num = napot_pte_num(napot_cont_order(orig_pte));
235
236         return get_clear_contig(mm, addr, ptep, pte_num);
237 }
238
239 void huge_ptep_set_wrprotect(struct mm_struct *mm,
240                              unsigned long addr,
241                              pte_t *ptep)
242 {
243         pte_t pte = ptep_get(ptep);
244         unsigned long order;
245         pte_t orig_pte;
246         int i, pte_num;
247
248         if (!pte_napot(pte)) {
249                 ptep_set_wrprotect(mm, addr, ptep);
250                 return;
251         }
252
253         order = napot_cont_order(pte);
254         pte_num = napot_pte_num(order);
255         ptep = huge_pte_offset(mm, addr, napot_cont_size(order));
256         orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num);
257
258         orig_pte = pte_wrprotect(orig_pte);
259
260         for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
261                 set_pte_at(mm, addr, ptep, orig_pte);
262 }
263
264 pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
265                             unsigned long addr,
266                             pte_t *ptep)
267 {
268         pte_t pte = ptep_get(ptep);
269         int pte_num;
270
271         if (!pte_napot(pte))
272                 return ptep_clear_flush(vma, addr, ptep);
273
274         pte_num = napot_pte_num(napot_cont_order(pte));
275
276         return get_clear_contig_flush(vma->vm_mm, addr, ptep, pte_num);
277 }
278
279 void huge_pte_clear(struct mm_struct *mm,
280                     unsigned long addr,
281                     pte_t *ptep,
282                     unsigned long sz)
283 {
284         pte_t pte = READ_ONCE(*ptep);
285         int i, pte_num;
286
287         if (!pte_napot(pte)) {
288                 pte_clear(mm, addr, ptep);
289                 return;
290         }
291
292         pte_num = napot_pte_num(napot_cont_order(pte));
293         for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++)
294                 pte_clear(mm, addr, ptep);
295 }
296
297 static __init bool is_napot_size(unsigned long size)
298 {
299         unsigned long order;
300
301         if (!has_svnapot())
302                 return false;
303
304         for_each_napot_order(order) {
305                 if (size == napot_cont_size(order))
306                         return true;
307         }
308         return false;
309 }
310
311 static __init int napot_hugetlbpages_init(void)
312 {
313         if (has_svnapot()) {
314                 unsigned long order;
315
316                 for_each_napot_order(order)
317                         hugetlb_add_hstate(order);
318         }
319         return 0;
320 }
321 arch_initcall(napot_hugetlbpages_init);
322
323 #else
324
325 static __init bool is_napot_size(unsigned long size)
326 {
327         return false;
328 }
329
330 #endif /*CONFIG_RISCV_ISA_SVNAPOT*/
331
332 int pud_huge(pud_t pud)
333 {
334         return pud_leaf(pud);
335 }
336
337 int pmd_huge(pmd_t pmd)
338 {
339         return pmd_leaf(pmd);
340 }
341
342 bool __init arch_hugetlb_valid_size(unsigned long size)
343 {
344         if (size == HPAGE_SIZE)
345                 return true;
346         else if (IS_ENABLED(CONFIG_64BIT) && size == PUD_SIZE)
347                 return true;
348         else if (is_napot_size(size))
349                 return true;
350         else
351                 return false;
352 }
353
354 #ifdef CONFIG_CONTIG_ALLOC
355 static __init int gigantic_pages_init(void)
356 {
357         /* With CONTIG_ALLOC, we can allocate gigantic pages at runtime */
358         if (IS_ENABLED(CONFIG_64BIT))
359                 hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
360         return 0;
361 }
362 arch_initcall(gigantic_pages_init);
363 #endif