[RFC PATCH 3/3] luo: memfd: Move to feature flags instead of compatibility strings
From: Logan Odell <hidden>
Date: 2026-09-03 02:35:04
Also in:
kexec, kvm, kvmarm, linux-arch, linux-iommu, linux-mm, linux-pci, lkml
Subsystem:
kexec handover (kho), library code, live update, memory management, the rest · Maintainers:
Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Andrew Morton, Linus Torvalds
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 <redacted> --- 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