Re: [PATCH 3/5] mm: eliminate adj_start parameter from commit_merge()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 1/27/25 16:50, Lorenzo Stoakes wrote:
> Introduce internal __VMG_FLAG_ADJUST_MIDDLE_START and
> __VMG_FLAG_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.
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@xxxxxxxxxx>

Reviewed-by: Vlastimil Babka <vbabka@xxxxxxx>

> ---
>  mm/vma.c | 53 ++++++++++++++++++++++++++++++++---------------------
>  mm/vma.h | 14 ++++++++++++--
>  2 files changed, 44 insertions(+), 23 deletions(-)
> 
> diff --git a/mm/vma.c b/mm/vma.c
> index 955c5ebd5739..e78d65de734b 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -633,29 +633,45 @@ 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;
> +	unsigned long flags = vmg->merge_flags;
> +	struct vma_prepare vp;
>  	struct vm_area_struct *adjust = NULL;
> +	long adj_start;
> +	bool merge_target;
> +
>  	/*
> -	 * In all cases but that of merge right, shrink next, we write
> -	 * vmg->target to the maple tree and return this as the merged VMA.
> +	 * If modifying an existing VMA and we don't remove vmg->middle, then we
> +	 * shrink the adjacent VMA.
>  	 */
> -	bool merge_target = adj_start >= 0;
> +	if (flags & __VMG_FLAG_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 (flags & __VMG_FLAG_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);

Wouldn't it make more sense to use vmg->next->vm_start here, which AFAIU is
the same value as vmg->middle->vm_end, but perhaps a bit more obvious
considering the flag and comment says we're adjusting vmg->next->vm_start.
(somewhat less important if this code is temporary)

> +		/*
> +		 * In all cases but this - merge right, shrink next - we write
> +		 * vmg->target to the maple tree and return this as the merged VMA.
> +		 */
> +		merge_target = false;

That comment reads like it would make sense to init merge_target as true and
only here change it to false.

> +	} else {
> +		adjust = NULL;
> +		adj_start = 0;
> +		merge_target = true;
> +	}

I guess all these could be initial assignments and we don't need the else
branch at all.

> -	if (vmg->merge_flags & __VMG_FLAG_REMOVE_MIDDLE)
> +	if (flags & __VMG_FLAG_REMOVE_MIDDLE)
>  		remove = vmg->middle;
>  	if (vmg->merge_flags & __VMG_FLAG_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 &&
> @@ -739,7 +755,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_will_delete_middle, merge_will_delete_next;
>  	bool merge_left, merge_right, merge_both;
>  
> @@ -860,11 +875,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 (!merge_will_delete_middle)
> -			adj_start = vmg->end - middle->vm_start;
> +			vmg->merge_flags |= __VMG_FLAG_ADJUST_MIDDLE_START;
>  
>  		err = dup_anon_vma(prev, middle, &anon_dup);
>  	} else { /* merge_right */
> @@ -893,12 +905,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->merge_flags |= __VMG_FLAG_ADJUST_NEXT_START;
>  			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);
> @@ -912,7 +923,7 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
>  	if (merge_will_delete_next)
>  		vmg->merge_flags |= __VMG_FLAG_REMOVE_NEXT;
>  
> -	res = commit_merge(vmg, adj_start);
> +	res = commit_merge(vmg);
>  	if (!res) {
>  		if (anon_dup)
>  			unlink_anon_vmas(anon_dup);
> @@ -1087,7 +1098,7 @@ int vma_expand(struct vma_merge_struct *vmg)
>  	if (remove_next)
>  		vmg->merge_flags |= __VMG_FLAG_REMOVE_NEXT;
>  
> -	if (!commit_merge(vmg, 0))
> +	if (!commit_merge(vmg))
>  		goto nomem;
>  
>  	return 0;
> diff --git a/mm/vma.h b/mm/vma.h
> index ffbfefb9a83d..ddf567359880 100644
> --- a/mm/vma.h
> +++ b/mm/vma.h
> @@ -67,16 +67,26 @@ enum vma_merge_flags {
>  	 * at the gap.
>  	 */
>  	VMG_FLAG_JUST_EXPAND = 1 << 0,
> +	/*
> +	 * Internal flag indicating the merge increases vmg->middle->vm_start
> +	 * (and thereby, vmg->prev->vm_end).
> +	 */
> +	__VMG_FLAG_ADJUST_MIDDLE_START = 1 << 1,
> +	/*
> +	 * Internal flag indicating the merge decreases vmg->next->vm_start
> +	 * (and thereby, vmg->middle->vm_end).
> +	 */
> +	__VMG_FLAG_ADJUST_NEXT_START = 1 << 2,
>  	/*
>  	 * Internal flag used during the merge operation to indicate we will
>  	 * remove vmg->middle.
>  	 */
> -	__VMG_FLAG_REMOVE_MIDDLE = 1 << 1,
> +	__VMG_FLAG_REMOVE_MIDDLE = 1 << 3,
>  	/*
>  	 * Internal flag used during the merge operationr to indicate we will
>  	 * remove vmg->next.
>  	 */
> -	__VMG_FLAG_REMOVE_NEXT = 1 << 2,
> +	__VMG_FLAG_REMOVE_NEXT = 1 << 4,
>  };
>  
>  /*





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux