Re: [PATCH v3] liveupdate: add LUO_SESSION_MAGIC magic inode type

Pasha Tatashin
2026-04-17T20:53:35Z

On 04-17 21:40, luca.boccassi@gmail.com wrote:
> From: Luca Boccassi <luca.boccassi@gmail.com>
>
> In userspace when managing LUO sessions we want to be able to identify
> a FD as a LUO session, in order to be able to do the special handling
> that they require in order to function as intended on kexec.
>
> Currently this requires scraping procfs and doing string matching on
> the prefix of the dname, which is not an ideal interface.
>
> Add a singleton inode type with a magic value, so that we can
> programmatically identify a fd as a LUO session via fstat().
>
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
> ---
> This was requested by Lennart and Christian when discussing integration
> with systemd for LUO management.
>
> v2: apply one fix from bot review about cleanup on error path:
> https://sashiko.dev/#/patchset/20260415184536.1155220-1-luca.boccassi%40gmail.com
> the other comments are invalid: luo is not a kmod, and the write hooks are not set up
> v3: add test case to liveupdate selftest

This should be split into two patches, also please CC:
linux-kernel@vger.kernel.org

>
> include/uapi/linux/magic.h | 1 +
> kernel/liveupdate/luo_core.c | 10 ++-
> kernel/liveupdate/luo_internal.h | 2 +
> kernel/liveupdate/luo_session.c | 73 +++++++++++++++++--
> .../testing/selftests/liveupdate/liveupdate.c | 40 ++++++++++
> 5 files changed, 120 insertions(+), 6 deletions(-)
>
> diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> index 4f2da935a76cc..4f51005522ffe 100644
> --- a/include/uapi/linux/magic.h
> +++ b/include/uapi/linux/magic.h
> @@ -105,5 +105,6 @@
> #define PID_FS_MAGIC 0x50494446 /* "PIDF" */
> #define GUEST_MEMFD_MAGIC 0x474d454d /* "GMEM" */
> #define NULL_FS_MAGIC 0x4E554C4C /* "NULL" */
> +#define LUO_SESSION_MAGIC 0x4c554f53 /* "LUOS" */
>
> #endif /* __LINUX_MAGIC_H__ */
> diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
> index dda7bb57d421c..f1a63ebe4fa44 100644
> --- a/kernel/liveupdate/luo_core.c
> +++ b/kernel/liveupdate/luo_core.c
> @@ -197,9 +197,17 @@ static int __init luo_late_startup(void)
> if (!liveupdate_enabled())
> return 0;
>
> + err = luo_session_fs_init();
> + if (err) {
> + luo_global.enabled = false;
> + return err;
> + }
> +
> err = luo_fdt_setup();
> - if (err)
> + if (err) {
> + luo_session_fs_cleanup();
> luo_global.enabled = false;
> + }
>
> return err;
> }
> diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> index 8083d8739b093..d4ac7b4c58829 100644
> --- a/kernel/liveupdate/luo_internal.h
> +++ b/kernel/liveupdate/luo_internal.h
> @@ -79,6 +79,8 @@ struct luo_session {
>
> int luo_session_create(const char *name, struct file **filep);
> int luo_session_retrieve(const char *name, struct file **filep);
> +int __init luo_session_fs_init(void);
> +void __init luo_session_fs_cleanup(void);
> int __init luo_session_setup_outgoing(void *fdt);
> int __init luo_session_setup_incoming(void *fdt);
> int luo_session_serialize(void);
> diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
> index af9e498206d24..f30151c37bd43 100644
> --- a/kernel/liveupdate/luo_session.c
> +++ b/kernel/liveupdate/luo_session.c
> @@ -50,7 +50,6 @@
>
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> -#include <linux/anon_inodes.h>
> #include <linux/cleanup.h>
> #include <linux/err.h>
> #include <linux/errno.h>
> @@ -62,7 +61,10 @@
> #include <linux/libfdt.h>
> #include <linux/list.h>
> #include <linux/liveupdate.h>
> +#include <linux/magic.h>
> +#include <linux/mount.h>
> #include <linux/mutex.h>
> +#include <linux/pseudo_fs.h>
> #include <linux/rwsem.h>
> #include <linux/slab.h>
> #include <linux/unaligned.h>
> @@ -376,18 +378,58 @@ static const struct file_operations luo_session_fops = {
> .unlocked_ioctl = luo_session_ioctl,
> };
>
> +static struct vfsmount *luo_session_mnt __ro_after_init;
> +static struct inode *luo_session_inode __ro_after_init;
> +
> +static char *luo_session_dname(struct dentry *dentry, char *buffer, int buflen)
> +{
> + return dynamic_dname(buffer, buflen, "luo_session:%s",
> + dentry->d_name.name);
> +}
> +
> +static const struct dentry_operations luo_session_dentry_operations = {
> + .d_dname = luo_session_dname,
> +};
> +
> +static int luo_session_init_fs_context(struct fs_context *fc)
> +{
> + struct pseudo_fs_context *ctx;
> +
> + ctx = init_pseudo(fc, LUO_SESSION_MAGIC);
> + if (!ctx)
> + return -ENOMEM;
> +
> + fc->s_iflags |= SB_I_NOEXEC;
> + fc->s_iflags |= SB_I_NODEV;
> + ctx->dops = &luo_session_dentry_operations;
> + return 0;
> +}
> +
> +static struct file_system_type luo_session_fs_type = {
> + .name = "luo_session",
> + .init_fs_context = luo_session_init_fs_context,
> + .kill_sb = kill_anon_super,
> +};
> +
> /* Create a "struct file" for session */
> static int luo_session_getfile(struct luo_session *session, struct file **filep)
> {
> - char name_buf[128];
> + char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH + 1];
> struct file *file;
>
> lockdep_assert_held(&session->mutex);
> - snprintf(name_buf, sizeof(name_buf), "[luo_session] %s", session->name);
> - file = anon_inode_getfile(name_buf, &luo_session_fops, session, O_RDWR);
> - if (IS_ERR(file))
> +
> + ihold(luo_session_inode);
> +
> + snprintf(name_buf, sizeof(name_buf), "%s", session->name);
> + file = alloc_file_pseudo(luo_session_inode, luo_session_mnt, name_buf,
> + O_RDWR, &luo_session_fops);
> + if (IS_ERR(file)) {
> + iput(luo_session_inode);
> return PTR_ERR(file);
> + }
>
> + file->private_data = session;
> *filep = file;
>
> return 0;
> @@ -662,3 +704,24 @@ void luo_session_resume(void)
> up_write(&luo_session_global.outgoing.rwsem);
> up_write(&luo_session_global.incoming.rwsem);
> }
> +
> +int __init luo_session_fs_init(void)
> +{
> + luo_session_mnt = kern_mount(&luo_session_fs_type);
> + if (IS_ERR(luo_session_mnt))
> + return PTR_ERR(luo_session_mnt);
> +
> + luo_session_inode = alloc_anon_inode(luo_session_mnt->mnt_sb);
> + if (IS_ERR(luo_session_inode)) {
> + kern_unmount(luo_session_mnt);
> + return PTR_ERR(luo_session_inode);
> + }
> +
> + return 0;
> +}
> +
> +void __init luo_session_fs_cleanup(void)
> +{
> + iput(luo_session_inode);
> + kern_unmount(luo_session_mnt);
> +}
> diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
> index bdc1cedc4f944..27b3cbff6c739 100644
> --- a/tools/testing/selftests/liveupdate/liveupdate.c
> +++ b/tools/testing/selftests/liveupdate/liveupdate.c
> @@ -22,9 +22,12 @@
> #include <fcntl.h>
> #include <string.h>
> #include <sys/ioctl.h>
> +#include <sys/stat.h>
> +#include <sys/vfs.h>
> #include <unistd.h>
>
> #include <linux/liveupdate.h>
> +#include <linux/magic.h>
>
> #include "../kselftest.h"
> #include "../kselftest_harness.h"
> @@ -416,4 +419,41 @@ TEST_F(liveupdate_device, get_session_name_max_length)
> ASSERT_EQ(close(session_fd), 0);
> }
>
> +/*
> + * Test Case: Session fstat
> + *
> + * Verifies that fstatfs() on a session file descriptor reports the
> + * LUO_SESSION_MAGIC filesystem type, and that fstat() returns consistent
> + * inode numbers across different sessions (shared singleton inode).
> + */
> +TEST_F(liveupdate_device, session_fstat)
> +{
> + int session_fd1, session_fd2;
> + struct statfs sfs;
> + struct stat st1, st2;

Reverse-christmast-tree order please.

> +
> + self->fd1 = open(LIVEUPDATE_DEV, O_RDWR);
> + if (self->fd1 < 0 && errno == ENOENT)
> + SKIP(return, "%s does not exist", LIVEUPDATE_DEV);
> + ASSERT_GE(self->fd1, 0);
> +
> + session_fd1 = create_session(self->fd1, "fstat-session-1");
> + ASSERT_GE(session_fd1, 0);
> +
> + session_fd2 = create_session(self->fd1, "fstat-session-2");
> + ASSERT_GE(session_fd2, 0);
> +
> + /* Verify the filesystem type is LUO_SESSION_MAGIC */
> + ASSERT_EQ(fstatfs(session_fd1, &sfs), 0);
> + EXPECT_EQ(sfs.f_type, LUO_SESSION_MAGIC);
> +
> + /* Verify both sessions share the same inode number */
> + ASSERT_EQ(fstat(session_fd1, &st1), 0);
> + ASSERT_EQ(fstat(session_fd2, &st2), 0);
> + EXPECT_EQ(st1.st_ino, st2.st_ino);
> +
> + ASSERT_EQ(close(session_fd1), 0);
> + ASSERT_EQ(close(session_fd2), 0);
> +}
> +
> TEST_HARNESS_MAIN
> --
> 2.47.3
>