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 <[email protected]> --- 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

