* [RFC PATCH 0/3] liveupdate: Move to feature flags for LUO and memfd ABI compatibility
@ 2026-09-03 2:34 Logan Odell
2026-09-03 2:34 ` [RFC PATCH 1/3] luo: Move to feature flags instead of compatibility strings Logan Odell
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Logan Odell @ 2026-09-03 2:34 UTC (permalink / raw)
To: arnd, pasha.tatashin, rppt, pratyush, graf, akpm, pbonzini, maz,
oupton, seanjc, bhelgaas, alex, jgg, kevin.tian, dwmw2, baolu.lu,
joro, will, robin.murphy
Cc: linux-arch, linux-kernel, kexec, linux-mm, kvm, linux-arm-kernel,
kvmarm, linux-pci, iommu, Logan Odell
We're including maintainers from all subsystems that currently is or
will be expected to participate in live update to ensure alignment on
the path forward for compatibility.
Currently, Live Update Orchestrator (LUO) and its file handlers rely on
monolithic compatibility strings (such as "luo-v5" and "memfd-v1") to
validate ABI compatibility across kexec live updates. Any modification
to serialized structures requires bumping the version string, which
strictly breaks compatibility between adjacent kernels even when changes
are additive, backwards-compatible, or optional.
This RFC series transitions LUO and subsystem file handlers to use
granular feature bitmasks instead of compatibility strings. We replace
the compatibility string with a header structure that includes some
reserved space to define the features that are included after the
feature.
We include 3 bits of data for each feature: supported, active, and
required. When the supported bit is set in the next kernel, the previous
kernel can serialize the feature and pass it to the next kernel. When
the active bit is set, it means the previous occurred and the data for
that feature is valid. When the required bit is set, it means that the
next kernel must have the associated supported bit in order to be
compatible.
If a feature is required in that it cannot live update to a kernel that
does not support the feature, it is expected that the feature not be
active or required initially. This allows for an intermediate upgrade
path.
Overview of Changes:
1. Define feature header and migrate luo_ser (Patch 1):
- Introudce the feature header with the supported, active, and
required bits.
- Replace the luo compatibility string with this new header.
- Validate the preserved size is at least the size of the header. This
breaks compatibility with the old way.
2. Export feature header information to vmlinux (Patch 2):
- Adds a .liveupdate_features section in vmlinux containing
- Adds helper macros to export feature information for a given
subsystem
- Export luo's feature information
3. Memfd Handler Migration (Patch 3):
- Migrate memfd to use the feature header instead of compatibility
strings
Logan Odell (3):
luo: Move to feature flags instead of compatibility strings
luo: Export feature support to vmlinux section
luo: memfd: Move to feature flags instead of compatibility strings
include/asm-generic/vmlinux.lds.h | 12 ++++
include/linux/kho/abi/luo.h | 114 +++++++++++++++++++++++++-----
include/linux/kho/abi/memfd.h | 41 +++++++----
include/linux/liveupdate.h | 22 ++++--
kernel/liveupdate/luo_core.c | 60 ++++++++++++----
kernel/liveupdate/luo_file.c | 28 ++++----
kernel/liveupdate/luo_flb.c | 2 +-
lib/tests/liveupdate.c | 2 +-
mm/memfd_luo.c | 40 ++++++++---
9 files changed, 246 insertions(+), 75 deletions(-)
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFC PATCH 1/3] luo: Move to feature flags instead of compatibility strings
2026-09-03 2:34 [RFC PATCH 0/3] liveupdate: Move to feature flags for LUO and memfd ABI compatibility Logan Odell
@ 2026-09-03 2:34 ` Logan Odell
2026-09-03 2:34 ` [RFC PATCH 2/3] luo: Export feature support to vmlinux section Logan Odell
2026-09-03 2:34 ` [RFC PATCH 3/3] luo: memfd: Move to feature flags instead of compatibility strings Logan Odell
2 siblings, 0 replies; 4+ messages in thread
From: Logan Odell @ 2026-09-03 2:34 UTC (permalink / raw)
To: arnd, pasha.tatashin, rppt, pratyush, graf, akpm, pbonzini, maz,
oupton, seanjc, bhelgaas, alex, jgg, kevin.tian, dwmw2, baolu.lu,
joro, will, robin.murphy
Cc: linux-arch, linux-kernel, kexec, linux-mm, kvm, linux-arm-kernel,
kvmarm, linux-pci, iommu, Logan Odell
Define a 128 byte header that can be used by all live update serialized
structures. In this header, we reserve room for 64 features with
supported, active, and required bits. Update the top level luo struct to
use this instead of compatibility strings. Previous versions of the
struct had smaller sizes, so add a minimum size check to avoid
collisions with older kernels.
Signed-off-by: Logan Odell <loganodell@google.com>
---
include/linux/kho/abi/luo.h | 74 +++++++++++++++++++++++++++++-------
kernel/liveupdate/luo_core.c | 44 +++++++++++++--------
2 files changed, 90 insertions(+), 28 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 288076de6d4a..0a7662a0cf9a 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -13,15 +13,17 @@
* kernel. The ABI is built upon the Kexec HandOver framework and registers
* the central `struct luo_ser` via the KHO raw subtree API.
*
- * This interface is a contract. Any modification to the structure fields,
- * compatible strings, or the layout of the `__packed` serialization
- * structures defined here constitutes a breaking change. Such changes require
- * incrementing the version number in the relevant `_COMPATIBLE` string to
- * prevent a new kernel from misinterpreting data from an old kernel.
+ * This interface is a contract. To ensure the stability of moving between
+ * kernel versions, any changes to the structures should only be additive
+ * and should utilize the feature flags to denote the supported, active, and
+ * required status of the feature.
+ *
+ * Features that are entirely optional can be added with the supported and
+ * active flags both asserted, and the required flag cleared. Features that
+ * require that the next kernel supports the feature should only be added as
+ * supported to allow compatible transition kernels. At a later time, the
+ * active and required flag should be asserted.
*
- * Changes are allowed provided the compatibility version is incremented;
- * however, backward/forward compatibility is only guaranteed for kernels
- * supporting the same ABI version.
*
* KHO Structure Overview:
* The entire LUO state is encapsulated within a single KHO entry named "LUO".
@@ -30,7 +32,7 @@
* Serialization Structures:
* - struct luo_ser:
* The central ABI structure that contains the overall state of the LUO.
- * It includes the compatibility string, the liveupdate-number, and pointers
+ * It includes the feature flags, the liveupdate-number, and pointers
* to sessions and FLBs.
*
* - struct luo_session_ser:
@@ -58,19 +60,65 @@
#define _LINUX_KHO_ABI_LUO_H
#include <linux/align.h>
+#include <linux/bits.h>
#include <linux/kho/abi/block.h>
#include <uapi/linux/liveupdate.h>
+/**
+ * struct luo_feature_hdr - Feature negotiation and versioning header.
+ * @supp: Bitmask of features supported by the producing subsystem.
+ * @req: Bitmask of features required for safe deserialization by the
+ * receiving subsystem.
+ * @active: Bitmask of features actually active/used in the serialized payload.
+ * @reserved: Reserved for future use.
+ *
+ * This header can be embedded at the beginning of any serialized structure to
+ * provide forward and backward compatibility via feature negotiation across
+ * live update.
+ */
+struct luo_feature_hdr {
+ u64 supp;
+ u64 req;
+ u64 active;
+ u64 reserved[13];
+} __packed;
+
+#define LUO_FEATURE_ACTIVE(s, f) \
+ do { \
+ (s)->features.supp |= (u64)(f); \
+ (s)->features.active |= (u64)(f); \
+ } while (0)
+#define LUO_FEATURE_SUPPORTED(s, f) ((s)->features.supp |= (u64)(f))
+#define LUO_FEATURE_REQUIRED(s, f) ((s)->features.req |= (u64)(f))
+#define LUO_FEATURE_IS_ACTIVE(s, f) (!!((s)->features.active & (u64)(f)))
+#define LUO_FEATURE_IS_SUPPORTED(s, f) (!!((s)->features.supp & (u64)(f)))
+#define LUO_FEATURE_IS_REQUIRED(s, f) (!!((s)->features.req & (u64)(f)))
+
/*
* The LUO state is registered under this KHO entry name.
*/
#define LUO_KHO_ENTRY_NAME "LUO"
-#define LUO_ABI_COMPATIBLE "luo-v5"
-#define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8)
+
+/*
+ * Bits associated with the luo_feature_hdr values.
+ */
+#define LUO_FEATURE_NUMBER BIT_ULL(0)
+#define LUO_FEATURE_SESSIONS BIT_ULL(1)
+#define LUO_FEATURE_FLBS BIT_ULL(2)
+
+#define LUO_CORE_FEATURES_SUPP (LUO_FEATURE_NUMBER | \
+ LUO_FEATURE_SESSIONS | \
+ LUO_FEATURE_FLBS)
+#define LUO_CORE_FEATURES_REQ (LUO_FEATURE_NUMBER | \
+ LUO_FEATURE_SESSIONS | \
+ LUO_FEATURE_FLBS)
+#define LUO_CORE_FEATURES_ACTIVE (LUO_FEATURE_NUMBER | \
+ LUO_FEATURE_SESSIONS | \
+ LUO_FEATURE_FLBS)
/**
* struct luo_ser - Centralized LUO ABI header.
- * @compatible: Compatibility string identifying the LUO ABI version.
+ * @features: Bit mask of supported and active features.
* @liveupdate_num: A counter tracking the number of successful live updates.
* @sessions_pa: Physical address of the first session block header.
* @flbs_pa: Physical address of the FLB header.
@@ -78,7 +126,7 @@
* This structure is the root of all preserved LUO state.
*/
struct luo_ser {
- char compatible[LUO_ABI_COMPAT_LEN];
+ struct luo_feature_hdr features;
u64 liveupdate_num;
u64 sessions_pa;
u64 flbs_pa;
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 1b2bda22902d..6add1ecc463f 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -107,26 +107,37 @@ static int __init luo_early_startup(void)
return 0;
}
- if (len < sizeof(*luo_ser)) {
- pr_err("LUO state is too small (%zu < %zu)\n", len, sizeof(*luo_ser));
- return -EINVAL;
+ luo_ser = phys_to_virt(luo_ser_phys);
+
+ if (len < sizeof(struct luo_feature_hdr)) {
+ pr_err("LUO state is too small (%zu < %zu)\n",
+ len, sizeof(struct luo_feature_hdr));
+ err = -EINVAL;
+ goto out_free_ser;
}
- luo_ser = phys_to_virt(luo_ser_phys);
- if (strncmp(luo_ser->compatible, LUO_ABI_COMPATIBLE, LUO_ABI_COMPAT_LEN)) {
- pr_err("LUO state is incompatible with '%s'\n", LUO_ABI_COMPATIBLE);
- return -EINVAL;
+ if (luo_ser->features.req & ~LUO_CORE_FEATURES_SUPP) {
+ pr_err("Unsupported required LUO feature (req: 0x%llx, supp: 0x%llx)\n",
+ luo_ser->features.req, (u64)LUO_CORE_FEATURES_SUPP);
+ err = -EOPNOTSUPP;
+ goto out_free_ser;
}
- luo_global.liveupdate_num = luo_ser->liveupdate_num;
- pr_info("Retrieved live update data, liveupdate number: %lld\n",
- luo_global.liveupdate_num);
+ if (LUO_FEATURE_IS_ACTIVE(luo_ser, LUO_FEATURE_NUMBER)) {
+ luo_global.liveupdate_num = luo_ser->liveupdate_num;
+ pr_info("Retrieved live update data, liveupdate number: %lld\n",
+ luo_global.liveupdate_num);
+ }
- err = luo_session_setup_incoming(luo_ser->sessions_pa);
- if (err)
- goto out_free_ser;
- luo_flb_setup_incoming(luo_ser->flbs_pa);
+ if (LUO_FEATURE_IS_ACTIVE(luo_ser, LUO_FEATURE_SESSIONS)) {
+ err = luo_session_setup_incoming(luo_ser->sessions_pa);
+ if (err)
+ goto out_free_ser;
+ }
+
+ if (LUO_FEATURE_IS_ACTIVE(luo_ser, LUO_FEATURE_FLBS))
+ luo_flb_setup_incoming(luo_ser->flbs_pa);
err = 0;
@@ -162,7 +173,10 @@ static int __init luo_state_setup(void)
return PTR_ERR(luo_ser);
}
- strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
+ luo_ser->features.supp = LUO_CORE_FEATURES_SUPP;
+ luo_ser->features.req = LUO_CORE_FEATURES_REQ;
+ luo_ser->features.active = LUO_CORE_FEATURES_ACTIVE;
+
luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
luo_session_setup_outgoing(&luo_ser->sessions_pa);
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFC PATCH 2/3] luo: Export feature support to vmlinux section
2026-09-03 2:34 [RFC PATCH 0/3] liveupdate: Move to feature flags for LUO and memfd ABI compatibility Logan Odell
2026-09-03 2:34 ` [RFC PATCH 1/3] luo: Move to feature flags instead of compatibility strings Logan Odell
@ 2026-09-03 2:34 ` Logan Odell
2026-09-03 2:34 ` [RFC PATCH 3/3] luo: memfd: Move to feature flags instead of compatibility strings Logan Odell
2 siblings, 0 replies; 4+ messages in thread
From: Logan Odell @ 2026-09-03 2:34 UTC (permalink / raw)
To: arnd, pasha.tatashin, rppt, pratyush, graf, akpm, pbonzini, maz,
oupton, seanjc, bhelgaas, alex, jgg, kevin.tian, dwmw2, baolu.lu,
joro, will, robin.murphy
Cc: linux-arch, linux-kernel, kexec, linux-mm, kvm, linux-arm-kernel,
kvmarm, linux-pci, iommu, Logan Odell
Create a liveupdate_features section that contains the feature
information for a given liveupdate component. Add the LUO component
first. These can be compared between two kernels to determine live
update compatibility. Features marked as required in the running kernel
will need to have that feature marked as supported in the next kernel to
be eligible for compatibility.
Suggested-by: Pratyush Yadav <pratyush@kernel.org>
Signed-off-by: Logan Odell <loganodell@google.com>
---
include/asm-generic/vmlinux.lds.h | 12 +++++++++++
include/linux/kho/abi/luo.h | 34 +++++++++++++++++++++++++++++++
include/linux/liveupdate.h | 12 +++++++++++
kernel/liveupdate/luo_core.c | 16 +++++++++++++++
4 files changed, 74 insertions(+)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 5659f4b5a125..aed4e282a78c 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -359,6 +359,17 @@
#define THERMAL_TABLE(name)
#endif
+#ifdef CONFIG_LIVEUPDATE
+#define LIVEUPDATE_FEATURES \
+ . = ALIGN(8); \
+ .liveupdate_features : AT(ADDR(.liveupdate_features) - LOAD_OFFSET) { \
+ KEEP(*(.liveupdate_sec_hdr)) \
+ KEEP(*(.liveupdate_features)) \
+ }
+#else
+#define LIVEUPDATE_FEATURES
+#endif
+
#define KERNEL_DTB() \
STRUCT_ALIGN(); \
__dtb_start = .; \
@@ -554,6 +565,7 @@
RO_EXCEPTION_TABLE \
NOTES \
BTF \
+ LIVEUPDATE_FEATURES \
\
. = ALIGN((align)); \
__end_rodata = .;
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 0a7662a0cf9a..5b25e1b48cf5 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -230,4 +230,38 @@ struct luo_flb_ser {
#define LIVEUPDATE_TEST_FLB_COMPATIBLE(i) "liveupdate-test-flb-v" #i
#endif
+#define LIVEUPDATE_VER_HDR_MAGIC 0x4c565550 /* 'LVUP' */
+#define LIVEUPDATE_VER_HDR_VER 1
+
+/**
+ * struct liveupdate_ver_hdr - Header of vmlinux section with version lists
+ * @magic: Magic number ('LVUP').
+ * @version: Version of the header format.
+ *
+ * This struct is the header for the vmlinux section ".liveupdate_features". The
+ * section contains the list of feature/version entries that the kernel supports.
+ */
+struct liveupdate_ver_hdr {
+ u32 magic;
+ u32 version;
+} __packed;
+
+/**
+ * struct liveupdate_feature_entry - Live update feature/version entry
+ * @name: Name of the subsystem or feature ("luo" for core).
+ * @feat_bytes: Number of bytes covered by supp, req, and active bitmaps (e.g. 8).
+ * @reserved: Reserved / padding for alignment.
+ * @supp: Bitmask of supported features.
+ * @req: Bitmask of required features.
+ * @active: Bitmask of active features.
+ */
+struct liveupdate_feature_entry {
+ char name[LIVEUPDATE_HNDL_COMPAT_LENGTH];
+ u32 feat_bytes;
+ u32 reserved;
+ u64 supp;
+ u64 req;
+ u64 active;
+} __packed;
+
#endif /* _LINUX_KHO_ABI_LUO_H */
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index 6051abc0612c..e058df23fed1 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -229,6 +229,16 @@ struct liveupdate_flb {
#ifdef CONFIG_LIVEUPDATE
+#define LIVEUPDATE_FEATURE_ENTRY(_id, _name, _supp, _req, _active) \
+ static const struct liveupdate_feature_entry __lu_feat_##_id \
+ __used __section(".liveupdate_features") __aligned(8) = { \
+ .name = _name, \
+ .feat_bytes = sizeof(u64), \
+ .supp = (_supp), \
+ .req = (_req), \
+ .active = (_active), \
+ }
+
/* Return true if live update orchestrator is enabled */
bool liveupdate_enabled(void);
@@ -259,6 +269,8 @@ int liveupdate_get_token_outgoing(struct liveupdate_session *s,
#else /* CONFIG_LIVEUPDATE */
+#define LIVEUPDATE_FEATURE_ENTRY(_id, _name, _supp, _req, _active)
+
static inline bool liveupdate_enabled(void)
{
return false;
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 6add1ecc463f..27413f895174 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -64,6 +64,22 @@
#include "kexec_handover_internal.h"
#include "luo_internal.h"
+/*
+ * This is the header for the ".liveupdate_features" section in vmlinux.
+ * The linker makes sure that this header precedes the entries.
+ */
+static const struct liveupdate_ver_hdr ver_hdr
+ __used __section(".liveupdate_sec_hdr") __aligned(8) = {
+ .magic = LIVEUPDATE_VER_HDR_MAGIC,
+ .version = LIVEUPDATE_VER_HDR_VER,
+};
+
+/* Core LUO features */
+LIVEUPDATE_FEATURE_ENTRY(luo_core, "luo",
+ LUO_CORE_FEATURES_SUPP,
+ LUO_CORE_FEATURES_REQ,
+ LUO_CORE_FEATURES_ACTIVE);
+
static struct {
bool enabled;
struct luo_ser *luo_ser_out;
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFC PATCH 3/3] luo: memfd: Move to feature flags instead of compatibility strings
2026-09-03 2:34 [RFC PATCH 0/3] liveupdate: Move to feature flags for LUO and memfd ABI compatibility Logan Odell
2026-09-03 2:34 ` [RFC PATCH 1/3] luo: Move to feature flags instead of compatibility strings Logan Odell
2026-09-03 2:34 ` [RFC PATCH 2/3] luo: Export feature support to vmlinux section Logan Odell
@ 2026-09-03 2:34 ` Logan Odell
2 siblings, 0 replies; 4+ messages in thread
From: Logan Odell @ 2026-09-03 2:34 UTC (permalink / raw)
To: arnd, pasha.tatashin, rppt, pratyush, graf, akpm, pbonzini, maz,
oupton, seanjc, bhelgaas, alex, jgg, kevin.tian, dwmw2, baolu.lu,
joro, will, robin.murphy
Cc: linux-arch, linux-kernel, kexec, linux-mm, kvm, linux-arm-kernel,
kvmarm, linux-pci, iommu, Logan Odell
Update struct memfd_luo_ser to embed struct luo_feature_hdr features.
Define feature flags for memfd (MEMFD_LUO_FEATURE_SEALS and
MEMFD_LUO_FEATURE_FOLIOS), emit the liveupdate feature entry for memfd,
and validate required features and active flags during deserialization.
Also replace the version bump requirement for seals with
MEMFD_LUO_BASE_SEALS to allow new seals to be introduced granularly as
feature bits.
Signed-off-by: Logan Odell <loganodell@google.com>
---
include/linux/kho/abi/luo.h | 8 +++---
include/linux/kho/abi/memfd.h | 41 ++++++++++++++++++++----------
include/linux/liveupdate.h | 10 ++++----
kernel/liveupdate/luo_file.c | 28 ++++++++++-----------
kernel/liveupdate/luo_flb.c | 2 +-
lib/tests/liveupdate.c | 2 +-
mm/memfd_luo.c | 47 +++++++++++++++++++++++++----------
7 files changed, 87 insertions(+), 51 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 5b25e1b48cf5..450a0e40e2ab 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -132,18 +132,18 @@ struct luo_ser {
u64 flbs_pa;
} __packed;
-#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48
+#define LIVEUPDATE_HNDL_NAME_LENGTH 48
/**
* struct luo_file_ser - Represents the serialized preserves files.
- * @compatible: File handler compatible string.
+ * @name: File handler name.
* @data: Private data
* @token: User provided token for this file
*
* If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_file_ser {
- char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH];
+ char name[LIVEUPDATE_HNDL_NAME_LENGTH];
u64 data;
u64 token;
} __packed;
@@ -256,7 +256,7 @@ struct liveupdate_ver_hdr {
* @active: Bitmask of active features.
*/
struct liveupdate_feature_entry {
- char name[LIVEUPDATE_HNDL_COMPAT_LENGTH];
+ char name[LIVEUPDATE_HNDL_NAME_LENGTH];
u32 feat_bytes;
u32 reserved;
u64 supp;
diff --git a/include/linux/kho/abi/memfd.h b/include/linux/kho/abi/memfd.h
index 08b10fea2afc..9961df5be423 100644
--- a/include/linux/kho/abi/memfd.h
+++ b/include/linux/kho/abi/memfd.h
@@ -11,6 +11,8 @@
#ifndef _LINUX_KHO_ABI_MEMFD_H
#define _LINUX_KHO_ABI_MEMFD_H
+#include <linux/bits.h>
+#include <linux/kho/abi/luo.h>
#include <linux/types.h>
#include <linux/kho/abi/kexec_handover.h>
@@ -23,11 +25,20 @@
* The state is serialized into a packed structure `struct memfd_luo_ser`
* which is handed over to the next kernel via the KHO mechanism.
*
- * This interface is a contract. Any modification to the structure layout
- * constitutes a breaking change. Such changes require incrementing the
- * version number in the MEMFD_LUO_FH_COMPATIBLE string.
+ * This interface is a contract. Any changes should be additive using feature
+ * flags to ensure backwards compatibility.
*/
+#define MEMFD_LUO_FEATURE_SEALS BIT_ULL(0)
+#define MEMFD_LUO_FEATURE_FOLIOS BIT_ULL(1)
+
+#define MEMFD_LUO_FEATURES_SUPP (MEMFD_LUO_FEATURE_SEALS | \
+ MEMFD_LUO_FEATURE_FOLIOS)
+#define MEMFD_LUO_FEATURES_REQ (MEMFD_LUO_FEATURE_SEALS | \
+ MEMFD_LUO_FEATURE_FOLIOS)
+#define MEMFD_LUO_FEATURES_ACTIVE (MEMFD_LUO_FEATURE_SEALS | \
+ MEMFD_LUO_FEATURE_FOLIOS)
+
/**
* MEMFD_LUO_FOLIO_DIRTY - The folio is dirty.
*
@@ -57,18 +68,21 @@ struct memfd_luo_folio_ser {
} __packed;
/*
- * The set of seals this version supports preserving. If support for any new
- * seals is needed, add it here and bump version.
+ * The set of base seals supported by MEMFD_LUO_FEATURE_SEALS.
+ * If support for new seals is needed, define a dedicated feature bit
+ * (e.g. MEMFD_LUO_FEATURE_SEAL_<NAME>) to allow granular compatibility.
*/
-#define MEMFD_LUO_ALL_SEALS (F_SEAL_SEAL | \
- F_SEAL_SHRINK | \
- F_SEAL_GROW | \
- F_SEAL_WRITE | \
- F_SEAL_FUTURE_WRITE | \
- F_SEAL_EXEC)
+#define MEMFD_LUO_BASE_SEALS (F_SEAL_SEAL | \
+ F_SEAL_SHRINK | \
+ F_SEAL_GROW | \
+ F_SEAL_WRITE | \
+ F_SEAL_FUTURE_WRITE | \
+ F_SEAL_EXEC)
+#define MEMFD_LUO_ALL_SEALS MEMFD_LUO_BASE_SEALS
/**
* struct memfd_luo_ser - Main serialization structure for a memfd.
+ * @features: Bit mask of supported, required, and active features.
* @pos: The file's current position (f_pos).
* @size: The total size of the file in bytes (i_size).
* @seals: The seals present on the memfd. The seals are uABI so it is safe
@@ -79,6 +93,7 @@ struct memfd_luo_folio_ser {
* struct memfd_luo_folio_ser.
*/
struct memfd_luo_ser {
+ struct luo_feature_hdr features;
u64 pos;
u64 size;
u32 seals;
@@ -87,7 +102,7 @@ struct memfd_luo_ser {
struct kho_vmalloc folios;
} __packed;
-/* The compatibility string for memfd file handler */
-#define MEMFD_LUO_FH_COMPATIBLE "memfd-v2"
+/* The name for memfd file handler */
+#define MEMFD_LUO_FH_NAME "memfd"
#endif /* _LINUX_KHO_ABI_MEMFD_H */
diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
index e058df23fed1..4489e344f76f 100644
--- a/include/linux/liveupdate.h
+++ b/include/linux/liveupdate.h
@@ -90,10 +90,10 @@ struct liveupdate_file_ops {
/**
* struct liveupdate_file_handler - Represents a handler for a live-updatable file type.
* @ops: Callback functions
- * @compatible: The compatibility string (e.g., "memfd-v1", "vfiofd-v1")
- * that uniquely identifies the file type this handler
- * supports. This is matched against the compatible string
- * associated with individual &struct file instances.
+ * @name: The name (e.g., "memfd", "vfiofd") that uniquely
+ * identifies the file type this handler supports. This
+ * is matched against the name associated with individual
+ * &struct file instances.
*
* Modules that want to support live update for specific file types should
* register an instance of this structure. LUO uses this registration to
@@ -102,7 +102,7 @@ struct liveupdate_file_ops {
*/
struct liveupdate_file_handler {
const struct liveupdate_file_ops *ops;
- const char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH];
+ const char name[LIVEUPDATE_HNDL_NAME_LENGTH];
/* private: */
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index dbae0715220b..775484af0750 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -480,13 +480,13 @@ int luo_file_freeze(struct luo_file_set *file_set,
err = luo_file_freeze_one(file_set, luo_file);
if (err < 0) {
pr_warn("Freeze failed for token[%#0llx] handler[%s] err[%pe]\n",
- luo_file->token, luo_file->fh->compatible,
+ luo_file->token, luo_file->fh->name,
ERR_PTR(err));
goto err_unfreeze;
}
- strscpy(file_ser->compatible, luo_file->fh->compatible,
- sizeof(file_ser->compatible));
+ strscpy(file_ser->name, luo_file->fh->name,
+ sizeof(file_ser->name));
file_ser->data = luo_file->serialized_data;
file_ser->token = luo_file->token;
}
@@ -732,7 +732,7 @@ static int luo_file_deserialize_one(struct luo_file_set *file_set,
down_read(&luo_register_rwlock);
list_private_for_each_entry(fh, &luo_file_handler_list, list) {
- if (!strcmp(fh->compatible, ser->compatible)) {
+ if (!strcmp(fh->name, ser->name)) {
if (try_module_get(fh->ops->owner))
handler_found = true;
break;
@@ -741,9 +741,9 @@ static int luo_file_deserialize_one(struct luo_file_set *file_set,
up_read(&luo_register_rwlock);
if (!handler_found) {
- pr_warn("No registered handler for compatible '%.*s'\n",
- (int)sizeof(ser->compatible),
- ser->compatible);
+ pr_warn("No registered handler for name '%.*s'\n",
+ (int)sizeof(ser->name),
+ ser->name);
return -ENOENT;
}
@@ -774,9 +774,9 @@ static int luo_file_deserialize_one(struct luo_file_set *file_set,
* in-memory linked list of 'struct luo_file' instances.
*
* For each serialized entry, it performs the following steps:
- * 1. Reads the 'compatible' string.
+ * 1. Reads the 'name' string.
* 2. Searches the global list of registered file handlers for one that
- * matches the compatible string.
+ * matches the name.
* 3. Allocates a new 'struct luo_file'.
* 4. Populates the new structure with the deserialized data (token, private
* data handle) and links it to the found handler. The 'file' pointer is
@@ -870,7 +870,7 @@ void luo_file_set_destroy(struct luo_file_set *file_set)
* 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
+ * 'name' string and 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 that
@@ -893,11 +893,11 @@ int liveupdate_register_file_handler(struct liveupdate_file_handler *fh)
}
down_write(&luo_register_rwlock);
- /* Check for duplicate compatible strings */
+ /* Check for duplicate handler names */
list_private_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' already registered.\n",
- fh->compatible);
+ if (!strcmp(fh_iter->name, fh->name)) {
+ pr_err("File handler registration failed: Handler name '%s' already registered.\n",
+ fh->name);
err = -EEXIST;
goto err_unlock;
}
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index cd715a7c1d99..cb8c15f181e0 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -337,7 +337,7 @@ static void luo_flb_unregister_one(struct liveupdate_file_handler *fh,
if (!found) {
pr_warn("Failed to unregister FLB '%s': not found in file handler '%s'\n",
- flb->compatible, fh->compatible);
+ flb->compatible, fh->name);
return;
}
diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c
index 4c08a7c6fb78..d3a8573a648e 100644
--- a/lib/tests/liveupdate.c
+++ b/lib/tests/liveupdate.c
@@ -135,7 +135,7 @@ void liveupdate_test_register(struct liveupdate_file_handler *fh)
}
pr_info("Registered %d FLBs with file handler: [%s]\n",
- TEST_NFLBS, fh->compatible);
+ TEST_NFLBS, fh->name);
}
MODULE_LICENSE("GPL");
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index 59de210bee5f..36ee503672a2 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -52,8 +52,8 @@
*
* Seals
* File seals set on the memfd are preserved and re-applied on restore.
- * Only seals known to this LUO version (see ``MEMFD_LUO_ALL_SEALS``) may
- * be present; preservation fails with ``-EOPNOTSUPP`` otherwise.
+ * Only base seals supported by this LUO version (see ``MEMFD_LUO_BASE_SEALS``)
+ * may be present; preservation fails with ``-EOPNOTSUPP`` otherwise.
*
* Non-Preserved Properties
* ========================
@@ -273,6 +273,10 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
goto err_unlock;
}
+ ser->features.supp = MEMFD_LUO_FEATURES_SUPP;
+ ser->features.req = MEMFD_LUO_FEATURES_REQ;
+ ser->features.active = MEMFD_LUO_FEATURES_ACTIVE;
+
seals = memfd_get_seals(args->file);
if (seals < 0) {
err = seals;
@@ -352,8 +356,9 @@ static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args)
ser = phys_to_virt(args->serialized_data);
- memfd_luo_unpreserve_folios(&ser->folios, args->private_data,
- ser->nr_folios);
+ if (LUO_FEATURE_IS_ACTIVE(ser, MEMFD_LUO_FEATURE_FOLIOS) && ser->nr_folios)
+ memfd_luo_unpreserve_folios(&ser->folios, args->private_data,
+ ser->nr_folios);
kho_unpreserve_free(ser);
inode_unlock(inode);
@@ -401,7 +406,7 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args)
if (!ser)
return;
- if (ser->nr_folios) {
+ if (LUO_FEATURE_IS_ACTIVE(ser, MEMFD_LUO_FEATURE_FOLIOS) && ser->nr_folios) {
folios_ser = kho_restore_vmalloc(&ser->folios);
if (!folios_ser)
goto out;
@@ -526,12 +531,21 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
if (!ser)
return -EINVAL;
- /* Make sure the file only has seals supported by this version. */
- if (ser->seals & ~MEMFD_LUO_ALL_SEALS) {
+ if (ser->features.req & ~MEMFD_LUO_FEATURES_SUPP) {
+ pr_err("Unsupported required memfd feature (req: 0x%llx, supp: 0x%llx)\n",
+ ser->features.req, (u64)MEMFD_LUO_FEATURES_SUPP);
err = -EOPNOTSUPP;
goto free_ser;
}
+ if (LUO_FEATURE_IS_ACTIVE(ser, MEMFD_LUO_FEATURE_SEALS)) {
+ /* Make sure the file only has seals supported by this version. */
+ if (ser->seals & ~MEMFD_LUO_ALL_SEALS) {
+ err = -EOPNOTSUPP;
+ goto free_ser;
+ }
+ }
+
/*
* The seals are preserved. Allow sealing here so they can be added
* later.
@@ -543,16 +557,18 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
goto free_ser;
}
- err = memfd_add_seals(file, ser->seals);
- if (err) {
- pr_err("failed to add seals: %pe\n", ERR_PTR(err));
- goto put_file;
+ if (LUO_FEATURE_IS_ACTIVE(ser, MEMFD_LUO_FEATURE_SEALS)) {
+ err = memfd_add_seals(file, ser->seals);
+ if (err) {
+ pr_err("failed to add seals: %pe\n", ERR_PTR(err));
+ goto put_file;
+ }
}
vfs_setpos(file, ser->pos, MAX_LFS_FILESIZE);
i_size_write(file_inode(file), ser->size);
- if (ser->nr_folios) {
+ if (LUO_FEATURE_IS_ACTIVE(ser, MEMFD_LUO_FEATURE_FOLIOS) && ser->nr_folios) {
folios_ser = kho_restore_vmalloc(&ser->folios);
if (!folios_ser) {
err = -EINVAL;
@@ -601,9 +617,14 @@ static const struct liveupdate_file_ops memfd_luo_file_ops = {
.owner = THIS_MODULE,
};
+LIVEUPDATE_FEATURE_ENTRY(memfd_luo, MEMFD_LUO_FH_NAME,
+ MEMFD_LUO_FEATURES_SUPP,
+ MEMFD_LUO_FEATURES_REQ,
+ MEMFD_LUO_FEATURES_ACTIVE);
+
static struct liveupdate_file_handler memfd_luo_handler = {
.ops = &memfd_luo_file_ops,
- .compatible = MEMFD_LUO_FH_COMPATIBLE,
+ .name = MEMFD_LUO_FH_NAME,
};
static int __init memfd_luo_init(void)
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-03 2:35 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 2:34 [RFC PATCH 0/3] liveupdate: Move to feature flags for LUO and memfd ABI compatibility Logan Odell
2026-09-03 2:34 ` [RFC PATCH 1/3] luo: Move to feature flags instead of compatibility strings Logan Odell
2026-09-03 2:34 ` [RFC PATCH 2/3] luo: Export feature support to vmlinux section Logan Odell
2026-09-03 2:34 ` [RFC PATCH 3/3] luo: memfd: Move to feature flags instead of compatibility strings Logan Odell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox