[PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move

Lorenzo Stoakes posted 2 patches 2 months ago
There is a newer version of this series
[PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Lorenzo Stoakes 2 months ago
Document the new behaviour introduced in Linux 6.17 whereby it is now
possible to move multiple mappings in a single operation, as long as the
operation is purely a move, that is old_size is equal to new_size and
MREMAP_FIXED is specified.

To make things clearer, also describe this 'pure move' operation, before
expanding upon it to describe the newly introduced behaviour.

This change also explains the limitations of of this method and the
possibility of partial failure.

Finally, we pluralise language where it makes sense to so the documentation
does not contradict either this new capability nor the pre-existing edge
case.

Example code is enclosed below demonstrating the behaviour which is now
possible:

int main(void)
{
	unsigned long page_size = sysconf(_SC_PAGESIZE);
	void *ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
			 MAP_ANON | MAP_PRIVATE, -1, 0);
	void *tgt_ptr = mmap(NULL, 10 * page_size, PROT_NONE,
			     MAP_ANON | MAP_PRIVATE, -1, 0);
	int i;

	if (ptr == MAP_FAILED || tgt_ptr == MAP_FAILED) {
		perror("mmap");
		return EXIT_FAILURE;
	}

	/* Unmap every other page. */
	for (i = 1; i < 10; i += 2)
		munmap(ptr + i * page_size, page_size);

	/* Now move all 5 distinct mappings to tgt_ptr. */
	ptr = mremap(ptr, 10 * page_size, 10 * page_size,
		     MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
	if (ptr == MAP_FAILED) {
		perror("mremap");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
 man/man2/mremap.2 | 84 ++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 73 insertions(+), 11 deletions(-)

diff --git a/man/man2/mremap.2 b/man/man2/mremap.2
index 2168ca728..6ba51310c 100644
--- a/man/man2/mremap.2
+++ b/man/man2/mremap.2
@@ -25,18 +25,47 @@ moving it at the same time (controlled by the
 argument and
 the available virtual address space).
 .P
+Mappings can also simply be moved
+(without any resizing)
+by specifying equal
+.I old_size
+and
+.I new_size
+and using the
+.B MREMAP_FIXED
+flag
+(see below).
+Since Linux 6.17,
+while
+.I old_address
+must reside within a mapping,
+.I old_size
+may span multiple mappings
+which do not have to be
+adjacent to one another when
+performing a move like this.
+The
+.B MREMAP_DONTUNMAP
+flag may be specified.
+.P
+If the operation is not
+simply moving mappings,
+then
+.I old_size
+must span only a single mapping.
+.P
 .I old_address
-is the old address of the virtual memory block that you
-want to expand (or shrink).
+is the old address of the first virtual memory block that you
+want to expand, shrink, and/or move.
 Note that
 .I old_address
 has to be page aligned.
 .I old_size
-is the old size of the
-virtual memory block.
+is the size of the range containing
+virtual memory blocks to be manipulated.
 .I new_size
 is the requested size of the
-virtual memory block after the resize.
+virtual memory blocks after the resize.
 An optional fifth argument,
 .IR new_address ,
 may be provided; see the description of
@@ -105,13 +134,43 @@ If
 is specified, then
 .B MREMAP_MAYMOVE
 must also be specified.
+.IP
+Since Linux 6.17,
+if
+.I old_size
+is equal to
+.I new_size
+and
+.B MREMAP_FIXED
+is specified, then
+.I old_size
+may span beyond the mapping in which
+.I old_address
+resides.
+In this case,
+gaps between mappings in the original range
+are maintained in the new range.
+The whole operation is performed atomically
+unless an error arises,
+in which case the operation may be partially
+completed,
+that is,
+some mappings may be moved and others not.
+.IP
+
+Moving multiple mappings is not permitted if
+any of those mappings have either
+been registered with
+.BR userfaultfd (2) ,
+or map drivers that
+specify their own custom address mapping logic.
 .TP
 .BR MREMAP_DONTUNMAP " (since Linux 5.7)"
 .\" commit e346b3813067d4b17383f975f197a9aa28a3b077
 This flag, which must be used in conjunction with
 .BR MREMAP_MAYMOVE ,
-remaps a mapping to a new address but does not unmap the mapping at
-.IR old_address .
+remaps mappings to a new address but does not unmap them
+from their original address.
 .IP
 The
 .B MREMAP_DONTUNMAP
@@ -149,13 +208,13 @@ mapped.
 See NOTES for some possible applications of
 .BR MREMAP_DONTUNMAP .
 .P
-If the memory segment specified by
+If the memory segments specified by
 .I old_address
 and
 .I old_size
-is locked (using
+are locked (using
 .BR mlock (2)
-or similar), then this lock is maintained when the segment is
+or similar), then this lock is maintained when the segments are
 resized and/or relocated.
 As a consequence, the amount of memory locked by the process may change.
 .SH RETURN VALUE
@@ -188,7 +247,10 @@ virtual memory address for this process.
 You can also get
 .B EFAULT
 even if there exist mappings that cover the
-whole address space requested, but those mappings are of different types.
+whole address space requested, but those mappings are of different types,
+and the
+.BR mremap ()
+operation being performed does not support this.
 .TP
 .B EINVAL
 An invalid argument was given.
-- 
2.50.1
Re: [PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Alejandro Colomar 1 month, 3 weeks ago
Hi Lorenzo,

On Tue, Aug 05, 2025 at 06:31:55PM +0100, Lorenzo Stoakes wrote:
> Document the new behaviour introduced in Linux 6.17 whereby it is now
> possible to move multiple mappings in a single operation, as long as the
> operation is purely a move, that is old_size is equal to new_size and
> MREMAP_FIXED is specified.
> 
> To make things clearer, also describe this 'pure move' operation, before
> expanding upon it to describe the newly introduced behaviour.

Can we split this further into two commits?

> 
> This change also explains the limitations of of this method and the
> possibility of partial failure.
> 
> Finally, we pluralise language where it makes sense to so the documentation
> does not contradict either this new capability nor the pre-existing edge
> case.
> 
> Example code is enclosed below demonstrating the behaviour which is now
> possible:
> 
> int main(void)
> {
> 	unsigned long page_size = sysconf(_SC_PAGESIZE);
> 	void *ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
> 			 MAP_ANON | MAP_PRIVATE, -1, 0);
> 	void *tgt_ptr = mmap(NULL, 10 * page_size, PROT_NONE,
> 			     MAP_ANON | MAP_PRIVATE, -1, 0);
> 	int i;
> 
> 	if (ptr == MAP_FAILED || tgt_ptr == MAP_FAILED) {
> 		perror("mmap");
> 		return EXIT_FAILURE;
> 	}
> 
> 	/* Unmap every other page. */
> 	for (i = 1; i < 10; i += 2)
> 		munmap(ptr + i * page_size, page_size);
> 
> 	/* Now move all 5 distinct mappings to tgt_ptr. */
> 	ptr = mremap(ptr, 10 * page_size, 10 * page_size,
> 		     MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
> 	if (ptr == MAP_FAILED) {
> 		perror("mremap");
> 		return EXIT_FAILURE;
> 	}
> 
> 	return EXIT_SUCCESS;
> }
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> ---
>  man/man2/mremap.2 | 84 ++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 73 insertions(+), 11 deletions(-)
> 
> diff --git a/man/man2/mremap.2 b/man/man2/mremap.2
> index 2168ca728..6ba51310c 100644
> --- a/man/man2/mremap.2
> +++ b/man/man2/mremap.2
> @@ -25,18 +25,47 @@ moving it at the same time (controlled by the
>  argument and
>  the available virtual address space).
>  .P
> +Mappings can also simply be moved
> +(without any resizing)
> +by specifying equal
> +.I old_size
> +and
> +.I new_size
> +and using the
> +.B MREMAP_FIXED
> +flag
> +(see below).
> +Since Linux 6.17,
> +while
> +.I old_address
> +must reside within a mapping,

I don't understand this.  What does it mean that old_address must reside
within a mapping?  It's a point, not a size, so I'm not sure I
understand it.

> +.I old_size
> +may span multiple mappings
> +which do not have to be
> +adjacent to one another when
> +performing a move like this.
> +The
> +.B MREMAP_DONTUNMAP
> +flag may be specified.
> +.P
> +If the operation is not
> +simply moving mappings,
> +then
> +.I old_size
> +must span only a single mapping.
> +.P
>  .I old_address
> -is the old address of the virtual memory block that you
> -want to expand (or shrink).
> +is the old address of the first virtual memory block that you
> +want to expand, shrink, and/or move.
>  Note that
>  .I old_address
>  has to be page aligned.
>  .I old_size
> -is the old size of the
> -virtual memory block.
> +is the size of the range containing
> +virtual memory blocks to be manipulated.
>  .I new_size
>  is the requested size of the
> -virtual memory block after the resize.
> +virtual memory blocks after the resize.
>  An optional fifth argument,
>  .IR new_address ,
>  may be provided; see the description of
> @@ -105,13 +134,43 @@ If
>  is specified, then
>  .B MREMAP_MAYMOVE
>  must also be specified.
> +.IP
> +Since Linux 6.17,
> +if
> +.I old_size
> +is equal to
> +.I new_size
> +and
> +.B MREMAP_FIXED
> +is specified, then
> +.I old_size
> +may span beyond the mapping in which
> +.I old_address
> +resides.
> +In this case,
> +gaps between mappings in the original range
> +are maintained in the new range.
> +The whole operation is performed atomically
> +unless an error arises,
> +in which case the operation may be partially
> +completed,
> +that is,
> +some mappings may be moved and others not.
> +.IP
> +

Why this blank?


Cheers,
Alex

> +Moving multiple mappings is not permitted if
> +any of those mappings have either
> +been registered with
> +.BR userfaultfd (2) ,
> +or map drivers that
> +specify their own custom address mapping logic.
>  .TP
>  .BR MREMAP_DONTUNMAP " (since Linux 5.7)"
>  .\" commit e346b3813067d4b17383f975f197a9aa28a3b077
>  This flag, which must be used in conjunction with
>  .BR MREMAP_MAYMOVE ,
> -remaps a mapping to a new address but does not unmap the mapping at
> -.IR old_address .
> +remaps mappings to a new address but does not unmap them
> +from their original address.
>  .IP
>  The
>  .B MREMAP_DONTUNMAP
> @@ -149,13 +208,13 @@ mapped.
>  See NOTES for some possible applications of
>  .BR MREMAP_DONTUNMAP .
>  .P
> -If the memory segment specified by
> +If the memory segments specified by
>  .I old_address
>  and
>  .I old_size
> -is locked (using
> +are locked (using
>  .BR mlock (2)
> -or similar), then this lock is maintained when the segment is
> +or similar), then this lock is maintained when the segments are
>  resized and/or relocated.
>  As a consequence, the amount of memory locked by the process may change.
>  .SH RETURN VALUE
> @@ -188,7 +247,10 @@ virtual memory address for this process.
>  You can also get
>  .B EFAULT
>  even if there exist mappings that cover the
> -whole address space requested, but those mappings are of different types.
> +whole address space requested, but those mappings are of different types,
> +and the
> +.BR mremap ()
> +operation being performed does not support this.
>  .TP
>  .B EINVAL
>  An invalid argument was given.
> -- 
> 2.50.1
> 

-- 
<https://www.alejandro-colomar.es/>
Re: [PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Lorenzo Stoakes 1 month, 3 weeks ago
On Sat, Aug 09, 2025 at 04:25:18PM +0200, Alejandro Colomar wrote:
> Hi Lorenzo,
>
> On Tue, Aug 05, 2025 at 06:31:55PM +0100, Lorenzo Stoakes wrote:
> > Document the new behaviour introduced in Linux 6.17 whereby it is now
> > possible to move multiple mappings in a single operation, as long as the
> > operation is purely a move, that is old_size is equal to new_size and
> > MREMAP_FIXED is specified.
> >
> > To make things clearer, also describe this 'pure move' operation, before
> > expanding upon it to describe the newly introduced behaviour.
>
> Can we split this further into two commits?

OK can do.

>
> >
> > This change also explains the limitations of of this method and the
> > possibility of partial failure.
> >
> > Finally, we pluralise language where it makes sense to so the documentation
> > does not contradict either this new capability nor the pre-existing edge
> > case.
> >
> > Example code is enclosed below demonstrating the behaviour which is now
> > possible:
> >
> > int main(void)
> > {
> > 	unsigned long page_size = sysconf(_SC_PAGESIZE);
> > 	void *ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE,
> > 			 MAP_ANON | MAP_PRIVATE, -1, 0);
> > 	void *tgt_ptr = mmap(NULL, 10 * page_size, PROT_NONE,
> > 			     MAP_ANON | MAP_PRIVATE, -1, 0);
> > 	int i;
> >
> > 	if (ptr == MAP_FAILED || tgt_ptr == MAP_FAILED) {
> > 		perror("mmap");
> > 		return EXIT_FAILURE;
> > 	}
> >
> > 	/* Unmap every other page. */
> > 	for (i = 1; i < 10; i += 2)
> > 		munmap(ptr + i * page_size, page_size);
> >
> > 	/* Now move all 5 distinct mappings to tgt_ptr. */
> > 	ptr = mremap(ptr, 10 * page_size, 10 * page_size,
> > 		     MREMAP_MAYMOVE | MREMAP_FIXED, tgt_ptr);
> > 	if (ptr == MAP_FAILED) {
> > 		perror("mremap");
> > 		return EXIT_FAILURE;
> > 	}
> >
> > 	return EXIT_SUCCESS;
> > }
> >
> > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> > ---
> >  man/man2/mremap.2 | 84 ++++++++++++++++++++++++++++++++++++++++-------
> >  1 file changed, 73 insertions(+), 11 deletions(-)
> >
> > diff --git a/man/man2/mremap.2 b/man/man2/mremap.2
> > index 2168ca728..6ba51310c 100644
> > --- a/man/man2/mremap.2
> > +++ b/man/man2/mremap.2
> > @@ -25,18 +25,47 @@ moving it at the same time (controlled by the
> >  argument and
> >  the available virtual address space).
> >  .P
> > +Mappings can also simply be moved
> > +(without any resizing)
> > +by specifying equal
> > +.I old_size
> > +and
> > +.I new_size
> > +and using the
> > +.B MREMAP_FIXED
> > +flag
> > +(see below).
> > +Since Linux 6.17,
> > +while
> > +.I old_address
> > +must reside within a mapping,
>
> I don't understand this.  What does it mean that old_address must reside
> within a mapping?  It's a point, not a size, so I'm not sure I
> understand it.

I think if it were a size it would be more confusing no?

It's an address, the address must be located within an existing memory mapping.

Will replace with 'located' for clarity.

>
> > +.I old_size
> > +may span multiple mappings
> > +which do not have to be
> > +adjacent to one another when
> > +performing a move like this.
> > +The
> > +.B MREMAP_DONTUNMAP
> > +flag may be specified.
> > +.P
> > +If the operation is not
> > +simply moving mappings,
> > +then
> > +.I old_size
> > +must span only a single mapping.
> > +.P
> >  .I old_address
> > -is the old address of the virtual memory block that you
> > -want to expand (or shrink).
> > +is the old address of the first virtual memory block that you
> > +want to expand, shrink, and/or move.
> >  Note that
> >  .I old_address
> >  has to be page aligned.
> >  .I old_size
> > -is the old size of the
> > -virtual memory block.
> > +is the size of the range containing
> > +virtual memory blocks to be manipulated.
> >  .I new_size
> >  is the requested size of the
> > -virtual memory block after the resize.
> > +virtual memory blocks after the resize.
> >  An optional fifth argument,
> >  .IR new_address ,
> >  may be provided; see the description of
> > @@ -105,13 +134,43 @@ If
> >  is specified, then
> >  .B MREMAP_MAYMOVE
> >  must also be specified.
> > +.IP
> > +Since Linux 6.17,
> > +if
> > +.I old_size
> > +is equal to
> > +.I new_size
> > +and
> > +.B MREMAP_FIXED
> > +is specified, then
> > +.I old_size
> > +may span beyond the mapping in which
> > +.I old_address
> > +resides.
> > +In this case,
> > +gaps between mappings in the original range
> > +are maintained in the new range.
> > +The whole operation is performed atomically
> > +unless an error arises,
> > +in which case the operation may be partially
> > +completed,
> > +that is,
> > +some mappings may be moved and others not.
> > +.IP
> > +
>
> Why this blank?

Oops that was a mistake, will fix on respin.

>
>
> Cheers,
> Alex
>
> > +Moving multiple mappings is not permitted if
> > +any of those mappings have either
> > +been registered with
> > +.BR userfaultfd (2) ,
> > +or map drivers that
> > +specify their own custom address mapping logic.
> >  .TP
> >  .BR MREMAP_DONTUNMAP " (since Linux 5.7)"
> >  .\" commit e346b3813067d4b17383f975f197a9aa28a3b077
> >  This flag, which must be used in conjunction with
> >  .BR MREMAP_MAYMOVE ,
> > -remaps a mapping to a new address but does not unmap the mapping at
> > -.IR old_address .
> > +remaps mappings to a new address but does not unmap them
> > +from their original address.
> >  .IP
> >  The
> >  .B MREMAP_DONTUNMAP
> > @@ -149,13 +208,13 @@ mapped.
> >  See NOTES for some possible applications of
> >  .BR MREMAP_DONTUNMAP .
> >  .P
> > -If the memory segment specified by
> > +If the memory segments specified by
> >  .I old_address
> >  and
> >  .I old_size
> > -is locked (using
> > +are locked (using
> >  .BR mlock (2)
> > -or similar), then this lock is maintained when the segment is
> > +or similar), then this lock is maintained when the segments are
> >  resized and/or relocated.
> >  As a consequence, the amount of memory locked by the process may change.
> >  .SH RETURN VALUE
> > @@ -188,7 +247,10 @@ virtual memory address for this process.
> >  You can also get
> >  .B EFAULT
> >  even if there exist mappings that cover the
> > -whole address space requested, but those mappings are of different types.
> > +whole address space requested, but those mappings are of different types,
> > +and the
> > +.BR mremap ()
> > +operation being performed does not support this.
> >  .TP
> >  .B EINVAL
> >  An invalid argument was given.
> > --
> > 2.50.1
> >
>
> --
> <https://www.alejandro-colomar.es/>
Re: [PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Alejandro Colomar 1 month, 3 weeks ago
Hi Lorenzo,

On Mon, Aug 11, 2025 at 06:30:38AM +0100, Lorenzo Stoakes wrote:
> > > +Mappings can also simply be moved
> > > +(without any resizing)
> > > +by specifying equal
> > > +.I old_size
> > > +and
> > > +.I new_size
> > > +and using the
> > > +.B MREMAP_FIXED
> > > +flag
> > > +(see below).
> > > +Since Linux 6.17,
> > > +while
> > > +.I old_address
> > > +must reside within a mapping,
> >
> > I don't understand this.  What does it mean that old_address must reside
> > within a mapping?  It's a point, not a size, so I'm not sure I
> > understand it.
> 
> I think if it were a size it would be more confusing no?
> 
> It's an address, the address must be located within an existing memory mapping.

What I don't understand is: how could you not comply with that?  Could
you pass some old_address that is in two mappings?  Being a single
address, that would be impossible, right?

> Will replace with 'located' for clarity.
> 
> >
> > > +.I old_size
> > > +may span multiple mappings
> > > +which do not have to be
> > > +adjacent to one another when
> > > +performing a move like this.


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es/>
Re: [PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Lorenzo Stoakes 1 month, 3 weeks ago
On Mon, Aug 11, 2025 at 11:20:05AM +0200, Alejandro Colomar wrote:
> Hi Lorenzo,
>
> On Mon, Aug 11, 2025 at 06:30:38AM +0100, Lorenzo Stoakes wrote:
> > > > +Mappings can also simply be moved
> > > > +(without any resizing)
> > > > +by specifying equal
> > > > +.I old_size
> > > > +and
> > > > +.I new_size
> > > > +and using the
> > > > +.B MREMAP_FIXED
> > > > +flag
> > > > +(see below).
> > > > +Since Linux 6.17,
> > > > +while
> > > > +.I old_address
> > > > +must reside within a mapping,
> > >
> > > I don't understand this.  What does it mean that old_address must reside
> > > within a mapping?  It's a point, not a size, so I'm not sure I
> > > understand it.
> >
> > I think if it were a size it would be more confusing no?
> >
> > It's an address, the address must be located within an existing memory mapping.
>
> What I don't understand is: how could you not comply with that?  Could
> you pass some old_address that is in two mappings?  Being a single
> address, that would be impossible, right?

It can be in an unmapped area. It's either in an unmapped area or a mapped one.

I could simply reword this as 'old_address must be mapped'?

>
> > Will replace with 'located' for clarity.
> >
> > >
> > > > +.I old_size
> > > > +may span multiple mappings
> > > > +which do not have to be
> > > > +adjacent to one another when
> > > > +performing a move like this.
>
>
> Have a lovely day!
> Alex
>
> --
> <https://www.alejandro-colomar.es/>
Re: [PATCH v4 1/2] man/man2/mremap.2: describe multiple mapping move
Posted by Alejandro Colomar 1 month, 3 weeks ago
On Mon, Aug 11, 2025 at 10:25:56AM +0100, Lorenzo Stoakes wrote:
> On Mon, Aug 11, 2025 at 11:20:05AM +0200, Alejandro Colomar wrote:
> > Hi Lorenzo,
> >
> > On Mon, Aug 11, 2025 at 06:30:38AM +0100, Lorenzo Stoakes wrote:
> > > > > +Mappings can also simply be moved
> > > > > +(without any resizing)
> > > > > +by specifying equal
> > > > > +.I old_size
> > > > > +and
> > > > > +.I new_size
> > > > > +and using the
> > > > > +.B MREMAP_FIXED
> > > > > +flag
> > > > > +(see below).
> > > > > +Since Linux 6.17,
> > > > > +while
> > > > > +.I old_address
> > > > > +must reside within a mapping,
> > > >
> > > > I don't understand this.  What does it mean that old_address must reside
> > > > within a mapping?  It's a point, not a size, so I'm not sure I
> > > > understand it.
> > >
> > > I think if it were a size it would be more confusing no?
> > >
> > > It's an address, the address must be located within an existing memory mapping.
> >
> > What I don't understand is: how could you not comply with that?  Could
> > you pass some old_address that is in two mappings?  Being a single
> > address, that would be impossible, right?
> 
> It can be in an unmapped area. It's either in an unmapped area or a mapped one.
> 
> I could simply reword this as 'old_address must be mapped'?

Yup, that seems better.  Thanks!

Cheers,
Alex

> > > Will replace with 'located' for clarity.
> > >
> > > >
> > > > > +.I old_size
> > > > > +may span multiple mappings
> > > > > +which do not have to be
> > > > > +adjacent to one another when
> > > > > +performing a move like this.
> >
> >
> > Have a lovely day!
> > Alex
> >
> > --
> > <https://www.alejandro-colomar.es/>

-- 
<https://www.alejandro-colomar.es/>