init_vma_prep(&vp, vma);
vp.insert = new;
vma_prepare(&vp);
- vma_adjust_trans_huge(vma, vma->vm_start, addr, 0);
+ vma_adjust_trans_huge(vma, vma->vm_start, addr, NULL);
if (new_below) {
vma->vm_start = addr;
#endif /* CONFIG_DEBUG_VM_MAPLE_TREE */
/*
- * Actually perform the VMA merge operation.
- *
- * On success, returns the merged VMA. Otherwise returns NULL.
+ * Based on the vmg flag indicating whether we need to adjust the vm_start field
+ * for the middle or next VMA, we calculate what the range of the newly adjusted
+ * VMA ought to be, and set the VMA's range accordingly.
*/
-static struct vm_area_struct *commit_merge(struct vma_merge_struct *vmg)
+static void vmg_adjust_set_range(struct vma_merge_struct *vmg)
{
- struct vm_area_struct *vma;
- struct vma_prepare vp;
struct vm_area_struct *adjust;
- long adj_start;
+ pgoff_t pgoff;
- /*
- * If modifying an existing VMA and we don't remove vmg->middle, then we
- * shrink the adjacent VMA.
- */
if (vmg->__adjust_middle_start) {
- vma = vmg->target;
adjust = vmg->middle;
- /* The POSITIVE value by which we offset vmg->middle->vm_start. */
- adj_start = vmg->end - vmg->middle->vm_start;
-
- /* Note: vma iterator must be pointing to 'start'. */
- vma_iter_config(vmg->vmi, vmg->start, vmg->end);
+ pgoff = adjust->vm_pgoff + PHYS_PFN(vmg->end - adjust->vm_start);
} else if (vmg->__adjust_next_start) {
- /*
- * In this case alone, the VMA we manipulate is vmg->middle, but
- * we ultimately return vmg->next.
- */
- vma = vmg->middle;
adjust = vmg->next;
- /* The NEGATIVE value by which we offset vmg->next->vm_start. */
- adj_start = -(vmg->middle->vm_end - vmg->end);
+ pgoff = adjust->vm_pgoff - PHYS_PFN(adjust->vm_start - vmg->end);
+ } else {
+ return;
+ }
+
+ vma_set_range(adjust, vmg->end, adjust->vm_end, pgoff);
+}
+
+/*
+ * Actually perform the VMA merge operation.
+ *
+ * Returns 0 on success, or an error value on failure.
+ */
+static int commit_merge(struct vma_merge_struct *vmg)
+{
+ struct vm_area_struct *vma;
+ struct vma_prepare vp;
- vma_iter_config(vmg->vmi, vmg->next->vm_start + adj_start,
- vmg->next->vm_end);
+ if (vmg->__adjust_next_start) {
+ /* We manipulate middle and adjust next, which is the target. */
+ vma = vmg->middle;
+ vma_iter_config(vmg->vmi, vmg->end, vmg->next->vm_end);
} else {
vma = vmg->target;
- adjust = NULL;
- adj_start = 0;
-
/* Note: vma iterator must be pointing to 'start'. */
vma_iter_config(vmg->vmi, vmg->start, vmg->end);
}
init_multi_vma_prep(&vp, vma, vmg);
if (vma_iter_prealloc(vmg->vmi, vma))
- return NULL;
+ return -ENOMEM;
vma_prepare(&vp);
- vma_adjust_trans_huge(vma, vmg->start, vmg->end, adj_start);
+ /*
+ * THP pages may need to do additional splits if we increase
+ * middle->vm_start.
+ */
+ vma_adjust_trans_huge(vma, vmg->start, vmg->end,
+ vmg->__adjust_middle_start ? vmg->middle : NULL);
vma_set_range(vma, vmg->start, vmg->end, vmg->pgoff);
-
- if (adj_start) {
- adjust->vm_start += adj_start;
- adjust->vm_pgoff += PHYS_PFN(adj_start);
- }
-
+ vmg_adjust_set_range(vmg);
vma_iter_store(vmg->vmi, vmg->target);
vma_complete(&vp, vmg->vmi, vma->vm_mm);
- return vmg->target;
+ return 0;
}
/* We can only remove VMAs when merging if they do not have a close hook. */
{
struct vm_area_struct *middle = vmg->middle;
struct vm_area_struct *prev = vmg->prev;
- struct vm_area_struct *next, *res;
+ struct vm_area_struct *next;
struct vm_area_struct *anon_dup = NULL;
unsigned long start = vmg->start;
unsigned long end = vmg->end;
vmg->end = next->vm_end;
vmg->pgoff = next->vm_pgoff - pglen;
} else {
- /*
- * We shrink middle and expand next.
- *
- * IMPORTANT: This is the ONLY case where the final
- * merged VMA is NOT vmg->target, but rather vmg->next.
- */
+ /* We shrink middle and expand next. */
vmg->__adjust_next_start = true;
vmg->start = middle->vm_start;
vmg->end = start;
if (err)
goto abort;
- res = commit_merge(vmg);
- if (!res) {
+ err = commit_merge(vmg);
+ if (err) {
+ VM_WARN_ON(err != -ENOMEM);
+
if (anon_dup)
unlink_anon_vmas(anon_dup);
return NULL;
}
- khugepaged_enter_vma(res, vmg->flags);
+ khugepaged_enter_vma(vmg->target, vmg->flags);
vmg->state = VMA_MERGE_SUCCESS;
- return res;
+ return vmg->target;
abort:
vma_iter_set(vmg->vmi, start);
if (remove_next)
vmg->__remove_next = true;
- if (!commit_merge(vmg))
+ if (commit_merge(vmg))
goto nomem;
return 0;
init_vma_prep(&vp, vma);
vma_prepare(&vp);
- vma_adjust_trans_huge(vma, start, end, 0);
+ vma_adjust_trans_huge(vma, start, end, NULL);
vma_iter_clear(vmi);
vma_set_range(vma, start, end, pgoff);