linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes
@ 2026-04-18 16:28 luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length luca.boccassi
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

A series of patches matured while implementing LUO support in userspace
in systemd.

- reject session names that are either empty strings or too long
- change inode type of session FDs to a new singleton magic number,
  so that userspace can immediately identify them without string
  parsing in procfs
- add new ioctl to retrieve session name from FD, so that userspace
  can query it without string parsing in procfs

All changes come with a follow-up patch add test coverage via the
existing selftest.

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
v4: split test case in separate follow-up patch
v5: add r-b tag, merge series with LIVEUPDATE_SESSION_GET_NAME ioctl
    as they both change the same unit test source file, to avoid merge conflicts
    add '__u32 reserved' to the UAPI struct
v6: add more test cases as suggested
    more verbose commit message
    fix docstring
v7: apply suggestion from review bot to stub out setattr ops,
    like it was done in 22bdf3d6581a
v8: add new patches to reject session names that are empty or too long

Luca Boccassi (6):
  liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name
    length
  selftests/liveupdate: add test cases for
    LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length
  liveupdate: add LUO_SESSION_MAGIC magic inode type
  selftests/liveupdate: add test case for LUO_SESSION_MAGIC
  liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl
  selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME

 include/uapi/linux/liveupdate.h               |  21 +++
 include/uapi/linux/magic.h                    |   1 +
 kernel/liveupdate/luo_core.c                  |  10 +-
 kernel/liveupdate/luo_internal.h              |   2 +
 kernel/liveupdate/luo_session.c               | 106 +++++++++++-
 .../testing/selftests/liveupdate/liveupdate.c | 153 ++++++++++++++++++
 6 files changed, 287 insertions(+), 6 deletions(-)

-- 
2.47.3



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

* [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-19 15:06   ` Pasha Tatashin
  2026-04-18 16:28 ` [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length luca.boccassi
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

A session name must not be an empty string, and must not exceed the
maximum size define in the uapi header, including null termination.

Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support")

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
---
 kernel/liveupdate/luo_session.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 25ae704d7787..5e316a4c5d71 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -382,9 +382,13 @@ static int luo_session_getfile(struct luo_session *session, struct file **filep)
 
 int luo_session_create(const char *name, struct file **filep)
 {
+	size_t len = strnlen(name, LIVEUPDATE_SESSION_NAME_LENGTH);
 	struct luo_session *session;
 	int err;
 
+	if (len == 0 || len > LIVEUPDATE_SESSION_NAME_LENGTH - 1)
+		return -EINVAL;
+
 	session = luo_session_alloc(name);
 	if (IS_ERR(session))
 		return PTR_ERR(session);
-- 
2.47.3



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

* [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-19 15:11   ` Pasha Tatashin
  2026-04-18 16:28 ` [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type luca.boccassi
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

Verify that LIVEUPDATE_IOCTL_CREATE_SESSION ioctl which provide a name
that is an empty string or too long are not allowed.

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
---
 .../testing/selftests/liveupdate/liveupdate.c | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index c2878e3d5ef9..f0a8e600c154 100644
--- a/tools/testing/selftests/liveupdate/liveupdate.c
+++ b/tools/testing/selftests/liveupdate/liveupdate.c
@@ -345,4 +345,46 @@ TEST_F(liveupdate_device, preserve_unsupported_fd)
 	ASSERT_EQ(close(session_fd), 0);
 }
 
+/*
+ * Test Case: Create Session with No Null Termination
+ *
+ * Verifies that filling the entire 64-byte name field with non-null characters
+ * (no '\0' terminator) is rejected by the kernel with EINVAL.
+ */
+TEST_F(liveupdate_device, create_session_no_null_termination)
+{
+	struct liveupdate_ioctl_create_session args = {};
+
+	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);
+
+	/* Fill entire name field with 'X', no null terminator */
+	args.size = sizeof(args);
+	memset(args.name, 'X', sizeof(args.name));
+
+	EXPECT_LT(ioctl(self->fd1, LIVEUPDATE_IOCTL_CREATE_SESSION, &args), 0);
+	EXPECT_EQ(errno, EINVAL);
+}
+
+/*
+ * Test Case: Create Session with Empty Name
+ *
+ * Verifies that creating a session with an empty string name fails
+ * with EINVAL.
+ */
+TEST_F(liveupdate_device, create_session_empty_name)
+{
+	int session_fd;
+
+	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_fd = create_session(self->fd1, "");
+	EXPECT_EQ(session_fd, -EINVAL);
+}
+
 TEST_HARNESS_MAIN
-- 
2.47.3



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

* [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-20 12:26   ` Christian Brauner
  2026-04-18 16:28 ` [PATCH v8 4/6] selftests/liveupdate: add test case for LUO_SESSION_MAGIC luca.boccassi
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

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 fstatfs().

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 include/uapi/linux/magic.h       |  1 +
 kernel/liveupdate/luo_core.c     | 10 +++-
 kernel/liveupdate/luo_internal.h |  2 +
 kernel/liveupdate/luo_session.c  | 89 ++++++++++++++++++++++++++++++--
 4 files changed, 96 insertions(+), 6 deletions(-)

diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 4f2da935a76c..4f51005522ff 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 dda7bb57d421..f1a63ebe4fa4 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 8083d8739b09..d4ac7b4c5882 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 5e316a4c5d71..21cbe99fc819 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>
@@ -363,18 +365,73 @@ 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;
+
+/*
+ * Reject all attribute changes on the singleton session inode.
+ * Without this the VFS falls back to simple_setattr(), allowing
+ * fchmod()/fchown() to modify the shared inode.
+ */
+static int luo_session_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+			       struct iattr *attr)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct inode_operations luo_session_inode_operations = {
+	.setattr	= luo_session_setattr,
+};
+
+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;
@@ -653,3 +710,25 @@ 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);
+	}
+	luo_session_inode->i_op = &luo_session_inode_operations;
+
+	return 0;
+}
+
+void __init luo_session_fs_cleanup(void)
+{
+	iput(luo_session_inode);
+	kern_unmount(luo_session_mnt);
+}
-- 
2.47.3



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

* [PATCH v8 4/6] selftests/liveupdate: add test case for LUO_SESSION_MAGIC
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
                   ` (2 preceding siblings ...)
  2026-04-18 16:28 ` [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 5/6] liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME luca.boccassi
  5 siblings, 0 replies; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

Verify that fstat works as expected after the switch from anon_inode
to the new magic number.

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 .../testing/selftests/liveupdate/liveupdate.c | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index f0a8e600c154..5e99af0cc6e9 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"
@@ -387,4 +390,41 @@ TEST_F(liveupdate_device, create_session_empty_name)
 	EXPECT_EQ(session_fd, -EINVAL);
 }
 
+/*
+ * 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 stat st1, st2;
+	struct statfs sfs;
+
+	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



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

* [PATCH v8 5/6] liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
                   ` (3 preceding siblings ...)
  2026-04-18 16:28 ` [PATCH v8 4/6] selftests/liveupdate: add test case for LUO_SESSION_MAGIC luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-18 16:28 ` [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME luca.boccassi
  5 siblings, 0 replies; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

Userspace when requesting a session via the ioctl specifies a name and
gets a FD, but then there is no ioctl to go back the other way and get
the name given a LUO session FD. This is problematic especially when
there is a userspace orchestrator that wants to check what FDs it is
handling for clients without having to do manual string scraping of
procfs, or without procfs at all.

Add a ioctl to simply get the name from an FD.

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 include/uapi/linux/liveupdate.h | 21 +++++++++++++++++++++
 kernel/liveupdate/luo_session.c | 13 +++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
index 30bc66ee9436..3a9ff53b02e0 100644
--- a/include/uapi/linux/liveupdate.h
+++ b/include/uapi/linux/liveupdate.h
@@ -59,6 +59,7 @@ enum {
 	LIVEUPDATE_CMD_SESSION_PRESERVE_FD = LIVEUPDATE_CMD_SESSION_BASE,
 	LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41,
 	LIVEUPDATE_CMD_SESSION_FINISH = 0x42,
+	LIVEUPDATE_CMD_SESSION_GET_NAME = 0x43,
 };
 
 /**
@@ -213,4 +214,24 @@ struct liveupdate_session_finish {
 #define LIVEUPDATE_SESSION_FINISH					\
 	_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FINISH)
 
+/**
+ * struct liveupdate_session_get_name - ioctl(LIVEUPDATE_SESSION_GET_NAME)
+ * @size:  Input; sizeof(struct liveupdate_session_get_name)
+ * @reserved: Input; Must be zero. Reserved for future use.
+ * @name:  Output; A null-terminated string with the full session name.
+ *
+ * Retrieves the full name of the session associated with this file descriptor.
+ * This is useful because the kernel may truncate the name shown in /proc.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+struct liveupdate_session_get_name {
+	__u32		size;
+	__u32		reserved;
+	__u8		name[LIVEUPDATE_SESSION_NAME_LENGTH];
+};
+
+#define LIVEUPDATE_SESSION_GET_NAME					\
+	_IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_GET_NAME)
+
 #endif /* _UAPI_LIVEUPDATE_H */
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 21cbe99fc819..ca5e44274dcb 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -291,10 +291,21 @@ static int luo_session_finish(struct luo_session *session,
 	return luo_ucmd_respond(ucmd, sizeof(*argp));
 }
 
+static int luo_session_get_name(struct luo_session *session,
+				struct luo_ucmd *ucmd)
+{
+	struct liveupdate_session_get_name *argp = ucmd->cmd;
+
+	strscpy((char *)argp->name, session->name, sizeof(argp->name));
+
+	return luo_ucmd_respond(ucmd, sizeof(*argp));
+}
+
 union ucmd_buffer {
 	struct liveupdate_session_finish finish;
 	struct liveupdate_session_preserve_fd preserve;
 	struct liveupdate_session_retrieve_fd retrieve;
+	struct liveupdate_session_get_name get_name;
 };
 
 struct luo_ioctl_op {
@@ -321,6 +332,8 @@ static const struct luo_ioctl_op luo_session_ioctl_ops[] = {
 		 struct liveupdate_session_preserve_fd, token),
 	IOCTL_OP(LIVEUPDATE_SESSION_RETRIEVE_FD, luo_session_retrieve_fd,
 		 struct liveupdate_session_retrieve_fd, token),
+	IOCTL_OP(LIVEUPDATE_SESSION_GET_NAME, luo_session_get_name,
+		 struct liveupdate_session_get_name, name),
 };
 
 static long luo_session_ioctl(struct file *filep, unsigned int cmd,
-- 
2.47.3



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

* [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME
  2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
                   ` (4 preceding siblings ...)
  2026-04-18 16:28 ` [PATCH v8 5/6] liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl luca.boccassi
@ 2026-04-18 16:28 ` luca.boccassi
  2026-04-19 15:12   ` Pasha Tatashin
  5 siblings, 1 reply; 19+ messages in thread
From: luca.boccassi @ 2026-04-18 16:28 UTC (permalink / raw)
  To: kexec
  Cc: linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel, Luca Boccassi

From: Luca Boccassi <luca.boccassi@gmail.com>

Verify that the new LIVEUPDATE_SESSION_GET_NAME ioctl works
as expected via new test cases in the existing liveupdate selftest.

Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
---
 .../testing/selftests/liveupdate/liveupdate.c | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
index 5e99af0cc6e9..c21354dc9b93 100644
--- a/tools/testing/selftests/liveupdate/liveupdate.c
+++ b/tools/testing/selftests/liveupdate/liveupdate.c
@@ -105,6 +105,22 @@ static int create_session(int lu_fd, const char *name)
 	return args.fd;
 }
 
+/* Helper function to get a session name via ioctl. */
+static int get_session_name(int session_fd, char *name, size_t name_len)
+{
+	struct liveupdate_session_get_name args = {};
+
+	args.size = sizeof(args);
+
+	if (ioctl(session_fd, LIVEUPDATE_SESSION_GET_NAME, &args))
+		return -errno;
+
+	strncpy(name, (char *)args.name, name_len - 1);
+	name[name_len - 1] = '\0';
+
+	return 0;
+}
+
 /*
  * Test Case: Create Duplicate Session
  *
@@ -427,4 +443,59 @@ TEST_F(liveupdate_device, session_fstat)
 	ASSERT_EQ(close(session_fd2), 0);
 }
 
+/*
+ * Test Case: Get Session Name
+ *
+ * Verifies that the full session name can be retrieved from a session file
+ * descriptor via ioctl.
+ */
+TEST_F(liveupdate_device, get_session_name)
+{
+	char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {};
+	const char *session_name = "get-name-test-session";
+	int session_fd;
+
+	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_fd = create_session(self->fd1, session_name);
+	ASSERT_GE(session_fd, 0);
+
+	ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0);
+	ASSERT_STREQ(name_buf, session_name);
+
+	ASSERT_EQ(close(session_fd), 0);
+}
+
+/*
+ * Test Case: Get Session Name at Maximum Length
+ *
+ * Verifies that a session name using the full LIVEUPDATE_SESSION_NAME_LENGTH
+ * (minus the null terminator) can be correctly retrieved.
+ */
+TEST_F(liveupdate_device, get_session_name_max_length)
+{
+	char name_buf[LIVEUPDATE_SESSION_NAME_LENGTH] = {};
+	char long_name[LIVEUPDATE_SESSION_NAME_LENGTH];
+	int session_fd;
+
+	memset(long_name, 'A', sizeof(long_name) - 1);
+	long_name[sizeof(long_name) - 1] = '\0';
+
+	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_fd = create_session(self->fd1, long_name);
+	ASSERT_GE(session_fd, 0);
+
+	ASSERT_EQ(get_session_name(session_fd, name_buf, sizeof(name_buf)), 0);
+	ASSERT_STREQ(name_buf, long_name);
+
+	ASSERT_EQ(close(session_fd), 0);
+}
+
 TEST_HARNESS_MAIN
-- 
2.47.3



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

* Re: [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length
  2026-04-18 16:28 ` [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length luca.boccassi
@ 2026-04-19 15:06   ` Pasha Tatashin
  0 siblings, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-19 15:06 UTC (permalink / raw)
  To: luca.boccassi
  Cc: kexec, linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel

On 04-18 17:28, luca.boccassi@gmail.com wrote:
> From: Luca Boccassi <luca.boccassi@gmail.com>
> 
> A session name must not be an empty string, and must not exceed the
> maximum size define in the uapi header, including null termination.
> 
> Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support")
> 
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>

Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com> 


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

* Re: [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length
  2026-04-18 16:28 ` [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length luca.boccassi
@ 2026-04-19 15:11   ` Pasha Tatashin
  0 siblings, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-19 15:11 UTC (permalink / raw)
  To: luca.boccassi
  Cc: kexec, linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel

On 04-18 17:28, luca.boccassi@gmail.com wrote:
> From: Luca Boccassi <luca.boccassi@gmail.com>
> 
> Verify that LIVEUPDATE_IOCTL_CREATE_SESSION ioctl which provide a name
> that is an empty string or too long are not allowed.
> 
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>

Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com> 


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

* Re: [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME
  2026-04-18 16:28 ` [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME luca.boccassi
@ 2026-04-19 15:12   ` Pasha Tatashin
  0 siblings, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-19 15:12 UTC (permalink / raw)
  To: luca.boccassi
  Cc: kexec, linux-mm, graf, rppt, pasha.tatashin, pratyush, brauner,
	linux-kernel

On 04-18 17:28, luca.boccassi@gmail.com wrote:
> From: Luca Boccassi <luca.boccassi@gmail.com>
> 
> Verify that the new LIVEUPDATE_SESSION_GET_NAME ioctl works
> as expected via new test cases in the existing liveupdate selftest.
> 
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>

Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com> 


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-18 16:28 ` [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type luca.boccassi
@ 2026-04-20 12:26   ` Christian Brauner
  2026-04-20 14:22     ` Luca Boccassi
  2026-04-20 14:55     ` Pasha Tatashin
  0 siblings, 2 replies; 19+ messages in thread
From: Christian Brauner @ 2026-04-20 12:26 UTC (permalink / raw)
  To: luca.boccassi
  Cc: kexec, linux-mm, graf, rppt, pasha.tatashin, pratyush, linux-kernel

On Sat, Apr 18, 2026 at 05:28:20PM +0100, 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 fstatfs().
> 
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
> Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> ---
>  include/uapi/linux/magic.h       |  1 +
>  kernel/liveupdate/luo_core.c     | 10 +++-
>  kernel/liveupdate/luo_internal.h |  2 +
>  kernel/liveupdate/luo_session.c  | 89 ++++++++++++++++++++++++++++++--
>  4 files changed, 96 insertions(+), 6 deletions(-)
> 
> diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> index 4f2da935a76c..4f51005522ff 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 dda7bb57d421..f1a63ebe4fa4 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 8083d8739b09..d4ac7b4c5882 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 5e316a4c5d71..21cbe99fc819 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>
> @@ -363,18 +365,73 @@ 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;
> +
> +/*
> + * Reject all attribute changes on the singleton session inode.
> + * Without this the VFS falls back to simple_setattr(), allowing
> + * fchmod()/fchown() to modify the shared inode.
> + */
> +static int luo_session_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> +			       struct iattr *attr)

Don't duplicate, please. Use the generic helper instead:

int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
		       struct iattr *attr)

> +{
> +	return -EOPNOTSUPP;



> +}
> +
> +static const struct inode_operations luo_session_inode_operations = {
> +	.setattr	= luo_session_setattr,
> +};
> +
> +static char *luo_session_dname(struct dentry *dentry, char *buffer, int buflen)
> +{
> +	return dynamic_dname(buffer, buflen, "luo_session:%s",
> +			     dentry->d_name.name);

Use the luo_session:[%s] which is the canonical format for this
(ignoring historcal abberations).

> +}
> +
> +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);

I'd just call that LUO_FS_MAGIC.

> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	fc->s_iflags |= SB_I_NOEXEC;
> +	fc->s_iflags |= SB_I_NODEV;

        ctx->s_d_flags |= DCACHE_DONTCACHE;

static const struct super_operations luo_session_sops = {
        .drop_inode     = inode_just_drop,
        .statfs         = simple_statfs,
};


> +	ctx->dops = &luo_session_dentry_operations;

        ctx->ops = &luo_session_sops;

> +	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)

Luo is going full anti-pattern here. This whole return via a function
argument completely messes up the later codepths. We don't do manual
get_unused_fd_flags() flags and then file in new code, and then fail
in-between:

        argp->fd = get_unused_fd_flags(O_CLOEXEC);
        if (argp->fd < 0)
                return argp->fd;

        err = luo_session_create(argp->name, &file);
        if (err)
                goto err_put_fd;

        err = luo_ucmd_respond(ucmd, sizeof(*argp));
        if (err)
                goto err_put_file;

        fd_install(argp->fd, file);

Restructure the code so it just becomes:

struct file *luo_session_create(argp->name);

static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
{
        struct liveupdate_ioctl_create_session *argp = ucmd->cmd;

        return FD_ADD(O_CLOEXEC, luo_session_create(argp->name));
}

and get rid of all this state and error handling. Please fix this.

>  {
> -	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);

Right, you're now sharing the same inode among all luo sessions. So
you've gained the ability to recognize luo inodes via fstatfs() but you
still can't compare two luo session file descriptors for equality using
stat() which is a major win and if you're doing this work anyway, let's
just go the extra step. You can mostly mirror pidfs and it's not
difficult:

// unique inode numbers for the lifetime of the system instead of using
// the shared ino allocator that can overflow

DEFINE_COOKIE(luo_session_cookie);

static u64 luo_alloc_ino(void)
{
        u64 ino;

        preempt_disable();
        ino = gen_cookie_next(&luo_ino_cookie);
        preempt_enable();

        VFS_WARN_ON_ONCE(ino < 1);
        return ino;
}

static struct inode *luo_new_inode()
{
	inode = new_inode_pseudo(sb);

	inode->i_flags	|= S_IMMUTABLE;
	inode->i_mode	|= S_IRWXU;
	inode->i_flags	|= S_PRIVATE | S_ANON_INODE;
	simple_inode_init_ts(inode);

	inode->i_op	= &luo_session_inode_operations;
	// Make sure to leave inode->i_fop unset so it luo fds cannot be reopened via procfs

	// Unique 64-bit inode. On 32-bit this will obviously be
	// truncated but a) LUO doesn't support 32-bit b) let's declare
	// anyone that wants LUO on 32-bit insane c) who uses more than
	// 32-bit worth of luo sessions d) if that's really something
	// you want to address you can do so later doing the same thing
	// I did for pidfs (see there for context).
        inode->i_ino	= luo_alloc_ino();
        inode->i_generation = pidfs_gen(pid->ino);

	simple_inode_init_ts(inode);
	return inode;
}

Now you can do

stat(fd_luo1, &st1);
stat(fd_luo2, &st2);

if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
		return true; // same luo session

Sooner or later you will want this. Let's do it correctly, right now.

A bigger design question for the LUO people:

How do the lifetimes of the luo session and the luo fd relate to each
other? The way the code is structured it looks like the luo session
thing that you currently store in the file:

        file->private_data = session;

outlives the file? Why?

Imho, two things should change in addition to the switch to the tiny fs:

(1) The lifetime of the luo session object should be tied to the
    lifetime of the session file descriptor so that when the last file
    descriptor for that luo session is closed the @session thing gets
    cleaned up. I assume that's the case anyway?

(2) Instead of stashing the session in file->private_data store it in
    inode->private_data. This has various advantages:

    * You can stash other information in file->private_data.
    * You can at some point have multiple files referring to the same
      inode/luo session.

> +
> +	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;
> @@ -653,3 +710,25 @@ 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))

panic()

> +		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);
> +	}
> +	luo_session_inode->i_op = &luo_session_inode_operations;

Kill all of this now that you allocate inodes on-demand.

> +
> +	return 0;
> +}
> +
> +void __init luo_session_fs_cleanup(void)
> +{
> +	iput(luo_session_inode);
> +	kern_unmount(luo_session_mnt);
> +}
> -- 
> 2.47.3
> 


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 12:26   ` Christian Brauner
@ 2026-04-20 14:22     ` Luca Boccassi
  2026-04-20 14:57       ` Pasha Tatashin
  2026-04-20 15:05       ` Christian Brauner
  2026-04-20 14:55     ` Pasha Tatashin
  1 sibling, 2 replies; 19+ messages in thread
From: Luca Boccassi @ 2026-04-20 14:22 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-mm, kexec, graf, rppt, pasha.tatashin, pratyush, linux-kernel

On Mon, 20 Apr 2026 at 13:26, Christian Brauner <brauner@kernel.org> wrote:
>
> On Sat, Apr 18, 2026 at 05:28:20PM +0100, luca.boccassi@gmail.com wrote:
> > +}
> > +
> > +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);
>
> I'd just call that LUO_FS_MAGIC.

It's specific for the session fd, so I'd rather keep it as-is. Pasha
what do you think?

> > +     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)
>
> Luo is going full anti-pattern here. This whole return via a function
> argument completely messes up the later codepths. We don't do manual
> get_unused_fd_flags() flags and then file in new code, and then fail
> in-between:

This is getting a bit too complex for me and it's all existing stuff,
so I'll leave it for Pasha and the other maintainers to look at later,
they are CC'ed

> >  {
> > -     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);
>
> Right, you're now sharing the same inode among all luo sessions. So
> you've gained the ability to recognize luo inodes via fstatfs() but you
> still can't compare two luo session file descriptors for equality using
> stat() which is a major win and if you're doing this work anyway, let's
> just go the extra step.

I'm not aware of any use case for this, it wasn't even discussed, so
I'll leave this out for now, if the need comes up it can certainly be
revisited later. A singleton is nice as it saves some resources.


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 12:26   ` Christian Brauner
  2026-04-20 14:22     ` Luca Boccassi
@ 2026-04-20 14:55     ` Pasha Tatashin
  2026-04-20 14:59       ` Luca Boccassi
  2026-04-20 15:28       ` Christian Brauner
  1 sibling, 2 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-20 14:55 UTC (permalink / raw)
  To: Christian Brauner
  Cc: luca.boccassi, kexec, linux-mm, graf, rppt, pasha.tatashin,
	pratyush, linux-kernel

On 04-20 14:26, Christian Brauner wrote:
> On Sat, Apr 18, 2026 at 05:28:20PM +0100, 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 fstatfs().
> > 
> > Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
> > Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > ---
> >  include/uapi/linux/magic.h       |  1 +
> >  kernel/liveupdate/luo_core.c     | 10 +++-
> >  kernel/liveupdate/luo_internal.h |  2 +
> >  kernel/liveupdate/luo_session.c  | 89 ++++++++++++++++++++++++++++++--
> >  4 files changed, 96 insertions(+), 6 deletions(-)
> > 
> > diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> > index 4f2da935a76c..4f51005522ff 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 dda7bb57d421..f1a63ebe4fa4 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 8083d8739b09..d4ac7b4c5882 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 5e316a4c5d71..21cbe99fc819 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>
> > @@ -363,18 +365,73 @@ 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;
> > +
> > +/*
> > + * Reject all attribute changes on the singleton session inode.
> > + * Without this the VFS falls back to simple_setattr(), allowing
> > + * fchmod()/fchown() to modify the shared inode.
> > + */
> > +static int luo_session_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> > +			       struct iattr *attr)
> 
> Don't duplicate, please. Use the generic helper instead:
> 
> int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> 		       struct iattr *attr)
> 
> > +{
> > +	return -EOPNOTSUPP;
> 
> 
> 
> > +}
> > +
> > +static const struct inode_operations luo_session_inode_operations = {
> > +	.setattr	= luo_session_setattr,
> > +};
> > +
> > +static char *luo_session_dname(struct dentry *dentry, char *buffer, int buflen)
> > +{
> > +	return dynamic_dname(buffer, buflen, "luo_session:%s",
> > +			     dentry->d_name.name);
> 
> Use the luo_session:[%s] which is the canonical format for this
> (ignoring historcal abberations).
> 
> > +}
> > +
> > +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);
> 
> I'd just call that LUO_FS_MAGIC.
> 
> > +	if (!ctx)
> > +		return -ENOMEM;
> > +
> > +	fc->s_iflags |= SB_I_NOEXEC;
> > +	fc->s_iflags |= SB_I_NODEV;
> 
>         ctx->s_d_flags |= DCACHE_DONTCACHE;
> 
> static const struct super_operations luo_session_sops = {
>         .drop_inode     = inode_just_drop,
>         .statfs         = simple_statfs,
> };
> 
> 
> > +	ctx->dops = &luo_session_dentry_operations;
> 
>         ctx->ops = &luo_session_sops;
> 
> > +	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)
> 
> Luo is going full anti-pattern here. This whole return via a function
> argument completely messes up the later codepths. We don't do manual
> get_unused_fd_flags() flags and then file in new code, and then fail
> in-between:
> 
>         argp->fd = get_unused_fd_flags(O_CLOEXEC);
>         if (argp->fd < 0)
>                 return argp->fd;
> 
>         err = luo_session_create(argp->name, &file);
>         if (err)
>                 goto err_put_fd;
> 
>         err = luo_ucmd_respond(ucmd, sizeof(*argp));
>         if (err)
>                 goto err_put_file;
> 
>         fd_install(argp->fd, file);
> 
> Restructure the code so it just becomes:
> 
> struct file *luo_session_create(argp->name);
> 
> static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
> {
>         struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
> 
>         return FD_ADD(O_CLOEXEC, luo_session_create(argp->name));
> }
> 
> and get rid of all this state and error handling. Please fix this.

We cannot do it this way because we must use copy_to_user() to return fd 
via ioctl(), and since copy_to_user() may fail, we must do it prior to 
fd_install().

Unless there is a specific VFS macro you'd prefer for this 
delayed-install pattern, I do not see any other way to do this but 
maintain the get_unused_fd_flags() -> copy_to_user() -> fd_install() to 
prevent the fd being leaked into the process's table.

> 
> >  {
> > -	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);
> 
> Right, you're now sharing the same inode among all luo sessions. So
> you've gained the ability to recognize luo inodes via fstatfs() but you
> still can't compare two luo session file descriptors for equality using
> stat() which is a major win and if you're doing this work anyway, let's

Luca, is there a specific use case in userspace where we need to compare 
LUO sessions for equality?

Christian's proposed solution of using unique inodes provides a standard 
VFS interface, but it introduces some memory overhead and, more 
importantly, a performance overhead due to the extra metadata 
allocations required during the performance-critical kexec blackout 
window.

I do not mind adding it, but I want to verify if this equality check is 
actually needed for your userspace agents, once we extend this, there is 
no way back.

> just go the extra step. You can mostly mirror pidfs and it's not
> difficult:
> 
> // unique inode numbers for the lifetime of the system instead of using
> // the shared ino allocator that can overflow
> 
> DEFINE_COOKIE(luo_session_cookie);
> 
> static u64 luo_alloc_ino(void)
> {
>         u64 ino;
> 
>         preempt_disable();
>         ino = gen_cookie_next(&luo_ino_cookie);
>         preempt_enable();
> 
>         VFS_WARN_ON_ONCE(ino < 1);
>         return ino;
> }
> 
> static struct inode *luo_new_inode()
> {
> 	inode = new_inode_pseudo(sb);
> 
> 	inode->i_flags	|= S_IMMUTABLE;
> 	inode->i_mode	|= S_IRWXU;
> 	inode->i_flags	|= S_PRIVATE | S_ANON_INODE;
> 	simple_inode_init_ts(inode);
> 
> 	inode->i_op	= &luo_session_inode_operations;
> 	// Make sure to leave inode->i_fop unset so it luo fds cannot be reopened via procfs
> 
> 	// Unique 64-bit inode. On 32-bit this will obviously be
> 	// truncated but a) LUO doesn't support 32-bit b) let's declare
> 	// anyone that wants LUO on 32-bit insane c) who uses more than
> 	// 32-bit worth of luo sessions d) if that's really something
> 	// you want to address you can do so later doing the same thing
> 	// I did for pidfs (see there for context).
>         inode->i_ino	= luo_alloc_ino();
>         inode->i_generation = pidfs_gen(pid->ino);
> 
> 	simple_inode_init_ts(inode);
> 	return inode;
> }
> 
> Now you can do
> 
> stat(fd_luo1, &st1);
> stat(fd_luo2, &st2);
> 
> if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
> 		return true; // same luo session
> 
> Sooner or later you will want this. Let's do it correctly, right now.
> 
> A bigger design question for the LUO people:
> 
> How do the lifetimes of the luo session and the luo fd relate to each
> other? The way the code is structured it looks like the luo session
> thing that you currently store in the file:
> 
>         file->private_data = session;
> 
> outlives the file? Why?
> 
> Imho, two things should change in addition to the switch to the tiny fs:
> 
> (1) The lifetime of the luo session object should be tied to the
>     lifetime of the session file descriptor so that when the last file
>     descriptor for that luo session is closed the @session thing gets
>     cleaned up. I assume that's the case anyway?
> 
> (2) Instead of stashing the session in file->private_data store it in
>     inode->private_data. This has various advantages:
> 
>     * You can stash other information in file->private_data.
>     * You can at some point have multiple files referring to the same
>       inode/luo session.
> 
> > +
> > +	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;
> > @@ -653,3 +710,25 @@ 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))
> 
> panic()
> 
> > +		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);
> > +	}
> > +	luo_session_inode->i_op = &luo_session_inode_operations;
> 
> Kill all of this now that you allocate inodes on-demand.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +void __init luo_session_fs_cleanup(void)
> > +{
> > +	iput(luo_session_inode);
> > +	kern_unmount(luo_session_mnt);
> > +}
> > -- 
> > 2.47.3
> > 


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 14:22     ` Luca Boccassi
@ 2026-04-20 14:57       ` Pasha Tatashin
  2026-04-20 15:05       ` Christian Brauner
  1 sibling, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-20 14:57 UTC (permalink / raw)
  To: Luca Boccassi
  Cc: Christian Brauner, linux-mm, kexec, graf, rppt, pasha.tatashin,
	pratyush, linux-kernel

On 04-20 15:22, Luca Boccassi wrote:
> On Mon, 20 Apr 2026 at 13:26, Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Sat, Apr 18, 2026 at 05:28:20PM +0100, luca.boccassi@gmail.com wrote:
> > > +}
> > > +
> > > +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);
> >
> > I'd just call that LUO_FS_MAGIC.
> 
> It's specific for the session fd, so I'd rather keep it as-is. Pasha
> what do you think?

I am OK with what Christian is proposing.

Luca, please wait before sending the new versions, lets discussion 
settle.

Pasha


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 14:55     ` Pasha Tatashin
@ 2026-04-20 14:59       ` Luca Boccassi
  2026-04-20 15:28       ` Christian Brauner
  1 sibling, 0 replies; 19+ messages in thread
From: Luca Boccassi @ 2026-04-20 14:59 UTC (permalink / raw)
  To: Pasha Tatashin
  Cc: Christian Brauner, kexec, linux-mm, graf, rppt, pratyush, linux-kernel

On Mon, 20 Apr 2026 at 15:55, Pasha Tatashin <pasha.tatashin@soleen.com> wrote:
>
> On 04-20 14:26, Christian Brauner wrote:
> > On Sat, Apr 18, 2026 at 05:28:20PM +0100, luca.boccassi@gmail.com wrote:
> > > From: Luca Boccassi <luca.boccassi@gmail.com>
> > >  {
> > > -   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);
> >
> > Right, you're now sharing the same inode among all luo sessions. So
> > you've gained the ability to recognize luo inodes via fstatfs() but you
> > still can't compare two luo session file descriptors for equality using
> > stat() which is a major win and if you're doing this work anyway, let's
>
> Luca, is there a specific use case in userspace where we need to compare
> LUO sessions for equality?
>
> Christian's proposed solution of using unique inodes provides a standard
> VFS interface, but it introduces some memory overhead and, more
> importantly, a performance overhead due to the extra metadata
> allocations required during the performance-critical kexec blackout
> window.
>
> I do not mind adding it, but I want to verify if this equality check is
> actually needed for your userspace agents, once we extend this, there is
> no way back.

I don't have any use for this comparison as of now, and cannot think
of any future one either. So I agree with you and I think we should
postpone until there's an actual use case for it, to avoid the
downsides.


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 14:22     ` Luca Boccassi
  2026-04-20 14:57       ` Pasha Tatashin
@ 2026-04-20 15:05       ` Christian Brauner
  1 sibling, 0 replies; 19+ messages in thread
From: Christian Brauner @ 2026-04-20 15:05 UTC (permalink / raw)
  To: Luca Boccassi
  Cc: linux-mm, kexec, graf, rppt, pasha.tatashin, pratyush, linux-kernel

On Mon, Apr 20, 2026 at 03:22:03PM +0100, Luca Boccassi wrote:
> On Mon, 20 Apr 2026 at 13:26, Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Sat, Apr 18, 2026 at 05:28:20PM +0100, luca.boccassi@gmail.com wrote:
> > > +}
> > > +
> > > +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);
> >
> > I'd just call that LUO_FS_MAGIC.
> 
> It's specific for the session fd, so I'd rather keep it as-is. Pasha
> what do you think?
> 
> > > +     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)
> >
> > Luo is going full anti-pattern here. This whole return via a function
> > argument completely messes up the later codepths. We don't do manual
> > get_unused_fd_flags() flags and then file in new code, and then fail
> > in-between:
> 
> This is getting a bit too complex for me and it's all existing stuff,
> so I'll leave it for Pasha and the other maintainers to look at later,
> they are CC'ed
> 
> > >  {
> > > -     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);
> >
> > Right, you're now sharing the same inode among all luo sessions. So
> > you've gained the ability to recognize luo inodes via fstatfs() but you
> > still can't compare two luo session file descriptors for equality using
> > stat() which is a major win and if you're doing this work anyway, let's
> > just go the extra step.
> 
> I'm not aware of any use case for this, it wasn't even discussed, so
> I'll leave this out for now, if the need comes up it can certainly be
> revisited later. A singleton is nice as it saves some resources.

I've almost written the code for you in my first reply and it's also a
literal 1:1 templated copy from what I already did pidfs as well.

Let's do things right from the start and not taky nasty shortcuts to fix
one minor urge without having any foresight at all. If you want to have
an actual tiny filesystem to identify an inode type do it properly and
add the semantics to allocate separate inodes and get all the goodies at
once and make it possible to maintain this properly.

You will need this for so many other reasons than inode comparison such
as lsm integration which definitely want to end up managing this. None
of this works with a single inode. This is not difficult.

And this cop-out would never fly in systemd. I'm regularly sent through
15 review rounds to implement 5 different approaches to the same
problem. So are you. Please take the code I've given you and simply copy
and paste it over what you already did and solve this correctly once and
for all instead of hacking up a filesystem for a single inode so you can
define an inode type.


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 14:55     ` Pasha Tatashin
  2026-04-20 14:59       ` Luca Boccassi
@ 2026-04-20 15:28       ` Christian Brauner
  2026-04-20 15:57         ` Pasha Tatashin
  2026-04-20 16:39         ` Pasha Tatashin
  1 sibling, 2 replies; 19+ messages in thread
From: Christian Brauner @ 2026-04-20 15:28 UTC (permalink / raw)
  To: Pasha Tatashin
  Cc: luca.boccassi, kexec, linux-mm, graf, rppt, pratyush, linux-kernel

On Mon, Apr 20, 2026 at 02:55:56PM +0000, Pasha Tatashin wrote:
> On 04-20 14:26, Christian Brauner wrote:
> > On Sat, Apr 18, 2026 at 05:28:20PM +0100, 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 fstatfs().
> > > 
> > > Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
> > > Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > > ---
> > >  include/uapi/linux/magic.h       |  1 +
> > >  kernel/liveupdate/luo_core.c     | 10 +++-
> > >  kernel/liveupdate/luo_internal.h |  2 +
> > >  kernel/liveupdate/luo_session.c  | 89 ++++++++++++++++++++++++++++++--
> > >  4 files changed, 96 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> > > index 4f2da935a76c..4f51005522ff 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 dda7bb57d421..f1a63ebe4fa4 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 8083d8739b09..d4ac7b4c5882 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 5e316a4c5d71..21cbe99fc819 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>
> > > @@ -363,18 +365,73 @@ 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;
> > > +
> > > +/*
> > > + * Reject all attribute changes on the singleton session inode.
> > > + * Without this the VFS falls back to simple_setattr(), allowing
> > > + * fchmod()/fchown() to modify the shared inode.
> > > + */
> > > +static int luo_session_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> > > +			       struct iattr *attr)
> > 
> > Don't duplicate, please. Use the generic helper instead:
> > 
> > int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> > 		       struct iattr *attr)
> > 
> > > +{
> > > +	return -EOPNOTSUPP;
> > 
> > 
> > 
> > > +}
> > > +
> > > +static const struct inode_operations luo_session_inode_operations = {
> > > +	.setattr	= luo_session_setattr,
> > > +};
> > > +
> > > +static char *luo_session_dname(struct dentry *dentry, char *buffer, int buflen)
> > > +{
> > > +	return dynamic_dname(buffer, buflen, "luo_session:%s",
> > > +			     dentry->d_name.name);
> > 
> > Use the luo_session:[%s] which is the canonical format for this
> > (ignoring historcal abberations).
> > 
> > > +}
> > > +
> > > +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);
> > 
> > I'd just call that LUO_FS_MAGIC.
> > 
> > > +	if (!ctx)
> > > +		return -ENOMEM;
> > > +
> > > +	fc->s_iflags |= SB_I_NOEXEC;
> > > +	fc->s_iflags |= SB_I_NODEV;
> > 
> >         ctx->s_d_flags |= DCACHE_DONTCACHE;
> > 
> > static const struct super_operations luo_session_sops = {
> >         .drop_inode     = inode_just_drop,
> >         .statfs         = simple_statfs,
> > };
> > 
> > 
> > > +	ctx->dops = &luo_session_dentry_operations;
> > 
> >         ctx->ops = &luo_session_sops;
> > 
> > > +	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)
> > 
> > Luo is going full anti-pattern here. This whole return via a function
> > argument completely messes up the later codepths. We don't do manual
> > get_unused_fd_flags() flags and then file in new code, and then fail
> > in-between:
> > 
> >         argp->fd = get_unused_fd_flags(O_CLOEXEC);
> >         if (argp->fd < 0)
> >                 return argp->fd;
> > 
> >         err = luo_session_create(argp->name, &file);
> >         if (err)
> >                 goto err_put_fd;
> > 
> >         err = luo_ucmd_respond(ucmd, sizeof(*argp));
> >         if (err)
> >                 goto err_put_file;
> > 
> >         fd_install(argp->fd, file);
> > 
> > Restructure the code so it just becomes:
> > 
> > struct file *luo_session_create(argp->name);
> > 
> > static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
> > {
> >         struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
> > 
> >         return FD_ADD(O_CLOEXEC, luo_session_create(argp->name));
> > }
> > 
> > and get rid of all this state and error handling. Please fix this.
> 
> We cannot do it this way because we must use copy_to_user() to return fd 
> via ioctl(), and since copy_to_user() may fail, we must do it prior to 
> fd_install().
> 
> Unless there is a specific VFS macro you'd prefer for this 
> delayed-install pattern, I do not see any other way to do this but 
> maintain the get_unused_fd_flags() -> copy_to_user() -> fd_install() to 
> prevent the fd being leaked into the process's table.

The usercopy happens in luo_ucmd_respond it's perfectly fine if that
fails. FD_ADD() handles all that. It reserves an fd, it opens the file
and if that somehow fails it cleans up both the preallocated fd and the
file (And if you need to do more stuff in between there's: FD_PREPARE()
+ fd_publish()).

What I meant is:

	static struct file *luo_session_open(struct luo_ucmd *ucmd)
	{
		struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
	
		err = luo_ucmd_respond(ucmd, sizeof(*argp));
		if (err)
			return err;
	
		return luo_session_create(argp->name);
	}
	
	
	static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
	{
		return FD_ADD(O_CLOEXEC, luo_session_open(ucmd);
	}

I'm not sure why you'd want file first then usercopy but if you need
that then:

	static struct file *luo_session_open(struct luo_ucmd *ucmd)
	{
		struct file *file __free(fput) = NULL;
		struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
		int err;
	
		file = luo_ucmd_respond(ucmd, sizeof(*argp));
		if (IS_ERR(file))
			return file;
	
		err = luo_session_create(argp->name);
		if (err)
			return ERR_CAST(err);
	
		return no_free_ptr(file);
	}
	
	static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
	{
		return FD_ADD(O_CLOEXEC, luo_session_open(ucmd);
	}

> 
> > 
> > >  {
> > > -	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);
> > 
> > Right, you're now sharing the same inode among all luo sessions. So
> > you've gained the ability to recognize luo inodes via fstatfs() but you
> > still can't compare two luo session file descriptors for equality using
> > stat() which is a major win and if you're doing this work anyway, let's
> 
> Luca, is there a specific use case in userspace where we need to compare 
> LUO sessions for equality?
> 
> Christian's proposed solution of using unique inodes provides a standard 
> VFS interface, but it introduces some memory overhead and, more 
> importantly, a performance overhead due to the extra metadata 
> allocations required during the performance-critical kexec blackout 
> window.

I'm excited to be convinced that the memory and performance overhead
matters for luo file descriptors in any shape or form. Userspace manages
processes using file descriptors via pidfs - systemd exclusively so. So
even if luo session fds are created at the same rate and amount like
processes you can rest assured that it will be fine.

Let me turn the argument around: You are adding a full-fledged
filesystem to the kernel for the sole purpose of providing a separate
filesystem type. Why are you bloating the whole kernel for this? Use
the anonymous inode api that allocates a separate inode, use your own
inode operations and then add an ioctl on top of luo if that's all you
need. If this is a proper fs, please do it properly and with foresight.

This whole patchset is based on an idea of mine and I don't need to see
it twisted into oblivion otherwise I'll just do it myself and properly.

I definitely want to be able to compare luo session by fd sooner or
later and retroactively bolting this on with the next hack because you
have userspace depend on the single inode stuff is not going to fly.

You also need to have LSM filtering on what may be persisted and LUO in
general. All of that falls out for free _trivially_ if you modify the
code to what I did. It is incredibly easy to do. To me this is ducking
behind questionable arguments to get something merged as quickly as
possible.


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 15:28       ` Christian Brauner
@ 2026-04-20 15:57         ` Pasha Tatashin
  2026-04-20 16:39         ` Pasha Tatashin
  1 sibling, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-20 15:57 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Pasha Tatashin, luca.boccassi, kexec, linux-mm, graf, rppt,
	pratyush, linux-kernel

On 04-20 17:28, Christian Brauner wrote:
> On Mon, Apr 20, 2026 at 02:55:56PM +0000, Pasha Tatashin wrote:
> > On 04-20 14:26, Christian Brauner wrote:
> > > On Sat, Apr 18, 2026 at 05:28:20PM +0100, 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 fstatfs().
> > > > 
> > > > Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
> > > > Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
> > > > ---
> > > >  include/uapi/linux/magic.h       |  1 +
> > > >  kernel/liveupdate/luo_core.c     | 10 +++-
> > > >  kernel/liveupdate/luo_internal.h |  2 +
> > > >  kernel/liveupdate/luo_session.c  | 89 ++++++++++++++++++++++++++++++--
> > > >  4 files changed, 96 insertions(+), 6 deletions(-)
> > > > 
> > > > diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
> > > > index 4f2da935a76c..4f51005522ff 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 dda7bb57d421..f1a63ebe4fa4 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 8083d8739b09..d4ac7b4c5882 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 5e316a4c5d71..21cbe99fc819 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>
> > > > @@ -363,18 +365,73 @@ 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;
> > > > +
> > > > +/*
> > > > + * Reject all attribute changes on the singleton session inode.
> > > > + * Without this the VFS falls back to simple_setattr(), allowing
> > > > + * fchmod()/fchown() to modify the shared inode.
> > > > + */
> > > > +static int luo_session_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> > > > +			       struct iattr *attr)
> > > 
> > > Don't duplicate, please. Use the generic helper instead:
> > > 
> > > int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> > > 		       struct iattr *attr)
> > > 
> > > > +{
> > > > +	return -EOPNOTSUPP;
> > > 
> > > 
> > > 
> > > > +}
> > > > +
> > > > +static const struct inode_operations luo_session_inode_operations = {
> > > > +	.setattr	= luo_session_setattr,
> > > > +};
> > > > +
> > > > +static char *luo_session_dname(struct dentry *dentry, char *buffer, int buflen)
> > > > +{
> > > > +	return dynamic_dname(buffer, buflen, "luo_session:%s",
> > > > +			     dentry->d_name.name);
> > > 
> > > Use the luo_session:[%s] which is the canonical format for this
> > > (ignoring historcal abberations).
> > > 
> > > > +}
> > > > +
> > > > +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);
> > > 
> > > I'd just call that LUO_FS_MAGIC.
> > > 
> > > > +	if (!ctx)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	fc->s_iflags |= SB_I_NOEXEC;
> > > > +	fc->s_iflags |= SB_I_NODEV;
> > > 
> > >         ctx->s_d_flags |= DCACHE_DONTCACHE;
> > > 
> > > static const struct super_operations luo_session_sops = {
> > >         .drop_inode     = inode_just_drop,
> > >         .statfs         = simple_statfs,
> > > };
> > > 
> > > 
> > > > +	ctx->dops = &luo_session_dentry_operations;
> > > 
> > >         ctx->ops = &luo_session_sops;
> > > 
> > > > +	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)
> > > 
> > > Luo is going full anti-pattern here. This whole return via a function
> > > argument completely messes up the later codepths. We don't do manual
> > > get_unused_fd_flags() flags and then file in new code, and then fail
> > > in-between:
> > > 
> > >         argp->fd = get_unused_fd_flags(O_CLOEXEC);
> > >         if (argp->fd < 0)
> > >                 return argp->fd;
> > > 
> > >         err = luo_session_create(argp->name, &file);
> > >         if (err)
> > >                 goto err_put_fd;
> > > 
> > >         err = luo_ucmd_respond(ucmd, sizeof(*argp));
> > >         if (err)
> > >                 goto err_put_file;
> > > 
> > >         fd_install(argp->fd, file);
> > > 
> > > Restructure the code so it just becomes:
> > > 
> > > struct file *luo_session_create(argp->name);
> > > 
> > > static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
> > > {
> > >         struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
> > > 
> > >         return FD_ADD(O_CLOEXEC, luo_session_create(argp->name));
> > > }
> > > 
> > > and get rid of all this state and error handling. Please fix this.
> > 
> > We cannot do it this way because we must use copy_to_user() to return fd 
> > via ioctl(), and since copy_to_user() may fail, we must do it prior to 
> > fd_install().
> > 
> > Unless there is a specific VFS macro you'd prefer for this 
> > delayed-install pattern, I do not see any other way to do this but 
> > maintain the get_unused_fd_flags() -> copy_to_user() -> fd_install() to 
> > prevent the fd being leaked into the process's table.
> 
> The usercopy happens in luo_ucmd_respond it's perfectly fine if that
> fails. FD_ADD() handles all that. It reserves an fd, it opens the file
> and if that somehow fails it cleans up both the preallocated fd and the
> file (And if you need to do more stuff in between there's: FD_PREPARE()
> + fd_publish()).
> 
> What I meant is:
> 
> 	static struct file *luo_session_open(struct luo_ucmd *ucmd)
> 	{
> 		struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
> 	
> 		err = luo_ucmd_respond(ucmd, sizeof(*argp));

The fd is returned in 'argp->fd', not via ioctl() return value. so this 
response won't contain the fd number that will be returned to usersapce.

This is similar to how it is done in iommufd for example, see 

iommufd_fault_alloc()
	cmd->out_fault_fd = fdno;
	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
	fd_install(fdno, fault->common.filep); 
	return rc

Pasha


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

* Re: [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type
  2026-04-20 15:28       ` Christian Brauner
  2026-04-20 15:57         ` Pasha Tatashin
@ 2026-04-20 16:39         ` Pasha Tatashin
  1 sibling, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-20 16:39 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Pasha Tatashin, luca.boccassi, kexec, linux-mm, graf, rppt,
	pratyush, linux-kernel

> > > Right, you're now sharing the same inode among all luo sessions. So
> > > you've gained the ability to recognize luo inodes via fstatfs() but you
> > > still can't compare two luo session file descriptors for equality using
> > > stat() which is a major win and if you're doing this work anyway, let's
> > 
> > Luca, is there a specific use case in userspace where we need to compare 
> > LUO sessions for equality?
> > 
> > Christian's proposed solution of using unique inodes provides a standard 
> > VFS interface, but it introduces some memory overhead and, more 
> > importantly, a performance overhead due to the extra metadata 
> > allocations required during the performance-critical kexec blackout 
> > window.
> 
> I'm excited to be convinced that the memory and performance overhead
> matters for luo file descriptors in any shape or form. Userspace manages
> processes using file descriptors via pidfs - systemd exclusively so. So
> even if luo session fds are created at the same rate and amount like
> processes you can rest assured that it will be fine.

That is fair, I do not mind having an inode per-session.

> Let me turn the argument around: You are adding a full-fledged
> filesystem to the kernel for the sole purpose of providing a separate
> filesystem type. Why are you bloating the whole kernel for this? Use
> the anonymous inode api that allocates a separate inode, use your own
> inode operations and then add an ioctl on top of luo if that's all you
> need. If this is a proper fs, please do it properly and with foresight.

That's exactly what we added initially: an anonymous inode, and a way to 
identify if it is a session by poking /proc/ for the [luo_session] 
prefix. However, I understand that is not ideal. I originally thought a 
luo agent would never need to verify if an FD belongs to a LUO session, 
but since userspace will need this capability, I am ok with doing it the 
right way.

> This whole patchset is based on an idea of mine and I don't need to see
> it twisted into oblivion otherwise I'll just do it myself and properly.

I do not think that is happening.

> I definitely want to be able to compare luo session by fd sooner or
> later and retroactively bolting this on with the next hack because you
> have userspace depend on the single inode stuff is not going to fly.

Sounds good.

> You also need to have LSM filtering on what may be persisted and LUO in
> general. All of that falls out for free _trivially_ if you modify the
> code to what I did. It is incredibly easy to do. To me this is ducking
> behind questionable arguments to get something merged as quickly as
> possible.

+1.

Pasha


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

end of thread, other threads:[~2026-04-20 16:39 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-18 16:28 [PATCH v8 0/6] liveupdate: new ioctl, change session inode type, bug fixes luca.boccassi
2026-04-18 16:28 ` [PATCH v8 1/6] liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length luca.boccassi
2026-04-19 15:06   ` Pasha Tatashin
2026-04-18 16:28 ` [PATCH v8 2/6] selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length luca.boccassi
2026-04-19 15:11   ` Pasha Tatashin
2026-04-18 16:28 ` [PATCH v8 3/6] liveupdate: add LUO_SESSION_MAGIC magic inode type luca.boccassi
2026-04-20 12:26   ` Christian Brauner
2026-04-20 14:22     ` Luca Boccassi
2026-04-20 14:57       ` Pasha Tatashin
2026-04-20 15:05       ` Christian Brauner
2026-04-20 14:55     ` Pasha Tatashin
2026-04-20 14:59       ` Luca Boccassi
2026-04-20 15:28       ` Christian Brauner
2026-04-20 15:57         ` Pasha Tatashin
2026-04-20 16:39         ` Pasha Tatashin
2026-04-18 16:28 ` [PATCH v8 4/6] selftests/liveupdate: add test case for LUO_SESSION_MAGIC luca.boccassi
2026-04-18 16:28 ` [PATCH v8 5/6] liveupdate: add LIVEUPDATE_SESSION_GET_NAME ioctl luca.boccassi
2026-04-18 16:28 ` [PATCH v8 6/6] selftests/liveupdate: add test cases for LIVEUPDATE_SESSION_GET_NAME luca.boccassi
2026-04-19 15:12   ` Pasha Tatashin

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