diff --git a/mm/vma.c b/mm/vma.c index af8e65ada23b..d416ba58eb4c 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -1141,6 +1141,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) struct vm_area_struct *next = vmg->next; unsigned long end = vmg->end; bool can_merge_left, can_merge_right; + bool move_iter_back = false; mmap_assert_write_locked(vmg->mm); VM_WARN_ON_VMG(vmg->middle, vmg); @@ -1182,23 +1183,30 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) vmg->end = end; /* In expand-only case we are already positioned at prev. */ - if (!vmg->just_expand) { - /* Equivalent to going to the previous range. */ - vma_prev(vmg->vmi); - } + if (!vmg->just_expand) + move_iter_back = true; } + if (!can_merge_left && !can_merge_right) + return NULL; + + if (move_iter_back) + vma_prev(vmg->vmi); + /* * Now try to expand adjacent VMA(s). This takes care of removing the * following VMA if we have VMAs on both sides. */ - if (vmg->target && !vma_expand(vmg)) { - khugepaged_enter_vma(vmg->target, vmg->vm_flags); - vmg->state = VMA_MERGE_SUCCESS; - return vmg->target; + if (vma_expand(vmg)) { + /* Error occurred, restore iterator. */ + if (move_iter_back) + vma_iter_next_range(vmg->vmi); + return NULL; } - return NULL; + khugepaged_enter_vma(vmg->target, vmg->vm_flags); + vmg->state = VMA_MERGE_SUCCESS; + return vmg->target; } /* @@ -1288,7 +1296,7 @@ int vma_expand(struct vma_merge_struct *vmg) if (!ret && vmg->copied_from) ret = dup_anon_vma(target, vmg->copied_from, &anon_dup); if (ret) - return ret; + goto nomem; if (remove_next) { vma_start_write(next);