From: David Matlack <dmatlack@google.com>
To: kexec@lists.infradead.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-mm@kvack.org
Cc: Alexander Graf <graf@amazon.com>,
Andrew Morton <akpm@linux-foundation.org>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
Christian Brauner <brauner@kernel.org>,
David Hildenbrand <david@kernel.org>,
Hugh Dickins <hughd@google.com>, Jonathan Corbet <corbet@lwn.net>,
"Liam R. Howlett" <liam@infradead.org>,
Lorenzo Stoakes <ljs@kernel.org>, Michal Hocko <mhocko@suse.com>,
Mike Rapoport <rppt@kernel.org>,
Pasha Tatashin <pasha.tatashin@soleen.com>,
Pratyush Yadav <pratyush@kernel.org>,
Randy Dunlap <rdunlap@infradead.org>,
Samiullah Khawaja <skhawaja@google.com>,
Shuah Khan <skhan@linuxfoundation.org>,
Suren Baghdasaryan <surenb@google.com>,
Vlastimil Babka <vbabka@kernel.org>,
David Matlack <dmatlack@google.com>
Subject: [PATCH v2 01/10] liveupdate: Pass user argument to file preserve and retrieve
Date: Thu, 22 Oct 2026 22:50:26 +0000 [thread overview]
Message-ID: <20261022225035.377445-2-dmatlack@google.com> (raw)
In-Reply-To: <20261022225035.377445-1-dmatlack@google.com>
From: Pasha Tatashin <pasha.tatashin@soleen.com>
Allow userspace to pass a file-handler-specific argument structure when
preserving or retrieving an FD via LUO. File handlers may need
additional context during preservation or retrieval, such as a target
FD, device location, or handler-specific parameters, without introducing
separate ioctls for each file type. Whether the argument is required,
optional, or unused is defined by the file handler.
Extend struct liveupdate_session_preserve_fd and
struct liveupdate_session_retrieve_fd with a 64-bit arg field pointing
to a handler-defined argument structure that begins with a 32-bit size
field. Because IOCTL_OP validates a minimum size up to the token field,
existing callers passing the 16-byte struct remain compatible and have
arg zero-filled by copy_struct_from_user().
Add parg and rarg (struct liveupdate_arg_spec) to
struct liveupdate_file_ops, initialized via LIVEUPDATE_ARG(_struct,
_min_last), which records the full struct size and minimum required size
while enforcing at compile time that handler argument structures begin
with a 32-bit size field. When arg is non-zero, LUO validates the
minimum size and copies the user argument via copy_struct_from_user() up
to the handler's expected size and passes the kernel copy in
struct liveupdate_file_op_args, or returns -EOPNOTSUPP if the handler
does not accept an argument.
The argument only applies to the retrieve that restores the file.
Retrieving a token that was already retrieved returns the existing file,
so fail with -EBUSY if a non-zero argument is passed then, instead of
silently ignoring it.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: David Matlack <dmatlack@google.com>
---
include/linux/liveupdate.h | 44 +++++++++++++++++++
include/uapi/linux/liveupdate.h | 10 +++++
kernel/liveupdate/luo_file.c | 73 +++++++++++++++++++++++++++++---
kernel/liveupdate/luo_internal.h | 5 ++-
kernel/liveupdate/luo_session.c | 6 ++-
5 files changed, 128 insertions(+), 10 deletions(-)
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index 6051abc0612c..7543f5d648d3 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -22,6 +22,36 @@ struct liveupdate_flb;
struct liveupdate_session;
struct file;
+/**
+ * struct liveupdate_arg_spec - Specification of a file handler user argument.
+ * @size: Full size of the argument structure in the current kernel.
+ * @min_size: Minimum valid user-supplied size (offsetofend of the last
+ * required field).
+ */
+struct liveupdate_arg_spec {
+ size_t size;
+ size_t min_size;
+};
+
+/**
+ * LIVEUPDATE_ARG - Validated specification of a file handler argument struct.
+ * @_struct: The argument structure type.
+ * @_min_last: The last required member in @_struct.
+ *
+ * All file handler argument structures passed via
+ * &liveupdate_session_preserve_fd.arg or &liveupdate_session_retrieve_fd.arg
+ * must begin with a 32-bit @size field so they can be extended compatibly.
+ * Use this macro to initialize &liveupdate_file_ops.parg and
+ * &liveupdate_file_ops.rarg.
+ */
+#define LIVEUPDATE_ARG(_struct, _min_last) \
+ { \
+ .size = sizeof(_struct) + \
+ BUILD_BUG_ON_ZERO(offsetof(_struct, size) != 0) + \
+ BUILD_BUG_ON_ZERO(sizeof_field(_struct, size) != sizeof(u32)), \
+ .min_size = offsetofend(_struct, _min_last), \
+ }
+
/**
* struct liveupdate_file_op_args - Arguments for file operation callbacks.
* @handler: The file handler being called.
@@ -40,6 +70,11 @@ struct file;
* is not preserved. Set by the handler's .preserve()
* callback, and must be freed in the handler's
* .unpreserve() callback.
+ * @arg: Kernel copy of the user-provided argument for .preserve()
+ * and .retrieve(), or %NULL if none was provided. Allocated
+ * to the handler's expected argument size with any trailing
+ * bytes beyond the user-supplied size zero-filled. Valid
+ * only for the duration of the callback.
*
* This structure bundles all parameters for the file operation callbacks.
* The 'data' and 'file' fields are used for both input and output.
@@ -51,6 +86,7 @@ struct liveupdate_file_op_args {
struct file *file;
u64 serialized_data;
void *private_data;
+ const void *arg;
};
/**
@@ -68,6 +104,12 @@ struct liveupdate_file_op_args {
* resources in the session.
* @finish: Required. Final cleanup in the new kernel.
* @get_id: Optional. Returns a unique identifier for the file.
+ * @parg: Optional. Specification of the user argument structure
+ * accepted by @preserve, initialized via LIVEUPDATE_ARG(). If
+ * unset, non-zero user arguments are rejected with -EOPNOTSUPP.
+ * @rarg: Optional. Specification of the user argument structure
+ * accepted by @retrieve, initialized via LIVEUPDATE_ARG(). If
+ * unset, non-zero user arguments are rejected with -EOPNOTSUPP.
* @owner: Module reference
*
* All operations (except can_preserve) receive a pointer to a
@@ -84,6 +126,8 @@ struct liveupdate_file_ops {
bool (*can_finish)(struct liveupdate_file_op_args *args);
void (*finish)(struct liveupdate_file_op_args *args);
unsigned long (*get_id)(struct file *file);
+ struct liveupdate_arg_spec parg;
+ struct liveupdate_arg_spec rarg;
struct module *owner;
};
diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
index 4043d4038712..e806752c2fc6 100644
--- a/include/uapi/linux/liveupdate.h
+++ b/include/uapi/linux/liveupdate.h
@@ -126,6 +126,8 @@ struct liveupdate_ioctl_retrieve_session {
* @size: Input; sizeof(struct liveupdate_session_preserve_fd)
* @fd: Input; The user-space file descriptor to be preserved.
* @token: Input; An opaque, unique token for preserved resource.
+ * @arg: Input; User-space pointer to a file-handler-specific argument
+ * structure starting with a 32-bit size field, or 0 if unused.
*
* Holds parameters for preserving a file descriptor.
*
@@ -149,6 +151,7 @@ struct liveupdate_session_preserve_fd {
__u32 size;
__s32 fd;
__aligned_u64 token;
+ __aligned_u64 arg;
};
#define LIVEUPDATE_SESSION_PRESERVE_FD \
@@ -160,6 +163,8 @@ struct liveupdate_session_preserve_fd {
* @fd: Output; The new file descriptor representing the fully restored
* kernel resource.
* @token: Input; An opaque, token that was used to preserve the resource.
+ * @arg: Input; User-space pointer to a file-handler-specific argument
+ * structure starting with a 32-bit size field, or 0 if unused.
*
* Retrieve a previously preserved file descriptor.
*
@@ -173,12 +178,17 @@ struct liveupdate_session_preserve_fd {
* attempts to retrieve the token fail with the same error code. Failed
* retrieves are not retried.
*
+ * Retrieving a token that was already retrieved successfully returns a new file
+ * descriptor for the same file. The @arg only applies to the retrieve that
+ * restores the file, so passing a non-zero @arg then fails with -EBUSY.
+ *
* Return: 0 on success, negative error code on failure (e.g., invalid token).
*/
struct liveupdate_session_retrieve_fd {
__u32 size;
__s32 fd;
__aligned_u64 token;
+ __aligned_u64 arg;
};
#define LIVEUPDATE_SESSION_RETRIEVE_FD \
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a39992da504f..eeeb3b5ff2fa 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -126,6 +126,7 @@
#include <linux/xarray.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/uaccess.h>
#include "luo_internal.h"
static LIST_HEAD(luo_file_handler_list);
@@ -202,11 +203,45 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
return false;
}
+static int luo_copy_user_arg(u64 user_arg, const void **argp,
+ const struct liveupdate_arg_spec *spec)
+{
+ void *karg __free(kfree) = NULL;
+ void __user *uptr;
+ u32 user_size;
+ int err;
+
+ if (!user_arg)
+ return 0;
+ if (!spec->size)
+ return -EOPNOTSUPP;
+
+ uptr = u64_to_user_ptr(user_arg);
+ if (get_user(user_size, (u32 __user *)uptr))
+ return -EFAULT;
+ if (user_size < spec->min_size)
+ return -EINVAL;
+ if (user_size > PAGE_SIZE)
+ return -E2BIG;
+
+ karg = kmalloc(spec->size, GFP_KERNEL);
+ if (!karg)
+ return -ENOMEM;
+
+ err = copy_struct_from_user(karg, spec->size, uptr, user_size);
+ if (err)
+ return err;
+
+ *argp = no_free_ptr(karg);
+ return 0;
+}
+
/**
* luo_preserve_file - Initiate the preservation of a file descriptor.
* @file_set: The file_set to which the preserved file will be added.
* @token: A unique, user-provided identifier for the file.
* @fd: The file descriptor to be preserved.
+ * @arg: User-space pointer to the handler argument structure, or 0.
*
* This function orchestrates the first phase of preserving a file. Upon entry,
* it takes a reference to the 'struct file' via fget(), effectively making LUO
@@ -222,9 +257,11 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
* (allocates if needed).
* 3. Iterates through registered handlers, calling can_preserve() to find one
* compatible with the given @fd.
- * 4. Calls the handler's .preserve() operation, which saves the file's state
+ * 4. Copies the user argument structure (if @arg is non-zero) according to the
+ * handler's parg specification.
+ * 5. Calls the handler's .preserve() operation, which saves the file's state
* and returns an opaque private data handle.
- * 5. Adds the new instance to the file_set's internal list.
+ * 6. Adds the new instance to the file_set's internal list.
*
* On success, LUO takes a reference to the 'struct file' and considers it
* under its management until it is unpreserved or finished.
@@ -239,10 +276,12 @@ static bool luo_token_is_used(struct luo_file_set *file_set, u64 token)
* -EBADF if the file descriptor is invalid.
* -ENOSPC if the file_set is full.
* -ENOENT if no compatible handler is found.
+ * -EOPNOTSUPP if @arg is non-zero and the handler does not accept an arg.
* -ENOMEM on memory allocation failure.
* Other erros might be returned by .preserve().
*/
-int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
+int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd,
+ u64 arg)
{
struct liveupdate_file_op_args args = {0};
struct liveupdate_file_handler *fh;
@@ -278,10 +317,14 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
if (err)
goto err_fput;
+ err = luo_copy_user_arg(arg, &args.arg, &fh->ops->parg);
+ if (err)
+ goto err_module_put;
+
err = xa_insert(&luo_preserved_files, luo_get_id(fh, file),
file, GFP_KERNEL);
if (err)
- goto err_module_put;
+ goto err_free_arg;
err = luo_flb_file_preserve(fh);
if (err)
@@ -305,6 +348,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
if (err)
goto err_kfree;
+ kfree(args.arg);
luo_file->serialized_data = args.serialized_data;
luo_file->private_data = args.private_data;
list_add_tail(&luo_file->list, &file_set->files_list);
@@ -318,6 +362,8 @@ int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd)
luo_flb_file_unpreserve(fh);
err_erase_xa:
xa_erase(&luo_preserved_files, luo_get_id(fh, file));
+err_free_arg:
+ kfree(args.arg);
err_module_put:
module_put(fh->ops->owner);
err_fput:
@@ -547,6 +593,7 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
* luo_retrieve_file - Restores a preserved file from a file_set by its token.
* @file_set: The file_set from which to retrieve the file.
* @token: The unique token identifying the file to be restored.
+ * @arg: User-space pointer to the handler argument structure, or 0.
* @filep: Output parameter; on success, this is populated with a pointer
* to the newly retrieved 'struct file'.
*
@@ -566,9 +613,11 @@ void luo_file_unfreeze(struct luo_file_set *file_set,
* kernel.
* Return: 0 on success. Returns a negative errno on failure:
* -ENOENT if no file with the matching token is found.
+ * -EOPNOTSUPP if @arg is non-zero and the handler does not accept an arg.
+ * -EBUSY if @arg is non-zero and the file was already retrieved.
* Any error code returned by the handler's .retrieve() op.
*/
-int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
+int luo_retrieve_file(struct luo_file_set *file_set, u64 token, u64 arg,
struct file **filep)
{
struct liveupdate_file_op_args args = {0};
@@ -596,6 +645,13 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
}
if (luo_file->retrieve_status > 0) {
+ /*
+ * The argument only applies to the retrieve that restores the
+ * file. Don't silently ignore it.
+ */
+ if (arg)
+ return -EBUSY;
+
/*
* Someone is asking for this file again, so get a reference
* for them.
@@ -605,10 +661,15 @@ int luo_retrieve_file(struct luo_file_set *file_set, u64 token,
return 0;
}
+ err = luo_copy_user_arg(arg, &args.arg, &luo_file->fh->ops->rarg);
+ if (err)
+ return err;
+
args.handler = luo_file->fh;
args.session = luo_session_from_file_set(file_set);
args.serialized_data = luo_file->serialized_data;
err = luo_file->fh->ops->retrieve(&args);
+ kfree(args.arg);
if (err) {
/* Keep the error code for later use. */
luo_file->retrieve_status = err;
@@ -1007,6 +1068,6 @@ int liveupdate_get_file_incoming(struct liveupdate_session *s, u64 token,
struct file **filep)
{
return luo_retrieve_file(luo_file_set_from_session_locked(s),
- token, filep);
+ token, 0, filep);
}
EXPORT_SYMBOL_GPL(liveupdate_get_file_incoming);
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index dac6644bfb18..4126b71e9a2e 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -101,13 +101,14 @@ int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
-int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd);
+int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd,
+ u64 arg);
void luo_file_unpreserve_files(struct luo_file_set *file_set);
int luo_file_freeze(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser);
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,
+int luo_retrieve_file(struct luo_file_set *file_set, u64 token, u64 arg,
struct file **filep);
int luo_file_finish(struct luo_file_set *file_set);
int luo_file_deserialize(struct luo_file_set *file_set,
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index f48e9a4185f9..ed9d53c7db77 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -267,7 +267,8 @@ static int luo_session_preserve_fd(struct luo_session *session,
int err;
guard(mutex)(&session->mutex);
- err = luo_preserve_file(&session->file_set, argp->token, argp->fd);
+ err = luo_preserve_file(&session->file_set, argp->token, argp->fd,
+ argp->arg);
if (err)
return err;
@@ -290,7 +291,8 @@ static int luo_session_retrieve_fd(struct luo_session *session,
return argp->fd;
mutex_lock(&session->mutex);
- err = luo_retrieve_file(&session->file_set, argp->token, &file);
+ err = luo_retrieve_file(&session->file_set, argp->token, argp->arg,
+ &file);
mutex_unlock(&session->mutex);
if (err < 0)
goto err_put_fd;
--
2.56.0.257.gf7497c7d27-goog
next prev parent reply other threads:[~2026-10-22 22:50 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 22:50 [PATCH v2 00/10] liveupdate: Preserve tmpfs files across Live Update David Matlack
2026-10-22 22:50 ` David Matlack [this message]
2026-10-22 22:50 ` [PATCH v2 02/10] selftests: liveupdate: Add tests for preserve and retrieve user argument David Matlack
2026-10-22 22:50 ` [PATCH v2 03/10] liveupdate: Allow file handlers to mark retrieve failures as retryable David Matlack
2026-10-22 22:50 ` [PATCH v2 04/10] mm: Rename memfd_luo to shmem_luo David Matlack
2026-10-22 22:50 ` [PATCH v2 05/10] liveupdate: Don't WARN if tracking a retrieved file fails David Matlack
2026-10-22 22:50 ` [PATCH v2 06/10] mm/shmem_luo: Don't make early retrieve failures permanent David Matlack
2026-10-22 22:50 ` [PATCH v2 07/10] mm/shmem_luo: Retrieve tmpfs files onto a tmpfs mount chosen by userspace David Matlack
2026-10-22 22:50 ` [PATCH v2 08/10] mm/shmem_luo: Allow preserving tmpfs files that are still linked David Matlack
2026-10-22 22:50 ` [PATCH v2 09/10] mm/shmem_luo: Allow retrieving tmpfs files with O_RDONLY David Matlack
2026-10-22 22:50 ` [PATCH v2 10/10] selftests/liveupdate: Add a test for preserving tmpfs files David Matlack
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261022225035.377445-2-dmatlack@google.com \
--to=dmatlack@google.com \
--cc=akpm@linux-foundation.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=brauner@kernel.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=graf@amazon.com \
--cc=hughd@google.com \
--cc=kexec@lists.infradead.org \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=pasha.tatashin@soleen.com \
--cc=pratyush@kernel.org \
--cc=rdunlap@infradead.org \
--cc=rppt@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=skhawaja@google.com \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®