From nobody Mon Feb 9 03:11:51 2026 Received: from sender4-pp-g123.zoho.com (sender4-pp-g123.zoho.com [136.143.188.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D496E31BC95 for ; Fri, 30 Jan 2026 14:52:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.123 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769784733; cv=pass; b=Im9lRH5zDp1q0TitZIC39HgRsZD3oQw1/bHglAoPmYBN5lsP5ICIdtJk+ivEMCP5v8RKsYMCEyu4oJqnu5/rolZMHwVbproG9t6vQTZCkdSfOlphUFIveXCY43yNmTfqtq9cKmuOFogQr24V4FdGNbS9zxs2QChy2PkDeEDjDvk= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769784733; c=relaxed/simple; bh=+w1Y00hggF0M/OMkwUROq7lyblyWhRDsAI2q5xhBeJs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aNGeOAPqA7LU9629KN1dgbGR3ogd31xo1tBcye+BDrAxnJIldU+WxhTMzIF3ZgPxDPLzEbCiqAYnIFfLnuraiI8OLyFVDugYW7RcZsMCo35uVRmLM8Ioq62Uqp3uktvF8kLifB4umjsSNKw6sWaUfys0hNyEsYRJKaUTO+NHLW0= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=To5n8mYC; arc=pass smtp.client-ip=136.143.188.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="To5n8mYC" ARC-Seal: i=1; a=rsa-sha256; t=1769784708; cv=none; d=zohomail.com; s=zohoarc; b=FVpEFdAwp3x4il4of3BzkzRjzu31+0VOXWeoMXO7BhdqaJpyvDURuiinhjIAztnI/sk52OmdGYNuzuOpdDFjS0qJz98Xyjao2ZDnG1TTNrjDLlmxeWilGBE4BsNe+Kr+Q5McmBV0bl1MsTQhLpSjocsd5aHH/xyZAdM/E30fA9A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1769784708; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=Qr4dp1DPnegx3roUBm2SMQe7fX+ISr7dRpn3wVvsn3U=; b=j2Yeoi+hkW0u1AOe+9txksDDFzdc+T6X8IsyJMdw7x9o3EHa6bb4Wpl2/pYZBzGT7f3Iidds1iJMiZXB00vClmIgpxoiJ7c7Je/sUfsmsSY0iKETsS7hym/Lm/rY5pdzobPmwA+0GYREbjfMhwxG21InqXsiFBPDVodEOPahT5E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1769784708; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=Qr4dp1DPnegx3roUBm2SMQe7fX+ISr7dRpn3wVvsn3U=; b=To5n8mYCO1EiKykFgxoV20S9snfIVTy/B0P0IUXBqvotv0FOvfZuHKyuRG5FYdiC SUbS5lV1BJK08NiK2j/b0NLoEVdE2wfkkEs6VY946lMQ3M4GlU2jN5LK37PJviOa0bM XxO729N3cvbqgiuQC292rw5R9y1nHMassXQLjVFw= Received: by mx.zohomail.com with SMTPS id 176978470596793.32935511186281; Fri, 30 Jan 2026 06:51:45 -0800 (PST) From: Li Chen To: Pasha Tatashin , Mike Rapoport , Pratyush Yadav , linux-kernel@vger.kernel.org Cc: Li Chen Subject: [PATCH v1 2/3] liveupdate: bound and control post-kexec restore window Date: Fri, 30 Jan 2026 22:51:18 +0800 Message-ID: <20260130145122.368748-3-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260130145122.368748-1-me@linux.beauty> References: <20260130145122.368748-1-me@linux.beauty> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" The restore window can remain open indefinitely if userspace never finishes some incoming sessions. Bound the window with a hard timeout and add a RESTORE_DONE ioctl on /dev/liveupdate so an orchestrator can end it explicitly once restoration is complete. Signed-off-by: Li Chen --- include/linux/liveupdate.h | 4 +++ include/uapi/linux/liveupdate.h | 34 ++++++++++++++++++ kernel/liveupdate/luo_core.c | 15 ++++++++ kernel/liveupdate/luo_internal.h | 1 + kernel/liveupdate/luo_session.c | 59 ++++++++++++++++++++++++++------ 5 files changed, 103 insertions(+), 10 deletions(-) diff --git a/include/linux/liveupdate.h b/include/linux/liveupdate.h index 406a6e2dd4a1..301d3e94516e 100644 --- a/include/linux/liveupdate.h +++ b/include/linux/liveupdate.h @@ -220,6 +220,10 @@ bool liveupdate_enabled(void); /* * Return true during a kexec-based live update boot while userspace is st= ill * restoring preserved sessions/resources. + * + * The restore window ends automatically once all incoming sessions have b= een + * retrieved and finished. Userspace can also terminate the window explici= tly + * (and early) via LIVEUPDATE_IOCTL_RESTORE_DONE. */ bool liveupdate_restore_in_progress(void); =20 diff --git a/include/uapi/linux/liveupdate.h b/include/uapi/linux/liveupdat= e.h index 30bc66ee9436..357137d9a78c 100644 --- a/include/uapi/linux/liveupdate.h +++ b/include/uapi/linux/liveupdate.h @@ -51,6 +51,7 @@ enum { LIVEUPDATE_CMD_BASE =3D 0x00, LIVEUPDATE_CMD_CREATE_SESSION =3D LIVEUPDATE_CMD_BASE, LIVEUPDATE_CMD_RETRIEVE_SESSION =3D 0x01, + LIVEUPDATE_CMD_RESTORE_DONE =3D 0x02, }; =20 /* ioctl commands for session file descriptors */ @@ -118,6 +119,39 @@ struct liveupdate_ioctl_retrieve_session { #define LIVEUPDATE_IOCTL_RETRIEVE_SESSION \ _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RETRIEVE_SESSION) =20 +/** + * struct liveupdate_ioctl_restore_done - ioctl(LIVEUPDATE_IOCTL_RESTORE_D= ONE) + * @size: Input; sizeof(struct liveupdate_ioctl_restore_done) + * @reserved: Input; Must be zero. Reserved for future use. + * + * Marks the completion of the post-kexec restore window. + * + * After a live update (kexec), userspace needs time to restore preserved + * sessions/resources. Some kernel subsystems may apply temporary compatib= ility + * behavior during this window. Userspace should call this ioctl once it h= as + * completed restoration and wants normal kernel behavior to resume, even = if + * some incoming sessions are left unused. + * + * This ioctl updates global state and is not tied to a specific live upda= te + * session file descriptor. A typical userspace agent calls it once per li= ve + * update, after it has restored the required state. + * + * Note that the restore window may also end automatically once all incomi= ng + * sessions have been retrieved and finished (or their session file + * descriptors have been closed). This ioctl is intended for cases where an + * agent intentionally does not retrieve all incoming sessions or does not= want + * to wait for them to be finished/closed. + * + * Return: 0 on success, negative error code on failure. + */ +struct liveupdate_ioctl_restore_done { + __u32 size; + __u32 reserved; +}; + +#define LIVEUPDATE_IOCTL_RESTORE_DONE \ + _IO(LIVEUPDATE_IOCTL_TYPE, LIVEUPDATE_CMD_RESTORE_DONE) + /* Session specific IOCTLs */ =20 /** diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c index 19c91843fbdb..fb6a73c08979 100644 --- a/kernel/liveupdate/luo_core.c +++ b/kernel/liveupdate/luo_core.c @@ -340,6 +340,18 @@ static int luo_ioctl_retrieve_session(struct luo_ucmd = *ucmd) return err; } =20 +static int luo_ioctl_restore_done(struct luo_ucmd *ucmd) +{ + struct liveupdate_ioctl_restore_done *argp =3D ucmd->cmd; + + if (argp->reserved) + return -EINVAL; + + luo_session_restore_done(); + + return luo_ucmd_respond(ucmd, sizeof(*argp)); +} + static int luo_open(struct inode *inodep, struct file *filep) { struct luo_device_state *ldev =3D container_of(filep->private_data, @@ -371,6 +383,7 @@ static int luo_release(struct inode *inodep, struct fil= e *filep) union ucmd_buffer { struct liveupdate_ioctl_create_session create; struct liveupdate_ioctl_retrieve_session retrieve; + struct liveupdate_ioctl_restore_done restore_done; }; =20 struct luo_ioctl_op { @@ -395,6 +408,8 @@ static const struct luo_ioctl_op luo_ioctl_ops[] =3D { struct liveupdate_ioctl_create_session, name), IOCTL_OP(LIVEUPDATE_IOCTL_RETRIEVE_SESSION, luo_ioctl_retrieve_session, struct liveupdate_ioctl_retrieve_session, name), + IOCTL_OP(LIVEUPDATE_IOCTL_RESTORE_DONE, luo_ioctl_restore_done, + struct liveupdate_ioctl_restore_done, reserved), }; =20 static long luo_ioctl(struct file *filep, unsigned int cmd, unsigned long = arg) diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 8aa4c5b0101b..03a5b27498be 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -96,6 +96,7 @@ int luo_session_serialize(void); int luo_session_deserialize(void); bool luo_session_quiesce(void); void luo_session_resume(void); +void luo_session_restore_done(void); =20 int luo_preserve_file(struct luo_file_set *file_set, u64 token, int fd); void luo_file_unpreserve_files(struct luo_file_set *file_set); diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_sessio= n.c index e1d1ab795c40..2c7dd3b12303 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,7 @@ #include #include #include +#include #include #include "luo_internal.h" =20 @@ -118,8 +120,28 @@ static struct luo_session_global luo_session_global = =3D { }, }; =20 +/* + * Count of incoming sessions that still keep the post-kexec restore window + * open. This is initialized from the serialized session header and decrem= ented + * when a retrieved session is finished (or closed). Userspace can also en= d the + * restore window explicitly via LIVEUPDATE_IOCTL_RESTORE_DONE. + */ static atomic_long_t liveupdate_incoming_sessions_left =3D ATOMIC_LONG_INI= T(0); =20 +#define LIVEUPDATE_RESTORE_WINDOW_TIMEOUT (15 * 60 * HZ) + +static void liveupdate_restore_window_timeout(struct work_struct *work) +{ + if (!liveupdate_restore_in_progress()) + return; + + pr_warn_once("liveupdate restore window timed out\n"); + atomic_long_set(&liveupdate_incoming_sessions_left, 0); +} + +static DECLARE_DELAYED_WORK(liveupdate_restore_window_timeout_work, + liveupdate_restore_window_timeout); + bool liveupdate_restore_in_progress(void) { return atomic_long_read(&liveupdate_incoming_sessions_left) > 0; @@ -141,6 +163,16 @@ void __init luo_session_restore_window_init(void) } =20 atomic_long_set(&liveupdate_incoming_sessions_left, (long)count); + + if (count > 0) + schedule_delayed_work(&liveupdate_restore_window_timeout_work, + LIVEUPDATE_RESTORE_WINDOW_TIMEOUT); +} + +void luo_session_restore_done(void) +{ + atomic_long_set(&liveupdate_incoming_sessions_left, 0); + cancel_delayed_work_sync(&liveupdate_restore_window_timeout_work); } =20 static struct luo_session *luo_session_alloc(const char *name) @@ -209,18 +241,26 @@ static void luo_session_remove(struct luo_session_hea= der *sh, static int luo_session_finish_one(struct luo_session *session) { int err; + bool cancel_timeout =3D false; =20 - guard(mutex)(&session->mutex); - if (session->finished) - return 0; + { + guard(mutex)(&session->mutex); + if (session->finished) + return 0; =20 - err =3D luo_file_finish(&session->file_set); - if (err) - return err; + err =3D luo_file_finish(&session->file_set); + if (err) + return err; =20 - session->finished =3D true; - if (session->retrieved) - atomic_long_dec(&liveupdate_incoming_sessions_left); + session->finished =3D true; + if (session->retrieved) { + if (atomic_long_dec_if_positive(&liveupdate_incoming_sessions_left) =3D= =3D 0) + cancel_timeout =3D true; + } + } + + if (cancel_timeout) + cancel_delayed_work_sync(&liveupdate_restore_window_timeout_work); =20 return 0; } @@ -545,7 +585,6 @@ int __init luo_session_setup_incoming(void *fdt_in) luo_session_global.incoming.header_ser =3D header_ser; luo_session_global.incoming.ser =3D (void *)(header_ser + 1); luo_session_global.incoming.active =3D true; - return 0; } =20 --=20 2.52.0