[RFC PATCH] liveupdate: Allow multiple openers for /dev/liveupdate
Mike Rapoport
rppt at kernel.org
Wed Jul 15 23:06:53 PDT 2026
(adding Luca for systemd POV)
On Tue, Jul 14, 2026 at 07:03:56PM +0000, David Matlack wrote:
> Remove the single-opener restriction for /dev/liveupdate by removing the
> atomic in_use tracking and the exclusive open check in luo_open() that
> returned -EBUSY. Protect luo_session_deserialize() with a mutex guard so
> that concurrent open attempts by multiple processes safely executes
> deserialization only once. Update liveupdate selftest to verify that
> multiple concurrent openers succeed.
>
> LUO does not inherently require a single opener. There is some
> documentation about it simplifying state management, but the only thing
> it actually protects is the session deserialization during first open,
> which can be easily handled with a mutex.
>
> Relaxing the single-opener requirement avoids the kernel forcing a
> design pattern on userspace that it itself does not require, e.g.
> allowing multiple userspace processes to create and manage sessions.
>
> Signed-off-by: David Matlack <dmatlack at google.com>
> ---
> Cc: Jason Gunthorpe <jgg at nvidia.com>
> Cc: Samiullah Khawaja <skhawaja at google.com>
>
> kernel/liveupdate/luo_core.c | 46 +++----------------
> kernel/liveupdate/luo_session.c | 8 +++-
> .../testing/selftests/liveupdate/liveupdate.c | 14 +++---
> 3 files changed, 18 insertions(+), 50 deletions(-)
>
> diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
> index 1b2bda22902d..931bb79da276 100644
> --- a/kernel/liveupdate/luo_core.c
> +++ b/kernel/liveupdate/luo_core.c
> @@ -254,19 +254,8 @@ bool liveupdate_enabled(void)
> * which allows a userspace agent to manage the LUO state machine and its
> * associated resources, such as preservable file descriptors.
> *
> - * To ensure that the state machine is controlled by a single entity, access
> - * to this device is exclusive: only one process is permitted to have
> - * ``/dev/liveupdate`` open at any given time. Subsequent open attempts will
> - * fail with -EBUSY until the first process closes its file descriptor.
> - * This singleton model simplifies state management by preventing conflicting
> - * commands from multiple userspace agents.
> */
>
> -struct luo_device_state {
> - struct miscdevice miscdev;
> - atomic_t in_use;
> -};
> -
> static int luo_ioctl_create_session(struct luo_ucmd *ucmd)
> {
> struct liveupdate_ioctl_create_session *argp = ucmd->cmd;
> @@ -329,28 +318,9 @@ static int luo_ioctl_retrieve_session(struct luo_ucmd *ucmd)
>
> static int luo_open(struct inode *inodep, struct file *filep)
> {
> - struct luo_device_state *ldev = container_of(filep->private_data,
> - struct luo_device_state,
> - miscdev);
> -
> - if (atomic_cmpxchg(&ldev->in_use, 0, 1))
> - return -EBUSY;
> -
> /* Always return -EIO to user if deserialization fail */
> - if (luo_session_deserialize()) {
> - atomic_set(&ldev->in_use, 0);
> + if (luo_session_deserialize())
> return -EIO;
> - }
> -
> - return 0;
> -}
> -
> -static int luo_release(struct inode *inodep, struct file *filep)
> -{
> - struct luo_device_state *ldev = container_of(filep->private_data,
> - struct luo_device_state,
> - miscdev);
> - atomic_set(&ldev->in_use, 0);
>
> return 0;
> }
> @@ -419,17 +389,13 @@ static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
> static const struct file_operations luo_fops = {
> .owner = THIS_MODULE,
> .open = luo_open,
> - .release = luo_release,
> .unlocked_ioctl = luo_ioctl,
> };
>
> -static struct luo_device_state luo_dev = {
> - .miscdev = {
> - .minor = MISC_DYNAMIC_MINOR,
> - .name = "liveupdate",
> - .fops = &luo_fops,
> - },
> - .in_use = ATOMIC_INIT(0),
> +static struct miscdevice luo_dev = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "liveupdate",
> + .fops = &luo_fops,
> };
>
> static int __init liveupdate_ioctl_init(void)
> @@ -437,6 +403,6 @@ static int __init liveupdate_ioctl_init(void)
> if (!liveupdate_enabled())
> return 0;
>
> - return misc_register(&luo_dev.miscdev);
> + return misc_register(&luo_dev);
> }
> late_initcall(liveupdate_ioctl_init);
> diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
> index b79b2a488974..ca4d0639d39a 100644
> --- a/kernel/liveupdate/luo_session.c
> +++ b/kernel/liveupdate/luo_session.c
> @@ -584,13 +584,17 @@ static int luo_session_deserialize_one(struct luo_session_header *sh,
>
> int luo_session_deserialize(void)
> {
> - struct luo_session_header *sh = &luo_session_global.incoming;
> + static DEFINE_MUTEX(luo_session_deserialize_lock);
> static bool is_deserialized;
> + static int saved_err;
> +
> + struct luo_session_header *sh = &luo_session_global.incoming;
> struct luo_session_ser *ser;
> struct kho_block_set_it it;
> - static int saved_err;
> int err;
>
> + guard(mutex)(&luo_session_deserialize_lock);
> +
> /* If has been deserialized, always return the same error code */
> if (is_deserialized)
> return saved_err;
> diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testing/selftests/liveupdate/liveupdate.c
> index 502fb3567e38..a8e1a8911849 100644
> --- a/tools/testing/selftests/liveupdate/liveupdate.c
> +++ b/tools/testing/selftests/liveupdate/liveupdate.c
> @@ -11,7 +11,7 @@
> * /dev/liveupdate character device and its session management capabilities.
> *
> * Tests include:
> - * - Device access: basic open/close, and enforcement of exclusive access.
> + * - Device access: basic open/close, and support for multiple openers.
> * - Session management: creation of unique sessions, and duplicate name detection.
> * - Resource preservation: successfully preserving individual and multiple memfds,
> * verifying contents remain accessible.
> @@ -70,13 +70,12 @@ TEST_F(liveupdate_device, basic_open_close)
> }
>
> /*
> - * Test Case: Exclusive Open Enforcement
> + * Test Case: Multiple Open Support
> *
> - * Verifies that the /dev/liveupdate device can only be opened by one process
> - * at a time. It checks that a second attempt to open the device fails with
> - * the EBUSY error code.
> + * Verifies that the /dev/liveupdate device can be opened by multiple openers
> + * concurrently without errors.
> */
> -TEST_F(liveupdate_device, exclusive_open)
> +TEST_F(liveupdate_device, multiple_open)
> {
> self->fd1 = open(LIVEUPDATE_DEV, O_RDWR);
>
> @@ -85,8 +84,7 @@ TEST_F(liveupdate_device, exclusive_open)
>
> ASSERT_GE(self->fd1, 0);
> self->fd2 = open(LIVEUPDATE_DEV, O_RDWR);
> - EXPECT_LT(self->fd2, 0);
> - EXPECT_EQ(errno, EBUSY);
> + ASSERT_GE(self->fd2, 0);
> }
>
> /* Helper function to create a LUO session via ioctl. */
>
> base-commit: 10fd52dc7bbd6013e949ff1833be0821b7553fb0
> --
> 2.55.0.141.g00534a21ce-goog
>
--
Sincerely yours,
Mike.
More information about the kexec
mailing list