Commit fe3e9cf0 authored by Lorenzo Stoakes's avatar Lorenzo Stoakes Committed by Andrew Morton
Browse files

mm: eliminate adj_start parameter from commit_merge()

Introduce internal vmg->__adjust_middle_start and vmg->__adjust_next_start
merge flags, enabling us to indicate to commit_merge() that we are
performing a merge which either spans only part of vmg->middle, or part of
vmg->next respectively.

In the former instance, we change the start of vmg->middle to match the
attributes of vmg->prev, without spanning all of vmg->middle.

This implies that vmg->prev->vm_end and vmg->middle->vm_start are both
increased to form the new merged VMA (vmg->prev) and the new subsequent
VMA (vmg->middle).

In the latter case, we change the end of vmg->middle to match the
attributes of vmg->next, without spanning all of vmg->next.

This implies that vmg->middle->vm_end and vmg->next->vm_start are both
decreased to form the new merged VMA (vmg->next) and the new prior VMA
(vmg->middle).

Since we now have a stable set of prev, middle, next VMAs threaded through
vmg and with these flags set know what is happening, we can perform the
calculation in commit_merge() instead.

This allows us to drop the confusing adj_start parameter and instead pass
semantic information to commit_merge().

In the latter case the -(middle->vm_end - start) calculation becomes
-(middle->vm-end - vmg->end), however this is correct as vmg->end is set
to the start parameter.

This is because in this case (rather confusingly), we manipulate
vmg->middle, but ultimately return vmg->next, whose range will be
correctly specified.  At this point vmg->start, end is the new range for
the prior VMA rather than the merged one.

This patch has no change in functional behaviour.

Link: https://lkml.kernel.org/r/bcec0cd980b373a5eb02236cb033034ce1effe42.1738326519.git.lorenzo.stoakes@oracle.com


Signed-off-by: default avatarLorenzo Stoakes <lorenzo.stoakes@oracle.com>
Reviewed-by: default avatarVlastimil Babka <vbabka@suse.cz>
Cc: Jann Horn <jannh@google.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 6ab2d9c7
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -267,7 +267,9 @@ void dump_vmg(const struct vma_merge_struct *vmg, const char *reason)
		"uffd_ctx %px\n"
		"anon_name %px\n"
		"state %x\n"
		"just_expand %d __remove_middle %d __remove_next %d\n",
		"just_expand %d\n"
		"__adjust_middle_start %d __adjust_next_start %d\n"
		"__remove_middle %d __remove_next %d\n",
		vmg, vmg->mm, vmg->pgoff,
		vmg->vmi, vmg->vmi ? vma_iter_addr(vmg->vmi) : 0,
		vmg->vmi ? vma_iter_end(vmg->vmi) : 0,
@@ -281,7 +283,9 @@ void dump_vmg(const struct vma_merge_struct *vmg, const char *reason)
#endif
		vmg->anon_name,
		(int)vmg->state,
		vmg->just_expand, vmg->__remove_middle, vmg->__remove_next);
		vmg->just_expand,
		vmg->__adjust_middle_start, vmg->__adjust_next_start,
		vmg->__remove_middle, vmg->__remove_next);

	if (vmg->mm) {
		pr_warn("vmg %px mm:\n", vmg);
+30 −20
Original line number Diff line number Diff line
@@ -632,29 +632,44 @@ void validate_mm(struct mm_struct *mm)
 *
 * On success, returns the merged VMA. Otherwise returns NULL.
 */
static struct vm_area_struct *commit_merge(struct vma_merge_struct *vmg,
		long adj_start)
static struct vm_area_struct *commit_merge(struct vma_merge_struct *vmg)
{
	struct vma_prepare vp;
	struct vm_area_struct *remove = NULL;
	struct vm_area_struct *remove2 = NULL;
	struct vma_prepare vp;
	struct vm_area_struct *adjust = NULL;
	long adj_start;
	bool merge_target;

	/*
	 * If modifying an existing VMA and we don't remove vmg->middle, then we
	 * shrink the adjacent VMA.
	 */
	if (vmg->__adjust_middle_start) {
		adjust = vmg->middle;
		/* The POSITIVE value by which we offset vmg->middle->vm_start. */
		adj_start = vmg->end - vmg->middle->vm_start;
		merge_target = true;
	} else if (vmg->__adjust_next_start) {
		adjust = vmg->next;
		/* The NEGATIVE value by which we offset vmg->next->vm_start. */
		adj_start = -(vmg->middle->vm_end - vmg->end);
		/*
	 * In all cases but that of merge right, shrink next, we write
		 * In all cases but this - merge right, shrink next - we write
		 * vmg->target to the maple tree and return this as the merged VMA.
		 */
	bool merge_target = adj_start >= 0;
		merge_target = false;
	} else {
		adjust = NULL;
		adj_start = 0;
		merge_target = true;
	}

	if (vmg->__remove_middle)
		remove = vmg->middle;
	if (vmg->__remove_next)
		remove2 = vmg->next;

	if (adj_start > 0)
		adjust = vmg->middle;
	else if (adj_start < 0)
		adjust = vmg->next;

	init_multi_vma_prep(&vp, vmg->target, adjust, remove, remove2);

	VM_WARN_ON(vp.anon_vma && adjust && adjust->anon_vma &&
@@ -738,7 +753,6 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
	bool left_side = middle && start == middle->vm_start;
	bool right_side = middle && end == middle->vm_end;
	int err = 0;
	long adj_start = 0;
	bool merge_left, merge_right, merge_both;

	mmap_assert_write_locked(vmg->mm);
@@ -858,11 +872,8 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
		vmg->start = prev->vm_start;
		vmg->pgoff = prev->vm_pgoff;

		/*
		 * We both expand prev and shrink middle.
		 */
		if (!vmg->__remove_middle)
			adj_start = vmg->end - middle->vm_start;
			vmg->__adjust_middle_start = true;

		err = dup_anon_vma(prev, middle, &anon_dup);
	} else { /* merge_right */
@@ -891,12 +902,11 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
			 * IMPORTANT: This is the ONLY case where the final
			 * merged VMA is NOT vmg->target, but rather vmg->next.
			 */
			vmg->__adjust_next_start = true;
			vmg->target = middle;
			vmg->start = middle->vm_start;
			vmg->end = start;
			vmg->pgoff = middle->vm_pgoff;

			adj_start = -(middle->vm_end - start);
		}

		err = dup_anon_vma(next, middle, &anon_dup);
@@ -905,7 +915,7 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
	if (err)
		goto abort;

	res = commit_merge(vmg, adj_start);
	res = commit_merge(vmg);
	if (!res) {
		if (anon_dup)
			unlink_anon_vmas(anon_dup);
@@ -1079,7 +1089,7 @@ int vma_expand(struct vma_merge_struct *vmg)
	if (remove_next)
		vmg->__remove_next = true;

	if (!commit_merge(vmg, 0))
	if (!commit_merge(vmg))
		goto nomem;

	return 0;
+10 −0
Original line number Diff line number Diff line
@@ -120,6 +120,16 @@ struct vma_merge_struct {

	/* Internal flags set during merge process: */

	/*
	 * Internal flag indicating the merge increases vmg->middle->vm_start
	 * (and thereby, vmg->prev->vm_end).
	 */
	bool __adjust_middle_start :1;
	/*
	 * Internal flag indicating the merge decreases vmg->next->vm_start
	 * (and thereby, vmg->middle->vm_end).
	 */
	bool __adjust_next_start :1;
	/*
	 * Internal flag used during the merge operation to indicate we will
	 * remove vmg->middle.
+2 −0
Original line number Diff line number Diff line
@@ -159,6 +159,8 @@ static void vmg_set_range(struct vma_merge_struct *vmg, unsigned long start,
	vmg->just_expand = false;
	vmg->__remove_middle = false;
	vmg->__remove_next = false;
	vmg->__adjust_middle_start = false;
	vmg->__adjust_next_start = false;
}

/*