On Wed, Sep 23, 2026 at 02:28:29PM +0800, Wen Jiang wrote:
> From: "Barry Song (Xiaomi)" <[email protected]>
> 
> vmap_pages_range_noflush_walk() (formerly vmap_small_pages_range_noflush())
> provides a clean interface by taking struct page **pages and mapping them
> via direct PTE iteration. This avoids the page table rewalk seen when
> using vmap_range_noflush() for page_shift values other than PAGE_SHIFT.
> 
> Extend it to support larger page_shift values, and add PMD- and
> contiguous-PTE mappings as well.
> 
> Rename it to vmap_pages_range_noflush_walk() since it now handles
> more than just small pages.
> 
> For vmalloc() allocations with VM_ALLOW_HUGE_VMAP, we no longer need to
> iterate over pages one by one via vmap_range_noflush(), which would
> otherwise lead to page table rewalk. The code is now unified with the
> PAGE_SHIFT case by simply calling vmap_pages_range_noflush_walk().
> 
> Signed-off-by: Barry Song (Xiaomi) <[email protected]>
> Signed-off-by: Wen Jiang <[email protected]>
> Tested-by: Xueyuan Chen <[email protected]>
> Tested-by: Leo Yan <[email protected]>
> Reviewed-by: Dev Jain <[email protected]>
> ---
>  mm/vmalloc.c | 84 +++++++++++++++++++++++++++++++---------------------
>  1 file changed, 50 insertions(+), 34 deletions(-)
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index d475e52110e4a..6e0dec73107a7 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -558,8 +558,10 @@ void vunmap_range(unsigned long addr, unsigned long end)
>  
>  static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -             pgtbl_mod_mask *mask)
> +             pgtbl_mod_mask *mask, unsigned int shift)
>  {
> +     unsigned long pfn, size;
> +     unsigned int steps;
>       int err = 0;
>       pte_t *pte;
>  
> @@ -590,9 +592,10 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned 
> long addr,
>                       break;
>               }
>  
> -             set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));
> -             (*nr)++;
> -     } while (pte++, addr += PAGE_SIZE, addr != end);
> +             pfn = page_to_pfn(page);
> +             size = vmap_set_ptes(pte, addr, end, pfn, prot, shift);
> +             steps = PFN_DOWN(size);
> +     } while (pte += steps, *nr += steps, addr += size, addr != end);
>
nr_mapped_pages?

>  
>       lazy_mmu_mode_disable();
>       *mask |= PGTBL_PTE_MODIFIED;
> @@ -602,60 +605,88 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned 
> long addr,
>  
>  static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -             pgtbl_mod_mask *mask)
> +             pgtbl_mod_mask *mask, unsigned int shift)
>  {
>       pmd_t *pmd;
>       unsigned long next;
> +     int err;
>  
>       pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
>       if (!pmd)
>               return -ENOMEM;
>       do {
>               next = pmd_addr_end(addr, end);
> -             if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, 
> mask))
> -                     return -ENOMEM;
> +
> +             if (shift >= PMD_SHIFT) {
> +                     struct page *page = pages[*nr];
> +                     phys_addr_t phys_addr;
> +
> +                     if (WARN_ON(!page))
> +                             return -ENOMEM;
> +                     if (WARN_ON(!pfn_valid(page_to_pfn(page))))
> +                             return -EINVAL;
> +
> +                     phys_addr = page_to_phys(page);
> +
> +                     if (vmap_try_huge_pmd(pmd, addr, next, phys_addr, prot, 
> shift)) {
> +                             *mask |= PGTBL_PMD_MODIFIED;
> +                             *nr += 1 << (PMD_SHIFT - PAGE_SHIFT);
>
*nr += 1 << (PMD_SHIFT - PAGE_SHIFT); --->  *nr += 1U << (PMD_SHIFT - 
PAGE_SHIFT);
as it used to be?

> +                             continue;
> +                     }
> +             }
> +             err = vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, 
> mask, shift);
> +             if (err)
> +                     return err;
>       } while (pmd++, addr = next, addr != end);
>       return 0;
>  }
>  
>  static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -             pgtbl_mod_mask *mask)
> +             pgtbl_mod_mask *mask, unsigned int shift)
>  {
>       pud_t *pud;
>       unsigned long next;
> +     int err;
>  
>       pud = pud_alloc_track(&init_mm, p4d, addr, mask);
>       if (!pud)
>               return -ENOMEM;
>       do {
>               next = pud_addr_end(addr, end);
> -             if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, 
> mask))
> -                     return -ENOMEM;
> +             err = vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, 
> mask, shift);
> +             if (err)
> +                     return err;
>       } while (pud++, addr = next, addr != end);
>       return 0;
>  }
>  
>  static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
>               unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -             pgtbl_mod_mask *mask)
> +             pgtbl_mod_mask *mask, unsigned int shift)
>  {
>       p4d_t *p4d;
>       unsigned long next;
> +     int err;
>  
>       p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
>       if (!p4d)
>               return -ENOMEM;
>       do {
>               next = p4d_addr_end(addr, end);
> -             if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, 
> mask))
> -                     return -ENOMEM;
> +             err = vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, 
> mask, shift);
> +             if (err)
> +                     return err;
>       } while (p4d++, addr = next, addr != end);
>       return 0;
>  }
>  
> -static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long 
> end,
> -             pgprot_t prot, struct page **pages)
> +/*
> + * It can take an array of pages which are not all contiguous, but it
> + * may have contiguous chunks, as hinted by @shift.
> + */
> +static int vmap_pages_range_noflush_walk(unsigned long addr, unsigned long 
> end,
> +             pgprot_t prot, struct page **pages, unsigned int shift)
>  {
>       unsigned long start = addr;
>       pgd_t *pgd;
> @@ -670,7 +701,7 @@ static int vmap_small_pages_range_noflush(unsigned long 
> addr, unsigned long end,
>               next = pgd_addr_end(addr, end);
>               if (pgd_bad(*pgd))
>                       mask |= PGTBL_PGD_MODIFIED;
> -             err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, 
> &mask);
> +             err = vmap_pages_p4d_range(pgd, addr, next, prot, pages, &nr, 
> &mask, shift);
>               if (err)
>                       break;
>       } while (pgd++, addr = next, addr != end);
> @@ -693,27 +724,12 @@ static int vmap_small_pages_range_noflush(unsigned long 
> addr, unsigned long end,
>  int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
>               pgprot_t prot, struct page **pages, unsigned int page_shift)
>  {
> -     unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
> -
>       WARN_ON(page_shift < PAGE_SHIFT);
>
This is not about this patch but probably on that WARN_ON we should bail out
early.

--
Uladzislau Rezki

Reply via email to