Re: [PATCH v3 10/26] mm: Add more flags for __apply_to_page_range()
Yosry Ahmed <[email protected]> Tue, 4 Aug 2026 00:08:37 +0000
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On Sun, Jul 26, 2026 at 10:22:43PM +0000, Brendan Jackman wrote: > Add two flags to make this API more generic: > > 1. Separate "create" into two levels - one to allow creating new > mappings without allocating pagetables, and one for the current > behaviour that allows both of these. > > 2. Create a new flag to report that the caller has taken care of > synchronization and no locks are required. > > Both of these will serve to allow calling this API from restricted > contexts where allocation and pagetable locking are not possible. > > Signed-off-by: Brendan Jackman <[email protected]> > --- > mm/internal.h | 26 +++++++++++++++++++++++++- > mm/memory.c | 59 ++++++++++++++++++++++++++++++++++------------------------- > 2 files changed, 59 insertions(+), 26 deletions(-) > > diff --git a/mm/internal.h b/mm/internal.h > index 395331a12d62d..5a237d9c5fa96 100644 > --- a/mm/internal.h > +++ b/mm/internal.h > @@ -1662,9 +1662,33 @@ static inline bool can_spin_trylock(void) > > /* > * Create a mapping if it doesn't exist. (Otherwise, skip regions with no > - * existing mapping, and return an error for regions with no leaf pagetable). > + * existing mapping). This doesn't allow allocating, most users will want > + * PGRANGE_ALLOC. > + * > + * Do not test this bit directly as it is implied by PGRANGE_ALLOC, use > + * pgrange_create() instead. > */ > #define PGRANGE_CREATE (1 << 0) > +/* > + * Allocate a pagetable if one is missing. (Otherwise, return an error for > + * regions with no leaf pagetable). Also implies PGRANGE_CREATE. > + * > + * Note that __apply_to_page_range() assumes that pagetables for the area are > + * already initialised down to PMD level, so this only affects PTEs in practice. > + */ > +#define PGRANGE_ALLOC (1 << 1) > +/* > + * Do not take any locks. This means the caller has taken care of > + * synchronisation. This is incompatible with PGRANGE_ALLOC and also with > + * mm=&init_mm. > + */ > +#define PGRANGE_NOLOCK (1 << 2) I assume this is used by the mermap as locking is not required because the mappings are per-CPU and migration is disabled while the mermap is used? Also, why is this incompatible with init_mm? It actually seems like apply_to_pte_range() is always lockless for init_mm (uses pte_offset_kernel()), probably callers are also synchronizing in their own way (e.g. exclusive access to a vmap area?). > + > + > +static inline bool pgrange_create(unsigned int flags) > +{ > + return flags & (PGRANGE_CREATE | PGRANGE_ALLOC); > +} > > int __apply_to_page_range(struct mm_struct *mm, unsigned long addr, > unsigned long size, pte_fn_t fn, > diff --git a/mm/memory.c b/mm/memory.c > index c4defefea1574..d00508db1021e 100644 > --- a/mm/memory.c > +++ b/mm/memory.c > @@ -3441,30 +3441,35 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, > pte_fn_t fn, void *data, unsigned int flags, > pgtbl_mod_mask *mask) > { > - bool create = flags & PGRANGE_CREATE; > pte_t *pte, *mapped_pte; > int err = 0; > spinlock_t *ptl; > > - if (create) { > + if (flags & PGRANGE_ALLOC) { > + VM_WARN_ON(flags & PGRANGE_NOLOCK); > + > mapped_pte = pte = (mm == &init_mm) ? > pte_alloc_kernel_track(pmd, addr, mask) : > pte_alloc_map_lock(mm, pmd, addr, &ptl); > if (!pte) > return -ENOMEM; > } else { > - mapped_pte = pte = (mm == &init_mm) ? > - pte_offset_kernel(pmd, addr) : > - pte_offset_map_lock(mm, pmd, addr, &ptl); > + if (mm == &init_mm) > + pte = pte_offset_kernel(pmd, addr); > + else if (flags & PGRANGE_NOLOCK) > + pte = pte_offset_map(pmd, addr); > + else > + pte = pte_offset_map_lock(mm, pmd, addr, &ptl); > if (!pte) > return -EINVAL; > + mapped_pte = pte; > } > > lazy_mmu_mode_enable(); > > if (fn) { > do { > - if (create || !pte_none(ptep_get(pte))) { > + if (pgrange_create(flags) || !pte_none(ptep_get(pte))) { > err = fn(pte, addr, data); > if (err) > break; > @@ -3475,8 +3480,13 @@ static int apply_to_pte_range(struct mm_struct *mm, pmd_t *pmd, > > lazy_mmu_mode_disable(); > > - if (mm != &init_mm) > - pte_unmap_unlock(mapped_pte, ptl); > + if (mm != &init_mm) { > + if (flags & PGRANGE_NOLOCK) > + pte_unmap(mapped_pte); > + else > + pte_unmap_unlock(mapped_pte, ptl); > + } > + > return err; > } [..]