From nobody Sun Oct 5 09:20:47 2025 Received: from mail-qt1-f181.google.com (mail-qt1-f181.google.com [209.85.160.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F382223311 for ; Mon, 29 Sep 2025 01:03:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759107829; cv=none; b=kHu7YiStnlrprqwfJpaeVW2ADSbb7TNcHx4Oagd5HgSzyQ7U258QG4fUpi1f6I+2EvqOpVKzD4hEKhy0FJGm46TXfF9QOPDUuI3FBa/yZazFN9df2NOKOFGw36iIffrezRc1l2aR/LoltFEAX51B7bjOK7ABu+eug5WSVnMGuKU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759107829; c=relaxed/simple; bh=zK93UZTn2t7PGQ3H6iYrbXG4ZbFX30XxJbMiQ2s5Z6o=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PQZMT2PgbGsjcS1X2dIh3QKiB6Lt5oeSGfyHKoLHdPmqs/gDw9KHFM4oUcLpHJozEQZ6nvu+64q2SRuuxjLqPXMq+K/pbhnxkKtalbmDNopkEzdo0tNn9cMiq2rtQyTuY6IvwaR3EiNv/z1DIUu51NG6RUpfnRi5nZoJeGvrDJ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=Z0B5rVxJ; arc=none smtp.client-ip=209.85.160.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="Z0B5rVxJ" Received: by mail-qt1-f181.google.com with SMTP id d75a77b69052e-4df0467b510so21093901cf.3 for ; Sun, 28 Sep 2025 18:03:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1759107825; x=1759712625; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=JRKEwXa1tg/M6bNaqOfoeFXrUpvvgwejj9DdMRFtL4w=; b=Z0B5rVxJDSr91zM/+frz61GmcvcSePgnhnSQtMyxvEcTWsebr44znOfryGcB/3AhSN xaX9KTmsnn3zsOXkmRwtCx9dOZcZii1jkzAkhh4W47WVK2iVMeMVl7VhsljxDNm5DnYV mrm65QLHMGDfq1z99nxqqGGAJqhoUfmdsU4MBGmWbMoeNmpkP72nqP5Uu/YzIk2BAwX9 +apPgqDBFDEp6V2VgHnL9gtMGn6qn+ym0UADdI6HLgYmBeKACHkScDtqfWm8mLDxZB7b 3Bcti+yLDegTnRPn/JfiAVzSoVG/Tai6eNWuHDrsaoBIYVkD6FNO6xOnwniUaQml5M4Y pMWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1759107825; x=1759712625; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JRKEwXa1tg/M6bNaqOfoeFXrUpvvgwejj9DdMRFtL4w=; b=pGpE6DhDwaDPUQwcCpu353sBFJ3jfnDmv6D9RCo/NAbEXUTt7LXWG3x4AuKIE/PLyq Dl/uGYkbW7yTfHOy8Ahlr8A42vtUMiCOIwAWYaFb7LqyTUFsUqo5W//WnolnMFxlz3Qj P30oCR+pFSlv6I/2AmAur+TOWPPisu53IkJrD/Oa7OEHOrvP9zRNoab0HEeSp8ldvmUO SsIV8MQg29ulO0eh9SiYifLHv/EUcFqESHfDege8WCPOVbRa8UO69N8DEemIgCwOMO/R OYL9iJjt7zgOrwZe9LjMVU3TC1O87FDto1R9UeiBhgLVqV93C5otPZNicTMy1EZxIV63 3XGA== X-Forwarded-Encrypted: i=1; AJvYcCVUcIJTcJrpqo+xqkvEBz7QvVvmg8/g5pQka7gkFojZFLDeRkyWsxlnOD16e6txw7Hwq2/1EDi9ozAj4k8=@vger.kernel.org X-Gm-Message-State: AOJu0YzRWyAfttr6l0YuXqQlHGWTw9EbTfYo/aN6BDq4SVPLHYuZ/WY/ DV1BX/eA16hdN+BfcL2j2L617pFrF0Uu+4Yb7whTKbcc1Rd2BoqKX4ZqerZBg0Yf9N0= X-Gm-Gg: ASbGncs8PHAue5IWcMOf30RyCe90J5TplSmHE9UduBoYFNKWioU9JCnEmu8nbOWKBCb LvDpKCcudG6ngNQypQPHaOxXmYr6+e+3oIPnTgm5UgD+1JdLw528lTr2ChJi0/Wm4qNjvOSUjJi kyl3O1RsMja5+6Lg/xd2cIDFXbmtCNgfmsuOeg3OjiOkD6CNsk2f3McBQJ0LXDzwCb95W/5i9Vo pxeEsRhBrNFBT9KtCUDZ9BuaSBS40VB8ROjCg2O6ZfbHW/KlfYMq9LpDx1iPvsp6vgZxDWpUMew sHXWMeO0aHsplgNRVS5gRPnJP1IFpZ+KrKDaHvE9Q/H8SX6db0EpIwNju9jOK3IiWafV897Fm0x GjGPfBQiguz3AMNQQNRclhvtmbgz/mRRTs8ZfP5fC8os/upfbgtyLpxMB0C/X9mK3mncWeiX3Yo uWF1dTosRk+CyRj4Yrhg== X-Google-Smtp-Source: AGHT+IEwhqe/BqmKvNuWGS3uf0Nm2/PuSwk/9IAcyGtlVKkcjZXQAFpVwNkxDeP2e7Bl4f/no/k03w== X-Received: by 2002:ac8:7e86:0:b0:4d9:c572:f9b6 with SMTP id d75a77b69052e-4da4bbe418cmr172161361cf.55.1759107824543; Sun, 28 Sep 2025 18:03:44 -0700 (PDT) Received: from soleen.c.googlers.com.com (53.47.86.34.bc.googleusercontent.com. [34.86.47.53]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4db0c0fbe63sm64561521cf.23.2025.09.28.18.03.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 28 Sep 2025 18:03:43 -0700 (PDT) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.com, changyuanl@google.com, pasha.tatashin@soleen.com, rppt@kernel.org, dmatlack@google.com, rientjes@google.com, corbet@lwn.net, rdunlap@infradead.org, ilpo.jarvinen@linux.intel.com, kanie@linux.alibaba.com, ojeda@kernel.org, aliceryhl@google.com, masahiroy@kernel.org, akpm@linux-foundation.org, tj@kernel.org, yoann.congal@smile.fr, mmaurer@google.com, roman.gushchin@linux.dev, chenridong@huawei.com, axboe@kernel.dk, mark.rutland@arm.com, jannh@google.com, vincent.guittot@linaro.org, hannes@cmpxchg.org, dan.j.williams@intel.com, david@redhat.com, joel.granados@kernel.org, rostedt@goodmis.org, anna.schumaker@oracle.com, song@kernel.org, zhangguopeng@kylinos.cn, linux@weissschuh.net, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, gregkh@linuxfoundation.org, tglx@linutronix.de, mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com, x86@kernel.org, hpa@zytor.com, rafael@kernel.org, dakr@kernel.org, bartosz.golaszewski@linaro.org, cw00.choi@samsung.com, myungjoo.ham@samsung.com, yesanishhere@gmail.com, Jonathan.Cameron@huawei.com, quic_zijuhu@quicinc.com, aleksander.lobakin@intel.com, ira.weiny@intel.com, andriy.shevchenko@linux.intel.com, leon@kernel.org, lukas@wunner.de, bhelgaas@google.com, wagi@kernel.org, djeffery@redhat.com, stuart.w.hayes@gmail.com, ptyadav@amazon.de, lennart@poettering.net, brauner@kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, saeedm@nvidia.com, ajayachandra@nvidia.com, jgg@nvidia.com, parav@nvidia.com, leonro@nvidia.com, witu@nvidia.com, hughd@google.com, skhawaja@google.com, chrisl@kernel.org, steven.sistare@oracle.com Subject: [PATCH v4 09/30] liveupdate: luo_subsystems: add subsystem registration Date: Mon, 29 Sep 2025 01:03:00 +0000 Message-ID: <20250929010321.3462457-10-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.0.536.g15c5d4f767-goog In-Reply-To: <20250929010321.3462457-1-pasha.tatashin@soleen.com> References: <20250929010321.3462457-1-pasha.tatashin@soleen.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce the framework for kernel subsystems (e.g., KVM, IOMMU, device drivers) to register with LUO and participate in the live update process via callbacks. Subsystem Registration: - Defines struct liveupdate_subsystem in linux/liveupdate.h, which subsystems use to provide their name and optional callbacks (prepare, freeze, cancel, finish). The callbacks accept a u64 *data intended for passing state/handles. - Exports liveupdate_register_subsystem() and liveupdate_unregister_subsystem() API functions. - Adds drivers/misc/liveupdate/luo_subsystems.c to manage a list of registered subsystems. Registration/unregistration is restricted to specific LUO states (NORMAL/UPDATED). Callback Framework: - The main luo_core.c state transition functions now delegate to new luo_do_subsystems_*_calls() functions defined in luo_subsystems.c. - These new functions are intended to iterate through the registered subsystems and invoke their corresponding callbacks. FDT Integration: - Adds a /subsystems subnode within the main LUO FDT created in luo_core.c. This node has its own compatibility string (subsystems-v1). - luo_subsystems_fdt_setup() populates this node by adding a property for each registered subsystem, using the subsystem's name. Currently, these properties are initialized with a placeholder u64 value (0). - luo_subsystems_startup() is called from luo_core.c on boot to find and validate the /subsystems node in the FDT received via KHO. - Adds a stub API function liveupdate_get_subsystem_data() intended for subsystems to retrieve their persisted u64 data from the FDT in the new kernel. Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 66 +++++++ kernel/liveupdate/Makefile | 3 +- kernel/liveupdate/luo_core.c | 19 +- kernel/liveupdate/luo_internal.h | 7 + kernel/liveupdate/luo_subsystems.c | 291 +++++++++++++++++++++++++++++ 5 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 kernel/liveupdate/luo_subsystems.c diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 85a6828c95b0..4c378a986cfe 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -12,6 +12,52 @@ #include #include =20 +struct liveupdate_subsystem; + +/** + * struct liveupdate_subsystem_ops - LUO events callback functions + * @prepare: Optional. Called during LUO prepare phase. Should perform + * preparatory actions and can store a u64 handle/state + * via the 'data' pointer for use in later callbacks. + * Return 0 on success, negative error code on failure. + * @freeze: Optional. Called during LUO freeze event (before actual = jump + * to new kernel). Should perform final state saving action= s and + * can update the u64 handle/state via the 'data' pointer. = Retur: + * 0 on success, negative error code on failure. + * @cancel: Optional. Called if the live update process is canceled = after + * prepare (or freeze) was called. Receives the u64 data + * set by prepare/freeze. Used for cleanup. + * @boot: Optional. Call durng boot post live update. This callbac= k is + * done when subsystem register during live update. + * @finish: Optional. Called after the live update is finished in th= e new + * kernel. + * Receives the u64 data set by prepare/freeze. Used for cl= eanup. + * @owner: Module reference + */ +struct liveupdate_subsystem_ops { + int (*prepare)(struct liveupdate_subsystem *handle, u64 *data); + int (*freeze)(struct liveupdate_subsystem *handle, u64 *data); + void (*cancel)(struct liveupdate_subsystem *handle, u64 data); + void (*boot)(struct liveupdate_subsystem *handle, u64 data); + void (*finish)(struct liveupdate_subsystem *handle, u64 data); + struct module *owner; +}; + +/** + * struct liveupdate_subsystem - Represents a subsystem participating in L= UO + * @ops: Callback functions + * @name: Unique name identifying the subsystem. + * @list: List head used internally by LUO. Should not be modified= by + * caller after registration. + * @private_data: For LUO internal use, cached value of data field. + */ +struct liveupdate_subsystem { + const struct liveupdate_subsystem_ops *ops; + const char *name; + struct list_head list; + u64 private_data; +}; + #ifdef CONFIG_LIVEUPDATE =20 /* Return true if live update orchestrator is enabled */ @@ -33,6 +79,10 @@ bool liveupdate_state_normal(void); =20 enum liveupdate_state liveupdate_get_state(void); =20 +int liveupdate_register_subsystem(struct liveupdate_subsystem *h); +int liveupdate_unregister_subsystem(struct liveupdate_subsystem *h); +int liveupdate_get_subsystem_data(struct liveupdate_subsystem *h, u64 *dat= a); + #else /* CONFIG_LIVEUPDATE */ =20 static inline int liveupdate_reboot(void) @@ -60,5 +110,21 @@ static inline enum liveupdate_state liveupdate_get_stat= e(void) return LIVEUPDATE_STATE_NORMAL; } =20 +static inline int liveupdate_register_subsystem(struct liveupdate_subsyste= m *h) +{ + return 0; +} + +static inline int liveupdate_unregister_subsystem(struct liveupdate_subsys= tem *h) +{ + return 0; +} + +static inline int liveupdate_get_subsystem_data(struct liveupdate_subsyste= m *h, + u64 *data) +{ + return -ENODATA; +} + #endif /* CONFIG_LIVEUPDATE */ #endif /* _LINUX_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index d90cc3b4bf7b..2881bab0c6df 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -2,7 +2,8 @@ =20 luo-y :=3D \ luo_core.o \ - luo_ioctl.o + luo_ioctl.o \ + luo_subsystems.o =20 obj-$(CONFIG_KEXEC_HANDOVER) +=3D kexec_handover.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) +=3D kexec_handover_debug.o diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 10796481447a..92edaeaaad3e 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -130,6 +130,10 @@ static int luo_fdt_setup(void) if (ret) goto exit_free; =20 + ret =3D luo_subsystems_fdt_setup(fdt_out); + if (ret) + goto exit_free; + ret =3D kho_add_subtree(LUO_KHO_ENTRY_NAME, fdt_out); if (ret) goto exit_free; @@ -153,20 +157,30 @@ static void luo_fdt_destroy(void) =20 static int luo_do_prepare_calls(void) { - return 0; + int ret; + + ret =3D luo_do_subsystems_prepare_calls(); + + return ret; } =20 static int luo_do_freeze_calls(void) { - return 0; + int ret; + + ret =3D luo_do_subsystems_freeze_calls(); + + return ret; } =20 static void luo_do_finish_calls(void) { + luo_do_subsystems_finish_calls(); } =20 static void luo_do_cancel_calls(void) { + luo_do_subsystems_cancel_calls(); } =20 static int __luo_prepare(void) @@ -408,6 +422,7 @@ static int __init luo_startup(void) } =20 __luo_set_state(LIVEUPDATE_STATE_UPDATED); + luo_subsystems_startup(luo_fdt_in); =20 return 0; } diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index c98842caa4a0..c62fbbb0790c 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -32,4 +32,11 @@ 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); =20 +void luo_subsystems_startup(void *fdt); +int luo_subsystems_fdt_setup(void *fdt); +int luo_do_subsystems_prepare_calls(void); +int luo_do_subsystems_freeze_calls(void); +void luo_do_subsystems_finish_calls(void); +void luo_do_subsystems_cancel_calls(void); + #endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/kernel/liveupdate/luo_subsystems.c b/kernel/liveupdate/luo_sub= systems.c new file mode 100644 index 000000000000..69f00d5c000e --- /dev/null +++ b/kernel/liveupdate/luo_subsystems.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: LUO Subsystems support + * + * Various kernel subsystems register with the Live Update Orchestrator to + * participate in the live update process. These subsystems are notified at + * different stages of the live update sequence, allowing them to serialize + * device state before the reboot and restore it afterwards. Examples incl= ude + * the device layer, interrupt controllers, KVM, IOMMU, and specific device + * drivers. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "luo_internal.h" + +#define LUO_SUBSYSTEMS_NODE_NAME "subsystems" +#define LUO_SUBSYSTEMS_COMPATIBLE "subsystems-v1" + +static DEFINE_MUTEX(luo_subsystem_list_mutex); +static LIST_HEAD(luo_subsystems_list); +static void *luo_fdt_out; +static void *luo_fdt_in; + +/** + * luo_subsystems_fdt_setup - Adds and populates the 'subsystems' node in = the + * FDT. + * @fdt: Pointer to the LUO FDT blob. + * + * Add subsystems node and each subsystem to the LUO FDT blob. + * + * Returns: 0 on success, negative errno on failure. + */ +int luo_subsystems_fdt_setup(void *fdt) +{ + struct liveupdate_subsystem *subsystem; + const u64 zero_data =3D 0; + int ret, node_offset; + + guard(mutex)(&luo_subsystem_list_mutex); + ret =3D fdt_add_subnode(fdt, 0, LUO_SUBSYSTEMS_NODE_NAME); + if (ret < 0) + goto exit_error; + + node_offset =3D ret; + ret =3D fdt_setprop_string(fdt, node_offset, "compatible", + LUO_SUBSYSTEMS_COMPATIBLE); + if (ret < 0) + goto exit_error; + + list_for_each_entry(subsystem, &luo_subsystems_list, list) { + ret =3D fdt_setprop(fdt, node_offset, subsystem->name, + &zero_data, sizeof(zero_data)); + if (ret < 0) + goto exit_error; + } + + luo_fdt_out =3D fdt; + return 0; +exit_error: + pr_err("Failed to setup 'subsystems' node to FDT: %s\n", + fdt_strerror(ret)); + return -ENOSPC; +} + +/** + * luo_subsystems_startup - Validates the LUO subsystems FDT node at start= up. + * @fdt: Pointer to the LUO FDT blob passed from the previous kernel. + * + * This __init function checks the existence and validity of the '/subsyst= ems' + * node in the FDT. This node is considered mandatory. + */ +void __init luo_subsystems_startup(void *fdt) +{ + int ret, node_offset; + + guard(mutex)(&luo_subsystem_list_mutex); + node_offset =3D fdt_subnode_offset(fdt, 0, LUO_SUBSYSTEMS_NODE_NAME); + if (node_offset < 0) + luo_restore_fail("Failed to find /subsystems node\n"); + + ret =3D fdt_node_check_compatible(fdt, node_offset, + LUO_SUBSYSTEMS_COMPATIBLE); + if (ret) { + luo_restore_fail("FDT '%s' is incompatible with '%s' [%d]\n", + LUO_SUBSYSTEMS_NODE_NAME, + LUO_SUBSYSTEMS_COMPATIBLE, ret); + } + luo_fdt_in =3D fdt; +} + +static int luo_get_subsystem_data(struct liveupdate_subsystem *h, u64 *dat= a) +{ + return 0; +} + +/** + * luo_do_subsystems_prepare_calls - Calls prepare callbacks and updates F= DT + * if all prepares succeed. Handles cancellation on failure. + * + * Phase 1: Calls 'prepare' for all subsystems and stores results temporar= ily. + * If any 'prepare' fails, calls 'cancel' on previously prepared subsystems + * and returns the error. + * Phase 2: If all 'prepare' calls succeeded, writes the stored data to th= e FDT. + * If any FDT write fails, calls 'cancel' on *all* prepared subsystems and + * returns the FDT error. + * + * Returns: 0 on success. Negative errno on failure. + */ +int luo_do_subsystems_prepare_calls(void) +{ + return 0; +} + +/** + * luo_do_subsystems_freeze_calls - Calls freeze callbacks and updates FDT + * if all freezes succeed. Handles cancellation on failure. + * + * Phase 1: Calls 'freeze' for all subsystems and stores results temporari= ly. + * If any 'freeze' fails, calls 'cancel' on previously called subsystems + * and returns the error. + * Phase 2: If all 'freeze' calls succeeded, writes the stored data to the= FDT. + * If any FDT write fails, calls 'cancel' on *all* subsystems and + * returns the FDT error. + * + * Returns: 0 on success. Negative errno on failure. + */ +int luo_do_subsystems_freeze_calls(void) +{ + return 0; +} + +/** + * luo_do_subsystems_finish_calls- Calls finish callbacks for all subsyste= ms. + * + * This function is called at the end of live update cycle to do the final + * clean-up or housekeeping of the post-live update states. + */ +void luo_do_subsystems_finish_calls(void) +{ +} + +/** + * luo_do_subsystems_cancel_calls - Calls cancel callbacks for all subsyst= ems. + * + * This function is typically called when the live update process needs to= be + * aborted externally, for example, after the prepare phase may have run b= ut + * before actual reboot. It iterates through all registered subsystems and= calls + * the 'cancel' callback for those that implement it and likely completed + * prepare. + */ +void luo_do_subsystems_cancel_calls(void) +{ +} + +/** + * liveupdate_register_subsystem - Register a kernel subsystem handler wit= h LUO + * @h: Pointer to the liveupdate_subsystem structure allocated and populat= ed + * by the calling subsystem. + * + * Registers a subsystem handler that provides callbacks for different eve= nts + * of the live update cycle. Registration is typically done during the + * subsystem's module init or core initialization. + * + * Can only be called when LUO is in the NORMAL or UPDATED states. + * The provided name (@h->name) must be unique among registered subsystems. + * + * Return: 0 on success, negative error code otherwise. + */ +int liveupdate_register_subsystem(struct liveupdate_subsystem *h) +{ + struct liveupdate_subsystem *iter; + int ret =3D 0; + + luo_state_read_enter(); + if (!liveupdate_state_normal() && !liveupdate_state_updated()) { + luo_state_read_exit(); + return -EBUSY; + } + + guard(mutex)(&luo_subsystem_list_mutex); + list_for_each_entry(iter, &luo_subsystems_list, list) { + if (iter =3D=3D h) { + pr_warn("Subsystem '%s' (%p) already registered.\n", + h->name, h); + ret =3D -EEXIST; + goto out_unlock; + } + + if (!strcmp(iter->name, h->name)) { + pr_err("Subsystem with name '%s' already registered.\n", + h->name); + ret =3D -EEXIST; + goto out_unlock; + } + } + + if (!try_module_get(h->ops->owner)) { + pr_warn("Subsystem '%s' unable to get reference.\n", h->name); + ret =3D -EAGAIN; + goto out_unlock; + } + + INIT_LIST_HEAD(&h->list); + list_add_tail(&h->list, &luo_subsystems_list); + +out_unlock: + /* + * If we are booting during live update, and subsystem provided a boot + * callback, do it now, since we know that subsystem has already + * initialized. + */ + if (!ret && liveupdate_state_updated() && h->ops->boot) { + u64 data; + + ret =3D luo_get_subsystem_data(h, &data); + if (!WARN_ON_ONCE(ret)) + h->ops->boot(h, data); + } + + luo_state_read_exit(); + + return ret; +} + +/** + * liveupdate_unregister_subsystem - Unregister a kernel subsystem handler= from + * LUO + * @h: Pointer to the same liveupdate_subsystem structure that was used du= ring + * registration. + * + * Unregisters a previously registered subsystem handler. Typically called + * during module exit or subsystem teardown. LUO removes the structure fro= m its + * internal list; the caller is responsible for any necessary memory clean= up + * of the structure itself. + * + * Return: 0 on success, negative error code otherwise. + * -EINVAL if h is NULL. + * -ENOENT if the specified handler @h is not found in the registration li= st. + * -EBUSY if LUO is not in the NORMAL state. + */ +int liveupdate_unregister_subsystem(struct liveupdate_subsystem *h) +{ + struct liveupdate_subsystem *iter; + bool found =3D false; + int ret =3D 0; + + luo_state_read_enter(); + if (!liveupdate_state_normal() && !liveupdate_state_updated()) { + luo_state_read_exit(); + return -EBUSY; + } + + guard(mutex)(&luo_subsystem_list_mutex); + list_for_each_entry(iter, &luo_subsystems_list, list) { + if (iter =3D=3D h) { + found =3D true; + break; + } + } + + if (found) { + list_del_init(&h->list); + } else { + pr_warn("Subsystem handler '%s' not found for unregistration.\n", + h->name); + ret =3D -ENOENT; + } + + module_put(h->ops->owner); + luo_state_read_exit(); + + return ret; +} + +int liveupdate_get_subsystem_data(struct liveupdate_subsystem *h, u64 *dat= a) +{ + return 0; +} --=20 2.51.0.536.g15c5d4f767-goog