lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250929010321.3462457-12-pasha.tatashin@soleen.com>
Date: Mon, 29 Sep 2025 01:03:02 +0000
From: Pasha Tatashin <pasha.tatashin@...een.com>
To: pratyush@...nel.org,
	jasonmiu@...gle.com,
	graf@...zon.com,
	changyuanl@...gle.com,
	pasha.tatashin@...een.com,
	rppt@...nel.org,
	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,
	zhangguopeng@...inos.cn,
	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,
	steven.sistare@...cle.com
Subject: [PATCH v4 11/30] 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.

Signed-off-by: Pasha Tatashin <pasha.tatashin@...een.com>
---
 include/uapi/linux/liveupdate.h  |   3 +
 kernel/liveupdate/Makefile       |   1 +
 kernel/liveupdate/luo_internal.h |  34 ++
 kernel/liveupdate/luo_session.c  | 607 +++++++++++++++++++++++++++++++
 4 files changed, 645 insertions(+)
 create mode 100644 kernel/liveupdate/luo_session.c

diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
index 3cb09b2c4353..e8c0c210a790 100644
--- a/include/uapi/linux/liveupdate.h
+++ b/include/uapi/linux/liveupdate.h
@@ -91,4 +91,7 @@ enum liveupdate_event {
 	LIVEUPDATE_CANCEL = 3,
 };
 
+/* The maximum length of session name including null termination */
+#define LIVEUPDATE_SESSION_NAME_LENGTH 56
+
 #endif /* _UAPI_LIVEUPDATE_H */
diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile
index 2881bab0c6df..f64cfc92cbf0 100644
--- a/kernel/liveupdate/Makefile
+++ b/kernel/liveupdate/Makefile
@@ -3,6 +3,7 @@
 luo-y :=								\
 		luo_core.o						\
 		luo_ioctl.o						\
+		luo_session.o						\
 		luo_subsystems.o
 
 obj-$(CONFIG_KEXEC_HANDOVER)		+= kexec_handover.o
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index c62fbbb0790c..9223f71844ca 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -32,6 +32,40 @@ void *luo_contig_alloc_preserve(size_t size);
 void luo_contig_free_unpreserve(void *mem, size_t size);
 void luo_contig_free_restore(void *mem, size_t size);
 
+/**
+ * struct luo_session - Represents an active or incoming Live Update session.
+ * @name:      A unique name for this session, used for identification and
+ *             retrieval.
+ * @files_xa:  An xarray used to store the files associated with this session.
+ * @ser:       Pointer to the serialized data for this session.
+ * @count:     A counter tracking the number of files currently stored in the
+ *             @files_xa for this session.
+ * @list:      A list_head member used to link this session into a global list
+ *             of either outgoing (to be preserved) or incoming (restored from
+ *             previous kernel) sessions.
+ * @retrieved: A boolean flag indicating whether this session has been retrieved
+ *             by a consumer in the new kernel. Valid only during the
+ *             LIVEUPDATE_STATE_UPDATED state.
+ * @mutex:     Session lock, protects files_xa, and count.
+ * @state:     State of this session: prepared/frozen/updated/normal.
+ * @files:     The physical address of a contiguous memory block that holds
+ *             the serialized state of files.
+ */
+struct luo_session {
+	char name[LIVEUPDATE_SESSION_NAME_LENGTH];
+	struct xarray files_xa;
+	struct luo_session_ser *ser;
+	long count;
+	struct list_head list;
+	bool retrieved;
+	struct mutex mutex;
+	enum liveupdate_state state;
+	u64 files;
+};
+
+int luo_session_create(const char *name, struct file **filep);
+int luo_session_retrieve(const char *name, struct file **filep);
+
 void luo_subsystems_startup(void *fdt);
 int luo_subsystems_fdt_setup(void *fdt);
 int luo_do_subsystems_prepare_calls(void);
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
new file mode 100644
index 000000000000..74dee42e24b7
--- /dev/null
+++ b/kernel/liveupdate/luo_session.c
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@...een.com>
+ */
+
+/**
+ * DOC: LUO Sessions
+ *
+ * LUO Sessions provide the core mechanism for grouping and managing file
+ * descriptors that need to be preserved across a kexec-based live update.
+ * Each session acts as a named container for a set of file objects, allowing
+ * a userspace agent (e.g., a Live Update Orchestration Daemon) to manage the
+ * lifecycle of resources critical to a workload.
+ *
+ * Core Concepts:
+ *
+ * - Named Containers: Sessions are identified by a unique, user-provided name,
+ *   which is used for both creation and retrieval.
+ *
+ * - Userspace Interface: Session management is driven from userspace via
+ *   ioctls on /dev/liveupdate (e.g., CREATE_SESSION, RETRIEVE_SESSION).
+ *
+ * - Serialization: Session metadata is preserved using the KHO framework.
+ *   During the 'prepare' phase, an array of `struct luo_session_ser` is
+ *   allocated and preserved. An FDT node is also created, containing the
+ *   count of sessions and the physical address of this array.
+ *
+ * Session Lifecycle and State Management:
+ *
+ * 1.  Creation: A userspace agent calls `luo_session_create()` to create a new,
+ *     empty session, receiving a file descriptor handle. This can be done in
+ *     the NORMAL or UPDATED states.
+ *
+ * 2.  Name Collision: In the UPDATED state, `luo_session_create()` checks for
+ *     name conflicts against sessions preserved from the previous kernel to
+ *     prevent ambiguity.
+ *
+ * 3.  Preparation (`prepare` callback): When the global LUO PREPARE event is
+ *     triggered, the list of all created sessions is serialized. The main
+ *     `ser` array is allocated, and each active `struct luo_session` is given
+ *     a direct pointer to its corresponding entry in this array.
+ *
+ * 4.  Release After Prepare: When a session FD is closed *after* the PREPARE
+ *     event, the `.release` handler uses the session's direct pointer to
+ *     `memset(0)` its entry in the `ser` array. This effectively marks the
+ *     session as defunct without needing to resize the already-preserved
+ *     memory.
+ *
+ * 5.  Boot (`boot` callback): In the new kernel, the FDT is read to locate
+ *     the preserved `ser` array. The metadata (count, physical address) is
+ *     stored in the `luo_session` global.
+ *
+ * 6.  Lazy Deserialization: The actual `luo_session` list is populated on
+ *     first use (e.g., by `retrieve`, `finish`, or `create`). During this
+ *     process, any zeroed-out entries from step 4 are skipped.
+ *
+ * 7.  Retrieval: The userspace agent calls `luo_session_retrieve()` in the new
+ *     kernel to get a new FD handle for a preserved session by its name.
+ *
+ * 8.  Finalization (`finish` callback): When the global LUO FINISH event is
+ *     sent, any preserved sessions that were successfully retrieved are moved
+ *     to the `luo_session_global` list, making them available for a subsequent
+ *     live update. Any sessions that were not retrieved are considered stale
+ *     and are cleaned up.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/anon_inodes.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/libfdt.h>
+#include <linux/liveupdate.h>
+#include <uapi/linux/liveupdate.h>
+#include "luo_internal.h"
+
+#define LUO_SESSION_NODE_NAME	"luo-session"
+#define LUO_SESSION_COMPATIBLE	"luo-session-v1"
+
+/**
+ * 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.
+ * @files:   The physical address of a contiguous memory block that holds
+ *           the serialized state of files.
+ * @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.
+ */
+struct luo_session_ser {
+	char name[LIVEUPDATE_SESSION_NAME_LENGTH];
+	u64 files;
+	u64 pgcnt;
+	u64 count;
+} __packed;
+
+/**
+ * struct luo_session_global - Global container for managing LUO sessions.
+ * @count: The number of sessions currently tracked in the @list.
+ * @list:  The head of the linked list of `struct luo_session` instances.
+ * @rwsem: A read-write semaphore providing synchronized access to the session
+ *         list and other fields in this structure.
+ * @ser:   A pointer to the contiguous block of memory holding the serialized
+ *         session data (an array of `struct luo_session_ser`). For `_out`, this
+ *         is allocated and populated during `prepare`. For `_in`, this points
+ *         to the data restored from the previous kernel.
+ * @pgcnt: The size, in pages, of the memory block pointed to by @ser.
+ * @fdt:   A pointer to the FDT blob that contains the metadata for this group
+ *         of sessions. This FDT is what is ultimately passed to the parent LUO
+ *         subsystem for preservation.
+ */
+struct luo_session_global {
+	long count;
+	struct list_head list;
+	struct rw_semaphore rwsem;
+	struct luo_session_ser *ser;
+	u64 pgcnt;
+	void *fdt;
+	long ser_count;
+};
+
+static struct luo_session_global luo_session_global;
+
+static struct luo_session *luo_session_alloc(const char *name)
+{
+	struct luo_session *session = kzalloc(sizeof(*session), GFP_KERNEL);
+
+	if (!session)
+		return NULL;
+
+	strscpy(session->name, name, sizeof(session->name));
+	xa_init(&session->files_xa);
+	session->count = 0;
+	INIT_LIST_HEAD(&session->list);
+	mutex_init(&session->mutex);
+	session->state = LIVEUPDATE_STATE_NORMAL;
+
+	return session;
+}
+
+static void luo_session_free(struct luo_session *session)
+{
+	WARN_ON(session->count);
+	xa_destroy(&session->files_xa);
+	mutex_destroy(&session->mutex);
+	kfree(session);
+}
+
+static int luo_session_insert(struct luo_session *session)
+{
+	struct luo_session *it;
+
+	lockdep_assert_held_write(&luo_session_global.rwsem);
+	/*
+	 * For small number of sessions this loop won't hurt performance
+	 * but if we ever start using a lot of sessions, this might
+	 * become a bottle neck during deserialization time, as it would
+	 * cause O(n*n) complexity.
+	 */
+	list_for_each_entry(it, &luo_session_global.list, list) {
+		if (!strncmp(it->name, session->name, sizeof(it->name)))
+			return -EEXIST;
+	}
+	list_add_tail(&session->list, &luo_session_global.list);
+	luo_session_global.count++;
+
+	return 0;
+}
+
+static void luo_session_remove(struct luo_session *session)
+{
+	lockdep_assert_held_write(&luo_session_global.rwsem);
+	list_del(&session->list);
+	luo_session_global.count--;
+}
+
+/* One session switches from the updated state to  normal state */
+static void luo_session_finish_one(struct luo_session *session)
+{
+}
+
+/* Cancel one session from frozen or prepared state, back to normal */
+static void luo_session_cancel_one(struct luo_session *session)
+{
+}
+
+/* One session is changed from normal to prepare state */
+static int luo_session_prepare_one(struct luo_session *session)
+{
+	return 0;
+}
+
+static int luo_session_release(struct inode *inodep, struct file *filep)
+{
+	struct luo_session *session = filep->private_data;
+
+	scoped_guard(rwsem_read, &luo_session_global.rwsem) {
+		scoped_guard(mutex, &session->mutex) {
+			if (session->ser) {
+				memset(session->ser, 0,
+				       sizeof(struct luo_session_ser));
+			}
+		}
+	}
+
+	if (session->state == LIVEUPDATE_STATE_UPDATED)
+		luo_session_finish_one(session);
+	if (session->state == LIVEUPDATE_STATE_PREPARED ||
+	    session->state == LIVEUPDATE_STATE_FROZEN) {
+		luo_session_cancel_one(session);
+	}
+
+	scoped_guard(rwsem_write, &luo_session_global.rwsem)
+		luo_session_remove(session);
+	luo_session_free(session);
+
+	return 0;
+}
+
+static const struct file_operations luo_session_fops = {
+	.owner = THIS_MODULE,
+	.release = luo_session_release,
+};
+
+static void luo_session_deserialize(void)
+{
+	static int visited;
+	int i;
+
+	if (visited)
+		return;
+
+	guard(rwsem_write)(&luo_session_global.rwsem);
+	if (visited)
+		return;
+	visited++;
+	for (i = 0; i < luo_session_global.ser_count; i++) {
+		struct luo_session *session;
+
+		/*
+		 * If there is no name, this session was remove from
+		 * preservation after prepare. So, skip it.
+		 */
+		if (!luo_session_global.ser[i].name[0])
+			continue;
+
+		session = luo_session_alloc(luo_session_global.ser[i].name);
+		if (!session)
+			luo_restore_fail("Failed to allocate session on boot\n");
+
+		if (luo_session_insert(session)) {
+			luo_restore_fail("Failed to insert session due to name conflict [%s]\n",
+					 session->name);
+		}
+
+		session->state = LIVEUPDATE_STATE_UPDATED;
+		session->count = luo_session_global.ser[i].count;
+		session->files = luo_session_global.ser[i].files;
+	}
+}
+
+/* Create a "struct file" for session, and delete it on case of failure */
+static int luo_session_getfile(struct luo_session *session, struct file **filep)
+{
+	char name_buf[128];
+	struct file *file;
+
+	scoped_guard(mutex, &session->mutex) {
+		lockdep_assert_held(&session->mutex);
+		snprintf(name_buf, sizeof(name_buf), "[luo_session] %s",
+			 session->name);
+		file = anon_inode_getfile(name_buf, &luo_session_fops, session,
+					  O_RDWR);
+	}
+	if (IS_ERR(file)) {
+		scoped_guard(rwsem_write, &luo_session_global.rwsem)
+			luo_session_remove(session);
+		luo_session_free(session);
+		return PTR_ERR(file);
+	}
+
+	*filep = file;
+	return 0;
+}
+
+int luo_session_create(const char *name, struct file **filep)
+{
+	struct luo_session *session;
+	int ret;
+
+	guard(rwsem_read)(&luo_state_rwsem);
+
+	/* New sessions cannot be added after prepared state */
+	if (!liveupdate_state_normal() && !liveupdate_state_updated())
+		return -EAGAIN;
+
+	session = luo_session_alloc(name);
+	if (!session)
+		return -ENOMEM;
+
+	scoped_guard(rwsem_write, &luo_session_global.rwsem)
+		ret = luo_session_insert(session);
+	if (ret) {
+		luo_session_free(session);
+		return ret;
+	}
+
+	return luo_session_getfile(session, filep);
+}
+
+int luo_session_retrieve(const char *name, struct file **filep)
+{
+	struct luo_session *session = NULL;
+	struct luo_session *it;
+
+	guard(rwsem_read)(&luo_state_rwsem);
+
+	/* Can only retrieve in the updated state */
+	if (!liveupdate_state_updated())
+		return -EAGAIN;
+
+	luo_session_deserialize();
+	scoped_guard(rwsem_read, &luo_session_global.rwsem) {
+		list_for_each_entry(it, &luo_session_global.list, list) {
+			if (!strncmp(it->name, name, sizeof(it->name))) {
+				session = it;
+				break;
+			}
+		}
+	}
+
+	if (!session)
+		return -ENOENT;
+
+	scoped_guard(mutex, &session->mutex) {
+		/*
+		 * Session already retrieved or a session with the same name was
+		 * created during updated state
+		 */
+		if (session->retrieved || session->state != LIVEUPDATE_STATE_UPDATED)
+			return -EADDRINUSE;
+
+		session->retrieved = true;
+	}
+
+	return luo_session_getfile(session, filep);
+}
+
+static void luo_session_global_preserved_cleanup(void)
+{
+	lockdep_assert_held_write(&luo_session_global.rwsem);
+	if (luo_session_global.ser && !IS_ERR(luo_session_global.ser)) {
+		luo_contig_free_unpreserve(luo_session_global.ser,
+					   luo_session_global.pgcnt << PAGE_SHIFT);
+	}
+	if (luo_session_global.fdt && !IS_ERR(luo_session_global.fdt))
+		luo_contig_free_unpreserve(luo_session_global.fdt, PAGE_SIZE);
+
+	luo_session_global.fdt = NULL;
+	luo_session_global.ser = NULL;
+	luo_session_global.ser_count = 0;
+	luo_session_global.pgcnt = 0;
+}
+
+static int luo_session_fdt_setup(void)
+{
+	u64 ser_pa;
+	int ret;
+
+	lockdep_assert_held_write(&luo_session_global.rwsem);
+	luo_session_global.pgcnt = DIV_ROUND_UP(luo_session_global.count *
+				sizeof(struct luo_session_ser), PAGE_SIZE);
+
+	if (luo_session_global.pgcnt > 0) {
+		size_t ser_size = luo_session_global.pgcnt << PAGE_SHIFT;
+
+		luo_session_global.ser = luo_contig_alloc_preserve(ser_size);
+		if (IS_ERR(luo_session_global.ser)) {
+			ret = PTR_ERR(luo_session_global.ser);
+			goto exit_cleanup;
+		}
+	}
+
+	luo_session_global.fdt = luo_contig_alloc_preserve(PAGE_SIZE);
+	if (IS_ERR(luo_session_global.fdt)) {
+		ret = PTR_ERR(luo_session_global.fdt);
+		goto exit_cleanup;
+	}
+
+	ret = fdt_create(luo_session_global.fdt, PAGE_SIZE);
+	if (ret < 0)
+		goto exit_cleanup;
+
+	ret = fdt_finish_reservemap(luo_session_global.fdt);
+	if (ret < 0)
+		goto exit_finish;
+
+	ret = fdt_begin_node(luo_session_global.fdt, LUO_SESSION_NODE_NAME);
+	if (ret < 0)
+		goto exit_finish;
+
+	ret = fdt_property_string(luo_session_global.fdt, "compatible",
+				  LUO_SESSION_COMPATIBLE);
+	if (ret < 0)
+		goto exit_end_node;
+
+	ret = fdt_property_u64(luo_session_global.fdt, "count",
+			       luo_session_global.count);
+	if (ret < 0)
+		goto exit_end_node;
+
+	ser_pa = luo_session_global.ser ? __pa(luo_session_global.ser) : 0;
+	ret = fdt_property_u64(luo_session_global.fdt, "data", ser_pa);
+	if (ret < 0)
+		goto exit_end_node;
+
+	ret = fdt_property_u64(luo_session_global.fdt, "pgcnt",
+			       luo_session_global.pgcnt);
+	if (ret < 0)
+		goto exit_end_node;
+
+	ret = fdt_end_node(luo_session_global.fdt);
+	if (ret < 0)
+		goto exit_finish;
+
+	ret = fdt_finish(luo_session_global.fdt);
+	if (ret < 0)
+		goto exit_cleanup;
+
+	return 0;
+
+exit_end_node:
+	fdt_end_node(luo_session_global.fdt);
+exit_finish:
+	fdt_finish(luo_session_global.fdt);
+exit_cleanup:
+	luo_session_global_preserved_cleanup();
+
+	return ret;
+}
+
+/*
+ * Change all sessions to normal state: make every file within each session
+ * to be in the normal state.
+ */
+static void luo_session_cancel(struct liveupdate_subsystem *h, u64 data)
+{
+	struct luo_session *it;
+
+	guard(rwsem_write)(&luo_session_global.rwsem);
+	list_for_each_entry(it, &luo_session_global.list, list)
+		luo_session_cancel_one(it);
+	luo_session_global_preserved_cleanup();
+}
+
+static int luo_session_prepare(struct liveupdate_subsystem *h, u64 *data)
+{
+	struct luo_session_ser *ser;
+	struct luo_session *it;
+	int ret;
+
+	scoped_guard(rwsem_write, &luo_session_global.rwsem) {
+		ret = luo_session_fdt_setup();
+		if (ret)
+			return ret;
+
+		ser = luo_session_global.ser;
+		list_for_each_entry(it, &luo_session_global.list, list) {
+			if (it->state == LIVEUPDATE_STATE_NORMAL) {
+				ret = luo_session_prepare_one(it);
+				if (ret)
+					break;
+			}
+			strscpy(ser->name, it->name, sizeof(ser->name));
+			ser->count = it->count;
+			ser->files = it->files;
+			it->ser = ser;
+			ser++;
+		}
+
+		if (!ret)
+			*data = __pa(luo_session_global.fdt);
+	}
+
+	if (ret)
+		luo_session_cancel(h, 0);
+
+	return ret;
+}
+
+static int luo_session_freeze(struct liveupdate_subsystem *h, u64 *data)
+{
+	return 0;
+}
+
+/*
+ * Finish every file within each session. If session has not been reclaimed
+ * remove it, otherwise keep this session, so it can participate in the
+ * next live update.
+ */
+static void luo_session_finish(struct liveupdate_subsystem *h, u64 data)
+{
+	struct luo_session *session, *tmp;
+
+	luo_session_deserialize();
+
+	list_for_each_entry_safe(session, tmp, &luo_session_global.list, list) {
+		/*
+		 * Skip sessions that were created in new kernel or have been
+		 * finished already.
+		 */
+		if (session->state != LIVEUPDATE_STATE_UPDATED)
+			continue;
+		luo_session_finish_one(session);
+		if (!session->retrieved) {
+			pr_warn("Removing unreclaimed session[%s]\n",
+				session->name);
+			scoped_guard(rwsem_write, &luo_session_global.rwsem)
+				luo_session_remove(session);
+			luo_session_free(session);
+		}
+	}
+
+	scoped_guard(rwsem_write, &luo_session_global.rwsem)
+		luo_session_global_preserved_cleanup();
+}
+
+static void luo_session_boot(struct liveupdate_subsystem *h, u64 data)
+{
+	u64 count, data_pa, pgcnt;
+	const void *prop;
+	int prop_len;
+	void *fdt;
+
+	fdt = __va(data);
+	if (fdt_node_check_compatible(fdt, 0, LUO_SESSION_COMPATIBLE))
+		luo_restore_fail("luo-session FDT incompatible\n");
+
+	prop = fdt_getprop(fdt, 0, "count", &prop_len);
+	if (!prop || prop_len != sizeof(u64))
+		luo_restore_fail("luo-session FDT missing or invalid 'count'\n");
+	count = be64_to_cpup(prop);
+
+	prop = fdt_getprop(fdt, 0, "data", &prop_len);
+	if (!prop || prop_len != sizeof(u64))
+		luo_restore_fail("luo-session FDT missing or invalid 'data'\n");
+	data_pa = be64_to_cpup(prop);
+
+	prop = fdt_getprop(fdt, 0, "pgcnt", &prop_len);
+	if (!prop || prop_len != sizeof(u64))
+		luo_restore_fail("luo-session FDT missing or invalid 'pgcnt'\n");
+	pgcnt = be64_to_cpup(prop);
+
+	if (!count)
+		return;
+
+	guard(rwsem_write)(&luo_session_global.rwsem);
+	luo_session_global.fdt = fdt;
+	luo_session_global.ser = __va(data_pa);
+	luo_session_global.ser_count = count;
+	luo_session_global.pgcnt = pgcnt;
+}
+
+static const struct liveupdate_subsystem_ops luo_session_subsys_ops = {
+	.prepare = luo_session_prepare,
+	.freeze = luo_session_freeze,
+	.cancel = luo_session_cancel,
+	.boot = luo_session_boot,
+	.finish = luo_session_finish,
+	.owner = THIS_MODULE,
+};
+
+static struct liveupdate_subsystem luo_session_subsys = {
+	.ops = &luo_session_subsys_ops,
+	.name = LUO_SESSION_COMPATIBLE,
+};
+
+static int __init luo_session_startup(void)
+{
+	int ret;
+
+	if (!liveupdate_enabled())
+		return 0;
+
+	init_rwsem(&luo_session_global.rwsem);
+	INIT_LIST_HEAD(&luo_session_global.list);
+
+	ret = liveupdate_register_subsystem(&luo_session_subsys);
+	if (ret) {
+		pr_warn("Failed to register luo_session subsystem [%d]\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+late_initcall(luo_session_startup);
-- 
2.51.0.536.g15c5d4f767-goog


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ