From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f172.google.com (mail-yw1-f172.google.com [209.85.128.172]) (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 2A428261B9B for ; Fri, 7 Nov 2025 21:05:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549537; cv=none; b=gfoWm+Xq5aaObT0aQ9QzvcAxvTnoxsWAk1CpfgeJjF5kJ53sXd14uD5JTb0HyNThqMH/tLH4D/SV1GhV6p3OgB0rp2DoyQ6IL9csBsF0PySSXJbF6rggsVrtuhQ1mQoJbTRuuUbh1LjmgVYqktsnRoXzc2L4K3wCTgfkcdgZJy8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549537; c=relaxed/simple; bh=uQw6SGnZOWAOjWeWczV4MS6Gt9k2Q4Va7EYPM+WPPIc=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j6klvwqlc/EH0EldtH9XL245aciWcBfr1I2ihdPgx62CcEJVmXQWHBKPr8QBsggvC6sjYAbcQb5vUxsifPMpQGlw6f+4IUWswbKvM+wr95iVS3psPHRkZ1Oody7zMFFqmCyBrpn2a28NjCzMe+nFDiNdVSnTy3S7Jc9g/Mr7vbY= 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=IhsyqgB4; arc=none smtp.client-ip=209.85.128.172 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="IhsyqgB4" Received: by mail-yw1-f172.google.com with SMTP id 00721157ae682-78488cdc20aso12732177b3.1 for ; Fri, 07 Nov 2025 13:05:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549534; x=1763154334; 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=Zu+sQZxV/DK1mZF4LzYMVwMmIZV5lUcGvIMy5Jfwns0=; b=IhsyqgB4Wl6g+6wrOHs24i0Jry9ozWFeEy2Bg4w1ocms7sXzpMZf2bLJ+aApsbtW4l Pqoh1BMlhkOmKAM/fCCxXJpSo86siU56TjVXlqV41B3igpuXzDSyt3BnOmYrBzLLMCVY y/19DShA6MytWcWjvgCLGQtasW65niK22hOrAkmq6WooNGN5v3yQPrnBhEnRhkS78fKa GyNmZpuOCFjvoAdG7e83b2xDVeZbugZ+Ghrtlv+Hb1Bq2xx4QIYkhCDZxf4Ip0KZ/M35 h7W9iqO0O9F/1e5AuwazYYTEb6bl7FFW6huWsPqH+DCT/yEn/iwKrcUjo5JMOUUcRtGD l2hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549534; x=1763154334; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Zu+sQZxV/DK1mZF4LzYMVwMmIZV5lUcGvIMy5Jfwns0=; b=X8zhMWRECiCqxxJZrUaJoqKUciNCGiRz54U00M0Q3xa/Nw+Vw521kRysFJo7EoAY03 5RC9J+UnoVduESYrONF5FTlqco5pBfNvIPNoHfRXZdfPG8G97tzSosnbVrkybW/CzTiS cyXDF84bVISAAmDRy5NDljws252AprJ3ZexjU/N7PHW1XnlC6GMAWKmxnJlAz77X/+En xO4ObDBt55SzwU31BkZmN1STSt2Tiluh23zpLuyhheHrUg8XjCd0EgsRk3XBVZpcZ1wT 8x9WnuTpDYt5TY3dmYQoy1ipOgdUaE5IO7QesNEVh+YVeiCkbi8wm3vbsNBT76Bm4Bgy tePg== X-Forwarded-Encrypted: i=1; AJvYcCUNSF7nrED3ZucH5r0GCqZ4DnliZMkmQGwfD1g2p4U6XgrhcPHiVL5DT+xIUILzHekx7nNNVwBf/C+E49M=@vger.kernel.org X-Gm-Message-State: AOJu0YwyiwxBH4iKSG4ysQQ24NhepMKeJKg0oHAZKZASspjkH4iWSA33 xRC9QS5Ab/AAF4+jxmzPz4VEL0RUQuT4iwwrvzPAX+3bNOwYClhfyVZNO4EbhckZeWw= X-Gm-Gg: ASbGncuV13W5IRuKrKW/9bVjmOPcX4xHPkVaGDXHz9KXvoQGWuNvxAhe88wE1cLO6zL yZMD3mOvwjvBGnLATrDn6ZBicGPtnrxQZKd5gQBdWM7wFC/pbIQOqfJ06ud3ZEFggCaqkviHY73 3nwZCTGaj421EOH9WjGnjxhIoDtuWEw0yGccTCn3SaLuPZfpeU8kXio+HGun6dGWDWxrNlegSyG FhcFFXRxDm9r2AsIlHNe4VRCN9I+ofNp0fMwaNy4twERUVZcFMR7/UW5e3JeQPiQuh2czkf3vFo Rv6DgByq/E5F0/87EZEpoa8Nykt0/Q6Lr5ZVS3w3SLXQUmHS1EHU5Alpol+HfonQhUIVHV9/SMh Z9RzdQhhiz9ZxOvXSh3M86V6iP806qKUH2K0pbPJK/8tSrX8LfTPw8p5nFp5pp3hPaj0f3xT7E6 TBNi9nfjIfM2UylTP8Sny2lmbrP3uGqkPyuwb30+vc+RythpJQyWesEh6wnDgVWoJ5HMTeOR1vc 1RkVL2iOYMi X-Google-Smtp-Source: AGHT+IGkl2IVxRZTWeoOPperMhisjG1fXbHj29RAaasJvCOsVAl4Mwy+BOKFhe1CUBjQYDl+bzz8kA== X-Received: by 2002:a05:690c:9689:b0:784:841e:c044 with SMTP id 00721157ae682-787d54521a4mr7498967b3.59.1762549533932; Fri, 07 Nov 2025 13:05:33 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:33 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 01/22] liveupdate: luo_core: luo_ioctl: Live Update Orchestrator Date: Fri, 7 Nov 2025 16:02:59 -0500 Message-ID: <20251107210526.257742-2-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 LUO, a mechanism intended to facilitate kernel updates while keeping designated devices operational across the transition (e.g., via kexec). The primary use case is updating hypervisors with minimal disruption to running virtual machines. For userspace side of hypervisor update we have copyless migration. LUO is for updating the kernel. This initial patch lays the groundwork for the LUO subsystem. Further functionality, including the implementation of state transition logic, integration with KHO, and hooks for subsystems and file descriptors, will be added in subsequent patches. Create a character device at /dev/liveupdate. A new uAPI header, , will define the necessary structures. The magic number for IOCTL is registered in Documentation/userspace-api/ioctl/ioctl-number.rst. Signed-off-by: Pasha Tatashin --- .../userspace-api/ioctl/ioctl-number.rst | 2 + include/linux/liveupdate.h | 35 ++++++++ include/uapi/linux/liveupdate.h | 46 ++++++++++ kernel/liveupdate/Kconfig | 27 ++++++ kernel/liveupdate/Makefile | 6 ++ kernel/liveupdate/luo_core.c | 86 +++++++++++++++++++ kernel/liveupdate/luo_ioctl.c | 45 ++++++++++ 7 files changed, 247 insertions(+) create mode 100644 include/linux/liveupdate.h create mode 100644 include/uapi/linux/liveupdate.h create mode 100644 kernel/liveupdate/luo_core.c create mode 100644 kernel/liveupdate/luo_ioctl.c diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documenta= tion/userspace-api/ioctl/ioctl-number.rst index 7c527a01d1cf..7232b3544cec 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -385,6 +385,8 @@ Code Seq# Include File = Comments 0xB8 01-02 uapi/misc/mrvl_cn10k_dpi.h Mar= vell CN10K DPI driver 0xB8 all uapi/linux/mshv.h Mic= rosoft Hyper-V /dev/mshv driver +0xBA 00-0F uapi/linux/liveupdate.h Pas= ha Tatashin + 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h Dea= d since 6.15 0xCA 10-2F uapi/misc/ocxl.h diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h new file mode 100644 index 000000000000..730b76625fec --- /dev/null +++ b/include/linux/liveupdate.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ +#ifndef _LINUX_LIVEUPDATE_H +#define _LINUX_LIVEUPDATE_H + +#include +#include +#include + +#ifdef CONFIG_LIVEUPDATE + +/* Return true if live update orchestrator is enabled */ +bool liveupdate_enabled(void); + +/* Called during kexec to tell LUO that entered into reboot */ +int liveupdate_reboot(void); + +#else /* CONFIG_LIVEUPDATE */ + +static inline bool liveupdate_enabled(void) +{ + return false; +} + +static inline int liveupdate_reboot(void) +{ + return 0; +} + +#endif /* CONFIG_LIVEUPDATE */ +#endif /* _LINUX_LIVEUPDATE_H */ diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h new file mode 100644 index 000000000000..df34c1642c4d --- /dev/null +++ b/include/uapi/linux/liveupdate.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +/* + * Userspace interface for /dev/liveupdate + * Live Update Orchestrator + * + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#ifndef _UAPI_LIVEUPDATE_H +#define _UAPI_LIVEUPDATE_H + +#include +#include + +/** + * DOC: General ioctl format + * + * The ioctl interface follows a general format to allow for extensibility= . Each + * ioctl is passed in a structure pointer as the argument providing the si= ze of + * the structure in the first u32. The kernel checks that any structure sp= ace + * beyond what it understands is 0. This allows userspace to use the backw= ard + * compatible portion while consistently using the newer, larger, structur= es. + * + * ioctls use a standard meaning for common errnos: + * + * - ENOTTY: The IOCTL number itself is not supported at all + * - E2BIG: The IOCTL number is supported, but the provided structure has + * non-zero in a part the kernel does not understand. + * - EOPNOTSUPP: The IOCTL number is supported, and the structure is + * understood, however a known field has a value the kernel does not + * understand or support. + * - EINVAL: Everything about the IOCTL was understood, but a field is not + * correct. + * - ENOENT: A provided token does not exist. + * - ENOMEM: Out of memory. + * - EOVERFLOW: Mathematics overflowed. + * + * As well as additional errnos, within specific ioctls. + */ + +/* The ioctl type, documented in ioctl-number.rst */ +#define LIVEUPDATE_IOCTL_TYPE 0xBA + +#endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index 1379a4c40b09..e1fdcf7f57f3 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -1,7 +1,34 @@ # SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (c) 2025, Google LLC. +# Pasha Tatashin +# +# Live Update Orchestrator +# =20 menu "Live Update and Kexec HandOver" =20 +config LIVEUPDATE + bool "Live Update Orchestrator" + depends on KEXEC_HANDOVER + help + Enable the Live Update Orchestrator. Live Update is a mechanism, + typically based on kexec, that allows the kernel to be updated + while keeping selected devices operational across the transition. + These devices are intended to be reclaimed by the new kernel and + re-attached to their original workload without requiring a device + reset. + + Ability to handover a device from current to the next kernel depends + on specific support within device drivers and related kernel + subsystems. + + This feature primarily targets virtual machine hosts to quickly update + the kernel hypervisor with minimal disruption to the running virtual + machines. + + If unsure, say N. + config KEXEC_HANDOVER bool "kexec handover" depends on ARCH_SUPPORTS_KEXEC_HANDOVER && ARCH_SUPPORTS_KEXEC_FILE diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index f52ce1ebcf86..413722002b7a 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -1,5 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 =20 +luo-y :=3D \ + luo_core.o \ + luo_ioctl.o + obj-$(CONFIG_KEXEC_HANDOVER) +=3D kexec_handover.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) +=3D kexec_handover_debug.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUGFS) +=3D kexec_handover_debugfs.o + +obj-$(CONFIG_LIVEUPDATE) +=3D luo.o diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c new file mode 100644 index 000000000000..0e1ab19fa1cd --- /dev/null +++ b/kernel/liveupdate/luo_core.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: Live Update Orchestrator (LUO) + * + * Live Update is a specialized, kexec-based reboot process that allows a + * running kernel to be updated from one version to another while preservi= ng + * the state of selected resources and keeping designated hardware devices + * operational. For these devices, DMA activity may continue throughout the + * kernel transition. + * + * While the primary use case driving this work is supporting live updates= of + * the Linux kernel when it is used as a hypervisor in cloud environments,= the + * LUO framework itself is designed to be workload-agnostic. Much like Ker= nel + * Live Patching, which applies security fixes regardless of the workload, + * Live Update facilitates a full kernel version upgrade for any type of s= ystem. + * + * For example, a non-hypervisor system running an in-memory cache like + * memcached with many gigabytes of data can use LUO. The userspace service + * can place its cache into a memfd, have its state preserved by LUO, and + * restore it immediately after the kernel kexec. + * + * Whether the system is running virtual machines, containers, a + * high-performance database, or networking services, LUO's primary goal i= s to + * enable a full kernel update by preserving critical userspace state and + * keeping essential devices operational. + * + * The core of LUO is a mechanism that tracks the progress of a live updat= e, + * along with a callback API that allows other kernel subsystems to partic= ipate + * in the process. Example subsystems that can hook into LUO include: kvm, + * iommu, interrupts, vfio, participating filesystems, and memory manageme= nt. + * + * LUO uses Kexec Handover to transfer memory state from the current kerne= l to + * the next kernel. For more details see + * Documentation/core-api/kho/concepts.rst. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +static struct { + bool enabled; +} luo_global; + +static int __init early_liveupdate_param(char *buf) +{ + return kstrtobool(buf, &luo_global.enabled); +} +early_param("liveupdate", early_liveupdate_param); + +/* Public Functions */ + +/** + * liveupdate_reboot() - Kernel reboot notifier for live update final + * serialization. + * + * This function is invoked directly from the reboot() syscall pathway + * if kexec is in progress. + * + * If any callback fails, this function aborts KHO, undoes the freeze() + * callbacks, and returns an error. + */ +int liveupdate_reboot(void) +{ + return 0; +} + +/** + * liveupdate_enabled - Check if the live update feature is enabled. + * + * This function returns the state of the live update feature flag, which + * can be controlled via the ``liveupdate`` kernel command-line parameter. + * + * @return true if live update is enabled, false otherwise. + */ +bool liveupdate_enabled(void) +{ + return luo_global.enabled; +} diff --git a/kernel/liveupdate/luo_ioctl.c b/kernel/liveupdate/luo_ioctl.c new file mode 100644 index 000000000000..44d365185f7c --- /dev/null +++ b/kernel/liveupdate/luo_ioctl.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#include +#include + +struct luo_device_state { + struct miscdevice miscdev; +}; + +static const struct file_operations luo_fops =3D { + .owner =3D THIS_MODULE, +}; + +static struct luo_device_state luo_dev =3D { + .miscdev =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "liveupdate", + .fops =3D &luo_fops, + }, +}; + +static int __init liveupdate_ioctl_init(void) +{ + if (!liveupdate_enabled()) + return 0; + + return misc_register(&luo_dev.miscdev); +} +module_init(liveupdate_ioctl_init); + +static void __exit liveupdate_exit(void) +{ + misc_deregister(&luo_dev.miscdev); +} +module_exit(liveupdate_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pasha Tatashin"); +MODULE_DESCRIPTION("Live Update Orchestrator"); +MODULE_VERSION("0.1"); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f41.google.com (mail-yx1-f41.google.com [74.125.224.41]) (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 4318430DD0B for ; Fri, 7 Nov 2025 21:05:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549540; cv=none; b=Fz/tI/H+RUFEyq5rcCjmmznC8UZ4bLSth8snw77kfrmZ6UF3WDvZf8Qxnyzx3NJebxi2GPLd/r7OlUxrx4VBOllC72vws0kQuy9rEBlLQASfPww4Bklt3829L7VeWbknO/lrFTg5bM/ihk+sJJS/X7OAb0DkryKHA9sNJxwL170= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549540; c=relaxed/simple; bh=XJDPtcEja0bEI+2/3EfwT35vgulH1PsXK2lUYfeZtv0=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o72MNzd2CI1Uo+GedHLJqbpwGJG5jx3Jujri/KD/rvpeeTEVQVutJ+hQRZ17KQxFN31YfH+mtZvO9MBVZVvvvAKc9zgiBFTfDyboxK6z2BvyhTR3Z027rhbuRzpa/3WSD6UHFo74vA9Yqysfi62XS1PLjKcY+AcIkAqkqaivfpg= 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=YaeAaHcY; arc=none smtp.client-ip=74.125.224.41 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="YaeAaHcY" Received: by mail-yx1-f41.google.com with SMTP id 956f58d0204a3-640d43060d2so186931d50.2 for ; Fri, 07 Nov 2025 13:05:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549536; x=1763154336; 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=8DFrgL9rmFv8Z3TsA72WaRzW9XWB4UJiC+A2rA/G5Wg=; b=YaeAaHcYtFIV5h+bh674QfITo1UoKAyEoUpN2bwKMDdZXgP40uzbKKGTlAGe1JRZLT ZK5awINfy5ASyYF4t4I84kdD/zA9JhzO9yf4hVNXozUebk064Mp1ChjI+VYLT3IGeg4H qKToilTLSjcSOXd9imQxtwNDR/hKsewJ76VPwsydeATb/g+D8Ey62mPgdsM2fqHHoaJ/ buDzeZz3k0RAQpQMaw7sAAdoUFGtUomNFSFEwodIFw50PITBKgxfUeQDZVwYdR61T+px ZIpwR/IBehXGg++ExpW4wFPqn2sqVzFDiv/mM14tVLtIzxrDLST8O3/YWRNUWvOpNcp+ 5SfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549536; x=1763154336; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=8DFrgL9rmFv8Z3TsA72WaRzW9XWB4UJiC+A2rA/G5Wg=; b=L2XShX5OoO1Y+V0auQjYOtRfdIq2yDfnpVCK/LgvV0kuP/H5J7+OvbQC2ps92eycYq Pkn+NUU0R7WRxUi4EH1rcL08RAiZGJbHtV1WGGQHtsV4YK23yESry1sy0J+h4Kk7OrXk tutMHzJEgvJLeROxV8H9sXiW+jGvUfe0UFH27H/A/8cl1WtziyoOAkzWg4nK5WhAsa/1 aaEVRJMA9NuXEENu3ZoUljA2ZC7S6EFSRHM6WyaIE0MGRKkC2PA+r743uan1/E1NvhvM olcTmobe4gcTlB27p5wSr04JRKmsiCikpbQQrbA/0RAPxjXcMu0jzSTJQNkYoDrO4kdi sd0g== X-Forwarded-Encrypted: i=1; AJvYcCWUa4zG19LwLva5exngOM/cm+iFmQtoxWhOTp5+iysEoB7Y/WyJQzyg8/swCJ9LdUJfl18sFTDLsvpKw7k=@vger.kernel.org X-Gm-Message-State: AOJu0YxHqOhccvj6Bs0M+45iUvAIcm8UjG9fz/qvtHDiTtohr7L5SXcN bv/awfhcu0ybsnS7qvVaZ6A9EPhQI6W05pwi4Xr9rP9eRYngN4LWPQOHMAzRB6CfeEs= X-Gm-Gg: ASbGncsN5Qu0ltEQjw/ofFr0NGeNMg97Bs1NXLhNmVhk186WA8uMqk5HgwEXTc7hBu6 WA3or38vz9+Q0Pif9eTv6UOexrdhAKbYR+oSFZLfa8jL/iL5RIjFWutk/q6aY2QUddU+TaSBy+j 0v+dsv7+v5wD6C/KzC1WfK7d86lkvAun6OpKLfXhwxL3z8utoxJRqBwVBOHNp1pHBWzQcfj9YJX Tj8zqTOiX4cITVtYbZGs+RGSKCj5APdHERMbTph9uNdN9hyYtmJptIen0mPFtRj6KyWWFgcwZFR G7O44pp2Pkus/mBpY2XvCSBP+Kzdm8DmWebD+tpGLVaUdJJAT8t3TZidw8HS0lmpr0lpqbwSR+I wR6rCFyvwtjtA3RClBG0EO/GD+hU8K+bimxAgm1xOSL7/0aQT7RdqNYSFuvLTBoBN4pekjlAxO8 J7zM67dTi4VDDFHE6WsFwsiot6OvMMIP800wM8L0aRF0ARM4oD6lfvJ1b9sFN4+V0= X-Google-Smtp-Source: AGHT+IEMk80coTnlUDmPPQWbhE5B5F4oVkxuYy4WClnNTFRgaSNjVcVYPgxObppzl4xgqVZVarxCQQ== X-Received: by 2002:a05:690e:160f:b0:640:a958:f599 with SMTP id 956f58d0204a3-640d45cec8amr310541d50.46.1762549535914; Fri, 07 Nov 2025 13:05:35 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:35 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 02/22] liveupdate: luo_core: integrate with KHO Date: Fri, 7 Nov 2025 16:03:00 -0500 Message-ID: <20251107210526.257742-3-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Integrate the LUO with the KHO framework to enable passing LUO state across a kexec reboot. When LUO is transitioned to a "prepared" state, it tells KHO to finalize, so all memory segments that were added to KHO preservation list are getting preserved. After "Prepared" state no new segments can be preserved. If LUO is canceled, it also tells KHO to cancel the serialization, and therefore, later LUO can go back into the prepared state. This patch introduces the following changes: - During the KHO finalization phase allocate FDT blob. - Populate this FDT with a LUO compatibility string ("luo-v1"). LUO now depends on `CONFIG_KEXEC_HANDOVER`. The core state transition logic (`luo_do_*_calls`) remains unimplemented in this patch. Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 6 + include/linux/liveupdate/abi/luo.h | 54 +++++++ kernel/liveupdate/luo_core.c | 243 ++++++++++++++++++++++++++++- kernel/liveupdate/luo_internal.h | 17 ++ mm/mm_init.c | 4 + 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 include/linux/liveupdate/abi/luo.h create mode 100644 kernel/liveupdate/luo_internal.h diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 730b76625fec..0be8804fc42a 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -13,6 +13,8 @@ =20 #ifdef CONFIG_LIVEUPDATE =20 +void __init liveupdate_init(void); + /* Return true if live update orchestrator is enabled */ bool liveupdate_enabled(void); =20 @@ -21,6 +23,10 @@ int liveupdate_reboot(void); =20 #else /* CONFIG_LIVEUPDATE */ =20 +static inline void liveupdate_init(void) +{ +} + static inline bool liveupdate_enabled(void) { return false; diff --git a/include/linux/liveupdate/abi/luo.h b/include/linux/liveupdate/= abi/luo.h new file mode 100644 index 000000000000..9483a294287f --- /dev/null +++ b/include/linux/liveupdate/abi/luo.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: Live Update Orchestrator ABI + * + * This header defines the stable Application Binary Interface used by the + * Live Update Orchestrator to pass state from a pre-update kernel to a + * post-update kernel. The ABI is built upon the Kexec HandOver framework + * and uses a Flattened Device Tree to describe the preserved data. + * + * This interface is a contract. Any modification to the FDT structure, no= de + * properties, compatible strings, or the layout of the `__packed` seriali= zation + * structures defined here constitutes a breaking change. Such changes req= uire + * incrementing the version number in the relevant `_COMPATIBLE` string to + * prevent a new kernel from misinterpreting data from an old kernel. + * + * FDT Structure Overview: + * The entire LUO state is encapsulated within a single KHO entry named = "LUO". + * This entry contains an FDT with the following layout: + * + * .. code-block:: none + * + * / { + * compatible =3D "luo-v1"; + * liveupdate-number =3D <...>; + * }; + * + * Main LUO Node (/): + * + * - compatible: "luo-v1" + * Identifies the overall LUO ABI version. + * - liveupdate-number: u64 + * A counter tracking the number of successful live updates performed. + */ + +#ifndef _LINUX_LIVEUPDATE_ABI_LUO_H +#define _LINUX_LIVEUPDATE_ABI_LUO_H + +/* + * The LUO FDT hooks all LUO state for sessions, fds, etc. + * In the root it allso carries "liveupdate-number" 64-bit property that + * corresponds to the number of live-updates performed on this machine. + */ +#define LUO_FDT_SIZE PAGE_SIZE +#define LUO_FDT_KHO_ENTRY_NAME "LUO" +#define LUO_FDT_COMPATIBLE "luo-v1" +#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number" + +#endif /* _LINUX_LIVEUPDATE_ABI_LUO_H */ diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 0e1ab19fa1cd..c1bd236bccb0 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -42,11 +42,23 @@ =20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt =20 +#include #include +#include #include +#include +#include +#include +#include + +#include "luo_internal.h" +#include "kexec_handover_internal.h" =20 static struct { bool enabled; + void *fdt_out; + void *fdt_in; + u64 liveupdate_num; } luo_global; =20 static int __init early_liveupdate_param(char *buf) @@ -55,6 +67,122 @@ static int __init early_liveupdate_param(char *buf) } early_param("liveupdate", early_liveupdate_param); =20 +static int __init luo_early_startup(void) +{ + phys_addr_t fdt_phys; + int err, ln_size; + const void *ptr; + + if (!kho_is_enabled()) { + if (liveupdate_enabled()) + pr_warn("Disabling liveupdate because KHO is disabled\n"); + luo_global.enabled =3D false; + return 0; + } + + /* Retrieve LUO subtree, and verify its format. */ + err =3D kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys); + if (err) { + if (err !=3D -ENOENT) { + pr_err("failed to retrieve FDT '%s' from KHO: %pe\n", + LUO_FDT_KHO_ENTRY_NAME, ERR_PTR(err)); + return err; + } + + return 0; + } + + luo_global.fdt_in =3D __va(fdt_phys); + err =3D fdt_node_check_compatible(luo_global.fdt_in, 0, + LUO_FDT_COMPATIBLE); + if (err) { + pr_err("FDT '%s' is incompatible with '%s' [%d]\n", + LUO_FDT_KHO_ENTRY_NAME, LUO_FDT_COMPATIBLE, err); + + return -EINVAL; + } + + ln_size =3D 0; + ptr =3D fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM, + &ln_size); + if (!ptr || ln_size !=3D sizeof(luo_global.liveupdate_num)) { + pr_err("Unable to get live update number '%s' [%d]\n", + LUO_FDT_LIVEUPDATE_NUM, ln_size); + + return -EINVAL; + } + memcpy(&luo_global.liveupdate_num, ptr, + sizeof(luo_global.liveupdate_num)); + pr_info("Retrieved live update data, liveupdate number: %lld\n", + luo_global.liveupdate_num); + + return 0; +} + +void __init liveupdate_init(void) +{ + int err; + + err =3D luo_early_startup(); + if (err) { + pr_err("The incoming tree failed to initialize properly [%pe], disabling= live update\n", + ERR_PTR(err)); + luo_global.enabled =3D false; + } +} + +/* Called during boot to create LUO fdt tree */ +static int __init luo_fdt_setup(void) +{ + const u64 ln =3D luo_global.liveupdate_num + 1; + void *fdt_out; + int err; + + fdt_out =3D luo_alloc_preserve(LUO_FDT_SIZE); + if (IS_ERR(fdt_out)) { + pr_err("failed to allocate/preserve FDT memory\n"); + return PTR_ERR(fdt_out); + } + + err =3D fdt_create(fdt_out, LUO_FDT_SIZE); + err |=3D fdt_finish_reservemap(fdt_out); + err |=3D fdt_begin_node(fdt_out, ""); + err |=3D fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); + err |=3D fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); + err |=3D fdt_end_node(fdt_out); + err |=3D fdt_finish(fdt_out); + if (err) + goto exit_free; + + err =3D kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out); + if (err) + goto exit_free; + luo_global.fdt_out =3D fdt_out; + + return 0; + +exit_free: + luo_free_unpreserve(fdt_out, LUO_FDT_SIZE); + pr_err("failed to prepare LUO FDT: %d\n", err); + + return err; +} + +static int __init luo_late_startup(void) +{ + int err; + + if (!liveupdate_enabled()) + return 0; + + err =3D luo_fdt_setup(); + if (err) + luo_global.enabled =3D false; + + return err; +} +late_initcall(luo_late_startup); + /* Public Functions */ =20 /** @@ -69,7 +197,22 @@ early_param("liveupdate", early_liveupdate_param); */ int liveupdate_reboot(void) { - return 0; + int err; + + if (!liveupdate_enabled()) + return 0; + + err =3D kho_finalize(); + if (err) { + pr_err("kho_finalize failed %d\n", err); + /* + * kho_finalize() may return libfdt errors, to aboid passing to + * userspace unknown errors, change this to EAGAIN. + */ + err =3D -EAGAIN; + } + + return err; } =20 /** @@ -84,3 +227,101 @@ bool liveupdate_enabled(void) { return luo_global.enabled; } + +/** + * luo_alloc_preserve - Allocate, zero, and preserve memory. + * @size: The number of bytes to allocate. + * + * Allocates a physically contiguous block of zeroed pages that is large + * enough to hold @size bytes. The allocated memory is then registered with + * KHO for preservation across a kexec. + * + * Note: The actual allocated size will be rounded up to the nearest + * power-of-two page boundary. + * + * @return A virtual pointer to the allocated and preserved memory on succ= ess, + * or an ERR_PTR() encoded error on failure. + */ +void *luo_alloc_preserve(size_t size) +{ + struct folio *folio; + int order, ret; + + if (!size) + return ERR_PTR(-EINVAL); + + order =3D get_order(size); + if (order > MAX_PAGE_ORDER) + return ERR_PTR(-E2BIG); + + folio =3D folio_alloc(GFP_KERNEL | __GFP_ZERO, order); + if (!folio) + return ERR_PTR(-ENOMEM); + + ret =3D kho_preserve_folio(folio); + if (ret) { + folio_put(folio); + return ERR_PTR(ret); + } + + return folio_address(folio); +} + +/** + * luo_free_unpreserve - Unpreserve and free memory. + * @mem: Pointer to the memory allocated by luo_alloc_preserve(). + * @size: The original size requested during allocation. This is used to + * recalculate the correct order for freeing the pages. + * + * Unregisters the memory from KHO preservation and frees the underlying + * pages back to the system. This function should be called to clean up + * memory allocated with luo_alloc_preserve(). + */ +void luo_free_unpreserve(void *mem, size_t size) +{ + struct folio *folio; + + unsigned int order; + + if (!mem || !size) + return; + + order =3D get_order(size); + if (WARN_ON_ONCE(order > MAX_PAGE_ORDER)) + return; + + folio =3D virt_to_folio(mem); + WARN_ON_ONCE(kho_unpreserve_folio(folio)); + folio_put(folio); +} + +/** + * luo_free_restore - Restore and free memory after kexec. + * @mem: Pointer to the memory (in the new kernel's address space) + * that was allocated by the old kernel. + * @size: The original size requested during allocation. This is used to + * recalculate the correct order for freeing the pages. + * + * This function is intended to be called in the new kernel (post-kexec) + * to take ownership of and free a memory region that was preserved by the + * old kernel using luo_alloc_preserve(). + * + * It first restores the pages from KHO (using their physical address) + * and then frees the pages back to the new kernel's page allocator. + */ +void luo_free_restore(void *mem, size_t size) +{ + struct folio *folio; + unsigned int order; + + if (!mem || !size) + return; + + order =3D get_order(size); + if (WARN_ON_ONCE(order > MAX_PAGE_ORDER)) + return; + + folio =3D kho_restore_folio(__pa(mem)); + if (!WARN_ON(!folio)) + free_pages((unsigned long)mem, order); +} diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h new file mode 100644 index 000000000000..29f47a69be0b --- /dev/null +++ b/kernel/liveupdate/luo_internal.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#ifndef _LINUX_LUO_INTERNAL_H +#define _LINUX_LUO_INTERNAL_H + +#include + +void *luo_alloc_preserve(size_t size); +void luo_free_unpreserve(void *mem, size_t size); +void luo_free_restore(void *mem, size_t size); + +#endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/mm/mm_init.c b/mm/mm_init.c index c6812b4dbb2e..20c850a52167 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2703,6 +2704,9 @@ void __init mm_core_init(void) */ kho_memory_init(); =20 + /* Live Update should follow right after KHO is initialized */ + liveupdate_init(); + memblock_free_all(); mem_init(); kmem_cache_init(); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f172.google.com (mail-yw1-f172.google.com [209.85.128.172]) (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 4D2D03101DA for ; Fri, 7 Nov 2025 21:05:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549540; cv=none; b=IIMNMlulE0OF7fXiWLRo13irf2QHNwIaBHrn1/j661w0qV5Rsk76iqwamkeF8rUE0UMGTb+s0oBn+DVNEIy53uILUE5RgDvkcbCpqgtFr4r9mhTZ/YqKG5HUWKLe6bripsckzdQuNk57UqfjJVfuO+7ZbsnU9W/6n4cNbWr+zZA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549540; c=relaxed/simple; bh=frmW4uur/JiJP0+axQ4M0kbVoUhK8VyFB7s+YzrbZmA=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Jh2eoBuPCYa/ARu6tCnMlIGjhTntYOxO9laeiOyIdGU726V6Vy2DfpxBi0NhpJR8Zl4r1+Kj6tKmElkum4yufFy5Eii6MHbw6CkAtcu0SxJ6R09VqzBHqYPS731Tg7ImKD36PCtMP4M9XYVQ5RyUqGBX/Kvwo5+Wjzonb/maFvQ= 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=bN97aslA; arc=none smtp.client-ip=209.85.128.172 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="bN97aslA" Received: by mail-yw1-f172.google.com with SMTP id 00721157ae682-787cc9de986so8595117b3.2 for ; Fri, 07 Nov 2025 13:05:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549538; x=1763154338; 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=LAfmXKExFsP/wCpt9jvB5iXqu6nwsrz4tNI+tUSnwDI=; b=bN97aslAgDHF9EpIrRRoahib39FwTlRaylf7ZUHm3nMtLWNtDm0fHBnz+9skzMLgoE q/YLCa6wI4G8G9pFzO5+mPiBGlK6yZpsAm4gF5s/FGBdLOEYWGQytYNw4aNHN5/0CZus znKxXV4D/NC+3IlLnVBBTgIMnyyqiyVzmwd7hG8xPEgos8oo51nFd/yJdrroy35XcJmg TX3e/r+hAP2rLJWc78b+k5LWRuA6ZBC3uTuS+WGLEB8DHQlbpJxj3y0llXa7mR215Gbz PKWTow9F/zRCRED9CrUDQdcYpuLlEUVnpBrYB5esWwmM+DtrwLqRVysmKc9WG8IeZ3Vb QgoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549538; x=1763154338; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LAfmXKExFsP/wCpt9jvB5iXqu6nwsrz4tNI+tUSnwDI=; b=OPTq/8S09dZcWoioCQlejYaT+F3ei2tkv46ZsZyFZ84JzLyr8Hu7hhO6+4Du+0m14Q jHiw94xi/JVMRki4D/eOtuimhe6bENIbxI4q9bnPieXjUwrHpZq3JHvKiJce88z37s2b upfVrG0QpmsEwKaZnQamuBw8i/Ib9V5DHTZmlOCCrA4OFkaKfbh+9r550NTV8rDOnJfU 6GW5iohja9KnToB+8nQc5FXrDmkoU3Bez/84hvdCHfhfLPraZ5ixs+iZ+E8GBzxpZJuq lD3ZrnrWLspnz4O15UuHyzTNmv8opwazEmaNUSJds3sMy/l8VF/cUP/cvrfPjMmBf/yx sWvQ== X-Forwarded-Encrypted: i=1; AJvYcCWHsCapAfOuLDehMtVIiEuBYQcVNl/Bn7QvJa3rVYLR7IBsOdEMhEHpk2X9IgXhW3jQxd1y4d0f6biTA0M=@vger.kernel.org X-Gm-Message-State: AOJu0YwO+r9P/1VN/5ww65RCQDjXLu6jrHNALpVH38uoDkrS3oiSegUj N2qww0pg3NLboxaLkLhxL4lag2unX5aOLHrWAgtlt3aH347I125npiHSSoWJ4slSU+U= X-Gm-Gg: ASbGncsRjmf4NSVL28VGz/Z5UhXpkhGByosjro66eFo1nWh1bH0yHWgLpap9m3hNvMM pzjEqluM0QH2pLvZymt29QpkWWjkEdaV9HgKDgFsUoBh/org6JqsyDULMLGRwSL4NDfjYK0NXmG C50C82JzH6UaIIWbrC97f3t1C9kqYz3IwTZBugs1HSGBJngusbPkt/8T/AB68EcozCjOsTm+/sn aE6cpMbDgSRA5jpFOXotGwaBZ/BUEsUbX7UNDW92MmApJt5g7UudURlpFcsqBvdPMPyNUR35VFk tetBNOcpzSGDCdZbUk5BdYiSC3y/37wQv8uzA3X/SivTakZiy3EfPeAt5zFiiZRqOe78vpYPRMp OO4ZNptTvA69E424LOn18O3kDCykoUSzGNKHTM1nLMeleL8j/q8x9uPfj4YLa2eMWiOzhgbpUln k/xBvTcX+QICNPmOEreWCS5QbqTWWAh+/2mvcNpKhIVvZ1LoRaapRgFiQaJrSqGgRYH3Jr5zfvT A== X-Google-Smtp-Source: AGHT+IHP3QoDDPe69Dq6qZNTn006B72sZIHWCNrPLWZh2Sy97b00SWoXLarWg9cKIVLBa+/0WbjfEw== X-Received: by 2002:a05:690c:a003:b0:785:e76e:59bf with SMTP id 00721157ae682-787d542919dmr6289787b3.50.1762549537853; Fri, 07 Nov 2025 13:05:37 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:37 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 03/22] reboot: call liveupdate_reboot() before kexec Date: Fri, 7 Nov 2025 16:03:01 -0500 Message-ID: <20251107210526.257742-4-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Modify the reboot() syscall handler in kernel/reboot.c to call liveupdate_reboot() when processing the LINUX_REBOOT_CMD_KEXEC command. This ensures that the Live Update Orchestrator is notified just before the kernel executes the kexec jump. The liveupdate_reboot() function triggers the final freeze event, allowing participating FDs perform last-minute check or state saving within the blackout window. The call is placed immediately before kernel_kexec() to ensure LUO finalization happens at the latest possible moment before the kernel transition. If liveupdate_reboot() returns an error (indicating a failure during LUO finalization), the kexec operation is aborted to prevent proceeding with an inconsistent state. Signed-off-by: Pasha Tatashin --- kernel/reboot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/reboot.c b/kernel/reboot.c index ec087827c85c..bdeb04a773db 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -797,6 +798,9 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsig= ned int, cmd, =20 #ifdef CONFIG_KEXEC_CORE case LINUX_REBOOT_CMD_KEXEC: + ret =3D liveupdate_reboot(); + if (ret) + break; ret =3D kernel_kexec(); break; #endif --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f51.google.com (mail-yx1-f51.google.com [74.125.224.51]) (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 CD577276058 for ; Fri, 7 Nov 2025 21:05:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549543; cv=none; b=RtcawdNLG12VceZsgP9/AaLc9R9bP7EkyucK1teGMv7kKlBAga3qCl+QIqY+69vw2+MtTaU1Zs3kZ+JwMmot3jN1pwswVawvpz+uTS2suTzJbMxo0Ss6G5ySqbbKzWXhM+vATHKg9KkkgzPTTB9BKe01FISdsuKLv+PwvqHfqTg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549543; c=relaxed/simple; bh=2AXBG/P4zHHPE991SiFKrlF/2yl4Orgmw9rox7i8sZA=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PzrRGYRuKRMS8ZAmna8LRbB3JSNIiz75oKkr2Ym5haT+vECqhDKN07zBwidYNgc7TpWQs4ib+4Xzkt7l5Lv9EXx3reFryKT1Obadac/G1Rxk3gClFuV4nFBcwKgnJB7lv0nqsoC03ZMgkADyzaKhBpxdr6tvevDBdVH2z4q78fU= 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=KlrOE5Ab; arc=none smtp.client-ip=74.125.224.51 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="KlrOE5Ab" Received: by mail-yx1-f51.google.com with SMTP id 956f58d0204a3-640d43060d2so186983d50.2 for ; Fri, 07 Nov 2025 13:05:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549540; x=1763154340; 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=eG3KZj/IJ+feH5LNOQCm1I/xQEDt38CQ/0swaPI9wVw=; b=KlrOE5AbYBpsv4e2mhXSJRLmPj6kQUoPp8bfWosYQg1KcuemMvi+VqHr20Gs6IG7Np tROfxZpQI8C+VW3/ojJiRzQyNKxTV/977xFgNnfJyA2MQk2dvdlDF+TsDsYh6V77UJEc 9OClbSPa4kE5JnGEDErFu+/uMY1tr902iyYZbMbRH7aNvGHIyAtIrwFU+3T5aSrUJsMB NppOm/UVb8f35QJA/XMDdnOe2yxintuvRrqY6dNvJLbkL4wkXpNqLAb59ZQehESpSCaZ CbvGZi3IC0cBOuESBiMXnkUAnGXCPp9eRvXeMC9NLQGPwY+y/ecIm6uohrUxkLeacJPJ c07Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549540; x=1763154340; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=eG3KZj/IJ+feH5LNOQCm1I/xQEDt38CQ/0swaPI9wVw=; b=o27Ldjvor3fHhGKmYLl3nhyHlEEtSy37Ufh0lrtYC4pvmdP1Kv6aFnhrdFyoiJ/IGd sPPpne3cvmcsUDdPfwNraldB5JJf+jMkcFTLhp+UF73rDn7C4kQ6vhVXDK4XAQXf2MnT K+C0DwQfWAPK6/42XrrbWlzSIWflluPxScffhe4aQxEfwkS5OexEyh3FntYIEPm33B92 G5pZD30HXm/H00xicqSaoyEJHXhno3ewQy/3kwtUPJKZjmHfeK1DwP+12i1y/qVnMRkM YLVIUayWIhfFhVfS6UAi7nqwAN4oRuIIrq2X3IIpk4CwbiOCXE1CAnh6fCIX3cjojh1I Sc2A== X-Forwarded-Encrypted: i=1; AJvYcCUCyPPit7haO3t063OdqHKvNTnq92A7g50Ua07CWNhRHdD7rb9OlpmU9r/MO4lk1hK7w2pNUfss9hkWVYQ=@vger.kernel.org X-Gm-Message-State: AOJu0YwhokNB+oYS4yd3SZiANdYROJFGgxoGT2SArVuiqnfOLcqgWabG NPBvi4531mAMfF6ZWqEcOTmGRvAKFkpq1LilbFiO9688uB2jG9Yq5Hj2FJd9y2OKXy8= X-Gm-Gg: ASbGncs6khyXXCvKvxhzkLtMBcX2YRi5w6HWxta/fD+butXrUfnq6Ahfwgi5GbY/eGR pnn8jwfXGlcnzK+q4npRNHoAZQdjUiGYBOvsJfCr49RB8Scw4D0d+UBm5CbCWDUjscBlZZJs8Z6 ohVb0PONn2BWCrdTMmlvftKpcFGZ6I+ftnBVvi7giIZXNWMRMQGd8hdmY6UYkYB2E85S4NcQOBL HCVzkRzzw3EHFrvAkfqWj+I4R4ANBThYdfPI7rxWvhypHlXwTM/pf0ULn8XGnW+JvXRR1k4lDNU K1ILjkbqzjmcci/C53tMo9bQ1afJnmQl75UQc/zeQIjUv5s3mTe6cUKt6Wo1Mq9VnQTyqTn8TQJ sk32/zYcIqt7ThUD7X+U3vjzVBnBo0q1a6kLzB3wXxmo8Q6pufS2OplywUZP4uMwy+7E7hO/VW9 Eh3kPfze6rHmtUksoXQHgPDwjB4krIFf9Xq2U8v4Ra7bmF/Kh1k+BXdWWpMOGx2SQ= X-Google-Smtp-Source: AGHT+IGgi833lso4oL9ECYqCcFQNiRqXoYUWKz1MoKHjJMaptGXdg+WJ5uxd88A3KFV7TB/Wv9G8Zg== X-Received: by 2002:a05:690e:431b:b0:63f:a524:1f93 with SMTP id 956f58d0204a3-640d45ff3e1mr344048d50.68.1762549539784; Fri, 07 Nov 2025 13:05:39 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:39 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 04/22] liveupdate: Kconfig: Make debugfs optional Date: Fri, 7 Nov 2025 16:03:02 -0500 Message-ID: <20251107210526.257742-5-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Now, that LUO can drive KHO state internally, the debugfs API became optional, so remove the default config. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index e1fdcf7f57f3..054f6375a7af 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -54,7 +54,6 @@ config KEXEC_HANDOVER_DEBUG =20 config KEXEC_HANDOVER_DEBUGFS bool "kexec handover debugfs interface" - default KEXEC_HANDOVER depends on KEXEC_HANDOVER select DEBUG_FS help --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f177.google.com (mail-yw1-f177.google.com [209.85.128.177]) (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 D7791334C35 for ; Fri, 7 Nov 2025 21:05:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549544; cv=none; b=lV1qoa7aukbj+hpjZTrhRCnHP3bXhu0L2wELDOb2UmDdG8rLND+AO9cVBuba0H2kQPyOyYEV/uKNG36lsrQKHznXT8W/Dp5A1lI+xb/t1VT+9NlbgIkaUV+i8JeGuJe9CSOTTH8DfmVFEnDc1N6hHOSrio+7s22aLQ+P/zPfV50= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549544; c=relaxed/simple; bh=rJJzACH6ND4KsnbPDxqHJ1cqaNF23c/cUIT9/Mg0NMc=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XTgmQe1X1Al3QIWtIhJcEv5JeVKtorpehG1ndYZ541eA/xNv/1IEdJyqdLjEbOsN2f5LSzgsbvl6PBquC3hy7xGm8+6qF1L0qRmuSnxQ0vSSV2ZNRrCSPYXHiRVy/VQwtZq2sJFz7F2Xn+/dujGJho3YFjBWQFlFzw/BA4TSfxg= 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=Dxrtp3Dp; arc=none smtp.client-ip=209.85.128.177 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="Dxrtp3Dp" Received: by mail-yw1-f177.google.com with SMTP id 00721157ae682-7866c61a9bbso11319437b3.1 for ; Fri, 07 Nov 2025 13:05:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549542; x=1763154342; 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=b9ls15f+4MIh5Yb5Zu+WvMWjZ1HqhhM9dptW1sLofus=; b=Dxrtp3DpJ8bM8lUNVIKmMBmLhQroPEWgsBFR8pWmBfpoVhz3x2TFW2ZT8YQtHeAv8n mAs8UBGOJehkVKbX2yFsXnkNzBIeqH4u7ClYrhMp5O5YSfnK3/n2/8Wy2NQAOspk0USY FORl/pAIc2ra5TUvQR4sPksfOVdfwCboqX2GSOBiSwFsAQROk9+SvC5uE/RfooLsaPPv N++K1UzVnHxCu1NC7n9U0DBz2S2/Q85ORQR9sT4ya/Mjq7+LK0NPTnmlDZPOQjcr/Cpx CpzSlw873HUqc0+aitwaUsBz5vZ9Ea/VlGLVc30uH/RS6F8NliiGwK0NidR+IYJAiT81 mxsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549542; x=1763154342; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=b9ls15f+4MIh5Yb5Zu+WvMWjZ1HqhhM9dptW1sLofus=; b=skCLWXO43ahiKy7rVM1ksDRjCYH+N1WUKEXOF1tamHCr4stXKIOc5TCePgPwQwojsR xBy27eq6HEzBBB4waftOQvach7DyGU+KvLDAdvryGM9HTq2gfq9qZ2dg4qE59EaD2oHK /EnjlWxuch+5HPB0+oXA0AQZfJLdwNJkWx2lAZ+zN2BRVofNRYeDBjcTDr1YxSyn/MmA HBFPeAJ8i3oghEZHq4jD82HZkAJWVtLPFpNlVPuqmfs+ZkMMxCBPwp8s0VSRCK+qcVW4 BBzH1u7vnq0N+zqgWn8u1MJSEx+k03paSuhhd4Ne+wjmP/dsFEJ6LPO2vz6YOOL02s8q IJzw== X-Forwarded-Encrypted: i=1; AJvYcCV0BxBP+3hSDrvdh3QKQlnLFZLE4s2xPQgBmxEzLQYLTDJKi70f0/r9oiBw2tejGeACBw0oZdOCKvlxf5E=@vger.kernel.org X-Gm-Message-State: AOJu0YyIKfIB7RI5uj8EnTPh3NtZA5da3O6ahwA6yBN2MDf8pdv7uOIJ xPvEGbKI4KRMmkd356c2w03LTzA0bau+exg9w2ldsM2EUlF5dn5fPnysjYlHIxIQGv0= X-Gm-Gg: ASbGncuxcyxafmGudADBLJJNQ6TZcUKOvghBMof9FGW+LTDK1/i41zcQD+MbOKIQJZ9 BZATMFteYUK2eXLY8P3xz9jSWaAlh3RMpOxjzn54z9qAfHmpxpoDDtj51wyerdkXLlEaXkEd7LY synOB5d4h9uRcVFHZCIwITnmeEflK+T01ucreb8977NvyAmMlERJM6H9F8ioO2YzYO9V5sH60Pc FtpoHLj2scMM52FdRZekdVycFiFbD38NbzZOpWhQbK3ug0Ee0mIEcf0HSuH1QeZlfN+qZtXH1pZ 1yxLJQG6/zq9dhlGvbIYMTiRByH5OqrFehwatjaljXpsFdbEYFZWX1l00aNv3HYo4EgZ1cnCb/P VR9Jhy6iUuWRqHlMRFydgy6LXME9KW8yuAK0UizN5ekPFVrVNcPS0YBnnTUbO5pJ0i69PhqyMJH 1uCwciGcMAVHlZ5/J3eO0bE3Dta2fzdNBZ/hn8MdN9H2QxfCmKyMSb/JKNRAnRBJE= X-Google-Smtp-Source: AGHT+IFMBy2As0ojpganARRAtGLKmm8lcgodoc22XvJ7tunGfpqLiCKm9BOIEA7E25Bvbl2mUycg2A== X-Received: by 2002:a0d:d0c5:0:b0:787:bf16:d489 with SMTP id 00721157ae682-787d5467e1cmr5985997b3.62.1762549541688; Fri, 07 Nov 2025 13:05:41 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:41 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 05/22] liveupdate: kho: when live update add KHO image during kexec load Date: Fri, 7 Nov 2025 16:03:03 -0500 Message-ID: <20251107210526.257742-6-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" In case KHO is driven from within kernel via live update, finalize will always happen during reboot, so add the KHO image unconditionally. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/kexec_handover.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index 9f0913e101be..b54ca665e005 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1489,7 +1490,7 @@ int kho_fill_kimage(struct kimage *image) int err =3D 0; struct kexec_buf scratch; =20 - if (!kho_out.finalized) + if (!kho_out.finalized && !liveupdate_enabled()) return 0; =20 image->kho.fdt =3D virt_to_phys(kho_out.fdt); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f52.google.com (mail-yx1-f52.google.com [74.125.224.52]) (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 E7EA8334691 for ; Fri, 7 Nov 2025 21:05:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549547; cv=none; b=UIYVXCUjGYyqvqpGOtQ7MwEYYLpYf6C6mKcaIKWBFHa1VNNfTUmggzPGB1tYDkQe97gBPIIwWDd7eA+0HAzq618jgelS+oFf9btXZykV0F2M9bdYP4pZSbWbbYViagSiMQs/q23Ir+yVBRcURcKs+FTz5diAvZYuM1j9jqNhx0M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549547; c=relaxed/simple; bh=mPn/8YK8eR57eXYMETr2MhujsH5VgsoT67HRJTBNXGg=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UXGmJ8MKpkxjvSRE2rKn3PorRUdn2uVOUV1R3OZEsx0ls8bcgCmpLBZf7mBdoSx4M8cqmopIrC5rXg6g07aBY6XSOOVHDfp7/ytRc/e7keL9E5aIwI4X8yXls9wl1XjggaiQVQI0s1yUbfHQoG+GMrCOIqaig7WCRqDMcEyWp9s= 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=Mpe7MRLT; arc=none smtp.client-ip=74.125.224.52 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="Mpe7MRLT" Received: by mail-yx1-f52.google.com with SMTP id 956f58d0204a3-640d4f2f13dso23977d50.1 for ; Fri, 07 Nov 2025 13:05:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549544; x=1763154344; 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=kWYNesGhkR10PkoKcBey+XtSQMZ3Xl6BgxZ5cmCCRvI=; b=Mpe7MRLTjZN+U5tD8bhWRoWz94rPSFComEsz0U7vo/iCdZpQDEr2RVmSFDNJFohdIH K7iGdbmTFqjpSdNrgWTvR03xwbPnigup9/bldo9Qq75PTMvH6mj8IpwhkUYqWSDhrQ0M /bafATzCrAPAA6EnOUGZB7ynYyui2RZYhuMkxg1IjMrNq7xFtUNjnUt9TijcipdMbPR/ N8pJ0KGNBvGjf++yWdwitVbQx6E0KYM0yKeqN/lqaoSTwUbt9VvXTGe7uwN5LUqsUF1N nGPK5E/2GdZtGruqaBUE42Zwaru7B1dL+OWIQUij2Ku2eGps0k6G2b2PxJJFggffTDK+ lBzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549544; x=1763154344; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=kWYNesGhkR10PkoKcBey+XtSQMZ3Xl6BgxZ5cmCCRvI=; b=CmVicZgpmBz0/PBObhGXPWFGRvMA9KRyTvPY4sheIa7JJXmlubiEw/l5huvhgzWFQs zOQTX4ofh3nCocmM5IpQmPg8uQi3kVbLWgx3l3JoSJpvHSV3TQ33LqFGWtOb175NcZnK L/oeSnCefVHVidmAzPZJbkOjnmm7Ppu6BOgFXlavyJUxB5GkQzXiYVPglXeyA0tv3Mrg xM1VOL2UeA7IR2MjQS5oaxxkfAEAf7lqWTVzxIqCqjAeWRkIM6X5R0yx0S0y532FXmHl FECap0GW1vHXy9weVzrShOuUsnhgMIJ+whocayFxUrc08szHmL31FRjMtuB5He+NVwG0 fynA== X-Forwarded-Encrypted: i=1; AJvYcCXPr8xzbiARCOTLy9N/zHmrUViWSk2T3V/U74W5DSKai9g8wrouhFBeQlUm7v0J/P1tPAR2ovGx/FxGNHo=@vger.kernel.org X-Gm-Message-State: AOJu0YzeExr9a2jwLaD+O8yTbAA6ChBs2SOsNNqkM6slcaqfojWNgAg4 b8qC1upqqCoOgAP7VvtOqE1oS89WXqZrJkP3EYXUAmijxJpcIoz0gSl8CwFi3zdJhAs= X-Gm-Gg: ASbGncvXKxh8VcfyT5J13XegrwGDxX9nsJ7hkK5wADkP8AXK5ngO3iN2BMWJUHJ9bjd k+ShNDLgYBnuOtt/KcxmY1M8MxGi1ey/PPcR1eFMRwEX3s4rS+3hrIwCSTFwmuqVX9LyJyq68XK Cj6fdtt8PF0r9Tz3rdexk5jNz+qI8K9HsBUgorD40U1ksRiaOrP5adZa4cbxSJ3vB9TbCOD5UJ/ qlc8buKvJKh1xGYp5Yb0TjJNuaIQv13aaAfd7RO3mYQczwwmsBhj9sZ3zrd6Zt7zhcVJW152bge 7aynLsIBLnfsRYR4PFxkGnEPEVn7J/7bx971VSFvby2hnRvLa+UY7jjl2zS7kI22+cjS8zeFOTY podRy1lroUVexrCvOaKUj2o+rv7tOyUUNl1eXBNKr1GukwXw8N2X8Bd1ZpdT92q9E3IRWWtUbgt n1gk+ERYirRxf/vqWobEh8efme0vUOOCL1ANWzUY35jVXXBb2Bp+GHDtHp8DmKMqE= X-Google-Smtp-Source: AGHT+IHZkA+iubD7mrzJvp/IHYrx6lwdQhNYRHBn0SrBrrOgi5Sn4DCq+5KS+qqyTyRoxWhz+JIbTg== X-Received: by 2002:a05:690e:150e:b0:63f:9d2c:3579 with SMTP id 956f58d0204a3-640d45c34d8mr431150d50.34.1762549543648; Fri, 07 Nov 2025 13:05:43 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:43 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 06/22] liveupdate: luo_session: add sessions support Date: Fri, 7 Nov 2025 16:03:04 -0500 Message-ID: <20251107210526.257742-7-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 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 --- include/linux/liveupdate/abi/luo.h | 81 ++++++ include/uapi/linux/liveupdate.h | 3 + kernel/liveupdate/Makefile | 3 +- kernel/liveupdate/luo_core.c | 9 + kernel/liveupdate/luo_internal.h | 39 +++ kernel/liveupdate/luo_session.c | 405 +++++++++++++++++++++++++++++ 6 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 kernel/liveupdate/luo_session.c diff --git a/include/linux/liveupdate/abi/luo.h b/include/linux/liveupdate/= abi/luo.h index 9483a294287f..37b9fecef3f7 100644 --- a/include/linux/liveupdate/abi/luo.h +++ b/include/linux/liveupdate/abi/luo.h @@ -28,6 +28,11 @@ * / { * compatible =3D "luo-v1"; * liveupdate-number =3D <...>; + * + * luo-session { + * compatible =3D "luo-session-v1"; + * luo-session-head =3D ; + * }; * }; * * Main LUO Node (/): @@ -36,11 +41,37 @@ * Identifies the overall LUO ABI version. * - liveupdate-number: u64 * A counter tracking the number of successful live updates performed. + * + * Session Node (luo-session): + * This node describes all preserved user-space sessions. + * + * - compatible: "luo-session-v1" + * Identifies the session ABI version. + * - luo-session-head: u64 + * The physical address of a `struct luo_session_head_ser`. This struc= ture is + * the header for a contiguous block of memory containing an array of + * `struct luo_session_ser`, one for each preserved session. + * + * Serialization Structures: + * The FDT properties point to memory regions containing arrays of simpl= e, + * `__packed` structures. These structures contain the actual preserved = state. + * + * - struct luo_session_head_ser: + * Header for the session array. Contains the total page count of the + * preserved memory block and the number of `struct luo_session_ser` + * entries that follow. + * + * - struct luo_session_ser: + * Metadata for a single session, including its name and a physical po= inter + * to another preserved memory block containing an array of + * `struct luo_file_ser` for all files in that session. */ =20 #ifndef _LINUX_LIVEUPDATE_ABI_LUO_H #define _LINUX_LIVEUPDATE_ABI_LUO_H =20 +#include + /* * The LUO FDT hooks all LUO state for sessions, fds, etc. * In the root it allso carries "liveupdate-number" 64-bit property that @@ -51,4 +82,54 @@ #define LUO_FDT_COMPATIBLE "luo-v1" #define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number" =20 +/* + * LUO FDT session node + * LUO_FDT_SESSION_HEAD: is a u64 physical address of struct + * luo_session_head_ser + */ +#define LUO_FDT_SESSION_NODE_NAME "luo-session" +#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v1" +#define LUO_FDT_SESSION_HEAD "luo-session-head" + +/** + * struct luo_session_head_ser - Header for the serialized session data bl= ock. + * @pgcnt: The total size, in pages, of the entire preserved memory block + * that this header describes. + * @count: The number of 'struct luo_session_ser' entries that immediately + * follow this header in the memory block. + * + * This structure is located at the beginning of a contiguous block of + * physical memory preserved across the kexec. It provides the necessary + * metadata to interpret the array of session entries that follow. + */ +struct luo_session_head_ser { + u64 pgcnt; + u64 count; +} __packed; + +/** + * struct luo_session_ser - Represents the serialized metadata for a LUO s= ession. + * @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 duri= ng + * 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 p= er + * session) is created and passed to the new kernel, allowing it to recons= truct + * 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; + #endif /* _LINUX_LIVEUPDATE_ABI_LUO_H */ diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.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 =20 +/* 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 413722002b7a..83285e7ad726 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_session.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 c1bd236bccb0..83257ab93ebb 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -116,6 +116,10 @@ static int __init luo_early_startup(void) pr_info("Retrieved live update data, liveupdate number: %lld\n", luo_global.liveupdate_num); =20 + err =3D luo_session_setup_incoming(luo_global.fdt_in); + if (err) + return err; + return 0; } =20 @@ -149,6 +153,7 @@ static int __init luo_fdt_setup(void) err |=3D fdt_begin_node(fdt_out, ""); err |=3D fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); err |=3D fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); + err |=3D luo_session_setup_outgoing(fdt_out); err |=3D fdt_end_node(fdt_out); err |=3D fdt_finish(fdt_out); if (err) @@ -202,6 +207,10 @@ int liveupdate_reboot(void) if (!liveupdate_enabled()) return 0; =20 + err =3D luo_session_serialize(); + if (err) + return err; + err =3D kho_finalize(); if (err) { pr_err("kho_finalize failed %d\n", err); diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 29f47a69be0b..b4f2d1443c76 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -14,4 +14,43 @@ void *luo_alloc_preserve(size_t size); void luo_free_unpreserve(void *mem, size_t size); void luo_free_restore(void *mem, size_t size); =20 +/** + * struct luo_session - Represents an active or incoming Live Update sessi= on. + * @name: A unique name for this session, used for identification and + * retrieval. + * @files_list: An ordered list of files associated with this session, it = is + * ordered by preservation time. + * @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. + * @mutex: Session lock, protects files_list, and count. + * @files: The physically contiguous memory block that holds the seri= alized + * state of files. + * @pgcnt: The number of pages files occupy. + */ +struct luo_session { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + struct list_head files_list; + struct luo_session_ser *ser; + long count; + struct list_head list; + bool retrieved; + struct mutex mutex; + struct luo_file_ser *files; + u64 pgcnt; +}; + +int luo_session_create(const char *name, struct file **filep); +int luo_session_retrieve(const char *name, struct file **filep); +int __init luo_session_setup_outgoing(void *fdt); +int __init luo_session_setup_incoming(void *fdt); +int luo_session_serialize(void); +int luo_session_deserialize(void); +bool luo_session_is_deserialized(void); + #endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_sessio= n.c new file mode 100644 index 000000000000..a3513118aa74 --- /dev/null +++ b/kernel/liveupdate/luo_session.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: LUO Sessions + * + * LUO Sessions provide the core mechanism for grouping and managing `stru= ct + * file *` instances that need to be preserved across a kexec-based live + * update. Each session acts as a named container for a set of file object= s, + * allowing a userspace agent to manage the lifecycle of resources critica= l to a + * workload. + * + * Core Concepts: + * + * - Named Containers: Sessions are identified by a unique, user-provided = name, + * which is used for both creation in the current kernel and retrieval i= n the + * next kernel. + * + * - Userspace Interface: Session management is driven from userspace via + * ioctls on /dev/liveupdate. + * + * - Serialization: Session metadata is preserved using the KHO framework.= When + * a live update is triggered via kexec, an array of `struct luo_session= _ser` + * is populated and placed in a preserved memory region. An FDT node is = also + * created, containing the count of sessions and the physical address of= this + * array. + * + * Session Lifecycle: + * + * 1. Creation: A userspace agent calls `luo_session_create()` to create a + * new, empty session and receives a file descriptor for it. + * + * 2. Serialization: When the `reboot(LINUX_REBOOT_CMD_KEXEC)` syscall is + * made, `luo_session_serialize()` is called. It iterates through all + * active sessions and writes their metadata into a memory area preser= ved + * by KHO. + * + * 3. Deserialization (in new kernel): After kexec, `luo_session_deserial= ize()` + * runs, reading the serialized data and creating a list of `struct + * luo_session` objects representing the preserved sessions. + * + * 4. Retrieval: A userspace agent in the new kernel can then call + * `luo_session_retrieve()` with a session name to get a new file + * descriptor and access the preserved state. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "luo_internal.h" + +/* 16 4K pages, give space for 819 sessions */ +#define LUO_SESSION_PGCNT 16ul +#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ + sizeof(struct luo_session_head_ser)) / \ + sizeof(struct luo_session_ser)) + +/** + * struct luo_session_head - Head struct 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` instance= s. + * @rwsem: A read-write semaphore providing synchronized access to the + * session list and other fields in this structure. + * @head_ser: The head data of serialization array. + * @ser: The serialized session data (an array of + * `struct luo_session_ser`). + * @active: Set to true when first initialized. If previous kernel did n= ot + * send session data, active stays false for incoming. + */ +struct luo_session_head { + long count; + struct list_head list; + struct rw_semaphore rwsem; + struct luo_session_head_ser *head_ser; + struct luo_session_ser *ser; + bool active; +}; + +/** + * struct luo_session_global - Global container for managing LUO sessions. + * @incoming: The sessions passed from the previous kernel. + * @outgoing: The sessions that are going to be passed to the next ker= nel. + * @deserialized: The sessions have been deserialized once /dev/liveupdate + * has been opened. + */ +struct luo_session_global { + struct luo_session_head incoming; + struct luo_session_head outgoing; + bool deserialized; +} luo_session_global; + +static struct luo_session *luo_session_alloc(const char *name) +{ + struct luo_session *session =3D kzalloc(sizeof(*session), GFP_KERNEL); + + if (!session) + return NULL; + + strscpy(session->name, name, sizeof(session->name)); + INIT_LIST_HEAD(&session->files_list); + session->count =3D 0; + INIT_LIST_HEAD(&session->list); + mutex_init(&session->mutex); + + return session; +} + +static void luo_session_free(struct luo_session *session) +{ + WARN_ON(session->count); + WARN_ON(!list_empty(&session->files_list)); + mutex_destroy(&session->mutex); + kfree(session); +} + +static int luo_session_insert(struct luo_session_head *sh, + struct luo_session *session) +{ + struct luo_session *it; + + guard(rwsem_write)(&sh->rwsem); + + /* + * For outgoing we should make sure there is room in serialization array + * for new session. + */ + if (sh =3D=3D &luo_session_global.outgoing) { + if (sh->count =3D=3D LUO_SESSION_MAX) + return -ENOMEM; + } + + /* + * 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, &sh->list, list) { + if (!strncmp(it->name, session->name, sizeof(it->name))) + return -EEXIST; + } + list_add_tail(&session->list, &sh->list); + sh->count++; + + return 0; +} + +static void luo_session_remove(struct luo_session_head *sh, + struct luo_session *session) +{ + guard(rwsem_write)(&sh->rwsem); + list_del(&session->list); + sh->count--; +} + +static int luo_session_release(struct inode *inodep, struct file *filep) +{ + struct luo_session *session =3D filep->private_data; + struct luo_session_head *sh; + + /* If retrieved is set, it means this session is from incoming list */ + if (session->retrieved) + sh =3D &luo_session_global.incoming; + else + sh =3D &luo_session_global.outgoing; + + luo_session_remove(sh, session); + luo_session_free(session); + + return 0; +} + +static const struct file_operations luo_session_fops =3D { + .owner =3D THIS_MODULE, + .release =3D luo_session_release, +}; + +/* Create a "struct file" for session */ +static int luo_session_getfile(struct luo_session *session, struct file **= filep) +{ + char name_buf[128]; + struct file *file; + + guard(mutex)(&session->mutex); + snprintf(name_buf, sizeof(name_buf), "[luo_session] %s", session->name); + file =3D anon_inode_getfile(name_buf, &luo_session_fops, session, O_RDWR); + if (IS_ERR(file)) + return PTR_ERR(file); + + *filep =3D file; + + return 0; +} + +int luo_session_create(const char *name, struct file **filep) +{ + struct luo_session *session; + int err; + + session =3D luo_session_alloc(name); + if (!session) + return -ENOMEM; + + err =3D luo_session_insert(&luo_session_global.outgoing, session); + if (err) { + luo_session_free(session); + return err; + } + + err =3D luo_session_getfile(session, filep); + if (err) { + luo_session_remove(&luo_session_global.outgoing, session); + luo_session_free(session); + } + + return err; +} + +int luo_session_retrieve(const char *name, struct file **filep) +{ + struct luo_session_head *sh =3D &luo_session_global.incoming; + struct luo_session *session =3D 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 =3D it; + break; + } + } + } + + if (!session) + return -ENOENT; + + scoped_guard(mutex, &session->mutex) { + if (session->retrieved) + return -EINVAL; + } + + err =3D luo_session_getfile(session, filep); + if (!err) { + scoped_guard(mutex, &session->mutex) + session->retrieved =3D true; + } + + return err; +} + +int __init luo_session_setup_outgoing(void *fdt_out) +{ + struct luo_session_head_ser *head_ser; + u64 head_ser_pa; + int err; + + head_ser =3D luo_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); + if (IS_ERR(head_ser)) + return PTR_ERR(head_ser); + head_ser_pa =3D __pa(head_ser); + + err =3D fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME); + err |=3D fdt_property_string(fdt_out, "compatible", + LUO_FDT_SESSION_COMPATIBLE); + err |=3D fdt_property(fdt_out, LUO_FDT_SESSION_HEAD, &head_ser_pa, + sizeof(head_ser_pa)); + err |=3D fdt_end_node(fdt_out); + + if (err) + goto err_unpreserve; + + head_ser->pgcnt =3D LUO_SESSION_PGCNT; + INIT_LIST_HEAD(&luo_session_global.outgoing.list); + init_rwsem(&luo_session_global.outgoing.rwsem); + luo_session_global.outgoing.head_ser =3D head_ser; + luo_session_global.outgoing.ser =3D (void *)(head_ser + 1); + luo_session_global.outgoing.active =3D true; + + return 0; + +err_unpreserve: + luo_free_unpreserve(head_ser, LUO_SESSION_PGCNT << PAGE_SHIFT); + return err; +} + +int __init luo_session_setup_incoming(void *fdt_in) +{ + struct luo_session_head_ser *head_ser; + int err, head_size, offset; + const void *ptr; + u64 head_ser_pa; + + offset =3D 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 =3D fdt_node_check_compatible(fdt_in, offset, + LUO_FDT_SESSION_COMPATIBLE); + if (err) { + pr_err("Session node incompatibale [%s]\n", + LUO_FDT_SESSION_COMPATIBLE); + return -EINVAL; + } + + head_size =3D 0; + ptr =3D fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEAD, &head_size); + if (!ptr || head_size !=3D sizeof(u64)) { + pr_err("Unable to get session head '%s' [%d]\n", + LUO_FDT_SESSION_HEAD, head_size); + return -EINVAL; + } + + memcpy(&head_ser_pa, ptr, sizeof(u64)); + head_ser =3D __va(head_ser_pa); + + luo_session_global.incoming.head_ser =3D head_ser; + luo_session_global.incoming.ser =3D (void *)(head_ser + 1); + INIT_LIST_HEAD(&luo_session_global.incoming.list); + init_rwsem(&luo_session_global.incoming.rwsem); + luo_session_global.incoming.active =3D true; + + return 0; +} + +bool luo_session_is_deserialized(void) +{ + return luo_session_global.deserialized; +} + +int luo_session_deserialize(void) +{ + struct luo_session_head *sh =3D &luo_session_global.incoming; + + if (luo_session_is_deserialized()) + return 0; + + luo_session_global.deserialized =3D true; + if (!sh->active) { + INIT_LIST_HEAD(&sh->list); + init_rwsem(&sh->rwsem); + return 0; + } + + for (int i =3D 0; i < sh->head_ser->count; i++) { + struct luo_session *session; + + session =3D luo_session_alloc(sh->ser[i].name); + if (!session) { + pr_warn("Failed to allocate session [%s] during deserialization\n", + sh->ser[i].name); + return -ENOMEM; + } + + if (luo_session_insert(sh, session)) { + pr_warn("Failed to insert session due to name conflict [%s]\n", + session->name); + return -EEXIST; + } + + session->count =3D sh->ser[i].count; + session->files =3D __va(sh->ser[i].files); + session->pgcnt =3D sh->ser[i].pgcnt; + } + + luo_free_restore(sh->head_ser, sh->head_ser->pgcnt << PAGE_SHIFT); + sh->head_ser =3D NULL; + sh->ser =3D NULL; + + return 0; +} + +int luo_session_serialize(void) +{ + struct luo_session_head *sh =3D &luo_session_global.outgoing; + struct luo_session *session; + int i =3D 0; + + guard(rwsem_write)(&sh->rwsem); + list_for_each_entry(session, &sh->list, list) { + strscpy(sh->ser[i].name, session->name, + sizeof(sh->ser[i].name)); + sh->ser[i].count =3D session->count; + sh->ser[i].files =3D __pa(session->files); + sh->ser[i].pgcnt =3D session->pgcnt; + i++; + } + sh->head_ser->count =3D sh->count; + + return 0; +} --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f43.google.com (mail-yx1-f43.google.com [74.125.224.43]) (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 D70C833CEA7 for ; Fri, 7 Nov 2025 21:05:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549549; cv=none; b=LweM+EFjpXVaPAU8QHMt1DVgm/oiwUhA/p+1CNi8waYv2cXn3HgYEMbHNukTUkGgnxlN0jdfXzv1/aXwUgKhKfBaGp/5o/fzRLtMHi/13hNn7qbTAPZXZdlIgTZwaQcRYvsSyyPvd2Xc1g30IwB8f36ubxZFufz9U54ZsXUwaZk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549549; c=relaxed/simple; bh=arMv3Uml3w7W+Y/8Q8q1oELKbHlFGIzA8SRpMFCYgdU=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Q9aF2zZw7qI0CUGmK9yuhsXEDnuBvYBwXMeuGhbVmjpQt5yrjEDDmDkEHmoK+D+FNs7DHA60dsJ2LLo2FG618R2BAuqix7IvR/D0+AKsd8mbsgEjxjjIpUpVbm8TitO0zF3LoQzjWebxRQ8q6wejhQgspmt51Z9hhQtG1YVMw3k= 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=TQL0YT54; arc=none smtp.client-ip=74.125.224.43 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="TQL0YT54" Received: by mail-yx1-f43.google.com with SMTP id 956f58d0204a3-63f976ef4bdso1101062d50.1 for ; Fri, 07 Nov 2025 13:05:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549546; x=1763154346; 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=gdwSyej57m2z8fVHqgqF2nxiJqoHnbMJTxDva58Hcjk=; b=TQL0YT54kreAk8jInheAbBoM6kOVOuos1ju+ZgCXfznJWko6iBlnXY8PGQoT+PzY+c k2fEXbT1SBtbhbAYPEIEh5HvoYOFJ5Gcjw45lMN6OUKtRPX3pYF5Tbzlias1EFNuqn05 3FjRM3OFaCu8tGB8ryM75kLy+GHS7rvLLcvx3G4bxWNZ2Kpmxa0KrEmURxbg6f34cx1N PGmpZ39I/xfVVt2C8dlUbIf3uqbEAIUav+d4UT/pmZ5LvlOkGCQ1UY3l5Tth05VwcuIW j2Qyv0GF+QSoatkSWuGdG4SsBvAwoHEPOdiV9mOKEYQj6J5rgm1TvTBXsZTNlCV6+ZGA 1C2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549546; x=1763154346; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=gdwSyej57m2z8fVHqgqF2nxiJqoHnbMJTxDva58Hcjk=; b=N21+6Rr07LxLXi9jS7k1Mu/SA6WVcWr+q3lyV3jQhYFpP9dl25bcvPumKMST+t6tcI zY2TOS858hXCBIX5Qh3P3m38/kdw0UTL3AADe8Bi0+Lmmw5o2/UKatS+2qro4lht8EEo fvzQ6qekMWlHuBS4vdn2+TEkqJpDCxY6qizyv/8JS3hsN1lj3MJhQUanDn3zjhKh4ZyJ 1sqGwvubiARcGsW5zGiath1XnzdhMe2+kAyEI4Tut+4zTpq3pX7JvYDzKsSWv/Bk/JAy p+7GFV+x2CcTfJBV63iItNK86RxOGJJ89jwiGTpiBb+SANKxli+ZUXwd5QPbSxqv/IHZ K1rw== X-Forwarded-Encrypted: i=1; AJvYcCU4m273yMMGMB4aa2gwGeWSpz7nNag5g+1AqVTC/pzFyBlxOzk6WytQL84YJ50j3VtFYpqojTYz1an5hFs=@vger.kernel.org X-Gm-Message-State: AOJu0Yz81UG9dngJg7eNhZ2fRmQG8TM9x+l4RjZg7lNnyNh0EBFZQ53q 16uzaeIaIod7vIRnMKLVzwgrcyRN/ttcTrBBL9nJTb4fqAW6SIpnKl5miW9VSg/LhE4= X-Gm-Gg: ASbGncsnERjkZxa/MVotHqA/iNqiojrLvGHl7BrF3v7hxJFOl2EOGRbIR8KP3oBE1WL +RhJ7qIcjd1nLC4HKBuSaK9MMPQchjDq0ebxrNSvNVCu9GprE4wCEeQgIqre1LSwDdP0qxxz9Z1 TiryNTxFm1Y6mItGrZR6WFrpeW0qJVeN7Csy/IjAIY0lAnzFDx0HQUp3r2ozCPeIuV851CEh1jd O2S9VdRwvlyJGDb/37NaQ9N5fc7hayfGbUVsqdgaVAkrTuuYYDIHkAat7NtbyGBlYCOyFi606ok X4d+hInfelYgTjQGhOmhDaBM0FsAyM3HVOPZR4gepZ+dt8VOiScgucKV+ndwHOlvUDOz4cNRXPr OzdomS2ceeaAieTurF3peoPV4ALcfSFVBaHCZFaOLr9WKF+QVBwfsjKPq/VLDIYr4zdsd98tl/E CbZuUcQ4BH9TajkM+O/MEi+LnnWlaGHD11i72tD2g/uV9Z3xAYRsUBjAissDU/fLhkajyIQh7+0 q4tPRT6yEP8 X-Google-Smtp-Source: AGHT+IHDSr1AQF8Wap6vS17SlScfz5Kr2vkF9XLTYeISsPUyhEI9f5yexLUWISImV79oI8Dockuc/Q== X-Received: by 2002:a05:690e:1657:b0:63f:aadb:397a with SMTP id 956f58d0204a3-640d45c8834mr350073d50.55.1762549545602; Fri, 07 Nov 2025 13:05:45 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:45 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 07/22] liveupdate: luo_ioctl: add user interface Date: Fri, 7 Nov 2025 16:03:05 -0500 Message-ID: <20251107210526.257742-8-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 user-space interface for the Live Update Orchestrator via ioctl commands, enabling external control over the live update process and management of preserved resources. The idea is that there is going to be a single userspace agent driving the live update, therefore, only a single process can ever hold this device opened at a time. The following ioctl commands are introduced: LIVEUPDATE_IOCTL_CREATE_SESSION Provides a way for userspace to create a named session for grouping file descriptors that need to be preserved. It returns a new file descriptor representing the session. LIVEUPDATE_IOCTL_RETRIEVE_SESSION Allows the userspace agent in the new kernel to reclaim a preserved session by its name, receiving a new file descriptor to manage the restored resources. Signed-off-by: Pasha Tatashin --- include/uapi/linux/liveupdate.h | 64 ++++++++++++ kernel/liveupdate/luo_internal.h | 21 ++++ kernel/liveupdate/luo_ioctl.c | 173 +++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h index d2ef2f7e0dbd..3ce60e976ecc 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -46,4 +46,68 @@ /* The maximum length of session name including null termination */ #define LIVEUPDATE_SESSION_NAME_LENGTH 56 =20 +/* The /dev/liveupdate ioctl commands */ +enum { + LIVEUPDATE_CMD_BASE =3D 0x00, + LIVEUPDATE_CMD_CREATE_SESSION =3D LIVEUPDATE_CMD_BASE, + LIVEUPDATE_CMD_RETRIEVE_SESSION =3D 0x01, +}; + +/** + * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_= SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_create_session) + * @fd: Output; The new file descriptor for the created session. + * @name: Input; A null-terminated string for the session name, max + * length %LIVEUPDATE_SESSION_NAME_LENGTH including termination + * char. + * + * Creates a new live update session for managing preserved resources. + * This ioctl can only be called on the main /dev/liveupdate device. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_create_session { + __u32 size; + __s32 fd; + __u8 name[LIVEUPDATE_SESSION_NAME_LENGTH]; +}; + +#define LIVEUPDATE_IOCTL_CREATE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_CREATE_SESSION) + +/** + * struct liveupdate_ioctl_retrieve_session - ioctl(LIVEUPDATE_IOCTL_RETRI= EVE_SESSION) + * @size: Input; sizeof(struct liveupdate_ioctl_retrieve_session) + * @fd: Output; The new file descriptor for the retrieved session. + * @name: Input; A null-terminated string identifying the session to re= trieve. + * The name must exactly match the name used when the session was + * created in the previous kernel. + * + * Retrieves a handle (a new file descriptor) for a preserved session by i= ts + * name. This is the primary mechanism for a userspace agent to regain con= trol + * of its preserved resources after a live update. + * + * The userspace application provides the null-terminated `name` of a sess= ion + * it created before the live update. If a preserved session with a matchi= ng + * name is found, the kernel instantiates it and returns a new file descri= ptor + * in the `fd` field. This new session FD can then be used for all file-sp= ecific + * operations, such as restoring individual file descriptors with + * LIVEUPDATE_SESSION_RETRIEVE_FD. + * + * It is the responsibility of the userspace application to know the names= of + * the sessions it needs to retrieve. If no session with the given name is + * found, the ioctl will fail with -ENOENT. + * + * This ioctl can only be called on the main /dev/liveupdate device when t= he + * system is in the LIVEUPDATE_STATE_UPDATED state. + */ +struct liveupdate_ioctl_retrieve_session { + __u32 size; + __s32 fd; + __u8 name[64]; +}; + +#define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index b4f2d1443c76..ab4652d79e64 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -9,6 +9,27 @@ #define _LINUX_LUO_INTERNAL_H =20 #include +#include + +struct luo_ucmd { + void __user *ubuffer; + u32 user_size; + void *cmd; +}; + +static inline int luo_ucmd_respond(struct luo_ucmd *ucmd, + size_t kernel_cmd_size) +{ + /* + * Copy the minimum of what the user provided and what we actually + * have. + */ + if (copy_to_user(ucmd->ubuffer, ucmd->cmd, + min_t(size_t, ucmd->user_size, kernel_cmd_size))) { + return -EFAULT; + } + return 0; +} =20 void *luo_alloc_preserve(size_t size); void luo_free_unpreserve(void *mem, size_t size); diff --git a/kernel/liveupdate/luo_ioctl.c b/kernel/liveupdate/luo_ioctl.c index 44d365185f7c..ab03792fec0f 100644 --- a/kernel/liveupdate/luo_ioctl.c +++ b/kernel/liveupdate/luo_ioctl.c @@ -5,15 +5,187 @@ * Pasha Tatashin */ =20 +/** + * DOC: LUO ioctl Interface + * + * The IOCTL user-space control interface for the LUO subsystem. + * It registers a character device, typically found at ``/dev/liveupdate``, + * which allows a userspace agent to manage the LUO state machine and its + * associated resources, such as preservable file descriptors. + * + * To ensure that the state machine is controlled by a single entity, acce= ss + * to this device is exclusive: only one process is permitted to have + * ``/dev/liveupdate`` open at any given time. Subsequent open attempts wi= ll + * fail with -EBUSY until the first process closes its file descriptor. + * This singleton model simplifies state management by preventing conflict= ing + * commands from multiple userspace agents. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include #include #include +#include +#include "luo_internal.h" =20 struct luo_device_state { struct miscdevice miscdev; + atomic_t in_use; }; =20 +static int luo_ioctl_create_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_create_session *argp =3D ucmd->cmd; + struct file *file; + int ret; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + ret =3D luo_session_create(argp->name, &file); + if (ret) + return ret; + + ret =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (ret) { + fput(file); + put_unused_fd(argp->fd); + return ret; + } + + fd_install(argp->fd, file); + + return 0; +} + +static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_retrieve_session *argp =3D ucmd->cmd; + struct file *file; + int ret; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + ret =3D luo_session_retrieve(argp->name, &file); + if (ret < 0) { + put_unused_fd(argp->fd); + + return ret; + } + + ret =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (ret) { + fput(file); + put_unused_fd(argp->fd); + return ret; + } + + fd_install(argp->fd, file); + + return 0; +} + +static int luo_open(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + + if (atomic_cmpxchg(&ldev->in_use, 0, 1)) + return -EBUSY; + + luo_session_deserialize(); + + return 0; +} + +static int luo_release(struct inode *inodep, struct file *filep) +{ + struct luo_device_state *ldev =3D container_of(filep->private_data, + struct luo_device_state, + miscdev); + atomic_set(&ldev->in_use, 0); + + return 0; +} + +union ucmd_buffer { + struct liveupdate_ioctl_create_session create; + struct liveupdate_ioctl_retrieve_session retrieve; +}; + +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) = \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_BASE] =3D { \ + .size =3D sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size =3D offsetofend(_struct, _last), \ + .ioctl_num =3D _ioctl, \ + .execute =3D _fn, \ + } + +static const struct luo_ioctl_op luo_ioctl_ops[] =3D { + IOCTL_OP(LIVEUPDATE_IOCTL_CREATE_SESSION, luo_ioctl_create_session, + struct liveupdate_ioctl_create_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session, + struct liveupdate_ioctl_retrieve_session, name), +}; + +static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long = arg) +{ + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd =3D {}; + union ucmd_buffer buf; + unsigned int nr; + int ret; + + nr =3D _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_BASE || + (nr - LIVEUPDATE_CMD_BASE) >=3D ARRAY_SIZE(luo_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer =3D (void __user *)arg; + ret =3D get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (ret) + return ret; + + op =3D &luo_ioctl_ops[nr - LIVEUPDATE_CMD_BASE]; + if (op->ioctl_num !=3D cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd =3D &buf; + ret =3D copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (ret) + return ret; + + return op->execute(&ucmd); +} + static const struct file_operations luo_fops =3D { .owner =3D THIS_MODULE, + .open =3D luo_open, + .release =3D luo_release, + .unlocked_ioctl =3D luo_ioctl, }; =20 static struct luo_device_state luo_dev =3D { @@ -22,6 +194,7 @@ static struct luo_device_state luo_dev =3D { .name =3D "liveupdate", .fops =3D &luo_fops, }, + .in_use =3D ATOMIC_INIT(0), }; =20 static int __init liveupdate_ioctl_init(void) --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f175.google.com (mail-yw1-f175.google.com [209.85.128.175]) (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 1D77033EB04 for ; Fri, 7 Nov 2025 21:05:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549553; cv=none; b=t3TCn5xvGYR9pobfOF/2xmNsSWX38bl2rV+O6zFHJs5CGh4SQxYAZL6Yim4VuCZ6qd30DMFDwU+KUomccXztWCktdppgSgDF5FO+EhIePRbwKAIzbEZD5qt5+lu6/fWqCod1kkt5H43mVDRqCayBQZ6feNYniMgdiZws1y3NGjs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549553; c=relaxed/simple; bh=FtL1d2u9MpnaV5ZsDwWB/HK4zObWc5ypWJpf9t4qSuo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qpUsQBpI9TgR3R3wkZxNP5iVHSCqOK8pZTyqXMQlIp6XbCLox9OEl4PAkb6n+o6sEEzrq6ZOKc8yMvnsuKWDmR2FoP+fQEJz5P8W2Rr8xjccwyauxiV/qfaeGWZoRc3akoK3ec/cQgugJ8lpNkbyxXN1EBUP/osiPdQnk1PIUSc= 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=Q8yMv1hJ; arc=none smtp.client-ip=209.85.128.175 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="Q8yMv1hJ" Received: by mail-yw1-f175.google.com with SMTP id 00721157ae682-787be077127so14123927b3.2 for ; Fri, 07 Nov 2025 13:05:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549548; x=1763154348; 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=NMpfRl10O/uvpI2ck3agOJLQCDNfOhBohSo1oZ32lxw=; b=Q8yMv1hJm+lVYaGu0/ivk9JPTe6Cx7iqQ31Z3OGnWlZ4Hsxd/tukFAWyCaWxEvN6Nm J/eljZVpDMhKbW9Q9wOmHlElRWoILAamtc6v+wXESxp7bjq+mMsM2RGmA2ius/qk1Jx2 /XxVlcUFFtDg4iRY/qzR7c6IPfrQUsrkhjjIPp+nmK7bXKkXwwEg9l5VrFLlsNOmw9aY zK0BEih485zGeFVL8rorfkrKw+RXB1YND4+A7xLRJQT9ez5rRxuGGUK4HhO5ibo6r3xV fU7KOUPUjNXiP/hxUMIQZssYlcfA6H75tGHz56FOEdhU8dMNE8wNyHZqZpDVAISv1X36 z9Ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549548; x=1763154348; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=NMpfRl10O/uvpI2ck3agOJLQCDNfOhBohSo1oZ32lxw=; b=l92162pHrpr/Im0DszCcUG0QWrVW4jfcHhFucKlZ3zFCxtFEjT6Xr9xkp75pICoWv5 +xsR20/gz85y8IdNxhXPtxnAiqF+yM1vbwJuZGfIzY8K06OHHesEsH52hmPuEtEoihsO AnfS7SBGIANLVmZNCOzG1EjFe2VrXrTkTt5kBXgnbrdxrfSJVJxvjwe3zKi8YMfozZKQ gqvdGYz1hO4aZEJBpUJwTH2uNdKNoKLCNfphdlBnVwcZCdjwIwnbvRXy1CuAhSAsMKa7 H01vqWj3iuecQAM7uv1/VC/Xrq+vXc3F8RJUckXs/3BcQvdg8Eu5CUUv0/6DUnA3MTKO xAhg== X-Forwarded-Encrypted: i=1; AJvYcCX1zsiY3TIwf73XXyX+i8VvIqOuY+sA+tA0pWUBwKBIjI9d0KiaYz+k9eZXwwKpizkn/m1UCk+v6ac4kVI=@vger.kernel.org X-Gm-Message-State: AOJu0Ywi5qZjKWGoI7VXIpHfKUJzmD1KKl8w+8XX9odlQ1Ye1OLT8LbZ 0T5Z2MBYNPBbF0N1gnz/JsGtdliFFJfhMd13wAVGxoXpNtI+RGXskFenZZ4dKvFomh0= X-Gm-Gg: ASbGncvro9y5agj8iEQxG9wjC4oDv+VlHgUDCTUk0Y2XXhXgSjt2gMRWgdidEEieAi7 u8sFbEHCJX2ynox5LPLzmT/bMccInmS06XYAuMD7QqVbNFJ1wFehOhE3sEE5b0V2qpWIk8JE3/W wkXLsm9qU/wCiq25BAEAauZC1AYdL7ZL87ir91DctPMMLlrsXs5ywzWWTlznHm6oKkRX+eIhJbh +e8q0HY0fbin0AdGnQQPJsYp9Oz3Ihrgxw4rY2gpvWbxSY8iQgvgA7lbWJ4UGnir03ZAZToXY1Z 0JVwOR1dj/l3fvHrely5K7i3+4Yaq5fj3lOEF9qdOo2jU0pH/bsvB80HZXYx5kjDBvReb1QJU77 2o2U0zRXILZF6tZq/6Tc64UDxkfxhVwKaEEKQoQpuGG9fTOOhJfYZqwP5rsETVPavzGsI+/6hyp AemLNZzQoB79iOhejwpnUExwCUWp9i6mv1B07wOeqYpEAKAWS0I6ejKqOn/Ux3GI4= X-Google-Smtp-Source: AGHT+IELh1U4Z80ZsRnHlL5NFnmmdeRa0T9HnFDpkdv0gXjF0TeGRk1uO3zemrOHsSGwTXg3H+yXng== X-Received: by 2002:a05:690c:6e0e:b0:786:59d3:49c2 with SMTP id 00721157ae682-787d5414d57mr6405237b3.35.1762549547582; Fri, 07 Nov 2025 13:05:47 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:47 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 08/22] liveupdate: luo_file: implement file systems callbacks Date: Fri, 7 Nov 2025 16:03:06 -0500 Message-ID: <20251107210526.257742-9-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This patch implements the core mechanism for managing preserved files throughout the live update lifecycle. It provides the logic to invoke the file handler callbacks (preserve, unpreserve, freeze, unfreeze, retrieve, and finish) at the appropriate stages. During the reboot phase, luo_file_freeze() serializes the final metadata for each file (handler compatible string, token, and data handle) into a memory region preserved by KHO. In the new kernel, luo_file_deserialize() reconstructs the in-memory file list from this data, preparing the session for retrieval. Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 109 ++++ include/linux/liveupdate/abi/luo.h | 22 + kernel/liveupdate/Makefile | 1 + kernel/liveupdate/luo_file.c | 882 +++++++++++++++++++++++++++++ kernel/liveupdate/luo_internal.h | 9 + 5 files changed, 1023 insertions(+) create mode 100644 kernel/liveupdate/luo_file.c diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 0be8804fc42a..d18a788222ab 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -10,6 +10,88 @@ #include #include #include +#include +#include + +struct liveupdate_file_handler; +struct liveupdate_session; +struct file; + +/** + * struct liveupdate_file_op_args - Arguments for file operation callbacks. + * @handler: The file handler being called. + * @session: The session this file belongs to. + * @retrieved: The retrieve status for the 'can_finish / finish' + * operation. + * @file: The file object. For retrieve: [OUT] The callback se= ts + * this to the new file. For other ops: [IN] The caller= sets + * this to the file being operated on. + * @serialized_data: The opaque u64 handle, preserve/prepare/freeze may u= pdate + * this field. + * + * This structure bundles all parameters for the file operation callbacks. + * The 'data' and 'file' fields are used for both input and output. + */ +struct liveupdate_file_op_args { + struct liveupdate_file_handler *handler; + struct liveupdate_session *session; + bool retrieved; + struct file *file; + u64 serialized_data; +}; + +/** + * struct liveupdate_file_ops - Callbacks for live-updatable files. + * @can_preserve: Required. Lightweight check to see if this handler is + * compatible with the given file. + * @preserve: Required. Performs state-saving for the file. + * @unpreserve: Required. Cleans up any resources allocated by @preserve. + * @freeze: Optional. Final actions just before kernel transition. + * @unfreeze: Optional. Undo freeze operations. + * @retrieve: Required. Restores the file in the new kernel. + * @can_finish: Optional. Check if this FD can finish, i.e. all restorat= ion + * pre-requirements for this FD are satisfied. Called prior= to + * finish, in order to do successful finish calls for all + * resources in the session. + * @finish: Required. Final cleanup in the new kernel. + * @owner: Module reference + * + * All operations (except can_preserve) receive a pointer to a + * 'struct liveupdate_file_op_args' containing the necessary context. + */ +struct liveupdate_file_ops { + bool (*can_preserve)(struct liveupdate_file_handler *handler, + struct file *file); + int (*preserve)(struct liveupdate_file_op_args *args); + void (*unpreserve)(struct liveupdate_file_op_args *args); + int (*freeze)(struct liveupdate_file_op_args *args); + void (*unfreeze)(struct liveupdate_file_op_args *args); + int (*retrieve)(struct liveupdate_file_op_args *args); + bool (*can_finish)(struct liveupdate_file_op_args *args); + void (*finish)(struct liveupdate_file_op_args *args); + struct module *owner; +}; + +/** + * struct liveupdate_file_handler - Represents a handler for a live-updata= ble file type. + * @ops: Callback functions + * @compatible: The compatibility string (e.g., "memfd-v1", "vfiof= d-v1") + * that uniquely identifies the file type this handler + * supports. This is matched against the compatible s= tring + * associated with individual &struct file instances. + * @list: Used for linking this handler instance into a glob= al + * list of registered file handlers. + * + * Modules that want to support live update for specific file types should + * register an instance of this structure. LUO uses this registration to + * determine if a given file can be preserved and to find the appropriate + * operations to manage its state across the update. + */ +struct liveupdate_file_handler { + const struct liveupdate_file_ops *ops; + const char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH]; + struct list_head list; +}; =20 #ifdef CONFIG_LIVEUPDATE =20 @@ -21,6 +103,16 @@ bool liveupdate_enabled(void); /* Called during kexec to tell LUO that entered into reboot */ int liveupdate_reboot(void); =20 +int liveupdate_register_file_handler(struct liveupdate_file_handler *h); + +/* kernel can internally retrieve files */ +int liveupdate_get_file_incoming(struct liveupdate_session *s, u64 token, + struct file **filep); + +/* Get a token for an outgoing file, or -ENOENT if file is not preserved */ +int liveupdate_get_token_outgoing(struct liveupdate_session *s, + struct file *file, u64 *tokenp); + #else /* CONFIG_LIVEUPDATE */ =20 static inline void liveupdate_init(void) @@ -37,5 +129,22 @@ static inline int liveupdate_reboot(void) return 0; } =20 +static inline int liveupdate_register_file_handler(struct liveupdate_file_= handler *h) +{ + return 0; +} + +static inline int liveupdate_get_file_incoming(struct liveupdate_session *= s, + u64 token, struct file **filep) +{ + return -EOPNOTSUPP; +} + +static inline int liveupdate_get_token_outgoing(struct liveupdate_session = *s, + struct file *file, u64 *tokenp) +{ + return -EOPNOTSUPP; +} + #endif /* CONFIG_LIVEUPDATE */ #endif /* _LINUX_LIVEUPDATE_H */ diff --git a/include/linux/liveupdate/abi/luo.h b/include/linux/liveupdate/= abi/luo.h index 37b9fecef3f7..df0a6cd1d5d2 100644 --- a/include/linux/liveupdate/abi/luo.h +++ b/include/linux/liveupdate/abi/luo.h @@ -65,6 +65,11 @@ * Metadata for a single session, including its name and a physical po= inter * to another preserved memory block containing an array of * `struct luo_file_ser` for all files in that session. + * + * - struct luo_file_ser: + * Metadata for a single preserved file. Contains the `compatible` str= ing to + * find the correct handler in the new kernel, a user-provided `token`= for + * identification, and an opaque `data` handle for the handler to use. */ =20 #ifndef _LINUX_LIVEUPDATE_ABI_LUO_H @@ -132,4 +137,21 @@ struct luo_session_ser { u64 count; } __packed; =20 +/* The max size is set so it can be reliably used during in serialization = */ +#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48 + +/** + * struct luo_file_ser - Represents the serialized preserves files. + * @compatible: File handler compatabile string. + * @data: Private data + * @token: User provided token for this file + * + * If this structure is modified, LUO_SESSION_COMPATIBLE must be updated. + */ +struct luo_file_ser { + char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH]; + u64 data; + u64 token; +} __packed; + #endif /* _LINUX_LIVEUPDATE_ABI_LUO_H */ diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index 83285e7ad726..c2252a2ad7bd 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -2,6 +2,7 @@ =20 luo-y :=3D \ luo_core.o \ + luo_file.o \ luo_ioctl.o \ luo_session.o =20 diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c new file mode 100644 index 000000000000..d5197149b3b7 --- /dev/null +++ b/kernel/liveupdate/luo_file.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: LUO File Descriptors + * + * LUO provides the infrastructure to preserve specific, stateful file + * descriptors across a kexec-based live update. The primary goal is to al= low + * workloads, such as virtual machines using vfio, memfd, or iommufd, to + * retain access to their essential resources without interruption. + * + * The framework is built around a callback-based handler model and a well- + * defined lifecycle for each preserved file. + * + * Handler Registration: + * Kernel modules responsible for a specific file type (e.g., memfd, vfio) + * register a &struct liveupdate_file_handler. This handler provides a set= of + * callbacks that LUO invokes at different stages of the update process, m= ost + * notably: + * + * - can_preserve(): A lightweight check to determine if the handler is + * compatible with a given 'struct file'. + * - preserve(): The heavyweight operation that saves the file's state a= nd + * returns an opaque u64 handle, happens while vcpus are still running. + * LUO becomes the owner of this file until session is closed or file = is + * finished. + * - unpreserve(): Cleans up any resources allocated by .preserve(), cal= led + * if the preservation process is aborted before the reboot (i.e. sess= ion is + * closed). + * - freeze(): A final pre-reboot opportunity to prepare the state for k= exec. + * We are already in reboot syscall, and therefore userspace cannot mu= tate + * the file anymore. + * - unfreeze(): Undoes the actions of .freeze(), called if the live upd= ate + * is aborted after the freeze phase. + * - retrieve(): Reconstructs the file in the new kernel from the preser= ved + * handle. + * - finish(): Performs final check and cleanup in the new kernel. After + * succesul finish call, LUO gives up ownership to this file. + * + * File Preservation Lifecycle happy path: + * + * 1. Preserve (Normal Operation): A userspace agent preserves files one b= y one + * via an ioctl. For each file, luo_preserve_file() finds a compatible + * handler, calls its .preserve() op, and creates an internal &struct + * luo_file to track the live state. + * + * 2. Freeze (Pre-Reboot): Just before the kexec, luo_file_freeze() is cal= led. + * It iterates through all preserved files, calls their respective .fre= eze() + * ops, and serializes their final metadata (compatible string, token, = and + * data handle) into a contiguous memory block for KHO. + * + * 3. Deserialize (New Kernel - Early Boot): After kexec, luo_file_deseria= lize() + * runs. It reads the serialized data from the KHO memory region and + * reconstructs the in-memory list of &struct luo_file instances for th= e new + * kernel, linking them to their corresponding handlers. + * + * 4. Retrieve (New Kernel - Userspace Ready): The userspace agent can now + * restore file descriptors by providing a token. luo_retrieve_file() + * searches for the matching token, calls the handler's .retrieve() op = to + * re-create the 'struct file', and returns a new FD. Files can be + * retrieved in ANY order. + * + * 5. Finish (New Kernel - Cleanup): Once a session retrival is complete, + * luo_file_finish() is called. It iterates through all files, + * invokes their .finish() ops for final cleanup, and releases all + * associated kernel resources. + * + * File Preservation Lifecycle unhappy paths: + * + * 1. Abort Before Reboot: If the userspace agent aborts the live update + * process before calling reboot (e.g., by closing the session file + * descriptor), the session's release handler calls + * luo_file_unpreserve_files(). This invokes the .unpreserve() callback= on + * all preserved files, ensuring all allocated resources are cleaned up= and + * returning the system to a clean state. + * + * 2. Freeze Failure: During the reboot() syscall, if any handler's .freez= e() + * op fails, the .unfreeze() op is invoked on all previously *successfu= l* + * freezes to roll back their state. The reboot() syscall then returns = an + * error to userspace, canceling the live update. + * + * 3. Finish Failure: In the new kernel, if a handler's .finish() op fails, + * the luo_file_finish() operation is aborted. LUO retains ownership of + * all files within that session, including those that were not yet + * processed. The userspace agent can attempt to call the finish operat= ion + * again later. If the issue cannot be resolved, these resources will b= e held + * by LUO until the next live update cycle, at which point they will be + * discarded. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "luo_internal.h" + +static LIST_HEAD(luo_file_handler_list); + +/* 2 4K pages, give space for 128 files per session */ +#define LUO_FILE_PGCNT 2ul +#define LUO_FILE_MAX \ + ((LUO_FILE_PGCNT << PAGE_SHIFT) / sizeof(struct luo_file_ser)) + +/** + * struct luo_file - Represents a single preserved file instance. + * @fh: Pointer to the &struct liveupdate_file_handler that man= ages + * this type of file. + * @file: Pointer to the kernel's &struct file that is being pres= erved. + * This is NULL in the new kernel until the file is succes= sfully + * retrieved. + * @serialized_data: The opaque u64 handle to the serialized state of the = file. + * This handle is passed back to the handler's .freeze(), + * .retrieve(), and .finish() callbacks, allowing it to tr= ack + * and update its serialized state across phases. + * @retrieved: A flag indicating whether a user/kernel in the new kern= el has + * successfully called retrieve() on this file. This preve= nts + * multiple retrieval attempts. + * @mutex: A mutex that protects the fields of this specific insta= nce + * (e.g., @retrieved, @file), ensuring that operations like + * retrieving or finishing a file are atomic. + * @list: The list_head linking this instance into its parent + * session's list of preserved files. + * @token: The user-provided unique token used to identify this fi= le. + * + * This structure is the core in-kernel representation of a single file be= ing + * managed through a live update. An instance is created by luo_preserve_f= ile() + * to link a 'struct file' to its corresponding handler, a user-provided t= oken, + * and the serialized state handle returned by the handler's .preserve() + * operation. + * + * These instances are tracked in a per-session list. The @serialized_data + * field, which holds a handle to the file's serialized state, may be upda= ted + * during the .freeze() callback before being serialized for the next kern= el. + * After reboot, these structures are recreated by luo_file_deserialize() = and + * are finally cleaned up by luo_file_finish(). + */ +struct luo_file { + struct liveupdate_file_handler *fh; + struct file *file; + u64 serialized_data; + bool retrieved; + struct mutex mutex; + struct list_head list; + u64 token; +}; + +static int luo_session_alloc_files_mem(struct luo_session *session) +{ + size_t size; + void *mem; + + if (session->files) + return 0; + + WARN_ON_ONCE(session->count); + + size =3D LUO_FILE_PGCNT << PAGE_SHIFT; + mem =3D luo_alloc_preserve(size); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + session->files =3D mem; + session->pgcnt =3D LUO_FILE_PGCNT; + + return 0; +} + +static void luo_session_free_files_mem(struct luo_session *session) +{ + /* If session has files, no need to free preservation memory */ + if (session->count) + return; + + if (!session->files) + return; + + luo_free_unpreserve(session->files, session->pgcnt << PAGE_SHIFT); + session->files =3D NULL; + session->pgcnt =3D 0; +} + +static bool luo_token_is_used(struct luo_session *session, u64 token) +{ + struct luo_file *iter; + + list_for_each_entry(iter, &session->files_list, list) { + if (iter->token =3D=3D token) + return true; + } + + return false; +} + +/** + * luo_preserve_file - Initiate the preservation of a file descriptor. + * @session: The session to which the preserved file will be added. + * @token: A unique, user-provided identifier for the file. + * @fd: The file descriptor to be preserved. + * + * This function orchestrates the first phase of preserving a file. Upon e= ntry, + * it takes a reference to the 'struct file' via fget(), effectively makin= g LUO + * a co-owner of the file. This reference is held until the file is either + * unpreserved or successfully finished in the next kernel, preventing the= file + * from being prematurely destroyed. + * + * This function orchestrates the first phase of preserving a file. It per= forms + * the following steps: + * + * 1. Validates that the @token is not already in use within the session. + * 2. Ensures the session's memory for files serialization is allocated + * (allocates if needed). + * 3. Iterates through registered handlers, calling can_preserve() to find= one + * compatible with the given @fd. + * 4. Calls the handler's .preserve() operation, which saves the file's st= ate + * and returns an opaque private data handle. + * 5. Adds the new instance to the session's internal list. + * + * On success, LUO takes a reference to the 'struct file' and considers it + * under its management until it is unpreserved or finished. + * + * In case of any failure, all intermediate allocations (file reference, m= emory + * for the 'luo_file' struct, etc.) are cleaned up before returning an err= or. + * + * Context: Can be called from an ioctl handler during normal system opera= tion. + * Return: 0 on success. Returns a negative errno on failure: + * -EEXIST if the token is already used. + * -EBADF if the file descriptor is invalid. + * -ENOSPC if the session is full. + * -ENOENT if no compatible handler is found. + * -ENOMEM on memory allocation failure. + * Other erros might be returned by .preserve(). + */ +int luo_preserve_file(struct luo_session *session, u64 token, int fd) +{ + struct liveupdate_file_op_args args =3D {0}; + struct liveupdate_file_handler *fh; + struct luo_file *luo_file; + struct file *file; + int err =3D -ENOENT; + + lockdep_assert_held(&session->mutex); + + if (luo_token_is_used(session, token)) + return -EEXIST; + + file =3D fget(fd); + if (!file) + return -EBADF; + + err =3D luo_session_alloc_files_mem(session); + if (err) + goto exit_err; + + if (session->count =3D=3D LUO_FILE_MAX) { + err =3D -ENOSPC; + goto exit_err; + } + + list_for_each_entry(fh, &luo_file_handler_list, list) { + if (fh->ops->can_preserve(fh, file)) { + err =3D 0; + break; + } + } + + /* err is still -ENOENT if no handler was found */ + if (err) + goto exit_err; + + luo_file =3D kzalloc(sizeof(*luo_file), GFP_KERNEL); + if (!luo_file) { + err =3D -ENOMEM; + goto exit_err; + } + + luo_file->file =3D file; + luo_file->fh =3D fh; + luo_file->token =3D token; + luo_file->retrieved =3D false; + mutex_init(&luo_file->mutex); + + args.handler =3D fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D file; + err =3D fh->ops->preserve(&args); + if (err) { + mutex_destroy(&luo_file->mutex); + kfree(luo_file); + goto exit_err; + } else { + luo_file->serialized_data =3D args.serialized_data; + list_add_tail(&luo_file->list, &session->files_list); + session->count++; + } + + return 0; + +exit_err: + fput(file); + luo_session_free_files_mem(session); + + return err; +} + +/** + * luo_file_unpreserve_files - Unpreserves all files from a session. + * @session: The session to be cleaned up. + * + * This function serves as the primary cleanup path for a session. It is + * invoked when the userspace agent closes the session's file descriptor. + * + * For each file, it performs the following cleanup actions: + * 1. Calls the handler's .unpreserve() callback to allow the handler to + * release any resources it allocated. + * 2. Removes the file from the session's internal tracking list. + * 3. Releases the reference to the 'struct file' that was taken by + * luo_preserve_file() via fput(), returning ownership. + * 4. Frees the memory associated with the internal 'struct luo_file'. + * + * After all individual files are unpreserved, it frees the contiguous mem= ory + * block that was allocated to hold their serialization data. + */ +void luo_file_unpreserve_files(struct luo_session *session) +{ + struct luo_file *luo_file; + + lockdep_assert_held(&session->mutex); + + while (!list_empty(&session->files_list)) { + struct liveupdate_file_op_args args =3D {0}; + + luo_file =3D list_last_entry(&session->files_list, + struct luo_file, list); + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D luo_file->file; + args.serialized_data =3D luo_file->serialized_data; + luo_file->fh->ops->unpreserve(&args); + + list_del(&luo_file->list); + session->count--; + + fput(luo_file->file); + mutex_destroy(&luo_file->mutex); + kfree(luo_file); + } + + luo_session_free_files_mem(session); +} + +static int luo_file_freeze_one(struct luo_session *session, + struct luo_file *luo_file) +{ + int err =3D 0; + + guard(mutex)(&luo_file->mutex); + + if (luo_file->fh->ops->freeze) { + struct liveupdate_file_op_args args =3D {0}; + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D luo_file->file; + args.serialized_data =3D luo_file->serialized_data; + + err =3D luo_file->fh->ops->freeze(&args); + if (!err) + luo_file->serialized_data =3D args.serialized_data; + } + + return err; +} + +static void luo_file_unfreeze_one(struct luo_session *session, + struct luo_file *luo_file) +{ + guard(mutex)(&luo_file->mutex); + + if (luo_file->fh->ops->unfreeze) { + struct liveupdate_file_op_args args =3D {0}; + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D luo_file->file; + args.serialized_data =3D luo_file->serialized_data; + + luo_file->fh->ops->unfreeze(&args); + } + + luo_file->serialized_data =3D 0; +} + +static void __luo_file_unfreeze(struct luo_session *session, + struct luo_file *failed_entry) +{ + struct list_head *files_list =3D &session->files_list; + struct luo_file *luo_file; + + list_for_each_entry(luo_file, files_list, list) { + if (luo_file =3D=3D failed_entry) + break; + + luo_file_unfreeze_one(session, luo_file); + } + + memset(session->files, 0, session->pgcnt << PAGE_SHIFT); +} + +/** + * luo_file_freeze - Freezes all preserved files and serializes their meta= data. + * @session: The session whose files are to be frozen. + * + * This function is called from the reboot() syscall path, just before the + * kernel transitions to the new image via kexec. Its purpose is to perfor= m the + * final preparation and serialization of all preserved files in the sessi= on. + * + * It iterates through each preserved file in FIFO order (the order of + * preservation) and performs two main actions: + * + * 1. Freezes the File: It calls the handler's .freeze() callback for each + * file. This gives the handler a final opportunity to quiesce the devi= ce or + * prepare its state for the upcoming reboot. The handler may update its + * private data handle during this step. + * + * 2. Serializes Metadata: After a successful freeze, it copies the final = file + * metadata=E2=80=94the handler's compatible string, the user token, an= d the final + * private data handle=E2=80=94into the pre-allocated contiguous memory= buffer + * (session->files) that will be handed over to the next kernel via KHO. + * + * Error Handling (Rollback): + * This function is atomic. If any handler's .freeze() operation fails, the + * entire live update is aborted. The __luo_file_unfreeze() helper is + * immediately called to invoke the .unfreeze() op on all files that were + * successfully frozen before the point of failure, rolling them back to a + * running state. The function then returns an error, causing the reboot() + * syscall to fail. + * + * Context: Called only from the liveupdate_reboot() path. + * Return: 0 on success, or a negative errno on failure. + */ +int luo_file_freeze(struct luo_session *session) +{ + struct luo_file_ser *file_ser =3D session->files; + struct luo_file *luo_file; + int err; + int i; + + lockdep_assert_held(&session->mutex); + + if (!session->count) + return 0; + + if (WARN_ON(!file_ser)) + return -EINVAL; + + i =3D 0; + list_for_each_entry(luo_file, &session->files_list, list) { + err =3D luo_file_freeze_one(session, luo_file); + if (err < 0) { + pr_warn("Freeze failed for session[%s] token[%#0llx] handler[%s] err[%p= e]\n", + session->name, luo_file->token, + luo_file->fh->compatible, ERR_PTR(err)); + goto exit_err; + } + + strscpy(file_ser[i].compatible, luo_file->fh->compatible, + sizeof(file_ser[i].compatible)); + file_ser[i].data =3D luo_file->serialized_data; + file_ser[i].token =3D luo_file->token; + i++; + } + + return 0; + +exit_err: + __luo_file_unfreeze(session, luo_file); + + return err; +} + +/** + * luo_file_unfreeze - Unfreezes all files in a session. + * @session: The session whose files are to be unfrozen. + * + * This function rolls back the state of all files in a session after the = freeze + * phase has begun but must be aborted. It is the counterpart to + * luo_file_freeze(). + * + * It invokes the __luo_file_unfreeze() helper with a NULL argument, which + * signals the helper to iterate through all files in the session and call + * their respective .unfreeze() handler callbacks. + * + * Context: This is called when the live update is aborted during + * the reboot() syscall, after luo_file_freeze() has been called. + */ +void luo_file_unfreeze(struct luo_session *session) +{ + lockdep_assert_held(&session->mutex); + + if (!session->count) + return; + + __luo_file_unfreeze(session, NULL); +} + +/** + * luo_retrieve_file - Restores a preserved file from a session by its tok= en. + * @session: The session from which to retrieve the file. + * @token: The unique token identifying the file to be restored. + * @filep: Output parameter; on success, this is populated with a pointer + * to the newly retrieved 'struct file'. + * + * This function is the primary mechanism for recreating a file in the new + * kernel after a live update. It searches the session's list of deseriali= zed + * files for an entry matching the provided @token. + * + * The operation is idempotent: if a file has already been successfully + * retrieved, this function will simply return a pointer to the existing + * 'struct file' and report success without re-executing the retrieve + * operation. This is handled by checking the 'retrieved' flag under a loc= k. + * + * File retrieval can happen in any order; it is not bound by the order of + * preservation. + * + * Context: Can be called from an ioctl or other in-kernel code in the new + * kernel. + * Return: 0 on success. Returns a negative errno on failure: + * -ENOENT if no file with the matching token is found. + * Any error code returned by the handler's .retrieve() op. + */ +int luo_retrieve_file(struct luo_session *session, u64 token, + struct file **filep) +{ + struct liveupdate_file_op_args args =3D {0}; + struct luo_file *luo_file; + int err; + + lockdep_assert_held(&session->mutex); + + if (list_empty(&session->files_list)) + return -ENOENT; + + list_for_each_entry(luo_file, &session->files_list, list) { + if (luo_file->token =3D=3D token) + break; + } + + if (luo_file->token !=3D token) + return -ENOENT; + + guard(mutex)(&luo_file->mutex); + if (luo_file->retrieved) { + /* + * Someone is asking for this file again, so get a reference + * for them. + */ + get_file(luo_file->file); + *filep =3D luo_file->file; + return 0; + } + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.serialized_data =3D luo_file->serialized_data; + err =3D luo_file->fh->ops->retrieve(&args); + if (!err) { + luo_file->file =3D args.file; + + /* Get reference so we can keep this file in LUO until finish */ + get_file(luo_file->file); + *filep =3D luo_file->file; + luo_file->retrieved =3D true; + } + + return err; +} + +static int luo_file_can_finish_one(struct luo_session *session, + struct luo_file *luo_file) +{ + bool can_finish =3D true; + + guard(mutex)(&luo_file->mutex); + + if (luo_file->fh->ops->can_finish) { + struct liveupdate_file_op_args args =3D {0}; + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D luo_file->file; + args.serialized_data =3D luo_file->serialized_data; + args.retrieved =3D luo_file->retrieved; + can_finish =3D luo_file->fh->ops->can_finish(&args); + } + + return can_finish ? 0 : -EBUSY; +} + +static void luo_file_finish_one(struct luo_session *session, + struct luo_file *luo_file) +{ + struct liveupdate_file_op_args args =3D {0}; + + guard(mutex)(&luo_file->mutex); + + args.handler =3D luo_file->fh; + args.session =3D (struct liveupdate_session *)session; + args.file =3D luo_file->file; + args.serialized_data =3D luo_file->serialized_data; + args.retrieved =3D luo_file->retrieved; + + luo_file->fh->ops->finish(&args); +} + +/** + * luo_file_finish - Completes the lifecycle for all files in a session. + * @session: The session to be finalized. + * + * This function orchestrates the final teardown of a live update session = in the + * new kernel. It should be called after all necessary files have been + * retrieved and the userspace agent is ready to release the preserved sta= te. + * + * The function iterates through all tracked files. For each file, it perf= orms + * the following sequence of cleanup actions: + * + * 1. If file is not yet retrieved, retrieves it, and calls can_finish() on + * every file in the session. If all can_finish return true, continue to + * finish. + * 2. Calls the handler's .finish() callback (via luo_file_finish_one) to + * allow for final resource cleanup within the handler. + * 3. Releases LUO's ownership reference on the 'struct file' via fput(). = This + * is the counterpart to the get_file() call in luo_retrieve_file(). + * 4. Removes the 'struct luo_file' from the session's internal list. + * 5. Frees the memory for the 'struct luo_file' instance itself. + * + * After successfully finishing all individual files, it frees the + * contiguous memory block that was used to transfer the serialized metada= ta + * from the previous kernel. + * + * Error Handling (Atomic Failure): + * This operation is atomic. If any handler's .can_finish() op fails, the = entire + * function aborts immediately and returns an error. + * + * Context: Can be called from an ioctl handler in the new kernel. + * Return: 0 on success, or a negative errno on failure. + */ +int luo_file_finish(struct luo_session *session) +{ + struct list_head *files_list =3D &session->files_list; + struct luo_file *luo_file; + int err; + + if (!session->count) + return 0; + + lockdep_assert_held(&session->mutex); + + list_for_each_entry(luo_file, files_list, list) { + err =3D luo_file_can_finish_one(session, luo_file); + if (err) + return err; + } + + while (!list_empty(&session->files_list)) { + luo_file =3D list_last_entry(&session->files_list, + struct luo_file, list); + + luo_file_finish_one(session, luo_file); + + if (luo_file->file) + fput(luo_file->file); + list_del(&luo_file->list); + session->count--; + mutex_destroy(&luo_file->mutex); + kfree(luo_file); + } + + if (session->files) { + luo_free_restore(session->files, session->pgcnt << PAGE_SHIFT); + session->files =3D NULL; + session->pgcnt =3D 0; + } + + return 0; +} + +/** + * luo_file_deserialize - Reconstructs the list of preserved files in the = new kernel. + * @session: The incoming session containing the serialized file data from= KHO. + * + * This function is called during the early boot process of the new kernel= . It + * takes the raw, contiguous memory block of 'struct luo_file_ser' entries, + * provided by the previous kernel, and transforms it back into a live, + * in-memory linked list of 'struct luo_file' instances. + * + * For each serialized entry, it performs the following steps: + * 1. Reads the 'compatible' string. + * 2. Searches the global list of registered file handlers for one that + * matches the compatible string. + * 3. Allocates a new 'struct luo_file'. + * 4. Populates the new structure with the deserialized data (token, pri= vate + * data handle) and links it to the found handler. The 'file' pointer= is + * initialized to NULL, as the file has not been retrieved yet. + * 5. Adds the new 'struct luo_file' to the session's files_list. + * + * This prepares the session for userspace, which can later call + * luo_retrieve_file() to restore the actual file descriptors. + * + * Context: Called from session deserialization. + */ +int luo_file_deserialize(struct luo_session *session) +{ + struct luo_file_ser *file_ser; + u64 i; + + lockdep_assert_held(&session->mutex); + + if (!session->files) + return 0; + + file_ser =3D session->files; + for (i =3D 0; i < session->count; i++) { + struct liveupdate_file_handler *fh; + bool handler_found =3D false; + struct luo_file *luo_file; + + list_for_each_entry(fh, &luo_file_handler_list, list) { + if (!strcmp(fh->compatible, file_ser[i].compatible)) { + handler_found =3D true; + break; + } + } + + if (!handler_found) { + pr_warn("No registered handler for compatible '%s'\n", + file_ser[i].compatible); + return -ENOENT; + } + + luo_file =3D kzalloc(sizeof(*luo_file), GFP_KERNEL); + if (!luo_file) + return -ENOMEM; + + luo_file->fh =3D fh; + luo_file->file =3D NULL; + luo_file->serialized_data =3D file_ser[i].data; + luo_file->token =3D file_ser[i].token; + luo_file->retrieved =3D false; + mutex_init(&luo_file->mutex); + list_add_tail(&luo_file->list, &session->files_list); + } + + return 0; +} + +/** + * liveupdate_register_file_handler - Register a file handler with LUO. + * @fh: Pointer to a caller-allocated &struct liveupdate_file_handler. + * The caller must initialize this structure, including a unique + * 'compatible' string and a valid 'fh' callbacks. This function adds the + * handler to the global list of supported file handlers. + * + * Context: Typically called during module initialization for file types t= hat + * support live update preservation. + * + * Return: 0 on success. Negative errno on failure. + */ +int liveupdate_register_file_handler(struct liveupdate_file_handler *fh) +{ + static DEFINE_MUTEX(register_file_handler_lock); + struct liveupdate_file_handler *fh_iter; + + /* + * Once sessions have been deserialized, file handlers cannot be + * registered, it is too late. + */ + if (WARN_ON(luo_session_is_deserialized())) + return -EBUSY; + + /* Sanity check that all required callbacks are set */ + if (!fh->ops->preserve || !fh->ops->unpreserve || + !fh->ops->retrieve || !fh->ops->finish) { + return -EINVAL; + } + + guard(mutex)(®ister_file_handler_lock); + list_for_each_entry(fh_iter, &luo_file_handler_list, list) { + if (!strcmp(fh_iter->compatible, fh->compatible)) { + pr_err("File handler registration failed: Compatible string '%s' alread= y registered.\n", + fh->compatible); + return -EEXIST; + } + } + + if (!try_module_get(fh->ops->owner)) + return -EAGAIN; + + INIT_LIST_HEAD(&fh->list); + list_add_tail(&fh->list, &luo_file_handler_list); + + return 0; +} + +/** + * liveupdate_get_token_outgoing - Get the token for a preserved file. + * @s: The outgoing liveupdate session. + * @file: The file object to search for. + * @tokenp: Output parameter for the found token. + * + * Searches the list of preserved files in an outgoing session for a match= ing + * file object. If found, the corresponding user-provided token is returne= d. + * + * This function is intended for in-kernel callers that need to correlate a + * file with its liveupdate token. + * + * Context: Can be called from any context that can acquire the session mu= tex. + * Return: 0 on success, -ENOENT if the file is not preserved in this sess= ion. + */ +int liveupdate_get_token_outgoing(struct liveupdate_session *s, + struct file *file, u64 *tokenp) +{ + struct luo_session *session =3D (struct luo_session *)s; + struct luo_file *luo_file; + int err =3D -ENOENT; + + list_for_each_entry(luo_file, &session->files_list, list) { + if (luo_file->file =3D=3D file) { + if (tokenp) + *tokenp =3D luo_file->token; + err =3D 0; + break; + } + } + + return err; +} + +/** + * liveupdate_get_file_incoming - Retrieves a preserved file for in-kernel= use. + * @s: The incoming liveupdate session (restored from the previous ke= rnel). + * @token: The unique token identifying the file to retrieve. + * @filep: On success, this will be populated with a pointer to the retri= eved + * 'struct file'. + * + * Provides a kernel-internal API for other subsystems to retrieve their + * preserved files after a live update. This function is a simple wrapper + * around luo_retrieve_file(), allowing callers to find a file by its toke= n. + * + * The operation is idempotent; subsequent calls for the same token will r= eturn + * a pointer to the same 'struct file' object. + * + * The caller receives a pointer to the file but does not receive a new + * reference. The file's lifetime is managed by LUO and any userspace file + * descriptors. If the caller needs to hold a reference to the file beyond= the + * immediate scope, it must call get_file() itself. + * + * Context: Can be called from any context in the new kernel that has a ha= ndle + * to a restored session. + * Return: 0 on success. Returns -ENOENT if no file with the matching toke= n is + * found, or any other negative errno on failure. + */ +int liveupdate_get_file_incoming(struct liveupdate_session *s, u64 token, + struct file **filep) +{ + struct luo_session *session =3D (struct luo_session *)s; + + return luo_retrieve_file(session, token, filep); +} diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index ab4652d79e64..3fd5d39fdd61 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -74,4 +74,13 @@ int luo_session_serialize(void); int luo_session_deserialize(void); bool luo_session_is_deserialized(void); =20 +int luo_preserve_file(struct luo_session *session, u64 token, int fd); +void luo_file_unpreserve_files(struct luo_session *session); +int luo_file_freeze(struct luo_session *session); +void luo_file_unfreeze(struct luo_session *session); +int luo_retrieve_file(struct luo_session *session, u64 token, + struct file **filep); +int luo_file_finish(struct luo_session *session); +int luo_file_deserialize(struct luo_session *session); + #endif /* _LINUX_LUO_INTERNAL_H */ --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f174.google.com (mail-yw1-f174.google.com [209.85.128.174]) (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 D645C33F37D for ; Fri, 7 Nov 2025 21:05:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549554; cv=none; b=psbgDLC3dkbRwrXkRe9tLwZT0M3JLqc7POdEZWDrUt1SeBSiRPvDTiVFxHEmZ5wVj9OKoN/Lvwmi0cc2RUwI0/oaH3TKa51mDugZlXQJ86R0hs2NlaJkxSXi1KRw259lQTJKpU5YtqJ4FM6GILdIQfsSj5pOPfpQ7RJq7QRsgGw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549554; c=relaxed/simple; bh=1tAUjU7HRPRMnOOaxXw2LBOaUHpXqy5QA8W9SlbrSto=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kWbbn109zFk1GMr7R+7xvJNDRqvD+fBvsc3IzFhESmkqGdAppASQuXxg7cfUsIJSGOXtltz9ab1iqTxxzWm+1Z+nwyA2QL7OeCGifE90WmKx7H4Z8TGqqN853IPK/PVULH8yoVFYW2bcv50jaADGBWxnhpARNY0QhlbjDJ+puLU= 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=S1PCexuT; arc=none smtp.client-ip=209.85.128.174 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="S1PCexuT" Received: by mail-yw1-f174.google.com with SMTP id 00721157ae682-7866dcf50b1so11449017b3.3 for ; Fri, 07 Nov 2025 13:05:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549550; x=1763154350; 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=0H1a2b10/az7J4jiEg4FN+EzMD5QNU4qG1nwYSONnc0=; b=S1PCexuTKw7a/5Qh5YcXzmCmMfpBdyHyVhtf6Y0UmLH5vDKFql2p+L+HHyNgnKOOL/ Db/tbCJ+AemovQtwnKMWheVWlJVf4OXH/9pHQVQUKJXXpfXlWl+0dhebex/5sAgnH5Ja 5o5nTVCJLROQvOM7epU0X5Fztd4DvIow1JR0M6uCT3BCwfwLxzf+vORXW4A/Pr+1RCDM 9g0tqCVeUM/N4cTs1B1hVfC5DV9kTK8zpS1h7U89NWkVj4fj3NHawX24qKGC8463rbM5 H5IqCgLa4VEVDQ+3AD9hvYa99YCRsePsb/ch+eKM+8PZzYQTd4uxG4FzyRF6b57MPe3Y M9kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549550; x=1763154350; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=0H1a2b10/az7J4jiEg4FN+EzMD5QNU4qG1nwYSONnc0=; b=hOr4q4DlKMgCo4GMJqddVMnFOE+aancIBivoYKmtqnKkrNHZ3DmWcuYWVaXOjLxVDN MmdibCUdXSJnfCb829vURy5MbSr2hl/2fey2dAulqsQx3GrsB+nzbVT1PAnfa1fQFbuV uWISuJ12GlV6zZVpnCtE1i7hKo60mydAVZaOrWE6EQLeWLy0qy79HAZ5q00yYK3bLxvv nECaapDc3Glcg4gk/7yEtxb4xqo6PAR+FAM5VIl7pfmtUhycyd/gseOHfDpr7A5ROWm3 2L5Af6zUq4IHvwb8lVDTpmoRjwOYsryQVOrVfEqE/m5fr6AtHCpYpdcsGqAy/SiiHnNp MaXg== X-Forwarded-Encrypted: i=1; AJvYcCUphIjYOuv3zqJCIJVCs0BTMUIhu5nR+Pu65EvElf6Pf6lLtSWg8UuatEM4QC+zebzWzMkYZiMz8RHMN/A=@vger.kernel.org X-Gm-Message-State: AOJu0YxYlFU68oGw7KdZ2hqdVFI+OhMUOJxgxtDHJjeJs3fBA5zAoPLk G14jWJidbKJ5P4eM7Ay/OLgxYnUgGVCwL61FQtcwFldoUt4QJs87IUWtH+hQX08bF6k= X-Gm-Gg: ASbGncu+24OKLyRZaE4ZiKfdqNLt3xJSK9Sdz4b45veGnH6FLp3KBH9ANrnDA6dn1Mm q5211uoN0FtB6RHjXxSFZGacmYo5yu2amtaQ8cF3+dvecCpyrJ5GFPGeV7HJR9EsSOHHVcvRoA9 nsULvNLUsNYSJCKw9xh2VmX12xYTfXo7tpS8XRyM16T4J6j0rc3lATOjZpdOpBkEtuSMq/wsIJ8 XXpo0nXjSsy61m6A8vrIL6hBGiTEhEQii4Ua2Cusx+8yfKp607ATUc6ygOtWvdCD5YCoxd4HsDX xUu8+WQMWYhnmaURkFCThMOldZgQPO1SWW3OkUMGnJFXIYeZZF6zGxUZCXBBoP1EY35cBTdjbP5 0K4HUEt+hrTtygEKCqYNWcBI5vUHEQO4EXuZqRPLGBhjRyygjB4XTluNxs1Wyn2ysUM59tbj068 2SxlYEIXnGMY+bIYltm4QISATde1Wda57p/wkng/Y2c0YQDWfvtnHGyKDnfHswhjU= X-Google-Smtp-Source: AGHT+IFQwHqNI6dZv0OYvv/A/7mWGgOnDHLBKzgUSO31hY47HAU5moGRNVmS+T0lEJI58NPUXQlkuQ== X-Received: by 2002:a05:690c:8d10:b0:784:8b82:98d8 with SMTP id 00721157ae682-787d5369a5emr6209347b3.16.1762549549650; Fri, 07 Nov 2025 13:05:49 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:49 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 09/22] liveupdate: luo_session: Add ioctls for file preservation and state management Date: Fri, 7 Nov 2025 16:03:07 -0500 Message-ID: <20251107210526.257742-10-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Introducing the userspace interface and internal logic required to manage the lifecycle of file descriptors within a session. Previously, a session was merely a container; this change makes it a functional management unit. The following capabilities are added: A new set of ioctl commands are added, which operate on the file descriptor returned by CREATE_SESSION. This allows userspace to: - LIVEUPDATE_SESSION_PRESERVE_FD: Add a file descriptor to a session to be preserved across the live update. - LIVEUPDATE_SESSION_UNPRESERVE_FD: Remove a previously added file descriptor from the session. - LIVEUPDATE_SESSION_RESTORE_FD: Retrieve a preserved file in the new kernel using its unique token. A state machine for each individual session, distinct from the global LUO state. This enables more granular control, allowing userspace to prepare or freeze specific sessions independently. This is managed via: - LIVEUPDATE_SESSION_SET_EVENT: An ioctl to send PREPARE, FREEZE, CANCEL, or FINISH events to a single session. - LIVEUPDATE_SESSION_GET_STATE: An ioctl to query the current state of a single session. The global subsystem callbacks (luo_session_prepare, luo_session_freeze) are updated to iterate through all existing sessions. They now trigger the appropriate per-session state transitions for any sessions that haven't already been transitioned individually by userspace. The session's .release handler is enhanced to be state-aware. When a session's file descriptor is closed, it now correctly cancels or finishes the session based on its current state before freeing all associated file resources, preventing resource leaks. Signed-off-by: Pasha Tatashin --- include/uapi/linux/liveupdate.h | 104 ++++++++++++++++++ kernel/liveupdate/luo_session.c | 183 +++++++++++++++++++++++++++++++- 2 files changed, 283 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h index 3ce60e976ecc..c55b3c7a1a39 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -53,6 +53,14 @@ enum { LIVEUPDATE_CMD_RETRIEVE_SESSION =3D 0x01, }; =20 +/* ioctl commands for session file descriptors */ +enum { + LIVEUPDATE_CMD_SESSION_BASE =3D 0x40, + LIVEUPDATE_CMD_SESSION_PRESERVE_FD =3D LIVEUPDATE_CMD_SESSION_BASE, + LIVEUPDATE_CMD_SESSION_RETRIEVE_FD =3D 0x41, + LIVEUPDATE_CMD_SESSION_FINISH =3D 0x42, +}; + /** * struct liveupdate_ioctl_create_session - ioctl(LIVEUPDATE_IOCTL_CREATE_= SESSION) * @size: Input; sizeof(struct liveupdate_ioctl_create_session) @@ -110,4 +118,100 @@ struct liveupdate_ioctl_retrieve_session { #define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) =20 +/* Session specific IOCTLs */ + +/** + * struct liveupdate_session_preserve_fd - ioctl(LIVEUPDATE_SESSION_PRESER= VE_FD) + * @size: Input; sizeof(struct liveupdate_session_preserve_fd) + * @fd: Input; The user-space file descriptor to be preserved. + * @token: Input; An opaque, unique token for preserved resource. + * + * Holds parameters for preserving Validate and initiate preservation for = a file + * descriptor. + * + * User sets the @fd field identifying the file descriptor to preserve + * (e.g., memfd, kvm, iommufd, VFIO). The kernel validates if this FD type + * and its dependencies are supported for preservation. If validation pass= es, + * the kernel marks the FD internally and *initiates the process* of prepa= ring + * its state for saving. The actual snapshotting of the state typically oc= curs + * during the subsequent %LIVEUPDATE_IOCTL_PREPARE execution phase, though + * some finalization might occur during freeze. + * On successful validation and initiation, the kernel uses the @token + * field with an opaque identifier representing the resource being preserv= ed. + * This token confirms the FD is targeted for preservation and is required= for + * the subsequent %LIVEUPDATE_SESSION_RETRIEVE_FD call after the live upda= te. + * + * Return: 0 on success (validation passed, preservation initiated), negat= ive + * error code on failure (e.g., unsupported FD type, dependency issue, + * validation failed). + */ +struct liveupdate_session_preserve_fd { + __u32 size; + __s32 fd; + __aligned_u64 token; +}; + +#define LIVEUPDATE_SESSION_PRESERVE_FD \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_PRESERVE_FD) + +/** + * struct liveupdate_session_retrieve_fd - ioctl(LIVEUPDATE_SESSION_RETRIE= VE_FD) + * @size: Input; sizeof(struct liveupdate_session_RETRIEVE_fd) + * @fd: Output; The new file descriptor representing the fully restored + * kernel resource. + * @token: Input; An opaque, token that was used to preserve the resource. + * + * Retrieve a previously preserved file descriptor. + * + * User sets the @token field to the value obtained from a successful + * %LIVEUPDATE_IOCTL_FD_PRESERVE call before the live update. On success, + * the kernel restores the state (saved during the PREPARE/FREEZE phases) + * associated with the token and populates the @fd field with a new file + * descriptor referencing the restored resource in the current (new) kerne= l. + * This operation must be performed *before* signaling completion via + * %LIVEUPDATE_IOCTL_FINISH. + * + * Return: 0 on success, negative error code on failure (e.g., invalid tok= en). + */ +struct liveupdate_session_retrieve_fd { + __u32 size; + __s32 fd; + __aligned_u64 token; +}; + +#define LIVEUPDATE_SESSION_RETRIEVE_FD \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_RETRIEVE_FD) + +/** + * struct liveupdate_session_finish - ioctl(LIVEUPDATE_SESSION_FINISH) + * @size: Input; sizeof(struct liveupdate_session_finish) + * @reserved: Input; Must be zero. Reserved for future use. + * + * Signals the completion of the restoration process for a retrieved sessi= on. + * This is the final operation that should be performed on a session file + * descriptor after a live update. + * + * This ioctl must be called once all required file descriptors for the se= ssion + * have been successfully retrieved (using %LIVEUPDATE_SESSION_RETRIEVE_FD= ) and + * are fully restored from the userspace and kernel perspective. + * + * Upon success, the kernel releases its ownership of the preserved resour= ces + * associated with this session. This allows internal resources to be free= d, + * typically by decrementing reference counts on the underlying preserved + * objects. + * + * If this operation fails, the resources remain preserved in memory. User= space + * may attempt to call finish again. The resources will otherwise be reset + * during the next live update cycle. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_session_finish { + __u32 size; + __u8 reserved[2]; +}; + +#define LIVEUPDATE_SESSION_FINISH \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH) + #endif /* _UAPI_LIVEUPDATE_H */ diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_sessio= n.c index a3513118aa74..ec526de83987 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -164,26 +164,185 @@ static void luo_session_remove(struct luo_session_he= ad *sh, sh->count--; } =20 +static int luo_session_finish_one(struct luo_session *session) +{ + guard(mutex)(&session->mutex); + return luo_file_finish(session); +} + +static void luo_session_unfreeze_one(struct luo_session *session) +{ + guard(mutex)(&session->mutex); + luo_file_unfreeze(session); +} + +static int luo_session_freeze_one(struct luo_session *session) +{ + guard(mutex)(&session->mutex); + return luo_file_freeze(session); +} + static int luo_session_release(struct inode *inodep, struct file *filep) { struct luo_session *session =3D filep->private_data; struct luo_session_head *sh; + int err =3D 0; =20 /* If retrieved is set, it means this session is from incoming list */ - if (session->retrieved) + if (session->retrieved) { sh =3D &luo_session_global.incoming; - else + + err =3D luo_session_finish_one(session); + if (err) { + pr_warn("Unable to finish session [%s] on release\n", + session->name); + } else { + luo_session_remove(sh, session); + luo_session_free(session); + } + + } else { sh =3D &luo_session_global.outgoing; =20 - luo_session_remove(sh, session); - luo_session_free(session); + scoped_guard(mutex, &session->mutex) + luo_file_unpreserve_files(session); + luo_session_remove(sh, session); + luo_session_free(session); + } + + return err; +} + +static int luo_session_preserve_fd(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_preserve_fd *argp =3D ucmd->cmd; + int err; + + guard(mutex)(&session->mutex); + err =3D luo_preserve_file(session, argp->token, argp->fd); + if (err) + return err; + + err =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + pr_warn("The file was successfully preserved, but response to user faile= d\n"); + + return err; +} + +static int luo_session_retrieve_fd(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_retrieve_fd *argp =3D ucmd->cmd; + struct file *file; + int err; + + argp->fd =3D get_unused_fd_flags(O_CLOEXEC); + if (argp->fd < 0) + return argp->fd; + + guard(mutex)(&session->mutex); + err =3D luo_retrieve_file(session, argp->token, &file); + if (err < 0) { + put_unused_fd(argp->fd); + + return err; + } + + err =3D luo_ucmd_respond(ucmd, sizeof(*argp)); + if (err) + return err; + + fd_install(argp->fd, file); =20 return 0; } =20 +static int luo_session_finish(struct luo_session *session, + struct luo_ucmd *ucmd) +{ + struct liveupdate_session_finish *argp =3D ucmd->cmd; + int err =3D luo_session_finish_one(session); + + if (err) + return err; + + return luo_ucmd_respond(ucmd, sizeof(*argp)); +} + +union ucmd_buffer { + struct liveupdate_session_finish finish; + struct liveupdate_session_preserve_fd preserve; + struct liveupdate_session_retrieve_fd retrieve; +}; + +struct luo_ioctl_op { + unsigned int size; + unsigned int min_size; + unsigned int ioctl_num; + int (*execute)(struct luo_session *session, struct luo_ucmd *ucmd); +}; + +#define IOCTL_OP(_ioctl, _fn, _struct, _last) = \ + [_IOC_NR(_ioctl) - LIVEUPDATE_CMD_SESSION_BASE] =3D { \ + .size =3D sizeof(_struct) + \ + BUILD_BUG_ON_ZERO(sizeof(union ucmd_buffer) < \ + sizeof(_struct)), \ + .min_size =3D offsetofend(_struct, _last), \ + .ioctl_num =3D _ioctl, \ + .execute =3D _fn, \ + } + +static const struct luo_ioctl_op luo_session_ioctl_ops[] =3D { + IOCTL_OP(LIVEUPDATE_SESSION_FINISH, luo_session_finish, + struct liveupdate_session_finish, reserved), + IOCTL_OP(LIVEUPDATE_SESSION_PRESERVE_FD, luo_session_preserve_fd, + struct liveupdate_session_preserve_fd, token), + IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd, + struct liveupdate_session_retrieve_fd, token), +}; + +static long luo_session_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct luo_session *session =3D filep->private_data; + const struct luo_ioctl_op *op; + struct luo_ucmd ucmd =3D {}; + union ucmd_buffer buf; + unsigned int nr; + int ret; + + nr =3D _IOC_NR(cmd); + if (nr < LIVEUPDATE_CMD_SESSION_BASE || (nr - LIVEUPDATE_CMD_SESSION_BASE= ) >=3D + ARRAY_SIZE(luo_session_ioctl_ops)) { + return -EINVAL; + } + + ucmd.ubuffer =3D (void __user *)arg; + ret =3D get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer); + if (ret) + return ret; + + op =3D &luo_session_ioctl_ops[nr - LIVEUPDATE_CMD_SESSION_BASE]; + if (op->ioctl_num !=3D cmd) + return -ENOIOCTLCMD; + if (ucmd.user_size < op->min_size) + return -EINVAL; + + ucmd.cmd =3D &buf; + ret =3D copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer, + ucmd.user_size); + if (ret) + return ret; + + return op->execute(session, &ucmd); +} + static const struct file_operations luo_session_fops =3D { .owner =3D THIS_MODULE, .release =3D luo_session_release, + .unlocked_ioctl =3D luo_session_ioctl, }; =20 /* Create a "struct file" for session */ @@ -375,6 +534,8 @@ int luo_session_deserialize(void) session->count =3D sh->ser[i].count; session->files =3D __va(sh->ser[i].files); session->pgcnt =3D sh->ser[i].pgcnt; + scoped_guard(mutex, &session->mutex) + luo_file_deserialize(session); } =20 luo_free_restore(sh->head_ser, sh->head_ser->pgcnt << PAGE_SHIFT); @@ -389,9 +550,14 @@ int luo_session_serialize(void) struct luo_session_head *sh =3D &luo_session_global.outgoing; struct luo_session *session; int i =3D 0; + int err; =20 guard(rwsem_write)(&sh->rwsem); list_for_each_entry(session, &sh->list, list) { + err =3D luo_session_freeze_one(session); + if (err) + goto err_undo; + strscpy(sh->ser[i].name, session->name, sizeof(sh->ser[i].name)); sh->ser[i].count =3D session->count; @@ -402,4 +568,13 @@ int luo_session_serialize(void) sh->head_ser->count =3D sh->count; =20 return 0; + +err_undo: + list_for_each_entry_continue_reverse(session, &sh->list, list) { + luo_session_unfreeze_one(session); + memset(&sh->ser[i], 0, sizeof(sh->ser[i])); + i--; + } + + return err; } --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f169.google.com (mail-yw1-f169.google.com [209.85.128.169]) (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 0D20C33F39C for ; Fri, 7 Nov 2025 21:05:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549557; cv=none; b=q0kuw8AG+c5Xlbbq9xScXi7zkcZlOQ0YWLR3HNVZKPSeu+QVh9zHVQiEZpeQpFc8YGIbLZOsCPkLf32HgEzdkFFtX8oRGVvQ6lhmtnaNGUmKDTzkzfc1g84pmzGQ4zG2lXif3msc5m2cYQ8xFvuAxXCp7upqw0xS5GqekhGWc7c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549557; c=relaxed/simple; bh=MnNj64qqDUAMei581sP0CjO1xHAxSvTv4PgGWqRr7aQ=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KFGAUILYqZgacgbQ5KmIzX77kMLTIWoEkBwlBST9Aj9PyCzvPNmr5NbOIGZ29xr9/mF2U5LFRsZCypOHr9J9xjt0YDX0j9G2Kx+vSwyRkkRdRH1/I+NIblfsl/WQaQBCipdaRF01/htzIc4jdkLVYf7sUJsghJh4GP3j1oSfT90= 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=Rrn0EVLI; arc=none smtp.client-ip=209.85.128.169 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="Rrn0EVLI" Received: by mail-yw1-f169.google.com with SMTP id 00721157ae682-71d6014810fso10302977b3.0 for ; Fri, 07 Nov 2025 13:05:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549552; x=1763154352; 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=nxYEpFzHlGnsRCNSz3jhwZOZP8nS3BHccZllwOooN0Y=; b=Rrn0EVLIQgn6w1cYfuE9x92QPPU+LX5MWMUh6PNsdKaG5Vz4433hIX7dqZGxitOPYh DNguMSM5Td+S7jZx122harEKqzDjlW0KHvENqeR7pw284tsgnVzUjjORSi9uKus+0qWf vt5Ee2G86jkJM+EraKwUnBGpyNaukhznqRt/HQX467C383G6/UmU9Oe/WdH51VKHu5ua LJm+KGbRsNIES+m9UZrRXdNtA3FOlo70Wqm43QhpePFhtU0VCPaUhbRmVCpe3DNzCWUc DSf2aAeOANBl4HbuenQXua/W3Aa83hPACRWq09wnkqxrmp8WZcCUDuFGzPlTk0b/GGHB vnAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549552; x=1763154352; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=nxYEpFzHlGnsRCNSz3jhwZOZP8nS3BHccZllwOooN0Y=; b=i/nZXPJrXymhBf+9IkgUPI4U5jhIots5zBTOt3d8jgRzHV62Ja2Bf7nqbSYOWvnByA RspTkmUlWawHaUw/8mDNoIWOtYhhAwAAd4zdMg0sasZVcW7Knuv3AVayzf21ow3TUwBz p+wHzK9/6CnLLKg+aQt4N6V+p7K0QB+DHd4leQWbru6xEIP0mzlV+jpcukntk7kZz3So /W/g1wMXtJBjrHzvhPkXGZSqXtmr1/2hiN3edmUTFX70h8RPDRy32Y7VNmAr/cMYGDL6 L2PWcdFoE8DaF3AdT5FkoDqpa8ZjAkfgzPJg+Mh1YybZsTUVtMXgEVBj3jso/pNUZvdE ORFw== X-Forwarded-Encrypted: i=1; AJvYcCVHK8/9JeJ8RVCtp0juseFv33yA2qa4WG9cyTQKTVvt0JcKKupTU2d59QoYmGrfrimL6YcHa/r7xf6LrpM=@vger.kernel.org X-Gm-Message-State: AOJu0Yyq/n00KzVFNMtoXbdlAaJf7NSM30RSDXExuieV4z2UjgbelXtm p8H4jBQ8fb+GkW/Ll0UCmYE/m4oz3WB+aLxfETksyhGFAqZSkQrnmvbZtzXhQVkw4i8= X-Gm-Gg: ASbGncvlr4SsoSusfWAK41Qh3zYZyyFgVWRc0vVhX3kk/LDmBj0cDXvfFodt7UYcOvy TUrUC1YEMwrNi08+alOzN217YW1CNZk/lsiYh238RBYXAePxCmBUoNFUxMl9AaYt1sK4/Fohg2X kQPKbZdi20z9BEZ+uDrAPfNDNjv0ugDZcR4o6lFQnZ2j2VwRpaZ+n9X6eksa17GZ5KbKtTqQXoA fWNS4zOs74mwa3ERdcv9I1tiGvtcYcAiG73ldKiaCsQuA8ldBqaHe25ftPtndAORYQzmcbaFi+g qKnHsqfz0TGIkKCs+M6UBjXenjHh8F0+rn3Ef3rlsFbtqhS4GaaCLU9l6XZvbry2buN1RRRPctE G+Dntg3b0LkruWalHTrkMfGqBNF3wveqMv8CwpZM6rz2a1XpBCsFC7lsig4uc+/z5TQ0Eyj1UiQ ZnEntdyDdDpkpEdi8Q6orQAalZYLhGh436J3ySEklGFZJabjPKwqDMRCl9p3zKJ1vptcSqEeQpZ A== X-Google-Smtp-Source: AGHT+IG6dq6qP/kvDfmoFOJspr7wz8PF7oEYDucAnWH1JaIsWQUhk9Yg7/7e88T94sLUPfQJogJzrw== X-Received: by 2002:a05:690c:8e15:b0:784:9364:3cc5 with SMTP id 00721157ae682-787d54708a1mr6777887b3.53.1762549551593; Fri, 07 Nov 2025 13:05:51 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:51 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 10/22] liveupdate: luo_flb: Introduce File-Lifecycle-Bound global state Date: Fri, 7 Nov 2025 16:03:08 -0500 Message-ID: <20251107210526.257742-11-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 a mechanism for managing global kernel state whose lifecycle is tied to the preservation of one or more files. This is necessary for subsystems where multiple preserved file descriptors depend on a single, shared underlying resource. An example is HugeTLB, where multiple file descriptors such as memfd and guest_memfd may rely on the state of a single HugeTLB subsystem. Preserving this state for each individual file would be redundant and incorrect. The state should be preserved only once when the first file is preserved, and restored/finished only once the last file is handled. This patch introduces File-Lifecycle-Bound (FLB) objects to solve this problem. An FLB is a global, reference-counted object with a defined set of operations: - A file handler (struct liveupdate_file_handler) declares a dependency on one or more FLBs via a new registration function, liveupdate_register_flb(). - When the first file depending on an FLB is preserved, the FLB's .preserve() callback is invoked to save the shared global state. The reference count is then incremented for each subsequent file. - Conversely, when the last file is unpreserved (before reboot) or finished (after reboot), the FLB's .unpreserve() or .finish() callback is invoked to clean up the global resource. The implementation includes: - A new set of ABI definitions (luo_flb_ser, luo_flb_head_ser) and a corresponding FDT node (luo-flb) to serialize the state of all active FLBs and pass them via Kexec Handover. - Core logic in luo_flb.c to manage FLB registration, reference counting, and the invocation of lifecycle callbacks. - An API (liveupdate_flb_*_locked/*_unlock) for other kernel subsystems to safely access the live object managed by an FLB, both before and after the live update. This framework provides the necessary infrastructure for more complex subsystems like IOMMU, VFIO, and KVM to integrate with the Live Update Orchestrator. Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 118 ++++++ include/linux/liveupdate/abi/luo.h | 76 ++++ kernel/liveupdate/Makefile | 1 + kernel/liveupdate/luo_core.c | 7 +- kernel/liveupdate/luo_file.c | 8 + kernel/liveupdate/luo_flb.c | 628 +++++++++++++++++++++++++++++ kernel/liveupdate/luo_internal.h | 7 + 7 files changed, 844 insertions(+), 1 deletion(-) create mode 100644 kernel/liveupdate/luo_flb.c diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index d18a788222ab..ce39b77c89c3 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -14,6 +14,7 @@ #include =20 struct liveupdate_file_handler; +struct liveupdate_flb; struct liveupdate_session; struct file; =20 @@ -81,6 +82,7 @@ struct liveupdate_file_ops { * associated with individual &struct file instances. * @list: Used for linking this handler instance into a glob= al * list of registered file handlers. + * @flb_list: A list of FLB dependencies. * * Modules that want to support live update for specific file types should * register an instance of this structure. LUO uses this registration to @@ -91,6 +93,78 @@ struct liveupdate_file_handler { const struct liveupdate_file_ops *ops; const char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH]; struct list_head list; + struct list_head flb_list; +}; + +/** + * struct liveupdate_flb_op_args - Arguments for FLB operation callbacks. + * @flb: The global FLB instance for which this call is performed. + * @data: For .preserve(): [OUT] The callback sets this field. + * For .unpreserve(): [IN] The handle from .preserve(). + * For .retrieve(): [IN] The handle from .preserve(). + * @obj: For .preserve(): [OUT] Sets this to the live object. + * For .retrieve(): [OUT] Sets this to the live object. + * For .finish(): [IN] The live object from .retrieve(). + * + * This structure bundles all parameters for the FLB operation callbacks. + */ +struct liveupdate_flb_op_args { + struct liveupdate_flb *flb; + u64 data; + void *obj; +}; + +/** + * struct liveupdate_flb_ops - Callbacks for global File-Lifecycle-Bound d= ata. + * @preserve: Called when the first file using this FLB is preserve= d. + * The callback must save its state and return a single, + * self-contained u64 handle by setting the 'argp->data' + * field and 'argp->obj'. + * @unpreserve: Called when the last file using this FLB is unpreserv= ed + * (aborted before reboot). Receives the handle via + * 'argp->data' and live object via 'argp->obj'. + * @retrieve: Called on-demand in the new kernel, the first time a + * component requests access to the shared object. It re= ceives + * the preserved handle via 'argp->data' and must recons= truct + * the live object, returning it by setting the 'argp->o= bj' + * field. + * @finish: Called in the new kernel when the last file using thi= s FLB + * is finished. Receives the live object via 'argp->obj'= for + * cleanup. + * + * Operations that manage global shared data with file bound lifecycle, + * triggered by the first file that uses it and concluded by the last file= that + * uses it, across all sessions. + */ +struct liveupdate_flb_ops { + int (*preserve)(struct liveupdate_flb_op_args *argp); + void (*unpreserve)(struct liveupdate_flb_op_args *argp); + void (*retrieve)(struct liveupdate_flb_op_args *argp); + void (*finish)(struct liveupdate_flb_op_args *argp); +}; + +/** + * struct liveupdate_flb - A global definition for a shared data object. + * @ops: Callback functions + * @compatible: The compatibility string (e.g., "iommu-core-v1" + * that uniquely identifies the FLB type this handler + * supports. This is matched against the compatible string + * associated with individual &struct liveupdate_flb + * instances. + * @list: A global list of registered FLBs. + * @internal: Internal state, set in liveupdate_init_flb(). + * + * This struct is the "template" that a driver registers to define a share= d, + * file-lifecycle-bound object. The actual runtime state (the live object, + * refcount, etc.) is managed internally by the LUO core. + * Use liveupdate_init_flb() to initialize this struct before using it in + * other functions. + */ +struct liveupdate_flb { + const struct liveupdate_flb_ops *ops; + const char compatible[LIVEUPDATE_FLB_COMPAT_LENGTH]; + struct list_head list; + void *internal; }; =20 #ifdef CONFIG_LIVEUPDATE @@ -113,6 +187,17 @@ int liveupdate_get_file_incoming(struct liveupdate_ses= sion *s, u64 token, int liveupdate_get_token_outgoing(struct liveupdate_session *s, struct file *file, u64 *tokenp); =20 +/* Before using FLB for the first time it should be initialized */ +int liveupdate_init_flb(struct liveupdate_flb *flb); + +int liveupdate_register_flb(struct liveupdate_file_handler *h, + struct liveupdate_flb *flb); + +int liveupdate_flb_incoming_locked(struct liveupdate_flb *flb, void **objp= ); +void liveupdate_flb_incoming_unlock(struct liveupdate_flb *flb, void *obj); +int liveupdate_flb_outgoing_locked(struct liveupdate_flb *flb, void **objp= ); +void liveupdate_flb_outgoing_unlock(struct liveupdate_flb *flb, void *obj); + #else /* CONFIG_LIVEUPDATE */ =20 static inline void liveupdate_init(void) @@ -146,5 +231,38 @@ static inline int liveupdate_get_token_outgoing(struct= liveupdate_session *s, return -EOPNOTSUPP; } =20 +static inline int liveupdate_init_flb(struct liveupdate_flb *flb) +{ + return 0; +} + +static inline int liveupdate_register_flb(struct liveupdate_file_handler *= h, + struct liveupdate_flb *flb) +{ + return -EOPNOTSUPP; +} + +static inline int liveupdate_flb_incoming_locked(struct liveupdate_flb *fl= b, + void **objp) +{ + return -EOPNOTSUPP; +} + +static inline void liveupdate_flb_incoming_unlock(struct liveupdate_flb *f= lb, + void *obj) +{ +} + +static inline int liveupdate_flb_outgoing_locked(struct liveupdate_flb *fl= b, + void **objp) +{ + return -EOPNOTSUPP; +} + +static inline void liveupdate_flb_outgoing_unlock(struct liveupdate_flb *f= lb, + void *obj) +{ +} + #endif /* CONFIG_LIVEUPDATE */ #endif /* _LINUX_LIVEUPDATE_H */ diff --git a/include/linux/liveupdate/abi/luo.h b/include/linux/liveupdate/= abi/luo.h index df0a6cd1d5d2..768dd56189db 100644 --- a/include/linux/liveupdate/abi/luo.h +++ b/include/linux/liveupdate/abi/luo.h @@ -33,6 +33,11 @@ * compatible =3D "luo-session-v1"; * luo-session-head =3D ; * }; + * + * luo-flb { + * compatible =3D "luo-flb-v1"; + * luo-flb-head =3D ; + * }; * }; * * Main LUO Node (/): @@ -52,6 +57,17 @@ * the header for a contiguous block of memory containing an array of * `struct luo_session_ser`, one for each preserved session. * + * File-Lifecycle-Bound Node (luo-flb): + * This node describes all preserved global objects whose lifecycle is b= ound + * to that of the preserved files (e.g., shared IOMMU state). + * + * - compatible: "luo-flb-v1" + * Identifies the FLB ABI version. + * - luo-flb-head: u64 + * The physical address of a `struct luo_flb_head_ser`. This structure= is + * the header for a contiguous block of memory containing an array of + * `struct luo_flb_ser`, one for each preserved global object. + * * Serialization Structures: * The FDT properties point to memory regions containing arrays of simpl= e, * `__packed` structures. These structures contain the actual preserved = state. @@ -70,6 +86,16 @@ * Metadata for a single preserved file. Contains the `compatible` str= ing to * find the correct handler in the new kernel, a user-provided `token`= for * identification, and an opaque `data` handle for the handler to use. + * + * - struct luo_flb_head_ser: + * Header for the FLB array. Contains the total page count of the + * preserved memory block and the number of `struct luo_flb_ser` entri= es + * that follow. + * + * - struct luo_flb_ser: + * Metadata for a single preserved global object. Contains its `name` + * (compatible string), an opaque `data` handle, and the `count` + * number of files depending on it. */ =20 #ifndef _LINUX_LIVEUPDATE_ABI_LUO_H @@ -154,4 +180,54 @@ struct luo_file_ser { u64 token; } __packed; =20 +/* The max size is set so it can be reliably used during in serialization = */ +#define LIVEUPDATE_FLB_COMPAT_LENGTH 48 + +#define LUO_FDT_FLB_NODE_NAME "luo-flb" +#define LUO_FDT_FLB_COMPATIBLE "luo-flb-v1" +#define LUO_FDT_FLB_HEAD "luo-flb-head" + +/** + * struct luo_flb_head_ser - Header for the serialized FLB data block. + * @pgcnt: The total number of pages occupied by the entire preserved memo= ry + * region, including this header and the subsequent array of + * &struct luo_flb_ser entries. + * @count: The number of &struct luo_flb_ser entries that follow this head= er + * in the memory block. + * + * This structure is located at the physical address specified by the + * `LUO_FDT_FLB_HEAD` FDT property. It provides the new kernel with the + * necessary information to find and iterate over the array of preserved + * File-Lifecycle-Bound objects and to manage the underlying memory. + * + * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated. + */ +struct luo_flb_head_ser { + u64 pgcnt; + u64 count; +} __packed; + +/** + * struct luo_flb_ser - Represents the serialized state of a single FLB ob= ject. + * @name: The unique compatibility string of the FLB object, used to fi= nd the + * corresponding &struct liveupdate_flb handler in the new kerne= l. + * @data: The opaque u64 handle returned by the FLB's .preserve() opera= tion + * in the old kernel. This handle encapsulates the entire state = needed + * for restoration. + * @count: The reference count at the time of serialization; i.e., the n= umber + * of preserved files that depended on this FLB. This is used by= the + * new kernel to correctly manage the FLB's lifecycle. + * + * An array of these structures is created in a preserved memory region and + * passed to the new kernel. Each entry allows the LUO core to restore one + * global, shared object. + * + * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated. + */ +struct luo_flb_ser { + char name[LIVEUPDATE_FLB_COMPAT_LENGTH]; + u64 data; + u64 count; +} __packed; + #endif /* _LINUX_LIVEUPDATE_ABI_LUO_H */ diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index c2252a2ad7bd..8d5a8354ad5a 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -3,6 +3,7 @@ luo-y :=3D \ luo_core.o \ luo_file.o \ + luo_flb.o \ luo_ioctl.o \ luo_session.o =20 diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 83257ab93ebb..29a094ee225c 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -120,7 +120,9 @@ static int __init luo_early_startup(void) if (err) return err; =20 - return 0; + err =3D luo_flb_setup_incoming(luo_global.fdt_in); + + return err; } =20 void __init liveupdate_init(void) @@ -154,6 +156,7 @@ static int __init luo_fdt_setup(void) err |=3D fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE); err |=3D fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln)); err |=3D luo_session_setup_outgoing(fdt_out); + err |=3D luo_flb_setup_outgoing(fdt_out); err |=3D fdt_end_node(fdt_out); err |=3D fdt_finish(fdt_out); if (err) @@ -211,6 +214,8 @@ int liveupdate_reboot(void) if (err) return err; =20 + luo_flb_serialize(); + err =3D kho_finalize(); if (err) { pr_err("kho_finalize failed %d\n", err); diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index d5197149b3b7..7816c418a595 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -280,6 +280,10 @@ int luo_preserve_file(struct luo_session *session, u64= token, int fd) if (err) goto exit_err; =20 + err =3D luo_flb_file_preserve(fh); + if (err) + goto exit_err; + luo_file =3D kzalloc(sizeof(*luo_file), GFP_KERNEL); if (!luo_file) { err =3D -ENOMEM; @@ -299,6 +303,7 @@ int luo_preserve_file(struct luo_session *session, u64 = token, int fd) if (err) { mutex_destroy(&luo_file->mutex); kfree(luo_file); + luo_flb_file_unpreserve(fh); goto exit_err; } else { luo_file->serialized_data =3D args.serialized_data; @@ -350,6 +355,7 @@ void luo_file_unpreserve_files(struct luo_session *sess= ion) args.file =3D luo_file->file; args.serialized_data =3D luo_file->serialized_data; luo_file->fh->ops->unpreserve(&args); + luo_flb_file_unpreserve(luo_file->fh); =20 list_del(&luo_file->list); session->count--; @@ -622,6 +628,7 @@ static void luo_file_finish_one(struct luo_session *ses= sion, args.file =3D luo_file->file; args.serialized_data =3D luo_file->serialized_data; args.retrieved =3D luo_file->retrieved; + luo_flb_file_finish(luo_file->fh); =20 luo_file->fh->ops->finish(&args); } @@ -810,6 +817,7 @@ int liveupdate_register_file_handler(struct liveupdate_= file_handler *fh) return -EAGAIN; =20 INIT_LIST_HEAD(&fh->list); + INIT_LIST_HEAD(&fh->flb_list); list_add_tail(&fh->list, &luo_file_handler_list); =20 return 0; diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c new file mode 100644 index 000000000000..4a2757418cbe --- /dev/null +++ b/kernel/liveupdate/luo_flb.c @@ -0,0 +1,628 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/** + * DOC: LUO File Lifecycle Bound Global Data + * + * File-Lifecycle-Bound (FLB) objects provide a mechanism for managing glo= bal + * state that is shared across multiple live-updatable files. The lifecycl= e of + * this shared state is tied to the preservation of the files that depend = on it. + * + * An FLB represents a global resource, such as the IOMMU core state, that= is + * required by multiple file descriptors (e.g., all VFIO fds). + * + * The preservation of the FLB's state is triggered when the *first* file + * depending on it is preserved. The cleanup of this state (unpreserve or + * finish) is triggered when the *last* file depending on it is unpreserve= d or + * finished. + * + * Handler Dependency: A file handler declares its dependency on one or mo= re + * FLBs by registering them via liveupdate_register_flb(). + * + * Callback Model: Each FLB is defined by a set of operations + * (&struct liveupdate_flb_ops) that LUO invokes at key points: + * + * - .preserve(): Called for the first file. Saves global state. + * - .unpreserve(): Called for the last file (if aborted pre-reboot). + * - .retrieve(): Called on-demand in the new kernel to restore the st= ate. + * - .finish(): Called for the last file in the new kernel for cleanup. + * + * This reference-counted approach ensures that shared state is saved exac= tly + * once and restored exactly once, regardless of how many files depend on = it, + * and that its lifecycle is correctly managed across the kexec transition. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "luo_internal.h" + +#define LUO_FLB_PGCNT 1ul +#define LUO_FLB_MAX (((LUO_FLB_PGCNT << PAGE_SHIFT) - \ + sizeof(struct luo_flb_head_ser)) / sizeof(struct luo_flb_ser)) + +struct luo_flb_head { + struct luo_flb_head_ser *head_ser; + struct luo_flb_ser *ser; + bool active; +}; + +struct luo_flb_global { + struct luo_flb_head incoming; + struct luo_flb_head outgoing; + struct list_head list; + long count; +}; + +static struct luo_flb_global luo_flb_global =3D { + .list =3D LIST_HEAD_INIT(luo_flb_global.list), +}; + +/* + * struct luo_flb_link - Links an FLB definition to a file handler's inter= nal + * list of dependencies. + * @flb: A pointer to the registered &struct liveupdate_flb definition. + * @list: The list_head for linking. + */ +struct luo_flb_link { + struct liveupdate_flb *flb; + struct list_head list; +}; + +/* + * struct luo_flb_state - Holds the runtime state for one FLB lifecycle pa= th. + * @count: The number of preserved files currently depending on this FLB. + * This is used to trigger the preserve/unpreserve/finish ops on t= he + * first/last file. + * @data: The opaque u64 handle returned by .preserve() or passed to + * .retrieve(). + * @obj: The live kernel object returned by .preserve() or .retrieve(). + * @lock: A mutex that protects all fields within this structure, providi= ng + * the synchronization service for the FLB's ops. + */ +struct luo_flb_state { + long count; + u64 data; + void *obj; + struct mutex lock; +}; + +/* + * struct luo_flb_internal - Keep separate incoming and outgoing states. + * @outgoing: The runtime state for the pre-reboot (preserve/unpreserve) + * lifecycle. + * @incoming: The runtime state for the post-reboot (retrieve/finish) + * lifecycle. + */ +struct luo_flb_internal { + struct luo_flb_state outgoing; + struct luo_flb_state incoming; +}; + +static int luo_flb_file_preserve_one(struct liveupdate_flb *flb) +{ + struct luo_flb_internal *internal =3D flb->internal; + + scoped_guard(mutex, &internal->outgoing.lock) { + if (!internal->outgoing.count) { + struct liveupdate_flb_op_args args =3D {0}; + int err; + + args.flb =3D flb; + err =3D flb->ops->preserve(&args); + if (err) + return err; + internal->outgoing.data =3D args.data; + internal->outgoing.obj =3D args.obj; + } + internal->outgoing.count++; + } + + return 0; +} + +static void luo_flb_file_unpreserve_one(struct liveupdate_flb *flb) +{ + struct luo_flb_internal *internal =3D flb->internal; + + scoped_guard(mutex, &internal->outgoing.lock) { + internal->outgoing.count--; + if (!internal->outgoing.count) { + struct liveupdate_flb_op_args args =3D {0}; + + args.flb =3D flb; + args.data =3D internal->outgoing.data; + args.obj =3D internal->outgoing.obj; + + if (flb->ops->unpreserve) + flb->ops->unpreserve(&args); + + internal->outgoing.data =3D 0; + internal->outgoing.obj =3D NULL; + } + } +} + +static int luo_flb_retrieve_one(struct liveupdate_flb *flb) +{ + struct luo_flb_head *fh =3D &luo_flb_global.incoming; + struct luo_flb_internal *internal =3D flb->internal; + struct liveupdate_flb_op_args args =3D {0}; + bool found =3D false; + + guard(mutex)(&internal->incoming.lock); + + if (internal->incoming.obj) + return 0; + + if (!fh->active) + return -ENODATA; + + for (int i =3D 0; i < fh->head_ser->count; i++) { + if (!strcmp(fh->ser[i].name, flb->compatible)) { + internal->incoming.data =3D fh->ser[i].data; + internal->incoming.count =3D fh->ser[i].count; + found =3D true; + break; + } + } + + if (!found) + return -ENOENT; + + args.flb =3D flb; + args.data =3D internal->incoming.data; + + flb->ops->retrieve(&args); + internal->incoming.obj =3D args.obj; + + if (WARN_ON_ONCE(!internal->incoming.obj)) + return -EIO; + + return 0; +} + +static void luo_flb_file_finish_one(struct liveupdate_flb *flb) +{ + struct luo_flb_internal *internal =3D flb->internal; + u64 count; + + scoped_guard(mutex, &internal->incoming.lock) + count =3D --internal->incoming.count; + + if (!count) { + struct liveupdate_flb_op_args args =3D {0}; + + if (!internal->incoming.obj) { + int err =3D luo_flb_retrieve_one(flb); + + if (WARN_ON(err)) + return; + } + + scoped_guard(mutex, &internal->incoming.lock) { + args.flb =3D flb; + args.obj =3D internal->incoming.obj; + flb->ops->finish(&args); + + internal->incoming.data =3D 0; + internal->incoming.obj =3D NULL; + } + } +} + +/** + * luo_flb_file_preserve - Notifies FLBs that a file is about to be preser= ved. + * @h: The file handler for the preserved file. + * + * This function iterates through all FLBs associated with the given file + * handler. It increments the reference count for each FLB. If the count b= ecomes + * 1, it triggers the FLB's .preserve() callback to save the global state. + * + * This operation is atomic. If any FLB's .preserve() op fails, it will ro= ll + * back by calling .unpreserve() on any FLBs that were successfully preser= ved + * during this call. + * + * Context: Called from luo_preserve_file() + * Return: 0 on success, or a negative errno on failure. + */ +int luo_flb_file_preserve(struct liveupdate_file_handler *h) +{ + struct luo_flb_link *iter; + int err =3D 0; + + list_for_each_entry(iter, &h->flb_list, list) { + err =3D luo_flb_file_preserve_one(iter->flb); + if (err) + goto exit_err; + } + + return 0; + +exit_err: + list_for_each_entry_continue_reverse(iter, &h->flb_list, list) + luo_flb_file_unpreserve_one(iter->flb); + + return err; +} + +/** + * luo_flb_file_unpreserve - Notifies FLBs that a dependent file was unpre= served. + * @h: The file handler for the unpreserved file. + * + * This function iterates through all FLBs associated with the given file + * handler, in reverse order of registration. It decrements the reference = count + * for each FLB. If the count becomes 0, it triggers the FLB's .unpreserve= () + * callback to clean up the global state. + * + * Context: Called when a preserved file is being cleaned up before reboot + * (e.g., from luo_file_unpreserve_files()). + */ +void luo_flb_file_unpreserve(struct liveupdate_file_handler *h) +{ + struct luo_flb_link *iter; + + list_for_each_entry_reverse(iter, &h->flb_list, list) + luo_flb_file_unpreserve_one(iter->flb); +} + +/** + * luo_flb_file_finish - Notifies FLBs that a dependent file has been fini= shed. + * @h: The file handler for the finished file. + * + * This function iterates through all FLBs associated with the given file + * handler, in reverse order of registration. It decrements the incoming + * reference count for each FLB. If the count becomes 0, it triggers the F= LB's + * .finish() callback for final cleanup in the new kernel. + * + * Context: Called from luo_file_finish() for each file being finished. + */ +void luo_flb_file_finish(struct liveupdate_file_handler *h) +{ + struct luo_flb_link *iter; + + list_for_each_entry_reverse(iter, &h->flb_list, list) + luo_flb_file_finish_one(iter->flb); +} + +/** + * liveupdate_init_flb - Initializes a liveupdate FLB structure. + * @flb: The &struct liveupdate_flb to initialize. + * + * This function must be called to prepare an FLB structure before it can = be + * used with liveupdate_register_flb() or any other LUO functions. + * + * Context: Typically called once from a subsystem's module init function = for + * each global FLB object that the module defines. + * + * Return: 0 on success, or -ENOMEM if memory allocation fails. + */ +int liveupdate_init_flb(struct liveupdate_flb *flb) +{ + struct luo_flb_internal *internal =3D kzalloc(sizeof(*internal), + GFP_KERNEL | __GFP_ZERO); + + if (!internal) + return -ENOMEM; + + mutex_init(&internal->incoming.lock); + mutex_init(&internal->outgoing.lock); + + flb->internal =3D internal; + INIT_LIST_HEAD(&flb->list); + + return 0; +} + +/** + * liveupdate_register_flb - Associate an FLB with a file handler and regi= ster it globally. + * @h: The file handler that will now depend on the FLB. + * @flb: The File-Lifecycle-Bound object to associate. + * + * Establishes a dependency, informing the LUO core that whenever a file of + * type @h is preserved, the state of @flb must also be managed. + * + * On the first registration of a given @flb object, it is added to a glob= al + * registry. This function checks for duplicate registrations, both for a + * specific handler and globally, and ensures the total number of unique + * FLBs does not exceed the system limit. + * + * Context: Typically called from a subsystem's module init function after + * both the handler and the FLB have been defined and initialized. + * Return: 0 on success. Returns a negative errno on failure: + * -EINVAL if arguments are NULL or not initialized. + * -ENOMEM on memory allocation failure. + * -EEXIST if this FLB is already registered with this handler. + * -ENOSPC if the maximum number of global FLBs has been reached. + */ +int liveupdate_register_flb(struct liveupdate_file_handler *h, + struct liveupdate_flb *flb) +{ + struct luo_flb_internal *internal =3D flb->internal; + struct luo_flb_link *link __free(kfree) =3D NULL; + static DEFINE_MUTEX(register_flb_lock); + struct liveupdate_flb *gflb; + struct luo_flb_link *iter; + + if (WARN_ON(!h || !flb || !internal)) + return -EINVAL; + + if (WARN_ON(!flb->ops->preserve || !flb->ops->unpreserve || + !flb->ops->retrieve || !flb->ops->finish)) { + return -EINVAL; + } + + /* + * Once session/files have been deserialized, FLBs cannot be registered, + * it is too late. Deserialization uses file handlers, and FLB registers + * to file handlers. + */ + if (WARN_ON(luo_session_is_deserialized())) + return -EBUSY; + + /* + * File handler must already be registered, as it is initializes the + * flb_list + */ + if (WARN_ON(list_empty(&h->list))) + return -EINVAL; + + link =3D kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + guard(mutex)(®ister_flb_lock); + + /* Check that this FLB is not already linked to this file handler */ + list_for_each_entry(iter, &h->flb_list, list) { + if (iter->flb =3D=3D flb) + return -EEXIST; + } + + /* Is this FLB linked to global list ? */ + if (list_empty(&flb->list)) { + if (luo_flb_global.count =3D=3D LUO_FLB_MAX) + return -ENOSPC; + + /* Check that compatible string is unique in global list */ + list_for_each_entry(gflb, &luo_flb_global.list, list) { + if (!strcmp(gflb->compatible, flb->compatible)) + return -EEXIST; + } + + list_add_tail(&flb->list, &luo_flb_global.list); + luo_flb_global.count++; + } + + /* Finally, link the FLB to the file handler */ + link->flb =3D flb; + list_add_tail(&no_free_ptr(link)->list, &h->flb_list); + + return 0; +} + +/** + * liveupdate_flb_incoming_locked - Lock and retrieve the incoming FLB obj= ect. + * @flb: The FLB definition. + * @objp: Output parameter; will be populated with the live shared object. + * + * Acquires the FLB's internal lock and returns a pointer to its shared li= ve + * object for the incoming (post-reboot) path. + * + * If this is the first time the object is requested in the new kernel, th= is + * function will trigger the FLB's .retrieve() callback to reconstruct the + * object from its preserved state. Subsequent calls will return the same + * cached object. + * + * The caller MUST call liveupdate_flb_incoming_unlock() to release the lo= ck. + * + * Return: 0 on success, or a negative errno on failure. -ENODATA means no + * incoming FLB data, and -ENOENT means specific flb not found in the inco= ming + * data. + */ +int liveupdate_flb_incoming_locked(struct liveupdate_flb *flb, void **objp) +{ + struct luo_flb_internal *internal =3D flb->internal; + + if (WARN_ON(!internal)) + return -EINVAL; + + if (!internal->incoming.obj) { + int err =3D luo_flb_retrieve_one(flb); + + if (err) + return err; + } + + mutex_lock(&internal->incoming.lock); + *objp =3D internal->incoming.obj; + + return 0; +} + +/** + * liveupdate_flb_incoming_unlock - Unlock an incoming FLB object. + * @flb: The FLB definition. + * @obj: The object that was returned by the _locked call (used for valida= tion). + * + * Releases the internal lock acquired by liveupdate_flb_incoming_locked(). + */ +void liveupdate_flb_incoming_unlock(struct liveupdate_flb *flb, void *obj) +{ + struct luo_flb_internal *internal =3D flb->internal; + + lockdep_assert_held(&internal->incoming.lock); + internal->incoming.obj =3D obj; + mutex_unlock(&internal->incoming.lock); +} + +/** + * liveupdate_flb_outgoing_locked - Lock and retrieve the outgoing FLB obj= ect. + * @flb: The FLB definition. + * @objp: Output parameter; will be populated with the live shared object. + * + * Acquires the FLB's internal lock and returns a pointer to its shared li= ve + * object for the outgoing (pre-reboot) path. + * + * This function assumes the object has already been created by the FLB's + * .preserve() callback, which is triggered when the first dependent file + * is preserved. + * + * The caller MUST call liveupdate_flb_outgoing_unlock() to release the lo= ck. + * + * Return: 0 on success, or a negative errno on failure. + */ +int liveupdate_flb_outgoing_locked(struct liveupdate_flb *flb, void **objp) +{ + struct luo_flb_internal *internal =3D flb->internal; + + mutex_lock(&internal->outgoing.lock); + + /* The object must exist if any file is being preserved */ + if (WARN_ON_ONCE(!internal->outgoing.obj)) { + mutex_unlock(&internal->outgoing.lock); + return -ENOENT; + } + + *objp =3D internal->outgoing.obj; + + return 0; +} + +/** + * liveupdate_flb_outgoing_unlock - Unlock an outgoing FLB object. + * @flb: The FLB definition. + * @obj: The object that was returned by the _locked call (used for valida= tion). + * + * Releases the internal lock acquired by liveupdate_flb_outgoing_locked(). + */ +void liveupdate_flb_outgoing_unlock(struct liveupdate_flb *flb, void *obj) +{ + struct luo_flb_internal *internal =3D flb->internal; + + lockdep_assert_held(&internal->outgoing.lock); + internal->outgoing.obj =3D obj; + mutex_unlock(&internal->outgoing.lock); +} + +int __init luo_flb_setup_outgoing(void *fdt_out) +{ + struct luo_flb_head_ser *head_ser; + u64 head_ser_pa; + int err; + + head_ser =3D luo_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT); + if (IS_ERR(head_ser)) + return PTR_ERR(head_ser); + + head_ser_pa =3D __pa(head_ser); + + err =3D fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME); + err |=3D fdt_property_string(fdt_out, "compatible", + LUO_FDT_FLB_COMPATIBLE); + err |=3D fdt_property(fdt_out, LUO_FDT_FLB_HEAD, &head_ser_pa, + sizeof(head_ser_pa)); + err |=3D fdt_end_node(fdt_out); + + if (err) + goto err_unpreserve; + + head_ser->pgcnt =3D LUO_FLB_PGCNT; + luo_flb_global.outgoing.head_ser =3D head_ser; + luo_flb_global.outgoing.ser =3D (void *)(head_ser + 1); + luo_flb_global.outgoing.active =3D true; + + return 0; + +err_unpreserve: + luo_free_unpreserve(head_ser, LUO_FLB_PGCNT << PAGE_SHIFT); + + return err; +} + +int __init luo_flb_setup_incoming(void *fdt_in) +{ + struct luo_flb_head_ser *head_ser; + int err, head_size, offset; + const void *ptr; + u64 head_ser_pa; + + offset =3D fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME); + if (offset < 0) { + pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME); + + return -ENOENT; + } + + err =3D fdt_node_check_compatible(fdt_in, offset, + LUO_FDT_FLB_COMPATIBLE); + if (err) { + pr_err("FLB node is incompatible with '%s' [%d]\n", + LUO_FDT_FLB_COMPATIBLE, err); + + return -EINVAL; + } + + head_size =3D 0; + ptr =3D fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEAD, &head_size); + if (!ptr || head_size !=3D sizeof(u64)) { + pr_err("Unable to get FLB head property '%s' [%d]\n", + LUO_FDT_FLB_HEAD, head_size); + + return -EINVAL; + } + + memcpy(&head_ser_pa, ptr, sizeof(u64)); + head_ser =3D __va(head_ser_pa); + + luo_flb_global.incoming.head_ser =3D head_ser; + luo_flb_global.incoming.ser =3D (void *)(head_ser + 1); + luo_flb_global.incoming.active =3D true; + + return 0; +} + +/** + * luo_flb_serialize - Serializes all active FLB objects for KHO. + * + * This function is called from the reboot path. It iterates through all + * registered File-Lifecycle-Bound (FLB) objects. For each FLB that has be= en + * preserved (i.e., its reference count is greater than zero), it writes i= ts + * metadata into the memory region designated for Kexec Handover. + * + * The serialized data includes the FLB's compatibility string, its opaque + * data handle, and the final reference count. This allows the new kernel = to + * find the appropriate handler and reconstruct the FLB's state. + * + * Context: Called from liveupdate_reboot() just before kho_finalize(). + */ +void luo_flb_serialize(void) +{ + struct luo_flb_head *fh =3D &luo_flb_global.outgoing; + struct liveupdate_flb *flb; + int i =3D 0; + + list_for_each_entry(flb, &luo_flb_global.list, list) { + struct luo_flb_internal *internal =3D flb->internal; + + if (internal->outgoing.count > 0) { + strscpy(fh->ser[i].name, flb->compatible, + sizeof(fh->ser[i].name)); + fh->ser[i].data =3D internal->outgoing.data; + fh->ser[i].count =3D internal->outgoing.count; + i++; + } + } + + fh->head_ser->count =3D i; +} diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 3fd5d39fdd61..89c2fd97e5a7 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -83,4 +83,11 @@ int luo_retrieve_file(struct luo_session *session, u64 t= oken, int luo_file_finish(struct luo_session *session); int luo_file_deserialize(struct luo_session *session); =20 +int luo_flb_file_preserve(struct liveupdate_file_handler *h); +void luo_flb_file_unpreserve(struct liveupdate_file_handler *h); +void luo_flb_file_finish(struct liveupdate_file_handler *h); +int __init luo_flb_setup_outgoing(void *fdt); +int __init luo_flb_setup_incoming(void *fdt); +void luo_flb_serialize(void); + #endif /* _LINUX_LUO_INTERNAL_H */ --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (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 91F5433FE2F for ; Fri, 7 Nov 2025 21:05:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549557; cv=none; b=iDSEL7mKdjWI6/UXC75o1D8tKIZZLrjacooWqitX0NHNoaBY6wHVzpHByMVJhQYXIZmIRZjdlCqtHpc2JdGekUhXIK/EDcqm4Id5e9TllS3XQsgmYA8Vkph7v5CNeZVISxLyYnynAyzEXAn74Y7E0sFqPL4kNacxG1s76NEfDzc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549557; c=relaxed/simple; bh=uKOjvZQKso7oyRqI+EFwVBOcyaYMS+ZmmItsS5LMjXs=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LHVYNQBsSwg5Z1pyRyRU1PyDZnMKKqazuUMxSBJRiZdJLR0HD1fmVD6WKXzN9+uJ2PF9QzlDxcktpOKCoJN2+K1ZFsOjIiuMbbyJJfNrvCpdm7q2qX0FiJFLF4G0CclbihMI7fYUBUHgTMgEbNxzcITcE5iZbPqhHxBLpT5DVDo= 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=N3nULHcE; arc=none smtp.client-ip=209.85.128.171 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="N3nULHcE" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-7866dcf50b1so11449507b3.3 for ; Fri, 07 Nov 2025 13:05:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549553; x=1763154353; 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=eGX1JNngtcEnINGvpjjZOvPiM15RTyTbrS+2qficDEY=; b=N3nULHcEfeJW9vb0K9acHjVYC8tnA7xB98Bn2gv4gK2+UgBeEgiK2dMxzDLMwHON7c es08N7fsAL1V1801m24UcvZ/Wa83Iq2hRAE5dPkLDuf+bV7oLVmb77nuawry8Dmw+7qd sw2DYwiPslgPCcNI3gbBvUIkO4wcnsiTJa90lv5ki74kzSpxxUdmRp2REjhjh6LNSDC7 ch5QyN9+L11E1xRNc/PtkTq0iYTZb1ZCudkOozxk03ZECJ9LEjLB5X1YkO6L6l1pjKiv V8JCoU5ni/TiPTx/YxwjOzvDuKnmooWw6hWpfUAUioekHuDP8CU83G2nOButDFevqZzJ Qr2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549553; x=1763154353; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=eGX1JNngtcEnINGvpjjZOvPiM15RTyTbrS+2qficDEY=; b=GaO2bUDmGqppfeSLWXGnLSoW1TYbL4+xPEzOzZcyTgJ0xEdMJZYxfGgtYO05U0pdWr a+VZ82VF3QxvhY7A4VBdKhCWFCImgk0Q/FiXpsv0UfwL8p1HnpVdXwL2kpjYKI1sUb+V AF48/L9xJLAPQ+Pk0vl1m5akhwL8EYqRGlekFLI2EpeWU3CthA2J8FjukqIkcf8UbeOl wCifPtEaMYyJ7m76UEch34PWujtJG1q/zfF6COqMrtkE5oKoAxt2u5r3QM6ZxSMbPNb0 yBIYDXLnrHTy710VyQ6UloCnyGNx+oLnFR+ewKT3H/gKrMHrifB/tkxR89rfcQSHkZbf CgQQ== X-Forwarded-Encrypted: i=1; AJvYcCVpTpZc1pYGYYcomFwOUhXpZBH73yF6Fr7V6S1qWAz+kp36Tvbzwg+lEk75hXmIeLHnjLitv82WIiSKTrw=@vger.kernel.org X-Gm-Message-State: AOJu0YzBREqE9jBe7Jn8iBPFvHPQf31qPU841bVZzrwWbPXOUMgb/LZE sLSptEL8AMr8b/4jkoBwpeE8PmfhFlOfrht2fAWkF5tf4KZw8V2Tqw83SsJTiQ/wIy4= X-Gm-Gg: ASbGncvJjw+RTSxS+YlKE1lJehxbAdtvuphROQY3rCjpzdFwx9fcS8CwoBbLOib7wb0 +/7szZJUVl7QSDk1ft0AB7aJxK+8+dDCw+Um2WbbdHQKFdg1WlaBXLKd2sibscYZpEg3glnizss 7T7scJHfHFhmf1zm6Ynh4xbE+zlgJtusQOZzrY15Lbq3ZnTPuSryEtYGwHJ2O5yZT338RNpR2Gs LdP7XMbl/ifH27XWDyHITg1U8RAfJHzn/PleGLxPf6J34DJLr89t1MOogHJq9eQOUk2MBzxEAqY Uf9LaN+NPEkgD631vV1gu4LJKmsU8MsDo/acb4Jah/KmTmiyxJ8mlyEod3qFpXYf2c3fZ5Fb76j S+Fo2gNYVtoa6ldjTMXzUaglp91SoyHXeLZ8GZqGh1Ad1LRcOaVXUiFRes0dYE+9zFUeZ7BkN0j JqSgwD1TxtdZKV/NNK5FFXcWw2amTrRWG+zMvLjORi5vtFJfm4h937eiZEgw5VkyF5v8xeP9H7p Q== X-Google-Smtp-Source: AGHT+IFbkLlhQosQFQ+WdXArr3gHVo/6dHC8xvUgFjTdDqpIfNT6vGARK5zq5TsgxqtRCa2zCH7RmA== X-Received: by 2002:a05:690c:c341:b0:785:ec4f:3c8 with SMTP id 00721157ae682-787d543901cmr7438187b3.48.1762549553481; Fri, 07 Nov 2025 13:05:53 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:53 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 11/22] docs: add luo documentation Date: Fri, 7 Nov 2025 16:03:09 -0500 Message-ID: <20251107210526.257742-12-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Add the documentation files for the Live Update Orchestrator Signed-off-by: Pasha Tatashin --- Documentation/core-api/index.rst | 1 + Documentation/core-api/liveupdate.rst | 64 ++++++++++++++++++++++ Documentation/userspace-api/index.rst | 1 + Documentation/userspace-api/liveupdate.rst | 20 +++++++ 4 files changed, 86 insertions(+) create mode 100644 Documentation/core-api/liveupdate.rst create mode 100644 Documentation/userspace-api/liveupdate.rst diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/inde= x.rst index 6cbdcbfa79c3..5eb0fbbbc323 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -138,6 +138,7 @@ Documents that don't fit elsewhere or which have yet to= be categorized. :maxdepth: 1 =20 librs + liveupdate netlink =20 .. only:: subproject and html diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api= /liveupdate.rst new file mode 100644 index 000000000000..deacc098d024 --- /dev/null +++ b/Documentation/core-api/liveupdate.rst @@ -0,0 +1,64 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Live Update Orchestrator +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +:Author: Pasha Tatashin + +.. kernel-doc:: kernel/liveupdate/luo_core.c + :doc: Live Update Orchestrator (LUO) + +LUO Sessions +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: kernel/liveupdate/luo_session.c + :doc: LUO Sessions + +LUO Preserving File Descriptors +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: kernel/liveupdate/luo_file.c + :doc: LUO File Descriptors + +LUO File Lifecycle Bound Global Data +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: kernel/liveupdate/luo_flb.c + :doc: LUO File Lifecycle Bound Global Data + +Live Update Orchestrator ABI +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D +.. kernel-doc:: include/linux/liveupdate/abi/luo.h + :doc: Live Update Orchestrator ABI + +Public API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: include/linux/liveupdate.h + +.. kernel-doc:: include/linux/liveupdate/abi/luo.h + +.. kernel-doc:: kernel/liveupdate/luo_core.c + :export: + +.. kernel-doc:: kernel/liveupdate/luo_flb.c + :export: + +.. kernel-doc:: kernel/liveupdate/luo_file.c + :export: + +Internal API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: kernel/liveupdate/luo_core.c + :internal: + +.. kernel-doc:: kernel/liveupdate/luo_flb.c + :internal: + +.. kernel-doc:: kernel/liveupdate/luo_session.c + :internal: + +.. kernel-doc:: kernel/liveupdate/luo_file.c + :internal: + +See Also +=3D=3D=3D=3D=3D=3D=3D=3D + +- :doc:`Live Update uAPI ` +- :doc:`/core-api/kho/concepts` diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspac= e-api/index.rst index b8c73be4fb11..8a61ac4c1bf1 100644 --- a/Documentation/userspace-api/index.rst +++ b/Documentation/userspace-api/index.rst @@ -61,6 +61,7 @@ Everything else :maxdepth: 1 =20 ELF + liveupdate netlink/index sysfs-platform_profile vduse diff --git a/Documentation/userspace-api/liveupdate.rst b/Documentation/use= rspace-api/liveupdate.rst new file mode 100644 index 000000000000..04210a6cf6d6 --- /dev/null +++ b/Documentation/userspace-api/liveupdate.rst @@ -0,0 +1,20 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Live Update uAPI +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +:Author: Pasha Tatashin + +ioctl interface +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: kernel/liveupdate/luo_ioctl.c + :doc: LUO ioctl Interface + +ioctl uAPI +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +.. kernel-doc:: include/uapi/linux/liveupdate.h + +See Also +=3D=3D=3D=3D=3D=3D=3D=3D + +- :doc:`Live Update Orchestrator ` --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f182.google.com (mail-yw1-f182.google.com [209.85.128.182]) (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 B06643101C9 for ; Fri, 7 Nov 2025 21:05:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549559; cv=none; b=GkMM0H8SjaZVtYQCA3tQedao3INYgkiThxK1euFedlx11MPTR+ycFcLaDhUgbarhsN3HSmB2OTg/0L0m8UusiWW5Y1XnmW7w+3pFjIxutoOnzP/YufKU2SVSaobM/D1AXFRoKAzA0/bPkE+X6Zv9j2TUzwgz2f0HJOGg3e+CkoY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549559; c=relaxed/simple; bh=EhnYH4OG1yQcNdExdTlX8DtkF4c1hxEUjRTi3VHc5ZE=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QLIBNIcox4ldU6aUiLuCA1GfnRxPhLUXXbdtFYH8CSpeB7910Pjxbm3dodqLi9l4Km/yapPGBkUIRXuC4sAQuo0c+iSrXFBHkaRcGoWZPMa2LBEYj2Iiq5/OSzzBtTIyVhnlA0JIX6iWVpiRSUs1EKYDp05gU2hoeHDkDagVz0A= 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=PHmV1+0H; arc=none smtp.client-ip=209.85.128.182 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="PHmV1+0H" Received: by mail-yw1-f182.google.com with SMTP id 00721157ae682-787cc9de986so8596977b3.2 for ; Fri, 07 Nov 2025 13:05:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549555; x=1763154355; 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=sFdqd9exB0x0hPzLUkgD+8tlEyMvj6WmHDEVMBDy3B0=; b=PHmV1+0HTM4I2RRR9QcPLEkj+0X2mOIPFW/c/iVOIQ+G6lMV+NVL/RE7ppGcfIxdhW 0U2jtlN5hjXIMoLjs9CR/aL6Qm7CtCEAOu1+ePqGvVA9m10YXnJ6rLK9gk/wqI9tLPwd nvoQsfv2n0I328Ox6vxQ665eJ1aVcMhdC+4Ui33ojb0dtauuJ+lg2sL6oLulnE1s6FQi xK+dsyDjzxu8D+irAcaw+oX7xa5M0NzXzQ/ZWqFxQ0Hm9P+3NCCwlTtmgvsxfYoYefqF PUwoxvORZLZcZM0CpAomnD8/VOQViGHv3LutaAjgIep/8BOrk7no4IInRZMBmqYBqC8C aNeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549555; x=1763154355; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=sFdqd9exB0x0hPzLUkgD+8tlEyMvj6WmHDEVMBDy3B0=; b=MC9/JUwx5HPyrT0kF9gcKyEiW4EHplGHkVYE84w75nPx18ZlM/8XkutOeENR3SfbZo tyi79eo/pwamYXnnZaoHKCwcWKgzjcckujQIMxBsoSzUrpxuIYXEpT/B2bYOTvTizN1m rmgpSWKYS0cIKpiOeOVWx6afHlNkTO/ivkYRg7tCc1SILDfxz6nFPn1QLOe5EqSKHRKN juqe8FRIcelJwwuGSR/oyAjvkbLDcMwv5OAYpJzb3LDJH0wGCB0Oh7oVXw4gIQqeEaQh aqK0nydlwsXlJXg+a85WFIkfOI+Zrk11q1RVmO/7Rm4f1o8AsMfvr6dIi5Lm4J/hJNpr 6bOg== X-Forwarded-Encrypted: i=1; AJvYcCUFiaXOhHJrJLbrAhgO4bavXQk2txY/N+3a3aOmzZDHzHUG4PLN17ljW+HMROQ2xOfW0yUm6S7dp3K92pk=@vger.kernel.org X-Gm-Message-State: AOJu0Yyx1pfKv/doD8aagdFFLLLYq/6JbCTrhadw186Z4h1yYekSh+AU ez6j/yPn4V7ASUW8krguH1YwmkPPdXTxZbhtz3w0zrgVY7WDkSBwKSTToZQmKjLOaPU= X-Gm-Gg: ASbGncvauEdPaenmTjmNYylok4yeThyhV7piazU3SrUXwFRdMAVAEyGr9+jmHoqEXYa 7yuGY9T1KuqjRvXh0irAAlbxPiOMMjhijFWZAem2xkR+DUGjkIGwX3akM0aiTNfWowei9RMy4mY rGTry2dXyxPDnECPGfGB9zZf6jM0Of4ZCyBRVtYgzKuP+hy3sJLZI8AYF2KR3rbq5klJ2XtpqJE +mGilL3K0gIUiLz+mqDfKzmbItiPqK4xgKvPEbu5QW0k/J0/1gUkW/JUwbgKOahKl64by/rHuwa Raq7eb1yVbENrTjh+8hi6CJrR7W8sfbl7IHocqXPmFiiPD+mltyypbAQqMcDR2Co4L3ODJn1r6M KjOpIeb5CANN/A24f2PsxqPzdh/p21c48aLDH6P9hGN9AKzRPCboUWxWAbIV25FhrbFmORO8szt 59xAqlluQSJHLesluWzvHZYNuvD5Dloc1Cu8brumqa114QvID1L7fsu70SDyO6buCLAy9qDFL7f Q== X-Google-Smtp-Source: AGHT+IF010PbDrS6Jp4mBjzKbhPzLzB8jkjc+eSVHmds1J2sDdVJ/oNxdwOHlro9GSCJe4RgFsfJuA== X-Received: by 2002:a05:690c:c004:b0:786:4860:2226 with SMTP id 00721157ae682-787d5400978mr6003887b3.32.1762549555336; Fri, 07 Nov 2025 13:05:55 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:54 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 12/22] MAINTAINERS: add liveupdate entry Date: Fri, 7 Nov 2025 16:03:10 -0500 Message-ID: <20251107210526.257742-13-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" Add a MAINTAINERS file entry for the new Live Update Orchestrator introduced in previous patches. Signed-off-by: Pasha Tatashin --- MAINTAINERS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 58c7e3f678d8..11b546168fb1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14512,6 +14512,17 @@ F: samples/livepatch/ F: scripts/livepatch/ F: tools/testing/selftests/livepatch/ =20 +LIVE UPDATE +M: Pasha Tatashin +L: linux-kernel@vger.kernel.org +S: Maintained +F: Documentation/core-api/liveupdate.rst +F: Documentation/userspace-api/liveupdate.rst +F: include/linux/liveupdate.h +F: include/linux/liveupdate/ +F: include/uapi/linux/liveupdate.h +F: kernel/liveupdate/ + LLC (802.2) L: netdev@vger.kernel.org S: Odd fixes --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f52.google.com (mail-yx1-f52.google.com [74.125.224.52]) (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 928D1341642 for ; Fri, 7 Nov 2025 21:05:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549561; cv=none; b=aT77Or1/9XLbdUtUdHZ5zwnjFA18BDkJdl5fNRYAvCv277AEgaJQFxCXZvoCpoDLl6nXzVS4GBjZ9E3cZ8NAL2PCRhu4ktTiWhiwSQ2rLkfoL2vPsnXsOss9WEeWpfp9FbMGg79nzdYPC1v4cRPl56Gt3DfNU5hWXycj/Uuvk+U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549561; c=relaxed/simple; bh=OQDXqRJEO4eJVEoDOo87c6PBDuVjCEXxPA4VkxAZLEY=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ojqN9bIitUvEFXIaUq0ZhqRCchTJCx/MmBboUn/0A8H4TyZveYu4Q8cFBlQSP1OI1pYSItaSagetWRRhuutRhOBtQ4BivOaVWKjuc3wJtpBzOW2MMOvRyA9KVf/H+ZuXKByuXLNuSj8rly1NXGqLppoWPm5jYSHPXidhzevKiF0= 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=hh/RsXVr; arc=none smtp.client-ip=74.125.224.52 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="hh/RsXVr" Received: by mail-yx1-f52.google.com with SMTP id 956f58d0204a3-63f96d5038dso1192208d50.1 for ; Fri, 07 Nov 2025 13:05:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549557; x=1763154357; 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=Hhpreoxnn+nlJNzkmlMtkdI0F+At6xhejoNztjrsnbs=; b=hh/RsXVrZjbaHBkByitZJZVc3aDqbcYzGZTsx3ZpP8ymkkh1m/Tk2ncsz9wmhka5fU s1PCyuLMIweCiJKAoBUhxyqTs1ezQFhcCnhKeKnaqSqha9+zUSOMSsKa2d08aKsphAV7 FxoQ0SmGD23EPtKofv76n8ObNkbHIlx9ZHoN9bVlmfys/GOgLepiIjORIqHRJPHOaydK t8ALay7cTzU2EJ2DTeA71JiGnhavZU/NYz9k8qlgTfcxaGuTnYTiBlpXH2BsJjqNfNKQ /BOl1Usf+7Eu9i7c9+KJ0QTjoUr//LLIzicC8b3TUNYuLXhnS2Mz2LkXEcXrcAHhpNPF bwTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549557; x=1763154357; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Hhpreoxnn+nlJNzkmlMtkdI0F+At6xhejoNztjrsnbs=; b=P4M24vHUHmGg2z0FGQIxpUBpFPhmFbWPU+wLa3g37qLME5Md39Kdu8c5fZ66rZz/ZQ Yx/rrE/vXPhF+lbrK9v8qTjP0i4bIJVXFTS4Rr2DHGz69oZBG+gFjHuOmTj2tKFPum4r ZSi3y/4NkAMvojrRf3z7d+3eb3budctN7P4vj/J3C+CQIHq7tmJk7w+1M0n4L+ndJnt0 Pbrpukoek48yZAYcdxkAHujChECXSN+j6t5BORbDbqtN4mcrA1e5vkPAKK0bhVrqs0yj NsTgXk0W6/tA6Wif7xtw58E/fB/4Pycx7XnnAd5QSR3VnEppgOzty8iyTLxfUZyWBtUe KOaQ== X-Forwarded-Encrypted: i=1; AJvYcCW2c55XFP0B/aYT4CfGSrYe/nv09BGrpbcuvhLkEYBjX7ddXHywztCIeTpPk3DX5jcr0vcX4WOOrBg8iXU=@vger.kernel.org X-Gm-Message-State: AOJu0Yyaci1SvaWAMEocGF9E+JHVwlvR08F5zYdNkZEQ8kZ2JthpoOXY 2ei11hzHUX2oCkBXDDNICw2KzSWvib40j80kyX4+MZPVyYynr4W9Q1PKgQVEPBfUz2o= X-Gm-Gg: ASbGnctqKhxgrND0s8o52BIYUqlIvf1lGXep4xDx9UB2fgTHNsT4rquL1bYNGldLpmD puGTMt4JSf438ETxT09ci3b1Cs18C0NMvKgl+MzGKHC5+DmUtF+lFNbMu+fW07zuvKNxdI868c/ cVSLkdLboAb+HHTR3mk3e661IqN2+ZgCx9M1Z3mXvc5gQqFjg2d8ma9M+ttr0ByBqIfPKMuS4ie ojmEqgkw8JLe94J9MQpCpW1ryhkCTaaoCgy71j1gFoK/lQaD9flAeKyi4n8gFnSO3fGFqdD3ct4 yxnMESDXm4orzDf0ll+j3Rily4dk4CWQOhnHRlA+BxWXBq6pMJXd5kffcEVA/NPiryt/uG8oxEm zopmn6aAM3AZWjFkpTXLkIempTdprlCBafudZ4tWurcSAIUddSakzJPQHrPP6aQbNR744EhzWOV x5wG8lG755FUOQrVjGgl7HpL5AjpKf/MDnosmVUp8nhB1/X8TwZ123xL3Z8xql9m0= X-Google-Smtp-Source: AGHT+IHM85YUuXPyVoM2odrjHYP+mwyWLe1kB8N8z+ntfjkrejgyHe3L+oNXvJsvcedHEWGfvX9bBw== X-Received: by 2002:a53:b113:0:b0:63f:31f7:b956 with SMTP id 956f58d0204a3-640d455a4b6mr353591d50.27.1762549557262; Fri, 07 Nov 2025 13:05:57 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:56 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 13/22] mm: shmem: use SHMEM_F_* flags instead of VM_* flags Date: Fri, 7 Nov 2025 16:03:11 -0500 Message-ID: <20251107210526.257742-14-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav shmem_inode_info::flags can have the VM flags VM_NORESERVE and VM_LOCKED. These are used to suppress pre-accounting or to lock the pages in the inode respectively. Using the VM flags directly makes it difficult to add shmem-specific flags that are unrelated to VM behavior since one would need to find a VM flag not used by shmem and re-purpose it. Introduce SHMEM_F_NORESERVE and SHMEM_F_LOCKED which represent the same information, but their bits are independent of the VM flags. Callers can still pass VM_NORESERVE to shmem_get_inode(), but it gets transformed to the shmem-specific flag internally. No functional changes intended. Signed-off-by: Pratyush Yadav Signed-off-by: Pasha Tatashin --- include/linux/shmem_fs.h | 6 ++++++ mm/shmem.c | 29 ++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 5b368f9549d6..710b1dd681bf 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -10,6 +10,7 @@ #include #include #include +#include =20 struct swap_iocb; =20 @@ -19,6 +20,11 @@ struct swap_iocb; #define SHMEM_MAXQUOTAS 2 #endif =20 +/* Suppress pre-accounting of the entire object size. */ +#define SHMEM_F_NORESERVE BIT(0) +/* Disallow swapping. */ +#define SHMEM_F_LOCKED BIT(1) + struct shmem_inode_info { spinlock_t lock; unsigned int seals; /* shmem seals */ diff --git a/mm/shmem.c b/mm/shmem.c index 5c07e2b57c34..19be8c575647 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -175,20 +175,20 @@ static inline struct shmem_sb_info *SHMEM_SB(struct s= uper_block *sb) */ static inline int shmem_acct_size(unsigned long flags, loff_t size) { - return (flags & VM_NORESERVE) ? + return (flags & SHMEM_F_NORESERVE) ? 0 : security_vm_enough_memory_mm(current->mm, VM_ACCT(size)); } =20 static inline void shmem_unacct_size(unsigned long flags, loff_t size) { - if (!(flags & VM_NORESERVE)) + if (!(flags & SHMEM_F_NORESERVE)) vm_unacct_memory(VM_ACCT(size)); } =20 static inline int shmem_reacct_size(unsigned long flags, loff_t oldsize, loff_t newsize) { - if (!(flags & VM_NORESERVE)) { + if (!(flags & SHMEM_F_NORESERVE)) { if (VM_ACCT(newsize) > VM_ACCT(oldsize)) return security_vm_enough_memory_mm(current->mm, VM_ACCT(newsize) - VM_ACCT(oldsize)); @@ -206,7 +206,7 @@ static inline int shmem_reacct_size(unsigned long flags, */ static inline int shmem_acct_blocks(unsigned long flags, long pages) { - if (!(flags & VM_NORESERVE)) + if (!(flags & SHMEM_F_NORESERVE)) return 0; =20 return security_vm_enough_memory_mm(current->mm, @@ -215,7 +215,7 @@ static inline int shmem_acct_blocks(unsigned long flags= , long pages) =20 static inline void shmem_unacct_blocks(unsigned long flags, long pages) { - if (flags & VM_NORESERVE) + if (flags & SHMEM_F_NORESERVE) vm_unacct_memory(pages * VM_ACCT(PAGE_SIZE)); } =20 @@ -1580,7 +1580,7 @@ int shmem_writeout(struct folio *folio, struct swap_i= ocb **plug, int nr_pages; bool split =3D false; =20 - if ((info->flags & VM_LOCKED) || sbinfo->noswap) + if ((info->flags & SHMEM_F_LOCKED) || sbinfo->noswap) goto redirty; =20 if (!total_swap_pages) @@ -2938,15 +2938,15 @@ int shmem_lock(struct file *file, int lock, struct = ucounts *ucounts) * ipc_lock_object() when called from shmctl_do_lock(), * no serialization needed when called from shm_destroy(). */ - if (lock && !(info->flags & VM_LOCKED)) { + if (lock && !(info->flags & SHMEM_F_LOCKED)) { if (!user_shm_lock(inode->i_size, ucounts)) goto out_nomem; - info->flags |=3D VM_LOCKED; + info->flags |=3D SHMEM_F_LOCKED; mapping_set_unevictable(file->f_mapping); } - if (!lock && (info->flags & VM_LOCKED) && ucounts) { + if (!lock && (info->flags & SHMEM_F_LOCKED) && ucounts) { user_shm_unlock(inode->i_size, ucounts); - info->flags &=3D ~VM_LOCKED; + info->flags &=3D ~SHMEM_F_LOCKED; mapping_clear_unevictable(file->f_mapping); } retval =3D 0; @@ -3091,7 +3091,8 @@ static struct inode *__shmem_get_inode(struct mnt_idm= ap *idmap, spin_lock_init(&info->lock); atomic_set(&info->stop_eviction, 0); info->seals =3D F_SEAL_SEAL; - info->flags =3D flags & VM_NORESERVE; + if (flags & VM_NORESERVE) + info->flags =3D SHMEM_F_NORESERVE; info->i_crtime =3D inode_get_mtime(inode); info->fsflags =3D (dir =3D=3D NULL) ? 0 : SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED; @@ -5833,8 +5834,10 @@ static inline struct inode *shmem_get_inode(struct m= nt_idmap *idmap, /* common code */ =20 static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *n= ame, - loff_t size, unsigned long flags, unsigned int i_flags) + loff_t size, unsigned long vm_flags, + unsigned int i_flags) { + unsigned long flags =3D (vm_flags & VM_NORESERVE) ? SHMEM_F_NORESERVE : 0; struct inode *inode; struct file *res; =20 @@ -5851,7 +5854,7 @@ static struct file *__shmem_file_setup(struct vfsmoun= t *mnt, const char *name, return ERR_PTR(-ENOMEM); =20 inode =3D shmem_get_inode(&nop_mnt_idmap, mnt->mnt_sb, NULL, - S_IFREG | S_IRWXUGO, 0, flags); + S_IFREG | S_IRWXUGO, 0, vm_flags); if (IS_ERR(inode)) { shmem_unacct_size(flags, size); return ERR_CAST(inode); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (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 4A390343207 for ; Fri, 7 Nov 2025 21:06:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549562; cv=none; b=O8HFRg59hCjIq6jENoXBbHDHowhUZZCwR95KJPxEw0JtUnYAkcxbWHJYJMNsZE1ZFR713V0E08TQpqmuQYnqHLI/F3iWmRptN+wr6fH8czqFzfDNZyjXaSW2bcXPYuDfhNw0mb6JHvg73/qzjrDCdGCMFpTAQB5CGzyK7EnKIiM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549562; c=relaxed/simple; bh=UdO7PQgJIrKwVS+KHl4+e5iRRpO/7jhlAMj+9Y3xYzM=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ngheTueqEDzw+T0gkASRY3Lo0KBGWgHDEueFldf0xv+G3IMFCrtfYGjRJW7q4dPtA/ZzzPGoE/i3OepIsYpCZkPleRyk0Ae1PBQrgD+hk6dQdUaFHvUAsfPuezseKYGyrIAbjYj8SiuuqKf/ysjB4F5J3AAYpFIKSPmzQgtiFhI= 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=kxI9holv; arc=none smtp.client-ip=209.85.128.171 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="kxI9holv" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-7866dcf50b1so11450167b3.3 for ; Fri, 07 Nov 2025 13:06:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549559; x=1763154359; 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=pBi03kAb5JgjRparbbJCSt/bY+/4eC+JKPe6vY7ZjAQ=; b=kxI9holv6YqqZ+dIFBnm35F14UXP51jTNA2oeXQFA//v2Y6vplFOnq8gXoLNFUFE3q P2xSmDfMQW79Y2bDB4yxvBj5Q48HiYinsHdLuZA6phrojcg1xO7UUUpm/GeKGN++Hp6h q0Qtin/mGe5xgFBsyVjgNdbf4vUV3uUZ3i7mE84JEuAFPiGDkY7aHCWNTA8nJjqHMPbC XBiry75+QC8RMyTFwxI5FisZvGprfQWH1Nkhjgv7x+/xsFHB+FXTVQhA6gt3nsE7a4Am c4tjSJu867NQf6Jci3lsfEfNgOlAllyyZxYbBBUhwaraen14jT2fbtALvO9EJm9yps7m BPwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549559; x=1763154359; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=pBi03kAb5JgjRparbbJCSt/bY+/4eC+JKPe6vY7ZjAQ=; b=vUQMV9qDCU4b8IMte/tkEY+5x5e2fhywATPJa/qYCyl5OvL909LfNmybeQfIK6tTcz w5WSIiHHQ6UNf6EEUf2HHVIUcE545YlQ++g/noZ3QeCCCXHM9ci80pryQvTa4kH5UJyg fQnY2mcOXCPYJDJes/EDRW8C4A11NEqYW539WS6ujWLFKcKuSQmmtNnMnWEdKb2YUMD1 I16opV2qipHOuVdXc0DMuRv0kYx9yUNqmFxseeuG4jO56yoo6dsstrWlFPAyyKeDXVF5 4LznkzcZwiyxSYlAjDXZqf4POhsmHopuCDuEMU82gVw/0gFk3VnHNpPMbzzS0ntc7E7w GMiQ== X-Forwarded-Encrypted: i=1; AJvYcCWOHkUhpJqylDUKeoQ2fMs0a7+xKrinxv2jydT7jZXgVODwAB75fvI23hjznGoUYosaYAsogJM+d+R88cE=@vger.kernel.org X-Gm-Message-State: AOJu0Yw5G5gbwshAwYKyYi072jCt/2ea9orEizHbi4lvR/acRbbV2sws elJQ3mLaQY2jM411jC/d7Z5y1HbrDOBykvEn1d6iXGV0x8vqNDfPBRFQVYkcoVevdnE= X-Gm-Gg: ASbGncvugHKkpHReMB9I52hzaFv11FtbtRUb+Nd7ibQtPlFilt73yKP09ZIHf/ALMaX t3L985jPGg4ONMBDOWOZ2TYqqXE4qG6m+Qjnf18hsqviRQyPQVTnWbBCLNeHCvwIbsrFw6xbWtI Y2h3+PNoXKAyA96CuHBygP3fqcPleerNcIDlmzukRaLq/XJKAPfdTxxe61SXLKAplw9ZLXOxBhk xnTMk6gGknB2Y7Wec1ydooUQCKwdfHyDi1SHyo8YvagylKPPiSsItVjvathKTPOReu0D49C3SWW bcdeAearagHf4MrpmzegklfcfyJGHvk2J4ITzxtcdPZ60tzwmuFSeSUsv7LKYLF2Nx//CB3Qvox 1A5JTdoQ0K+9KgAxIseB4Nz4QSwmFctFrY7Ip0iQAEiDB981hWGfkNkNTdzCJjNKb3RqFxBhDMH khYIpkGnmOzuKRiSZqLQA07BVK7NkD4uMLvJXnfLxnRVwjWLFoF9Fe9TdltqojmSqsJp80ssq2+ g== X-Google-Smtp-Source: AGHT+IF+XOrK+gByf7DQ1+IWSR7ui8MkgfcgK6h+y28DPkokKVkVXPGBOJn/sPaYBxN5KAaR0pxqSw== X-Received: by 2002:a05:690c:760a:b0:787:cde8:b3d5 with SMTP id 00721157ae682-787d543da03mr6084207b3.52.1762549559282; Fri, 07 Nov 2025 13:05:59 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:05:58 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 14/22] mm: shmem: allow freezing inode mapping Date: Fri, 7 Nov 2025 16:03:12 -0500 Message-ID: <20251107210526.257742-15-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav To prepare a shmem inode for live update via the Live Update Orchestrator (LUO), its index -> folio mappings must be serialized. Once the mappings are serialized, they cannot change since it would cause the serialized data to become inconsistent. This can be done by pinning the folios to avoid migration, and by making sure no folios can be added to or removed from the inode. While mechanisms to pin folios already exist, the only way to stop folios being added or removed are the grow and shrink file seals. But file seals come with their own semantics, one of which is that they can't be removed. This doesn't work with liveupdate since it can be cancelled or error out, which would need the seals to be removed and the file's normal functionality to be restored. Introduce SHMEM_F_MAPPING_FROZEN to indicate this instead. It is internal to shmem and is not directly exposed to userspace. It functions similar to F_SEAL_GROW | F_SEAL_SHRINK, but additionally disallows hole punching, and can be removed. Signed-off-by: Pratyush Yadav Signed-off-by: Pasha Tatashin --- include/linux/shmem_fs.h | 17 +++++++++++++++++ mm/shmem.c | 12 +++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 710b1dd681bf..08f497673b06 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -24,6 +24,14 @@ struct swap_iocb; #define SHMEM_F_NORESERVE BIT(0) /* Disallow swapping. */ #define SHMEM_F_LOCKED BIT(1) +/* + * Disallow growing, shrinking, or hole punching in the inode. Combined wi= th + * folio pinning, makes sure the inode's mapping stays fixed. + * + * In some ways similar to F_SEAL_GROW | F_SEAL_SHRINK, but can be removed= and + * isn't directly visible to userspace. + */ +#define SHMEM_F_MAPPING_FROZEN BIT(2) =20 struct shmem_inode_info { spinlock_t lock; @@ -187,6 +195,15 @@ static inline bool shmem_file(struct file *file) return shmem_mapping(file->f_mapping); } =20 +/* Must be called with inode lock taken exclusive. */ +static inline void shmem_i_mapping_freeze(struct inode *inode, bool freeze) +{ + if (freeze) + SHMEM_I(inode)->flags |=3D SHMEM_F_MAPPING_FROZEN; + else + SHMEM_I(inode)->flags &=3D ~SHMEM_F_MAPPING_FROZEN; +} + /* * If fallocate(FALLOC_FL_KEEP_SIZE) has been used, there may be pages * beyond i_size's notion of EOF, which fallocate has committed to reservi= ng: diff --git a/mm/shmem.c b/mm/shmem.c index 19be8c575647..2e3cb0424a1f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1321,7 +1321,8 @@ static int shmem_setattr(struct mnt_idmap *idmap, loff_t newsize =3D attr->ia_size; =20 /* protected by i_rwsem */ - if ((newsize < oldsize && (info->seals & F_SEAL_SHRINK)) || + if ((info->flags & SHMEM_F_MAPPING_FROZEN) || + (newsize < oldsize && (info->seals & F_SEAL_SHRINK)) || (newsize > oldsize && (info->seals & F_SEAL_GROW))) return -EPERM; =20 @@ -3319,6 +3320,10 @@ shmem_write_begin(const struct kiocb *iocb, struct a= ddress_space *mapping, return -EPERM; } =20 + if (unlikely((info->flags & SHMEM_F_MAPPING_FROZEN) && + pos + len > inode->i_size)) + return -EPERM; + ret =3D shmem_get_folio(inode, index, pos + len, &folio, SGP_WRITE); if (ret) return ret; @@ -3692,6 +3697,11 @@ static long shmem_fallocate(struct file *file, int m= ode, loff_t offset, =20 inode_lock(inode); =20 + if (info->flags & SHMEM_F_MAPPING_FROZEN) { + error =3D -EPERM; + goto out; + } + if (mode & FALLOC_FL_PUNCH_HOLE) { struct address_space *mapping =3D file->f_mapping; loff_t unmap_start =3D round_up(offset, PAGE_SIZE); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f178.google.com (mail-yw1-f178.google.com [209.85.128.178]) (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 4B13C344038 for ; Fri, 7 Nov 2025 21:06:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549564; cv=none; b=Loo8xZ7pA96IGgdqYs0VzEugfnR5p+aGZ3Nx2Wu9JqePD9ahGIooK+3b3UFqTv7emH9RTk9sHzl+GWiGdXVRyHl++VUz0tN/Q96oF0gFQD/Y1o66bHksuHBNWnKsGyNiwLSwPGHt5Uzm9CxiGpD7f4xOhGPyDcUU/Jt6gEohlgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549564; c=relaxed/simple; bh=2YoVzov6bB1zBXUx8MCa0vSfim57g+g/ybqzdLdnqC4=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jJat8zKJ/eo2ccTwXmaryvbiL/wxsHSptuQF6q8IaIIRXW5U/FDA+xMhJlERlzPQltULGaJLFoptl0Iko8d36hhynqc2TEiKYJTH84POpjzvYD21W7DIGBo2yBlW8qlsi4awnuZ5/0BSthaTjRB+aNAE+1o5qyPnuZup3Vs+pUs= 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=WIaPj78v; arc=none smtp.client-ip=209.85.128.178 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="WIaPj78v" Received: by mail-yw1-f178.google.com with SMTP id 00721157ae682-7868b7b90b8so12002567b3.1 for ; Fri, 07 Nov 2025 13:06:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549561; x=1763154361; 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=7B04/0h/Qcfyqc4t6CaWgs850ouGgVZlycEHNrf/bt4=; b=WIaPj78vg695vgQwUK4DlX/6p67WRgOK7neDgslYM3rNERJhdHG6V3iAppvpCoXTmQ YvTETkOLzzTUpVKRqGJ0FQq8xkpNXICMaSHHmjFEC1R4Wo+uNOswQPK0HCBiWP/Ta6xz t6/waWvn92bKsy17K6arLVwjq2Z+Fj1rmRooOSYAxZp/6PUB8EKz7VeRTYR3qBfV8G3w KrHhQF3YDpIwYotZMcm4Mz85vIAaDcWT2IVmVeCiKb/X4XfDnJakQgvvXVF0MK+2iBkX Tofe6V17g4myb/cILaOQaOuNLKb7YXBNT0OngxZLx/Dv22Vr/WK6cE0UOjrZyMV9i0y/ 1/nA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549561; x=1763154361; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=7B04/0h/Qcfyqc4t6CaWgs850ouGgVZlycEHNrf/bt4=; b=ZqGRUTy6ipMUbtS9jz/5gvBLgV07Ig3nXbo/3Lofm/HLDlXoOJgDbiupnb6UYyD/Vm MZZ63sX2Df933C9Tks8fPQwVvWqfcsUBkcmsHpCvrbR/deK+j1Dk/IkeTS1ZJkQPmnVg PBhYk4TWAscd8KgYBPm5qNd14ekJ32q/mrTdFIQgxK3ixgGgEBoxTELOu7LeMPxuu5Zg y5xryxGUcQ0A6q0GxFlD8z5Ad9jGyOBM9SgVuuedHmdsdYaMChifFVxf+jodE4wN6Pw/ j4oGDE/qjxslWv0LRL1jaJbBw1ZtEoBgOBduXx9SU/75m9nNJ6wFLcxO/GIO/klPmj4R RMHg== X-Forwarded-Encrypted: i=1; AJvYcCWBX/NZt19Ld62prdorXargmumH3vJ0kcKX9olubCkJE+e6cH/A4RwKQtCUEg1haSQ82bazQQQhTG2a8jU=@vger.kernel.org X-Gm-Message-State: AOJu0Ywud6mXF7Oq0RTMn55H24+1Z8YMLEDeZAx8wFCyYzua59OZnzKW ht4A/SzKOvXCrk+KyudCyOOGQtYq4DrFdXhDl807WGgA0kA2wcN9OfAkAWAhkKbzGjQ= X-Gm-Gg: ASbGncuyVlGIzxM271TLd9q9JraEtTmd/kLRzcCafIHdKoZM7jGNvItkq0IU/quSB7D e/eaHiXZRAEM4NVCnva6wV8iuytUf/e9Tp0E6PFKuWKraeqN8YzJ0OC00R7iPbRcfqvOrzxjdkL Kg7L1pdko0PXfZFR3EukfmuKWgcAP7qR4MD/qM4pM6craa5ZFnZJY7R8TTbQUVGZQHWIrJe0gRj VUKgSZHIwXG6+FBOqlL3nIopGBzmMZm5bEAPFLJSa0So3UfquD8I6QDxadZr2zH0VcyNuj/r8wN 5oE2VU1ywEXfvUQhnBSGSxb5Gb1ByB+GA1HaS05aduf2hamS/vAzwIsjJwUfPb1w/Cfa3YrEirq iOkIeLiPtV8rPs3WGweksLjB1lowkDhITXxa6W/kOFA3Um2IDtP0OaENS77+1u4odRMewcq2MF0 agONl2Toq5Tgvsh3NGEd/XjHR2uhl6qXTE+WNYKHL1L+/N7dDP4m8lEFzIjK0erL7QS7g/w8T9l v4SrxIiMPfC X-Google-Smtp-Source: AGHT+IGF4+/4J86N/nzQZyRsPJ7pMtknxLR4bnE8JC68ZkTL3I3E4I5rinqYLEhJi2wbGwCzdCJ5TQ== X-Received: by 2002:a05:690c:55c5:b0:784:8153:c604 with SMTP id 00721157ae682-787d53ba2bemr6446727b3.31.1762549561166; Fri, 07 Nov 2025 13:06:01 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.05.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:00 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 15/22] mm: shmem: export some functions to internal.h Date: Fri, 7 Nov 2025 16:03:13 -0500 Message-ID: <20251107210526.257742-16-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav shmem_inode_acct_blocks(), shmem_recalc_inode(), and shmem_add_to_page_cache() are used by shmem_alloc_and_add_folio(). This functionality will also be used in the future by Live Update Orchestrator (LUO) to recreate memfd files after a live update. Signed-off-by: Pratyush Yadav Signed-off-by: Pasha Tatashin --- mm/internal.h | 6 ++++++ mm/shmem.c | 10 +++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/mm/internal.h b/mm/internal.h index 0af87f6c2889..3f957e86f43f 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1585,6 +1585,12 @@ void __meminit __init_page_from_nid(unsigned long pf= n, int nid); unsigned long shrink_slab(gfp_t gfp_mask, int nid, struct mem_cgroup *memc= g, int priority); =20 +int shmem_add_to_page_cache(struct folio *folio, + struct address_space *mapping, + pgoff_t index, void *expected, gfp_t gfp); +int shmem_inode_acct_blocks(struct inode *inode, long pages); +bool shmem_recalc_inode(struct inode *inode, long alloced, long swapped); + #ifdef CONFIG_SHRINKER_DEBUG static inline __printf(2, 0) int shrinker_debugfs_name_alloc( struct shrinker *shrinker, const char *fmt, va_list ap) diff --git a/mm/shmem.c b/mm/shmem.c index 2e3cb0424a1f..c3ed2dcd17f8 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -219,7 +219,7 @@ static inline void shmem_unacct_blocks(unsigned long fl= ags, long pages) vm_unacct_memory(pages * VM_ACCT(PAGE_SIZE)); } =20 -static int shmem_inode_acct_blocks(struct inode *inode, long pages) +int shmem_inode_acct_blocks(struct inode *inode, long pages) { struct shmem_inode_info *info =3D SHMEM_I(inode); struct shmem_sb_info *sbinfo =3D SHMEM_SB(inode->i_sb); @@ -435,7 +435,7 @@ static void shmem_free_inode(struct super_block *sb, si= ze_t freed_ispace) * * Return: true if swapped was incremented from 0, for shmem_writeout(). */ -static bool shmem_recalc_inode(struct inode *inode, long alloced, long swa= pped) +bool shmem_recalc_inode(struct inode *inode, long alloced, long swapped) { struct shmem_inode_info *info =3D SHMEM_I(inode); bool first_swapped =3D false; @@ -890,9 +890,9 @@ static void shmem_update_stats(struct folio *folio, int= nr_pages) /* * Somewhat like filemap_add_folio, but error if expected item has gone. */ -static int shmem_add_to_page_cache(struct folio *folio, - struct address_space *mapping, - pgoff_t index, void *expected, gfp_t gfp) +int shmem_add_to_page_cache(struct folio *folio, + struct address_space *mapping, + pgoff_t index, void *expected, gfp_t gfp) { XA_STATE_ORDER(xas, &mapping->i_pages, index, folio_order(folio)); unsigned long nr =3D folio_nr_pages(folio); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f44.google.com (mail-yx1-f44.google.com [74.125.224.44]) (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 3A44A3451C4 for ; Fri, 7 Nov 2025 21:06:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549566; cv=none; b=gpyXACbRQUgg4f9Aa+7d3HI5/5Hx5W7QKy5JAQ6ffEhDakielQW9CibZUfEA5rhbZoNkvIFlucQb05KWkjLKR33exqOoz8jaf5KtRJeYqUBhXg5eMvJM11pPuruNY26d4if10h73EtYv8/UaZ69RvR/TtAkx7W4nwacajp8fXqw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549566; c=relaxed/simple; bh=gBYU5h2sGDvuxmfL4Z2QJsnaixb4prWMGY/kIMgQSKs=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XuDDL5b+iiLUCFpW6Tw2dLWjM999JECR5XjdIn2sBDcJN/pPh5+WJAoKebYvCvtApqa/BFSyrcC5qXkk0gFDXqCm6p2W/92e580DBIXt0I09+j6uBdjZvmPcWzea21hgM6VLzGWCy4cWeV9UOhva3i+u7ewiwhZpT4bxiID3iFk= 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=dkaNzuWR; arc=none smtp.client-ip=74.125.224.44 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="dkaNzuWR" Received: by mail-yx1-f44.google.com with SMTP id 956f58d0204a3-63e393c4a8aso1016012d50.2 for ; Fri, 07 Nov 2025 13:06:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549563; x=1763154363; 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=kO0F0lPOuqRuP1r+X7yx5ua65c5v8nyw+1ERu/tsRgQ=; b=dkaNzuWRbi2KA9O7G1cE8o4AlQpH8KyXcDKyriGVvGSJCChqK3r/tMNiz3wfnpqhGC 86RV5evUXkXBgigC22waRO3uHc1gZ5xqxfv4G4Mh8XUSJgcD5hGR0GHzwv9UmkbVJjcl SBsyLpnZHmm/gp6jFd6QeKgPnUKgYTKirT1kNyzt9nS4mEGnI+6szZgJl1Y/54dKl0gO Sb/GAoPWrcIx/p94TaUGnLVYf+D6DgcEe/Eim2rtzeyaJCoWUzBx2BJUrk2OAkmU15Qc 6p42DZWzG5Ls6mcMhIFNqc7BwPJNOWci+dQZOIKPUDlbWN5TjTnwytGvJE1wBnvzou3U bsXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549563; x=1763154363; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=kO0F0lPOuqRuP1r+X7yx5ua65c5v8nyw+1ERu/tsRgQ=; b=AmOQIYgcTiANyt3k+2dq5GePS3NL7koSN/INmXN9HxS9Kg8x7UBvJBFaJj10lNjWJx QVkgOUd2cmyO49sSWVRzDkjtTV1Sdc/NjIGJYFTgsTcJ45g03vEScSa5eVGKDKwASf3l yc75pWTKcWQH28TogO9Cikea+p9l6nv0t4iGijyt0GauGv7ypp2vxxPDvPja/cfIiOJq 3GUA9KElGO8hm6hLcbrIBB5NL03yDY6Hlr8HKXkW3s+M0MBmEIVZbecVzFoSkGq+yRKY U1vPAcKde5EEJpN55kuy5A3YV8YdQXG+U7JcbaQesKtReTfwD8NQPNgUvqOyHCuxJbRh ojMQ== X-Forwarded-Encrypted: i=1; AJvYcCVr/0D7Drf1gF4PH8YEEZ/I8XfQbQsyIN9MlcajikNhibmXL0WNX65n+zK4gI2wzfE7eTwMXUBr/XO8t8w=@vger.kernel.org X-Gm-Message-State: AOJu0Yw5yykAzyNR1V4ZhixED8UoEd27f3AK6CKpdtjYZAz9GXveYSoi lbH0dSzyKo2Ca+VuMuQG9RxbzdaeP11W1U4b2M4dB1peU0R5AKhJz9xOsaLkgM1XW9I= X-Gm-Gg: ASbGncuIi2yR74X+Tz+h24x3O50eSkpIEwDm6zXM8LfeIn6Gky/I0R8H8UUGaqXPqSD kmadu/K4Az9tp9fVojPXw/qhMpmwDEd7sI8lCcZ8kyfQ5Hqj+rRERjxxjnjksHPXogEKpQlAiYA mjBQVvKxNysWABa2l09V1iNU60z9aEZK5MmRAOuHlX9oORK/sqZRyEJE21DM5Q8gTbYenwHukvb 20Yc7wSF/PI8xA4qSCrF88Rh8UORvvHJ2poABMdyP4Sdj+4FUWLKwrOQTq2hoKngsSKQVadDJrD N9zt+KXIqB325YmWEAAjmHoiM27g3GABuSxH7qAzZsKe43+ghNrWYn/LLbeGXCEncegKL8M3vxd ZKEbJjslgSwAPGrj19g7k49eIydCcmBb/+6dlb9sarnPIVWu2LXJ5nBTKRwLBjHDjXJgEPxEYgT MJj6XxOxpmFRKH3MUVkLuOpzi9cR8DaQ7ZFNqd5iZxkRl8DrOcVp2WqE+1UaxQruk= X-Google-Smtp-Source: AGHT+IGUrCGSnQ3TU6q4/qNhItgcbFdsWINOhF2gqc7ZvS0Ysq3WXcStnQvjg9cKvk8ErPoxGLBe8w== X-Received: by 2002:a53:d005:0:b0:63e:10f1:28d9 with SMTP id 956f58d0204a3-640d45ce885mr343426d50.57.1762549563137; Fri, 07 Nov 2025 13:06:03 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:02 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 16/22] liveupdate: luo_file: add private argument to store runtime state Date: Fri, 7 Nov 2025 16:03:14 -0500 Message-ID: <20251107210526.257742-17-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav Currently file handlers only get the serialized_data field to store their state. This field has a pointer to the serialized state of the file, and it becomes a part of LUO file's serialized state. File handlers can also need some runtime state to track information that shouldn't make it in the serialized data. One such example is a vmalloc pointer. While kho_preserve_vmalloc() preserves the memory backing a vmalloc allocation, it does not store the original vmap pointer, since that has no use being passed to the next kernel. The pointer is needed to free the memory in case the file is unpreserved. Provide a private field in struct luo_file and pass it to all the callbacks. The field's can be set by preserve, and must be freed by unpreserve. Signed-off-by: Pratyush Yadav Co-developed-by: Pasha Tatashin Signed-off-by: Pasha Tatashin --- include/linux/liveupdate.h | 5 +++++ kernel/liveupdate/luo_file.c | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index ce39b77c89c3..3f55447d18ab 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -29,6 +29,10 @@ struct file; * this to the file being operated on. * @serialized_data: The opaque u64 handle, preserve/prepare/freeze may u= pdate * this field. + * @private_data: Private data for the file used to hold runtime state= that + * is not preserved. Set by the handler's .preserve() + * callback, and must be freed in the handlers's + * .unpreserve() callback. * * This structure bundles all parameters for the file operation callbacks. * The 'data' and 'file' fields are used for both input and output. @@ -39,6 +43,7 @@ struct liveupdate_file_op_args { bool retrieved; struct file *file; u64 serialized_data; + void *private_data; }; =20 /** diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 7816c418a595..713069b96278 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -125,6 +125,10 @@ static LIST_HEAD(luo_file_handler_list); * This handle is passed back to the handler's .freeze(), * .retrieve(), and .finish() callbacks, allowing it to tr= ack * and update its serialized state across phases. + * @private_data: Pointer to the private data for the file used to hold r= untime + * state that is not preserved. Set by the handler's .pres= erve() + * callback, and must be freed in the handlers's .unpreser= ve() + * callback. * @retrieved: A flag indicating whether a user/kernel in the new kern= el has * successfully called retrieve() on this file. This preve= nts * multiple retrieval attempts. @@ -151,6 +155,7 @@ struct luo_file { struct liveupdate_file_handler *fh; struct file *file; u64 serialized_data; + void *private_data; bool retrieved; struct mutex mutex; struct list_head list; @@ -307,6 +312,7 @@ int luo_preserve_file(struct luo_session *session, u64 = token, int fd) goto exit_err; } else { luo_file->serialized_data =3D args.serialized_data; + luo_file->private_data =3D args.private_data; list_add_tail(&luo_file->list, &session->files_list); session->count++; } @@ -354,6 +360,7 @@ void luo_file_unpreserve_files(struct luo_session *sess= ion) args.session =3D (struct liveupdate_session *)session; args.file =3D luo_file->file; args.serialized_data =3D luo_file->serialized_data; + args.private_data =3D luo_file->private_data; luo_file->fh->ops->unpreserve(&args); luo_flb_file_unpreserve(luo_file->fh); =20 @@ -382,6 +389,7 @@ static int luo_file_freeze_one(struct luo_session *sess= ion, args.session =3D (struct liveupdate_session *)session; args.file =3D luo_file->file; args.serialized_data =3D luo_file->serialized_data; + args.private_data =3D luo_file->private_data; =20 err =3D luo_file->fh->ops->freeze(&args); if (!err) @@ -403,6 +411,7 @@ static void luo_file_unfreeze_one(struct luo_session *s= ession, args.session =3D (struct liveupdate_session *)session; args.file =3D luo_file->file; args.serialized_data =3D luo_file->serialized_data; + args.private_data =3D luo_file->private_data; =20 luo_file->fh->ops->unfreeze(&args); } --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f49.google.com (mail-yx1-f49.google.com [74.125.224.49]) (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 6BDFC32ABF3 for ; Fri, 7 Nov 2025 21:06:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549569; cv=none; b=sYX8xXBbxaXjtTmTwgqj8jTMG9emUVJ3LRYFcEk+B/8OGmC+TrQiA2biRcEGKpuxYy563hMjLlXlhKve91G5rVy8N1we++CyhaXBwAprwFmfinxaiHZg3T9xqxrqDIQwwJVUOCdNu19O9HKD8iudGX/Ja+391sRv1FoVaqg0sqs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549569; c=relaxed/simple; bh=EjQ8peAIc9yOOF6qNCxFHDArIE9acUxfCw5wzzZBPRw=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sEhXBlUCPnwPlon5UW13IsIiq0O1UJygxFxKiZBGtX/aV+DhE0UeA4xJwSJzZ3Gvub+KzSYQA8yrURLDabKatMM0qizFoSXRl094O0LOnHeXSsjMBiKJw/rZ3ziMikiEHPvs/wrJC1nHF/RJTbAYf6SybLIwuoKCuYjzHWIooRg= 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=jYp1oEsv; arc=none smtp.client-ip=74.125.224.49 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="jYp1oEsv" Received: by mail-yx1-f49.google.com with SMTP id 956f58d0204a3-63f97c4eccaso1196795d50.2 for ; Fri, 07 Nov 2025 13:06:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549565; x=1763154365; 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=09YtFNBuBcrdwWRDfQe183nmO/j9JR0mycDV+z3OXls=; b=jYp1oEsvwiMVCppoMBjyctPF65EBEQIeY8ZleBIsO/Ztlh7muD07Nj7oLhZ6V1bOfH RkxfqmKk/a9bM2H9lUZmmlcmxtj0rnBaabvEuyYoLpZieEIDJDwv/S9kjQsobMoxnn56 w9HRrSQkZz0Zxidc1+T/LMNw4pFOE6QI1xA8ZsuNE3jQDdyDPzjMHYlvmNfY67LfCXJ8 6ORaEwG4hWwu79W+WZLUhyusuXsrx/Ff/JUmTSznTAlduqBk8b8wbSRuzylxxB9JgCk5 X5hRZyVhetRLdrOFe1P0Gb8bjN7AsxgIovMVtt2jMOZqSn2vwf3eER+Bl9RLeQJZxcRd e3JA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549565; x=1763154365; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=09YtFNBuBcrdwWRDfQe183nmO/j9JR0mycDV+z3OXls=; b=Mv6/S2xq7Y7rNrXVcsNZueIVmOB0u+xS+WBIqmCP57QEIAh9ymgWzcwSAI3aCJ00+p ZcfiiVX2Djg9oQ+m8/BPVqq+1t11qgqpiLx8abudwu0xoxnDU1ExKY/85bp4rgD0GFDY UgeCMgvqcLWVsPia4A2B/sul9LqSATxfZ8Homp7OyN1aO5WCClPGxIpV0NTyMlZcAfwT 4KBeIxiXTyp6ql7owF+eghn+gju90jtQeTl0ZTSbyso1uqc1EbP+tYRqhRc9Py/b0TFu elLF5Kn13Xq9ihyoUVJqqlhDf+EzGZ3uv0VWdSQy/jgwRTZqXIcuAJcPTfG2Ks5CKVWu fijA== X-Forwarded-Encrypted: i=1; AJvYcCUcqD0vkEHZPoZePVJiB5Q62gN6WQb05Mpbp33dqk9AXuRk1qD1yYHOheuQqkB9HsM06+3UACidNrCiOFA=@vger.kernel.org X-Gm-Message-State: AOJu0YyVDu3Y877Ul52tvqucyCnRAyoqnQ1LgO1uIA3HYFFjfo7bfo0k Y2uZ2QyGO6ZpsY2/DvYhapDhpywEB8qBkkgql83J5q+NK1DMI2hN9O2GfFVtqq1RS6g= X-Gm-Gg: ASbGncuJ+LKEXHB8fudst4bIHL6Zr7zPRY535kqwPbtwfeQOiZkOUwjpykSXnMUfbRW BeD/Uqu8HOpj1/ZH9Ferlca+juTsGFcpqeAZy0hx4pfaumpIdcGxG9t+UtzleIkLUUP7M7oafmr +k/1lU8ZM0Nan5uuFCLtBsU2ukwStnVgskMTsX1Dpso/iFsQpQiC2DdG9NP9Tdof+8zzn62KfRn VoO2i66H+2sadK9L77v1MFRo2tDVmqM6QYhCQnp16UrhH1bI+gnjNILOV1ZoElmTl4+fZjpxBs8 Zf76AQTF0KZXhwaoJ4/0CImAXLs8/HXEptCexDmRlh3QcfzmTqBjBSphvqVf8JDCvzcBWUCk+ly MCFJgVZhTye1f5levtTKZZEZU2MLN5z+HmChCjwNTu4mADzbnA2eu8sgWsG9H6hEVVxLc6I79IN QxCI1FVhVdR5l8voTuHsbf13bUv/6biahEjc43oSwdmCVVkdFtT658Hdr7iaJpKrQNej2CYLap3 g== X-Google-Smtp-Source: AGHT+IFsnvJKTM+35vwbspipp4TGFGs25ZYPlOLbrN33R++DlcW3HXwiogWfPX3LSZ/C792wt/YuwA== X-Received: by 2002:a05:690e:42c8:b0:63b:6e65:ba27 with SMTP id 956f58d0204a3-640d45eeacemr331165d50.50.1762549565168; Fri, 07 Nov 2025 13:06:05 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:04 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 17/22] mm: memfd_luo: allow preserving memfd Date: Fri, 7 Nov 2025 16:03:15 -0500 Message-ID: <20251107210526.257742-18-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav The ability to preserve a memfd allows userspace to use KHO and LUO to transfer its memory contents to the next kernel. This is useful in many ways. For one, it can be used with IOMMUFD as the backing store for IOMMU page tables. Preserving IOMMUFD is essential for performing a hypervisor live update with passthrough devices. memfd support provides the first building block for making that possible. For another, applications with a large amount of memory that takes time to reconstruct, reboots to consume kernel upgrades can be very expensive. memfd with LUO gives those applications reboot-persistent memory that they can use to quickly save and reconstruct that state. While memfd is backed by either hugetlbfs or shmem, currently only support on shmem is added. To be more precise, support for anonymous shmem files is added. The handover to the next kernel is not transparent. All the properties of the file are not preserved; only its memory contents, position, and size. The recreated file gets the UID and GID of the task doing the restore, and the task's cgroup gets charged with the memory. After LUO is in prepared state, the file cannot grow or shrink, and all its pages are pinned to avoid migrations and swapping. The file can still be read from or written to. Use vmalloc to get the buffer to hold the folios, and preserve it using kho_preserve_vmalloc(). This doesn't have the size limit. Co-developed-by: Pasha Tatashin Signed-off-by: Pasha Tatashin Signed-off-by: Pratyush Yadav --- MAINTAINERS | 2 + include/linux/liveupdate/abi/memfd.h | 88 ++++ mm/Makefile | 1 + mm/memfd_luo.c | 609 +++++++++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 include/linux/liveupdate/abi/memfd.h create mode 100644 mm/memfd_luo.c diff --git a/MAINTAINERS b/MAINTAINERS index 11b546168fb1..3497354b7fbb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14514,6 +14514,7 @@ F: tools/testing/selftests/livepatch/ =20 LIVE UPDATE M: Pasha Tatashin +R: Pratyush Yadav L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/core-api/liveupdate.rst @@ -14522,6 +14523,7 @@ F: include/linux/liveupdate.h F: include/linux/liveupdate/ F: include/uapi/linux/liveupdate.h F: kernel/liveupdate/ +F: mm/memfd_luo.c =20 LLC (802.2) L: netdev@vger.kernel.org diff --git a/include/linux/liveupdate/abi/memfd.h b/include/linux/liveupdat= e/abi/memfd.h new file mode 100644 index 000000000000..bf848e5bd1de --- /dev/null +++ b/include/linux/liveupdate/abi/memfd.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + * + * Copyright (C) 2025 Amazon.com Inc. or its affiliates. + * Pratyush Yadav + */ + +#ifndef _LINUX_LIVEUPDATE_ABI_MEMFD_H +#define _LINUX_LIVEUPDATE_ABI_MEMFD_H + +/** + * DOC: memfd Live Update ABI + * + * This header defines the ABI for preserving the state of a memfd across a + * kexec reboot using the LUO. + * + * The state is serialized into a Flattened Device Tree which is then hand= ed + * over to the next kernel via the KHO mechanism. The FDT is passed as the + * opaque `data` handle in the file handler callbacks. + * + * This interface is a contract. Any modification to the FDT structure, + * node properties, compatible string, or the layout of the serialization + * structures defined here constitutes a breaking change. Such changes req= uire + * incrementing the version number in the MEMFD_LUO_FH_COMPATIBLE string. + * + * FDT Structure Overview: + * The memfd state is contained within a single FDT with the following l= ayout: + * + * .. code-block:: none + * + * / { + * pos =3D <...>; + * size =3D <...>; + * nr_folios =3D <...>; + * folios =3D < ... binary data ... >; + * }; + * + * Node Properties: + * - pos: u64 + * The file's current position (f_pos). + * - size: u64 + * The total size of the file in bytes (i_size). + * - nr_folios: u64 + * Number of folios in folios array. Only present when size > 0. + * - folios: struct kho_vmalloc + * KHO vmalloc preservation for an array of &struct memfd_luo_folio_= ser, + * one for each preserved folio from the original file's mapping. On= ly + * present when size > 0. + */ + +/** + * struct memfd_luo_folio_ser - Serialized state of a single folio. + * @foliodesc: A packed 64-bit value containing both the PFN and status fl= ags of + * the preserved folio. The upper 52 bits store the PFN, and t= he + * lower 12 bits are reserved for flags (e.g., dirty, uptodate= ). + * @index: The page offset (pgoff_t) of the folio within the original = file's + * address space. This is used to correctly position the folio + * during restoration. + * + * This structure represents the minimal information required to restore a + * single folio in the new kernel. An array of these structs forms the bin= ary + * data for the "folios" property in the handover FDT. + */ +struct memfd_luo_folio_ser { + u64 foliodesc; + u64 index; +}; + +/* The strings used for memfd KHO FDT sub-tree. */ + +/* 64-bit pos value for the preserved memfd */ +#define MEMFD_FDT_POS "pos" + +/* 64-bit size value of the preserved memfd */ +#define MEMFD_FDT_SIZE "size" + +#define MEMFD_FDT_FOLIOS "folios" + +/* Number of folios in the folios array. */ +#define MEMFD_FDT_NR_FOLIOS "nr_folios" + +/* The compatibility string for memfd file handler */ +#define MEMFD_LUO_FH_COMPATIBLE "memfd-v1" + +#endif /* _LINUX_LIVEUPDATE_ABI_MEMFD_H */ diff --git a/mm/Makefile b/mm/Makefile index 21abb3353550..7738ec416f00 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_NUMA) +=3D memory-tiers.o obj-$(CONFIG_DEVICE_MIGRATION) +=3D migrate_device.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) +=3D huge_memory.o khugepaged.o obj-$(CONFIG_PAGE_COUNTER) +=3D page_counter.o +obj-$(CONFIG_LIVEUPDATE) +=3D memfd_luo.o obj-$(CONFIG_MEMCG_V1) +=3D memcontrol-v1.o obj-$(CONFIG_MEMCG) +=3D memcontrol.o vmpressure.o ifdef CONFIG_SWAP diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c new file mode 100644 index 000000000000..e366de627264 --- /dev/null +++ b/mm/memfd_luo.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + * + * Copyright (C) 2025 Amazon.com Inc. or its affiliates. + * Pratyush Yadav + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define PRESERVED_PFN_MASK GENMASK(63, 12) +#define PRESERVED_PFN_SHIFT 12 +#define PRESERVED_FLAG_DIRTY BIT(0) +#define PRESERVED_FLAG_UPTODATE BIT(1) + +#define PRESERVED_FOLIO_PFN(desc) (((desc) & PRESERVED_PFN_MASK) >> PRESER= VED_PFN_SHIFT) +#define PRESERVED_FOLIO_FLAGS(desc) ((desc) & ~PRESERVED_PFN_MASK) +#define PRESERVED_FOLIO_MKDESC(pfn, flags) (((pfn) << PRESERVED_PFN_SHIFT)= | (flags)) + +struct memfd_luo_private { + struct memfd_luo_folio_ser *pfolios; + u64 nr_folios; +}; + +static struct memfd_luo_folio_ser *memfd_luo_preserve_folios(struct file *= file, void *fdt, + u64 *nr_foliosp) +{ + struct inode *inode =3D file_inode(file); + struct memfd_luo_folio_ser *pfolios; + struct kho_vmalloc *kho_vmalloc; + unsigned int max_folios; + long i, size, nr_pinned; + struct folio **folios; + int err =3D -EINVAL; + pgoff_t offset; + u64 nr_folios; + + size =3D i_size_read(inode); + /* + * If the file has zero size, then the folios and nr_folios properties + * are not set. + */ + if (!size) { + *nr_foliosp =3D 0; + return NULL; + } + + /* + * Guess the number of folios based on inode size. Real number might end + * up being smaller if there are higher order folios. + */ + max_folios =3D PAGE_ALIGN(size) / PAGE_SIZE; + folios =3D kvmalloc_array(max_folios, sizeof(*folios), GFP_KERNEL); + if (!folios) + return ERR_PTR(-ENOMEM); + + /* + * Pin the folios so they don't move around behind our back. This also + * ensures none of the folios are in CMA -- which ensures they don't + * fall in KHO scratch memory. It also moves swapped out folios back to + * memory. + * + * A side effect of doing this is that it allocates a folio for all + * indices in the file. This might waste memory on sparse memfds. If + * that is really a problem in the future, we can have a + * memfd_pin_folios() variant that does not allocate a page on empty + * slots. + */ + nr_pinned =3D memfd_pin_folios(file, 0, size - 1, folios, max_folios, + &offset); + if (nr_pinned < 0) { + err =3D nr_pinned; + pr_err("failed to pin folios: %d\n", err); + goto err_free_folios; + } + nr_folios =3D nr_pinned; + + err =3D fdt_property(fdt, MEMFD_FDT_NR_FOLIOS, &nr_folios, sizeof(nr_foli= os)); + if (err) + goto err_unpin; + + err =3D fdt_property_placeholder(fdt, MEMFD_FDT_FOLIOS, sizeof(*kho_vmall= oc), + (void **)&kho_vmalloc); + if (err) { + pr_err("Failed to reserve '%s' property in FDT: %s\n", + MEMFD_FDT_FOLIOS, fdt_strerror(err)); + err =3D -ENOMEM; + goto err_unpin; + } + + pfolios =3D vcalloc(nr_folios, sizeof(*pfolios)); + if (!pfolios) { + err =3D -ENOMEM; + goto err_unpin; + } + + for (i =3D 0; i < nr_folios; i++) { + struct memfd_luo_folio_ser *pfolio =3D &pfolios[i]; + struct folio *folio =3D folios[i]; + unsigned int flags =3D 0; + unsigned long pfn; + + err =3D kho_preserve_folio(folio); + if (err) + goto err_unpreserve; + + pfn =3D folio_pfn(folio); + if (folio_test_dirty(folio)) + flags |=3D PRESERVED_FLAG_DIRTY; + if (folio_test_uptodate(folio)) + flags |=3D PRESERVED_FLAG_UPTODATE; + + pfolio->foliodesc =3D PRESERVED_FOLIO_MKDESC(pfn, flags); + pfolio->index =3D folio->index; + } + + err =3D kho_preserve_vmalloc(pfolios, kho_vmalloc); + if (err) + goto err_unpreserve; + + kvfree(folios); + *nr_foliosp =3D nr_folios; + return pfolios; + +err_unpreserve: + i--; + for (; i >=3D 0; i--) + WARN_ON_ONCE(kho_unpreserve_folio(folios[i])); + vfree(pfolios); +err_unpin: + unpin_folios(folios, nr_folios); +err_free_folios: + kvfree(folios); + return ERR_PTR(err); +} + +static void memfd_luo_unpreserve_folios(void *fdt, struct memfd_luo_folio_= ser *pfolios, + u64 nr_folios) +{ + struct kho_vmalloc *kho_vmalloc; + long i; + + if (!nr_folios) + return; + + kho_vmalloc =3D (struct kho_vmalloc *)fdt_getprop(fdt, 0, MEMFD_FDT_FOLIO= S, NULL); + /* The FDT was created by this kernel so expect it to be sane. */ + WARN_ON_ONCE(!kho_vmalloc); + kho_unpreserve_vmalloc(kho_vmalloc); + + for (i =3D 0; i < nr_folios; i++) { + const struct memfd_luo_folio_ser *pfolio =3D &pfolios[i]; + struct folio *folio; + + if (!pfolio->foliodesc) + continue; + + folio =3D pfn_folio(PRESERVED_FOLIO_PFN(pfolio->foliodesc)); + + WARN_ON_ONCE(kho_unpreserve_folio(folio)); + unpin_folio(folio); + } + + vfree(pfolios); +} + +static struct memfd_luo_folio_ser *memfd_luo_fdt_folios(const void *fdt, u= 64 *nr_folios) +{ + const struct kho_vmalloc *kho_vmalloc; + struct memfd_luo_folio_ser *pfolios; + const u64 *nr; + int len; + + nr =3D fdt_getprop(fdt, 0, MEMFD_FDT_NR_FOLIOS, &len); + if (!nr || len !=3D sizeof(*nr)) { + pr_err("invalid '%s' property\n", MEMFD_FDT_NR_FOLIOS); + return NULL; + } + + kho_vmalloc =3D fdt_getprop(fdt, 0, MEMFD_FDT_FOLIOS, &len); + if (!kho_vmalloc || len !=3D sizeof(*kho_vmalloc)) { + pr_err("invalid '%s' property\n", MEMFD_FDT_FOLIOS); + return NULL; + } + + pfolios =3D kho_restore_vmalloc(kho_vmalloc); + if (!pfolios) + return NULL; + + *nr_folios =3D *nr; + return pfolios; +} + +static void *memfd_luo_create_fdt(void) +{ + struct folio *fdt_folio; + int err =3D 0; + void *fdt; + + /* + * The FDT only contains a couple of properties and a kho_vmalloc + * object. One page should be enough for that. + */ + fdt_folio =3D folio_alloc(GFP_KERNEL | __GFP_ZERO, 0); + if (!fdt_folio) + return NULL; + + fdt =3D folio_address(fdt_folio); + + err |=3D fdt_create(fdt, folio_size(fdt_folio)); + err |=3D fdt_finish_reservemap(fdt); + err |=3D fdt_begin_node(fdt, ""); + if (err) + goto free; + + return fdt; + +free: + folio_put(fdt_folio); + return NULL; +} + +static int memfd_luo_finish_fdt(void *fdt) +{ + int err; + + err =3D fdt_end_node(fdt); + if (err) + return err; + + return fdt_finish(fdt); +} + +static int memfd_luo_preserve(struct liveupdate_file_op_args *args) +{ + struct inode *inode =3D file_inode(args->file); + struct memfd_luo_folio_ser *pfolios; + struct memfd_luo_private *private; + u64 pos, nr_folios; + int err =3D 0; + void *fdt; + long size; + + private =3D kmalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + inode_lock(inode); + shmem_i_mapping_freeze(inode, true); + + size =3D i_size_read(inode); + + fdt =3D memfd_luo_create_fdt(); + if (!fdt) { + err =3D -ENOMEM; + goto err_unlock; + } + + pos =3D args->file->f_pos; + err =3D fdt_property(fdt, MEMFD_FDT_POS, &pos, sizeof(pos)); + if (err) + goto err_free_fdt; + + err =3D fdt_property(fdt, MEMFD_FDT_SIZE, &size, sizeof(size)); + if (err) + goto err_free_fdt; + + pfolios =3D memfd_luo_preserve_folios(args->file, fdt, &nr_folios); + if (IS_ERR(pfolios)) { + err =3D PTR_ERR(pfolios); + goto err_free_fdt; + } + + err =3D memfd_luo_finish_fdt(fdt); + if (err) + goto err_unpreserve_folios; + + err =3D kho_preserve_folio(virt_to_folio(fdt)); + if (err) + goto err_unpreserve_folios; + + inode_unlock(inode); + + private->pfolios =3D pfolios; + private->nr_folios =3D nr_folios; + args->private_data =3D private; + args->serialized_data =3D virt_to_phys(fdt); + return 0; + +err_unpreserve_folios: + memfd_luo_unpreserve_folios(fdt, pfolios, nr_folios); +err_free_fdt: + folio_put(virt_to_folio(fdt)); +err_unlock: + shmem_i_mapping_freeze(inode, false); + inode_unlock(inode); + kfree(private); + return err; +} + +static int memfd_luo_freeze(struct liveupdate_file_op_args *args) +{ + u64 pos =3D args->file->f_pos; + void *fdt; + int err; + + if (WARN_ON_ONCE(!args->serialized_data)) + return -EINVAL; + + fdt =3D phys_to_virt(args->serialized_data); + + /* + * The pos might have changed since prepare. Everything else stays the + * same. + */ + err =3D fdt_setprop(fdt, 0, "pos", &pos, sizeof(pos)); + if (err) + return err; + + return 0; +} + +static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args) +{ + struct memfd_luo_private *private =3D args->private_data; + struct inode *inode =3D file_inode(args->file); + struct folio *fdt_folio; + void *fdt; + + if (WARN_ON_ONCE(!args->serialized_data || !args->private_data)) + return; + + inode_lock(inode); + shmem_i_mapping_freeze(inode, false); + + fdt =3D phys_to_virt(args->serialized_data); + fdt_folio =3D virt_to_folio(fdt); + + memfd_luo_unpreserve_folios(fdt, private->pfolios, private->nr_folios); + + kho_unpreserve_folio(fdt_folio); + folio_put(fdt_folio); + inode_unlock(inode); + kfree(private); +} + +static struct folio *memfd_luo_get_fdt(u64 data) +{ + return kho_restore_folio((phys_addr_t)data); +} + +static void memfd_luo_discard_folios(const struct memfd_luo_folio_ser *pfo= lios, + long nr_folios) +{ + unsigned int i; + + for (i =3D 0; i < nr_folios; i++) { + const struct memfd_luo_folio_ser *pfolio =3D &pfolios[i]; + struct folio *folio; + phys_addr_t phys; + + if (!pfolio->foliodesc) + continue; + + phys =3D PFN_PHYS(PRESERVED_FOLIO_PFN(pfolio->foliodesc)); + folio =3D kho_restore_folio(phys); + if (!folio) { + pr_warn_ratelimited("Unable to restore folio at physical address: %llx\= n", + phys); + continue; + } + + folio_put(folio); + } +} + +static void memfd_luo_finish(struct liveupdate_file_op_args *args) +{ + const struct memfd_luo_folio_ser *pfolios; + struct folio *fdt_folio; + const void *fdt; + u64 nr_folios; + + if (args->retrieved) + return; + + fdt_folio =3D memfd_luo_get_fdt(args->serialized_data); + if (!fdt_folio) { + pr_err("failed to restore memfd FDT\n"); + return; + } + + fdt =3D folio_address(fdt_folio); + + pfolios =3D memfd_luo_fdt_folios(fdt, &nr_folios); + if (!pfolios) + goto out; + + memfd_luo_discard_folios(pfolios, nr_folios); + vfree(pfolios); + +out: + folio_put(fdt_folio); +} + +static int memfd_luo_retrieve_folios(struct file *file, const void *fdt) +{ + const struct memfd_luo_folio_ser *pfolios; + struct inode *inode =3D file_inode(file); + struct address_space *mapping; + struct folio *folio; + u64 nr_folios; + long i =3D 0; + int err; + + /* Careful: folios don't exist in FDT on zero-size files. */ + if (!inode->i_size) + return 0; + + pfolios =3D memfd_luo_fdt_folios(fdt, &nr_folios); + if (!pfolios) { + pr_err("failed to fetch preserved folio list\n"); + return -EINVAL; + } + + inode =3D file->f_inode; + mapping =3D inode->i_mapping; + + for (; i < nr_folios; i++) { + const struct memfd_luo_folio_ser *pfolio =3D &pfolios[i]; + phys_addr_t phys; + u64 index; + int flags; + + if (!pfolio->foliodesc) + continue; + + phys =3D PFN_PHYS(PRESERVED_FOLIO_PFN(pfolio->foliodesc)); + folio =3D kho_restore_folio(phys); + if (!folio) { + pr_err("Unable to restore folio at physical address: %llx\n", + phys); + goto put_folios; + } + index =3D pfolio->index; + flags =3D PRESERVED_FOLIO_FLAGS(pfolio->foliodesc); + + /* Set up the folio for insertion. */ + __folio_set_locked(folio); + __folio_set_swapbacked(folio); + + err =3D mem_cgroup_charge(folio, NULL, mapping_gfp_mask(mapping)); + if (err) { + pr_err("shmem: failed to charge folio index %ld: %d\n", + i, err); + goto unlock_folio; + } + + err =3D shmem_add_to_page_cache(folio, mapping, index, NULL, + mapping_gfp_mask(mapping)); + if (err) { + pr_err("shmem: failed to add to page cache folio index %ld: %d\n", + i, err); + goto unlock_folio; + } + + if (flags & PRESERVED_FLAG_UPTODATE) + folio_mark_uptodate(folio); + if (flags & PRESERVED_FLAG_DIRTY) + folio_mark_dirty(folio); + + err =3D shmem_inode_acct_blocks(inode, 1); + if (err) { + pr_err("shmem: failed to account folio index %ld: %d\n", + i, err); + goto unlock_folio; + } + + shmem_recalc_inode(inode, 1, 0); + folio_add_lru(folio); + folio_unlock(folio); + folio_put(folio); + } + + vfree(pfolios); + return 0; + +unlock_folio: + folio_unlock(folio); + folio_put(folio); + i++; +put_folios: + /* + * Note: don't free the folios already added to the file. They will be + * freed when the file is freed. Free the ones not added yet here. + */ + for (; i < nr_folios; i++) { + const struct memfd_luo_folio_ser *pfolio =3D &pfolios[i]; + + folio =3D kho_restore_folio(PRESERVED_FOLIO_PFN(pfolio->foliodesc)); + if (folio) + folio_put(folio); + } + + vfree(pfolios); + return err; +} + +static int memfd_luo_retrieve(struct liveupdate_file_op_args *args) +{ + struct folio *fdt_folio; + const u64 *pos, *size; + struct file *file; + int len, ret =3D 0; + const void *fdt; + + fdt_folio =3D memfd_luo_get_fdt(args->serialized_data); + if (!fdt_folio) + return -ENOENT; + + fdt =3D page_to_virt(folio_page(fdt_folio, 0)); + + size =3D fdt_getprop(fdt, 0, "size", &len); + if (!size || len !=3D sizeof(u64)) { + pr_err("invalid 'size' property\n"); + ret =3D -EINVAL; + goto put_fdt; + } + + pos =3D fdt_getprop(fdt, 0, "pos", &len); + if (!pos || len !=3D sizeof(u64)) { + pr_err("invalid 'pos' property\n"); + ret =3D -EINVAL; + goto put_fdt; + } + + file =3D shmem_file_setup("", 0, VM_NORESERVE); + + if (IS_ERR(file)) { + ret =3D PTR_ERR(file); + pr_err("failed to setup file: %d\n", ret); + goto put_fdt; + } + + vfs_setpos(file, *pos, MAX_LFS_FILESIZE); + file->f_inode->i_size =3D *size; + + ret =3D memfd_luo_retrieve_folios(file, fdt); + if (ret) + goto put_file; + + args->file =3D file; + folio_put(fdt_folio); + return 0; + +put_file: + fput(file); +put_fdt: + folio_put(fdt_folio); + return ret; +} + +static bool memfd_luo_can_preserve(struct liveupdate_file_handler *handler, + struct file *file) +{ + struct inode *inode =3D file_inode(file); + + return shmem_file(file) && !inode->i_nlink; +} + +static const struct liveupdate_file_ops memfd_luo_file_ops =3D { + .freeze =3D memfd_luo_freeze, + .finish =3D memfd_luo_finish, + .retrieve =3D memfd_luo_retrieve, + .preserve =3D memfd_luo_preserve, + .unpreserve =3D memfd_luo_unpreserve, + .can_preserve =3D memfd_luo_can_preserve, + .owner =3D THIS_MODULE, +}; + +static struct liveupdate_file_handler memfd_luo_handler =3D { + .ops =3D &memfd_luo_file_ops, + .compatible =3D MEMFD_LUO_FH_COMPATIBLE, +}; + +static int __init memfd_luo_init(void) +{ + int err; + + err =3D liveupdate_register_file_handler(&memfd_luo_handler); + if (err) + pr_err("Could not register luo filesystem handler: %d\n", err); + + return err; +} +late_initcall(memfd_luo_init); --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yx1-f50.google.com (mail-yx1-f50.google.com [74.125.224.50]) (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 54E3732ABCA for ; Fri, 7 Nov 2025 21:06:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.224.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549570; cv=none; b=mkpsi3X+8aVjRePKuGpBK3BqwwinVXFUHpaDPCfTr1YJSoyZGKFfn2yc4CbDcuq0PPOgY+PdqFqcM6nuiLS7aNEZOEkTmXHrB0UbH0we20p2Nu0ATpj/WKfRyzSBwIAke8okQ6PE28YOEZumgAsc+F3fEXfgG+lAebCgEJo4dmY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549570; c=relaxed/simple; bh=LXpw63l9s85+A2ywC95X1cdCWNl0UMCa9L8ALqPe8sU=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dgfu5o7Ai0tarzJ6zshMeKEnMSRBdrV5QWfiMfSOL6FThfxyQdeDCqi5/S6UGfWRlsduZ0CN+hVkwkiO0ChG0ol79ZF90lswBZnBG7U9x7hJ6VbJun+2PxzOuRag5Mea69Q80m5vn1E99sqRADt4VypF24M0gUzB3HovonKpuRA= 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=b60BptG+; arc=none smtp.client-ip=74.125.224.50 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="b60BptG+" Received: by mail-yx1-f50.google.com with SMTP id 956f58d0204a3-63e2cc1ac4aso1179914d50.2 for ; Fri, 07 Nov 2025 13:06:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549567; x=1763154367; 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=JsTES3jh+g2TZ5eZZXKIDmlAfD8Fsxtr8nl7dy6CsdM=; b=b60BptG+gjWR+9zKdkv6BZG/IMz7hbn1ri7IN1sa6ZcL141v4Dp217b9N1emp+0vMy kl7G+2qUHck08rVk94JhfctLp8ec5WmCLI7or4a53ZUEtavB7SL8JIaRH4gdIYCQN3BN 5F+HU5ncrw2qUSxcgAqwuLB+SxTM368hS0jR917KbPhwZptzxoXGbPBBsTQeDdiUpPBY PVZKD2eyGCkkiAFct/JWu/6gEQfhHz6b3eE0eic8n+6Q9TXGx4VnewA2NpjoGDy2ezOq eDPhI0UrnpqBkGq6IZohIl7xOZQ6oYXDKSKOd3HYI68ZEQ9xY272OfG4srKj6dBt2rMn jxPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549567; x=1763154367; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=JsTES3jh+g2TZ5eZZXKIDmlAfD8Fsxtr8nl7dy6CsdM=; b=O8YrMVJZdpmQodHrLWTYiG1WBs04a9Mep5iikGdkEvxipsUfH1xdb/tCMQhlyrHaC/ 3H0mypn2Q+z2sWp0/MjI+8QKdDzQ0sAWuQGkxsdAAqrwGychypvYS4OlJn3ZZ80JiwJb 5elTNXKCV/17ICudGn/dbcoRY/tk23P5gRLi6mkLeUMioBE6VOLSXnSyibP+e86BHXg/ Lv/dIioZzl2jODNn3SnAAbO999RKJEj7bkQNBoxKt5A6AkviVuTOASs7Smr1y266eJzy lHRd5enZWSRAmJWp2mbh55sNAQqXoKGIbfHfWALn4TSRrCwahBOYuZWKKiwPtMF9AEvg 4DXw== X-Forwarded-Encrypted: i=1; AJvYcCV/8N9mL570sQ3ZCOW+ZzTtnQCD/4097RxX7Nb//kFGm/Pp7TrLP73TXlpI92lSUdtHmAkOUJQ9twxETE4=@vger.kernel.org X-Gm-Message-State: AOJu0YxKNiVUErjMfF4vZmzSLBvELSo3LGjLHZ+5BgqNgR+cCQRN+ENW tJGuFBX93KTatTkDepdQUwMxA71iZ23tiQg6jrt8m7WPvOqBeAJvamEYw44BKG+uhTg= X-Gm-Gg: ASbGncvoHK4iTPZVxbh0AmvtbcP1nEdCfKr6zLFXfv5X+SVKbZKs3rI4kRibmkSlXO+ shldZlJmw0afk97CIn6BCSlPAsorAR+JyjuVmzutv2rv6mnEDT+rQQ6EVWIxRhcmoeb+pWPz3QN Q8b2izEgiZIyjnq5EzzaPC4BsxqPxdzCFeolKedC+d7hh5Bih+1CgRNEANt05JiJzirlk2VpCpV 2ZtJPTA13NIgv0OspoDaZ2oPMY085LlNHZbRB6/Kyh0HMlJ9Pk6ZgnksWalxAnZrIHCp36th5/D aeOgnv/M1b2Yrc08+Eq4t7b+w8hgQcElCuNs3pk0/54cTHX5ewVgG+REAd2fyPOsuf7VHxZvWJf TWWrV+BKjRu22PmFc3b/R3HUaRV9y7xXoH7RloTL66JmJl0z51ifSErx/e03Xdrt3zWVEjfPB7W 6n+vlm70fyKii4rogJuSxRS1AH+/p8eXkkQrKPiaO2itUetwXuJmqTTaLqnVkzplQmd4fCFyA1u w== X-Google-Smtp-Source: AGHT+IFYVLUYsoz4qqSx1jdSCSSHR6CFRDP48gNnoQOXCk0YbarCB32vquZ/fupTODYyB7YDg+0Eyw== X-Received: by 2002:a05:690e:1604:b0:640:caa5:57bf with SMTP id 956f58d0204a3-640d454752emr376765d50.24.1762549567082; Fri, 07 Nov 2025 13:06:07 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:06 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 18/22] docs: add documentation for memfd preservation via LUO Date: Fri, 7 Nov 2025 16:03:16 -0500 Message-ID: <20251107210526.257742-19-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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" From: Pratyush Yadav Add the documentation under the "Preserving file descriptors" section of LUO's documentation. The doc describes the properties preserved, behaviour of the file under different LUO states, serialization format, and current limitations. Signed-off-by: Pratyush Yadav Signed-off-by: Pasha Tatashin --- Documentation/core-api/liveupdate.rst | 7 ++ Documentation/mm/index.rst | 1 + Documentation/mm/memfd_preservation.rst | 138 ++++++++++++++++++++++++ MAINTAINERS | 1 + 4 files changed, 147 insertions(+) create mode 100644 Documentation/mm/memfd_preservation.rst diff --git a/Documentation/core-api/liveupdate.rst b/Documentation/core-api= /liveupdate.rst index deacc098d024..384de79a2457 100644 --- a/Documentation/core-api/liveupdate.rst +++ b/Documentation/core-api/liveupdate.rst @@ -28,6 +28,13 @@ Live Update Orchestrator ABI .. kernel-doc:: include/linux/liveupdate/abi/luo.h :doc: Live Update Orchestrator ABI =20 +The following types of file descriptors can be preserved + +.. toctree:: + :maxdepth: 1 + + ../mm/memfd_preservation + Public API =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D .. kernel-doc:: include/linux/liveupdate.h diff --git a/Documentation/mm/index.rst b/Documentation/mm/index.rst index ba6a8872849b..7aa2a8886908 100644 --- a/Documentation/mm/index.rst +++ b/Documentation/mm/index.rst @@ -48,6 +48,7 @@ documentation, or deleted if it has served its purpose. hugetlbfs_reserv ksm memory-model + memfd_preservation mmu_notifier multigen_lru numa diff --git a/Documentation/mm/memfd_preservation.rst b/Documentation/mm/mem= fd_preservation.rst new file mode 100644 index 000000000000..3fc612e1288c --- /dev/null +++ b/Documentation/mm/memfd_preservation.rst @@ -0,0 +1,138 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +Memfd Preservation via LUO +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +Memory file descriptors (memfd) can be preserved over a kexec using the Li= ve +Update Orchestrator (LUO) file preservation. This allows userspace to tran= sfer +its memory contents to the next kernel after a kexec. + +The preservation is not intended to be transparent. Only select properties= of +the file are preserved. All others are reset to default. The preserved +properties are described below. + +.. note:: + The LUO API is not stabilized yet, so the preserved properties of a mem= fd are + also not stable and are subject to backwards incompatible changes. + +.. note:: + Currently a memfd backed by Hugetlb is not supported. Memfds created + with ``MFD_HUGETLB`` will be rejected. + +Preserved Properties +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The following properties of the memfd are preserved across kexec: + +File Contents + All data stored in the file is preserved. + +File Size + The size of the file is preserved. Holes in the file are filled by alloc= ating + pages for them during preservation. + +File Position + The current file position is preserved, allowing applications to continue + reading/writing from their last position. + +File Status Flags + memfds are always opened with ``O_RDWR`` and ``O_LARGEFILE``. This prope= rty is + maintained. + +Non-Preserved Properties +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +All properties which are not preserved must be assumed to be reset to defa= ult. +This section describes some of those properties which may be more of note. + +``FD_CLOEXEC`` flag + A memfd can be created with the ``MFD_CLOEXEC`` flag that sets the + ``FD_CLOEXEC`` on the file. This flag is not preserved and must be set a= gain + after restore via ``fcntl()``. + +Seals + File seals are not preserved. The file is unsealed on restore and if nee= ded, + must be sealed again via ``fcntl()``. + +Behavior with LUO states +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This section described the behavior of the memfd in the different LUO stat= es. + +Normal Phase + During the normal phase, the memfd can be marked for preservation using = the + ``LIVEUPDATE_SESSION_PRESERVE_FD`` ioctl. The memfd acts as a regular me= mfd + during this phase with no additional restrictions. + +Prepared Phase + After LUO enters ``LIVEUPDATE_STATE_PREPARED``, the memfd is serialized = and + prepared for the next kernel. During this phase, the below things happen: + + - All the folios are pinned. If some folios reside in ``ZONE_MIGRATE``, = they + are migrated out. This ensures none of the preserved folios land in KHO + scratch area. + - Pages in swap are swapped in. Currently, there is no way to pass pages= in + swap over KHO, so all swapped out pages are swapped back in and pinned. + - The memfd goes into "frozen mapping" mode. The file can no longer grow= or + shrink, or punch holes. This ensures the serialized mappings stay in s= ync. + The file can still be read from or written to or mmap-ed. + +Freeze Phase + Updates the current file position in the serialized data to capture any + changes that occurred between prepare and freeze phases. After this, the= FD is + not allowed to be accessed. + +Restoration Phase + After being restored, the memfd is functional as normal with the propert= ies + listed above restored. + +Cancellation + If the liveupdate is cancelled after going into prepared phase, the memfd + functions like in normal phase. + +Serialization format +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The state is serialized in an FDT with the following structure:: + + /dts-v1/; + + / { + compatible =3D "memfd-v1"; + pos =3D ; + size =3D ; + folios =3D ; + }; + +Each folio descriptor contains: + +- PFN + flags (8 bytes) + + - Physical frame number (PFN) of the preserved folio (bits 63:12). + - Folio flags (bits 11:0): + + - ``PRESERVED_FLAG_DIRTY`` (bit 0) + - ``PRESERVED_FLAG_UPTODATE`` (bit 1) + +- Folio index within the file (8 bytes). + +Limitations +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The current implementation has the following limitations: + +Size + Currently the size of the file is limited by the size of the FDT. The FD= T can + be at of most ``MAX_PAGE_ORDER`` order. By default this is 4 MiB with 4K + pages. Each page in the file is tracked using 16 bytes. This limits the + maximum size of the file to 1 GiB. + +See Also +=3D=3D=3D=3D=3D=3D=3D=3D + +- :doc:`Live Update Orchestrator ` +- :doc:`/core-api/kho/concepts` diff --git a/MAINTAINERS b/MAINTAINERS index 3497354b7fbb..3ece47c552a8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14518,6 +14518,7 @@ R: Pratyush Yadav L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/core-api/liveupdate.rst +F: Documentation/mm/memfd_preservation.rst F: Documentation/userspace-api/liveupdate.rst F: include/linux/liveupdate.h F: include/linux/liveupdate/ --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (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 39473346A15 for ; Fri, 7 Nov 2025 21:06:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549573; cv=none; b=DIRYX93ahktroX+j9bSUn556BLqqlNfcwjo7geh0xNH/vGwAOXctD2Y1+QmP/FqZ2YdKMjY3g4iVhHm5WyYt12ryj8Xk2u65WTkY4c6cwQysUl42Qh1TBgeFLuQldGvC5AOy/japEQy0hi5OcmQtz7HCDPmcWmrIX1UTDs5lApA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549573; c=relaxed/simple; bh=0TF9zNcxe3WTL2vyG+bYfveB1L+Of18sKisqgFhAAUA=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=muDz3YJVaF5ulrsTM1KqgZfyX+x6BZy8VoutbIuMRBbkd7CKYJ9ZuVjrgPDjBNdr3F6rjIlCLEt7iRT7NHq2LNr8Rxt7diEpB+9TBtL3AOrpH2gHuuMjn4IGowvV3W3+X3pa2Mr0/yX25C8xXYugc5aKUnFeVKHt2o10lW3cjOw= 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=cgJtg8Gz; arc=none smtp.client-ip=209.85.128.170 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="cgJtg8Gz" Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-7866aca9ff4so14647097b3.3 for ; Fri, 07 Nov 2025 13:06:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549569; x=1763154369; 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=2SXDWAbA53PdIOw33GR0LKCvwxTUGeSycQUW5TmKy64=; b=cgJtg8Gzq51cU8lAve21aLSyGDZ74tuyRkmn4LkM8rBCDt8oTnSZpIMI6Zkt7K4NT6 u8RjT3M8kEd4RpWZURa5y8V5IUkX1PGGEFLMhQABT06fjxp/igtfR3Mz8nSovO4++4V1 AtrPCCEtM0TyxslY/X9R4V1mV4HXknOZqIKJ3ywr9lfY55YgPsXm3g/X02sjW7McIg6A MZEYSksq0pU6Aq5yM2K8jU6A9KtdPu2JGFITOClxV9f8ldnsOIthgmVO5JM8iIfSe7XE ksFKr/AXKbZ4o/5HG759Bz7RuIi5G7TEOmHS4xQ32yRO1Dhq4eDy7W6wLuSBg9ez1cLC 8umg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549569; x=1763154369; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=2SXDWAbA53PdIOw33GR0LKCvwxTUGeSycQUW5TmKy64=; b=IFPI2zJ53p8rIZGZqw47rG6AlRv83quiq1ITNG4XguXej/0vWKLy3aa8xn2w31LGBH Z1q+9Uq+BPtp0Jmzag13O+zCzrf0p0zPttyZcjjVEnVELLm9BYPZ47s4DAd10YOmpmxj HSICNplh0ZQetmD76EflRFUPJYsDqZPFZ4ODddpa+hPUKFrBKacq2tYC1AKjGSFrbS6z aPRqiLIVkuUJnplGVNSQozCdX5cPWW7UJCfYqxzr+R4R621tOkK3Im/ie6Llmp9nlkha CXaavk7Uayvuh7rcx8OD4tbf0kDcoTEPdOHsmQNcP98bi2Ce9X0kpWxEu/Y1sH9ijSxE WsVw== X-Forwarded-Encrypted: i=1; AJvYcCUadDYJTzBVhtKoiqqg5ntTyo70yDLmtbrWF88R1GIv5n0txqVTaQUaOPmbsvBhBHk/Hw/dXmEeLniD2qY=@vger.kernel.org X-Gm-Message-State: AOJu0YyHhA/hsYMQJzJolajkoPk22/lcRFrHiYurnO2tIQO/4QHPRJxR X9elg4MX/UsSvKQ07Y9wZ2OUxyukl1XLR6ExulUPbeBQj2qMMp1pPev3JdObC6DhAeo= X-Gm-Gg: ASbGnct35NCIaOPsAvL8Kd90rfSxU/BiuA1iYrmCfRQdDxxTakpPvrAmIJlbNsIUwnX M4vjOlF+lSfYkgpuOFO//8Cv1o39e4ahX3EtpZ9BIaDWcDY2owWyho3PzZ+u5tll582nK4+lyCr UY+Gp1DcxsUqsw5ybUYYj/63Om4PDtg88bsRgujPUykO4pXOsCJpfpK0wiN+8a+A6Uw/Ljibghk /TjXxNoa76cT1U5TbGPT8dN3gVKhksXfhnx1B9B8TgNusoBgxAqCT5lcjRG/whICumr+ez9CM1I +YrlLC0WAbE4vjEwJEzP5GiPrrkHC8v5bEnvTpVG4mHv+obSFQzKwFX0+ZSVLaBPy47h1ojxPaZ nIfLYbu8T2g9tk4FxEqVo4MnLvxKnAp+cYBm92qA9/KZESz4JEHzIRJow3zMnLkntvnoyAfiC0/ G+lL5W3Z3PY85dvRPkH3eRwWCcHDX8zDnTgERT3xqvUyVHJRbdqKBOXr6f5BcyCGw= X-Google-Smtp-Source: AGHT+IG8hOr8PcT4Gk3FevcQPhtl/97Z8re6SN0GLBa+Vgcg2v8tRAI+kF0AjkpM6DM+zrl2P2CK9A== X-Received: by 2002:a05:690c:a741:b0:785:cecb:4b19 with SMTP id 00721157ae682-787d536404fmr6385657b3.5.1762549569068; Fri, 07 Nov 2025 13:06:09 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:08 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 19/22] selftests/liveupdate: Add userspace API selftests Date: Fri, 7 Nov 2025 16:03:17 -0500 Message-ID: <20251107210526.257742-20-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 a selftest suite for LUO. These tests validate the core userspace-facing API provided by the /dev/liveupdate device and its associated ioctls. The suite covers fundamental device behavior, session management, and the file preservation mechanism using memfd as a test case. This provides regression testing for the LUO uAPI. The following functionality is verified: Device Access: Basic open and close operations on /dev/liveupdate. Enforcement of exclusive device access (verifying EBUSY on a second open). Session Management: Successful creation of sessions with unique names. Failure to create sessions with duplicate names. File Preservation: Preserving a single memfd and verifying its content remains intact post-preservation. Preserving multiple memfds within a single session, each with unique data. A complex scenario involving multiple sessions, each containing a mix of empty and data-filled memfds. Note: This test suite is limited to verifying the pre-kexec functionality of LUO (e.g., session creation, file preservation). The post-kexec restoration of resources is not covered, as the kselftest framework does not currently support orchestrating a reboot and continuing execution in the new kernel. Signed-off-by: Pasha Tatashin --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/liveupdate/.gitignore | 1 + tools/testing/selftests/liveupdate/Makefile | 7 + tools/testing/selftests/liveupdate/config | 5 + .../testing/selftests/liveupdate/liveupdate.c | 317 ++++++++++++++++++ 6 files changed, 332 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/.gitignore create mode 100644 tools/testing/selftests/liveupdate/Makefile create mode 100644 tools/testing/selftests/liveupdate/config create mode 100644 tools/testing/selftests/liveupdate/liveupdate.c diff --git a/MAINTAINERS b/MAINTAINERS index 3ece47c552a8..21cd3c6181c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14525,6 +14525,7 @@ F: include/linux/liveupdate/ F: include/uapi/linux/liveupdate.h F: kernel/liveupdate/ F: mm/memfd_luo.c +F: tools/testing/selftests/liveupdate/ =20 LLC (802.2) L: netdev@vger.kernel.org diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Mak= efile index c46ebdb9b8ef..56e44a98d6a5 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -54,6 +54,7 @@ TARGETS +=3D kvm TARGETS +=3D landlock TARGETS +=3D lib TARGETS +=3D livepatch +TARGETS +=3D liveupdate TARGETS +=3D lkdtm TARGETS +=3D lsm TARGETS +=3D membarrier diff --git a/tools/testing/selftests/liveupdate/.gitignore b/tools/testing/= selftests/liveupdate/.gitignore new file mode 100644 index 000000000000..af6e773cf98f --- /dev/null +++ b/tools/testing/selftests/liveupdate/.gitignore @@ -0,0 +1 @@ +/liveupdate diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/se= lftests/liveupdate/Makefile new file mode 100644 index 000000000000..2a573c36016e --- /dev/null +++ b/tools/testing/selftests/liveupdate/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +CFLAGS +=3D -Wall -O2 -Wno-unused-function +CFLAGS +=3D $(KHDR_INCLUDES) + +TEST_GEN_PROGS +=3D liveupdate + +include ../lib.mk diff --git a/tools/testing/selftests/liveupdate/config b/tools/testing/self= tests/liveupdate/config new file mode 100644 index 000000000000..c0c7e7cc484e --- /dev/null +++ b/tools/testing/selftests/liveupdate/config @@ -0,0 +1,5 @@ +CONFIG_KEXEC_FILE=3Dy +CONFIG_KEXEC_HANDOVER=3Dy +CONFIG_KEXEC_HANDOVER_DEBUGFS=3Dy +CONFIG_KEXEC_HANDOVER_DEBUG=3Dy +CONFIG_LIVEUPDATE=3Dy diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testin= g/selftests/liveupdate/liveupdate.c new file mode 100644 index 000000000000..eec26288a102 --- /dev/null +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +/* + * Selftests for the Live Update Orchestrator. + * This test suite verifies the functionality and behavior of the + * /dev/liveupdate character device and its session management capabilitie= s. + * + * Tests include: + * - Device access: basic open/close, and enforcement of exclusive access. + * - Session management: creation of unique sessions, and duplicate name d= etection. + * - Resource preservation: successfully preserving individual and multipl= e memfds, + * verifying contents remain accessible. + * - Complex multi-session scenarios involving mixed empty and populated f= iles. + */ + +#include +#include +#include +#include +#include + +#include + +#include "../kselftest.h" +#include "../kselftest_harness.h" + +#define LIVEUPDATE_DEV "/dev/liveupdate" + +FIXTURE(liveupdate_device) { + int fd1; + int fd2; +}; + +FIXTURE_SETUP(liveupdate_device) +{ + self->fd1 =3D -1; + self->fd2 =3D -1; +} + +FIXTURE_TEARDOWN(liveupdate_device) +{ + if (self->fd1 >=3D 0) + close(self->fd1); + if (self->fd2 >=3D 0) + close(self->fd2); +} + +/* + * Test Case: Basic Open and Close + * + * Verifies that the /dev/liveupdate device can be opened and subsequently + * closed without errors. Skips if the device does not exist. + */ +TEST_F(liveupdate_device, basic_open_close) +{ + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); + + ASSERT_GE(self->fd1, 0); + ASSERT_EQ(close(self->fd1), 0); + self->fd1 =3D -1; +} + +/* + * Test Case: Exclusive Open Enforcement + * + * Verifies that the /dev/liveupdate device can only be opened by one proc= ess + * at a time. It checks that a second attempt to open the device fails with + * the EBUSY error code. + */ +TEST_F(liveupdate_device, exclusive_open) +{ + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist.", LIVEUPDATE_DEV); + + ASSERT_GE(self->fd1, 0); + self->fd2 =3D open(LIVEUPDATE_DEV, O_RDWR); + EXPECT_LT(self->fd2, 0); + EXPECT_EQ(errno, EBUSY); +} + +/* Helper function to create a LUO session via ioctl. */ +static int create_session(int lu_fd, const char *name) +{ + struct liveupdate_ioctl_create_session args =3D {}; + + args.size =3D sizeof(args); + strncpy((char *)args.name, name, sizeof(args.name) - 1); + + if (ioctl(lu_fd, LIVEUPDATE_IOCTL_CREATE_SESSION, &args)) + return -errno; + + return args.fd; +} + +/* + * Test Case: Create Duplicate Session + * + * Verifies that attempting to create two sessions with the same name fails + * on the second attempt with EEXIST. + */ +TEST_F(liveupdate_device, create_duplicate_session) +{ + int session_fd1, session_fd2; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + + ASSERT_GE(self->fd1, 0); + + session_fd1 =3D create_session(self->fd1, "duplicate-session-test"); + ASSERT_GE(session_fd1, 0); + + session_fd2 =3D create_session(self->fd1, "duplicate-session-test"); + EXPECT_LT(session_fd2, 0); + EXPECT_EQ(-session_fd2, EEXIST); + + ASSERT_EQ(close(session_fd1), 0); +} + +/* + * Test Case: Create Distinct Sessions + * + * Verifies that creating two sessions with different names succeeds. + */ +TEST_F(liveupdate_device, create_distinct_sessions) +{ + int session_fd1, session_fd2; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + + ASSERT_GE(self->fd1, 0); + + session_fd1 =3D create_session(self->fd1, "distinct-session-1"); + ASSERT_GE(session_fd1, 0); + + session_fd2 =3D create_session(self->fd1, "distinct-session-2"); + ASSERT_GE(session_fd2, 0); + + ASSERT_EQ(close(session_fd1), 0); + ASSERT_EQ(close(session_fd2), 0); +} + +static int preserve_fd(int session_fd, int fd_to_preserve, __u64 token) +{ + struct liveupdate_session_preserve_fd args =3D {}; + + args.size =3D sizeof(args); + args.fd =3D fd_to_preserve; + args.token =3D token; + + if (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &args)) + return -errno; + + return 0; +} + +/* + * Test Case: Preserve MemFD + * + * Verifies that a valid memfd can be successfully preserved in a session = and + * that its contents remain intact after the preservation call. + */ +TEST_F(liveupdate_device, preserve_memfd) +{ + const char *test_str =3D "hello liveupdate"; + char read_buf[64] =3D {}; + int session_fd, mem_fd; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd =3D create_session(self->fd1, "preserve-memfd-test"); + ASSERT_GE(session_fd, 0); + + mem_fd =3D memfd_create("test-memfd", 0); + ASSERT_GE(mem_fd, 0); + + ASSERT_EQ(write(mem_fd, test_str, strlen(test_str)), strlen(test_str)); + ASSERT_EQ(preserve_fd(session_fd, mem_fd, 0x1234), 0); + ASSERT_EQ(close(session_fd), 0); + + ASSERT_EQ(lseek(mem_fd, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd, read_buf, sizeof(read_buf)), strlen(test_str)); + ASSERT_STREQ(read_buf, test_str); + ASSERT_EQ(close(mem_fd), 0); +} + +/* + * Test Case: Preserve Multiple MemFDs + * + * Verifies that multiple memfds can be preserved in a single session, + * each with a unique token, and that their contents remain distinct and + * correct after preservation. + */ +TEST_F(liveupdate_device, preserve_multiple_memfds) +{ + const char *test_str1 =3D "data for memfd one"; + const char *test_str2 =3D "data for memfd two"; + char read_buf[64] =3D {}; + int session_fd, mem_fd1, mem_fd2; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd =3D create_session(self->fd1, "preserve-multi-memfd-test"); + ASSERT_GE(session_fd, 0); + + mem_fd1 =3D memfd_create("test-memfd-1", 0); + ASSERT_GE(mem_fd1, 0); + mem_fd2 =3D memfd_create("test-memfd-2", 0); + ASSERT_GE(mem_fd2, 0); + + ASSERT_EQ(write(mem_fd1, test_str1, strlen(test_str1)), strlen(test_str1)= ); + ASSERT_EQ(write(mem_fd2, test_str2, strlen(test_str2)), strlen(test_str2)= ); + + ASSERT_EQ(preserve_fd(session_fd, mem_fd1, 0xAAAA), 0); + ASSERT_EQ(preserve_fd(session_fd, mem_fd2, 0xBBBB), 0); + + memset(read_buf, 0, sizeof(read_buf)); + ASSERT_EQ(lseek(mem_fd1, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd1, read_buf, sizeof(read_buf)), strlen(test_str1)); + ASSERT_STREQ(read_buf, test_str1); + + memset(read_buf, 0, sizeof(read_buf)); + ASSERT_EQ(lseek(mem_fd2, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd2, read_buf, sizeof(read_buf)), strlen(test_str2)); + ASSERT_STREQ(read_buf, test_str2); + + ASSERT_EQ(close(mem_fd1), 0); + ASSERT_EQ(close(mem_fd2), 0); + ASSERT_EQ(close(session_fd), 0); +} + +/* + * Test Case: Preserve Complex Scenario + * + * Verifies a more complex scenario with multiple sessions and a mix of em= pty + * and non-empty memfds distributed across them. + */ +TEST_F(liveupdate_device, preserve_complex_scenario) +{ + const char *data1 =3D "data for session 1"; + const char *data2 =3D "data for session 2"; + char read_buf[64] =3D {}; + int session_fd1, session_fd2; + int mem_fd_data1, mem_fd_empty1, mem_fd_data2, mem_fd_empty2; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd1 =3D create_session(self->fd1, "complex-session-1"); + ASSERT_GE(session_fd1, 0); + session_fd2 =3D create_session(self->fd1, "complex-session-2"); + ASSERT_GE(session_fd2, 0); + + mem_fd_data1 =3D memfd_create("data1", 0); + ASSERT_GE(mem_fd_data1, 0); + ASSERT_EQ(write(mem_fd_data1, data1, strlen(data1)), strlen(data1)); + + mem_fd_empty1 =3D memfd_create("empty1", 0); + ASSERT_GE(mem_fd_empty1, 0); + + mem_fd_data2 =3D memfd_create("data2", 0); + ASSERT_GE(mem_fd_data2, 0); + ASSERT_EQ(write(mem_fd_data2, data2, strlen(data2)), strlen(data2)); + + mem_fd_empty2 =3D memfd_create("empty2", 0); + ASSERT_GE(mem_fd_empty2, 0); + + ASSERT_EQ(preserve_fd(session_fd1, mem_fd_data1, 0x1111), 0); + ASSERT_EQ(preserve_fd(session_fd1, mem_fd_empty1, 0x2222), 0); + ASSERT_EQ(preserve_fd(session_fd2, mem_fd_data2, 0x3333), 0); + ASSERT_EQ(preserve_fd(session_fd2, mem_fd_empty2, 0x4444), 0); + + ASSERT_EQ(lseek(mem_fd_data1, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd_data1, read_buf, sizeof(read_buf)), strlen(data1)); + ASSERT_STREQ(read_buf, data1); + + memset(read_buf, 0, sizeof(read_buf)); + ASSERT_EQ(lseek(mem_fd_data2, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd_data2, read_buf, sizeof(read_buf)), strlen(data2)); + ASSERT_STREQ(read_buf, data2); + + ASSERT_EQ(lseek(mem_fd_empty1, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd_empty1, read_buf, sizeof(read_buf)), 0); + + ASSERT_EQ(lseek(mem_fd_empty2, 0, SEEK_SET), 0); + ASSERT_EQ(read(mem_fd_empty2, read_buf, sizeof(read_buf)), 0); + + ASSERT_EQ(close(mem_fd_data1), 0); + ASSERT_EQ(close(mem_fd_empty1), 0); + ASSERT_EQ(close(mem_fd_data2), 0); + ASSERT_EQ(close(mem_fd_empty2), 0); + ASSERT_EQ(close(session_fd1), 0); + ASSERT_EQ(close(session_fd2), 0); +} + +TEST_HARNESS_MAIN --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (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 A6577346FA7 for ; Fri, 7 Nov 2025 21:06:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549576; cv=none; b=OhKb1f1NMJ9YEbhyEjWSofLmgogQpC7VtH7ZvrU/UmWlTqFYBDKnLff792y/5G0rho6A6d4T9q3+jy4WB0lkVqFGjzCHqzhoSEF9ra0pNNxwxy6hUj9HLJSytCQ7WZ2PRCNAYatdR++EvrgdvCkZYEAtDiYNFnrVdt4ITXYIdr0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549576; c=relaxed/simple; bh=ex9Ty+ajRZX6NEjdJenwudqrabq/5m4ytLE7jyNiaXU=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qXgUJBKJWcbgdmWKNj4xOoxaoUhVPbute3DY7GQOBWhnOsEr0KXRXejY91QZRb39ALqZR+ep/HWZ6x6J5scZE3PehaC/U1R0lN9iKaNvbCxmZdOcyfadpIJQkeHMun9b66Qv3ho4xb0Ea8PKRDqo5/XGoWWxrxesI0icDJWlUIU= 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=U8dLtQtY; arc=none smtp.client-ip=209.85.128.170 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="U8dLtQtY" Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-7869819394aso11261367b3.3 for ; Fri, 07 Nov 2025 13:06:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549571; x=1763154371; 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=wMSpKE1l2D+pbDUsxKcM6He86wroChEvKIhAzF63c/s=; b=U8dLtQtYJs1I6q+RGrFKe1vUk5v/5xhK9ST8sjPUhZYOHtU4P48MxrGHE7vzVq2t/O KOG/r3hHe2uXaJF7xomMuLV7wEeipasxJwanzqRqOjCTjxrAlPNuyJMkr2U+CWV+XXoQ RG1M0i4OIzW4uh2wtLjeGW7tDl62xc80TZ3xpOiPHEGGQVmd1VPhsm4eJxTYfC72iP5a tkTMpF+C3+DKySxu3d+bmrC1PGtucNg50X4TGn7ceRXxRXXBPpYCI70T+CczdcvCLAQ1 iJofcNmB5cK4Ao/5olGvVxSb1/aQKDtdQEptSPJI4K2pL+X84lhtlE2IKEHs/LfrZiyd EEwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549571; x=1763154371; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=wMSpKE1l2D+pbDUsxKcM6He86wroChEvKIhAzF63c/s=; b=r90a8VgDl4eDWsuABDFDMo0vETkBxORjUqDgQ35mcuC/tV/NH1ALuMwUipCOwoCyJd j74E8HVaHO905NR5Sefldqb56xK3EDw4mEHAHPCnXKxBfmwtM+Bysc7IHCa0sCl2Q3pu J30mjRyCLQWR0dtzezsRTs+rm4J+WeT8RVbxVQN+iRv3BFV8qT7Kmwds26IKtY6HT5jr kovJR9cx/k78v1v/9+KkFyqVqcM/S4BmxApWAByOIqajbO7SE6HGCVo/YoInPKyclCex JB6UY0gNoQFx71IwLJHD/3Kf+Yog7ToPpedh6yEQECLbAGhH0hALodk2gUJwVv8RRdaT pYmQ== X-Forwarded-Encrypted: i=1; AJvYcCUuNl9qNkkCPmPC0T4RFWcDFRaknFwzAv4A0RE/u1PweseIhbst+OVhmTuEak9Ai3ThXIbf5JLs8Ila7HY=@vger.kernel.org X-Gm-Message-State: AOJu0YwvqZCl9F4bqyridzFfSCQy2aZSpwZYvN6/xtq3L2fs12+tjHV/ EHG3zRj1Mp3uugQrEZcNJ0+JRedNuLZp3WePI4aCD/DFjkvLjZp3w3j9DBV7Mn9YS2U= X-Gm-Gg: ASbGncs4yober545ny8SxDJJCjtMEti/EBc1xGK75n40fAzwbEKSZyjyX+mGTPbat8M xerU7RizLypkfxozTNckrWMQJMgwQBCum+NUFsb8hIjcEUT+d4NmkLumVVF595hFK6wFL/Wiuo0 ZrwVHBNuKO9aTWgKoarZrPWCCwmTKV1+WtF0LZxg5SNHVD/mobgnKh+fzpmwMVthxQhA7CSt8Sz qOaLupFh4koVB0wcgLqPOz0L+OM9ZGbGahlSTFsRzsWY85O9g6+7Qm0ai32oVwjskkaIFHkvkbA pH3HFsYdVQBe96QZTiA19mjhuTl42zvVCQXLsApLmI42z9ZyKBt6Agh0M54EVt0IOKrPNKHN0SM 1uYU03mlY1T70MMOMfmmPSGR32UHQzmmFfI0COtXv9zPsU4kgp+gMASJ2es1M8R1Rruj7X8//Z8 UnQsN7jTISlDOiXhJERxhpB76rg+Ar5db78jnHRT4oRPZInkC+HrLlrMIx/DvxXpk= X-Google-Smtp-Source: AGHT+IFflyRO6w1q3DXVe7O2dxbNRv5YmoMD7A2svs64snuag/XfdkXNTRwcLqUkAwpierKFK5vHIg== X-Received: by 2002:a05:690c:4d09:b0:786:4fbb:642 with SMTP id 00721157ae682-787d548410fmr7640347b3.60.1762549571027; Fri, 07 Nov 2025 13:06:11 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:10 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 20/22] selftests/liveupdate: Add kexec-based selftest for session lifecycle Date: Fri, 7 Nov 2025 16:03:18 -0500 Message-ID: <20251107210526.257742-21-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Introduce a kexec-based selftest, luo_kexec_simple, to validate the end-to-end lifecycle of a Live Update Orchestrator (LUO) session across a reboot. While existing tests verify the uAPI in a pre-reboot context, this test ensures that the core functionality=E2=80=94preserving state via Kexec Hand= over and restoring it in a new kernel=E2=80=94works as expected. The test operates in two stages, managing its state across the reboot by preserving a dedicated "state session" containing a memfd. This mechanism dogfoods the LUO feature itself for state tracking, making the test self-contained. The test validates the following sequence: Stage 1 (Pre-kexec): - Creates a test session (test-session). - Creates and preserves a memfd with a known data pattern into the test session. - Creates the state-tracking session to signal progression to Stage 2. - Executes a kexec reboot via a helper script. Stage 2 (Post-kexec): - Retrieves the state-tracking session to confirm it is in the post-reboot stage. - Retrieves the preserved test session. - Restores the memfd from the test session and verifies its contents match the original data pattern written in Stage 1. - Finalizes both the test and state sessions to ensure a clean teardown. The test relies on a helper script (do_kexec.sh) to perform the reboot and a shared utility library (luo_test_utils.c) for common LUO operations, keeping the main test logic clean and focused. Signed-off-by: Pasha Tatashin --- tools/testing/selftests/liveupdate/.gitignore | 1 + tools/testing/selftests/liveupdate/Makefile | 32 ++++ .../testing/selftests/liveupdate/do_kexec.sh | 6 + .../selftests/liveupdate/luo_kexec_simple.c | 114 ++++++++++++ .../selftests/liveupdate/luo_test_utils.c | 168 ++++++++++++++++++ .../selftests/liveupdate/luo_test_utils.h | 39 ++++ 6 files changed, 360 insertions(+) create mode 100755 tools/testing/selftests/liveupdate/do_kexec.sh create mode 100644 tools/testing/selftests/liveupdate/luo_kexec_simple.c create mode 100644 tools/testing/selftests/liveupdate/luo_test_utils.c create mode 100644 tools/testing/selftests/liveupdate/luo_test_utils.h diff --git a/tools/testing/selftests/liveupdate/.gitignore b/tools/testing/= selftests/liveupdate/.gitignore index af6e773cf98f..daeef116174d 100644 --- a/tools/testing/selftests/liveupdate/.gitignore +++ b/tools/testing/selftests/liveupdate/.gitignore @@ -1 +1,2 @@ /liveupdate +/luo_kexec_simple diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/se= lftests/liveupdate/Makefile index 2a573c36016e..1563ac84006a 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -1,7 +1,39 @@ # SPDX-License-Identifier: GPL-2.0-only + +KHDR_INCLUDES ?=3D -I../../../../usr/include CFLAGS +=3D -Wall -O2 -Wno-unused-function CFLAGS +=3D $(KHDR_INCLUDES) +LDFLAGS +=3D -static +OUTPUT ?=3D . + +# --- Test Configuration (Edit this section when adding new tests) --- +LUO_SHARED_SRCS :=3D luo_test_utils.c +LUO_SHARED_HDRS +=3D luo_test_utils.h + +LUO_MANUAL_TESTS +=3D luo_kexec_simple + +TEST_FILES +=3D do_kexec.sh =20 TEST_GEN_PROGS +=3D liveupdate =20 +# --- Automatic Rule Generation (Do not edit below) --- + +TEST_GEN_PROGS_EXTENDED +=3D $(LUO_MANUAL_TESTS) + +# Define the full list of sources for each manual test. +$(foreach test,$(LUO_MANUAL_TESTS), \ + $(eval $(test)_SOURCES :=3D $(test).c $(LUO_SHARED_SRCS))) + +# This loop automatically generates an explicit build rule for each manual= test. +# It includes dependencies on the shared headers and makes the output +# executable. +# Note the use of '$$' to escape automatic variables for the 'eval' comman= d. +$(foreach test,$(LUO_MANUAL_TESTS), \ + $(eval $(OUTPUT)/$(test): $($(test)_SOURCES) $(LUO_SHARED_HDRS) \ + $(call msg,LINK,,$$@) ; \ + $(Q)$(LINK.c) $$^ $(LDLIBS) -o $$@ ; \ + $(Q)chmod +x $$@ \ + ) \ +) + include ../lib.mk diff --git a/tools/testing/selftests/liveupdate/do_kexec.sh b/tools/testing= /selftests/liveupdate/do_kexec.sh new file mode 100755 index 000000000000..bb396a92c3b8 --- /dev/null +++ b/tools/testing/selftests/liveupdate/do_kexec.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +set -e + +kexec -l -s --reuse-cmdline /boot/bzImage +kexec -e diff --git a/tools/testing/selftests/liveupdate/luo_kexec_simple.c b/tools/= testing/selftests/liveupdate/luo_kexec_simple.c new file mode 100644 index 000000000000..67ab6ebf9eec --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_kexec_simple.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + * + * A simple selftest to validate the end-to-end lifecycle of a LUO session + * across a single kexec reboot. + */ + +#include "luo_test_utils.h" + +/* Test-specific constants are now defined locally */ +#define KEXEC_SCRIPT "./do_kexec.sh" +#define TEST_SESSION_NAME "test-session" +#define TEST_MEMFD_TOKEN 0x1A +#define TEST_MEMFD_DATA "hello kexec world" + +/* Constants for the state-tracking mechanism, specific to this test file.= */ +#define STATE_SESSION_NAME "kexec_simple_state" +#define STATE_MEMFD_TOKEN 999 + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int session_fd; + + ksft_print_msg("[STAGE 1] Starting pre-kexec setup...\n"); + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating session '%s' and preserving memfd...\n= ", + TEST_SESSION_NAME); + session_fd =3D luo_create_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_create_session for '%s'", TEST_SESSION_NAME); + + if (create_and_preserve_memfd(session_fd, TEST_MEMFD_TOKEN, + TEST_MEMFD_DATA) < 0) { + fail_exit("create_and_preserve_memfd for token %#x", + TEST_MEMFD_TOKEN); + } + + ksft_print_msg("[STAGE 1] Executing kexec...\n"); + if (system(KEXEC_SCRIPT) !=3D 0) + fail_exit("kexec script failed"); + exit(EXIT_FAILURE); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int session_fd, mfd, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage !=3D 2) + fail_exit("Expected stage 2, but state file contains %d", stage); + + ksft_print_msg("[STAGE 2] Retrieving session '%s'...\n", TEST_SESSION_NAM= E); + session_fd =3D luo_retrieve_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_retrieve_session for '%s'", TEST_SESSION_NAME); + + ksft_print_msg("[STAGE 2] Restoring and verifying memfd (token %#x)...\n", + TEST_MEMFD_TOKEN); + mfd =3D restore_and_verify_memfd(session_fd, TEST_MEMFD_TOKEN, + TEST_MEMFD_DATA); + if (mfd < 0) + fail_exit("restore_and_verify_memfd for token %#x", TEST_MEMFD_TOKEN); + close(mfd); + + ksft_print_msg("[STAGE 2] Test data verified successfully.\n"); + ksft_print_msg("[STAGE 2] Finalizing test session...\n"); + if (luo_session_finish(session_fd) < 0) + fail_exit("luo_session_finish for test session"); + close(session_fd); + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- SIMPLE KEXEC TEST PASSED ---\n"); +} + +int main(int argc, char *argv[]) +{ + int luo_fd; + int state_session_fd; + + luo_fd =3D luo_open_device(); + if (luo_fd < 0) + ksft_exit_skip("Failed to open %s. Is the luo module loaded?\n", + LUO_DEVICE); + + /* + * Determine the stage by attempting to retrieve the state session. + * If it doesn't exist (ENOENT), we are in Stage 1 (pre-kexec). + */ + state_session_fd =3D luo_retrieve_session(luo_fd, STATE_SESSION_NAME); + if (state_session_fd =3D=3D -ENOENT) { + run_stage_1(luo_fd); + } else if (state_session_fd >=3D 0) { + /* We got a valid handle, pass it directly to stage 2 */ + run_stage_2(luo_fd, state_session_fd); + } else { + fail_exit("Failed to check for state session"); + } + + close(luo_fd); +} diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.c b/tools/te= sting/selftests/liveupdate/luo_test_utils.c new file mode 100644 index 000000000000..0a24105cbc54 --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_test_utils.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "luo_test_utils.h" + +int luo_open_device(void) +{ + return open(LUO_DEVICE, O_RDWR); +} + +int luo_create_session(int luo_fd, const char *name) +{ + struct liveupdate_ioctl_create_session arg =3D { .size =3D sizeof(arg) }; + + snprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, "%.*s", + LIVEUPDATE_SESSION_NAME_LENGTH - 1, name); + + if (ioctl(luo_fd, LIVEUPDATE_IOCTL_CREATE_SESSION, &arg) < 0) + return -errno; + + return arg.fd; +} + +int luo_retrieve_session(int luo_fd, const char *name) +{ + struct liveupdate_ioctl_retrieve_session arg =3D { .size =3D sizeof(arg) = }; + + snprintf((char *)arg.name, LIVEUPDATE_SESSION_NAME_LENGTH, "%.*s", + LIVEUPDATE_SESSION_NAME_LENGTH - 1, name); + + if (ioctl(luo_fd, LIVEUPDATE_IOCTL_RETRIEVE_SESSION, &arg) < 0) + return -errno; + + return arg.fd; +} + +int create_and_preserve_memfd(int session_fd, int token, const char *data) +{ + struct liveupdate_session_preserve_fd arg =3D { .size =3D sizeof(arg) }; + long page_size =3D sysconf(_SC_PAGE_SIZE); + void *map =3D MAP_FAILED; + int mfd =3D -1, ret =3D -1; + + mfd =3D memfd_create("test_mfd", 0); + if (mfd < 0) + return -errno; + + if (ftruncate(mfd, page_size) !=3D 0) + goto out; + + map =3D mmap(NULL, page_size, PROT_WRITE, MAP_SHARED, mfd, 0); + if (map =3D=3D MAP_FAILED) + goto out; + + snprintf(map, page_size, "%s", data); + munmap(map, page_size); + + arg.fd =3D mfd; + arg.token =3D token; + if (ioctl(session_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &arg) < 0) + goto out; + + ret =3D 0; +out: + if (ret !=3D 0 && errno !=3D 0) + ret =3D -errno; + if (mfd >=3D 0) + close(mfd); + return ret; +} + +int restore_and_verify_memfd(int session_fd, int token, + const char *expected_data) +{ + struct liveupdate_session_retrieve_fd arg =3D { .size =3D sizeof(arg) }; + long page_size =3D sysconf(_SC_PAGE_SIZE); + void *map =3D MAP_FAILED; + int mfd =3D -1, ret =3D -1; + + arg.token =3D token; + if (ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_FD, &arg) < 0) + return -errno; + mfd =3D arg.fd; + + map =3D mmap(NULL, page_size, PROT_READ, MAP_SHARED, mfd, 0); + if (map =3D=3D MAP_FAILED) + goto out; + + if (expected_data && strcmp(expected_data, map) !=3D 0) { + ksft_print_msg("Data mismatch! Expected '%s', Got '%s'\n", + expected_data, (char *)map); + ret =3D -EINVAL; + goto out_munmap; + } + + ret =3D mfd; +out_munmap: + munmap(map, page_size); +out: + if (ret < 0 && errno !=3D 0) + ret =3D -errno; + if (ret < 0 && mfd >=3D 0) + close(mfd); + return ret; +} + +int luo_session_finish(int session_fd) +{ + struct liveupdate_session_finish arg =3D { .size =3D sizeof(arg) }; + + if (ioctl(session_fd, LIVEUPDATE_SESSION_FINISH, &arg) < 0) + return -errno; + + return 0; +} + +void create_state_file(int luo_fd, const char *session_name, int token, + int next_stage) +{ + char buf[32]; + int state_session_fd; + + state_session_fd =3D luo_create_session(luo_fd, session_name); + if (state_session_fd < 0) + fail_exit("luo_create_session for state tracking"); + + snprintf(buf, sizeof(buf), "%d", next_stage); + if (create_and_preserve_memfd(state_session_fd, token, buf) < 0) + fail_exit("create_and_preserve_memfd for state tracking"); + + /* + * DO NOT close session FD, otherwise it is going to be unpreserved + */ +} + +void restore_and_read_stage(int state_session_fd, int token, int *stage) +{ + char buf[32] =3D {0}; + int mfd; + + mfd =3D restore_and_verify_memfd(state_session_fd, token, NULL); + if (mfd < 0) + fail_exit("failed to restore state memfd"); + + if (read(mfd, buf, sizeof(buf) - 1) < 0) + fail_exit("failed to read state mfd"); + + *stage =3D atoi(buf); + + close(mfd); +} diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.h b/tools/te= sting/selftests/liveupdate/luo_test_utils.h new file mode 100644 index 000000000000..093e787b9f4b --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_test_utils.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + * + * Utility functions for LUO kselftests. + */ + +#ifndef LUO_TEST_UTILS_H +#define LUO_TEST_UTILS_H + +#include +#include +#include +#include "../kselftest.h" + +#define LUO_DEVICE "/dev/liveupdate" + +#define fail_exit(fmt, ...) \ + ksft_exit_fail_msg("[%s:%d] " fmt " (errno: %s)\n", \ + __func__, __LINE__, ##__VA_ARGS__, strerror(errno)) + +/* Generic LUO and session management helpers */ +int luo_open_device(void); +int luo_create_session(int luo_fd, const char *name); +int luo_retrieve_session(int luo_fd, const char *name); +int luo_session_finish(int session_fd); + +/* Generic file preservation and restoration helpers */ +int create_and_preserve_memfd(int session_fd, int token, const char *data); +int restore_and_verify_memfd(int session_fd, int token, const char *expect= ed_data); + +/* Kexec state-tracking helpers */ +void create_state_file(int luo_fd, const char *session_name, int token, + int next_stage); +void restore_and_read_stage(int state_session_fd, int token, int *stage); + +#endif /* LUO_TEST_UTILS_H */ --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f173.google.com (mail-yw1-f173.google.com [209.85.128.173]) (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 49E8C347BBD for ; Fri, 7 Nov 2025 21:06:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549577; cv=none; b=smFGGh260jmxLqBtqqD4DywD3HmL9lHWrkHAAsIbDCqUv+LW7RbJKQfaYvxEbCmt69d4TIMLOfvCXgWHr0ZiSQXwjzOlpNr6gCE88o6oTpusHDnYXgOxaI6OuJkLwieChmMfiCA04ds16ZR8y0aFza+aUo/msKtM6JAu8+Wsxcw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549577; c=relaxed/simple; bh=1wTqPfHzUHv0FGZcXb2vOKDwv2ZgcOIsvnW2P64RZwo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RE7866YtPBjDQ9her7f7DhfyAjNEAsiNVn98lzDNnH8JpMprJrBuMIZHIiF+7OfRU1OZqZC1VYnFCPEgnF3Y9G/D5l0WpvU0mUHaDP0pRNpu4fYRvNKgNFa1FC24gn89UXBvmNCijhuw3aB5NIuRcDBJoki9CCH9eMUB5nKFcR8= 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=O8X+VxWq; arc=none smtp.client-ip=209.85.128.173 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="O8X+VxWq" Received: by mail-yw1-f173.google.com with SMTP id 00721157ae682-7866e9e62e1so12765337b3.2 for ; Fri, 07 Nov 2025 13:06:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549573; x=1763154373; 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=qjuXuvmfL1K8By8qdYZh9RjNKTHIKCPX1bM6iJVu4Us=; b=O8X+VxWqjmCDzqn46G0VibAWreLK+4mK/GkgkUX4er9cYe+Ve6g2sS9weXW1owk8H7 it3ujAKLyvOO8H0H97oxEQyQZu8haFwb5Lc8MhmsEq5qRsDHYFmrxfWqtg1djt/XxC5G lfKtNF400o+HKw5FnQp81rPSjqRq6zN+9zpSrdqIuMU/ktIxgrAByt3ArMs/hc8FS7Uk LUqhXuDM5J6zHHcfo1qlVy4hubgn+nBnx+RjoJCvrEoJAP8qvIYVyLqDHuTIawgmRaPA HaiXShU5b0ck80mf0BBKDdnGB7Bp8FEgd1y49HEpPKtz4P/fA2dZnEO4haEWhGHuZaUi kPyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549573; x=1763154373; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=qjuXuvmfL1K8By8qdYZh9RjNKTHIKCPX1bM6iJVu4Us=; b=XUwWJ4Zjk4pNi1b+sl4DbubyvC6GqxHgjA3J7c3utesIYXFcRMcBaMYR/CMutt7R+l CudLlsVlIHFdmas4jwdgrReUt5YsLMEvqOAG4WII1C1yeM2b4XahktPNwRIhz0WK9URh 20dszNt00sCVsWlqaR8rOH5bJUz0valhlHR/1Q00EwEiFfHMxY8XHWs+VIKYR02/kk3i Ce46EHu+6eOBimL4aeCm+2VbrIQWUdFPW2+ewOIyBBp7LWqWT/4hdPIGcoNoLsvTgM8P 3T6Xf7HkOshj/UE/aV9Q3wFa72T0dP52p8FdrfwjMBVxv8UpvNY4G1TqV0PlBecLhAWD D40A== X-Forwarded-Encrypted: i=1; AJvYcCVtmW+nQeU73gAMCue/d3mzzjyb51gFF3Uyp/WwDBMtSygJQzN+phIGgH3SsoENaDKM5YYYcirCYdZrJUQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxN7uFHlRLLcvJ1kAckKmjyX5SpZQPRCHNatKZhs2QqXtfazlwo a9uC76+McQkvkfeNIHj8QGcnE3YcIo5CPmRp2vRCpKu+CvQkMkxt1YBvr0DeB+/n5dk= X-Gm-Gg: ASbGncuzgnu1gzx0Gt1UOyMP65BoEZ7sCXnX/L0QoussxCfUMGTwoSGyXH+HxsEXr4g oGw0CzfcWMpJw7OVlsUgZGdrjfzhjR3yIVGeoIntPniBp4rK/giEpyRu6QF8Z4dPh+Ive+Z0sG5 QB7nxWEKQ17d25nV7PBEEhutf48E4tLb2cB04MZnsWAlYuKz9gNVSKjKqBy1FDF5O2AvnRF7/kN pyMP5UdoTy247aPoyg9Tg/or7OOkgaz78/CWUcIhg9M4FxvNSLdj+aU/bfs3cOqbKfl/mvtL0x7 p7RnV4XYKO5ZbAImZnyT2gwQKnbKGAE+QNnvlDwjxyHidLbFKxqyP+oTc3ZFnG7tbWMBg/pxlzk HC+lruAKX7gu7MTgtCT+MzD1aHOg+AiKWUhlkX43YNGgYnKQVC6kDpu5JtaRkuAvWHFhw+6keH9 56ue/NJC26xx9gsBM2npp4LPzdI9Z6PX3H0/aPqzmCDmF/de8LFXFRrmB/DmfAPYIQgfyI/93a1 A== X-Google-Smtp-Source: AGHT+IE4r8jHOQKB/UiTHgSqUgazEpkLY0csKmQ3EsY7bV+rYNEGHwWx8lwO5MiuiV1LIw2GfopjMw== X-Received: by 2002:a05:690c:6385:b0:786:7017:9506 with SMTP id 00721157ae682-787d541b17emr8282027b3.43.1762549573027; Fri, 07 Nov 2025 13:06:13 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:12 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 21/22] selftests/liveupdate: Add kexec test for multiple and empty sessions Date: Fri, 7 Nov 2025 16:03:19 -0500 Message-ID: <20251107210526.257742-22-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 a new kexec-based selftest, luo_kexec_multi_session, to validate the end-to-end lifecycle of a more complex LUO scenario. While the existing luo_kexec_simple test covers the basic end-to-end lifecycle, it is limited to a single session with one preserved file. This new test significantly expands coverage by verifying LUO's ability to handle a mixed workload involving multiple sessions, some of which are intentionally empty. This ensures that the LUO core correctly preserves and restores the state of all session types across a reboot. The test validates the following sequence: Stage 1 (Pre-kexec): - Creates two empty test sessions (multi-test-empty-1, multi-test-empty-2). - Creates a session with one preserved memfd (multi-test-files-1). - Creates another session with two preserved memfds (multi-test-files-2), each containing unique data. - Creates a state-tracking session to manage the transition to Stage 2. - Executes a kexec reboot via the helper script. Stage 2 (Post-kexec): - Retrieves the state-tracking session to confirm it is in the post-reboot stage. - Retrieves all four test sessions (both the empty and non-empty ones). - For the non-empty sessions, restores the preserved memfds and verifies their contents match the original data patterns. - Finalizes all test sessions and the state session to ensure a clean teardown and that all associated kernel resources are correctly released. This test provides greater confidence in the robustness of the LUO framework by validating its behavior in a more realistic, multi-faceted scenario. Signed-off-by: Pasha Tatashin --- tools/testing/selftests/liveupdate/.gitignore | 1 + tools/testing/selftests/liveupdate/Makefile | 1 + .../selftests/liveupdate/luo_multi_session.c | 190 ++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/luo_multi_session.c diff --git a/tools/testing/selftests/liveupdate/.gitignore b/tools/testing/= selftests/liveupdate/.gitignore index daeef116174d..42a15a8d5d9e 100644 --- a/tools/testing/selftests/liveupdate/.gitignore +++ b/tools/testing/selftests/liveupdate/.gitignore @@ -1,2 +1,3 @@ /liveupdate /luo_kexec_simple +/luo_multi_session diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/se= lftests/liveupdate/Makefile index 1563ac84006a..6ee6efeec62d 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -11,6 +11,7 @@ LUO_SHARED_SRCS :=3D luo_test_utils.c LUO_SHARED_HDRS +=3D luo_test_utils.h =20 LUO_MANUAL_TESTS +=3D luo_kexec_simple +LUO_MANUAL_TESTS +=3D luo_multi_session =20 TEST_FILES +=3D do_kexec.sh =20 diff --git a/tools/testing/selftests/liveupdate/luo_multi_session.c b/tools= /testing/selftests/liveupdate/luo_multi_session.c new file mode 100644 index 000000000000..c9955f1b6e97 --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_multi_session.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + * + * A selftest to validate the end-to-end lifecycle of multiple LUO sessions + * across a kexec reboot, including empty sessions and sessions with multi= ple + * files. + */ + +#include "luo_test_utils.h" + +#define KEXEC_SCRIPT "./do_kexec.sh" + +#define SESSION_EMPTY_1 "multi-test-empty-1" +#define SESSION_EMPTY_2 "multi-test-empty-2" +#define SESSION_FILES_1 "multi-test-files-1" +#define SESSION_FILES_2 "multi-test-files-2" + +#define MFD1_TOKEN 0x1001 +#define MFD2_TOKEN 0x2002 +#define MFD3_TOKEN 0x3003 + +#define MFD1_DATA "Data for session files 1" +#define MFD2_DATA "First file for session files 2" +#define MFD3_DATA "Second file for session files 2" + +#define STATE_SESSION_NAME "kexec_multi_state" +#define STATE_MEMFD_TOKEN 998 + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int s_empty1_fd, s_empty2_fd, s_files1_fd, s_files2_fd; + + ksft_print_msg("[STAGE 1] Starting pre-kexec setup for multi-session test= ...\n"); + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating empty sessions '%s' and '%s'...\n", + SESSION_EMPTY_1, SESSION_EMPTY_2); + s_empty1_fd =3D luo_create_session(luo_fd, SESSION_EMPTY_1); + if (s_empty1_fd < 0) + fail_exit("luo_create_session for '%s'", SESSION_EMPTY_1); + + s_empty2_fd =3D luo_create_session(luo_fd, SESSION_EMPTY_2); + if (s_empty2_fd < 0) + fail_exit("luo_create_session for '%s'", SESSION_EMPTY_2); + + ksft_print_msg("[STAGE 1] Creating session '%s' with one memfd...\n", + SESSION_FILES_1); + + s_files1_fd =3D luo_create_session(luo_fd, SESSION_FILES_1); + if (s_files1_fd < 0) + fail_exit("luo_create_session for '%s'", SESSION_FILES_1); + if (create_and_preserve_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA) < 0) { + fail_exit("create_and_preserve_memfd for token %#x", + MFD1_TOKEN); + } + + ksft_print_msg("[STAGE 1] Creating session '%s' with two memfds...\n", + SESSION_FILES_2); + + s_files2_fd =3D luo_create_session(luo_fd, SESSION_FILES_2); + if (s_files2_fd < 0) + fail_exit("luo_create_session for '%s'", SESSION_FILES_2); + if (create_and_preserve_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA) < 0) { + fail_exit("create_and_preserve_memfd for token %#x", + MFD2_TOKEN); + } + if (create_and_preserve_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA) < 0) { + fail_exit("create_and_preserve_memfd for token %#x", + MFD3_TOKEN); + } + + ksft_print_msg("[STAGE 1] Executing kexec...\n"); + + if (system(KEXEC_SCRIPT) !=3D 0) + fail_exit("kexec script failed"); + + exit(EXIT_FAILURE); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int s_empty1_fd, s_empty2_fd, s_files1_fd, s_files2_fd; + int mfd1, mfd2, mfd3, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage !=3D 2) { + fail_exit("Expected stage 2, but state file contains %d", + stage); + } + + ksft_print_msg("[STAGE 2] Retrieving all sessions...\n"); + s_empty1_fd =3D luo_retrieve_session(luo_fd, SESSION_EMPTY_1); + if (s_empty1_fd < 0) + fail_exit("luo_retrieve_session for '%s'", SESSION_EMPTY_1); + + s_empty2_fd =3D luo_retrieve_session(luo_fd, SESSION_EMPTY_2); + if (s_empty2_fd < 0) + fail_exit("luo_retrieve_session for '%s'", SESSION_EMPTY_2); + + s_files1_fd =3D luo_retrieve_session(luo_fd, SESSION_FILES_1); + if (s_files1_fd < 0) + fail_exit("luo_retrieve_session for '%s'", SESSION_FILES_1); + + s_files2_fd =3D luo_retrieve_session(luo_fd, SESSION_FILES_2); + if (s_files2_fd < 0) + fail_exit("luo_retrieve_session for '%s'", SESSION_FILES_2); + + ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n", + SESSION_FILES_1); + mfd1 =3D restore_and_verify_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA); + if (mfd1 < 0) + fail_exit("restore_and_verify_memfd for token %#x", MFD1_TOKEN); + close(mfd1); + + ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n", + SESSION_FILES_2); + + mfd2 =3D restore_and_verify_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA); + if (mfd2 < 0) + fail_exit("restore_and_verify_memfd for token %#x", MFD2_TOKEN); + close(mfd2); + + mfd3 =3D restore_and_verify_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA); + if (mfd3 < 0) + fail_exit("restore_and_verify_memfd for token %#x", MFD3_TOKEN); + close(mfd3); + + ksft_print_msg("[STAGE 2] Test data verified successfully.\n"); + + ksft_print_msg("[STAGE 2] Finalizing all test sessions...\n"); + if (luo_session_finish(s_empty1_fd) < 0) + fail_exit("luo_session_finish for '%s'", SESSION_EMPTY_1); + close(s_empty1_fd); + + if (luo_session_finish(s_empty2_fd) < 0) + fail_exit("luo_session_finish for '%s'", SESSION_EMPTY_2); + close(s_empty2_fd); + + if (luo_session_finish(s_files1_fd) < 0) + fail_exit("luo_session_finish for '%s'", SESSION_FILES_1); + close(s_files1_fd); + + if (luo_session_finish(s_files2_fd) < 0) + fail_exit("luo_session_finish for '%s'", SESSION_FILES_2); + close(s_files2_fd); + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- MULTI-SESSION KEXEC TEST PASSED ---\n"); +} + +int main(int argc, char *argv[]) +{ + int luo_fd; + int state_session_fd; + + luo_fd =3D luo_open_device(); + if (luo_fd < 0) + ksft_exit_skip("Failed to open %s. Is the luo module loaded?\n", + LUO_DEVICE); + + /* + * Determine the stage by attempting to retrieve the state session. + * If it doesn't exist (ENOENT), we are in Stage 1 (pre-kexec). + */ + state_session_fd =3D luo_retrieve_session(luo_fd, STATE_SESSION_NAME); + if (state_session_fd =3D=3D -ENOENT) { + run_stage_1(luo_fd); + } else if (state_session_fd >=3D 0) { + /* We got a valid handle, pass it directly to stage 2 */ + run_stage_2(luo_fd, state_session_fd); + } else { + fail_exit("Failed to check for state session"); + } + + close(luo_fd); + return 0; +} --=20 2.51.2.1041.gc1ab5b90ca-goog From nobody Mon Feb 9 08:33:13 2026 Received: from mail-yw1-f170.google.com (mail-yw1-f170.google.com [209.85.128.170]) (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 8322B34845C for ; Fri, 7 Nov 2025 21:06:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549579; cv=none; b=njxkJi1KQk4uvDbpGjZw0zBR52CxmoAyK7BJ15xtjY3u52ujRM/pabvopvPEFvADtTCfVr1sII9xjam5ab1xnmscwbbjCVUwYz7zK/Grjf9GF6iuQxdL+obUjD1HJXuEgrjlMRJ4jFxhPEzqOuLy4McNnWa4zp9rnV9yMLO+HkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762549579; c=relaxed/simple; bh=Kl9YsS1PIGvtggF1uJm277eJ2mqf4fTidxqHPnljr/8=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BzobUYG2im/3ToQfXdqedyiZUO7ZhS+XYmjLcV0d2ZtN4vcGBTyaHJN/u+V92s3XNoOHNSWTg/2QvqJKi8Q7nnLsgl9qbxkxDwswzO8um/S3bdPpRdxvwjHNJ2ECul2yNfJC3AXe3uKgR4Xjw2HMlfcMgGgzjj04/kr9eb+pnWU= 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=dGTKGzaP; arc=none smtp.client-ip=209.85.128.170 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="dGTKGzaP" Received: by mail-yw1-f170.google.com with SMTP id 00721157ae682-7869819394aso11261967b3.3 for ; Fri, 07 Nov 2025 13:06:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1762549575; x=1763154375; 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=zzZNGPgWx94ISN9X3lQ14PyLWRhGYF2GiiJN7CqbrDw=; b=dGTKGzaPbq2rhZiIVJlx9wsOVAvU3zIy/GGqJAm+YflUZKGmGJSsOABdRm2E//1vjU rRZhmIhqK/+7avXKXIVl95eiDOqOWbKHPzR9TAFutXZtRqE+Tg9XTWlSbq2QSQnm0UKl kU1L6BTDqIBBq9q0ZU2U0asik3f7PjaD7AuJk5j/+GLNLfD0ndJPm/UJPV1DxMVOWjpA 8HcGgklHfuhxYXyAB5IphIp0nFPL71SJwKF9mXMvuhU7k6h08I6OkCQeq6JMIF9QSdkm cSTepREt1VUqWsAp/TKfGVx6uqYPJRd76LKx3lKu8bHhMnJ5gJ5SXR+muQMWJcEjEc5Q 1Dlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762549575; x=1763154375; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=zzZNGPgWx94ISN9X3lQ14PyLWRhGYF2GiiJN7CqbrDw=; b=k64CoVOa+N6DdhhNvlKh4SNSEl0/QsLjo7ZJC3xrBKqkbMdqSuHB6IeBLxu3/6tu/m sOqqgXM7ZBw52YOVx0ZjAAHd71HbBGU7VFhoqtqrLrKdrCaORAMHWVPnd/YJqu73dH54 ZwLakC83JBFt1zpPYi2tp+89CRQOEl/7axDoIaJ3FDZYaHnck23NfAv0o2qOfF6V5iT9 AhXFgVP0xxxrsKAvFjbhcvGBOkfmcOVGsiVfhmrxvVivXrh7KhnfbH32AqJNCXcD8RX/ vYcMPGw9kW5XZIBfK5JOseI+jCp7a2WHqlLfMqOaQ9YduD8cQXrrbC99GMBBvuYqD4Kp zeOw== X-Forwarded-Encrypted: i=1; AJvYcCXtWlITxvYXtFGctddjb+OPDr45sw68k122b107a18f5hRr7DxVl9fuf4QYxL27jivAzMOp4INkTwwOawk=@vger.kernel.org X-Gm-Message-State: AOJu0YxZR87AcqTRmOQZmWdb4OdKB1vQyCvr7NKUhoOOQLRLq/WGHGe7 8xvBOD4l+ocG7q9NYIwSe59R4feRcR9zqSRsllJTymxwMa653KS753k9+xnKNwtIcAs= X-Gm-Gg: ASbGncvQPixDzO/8n4ttn1YuSyknD56yy7F0ceBRllCVm7MYPpbmYjzep0gRha3aFkG 2UnaEtH6ydalZeTMP424Bfiadqs1+7ktV0jPMUIgBuhHiHw+MQOaKHNQVNw3Uj9uZt4tIGXA0qT CyBXdxgajaVeeFG98UExRWXesbJd9nTXdhIR5WHi3zrVjD+m8iq9cuVZtiaSlGQ3AiyiSylu9Gd FGaZSBdfLOWpucrW+Y2G+CfdTYM7iSy8Qy4mFiUHQECxckwt2uGA9hD6YutXmbc39Xl9/yLHZ3h Nujz5UX0JL94B4m3r+qdXhrewMbGtHbDfKspIMR4SNmPXYtLIVTu6tiAXcOhvzZBh/dpS5NjL1K ZLK9XhZy4utkpEv2lAB5xWYmtD8vRZ4z80Ww4foDdIwCdBZ85GprOvYaL+e5yDjB+rs4Z6PcjWC NZYFODhbulwW0aX58UUSKEsr8wJv3mgmA5KBFLn/45d/Sul0/AfDOEedrWPlTe3dSJnMQbXFFAi A== X-Google-Smtp-Source: AGHT+IHOz7qc/OdNH+hwEFR2mgMLzMFENl/tjweqpUXWP41GYcTPchZvuPAXTMaj9WvfxwDjBEwMvw== X-Received: by 2002:a05:690c:690b:b0:786:6df0:8a36 with SMTP id 00721157ae682-787d53afd94mr7203087b3.29.1762549575032; Fri, 07 Nov 2025 13:06:15 -0800 (PST) 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 00721157ae682-787d68754d3sm990817b3.26.2025.11.07.13.06.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Nov 2025 13:06:14 -0800 (PST) From: Pasha Tatashin To: pratyush@kernel.org, jasonmiu@google.com, graf@amazon.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 Subject: [PATCH v5 22/22] tests/liveupdate: Add in-kernel liveupdate test Date: Fri, 7 Nov 2025 16:03:20 -0500 Message-ID: <20251107210526.257742-23-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.51.2.1041.gc1ab5b90ca-goog In-Reply-To: <20251107210526.257742-1-pasha.tatashin@soleen.com> References: <20251107210526.257742-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 an in-kernel test module to validate the core logic of the Live Update Orchestrator's File-Lifecycle-Bound feature. This provides a low-level, controlled environment to test FLB registration and callback invocation without requiring userspace interaction or actual kexec reboots. The test is enabled by the CONFIG_LIVEUPDATE_TEST Kconfig option. Signed-off-by: Pasha Tatashin --- kernel/liveupdate/luo_file.c | 2 + kernel/liveupdate/luo_internal.h | 8 ++ lib/Kconfig.debug | 23 ++++++ lib/tests/Makefile | 1 + lib/tests/liveupdate.c | 130 +++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 lib/tests/liveupdate.c diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index 713069b96278..4c0a75918f3d 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -829,6 +829,8 @@ int liveupdate_register_file_handler(struct liveupdate_= file_handler *fh) INIT_LIST_HEAD(&fh->flb_list); list_add_tail(&fh->list, &luo_file_handler_list); =20 + liveupdate_test_register(fh); + return 0; } =20 diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 89c2fd97e5a7..be8986f7ac9b 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -90,4 +90,12 @@ int __init luo_flb_setup_outgoing(void *fdt); int __init luo_flb_setup_incoming(void *fdt); void luo_flb_serialize(void); =20 +#ifdef CONFIG_LIVEUPDATE_TEST +void liveupdate_test_register(struct liveupdate_file_handler *h); +#else +static inline void liveupdate_test_register(struct liveupdate_file_handler= *h) +{ +} +#endif + #endif /* _LINUX_LUO_INTERNAL_H */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ddacf9e665a2..2cbfa3dead0b 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2813,6 +2813,29 @@ config LINEAR_RANGES_TEST =20 If unsure, say N. =20 +config LIVEUPDATE_TEST + bool "Live Update Kernel Test" + default n + depends on LIVEUPDATE + help + Enable a built-in kernel test module for the Live Update + Orchestrator. + + This module validates the File-Lifecycle-Bound subsystem by + registering a set of mock FLB objects with any real file handlers + that support live update (such as the memfd handler). + + When live update operations are performed, this test module will + output messages to the kernel log (dmesg), confirming that its + registration and various callback functions (preserve, retrieve, + finish, etc.) are being invoked correctly. + + This is a debugging and regression testing tool for developers + working on the Live Update subsystem. It should not be enabled in + production kernels. + + If unsure, say N + config CMDLINE_KUNIT_TEST tristate "KUnit test for cmdline API" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/tests/Makefile b/lib/tests/Makefile index f7460831cfdd..8e5c527a94ac 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_LIST_KUNIT_TEST) +=3D list-test.o obj-$(CONFIG_KFIFO_KUNIT_TEST) +=3D kfifo_kunit.o obj-$(CONFIG_TEST_LIST_SORT) +=3D test_list_sort.o obj-$(CONFIG_LINEAR_RANGES_TEST) +=3D test_linear_ranges.o +obj-$(CONFIG_LIVEUPDATE_TEST) +=3D liveupdate.o =20 CFLAGS_longest_symbol_kunit.o +=3D $(call cc-disable-warning, missing-prot= otypes) obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) +=3D longest_symbol_kunit.o diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c new file mode 100644 index 000000000000..62c592aa859f --- /dev/null +++ b/lib/tests/liveupdate.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2025, Google LLC. + * Pasha Tatashin + */ + +#define pr_fmt(fmt) KBUILD_MODNAME " test: " fmt + +#include +#include +#include +#include "../../kernel/liveupdate/luo_internal.h" + +#define TEST_NFLBS 3 +#define TEST_FLB_MAGIC_BASE 0xFEEDF00DCAFEBEE0ULL + +static struct liveupdate_flb test_flbs[TEST_NFLBS]; + +static int test_flb_preserve(struct liveupdate_flb_op_args *argp) +{ + ptrdiff_t index =3D argp->flb - test_flbs; + + pr_info("%s: preserve was triggered\n", argp->flb->compatible); + argp->data =3D TEST_FLB_MAGIC_BASE + index; + + return 0; +} + +static void test_flb_unpreserve(struct liveupdate_flb_op_args *argp) +{ + pr_info("%s: unpreserve was triggered\n", argp->flb->compatible); +} + +static void test_flb_retrieve(struct liveupdate_flb_op_args *argp) +{ + ptrdiff_t index =3D argp->flb - test_flbs; + u64 expected_data =3D TEST_FLB_MAGIC_BASE + index; + + if (argp->data =3D=3D expected_data) { + pr_info("%s: found flb data from the previous boot\n", + argp->flb->compatible); + argp->obj =3D (void *)argp->data; + } else { + pr_err("%s: ERROR - incorrect data handle: %llx, expected %llx\n", + argp->flb->compatible, argp->data, expected_data); + } +} + +static void test_flb_finish(struct liveupdate_flb_op_args *argp) +{ + ptrdiff_t index =3D argp->flb - test_flbs; + void *expected_obj =3D (void *)(TEST_FLB_MAGIC_BASE + index); + + if (argp->obj =3D=3D expected_obj) + pr_info("%s: finish was triggered\n", argp->flb->compatible); + else + pr_err("%s: ERROR - finish called with invalid object\n", + argp->flb->compatible); +} + +static const struct liveupdate_flb_ops test_flb_ops =3D { + .preserve =3D test_flb_preserve, + .unpreserve =3D test_flb_unpreserve, + .retrieve =3D test_flb_retrieve, + .finish =3D test_flb_finish, +}; + +#define DEFINE_TEST_FLB(i) \ + { .ops =3D &test_flb_ops, .compatible =3D "test-flb-v" #i } + +static struct liveupdate_flb test_flbs[TEST_NFLBS] =3D { + DEFINE_TEST_FLB(0), + DEFINE_TEST_FLB(1), + DEFINE_TEST_FLB(2), +}; + +static int __init liveupdate_test_early_init(void) +{ + int i; + + if (!liveupdate_enabled()) + return 0; + + for (i =3D 0; i < TEST_NFLBS; i++) { + struct liveupdate_flb *flb =3D &test_flbs[i]; + void *obj; + int err; + + liveupdate_init_flb(flb); + + err =3D liveupdate_flb_incoming_locked(flb, &obj); + if (!err) { + liveupdate_flb_incoming_unlock(flb, obj); + } else if (err !=3D -ENODATA && err !=3D -ENOENT) { + pr_err("liveupdate_flb_incoming_locked for %s failed: %pe\n", + flb->compatible, ERR_PTR(err)); + } + } + + return 0; +} +early_initcall(liveupdate_test_early_init); + +void liveupdate_test_register(struct liveupdate_file_handler *h) +{ + int err, i; + + for (i =3D 0; i < TEST_NFLBS; i++) { + struct liveupdate_flb *flb =3D &test_flbs[i]; + + err =3D liveupdate_register_flb(h, flb); + if (err) + pr_err("Failed to register %s %pe\n", + flb->compatible, ERR_PTR(err)); + } + + err =3D liveupdate_register_flb(h, &test_flbs[0]); + if (!err || err !=3D -EEXIST) { + pr_err("Failed: %s should be already registered, but got err: %pe\n", + test_flbs[0].compatible, ERR_PTR(err)); + } + + pr_info("Registered %d FLBs with file handler: [%s]\n", + TEST_NFLBS, h->compatible); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pasha Tatashin "); +MODULE_DESCRIPTION("In-kernel test for LUO mechanism"); --=20 2.51.2.1041.gc1ab5b90ca-goog