Hi Andrew, Sorry to be a pain here, and can respin if it's easier, but can we update the text of the comments below? As in discussion with Liam off-list we agreed that the current wording is rather unclear and we can do a lot better.
I provide the improved version inline below: On Fri, Nov 07, 2025 at 04:11:48PM +0000, Lorenzo Stoakes wrote: > It is useful to be able to designate that certain flags are 'sticky', that > is, if two VMAs are merged one with a flag of this nature and one without, > the merged VMA sets this flag. > > As a result we ignore these flags for the purposes of determining VMA flag > differences between VMAs being considered for merge. > > This patch therefore updates the VMA merge logic to perform this action, > with flags possessing this property being described in the VM_STICKY > bitmap. > > Those flags which ought to be ignored for the purposes of VMA merge are > described in the VM_IGNORE_MERGE bitmap, which the VMA merge logic is also > updated to use. > > As part of this change we place VM_SOFTDIRTY in VM_IGNORE_MERGE as it > already had this behaviour, alongside VM_STICKY as sticky flags by > implication must not disallow merge. > > Ultimately it seems that we should make VM_SOFTDIRTY a sticky flag in its > own right, but this change is out of scope for this series. > > The only sticky flag designated as such is VM_MAYBE_GUARD, so as a result > of this change, once the VMA flag is set upon guard region installation, > VMAs with guard ranges will now not have their merge behaviour impacted as > a result and can be freely merged with other VMAs without VM_MAYBE_GUARD > set. > > We also update the VMA userland tests to account for the changes. > > Reviewed-by: Pedro Falcato <[email protected]> > Reviewed-by: Vlastimil Babka <[email protected]> > Signed-off-by: Lorenzo Stoakes <[email protected]> > --- > include/linux/mm.h | 29 +++++++++++++++++++++++++++++ > mm/vma.c | 22 ++++++++++++---------- > tools/testing/vma/vma_internal.h | 29 +++++++++++++++++++++++++++++ > 3 files changed, 70 insertions(+), 10 deletions(-) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 699566c21ff7..6c1c459e9acb 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -527,6 +527,35 @@ extern unsigned int kobjsize(const void *objp); > #endif > #define VM_FLAGS_CLEAR (ARCH_VM_PKEY_FLAGS | VM_ARCH_CLEAR) > > +/* > + * Flags which should be 'sticky' on merge - that is, flags which, when one > VMA > + * possesses it but the other does not, the merged VMA should nonetheless > have > + * applied to it: > + * > + * VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that > + * mapped page tables may contain metadata not described by > the > + * VMA and thus any merged VMA may also contain this > metadata, > + * and thus we must make this flag sticky. > + */ > +#define VM_STICKY VM_MAYBE_GUARD > + > +/* > + * VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one > + * of these flags and the other not does not preclude a merge. > + * > + * VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags > but > + * dirty bit -- the caller should mark merged VMA as dirty. If > + * dirty bit won't be excluded from comparison, we increase > + * pressure on the memory system forcing the kernel to > generate > + * new VMAs when old one could be extended instead. > + * Could you replace this: > + * VM_STICKY - If one VMA has flags which most be 'sticky', that is ones > + * which should propagate to all VMAs, but the other does not, > + * the merge should still proceed with the merge logic > applying > + * sticky flags to the final VMA. With this: * VM_STICKY - When merging VMAs, VMA flags must match, unless they are * 'sticky'. If any sticky flags exist in either VMA, we simply * set all of them on the merged VMA. > + */ > +#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) > + > /* > * mapping from the currently active vm_flags protection bits (the > * low four bits) to a page protection mask.. > diff --git a/mm/vma.c b/mm/vma.c > index 0c5e391fe2e2..6cb082bc5e29 100644 > --- a/mm/vma.c > +++ b/mm/vma.c > @@ -89,15 +89,7 @@ static inline bool is_mergeable_vma(struct > vma_merge_struct *vmg, bool merge_nex > > if (!mpol_equal(vmg->policy, vma_policy(vma))) > return false; > - /* > - * VM_SOFTDIRTY should not prevent from VMA merging, if we > - * match the flags but dirty bit -- the caller should mark > - * merged VMA as dirty. If dirty bit won't be excluded from > - * comparison, we increase pressure on the memory system forcing > - * the kernel to generate new VMAs when old one could be > - * extended instead. > - */ > - if ((vma->vm_flags ^ vmg->vm_flags) & ~VM_SOFTDIRTY) > + if ((vma->vm_flags ^ vmg->vm_flags) & ~VM_IGNORE_MERGE) > return false; > if (vma->vm_file != vmg->file) > return false; > @@ -808,6 +800,7 @@ static bool can_merge_remove_vma(struct vm_area_struct > *vma) > static __must_check struct vm_area_struct *vma_merge_existing_range( > struct vma_merge_struct *vmg) > { > + vm_flags_t sticky_flags = vmg->vm_flags & VM_STICKY; > struct vm_area_struct *middle = vmg->middle; > struct vm_area_struct *prev = vmg->prev; > struct vm_area_struct *next; > @@ -900,11 +893,13 @@ static __must_check struct vm_area_struct > *vma_merge_existing_range( > if (merge_right) { > vma_start_write(next); > vmg->target = next; > + sticky_flags |= (next->vm_flags & VM_STICKY); > } > > if (merge_left) { > vma_start_write(prev); > vmg->target = prev; > + sticky_flags |= (prev->vm_flags & VM_STICKY); > } > > if (merge_both) { > @@ -974,6 +969,7 @@ static __must_check struct vm_area_struct > *vma_merge_existing_range( > if (err || commit_merge(vmg)) > goto abort; > > + vm_flags_set(vmg->target, sticky_flags); > khugepaged_enter_vma(vmg->target, vmg->vm_flags); > vmg->state = VMA_MERGE_SUCCESS; > return vmg->target; > @@ -1124,6 +1120,10 @@ int vma_expand(struct vma_merge_struct *vmg) > bool remove_next = false; > struct vm_area_struct *target = vmg->target; > struct vm_area_struct *next = vmg->next; > + vm_flags_t sticky_flags; > + > + sticky_flags = vmg->vm_flags & VM_STICKY; > + sticky_flags |= target->vm_flags & VM_STICKY; > > VM_WARN_ON_VMG(!target, vmg); > > @@ -1133,6 +1133,7 @@ int vma_expand(struct vma_merge_struct *vmg) > if (next && (target != next) && (vmg->end == next->vm_end)) { > int ret; > > + sticky_flags |= next->vm_flags & VM_STICKY; > remove_next = true; > /* This should already have been checked by this point. */ > VM_WARN_ON_VMG(!can_merge_remove_vma(next), vmg); > @@ -1159,6 +1160,7 @@ int vma_expand(struct vma_merge_struct *vmg) > if (commit_merge(vmg)) > goto nomem; > > + vm_flags_set(target, sticky_flags); > return 0; > > nomem: > @@ -1902,7 +1904,7 @@ static int anon_vma_compatible(struct vm_area_struct > *a, struct vm_area_struct * > return a->vm_end == b->vm_start && > mpol_equal(vma_policy(a), vma_policy(b)) && > a->vm_file == b->vm_file && > - !((a->vm_flags ^ b->vm_flags) & ~(VM_ACCESS_FLAGS | > VM_SOFTDIRTY)) && > + !((a->vm_flags ^ b->vm_flags) & ~(VM_ACCESS_FLAGS | > VM_IGNORE_MERGE)) && > b->vm_pgoff == a->vm_pgoff + ((b->vm_start - a->vm_start) >> > PAGE_SHIFT); > } > > diff --git a/tools/testing/vma/vma_internal.h > b/tools/testing/vma/vma_internal.h > index 46acb4df45de..a54990aa3009 100644 > --- a/tools/testing/vma/vma_internal.h > +++ b/tools/testing/vma/vma_internal.h > @@ -117,6 +117,35 @@ extern unsigned long dac_mmap_min_addr; > #define VM_SEALED VM_NONE > #endif > > +/* > + * Flags which should be 'sticky' on merge - that is, flags which, when one > VMA > + * possesses it but the other does not, the merged VMA should nonetheless > have > + * applied to it: > + * > + * VM_MAYBE_GUARD - If a VMA may have guard regions in place it implies that > + * mapped page tables may contain metadata not described by > the > + * VMA and thus any merged VMA may also contain this > metadata, > + * and thus we must make this flag sticky. > + */ > +#define VM_STICKY VM_MAYBE_GUARD > + > +/* > + * VMA flags we ignore for the purposes of merge, i.e. one VMA possessing one > + * of these flags and the other not does not preclude a merge. > + * > + * VM_SOFTDIRTY - Should not prevent from VMA merging, if we match the flags > but > + * dirty bit -- the caller should mark merged VMA as dirty. If > + * dirty bit won't be excluded from comparison, we increase > + * pressure on the memory system forcing the kernel to > generate > + * new VMAs when old one could be extended instead. > + * Also here, we dupicate this for the VMA unit tests, could we replace: > + * VM_STICKY - If one VMA has flags which most be 'sticky', that is ones > + * which should propagate to all VMAs, but the other does not, > + * the merge should still proceed with the merge logic > applying > + * sticky flags to the final VMA. With: * VM_STICKY - When merging VMAs, VMA flags must match, unless they are * 'sticky'. If any sticky flags exist in either VMA, we simply * set all of them on the merged VMA. > + */ > +#define VM_IGNORE_MERGE (VM_SOFTDIRTY | VM_STICKY) > + > #define FIRST_USER_ADDRESS 0UL > #define USER_PGTABLES_CEILING 0UL > > -- > 2.51.0 > Thanks, Lorenzo
