Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

liveupdate: luo_session: add sessions support

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.

[dan.carpenter@linaro.org: fix use after free in luo_session_deserialize()]
Link: https://lkml.kernel.org/r/c5dd637d7eed3a3be48c5e9fedb881596a3b1f5a.1764163896.git.dan.carpenter@linaro.org
Link: https://lkml.kernel.org/r/20251125165850.3389713-5-pasha.tatashin@soleen.com
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Tested-by: David Matlack <dmatlack@google.com>
Cc: Aleksander Lobakin <aleksander.lobakin@intel.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Andriy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: anish kumar <yesanishhere@gmail.com>
Cc: Anna Schumaker <anna.schumaker@oracle.com>
Cc: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Borislav Betkov <bp@alien8.de>
Cc: Chanwoo Choi <cw00.choi@samsung.com>
Cc: Chen Ridong <chenridong@huawei.com>
Cc: Chris Li <chrisl@kernel.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Daniel Wagner <wagi@kernel.org>
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: David Jeffery <djeffery@redhat.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Guixin Liu <kanie@linux.alibaba.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Jann Horn <jannh@google.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Joanthan Cameron <Jonathan.Cameron@huawei.com>
Cc: Joel Granados <joel.granados@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lennart Poettering <lennart@poettering.net>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Leon Romanovsky <leonro@nvidia.com>
Cc: Lukas Wunner <lukas@wunner.de>
Cc: Marc Rutland <mark.rutland@arm.com>
Cc: Masahiro Yamada <masahiroy@kernel.org>
Cc: Matthew Maurer <mmaurer@google.com>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Myugnjoo Ham <myungjoo.ham@samsung.com>
Cc: Parav Pandit <parav@nvidia.com>
Cc: Pratyush Yadav <ptyadav@amazon.de>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Saeed Mahameed <saeedm@nvidia.com>
Cc: Samiullah Khawaja <skhawaja@google.com>
Cc: Song Liu <song@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Stuart Hayes <stuart.w.hayes@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: Thomas Weißschuh <linux@weissschuh.net>
Cc: Vincent Guittot <vincent.guittot@linaro.org>
Cc: William Tu <witu@nvidia.com>
Cc: Yoann Congal <yoann.congal@smile.fr>
Cc: Zhu Yanjun <yanjun.zhu@linux.dev>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Pasha Tatashin and committed by
Andrew Morton
0153094d db8bed80

+577 -1
+71
include/linux/kho/abi/luo.h
··· 32 32 * / { 33 33 * compatible = "luo-v1"; 34 34 * liveupdate-number = <...>; 35 + * 36 + * luo-session { 37 + * compatible = "luo-session-v1"; 38 + * luo-session-header = <phys_addr_of_session_header_ser>; 39 + * }; 35 40 * }; 36 41 * 37 42 * Main LUO Node (/): ··· 45 40 * Identifies the overall LUO ABI version. 46 41 * - liveupdate-number: u64 47 42 * A counter tracking the number of successful live updates performed. 43 + * 44 + * Session Node (luo-session): 45 + * This node describes all preserved user-space sessions. 46 + * 47 + * - compatible: "luo-session-v1" 48 + * Identifies the session ABI version. 49 + * - luo-session-header: u64 50 + * The physical address of a `struct luo_session_header_ser`. This structure 51 + * is the header for a contiguous block of memory containing an array of 52 + * `struct luo_session_ser`, one for each preserved session. 53 + * 54 + * Serialization Structures: 55 + * The FDT properties point to memory regions containing arrays of simple, 56 + * `__packed` structures. These structures contain the actual preserved state. 57 + * 58 + * - struct luo_session_header_ser: 59 + * Header for the session array. Contains the total page count of the 60 + * preserved memory block and the number of `struct luo_session_ser` 61 + * entries that follow. 62 + * 63 + * - struct luo_session_ser: 64 + * Metadata for a single session, including its name and a physical pointer 65 + * to another preserved memory block containing an array of 66 + * `struct luo_file_ser` for all files in that session. 48 67 */ 49 68 50 69 #ifndef _LINUX_KHO_ABI_LUO_H 51 70 #define _LINUX_KHO_ABI_LUO_H 71 + 72 + #include <uapi/linux/liveupdate.h> 52 73 53 74 /* 54 75 * The LUO FDT hooks all LUO state for sessions, fds, etc. ··· 85 54 #define LUO_FDT_KHO_ENTRY_NAME "LUO" 86 55 #define LUO_FDT_COMPATIBLE "luo-v1" 87 56 #define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number" 57 + 58 + /* 59 + * LUO FDT session node 60 + * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct 61 + * luo_session_header_ser 62 + */ 63 + #define LUO_FDT_SESSION_NODE_NAME "luo-session" 64 + #define LUO_FDT_SESSION_COMPATIBLE "luo-session-v1" 65 + #define LUO_FDT_SESSION_HEADER "luo-session-header" 66 + 67 + /** 68 + * struct luo_session_header_ser - Header for the serialized session data block. 69 + * @count: The number of `struct luo_session_ser` entries that immediately 70 + * follow this header in the memory block. 71 + * 72 + * This structure is located at the beginning of a contiguous block of 73 + * physical memory preserved across the kexec. It provides the necessary 74 + * metadata to interpret the array of session entries that follow. 75 + * 76 + * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. 77 + */ 78 + struct luo_session_header_ser { 79 + u64 count; 80 + } __packed; 81 + 82 + /** 83 + * struct luo_session_ser - Represents the serialized metadata for a LUO session. 84 + * @name: The unique name of the session, provided by the userspace at 85 + * the time of session creation. 86 + * 87 + * This structure is used to package session-specific metadata for transfer 88 + * between kernels via Kexec Handover. An array of these structures (one per 89 + * session) is created and passed to the new kernel, allowing it to reconstruct 90 + * the session context. 91 + * 92 + * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated. 93 + */ 94 + struct luo_session_ser { 95 + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; 96 + } __packed; 88 97 89 98 #endif /* _LINUX_KHO_ABI_LUO_H */
+3
include/uapi/linux/liveupdate.h
··· 43 43 /* The ioctl type, documented in ioctl-number.rst */ 44 44 #define LIVEUPDATE_IOCTL_TYPE 0xBA 45 45 46 + /* The maximum length of session name including null termination */ 47 + #define LIVEUPDATE_SESSION_NAME_LENGTH 64 48 + 46 49 #endif /* _UAPI_LIVEUPDATE_H */
+2 -1
kernel/liveupdate/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 luo-y := \ 4 - luo_core.o 4 + luo_core.o \ 5 + luo_session.o 5 6 6 7 obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o 7 8 obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o
+9
kernel/liveupdate/luo_core.c
··· 118 118 pr_info("Retrieved live update data, liveupdate number: %lld\n", 119 119 luo_global.liveupdate_num); 120 120 121 + err = luo_session_setup_incoming(luo_global.fdt_in); 122 + if (err) 123 + return err; 124 + 121 125 return 0; 122 126 } 123 127 ··· 158 154 err |= fdt_begin_node(fdt_out, ""); 159 155 err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); 160 156 err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); 157 + err |= luo_session_setup_outgoing(fdt_out); 161 158 err |= fdt_end_node(fdt_out); 162 159 err |= fdt_finish(fdt_out); 163 160 if (err) ··· 215 210 216 211 if (!liveupdate_enabled()) 217 212 return 0; 213 + 214 + err = luo_session_serialize(); 215 + if (err) 216 + return err; 218 217 219 218 err = kho_finalize(); 220 219 if (err) {
+29
kernel/liveupdate/luo_internal.h
··· 19 19 */ 20 20 #define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__) 21 21 22 + /** 23 + * struct luo_session - Represents an active or incoming Live Update session. 24 + * @name: A unique name for this session, used for identification and 25 + * retrieval. 26 + * @ser: Pointer to the serialized data for this session. 27 + * @list: A list_head member used to link this session into a global list 28 + * of either outgoing (to be preserved) or incoming (restored from 29 + * previous kernel) sessions. 30 + * @retrieved: A boolean flag indicating whether this session has been 31 + * retrieved by a consumer in the new kernel. 32 + * @mutex: protects fields in the luo_session. 33 + */ 34 + struct luo_session { 35 + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; 36 + struct luo_session_ser *ser; 37 + struct list_head list; 38 + bool retrieved; 39 + struct mutex mutex; 40 + }; 41 + 42 + int luo_session_create(const char *name, struct file **filep); 43 + int luo_session_retrieve(const char *name, struct file **filep); 44 + int __init luo_session_setup_outgoing(void *fdt); 45 + int __init luo_session_setup_incoming(void *fdt); 46 + int luo_session_serialize(void); 47 + int luo_session_deserialize(void); 48 + bool luo_session_quiesce(void); 49 + void luo_session_resume(void); 50 + 22 51 #endif /* _LINUX_LUO_INTERNAL_H */
+463
kernel/liveupdate/luo_session.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Copyright (c) 2025, Google LLC. 5 + * Pasha Tatashin <pasha.tatashin@soleen.com> 6 + */ 7 + 8 + /** 9 + * DOC: LUO Sessions 10 + * 11 + * LUO Sessions provide the core mechanism for grouping and managing `struct 12 + * file *` instances that need to be preserved across a kexec-based live 13 + * update. Each session acts as a named container for a set of file objects, 14 + * allowing a userspace agent to manage the lifecycle of resources critical to a 15 + * workload. 16 + * 17 + * Core Concepts: 18 + * 19 + * - Named Containers: Sessions are identified by a unique, user-provided name, 20 + * which is used for both creation in the current kernel and retrieval in the 21 + * next kernel. 22 + * 23 + * - Userspace Interface: Session management is driven from userspace via 24 + * ioctls on /dev/liveupdate. 25 + * 26 + * - Serialization: Session metadata is preserved using the KHO framework. When 27 + * a live update is triggered via kexec, an array of `struct luo_session_ser` 28 + * is populated and placed in a preserved memory region. An FDT node is also 29 + * created, containing the count of sessions and the physical address of this 30 + * array. 31 + * 32 + * Session Lifecycle: 33 + * 34 + * 1. Creation: A userspace agent calls `luo_session_create()` to create a 35 + * new, empty session and receives a file descriptor for it. 36 + * 37 + * 2. Serialization: When the `reboot(LINUX_REBOOT_CMD_KEXEC)` syscall is 38 + * made, `luo_session_serialize()` is called. It iterates through all 39 + * active sessions and writes their metadata into a memory area preserved 40 + * by KHO. 41 + * 42 + * 3. Deserialization (in new kernel): After kexec, `luo_session_deserialize()` 43 + * runs, reading the serialized data and creating a list of `struct 44 + * luo_session` objects representing the preserved sessions. 45 + * 46 + * 4. Retrieval: A userspace agent in the new kernel can then call 47 + * `luo_session_retrieve()` with a session name to get a new file 48 + * descriptor and access the preserved state. 49 + */ 50 + 51 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 52 + 53 + #include <linux/anon_inodes.h> 54 + #include <linux/cleanup.h> 55 + #include <linux/err.h> 56 + #include <linux/errno.h> 57 + #include <linux/file.h> 58 + #include <linux/fs.h> 59 + #include <linux/io.h> 60 + #include <linux/kexec_handover.h> 61 + #include <linux/kho/abi/luo.h> 62 + #include <linux/libfdt.h> 63 + #include <linux/list.h> 64 + #include <linux/liveupdate.h> 65 + #include <linux/mutex.h> 66 + #include <linux/rwsem.h> 67 + #include <linux/slab.h> 68 + #include <linux/unaligned.h> 69 + #include <uapi/linux/liveupdate.h> 70 + #include "luo_internal.h" 71 + 72 + /* 16 4K pages, give space for 744 sessions */ 73 + #define LUO_SESSION_PGCNT 16ul 74 + #define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ 75 + sizeof(struct luo_session_header_ser)) / \ 76 + sizeof(struct luo_session_ser)) 77 + 78 + /** 79 + * struct luo_session_header - Header struct for managing LUO sessions. 80 + * @count: The number of sessions currently tracked in the @list. 81 + * @list: The head of the linked list of `struct luo_session` instances. 82 + * @rwsem: A read-write semaphore providing synchronized access to the 83 + * session list and other fields in this structure. 84 + * @header_ser: The header data of serialization array. 85 + * @ser: The serialized session data (an array of 86 + * `struct luo_session_ser`). 87 + * @active: Set to true when first initialized. If previous kernel did not 88 + * send session data, active stays false for incoming. 89 + */ 90 + struct luo_session_header { 91 + long count; 92 + struct list_head list; 93 + struct rw_semaphore rwsem; 94 + struct luo_session_header_ser *header_ser; 95 + struct luo_session_ser *ser; 96 + bool active; 97 + }; 98 + 99 + /** 100 + * struct luo_session_global - Global container for managing LUO sessions. 101 + * @incoming: The sessions passed from the previous kernel. 102 + * @outgoing: The sessions that are going to be passed to the next kernel. 103 + */ 104 + struct luo_session_global { 105 + struct luo_session_header incoming; 106 + struct luo_session_header outgoing; 107 + }; 108 + 109 + static struct luo_session_global luo_session_global = { 110 + .incoming = { 111 + .list = LIST_HEAD_INIT(luo_session_global.incoming.list), 112 + .rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), 113 + }, 114 + .outgoing = { 115 + .list = LIST_HEAD_INIT(luo_session_global.outgoing.list), 116 + .rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), 117 + }, 118 + }; 119 + 120 + static struct luo_session *luo_session_alloc(const char *name) 121 + { 122 + struct luo_session *session = kzalloc(sizeof(*session), GFP_KERNEL); 123 + 124 + if (!session) 125 + return ERR_PTR(-ENOMEM); 126 + 127 + strscpy(session->name, name, sizeof(session->name)); 128 + INIT_LIST_HEAD(&session->list); 129 + mutex_init(&session->mutex); 130 + 131 + return session; 132 + } 133 + 134 + static void luo_session_free(struct luo_session *session) 135 + { 136 + mutex_destroy(&session->mutex); 137 + kfree(session); 138 + } 139 + 140 + static int luo_session_insert(struct luo_session_header *sh, 141 + struct luo_session *session) 142 + { 143 + struct luo_session *it; 144 + 145 + guard(rwsem_write)(&sh->rwsem); 146 + 147 + /* 148 + * For outgoing we should make sure there is room in serialization array 149 + * for new session. 150 + */ 151 + if (sh == &luo_session_global.outgoing) { 152 + if (sh->count == LUO_SESSION_MAX) 153 + return -ENOMEM; 154 + } 155 + 156 + /* 157 + * For small number of sessions this loop won't hurt performance 158 + * but if we ever start using a lot of sessions, this might 159 + * become a bottle neck during deserialization time, as it would 160 + * cause O(n*n) complexity. 161 + */ 162 + list_for_each_entry(it, &sh->list, list) { 163 + if (!strncmp(it->name, session->name, sizeof(it->name))) 164 + return -EEXIST; 165 + } 166 + list_add_tail(&session->list, &sh->list); 167 + sh->count++; 168 + 169 + return 0; 170 + } 171 + 172 + static void luo_session_remove(struct luo_session_header *sh, 173 + struct luo_session *session) 174 + { 175 + guard(rwsem_write)(&sh->rwsem); 176 + list_del(&session->list); 177 + sh->count--; 178 + } 179 + 180 + static int luo_session_release(struct inode *inodep, struct file *filep) 181 + { 182 + struct luo_session *session = filep->private_data; 183 + struct luo_session_header *sh; 184 + 185 + /* If retrieved is set, it means this session is from incoming list */ 186 + if (session->retrieved) 187 + sh = &luo_session_global.incoming; 188 + else 189 + sh = &luo_session_global.outgoing; 190 + 191 + luo_session_remove(sh, session); 192 + luo_session_free(session); 193 + 194 + return 0; 195 + } 196 + 197 + static const struct file_operations luo_session_fops = { 198 + .owner = THIS_MODULE, 199 + .release = luo_session_release, 200 + }; 201 + 202 + /* Create a "struct file" for session */ 203 + static int luo_session_getfile(struct luo_session *session, struct file **filep) 204 + { 205 + char name_buf[128]; 206 + struct file *file; 207 + 208 + lockdep_assert_held(&session->mutex); 209 + snprintf(name_buf, sizeof(name_buf), "[luo_session] %s", session->name); 210 + file = anon_inode_getfile(name_buf, &luo_session_fops, session, O_RDWR); 211 + if (IS_ERR(file)) 212 + return PTR_ERR(file); 213 + 214 + *filep = file; 215 + 216 + return 0; 217 + } 218 + 219 + int luo_session_create(const char *name, struct file **filep) 220 + { 221 + struct luo_session *session; 222 + int err; 223 + 224 + session = luo_session_alloc(name); 225 + if (IS_ERR(session)) 226 + return PTR_ERR(session); 227 + 228 + err = luo_session_insert(&luo_session_global.outgoing, session); 229 + if (err) 230 + goto err_free; 231 + 232 + scoped_guard(mutex, &session->mutex) 233 + err = luo_session_getfile(session, filep); 234 + if (err) 235 + goto err_remove; 236 + 237 + return 0; 238 + 239 + err_remove: 240 + luo_session_remove(&luo_session_global.outgoing, session); 241 + err_free: 242 + luo_session_free(session); 243 + 244 + return err; 245 + } 246 + 247 + int luo_session_retrieve(const char *name, struct file **filep) 248 + { 249 + struct luo_session_header *sh = &luo_session_global.incoming; 250 + struct luo_session *session = NULL; 251 + struct luo_session *it; 252 + int err; 253 + 254 + scoped_guard(rwsem_read, &sh->rwsem) { 255 + list_for_each_entry(it, &sh->list, list) { 256 + if (!strncmp(it->name, name, sizeof(it->name))) { 257 + session = it; 258 + break; 259 + } 260 + } 261 + } 262 + 263 + if (!session) 264 + return -ENOENT; 265 + 266 + guard(mutex)(&session->mutex); 267 + if (session->retrieved) 268 + return -EINVAL; 269 + 270 + err = luo_session_getfile(session, filep); 271 + if (!err) 272 + session->retrieved = true; 273 + 274 + return err; 275 + } 276 + 277 + int __init luo_session_setup_outgoing(void *fdt_out) 278 + { 279 + struct luo_session_header_ser *header_ser; 280 + u64 header_ser_pa; 281 + int err; 282 + 283 + header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); 284 + if (IS_ERR(header_ser)) 285 + return PTR_ERR(header_ser); 286 + header_ser_pa = virt_to_phys(header_ser); 287 + 288 + err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME); 289 + err |= fdt_property_string(fdt_out, "compatible", 290 + LUO_FDT_SESSION_COMPATIBLE); 291 + err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa, 292 + sizeof(header_ser_pa)); 293 + err |= fdt_end_node(fdt_out); 294 + 295 + if (err) 296 + goto err_unpreserve; 297 + 298 + luo_session_global.outgoing.header_ser = header_ser; 299 + luo_session_global.outgoing.ser = (void *)(header_ser + 1); 300 + luo_session_global.outgoing.active = true; 301 + 302 + return 0; 303 + 304 + err_unpreserve: 305 + kho_unpreserve_free(header_ser); 306 + return err; 307 + } 308 + 309 + int __init luo_session_setup_incoming(void *fdt_in) 310 + { 311 + struct luo_session_header_ser *header_ser; 312 + int err, header_size, offset; 313 + u64 header_ser_pa; 314 + const void *ptr; 315 + 316 + offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME); 317 + if (offset < 0) { 318 + pr_err("Unable to get session node: [%s]\n", 319 + LUO_FDT_SESSION_NODE_NAME); 320 + return -EINVAL; 321 + } 322 + 323 + err = fdt_node_check_compatible(fdt_in, offset, 324 + LUO_FDT_SESSION_COMPATIBLE); 325 + if (err) { 326 + pr_err("Session node incompatible [%s]\n", 327 + LUO_FDT_SESSION_COMPATIBLE); 328 + return -EINVAL; 329 + } 330 + 331 + header_size = 0; 332 + ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size); 333 + if (!ptr || header_size != sizeof(u64)) { 334 + pr_err("Unable to get session header '%s' [%d]\n", 335 + LUO_FDT_SESSION_HEADER, header_size); 336 + return -EINVAL; 337 + } 338 + 339 + header_ser_pa = get_unaligned((u64 *)ptr); 340 + header_ser = phys_to_virt(header_ser_pa); 341 + 342 + luo_session_global.incoming.header_ser = header_ser; 343 + luo_session_global.incoming.ser = (void *)(header_ser + 1); 344 + luo_session_global.incoming.active = true; 345 + 346 + return 0; 347 + } 348 + 349 + int luo_session_deserialize(void) 350 + { 351 + struct luo_session_header *sh = &luo_session_global.incoming; 352 + static bool is_deserialized; 353 + static int err; 354 + 355 + /* If has been deserialized, always return the same error code */ 356 + if (is_deserialized) 357 + return err; 358 + 359 + is_deserialized = true; 360 + if (!sh->active) 361 + return 0; 362 + 363 + /* 364 + * Note on error handling: 365 + * 366 + * If deserialization fails (e.g., allocation failure or corrupt data), 367 + * we intentionally skip cleanup of sessions that were already restored. 368 + * 369 + * A partial failure leaves the preserved state inconsistent. 370 + * Implementing a safe "undo" to unwind complex dependencies (sessions, 371 + * files, hardware state) is error-prone and provides little value, as 372 + * the system is effectively in a broken state. 373 + * 374 + * We treat these resources as leaked. The expected recovery path is for 375 + * userspace to detect the failure and trigger a reboot, which will 376 + * reliably reset devices and reclaim memory. 377 + */ 378 + for (int i = 0; i < sh->header_ser->count; i++) { 379 + struct luo_session *session; 380 + 381 + session = luo_session_alloc(sh->ser[i].name); 382 + if (IS_ERR(session)) { 383 + pr_warn("Failed to allocate session [%s] during deserialization %pe\n", 384 + sh->ser[i].name, session); 385 + return PTR_ERR(session); 386 + } 387 + 388 + err = luo_session_insert(sh, session); 389 + if (err) { 390 + pr_warn("Failed to insert session [%s] %pe\n", 391 + session->name, ERR_PTR(err)); 392 + luo_session_free(session); 393 + return err; 394 + } 395 + } 396 + 397 + kho_restore_free(sh->header_ser); 398 + sh->header_ser = NULL; 399 + sh->ser = NULL; 400 + 401 + return 0; 402 + } 403 + 404 + int luo_session_serialize(void) 405 + { 406 + struct luo_session_header *sh = &luo_session_global.outgoing; 407 + struct luo_session *session; 408 + int i = 0; 409 + 410 + guard(rwsem_write)(&sh->rwsem); 411 + list_for_each_entry(session, &sh->list, list) { 412 + strscpy(sh->ser[i].name, session->name, 413 + sizeof(sh->ser[i].name)); 414 + i++; 415 + } 416 + sh->header_ser->count = sh->count; 417 + 418 + return 0; 419 + } 420 + 421 + /** 422 + * luo_session_quiesce - Ensure no active sessions exist and lock session lists. 423 + * 424 + * Acquires exclusive write locks on both incoming and outgoing session lists. 425 + * It then validates no sessions exist in either list. 426 + * 427 + * This mechanism is used during file handler un/registration to ensure that no 428 + * sessions are currently using the handler, and no new sessions can be created 429 + * while un/registration is in progress. 430 + * 431 + * This prevents registering new handlers while sessions are active or 432 + * while deserialization is in progress. 433 + * 434 + * Return: 435 + * true - System is quiescent (0 sessions) and locked. 436 + * false - Active sessions exist. The locks are released internally. 437 + */ 438 + bool luo_session_quiesce(void) 439 + { 440 + down_write(&luo_session_global.incoming.rwsem); 441 + down_write(&luo_session_global.outgoing.rwsem); 442 + 443 + if (luo_session_global.incoming.count || 444 + luo_session_global.outgoing.count) { 445 + up_write(&luo_session_global.outgoing.rwsem); 446 + up_write(&luo_session_global.incoming.rwsem); 447 + return false; 448 + } 449 + 450 + return true; 451 + } 452 + 453 + /** 454 + * luo_session_resume - Unlock session lists and resume normal activity. 455 + * 456 + * Releases the exclusive locks acquired by a successful call to 457 + * luo_session_quiesce(). 458 + */ 459 + void luo_session_resume(void) 460 + { 461 + up_write(&luo_session_global.outgoing.rwsem); 462 + up_write(&luo_session_global.incoming.rwsem); 463 + }