From nobody Fri Oct 2 12:19:59 2026 Received: from mail-pg1-f197.google.com (mail-pg1-f197.google.com [209.85.215.197]) (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 37EBF331EBA for ; Fri, 31 Jul 2026 21:53:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534803; cv=none; b=CQB5HHWQabf2zjA2dqr/1bC0LAxozjI7H6fHdupUIdeF4h2qMLvFiANZvCzb1hUrFZGMhudD9/rKicS5W+740DbXI2aCsIHz3OMmrchCk1AVbts9dsFb/GOU+B91SmpubKu2C1/p9T7cBuZr/tlkXkwM+esOZwt1VKM/579Ji6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534803; c=relaxed/simple; bh=VlFqQBVHRVeDKOQGdRirMGbhxjDhGYQc4+JPJwShZMM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=LLwEg+B9fy8LcEKNadVLiQ7/UsilKCYLLysgT6Ezs7qnmYkdwoKq8Zsq0xdD98wQf2IPkY8HF4V5fN6bvgBfzRywCpD1/cp+mwNqpw/0wVt8E02mZcAC9tzq7aoUquNGySnZKOXGmARg/il8/fiOCIksg9ToDcPJgnZ/nW7pYyQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=nrcWwsGr; arc=none smtp.client-ip=209.85.215.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="nrcWwsGr" Received: by mail-pg1-f197.google.com with SMTP id 41be03b00d2f7-c9fe4c5eb39so1001230a12.1 for ; Fri, 31 Jul 2026 14:53:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1785534801; x=1786139601; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=IXN1kpPZzQWxyZLtIWIq6X9QJe/5QaMTFSKsE0QskIU=; b=nrcWwsGrEwy8WK9eGsOyKqWxxE7vHTc0+6qHuFwS/gCtjrY1lwa+lKRFmx2if49pL8 lfB/d5hyllDVR21NqOtgfCIS65pbtFpBPuKAojhL5ezGddIsVYkt9q+b22oKSYXPUXNR 7+CSia79TsPziT3J9YRb5jToG+VP7Xcb/kVwk/tzZS6zYDtyi95xwcAOANXv+JVLXrfh mvY1is6WhjFlukrkrE1w0dthVms8AjlFl88JJ5/m+Rkci7jPg0s6llOObEHk6FgtDg5j 0/2d1h4VpxywITvlVeESRDq5b3uZcYoJXVZ+taAomBD6I3Nc59raMelWuG9G3jLqHIDc sd1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785534801; x=1786139601; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=IXN1kpPZzQWxyZLtIWIq6X9QJe/5QaMTFSKsE0QskIU=; b=lSAbYmujV1aTj6ttc91CvJNyikvFITrwUdZAtMAlkFkUabQI7RG8YOx8pcxkEybXVs dIz8JAj/qsDPPZRFkHgHMbw9F/mn2z+xv/3H0pqjKb8iVDHqBsueTEdabQoDUW0ziuvo ZdvaSu5fugXXE9KQwNKR09G+jYvVWVnggsT7ci6asDKMWkNzQIbhQ1IFuKhDNdO91oN6 HoH2oZeKdkREbGnZnpd+ac2f69RqsiwQgulgAhnNdP5NYnVpKlBjiQOGkKlPr5g/aAyG TxQXBzvMkNI2X/3jnKyNp0+mNcZlsyjqkFcKfXEfUDm/PkR2dt2tyC0WvFXfmIPlfpJb qchA== X-Forwarded-Encrypted: i=1; AHgh+RrolKMsdzanB93/8jTAKT+xqm07dLeflMR/GSK1k2tBk6A3Vs65VOA20OVDJDRkwH0X0D7lw2OwWk1/Fp8=@vger.kernel.org X-Gm-Message-State: AOJu0YzRPRik209GJZDpi09c7uUo2necGE9wUMqumTVN6wIRsiBUbP0k V13z2ymkM7c/OlJwIj223vpfGjoGmf8QPh+thfB/uPJRXNkXSFAHZfl5RgKyOa2Bo13GiZ3aspz gADW62IhLqehxS4wKPXFVzA== X-Received: from dybkz17.prod.google.com ([2002:a05:7301:1a11:b0:313:b255:3ca2]) (user=loganodell job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:3393:b0:3b1:a9ce:507e with SMTP id adf61e73a8af0-3c92a60a0f1mr1289190637.32.1785534801433; Fri, 31 Jul 2026 14:53:21 -0700 (PDT) Date: Fri, 31 Jul 2026 14:52:21 -0700 In-Reply-To: <20260731215224.831696-1-loganodell@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260731215224.831696-1-loganodell@google.com> X-Mailer: git-send-email 2.55.0.508.g3f0d502094-goog Message-ID: <20260731215224.831696-2-loganodell@google.com> Subject: [RFC PATCH 1/4] kho: Move subtree blob logic to separate function From: Logan Odell To: graf@amazon.com, rppt@kernel.org, pasha.tatashin@soleen.com Cc: pratyush@kernel.org, akpm@linux-foundation.org, kexec@lists.infradead.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Logan Odell Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the logic to set the blob data in a subtree node into a separate function to allow for re-use. No functional change. Signed-off-by: Logan Odell --- kernel/liveupdate/kexec_handover.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index cc68a3692905..29a05cec2625 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -722,6 +722,21 @@ static void __init kho_reserve_scratch(void) kho_enable =3D false; } =20 +static int kho_add_subtree_node(const char *name, void *root_fdt, void *fd= t, int off) +{ + int err; + phys_addr_t phys =3D virt_to_phys(fdt); + + err =3D fdt_setprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, + &phys, sizeof(phys)); + if (err) + return err; + + WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false)); + + return 0; +} + /** * kho_add_subtree - record the physical address of a sub FDT in KHO root = tree. * @name: name of the sub tree. @@ -739,7 +754,6 @@ static void __init kho_reserve_scratch(void) */ int kho_add_subtree(const char *name, void *fdt) { - phys_addr_t phys =3D virt_to_phys(fdt); void *root_fdt =3D kho_out.fdt; int err =3D -ENOMEM; int off, fdt_err; @@ -757,12 +771,7 @@ int kho_add_subtree(const char *name, void *fdt) goto out_pack; } =20 - err =3D fdt_setprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, - &phys, sizeof(phys)); - if (err < 0) - goto out_pack; - - WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false)); + err =3D kho_add_subtree_node(name, root_fdt, fdt, off); =20 out_pack: fdt_pack(root_fdt); --=20 2.55.0.508.g3f0d502094-goog From nobody Fri Oct 2 12:19:59 2026 Received: from mail-pg1-f198.google.com (mail-pg1-f198.google.com [209.85.215.198]) (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 242292D5408 for ; Fri, 31 Jul 2026 21:53:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534804; cv=none; b=sKfoKu8B8RN+ajpr1Or1rSfFNlRluCMeECNsKq8KwuM1cFIs3EcS4GIdA9uBsKsG6/GISDuIDouFknEaXvIS99CZ9FiCC6e2GLcSPSeSxFpPzn1tYyQWQ6uUo+vg0kdVX+TwhBWlRww9+iMcYSH261+/USEwYqZUr+t2xl93BY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534804; c=relaxed/simple; bh=WCb3tzkObtzE+dO6XA53MO9JCyMlRBQL6jUZhWg5/Pk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=N3GE016+d3xktsPhg7zyopV+HIhBUS0k6gkDuzv8Ngl8z+KniB1fc2Sh/g28Tz2P/AZpZ/VRDeF2QQdWJ1b8tx4/gOBBoOctSH0sixPLTr3nSFnFAk5UorYuoqEDKvaQlSAdyiYN+pCIKn+zkPW+mWGnQdL39wmWC1LB4DkUCjo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=V8tctXLr; arc=none smtp.client-ip=209.85.215.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="V8tctXLr" Received: by mail-pg1-f198.google.com with SMTP id 41be03b00d2f7-ca6bd8a190cso1781281a12.0 for ; Fri, 31 Jul 2026 14:53:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1785534802; x=1786139602; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=pFbpBG3BWtAJOPPgNm/eX/59dGOWD3hR8l2ysdNLveY=; b=V8tctXLrqOLpUADa+Y/071LgyyYzlc3YigHaHsIzquAfNgm6gTFULsbOlItG0lSzcU ZLsyy+9jCrINTlYas78XbWRGmP+wlRZkzDdjGmhQPG9wDv8a8lm6HoLiyveaBHCn5IO3 H11UErj9wZUH6R1JHnvAxFwroHmKqYhaTujwtWgxgkK8PthpBshB0ShbemGPv+0x8gGn u9zilpgXlohXKJOZRv+GohihuH1JQhZCTI2yqZTnNCWIJ8Wtj2WQh7+d3Xhk+9yF0RWR dPR3J589MmVRl+OBEkqqEjRnodaenFsdGlG3QK9XKFhLb8UsrnlbrU65EmSSQv1woEkV ebiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785534802; x=1786139602; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=pFbpBG3BWtAJOPPgNm/eX/59dGOWD3hR8l2ysdNLveY=; b=OKZBkisPyFFohIRnpT+Kw32dwMppOW99yCGg/oVhNsDd93KAGbF9qoBO+0mgwkchEK MIj7xdjatWE0Z2waNrnz0AjQwgbioc0ohurHnzdgyFSBTyuqPwOkoy3ZXDCMImq1Slop 808KK/Pv6PLb58oowukN5KNJ3keNQGMjoS7F/hYkEySh2L9c8ssSki0HmT05vnK6DOKz tKZJXOefztmjW30+4Ju2R/akcjKJOl2Gbinj1Er6qRyZPOePq5E5T01DlRF3F3tpz5WS iBStm6pF0bBTPfFdRjaCxW6wBMZqri3Pc4BHW9YI1mCAKqGf2ejz3iYpFgyxi5cPZCXe Delg== X-Forwarded-Encrypted: i=1; AHgh+Rqs5d84HYYdh+zpiE8/ckJPlvNV7LgJ+WbUno1u4pTiT1ybJvO8T+8nlav9/kwwWmvr5YxbqmR5ok3v8tw=@vger.kernel.org X-Gm-Message-State: AOJu0YxvcxF6bFbWQahcTvgAu9JksnffuP0rMRZJqvL5N5XKX4JyFpDJ 2Xr5yeqMRC803eh5irbzeKURMt+dt/VfjHuhv08qM+IMLv+1rVr+cgzSZxM2YkjNt/kQFx2yZZE Sql48W5m/Iumihcy01rXU/A== X-Received: from dycjx24.prod.google.com ([2002:a05:7300:fd18:b0:311:1ea1:d67]) (user=loganodell job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:e082:b0:3b3:bf95:f7e3 with SMTP id adf61e73a8af0-3c92a89cb5dmr1207283637.43.1785534802213; Fri, 31 Jul 2026 14:53:22 -0700 (PDT) Date: Fri, 31 Jul 2026 14:52:22 -0700 In-Reply-To: <20260731215224.831696-1-loganodell@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260731215224.831696-1-loganodell@google.com> X-Mailer: git-send-email 2.55.0.508.g3f0d502094-goog Message-ID: <20260731215224.831696-3-loganodell@google.com> Subject: [RFC PATCH 2/4] kho: Add support for multiple versions in subtrees From: Logan Odell To: graf@amazon.com, rppt@kernel.org, pasha.tatashin@soleen.com Cc: pratyush@kernel.org, akpm@linux-foundation.org, kexec@lists.infradead.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Logan Odell Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add explicit version support to KHO by creating subnodes with the version number. The first caller to kho_add_subtree_version will create the parent node and versioned subnode. Subsequent calls with a different version will add more subnodes with the version as the name. Remove is updated to handle cleanup when the final version is removed. Example Tree: / (Root Node) =E2=94=9C=E2=94=80=E2=94=80 compatible =3D "kho-v1" =E2=94=9C=E2=94=80=E2=94=80 preserved-memory-map =3D <0xphys_map> =E2=94=9C=E2=94=80=E2=94=80 some_legacy_node (Legacy Node) =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 preserved-data =3D <0xphys_l= egacy> =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 blob-size =3D <0xsize_legacy> =E2=94=94=E2=94=80=E2=94=80 LUO (Versioned Parent Node) =E2=94=9C=E2=94=80=E2=94=80 1 (Version 1 Node) =E2=94=82 =E2=94=9C=E2=94=80=E2=94=80 preserved-data =3D <0xph= ys_luo_v1> =E2=94=82 =E2=94=94=E2=94=80=E2=94=80 blob-size =3D <0xsize_lu= o_v1> =E2=94=94=E2=94=80=E2=94=80 2 (Version 2 Node) =E2=94=9C=E2=94=80=E2=94=80 preserved-data =3D <0xphys_luo_v= 2> =E2=94=94=E2=94=80=E2=94=80 blob-size =3D <0xsize_luo_v2> Signed-off-by: Logan Odell --- include/linux/kexec_handover.h | 14 ++ include/linux/kho/abi/kexec_handover.h | 16 ++ kernel/liveupdate/kexec_handover.c | 215 ++++++++++++++++++++++++- 3 files changed, 237 insertions(+), 8 deletions(-) diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h index ac4129d1d741..1d47ab76b02b 100644 --- a/include/linux/kexec_handover.h +++ b/include/linux/kexec_handover.h @@ -33,8 +33,11 @@ struct folio *kho_restore_folio(phys_addr_t phys); struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages); void *kho_restore_vmalloc(const struct kho_vmalloc *preservation); int kho_add_subtree(const char *name, void *fdt); +int kho_add_subtree_version(const char *name, int version, void *fdt); void kho_remove_subtree(void *fdt); +void kho_remove_subtree_version(const char *name, int version, void *fdt); int kho_retrieve_subtree(const char *name, phys_addr_t *phys); +int kho_retrieve_subtree_version(const char *name, int version, phys_addr_= t *phys); =20 void kho_memory_init(void); =20 @@ -102,13 +105,24 @@ static inline int kho_add_subtree(const char *name, v= oid *fdt) return -EOPNOTSUPP; } =20 +static inline int kho_add_subtree_version(const char *name, int version, v= oid *fdt) +{ + return -EOPNOTSUPP; +} + static inline void kho_remove_subtree(void *fdt) { } +static inline void kho_remove_subtree_version(const char *name, int versio= n, void *fdt) { } =20 static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys) { return -EOPNOTSUPP; } =20 +static inline int kho_retrieve_subtree_version(const char *name, int versi= on, phys_addr_t *phys) +{ + return -EOPNOTSUPP; +} + static inline void kho_memory_init(void) { } =20 static inline void kho_populate(phys_addr_t fdt_phys, u64 fdt_len, diff --git a/include/linux/kho/abi/kexec_handover.h b/include/linux/kho/abi= /kexec_handover.h index 2201a0d2c159..55acb73b1d27 100644 --- a/include/linux/kho/abi/kexec_handover.h +++ b/include/linux/kho/abi/kexec_handover.h @@ -73,6 +73,22 @@ * * Physical address pointing to a subnode FDT blob that is also * being preserved. + * + * Versioned Subnodes (): + * Alternatively, a subnode can group multiple versions of the same + * data. In this case, the subnode does not contain + * an 'fdt' property directly. Instead, it contains child nodes named + * after the version number (e.g., '1', '2'), which in turn contain + * the 'fdt' property. + * + * { + * { + * fdt =3D <0x...>; + * }; + * { + * fdt =3D <0x...>; + * }; + * }; */ =20 /* The compatible string for the KHO FDT root node. */ diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index 29a05cec2625..6dc7c328a6e2 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include =20 @@ -722,15 +723,31 @@ static void __init kho_reserve_scratch(void) kho_enable =3D false; } =20 +static int fdt_err_to_errno(int fdt_err) +{ + switch (fdt_err) { + case 0: + return 0; + case -FDT_ERR_NOSPACE: + return -ENOSPC; + case -FDT_ERR_EXISTS: + return -EEXIST; + case -FDT_ERR_NOTFOUND: + return -ENOENT; + default: + return -EINVAL; + } +} + static int kho_add_subtree_node(const char *name, void *root_fdt, void *fd= t, int off) { int err; - phys_addr_t phys =3D virt_to_phys(fdt); + u64 phys =3D virt_to_phys(fdt); =20 err =3D fdt_setprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, &phys, sizeof(phys)); if (err) - return err; + return fdt_err_to_errno(err); =20 WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false)); =20 @@ -762,12 +779,11 @@ int kho_add_subtree(const char *name, void *fdt) =20 fdt_err =3D fdt_open_into(root_fdt, root_fdt, PAGE_SIZE); if (fdt_err < 0) - return err; + return fdt_err_to_errno(fdt_err); =20 off =3D fdt_add_subnode(root_fdt, 0, name); if (off < 0) { - if (off =3D=3D -FDT_ERR_EXISTS) - err =3D -EEXIST; + err =3D fdt_err_to_errno(off); goto out_pack; } =20 @@ -780,6 +796,93 @@ int kho_add_subtree(const char *name, void *fdt) } EXPORT_SYMBOL_GPL(kho_add_subtree); =20 +/** + * kho_add_subtree_version - record physical address of a sub FDT in KHO r= oot tree with version. + * @name: name of the sub tree group. + * @version: version of the sub tree. + * @fdt: the sub tree blob. + * + * Finds or creates a child node named @name in KHO root FDT, and then + * creates a child node named @version under @name, and records + * the physical address of @fdt there. + * + * A debugfs blob entry is also created at + * ``/sys/kernel/debug/kho/out/sub_fdts/@name-@version`` when kernel is co= nfigured with + * CONFIG_KEXEC_HANDOVER_DEBUGFS + * + * Return: 0 on success, error code on failure + */ +int kho_add_subtree_version(const char *name, int version, void *fdt) +{ + void *root_fdt =3D kho_out.fdt; + int err =3D -ENOMEM; + int off, sub_off, fdt_err; + char version_str[12]; + char *dbg_name =3D NULL; + bool created_parent =3D false; + + mutex_lock(&kho_out.lock); + + fdt_err =3D fdt_open_into(root_fdt, root_fdt, PAGE_SIZE); + if (fdt_err < 0) { + err =3D fdt_err_to_errno(fdt_err); + goto out; + } + + off =3D fdt_subnode_offset(root_fdt, 0, name); + if (off =3D=3D -FDT_ERR_NOTFOUND) { + off =3D fdt_add_subnode(root_fdt, 0, name); + if (off < 0) { + err =3D fdt_err_to_errno(off); + goto out_pack; + } + created_parent =3D true; + } else if (off < 0) { + err =3D fdt_err_to_errno(off); + goto out_pack; + } + + snprintf(version_str, sizeof(version_str), "%d", version); + sub_off =3D fdt_add_subnode(root_fdt, off, version_str); + if (sub_off < 0) { + err =3D fdt_err_to_errno(sub_off); + goto err_del_parent; + } + + dbg_name =3D kasprintf(GFP_KERNEL, "%s-%s", name, version_str); + if (!dbg_name) { + err =3D -ENOMEM; + goto err_del_subnode; + } + + err =3D kho_add_subtree_node(dbg_name, root_fdt, fdt, sub_off); + if (err) + goto err_free_dbg_name; + + kfree(dbg_name); + +out_pack: + fdt_pack(root_fdt); +out: + mutex_unlock(&kho_out.lock); + return err; + +err_free_dbg_name: + kfree(dbg_name); +err_del_subnode: + if (created_parent) + fdt_del_node(root_fdt, off); + else + fdt_del_node(root_fdt, sub_off); + goto out_pack; + +err_del_parent: + if (created_parent) + fdt_del_node(root_fdt, off); + goto out_pack; +} +EXPORT_SYMBOL_GPL(kho_add_subtree_version); + void kho_remove_subtree(void *fdt) { phys_addr_t target_phys =3D virt_to_phys(fdt); @@ -799,10 +902,10 @@ void kho_remove_subtree(void *fdt) int len; =20 val =3D fdt_getprop(root_fdt, off, KHO_FDT_SUB_TREE_PROP_NAME, &len); - if (!val || len !=3D sizeof(phys_addr_t)) + if (!val || len !=3D sizeof(*val)) continue; =20 - if ((phys_addr_t)*val =3D=3D target_phys) { + if ((phys_addr_t)get_unaligned(val) =3D=3D target_phys) { fdt_del_node(root_fdt, off); kho_debugfs_fdt_remove(&kho_out.dbg, fdt); break; @@ -813,6 +916,59 @@ void kho_remove_subtree(void *fdt) } EXPORT_SYMBOL_GPL(kho_remove_subtree); =20 +/** + * kho_remove_subtree_version - remove a versioned sub FDT from KHO root t= ree. + * @name: name of the sub tree group. + * @version: version of the sub tree. + * @fdt: the sub tree blob to remove. + * + * Removes the @version subnode under @name node and its associated debugf= s entry. + * If @name node becomes empty, it is also removed. + */ +void kho_remove_subtree_version(const char *name, int version, void *fdt) +{ + phys_addr_t target_phys =3D virt_to_phys(fdt); + void *root_fdt =3D kho_out.fdt; + int off, sub_off; + int err; + char version_str[12]; + + mutex_lock(&kho_out.lock); + + err =3D fdt_open_into(root_fdt, root_fdt, PAGE_SIZE); + if (err < 0) + goto out; + + off =3D fdt_subnode_offset(root_fdt, 0, name); + if (off < 0) + goto out_pack; + + snprintf(version_str, sizeof(version_str), "%d", version); + sub_off =3D fdt_subnode_offset(root_fdt, off, version_str); + if (sub_off < 0) + goto out_pack; + + { + const u64 *val; + int len; + + val =3D fdt_getprop(root_fdt, sub_off, KHO_FDT_SUB_TREE_PROP_NAME, &len); + if (val && len =3D=3D sizeof(*val) && (phys_addr_t)get_unaligned(val) = =3D=3D target_phys) { + fdt_del_node(root_fdt, sub_off); + kho_debugfs_fdt_remove(&kho_out.dbg, fdt); + + if (fdt_first_subnode(root_fdt, off) =3D=3D -FDT_ERR_NOTFOUND) + fdt_del_node(root_fdt, off); + } + } + +out_pack: + fdt_pack(root_fdt); +out: + mutex_unlock(&kho_out.lock); +} +EXPORT_SYMBOL_GPL(kho_remove_subtree_version); + /** * kho_preserve_folio - preserve a folio across kexec. * @folio: folio to preserve. @@ -1336,12 +1492,55 @@ int kho_retrieve_subtree(const char *name, phys_add= r_t *phys) if (!val || len !=3D sizeof(*val)) return -EINVAL; =20 - *phys =3D (phys_addr_t)*val; + *phys =3D (phys_addr_t)get_unaligned(val); =20 return 0; } EXPORT_SYMBOL_GPL(kho_retrieve_subtree); =20 +/** + * kho_retrieve_subtree_version - retrieve a preserved versioned sub FDT. + * @name: the name of the sub FDT group passed to kho_add_subtree_version(= ). + * @version: the version of the sub FDT. + * @phys: if found, the physical address of the sub FDT is stored in @phys. + * + * Retrieve a preserved sub FDT named @version under @name and store its + * physical address in @phys. + * + * Return: 0 on success, error code on failure + */ +int kho_retrieve_subtree_version(const char *name, int version, phys_addr_= t *phys) +{ + const void *fdt =3D kho_get_fdt(); + const u64 *val; + int offset, sub_offset, len; + char version_str[12]; + + if (!fdt) + return -ENOENT; + + if (!phys) + return -EINVAL; + + offset =3D fdt_subnode_offset(fdt, 0, name); + if (offset < 0) + return -ENOENT; + + snprintf(version_str, sizeof(version_str), "%d", version); + sub_offset =3D fdt_subnode_offset(fdt, offset, version_str); + if (sub_offset < 0) + return -ENOENT; + + val =3D fdt_getprop(fdt, sub_offset, KHO_FDT_SUB_TREE_PROP_NAME, &len); + if (!val || len !=3D sizeof(*val)) + return -EINVAL; + + *phys =3D (phys_addr_t)get_unaligned(val); + + return 0; +} +EXPORT_SYMBOL_GPL(kho_retrieve_subtree_version); + static __init int kho_out_fdt_setup(void) { void *root =3D kho_out.fdt; --=20 2.55.0.508.g3f0d502094-goog From nobody Fri Oct 2 12:19:59 2026 Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) (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 03AE435F185 for ; Fri, 31 Jul 2026 21:53:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.71 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534805; cv=none; b=sOzou9MvbUtXdEeO3qJAfcy070LOASJWNS30rqQWxUGHKKEVyKC0+FgCt85a0m/RSzTFN9pl9wPpm4iUNL9MzGRLBjJgD2fIhhiIsraFq30fuAsrmw4CP35XFxDnLUM+MpcCb7lpZY31bGUCJWBs5uTCxq/K7hOABp/t6vLxqAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534805; c=relaxed/simple; bh=4E8xyICGCcGsJ1StAscsDxgjIkTmBCAuU6VW+ox0bJI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mkHwB8GmUkuu0Jj/KeLJqVixTXccDk+/QlxLM+1024BPXj0zfM38QGDhvdcvqpfAjPYgD4fF4yNbKWymM07f1S18YBprgxUMhUrXf9iqdxLn5idFEeXDz9MTmom5/r6EbSyOdmZPaCSpSJoGyPIDymSb/2OPaRCQQzsX7uSwx/I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=ptSRlaBD; arc=none smtp.client-ip=209.85.216.71 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="ptSRlaBD" Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-38ea32e57e2so2655679a91.1 for ; Fri, 31 Jul 2026 14:53:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1785534803; x=1786139603; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=als7b8ug69mz+jjevN//6dwMY7mF0ZGUUEH5Txoyuyo=; b=ptSRlaBDdRc8bGfNf4KamWfq8Z90lYin2lWzBqwN9e6l1rkm3Mm0WUUcIpNL2CWWlU hKSQgZj4InXpaOp3Vl3MbSZgQzEgSUBjoXgU1rlmlwNcyfjSdlwRL8evcsCr9pOpIOds SbERnjiCxq+qRXGYfrwCLLfxGXtY9+wWqvPGkHX3HLocagjoLjZfEeBnNXsxF6geoV2X 0c8V1bEAC3U5uS45qg7SCd8j73nuA/O9TzhUbQTSeJV8C+DSWBaW+2RLwW8IgIrT+k+a W6XM3+kFoTvx33eJdgMlD8DVDW+tWtRJYBtoxv5tkU5EJXExfTJkChqBVFPyxV5Ttdjq OKKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785534803; x=1786139603; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=als7b8ug69mz+jjevN//6dwMY7mF0ZGUUEH5Txoyuyo=; b=ehyr2LfV8fKhnTLCiRZX6o2M24YBF3GVDkhhGz6YlO6EnlFe8bYNRRnmd37cQE7Auh GOhLIG4gEVjeYmqyk6MxnoMdeP+ZWc/dJtKQD2IYjuqOuUP6tmWY8akybB3YhT+6n0bO By0t46NWQvQQCXC1I3jsW+tM82b+UpZ8pwnxbb3YJag0zwfRimL5QJ5c3wYSzjZAA988 TwylASBHPZDWYgo2jEGYkwzpJhMhGX0z5V+ydmHWa9QDcYSTtDnWmFjO2GB1OE7lAjbd ddK2awS9BNUBO+TO+st0guzi7pUO+7gh5r13tjaACtMOjTOY9Vw5rnsOWm3ZYHOWa7Gc i2BQ== X-Forwarded-Encrypted: i=1; AHgh+RpEjB78aBzHrrfYXjEotzD2rxAOELw2zaI5kudtdE2aOprd/61YzW541GVoMdWEIH+artvCINWwta66iJM=@vger.kernel.org X-Gm-Message-State: AOJu0Yy1JuhUAf+5MueOipfJTWjunc3romlMbXWueMkwZRg8oEHY5P/l /bvdvrWNo/KONfR3/lDnYPl2Wg+XmQuDF3SZ6tbRYZmrUQjgOIQW4/Krtghd49i2XQKyB+uR/KF Zn1q9zcJiXPhmJEQWZwjrJw== X-Received: from dycmf5.prod.google.com ([2002:a05:693c:2485:b0:30f:42b1:df2b]) (user=loganodell job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2dc7:b0:37f:9ce2:348c with SMTP id 98e67ed59e1d1-38fbc4eead6mr1133723a91.29.1785534803101; Fri, 31 Jul 2026 14:53:23 -0700 (PDT) Date: Fri, 31 Jul 2026 14:52:23 -0700 In-Reply-To: <20260731215224.831696-1-loganodell@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260731215224.831696-1-loganodell@google.com> X-Mailer: git-send-email 2.55.0.508.g3f0d502094-goog Message-ID: <20260731215224.831696-4-loganodell@google.com> Subject: [RFC PATCH 3/4] kho: Add support for multiple versions to debugfs From: Logan Odell To: graf@amazon.com, rppt@kernel.org, pasha.tatashin@soleen.com Cc: pratyush@kernel.org, akpm@linux-foundation.org, kexec@lists.infradead.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Logan Odell Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Update kho_in_debugfs_init to search for versioned subnodes (depth 2) and expose them as name-version in the input debugfs. This ensures versioned subtrees are visible to userspace for debugging, matching the behavior of the output side. Signed-off-by: Logan Odell --- kernel/liveupdate/kexec_handover_debugfs.c | 57 +++++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/kernel/liveupdate/kexec_handover_debugfs.c b/kernel/liveupdate= /kexec_handover_debugfs.c index 2f93939168ab..2ca055219900 100644 --- a/kernel/liveupdate/kexec_handover_debugfs.c +++ b/kernel/liveupdate/kexec_handover_debugfs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "kexec_handover_internal.h" =20 static struct dentry *debugfs_root; @@ -140,19 +141,49 @@ __init void kho_in_debugfs_init(struct kho_debugfs *d= bg, const void *fdt) const u64 *fdt_phys; =20 fdt_phys =3D fdt_getprop(fdt, child, "fdt", &len); - if (!fdt_phys) - continue; - if (len !=3D sizeof(*fdt_phys)) { - pr_warn("node %s prop fdt has invalid length: %d\n", - name, len); - continue; - } - err =3D __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name, - phys_to_virt(*fdt_phys)); - if (err) { - pr_warn("failed to add fdt %s to debugfs: %pe\n", name, - ERR_PTR(err)); - continue; + if (fdt_phys) { + if (len !=3D sizeof(*fdt_phys)) { + pr_warn("node %s prop fdt has invalid length: %d\n", + name, len); + continue; + } + err =3D __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name, + phys_to_virt(get_unaligned(fdt_phys))); + if (err) { + pr_warn("failed to add fdt %s to debugfs: %pe\n", name, + ERR_PTR(err)); + } + } else { + int grandchild; + + fdt_for_each_subnode(grandchild, fdt, child) { + const char *gc_name =3D fdt_get_name(fdt, grandchild, NULL); + const u64 *gc_fdt_phys; + char *combined_name; + + gc_fdt_phys =3D fdt_getprop(fdt, grandchild, "fdt", &len); + if (!gc_fdt_phys) + continue; + + if (len !=3D sizeof(*gc_fdt_phys)) { + pr_warn("node %s/%s prop fdt has invalid length: %d\n", + name, gc_name, len); + continue; + } + + combined_name =3D kasprintf(GFP_KERNEL, "%s-%s", name, gc_name); + if (!combined_name) + continue; + + err =3D __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, + combined_name, + phys_to_virt(get_unaligned(gc_fdt_phys))); + kfree(combined_name); + if (err) { + pr_warn("failed to add fdt %s-%s to debugfs: %pe\n", + name, gc_name, ERR_PTR(err)); + } + } } } =20 --=20 2.55.0.508.g3f0d502094-goog From nobody Fri Oct 2 12:19:59 2026 Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) (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 EAD32330307 for ; Fri, 31 Jul 2026 21:53:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534806; cv=none; b=MQPop6Am3/tsQC590f2+NohvkMl0NBrJFfqGA1lHTSrnRekDOk7JtoPYkDQmGkUbGepMPDeVGsJ2cWAYhVWuMSLD6ctONMhy28bsTTxvNVb4i1y4fWnYhgRdjOyBofXnmz6FjrSUUwnCkWjtHLz/kzOp+lDf212P/2/V0t2nVXU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785534806; c=relaxed/simple; bh=VtY+YB+7j7QIso1bFQwpw3RZIqQ74Jq6ZbuJHqo5SPk=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ailFaGm2JY0YKrN/JGWIIMuGC3AaCMRxpkR92cRrBk31Tw97ctYp8o9GD/agXNs9L+fTzQ1vuFDIedp5jZJNTiLtH/L96QJzD3Sbk4rXZFlCpL8dvJUpLt/f9mDoFUmPBcd2XqudS0dElPfPpBctR0UuXdhCuC1ljVGctQlLnrs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qKfEo2oQ; arc=none smtp.client-ip=209.85.216.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--loganodell.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qKfEo2oQ" Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-38f283baf1fso1411270a91.3 for ; Fri, 31 Jul 2026 14:53:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1785534804; x=1786139604; darn=vger.kernel.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:from:to:cc:subject:date:message-id:reply-to :content-type; bh=AKxRO57RwKQvgozbHnUQp+sB+wk0XehXYqNUOkWbMpk=; b=qKfEo2oQnow9bRMKdMkDvGBGnpursyhWPrO2dgyPp7CgXmbVqM6Svw2vt/lqF85AtH yykfiFrfMheEXxWFy8R/B4ezgEaxR7T2GnguV9kObgkRT6U6jXUys1ajhp45KJZSguzv KNoAKyKuPqqX8bqdQOJYT2nb8fMDBIPe8knWy7zQmrfR+6sf021uwA3wcc5/1WeH09S4 98spGHLwbh2h+hkPpGMvhcF4uv0+y2I93x76AmRDPGb6KbSDetumH6A5QkC7rI+LTt3J yH/yJ45fR+POeRV+9zOlD9Uo6MNx2mrpWqoU4u3PiR3n63qkMdRu8xqulJ3KJs+Rqn3F g9mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785534804; x=1786139604; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=AKxRO57RwKQvgozbHnUQp+sB+wk0XehXYqNUOkWbMpk=; b=qVg6sOKNwxI/RpnanSHEwzYSB0BBYFryPCJYeTcd0O2nh5BwTiIRewKM3sFSThXOer HpFy7vrhmKME3gfIFsjR4OKJ6DCAKCL7IylMPL4X+9dz0qZDFthzPAA4Q1t/2d5L5afF 4qxmzOBwjeqoFYfbcQomAKa1OT/St+FK5ES4ElOb4oBNI1CKp5NSbfFPGC6A4G4PvEi7 5GXmmH6yt9uYxA547JG0ytBkygepEPIsTb8hE4gWco0JIJdYcRaUs9VOlrNXgpIJ8qMr dowRLyP2oz3GJPXyfpn9W1FgL4J2mo8fH9lkjsJK2jL8adCWUZD9ZZbp+1dpG4E5ZAae zmeA== X-Forwarded-Encrypted: i=1; AHgh+RrJ8uF0rOAnCuAwuBc+PBRBDaZUQaNT/Gqo6RDF8UFPBUT0OJIZsmZECs/UCu2kk2lzwx1r15BY5i9Tpr0=@vger.kernel.org X-Gm-Message-State: AOJu0YywlJLiZHwoP7iQN5zrCTWRCwl734jn50TtFRddaO4jjCr40hSF xaZ22hWiCiDwCz0d66ei5cCHd/73VUt83I3kXutP4N6H7VhEhgrVzocbbEp751MZj1PodxINuwT H8uFS88kaR2zj0KoNwktt3A== X-Received: from dlan2-n1.prod.google.com ([2002:a05:7022:eb42:10b0:13b:9346:627a]) (user=loganodell job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4c41:b0:381:5a08:6291 with SMTP id 98e67ed59e1d1-38fbc4bc175mr1169108a91.20.1785534803987; Fri, 31 Jul 2026 14:53:23 -0700 (PDT) Date: Fri, 31 Jul 2026 14:52:24 -0700 In-Reply-To: <20260731215224.831696-1-loganodell@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260731215224.831696-1-loganodell@google.com> X-Mailer: git-send-email 2.55.0.508.g3f0d502094-goog Message-ID: <20260731215224.831696-5-loganodell@google.com> Subject: [RFC PATCH 4/4] kho: Add test cases for versioned subtrees From: Logan Odell To: graf@amazon.com, rppt@kernel.org, pasha.tatashin@soleen.com Cc: pratyush@kernel.org, akpm@linux-foundation.org, kexec@lists.infradead.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Logan Odell Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add new test cases to test_kho.c to verify saving and restoring multiple versions of a subtree. The test preserves versions 1 and 2 of "kho_test_ver" and retrieves them, verifying their contents. It also ensures no memory leaks occur during the restore process. Signed-off-by: Logan Odell --- lib/test_kho.c | 266 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 245 insertions(+), 21 deletions(-) diff --git a/lib/test_kho.c b/lib/test_kho.c index 7ef9e4061869..db531da63576 100644 --- a/lib/test_kho.c +++ b/lib/test_kho.c @@ -310,52 +310,276 @@ static int kho_test_restore(phys_addr_t fdt_phys) return 0; } =20 +static void kho_test_cleanup(void) +{ + /* unpreserve and free the data stored in folios */ + kho_test_unpreserve_data(&kho_test_state); + for (int i =3D 0; i < kho_test_state.nr_folios; i++) + folio_put(kho_test_state.folios[i]); + + kvfree(kho_test_state.folios); + + /* Unpreserve and release the FDT folio */ + kho_unpreserve_folio(kho_test_state.fdt); + folio_put(kho_test_state.fdt); +} + +#define KHO_TEST_VER_NAME "kho_test_ver" +#define KHO_TEST_VER_1 1 +#define KHO_TEST_VER_2 2 +#define KHO_TEST_VER_MAGIC_1 0x11111111 +#define KHO_TEST_VER_MAGIC_2 0x22222222 + +struct kho_test_versioned_state { + struct folio *fdt1; + struct folio *fdt2; +}; + +static struct kho_test_versioned_state kho_test_ver_state; + +static int kho_test_prepare_ver_fdt(struct folio *folio, int version, unsi= gned int magic) +{ + void *fdt =3D folio_address(folio); + int err; + + err =3D fdt_create(fdt, PAGE_SIZE); + err |=3D fdt_finish_reservemap(fdt); + err |=3D fdt_begin_node(fdt, ""); + err |=3D fdt_property(fdt, "compatible", KHO_TEST_COMPAT, sizeof(KHO_TEST= _COMPAT)); + err |=3D fdt_property(fdt, "magic", &magic, sizeof(magic)); + err |=3D fdt_property(fdt, "version", &version, sizeof(version)); + err |=3D fdt_end_node(fdt); + err |=3D fdt_finish(fdt); + + return err; +} + +static int kho_test_ver_save(void) +{ + struct kho_test_versioned_state *state =3D &kho_test_ver_state; + int err; + + state->fdt1 =3D folio_alloc(GFP_KERNEL, 0); + if (!state->fdt1) + return -ENOMEM; + + state->fdt2 =3D folio_alloc(GFP_KERNEL, 0); + if (!state->fdt2) { + err =3D -ENOMEM; + goto err_free_fdt1; + } + + err =3D kho_preserve_folio(state->fdt1); + if (err) + goto err_free_fdt2; + + err =3D kho_preserve_folio(state->fdt2); + if (err) + goto err_unpreserve_fdt1; + + err =3D kho_test_prepare_ver_fdt(state->fdt1, KHO_TEST_VER_1, KHO_TEST_VE= R_MAGIC_1); + if (err) + goto err_unpreserve_fdt2; + + err =3D kho_test_prepare_ver_fdt(state->fdt2, KHO_TEST_VER_2, KHO_TEST_VE= R_MAGIC_2); + if (err) + goto err_unpreserve_fdt2; + + err =3D kho_add_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_1, + folio_address(state->fdt1)); + if (err) + goto err_unpreserve_fdt2; + + err =3D kho_add_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_2, + folio_address(state->fdt2)); + if (err) + goto err_remove_ver1; + + pr_info("KHO versioned test save succeeded\n"); + return 0; + +err_remove_ver1: + kho_remove_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_1, folio_addre= ss(state->fdt1)); +err_unpreserve_fdt2: + kho_unpreserve_folio(state->fdt2); +err_unpreserve_fdt1: + kho_unpreserve_folio(state->fdt1); +err_free_fdt2: + folio_put(state->fdt2); + state->fdt2 =3D NULL; +err_free_fdt1: + folio_put(state->fdt1); + state->fdt1 =3D NULL; + return err; +} + +static int kho_test_ver_restore_one(phys_addr_t fdt_phys, int expected_ver= sion, + unsigned int expected_magic) +{ + void *fdt =3D phys_to_virt(fdt_phys); + const unsigned int *magic; + const int *version; + int node, len; + + node =3D fdt_path_offset(fdt, "/"); + if (node < 0) + return -EINVAL; + + if (fdt_node_check_compatible(fdt, node, KHO_TEST_COMPAT)) + return -EINVAL; + + magic =3D fdt_getprop(fdt, node, "magic", &len); + if (!magic || len !=3D sizeof(*magic) || *magic !=3D expected_magic) + return -EINVAL; + + version =3D fdt_getprop(fdt, node, "version", &len); + if (!version || len !=3D sizeof(*version) || *version !=3D expected_versi= on) + return -EINVAL; + + return 0; +} + +static int kho_test_ver_restore(void) +{ + phys_addr_t fdt_phys1, fdt_phys2; + struct folio *fdt1 =3D NULL, *fdt2 =3D NULL; + int err; + + err =3D kho_retrieve_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_1, &= fdt_phys1); + if (err) { + if (err !=3D -ENOENT) + pr_err("Failed to retrieve version 1: %d\n", err); + return err; + } + + fdt1 =3D kho_restore_folio(fdt_phys1); + if (!fdt1) { + pr_err("Failed to restore FDT 1 folio\n"); + return -EINVAL; + } + + err =3D kho_retrieve_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_2, &= fdt_phys2); + if (err) { + pr_err("Failed to retrieve version 2: %d\n", err); + goto out_put_fdt1; + } + + fdt2 =3D kho_restore_folio(fdt_phys2); + if (!fdt2) { + pr_err("Failed to restore FDT 2 folio\n"); + err =3D -EINVAL; + goto out_put_fdt1; + } + + err =3D kho_test_ver_restore_one(fdt_phys1, KHO_TEST_VER_1, KHO_TEST_VER_= MAGIC_1); + if (err) { + pr_err("Version 1 verification failed: %d\n", err); + goto out_put_fdt2; + } + + err =3D kho_test_ver_restore_one(fdt_phys2, KHO_TEST_VER_2, KHO_TEST_VER_= MAGIC_2); + if (err) { + pr_err("Version 2 verification failed: %d\n", err); + goto out_put_fdt2; + } + + pr_info("KHO versioned test restore succeeded\n"); + +out_put_fdt2: + folio_put(fdt2); +out_put_fdt1: + folio_put(fdt1); + return err; +} + +static void kho_test_ver_cleanup(void) +{ + struct kho_test_versioned_state *state =3D &kho_test_ver_state; + + if (state->fdt1) { + kho_unpreserve_folio(state->fdt1); + folio_put(state->fdt1); + state->fdt1 =3D NULL; + } + if (state->fdt2) { + kho_unpreserve_folio(state->fdt2); + folio_put(state->fdt2); + state->fdt2 =3D NULL; + } +} + +static void kho_test_ver_exit(void) +{ + struct kho_test_versioned_state *state =3D &kho_test_ver_state; + + if (state->fdt1) + kho_remove_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_1, + folio_address(state->fdt1)); + if (state->fdt2) + kho_remove_subtree_version(KHO_TEST_VER_NAME, KHO_TEST_VER_2, + folio_address(state->fdt2)); + kho_test_ver_cleanup(); +} + static int __init kho_test_init(void) { phys_addr_t fdt_phys; - int err; + int err, ver_err; + bool restored =3D false; =20 if (!kho_is_enabled()) return 0; =20 + // Try to restore non-versioned err =3D kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys); if (!err) { err =3D kho_test_restore(fdt_phys); - if (err) + if (err) { pr_err("KHO restore failed\n"); - else - pr_info("KHO restore succeeded\n"); - + return err; + } + pr_info("KHO restore succeeded\n"); + restored =3D true; + } else if (err !=3D -ENOENT) { + pr_warn("failed to retrieve %s FDT: %d\n", KHO_TEST_FDT, err); return err; } =20 - if (err !=3D -ENOENT) { - pr_warn("failed to retrieve %s FDT: %d\n", KHO_TEST_FDT, err); - return err; + // Try to restore versioned + ver_err =3D kho_test_ver_restore(); + if (!ver_err) { + restored =3D true; + } else if (ver_err !=3D -ENOENT) { + pr_warn("KHO versioned restore failed: %d\n", ver_err); + return ver_err; } =20 - return kho_test_save(); -} -module_init(kho_test_init); + if (restored) + return 0; =20 -static void kho_test_cleanup(void) -{ - /* unpreserve and free the data stored in folios */ - kho_test_unpreserve_data(&kho_test_state); - for (int i =3D 0; i < kho_test_state.nr_folios; i++) - folio_put(kho_test_state.folios[i]); + // If we got here, it means both returned -ENOENT (first boot). + // So we save both. =20 - kvfree(kho_test_state.folios); + err =3D kho_test_save(); + if (err) + return err; =20 - /* Unpreserve and release the FDT folio */ - kho_unpreserve_folio(kho_test_state.fdt); - folio_put(kho_test_state.fdt); + ver_err =3D kho_test_ver_save(); + if (ver_err) { + kho_remove_subtree(folio_address(kho_test_state.fdt)); + kho_test_cleanup(); + return ver_err; + } + + return 0; } +module_init(kho_test_init); =20 static void __exit kho_test_exit(void) { kho_remove_subtree(folio_address(kho_test_state.fdt)); kho_test_cleanup(); + kho_test_ver_exit(); } module_exit(kho_test_exit); =20 --=20 2.55.0.508.g3f0d502094-goog