On Fri, Oct 25, 2024 at 11:43:20AM +0200, Vlastimil Babka wrote: > On 10/23/24 22:38, Lorenzo Stoakes wrote: > > Rather than trying to merge again when ostensibly allocating a new VMA, > > instead defer until the VMA is added and attempt to merge the existing > > range. > > > > This way we have no complicated unwinding logic midway through the process > > of mapping the VMA. > > > > In addition this removes limitations on the VMA not being able to be the > > first in the virtual memory address space which was previously implicitly > > required. > > > > It also performs this merge after the final flag adjustments are performed, > > something that was not done previously and thus might have prevented > > possibly valid merges in the past. > > > > In theory, for this very same reason, we should unconditionally attempt > > merge here, however this is likely to have a performance impact so it is > > better to avoid this given the unlikely outcome of a merge. > > Maybe just expand the cases where we set map->retry_merge, i.e. in case the > final flag adjustments do anything? > > > The vmg state will already have been reset by the first attempt at a merge > > so we only need to reset the iterator, set the vma and flags and try again. > > > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx> > > Reviewed-by: Vlastimil Babka <vbabka@xxxxxxx> Thanks (and for 7/8!) > > A nit: > > > --- > > mm/vma.c | 75 ++++++++++++++++++++------------------------------------ > > 1 file changed, 26 insertions(+), 49 deletions(-) > > > > diff --git a/mm/vma.c b/mm/vma.c > > index 065f5e1f65be..c493ecebf394 100644 > > --- a/mm/vma.c > > +++ b/mm/vma.c > > @@ -19,6 +19,7 @@ struct mmap_state { > > struct file *file; > > > > unsigned long charged; > > + bool retry_merge; > > > > struct vm_area_struct *prev; > > struct vm_area_struct *next; > > @@ -2280,9 +2281,9 @@ static int __mmap_prepare(struct mmap_state *map, struct vma_merge_struct *vmg, > > return 0; > > } > > > > + > > static int __mmap_new_file_vma(struct mmap_state *map, > > - struct vma_merge_struct *vmg, > > - struct vm_area_struct **vmap, bool *mergedp) > > + struct vm_area_struct **vmap) > > { > > struct vma_iterator *vmi = map->vmi; > > struct vm_area_struct *vma = *vmap; > > @@ -2311,37 +2312,11 @@ static int __mmap_new_file_vma(struct mmap_state *map, > > !(map->flags & VM_MAYWRITE) && > > (vma->vm_flags & VM_MAYWRITE)); > > > > - vma_iter_config(vmi, map->addr, map->end); > > - /* > > - * If flags changed after mmap_file(), we should try merge > > - * vma again as we may succeed this time. > > - */ > > - if (unlikely(map->flags != vma->vm_flags && map->prev)) { > > - struct vm_area_struct *merge; > > - > > - vmg->flags = vma->vm_flags; > > - /* If this fails, state is reset ready for a reattempt. */ > > - merge = vma_merge_new_range(vmg); > > - > > - if (merge) { > > - /* > > - * ->mmap() can change vma->vm_file and fput > > - * the original file. So fput the vma->vm_file > > - * here or we would add an extra fput for file > > - * and cause general protection fault > > - * ultimately. > > - */ > > - fput(vma->vm_file); > > - vm_area_free(vma); > > - vma = merge; > > - *mergedp = true; > > - } else { > > - vma_iter_config(vmi, map->addr, map->end); > > - } > > - } > > + /* If the flags change (and are mergeable), let's retry later. */ > > + map->retry_merge = vma->vm_flags != map->flags && !(vma->vm_flags & VM_SPECIAL); > > > > + vma_iter_config(vmi, map->addr, map->end); > > Do we need this still? __mmap_new_vma() did that and nothing changed since > in the non-error case, AFAICS? You're right, this change really highlights that, will remove thanks! > > > map->flags = vma->vm_flags; > > - *vmap = vma; > > return 0; > > } > >