* [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD
@ 2026-09-01 18:07 David Matlack
2026-09-01 18:07 ` [RFC PATCH 1/5] liveupdate: Extract luo_file token lookup logic into helper function David Matlack
` (5 more replies)
0 siblings, 6 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
This series adds support for LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, a new
UAPI to enable preserved files to be restored into a userspace-provided
file instead of a kernel-allocated file, and uses this to support
preserving tmpfs files in-place without any changes to the LUO ABI.
The Live Update Orchestrator (LUO) API currently explicitly returns a
new struct file for every preserved file during retrieve() (e.g. via
memfd_alloc_file()). This then forces userspace to use anonymous memfds
for all in-memory files it wants to preserve. This works reasonably well
for VM guest memory but does not work well for other types of files that
userspace may want to preserve (e.g. in-memory logs, binaries,
configuration files, etc.).
Preserving entire tmpfs mounts in-place would require a large amount of
kernel support and would effectively make tmpfs an ABI, which is a
non-starter (or so I hear). Instead, we can delegate the reconstruction
of the tmpfs mounts (directory structure, permissions, etc.) post-kexec
to userspace. The kernel just needs to preserve the contents of tmpfs
files and provide userspace a mechanism to restore those contents back
into a specific file on the filesystem after the kexec. Hence,
LIVEUPDATE_SESSION_RETRIEVE_INTO_FD.
An alternative approach to restoring data to a named file would be
supporting a zero-copy sendfile() that can be used to convert named
tmpfs files to/from memfds across the kexec. But this poses significant
challenges on the *preserve* side since the preserved file might still
be actively in use. The benefit of the retrieve-into approach introduced
in this series is that it does not require dealing with two different
files. There is only ever one file that owns the preserved memory.
By leaving file and metadata allocation purely in the purview of
userspace, programs can create the target files where they want, with
the names and security permissions they desire, before directing the
kernel to restore the preserved folios into them.
Currently, only tmpfs shmem files are allowed as valid target receptors.
Attempting to target populated files, or anything other than an empty
shmem file, will trigger -EINVAL. HugeTLBfs files could be supported in
the future.
The coding work for this series was done with assistance from Gemini. I
am not sure what is the latest recommendation is with respect to adding
Assisted-by tags, but I can add them when I send out the real patches if
this RFC looks acceptable.
David Matlack (5):
liveupdate: Extract luo_file token lookup logic into helper function
liveupdate: Introduce SESSION_RETRIEVE_INTO_FD API and core support
mm/memfd_luo: Implement retrieve_into callback for shmem folios
mm/memfd_luo: Expand support beyond memfd to all generic shmem inodes
selftests: liveupdate: Add multi-session test coverage for
session_retrieve_into
include/linux/liveupdate.h | 1 +
include/uapi/linux/liveupdate.h | 21 ++++++
kernel/liveupdate/luo_file.c | 66 ++++++++++++++----
kernel/liveupdate/luo_internal.h | 2 +
kernel/liveupdate/luo_session.c | 30 ++++++++
mm/memfd_luo.c | 68 ++++++++++++++++---
.../liveupdate/lib/include/libliveupdate.h | 1 +
.../selftests/liveupdate/lib/lu_utils.c | 33 +++++++++
.../selftests/liveupdate/luo_multi_session.c | 56 +++++++++++++--
9 files changed, 249 insertions(+), 29 deletions(-)
base-commit: 4e1b74c6004371b046338325011a47cd7ab9dcc1
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 1/5] liveupdate: Extract luo_file token lookup logic into helper function
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
@ 2026-09-01 18:07 ` David Matlack
2026-09-01 18:07 ` [RFC PATCH 2/5] liveupdate: Introduce SESSION_RETRIEVE_INTO_FD API and core support David Matlack
` (4 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
Extract the token-based list iteration inside luo_retrieve_file() into a
standalone private helper luo_find_file_by_token().
This eliminates code duplication by paving the way for additional file
retrieval modalities (like retrieving into an existing file descriptor)
which also need to safely lookup file tracking structures from the token
list before bridging payloads.
No functional change intended.
Signed-off-by: David Matlack <dmatlack@google.com>
---
kernel/liveupdate/luo_file.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index c39f96961a85..5e160836a165 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -549,26 +549,32 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
* -ENOENT if no file with the matching token is found.
* Any error code returned by the handler's .retrieve() op.
*/
-int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
- struct file **filep)
+
+static struct luo_file *luo_find_file_by_token(struct luo_file_set *file_set, u64 token)
{
- struct liveupdate_file_op_args args = {0};
struct luo_file *luo_file;
- bool found = false;
- int err;
if (list_empty(&file_set->files_list))
- return -ENOENT;
+ return ERR_PTR(-ENOENT);
list_for_each_entry(luo_file, &file_set->files_list, list) {
- if (luo_file->token == token) {
- found = true;
- break;
- }
+ if (luo_file->token == token)
+ return luo_file;
}
- if (!found)
- return -ENOENT;
+ return ERR_PTR(-ENOENT);
+}
+
+int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
+ struct file **filep)
+{
+ struct liveupdate_file_op_args args = {0};
+ struct luo_file *luo_file;
+ int err;
+
+ luo_file = luo_find_file_by_token(file_set, token);
+ if (IS_ERR(luo_file))
+ return PTR_ERR(luo_file);
guard(mutex)(&luo_file->mutex);
if (luo_file->retrieve_status < 0) {
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 2/5] liveupdate: Introduce SESSION_RETRIEVE_INTO_FD API and core support
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
2026-09-01 18:07 ` [RFC PATCH 1/5] liveupdate: Extract luo_file token lookup logic into helper function David Matlack
@ 2026-09-01 18:07 ` David Matlack
2026-09-01 18:07 ` [RFC PATCH 3/5] mm/memfd_luo: Implement retrieve_into callback for shmem folios David Matlack
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
Define the LIVEUPDATE_CMD_SESSION_RETRIEVE_INTO_FD ioctl in the UAPI and
implement the core support into LUO sessions. Add a new .retrieve_into()
callback definition to struct liveupdate_file_handler but do not yet
implement it for any file types.
Delegating VFS topology recreation to userspace across Live Updates
requires an interface where userspace can provide an empty target file
descriptor, and the kernel can inject preserved memory into it. This API
supplies that missing routing. It uses the new luo_retrieve_into_file()
helper to safely look up tracking structures without duplicating backend
locks.
Signed-off-by: David Matlack <dmatlack@google.com>
---
include/linux/liveupdate.h | 1 +
include/uapi/linux/liveupdate.h | 21 +++++++++++++++++++
kernel/liveupdate/luo_file.c | 36 ++++++++++++++++++++++++++++++++
kernel/liveupdate/luo_internal.h | 2 ++
kernel/liveupdate/luo_session.c | 30 ++++++++++++++++++++++++++
5 files changed, 90 insertions(+)
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index 63ea5417de84..7b595e0f53bc 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -79,6 +79,7 @@ struct liveupdate_file_ops {
int (*freeze)(struct liveupdate_file_op_args *args);
void (*unfreeze)(struct liveupdate_file_op_args *args);
int (*retrieve)(struct liveupdate_file_op_args *args);
+ int (*retrieve_into)(struct liveupdate_file_op_args *args, struct file *target_file);
bool (*can_finish)(struct liveupdate_file_op_args *args);
void (*finish)(struct liveupdate_file_op_args *args);
unsigned long (*get_id)(struct file *file);
diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
index 4043d4038712..ed2049529cc0 100644
--- a/include/uapi/linux/liveupdate.h
+++ b/include/uapi/linux/liveupdate.h
@@ -59,7 +59,11 @@ enum {
LIVEUPDATE_CMD_SESSION_PRESERVE_FD = LIVEUPDATE_CMD_SESSION_BASE,
LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41,
LIVEUPDATE_CMD_SESSION_FINISH = 0x42,
+
LIVEUPDATE_CMD_SESSION_GET_NAME = 0x43,
+
+ LIVEUPDATE_CMD_SESSION_RETRIEVE_INTO_FD = 0x44,
+
};
/**
@@ -184,6 +188,23 @@ struct liveupdate_session_retrieve_fd {
#define LIVEUPDATE_SESSION_RETRIEVE_FD \
_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD)
+/**
+ * struct liveupdate_session_retrieve_into_fd - ioctl(LIVEUPDATE_SESSION_RETRIEVE_INTO_FD)
+ * @size: Input; sizeof(struct liveupdate_session_retrieve_into_fd)
+ * @fd: Input; The open file descriptor (e.g. empty tmpfs file) to inject the resource into.
+ * @token: Input; An opaque, token that was used to preserve the resource.
+ *
+ * Retrieve a previously preserved file descriptor into an existing file descriptor.
+ */
+struct liveupdate_session_retrieve_into_fd {
+ __u32 size;
+ __s32 fd;
+ __aligned_u64 token;
+};
+
+#define LIVEUPDATE_SESSION_RETRIEVE_INTO_FD \
+ _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_INTO_FD)
+
/**
* struct liveupdate_session_finish - ioctl(LIVEUPDATE_SESSION_FINISH)
* @size: Input; sizeof(struct liveupdate_session_finish)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index 5e160836a165..c4abfd25e149 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -932,3 +932,39 @@ void liveupdate_unregister_file_handler(struct liveupdate_file_handler *fh)
luo_flb_unregister_all(fh);
list_del(&ACCESS_PRIVATE(fh, list));
}
+
+int luo_retrieve_into_file(struct luo_file_set *file_set, u64 token,
+ struct file *target_file)
+{
+ struct liveupdate_file_op_args args = {0};
+ struct luo_file *luo_file;
+ int err;
+
+ luo_file = luo_find_file_by_token(file_set, token);
+ if (IS_ERR(luo_file))
+ return PTR_ERR(luo_file);
+
+ guard(mutex)(&luo_file->mutex);
+ if (luo_file->retrieve_status < 0)
+ return luo_file->retrieve_status;
+
+ if (luo_file->retrieve_status > 0)
+ return -EBUSY; /* Already retrieved */
+
+ if (!luo_file->fh->ops->retrieve_into)
+ return -EOPNOTSUPP;
+
+ args.handler = luo_file->fh;
+ args.serialized_data = luo_file->serialized_data;
+ err = luo_file->fh->ops->retrieve_into(&args, target_file);
+ if (err) {
+ luo_file->retrieve_status = err;
+ return err;
+ }
+
+ luo_file->file = target_file;
+ get_file(luo_file->file);
+ luo_file->retrieve_status = 1;
+
+ return 0;
+}
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index 64879ffe7378..1af9aebe6623 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -92,6 +92,8 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser);
int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
struct file **filep);
+int luo_retrieve_into_file(struct luo_file_set *file_set, u64 token,
+ struct file *target_file);
int luo_file_finish(struct luo_file_set *file_set);
int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser);
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index f48e9a4185f9..2b967f720ef5 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -278,6 +278,34 @@ static int luo_session_preserve_fd(struct luo_session *session,
return err;
}
+static int luo_session_retrieve_into_fd(struct luo_session *session,
+ struct luo_ucmd *ucmd)
+{
+ struct liveupdate_session_retrieve_into_fd *argp = ucmd->cmd;
+ struct fd target_fd;
+ int err;
+
+ target_fd = fdget(argp->fd);
+ if (fd_empty(target_fd))
+ return -EBADF;
+
+ guard(mutex)(&session->mutex);
+ err = luo_retrieve_into_file(&session->file_set, argp->token, fd_file(target_fd));
+ if (err < 0)
+ goto err_put_fd;
+
+ err = luo_ucmd_respond(ucmd, sizeof(*argp));
+ /*
+ * If we fail to respond, we cannot easily undo the retrieval. Let the
+ * normal session cleanup logic handle the file reference eventually.
+ */
+
+err_put_fd:
+ fdput(target_fd);
+
+ return err;
+}
+
static int luo_session_retrieve_fd(struct luo_session *session,
struct luo_ucmd *ucmd)
{
@@ -382,6 +410,8 @@ static const struct luo_ioctl_op luo_session_ioctl_ops[] = {
struct liveupdate_session_retrieve_fd, token, LUO_IOCTL_INCOMING),
IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name,
struct liveupdate_session_get_name, name, LUO_IOCTL_ALL),
+ IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, luo_session_retrieve_into_fd,
+ struct liveupdate_session_retrieve_into_fd, token, LUO_IOCTL_INCOMING),
};
static bool luo_ioctl_type_valid(struct luo_session *session,
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 3/5] mm/memfd_luo: Implement retrieve_into callback for shmem folios
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
2026-09-01 18:07 ` [RFC PATCH 1/5] liveupdate: Extract luo_file token lookup logic into helper function David Matlack
2026-09-01 18:07 ` [RFC PATCH 2/5] liveupdate: Introduce SESSION_RETRIEVE_INTO_FD API and core support David Matlack
@ 2026-09-01 18:07 ` David Matlack
2026-09-01 18:07 ` [RFC PATCH 4/5] mm/memfd_luo: Expand support beyond memfd to all generic shmem inodes David Matlack
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
Implement the .retrieve_into() file handler API for memfd/shmem files.
This implementation uses the standalone logic extracted previously to
securely drop physical memory into an empty target inode.
Userspace topology delegation requires injecting folios into existing
file descriptors. This implementation verifies the target descriptor is
a shmem file and strictly guarantees it is empty before locking the
inode and bridging the KHO folios into its address space.
Signed-off-by: David Matlack <dmatlack@google.com>
---
mm/memfd_luo.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index 59de210bee5f..dcda5bf98646 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -590,10 +590,58 @@ static unsigned long memfd_luo_get_id(struct file *file)
return (unsigned long)file_inode(file);
}
+static int memfd_luo_retrieve_into(struct liveupdate_file_op_args *args, struct file *target_file)
+{
+ struct inode *inode = file_inode(target_file);
+ struct memfd_luo_folio_ser *folios_ser;
+ struct memfd_luo_ser *ser;
+ int err;
+
+ /* Security/Safety checks: Ensure it's a shmem file and is completely empty */
+ if (!shmem_mapping(target_file->f_mapping))
+ return -EINVAL;
+
+ inode_lock(inode);
+ if (i_size_read(inode) != 0) {
+ inode_unlock(inode);
+ return -EBUSY;
+ }
+
+ ser = phys_to_virt(args->serialized_data);
+ if (!ser) {
+ inode_unlock(inode);
+ return -EINVAL;
+ }
+
+ if (ser->nr_folios) {
+ folios_ser = kho_restore_vmalloc(&ser->folios);
+ if (!folios_ser) {
+ inode_unlock(inode);
+ return -EINVAL;
+ }
+
+ /* Inject the physical pages from KHO using the extracted helper */
+ err = memfd_luo_retrieve_folios(target_file, folios_ser, ser->nr_folios);
+ kho_restore_free(folios_ser);
+ if (err) {
+ inode_unlock(inode);
+ return err;
+ }
+ }
+
+ /* Fast-forward the kernel inode size to match the injected data */
+ vfs_setpos(target_file, ser->pos, MAX_LFS_FILESIZE);
+ i_size_write(inode, ser->size);
+
+ inode_unlock(inode);
+ return 0;
+}
+
static const struct liveupdate_file_ops memfd_luo_file_ops = {
.freeze = memfd_luo_freeze,
.finish = memfd_luo_finish,
.retrieve = memfd_luo_retrieve,
+ .retrieve_into = memfd_luo_retrieve_into,
.preserve = memfd_luo_preserve,
.unpreserve = memfd_luo_unpreserve,
.can_preserve = memfd_luo_can_preserve,
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 4/5] mm/memfd_luo: Expand support beyond memfd to all generic shmem inodes
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
` (2 preceding siblings ...)
2026-09-01 18:07 ` [RFC PATCH 3/5] mm/memfd_luo: Implement retrieve_into callback for shmem folios David Matlack
@ 2026-09-01 18:07 ` David Matlack
2026-09-01 18:07 ` [RFC PATCH 5/5] selftests: liveupdate: Add multi-session test coverage for session_retrieve_into David Matlack
2026-09-11 18:04 ` [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD Ackerley Tng
5 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
Relax the early validation checks in the Live Update preservation path
to allow general shmem-backed files, completely dropping the requirement
that the file be unlinked (!inode->i_nlink).
This officially enables userspace to push standard, path-backed tmpfs
file descriptors through the LUO session ioctl instead of being
artificially restricted strictly to anonymous memfd pseudofiles.
Signed-off-by: David Matlack <dmatlack@google.com>
---
mm/memfd_luo.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index dcda5bf98646..107115c97bd8 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -9,14 +9,15 @@
*/
/**
- * DOC: Memfd Preservation via LUO
+ * DOC: Memfd and Tmpfs Preservation via LUO
*
* Overview
* ========
*
- * Memory file descriptors (memfd) can be preserved over a kexec using the Live
- * Update Orchestrator (LUO) file preservation. This allows userspace to
- * transfer its memory contents to the next kernel after a kexec.
+ * Memory file descriptors (memfd) and generic tmpfs files can be preserved
+ * over a kexec using the Live Update Orchestrator (LUO) file preservation.
+ * This allows userspace to transfer its memory contents to the next kernel
+ * after a kexec.
*
* The preservation is not intended to be transparent. Only select properties of
* the file are preserved. All others are reset to default. The preserved
@@ -24,16 +25,17 @@
*
* .. note::
* The LUO API is not stabilized yet, so the preserved properties of a memfd
- * are also not stable and are subject to backwards incompatible changes.
+ * or tmpfs file are also not stable and are subject to backwards
+ * incompatible changes.
*
* .. note::
- * Currently a memfd backed by Hugetlb is not supported. Memfds created
+ * Currently a file backed by Hugetlb is not supported. Memfds created
* with ``MFD_HUGETLB`` will be rejected.
*
* Preserved Properties
* ====================
*
- * The following properties of the memfd are preserved across kexec:
+ * The following properties of the memfd/tmpfs file are preserved across kexec:
*
* File Contents
* All data stored in the file is preserved.
@@ -580,9 +582,7 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
static bool memfd_luo_can_preserve(struct liveupdate_file_handler *handler,
struct file *file)
{
- struct inode *inode = file_inode(file);
-
- return shmem_file(file) && !inode->i_nlink;
+ return shmem_file(file) || shmem_mapping(file->f_mapping);
}
static unsigned long memfd_luo_get_id(struct file *file)
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 5/5] selftests: liveupdate: Add multi-session test coverage for session_retrieve_into
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
` (3 preceding siblings ...)
2026-09-01 18:07 ` [RFC PATCH 4/5] mm/memfd_luo: Expand support beyond memfd to all generic shmem inodes David Matlack
@ 2026-09-01 18:07 ` David Matlack
2026-09-11 18:04 ` [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD Ackerley Tng
5 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-01 18:07 UTC (permalink / raw)
To: kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan, David Matlack
Extend the multi-session Live Update test to specifically verify the new
LIVEUPDATE_SESSION_RETRIEVE_INTO_FD ioctl path. In Stage 2 of the kexec
workflow, the test explicitly delegates topology by allocating a clean
tmpfs target and driving the folios into it through the new API,
verifying that the data preserves identically to the native KHO mapping.
Signed-off-by: David Matlack <dmatlack@google.com>
---
.../liveupdate/lib/include/libliveupdate.h | 1 +
.../selftests/liveupdate/lib/lu_utils.c | 33 +++++++++++
.../selftests/liveupdate/luo_multi_session.c | 56 ++++++++++++++++---
3 files changed, 83 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
index fa07fed08364..8ab1e662eeaa 100644
--- a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
+++ b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
@@ -33,6 +33,7 @@ int luo_session_retrieve_fd(int session_fd, __u64 token);
int create_and_preserve_memfd(int session_fd, int token, const char *data);
int restore_and_verify_memfd(int session_fd, int token, const char *expected_data);
+int luo_verify_retrieve_into_memfd(int session_fd, int token, int mfd, const char *expected_data);
void create_state_file(int luo_fd, const char *session_name, int token,
int next_stage);
diff --git a/tools/testing/selftests/liveupdate/lib/lu_utils.c b/tools/testing/selftests/liveupdate/lib/lu_utils.c
index 74d41115c281..8ed9c367f92e 100644
--- a/tools/testing/selftests/liveupdate/lib/lu_utils.c
+++ b/tools/testing/selftests/liveupdate/lib/lu_utils.c
@@ -324,3 +324,36 @@ int luo_test(int argc, char *argv[],
return 0;
}
+
+int luo_verify_retrieve_into_memfd(int session_fd, int token, int mfd,
+ const char *expected_data)
+{
+ struct liveupdate_session_retrieve_into_fd retrieve_args = {
+ .size = sizeof(retrieve_args),
+ .token = token,
+ .fd = mfd,
+ };
+ long page_size = getpagesize();
+ void *map = MAP_FAILED;
+ int ret = -1;
+
+ ret = ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, &retrieve_args);
+ if (ret < 0) {
+ ksft_print_msg("ioctl LIVEUPDATE_SESSION_RETRIEVE_INTO_FD failed\n");
+ return -errno;
+ }
+
+ map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, mfd, 0);
+ if (map == MAP_FAILED)
+ return -errno;
+
+ if (expected_data && strcmp(expected_data, map) != 0) {
+ ksft_print_msg("Data mismatch! Expected '%s', Got '%s'\n",
+ expected_data, (char *)map);
+ munmap(map, page_size);
+ return -EINVAL;
+ }
+
+ munmap(map, page_size);
+ return 0;
+}
diff --git a/tools/testing/selftests/liveupdate/luo_multi_session.c b/tools/testing/selftests/liveupdate/luo_multi_session.c
index aac24a5f5ce3..7882f49aadaf 100644
--- a/tools/testing/selftests/liveupdate/luo_multi_session.c
+++ b/tools/testing/selftests/liveupdate/luo_multi_session.c
@@ -1,4 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
/*
* Copyright (c) 2025, Google LLC.
@@ -31,6 +36,8 @@
static void run_stage_1(int luo_fd)
{
int s_empty1_fd, s_empty2_fd, s_files1_fd, s_files2_fd;
+ int tmpfs_fd;
+ void *map;
ksft_print_msg("[STAGE 1] Starting pre-kexec setup for multi-session test...\n");
@@ -53,25 +60,50 @@ static void run_stage_1(int luo_fd)
s_files1_fd = luo_create_session(luo_fd, SESSION_FILES_1);
if (s_files1_fd < 0)
fail_exit("luo_create_session for '%s'", SESSION_FILES_1);
+ ksft_print_msg(" -> Preserving memfd (token %#x, content: \"%s\")\n",
+ MFD1_TOKEN, MFD1_DATA);
if (create_and_preserve_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA) < 0) {
fail_exit("create_and_preserve_memfd for token %#x",
MFD1_TOKEN);
}
- ksft_print_msg("[STAGE 1] Creating session '%s' with two memfds...\n",
+ ksft_print_msg("[STAGE 1] Creating session '%s' with memfd and tmpfs file...\n",
SESSION_FILES_2);
s_files2_fd = luo_create_session(luo_fd, SESSION_FILES_2);
if (s_files2_fd < 0)
fail_exit("luo_create_session for '%s'", SESSION_FILES_2);
+ ksft_print_msg(" -> Preserving memfd (token %#x, content: \"%s\")\n",
+ MFD2_TOKEN, MFD2_DATA);
if (create_and_preserve_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA) < 0) {
fail_exit("create_and_preserve_memfd for token %#x",
MFD2_TOKEN);
}
- if (create_and_preserve_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA) < 0) {
- fail_exit("create_and_preserve_memfd for token %#x",
- MFD3_TOKEN);
- }
+ ksft_print_msg(" -> Populating custom tmpfs file at /mnt/mfd3_source...\n");
+ mkdir("/mnt", 0777);
+ mount("tmpfs", "/mnt", "tmpfs", 0, NULL);
+ tmpfs_fd = open("/mnt/mfd3_source", O_CREAT | O_RDWR, 0666);
+ if (tmpfs_fd < 0)
+ fail_exit("failed to open tmpfs_fd");
+ ksft_print_msg(" -> Preserving generic tmpfs file (token %#x, content: \"%s\")\n",
+ MFD3_TOKEN, MFD3_DATA);
+ if (ftruncate(tmpfs_fd, getpagesize()) < 0)
+ fail_exit("ftruncate tmpfs_fd");
+ map = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, tmpfs_fd, 0);
+ if (map == MAP_FAILED)
+ fail_exit("mmap tmpfs_fd");
+ strcpy(map, MFD3_DATA);
+ munmap(map, getpagesize());
+
+ struct liveupdate_session_preserve_fd preserve_args = {
+ .size = sizeof(preserve_args),
+ .token = MFD3_TOKEN,
+ .fd = tmpfs_fd,
+ };
+ if (ioctl(s_files2_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &preserve_args) < 0)
+ fail_exit("preserve tmpfs fd");
+
+ close(tmpfs_fd);
close(luo_fd);
daemonize_and_wait();
@@ -110,6 +142,7 @@ static void run_stage_2(int luo_fd, int state_session_fd)
ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n",
SESSION_FILES_1);
+ ksft_print_msg(" -> Retrieving memfd (token %#x) from session...\n", MFD1_TOKEN);
mfd1 = restore_and_verify_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA);
if (mfd1 < 0)
fail_exit("restore_and_verify_memfd for token %#x", MFD1_TOKEN);
@@ -118,14 +151,23 @@ static void run_stage_2(int luo_fd, int state_session_fd)
ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n",
SESSION_FILES_2);
+ ksft_print_msg(" -> Retrieving memfd (token %#x) from session...\n", MFD2_TOKEN);
mfd2 = restore_and_verify_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA);
if (mfd2 < 0)
fail_exit("restore_and_verify_memfd for token %#x", MFD2_TOKEN);
close(mfd2);
- mfd3 = restore_and_verify_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA);
+ ksft_print_msg(" -> Creating empty tmpfs file at /mnt/mfd3_target...\n");
+ mkdir("/mnt", 0777);
+ mount("tmpfs", "/mnt", "tmpfs", 0, NULL);
+ mfd3 = open("/mnt/mfd3_target", O_CREAT | O_RDWR, 0666);
if (mfd3 < 0)
- fail_exit("restore_and_verify_memfd for token %#x", MFD3_TOKEN);
+ fail_exit("open tmpfs target for retrieve_into");
+
+ ksft_print_msg(" -> Validating LIVEUPDATE_SESSION_RETRIEVE_INTO_FD (token %#x)...\n",
+ MFD3_TOKEN);
+ if (luo_verify_retrieve_into_memfd(s_files2_fd, MFD3_TOKEN, mfd3, MFD3_DATA) < 0)
+ fail_exit("luo_verify_retrieve_into_memfd for token %#x", MFD3_TOKEN);
close(mfd3);
ksft_print_msg("[STAGE 2] Test data verified successfully.\n");
--
2.55.0.966.g6673acef38-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
` (4 preceding siblings ...)
2026-09-01 18:07 ` [RFC PATCH 5/5] selftests: liveupdate: Add multi-session test coverage for session_retrieve_into David Matlack
@ 2026-09-11 18:04 ` Ackerley Tng
2026-09-11 18:15 ` David Matlack
5 siblings, 1 reply; 10+ messages in thread
From: Ackerley Tng @ 2026-09-11 18:04 UTC (permalink / raw)
To: David Matlack, kexec, linux-kernel, linux-kselftest, linux-mm
Cc: Andrew Morton, Mike Rapoport, Pasha Tatashin, Pratyush Yadav,
Samiullah Khawaja, Shuah Khan
David Matlack <dmatlack@google.com> writes:
> This series adds support for LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, a new
> UAPI to enable preserved files to be restored into a userspace-provided
> file instead of a kernel-allocated file, and uses this to support
> preserving tmpfs files in-place without any changes to the LUO ABI.
>
I'm interested in the idea of retrieving folios into an fd for a
different reason, I'm exploring handling over ownership physical memory
to a guest_memfd.
> The Live Update Orchestrator (LUO) API currently explicitly returns a
> new struct file for every preserved file during retrieve() (e.g. via
> memfd_alloc_file()). This then forces userspace to use anonymous memfds
> for all in-memory files it wants to preserve. This works reasonably well
> for VM guest memory but does not work well for other types of files that
> userspace may want to preserve (e.g. in-memory logs, binaries,
> configuration files, etc.).
>
Does this assume that VM guest memory is usually provided using
anonymous memfds?
More generally, is the problem that there isn't a simple way to
implement retrieval into anything other than anonymous memfds?
> Preserving entire tmpfs mounts in-place would require a large amount of
> kernel support and would effectively make tmpfs an ABI, which is a
> non-starter (or so I hear). Instead, we can delegate the reconstruction
> of the tmpfs mounts (directory structure, permissions, etc.) post-kexec
> to userspace. The kernel just needs to preserve the contents of tmpfs
> files and provide userspace a mechanism to restore those contents back
> into a specific file on the filesystem after the kexec. Hence,
> LIVEUPDATE_SESSION_RETRIEVE_INTO_FD.
>
Suppose pre-kexec, the data was in a tmpfs file (and fd), how would the
preservation work? Would the user have to
1. Transfer the data from tmpfs fd -> anonymous fd
2. Preserve from anonymous fd
3. kexec
4. Set up the tmpfs fd
5. Retrieve into the tmpfs fd
> An alternative approach to restoring data to a named file would be
> supporting a zero-copy sendfile() that can be used to convert named
> tmpfs files to/from memfds across the kexec. But this poses significant
> challenges on the *preserve* side since the preserved file might still
> be actively in use. The benefit of the retrieve-into approach introduced
> in this series is that it does not require dealing with two different
> files. There is only ever one file that owns the preserved memory.
>
Would it be more symmetric if the process is
1. Transfer the data from tmpfs fd -> anonymous fd
2. Preserve from anonymous fd
3. kexec
4. Retrieve into anonymous fd
5. Transfer the data from anonymous fd -> tmpfs fd
As to the exact details of "transfer", I think the best we could do
would be some kind of move from one fd's page cache to the other fd's
page cache?
Is the goal of the transfer to avoid any memcpy and fully transfer
ownership (so, not just by increasing folio refcounts?)
> By leaving file and metadata allocation purely in the purview of
> userspace, programs can create the target files where they want, with
> the names and security permissions they desire, before directing the
> kernel to restore the preserved folios into them.
>
> Currently, only tmpfs shmem files are allowed as valid target receptors.
> Attempting to target populated files, or anything other than an empty
> shmem file, will trigger -EINVAL. HugeTLBfs files could be supported in
> the future.
>
Is this basically that at some point, all in-memory filesystems' fds can
support transfers?
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD
2026-09-11 18:04 ` [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD Ackerley Tng
@ 2026-09-11 18:15 ` David Matlack
2026-09-11 20:59 ` Ackerley Tng
0 siblings, 1 reply; 10+ messages in thread
From: David Matlack @ 2026-09-11 18:15 UTC (permalink / raw)
To: Ackerley Tng
Cc: kexec, linux-kernel, linux-kselftest, linux-mm, Andrew Morton,
Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Samiullah Khawaja,
Shuah Khan
On 2026-09-11 11:04 AM, Ackerley Tng wrote:
> David Matlack <dmatlack@google.com> writes:
>
> > This series adds support for LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, a new
> > UAPI to enable preserved files to be restored into a userspace-provided
> > file instead of a kernel-allocated file, and uses this to support
> > preserving tmpfs files in-place without any changes to the LUO ABI.
> >
>
> I'm interested in the idea of retrieving folios into an fd for a
> different reason, I'm exploring handling over ownership physical memory
> to a guest_memfd.
>
> > The Live Update Orchestrator (LUO) API currently explicitly returns a
> > new struct file for every preserved file during retrieve() (e.g. via
> > memfd_alloc_file()). This then forces userspace to use anonymous memfds
> > for all in-memory files it wants to preserve. This works reasonably well
> > for VM guest memory but does not work well for other types of files that
> > userspace may want to preserve (e.g. in-memory logs, binaries,
> > configuration files, etc.).
> >
>
> Does this assume that VM guest memory is usually provided using
> anonymous memfds?
>
> More generally, is the problem that there isn't a simple way to
> implement retrieval into anything other than anonymous memfds?
Roughly, yes.
> > Preserving entire tmpfs mounts in-place would require a large amount of
> > kernel support and would effectively make tmpfs an ABI, which is a
> > non-starter (or so I hear). Instead, we can delegate the reconstruction
> > of the tmpfs mounts (directory structure, permissions, etc.) post-kexec
> > to userspace. The kernel just needs to preserve the contents of tmpfs
> > files and provide userspace a mechanism to restore those contents back
> > into a specific file on the filesystem after the kexec. Hence,
> > LIVEUPDATE_SESSION_RETRIEVE_INTO_FD.
> >
>
> Suppose pre-kexec, the data was in a tmpfs file (and fd), how would the
> preservation work? Would the user have to
>
> 1. Transfer the data from tmpfs fd -> anonymous fd
> 2. Preserve from anonymous fd
> 3. kexec
> 4. Set up the tmpfs fd
> 5. Retrieve into the tmpfs fd
No, the proposal in this series is to support:
1. ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, {TOKEN, tmpfs_fd})
2. kexec
3. tmpfs_fd = open(..., O_CREAT)
4. ioctl(LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, {TOKEN, tmpfs_fd})
No transfering to or from an anonymous fd is required.
> > An alternative approach to restoring data to a named file would be
> > supporting a zero-copy sendfile() that can be used to convert named
> > tmpfs files to/from memfds across the kexec. But this poses significant
> > challenges on the *preserve* side since the preserved file might still
> > be actively in use. The benefit of the retrieve-into approach introduced
> > in this series is that it does not require dealing with two different
> > files. There is only ever one file that owns the preserved memory.
> >
>
> Would it be more symmetric if the process is
>
> 1. Transfer the data from tmpfs fd -> anonymous fd
> 2. Preserve from anonymous fd
> 3. kexec
> 4. Retrieve into anonymous fd
> 5. Transfer the data from anonymous fd -> tmpfs fd
>
> As to the exact details of "transfer", I think the best we could do
> would be some kind of move from one fd's page cache to the other fd's
> page cache?
>
> Is the goal of the transfer to avoid any memcpy and fully transfer
> ownership (so, not just by increasing folio refcounts?)
Yes this is the alternative proposal that has been proposed and
discussed a little bit off list, with sendfile() and copy_file_range()
being the possible mechanisms to do the "transfer". I think there are a
few fundamental problems with that approach on the pre-kexec side.
1. iommufd requires that fds mapped into it are preserved. If the file
mapped into iommufd is tmpfs, but then we transfer that file to memfd
for preservation, iommufd will fail to preserve because the tmpfs file
is not preserved.
2. Even if you only use this feature for files not mapped into an
iommufd, transfering the contents in a zero-copy way seems
challenging. What do you do if the tmpfs file is still in use when
you want to transfer it?
> > By leaving file and metadata allocation purely in the purview of
> > userspace, programs can create the target files where they want, with
> > the names and security permissions they desire, before directing the
> > kernel to restore the preserved folios into them.
> >
> > Currently, only tmpfs shmem files are allowed as valid target receptors.
> > Attempting to target populated files, or anything other than an empty
> > shmem file, will trigger -EINVAL. HugeTLBfs files could be supported in
> > the future.
> >
>
> Is this basically that at some point, all in-memory filesystems' fds can
> support transfers?
Yes, theoretically.
>
> >
> > [...snip...]
> >
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD
2026-09-11 18:15 ` David Matlack
@ 2026-09-11 20:59 ` Ackerley Tng
2026-09-11 21:10 ` David Matlack
0 siblings, 1 reply; 10+ messages in thread
From: Ackerley Tng @ 2026-09-11 20:59 UTC (permalink / raw)
To: David Matlack
Cc: kexec, linux-kernel, linux-kselftest, linux-mm, Andrew Morton,
Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Samiullah Khawaja,
Shuah Khan
David Matlack <dmatlack@google.com> writes:
> On 2026-09-11 11:04 AM, Ackerley Tng wrote:
>> David Matlack <dmatlack@google.com> writes:
>>
>> > This series adds support for LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, a new
>> > UAPI to enable preserved files to be restored into a userspace-provided
>> > file instead of a kernel-allocated file, and uses this to support
>> > preserving tmpfs files in-place without any changes to the LUO ABI.
>> >
>>
>> I'm interested in the idea of retrieving folios into an fd for a
>> different reason, I'm exploring handling over ownership physical memory
>> to a guest_memfd.
>>
>> > The Live Update Orchestrator (LUO) API currently explicitly returns a
>> > new struct file for every preserved file during retrieve() (e.g. via
>> > memfd_alloc_file()). This then forces userspace to use anonymous memfds
>> > for all in-memory files it wants to preserve. This works reasonably well
>> > for VM guest memory but does not work well for other types of files that
>> > userspace may want to preserve (e.g. in-memory logs, binaries,
>> > configuration files, etc.).
>> >
>>
>> Does this assume that VM guest memory is usually provided using
>> anonymous memfds?
>>
>> More generally, is the problem that there isn't a simple way to
>> implement retrieval into anything other than anonymous memfds?
>
> Roughly, yes.
>
>> > Preserving entire tmpfs mounts in-place would require a large amount of
>> > kernel support and would effectively make tmpfs an ABI, which is a
>> > non-starter (or so I hear). Instead, we can delegate the reconstruction
>> > of the tmpfs mounts (directory structure, permissions, etc.) post-kexec
>> > to userspace. The kernel just needs to preserve the contents of tmpfs
>> > files and provide userspace a mechanism to restore those contents back
>> > into a specific file on the filesystem after the kexec. Hence,
>> > LIVEUPDATE_SESSION_RETRIEVE_INTO_FD.
>> >
>>
>> Suppose pre-kexec, the data was in a tmpfs file (and fd), how would the
>> preservation work? Would the user have to
>>
>> 1. Transfer the data from tmpfs fd -> anonymous fd
>> 2. Preserve from anonymous fd
>> 3. kexec
>> 4. Set up the tmpfs fd
>> 5. Retrieve into the tmpfs fd
>
> No, the proposal in this series is to support:
>
> 1. ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, {TOKEN, tmpfs_fd})
> 2. kexec
> 3. tmpfs_fd = open(..., O_CREAT)
> 4. ioctl(LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, {TOKEN, tmpfs_fd})
>
> No transfering to or from an anonymous fd is required.
>
Cool that preserving a tmpfs_fd is supported. How is that done without
preserving the rest of the tmpfs stuff?
Why is it possible to preserve the fd without the rest of the tmpfs
stuff but not retrieve without the rest of the tmpfs stuff?
Is retrieving into an fd a bit too much of a shortcut? Would preserving
the rest of the tmpfs eventually be required, and hence retrieval of the
rest of tmpfs should also be eventually supported symmetrically?
>> > An alternative approach to restoring data to a named file would be
>> > supporting a zero-copy sendfile() that can be used to convert named
>> > tmpfs files to/from memfds across the kexec. But this poses significant
>> > challenges on the *preserve* side since the preserved file might still
>> > be actively in use. The benefit of the retrieve-into approach introduced
>> > in this series is that it does not require dealing with two different
>> > files. There is only ever one file that owns the preserved memory.
>> >
>>
>> Would it be more symmetric if the process is
>>
>> 1. Transfer the data from tmpfs fd -> anonymous fd
>> 2. Preserve from anonymous fd
>> 3. kexec
>> 4. Retrieve into anonymous fd
>> 5. Transfer the data from anonymous fd -> tmpfs fd
>>
>> As to the exact details of "transfer", I think the best we could do
>> would be some kind of move from one fd's page cache to the other fd's
>> page cache?
>>
>> Is the goal of the transfer to avoid any memcpy and fully transfer
>> ownership (so, not just by increasing folio refcounts?)
>
> Yes this is the alternative proposal that has been proposed and
> discussed a little bit off list, with sendfile() and copy_file_range()
> being the possible mechanisms to do the "transfer". I think there are a
> few fundamental problems with that approach on the pre-kexec side.
>
> 1. iommufd requires that fds mapped into it are preserved. If the file
> mapped into iommufd is tmpfs, but then we transfer that file to memfd
> for preservation, iommufd will fail to preserve because the tmpfs file
> is not preserved.
>
> 2. Even if you only use this feature for files not mapped into an
> iommufd, transfering the contents in a zero-copy way seems
> challenging. What do you do if the tmpfs file is still in use when
> you want to transfer it?
>
When you say in-use, I suppose you mean folio is in-use by some IO and
is mapped in IO page tables, and that the IO must continue while the
folio is transferred?
Are you expecting guests (as in stage 2 mappings) to continue to be
functional while the folio is transferred?
I'll have to think a bit more, I was initially thinking of just
transferring folios where I could unmap from everywhere (processes,
stage 2, IO page tables) for the transfer.
>> > By leaving file and metadata allocation purely in the purview of
>> > userspace, programs can create the target files where they want, with
>> > the names and security permissions they desire, before directing the
>> > kernel to restore the preserved folios into them.
>> >
>> > Currently, only tmpfs shmem files are allowed as valid target receptors.
>> > Attempting to target populated files, or anything other than an empty
>> > shmem file, will trigger -EINVAL. HugeTLBfs files could be supported in
>> > the future.
>> >
>>
>> Is this basically that at some point, all in-memory filesystems' fds can
>> support transfers?
>
> Yes, theoretically.
>
>>
>> >
>> > [...snip...]
>> >
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD
2026-09-11 20:59 ` Ackerley Tng
@ 2026-09-11 21:10 ` David Matlack
0 siblings, 0 replies; 10+ messages in thread
From: David Matlack @ 2026-09-11 21:10 UTC (permalink / raw)
To: Ackerley Tng
Cc: kexec, linux-kernel, linux-kselftest, linux-mm, Andrew Morton,
Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Samiullah Khawaja,
Shuah Khan
On Fri, Sep 11, 2026 at 1:59 PM Ackerley Tng <ackerleytng@google.com> wrote:
>
> David Matlack <dmatlack@google.com> writes:
>
> > On 2026-09-11 11:04 AM, Ackerley Tng wrote:
> >> David Matlack <dmatlack@google.com> writes:
> >>
> >> > This series adds support for LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, a new
> >> > UAPI to enable preserved files to be restored into a userspace-provided
> >> > file instead of a kernel-allocated file, and uses this to support
> >> > preserving tmpfs files in-place without any changes to the LUO ABI.
> >> >
> >>
> >> I'm interested in the idea of retrieving folios into an fd for a
> >> different reason, I'm exploring handling over ownership physical memory
> >> to a guest_memfd.
> >>
> >> > The Live Update Orchestrator (LUO) API currently explicitly returns a
> >> > new struct file for every preserved file during retrieve() (e.g. via
> >> > memfd_alloc_file()). This then forces userspace to use anonymous memfds
> >> > for all in-memory files it wants to preserve. This works reasonably well
> >> > for VM guest memory but does not work well for other types of files that
> >> > userspace may want to preserve (e.g. in-memory logs, binaries,
> >> > configuration files, etc.).
> >> >
> >>
> >> Does this assume that VM guest memory is usually provided using
> >> anonymous memfds?
> >>
> >> More generally, is the problem that there isn't a simple way to
> >> implement retrieval into anything other than anonymous memfds?
> >
> > Roughly, yes.
> >
> >> > Preserving entire tmpfs mounts in-place would require a large amount of
> >> > kernel support and would effectively make tmpfs an ABI, which is a
> >> > non-starter (or so I hear). Instead, we can delegate the reconstruction
> >> > of the tmpfs mounts (directory structure, permissions, etc.) post-kexec
> >> > to userspace. The kernel just needs to preserve the contents of tmpfs
> >> > files and provide userspace a mechanism to restore those contents back
> >> > into a specific file on the filesystem after the kexec. Hence,
> >> > LIVEUPDATE_SESSION_RETRIEVE_INTO_FD.
> >> >
> >>
> >> Suppose pre-kexec, the data was in a tmpfs file (and fd), how would the
> >> preservation work? Would the user have to
> >>
> >> 1. Transfer the data from tmpfs fd -> anonymous fd
> >> 2. Preserve from anonymous fd
> >> 3. kexec
> >> 4. Set up the tmpfs fd
> >> 5. Retrieve into the tmpfs fd
> >
> > No, the proposal in this series is to support:
> >
> > 1. ioctl(LIVEUPDATE_SESSION_PRESERVE_FD, {TOKEN, tmpfs_fd})
> > 2. kexec
> > 3. tmpfs_fd = open(..., O_CREAT)
> > 4. ioctl(LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, {TOKEN, tmpfs_fd})
> >
> > No transfering to or from an anonymous fd is required.
> >
>
> Cool that preserving a tmpfs_fd is supported. How is that done without
> preserving the rest of the tmpfs stuff?
>
> Why is it possible to preserve the fd without the rest of the tmpfs
> stuff but not retrieve without the rest of the tmpfs stuff?
>
> Is retrieving into an fd a bit too much of a shortcut? Would preserving
> the rest of the tmpfs eventually be required, and hence retrieval of the
> rest of tmpfs should also be eventually supported symmetrically?
The idea is that the kernel is responsible for preserving the
_contents_ of the tmpfs file in place. Userspace is responsible for
setting up a new tmpfs mount, configuring permissions, etc. after
kexec and telling the kernel which file should receive those preserved
contents. Userspace set up these mounts before kexec, so it is
responsible for setting them up after kexec as well. I see no issue
with that.
There is some nuance that this series doesn't handle though (it is RFC
after all). Pratyush brought up offline that the tmpfs mount might
have allocation policies attached to it. The
LIVEUPDATE_SESSION_RETRIEVE_INTO_FD handler needs to confirm the
preserved contents do not violate the allocation policies of the mount
it is retrieving into. But that seems easily solvable to me.
>
> >> > An alternative approach to restoring data to a named file would be
> >> > supporting a zero-copy sendfile() that can be used to convert named
> >> > tmpfs files to/from memfds across the kexec. But this poses significant
> >> > challenges on the *preserve* side since the preserved file might still
> >> > be actively in use. The benefit of the retrieve-into approach introduced
> >> > in this series is that it does not require dealing with two different
> >> > files. There is only ever one file that owns the preserved memory.
> >> >
> >>
> >> Would it be more symmetric if the process is
> >>
> >> 1. Transfer the data from tmpfs fd -> anonymous fd
> >> 2. Preserve from anonymous fd
> >> 3. kexec
> >> 4. Retrieve into anonymous fd
> >> 5. Transfer the data from anonymous fd -> tmpfs fd
> >>
> >> As to the exact details of "transfer", I think the best we could do
> >> would be some kind of move from one fd's page cache to the other fd's
> >> page cache?
> >>
> >> Is the goal of the transfer to avoid any memcpy and fully transfer
> >> ownership (so, not just by increasing folio refcounts?)
> >
> > Yes this is the alternative proposal that has been proposed and
> > discussed a little bit off list, with sendfile() and copy_file_range()
> > being the possible mechanisms to do the "transfer". I think there are a
> > few fundamental problems with that approach on the pre-kexec side.
> >
> > 1. iommufd requires that fds mapped into it are preserved. If the file
> > mapped into iommufd is tmpfs, but then we transfer that file to memfd
> > for preservation, iommufd will fail to preserve because the tmpfs file
> > is not preserved.
> >
> > 2. Even if you only use this feature for files not mapped into an
> > iommufd, transfering the contents in a zero-copy way seems
> > challenging. What do you do if the tmpfs file is still in use when
> > you want to transfer it?
> >
>
> When you say in-use, I suppose you mean folio is in-use by some IO and
> is mapped in IO page tables, and that the IO must continue while the
> folio is transferred?
>
> Are you expecting guests (as in stage 2 mappings) to continue to be
> functional while the folio is transferred?
>
> I'll have to think a bit more, I was initially thinking of just
> transferring folios where I could unmap from everywhere (processes,
> stage 2, IO page tables) for the transfer.
I was imagining a userspace process may have the tmpfs file mmap()'d
and is accessing the memory. I guess you can argue that userspace
should stop doing that during LU. But I think the iommufd use-case
shows that that is not really possible. The whole point is to preserve
the memory mapped into the iommufd throughout the Live Update.
>
> >> > By leaving file and metadata allocation purely in the purview of
> >> > userspace, programs can create the target files where they want, with
> >> > the names and security permissions they desire, before directing the
> >> > kernel to restore the preserved folios into them.
> >> >
> >> > Currently, only tmpfs shmem files are allowed as valid target receptors.
> >> > Attempting to target populated files, or anything other than an empty
> >> > shmem file, will trigger -EINVAL. HugeTLBfs files could be supported in
> >> > the future.
> >> >
> >>
> >> Is this basically that at some point, all in-memory filesystems' fds can
> >> support transfers?
> >
> > Yes, theoretically.
> >
> >>
> >> >
> >> > [...snip...]
> >> >
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-09-11 21:11 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-01 18:07 [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD David Matlack
2026-09-01 18:07 ` [RFC PATCH 1/5] liveupdate: Extract luo_file token lookup logic into helper function David Matlack
2026-09-01 18:07 ` [RFC PATCH 2/5] liveupdate: Introduce SESSION_RETRIEVE_INTO_FD API and core support David Matlack
2026-09-01 18:07 ` [RFC PATCH 3/5] mm/memfd_luo: Implement retrieve_into callback for shmem folios David Matlack
2026-09-01 18:07 ` [RFC PATCH 4/5] mm/memfd_luo: Expand support beyond memfd to all generic shmem inodes David Matlack
2026-09-01 18:07 ` [RFC PATCH 5/5] selftests: liveupdate: Add multi-session test coverage for session_retrieve_into David Matlack
2026-09-11 18:04 ` [RFC PATCH 0/5] liveupdate: LIVEUPDATE_SESSION_RETRIEVE_INTO_FD Ackerley Tng
2026-09-11 18:15 ` David Matlack
2026-09-11 20:59 ` Ackerley Tng
2026-09-11 21:10 ` David Matlack
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®