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: 31+ 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-23 22:51 ` [PATCH v2 01/10] liveupdate: Pass user argument to file preserve and retrieve sashiko-bot
2026-10-22 22:50 ` [PATCH v2 02/10] selftests: liveupdate: Add tests for preserve and retrieve user argument David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-22 22:50 ` [PATCH v2 03/10] liveupdate: Allow file handlers to mark retrieve failures as retryable David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-22 22:50 ` [PATCH v2 04/10] mm: Rename memfd_luo to shmem_luo David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-28 20:36 ` Pasha Tatashin
2026-10-22 22:50 ` [PATCH v2 05/10] liveupdate: Don't WARN if tracking a retrieved file fails David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-28 20:36 ` Pasha Tatashin
2026-10-22 22:50 ` [PATCH v2 06/10] mm/shmem_luo: Don't make early retrieve failures permanent David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-28 20:36 ` Pasha Tatashin
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-23 22:51 ` sashiko-bot
2026-10-27 5:04 ` kernel test robot
2026-10-27 5:38 ` kernel test robot
2026-10-28 22:02 ` Pasha Tatashin
2026-10-28 23:09 ` David Matlack
2026-10-29 0:04 ` Pasha Tatashin
2026-10-22 22:50 ` [PATCH v2 08/10] mm/shmem_luo: Allow preserving tmpfs files that are still linked David Matlack
2026-10-23 22:51 ` sashiko-bot
2026-10-28 23:22 ` 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-23 22:51 ` sashiko-bot
2026-10-28 23:13 ` David Matlack
2026-10-22 22:50 ` [PATCH v2 10/10] selftests/liveupdate: Add a test for preserving tmpfs files David Matlack
2026-10-23 22:51 ` sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.