@@ -117,9 +117,9 @@ static int mprotect_folio_pte_batch(struct folio *folio, pte_t *ptep,
117117}
118118
119119/* Set nr_ptes number of ptes, starting from idx */
120- static void prot_commit_flush_ptes (struct vm_area_struct * vma , unsigned long addr ,
121- pte_t * ptep , pte_t oldpte , pte_t ptent , int nr_ptes ,
122- int idx , bool set_write , struct mmu_gather * tlb )
120+ static __always_inline void prot_commit_flush_ptes (struct vm_area_struct * vma ,
121+ unsigned long addr , pte_t * ptep , pte_t oldpte , pte_t ptent ,
122+ int nr_ptes , int idx , bool set_write , struct mmu_gather * tlb )
123123{
124124 /*
125125 * Advance the position in the batch by idx; note that if idx > 0,
@@ -143,7 +143,7 @@ static void prot_commit_flush_ptes(struct vm_area_struct *vma, unsigned long add
143143 * !PageAnonExclusive() pages, starting from start_idx. Caller must enforce
144144 * that the ptes point to consecutive pages of the same anon large folio.
145145 */
146- static int page_anon_exclusive_sub_batch (int start_idx , int max_len ,
146+ static __always_inline int page_anon_exclusive_sub_batch (int start_idx , int max_len ,
147147 struct page * first_page , bool expected_anon_exclusive )
148148{
149149 int idx ;
@@ -169,7 +169,7 @@ static int page_anon_exclusive_sub_batch(int start_idx, int max_len,
169169 * pte of the batch. Therefore, we must individually check all pages and
170170 * retrieve sub-batches.
171171 */
172- static void commit_anon_folio_batch (struct vm_area_struct * vma ,
172+ static __always_inline void commit_anon_folio_batch (struct vm_area_struct * vma ,
173173 struct folio * folio , struct page * first_page , unsigned long addr , pte_t * ptep ,
174174 pte_t oldpte , pte_t ptent , int nr_ptes , struct mmu_gather * tlb )
175175{
@@ -188,7 +188,7 @@ static void commit_anon_folio_batch(struct vm_area_struct *vma,
188188 }
189189}
190190
191- static void set_write_prot_commit_flush_ptes (struct vm_area_struct * vma ,
191+ static __always_inline void set_write_prot_commit_flush_ptes (struct vm_area_struct * vma ,
192192 struct folio * folio , struct page * page , unsigned long addr , pte_t * ptep ,
193193 pte_t oldpte , pte_t ptent , int nr_ptes , struct mmu_gather * tlb )
194194{
@@ -277,6 +277,45 @@ static long change_softleaf_pte(struct vm_area_struct *vma,
277277 return 0 ;
278278}
279279
280+ static __always_inline void change_present_ptes (struct mmu_gather * tlb ,
281+ struct vm_area_struct * vma , unsigned long addr , pte_t * ptep ,
282+ int nr_ptes , unsigned long end , pgprot_t newprot ,
283+ struct folio * folio , struct page * page , unsigned long cp_flags )
284+ {
285+ const bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE ;
286+ const bool uffd_wp = cp_flags & MM_CP_UFFD_WP ;
287+ pte_t ptent , oldpte ;
288+
289+ oldpte = modify_prot_start_ptes (vma , addr , ptep , nr_ptes );
290+ ptent = pte_modify (oldpte , newprot );
291+
292+ if (uffd_wp )
293+ ptent = pte_mkuffd_wp (ptent );
294+ else if (uffd_wp_resolve )
295+ ptent = pte_clear_uffd_wp (ptent );
296+
297+ /*
298+ * In some writable, shared mappings, we might want
299+ * to catch actual write access -- see
300+ * vma_wants_writenotify().
301+ *
302+ * In all writable, private mappings, we have to
303+ * properly handle COW.
304+ *
305+ * In both cases, we can sometimes still change PTEs
306+ * writable and avoid the write-fault handler, for
307+ * example, if a PTE is already dirty and no other
308+ * COW or special handling is required.
309+ */
310+ if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE ) &&
311+ !pte_write (ptent ))
312+ set_write_prot_commit_flush_ptes (vma , folio , page ,
313+ addr , ptep , oldpte , ptent , nr_ptes , tlb );
314+ else
315+ prot_commit_flush_ptes (vma , addr , ptep , oldpte , ptent ,
316+ nr_ptes , /* idx = */ 0 , /* set_write = */ false, tlb );
317+ }
318+
280319static long change_pte_range (struct mmu_gather * tlb ,
281320 struct vm_area_struct * vma , pmd_t * pmd , unsigned long addr ,
282321 unsigned long end , pgprot_t newprot , unsigned long cp_flags )
@@ -287,7 +326,6 @@ static long change_pte_range(struct mmu_gather *tlb,
287326 bool is_private_single_threaded ;
288327 bool prot_numa = cp_flags & MM_CP_PROT_NUMA ;
289328 bool uffd_wp = cp_flags & MM_CP_UFFD_WP ;
290- bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE ;
291329 int nr_ptes ;
292330
293331 tlb_change_page_size (tlb , PAGE_SIZE );
@@ -308,7 +346,6 @@ static long change_pte_range(struct mmu_gather *tlb,
308346 int max_nr_ptes = (end - addr ) >> PAGE_SHIFT ;
309347 struct folio * folio = NULL ;
310348 struct page * page ;
311- pte_t ptent ;
312349
313350 /* Already in the desired state. */
314351 if (prot_numa && pte_protnone (oldpte ))
@@ -334,34 +371,20 @@ static long change_pte_range(struct mmu_gather *tlb,
334371
335372 nr_ptes = mprotect_folio_pte_batch (folio , pte , oldpte , max_nr_ptes , flags );
336373
337- oldpte = modify_prot_start_ptes (vma , addr , pte , nr_ptes );
338- ptent = pte_modify (oldpte , newprot );
339-
340- if (uffd_wp )
341- ptent = pte_mkuffd_wp (ptent );
342- else if (uffd_wp_resolve )
343- ptent = pte_clear_uffd_wp (ptent );
344-
345374 /*
346- * In some writable, shared mappings, we might want
347- * to catch actual write access -- see
348- * vma_wants_writenotify().
349- *
350- * In all writable, private mappings, we have to
351- * properly handle COW.
352- *
353- * In both cases, we can sometimes still change PTEs
354- * writable and avoid the write-fault handler, for
355- * example, if a PTE is already dirty and no other
356- * COW or special handling is required.
375+ * Optimize for the small-folio common case by
376+ * special-casing it here. Compiler constant propagation
377+ * plus copious amounts of __always_inline does wonders.
357378 */
358- if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE ) &&
359- !pte_write (ptent ))
360- set_write_prot_commit_flush_ptes (vma , folio , page ,
361- addr , pte , oldpte , ptent , nr_ptes , tlb );
362- else
363- prot_commit_flush_ptes (vma , addr , pte , oldpte , ptent ,
364- nr_ptes , /* idx = */ 0 , /* set_write = */ false, tlb );
379+ if (likely (nr_ptes == 1 )) {
380+ change_present_ptes (tlb , vma , addr , pte , 1 ,
381+ end , newprot , folio , page , cp_flags );
382+ } else {
383+ change_present_ptes (tlb , vma , addr , pte ,
384+ nr_ptes , end , newprot , folio , page ,
385+ cp_flags );
386+ }
387+
365388 pages += nr_ptes ;
366389 } else if (pte_none (oldpte )) {
367390 /*
0 commit comments