From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from toronto-edge.smtp.mymangomail.com (toronto-edge.smtp.mymangomail.com [209.38.81.170]) (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 199DB3B961D for ; Fri, 20 Mar 2026 16:45:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.38.81.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774025136; cv=none; b=LAoG8FJuDPosf5WGMvqUJhwAudteHKrcNMZE02h72cyDG1TaTKRBuHvVpzWW2GbegB81+DjsCaSrthYWqNUnVt2ypiMfBceYkGm6uYyMW6CkstSK70a1Smi+hafNOAHX/dujDjeE7l1UnM5KHPcLh2KiyseC/UDKqDS0qyWvTLA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774025136; c=relaxed/simple; bh=d0ek7AVQMvweJcV8KF6oPuPSOtLVpNAVzgGT5RI8EcU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=FNzNtrsEhLpjoGMFVIgkrSS2xcyz460ZCuyoSThbI9cZ8YQbDEaSFn5Tn+aj/A5AypNSNOS9hZB88VBzyYzi9IY4Cmxdq+ftWFPk3c2yRLls4huUQxzprH8ofF/qm6uQCM2vAICyrpPr0idwL3cjikyEPX6kEdKXZj+zdJyDUrA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=gerlicz.space; spf=pass smtp.mailfrom=gerlicz.space; dkim=pass (1024-bit key) header.d=gerlicz.space header.i=@gerlicz.space header.b=DSL9oMa3; arc=none smtp.client-ip=209.38.81.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=gerlicz.space Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gerlicz.space Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=gerlicz.space header.i=@gerlicz.space header.b="DSL9oMa3" Received: from smtp.mymangomail.com (localhost [127.0.0.1]) by smtp.mymangomail.com (Mango Mail) with ESMTP id C45144089D; Fri, 20 Mar 2026 12:39:33 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gerlicz.space; s=mango-1; t=1774024773; bh=d0ek7AVQMvweJcV8KF6oPuPSOtLVpNAVzgGT5RI8EcU=; h=From:To:Cc:Subject:Date:From; b=DSL9oMa38kQD7wIQawTgMO0RV9QMPigjVMbhXOFBzgog5IuDWtY7Rv57NTZRuVbuI 4HCJSWwK1CfT/nDEoJ/owhoqupk13oUBSh5z69FSSxfm0FnQl+Nl7BdviOJYPGZaQw OoTDqrIbtyEIF2Xsxz7zDvL7WluNYc2EWKrjIf04= X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 X-Mango-Origin: 1 Received: from authenticated-user (smtp.mymangomail.com [205.185.121.143]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp.mymangomail.com (Mango Mail) with ESMTPSA id 7038A40440; Fri, 20 Mar 2026 12:38:30 -0400 (EDT) From: Oskar Gerlicz Kowalczuk To: Pasha Tatashin , Mike Rapoport , Baoquan He Cc: Pratyush Yadav , Andrew Morton , linux-kernel@vger.kernel.org, kexec@lists.infradead.org, linux-mm@kvack.org, Oskar Gerlicz Kowalczuk Subject: [PATCH 1/5] liveupdate: block outgoing session updates during reboot Date: Fri, 20 Mar 2026 17:37:16 +0100 Message-ID: <20260320163720.100456-1-oskar@gerlicz.space> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit When kernel_kexec() starts a live update handover, LUO serializes outgoing sessions before the reboot path freezes tasks or shuts devices down. That leaves a window where close() and LIVEUPDATE_SESSION_PRESERVE_FD can still mutate an existing outgoing session after luo_session_serialize() has already captured it. The race is dangerous because the next kernel may inherit stale file metadata or references to memory that userspace has already unpreserved. That breaks handover consistency and can later trigger restore failures on already torn down state. Mark the outgoing session set as rebooting while serialization is in progress, reject new mutations with -EBUSY, and make release wait until rebooting finishes before unpreserving files. Reset the flag and wake waiters when serialization rolls back, and use READ_ONCE() and WRITE_ONCE() so the state is visible across CPUs. Signed-off-by: Oskar Gerlicz Kowalczuk --- kernel/liveupdate/luo_session.c | 58 ++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c index 783677295640..ee5ea2a8ed3f 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include "luo_internal.h" @@ -91,8 +92,10 @@ struct luo_session_header { long count; struct list_head list; struct rw_semaphore rwsem; + wait_queue_head_t reboot_waitq; struct luo_session_header_ser *header_ser; struct luo_session_ser *ser; + bool rebooting; bool active; }; @@ -110,13 +113,23 @@ static struct luo_session_global luo_session_global = { .incoming = { .list = LIST_HEAD_INIT(luo_session_global.incoming.list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), + .reboot_waitq = + __WAIT_QUEUE_HEAD_INITIALIZER(luo_session_global.incoming.reboot_waitq), }, .outgoing = { .list = LIST_HEAD_INIT(luo_session_global.outgoing.list), .rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), + .reboot_waitq = + __WAIT_QUEUE_HEAD_INITIALIZER(luo_session_global.outgoing.reboot_waitq), }, }; +static void luo_session_reboot_done(struct luo_session_header *sh) +{ + WRITE_ONCE(sh->rebooting, false); + wake_up_all(&sh->reboot_waitq); +} + static struct luo_session *luo_session_alloc(const char *name) { struct luo_session *session = kzalloc_obj(*session); @@ -152,6 +165,9 @@ static int luo_session_insert(struct luo_session_header *sh, * for new session. */ if (sh == &luo_session_global.outgoing) { + if (READ_ONCE(sh->rebooting)) + return -EBUSY; + if (sh->count == LUO_SESSION_MAX) return -ENOMEM; } @@ -216,9 +232,22 @@ static int luo_session_release(struct inode *inodep, struct file *filep) } sh = &luo_session_global.incoming; } else { - scoped_guard(mutex, &session->mutex) - luo_file_unpreserve_files(&session->file_set); sh = &luo_session_global.outgoing; + + for (;;) { + down_read(&sh->rwsem); + if (READ_ONCE(sh->rebooting)) { + up_read(&sh->rwsem); + wait_event(sh->reboot_waitq, + !READ_ONCE(sh->rebooting)); + continue; + } + + scoped_guard(mutex, &session->mutex) + luo_file_unpreserve_files(&session->file_set); + up_read(&sh->rwsem); + break; + } } luo_session_remove(sh, session); @@ -231,10 +260,22 @@ static int luo_session_preserve_fd(struct luo_session *session, struct luo_ucmd *ucmd) { struct liveupdate_session_preserve_fd *argp = ucmd->cmd; + struct luo_session_header *sh = NULL; int err; - guard(mutex)(&session->mutex); - err = luo_preserve_file(&session->file_set, argp->token, argp->fd); + if (!session->retrieved) { + sh = &luo_session_global.outgoing; + down_read(&sh->rwsem); + if (READ_ONCE(sh->rebooting)) { + up_read(&sh->rwsem); + return -EBUSY; + } + } + + scoped_guard(mutex, &session->mutex) + err = luo_preserve_file(&session->file_set, argp->token, argp->fd); + if (sh) + up_read(&sh->rwsem); if (err) return err; @@ -578,6 +619,10 @@ int luo_session_serialize(void) int err; guard(rwsem_write)(&sh->rwsem); + if (READ_ONCE(sh->rebooting)) + return -EBUSY; + + WRITE_ONCE(sh->rebooting, true); list_for_each_entry(session, &sh->list, list) { err = luo_session_freeze_one(session, &sh->ser[i]); if (err) @@ -595,8 +640,11 @@ int luo_session_serialize(void) list_for_each_entry_continue_reverse(session, &sh->list, list) { i--; luo_session_unfreeze_one(session, &sh->ser[i]); - memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name)); + memset(&sh->ser[i], 0, sizeof(sh->ser[i])); } + sh->header_ser->count = 0; + /* Reset rebooting flag on serialization failure. */ + luo_session_reboot_done(sh); return err; } -- 2.53.0