22#include <linux/hugetlb.h>
33#include <linux/err.h>
44
5+ #ifdef CONFIG_RISCV_ISA_SVNAPOT
6+ pte_t * huge_pte_alloc (struct mm_struct * mm ,
7+ struct vm_area_struct * vma ,
8+ unsigned long addr ,
9+ unsigned long sz )
10+ {
11+ unsigned long order ;
12+ pte_t * pte = NULL ;
13+ pgd_t * pgd ;
14+ p4d_t * p4d ;
15+ pud_t * pud ;
16+ pmd_t * pmd ;
17+
18+ pgd = pgd_offset (mm , addr );
19+ p4d = p4d_alloc (mm , pgd , addr );
20+ if (!p4d )
21+ return NULL ;
22+
23+ pud = pud_alloc (mm , p4d , addr );
24+ if (!pud )
25+ return NULL ;
26+
27+ if (sz == PUD_SIZE ) {
28+ pte = (pte_t * )pud ;
29+ goto out ;
30+ }
31+
32+ if (sz == PMD_SIZE ) {
33+ if (want_pmd_share (vma , addr ) && pud_none (* pud ))
34+ pte = huge_pmd_share (mm , vma , addr , pud );
35+ else
36+ pte = (pte_t * )pmd_alloc (mm , pud , addr );
37+ goto out ;
38+ }
39+
40+ pmd = pmd_alloc (mm , pud , addr );
41+ if (!pmd )
42+ return NULL ;
43+
44+ for_each_napot_order (order ) {
45+ if (napot_cont_size (order ) == sz ) {
46+ pte = pte_alloc_map (mm , pmd , addr & napot_cont_mask (order ));
47+ break ;
48+ }
49+ }
50+
51+ out :
52+ WARN_ON_ONCE (pte && pte_present (* pte ) && !pte_huge (* pte ));
53+ return pte ;
54+ }
55+
56+ pte_t * huge_pte_offset (struct mm_struct * mm ,
57+ unsigned long addr ,
58+ unsigned long sz )
59+ {
60+ unsigned long order ;
61+ pte_t * pte = NULL ;
62+ pgd_t * pgd ;
63+ p4d_t * p4d ;
64+ pud_t * pud ;
65+ pmd_t * pmd ;
66+
67+ pgd = pgd_offset (mm , addr );
68+ if (!pgd_present (* pgd ))
69+ return NULL ;
70+
71+ p4d = p4d_offset (pgd , addr );
72+ if (!p4d_present (* p4d ))
73+ return NULL ;
74+
75+ pud = pud_offset (p4d , addr );
76+ if (sz == PUD_SIZE )
77+ /* must be pud huge, non-present or none */
78+ return (pte_t * )pud ;
79+
80+ if (!pud_present (* pud ))
81+ return NULL ;
82+
83+ pmd = pmd_offset (pud , addr );
84+ if (sz == PMD_SIZE )
85+ /* must be pmd huge, non-present or none */
86+ return (pte_t * )pmd ;
87+
88+ if (!pmd_present (* pmd ))
89+ return NULL ;
90+
91+ for_each_napot_order (order ) {
92+ if (napot_cont_size (order ) == sz ) {
93+ pte = pte_offset_kernel (pmd , addr & napot_cont_mask (order ));
94+ break ;
95+ }
96+ }
97+ return pte ;
98+ }
99+
100+ static pte_t get_clear_contig (struct mm_struct * mm ,
101+ unsigned long addr ,
102+ pte_t * ptep ,
103+ unsigned long pte_num )
104+ {
105+ pte_t orig_pte = ptep_get (ptep );
106+ unsigned long i ;
107+
108+ for (i = 0 ; i < pte_num ; i ++ , addr += PAGE_SIZE , ptep ++ ) {
109+ pte_t pte = ptep_get_and_clear (mm , addr , ptep );
110+
111+ if (pte_dirty (pte ))
112+ orig_pte = pte_mkdirty (orig_pte );
113+
114+ if (pte_young (pte ))
115+ orig_pte = pte_mkyoung (orig_pte );
116+ }
117+
118+ return orig_pte ;
119+ }
120+
121+ static pte_t get_clear_contig_flush (struct mm_struct * mm ,
122+ unsigned long addr ,
123+ pte_t * ptep ,
124+ unsigned long pte_num )
125+ {
126+ pte_t orig_pte = get_clear_contig (mm , addr , ptep , pte_num );
127+ struct vm_area_struct vma = TLB_FLUSH_VMA (mm , 0 );
128+ bool valid = !pte_none (orig_pte );
129+
130+ if (valid )
131+ flush_tlb_range (& vma , addr , addr + (PAGE_SIZE * pte_num ));
132+
133+ return orig_pte ;
134+ }
135+
136+ pte_t arch_make_huge_pte (pte_t entry , unsigned int shift , vm_flags_t flags )
137+ {
138+ unsigned long order ;
139+
140+ for_each_napot_order (order ) {
141+ if (shift == napot_cont_shift (order )) {
142+ entry = pte_mknapot (entry , order );
143+ break ;
144+ }
145+ }
146+ if (order == NAPOT_ORDER_MAX )
147+ entry = pte_mkhuge (entry );
148+
149+ return entry ;
150+ }
151+
152+ void set_huge_pte_at (struct mm_struct * mm ,
153+ unsigned long addr ,
154+ pte_t * ptep ,
155+ pte_t pte )
156+ {
157+ int i , pte_num ;
158+
159+ if (!pte_napot (pte )) {
160+ set_pte_at (mm , addr , ptep , pte );
161+ return ;
162+ }
163+
164+ pte_num = napot_pte_num (napot_cont_order (pte ));
165+ for (i = 0 ; i < pte_num ; i ++ , ptep ++ , addr += PAGE_SIZE )
166+ set_pte_at (mm , addr , ptep , pte );
167+ }
168+
169+ int huge_ptep_set_access_flags (struct vm_area_struct * vma ,
170+ unsigned long addr ,
171+ pte_t * ptep ,
172+ pte_t pte ,
173+ int dirty )
174+ {
175+ struct mm_struct * mm = vma -> vm_mm ;
176+ unsigned long order ;
177+ pte_t orig_pte ;
178+ int i , pte_num ;
179+
180+ if (!pte_napot (pte ))
181+ return ptep_set_access_flags (vma , addr , ptep , pte , dirty );
182+
183+ order = napot_cont_order (pte );
184+ pte_num = napot_pte_num (order );
185+ ptep = huge_pte_offset (mm , addr , napot_cont_size (order ));
186+ orig_pte = get_clear_contig_flush (mm , addr , ptep , pte_num );
187+
188+ if (pte_dirty (orig_pte ))
189+ pte = pte_mkdirty (pte );
190+
191+ if (pte_young (orig_pte ))
192+ pte = pte_mkyoung (pte );
193+
194+ for (i = 0 ; i < pte_num ; i ++ , addr += PAGE_SIZE , ptep ++ )
195+ set_pte_at (mm , addr , ptep , pte );
196+
197+ return true;
198+ }
199+
200+ pte_t huge_ptep_get_and_clear (struct mm_struct * mm ,
201+ unsigned long addr ,
202+ pte_t * ptep )
203+ {
204+ pte_t orig_pte = ptep_get (ptep );
205+ int pte_num ;
206+
207+ if (!pte_napot (orig_pte ))
208+ return ptep_get_and_clear (mm , addr , ptep );
209+
210+ pte_num = napot_pte_num (napot_cont_order (orig_pte ));
211+
212+ return get_clear_contig (mm , addr , ptep , pte_num );
213+ }
214+
215+ void huge_ptep_set_wrprotect (struct mm_struct * mm ,
216+ unsigned long addr ,
217+ pte_t * ptep )
218+ {
219+ pte_t pte = ptep_get (ptep );
220+ unsigned long order ;
221+ int i , pte_num ;
222+
223+ if (!pte_napot (pte )) {
224+ ptep_set_wrprotect (mm , addr , ptep );
225+ return ;
226+ }
227+
228+ order = napot_cont_order (pte );
229+ pte_num = napot_pte_num (order );
230+ ptep = huge_pte_offset (mm , addr , napot_cont_size (order ));
231+
232+ for (i = 0 ; i < pte_num ; i ++ , addr += PAGE_SIZE , ptep ++ )
233+ ptep_set_wrprotect (mm , addr , ptep );
234+ }
235+
236+ pte_t huge_ptep_clear_flush (struct vm_area_struct * vma ,
237+ unsigned long addr ,
238+ pte_t * ptep )
239+ {
240+ pte_t pte = ptep_get (ptep );
241+ int pte_num ;
242+
243+ if (!pte_napot (pte ))
244+ return ptep_clear_flush (vma , addr , ptep );
245+
246+ pte_num = napot_pte_num (napot_cont_order (pte ));
247+
248+ return get_clear_contig_flush (vma -> vm_mm , addr , ptep , pte_num );
249+ }
250+
251+ void huge_pte_clear (struct mm_struct * mm ,
252+ unsigned long addr ,
253+ pte_t * ptep ,
254+ unsigned long sz )
255+ {
256+ pte_t pte = READ_ONCE (* ptep );
257+ int i , pte_num ;
258+
259+ if (!pte_napot (pte )) {
260+ pte_clear (mm , addr , ptep );
261+ return ;
262+ }
263+
264+ pte_num = napot_pte_num (napot_cont_order (pte ));
265+ for (i = 0 ; i < pte_num ; i ++ , addr += PAGE_SIZE , ptep ++ )
266+ pte_clear (mm , addr , ptep );
267+ }
268+
269+ static __init bool is_napot_size (unsigned long size )
270+ {
271+ unsigned long order ;
272+
273+ if (!has_svnapot ())
274+ return false;
275+
276+ for_each_napot_order (order ) {
277+ if (size == napot_cont_size (order ))
278+ return true;
279+ }
280+ return false;
281+ }
282+
283+ static __init int napot_hugetlbpages_init (void )
284+ {
285+ if (has_svnapot ()) {
286+ unsigned long order ;
287+
288+ for_each_napot_order (order )
289+ hugetlb_add_hstate (order );
290+ }
291+ return 0 ;
292+ }
293+ arch_initcall (napot_hugetlbpages_init );
294+
295+ #else
296+
297+ static __init bool is_napot_size (unsigned long size )
298+ {
299+ return false;
300+ }
301+
302+ #endif /*CONFIG_RISCV_ISA_SVNAPOT*/
303+
5304int pud_huge (pud_t pud )
6305{
7306 return pud_leaf (pud );
@@ -18,6 +317,8 @@ bool __init arch_hugetlb_valid_size(unsigned long size)
18317 return true;
19318 else if (IS_ENABLED (CONFIG_64BIT ) && size == PUD_SIZE )
20319 return true;
320+ else if (is_napot_size (size ))
321+ return true;
21322 else
22323 return false;
23324}
0 commit comments