[RFC PATCH v2 4/4] mm: memfd_luo: Add the 2-bit pair feature header

From: Tarun Sahu

Date: Wed Oct 21 2026 - 11:34:30 EST


Embed the fixed-size 128-byte struct luo_feature_hdr at the start of
struct memfd_luo_ser and define enum memfd_luo_feature
(MEMFD_LUO_FEAT_CORE and MEMFD_LUO_FEAT_MAX) for memfd preservation.

All current struct memfd_luo_ser fields (pos, size, seals, flags,
nr_folios, folios) and MEMFD_LUO_ALL_SEALS belong to
MEMFD_LUO_FEAT_CORE (LUO_FEAT_REQUIRED in memfd_luo_features). Future
changes add new entries to enum memfd_luo_feature before
MEMFD_LUO_FEAT_MAX and append any new fields to the end of struct
memfd_luo_ser so existing field offsets never shift.

Signed-off-by: Logan Odell <loganodell@xxxxxxxxxx>
Signed-off-by: Tarun Sahu <tarunsahu@xxxxxxxxxx>
---
include/linux/kho/abi/memfd.h | 35 ++++++++++++++++++++++++++++++-----
mm/memfd_luo.c | 14 ++++++++++++++
2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/include/linux/kho/abi/memfd.h b/include/linux/kho/abi/memfd.h
index 371ffd108a79..f0b5235a408d 100644
--- a/include/linux/kho/abi/memfd.h
+++ b/include/linux/kho/abi/memfd.h
@@ -13,6 +13,7 @@

#include <linux/types.h>
#include <linux/kho/abi/kexec_handover.h>
+#include <linux/kho/abi/luo.h>

/**
* DOC: memfd Live Update ABI
@@ -21,11 +22,20 @@
* reboot using the LUO.
*
* The state is serialized into a packed structure `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
- * constitutes a breaking change. Such changes require incrementing the
- * version number in the MEMFD_LUO_FH_COMPATIBLE string.
+ * This interface is a contract. `struct memfd_luo_ser` starts with a fixed
+ * 128-byte `struct luo_feature_hdr`. 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 in `enum memfd_luo_feature` (before `MEMFD_LUO_FEAT_MAX`), and
+ * retired fields are kept in place so subsequent offsets never shift. In
+ * the target kernel, fields of a `LUO_FEAT_REQUIRED` feature are guaranteed
+ * present once `luo_feature_hdr_validate()` succeeds, whereas fields of a
+ * `LUO_FEAT_OPTIONAL` feature should be accessed only after checking
+ * `luo_feature_is_active()`. All current fields (`pos`, `size`, `seals`,
+ * `flags`, `nr_folios`, `folios`) and `MEMFD_LUO_ALL_SEALS` belong to
+ * `MEMFD_LUO_FEAT_CORE` (`LUO_FEAT_REQUIRED` in `memfd_luo_features`).
*/

/**
@@ -58,7 +68,7 @@ struct memfd_luo_folio_ser {

/*
* The set of seals this version supports preserving. If support for any new
- * seals is needed, add it here and bump version.
+ * seals is needed, gate them behind a new feature flag.
*/
#define MEMFD_LUO_ALL_SEALS (F_SEAL_SEAL | \
F_SEAL_SHRINK | \
@@ -67,8 +77,22 @@ struct memfd_luo_folio_ser {
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,
+};
+
+static_assert((int)MEMFD_LUO_FEAT_MAX <= LUO_FEAT_MAX);
+
/**
* struct memfd_luo_ser - Main serialization structure for a memfd.
+ * @features: ABI feature and size header for memfd_luo_ser.
* @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 +103,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;
diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c
index 64f8b8cb75bd..ea1f4c8cd259 100644
--- a/mm/memfd_luo.c
+++ b/mm/memfd_luo.c
@@ -84,6 +84,10 @@

#include "internal.h"

+static const u8 memfd_luo_features[MEMFD_LUO_FEAT_MAX] = {
+ [MEMFD_LUO_FEAT_CORE] = LUO_FEAT_REQUIRED,
+};
+
static int memfd_luo_preserve_folios(struct file *file,
struct kho_vmalloc *kho_vmalloc,
struct memfd_luo_folio_ser **out_folios_ser,
@@ -284,6 +288,8 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args)
goto err_free_ser;
}

+ luo_feature_hdr_init(&ser->features, sizeof(*ser),
+ memfd_luo_features);
ser->pos = args->file->f_pos;
inode_size = i_size_read(inode);

@@ -525,6 +531,14 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args)
if (!ser)
return -EINVAL;

+ err = luo_feature_hdr_validate(&ser->features, memfd_luo_features);
+ if (err) {
+ pr_err("memfd_luo_ser feature header validation failed (size=%llu, flags[0]=%#llx): %pe\n",
+ ser->features.size, ser->features.flags[0],
+ ERR_PTR(err));
+ goto free_ser;
+ }
+
/* Make sure the file only has seals supported by this version. */
if (ser->seals & ~MEMFD_LUO_ALL_SEALS) {
err = -EOPNOTSUPP;
--
2.56.0.212.g129ede20d9-goog