[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aRoEduya5EO8Xc1b@kernel.org>
Date: Sun, 16 Nov 2025 19:05:58 +0200
From: Mike Rapoport <rppt@...nel.org>
To: Pasha Tatashin <pasha.tatashin@...een.com>
Cc: pratyush@...nel.org, jasonmiu@...gle.com, graf@...zon.com,
dmatlack@...gle.com, rientjes@...gle.com, corbet@....net,
rdunlap@...radead.org, ilpo.jarvinen@...ux.intel.com,
kanie@...ux.alibaba.com, ojeda@...nel.org, aliceryhl@...gle.com,
masahiroy@...nel.org, akpm@...ux-foundation.org, tj@...nel.org,
yoann.congal@...le.fr, mmaurer@...gle.com, roman.gushchin@...ux.dev,
chenridong@...wei.com, axboe@...nel.dk, mark.rutland@....com,
jannh@...gle.com, vincent.guittot@...aro.org, hannes@...xchg.org,
dan.j.williams@...el.com, david@...hat.com,
joel.granados@...nel.org, rostedt@...dmis.org,
anna.schumaker@...cle.com, song@...nel.org, linux@...ssschuh.net,
linux-kernel@...r.kernel.org, linux-doc@...r.kernel.org,
linux-mm@...ck.org, gregkh@...uxfoundation.org, tglx@...utronix.de,
mingo@...hat.com, bp@...en8.de, dave.hansen@...ux.intel.com,
x86@...nel.org, hpa@...or.com, rafael@...nel.org, dakr@...nel.org,
bartosz.golaszewski@...aro.org, cw00.choi@...sung.com,
myungjoo.ham@...sung.com, yesanishhere@...il.com,
Jonathan.Cameron@...wei.com, quic_zijuhu@...cinc.com,
aleksander.lobakin@...el.com, ira.weiny@...el.com,
andriy.shevchenko@...ux.intel.com, leon@...nel.org, lukas@...ner.de,
bhelgaas@...gle.com, wagi@...nel.org, djeffery@...hat.com,
stuart.w.hayes@...il.com, ptyadav@...zon.de, lennart@...ttering.net,
brauner@...nel.org, linux-api@...r.kernel.org,
linux-fsdevel@...r.kernel.org, saeedm@...dia.com,
ajayachandra@...dia.com, jgg@...dia.com, parav@...dia.com,
leonro@...dia.com, witu@...dia.com, hughd@...gle.com,
skhawaja@...gle.com, chrisl@...nel.org
Subject: Re: [PATCH v6 04/20] liveupdate: luo_session: add sessions support
On Sat, Nov 15, 2025 at 06:33:50PM -0500, Pasha Tatashin wrote:
> Introduce concept of "Live Update Sessions" within the LUO framework.
> LUO sessions provide a mechanism to group and manage `struct file *`
> instances (representing file descriptors) that need to be preserved
> across a kexec-based live update.
>
> Each session is identified by a unique name and acts as a container
> for file objects whose state is critical to a userspace workload, such
> as a virtual machine or a high-performance database, aiming to maintain
> their functionality across a kernel transition.
>
> This groundwork establishes the framework for preserving file-backed
> state across kernel updates, with the actual file data preservation
> mechanisms to be implemented in subsequent patches.
>
> Signed-off-by: Pasha Tatashin <pasha.tatashin@...een.com>
> ---
> include/linux/liveupdate/abi/luo.h | 83 +++++-
> include/uapi/linux/liveupdate.h | 3 +
> kernel/liveupdate/Makefile | 3 +-
> kernel/liveupdate/luo_core.c | 10 +
> kernel/liveupdate/luo_internal.h | 52 ++++
> kernel/liveupdate/luo_session.c | 421 +++++++++++++++++++++++++++++
> 6 files changed, 570 insertions(+), 2 deletions(-)
> create mode 100644 kernel/liveupdate/luo_internal.h
> create mode 100644 kernel/liveupdate/luo_session.c
...
> +/**
> + * struct luo_session_ser - Represents the serialized metadata for a LUO session.
> + * @name: The unique name of the session, copied from the `luo_session`
> + * structure.
I'd phase it as
The unique name of the session provided by the userspace at
the time of session creation.
> + * @files: The physical address of a contiguous memory block that holds
> + * the serialized state of files.
Maybe add ^ in this session?
> + * @pgcnt: The number of pages occupied by the `files` memory block.
> + * @count: The total number of files that were part of this session during
> + * serialization. Used for iteration and validation during
> + * restoration.
> + *
> + * This structure is used to package session-specific metadata for transfer
> + * between kernels via Kexec Handover. An array of these structures (one per
> + * session) is created and passed to the new kernel, allowing it to reconstruct
> + * the session context.
> + *
> + * If this structure is modified, LUO_SESSION_COMPATIBLE must be updated.
This comment applies to the luo_session_header_ser description as well.
> + */
> +struct luo_session_ser {
> + char name[LIVEUPDATE_SESSION_NAME_LENGTH];
> + u64 files;
> + u64 pgcnt;
> + u64 count;
> +} __packed;
> +
> #endif /* _LINUX_LIVEUPDATE_ABI_LUO_H */
> diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
> index df34c1642c4d..d2ef2f7e0dbd 100644
> --- a/include/uapi/linux/liveupdate.h
> +++ b/include/uapi/linux/liveupdate.h
> @@ -43,4 +43,7 @@
> /* The ioctl type, documented in ioctl-number.rst */
> #define LIVEUPDATE_IOCTL_TYPE 0xBA
>
> +/* The maximum length of session name including null termination */
> +#define LIVEUPDATE_SESSION_NAME_LENGTH 56
You decided not to bump it to 64 in the end? ;-)
> +
> #endif /* _UAPI_LIVEUPDATE_H */
> diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile
> index 413722002b7a..83285e7ad726 100644
> --- a/kernel/liveupdate/Makefile
> +++ b/kernel/liveupdate/Makefile
> @@ -2,7 +2,8 @@
>
> luo-y := \
> luo_core.o \
> - luo_ioctl.o
> + luo_ioctl.o \
> + luo_session.o
>
> obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o
> obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o
...
> +int luo_session_retrieve(const char *name, struct file **filep)
> +{
> + struct luo_session_header *sh = &luo_session_global.incoming;
> + struct luo_session *session = NULL;
> + struct luo_session *it;
> + int err;
> +
> + scoped_guard(rwsem_read, &sh->rwsem) {
> + list_for_each_entry(it, &sh->list, list) {
> + if (!strncmp(it->name, name, sizeof(it->name))) {
> + session = it;
> + break;
> + }
> + }
> + }
> +
> + if (!session)
> + return -ENOENT;
> +
> + scoped_guard(mutex, &session->mutex) {
> + if (session->retrieved)
> + return -EINVAL;
> + }
> +
> + err = luo_session_getfile(session, filep);
> + if (!err) {
> + scoped_guard(mutex, &session->mutex)
> + session->retrieved = true;
Retaking the mutex here seems a bit odd.
Do we really have to lock session->mutex in luo_session_getfile()?
> + }
> +
> + return err;
> +}
...
> +int __init luo_session_setup_incoming(void *fdt_in)
> +{
> + struct luo_session_header_ser *header_ser;
> + int err, header_size, offset;
> + u64 header_ser_pa;
> + const void *ptr;
> +
> + offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME);
> + if (offset < 0) {
> + pr_err("Unable to get session node: [%s]\n",
> + LUO_FDT_SESSION_NODE_NAME);
> + return -EINVAL;
> + }
> +
> + err = fdt_node_check_compatible(fdt_in, offset,
> + LUO_FDT_SESSION_COMPATIBLE);
> + if (err) {
> + pr_err("Session node incompatible [%s]\n",
> + LUO_FDT_SESSION_COMPATIBLE);
> + return -EINVAL;
> + }
> +
> + header_size = 0;
> + ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size);
> + if (!ptr || header_size != sizeof(u64)) {
> + pr_err("Unable to get session header '%s' [%d]\n",
> + LUO_FDT_SESSION_HEADER, header_size);
> + return -EINVAL;
> + }
> +
> + header_ser_pa = get_unaligned((u64 *)ptr);
> + header_ser = phys_to_virt(header_ser_pa);
> +
> + luo_session_global.incoming.header_ser = header_ser;
> + luo_session_global.incoming.ser = (void *)(header_ser + 1);
> + INIT_LIST_HEAD(&luo_session_global.incoming.list);
> + init_rwsem(&luo_session_global.incoming.rwsem);
> + luo_session_global.incoming.active = true;
> +
> + return 0;
> +}
> +
> +bool luo_session_is_deserialized(void)
> +{
> + return luo_session_global.deserialized;
> +}
> +
> +int luo_session_deserialize(void)
> +{
> + struct luo_session_header *sh = &luo_session_global.incoming;
> + int err;
> +
> + if (luo_session_is_deserialized())
> + return 0;
> +
> + luo_session_global.deserialized = true;
> + if (!sh->active) {
> + INIT_LIST_HEAD(&sh->list);
> + init_rwsem(&sh->rwsem);
> + return 0;
How this can happen? luo_session_deserialize() is supposed to be called
from ioctl and luo_session_global.incoming should be set up way earlier.
And, why don't we initialize ->list and ->rwsem statically?
> + }
> +
> + for (int i = 0; i < sh->header_ser->count; i++) {
> + struct luo_session *session;
> +
> + session = luo_session_alloc(sh->ser[i].name);
> + if (IS_ERR(session)) {
> + pr_warn("Failed to allocate session [%s] during deserialization %pe\n",
> + sh->ser[i].name, session);
> + return PTR_ERR(session);
> + }
The allocated sessions still need to be freed if an insert fails ;-)
> +
> + err = luo_session_insert(sh, session);
> + if (err) {
> + luo_session_free(session);
> + pr_warn("Failed to insert session [%s] %pe\n",
> + session->name, ERR_PTR(err));
> + return err;
> + }
> +
> + session->count = sh->ser[i].count;
> + session->files = sh->ser[i].files ? phys_to_virt(sh->ser[i].files) : 0;
> + session->pgcnt = sh->ser[i].pgcnt;
> + }
> +
> + kho_restore_free(sh->header_ser);
> + sh->header_ser = NULL;
> + sh->ser = NULL;
> +
> + return 0;
> +}
--
Sincerely yours,
Mike.
Powered by blists - more mailing lists