[PATCH v2 1/2] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl to freeze sessions early

Pasha Tatashin pasha.tatashin at soleen.com
Sun Oct 25 18:10:02 PDT 2026


On Fri, Oct 23, 2026 at 3:27 AM Mike Rapoport <rppt at kernel.org> wrote:
>
> On Tue, Oct 20, 2026 at 10:47:03AM -0400, Pasha Tatashin wrote:
> > Currently, LUO sessions are frozen automatically only inside the
> > reboot(LINUX_REBOOT_CMD_KEXEC) pathway, where luo_session_serialize()
> > iterates through all outgoing sessions under global write locks and
> > freezes them serially during shutdown. Allowing userspace to decide when
> > to freeze a session once it is done with it (and to freeze multiple
> > sessions in parallel, since luo_session_freeze() only acquires the
> > per-session mutex) avoids postponing this work to kexec shutdown and
> > reduces blackout time.
> >
> > Introduce LIVEUPDATE_SESSION_FREEZE on session file descriptors to freeze
> > a session early. The ioctl runs .freeze() across all preserved files in the
> > session and serializes their metadata. Once frozen, no additional files can
> > be added to the session, and subsequent freeze calls return -EBUSY. Freeze
> > is only valid on outgoing sessions and returns -EINVAL on incoming ones.
> >
> > During kexec, sessions that were already frozen early are skipped, while
> > sessions that did not invoke this ioctl auto-freeze as before. If an
> > early-frozen session is released before kexec, its files are rolled back
> > via .unfreeze() before being unpreserved.
> >
> > Signed-off-by: Pasha Tatashin <pasha.tatashin at soleen.com>
> > ---
> >  include/linux/liveupdate.h       |  3 +-
> >  include/uapi/linux/liveupdate.h  | 25 ++++++++++
> >  kernel/liveupdate/luo_file.c     | 73 +++++++++++++---------------
> >  kernel/liveupdate/luo_internal.h |  9 ++--
> >  kernel/liveupdate/luo_session.c  | 82 +++++++++++++++++++++++++-------
> >  mm/memfd_luo.c                   |  5 +-
> >  6 files changed, 134 insertions(+), 63 deletions(-)
> >
> > diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h
> > index 6051abc0612c..851692571e13 100644
> > --- a/include/linux/liveupdate.h
> > +++ b/include/linux/liveupdate.h
> > @@ -59,7 +59,8 @@ struct liveupdate_file_op_args {
> >   *                compatible with the given file.
> >   * @preserve:     Required. Performs state-saving for the file.
> >   * @unpreserve:   Required. Cleans up any resources allocated by @preserve.
> > - * @freeze:       Optional. Final actions just before kernel transition.
> > + * @freeze:       Optional. Prepares final state before kexec (via
> > + *                LIVEUPDATE_SESSION_FREEZE or during reboot).
> >   * @unfreeze:     Optional. Undo freeze operations.
> >   * @retrieve:     Required. Restores the file in the new kernel.
> >   * @can_finish:   Optional. Check if this FD can finish, i.e. all restoration
> > diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdate.h
> > index 4043d4038712..1644c502521e 100644
> > --- a/include/uapi/linux/liveupdate.h
> > +++ b/include/uapi/linux/liveupdate.h
> > @@ -60,6 +60,7 @@ enum {
> >       LIVEUPDATE_CMD_SESSION_RETRIEVE_FD = 0x41,
> >       LIVEUPDATE_CMD_SESSION_FINISH = 0x42,
> >       LIVEUPDATE_CMD_SESSION_GET_NAME = 0x43,
> > +     LIVEUPDATE_CMD_SESSION_FREEZE = 0x44,
> >  };
> >
> >  /**
> > @@ -236,4 +237,28 @@ struct liveupdate_session_get_name {
> >  #define LIVEUPDATE_SESSION_GET_NAME                                  \
> >       _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_GET_NAME)
> >
> > +/**
> > + * struct liveupdate_session_freeze - ioctl(LIVEUPDATE_SESSION_FREEZE)
> > + * @size:     Input; sizeof(struct liveupdate_session_freeze)
> > + * @reserved: Input; Must be zero. Reserved for future use.
> > + *
> > + * Freezes the session early from userspace before the reboot kexec phase.
> > + *
> > + * When this ioctl is called, the kernel executes the .freeze() callback on all
> > + * preserved files in the session and serializes their metadata. Once frozen,
> > + * no further files can be preserved in the session.
> > + *
> > + * During the subsequent reboot kexec, this session is recognized as already
>
> Nit: kexec reboot

Ok

>
> > + * frozen and is skipped from the late auto freeze.
> > + *
> > + * Return: 0 on success, negative error code on failure.
> > + */
> > +struct liveupdate_session_freeze {
> > +     __u32           size;
> > +     __u32           reserved;
> > +};
> > +
> > +#define LIVEUPDATE_SESSION_FREEZE                                    \
> > +     _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_SESSION_FREEZE)
> > +
> >  #endif /* _UAPI_LIVEUPDATE_H */
> > diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
> > index a39992da504f..7f79890beb31 100644
> > --- a/kernel/liveupdate/luo_file.c
> > +++ b/kernel/liveupdate/luo_file.c
> > @@ -31,9 +31,9 @@
> >   *   - unpreserve(): Cleans up any resources allocated by .preserve(), called
> >   *     if the preservation process is aborted before the reboot (i.e. session is
> >   *     closed).
> > - *   - freeze(): A final pre-reboot opportunity to prepare the state for kexec.
> > - *     We are already in reboot syscall, and therefore userspace cannot mutate
> > - *     the file anymore.
> > + *   - freeze(): Prepares the final state for kexec. Invoked either early via
>
> Nit: I'd drop "early", it seems relevant only to the current change, but
> will be more confusing than useful later.

Good point, will do.

>
> > + *     the LIVEUPDATE_SESSION_FREEZE ioctl once userspace is done modifying
> > + *     the session's files, or during the reboot() syscall.
> >   *   - unfreeze(): Undoes the actions of .freeze(), called if the live update
> >   *     is aborted after the freeze phase.
> >   *   - retrieve(): Reconstructs the file in the new kernel from the preserved
> > @@ -48,7 +48,8 @@
> >   *    handler, calls its .preserve() operation, and creates an internal &struct
> >   *    luo_file to track the live state.
> >   *
> > - * 2. Freeze (Pre-Reboot): Just before the kexec, luo_file_freeze() is called.
> > + * 2. Freeze (Pre-Reboot): Either explicitly via LIVEUPDATE_SESSION_FREEZE or
> > + *    just before the kexec during reboot(), luo_file_freeze() is called.
> >   *    It iterates through all preserved files, calls their respective .freeze()
> >   *    operation, and serializes their final metadata (compatible string, token,
> >   *    and data handle) into a contiguous memory block for KHO.
> > @@ -74,15 +75,16 @@
> >   *
> >   * 1. Abort Before Reboot: If the userspace agent aborts the live update
> >   *    process before calling reboot (e.g., by closing the session file
> > - *    descriptor), the session's release handler calls
> > - *    luo_file_unpreserve_files(). This invokes the .unpreserve() callback on
> > - *    all preserved files, ensuring all allocated resources are cleaned up and
> > - *    returning the system to a clean state.
> > + *    descriptor), the session's release handler unfreezes the files if the
> > + *    session was already frozen and calls luo_file_unpreserve_files(). This
> > + *    invokes the .unpreserve() callback on all preserved files, ensuring all
> > + *    allocated resources are cleaned up and returning the system to a clean
> > + *    state.
> >   *
> > - * 2. Freeze Failure: During the reboot() syscall, if any handler's .freeze()
> > - *    op fails, the .unfreeze() op is invoked on all previously *successful*
> > - *    freezes to roll back their state. The reboot() syscall then returns an
> > - *    error to userspace, canceling the live update.
> > + * 2. Freeze Failure: During LIVEUPDATE_SESSION_FREEZE or the reboot() syscall,
> > + *    if any handler's .freeze() op fails, the .unfreeze() op is invoked on all
> > + *    previously *successful* freezes to roll back their state, and an error is
> > + *    returned to userspace.
>
> I think we still want to say that freeze failure at reboot cancels live
> update.

OK

>
> >   *
> >   * 3. Finish Failure: In the new kernel, if a handler's .finish() op fails,
> >   *    the luo_file_finish() operation is aborted. LUO retains ownership of
> > @@ -440,12 +442,11 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set,
> >
> >  /**
> >   * luo_file_freeze - Freezes all preserved files and serializes their metadata.
> > - * @file_set:     The file_set whose files are to be frozen.
> > - * @file_set_ser: Where to put the serialized file_set.
> > + * @file_set: The file_set whose files are to be frozen.
> >   *
> > - * This function is called from the reboot() syscall path, just before the
> > - * kernel transitions to the new image via kexec. Its purpose is to perform the
> > - * final preparation and serialization of all preserved files in the file_set.
> > + * This function is called from LIVEUPDATE_SESSION_FREEZE or from the reboot()
> > + * syscall path. Its purpose is to perform the preparation and serialization of
>
> Nit:                                          ^ final?

OK

>
> > + * all preserved files in the file_set.
> >   *
> >   * It iterates through each preserved file in FIFO order (the order of
> >   * preservation) and performs two main actions:
> > @@ -457,22 +458,20 @@ static void __luo_file_unfreeze(struct luo_file_set *file_set,
> >   *
> >   * 2. Serializes Metadata: After a successful freeze, it copies the final file
> >   *    metadata—the handler's compatible string, the user token, and the final
> > - *    private data handle—into the pre-allocated contiguous memory buffer
> > - *    (file_set->files) that will be handed over to the next kernel via KHO.
> > + *    private data handle—into the pre-allocated serialization blocks
> > + *    (file_set->block_set) that will be handed over to the next kernel via KHO.
> >   *
> >   * Error Handling (Rollback):
> >   * This function is atomic. If any handler's .freeze() operation fails, the
> > - * entire live update is aborted. The __luo_file_unfreeze() helper is
> > - * immediately called to invoke the .unfreeze() op on all files that were
> > - * successfully frozen before the point of failure, rolling them back to a
> > - * running state. The function then returns an error, causing the reboot()
> > - * syscall to fail.
> > + * __luo_file_unfreeze() helper is immediately called to invoke the .unfreeze()
> > + * op on all files that were successfully frozen before the point of failure,
> > + * rolling them back to an unfrozen state, and an error is returned to the
> > + * caller.
>
> Don't you want to say that reboot is canceled here too?

Sure

>
> >   *
> > - * Context: Called only from the liveupdate_reboot() path.
> > + * Context: Called from LIVEUPDATE_SESSION_FREEZE or the liveupdate_reboot() path.
> >   * Return: 0 on success, or a negative errno on failure.
> >   */
> > -int luo_file_freeze(struct luo_file_set *file_set,
> > -                 struct luo_file_set_ser *file_set_ser)
> > +int luo_file_freeze(struct luo_file_set *file_set)
> >  {
> >       struct luo_file *luo_file;
> >       struct kho_block_set_it it;
> > @@ -506,9 +505,6 @@ int luo_file_freeze(struct luo_file_set *file_set,
> >               file_ser->token = luo_file->token;
> >       }
> >
> > -     file_set_ser->count = file_set->count;
> > -     file_set_ser->files = kho_block_set_head_pa(&file_set->block_set);
> > -
> >       return 0;
> >
> >  err_unfreeze:
> > @@ -519,28 +515,25 @@ int luo_file_freeze(struct luo_file_set *file_set,
> >
> >  /**
> >   * luo_file_unfreeze - Unfreezes all files in a file_set and clear serialization
> > - * @file_set:     The file_set whose files are to be unfrozen.
> > - * @file_set_ser: Serialized file_set.
> > + * @file_set: The file_set whose files are to be unfrozen.
> >   *
> >   * This function rolls back the state of all files in a file_set after the
> > - * freeze phase has begun but must be aborted. It is the counterpart to
> > - * luo_file_freeze().
> > + * freeze phase has begun. It is the counterpart to luo_file_freeze().
> >   *
> >   * It invokes the __luo_file_unfreeze() helper with a NULL argument, which
> > - * signals the helper to iterate through all files in the file_set and call
> > - * their respective .unfreeze() handler callbacks.
> > + * signals the helper to iterate through all files in the file_set, call their
> > + * respective .unfreeze() handler callbacks, and clear the serialized blocks.
> >   *
> >   * Context: This is called when the live update is aborted during
> > - *          the reboot() syscall, after luo_file_freeze() has been called.
> > + *          the reboot() syscall, after luo_file_freeze() has been called,
> > + *          or when an early-frozen session is released before kexec.
> >   */
> > -void luo_file_unfreeze(struct luo_file_set *file_set,
> > -                    struct luo_file_set_ser *file_set_ser)
> > +void luo_file_unfreeze(struct luo_file_set *file_set)
>
> I went several time back and forth trying to understand why we remove
> *file_set_ser here.
>
> Maybe add a preparation patch that does the renames and moves file_set_ser
> handling?

Yes, this is a good suggestion. I will add another patch  to rename,
and change the argument.

>
> >  {
> >       if (!file_set->count)
> >               return;
> >
> >       __luo_file_unfreeze(file_set, NULL);
> > -     memset(file_set_ser, 0, sizeof(*file_set_ser));
> >  }
> >
> >  /**
> > diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
> > index dac6644bfb18..b3fc3dcf5f36 100644
> > --- a/kernel/liveupdate/luo_internal.h
> > +++ b/kernel/liveupdate/luo_internal.h
> > @@ -64,6 +64,8 @@ struct luo_file_set {
> >   *              previous kernel) sessions.
> >   * @retrieved:  A boolean flag indicating whether this session has been
> >   *              retrieved by a consumer in the new kernel.
> > + * @frozen:     A boolean flag indicating whether this session has been
> > + *              frozen early from userspace.
>
> s/early/explicitly

OK

>
> >   * @file_set:   A set of files that belong to this session.
> >   * @mutex:      protects fields in the luo_session.
> >   */
> > @@ -71,6 +73,7 @@ struct luo_session {
> >       char name[LIVEUPDATE_SESSION_NAME_LENGTH];
> >       struct list_head list;
> >       bool retrieved;
> > +     bool frozen;
> >       struct luo_file_set file_set;
> >       struct mutex mutex;
> >  };
>
> --
> Sincerely yours,
> Mike.



More information about the kexec mailing list