From nobody Sat Jun 20 13:07:26 2026 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 798AC2E4257 for ; Tue, 14 Apr 2026 20:02:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196964; cv=none; b=nQKvAuvztRafG39J+fx6eYecu7X+3BXiOEVqZqRh+0saKmf7+Z5WbABGfAVQFnATZ6zN4e/w84jh2+LTTdvn/0l5U1I41GSF5CeO0cdTyB4JnyLkpDVoPRY7G0DJC5JYXqXzjiDFOqkXU5PSRlFheT7TtJvozgo5ZMcZsuIcdM8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196964; c=relaxed/simple; bh=4NsrRcNXwpIN9QF/D7B0lRo8GDtfy4hpZQDlNK3/VWY=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rdn5X6B6o2WTw9/lPolY1N9gVmY4KDUZ3RkysKu0Tiu0DXs2IzyJgOxNftXEB7gly71JoZmZmNDNzBOi9EtR1O0i0y+NdBhEcWdtFV4I1vsbcf3w4757zf96hOc25i0gQTT9IRZyfQRsKQDvcZAm8HIL/NbT168vWPUBjDPupHo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=Blu4L0hA; arc=none smtp.client-ip=209.85.219.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="Blu4L0hA" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-8a4b8c3a30bso68710266d6.3 for ; Tue, 14 Apr 2026 13:02:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1776196961; x=1776801761; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=LCjDlIp8tcNOYvfHKmC0ZdWTLeU0ejjgXIXAdGPq7KQ=; b=Blu4L0hAw+o+FKNkam/AwSwRgoZONVSCli9JA+55ogBk+lLy9HkponCOmU9xV5S2DD TgAi0pNF2gpTEtu9ex3Hk2Ap5ng0zoXICItZAoyOI7wfZbgiBZarPbooXD+OW9E2Y9Km VG1bqBzEHSQSrfMb3fyGnfas5gTP544AZrIjWmXtLhFAgnAQSl5+O8bCCrDJa6AxX/bv 5J0siBIYfmo4pXxcfnl00TZbR9T1n5d/h3scBMIpE8J4oz+ioTzFO6BLCyQvCrQl6W4s qF33FlCg6XvovDZ5O2L+tcslfzOVEvMGGxpA/rwlUmOryj5LfzJw1hrQgbeWw2VxKhnj dmgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776196961; x=1776801761; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LCjDlIp8tcNOYvfHKmC0ZdWTLeU0ejjgXIXAdGPq7KQ=; b=XnCTSfECD8CtYAjXkbdkPvGgxHtronwqvnIw2WHTwZE5bIbVPlGPFU7VzYXSkwjmKt dmqVzzNFIeMwG2U0jpUV5HH1SewiQN6agHJFDvDb1f12rQwdYCD+mch/IeHTn7bbygxb 616qkcPPRyIjlO2/DK5T25KnPcyt7jvnysTFAvCRFh4fyJSypmHR5vkyDpfuq4DJBa/4 d/7WvfvxqaUi/3DGP/efhEOOEIRkJ/QpEmpALYbW2gHo3l5Bb0DWl7Toabpg0NkqgfNe fL/LqoX7cZA4/DvS/RvRsj1IB/DQR/oPN1yXtsMLJnWuc/vy7nVzVxdDEbpPtBwpfG41 +GXA== X-Forwarded-Encrypted: i=1; AFNElJ+4rVKezwn5ZeuYnSjKpxpzYA8o40ukYahAGCWgcTyc+nsktKqfIFQUeorh4flxNzIxwK09066c5l+9jss=@vger.kernel.org X-Gm-Message-State: AOJu0YyDZhzCBfQD5Hwz9Q2OLy0bk4j90PFWILzGrBuhWBVgRw9RfDD1 CHgD7D5Znp5dltvbkffSZ6nlJ5BxTlBFqxu7MPmNPR/v48KGOjmkvzKz0OfZOi8QXSg= X-Gm-Gg: AeBDies9NztPD8tjqXQd46zoynDSohFkrGB5CH7R/QqvZlcm41JtWTZoW2LoSxDsefI 7LVMMyHowUSPBDrILfCnsllTTeGzqHORAao2InsgRVcaCXZO+POHT4ah3WnsAYXgCqtghUidUlP gypgUKM/TK+y0xlqwll0xzza/TzRzE22D6TvdjCVyQPozkl+w9KBGsiaUG9qjtozi+CuelBhZMS B7OSZKZdKEgnVN8Y8WdpsIR0WGnyDqL4cWhZP5sqTUkOU2S9dTAe4oi3RN7f2jtv1QqxMSDKk7b toD75O1g0r10teS/Uebz5K5NR+5IhqpNNKfraGPCwVjmf5451gY0/IeJJ7hOdLPRcfbyHEroNL4 +BZcMj68ol3eYes4BpGiCKquvdeQPM14rTldgAOAGTTNplwiKhxGGxulJZj50qq13/6DafFpFgu cBtCXQW3vFLlH6RV5xN5cbvlZv12GtXJ345WSefWxiNOTdnf2TttGeDBWZLVnWZTAChw== X-Received: by 2002:ad4:5966:0:b0:8ac:b738:45dd with SMTP id 6a1803df08f44-8acb7384f03mr120301296d6.34.1776196961105; Tue, 14 Apr 2026 13:02:41 -0700 (PDT) Received: from plex.localdomain ([71.181.43.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ac84a180a2sm132213656d6.15.2026.04.14.13.02.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Apr 2026 13:02:40 -0700 (PDT) From: Pasha Tatashin To: linux-kselftest@vger.kernel.org, rppt@kernel.org, shuah@kernel.org, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, pasha.tatashin@soleen.com, dmatlack@google.com, kexec@lists.infradead.org, pratyush@kernel.org, skhawaja@google.com, graf@amazon.com Subject: [PATCH 1/5] liveupdate: Remove limit on the number of sessions Date: Tue, 14 Apr 2026 20:02:33 +0000 Message-ID: <20260414200237.444170-2-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414200237.444170-1-pasha.tatashin@soleen.com> References: <20260414200237.444170-1-pasha.tatashin@soleen.com> 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 Content-Type: text/plain; charset="utf-8" Currently, the number of LUO sessions is limited by a fixed number of pre-allocated pages for serialization (16 pages, allowing for ~819 sessions). This limitation is problematic if LUO is used to support things such as systemd file descriptor store, and would be used not just as VM memory but to save other states on the machine. Remove this limit by transitioning to a linked-block approach for session metadata serialization. Instead of a single contiguous block, session metadata is now stored in a chain of 16-page blocks. Each block starts with a header containing the physical address of the next block and the number of session entries in the current block. - Bump session ABI version to v3. - Update struct luo_session_header_ser to include a 'next' pointer. - Implement dynamic block allocation in luo_session_insert(). - Update setup, serialization, and deserialization logic to traverse the block chain. - Remove LUO_SESSION_MAX limit. Signed-off-by: Pasha Tatashin --- include/linux/kho/abi/luo.h | 19 +-- kernel/liveupdate/luo_internal.h | 12 +- kernel/liveupdate/luo_session.c | 237 +++++++++++++++++++++++-------- 3 files changed, 197 insertions(+), 71 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index 46750a0ddf88..f5732958545e 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -57,9 +57,10 @@ * - compatible: "luo-session-v1" * Identifies the session ABI version. * - luo-session-header: u64 - * The physical address of a `struct luo_session_header_ser`. This str= ucture - * is the header for a contiguous block of memory containing an array = of - * `struct luo_session_ser`, one for each preserved session. + * The physical address of the first `struct luo_session_header_ser`. + * This structure is the header for a block of memory containing an ar= ray + * of `struct luo_session_ser` entries. Multiple blocks are linked via + * the `next` field in the header. * * File-Lifecycle-Bound Node (luo-flb): * This node describes all preserved global objects whose lifecycle is b= ound @@ -77,9 +78,9 @@ * `__packed` structures. These structures contain the actual preserved = state. * * - struct luo_session_header_ser: - * Header for the session array. Contains the total page count of the - * preserved memory block and the number of `struct luo_session_ser` - * entries that follow. + * Header for the session data block. Contains the physical address of= the + * next session data block and the number of `struct luo_session_ser` + * entries that follow this header in the current block. * * - struct luo_session_ser: * Metadata for a single session, including its name and a physical po= inter @@ -153,21 +154,23 @@ struct luo_file_set_ser { * luo_session_header_ser */ #define LUO_FDT_SESSION_NODE_NAME "luo-session" -#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v2" +#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v3" #define LUO_FDT_SESSION_HEADER "luo-session-header" =20 /** * struct luo_session_header_ser - Header for the serialized session data = block. + * @next: Physical address of the next struct luo_session_header_ser. * @count: The number of `struct luo_session_ser` entries that immediately * follow this header in the memory block. * - * This structure is located at the beginning of a contiguous block of + * This structure is located at the beginning of a block of * physical memory preserved across the kexec. It provides the necessary * metadata to interpret the array of session entries that follow. * * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be upd= ated. */ struct luo_session_header_ser { + u64 next; u64 count; } __packed; =20 diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index 875844d7a41d..a73f42069301 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -11,6 +11,16 @@ #include #include =20 +/* + * Safeguard limit for the number of serialization blocks. This is used to + * prevent infinite loops and excessive memory allocation in case of memory + * corruption in the preserved state. + * + * This limit allows for ~8.1 million sessions and ~1.2 million files per + * session, which is more than enough for all realistic use cases. + */ +#define LUO_MAX_BLOCKS 10000 + struct luo_ucmd { void __user *ubuffer; u32 user_size; @@ -59,7 +69,6 @@ struct luo_file_set { * struct luo_session - Represents an active or incoming Live Update sessi= on. * @name: A unique name for this session, used for identification and * retrieval. - * @ser: Pointer to the serialized data for this session. * @list: A list_head member used to link this session into a global= list * of either outgoing (to be preserved) or incoming (restored= from * previous kernel) sessions. @@ -70,7 +79,6 @@ struct luo_file_set { */ struct luo_session { char name[LIVEUPDATE_SESSION_NAME_LENGTH]; - struct luo_session_ser *ser; struct list_head list; bool retrieved; struct luo_file_set file_set; diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_sessio= n.c index 92b1af791889..007ca34eba79 100644 --- a/kernel/liveupdate/luo_session.c +++ b/kernel/liveupdate/luo_session.c @@ -69,30 +69,39 @@ #include #include "luo_internal.h" =20 -/* 16 4K pages, give space for 744 sessions */ +/* 16 4K pages, give space for 819 sessions per block */ #define LUO_SESSION_PGCNT 16ul -#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ +#define LUO_SESSION_BLOCK_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \ sizeof(struct luo_session_header_ser)) / \ sizeof(struct luo_session_ser)) =20 +/** + * struct luo_session_block - Internal representation of a session seriali= zation block. + * @list: List head for linking blocks in memory. + * @ser: Pointer to the serialized header in preserved memory. + */ +struct luo_session_block { + struct list_head list; + struct luo_session_header_ser *ser; +}; + /** * struct luo_session_header - Header struct for managing LUO sessions. * @count: The number of sessions currently tracked in the @list. + * @nblocks: The number of allocated serialization blocks. * @list: The head of the linked list of `struct luo_session` instan= ces. * @rwsem: A read-write semaphore providing synchronized access to the * session list and other fields in this structure. - * @header_ser: The header data of serialization array. - * @ser: The serialized session data (an array of - * `struct luo_session_ser`). + * @blocks: The list of serialization blocks (struct luo_session_block= ). * @active: Set to true when first initialized. If previous kernel did= not * send session data, active stays false for incoming. */ struct luo_session_header { long count; + long nblocks; struct list_head list; struct rw_semaphore rwsem; - struct luo_session_header_ser *header_ser; - struct luo_session_ser *ser; + struct list_head blocks; bool active; }; =20 @@ -110,10 +119,12 @@ static struct luo_session_global luo_session_global = =3D { .incoming =3D { .list =3D LIST_HEAD_INIT(luo_session_global.incoming.list), .rwsem =3D __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem), + .blocks =3D LIST_HEAD_INIT(luo_session_global.incoming.blocks), }, .outgoing =3D { .list =3D LIST_HEAD_INIT(luo_session_global.outgoing.list), .rwsem =3D __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem), + .blocks =3D LIST_HEAD_INIT(luo_session_global.outgoing.blocks), }, }; =20 @@ -140,6 +151,70 @@ static void luo_session_free(struct luo_session *sessi= on) kfree(session); } =20 +static int luo_session_add_block(struct luo_session_header *sh, + struct luo_session_header_ser *ser) +{ + struct luo_session_block *block; + + if (sh->nblocks >=3D LUO_MAX_BLOCKS) + return -ENOSPC; + + block =3D kzalloc_obj(*block); + if (!block) + return -ENOMEM; + + block->ser =3D ser; + list_add_tail(&block->list, &sh->blocks); + sh->nblocks++; + + return 0; +} + +static int luo_session_create_ser_block(struct luo_session_header *sh) +{ + struct luo_session_block *last =3D NULL; + struct luo_session_header_ser *ser; + int err; + + ser =3D kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); + if (IS_ERR(ser)) + return PTR_ERR(ser); + + if (!list_empty(&sh->blocks)) + last =3D list_last_entry(&sh->blocks, struct luo_session_block, list); + + err =3D luo_session_add_block(sh, ser); + if (err) + goto err_unpreserve; + + if (last) + last->ser->next =3D virt_to_phys(ser); + + return 0; + +err_unpreserve: + kho_unpreserve_free(ser); + return err; +} + +static void luo_session_destroy_ser_blocks(struct luo_session_header *sh, + bool unpreserve) +{ + struct luo_session_block *block, *tmp; + + list_for_each_entry_safe(block, tmp, &sh->blocks, list) { + if (block->ser) { + if (unpreserve) + kho_unpreserve_free(block->ser); + else + kho_restore_free(block->ser); + } + list_del(&block->list); + kfree(block); + sh->nblocks--; + } +} + static int luo_session_insert(struct luo_session_header *sh, struct luo_session *session) { @@ -147,15 +222,6 @@ static int luo_session_insert(struct luo_session_heade= r *sh, =20 guard(rwsem_write)(&sh->rwsem); =20 - /* - * For outgoing we should make sure there is room in serialization array - * for new session. - */ - if (sh =3D=3D &luo_session_global.outgoing) { - if (sh->count =3D=3D LUO_SESSION_MAX) - return -ENOMEM; - } - /* * For small number of sessions this loop won't hurt performance * but if we ever start using a lot of sessions, this might @@ -166,6 +232,20 @@ static int luo_session_insert(struct luo_session_heade= r *sh, if (!strncmp(it->name, session->name, sizeof(it->name))) return -EEXIST; } + + /* + * For outgoing we should make sure there is room in serialization array + * for new session. If not, allocate a new block. + */ + if (sh =3D=3D &luo_session_global.outgoing) { + if (sh->count =3D=3D sh->nblocks * LUO_SESSION_BLOCK_MAX) { + int err =3D luo_session_create_ser_block(sh); + + if (err) + return err; + } + } + list_add_tail(&session->list, &sh->list); sh->count++; =20 @@ -444,9 +524,12 @@ int __init luo_session_setup_outgoing(void *fdt_out) u64 header_ser_pa; int err; =20 - header_ser =3D kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT); - if (IS_ERR(header_ser)) - return PTR_ERR(header_ser); + err =3D luo_session_create_ser_block(&luo_session_global.outgoing); + if (err) + return err; + + header_ser =3D list_first_entry(&luo_session_global.outgoing.blocks, + struct luo_session_block, list)->ser; header_ser_pa =3D virt_to_phys(header_ser); =20 err =3D fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME); @@ -459,19 +542,18 @@ int __init luo_session_setup_outgoing(void *fdt_out) if (err) goto err_unpreserve; =20 - luo_session_global.outgoing.header_ser =3D header_ser; - luo_session_global.outgoing.ser =3D (void *)(header_ser + 1); luo_session_global.outgoing.active =3D true; =20 return 0; =20 err_unpreserve: - kho_unpreserve_free(header_ser); + luo_session_destroy_ser_blocks(&luo_session_global.outgoing, true); return err; } =20 int __init luo_session_setup_incoming(void *fdt_in) { + struct luo_session_header *sh =3D &luo_session_global.incoming; struct luo_session_header_ser *header_ser; int err, header_size, offset; u64 header_ser_pa; @@ -501,11 +583,14 @@ int __init luo_session_setup_incoming(void *fdt_in) } =20 header_ser_pa =3D get_unaligned((u64 *)ptr); - header_ser =3D phys_to_virt(header_ser_pa); - - 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; + while (header_ser_pa) { + header_ser =3D phys_to_virt(header_ser_pa); + err =3D luo_session_add_block(sh, header_ser); + if (err) + return err; + header_ser_pa =3D header_ser->next; + } + sh->active =3D true; =20 return 0; } @@ -513,6 +598,7 @@ int __init luo_session_setup_incoming(void *fdt_in) int luo_session_deserialize(void) { struct luo_session_header *sh =3D &luo_session_global.incoming; + struct luo_session_block *block; static bool is_deserialized; static int err; =20 @@ -539,40 +625,49 @@ int luo_session_deserialize(void) * userspace to detect the failure and trigger a reboot, which will * reliably reset devices and reclaim memory. */ - for (int i =3D 0; i < sh->header_ser->count; i++) { - struct luo_session *session; - - session =3D luo_session_alloc(sh->ser[i].name); - if (IS_ERR(session)) { - pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n= ", - (int)sizeof(sh->ser[i].name), - sh->ser[i].name, session); - err =3D PTR_ERR(session); - return err; - } + list_for_each_entry(block, &sh->blocks, list) { + struct luo_session_ser *ser =3D (void *)(block->ser + 1); =20 - err =3D luo_session_insert(sh, session); - if (err) { - pr_warn("Failed to insert session [%s] %pe\n", - session->name, ERR_PTR(err)); - luo_session_free(session); + if (block->ser->count > LUO_SESSION_BLOCK_MAX) { + pr_warn("Session block contains too many entries: %llu\n", + block->ser->count); + err =3D -EINVAL; return err; } =20 - scoped_guard(mutex, &session->mutex) { - err =3D luo_file_deserialize(&session->file_set, - &sh->ser[i].file_set_ser); - } - if (err) { - pr_warn("Failed to deserialize files for session [%s] %pe\n", - session->name, ERR_PTR(err)); - return err; + for (int i =3D 0; i < block->ser->count; i++) { + struct luo_session *session; + + session =3D luo_session_alloc(ser[i].name); + if (IS_ERR(session)) { + pr_warn("Failed to allocate session [%.*s] during deserialization %pe\= n", + (int)sizeof(ser[i].name), + ser[i].name, session); + err =3D PTR_ERR(session); + return err; + } + + err =3D luo_session_insert(sh, session); + if (err) { + pr_warn("Failed to insert session [%s] %pe\n", + session->name, ERR_PTR(err)); + luo_session_free(session); + return err; + } + + scoped_guard(mutex, &session->mutex) { + err =3D luo_file_deserialize(&session->file_set, + &ser[i].file_set_ser); + } + if (err) { + pr_warn("Failed to deserialize files for session [%s] %pe\n", + session->name, ERR_PTR(err)); + return err; + } } } =20 - kho_restore_free(sh->header_ser); - sh->header_ser =3D NULL; - sh->ser =3D NULL; + luo_session_destroy_ser_blocks(sh, false); =20 return 0; } @@ -580,31 +675,51 @@ int luo_session_deserialize(void) int luo_session_serialize(void) { struct luo_session_header *sh =3D &luo_session_global.outgoing; + struct luo_session_block *block; struct luo_session *session; + struct luo_session_ser *ser; int i =3D 0; int err; =20 guard(rwsem_write)(&sh->rwsem); + + if (list_empty(&sh->blocks)) + return 0; + + block =3D list_first_entry(&sh->blocks, struct luo_session_block, list); + ser =3D (void *)(block->ser + 1); + list_for_each_entry(session, &sh->list, list) { - err =3D luo_session_freeze_one(session, &sh->ser[i]); + if (i =3D=3D LUO_SESSION_BLOCK_MAX) { + block->ser->count =3D i; + block =3D list_next_entry(block, list); + ser =3D (void *)(block->ser + 1); + i =3D 0; + } + + err =3D luo_session_freeze_one(session, &ser[i]); if (err) goto err_undo; =20 - strscpy(sh->ser[i].name, session->name, - sizeof(sh->ser[i].name)); + strscpy(ser[i].name, session->name, + sizeof(ser[i].name)); i++; } - sh->header_ser->count =3D sh->count; + block->ser->count =3D i; =20 return 0; =20 err_undo: 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)); + if (i < 0) { + block =3D list_prev_entry(block, list); + ser =3D (void *)(block->ser + 1); + i =3D LUO_SESSION_BLOCK_MAX - 1; + } + luo_session_unfreeze_one(session, &ser[i]); + memset(ser[i].name, 0, sizeof(ser[i].name)); } =20 return err; } - --=20 2.43.0 From nobody Sat Jun 20 13:07:26 2026 Received: from mail-vs1-f53.google.com (mail-vs1-f53.google.com [209.85.217.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDA3B314D07 for ; Tue, 14 Apr 2026 20:02:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.217.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196965; cv=none; b=Bx5duMxrthx7ct2rHPywikKsuV5aUrtrTr6Ju1MUXLt3oaQYzbWNhqnEOSfVBOEEPKht1bUl590YlLJwRYptWaC/ID5bjwlNOTTk2nW6y/NrmAPIy476wt2kLpn225l6tqQQ9VmRVIKeavcJx2FantBcwqiyqHNBS8qwkiMbFA4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196965; c=relaxed/simple; bh=MBaNGpIUuUbeKrFAommYLCRpBo7Nxzr/DpXBchJIpvo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=m2JdE20ElA4obFAdscffn43wL6XBXzpnFpoof8TNnTIzrcUKq4R5eYST4qab19/RPMIlEGHZHNCwdI5kriQRVgyAcSwH4QugyIaZ/WxhxDK6FCk/rVWlDV4K5iTGVqZtI22CYmNcLVXAt37JWbUCDv1BHDz4o3zD4N2XmHxji/4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=AXKjsaTj; arc=none smtp.client-ip=209.85.217.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="AXKjsaTj" Received: by mail-vs1-f53.google.com with SMTP id ada2fe7eead31-605def5b800so3865653137.0 for ; Tue, 14 Apr 2026 13:02:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1776196963; x=1776801763; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=gGBPIgM/WV5ZDiKpyCtZbuV6OrCUIPCWn13n/Yh3SQE=; b=AXKjsaTjCGsdZWqpS4MZqlInXSPJuoQrEBxe5k/XH7nL6Qjqn+D7KVItJD+x0hk3gN lrFy0AVtCRrjvDiKyD45XFdRyrp/NdtWjAXNwbvbdAjFR6ZAhfqdaCb29r3M2G/hEIHH i0OtQZqQ0FakPikiPrnYpPMLEsoW/mcwd7XBYnx1NhrJnHIZ3yZtQcnQ72Uol+sb785I eiBPkB06iByaFrtkE7SLVvm67Wje4CGUxDGjpI14B4T1gs93bdF/2xBOFVV/iU1oODzK 3yscfe3hCdWSTJ/D4XZ345jRhAX50acTSRIAunp8GHLg0lSITjgcxc3jJ7MRsQSBn7zE EEHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776196963; x=1776801763; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=gGBPIgM/WV5ZDiKpyCtZbuV6OrCUIPCWn13n/Yh3SQE=; b=kLz1dl/0NFT0J23cGuVYpqkDBdNv07eDbaYk6pNxlhQWXHtG8CHyCEBsKs6Mcbd1vB 9W8cdJMolpksUAmE6InEyXZS2zIlo/tejDYF0iPiaEJhFJ3uS6JQoE7TFjdnfZSW5813 oNKYk7L57LyCiERXYR09rMCAv8MbjQXpaLJv7BWY7A6mir5CYw0cDrZgYhnsYLttNuR6 9IEHcF6tLV/xY89wbClH1pnoZXe2LFeDSAOhtTiyDMQw0CEP3drRk6XCPd6o0RwtfCpa AeAzau7wzjD85VJDm43g6rjXOUXcsibwQVnyWjyJekGkbbrorGby4cI21/5khCaN49fU kjVw== X-Forwarded-Encrypted: i=1; AFNElJ/QBfGO+DdN+vRiZbmuEuiLUXVQVUGs4D/M4iiDN9124ZNplH+Y1YJ1ksx7WgTRaGbbLZYflSl/vIq8+i0=@vger.kernel.org X-Gm-Message-State: AOJu0YzjbgVqFTZK5LvSA+RyOHWpWb90hKRxLEP1Gic1fkVpfmlse8bE lUQ7ItIgusaVc4RNLcTH466XrZHrLlTiqTyU2WKatcrol8aS7FjT+yQ0EBDAgdmc8zA= X-Gm-Gg: AeBDievYek+kwSzhKACGTnYreZPip6VXyxHJhFbVWk0rEbVtaye7N60WUpmW70yXUaB JklrqpY0N5HB6Mevgfq9q9aFXGq1wF7JEmHqDMmG9CSG/4mwYCPofPgF8IdYHtf/AFKqc2DpuUE Uu13Pn+wO2VV4AstflcKG0LyTcD6vgbxEVD323dPRjeAYoJmx9NNkCS7BM7WRvJXmaVtE9mDWXB 1hsWIEcHa3EktXD+v2stGK0n1JRbMC/6J9y7/ohXiW8uXhmLYDPOneSIQIo6rDIaSbjV8vdpP6p sW63pjid1mOUHI4nfG58NGId/oj4tyHHxwJ1jw8DWkh8X0VGLw7bPhRMaR1uScxkWgMaSIfLj+L G13+/+DyEjaVlCVVQvVfC4HjpJ+ppi/dd4enZAUk9IjXasFJ6TAOQzuhKsLalS6tUcqjPOToSJj I8qX9i3jbAlrYrvNzMjuj3o6B5/iNzD+dnPOof+9os6leHzt6MUVV73ePmydVvZp+t3CXm0zT9Q 1KQ X-Received: by 2002:a05:6102:5043:b0:604:ee6f:17d with SMTP id ada2fe7eead31-609feaaaca6mr8735682137.8.1776196962686; Tue, 14 Apr 2026 13:02:42 -0700 (PDT) Received: from plex.localdomain ([71.181.43.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ac84a180a2sm132213656d6.15.2026.04.14.13.02.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Apr 2026 13:02:42 -0700 (PDT) From: Pasha Tatashin To: linux-kselftest@vger.kernel.org, rppt@kernel.org, shuah@kernel.org, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, pasha.tatashin@soleen.com, dmatlack@google.com, kexec@lists.infradead.org, pratyush@kernel.org, skhawaja@google.com, graf@amazon.com Subject: [PATCH 2/5] liveupdate: Remove limit on the number of files per session Date: Tue, 14 Apr 2026 20:02:34 +0000 Message-ID: <20260414200237.444170-3-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414200237.444170-1-pasha.tatashin@soleen.com> References: <20260414200237.444170-1-pasha.tatashin@soleen.com> 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 Content-Type: text/plain; charset="utf-8" To remove the fixed limit on the number of preserved files per session, transition the file metadata serialization from a single contiguous memory block to a chain of linked blocks. ABI Changes: - Define 'struct luo_file_header_ser' to manage linked blocks in preserved memory. - Update 'struct luo_file_set_ser' to point to the head of the block chain. - Bump the session ABI version to 'luo-session-v4' to reflect these breaking changes. Signed-off-by: Pasha Tatashin --- include/linux/kho/abi/luo.h | 35 ++++- kernel/liveupdate/luo_file.c | 226 ++++++++++++++++++++----------- kernel/liveupdate/luo_internal.h | 17 ++- 3 files changed, 189 insertions(+), 89 deletions(-) diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h index f5732958545e..a15f6a0f3d93 100644 --- a/include/linux/kho/abi/luo.h +++ b/include/linux/kho/abi/luo.h @@ -84,8 +84,13 @@ * * - struct luo_session_ser: * Metadata for a single session, including its name and a physical po= inter - * to another preserved memory block containing an array of - * `struct luo_file_ser` for all files in that session. + * to the first `struct luo_file_header_ser` for all files in that ses= sion. + * Multiple blocks are linked via the `next` field in the header. + * + * - struct luo_file_header_ser: + * Header for the file data block. Contains the physical address of the + * next file data block and the number of `struct luo_file_ser` entries + * that follow this header in the current block. * * - struct luo_file_ser: * Metadata for a single preserved file. Contains the `compatible` str= ing to @@ -134,11 +139,29 @@ struct luo_file_ser { u64 token; } __packed; =20 +/** + * struct luo_file_header_ser - Header for the serialized file data block. + * @next: Physical address of the next struct luo_file_header_ser. + * @count: The number of `struct luo_file_ser` entries that immediately + * follow this header in the memory block. + * + * This structure is located at the beginning of a block of + * physical memory preserved across the kexec. It provides the necessary + * metadata to interpret the array of file entries that follow. + * + * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be upd= ated. + */ +struct luo_file_header_ser { + u64 next; + u64 count; +} __packed; + /** * struct luo_file_set_ser - Represents the serialized metadata for file s= et - * @files: The physical address of a contiguous memory block that holds - * the serialized state of files (array of luo_file_ser) in this= file - * set. + * @files: The physical address of the first `struct luo_file_header_ser= `. + * This structure is the header for a block of memory containing + * an array of `struct luo_file_ser` entries. Multiple blocks are + * linked via the `next` field in the header. * @count: The total number of files that were part of this session duri= ng * serialization. Used for iteration and validation during * restoration. @@ -154,7 +177,7 @@ struct luo_file_set_ser { * luo_session_header_ser */ #define LUO_FDT_SESSION_NODE_NAME "luo-session" -#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v3" +#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v4" #define LUO_FDT_SESSION_HEADER "luo-session-header" =20 /** diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c index dedbb8d02e6a..20a2d776cff4 100644 --- a/kernel/liveupdate/luo_file.c +++ b/kernel/liveupdate/luo_file.c @@ -119,10 +119,12 @@ static LIST_HEAD(luo_file_handler_list); /* Keep track of files being preserved by LUO */ static DEFINE_XARRAY(luo_preserved_files); =20 -/* 2 4K pages, give space for 128 files per file_set */ +/* 2 4K pages, give space for 127 files per block */ #define LUO_FILE_PGCNT 2ul -#define LUO_FILE_MAX \ - ((LUO_FILE_PGCNT << PAGE_SHIFT) / sizeof(struct luo_file_ser)) +#define LUO_FILE_BLOCK_MAX \ + (((LUO_FILE_PGCNT << PAGE_SHIFT) - \ + sizeof(struct luo_file_header_ser)) / \ + sizeof(struct luo_file_ser)) =20 /** * struct luo_file - Represents a single preserved file instance. @@ -158,7 +160,7 @@ static DEFINE_XARRAY(luo_preserved_files); * and the serialized state handle returned by the handler's .preserve() * operation. * - * These instances are tracked in a per-file_set list. The @serialized_data + * These structures are tracked in a per-file_set list. The @serialized_da= ta * field, which holds a handle to the file's serialized state, may be upda= ted * during the .freeze() callback before being serialized for the next kern= el. * After reboot, these structures are recreated by luo_file_deserialize() = and @@ -175,37 +177,68 @@ struct luo_file { u64 token; }; =20 -static int luo_alloc_files_mem(struct luo_file_set *file_set) +static int luo_file_add_block(struct luo_file_set *file_set, + struct luo_file_header_ser *ser) { - size_t size; - void *mem; + struct luo_file_block *block; =20 - if (file_set->files) - return 0; - - WARN_ON_ONCE(file_set->count); + if (file_set->nblocks >=3D LUO_MAX_BLOCKS) + return -ENOSPC; =20 - size =3D LUO_FILE_PGCNT << PAGE_SHIFT; - mem =3D kho_alloc_preserve(size); - if (IS_ERR(mem)) - return PTR_ERR(mem); + block =3D kzalloc_obj(*block); + if (!block) + return -ENOMEM; =20 - file_set->files =3D mem; + block->ser =3D ser; + list_add_tail(&block->list, &file_set->blocks); + file_set->nblocks++; =20 return 0; } =20 -static void luo_free_files_mem(struct luo_file_set *file_set) +static int luo_file_create_ser_block(struct luo_file_set *file_set) { - /* If file_set has files, no need to free preservation memory */ - if (file_set->count) - return; + struct luo_file_block *last =3D NULL; + struct luo_file_header_ser *ser; + int err; =20 - if (!file_set->files) - return; + ser =3D kho_alloc_preserve(LUO_FILE_PGCNT << PAGE_SHIFT); + if (IS_ERR(ser)) + return PTR_ERR(ser); + + if (!list_empty(&file_set->blocks)) + last =3D list_last_entry(&file_set->blocks, struct luo_file_block, list); + + err =3D luo_file_add_block(file_set, ser); + if (err) + goto err_unpreserve; + + if (last) + last->ser->next =3D virt_to_phys(ser); + + return 0; =20 - kho_unpreserve_free(file_set->files); - file_set->files =3D NULL; +err_unpreserve: + kho_unpreserve_free(ser); + return err; +} + +static void luo_file_destroy_ser_blocks(struct luo_file_set *file_set, + bool unpreserve) +{ + struct luo_file_block *block, *tmp; + + list_for_each_entry_safe(block, tmp, &file_set->blocks, list) { + if (block->ser) { + if (unpreserve) + kho_unpreserve_free(block->ser); + else + kho_restore_free(block->ser); + } + list_del(&block->list); + kfree(block); + file_set->nblocks--; + } } =20 static unsigned long luo_get_id(struct liveupdate_file_handler *fh, @@ -247,7 +280,9 @@ static bool luo_token_is_used(struct luo_file_set *file= _set, u64 token) * 3. Iterates through registered handlers, calling can_preserve() to find= one * compatible with the given @fd. * 4. Calls the handler's .preserve() operation, which saves the file's st= ate - * and returns an opaque private data handle. + * and returns an opaque u64 handle. This is typically performed while = the + * workload is still active to minimize the downtime during the + * actual reboot transition. * 5. Adds the new instance to the file_set's internal list. * * On success, LUO takes a reference to the 'struct file' and considers it @@ -277,17 +312,16 @@ int luo_preserve_file(struct luo_file_set *file_set, = u64 token, int fd) if (luo_token_is_used(file_set, token)) return -EEXIST; =20 - if (file_set->count =3D=3D LUO_FILE_MAX) - return -ENOSPC; + if (file_set->count =3D=3D file_set->nblocks * LUO_FILE_BLOCK_MAX) { + err =3D luo_file_create_ser_block(file_set); + if (err) + return err; + } =20 file =3D fget(fd); if (!file) return -EBADF; =20 - err =3D luo_alloc_files_mem(file_set); - if (err) - goto err_fput; - err =3D -ENOENT; down_read(&luo_register_rwlock); list_private_for_each_entry(fh, &luo_file_handler_list, list) { @@ -301,7 +335,7 @@ int luo_preserve_file(struct luo_file_set *file_set, u6= 4 token, int fd) =20 /* err is still -ENOENT if no handler was found */ if (err) - goto err_free_files_mem; + goto err_fput; =20 /* safe to use fh because its module is pinned */ err =3D xa_insert(&luo_preserved_files, luo_get_id(fh, file), @@ -345,8 +379,6 @@ int luo_preserve_file(struct luo_file_set *file_set, u6= 4 token, int fd) xa_erase(&luo_preserved_files, luo_get_id(fh, file)); err_module_put: module_put(fh->ops->owner); -err_free_files_mem: - luo_free_files_mem(file_set); err_fput: fput(file); =20 @@ -399,7 +431,7 @@ void luo_file_unpreserve_files(struct luo_file_set *fil= e_set) kfree(luo_file); } =20 - luo_free_files_mem(file_set); + luo_file_destroy_ser_blocks(file_set, true); } =20 static int luo_file_freeze_one(struct luo_file_set *file_set, @@ -446,6 +478,7 @@ static void __luo_file_unfreeze(struct luo_file_set *fi= le_set, struct luo_file *failed_entry) { struct list_head *files_list =3D &file_set->files_list; + struct luo_file_block *block; struct luo_file *luo_file; =20 list_for_each_entry(luo_file, files_list, list) { @@ -455,7 +488,11 @@ static void __luo_file_unfreeze(struct luo_file_set *f= ile_set, luo_file_unfreeze_one(file_set, luo_file); } =20 - memset(file_set->files, 0, LUO_FILE_PGCNT << PAGE_SHIFT); + list_for_each_entry(block, &file_set->blocks, list) { + block->ser->count =3D 0; + memset(block->ser + 1, 0, + (LUO_FILE_PGCNT << PAGE_SHIFT) - sizeof(*block->ser)); + } } =20 /** @@ -494,7 +531,8 @@ static void __luo_file_unfreeze(struct luo_file_set *fi= le_set, int luo_file_freeze(struct luo_file_set *file_set, struct luo_file_set_ser *file_set_ser) { - struct luo_file_ser *file_ser =3D file_set->files; + struct luo_file_block *block; + struct luo_file_ser *file_ser; struct luo_file *luo_file; int err; int i; @@ -502,11 +540,18 @@ int luo_file_freeze(struct luo_file_set *file_set, if (!file_set->count) return 0; =20 - if (WARN_ON(!file_ser)) - return -EINVAL; + block =3D list_first_entry(&file_set->blocks, struct luo_file_block, list= ); + file_ser =3D (void *)(block->ser + 1); =20 i =3D 0; list_for_each_entry(luo_file, &file_set->files_list, list) { + if (i =3D=3D LUO_FILE_BLOCK_MAX) { + block->ser->count =3D i; + block =3D list_next_entry(block, list); + file_ser =3D (void *)(block->ser + 1); + i =3D 0; + } + err =3D luo_file_freeze_one(file_set, luo_file); if (err < 0) { pr_warn("Freeze failed for token[%#0llx] handler[%s] err[%pe]\n", @@ -521,10 +566,13 @@ int luo_file_freeze(struct luo_file_set *file_set, file_ser[i].token =3D luo_file->token; i++; } + block->ser->count =3D i; =20 file_set_ser->count =3D file_set->count; - if (file_set->files) - file_set_ser->files =3D virt_to_phys(file_set->files); + if (!list_empty(&file_set->blocks)) { + block =3D list_first_entry(&file_set->blocks, struct luo_file_block, lis= t); + file_set_ser->files =3D virt_to_phys(block->ser); + } =20 return 0; =20 @@ -746,10 +794,7 @@ int luo_file_finish(struct luo_file_set *file_set) kfree(luo_file); } =20 - if (file_set->files) { - kho_restore_free(file_set->files); - file_set->files =3D NULL; - } + luo_file_destroy_ser_blocks(file_set, false); =20 return 0; } @@ -782,8 +827,10 @@ int luo_file_finish(struct luo_file_set *file_set) int luo_file_deserialize(struct luo_file_set *file_set, struct luo_file_set_ser *file_set_ser) { - struct luo_file_ser *file_ser; - u64 i; + struct luo_file_header_ser *header_ser; + struct luo_file_block *block; + u64 header_ser_pa; + int err; =20 if (!file_set_ser->files) { WARN_ON(file_set_ser->count); @@ -791,7 +838,15 @@ int luo_file_deserialize(struct luo_file_set *file_set, } =20 file_set->count =3D file_set_ser->count; - file_set->files =3D phys_to_virt(file_set_ser->files); + header_ser_pa =3D file_set_ser->files; + + while (header_ser_pa) { + header_ser =3D phys_to_virt(header_ser_pa); + err =3D luo_file_add_block(file_set, header_ser); + if (err) + return err; + header_ser_pa =3D header_ser->next; + } =20 /* * Note on error handling: @@ -808,42 +863,51 @@ int luo_file_deserialize(struct luo_file_set *file_se= t, * userspace to detect the failure and trigger a reboot, which will * reliably reset devices and reclaim memory. */ - file_ser =3D file_set->files; - for (i =3D 0; i < file_set->count; i++) { - struct liveupdate_file_handler *fh; - bool handler_found =3D false; - struct luo_file *luo_file; - - down_read(&luo_register_rwlock); - list_private_for_each_entry(fh, &luo_file_handler_list, list) { - if (!strcmp(fh->compatible, file_ser[i].compatible)) { - if (try_module_get(fh->ops->owner)) - handler_found =3D true; - break; - } - } - up_read(&luo_register_rwlock); + list_for_each_entry(block, &file_set->blocks, list) { + struct luo_file_ser *file_ser =3D (void *)(block->ser + 1); =20 - if (!handler_found) { - pr_warn("No registered handler for compatible '%.*s'\n", - (int)sizeof(file_ser[i].compatible), - file_ser[i].compatible); - return -ENOENT; + if (block->ser->count > LUO_FILE_BLOCK_MAX) { + pr_warn("File block contains too many entries: %llu\n", + block->ser->count); + return -EINVAL; } =20 - luo_file =3D kzalloc_obj(*luo_file); - if (!luo_file) { - module_put(fh->ops->owner); - return -ENOMEM; - } + for (int i =3D 0; i < block->ser->count; i++) { + struct liveupdate_file_handler *fh; + bool handler_found =3D false; + struct luo_file *luo_file; + + down_read(&luo_register_rwlock); + list_private_for_each_entry(fh, &luo_file_handler_list, list) { + if (!strcmp(fh->compatible, file_ser[i].compatible)) { + if (try_module_get(fh->ops->owner)) + handler_found =3D true; + break; + } + } + up_read(&luo_register_rwlock); =20 - /* safe to use fh because its module is pinned */ - luo_file->fh =3D fh; - luo_file->file =3D NULL; - luo_file->serialized_data =3D file_ser[i].data; - luo_file->token =3D file_ser[i].token; - mutex_init(&luo_file->mutex); - list_add_tail(&luo_file->list, &file_set->files_list); + if (!handler_found) { + pr_warn("No registered handler for compatible '%.*s'\n", + (int)sizeof(file_ser[i].compatible), + file_ser[i].compatible); + return -ENOENT; + } + + luo_file =3D kzalloc_obj(*luo_file); + if (!luo_file) { + module_put(fh->ops->owner); + return -ENOMEM; + } + + /* safe to use fh because its module is pinned */ + luo_file->fh =3D fh; + luo_file->file =3D NULL; + luo_file->serialized_data =3D file_ser[i].data; + luo_file->token =3D file_ser[i].token; + mutex_init(&luo_file->mutex); + list_add_tail(&luo_file->list, &file_set->files_list); + } } =20 return 0; @@ -852,12 +916,14 @@ int luo_file_deserialize(struct luo_file_set *file_se= t, void luo_file_set_init(struct luo_file_set *file_set) { INIT_LIST_HEAD(&file_set->files_list); + INIT_LIST_HEAD(&file_set->blocks); } =20 void luo_file_set_destroy(struct luo_file_set *file_set) { WARN_ON(file_set->count); WARN_ON(!list_empty(&file_set->files_list)); + WARN_ON(!list_empty(&file_set->blocks)); } =20 /** diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_inter= nal.h index a73f42069301..a92aaa9c073d 100644 --- a/kernel/liveupdate/luo_internal.h +++ b/kernel/liveupdate/luo_internal.h @@ -50,19 +50,30 @@ static inline int luo_ucmd_respond(struct luo_ucmd *ucm= d, */ #define luo_restore_fail(__fmt, ...) panic(__fmt, ##__VA_ARGS__) =20 +/** + * struct luo_file_block - Internal representation of a file serialization= block. + * @list: List head for linking blocks in memory. + * @ser: Pointer to the serialized header in preserved memory. + */ +struct luo_file_block { + struct list_head list; + struct luo_file_header_ser *ser; +}; + /** * struct luo_file_set - A set of files that belong to the same sessions. * @files_list: An ordered list of files associated with this session, it = is * ordered by preservation time. - * @files: The physically contiguous memory block that holds the seri= alized - * state of files. + * @blocks: The list of serialization blocks (struct luo_file_block). * @count: A counter tracking the number of files currently stored in= the * @files_list for this session. + * @nblocks: The number of allocated serialization blocks. */ struct luo_file_set { struct list_head files_list; - struct luo_file_ser *files; + struct list_head blocks; long count; + long nblocks; }; =20 /** --=20 2.43.0 From nobody Sat Jun 20 13:07:26 2026 Received: from mail-qv1-f47.google.com (mail-qv1-f47.google.com [209.85.219.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2AF8F23EAB2 for ; Tue, 14 Apr 2026 20:02:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196966; cv=none; b=Oq0md7wbzuRr1wjomRYOTf5IoFybjYBrA6AmCHCQ4YqRoXPCOnaHJm4oqJC/bOBEs97vCkP5byIpsQo2B+/D4JPpY5SJN9OQ+kFUlIhccpSB2tIOINQyUzMqXxAxSLKSix35Uf12jatWy34vMCtzM1ThE3NwAFESimVp1p6S4PA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196966; c=relaxed/simple; bh=G0DZF+0pwpWpvyjfgutOGcLoVHg/sZ7xURYEsVLHHAw=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RlemouA9CN0gEzIBpswpOOaIEeGYLeZK4FpF+/e3k71NQtxgumqHWlPgEtyvnjjZ+k5EU65oDL/8yIy1747pH7HaTZL++yd0DKn8BdduOyexGSZqAll8ZZQ4ve3FDDo6Zx2dcDxGUi14HTCPe4sFVgjq5RXZDM+djACPmQMlF8c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=QoTla27F; arc=none smtp.client-ip=209.85.219.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="QoTla27F" Received: by mail-qv1-f47.google.com with SMTP id 6a1803df08f44-8acb3daf2aaso31102966d6.0 for ; Tue, 14 Apr 2026 13:02:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1776196964; x=1776801764; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=yCXGOxyW+pw6N8ft4rnwW7MuGB9g0PHpMRF3XlVYG+8=; b=QoTla27FLB8i4Y1NsrqtWt9TkPk26c1KqXtTIJNEheA9kt946/z47uu27JHu8ZgDcQ dS5aSIWMDPPLxjnNZZ0yYen7wfr0xTnFv8YL7Fq+wyyj+koiI9HIzZwqVXE+iRgSdpPm 9FEDN2rmz2hYe/GVKBdCeK8P8/dJEnwMwFxlw1iq5tY+b5SKEVZsuQCdW+EDS0lhzIt2 zVAawEn1om5q8fZRud7CLCfHt9nR4N9OxG1r9yv0qXKwHs/3kq9yoFWXnv/2PIvu30RY NIDmbcTwTeOmCXvg0P36+kLiv3vkjgmeiyCbub5Bgi0WwMgzgYns6RYIs4ac4Y/O6lNu 0YjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776196964; x=1776801764; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=yCXGOxyW+pw6N8ft4rnwW7MuGB9g0PHpMRF3XlVYG+8=; b=Zs9mPM3+r/bteqARTdlLPCAXAM7zF495JZ+LM0ZYqXBHS1aHRlROu0vBTWRLw51dwR yu8NF0Ru2Ih6YZw70pbJ1PK34jNgWKG4CyjMBL2K5AAwoCvY0nS9D/RTKQKM96JAGiTt nta3wXq+utZrfg2IRCZcb9wS71Wgnn07W5az0qqEh/r0xZriDN7FPkB426CLvUSiFkwJ UIflN4UrUCD0VzDNIDV0aoMxaDxvYR6jXiXK359s2o0s5p0wMvalMQD6aCSfTb+nukTi XCMUldaz13b8VEsAEK/NyuSVQbiPhzoyIreEfLOFhcZr8Sp6eqgaYX4uBX5kl3sU0KRr rnWg== X-Forwarded-Encrypted: i=1; AFNElJ9WTeEBN7WRu8qs6UAMgeqplCi2XR/6IKjyyqFy9lhmEDyd4jn3XE9BfuGf0aAtEHmgmj+ZHDitmSys91U=@vger.kernel.org X-Gm-Message-State: AOJu0YybpS52pwvDXwwBkQgOce0cE2iLN73nMDaQbppuoBocsFPy8lZi O6WIAimEnKbW50KpaZe0ezE9/3UJATUvLDMwHxhSejbCocoHPnW6ghcVB752H1BCm44= X-Gm-Gg: AeBDievHZaFuxZg33Lk+B1sBGgICTyEpTll+rY4UuzCqQ7jaJCg3RqMe+DnJEHFvhPG +jNWUaBMTgdyW+tSDRLalJ9NLCNWD+IQjEYZz5vamkoCbnjL2k0feog1bL/v5F8B4dN2T+rtHzm NanZ4+iC6eI9X9/0DE7Nz4BJJTf9njsOFHh5Wwroej8gxhf/K9WMWuvBXQy3qxNg9X2iciu7Otz Hq3HkK98a5M6FuPSw6fFnSrvPhbhNeLC9dxGRgkVUzB+2w3c6XASqfG5tj3NdC0uN3ohXvd7kfI 3+6kRNU1EmBRy73UfGCeWLsEG6wSy1pXKdodYCsBFTyHfKksq/mO0l6m1djj+MiG3+dkTyg/Hza VN5NkOkzVPANQ/+Qdjp5+7OuWu9s4FH3c4jBz/wdHT48Az2ANbIqfcyhvdJaAMeHf61d0ItGGUF Mc7c59w8ZZpOkD991g3K2DuhdJY1qT6L6p+waIDRWiTLH+OE2oq4ipB1a5vbzU0tTedQ== X-Received: by 2002:a05:6214:f24:b0:89c:4cb3:4ea1 with SMTP id 6a1803df08f44-8ac861ef1bdmr302331696d6.20.1776196964120; Tue, 14 Apr 2026 13:02:44 -0700 (PDT) Received: from plex.localdomain ([71.181.43.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ac84a180a2sm132213656d6.15.2026.04.14.13.02.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Apr 2026 13:02:43 -0700 (PDT) From: Pasha Tatashin To: linux-kselftest@vger.kernel.org, rppt@kernel.org, shuah@kernel.org, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, pasha.tatashin@soleen.com, dmatlack@google.com, kexec@lists.infradead.org, pratyush@kernel.org, skhawaja@google.com, graf@amazon.com Subject: [PATCH 3/5] selftests/liveupdate: Test session and file limit removal Date: Tue, 14 Apr 2026 20:02:35 +0000 Message-ID: <20260414200237.444170-4-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414200237.444170-1-pasha.tatashin@soleen.com> References: <20260414200237.444170-1-pasha.tatashin@soleen.com> 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 Content-Type: text/plain; charset="utf-8" With the removal of static limits on the number of sessions and files per session, the orchestrator now uses dynamic allocation. Add new test cases to verify that the system can handle a large number of sessions and files. These tests ensure that the dynamic block allocation and reuse logic for session metadata and outgoing files work correctly beyond the previous static limits. Signed-off-by: Pasha Tatashin --- .../testing/selftests/liveupdate/liveupdate.c | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tools/testing/selftests/liveupdate/liveupdate.c b/tools/testin= g/selftests/liveupdate/liveupdate.c index 37c808fbe1e9..0eaf97b19267 100644 --- a/tools/testing/selftests/liveupdate/liveupdate.c +++ b/tools/testing/selftests/liveupdate/liveupdate.c @@ -22,6 +22,7 @@ #include #include #include +#include #include =20 #include @@ -386,4 +387,102 @@ TEST_F(liveupdate_device, prevent_double_preservation) ASSERT_EQ(close(session_fd2), 0); } =20 +static void ensure_nofile_limit(struct __test_metadata *_metadata, + long min_limit) +{ + struct rlimit hl; + + if (getrlimit(RLIMIT_NOFILE, &hl) < 0) + ksft_exit_fail_msg("getrlimit failed: %s\n", strerror(errno)); + + if (hl.rlim_cur >=3D min_limit) + return; + + hl.rlim_cur =3D min_limit; + if (hl.rlim_cur > hl.rlim_max) + hl.rlim_max =3D hl.rlim_cur; + + if (setrlimit(RLIMIT_NOFILE, &hl) < 0) { + if (errno =3D=3D EPERM) { + SKIP(return, "Insufficient privileges to set RLIMIT_NOFILE to %ld", + hl.rlim_cur); + } + ksft_exit_fail_msg("setrlimit to %ld failed: %s\n", + hl.rlim_cur, strerror(errno)); + } +} + +/* + * Test Case: Manage Many Sessions + * + * Verifies that a large number of sessions can be created and then + * destroyed during normal system operation. This specifically tests the + * dynamic block allocation and reuse logic for session metadata management + * without preserving any files. + */ +TEST_F(liveupdate_device, preserve_many_sessions) +{ +#define MANY_SESSIONS 2000 + int session_fds[MANY_SESSIONS]; + int i; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + ensure_nofile_limit(_metadata, MANY_SESSIONS + 10); + if (_metadata->exit_code =3D=3D KSFT_SKIP) + return; + + for (i =3D 0; i < MANY_SESSIONS; i++) { + char name[64]; + + snprintf(name, sizeof(name), "many-session-%d", i); + session_fds[i] =3D create_session(self->fd1, name); + ASSERT_GE(session_fds[i], 0); + } + + for (i =3D 0; i < MANY_SESSIONS; i++) + ASSERT_EQ(close(session_fds[i]), 0); +} + +/* + * Test Case: Preserve Many Files + * + * Verifies that a large number of files can be preserved in a single sess= ion + * and then destroyed during normal system operation. This tests the dynam= ic + * block allocation and management for outgoing files. + */ +TEST_F(liveupdate_device, preserve_many_files) +{ +#define MANY_FILES 500 + int mem_fds[MANY_FILES]; + int session_fd; + int i; + + self->fd1 =3D open(LIVEUPDATE_DEV, O_RDWR); + if (self->fd1 < 0 && errno =3D=3D ENOENT) + SKIP(return, "%s does not exist", LIVEUPDATE_DEV); + ASSERT_GE(self->fd1, 0); + + session_fd =3D create_session(self->fd1, "many-files-test"); + ASSERT_GE(session_fd, 0); + + ensure_nofile_limit(_metadata, MANY_FILES + 10); + if (_metadata->exit_code =3D=3D KSFT_SKIP) + return; + + for (i =3D 0; i < MANY_FILES; i++) { + mem_fds[i] =3D memfd_create("test-memfd", 0); + ASSERT_GE(mem_fds[i], 0); + ASSERT_EQ(preserve_fd(session_fd, mem_fds[i], i), 0); + } + + for (i =3D 0; i < MANY_FILES; i++) + ASSERT_EQ(close(mem_fds[i]), 0); + + ASSERT_EQ(close(session_fd), 0); +} + TEST_HARNESS_MAIN --=20 2.43.0 From nobody Sat Jun 20 13:07:26 2026 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F2C03191BA for ; Tue, 14 Apr 2026 20:02:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196968; cv=none; b=ZNKrzRzQf029BMuxm1tDV8+H/xoKsmppV/OthXRIS8ND72vpvn2ZYhStzfJPJMg6v/QuGtGWTweu1UIq+7mgsQZvhlKd9QlYHYEmwP3Hq37tJF3WuJvjnmNDM3DYotJZzNv7+S/ZuJb45nQgkD/+LGTmvwbNX98+1QBOj/O5ZcY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196968; c=relaxed/simple; bh=pjymxqiOCC0VYfpCLW/ulNpRB/Nr0nRJchJhF6Evfq8=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WLpnQqzW+0q/NDo+yfjD64VhY278ppEBGSbJKC4XLIkOLHNhoDvSnWVeArUTXyeC4XIvrzKZhBp5QauuRRk6MRefYinHmz+wyhz46BIdYH3CMKFtXXQKXcjUPRhJhoEhDQfIGLeKSRh32ZizCN0hA9VxbJSyeTIRc/dpFTSSgW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=BsRFOptD; arc=none smtp.client-ip=209.85.219.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="BsRFOptD" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-8aca0469204so31932796d6.2 for ; Tue, 14 Apr 2026 13:02:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1776196965; x=1776801765; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=ydncbOJwc9Epn5rxEKLGgwSNmxXWJlxhMwGYD93RLPE=; b=BsRFOptDjuUfcK3N55CmYT4AR5rnsxsB0/pdDe7Wdl+EFovfjDpNRbTNMpk/9gk3hQ IPeDaxDHj+YfX9LKaWc5pG/cQKNOWpkUOBuUWl24K6j1FyezuZm629HYxN/JII3Wh7Ra +MgRK0ZKX6gbOpgSLPPJOJvgBruMuLyZZORL0JawdgFeWjcm2HVc8nQe9AHcNGAR4NN6 Kr3UcUHilw7RGNzfO1kG0Yz+ow3uAa2jNHyro+9KhXL6zc0il3CoPQUkE0Ep8pgOYCMi ScQqS/l36d6ElQzh1DBBj4hkLutkZHZ/DAKGVaW1wmmKUHgixConx3GF7w5kq3YVcDf2 1asQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776196965; x=1776801765; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=ydncbOJwc9Epn5rxEKLGgwSNmxXWJlxhMwGYD93RLPE=; b=SFpZ4sNB0GwQB5EsYoyAq5RS8VnHtGxm/ZPKqd9fuKS0If3mL+YereUe1m0Pdx4ewP eflXuKH50gYdRlTwst2ELJssmxhBhbnjzgTdJ0lort7C0rdysT4J5h2WKv6H5uO7OacG EiSFf7Z+ReD843cSKEnUeRkB5eMswvHSbx25Vk9+jJebUAxLN19ijQUjs+LkYj0xeUXV Bvsbc8YU/5qc4J/GMYPn0+39M7VGf7h1uOI2N8FKjDLRqCeQ051f3E6dVQb+Vi6jgPdE CWF+KYEarQ5MpMoU7pEiGDA9PCljJE1IEtrHwcmC9ahT39Ln6tcK/zmLB/vca1HA/dSd jetA== X-Forwarded-Encrypted: i=1; AFNElJ+IJBm+L9bxR6ZUDSGCH2251fPKx+XsYa+PsZrpm38kFVR0ZAeNqE9yhfrXSN/99YQ9PoQPFFcpQbWVI5Q=@vger.kernel.org X-Gm-Message-State: AOJu0Yx3Waw5vkv4HPWUGvqjUURxipveIJG4etQrjXRyAv6yT1DBEILd g+QVcQl2SeLdSxqyp4/GLvNx8qdZjIHnzNTFa1Bl3vI/caSqVJ9zP3rxONTerkfg9e4= X-Gm-Gg: AeBDieusp3vAtJa38WJ7zSjvV0KKg9f6grt4WYUT9pTLqqO4nFM1EZc8t0inE2JLvgO fdtsLUI94W11yWTmBubMaBfN7NKP8+1Ork85//l4Rw0JS5ryp+Km8Pg8bye4pEzqpU+9FP35aol QTEliYXox2nYBdiOrKeE9GFIRWmNZU7WE7+65gwhv2cA2yn6Zztc02+sgIL82gVxdlHwtDmUpKB UeW5ATW29bStJaODLz7bRQue4fENoHa9YGvBp22KA8W2AvgFP/bDz32M++8ELJZMDAD9NmPztYE xhj8pKcdLBelrBPWqHuwm8ExcoLKKlUNst3Eo1pAiPznAR7RdtFtMdsZYI+zj14b6rPIFwCE0KA OJGfcDBGLJZQ9glQSHS5j33ToEeztpwJIp9XJt5i553y5PlY5XwkN7frHy4ScqVVyR/RXXUUrJm rTnzNGI3cKvbREf8i0CYi/5kANKoL99tcqY6yPYDGVgSRK+7q/DKqvjr7zvbC5MJSjqQ== X-Received: by 2002:a05:6214:4a8a:b0:8ac:ba63:a1af with SMTP id 6a1803df08f44-8acba63a68dmr104715286d6.53.1776196965499; Tue, 14 Apr 2026 13:02:45 -0700 (PDT) Received: from plex.localdomain ([71.181.43.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ac84a180a2sm132213656d6.15.2026.04.14.13.02.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Apr 2026 13:02:45 -0700 (PDT) From: Pasha Tatashin To: linux-kselftest@vger.kernel.org, rppt@kernel.org, shuah@kernel.org, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, pasha.tatashin@soleen.com, dmatlack@google.com, kexec@lists.infradead.org, pratyush@kernel.org, skhawaja@google.com, graf@amazon.com Subject: [PATCH 4/5] selftests/liveupdate: Add stress-sessions kexec test Date: Tue, 14 Apr 2026 20:02:36 +0000 Message-ID: <20260414200237.444170-5-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414200237.444170-1-pasha.tatashin@soleen.com> References: <20260414200237.444170-1-pasha.tatashin@soleen.com> 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 Content-Type: text/plain; charset="utf-8" Add a new test that creates 2000 LUO sessions before a kexec reboot and verifies their presence after the reboot. This ensures that the linked-block serialization mechanism works correctly for a large number of sessions. Signed-off-by: Pasha Tatashin --- tools/testing/selftests/liveupdate/Makefile | 1 + .../liveupdate/luo_stress_sessions.c | 98 +++++++++++++++++++ .../selftests/liveupdate/luo_test_utils.c | 25 +++++ .../selftests/liveupdate/luo_test_utils.h | 2 + 4 files changed, 126 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/luo_stress_sessions.c diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/se= lftests/liveupdate/Makefile index 080754787ede..ed7534468386 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -6,6 +6,7 @@ TEST_GEN_PROGS +=3D liveupdate =20 TEST_GEN_PROGS_EXTENDED +=3D luo_kexec_simple TEST_GEN_PROGS_EXTENDED +=3D luo_multi_session +TEST_GEN_PROGS_EXTENDED +=3D luo_stress_sessions =20 TEST_FILES +=3D do_kexec.sh =20 diff --git a/tools/testing/selftests/liveupdate/luo_stress_sessions.c b/too= ls/testing/selftests/liveupdate/luo_stress_sessions.c new file mode 100644 index 000000000000..cd6e9bb0db3c --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_stress_sessions.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + * + * Validate that LUO can handle a large number of sessions across a kexec + * reboot. + */ + +#include +#include +#include "luo_test_utils.h" + +#define NUM_SESSIONS 2000 +#define STATE_SESSION_NAME "kexec_many_state" +#define STATE_MEMFD_TOKEN 999 + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int i; + + ksft_print_msg("[STAGE 1] Increasing ulimit for open files...\n"); + luo_ensure_nofile_limit(NUM_SESSIONS + 10); + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating %d sessions...\n", NUM_SESSIONS); + + for (i =3D 0; i < NUM_SESSIONS; i++) { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + int s_fd; + + snprintf(name, sizeof(name), "many-test-%d", i); + s_fd =3D luo_create_session(luo_fd, name); + if (s_fd < 0) { + fail_exit("luo_create_session for '%s' at index %d", + name, i); + } + } + + ksft_print_msg("[STAGE 1] Successfully created %d sessions.\n", + NUM_SESSIONS); + + close(luo_fd); + daemonize_and_wait(); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int i, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage !=3D 2) { + fail_exit("Expected stage 2, but state file contains %d", + stage); + } + + ksft_print_msg("[STAGE 2] Retrieving and finishing %d sessions...\n", + NUM_SESSIONS); + + for (i =3D 0; i < NUM_SESSIONS; i++) { + char name[LIVEUPDATE_SESSION_NAME_LENGTH]; + int s_fd; + + snprintf(name, sizeof(name), "many-test-%d", i); + s_fd =3D luo_retrieve_session(luo_fd, name); + if (s_fd < 0) { + fail_exit("luo_retrieve_session for '%s' at index %d", + name, i); + } + + if (luo_session_finish(s_fd) < 0) { + fail_exit("luo_session_finish for '%s' at index %d", + name, i); + } + close(s_fd); + } + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- MANY-SESSIONS KEXEC TEST PASSED (%d sessions) ---\n= ", + NUM_SESSIONS); +} + +int main(int argc, char *argv[]) +{ + return luo_test(argc, argv, STATE_SESSION_NAME, + run_stage_1, run_stage_2); +} diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.c b/tools/te= sting/selftests/liveupdate/luo_test_utils.c index 3c8721c505df..37c330b9bb36 100644 --- a/tools/testing/selftests/liveupdate/luo_test_utils.c +++ b/tools/testing/selftests/liveupdate/luo_test_utils.c @@ -20,6 +20,7 @@ #include #include #include +#include =20 #include "luo_test_utils.h" =20 @@ -28,6 +29,30 @@ int luo_open_device(void) return open(LUO_DEVICE, O_RDWR); } =20 +void luo_ensure_nofile_limit(long min_limit) +{ + struct rlimit hl; + + if (getrlimit(RLIMIT_NOFILE, &hl) < 0) + ksft_exit_fail_msg("getrlimit failed: %s\n", strerror(errno)); + + if (hl.rlim_cur >=3D min_limit) + return; + + hl.rlim_cur =3D min_limit; + if (hl.rlim_cur > hl.rlim_max) + hl.rlim_max =3D hl.rlim_cur; + + if (setrlimit(RLIMIT_NOFILE, &hl) < 0) { + if (errno =3D=3D EPERM) { + ksft_exit_skip("Insufficient privileges to set RLIMIT_NOFILE to %ld\n", + hl.rlim_cur); + } + ksft_exit_fail_msg("setrlimit to %ld failed: %s\n", + hl.rlim_cur, strerror(errno)); + } +} + int luo_create_session(int luo_fd, const char *name) { struct liveupdate_ioctl_create_session arg =3D { .size =3D sizeof(arg) }; diff --git a/tools/testing/selftests/liveupdate/luo_test_utils.h b/tools/te= sting/selftests/liveupdate/luo_test_utils.h index 90099bf49577..9174d6757a7f 100644 --- a/tools/testing/selftests/liveupdate/luo_test_utils.h +++ b/tools/testing/selftests/liveupdate/luo_test_utils.h @@ -26,6 +26,8 @@ int luo_create_session(int luo_fd, const char *name); int luo_retrieve_session(int luo_fd, const char *name); int luo_session_finish(int session_fd); =20 +void luo_ensure_nofile_limit(long min_limit); + int create_and_preserve_memfd(int session_fd, int token, const char *data); int restore_and_verify_memfd(int session_fd, int token, const char *expect= ed_data); =20 --=20 2.43.0 From nobody Sat Jun 20 13:07:26 2026 Received: from mail-ua1-f49.google.com (mail-ua1-f49.google.com [209.85.222.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5431F3233E8 for ; Tue, 14 Apr 2026 20:02:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196969; cv=none; b=H9jFQ3EnLH7KRHPSCxETR1i3YqkD7jMH0xk1lfgzb8dB734TEaWyMgVNxTs4GTva7rWnsBqrgycggRTrv+eOzlX5Pse79itHBnxlxL8b9lSfXpRJ5LwM8c70IrRLeOl28/gRweN7tG/bu2sczlReQlMhp8OOCbpFjEL6mTHgATk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776196969; c=relaxed/simple; bh=cstkvhMPl2LYBSE9IY4nT+D67SitefrLtAq5Ft+KA1U=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W23avjLEI91lge7eE8tK+RmNjFPDctDSKuYF6cuRrq3NlMH7tkAA2IVg5Ltn5YFMXbA2+ZXwCkCI4s29RGXrSJnyKw77BT+eMf1x6/pnkoSRIXyKVz3na0CfytTohlo1vh0UIiWOOY7IFCMHkMp0tQs3k6/uOF8VFp8bw4pSw5I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com; spf=pass smtp.mailfrom=soleen.com; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b=Q2gKzhvG; arc=none smtp.client-ip=209.85.222.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=soleen.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=soleen.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=soleen.com header.i=@soleen.com header.b="Q2gKzhvG" Received: by mail-ua1-f49.google.com with SMTP id a1e0cc1a2514c-953a2a4761cso3165898241.1 for ; Tue, 14 Apr 2026 13:02:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=soleen.com; s=google; t=1776196967; x=1776801767; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=9TjUnXkwrio/0RMOWx8bIqnkUDx9TXK82cA2FHqDPhg=; b=Q2gKzhvG5B3LFMabTHeYlN88iVIUqKuirWTYg0Oagbwhe6CUgsws8UZWdfWRIxWZuj /GZIcLMVWFrt6KRJEjEBeyzzomDtyU0ZDBadrp6jShtdhkBeLtOfr5mJ8oPz8+GYdCao NwnDN6gUJBPy6jf/gsL9tEcU9VU2pcZ6AjvxYZtK8CmXVnKv0fChYoEJ4Yi4iqH28JI4 N+mTw7CuyLg/4DDKp5DWmXD++tJV9y2anPvaRLlqmNXrwPHoXwGneC2xXBVXwQ8FKtlY BL4bwHyGqr+SGhPVQC3oEJMBtBbf4S+e5JbkkAGK+pMiiSI484Ate1EvrO9qBEYHXWzQ Xqzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776196967; x=1776801767; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=9TjUnXkwrio/0RMOWx8bIqnkUDx9TXK82cA2FHqDPhg=; b=bc9+ryuJFTfCpeTc/IkgP3RY4Q3cWtglFZQ+GJepCd2kNI07RMSvknVTJ4lg+3U3Kb mmxh2r/ekqPRZDVjM1PW/nLUkw6yBsvCpqL/zcYRaMiPbqKsFwhL4qjk4NqHrOELshDl uGNXmuwPfTS4wHXvk4EyHdCeiuZM2br9l7WzZsj03Sm8UhCheW7KLLC3wQpCmyGjWfnS 5ehORJKF0AdBteu22jkzPsQ4Wei6JFE8JtiviEQ6Du2zLeCH6GXkXlCgeQfcVs8Xqk33 vAtGAIFU+tbxq3tmE++TbmkubHGmydiM67dZYMUamy3kqXE//leLHyAFsCMn851+imso VpOg== X-Forwarded-Encrypted: i=1; AFNElJ/IBoSMGV0mg6fcHRNLwBunbIXU4T+/RLKZeVi+h7T/L0AsRoUaL1DV77CAvCZpuqtRFiRcF210EuLzY8A=@vger.kernel.org X-Gm-Message-State: AOJu0YxkvWcJlyxrNMtrIXB5Z4Efuo+EhYzg5nAeMgp/vF282RugE20J 5UuztIyV0tHJzM7z9gnPHXE3rzQKmxoFJonCEhq5VBdeLmobOEvUgMfY1U35BlJseWM= X-Gm-Gg: AeBDietUny1uGH/qW8fdg3/E7ACmdlC3MCSPAqfHIN3KCpad452nw+83HTTPBjN1oa9 OJ/o6U2KxC1A/kKIGqXGQcUgwmeaRW4h10MS1ZQRF2gTgm49Mjf+En0PtUQj3cW/p2F6ipYw/tv QgKSje0MIstHPpesbAR+jHCMQSxpOmBL45UFMNUC1kdYFVNSdwny0zc9Osq+8tExNMJvtvQ6M2o c+etqQGr1btd9Rd3Z8JuC+aZabxCubPRrCMqYEZbJf5gWtwn5qrx3fByWZpOu2x5Z+2D+u78Tya PrtvZMuotTpOVVSZpqcgO3+m7y5VC3KhXmksxI8hl04YMi3j+euEhjREri3zS9n1XO7aqZBXw9j 9os6LQIDqa7eyg6pl2LQ53jnNDCM8Zi9zz2O44cxKVUE8OQLe+7ZSVAbvKXKlVqgjfHjebUGSdH dhmDv0ZTDhjBrU/vBfSkBYCuwH7GGpBwdSOpi3F/YTEoyHZlx60XOVicF+e16DMIb/w/tuqFd61 EyJ X-Received: by 2002:a05:6102:2c16:b0:607:95f4:53b5 with SMTP id ada2fe7eead31-609fecbd99dmr8981083137.4.1776196967110; Tue, 14 Apr 2026 13:02:47 -0700 (PDT) Received: from plex.localdomain ([71.181.43.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8ac84a180a2sm132213656d6.15.2026.04.14.13.02.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Apr 2026 13:02:46 -0700 (PDT) From: Pasha Tatashin To: linux-kselftest@vger.kernel.org, rppt@kernel.org, shuah@kernel.org, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, pasha.tatashin@soleen.com, dmatlack@google.com, kexec@lists.infradead.org, pratyush@kernel.org, skhawaja@google.com, graf@amazon.com Subject: [PATCH 5/5] selftests/liveupdate: Add stress-files kexec test Date: Tue, 14 Apr 2026 20:02:37 +0000 Message-ID: <20260414200237.444170-6-pasha.tatashin@soleen.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260414200237.444170-1-pasha.tatashin@soleen.com> References: <20260414200237.444170-1-pasha.tatashin@soleen.com> 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 Content-Type: text/plain; charset="utf-8" Add a new luo_stress_files kexec test that verifies preserving and retrieving 500 files across a kexec reboot. Signed-off-by: Pasha Tatashin Reviewed-by: Pratyush Yadav (Google) --- tools/testing/selftests/liveupdate/Makefile | 1 + .../selftests/liveupdate/luo_stress_files.c | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 tools/testing/selftests/liveupdate/luo_stress_files.c diff --git a/tools/testing/selftests/liveupdate/Makefile b/tools/testing/se= lftests/liveupdate/Makefile index ed7534468386..30689d22cb02 100644 --- a/tools/testing/selftests/liveupdate/Makefile +++ b/tools/testing/selftests/liveupdate/Makefile @@ -7,6 +7,7 @@ TEST_GEN_PROGS +=3D liveupdate TEST_GEN_PROGS_EXTENDED +=3D luo_kexec_simple TEST_GEN_PROGS_EXTENDED +=3D luo_multi_session TEST_GEN_PROGS_EXTENDED +=3D luo_stress_sessions +TEST_GEN_PROGS_EXTENDED +=3D luo_stress_files =20 TEST_FILES +=3D do_kexec.sh =20 diff --git a/tools/testing/selftests/liveupdate/luo_stress_files.c b/tools/= testing/selftests/liveupdate/luo_stress_files.c new file mode 100644 index 000000000000..6217122b92f4 --- /dev/null +++ b/tools/testing/selftests/liveupdate/luo_stress_files.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) 2026, Google LLC. + * Pasha Tatashin + * + * Validate that LUO can handle a large number of files per session across + * a kexec reboot. + */ + +#include +#include +#include "luo_test_utils.h" + +#define NUM_FILES 500 +#define STATE_SESSION_NAME "kexec_many_files_state" +#define STATE_MEMFD_TOKEN 9999 +#define TEST_SESSION_NAME "many_files_session" + +/* Stage 1: Executed before the kexec reboot. */ +static void run_stage_1(int luo_fd) +{ + int session_fd; + int i; + + ksft_print_msg("[STAGE 1] Increasing ulimit for open files...\n"); + luo_ensure_nofile_limit(NUM_FILES + 10); + + ksft_print_msg("[STAGE 1] Creating state file for next stage (2)...\n"); + create_state_file(luo_fd, STATE_SESSION_NAME, STATE_MEMFD_TOKEN, 2); + + ksft_print_msg("[STAGE 1] Creating test session '%s'...\n", TEST_SESSION_= NAME); + session_fd =3D luo_create_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_create_session"); + + ksft_print_msg("[STAGE 1] Preserving %d files...\n", NUM_FILES); + for (i =3D 0; i < NUM_FILES; i++) { + char data[64]; + + snprintf(data, sizeof(data), "file-data-%d", i); + if (create_and_preserve_memfd(session_fd, i, data) < 0) + fail_exit("create_and_preserve_memfd for index %d", i); + } + + ksft_print_msg("[STAGE 1] Successfully preserved %d files.\n", NUM_FILES); + + close(luo_fd); + daemonize_and_wait(); +} + +/* Stage 2: Executed after the kexec reboot. */ +static void run_stage_2(int luo_fd, int state_session_fd) +{ + int session_fd; + int i, stage; + + ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n"); + + restore_and_read_stage(state_session_fd, STATE_MEMFD_TOKEN, &stage); + if (stage !=3D 2) { + fail_exit("Expected stage 2, but state file contains %d", + stage); + } + + ksft_print_msg("[STAGE 2] Retrieving test session '%s'...\n", TEST_SESSIO= N_NAME); + session_fd =3D luo_retrieve_session(luo_fd, TEST_SESSION_NAME); + if (session_fd < 0) + fail_exit("luo_retrieve_session"); + + ksft_print_msg("[STAGE 2] Verifying %d files...\n", NUM_FILES); + for (i =3D 0; i < NUM_FILES; i++) { + char data[64]; + + snprintf(data, sizeof(data), "file-data-%d", i); + if (restore_and_verify_memfd(session_fd, i, data) < 0) + fail_exit("restore_and_verify_memfd for index %d", i); + } + + ksft_print_msg("[STAGE 2] Finishing test session...\n"); + if (luo_session_finish(session_fd) < 0) + fail_exit("luo_session_finish for test session"); + close(session_fd); + + ksft_print_msg("[STAGE 2] Finalizing state session...\n"); + if (luo_session_finish(state_session_fd) < 0) + fail_exit("luo_session_finish for state session"); + close(state_session_fd); + + ksft_print_msg("\n--- MANY-FILES KEXEC TEST PASSED (%d files) ---\n", + NUM_FILES); +} + +int main(int argc, char *argv[]) +{ + return luo_test(argc, argv, STATE_SESSION_NAME, + run_stage_1, run_stage_2); +} --=20 2.43.0