mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mike Rapoport <rppt@kernel.org>
To: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: linux-kselftest@vger.kernel.org, shuah@kernel.org,
	akpm@linux-foundation.org, linux-mm@kvack.org,
	vipinsh@google.com, linux-kernel@vger.kernel.org,
	dmatlack@google.com, kexec@lists.infradead.org,
	pratyush@kernel.org, graf@amazon.com
Subject: Re: [PATCH v2 1/2] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl to freeze sessions early
Date: Fri, 23 Oct 2026 10:27:33 +0300	[thread overview]
Message-ID: <atsMZW4ChOWj7Lqv@kernel.org> (raw)
In-Reply-To: <20261020144710.4154339-2-pasha.tatashin@soleen.com>

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@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

> + * 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.

> + *     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.

>   *
>   * 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?

> + * 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?

>   *
> - * 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?

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

>   * @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.

  parent reply	other threads:[~2026-10-23  7:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-20 14:47 [PATCH v2 0/2] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl Pasha Tatashin
2026-10-20 14:47 ` [PATCH v2 1/2] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl to freeze sessions early Pasha Tatashin
2026-10-21 10:14   ` Pratyush Yadav
2026-10-22  1:44     ` Pasha Tatashin
2026-10-23  7:27   ` Mike Rapoport [this message]
2026-10-20 14:47 ` [PATCH v2 2/2] selftests: liveupdate: Add tests for LIVEUPDATE_SESSION_FREEZE Pasha Tatashin
2026-10-21 10:17   ` Pratyush Yadav
2026-10-23  7:30   ` Mike Rapoport

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atsMZW4ChOWj7Lqv@kernel.org \
    --to=rppt@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=dmatlack@google.com \
    --cc=graf@amazon.com \
    --cc=kexec@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=pasha.tatashin@soleen.com \
    --cc=pratyush@kernel.org \
    --cc=shuah@kernel.org \
    --cc=vipinsh@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®