[PATCH 02/11] mm/mremap: add MREMAP_MUST_RELOCATE_ANON

Lorenzo Stoakes
2025-06-09T13:32:50Z

This flag is the same as MREMAP_RELOCATE_ANON, however it returns an
-EFAULT error should folios not be able to be relocated.

The operation is undone when this occurs so the user can choose to proceed
without setting this flag at this stage.

This is useful for cases where a use case absolutely requires mergeability,
or moreover a user needs to know whether it succeeded or not for internal
bookkeeping purposes.

If the move would be a no-op (could be merged, or folios in range are
unmapped), then the operation proceeds normally.

It is only in instances where we would have fallen back to the usual
mremap() logic if we were using MREMAP_RELOCATE_ANON that we return -EFAULT
for MREMAP_MUST_RELOCATE_ANON.

Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
include/uapi/linux/mman.h | 9 +++++----
mm/mremap.c | 35 ++++++++++++++++++++++++++---------
2 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/include/uapi/linux/mman.h b/include/uapi/linux/mman.h
index d0542f872e0c..a61dbe1e8b2b 100644
--- a/include/uapi/linux/mman.h
+++ b/include/uapi/linux/mman.h
@@ -6,10 +6,11 @@
#include <asm-generic/hugetlb_encode.h>
#include <linux/types.h>

-#define MREMAP_MAYMOVE 1
-#define MREMAP_FIXED 2
-#define MREMAP_DONTUNMAP 4
-#define MREMAP_RELOCATE_ANON 8
+#define MREMAP_MAYMOVE 1
+#define MREMAP_FIXED 2
+#define MREMAP_DONTUNMAP 4
+#define MREMAP_RELOCATE_ANON 8
+#define MREMAP_MUST_RELOCATE_ANON 16

#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
diff --git a/mm/mremap.c b/mm/mremap.c
index 2da064f8c898..41158aea8c29 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1385,14 +1385,18 @@ static void unmap_source_vma(struct vma_remap_struct *vrm)
* is being moved to by updating index and mapping fields accordingly?
*/
static bool should_relocate_anon(struct vma_remap_struct *vrm,
- struct pagetable_move_control *pmc)
+ struct pagetable_move_control *pmc, int *errp)
{
struct vm_area_struct *old = vrm->vma;

/* Currently we only do this if requested. */
- if (!(vrm->flags & MREMAP_RELOCATE_ANON))
+ if (!(vrm->flags & (MREMAP_RELOCATE_ANON | MREMAP_MUST_RELOCATE_ANON)))
return false;

+ /* Failures are fatal in the 'must' case. */
+ if (vrm->flags & MREMAP_MUST_RELOCATE_ANON)
+ *errp = -EFAULT;
+
/* We can't deal with special or hugetlb mappings. */
if (old->vm_flags & (VM_SPECIAL | VM_HUGETLB))
return false;
@@ -1401,14 +1405,17 @@ static bool should_relocate_anon(struct vma_remap_struct *vrm,
if (!vma_is_anonymous(old))
return false;

- /* If no folios are mapped, then no need to attempt this. */
- if (!old->anon_vma)
- return false;
-
/* We don't allow relocation of non-exclusive folios. */
if (vma_maybe_has_shared_anon_folios(old))
return false;

+ /* Below issues are non-fatal in 'must' case. */
+ *errp = 0;
+
+ /* If no folios are mapped, then no need to attempt this. */
+ if (!old->anon_vma)
+ return false;
+
/* Otherwise, we're good to go! */
return true;
}
@@ -1460,7 +1467,10 @@ static int copy_vma_and_data(struct vma_remap_struct *vrm,
struct vm_area_struct *new_vma;
int err = 0;
PAGETABLE_MOVE(pmc, NULL, NULL, vrm->addr, vrm->new_addr, vrm->old_len);
- bool relocate_anon = should_relocate_anon(vrm, &pmc);
+ bool relocate_anon = should_relocate_anon(vrm, &pmc, &err);
+
+ if (err)
+ return err;

again:
new_vma = copy_vma(&vma, vrm->new_addr, vrm->new_len, new_pgoff,
@@ -1491,6 +1501,12 @@ static int copy_vma_and_data(struct vma_remap_struct *vrm,

do_munmap(current->mm, start, size, NULL);
relocate_anon = false;
+ if (vrm->flags & MREMAP_MUST_RELOCATE_ANON) {
+ vrm_uncharge(vrm);
+ *new_vma_ptr = NULL;
+ return -EFAULT;
+ }
+
goto again;
}
}
@@ -1840,7 +1856,7 @@ static unsigned long check_mremap_params(struct vma_remap_struct *vrm)

/* Ensure no unexpected flag values. */
if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP |
- MREMAP_RELOCATE_ANON))
+ MREMAP_RELOCATE_ANON | MREMAP_MUST_RELOCATE_ANON))
return -EINVAL;

/* Start address must be page-aligned. */
@@ -1856,7 +1872,8 @@ static unsigned long check_mremap_params(struct vma_remap_struct *vrm)
return -EINVAL;

/* We can't relocate without allowing a move. */
- if ((flags & MREMAP_RELOCATE_ANON) && !(flags & MREMAP_MAYMOVE))
+ if ((flags & (MREMAP_RELOCATE_ANON | MREMAP_MUST_RELOCATE_ANON)) &&
+ !(flags & MREMAP_MAYMOVE))
return -EINVAL;

/* Remainder of checks are for cases with specific new_addr. */
--
2.49.0