[PATCH 3/3] mm: shmem: use mapping_set_frozen() for memfd preservation
Pratyush Yadav
pratyush at kernel.org
Tue Oct 27 10:30:28 PDT 2026
From: "Pratyush Yadav (Google)" <pratyush at kernel.org>
memfd preservation via LUO freezes the shmem mapping with
SHMEM_F_MAPPING_FROZEN. That flag only blocks size changes and
fallocate(), so other paths can still change the page cache of a
preserved memfd. memfd_luo avoids it by pinning all folios and faulting
in all the holes.
The page cache now natively supports freezing mappings. Switch over the
code to use that instead.
Disallow any type of truncation on frozen mappings, even one with same
size, since it might drop fallocated folios past EOF, and would fail in
shmem_undo_range() with a warning.
memfd_luo_preserve() now freezes the mapping after grabbing the folios,
instead of before. The grabbing fills holes in the file and may migrate
folios, both of which a frozen mapping refuses. The inode lock is held
throughout, so truncate, fallocate and writes can't change the file in
between.
Signed-off-by: Pratyush Yadav (Google) <pratyush at kernel.org>
---
include/linux/shmem_fs.h | 17 -----------------
mm/memfd_luo.c | 11 ++++++++---
mm/shmem.c | 15 +++++++--------
3 files changed, 15 insertions(+), 28 deletions(-)
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index a7c7a96a7cbf9..5b48598a7b952 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -23,14 +23,6 @@
#define SHMEM_F_NORESERVE BIT(0)
/* Disallow swapping. */
#define SHMEM_F_LOCKED BIT(1)
-/*
- * Disallow growing, shrinking, or hole punching in the inode. Combined with
- * folio pinning, makes sure the inode's mapping stays fixed.
- *
- * In some ways similar to F_SEAL_GROW | F_SEAL_SHRINK, but can be removed and
- * isn't directly visible to userspace.
- */
-#define SHMEM_F_MAPPING_FROZEN BIT(2)
struct shmem_inode_info {
spinlock_t lock;
@@ -202,15 +194,6 @@ static inline bool shmem_file(struct file *file)
return shmem_mapping(file->f_mapping);
}
-/* Must be called with inode lock taken exclusive. */
-static inline void shmem_freeze(struct inode *inode, bool freeze)
-{
- if (freeze)
- SHMEM_I(inode)->flags |= SHMEM_F_MAPPING_FROZEN;
- else
- SHMEM_I(inode)->flags &= ~SHMEM_F_MAPPING_FROZEN;
-}
-
/*
* If fallocate(FALLOC_FL_KEEP_SIZE) has been used, there may be pages
* beyond i_size's notion of EOF, which fallocate has committed to reserving:
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index fe182d5057890..ec5abd41ecb8b 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -84,6 +84,7 @@
#include <linux/swap.h>
#include <linux/vmalloc.h>
#include <linux/memfd.h>
+#include <linux/pagemap.h>
#include <uapi/linux/memfd.h>
#include "internal.h"
@@ -323,7 +324,6 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
int err = 0, seals;
inode_lock(inode);
- shmem_freeze(inode, true);
/* Allocate the main serialization structure in preserved memory */
ser = kho_alloc_preserve(sizeof(*ser));
@@ -355,6 +355,12 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
if (err)
goto err_free_ser;
+ /*
+ * All the folios in the file are now stable. Freeze the mapping to make
+ * sure new ones can't be added.
+ */
+ mapping_set_frozen(inode->i_mapping);
+
ser->nr_folios = nr_folios;
inode_unlock(inode);
@@ -366,7 +372,6 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
err_free_ser:
kho_unpreserve_free(ser);
err_unlock:
- shmem_freeze(inode, false);
inode_unlock(inode);
return err;
}
@@ -398,7 +403,7 @@ static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args)
return;
inode_lock(inode);
- shmem_freeze(inode, false);
+ mapping_clear_frozen(inode->i_mapping);
ser = phys_to_virt(args->serialized_data);
diff --git a/mm/shmem.c b/mm/shmem.c
index 6507b4246c8af..1c70fd306bec2 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1570,8 +1570,6 @@ static int shmem_setattr(struct mnt_idmap *idmap,
return -EPERM;
if (newsize != oldsize) {
- if (info->flags & SHMEM_F_MAPPING_FROZEN)
- return -EPERM;
error = shmem_reacct_size(SHMEM_I(inode)->flags,
oldsize, newsize);
if (error)
@@ -1583,6 +1581,11 @@ static int shmem_setattr(struct mnt_idmap *idmap,
}
if (newsize <= oldsize) {
loff_t holebegin = round_up(newsize, PAGE_SIZE);
+
+ /* No truncation allowed on a frozen mapping. */
+ if (mapping_frozen(inode->i_mapping))
+ return -EBUSY;
+
if (oldsize > holebegin)
unmap_mapping_range(inode->i_mapping,
holebegin, 0, 1);
@@ -3482,10 +3485,6 @@ shmem_write_begin(const struct kiocb *iocb, struct address_space *mapping,
return -EPERM;
}
- if (unlikely((info->flags & SHMEM_F_MAPPING_FROZEN) &&
- pos + len > inode->i_size))
- return -EPERM;
-
ret = shmem_get_folio(inode, index, pos + len, &folio, SGP_WRITE);
if (ret)
return ret;
@@ -3860,8 +3859,8 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
inode_lock(inode);
- if (info->flags & SHMEM_F_MAPPING_FROZEN) {
- error = -EPERM;
+ if (mapping_frozen(inode->i_mapping)) {
+ error = -EBUSY;
goto out;
}
--
2.56.0.307.g5fe7bd680c-goog
More information about the kexec
mailing list