(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 <[email protected]>
> ---
> Cc: Jason Gunthorpe <[email protected]>
> Cc: Samiullah Khawaja <[email protected]>
> 
>  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.

Reply via email to