mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility
@ 2026-10-26 17:42 Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 1/4] liveupdate: Remove __packed from LUO and memfd ABI structures Tarun Sahu
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Tarun Sahu @ 2026-10-26 17:42 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec, Tarun Sahu

Changes since RFC v2 [1]:
- Dropped the file handler compatible rename and per-FD handler
  selection patches.
- Replaced the fixed 128-byte header with an expandable struct
  luo_ser_hdr (size, nr_features, flags[]), kept separate from the
  payload structure. The header is 8 bytes with no features and 16 bytes
  for 1 to 32 features.
- Each subsystem declares its wrapper once with DECLARE_LUO_SER_WRAP(),
  passing its feature enum MAX; the macro sizes the flags entries and
  generates name##_wrap_init(), so callers never pass sizes or counts.
  The target kernel reaches the payload with luo_ser_payload().
- Feature bits describe only what each preserved object uses. The target
  kernel checks them against its own boolean supported table, and the
  header is trusted as the source of truth: validation only rejects a
  required feature the target kernel does not support. 0b10 is reserved.
- Moved the __packed removal into its own patch (1/4) and added a KUnit
  test (4/4).

Changes since RFC v1 [2]:
- Replaced the 3-mask (supp, req, active) design with a header of 2-bit
  feature states.

[1] https://lore.kernel.org/all/20261021152527.375070-1-tarunsahu@google.com/
[2] https://lore.kernel.org/all/20260903023452.721732-1-loganodell@google.com/

==========COVER LETTER==============

LUO and its file handlers rely only on coarse-grained compatibility
strings (such as luo-v5 and memfd-v2) to validate handed-over state
across kexec. Any change to a serialized structure, even an optional
additive field, requires bumping the string and breaks live update
between adjacent kernels.

This series adds a feature header (struct luo_ser_hdr) ahead of
serialized LUO structures so subsystems can evolve their serialized state
per feature. Each subsystem groups its serialized fields under feature
indices, with index 0 for the base fields. New capabilities append fields
at the end of the payload under a new feature index, and retired fields
stay in place so offsets never shift. Only a change that feature flags
cannot express needs a new handler with a bumped compatibility string.

Each feature has a 2-bit state set by the source kernel:
- LUO_FEAT_ABSENT (0b00): not in the payload; a target kernel that
  supports it can use default values.
- LUO_FEAT_OPTIONAL (0b01): in the payload; a target kernel that does not
  support it ignores the data.
- LUO_FEAT_REQUIRED (0b11): in the payload; a target kernel that does not
  support it rejects the state with -EOPNOTSUPP.
- LUO_FEAT_RESERVED (0b10): reserved, not checked by the target kernel.

The target kernel checks the header against its own boolean supported
table:

  Source hdr  Target supported[i]  Action
  ----------  -------------------  ----------------------------------
  00          0 or 1               Accept: feature absent in payload
  01          0                    Accept: ignore the feature data
  01          1                    Accept: use the feature data
  11          0                    Reject: -EOPNOTSUPP
  11          1                    Accept: use the feature data
  10          0 or 1               Reserved: not checked by target

struct luo_ser_hdr holds the total size of the serialized structure
(u32), the number of features (u32 nr_features), and 64-bit flags
entries with 32 features each. With no features the header is 8 bytes;
with 1 to 32 features it is 16 bytes.

On the source side, a subsystem declares its wrapper once with
DECLARE_LUO_SER_WRAP(name, expected_size, nr_features), passing its
feature enum MAX, then calls name##_wrap_init(), marks the features the
preserved object uses with luo_ser_set_feat(), and fills wrap->ser. On
the target side, incoming state is never cast to the wrapper, since the
source kernel may use a different number of flags entries. The target
kernel calls luo_ser_hdr_validate(), finds the payload with
luo_ser_payload(), and reads the fields of a feature only after
luo_ser_get_feat() shows it present.

Patch 1 removes __packed from the LUO and memfd ABI structures. Patch 2
adds the header and converts struct luo_ser. Patch 3 converts memfd and
bumps its compatibility string to memfd-v3. Patch 4 adds KUnit tests.

Tarun Sahu (4):
  liveupdate: Remove __packed from LUO and memfd ABI structures
  liveupdate: Add feature header for fine-grained ABI compatibility
  mm: memfd_luo: Add the feature header
  liveupdate: Add KUnit test for the feature header helpers

 MAINTAINERS                          |   1 +
 include/linux/kho/abi/luo.h          | 359 ++++++++++++++++++++++++---
 include/linux/kho/abi/memfd.h        |  43 +++-
 kernel/liveupdate/luo_core.c         |  62 +++--
 lib/Kconfig.debug                    |  16 ++
 lib/tests/Makefile                   |   1 +
 lib/tests/liveupdate_feature_kunit.c | 118 +++++++++
 mm/memfd_luo.c                       |  68 +++--
 8 files changed, 577 insertions(+), 91 deletions(-)
 create mode 100644 lib/tests/liveupdate_feature_kunit.c

-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v3 1/4] liveupdate: Remove __packed from LUO and memfd ABI structures
  2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
@ 2026-10-26 17:42 ` Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 2/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Tarun Sahu @ 2026-10-26 17:42 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec, Tarun Sahu

All structures in include/linux/kho/abi/luo.h (struct luo_ser, struct
luo_file_ser, struct luo_file_set_ser, struct luo_session_ser, struct
luo_flb_header_ser, struct luo_flb_ser) and include/linux/kho/abi/memfd.h
(struct memfd_luo_folio_ser, struct memfd_luo_ser) have naturally aligned
fields on 64-bit architectures, with no padding holes between fields or
tail padding. Any future unaligned field addition should come with
reserved field to keep the structure aligned.

Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
 include/linux/kho/abi/luo.h   | 25 +++++++++++++++++++------
 include/linux/kho/abi/memfd.h | 11 ++++++++---
 2 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 288076de6d4a..899943877e41 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -58,6 +58,7 @@
 #define _LINUX_KHO_ABI_LUO_H
 
 #include <linux/align.h>
+#include <linux/build_bug.h>
 #include <linux/kho/abi/block.h>
 #include <uapi/linux/liveupdate.h>
 
@@ -82,7 +83,9 @@ struct luo_ser {
 	u64 liveupdate_num;
 	u64 sessions_pa;
 	u64 flbs_pa;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_ser) == 32);
 
 #define LIVEUPDATE_HNDL_COMPAT_LENGTH	48
 
@@ -98,7 +101,9 @@ struct luo_file_ser {
 	char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH];
 	u64 data;
 	u64 token;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_file_ser) == 64);
 
 /**
  * struct luo_file_set_ser - Represents the serialized metadata for file set
@@ -113,7 +118,9 @@ struct luo_file_ser {
 struct luo_file_set_ser {
 	u64 files;
 	u64 count;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_file_set_ser) == 16);
 
 /**
  * struct luo_session_ser - Represents the serialized metadata for a LUO session.
@@ -131,7 +138,9 @@ struct luo_file_set_ser {
 struct luo_session_ser {
 	char name[LIVEUPDATE_SESSION_NAME_LENGTH];
 	struct luo_file_set_ser file_set_ser;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_session_ser) == 80);
 
 /* The max size is set so it can be reliably used during in serialization */
 #define LIVEUPDATE_FLB_COMPAT_LENGTH	48
@@ -152,7 +161,9 @@ struct luo_session_ser {
 struct luo_flb_header_ser {
 	u64 pgcnt;
 	u64 count;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_flb_header_ser) == 16);
 
 /**
  * struct luo_flb_ser - Represents the serialized state of a single FLB object.
@@ -175,7 +186,9 @@ struct luo_flb_ser {
 	char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
 	u64 data;
 	u64 count;
-} __packed;
+};
+
+static_assert(sizeof(struct luo_flb_ser) == 64);
 
 /* Kernel Live Update Test ABI */
 #ifdef CONFIG_LIVEUPDATE_TEST
diff --git a/include/linux/kho/abi/memfd.h b/include/linux/kho/abi/memfd.h
index 08b10fea2afc..7115a8a77cfd 100644
--- a/include/linux/kho/abi/memfd.h
+++ b/include/linux/kho/abi/memfd.h
@@ -11,6 +11,7 @@
 #ifndef _LINUX_KHO_ABI_MEMFD_H
 #define _LINUX_KHO_ABI_MEMFD_H
 
+#include <linux/build_bug.h>
 #include <linux/types.h>
 #include <linux/kho/abi/kexec_handover.h>
 
@@ -20,7 +21,7 @@
  * memfd uses the ABI defined below for preserving its state across a kexec
  * reboot using the LUO.
  *
- * The state is serialized into a packed structure `struct memfd_luo_ser`
+ * The state is serialized into `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
@@ -54,7 +55,9 @@ struct memfd_luo_folio_ser {
 	u64 pfn:52;
 	u64 flags:12;
 	u64 index;
-} __packed;
+};
+
+static_assert(sizeof(struct memfd_luo_folio_ser) == 16);
 
 /*
  * The set of seals this version supports preserving. If support for any new
@@ -85,7 +88,9 @@ struct memfd_luo_ser {
 	u32 flags;
 	u64 nr_folios;
 	struct kho_vmalloc folios;
-} __packed;
+};
+
+static_assert(sizeof(struct memfd_luo_ser) == 48);
 
 /* The compatibility string for memfd file handler */
 #define MEMFD_LUO_FH_COMPATIBLE	"memfd-v2"
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v3 2/4] liveupdate: Add feature header for fine-grained ABI compatibility
  2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 1/4] liveupdate: Remove __packed from LUO and memfd ABI structures Tarun Sahu
@ 2026-10-26 17:42 ` Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 3/4] mm: memfd_luo: Add the feature header Tarun Sahu
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Tarun Sahu @ 2026-10-26 17:42 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec, Tarun Sahu

LUO and its file handlers currently rely solely on coarse-grained
compatibility strings (such as luo-v5 and memfd-v2) to validate
handed-over state across kexec. Because compatibility is checked only
at the whole-structure level, any change to a serialized structure,
even an optional additive field, requires bumping the compatibility
string and breaks live update between adjacent kernels.

Introduce a feature header (struct luo_ser_hdr) ahead of serialized LUO
structures so subsystems can evolve their serialized state at
per-feature granularity. Each subsystem groups the fields of its
serialized structure under feature indices, starting with index 0 for the
base fields. When a subsystem adds a new capability, it appends any new
fields to the end of its payload structure under a new feature index.
Retired fields are kept in place so existing field offsets never shift;
if the type or format of a field needs to change, the existing field and
feature are kept intact until retired and a new feature index with an
appended field of the new type is added. Only when a change requires a
complete break in the serialized layout or semantics that feature flags
cannot handle, a new handler can be introduced with an incremented version
number in its compatibility string.

Each feature in the header has a 2-bit state (enum luo_feat_state):
- LUO_FEAT_ABSENT (0b00): Feature is absent in the serialized payload.
                        Target kernel can initialize the feature with
                        the default values if it supports it.
- LUO_FEAT_OPTIONAL (0b01): Feature is present in the payload and
  optional; a target kernel that does not support it ignores the feature
  data.
- LUO_FEAT_REQUIRED (0b11): Feature is present in the payload and
  required; a target kernel that does not support it rejects the state
  with -EOPNOTSUPP.
- LUO_FEAT_RESERVED (0b10): Reserved encoding, not checked by target.

During deserialization, the target kernel validates the incoming header
against its per feature supported information stored as a simple boolean
array. The truth table for the deserialization:

  Source hdr  Target supported[i]  Action
  ----------  -------------------  ----------------------------------
  00          0 or 1               Accept: feature absent in payload
  01          0                    Accept: ignore the feature data
  01          1                    Accept: use the feature data
  11          0                    Reject: -EOPNOTSUPP
  11          1                    Accept: use the feature data
  10          0 or 1               Reserved: not checked by target

Source hdr is the 2-bit state of feature i written by the source kernel.
Target supported[i] is the boolean entry for feature i in the target
kernel supported features table passed to luo_ser_hdr_validate() (1 =
supported, 0 = not supported; an index at or beyond the table size, or a
NULL table, is read as 0).

The preserved data from the source kernel includes hdr and payload (ABI
for serialized data).

Each subsystem declares a wrapper structure using

        DECLARE_LUO_SER_WRAP(name, expected_size, nr_features)

This Wrapper wraps the hdr and serialized ABI payload in one structure.
Which will be preserved and passed to target kernel. Target kernel will
deserialized the hdr and based on each feature it will deserialize the
payload.

For deserialization in the target kernel, incoming state is never cast
to struct name##_wrap because a source kernel with a different feature
count may have a different number of flags entries. Instead, the target
kernel validates the header with luo_ser_hdr_validate(), locates the
payload with ser = luo_ser_payload(hdr), and checks whether a feature
is present before reading its fields:

  if (luo_ser_get_feat(hdr, FEAT_X) != LUO_FEAT_ABSENT)
          val = ser->x_field;

Also, introduce the LUO_CORE_FEAT_CORE for LUO for the existing mechanism
which is also a required feature.

Signed-off-by: Logan Odell <loganodell@google.com>
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
 include/linux/kho/abi/luo.h  | 336 +++++++++++++++++++++++++++++++----
 kernel/liveupdate/luo_core.c |  62 ++++---
 2 files changed, 338 insertions(+), 60 deletions(-)

diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 899943877e41..2c30bbd26c10 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -9,29 +9,81 @@
  * DOC: Live Update Orchestrator ABI
  *
  * Live Update Orchestrator uses the stable Application Binary Interface
- * defined below to pass state from a pre-update kernel to a post-update
- * kernel. The ABI is built upon the Kexec HandOver framework and registers
- * the central `struct luo_ser` via the KHO raw subtree API.
+ * defined below to pass state from a source kernel to a target 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. `struct luo_ser`, and the serialized state
+ * of each file handler that uses it, start with an 8-byte base feature
+ * header (`struct luo_ser_hdr`) holding the combined structure `size`
+ * (`__u32`) and `nr_features` (`__u32`), followed by an array of 64-bit
+ * `flags` entries (`__u64 flags[]`) before the payload structure.
+ * When `nr_features == 0`, `flags` has zero entries and the header is exactly
+ * 8 bytes. For `1 <= nr_features <= 32`, `flags` has 1 entry and the header
+ * is 16 bytes.
  *
- * Changes are allowed provided the compatibility version is incremented;
- * however, backward/forward compatibility is only guaranteed for kernels
- * supporting the same ABI version.
+ * Feature state encoding (2 bits per feature):
+ *   `LUO_FEAT_ABSENT`   = 0b00: Feature absent in serialized payload
+ *   `LUO_FEAT_OPTIONAL` = 0b01: Feature present in serialized payload (optional)
+ *   `LUO_FEAT_RESERVED` = 0b10: Reserved encoding (not validated)
+ *   `LUO_FEAT_REQUIRED` = 0b11: Feature present in serialized payload (required)
+ *
+ * Feature flags mapping:
+ *   - Each 64-bit `flags` entry encodes 32 features (2 bits per feature).
+ *   - Feature N is encoded in `flags[N / 32]` at bits
+ *     `[2 * (N % 32) + 1 : 2 * (N % 32)]`.
+ *
+ * Truth table::
+ *
+ *   Source hdr  Target supported[i]  Action
+ *   ----------  -------------------  ----------------------------------
+ *   00          0 or 1               Accept: feature absent in payload
+ *   01          0                    Accept: ignore the feature data
+ *   01          1                    Accept: use the feature data
+ *   11          0                    Reject: -EOPNOTSUPP
+ *   11          1                    Accept: use the feature data
+ *   10          0 or 1               Reserved: not checked by target
+ *
+ * Source hdr is the 2-bit state (enum luo_feat_state) of feature i in the
+ * header written by the source kernel. Target supported[i] is the boolean entry
+ * for feature i in the target kernel's supported feature table passed to
+ * luo_ser_hdr_validate() (1 = supported, 0 = not supported; an index at or
+ * beyond @nr_supported, or a NULL table, is 0).
+ *
+ * The relative order and offsets of fields in serialized structures must
+ * remain unchanged as features are added or retired. New fields are always
+ * appended at the end of the structure under a new feature flag, and
+ * retired fields are kept in place so that the offsets of subsequent
+ * fields never shift. If a field's type or format needs to change, the
+ * existing field and feature are kept intact (and retired when no longer
+ * needed) and a new feature flag with a new appended field of the new type
+ * is added.
+ *
+ * Apart from missing required features (-EOPNOTSUPP), `luo_ser_hdr_validate()`
+ * rejects a header only when the source kernel marks a feature `LUO_FEAT_REQUIRED`
+ * that the target kernel does not support. A feature the source kernel did
+ * not send reads as `LUO_FEAT_ABSENT`, so the target kernel reads the fields
+ * of a feature only after checking it::
+ *
+ *   if (luo_ser_get_feat(hdr, FEAT_X) != LUO_FEAT_ABSENT)
+ *           val = ser->x_field;
+ *
+ * The source kernel is responsible for marking a feature it relies on as
+ * `LUO_FEAT_REQUIRED`. The target kernel handles each feature in its own
+ * retrieve code, for example by falling back to default behavior when a
+ * feature is absent; it does not guard against a source kernel that sets
+ * the wrong states.
  *
  * KHO Structure Overview:
  *   The entire LUO state is encapsulated within a single KHO entry named "LUO".
- *   This entry contains the `struct luo_ser` structure.
+ *   This entry contains a `struct luo_ser_hdr` followed by the
+ *   `struct luo_ser` payload.
  *
  * 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
- *     to sessions and FLBs.
+ *     The central ABI payload structure that contains the overall state of
+ *     the LUO (`LUO_CORE_FEAT_CORE`: the liveupdate-number and physical
+ *     pointers to sessions and FLBs), preceded by `struct luo_ser_hdr`.
  *
  *   - struct luo_session_ser:
  *     Metadata for a single session, including its name and a physical pointer
@@ -40,7 +92,7 @@
  *
  *   - struct luo_file_ser:
  *     Metadata for a single preserved file. Contains the `compatible` string to
- *     find the correct handler in the new kernel, a user-provided `token` for
+ *     find the correct handler in the target kernel, a user-provided `token` for
  *     identification, and an opaque `data` handle for the handler to use.
  *
  *   - struct luo_flb_header_ser:
@@ -57,35 +109,247 @@
 #ifndef _LINUX_KHO_ABI_LUO_H
 #define _LINUX_KHO_ABI_LUO_H
 
-#include <linux/align.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
 #include <linux/build_bug.h>
+#include <linux/errno.h>
 #include <linux/kho/abi/block.h>
+#include <linux/math.h>
+#include <linux/string.h>
+#include <linux/types.h>
 #include <uapi/linux/liveupdate.h>
 
 /*
  * 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)
+
+#define LUO_FEAT_PER_FLAGS	32
+#define LUO_FEAT_STATE_MASK	0b11ULL
+
+/**
+ * enum luo_feat_state - 2-bit state of a feature in the serialized payload.
+ * @LUO_FEAT_ABSENT:   Feature is absent in the serialized payload (`0b00`).
+ * @LUO_FEAT_OPTIONAL: Feature is present and optional (`0b01`).
+ * @LUO_FEAT_RESERVED: Reserved encoding (`0b10`).
+ * @LUO_FEAT_REQUIRED: Feature is present and required (`0b11`).
+ */
+enum luo_feat_state {
+	LUO_FEAT_ABSENT   = 0b00,
+	LUO_FEAT_OPTIONAL = 0b01,
+	LUO_FEAT_RESERVED = 0b10,
+	LUO_FEAT_REQUIRED = 0b11,
+};
+
+/**
+ * struct luo_ser_hdr - Base 8-byte header for serialized LUO ABI structures.
+ * @size:        Total size of the serialized structure in bytes (header +
+ *               `flags` + payload).
+ * @nr_features: Number of features encoded in @flags.
+ * @flags:       64-bit feature flags entries (`DIV_ROUND_UP(@nr_features, 32)`
+ *               entries). Each entry encodes 32 features (2 bits per feature:
+ *               feature `N` is in `flags[N / 32]` bits
+ *               `[2*(N % 32) + 1 : 2*(N % 32)]`).
+ */
+struct luo_ser_hdr {
+	__u32 size;
+	__u32 nr_features;
+	__u64 flags[];
+};
+
+static_assert(sizeof(struct luo_ser_hdr) == 8);
+
+/*
+ * Each subsystem declares a wrapper structure with DECLARE_LUO_SER_WRAP() (for
+ * example `struct memfd_luo_ser_wrap` or `struct luo_ser_wrap`) holding the
+ * header, any 64-bit `flags` entries and its payload, allocates it with
+ * sizeof() and initializes the header with the generated wrapper initializer
+ * (e.g. luo_ser_wrap_init()). The structure matches only the layout this kernel
+ * writes, so incoming state is never cast to it; the target kernel uses
+ * luo_ser_hdr_validate() and luo_ser_payload() instead.
+ */
+
+#define LUO_SER_NR_FLAGS(nr_features)	DIV_ROUND_UP_POW2(nr_features, LUO_FEAT_PER_FLAGS)
+
+/**
+ * DECLARE_LUO_SER_WRAP - Declare an outgoing LUO header + payload wrapper structure.
+ * @name:          Name of the payload structure (`struct @name`).
+ * @expected_size: Expected total size in bytes of `struct @name_wrap`.
+ * @nr_features:   Number of features defined for `struct @name`.
+ *
+ * Declares `struct @name_wrap` and inline initializer `@name_wrap_init()`
+ * for the source kernel to allocate and populate when preserving state.
+ * It matches only the layout written by this kernel; incoming state from
+ * another kernel must be validated with luo_ser_hdr_validate() and its
+ * payload reached via luo_ser_payload().
+ */
+#define DECLARE_LUO_SER_WRAP(name, expected_size, nr_features)		\
+	struct name##_wrap {						\
+		union {							\
+			struct luo_ser_hdr hdr;				\
+			struct {					\
+				__u32 __size;				\
+				__u32 __nr_features;			\
+				__u64 flags[LUO_SER_NR_FLAGS(nr_features)];\
+				struct name ser;			\
+			};						\
+		};							\
+	};								\
+	static_assert(sizeof(struct name##_wrap) == (expected_size),	\
+		      "LUO struct " #name " size mismatch!");		\
+	static inline void name##_wrap_init(struct name##_wrap *wrap)	\
+	{								\
+		luo_ser_hdr_init(&wrap->hdr, sizeof(*wrap), (nr_features));\
+	}
+
+/**
+ * luo_ser_hdr_size - Header size of a serialized header.
+ * @hdr: Pointer to the serialized &struct luo_ser_hdr.
+ *
+ * Return: Size of @hdr in bytes, including its 64-bit `flags` entries.
+ */
+static inline size_t luo_ser_hdr_size(const struct luo_ser_hdr *hdr)
+{
+	return struct_size(hdr, flags, LUO_SER_NR_FLAGS(hdr->nr_features));
+}
+
+/**
+ * luo_ser_hdr_init - Initialize a feature header.
+ * @hdr:         Pointer to the &struct luo_ser_hdr to initialize.
+ * @size:        Total size of the serialized structure (header, `flags` entries
+ *               and payload), normally sizeof() of the wrapper structure.
+ * @nr_features: Number of features to accommodate.
+ *
+ * Sets @hdr->size and @hdr->nr_features, and clears the 64-bit `flags` entries.
+ * Touches only the header.
+ */
+static inline void
+luo_ser_hdr_init(struct luo_ser_hdr *hdr, size_t size, u32 nr_features)
+{
+	hdr->size = size;
+	hdr->nr_features = nr_features;
+	memset(hdr->flags, 0, LUO_SER_NR_FLAGS(nr_features) * sizeof(hdr->flags[0]));
+}
+
+/*
+ * These helpers operate on any well-formed header: one initialized on the
+ * outgoing path or validated by luo_ser_hdr_validate() on the incoming path.
+ */
+
+/**
+ * luo_ser_payload - Get pointer to the payload immediately following @hdr.
+ * @hdr: Pointer to the serialized &struct luo_ser_hdr.
+ *
+ * @hdr must be initialized or validated.
+ *
+ * Return: Pointer to the payload structure.
+ */
+static inline void *luo_ser_payload(const struct luo_ser_hdr *hdr)
+{
+	return (void *)((const char *)hdr + luo_ser_hdr_size(hdr));
+}
+
+/**
+ * luo_ser_set_feat - Set the 2-bit state of feature @feat in @hdr.
+ * @hdr:   Pointer to &struct luo_ser_hdr.
+ * @feat:  Feature index.
+ * @state: Feature state (&enum luo_feat_state).
+ *
+ * @hdr must be initialized or validated.
+ */
+static inline void
+luo_ser_set_feat(struct luo_ser_hdr *hdr, u32 feat, enum luo_feat_state state)
+{
+	u32 idx, slot;
+
+	if (WARN_ON_ONCE(feat >= hdr->nr_features))
+		return;
+
+	idx = feat / LUO_FEAT_PER_FLAGS;
+	slot = feat % LUO_FEAT_PER_FLAGS;
+	hdr->flags[idx] &= ~(LUO_FEAT_STATE_MASK << (2 * slot));
+	hdr->flags[idx] |= (u64)(state & LUO_FEAT_STATE_MASK) << (2 * slot);
+}
+
+/**
+ * luo_ser_get_feat - Query the 2-bit state of feature @feat in @hdr.
+ * @hdr:  Pointer to &struct luo_ser_hdr.
+ * @feat: Feature index.
+ *
+ * @hdr must be initialized or validated.
+ *
+ * Return: &enum luo_feat_state (returns %LUO_FEAT_ABSENT if @feat is beyond
+ *         @hdr->nr_features).
+ */
+static inline enum luo_feat_state
+luo_ser_get_feat(const struct luo_ser_hdr *hdr, u32 feat)
+{
+	u32 idx, slot;
+
+	if (feat >= hdr->nr_features)
+		return LUO_FEAT_ABSENT;
+
+	idx = feat / LUO_FEAT_PER_FLAGS;
+	slot = feat % LUO_FEAT_PER_FLAGS;
+
+	return (hdr->flags[idx] >> (2 * slot)) & LUO_FEAT_STATE_MASK;
+}
 
 /**
- * struct luo_ser - Centralized LUO ABI header.
- * @compatible:     Compatibility string identifying the LUO ABI version.
+ * luo_ser_hdr_validate - Validate source kernel's feature header.
+ * @hdr:          Pointer to the feature header from the source kernel.
+ * @supported:    Target kernel's supported features table, indexed by feature.
+ * @nr_supported: Number of entries in @supported.
+ *
+ * Checks each source feature state in @hdr against @supported[i] using the
+ * truth table in the DOC section above.
+ *
+ * Return: 0 if compatible, or -EOPNOTSUPP on required feature mismatch.
+ */
+static inline int
+luo_ser_hdr_validate(const struct luo_ser_hdr *hdr,
+		     const bool *supported, u32 nr_supported)
+{
+	u32 feat;
+
+	for (feat = 0; feat < hdr->nr_features; feat++)
+		if (luo_ser_get_feat(hdr, feat) == LUO_FEAT_REQUIRED &&
+		    (feat >= nr_supported || !supported[feat]))
+			return -EOPNOTSUPP;
+
+	return 0;
+}
+
+/**
+ * enum luo_core_feature - Feature indices for &struct luo_ser.
+ * @LUO_CORE_FEAT_CORE: Core feature covering `liveupdate_num`, `sessions_pa`
+ *                      and `flbs_pa`.
+ * @LUO_CORE_FEAT_MAX:  Number of defined &enum luo_core_feature features.
+ */
+enum luo_core_feature {
+	LUO_CORE_FEAT_CORE = 0,
+	LUO_CORE_FEAT_MAX,
+};
+
+/**
+ * struct luo_ser - Centralized LUO ABI payload.
  * @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.
  *
- * This structure is the root of all preserved LUO state.
+ * This structure is the root payload of all preserved LUO state, preceded by
+ * &struct luo_ser_hdr.
  */
 struct luo_ser {
-	char compatible[LUO_ABI_COMPAT_LEN];
 	u64 liveupdate_num;
 	u64 sessions_pa;
 	u64 flbs_pa;
 };
 
-static_assert(sizeof(struct luo_ser) == 32);
+static_assert(sizeof(struct luo_ser) == 24);
+
+/* LUO state written by this kernel. Never cast incoming state to it. */
+DECLARE_LUO_SER_WRAP(luo_ser, 40, LUO_CORE_FEAT_MAX);
 
 #define LIVEUPDATE_HNDL_COMPAT_LENGTH	48
 
@@ -95,7 +359,7 @@ static_assert(sizeof(struct luo_ser) == 32);
  * @data:        Private data
  * @token:       User provided token for this file
  *
- * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
+ * Layout changes must be gated by a feature in &enum luo_core_feature.
  */
 struct luo_file_ser {
 	char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH];
@@ -130,10 +394,10 @@ static_assert(sizeof(struct luo_file_set_ser) == 16);
  *
  * This structure is used to package session-specific metadata for transfer
  * between kernels via Kexec Handover. An array of these structures (one per
- * session) is created and passed to the new kernel, allowing it to reconstruct
- * the session context.
+ * session) is created and passed to the target kernel, allowing it to
+ * reconstruct the session context.
  *
- * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
+ * Layout changes must be gated by a feature in &enum luo_core_feature.
  */
 struct luo_session_ser {
 	char name[LIVEUPDATE_SESSION_NAME_LENGTH];
@@ -156,7 +420,7 @@ static_assert(sizeof(struct luo_session_ser) == 80);
  * This structure is located at the physical address specified by the
  * flbs_pa in luo_ser.
  *
- * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
+ * Layout changes must be gated by a feature in &enum luo_core_feature.
  */
 struct luo_flb_header_ser {
 	u64 pgcnt;
@@ -168,19 +432,19 @@ static_assert(sizeof(struct luo_flb_header_ser) == 16);
 /**
  * struct luo_flb_ser - Represents the serialized state of a single FLB object.
  * @name:    The unique compatibility string of the FLB object, used to find the
- *           corresponding &struct liveupdate_flb handler in the new kernel.
+ *           corresponding &struct liveupdate_flb in the target kernel.
  * @data:    The opaque u64 handle returned by the FLB's .preserve() operation
- *           in the old kernel. This handle encapsulates the entire state needed
- *           for restoration.
+ *           in the source kernel. This handle encapsulates the entire state
+ *           needed for restoration.
  * @count:   The reference count at the time of serialization; i.e., the number
  *           of preserved files that depended on this FLB. This is used by the
- *           new kernel to correctly manage the FLB's lifecycle.
+ *           target 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.
+ * passed to the target kernel. Each entry allows the LUO core to restore
+ * one global, shared object.
  *
- * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
+ * Layout changes must be gated by a feature in &enum luo_core_feature.
  */
 struct luo_flb_ser {
 	char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 1b2bda22902d..ab0bcb5f6e11 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -66,10 +66,14 @@
 
 static struct {
 	bool enabled;
-	struct luo_ser *luo_ser_out;
+	struct luo_ser_wrap *luo_ser_out;
 	u64 liveupdate_num;
 } luo_global;
 
+static const bool luo_core_supported_feats[LUO_CORE_FEAT_MAX] = {
+	[LUO_CORE_FEAT_CORE] = true,
+};
+
 /*
  * luo_register_rwlock - Protects registration of file handlers and FLBs.
  */
@@ -84,7 +88,8 @@ early_param("liveupdate", early_liveupdate_param);
 static int __init luo_early_startup(void)
 {
 	phys_addr_t luo_ser_phys;
-	struct luo_ser *luo_ser;
+	struct luo_ser_hdr *hdr;
+	const struct luo_ser *ser;
 	size_t len;
 	int err;
 
@@ -107,31 +112,38 @@ 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;
+	hdr = phys_to_virt(luo_ser_phys);
+	if (len < sizeof(*hdr) || hdr->size > len) {
+		pr_err("LUO state size invalid (source kernel header size=%u, KHO len=%zu)\n",
+		       len >= sizeof(*hdr) ? hdr->size : 0, len);
+		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;
+	/* Only the core fields are needed; later fields are checked per feature. */
+	err = luo_ser_hdr_validate(hdr, luo_core_supported_feats,
+				   ARRAY_SIZE(luo_core_supported_feats));
+	if (err) {
+		pr_err("LUO state feature header validation failed (size=%u, nr_features=%u): %pe\n",
+		       hdr->size, hdr->nr_features, ERR_PTR(err));
+		goto out_free_ser;
 	}
 
-	luo_global.liveupdate_num = luo_ser->liveupdate_num;
+	ser = luo_ser_payload(hdr);
+	luo_global.liveupdate_num = 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);
+	err = luo_session_setup_incoming(ser->sessions_pa);
 	if (err)
 		goto out_free_ser;
 
-	luo_flb_setup_incoming(luo_ser->flbs_pa);
+	luo_flb_setup_incoming(ser->flbs_pa);
 
 	err = 0;
 
 out_free_ser:
-	kho_restore_free(luo_ser);
+	kho_restore_free(hdr);
 	return err;
 }
 
@@ -153,34 +165,36 @@ early_initcall(liveupdate_early_init);
 /* Called during boot to create outgoing LUO state */
 static int __init luo_state_setup(void)
 {
-	struct luo_ser *luo_ser;
+	struct luo_ser_wrap *wrap;
 	int err;
 
-	luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
-	if (IS_ERR(luo_ser)) {
+	wrap = kho_alloc_preserve(sizeof(*wrap));
+	if (IS_ERR(wrap)) {
 		pr_err("failed to allocate/preserve LUO state memory\n");
-		return PTR_ERR(luo_ser);
+		return PTR_ERR(wrap);
 	}
 
-	strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
-	luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
+	luo_ser_wrap_init(wrap);
+	luo_ser_set_feat(&wrap->hdr, LUO_CORE_FEAT_CORE, LUO_FEAT_REQUIRED);
+
+	wrap->ser.liveupdate_num = luo_global.liveupdate_num + 1;
 
-	luo_session_setup_outgoing(&luo_ser->sessions_pa);
+	luo_session_setup_outgoing(&wrap->ser.sessions_pa);
 
-	err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
+	err = luo_flb_setup_outgoing(&wrap->ser.flbs_pa);
 	if (err)
 		goto exit_free_luo_ser;
 
-	err = kho_add_subtree(LUO_KHO_ENTRY_NAME, luo_ser, sizeof(*luo_ser));
+	err = kho_add_subtree(LUO_KHO_ENTRY_NAME, wrap, sizeof(*wrap));
 	if (err)
 		goto exit_free_luo_ser;
 
-	luo_global.luo_ser_out = luo_ser;
+	luo_global.luo_ser_out = wrap;
 
 	return 0;
 
 exit_free_luo_ser:
-	kho_unpreserve_free(luo_ser);
+	kho_unpreserve_free(wrap);
 	pr_err("failed to prepare LUO state: %d\n", err);
 
 	return err;
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v3 3/4] mm: memfd_luo: Add the feature header
  2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 1/4] liveupdate: Remove __packed from LUO and memfd ABI structures Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 2/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
@ 2026-10-26 17:42 ` Tarun Sahu
  2026-10-26 17:42 ` [RFC PATCH v3 4/4] liveupdate: Add KUnit test for the feature header helpers Tarun Sahu
  2026-10-26 18:58 ` [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility tarunsahu
  4 siblings, 0 replies; 6+ messages in thread
From: Tarun Sahu @ 2026-10-26 17:42 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec, Tarun Sahu

Precede struct memfd_luo_ser with the 8-byte struct luo_ser_hdr for
memfd preservation. The source kernel writes the state through struct
memfd_luo_ser_wrap, declared with DECLARE_LUO_SER_WRAP():

  DECLARE_LUO_SER_WRAP(memfd_luo_ser, 64, MEMFD_LUO_FEAT_MAX);

This consists of hdr + memfd_luo_ser (payload).

The existing struct memfd_luo_ser fields (pos, size, seals, flags,
nr_folios, folios, and MEMFD_LUO_ALL_SEALS) form MEMFD_LUO_FEAT_CORE
(index 0 of enum memfd_luo_feature). The source kernel marks it as
LUO_FEAT_REQUIRED when preserving state, and the target kernel marks it
as supported (true) in its memfd_luo_supported_feats table passed to
luo_ser_hdr_validate(). Future changes append any new fields to the end
of struct memfd_luo_ser under a new feature index so existing field
offsets never shift.

memfd_luo_retrieve() and memfd_luo_finish() validate the incoming header
against memfd_luo_supported_feats with luo_ser_hdr_validate() before
accessing any payload field; finish warns and skips discarding the folios
when the header is invalid.

Prepending the header shifts every field of the serialized memfd state,
so bump MEMFD_LUO_FH_COMPATIBLE from memfd-v2 to memfd-v3.

Signed-off-by: Logan Odell <loganodell@google.com>
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
 include/linux/kho/abi/memfd.h | 32 +++++++++++++++--
 mm/memfd_luo.c                | 68 ++++++++++++++++++++++++-----------
 2 files changed, 77 insertions(+), 23 deletions(-)

diff --git a/include/linux/kho/abi/memfd.h b/include/linux/kho/abi/memfd.h
index 7115a8a77cfd..d467d18fe6e8 100644
--- a/include/linux/kho/abi/memfd.h
+++ b/include/linux/kho/abi/memfd.h
@@ -14,6 +14,7 @@
 #include <linux/build_bug.h>
 #include <linux/types.h>
 #include <linux/kho/abi/kexec_handover.h>
+#include <linux/kho/abi/luo.h>
 
 /**
  * DOC: memfd Live Update ABI
@@ -22,9 +23,18 @@
  * reboot using the LUO.
  *
  * The state is serialized into `struct memfd_luo_ser`
- * which is handed over to the next kernel via the KHO mechanism.
+ * which is handed over to the target kernel via the KHO mechanism.
  *
- * This interface is a contract. Any modification to the structure layout
+ * This interface is a contract. The serialized state starts with an
+ * 8-byte `struct luo_ser_hdr` followed by `struct memfd_luo_ser`.
+ * The relative order and offsets of fields must remain unchanged as
+ * features are added or retired: new fields are appended at the end of
+ * `struct memfd_luo_ser` under a new feature flag, and retired fields are
+ * kept in place so subsequent offsets never shift. In the target kernel,
+ * the fields of a feature are read only after `luo_ser_get_feat()` shows
+ * the feature present.
+ *
+ * Any modification to the structure layout beyond feature extensions
  * constitutes a breaking change. Such changes require incrementing the
  * version number in the MEMFD_LUO_FH_COMPATIBLE string.
  */
@@ -70,6 +80,17 @@ static_assert(sizeof(struct memfd_luo_folio_ser) == 16);
 			     F_SEAL_FUTURE_WRITE | \
 			     F_SEAL_EXEC)
 
+/**
+ * enum memfd_luo_feature - Feature indices for &struct memfd_luo_ser.
+ * @MEMFD_LUO_FEAT_CORE: Core feature covering `pos`, `size`, `seals`, `flags`,
+ *                       `nr_folios`, `folios`, and %MEMFD_LUO_ALL_SEALS.
+ * @MEMFD_LUO_FEAT_MAX:  Number of defined &enum memfd_luo_feature features.
+ */
+enum memfd_luo_feature {
+	MEMFD_LUO_FEAT_CORE = 0,
+	MEMFD_LUO_FEAT_MAX,
+};
+
 /**
  * struct memfd_luo_ser - Main serialization structure for a memfd.
  * @pos:       The file's current position (f_pos).
@@ -80,6 +101,8 @@ static_assert(sizeof(struct memfd_luo_folio_ser) == 16);
  * @nr_folios: Number of folios in the folios array.
  * @folios:    KHO vmalloc descriptor pointing to the array of
  *             struct memfd_luo_folio_ser.
+ *
+ * Preceded by &struct luo_ser_hdr.
  */
 struct memfd_luo_ser {
 	u64 pos;
@@ -92,7 +115,10 @@ struct memfd_luo_ser {
 
 static_assert(sizeof(struct memfd_luo_ser) == 48);
 
+/* memfd state written by this kernel. Never cast incoming state to it. */
+DECLARE_LUO_SER_WRAP(memfd_luo_ser, 64, MEMFD_LUO_FEAT_MAX);
+
 /* The compatibility string for memfd file handler */
-#define MEMFD_LUO_FH_COMPATIBLE	"memfd-v2"
+#define MEMFD_LUO_FH_COMPATIBLE	"memfd-v3"
 
 #endif /* _LINUX_KHO_ABI_MEMFD_H */
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index 6b68220275eb..85424a5fbc20 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -84,6 +84,10 @@
 
 #include "internal.h"
 
+static const bool memfd_luo_supported_feats[MEMFD_LUO_FEAT_MAX] = {
+	[MEMFD_LUO_FEAT_CORE] = true,
+};
+
 static int memfd_luo_preserve_folios(struct file *file,
 				     struct kho_vmalloc *kho_vmalloc,
 				     struct memfd_luo_folio_ser **out_folios_ser,
@@ -258,6 +262,7 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
 {
 	struct inode *inode = file_inode(args->file);
 	struct memfd_luo_folio_ser *folios_ser;
+	struct memfd_luo_ser_wrap *wrap;
 	struct memfd_luo_ser *ser;
 	u64 nr_folios, inode_size;
 	int err = 0, seals;
@@ -266,9 +271,9 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
 	shmem_freeze(inode, true);
 
 	/* Allocate the main serialization structure in preserved memory */
-	ser = kho_alloc_preserve(sizeof(*ser));
-	if (IS_ERR(ser)) {
-		err = PTR_ERR(ser);
+	wrap = kho_alloc_preserve(sizeof(*wrap));
+	if (IS_ERR(wrap)) {
+		err = PTR_ERR(wrap);
 		goto err_unlock;
 	}
 
@@ -284,6 +289,10 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
 		goto err_free_ser;
 	}
 
+	memfd_luo_ser_wrap_init(wrap);
+	luo_ser_set_feat(&wrap->hdr, MEMFD_LUO_FEAT_CORE, LUO_FEAT_REQUIRED);
+
+	ser = &wrap->ser;
 	ser->pos = args->file->f_pos;
 	inode_size = i_size_read(inode);
 
@@ -308,12 +317,12 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
 	inode_unlock(inode);
 
 	args->private_data = folios_ser;
-	args->serialized_data = virt_to_phys(ser);
+	args->serialized_data = virt_to_phys(wrap);
 
 	return 0;
 
 err_free_ser:
-	kho_unpreserve_free(ser);
+	kho_unpreserve_free(wrap);
 err_unlock:
 	shmem_freeze(inode, false);
 	inode_unlock(inode);
@@ -322,18 +331,18 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
 
 static int memfd_luo_freeze(struct liveupdate_file_op_args *args)
 {
-	struct memfd_luo_ser *ser;
+	struct memfd_luo_ser_wrap *wrap;
 
 	if (WARN_ON_ONCE(!args->serialized_data))
 		return -EINVAL;
 
-	ser = phys_to_virt(args->serialized_data);
+	wrap = phys_to_virt(args->serialized_data);
 
 	/*
 	 * The pos might have changed since prepare. Everything else stays the
 	 * same.
 	 */
-	ser->pos = args->file->f_pos;
+	wrap->ser.pos = args->file->f_pos;
 
 	return 0;
 }
@@ -341,7 +350,7 @@ static int memfd_luo_freeze(struct liveupdate_file_op_args *args)
 static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args)
 {
 	struct inode *inode = file_inode(args->file);
-	struct memfd_luo_ser *ser;
+	struct memfd_luo_ser_wrap *wrap;
 
 	if (WARN_ON_ONCE(!args->serialized_data))
 		return;
@@ -349,12 +358,12 @@ static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args)
 	inode_lock(inode);
 	shmem_freeze(inode, false);
 
-	ser = phys_to_virt(args->serialized_data);
+	wrap = phys_to_virt(args->serialized_data);
 
-	memfd_luo_unpreserve_folios(&ser->folios, args->private_data,
-				    ser->nr_folios);
+	memfd_luo_unpreserve_folios(&wrap->ser.folios, args->private_data,
+				    wrap->ser.nr_folios);
 
-	kho_unpreserve_free(ser);
+	kho_unpreserve_free(wrap);
 	inode_unlock(inode);
 }
 
@@ -385,6 +394,7 @@ static void memfd_luo_discard_folios(const struct memfd_luo_folio_ser *folios_se
 
 static void memfd_luo_finish(struct liveupdate_file_op_args *args)
 {
+	struct luo_ser_hdr *hdr;
 	struct memfd_luo_folio_ser *folios_ser;
 	struct memfd_luo_ser *ser;
 
@@ -396,10 +406,17 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args)
 	if (args->retrieve_status)
 		return;
 
-	ser = phys_to_virt(args->serialized_data);
-	if (!ser)
+	if (WARN_ON_ONCE(!args->serialized_data))
 		return;
 
+	hdr = phys_to_virt(args->serialized_data);
+	if (luo_ser_hdr_validate(hdr, memfd_luo_supported_feats,
+				 ARRAY_SIZE(memfd_luo_supported_feats))) {
+		pr_warn("invalid feature header, folios not discarded\n");
+		goto out;
+	}
+
+	ser = luo_ser_payload(hdr);
 	if (ser->nr_folios) {
 		folios_ser = kho_restore_vmalloc(&ser->folios);
 		if (!folios_ser)
@@ -410,7 +427,7 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args)
 	}
 
 out:
-	kho_restore_free(ser);
+	kho_restore_free(hdr);
 }
 
 static int memfd_luo_retrieve_folios(struct file *file,
@@ -516,15 +533,26 @@ static int memfd_luo_retrieve_folios(struct file *file,
 
 static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
 {
+	struct luo_ser_hdr *hdr;
 	struct memfd_luo_folio_ser *folios_ser;
 	struct memfd_luo_ser *ser;
 	struct file *file;
 	int err;
 
-	ser = phys_to_virt(args->serialized_data);
-	if (!ser)
+	if (WARN_ON_ONCE(!args->serialized_data))
 		return -EINVAL;
 
+	hdr = phys_to_virt(args->serialized_data);
+	err = luo_ser_hdr_validate(hdr, memfd_luo_supported_feats,
+				   ARRAY_SIZE(memfd_luo_supported_feats));
+	if (err) {
+		pr_err("memfd_luo_ser feature header validation failed (size=%u, nr_features=%u): %pe\n",
+		       hdr->size, hdr->nr_features, ERR_PTR(err));
+		goto free_ser;
+	}
+
+	ser = luo_ser_payload(hdr);
+
 	/* Make sure the file only has seals supported by this version. */
 	if (ser->seals & ~MEMFD_LUO_ALL_SEALS) {
 		err = -EOPNOTSUPP;
@@ -565,14 +593,14 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
 	}
 
 	args->file = file;
-	kho_restore_free(ser);
+	kho_restore_free(hdr);
 
 	return 0;
 
 put_file:
 	fput(file);
 free_ser:
-	kho_restore_free(ser);
+	kho_restore_free(hdr);
 	return err;
 }
 
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v3 4/4] liveupdate: Add KUnit test for the feature header helpers
  2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
                   ` (2 preceding siblings ...)
  2026-10-26 17:42 ` [RFC PATCH v3 3/4] mm: memfd_luo: Add the feature header Tarun Sahu
@ 2026-10-26 17:42 ` Tarun Sahu
  2026-10-26 18:58 ` [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility tarunsahu
  4 siblings, 0 replies; 6+ messages in thread
From: Tarun Sahu @ 2026-10-26 17:42 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec, Tarun Sahu

Add a KUnit test suite in lib/tests/liveupdate_feature_kunit.c covering
the Live Update feature header declared in include/linux/kho/abi/luo.h.

The suite contains two focused test cases:
- luo_ser_hdr_set_get_test: verifies DECLARE_LUO_SER_WRAP structure
  sizing, header initialization, payload pointer resolution, and 2-bit
  feature flag setting and retrieval across flags entry boundaries,
  including out-of-range queries.
- luo_ser_hdr_truth_table_test: verifies luo_ser_hdr_validate against
  all rows of the feature state truth table (absent, optional,
  required, and reserved 0b10 encodings), as well as empty supported
  tables.

Signed-off-by: Tarun Sahu <tarunsahu@google.com>
---
 MAINTAINERS                          |   1 +
 lib/Kconfig.debug                    |  16 ++++
 lib/tests/Makefile                   |   1 +
 lib/tests/liveupdate_feature_kunit.c | 118 +++++++++++++++++++++++++++
 4 files changed, 136 insertions(+)
 create mode 100644 lib/tests/liveupdate_feature_kunit.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5d1b58271989..dad09961c275 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15191,6 +15191,7 @@ F:	include/linux/liveupdate.h
 F:	include/uapi/linux/liveupdate.h
 F:	kernel/liveupdate/
 F:	lib/tests/liveupdate.c
+F:	lib/tests/liveupdate_feature_kunit.c
 F:	mm/memfd_luo.c
 F:	tools/testing/selftests/liveupdate/
 
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 134b15a44625..d8208f3f4515 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2906,6 +2906,22 @@ config LIVEUPDATE_TEST
 
 	  If unsure, say N
 
+config LIVEUPDATE_FEATURE_KUNIT_TEST
+	tristate "KUnit test for the Live Update feature header helpers" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  This option enables the KUnit test suite for the Live Update
+	  Orchestrator feature header helpers defined in
+	  include/linux/kho/abi/luo.h. It checks the feature flag encoding,
+	  the flags array sizing, the source/target kernel validation
+	  truth table, header initialization and the payload size checks.
+
+	  The helpers are self-contained, so the test does not depend on
+	  CONFIG_LIVEUPDATE and can also run under UML.
+
+	  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 3cac3b63a752..2f1f1f765617 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_KFIFO_KUNIT_TEST) += kfifo_kunit.o
 obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
 obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
 obj-$(CONFIG_LIVEUPDATE_TEST) += liveupdate.o
+obj-$(CONFIG_LIVEUPDATE_FEATURE_KUNIT_TEST) += liveupdate_feature_kunit.o
 
 CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes)
 obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o
diff --git a/lib/tests/liveupdate_feature_kunit.c b/lib/tests/liveupdate_feature_kunit.c
new file mode 100644
index 000000000000..fc9d2de263d9
--- /dev/null
+++ b/lib/tests/liveupdate_feature_kunit.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the Live Update Orchestrator feature header helpers
+ * in include/linux/kho/abi/luo.h.
+ *
+ * Copyright (c) 2026, Google LLC.
+ * Tarun Sahu <tarunsahu@google.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/kho/abi/luo.h>
+#include <linux/module.h>
+
+struct luo_test_multi_ser {
+	u64 val;
+};
+
+static_assert(sizeof(struct luo_test_multi_ser) == 8);
+DECLARE_LUO_SER_WRAP(luo_test_multi_ser, 32, 35);
+
+struct luo_test_one_ser {
+	u64 val;
+};
+
+static_assert(sizeof(struct luo_test_one_ser) == 8);
+DECLARE_LUO_SER_WRAP(luo_test_one_ser, 24, 1);
+
+struct luo_test_zero_ser {
+	u64 val;
+};
+
+static_assert(sizeof(struct luo_test_zero_ser) == 8);
+DECLARE_LUO_SER_WRAP(luo_test_zero_ser, 16, 0);
+
+static void luo_ser_hdr_set_get_test(struct kunit *test)
+{
+	struct luo_test_multi_ser_wrap out;
+	struct luo_test_zero_ser_wrap zero;
+
+	memset(&out, 0, sizeof(out));
+	luo_test_multi_ser_wrap_init(&out);
+
+	KUNIT_EXPECT_EQ(test, out.hdr.size, 32U);
+	KUNIT_EXPECT_EQ(test, out.hdr.nr_features, 35U);
+	KUNIT_EXPECT_EQ(test, LUO_SER_NR_FLAGS(out.hdr.nr_features), 2U);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_size(&out.hdr), 24UL);
+	KUNIT_EXPECT_PTR_EQ(test, luo_ser_payload(&out.hdr), (void *)&out.ser);
+
+	/* 0, 31 in flags[0]; 32, 34 in flags[1] */
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED);
+	luo_ser_set_feat(&out.hdr, 31, LUO_FEAT_OPTIONAL);
+	luo_ser_set_feat(&out.hdr, 32, LUO_FEAT_REQUIRED);
+	luo_ser_set_feat(&out.hdr, 34, LUO_FEAT_OPTIONAL);
+
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 0), (int)LUO_FEAT_REQUIRED);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 1), (int)LUO_FEAT_ABSENT);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 31), (int)LUO_FEAT_OPTIONAL);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 32), (int)LUO_FEAT_REQUIRED);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 33), (int)LUO_FEAT_ABSENT);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 34), (int)LUO_FEAT_OPTIONAL);
+
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 35), (int)LUO_FEAT_ABSENT);
+	KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 100), (int)LUO_FEAT_ABSENT);
+
+	memset(&zero, 0, sizeof(zero));
+	luo_test_zero_ser_wrap_init(&zero);
+	KUNIT_EXPECT_EQ(test, sizeof(struct luo_ser_hdr), 8UL);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_size(&zero.hdr), 8UL);
+	KUNIT_EXPECT_PTR_EQ(test, luo_ser_payload(&zero.hdr), (void *)&zero.ser);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&zero.hdr, NULL, 0), 0);
+}
+
+static void luo_ser_hdr_truth_table_test(struct kunit *test)
+{
+	struct luo_test_one_ser_wrap out;
+	bool supp_yes[1] = { true };
+	bool supp_no[1] = { false };
+
+	memset(&out, 0, sizeof(out));
+	luo_test_one_ser_wrap_init(&out);
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_ABSENT);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0);
+
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_OPTIONAL);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0);
+
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1),
+			-EOPNOTSUPP);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0);
+
+	out.flags[0] = (u64)LUO_FEAT_RESERVED;
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0);
+
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, NULL, 0),
+			-EOPNOTSUPP);
+	luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_OPTIONAL);
+	KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, NULL, 0), 0);
+}
+
+static struct kunit_case luo_feature_test_cases[] = {
+	KUNIT_CASE(luo_ser_hdr_set_get_test),
+	KUNIT_CASE(luo_ser_hdr_truth_table_test),
+	{}
+};
+
+static struct kunit_suite luo_feature_test_suite = {
+	.name = "liveupdate_feature",
+	.test_cases = luo_feature_test_cases,
+};
+
+kunit_test_suite(luo_feature_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for the Live Update feature header helpers");
+MODULE_LICENSE("GPL");
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility
  2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
                   ` (3 preceding siblings ...)
  2026-10-26 17:42 ` [RFC PATCH v3 4/4] liveupdate: Add KUnit test for the feature header helpers Tarun Sahu
@ 2026-10-26 18:58 ` tarunsahu
  4 siblings, 0 replies; 6+ messages in thread
From: tarunsahu @ 2026-10-26 18:58 UTC (permalink / raw)
  To: loganodell, alex, dwmw2, seanjc, Mike Rapoport, will, pbonzini,
	jgg, Pasha Tatashin, arnd, linux-mm, Andrew Morton, oupton,
	baolu.lu, David Matlack, robin.murphy, maz, Alexander Graf, joro,
	Bjorn Helgaas, kevin.tian, Pratyush Yadav
  Cc: linux-kernel, kexec


Withdrawing this version because of the following reasons.

1. KHO also has versioning which can also share the same concept of the
feature flags, And the header can be shared amongst KHO and LUO. In next
version, I will include this.

2. If there are any open threads in the V2, Can be settled based on this V3
reference. This V3 has included the suggestion from V2, like updated
truth table, nr_features based flags array, ABI wrapper MACRO etc.

~Tarun


Tarun Sahu <tarunsahu@google.com> writes:

> Changes since RFC v2 [1]:
> - Dropped the file handler compatible rename and per-FD handler
>   selection patches.
> - Replaced the fixed 128-byte header with an expandable struct
>   luo_ser_hdr (size, nr_features, flags[]), kept separate from the
>   payload structure. The header is 8 bytes with no features and 16 bytes
>   for 1 to 32 features.
> - Each subsystem declares its wrapper once with DECLARE_LUO_SER_WRAP(),
>   passing its feature enum MAX; the macro sizes the flags entries and
>   generates name##_wrap_init(), so callers never pass sizes or counts.
>   The target kernel reaches the payload with luo_ser_payload().
> - Feature bits describe only what each preserved object uses. The target
>   kernel checks them against its own boolean supported table, and the
>   header is trusted as the source of truth: validation only rejects a
>   required feature the target kernel does not support. 0b10 is reserved.
> - Moved the __packed removal into its own patch (1/4) and added a KUnit
>   test (4/4).
>
> Changes since RFC v1 [2]:
> - Replaced the 3-mask (supp, req, active) design with a header of 2-bit
>   feature states.
>
> [1] https://lore.kernel.org/all/20261021152527.375070-1-tarunsahu@google.com/
> [2] https://lore.kernel.org/all/20260903023452.721732-1-loganodell@google.com/
>
> ==========COVER LETTER==============
>
> LUO and its file handlers rely only on coarse-grained compatibility
> strings (such as luo-v5 and memfd-v2) to validate handed-over state
> across kexec. Any change to a serialized structure, even an optional
> additive field, requires bumping the string and breaks live update
> between adjacent kernels.
>
> This series adds a feature header (struct luo_ser_hdr) ahead of
> serialized LUO structures so subsystems can evolve their serialized state
> per feature. Each subsystem groups its serialized fields under feature
> indices, with index 0 for the base fields. New capabilities append fields
> at the end of the payload under a new feature index, and retired fields
> stay in place so offsets never shift. Only a change that feature flags
> cannot express needs a new handler with a bumped compatibility string.
>
> Each feature has a 2-bit state set by the source kernel:
> - LUO_FEAT_ABSENT (0b00): not in the payload; a target kernel that
>   supports it can use default values.
> - LUO_FEAT_OPTIONAL (0b01): in the payload; a target kernel that does not
>   support it ignores the data.
> - LUO_FEAT_REQUIRED (0b11): in the payload; a target kernel that does not
>   support it rejects the state with -EOPNOTSUPP.
> - LUO_FEAT_RESERVED (0b10): reserved, not checked by the target kernel.
>
> The target kernel checks the header against its own boolean supported
> table:
>
>   Source hdr  Target supported[i]  Action
>   ----------  -------------------  ----------------------------------
>   00          0 or 1               Accept: feature absent in payload
>   01          0                    Accept: ignore the feature data
>   01          1                    Accept: use the feature data
>   11          0                    Reject: -EOPNOTSUPP
>   11          1                    Accept: use the feature data
>   10          0 or 1               Reserved: not checked by target
>
> struct luo_ser_hdr holds the total size of the serialized structure
> (u32), the number of features (u32 nr_features), and 64-bit flags
> entries with 32 features each. With no features the header is 8 bytes;
> with 1 to 32 features it is 16 bytes.
>
> On the source side, a subsystem declares its wrapper once with
> DECLARE_LUO_SER_WRAP(name, expected_size, nr_features), passing its
> feature enum MAX, then calls name##_wrap_init(), marks the features the
> preserved object uses with luo_ser_set_feat(), and fills wrap->ser. On
> the target side, incoming state is never cast to the wrapper, since the
> source kernel may use a different number of flags entries. The target
> kernel calls luo_ser_hdr_validate(), finds the payload with
> luo_ser_payload(), and reads the fields of a feature only after
> luo_ser_get_feat() shows it present.
>
> Patch 1 removes __packed from the LUO and memfd ABI structures. Patch 2
> adds the header and converts struct luo_ser. Patch 3 converts memfd and
> bumps its compatibility string to memfd-v3. Patch 4 adds KUnit tests.
>
> Tarun Sahu (4):
>   liveupdate: Remove __packed from LUO and memfd ABI structures
>   liveupdate: Add feature header for fine-grained ABI compatibility
>   mm: memfd_luo: Add the feature header
>   liveupdate: Add KUnit test for the feature header helpers
>
>  MAINTAINERS                          |   1 +
>  include/linux/kho/abi/luo.h          | 359 ++++++++++++++++++++++++---
>  include/linux/kho/abi/memfd.h        |  43 +++-
>  kernel/liveupdate/luo_core.c         |  62 +++--
>  lib/Kconfig.debug                    |  16 ++
>  lib/tests/Makefile                   |   1 +
>  lib/tests/liveupdate_feature_kunit.c | 118 +++++++++
>  mm/memfd_luo.c                       |  68 +++--
>  8 files changed, 577 insertions(+), 91 deletions(-)
>  create mode 100644 lib/tests/liveupdate_feature_kunit.c
>
> -- 
> 2.56.0.307.g5fe7bd680c-goog

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-26 18:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 17:42 [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
2026-10-26 17:42 ` [RFC PATCH v3 1/4] liveupdate: Remove __packed from LUO and memfd ABI structures Tarun Sahu
2026-10-26 17:42 ` [RFC PATCH v3 2/4] liveupdate: Add feature header for fine-grained ABI compatibility Tarun Sahu
2026-10-26 17:42 ` [RFC PATCH v3 3/4] mm: memfd_luo: Add the feature header Tarun Sahu
2026-10-26 17:42 ` [RFC PATCH v3 4/4] liveupdate: Add KUnit test for the feature header helpers Tarun Sahu
2026-10-26 18:58 ` [RFC PATCH v3 0/4] liveupdate: Add feature header for fine-grained ABI compatibility tarunsahu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®