From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 207EA2AD0C; Tue, 9 Sep 2025 20:15:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448940; cv=none; b=UeWFavAS8DthztEZkRs+nd7qVy+br+kx6jWBhwlWAvN8S88artcS6xsbLOQtmkgmxnRvw8ojE1oWNNyC+iE5cmiAsywTBi6BWNQIPsEDoeWaOrfc/bgU3Ok1IKzXCCZQOUGG27o6WL6G+qUs4p2YUiFVQN0szRHeGliX+DxHWRM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448940; c=relaxed/simple; bh=epzB1q1ey7mcrlZUHSpH2WU1v4WMKeXwXIlTlN92sAg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Rw/AehRm2FM+Ect1apdXVM41fuGIoP9MLqg76KBm8Xf1ZFFfCvz+x7pLTuE0qMcZwUrhe5cKqHLQKCCZxquCK4BQ3yoj+fAvHwdLnYXvU0PcIedb1GfGAkQUDGmax8GuKga2VsNz1Nbex/ru9o1uZVsZA5KHLiWYzq04GQBBNNA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=neGSZgoq; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="neGSZgoq" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id 03AC6C0178; Tue, 09 Sep 2025 23:15:29 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-l4Vy856v; Tue, 09 Sep 2025 23:15:28 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448928; bh=BrTg0k8lCV2HUq6REznRngkexlNL5x/b5e/pmlQ15ZI=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=neGSZgoqXYXwpLcgher3hOxnbUXCh+LnfyKfxD6OWkli/tXddCyRc7h06ZF74m86Z KOW7OV80CpU3W0NMV/XxwboV0qA0Cd5wpuLTqhRluGsQJYAxSqi2Imnkg+R/1mMtyN lhAVl+kpBSt43LU/t9BrjK5i3nl0JtZpUGJwqX/w= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 1/7] kho: move fdt setup in separate helper. Date: Tue, 9 Sep 2025 22:14:36 +0200 Message-ID: <20250909201446.13138-2-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.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" Move FDT initialization logic from kho_populate() into a new helper function kho_fdt_init(). The helper takes care of mapping, validating, and unmapping the FDT. This prepares for using KSTATE in KHO instead of FDT. Signed-off-by: Andrey Ryabinin --- kernel/liveupdate/kexec_handover.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index 364a69a2fa1b..e5a436893f5b 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -1077,13 +1077,10 @@ void __init kho_memory_init(void) } } =20 -void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, - phys_addr_t scratch_phys, u64 scratch_len) +static int __init kho_fdt_init(phys_addr_t fdt_phys, u64 fdt_len) { void *fdt =3D NULL; - struct kho_scratch *scratch =3D NULL; int err =3D 0; - unsigned int scratch_cnt =3D scratch_len / sizeof(*kho_scratch); =20 /* Validate the input FDT */ fdt =3D early_memremap(fdt_phys, fdt_len); @@ -1107,6 +1104,26 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 f= dt_len, goto out; } =20 +out: + if (fdt) + early_memunmap(fdt, fdt_len); + + return err; +} + +void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, + phys_addr_t scratch_phys, u64 scratch_len) +{ + + struct kho_scratch *scratch =3D NULL; + int err =3D 0; + unsigned int scratch_cnt =3D scratch_len / sizeof(*kho_scratch); + + + err =3D kho_fdt_init(fdt_phys, fdt_len); + if (err) + goto out; + scratch =3D early_memremap(scratch_phys, scratch_len); if (!scratch) { pr_warn("setup: failed to memremap scratch (phys=3D0x%llx, len=3D%lld)\n= ", @@ -1151,8 +1168,6 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fd= t_len, pr_info("found kexec handover data. Will skip init for some devices\n"); =20 out: - if (fdt) - early_memunmap(fdt, fdt_len); if (scratch) early_memunmap(scratch, scratch_len); if (err) --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 930B020C023; Tue, 9 Sep 2025 20:15:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448939; cv=none; b=c+i/lIuLPoKtic/a8X4G+VOP5uB31YxEZdI58TnUY8NUbPvSGPk4HAWYgAraON4HGmcOL0p8qjzssuM236BmoZfIpZ6SlWIl4glx7MaS5HxV7Fi8YEc0wk3aXE0z0rtv3lDw/K9qsEEnio5ZxqZNYRrzORfFirLyW3zaMzf7T9I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448939; c=relaxed/simple; bh=7XelOSuRtMumCFS1W544pv1aQaXtEUDaqg7xGeLRhEw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RJB32aWbNa2uoHbGrGnsH/j1teS90yjSxsVBPQZPZ64K8x7v686R17AXsTEdJnAtkuVY7SCFjyCHgK7oo9zGqzPxxoZuWJvfq+CJH+OPeoNHWodKdA/SthejXF+JHKc/kEYpWbHFgqRkLwXkz7w2St56ECakY+WDatFisRFgfbQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=tUHIM9Il; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="tUHIM9Il" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id 93B5EC017C; Tue, 09 Sep 2025 23:15:32 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-lKmQ6Yfi; Tue, 09 Sep 2025 23:15:32 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448932; bh=v6j9zrtMltmIoN7tcvqfcj39ZzhThRROT8000Oa99k0=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=tUHIM9IlFhPrnvOXOM+8JQ/i0mj302skwkptt6zMP16DhVcdP4tW4deIOK4JJP3yD mN3ebdg4uVK1ZkH0Io+gOBUGzVZlGDfE8Uh2rX0ZDfqoO6ePTTS6rxm/DASPVuY9h0 iwbPtIEVLaRWS89T8gLAY0+3oaK1BizzhT9GYSH4= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 2/7] kho: move scratch memory in separate helper. Date: Tue, 9 Sep 2025 22:14:37 +0200 Message-ID: <20250909201446.13138-3-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.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" Move scratch memory initialization logic from kho_populate() into a new helper function kho_scrath_init(). This prepares for using KSTATE in KHO instead of FDT. Signed-off-by: Andrey Ryabinin --- kernel/liveupdate/kexec_handover.c | 34 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index e5a436893f5b..a99425fdfce4 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -1111,19 +1111,12 @@ static int __init kho_fdt_init(phys_addr_t fdt_phys= , u64 fdt_len) return err; } =20 -void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, - phys_addr_t scratch_phys, u64 scratch_len) +static int __init kho_scratch_init(phys_addr_t scratch_phys, u64 scratch_l= en) { - - struct kho_scratch *scratch =3D NULL; int err =3D 0; + struct kho_scratch *scratch =3D NULL; unsigned int scratch_cnt =3D scratch_len / sizeof(*kho_scratch); =20 - - err =3D kho_fdt_init(fdt_phys, fdt_len); - if (err) - goto out; - scratch =3D early_memremap(scratch_phys, scratch_len); if (!scratch) { pr_warn("setup: failed to memremap scratch (phys=3D0x%llx, len=3D%lld)\n= ", @@ -1161,6 +1154,27 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 f= dt_len, * memory reservations from the previous kernel. */ memblock_set_kho_scratch_only(); +out: + if (scratch) + early_memunmap(scratch, scratch_len); + + return err; +} + +void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, + phys_addr_t scratch_phys, u64 scratch_len) +{ + + int err =3D 0; + unsigned int scratch_cnt =3D scratch_len / sizeof(*kho_scratch); + + err =3D kho_fdt_init(fdt_phys, fdt_len); + if (err) + goto out; + + err =3D kho_scratch_init(scratch_phys, scratch_len); + if (err) + goto out; =20 kho_in.fdt_phys =3D fdt_phys; kho_in.scratch_phys =3D scratch_phys; @@ -1168,8 +1182,6 @@ void __init kho_populate(phys_addr_t fdt_phys, u64 fd= t_len, pr_info("found kexec handover data. Will skip init for some devices\n"); =20 out: - if (scratch) - early_memunmap(scratch, scratch_len); if (err) pr_warn("disabling KHO revival: %d\n", err); } --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA0151D799D; Tue, 9 Sep 2025 20:15:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448941; cv=none; b=u4T5s4R54Z5c9G9xtkVF0lGH1MIrURPVR8VyjUQ1ChA6CB/0olYD/0d+c9bey9ivs5anW0GcOnmd9SZAZjnrG/inZYBlWzIltwFsK+HgwpkIVzgoRt/YiJqIqaGEh1gjcoCSDRRGTMBShUr5SJABkFY62grGP/WUZwh4s2WUH5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448941; c=relaxed/simple; bh=vBGVBxnt2qFJN3czmlabugKsWtSrPKu++3u8P//VnKU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=B/lIbidG7BP9Sd/epEUvEeR1sc2jonYYJPklkQUVnCwka8Va+s4vGcSMwPCGZIoJOdcMjkKLQJvJsWWk2v3T66blucxVvBx8youSIWUM3K1VJxNfWs4mu6581SOShr5ho99GJ6YIz+gFd/OefwBu6Wvv8OXX6+gNIdLrX6Orc/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=LLoW/OFB; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="LLoW/OFB" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id E4B18C0199; Tue, 09 Sep 2025 23:15:36 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-2VgA54Rz; Tue, 09 Sep 2025 23:15:35 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448936; bh=/c7esRNmCAfQ76UO4jeOQ25FDN89DzqZ6jGwQAoMGHI=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=LLoW/OFBYKbCw94LOyHDEROGdNEcEyETC7TmDLkpL6I/LhPNow0PEUvnd9LvLNw89 Tykobr/CP5UErMO/llY79wJUvHBeWNdzN+kHlX5x9MYt+1v4PM15zUNFJ1+z8jeaou NGpLeCf3nkbl+5SCzmakKg9vcr9pPJ7uX92d3d5U= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 3/7] kstate: Add KSTATE - [de]serialization framework for KHO Date: Tue, 9 Sep 2025 22:14:38 +0200 Message-ID: <20250909201446.13138-4-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable KSTATE (kernel state) is a mechanism to describe some part of the internal kernel state, save it into the memory preserved by KHO and restore the state after kexec in the new kernel. The end goal here is to be able to update host kernel under VMs with VFIO pass-through devices running on that host. This implies that we need svae/restore a lot different structs/state across different subsyst= ems. The purpose of KSTATE is to provide common infrastructure for saving/restor= ing complex in-kernel states. Currently KHO uses FDT for that purpose, KSTATE aims to provide easier for use alternative. In this series KSTATE provides alternative to FDT usage in KHO, without replacing it completely. So both can be used and FDT user can be converted to KSTATE later if needed. The idea behind KSTATE resembles QEMU's migration framework [1], which solves quite similar problem - migrate state of VM/emulated devices across different versions of QEMU. State of kernel data (usually it's some struct) is described by the 'struct kstate_description' containing the array of individual fields descpriptions - 'struct kstate_field'. Each field has set of bits in ->flags which instructs how to save/restore a certain field of the struct. E.g.: - KS_BASE_TYPE flag tells that field can be just copied by value, - KS_POINTER means that the struct member is a pointer to the actual data, so it needs to be dereference before saving/restoring data to/from kstate data steam. - KS_STRUCT - contains another struct, field->ksd must point to another 'struct kstate_dscription' - KS_CUSTOM - Some non-trivial field that requires custom kstate_field->s= ave() ->restore() callbacks to save/restore data. - KS_ARRAY_OF_POINTER - array of pointers, the size of array determined b= y the field->count() callback - KS_ADDRESS - field is a pointer to either vmemmap area (struct page) or linear address. Stored as offset from the base address. - KS_END - special flag indicating the end of migration stream data. kstate_register() call accepts kstate_description along with an instance of an object and registers it in the global 'states' list. During 'finalize' phase of KHO we go through the list of 'kstate_descriptio= n's and each instance of kstate_description forms the 'struct kstate_entry' which save into the kstate's data stream. The 'kstate_entry' contains information like ID of kstate_description, vers= ion of it, size of migration data and the data itself. The ->data is formed in accordance to the kstate_field's of the corresponding kstate_description. After the reboot, when the kstate_restore() called it parses KSTATE's data stream, finds the appropriate 'kstate_entry' and restores the contents of the object in accordance with kstate_description and ->fields. [1] https://www.qemu.org/docs/master/devel/migration/main.html#vmstate Signed-off-by: Andrey Ryabinin --- MAINTAINERS | 6 + include/linux/kstate.h | 231 +++++++++++++ kernel/liveupdate/Kconfig | 8 + kernel/liveupdate/Makefile | 2 + kernel/liveupdate/kexec_handover.c | 4 + kernel/liveupdate/kstate.c | 536 +++++++++++++++++++++++++++++ 6 files changed, 787 insertions(+) create mode 100644 include/linux/kstate.h create mode 100644 kernel/liveupdate/kstate.c diff --git a/MAINTAINERS b/MAINTAINERS index 8296bf89e040..2cd9e49abee5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13718,6 +13718,12 @@ F: Documentation/admin-guide/auxdisplay/ks0108.rst F: drivers/auxdisplay/ks0108.c F: include/linux/ks0108.h =20 +KSTATE +M: Andrey Ryabinin +S: Maintained +F: include/linux/kstate.h +F: kernel/livupdate/kstate.c + KTD253 BACKLIGHT DRIVER M: Linus Walleij S: Maintained diff --git a/include/linux/kstate.h b/include/linux/kstate.h new file mode 100644 index 000000000000..53992593cb19 --- /dev/null +++ b/include/linux/kstate.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _KSTATE_H +#define _KSTATE_H + +#include +#include +#include +#include + +struct kstate_description; +struct kstate_stream; +struct kimage; + +enum kstate_flags { + + /* + * The struct member at 'obj + kstate_field.offset' is some basic + * type, just copy it by value. The size is kstate_field->size. + */ + + KS_BASE_TYPE =3D (1 << 0), + + /* + * The struct member at 'obj + kstate_field.offset' is a pointer + * to the actual data (e.g. struct a { int *b; }). + * save_kstate() will dereference the pointer to get the actual data + * and store it to the stream. restore_kstate() will copy the data from + * the stream to wherever the pointer points to. + */ + KS_POINTER =3D (1 << 1), + + /* + * The struct member at 'obj + kstate_field.offset' is another struct. + * kstate_field->ksd points to 'kstate_description' of that struct. + */ + KS_STRUCT =3D (1 << 2), + + /* + * Some non-trivial field that requires custom kstate_field->save() + * ->restore() callbacks to save/restore data. + */ + KS_CUSTOM =3D (1 << 3), + + /* + * The field is a array of kstate_field->count() pointers + * (e.g. struct a { uint8_t *b[]; }). Dereference each array entry + * before store/restore data. + */ + KS_ARRAY_OF_POINTER =3D (1 << 4), + + /* + * The field is a pointer to vmemmap or linear memory (determined by + * kstate_field->addr_type). This is used for pointers to persistent + * pages/data. Store offset from the start of the area instead of + * pointer itself, so we could defeat KASLR on restore phase (by adding + * new kernel's corresponding offset). + */ + KS_ADDRESS =3D (1 << 5), + + /* + * The field used to exist in older versions. kstate_field->version_id + * is latest version that have this field. + */ + KS_DEPRECATED =3D (1 << 6), + + /* Marks the end of fields list */ + KS_END =3D (1UL << 31), +}; + +enum kstate_addr_type { + KS_VMEMMAP_ADDR, + KS_LINEAR_ADDR, +}; + +struct kstate_stream { + void *pos; + struct folio *folio; +}; + +struct kstate_field { + const char *name; + size_t offset; + size_t size; + enum kstate_flags flags; + const struct kstate_description *ksd; + enum kstate_addr_type addr_type; + int version_id; + int (*restore)(struct kstate_stream *stream, void *obj, + const struct kstate_field *field); + int (*save)(struct kstate_stream *stream, void *obj, + const struct kstate_field *field); + int (*count)(void); +}; + +enum kstate_ids { + KSTATE_FOLIO_ID =3D 1, + KSTATE_LAST_ID =3D -1, +}; + +struct kstate_description { + const char *name; + enum kstate_ids id; + atomic_t instance_id; + int version_id; + int min_version_id; + + const struct kstate_field *fields; + const struct kstate_description **subsections; +}; + +struct state_entry { + u64 id; + struct list_head list; + struct kstate_description *kstd; + void *obj; +}; + +static inline bool kstate_get_byte(struct kstate_stream *stream) +{ + bool ret =3D *(u8 *)stream->pos; + + stream->pos++; + return ret; +} + +static inline unsigned long kstate_get_ulong(struct kstate_stream *stream) +{ + unsigned long ret =3D *(unsigned long *)stream->pos; + + stream->pos +=3D sizeof(unsigned long); + return ret; +} + +extern struct kstate_description page_state; + +#ifdef CONFIG_KSTATE + +extern phys_addr_t kstate_out_paddr; + +int kstate_save_state(void); +void free_kstate_stream(void); + +int kstate_save_data(struct kstate_stream *stream, const void *val, + size_t size); +void kstate_restore_data(struct kstate_stream *stream, void *val, size_t s= ize); +int kstate_register(struct kstate_description *state, void *obj, int id); +void kstate_unregister(struct kstate_description *state, void *obj, int id= ); +int kstate_restore(struct kstate_description *state, void *obj, int id); +int kstate_register_restore(struct kstate_description *state, void *obj); + +struct kstate_entry; + +int kstate_folio_save(struct kstate_stream *stream, void *obj, + const struct kstate_field *field); +int kstate_folio_restore(struct kstate_stream *stream, void *obj, + const struct kstate_field *field); + +int kstate_abort(void); +int kstate_finalize(void); + +int kstate_early_init(phys_addr_t kstate_entries, u64 len); + +#else + +#define kstate_register(state, obj) + +static inline int kstate_save_data(struct kstate_stream *stream, + const void *val, size_t size) +{ + return 0; +} +static inline void kstate_restore_data(struct kstate_stream *stream, void = *val, + size_t size) +{ +} + +#endif + +#define KSTATE_BASE_TYPE_V(_f, _state, _type, _v) { \ + .name =3D (__stringify(_f)), \ + .version_id =3D (_v), \ + .size =3D sizeof(_type) + BUILD_BUG_ON_ZERO( \ + !__same_type(typeof_member(_state, _f), _type)),\ + .flags =3D KS_BASE_TYPE, \ + .offset =3D offsetof(_state, _f), \ +} + +#define KSTATE_BASE_TYPE(_f, _state, _type) \ + KSTATE_BASE_TYPE_V(_f, _state, _type, 0) + +#define KSTATE_BASE_TYPE_DEPRECATED(_f, _type, _v) { \ + .name =3D (__stringify(_f)), \ + .version_id =3D (_v), \ + .size =3D sizeof(_type), \ + .flags =3D KS_DEPRECATED, \ +} + +#define KSTATE_POINTER_V(_f, _state, _v) { \ + .name =3D (__stringify(_f)), \ + .version_id =3D (_v), \ + .size =3D sizeof(*(((_state *)0)->_f)), \ + .flags =3D KS_POINTER, \ + .offset =3D offsetof(_state, _f), \ + } + +#define KSTATE_POINTER(_f, _state) KSTATE_POINTER_V(_f, _state, 0) + +#define KSTATE_ADDRESS_V(_f, _state, _addr_type, _v) { \ + .name =3D (__stringify(_f)), \ + .version_id =3D (_v), \ + .size =3D sizeof(*(((_state *)0)->_f)), \ + .addr_type =3D (_addr_type), \ + .flags =3D KS_ADDRESS, \ + .offset =3D offsetof(_state, _f), \ + } +#define KSTATE_ADDRESS(_f, _state, _addr_type) \ + KSTATE_ADDRESS_V(_f, _state, _addr_type, 0) + +#define KSTATE_FOLIO(_f, _state) { \ + .name =3D "folio", \ + .flags =3D KS_CUSTOM, \ + .offset =3D offsetof(_state, _f), \ + .save =3D kstate_folio_save, \ + .restore =3D kstate_folio_restore, \ + } + +#define KSTATE_END_OF_LIST() { \ + .flags =3D KS_END, \ + } + +#endif diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index 5be04ede357d..b6ea861006bf 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -62,12 +62,20 @@ config LIVEUPDATE_SELFTESTS =C2=A0 If you are unsure or are building a production kernel where size =C2=A0 or attack surface is a concern, say N. =20 +config KSTATE + bool + help + KSTATE (kernel state) is a mechanism to describe internal kernel + state, save it into the memory and restore the state after kexec + in new kernel. + config KEXEC_HANDOVER bool "kexec handover" depends on ARCH_SUPPORTS_KEXEC_HANDOVER && ARCH_SUPPORTS_KEXEC_FILE depends on !DEFERRED_STRUCT_PAGE_INIT select MEMBLOCK_KHO_SCRATCH select KEXEC_FILE + select KSTATE select DEBUG_FS select LIBFDT select CMA diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile index 9b8b69517463..0dc5122147d0 100644 --- a/kernel/liveupdate/Makefile +++ b/kernel/liveupdate/Makefile @@ -12,6 +12,8 @@ luo-y :=3D \ obj-$(CONFIG_KEXEC_HANDOVER) +=3D kexec_handover.o obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) +=3D kexec_handover_debug.o =20 +obj-$(CONFIG_KSTATE) +=3D kstate.o + obj-$(CONFIG_LIVEUPDATE) +=3D luo.o obj-$(CONFIG_LIVEUPDATE_SELFTESTS) +=3D luo_selftests.o obj-$(CONFIG_LIVEUPDATE_SYSFS_API) +=3D luo_sysfs.o diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index a99425fdfce4..f1c6378b2657 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -885,6 +886,9 @@ int kho_finalize(void) ret =3D -EEXIST; goto unlock; } + ret =3D kstate_finalize(); + if (ret) + goto unlock; =20 ret =3D __kho_finalize(); if (ret) diff --git a/kernel/liveupdate/kstate.c b/kernel/liveupdate/kstate.c new file mode 100644 index 000000000000..dde40899fd99 --- /dev/null +++ b/kernel/liveupdate/kstate.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KSTATE_MAGIC 0x3B37778C + +static DEFINE_MUTEX(states_lock); +static LIST_HEAD(states); + +phys_addr_t kstate_out_paddr; + +void *kstate_stream_addr; + +struct kstate_entry { + int section_type; + int state_id; + int version_id; + int instance_id; + int size; + DECLARE_FLEX_ARRAY(u8, data); +}; + +struct kstate_stream kstate_stream; + +enum { + KS_SUBSECTION =3D 1, + KS_SECTION, + KS_EOF =3D -1, +}; + +static unsigned long get_addr_offset(const struct kstate_field *field) +{ + switch (field->addr_type) { + case KS_VMEMMAP_ADDR: + return VMEMMAP_START; + case KS_LINEAR_ADDR: + return PAGE_OFFSET; + default: + WARN_ON(1); + } + return 0; +} + +static struct folio *folio_realloc(struct folio *folio, int new_order, + gfp_t gfp_mask) +{ + struct folio *new_folio =3D folio_alloc(GFP_KERNEL, new_order); + + if (!new_folio) + return NULL; + + memcpy(folio_address(new_folio), folio_address(folio), folio_size(folio)); + folio_put(folio); + return new_folio; +} + +static int alloc_space(struct kstate_stream *stream, size_t size) +{ + int new_order; + struct folio *new_folio; + size_t cur_size =3D stream->pos - folio_address(stream->folio); + + size =3D size + 4; /* Always alloc extra for KS_EOF */ + if (cur_size + size < folio_size(stream->folio)) + return 0; + + new_order =3D get_order(cur_size) + 1; + + new_folio =3D folio_realloc(stream->folio, new_order, GFP_KERNEL); + if (!new_folio) + return -ENOMEM; + + stream->pos =3D folio_address(stream->folio) + cur_size; + return 0; +} + +int kstate_save_data(struct kstate_stream *stream, const void *val, + size_t size) +{ + int ret; + + ret =3D alloc_space(stream, size); + if (ret) + return ret; + memcpy(stream->pos, val, size); + stream->pos +=3D size; + return 0; +} + +static int save_kstate(struct kstate_stream *stream, int id, + const struct kstate_description *kstate, + void *obj, int section_type) +{ + const struct kstate_field *field =3D kstate->fields; + struct kstate_entry *ke; + unsigned long ke_off; + int ret =3D 0; + + ret =3D alloc_space(stream, sizeof(*ke)); + if (ret) + goto err; + + ke_off =3D stream->pos - folio_address(stream->folio); + ke =3D stream->pos; + stream->pos +=3D sizeof(*ke); + + ke->section_type =3D section_type; + ke->state_id =3D kstate->id; + ke->version_id =3D kstate->version_id; + ke->instance_id =3D id; + + while (field->flags !=3D KS_END) { + void *first, *cur; + int n_elems =3D 1; + int size, i; + + first =3D obj + field->offset; + /* Fields of higher versions shouldn't exist */ + if (WARN_ON(field->version_id > kstate->version_id)) { + field++; + continue; + } + if (field->flags & KS_DEPRECATED) { + field++; + continue; + } + + if (field->flags & KS_POINTER) + first =3D *(void **)(obj + field->offset); + if (field->count) + n_elems =3D field->count(); + size =3D field->size; + for (i =3D 0; i < n_elems; i++) { + cur =3D first + i * size; + + if (field->flags & KS_ARRAY_OF_POINTER) + cur =3D *(void **)cur; + + if (field->flags & KS_STRUCT) { + ret =3D save_kstate(stream, 0, field->ksd, cur, section_type); + if (ret) + goto err; + } else if (field->flags & KS_CUSTOM) { + if (field->save) { + ret =3D field->save(stream, cur, field); + if (ret) + goto err; + } + } else if (field->flags & (KS_BASE_TYPE|KS_POINTER)) { + ret =3D kstate_save_data(stream, cur, size); + if (ret) + goto err; + } else if (field->flags & KS_ADDRESS) { + void *addr_offset =3D *(void **)cur + - get_addr_offset(field); + ret =3D kstate_save_data(stream, &addr_offset, + sizeof(addr_offset)); + if (ret) + goto err; + } else + WARN_ON_ONCE(1); + } + field++; + + } + + ke =3D folio_address(stream->folio) + ke_off; + ke->size =3D (stream->pos - folio_address(stream->folio)) - (ke_off + siz= eof(*ke)); +err: + if (ret) + pr_err("kstate: save of state %s failed\n", kstate->name); + + return ret; +} + +static int save_kstates(struct kstate_stream *stream, int id, + const struct kstate_description *kstate, + void *obj) +{ + int ret =3D 0; + const struct kstate_description *const *section; + + ret =3D save_kstate(stream, id, kstate, obj, KS_SECTION); + if (ret) + return ret; + + if (!kstate->subsections) + return ret; + + section =3D kstate->subsections; + while (*section) { + ret =3D save_kstate(stream, id, *section, obj, KS_SUBSECTION); + if (ret) + break; + section++; + } + + return ret; +} + +static int alloc_kstate_stream(void) +{ + struct folio *folio; + u32 *buf; + + folio =3D folio_alloc(GFP_KERNEL, 0); + if (!folio) + return -ENOMEM; + + buf =3D folio_address(folio); + *buf++ =3D KSTATE_MAGIC; + kstate_stream.pos =3D buf; + kstate_stream.folio =3D folio; + return 0; +} + +void free_kstate_stream(void) +{ + if (kstate_stream.folio) + folio_put(kstate_stream.folio); + + kstate_stream.folio =3D NULL; + kstate_stream.pos =3D NULL; +} + +int kstate_save_state(void) +{ + struct state_entry *se; + struct kstate_entry *ke; + int err =3D 0; + + err =3D alloc_kstate_stream(); + if (err) + return err; + + mutex_lock(&states_lock); + list_for_each_entry(se, &states, list) { + err =3D save_kstates(&kstate_stream, se->id, se->kstd, se->obj); + if (err) + goto out; + } + ke =3D kstate_stream.pos; + ke->section_type =3D KS_EOF; +out: + mutex_unlock(&states_lock); + if (err) + free_kstate_stream(); + return err; +} + +void kstate_restore_data(struct kstate_stream *stream, void *val, size_t s= ize) +{ + memcpy(val, stream->pos, size); + stream->pos +=3D size; +} + +static void restore_kstate(struct kstate_stream *stream, int id, + const struct kstate_description *kstate, void *obj) +{ + const struct kstate_field *field =3D kstate->fields; + struct kstate_entry *ke =3D stream->pos; + + stream->pos =3D ke->data; + + WARN_ONCE(ke->version_id !=3D kstate->version_id, "version mismatch %d %d= \n", + ke->version_id, kstate->version_id); + + WARN_ONCE(ke->instance_id !=3D id, "instance id mismatch %d %d\n", + ke->instance_id, id); + + while (field->flags !=3D KS_END) { + void *first, *cur; + int n_elems =3D 1; + int size, i; + + if (field->version_id > ke->version_id) { + field++; + continue; + } + if (field->flags & KS_DEPRECATED) { + if (ke->version_id <=3D field->version_id) + stream->pos +=3D field->size; + field++; + continue; + } + + first =3D obj + field->offset; + if (field->flags & KS_POINTER) + first =3D *(void **)(obj + field->offset); + if (field->count) + n_elems =3D field->count(); + size =3D field->size; + for (i =3D 0; i < n_elems; i++) { + cur =3D first + i * size; + + if (field->flags & KS_ARRAY_OF_POINTER) + cur =3D *(void **)cur; + + if (field->flags & KS_STRUCT) + restore_kstate(stream, 0, field->ksd, cur); + else if (field->flags & KS_CUSTOM) { + if (field->restore) + field->restore(stream, cur, field); + } else if (field->flags & (KS_BASE_TYPE | KS_POINTER)) { + memcpy(cur, stream->pos, size); + stream->pos +=3D size; + } else if (field->flags & KS_ADDRESS) { + *(void **)cur =3D (*(void **)stream->pos) + + get_addr_offset(field); + stream->pos +=3D sizeof(void *); + } else + WARN_ON_ONCE(1); + + } + field++; + } +} + +static struct kstate_entry *find_subsection(struct kstate_stream *stream, = int id) +{ + struct kstate_entry *ke =3D stream->pos; + + while (ke->section_type =3D=3D KS_SUBSECTION) { + if (ke->state_id =3D=3D id) + return ke; + + ke =3D (struct kstate_entry *)(ke->data + ke->size); + } + return NULL; +} + +static void subsection_load(struct kstate_stream *stream, int id, + struct kstate_description *ksd, void *obj) +{ + struct kstate_entry *start_ke, *ke; + const struct kstate_description *const *section; + + if (!ksd->subsections) + return; + + start_ke =3D stream->pos; + section =3D ksd->subsections; + while (*section) { + stream->pos =3D start_ke; + ke =3D find_subsection(stream, (*section)->id); + if (ke) { + stream->pos =3D ke; + restore_kstate(stream, id, *section, obj); + } + section++; + } +} + +int kstate_restore(struct kstate_description *state, void *obj, int id) +{ + struct kstate_stream stream; + struct kstate_entry *ke; + + if (kstate_stream_addr =3D=3D NULL) + return -ENOENT; + + if (*(u32 *)kstate_stream_addr !=3D KSTATE_MAGIC) { + kstate_stream_addr =3D NULL; + return -ENOENT; + } + + ke =3D (struct kstate_entry *)(kstate_stream_addr + sizeof(u32)); + if (WARN_ON_ONCE(ke->state_id =3D=3D 0)) + return -ENOENT; + + stream.pos =3D ke; + while (ke->section_type !=3D KS_EOF) { + if (ke->state_id !=3D state->id || + ke->instance_id !=3D id) { + ke =3D (struct kstate_entry *)(ke->data + ke->size); + continue; + } + stream.pos =3D ke; + restore_kstate(&stream, id, state, obj); + ke =3D (struct kstate_entry *)(ke->data + ke->size); + subsection_load(&stream, id, state, obj); + return 0; + } + return -ENOENT; +} + +int kstate_register(struct kstate_description *state, void *obj, int id) +{ + struct state_entry *se; + + se =3D kmalloc(sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + se->kstd =3D state; + se->id =3D id; + se->obj =3D obj; + + mutex_lock(&states_lock); + list_add(&se->list, &states); + mutex_unlock(&states_lock); + return 0; +} + +void kstate_unregister(struct kstate_description *state, void *obj, int id) +{ + struct state_entry *se, *tmp; + + mutex_lock(&states_lock); + list_for_each_entry_safe(se, tmp, &states, list) { + if (se->id =3D=3D id && se->obj =3D=3D obj) { + list_del(&se->list); + break; + } + } + mutex_unlock(&states_lock); + kfree(se); +} + +int kstate_register_restore(struct kstate_description *state, void *obj) +{ + int id =3D atomic_inc_return(&state->instance_id); + + kstate_register(state, obj, id); + return kstate_restore(state, obj, id); +} + +int kstate_folio_restore(struct kstate_stream *stream, void *obj, + const struct kstate_field *field) +{ + phys_addr_t paddr; + struct folio *folio; + + kstate_restore_data(stream, &paddr, sizeof(paddr)); + folio =3D kho_restore_folio(paddr); + if (!folio) + return -ENOENT; + + *(struct folio **)obj =3D folio; + return 0; +} + +int kstate_folio_save(struct kstate_stream *stream, void *obj, + const struct kstate_field *field) +{ + struct folio *folio =3D *(struct folio **)obj; + phys_addr_t paddr =3D PFN_PHYS(folio_pfn(folio)); + int ret; + + ret =3D kstate_save_data(stream, &paddr, sizeof(paddr)); + if (ret) + return ret; + + return kho_preserve_folio(folio); +} + + +struct kstate_out { + union { + phys_addr_t kstate_paddr; + u8 data[PAGE_SIZE]; + }; +}; + +int kstate_abort(void) +{ + free_kstate_stream(); + return 0; +} + +int kstate_finalize(void) +{ + int err =3D 0; + struct kstate_out *kstate_out =3D phys_to_virt(kstate_out_paddr); + struct folio *kstate_out_folio =3D page_folio(phys_to_page(kstate_out_pad= dr)); + + err =3D kstate_save_state(); + if (err) + return err; + + err =3D kho_preserve_folio(kstate_out_folio); + if (err) + goto out_save_state; + + err =3D kho_preserve_folio(kstate_stream.folio); + if (err) + goto out; + + kstate_out->kstate_paddr =3D PFN_PHYS(folio_pfn(kstate_stream.folio)); +out: + if (err) + kho_unpreserve_folio(kstate_out_folio); +out_save_state: + if (err) + free_kstate_stream(); + + return err; +} + +static int __init kstate_init(void) +{ + struct page *page; + int err; + + if (!kho_is_enabled()) + return 0; + + page =3D alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) + return -ENOMEM; + + kstate_out_paddr =3D page_to_phys(page); + return err; +} +late_initcall(kstate_init); + +int __init kstate_early_init(phys_addr_t kstate_entries, u64 len) +{ + struct kstate_out *kstate_out; + + kstate_out =3D early_memremap(kstate_entries, len); + if (!kstate_out) { + pr_err("%s failed\n", __func__); + return -ENOMEM; + } + kstate_stream_addr =3D phys_to_virt(kstate_out->kstate_paddr); + early_memunmap(kstate_out, len); + return 0; +} --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2A3B335E4E1; Tue, 9 Sep 2025 20:15:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448945; cv=none; b=Bm6aq2X5bfV4gJrmfLjaY1GuSK+vygd+dOCzpGDCZvTUYszC3w40Zi7M/r93+jRtZ2HB/U07CmsRfUzebsSn/fD/xXDlUTiozIwCRbVMCgqj1a95WSd6OCxbo/G6BkdeRrNNDX4nSiO42BDs2+uX4unfuidKGPR6THqTbrSpQSo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448945; c=relaxed/simple; bh=VvhyADt438BmE4NVshaXV0a6MM/XloFgZ/1b2JkQT5I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IbugUg9vnWRwkHCafS/lFD1N6ITyMxUHXAFIzq6SPjb1hm1ZpeHhpyhsSiOPhU5DPedZFb7STEPDcLak4M6gs49hzwKSkV2wsr5+K2MstXSRV9Qw0ijAj/rqy3Om2Bmkq6AoGGFYuvkyNPniTeKycOlIhokj/vfF9d+hSwIt2Ss= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=uEzS3uie; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="uEzS3uie" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id 7FC46C01A1; Tue, 09 Sep 2025 23:15:41 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-KRuqVmhs; Tue, 09 Sep 2025 23:15:40 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448940; bh=zCmAQ9E1zzf2oww3L+0ScmydA57h//TWKN5Ptw1aNtc=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=uEzS3uievKUGgVzVo0XM3CO7O2ofI0YqYncCVzgEakehhvnc4qFhCFRVQGZevHwi7 CsQOMPEcV3YUF+z629p8DXGKYRKMBn5aPJVuffYv3pT3WEedSZtyRhs7RNlM2tFIQO Rai9V/CC6os2Ibdv73XbRLEYv8Q0/+Dnk2oRckgk= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin , Rob Herring , Saravana Kannan , devicetree@vger.kernel.org Subject: [PATCH v3 4/7] kho: replace KHO FDT with kstate metadata Date: Tue, 9 Sep 2025 22:14:39 +0200 Message-ID: <20250909201446.13138-5-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.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" Store KSTATE physical address & size instead of FDT in kho_data. and initialize KSTATE from kho_populate(). To be able to use FDT in parallel with KSTATE place FDT address&size into 'struct kho_fdt' and save/restore it using KSTATE. This finishes wiring KSTATE with KHO, making it fully functional. Signed-off-by: Andrey Ryabinin --- arch/x86/include/uapi/asm/setup_data.h | 4 +- arch/x86/kernel/kexec-bzimage64.c | 6 +-- arch/x86/kernel/setup.c | 3 +- drivers/of/fdt.c | 6 +-- include/linux/kexec.h | 2 +- include/linux/kstate.h | 1 + kernel/liveupdate/kexec_handover.c | 60 +++++++++++++++++++++----- 7 files changed, 61 insertions(+), 21 deletions(-) diff --git a/arch/x86/include/uapi/asm/setup_data.h b/arch/x86/include/uapi= /asm/setup_data.h index 2671c4e1b3a0..844f5b93473f 100644 --- a/arch/x86/include/uapi/asm/setup_data.h +++ b/arch/x86/include/uapi/asm/setup_data.h @@ -83,8 +83,8 @@ struct ima_setup_data { * Locations of kexec handover metadata */ struct kho_data { - __u64 fdt_addr; - __u64 fdt_size; + __u64 kstate_addr; + __u64 kstate_size; __u64 scratch_addr; __u64 scratch_size; } __attribute__((packed)); diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzim= age64.c index 24a41f0e0cf1..1bf9474d4286 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -253,12 +253,12 @@ static void setup_kho(const struct kimage *image, str= uct boot_params *params, sd->len =3D sizeof(struct kho_data); =20 /* Only add if we have all KHO images in place */ - if (!image->kho.fdt || !image->kho.scratch) + if (!image->kho.kstate || !image->kho.scratch) return; =20 /* Add setup data */ - kho->fdt_addr =3D image->kho.fdt; - kho->fdt_size =3D PAGE_SIZE; + kho->kstate_addr =3D image->kho.kstate; + kho->kstate_size =3D PAGE_SIZE; kho->scratch_addr =3D image->kho.scratch->mem; kho->scratch_size =3D image->kho.scratch->bufsz; sd->next =3D params->hdr.setup_data; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 1b2edd07a3e1..eedcf4be8985 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -467,7 +467,8 @@ static void __init add_kho(u64 phys_addr, u32 data_len) return; } =20 - kho_populate(kho->fdt_addr, kho->fdt_size, kho->scratch_addr, kho->scratc= h_size); + kho_populate(kho->kstate_addr, kho->kstate_size, kho->scratch_addr, + kho->scratch_size); =20 early_memunmap(kho, size); } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 0edd639898a6..f682ef6a1187 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -893,8 +893,8 @@ static void __init early_init_dt_check_kho(void) if (l !=3D (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) return; =20 - fdt_start =3D dt_mem_next_cell(dt_root_addr_cells, &p); - fdt_size =3D dt_mem_next_cell(dt_root_addr_cells, &p); + kstate_start =3D dt_mem_next_cell(dt_root_addr_cells, &p); + kstate_size =3D dt_mem_next_cell(dt_root_addr_cells, &p); =20 p =3D of_get_flat_dt_prop(node, "linux,kho-scratch", &l); if (l !=3D (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32)) @@ -903,7 +903,7 @@ static void __init early_init_dt_check_kho(void) scratch_start =3D dt_mem_next_cell(dt_root_addr_cells, &p); scratch_size =3D dt_mem_next_cell(dt_root_addr_cells, &p); =20 - kho_populate(fdt_start, fdt_size, scratch_start, scratch_size); + kho_populate(kstate_addr, kstate_size, scratch_start, scratch_size); } =20 #ifdef CONFIG_SERIAL_EARLYCON diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 39fe3e6cd282..ebf70361269f 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -416,7 +416,7 @@ struct kimage { =20 struct { struct kexec_segment *scratch; - phys_addr_t fdt; + phys_addr_t kstate; } kho; =20 /* Core ELF header buffer */ diff --git a/include/linux/kstate.h b/include/linux/kstate.h index 53992593cb19..5a95960e5b03 100644 --- a/include/linux/kstate.h +++ b/include/linux/kstate.h @@ -94,6 +94,7 @@ struct kstate_field { =20 enum kstate_ids { KSTATE_FOLIO_ID =3D 1, + KSTATE_KHO_FDT_ID, KSTATE_LAST_ID =3D -1, }; =20 diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_h= andover.c index f1c6378b2657..216cb58553d6 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -806,6 +806,10 @@ int kho_abort(void) goto unlock; } =20 + ret =3D kstate_abort(); + if (ret) + goto unlock; + ret =3D __kho_abort(); if (ret) goto unlock; @@ -984,6 +988,25 @@ int kho_retrieve_subtree(const char *name, phys_addr_t= *phys) } EXPORT_SYMBOL_GPL(kho_retrieve_subtree); =20 + +#define KHO_FDT_INSTANCE_ID 1 + +static struct kho_fdt { + phys_addr_t fdt_phys; + u64 fdt_len; +} kho_fdt; + +struct kstate_description kho_fdt_state =3D { + .name =3D "kho_fdt", + .version_id =3D 1, + .id =3D KSTATE_KHO_FDT_ID, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(fdt_phys, struct kho_fdt, phys_addr_t), + KSTATE_BASE_TYPE(fdt_len, struct kho_fdt, u64), + KSTATE_END_OF_LIST() + }, +}; + static __init int kho_init(void) { int err =3D 0; @@ -1000,13 +1023,20 @@ static __init int kho_init(void) } kho_out.fdt =3D page_to_virt(fdt_page); =20 - err =3D kho_debugfs_init(); + err =3D kstate_register(&kho_fdt_state, &kho_fdt, KHO_FDT_INSTANCE_ID); if (err) goto err_free_fdt; =20 + kho_fdt.fdt_phys =3D page_to_phys(fdt_page); + kho_fdt.fdt_len =3D PAGE_SIZE; + + err =3D kho_debugfs_init(); + if (err) + goto err_free_kstate; + err =3D kho_out_debugfs_init(&kho_out.dbg); if (err) - goto err_free_fdt; + goto err_free_kstate; =20 if (fdt) { kho_in_debugfs_init(&kho_in.dbg, fdt); @@ -1025,6 +1055,8 @@ static __init int kho_init(void) =20 return 0; =20 +err_free_kstate: + kstate_unregister(&kho_fdt_state, &kho_fdt, KHO_FDT_INSTANCE_ID); err_free_fdt: put_page(fdt_page); kho_out.fdt =3D NULL; @@ -1165,24 +1197,30 @@ static int __init kho_scratch_init(phys_addr_t scra= tch_phys, u64 scratch_len) return err; } =20 -void __init kho_populate(phys_addr_t fdt_phys, u64 fdt_len, - phys_addr_t scratch_phys, u64 scratch_len) +void __init kho_populate(phys_addr_t kstate_phys, u64 kstate_len, + phys_addr_t scratch_phys, u64 scratch_len) { - int err =3D 0; - unsigned int scratch_cnt =3D scratch_len / sizeof(*kho_scratch); =20 - err =3D kho_fdt_init(fdt_phys, fdt_len); + err =3D kho_scratch_init(scratch_phys, scratch_len); if (err) goto out; =20 - err =3D kho_scratch_init(scratch_phys, scratch_len); + err =3D kstate_early_init(kstate_phys, kstate_len); + if (err) + goto out; + + err =3D kstate_restore(&kho_fdt_state, &kho_fdt, KHO_FDT_INSTANCE_ID); + if (err) + goto out; + + err =3D kho_fdt_init(kho_fdt.fdt_phys, kho_fdt.fdt_len); if (err) goto out; =20 - kho_in.fdt_phys =3D fdt_phys; + kho_in.fdt_phys =3D kho_fdt.fdt_phys; kho_in.scratch_phys =3D scratch_phys; - kho_scratch_cnt =3D scratch_cnt; + kho_scratch_cnt =3D scratch_len / sizeof(*kho_scratch); pr_info("found kexec handover data. Will skip init for some devices\n"); =20 out: @@ -1201,7 +1239,7 @@ int kho_fill_kimage(struct kimage *image) if (!kho_enable) return 0; =20 - image->kho.fdt =3D virt_to_phys(kho_out.fdt); + image->kho.kstate =3D kstate_out_paddr; =20 scratch_size =3D sizeof(*kho_scratch) * kho_scratch_cnt; scratch =3D (struct kexec_buf){ --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E4FB035FC19; Tue, 9 Sep 2025 20:15:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448948; cv=none; b=swYAia0m/E1W7FL4VISwJI6FtWqWKU0Cr+e8OWlLz8E5JY/M1uhRTVC5kB15w5b2e6yWOfWvjqOPcScGiIZsrTd/JQb2zAyddT4X249338LVzkkmefbftn54q1gAFIiPXv9zE8Mel2urzGx7b/FLsXoB0+qWNBpe23RvzqBIiJ0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448948; c=relaxed/simple; bh=GPLuHr63xpc9t6vpltdLrLVFbKIQahiI+2XFr9M6QF0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=U2VtZMPdP9Ly7MjIemhy9npWQE17I0gCVtrathXxgi4cISSxSGM8eo+vqWkwapiRaaqCe5HfJhcEfWg4yhiSiKL4Z1tjE5HC/u7TASJ0/MbkSf0DnXOBdA5vY2SAkECUr+umYrs045oOt26LpALd4PYktg+QyeXlWTG1peelgxo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=Ow3+Zk7L; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="Ow3+Zk7L" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id 817A1C01A8; Tue, 09 Sep 2025 23:15:45 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-fGTTr1hW; Tue, 09 Sep 2025 23:15:44 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448945; bh=SlubtQeQpBHXlVAu20qfBqsbGkV3gCqQjvicv97NAqE=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=Ow3+Zk7LzrqdqvKvZDbmFyysZMtA7DN68cVIb+B3JSmGFhVLFAPCiOp0Wj9AxagDy ChCIy5Wz0HngB7CVKrHaa2WeO2/0cYVyStkQFIrSE81CIlPTYWzzUhwHU8HNNv6Ivj Ib9YHJbY8VPMhFXHrGvwikRHwrHctRkIYIv49A+A= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 5/7] kstate, test: add test module for testing kstate subsystem. Date: Tue, 9 Sep 2025 22:14:40 +0200 Message-ID: <20250909201446.13138-6-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.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" This is simple test and playground useful kstate subsystem development. It contains some structure with different kind of data which migrated across kexec to the new kernel using kstate. Signed-off-by: Andrey Ryabinin --- MAINTAINERS | 1 + include/linux/kstate.h | 2 + kernel/liveupdate/Kconfig | 8 +++ lib/Makefile | 2 + lib/test_kstate.c | 116 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 lib/test_kstate.c diff --git a/MAINTAINERS b/MAINTAINERS index 2cd9e49abee5..e96da6d97e75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13723,6 +13723,7 @@ M: Andrey Ryabinin S: Maintained F: include/linux/kstate.h F: kernel/livupdate/kstate.c +F: lib/test_kstate.c =20 KTD253 BACKLIGHT DRIVER M: Linus Walleij diff --git a/include/linux/kstate.h b/include/linux/kstate.h index 5a95960e5b03..0ced0da37c8f 100644 --- a/include/linux/kstate.h +++ b/include/linux/kstate.h @@ -95,6 +95,8 @@ struct kstate_field { enum kstate_ids { KSTATE_FOLIO_ID =3D 1, KSTATE_KHO_FDT_ID, + KSTATE_TEST_ID, + KSTATE_TEST_ID_V2, KSTATE_LAST_ID =3D -1, }; =20 diff --git a/kernel/liveupdate/Kconfig b/kernel/liveupdate/Kconfig index b6ea861006bf..af9a25bdcd6e 100644 --- a/kernel/liveupdate/Kconfig +++ b/kernel/liveupdate/Kconfig @@ -69,6 +69,14 @@ config KSTATE state, save it into the memory and restore the state after kexec in new kernel. =20 +config KSTATE_TEST + bool "KSTATE test code" + help + Build a simple test/playground code that is useful for kstate + subsystem development. It contains some structure with different + kind of data which migrated across kexec to the new kernel + using KSTATE. + config KEXEC_HANDOVER bool "kexec handover" depends on ARCH_SUPPORTS_KEXEC_HANDOVER && ARCH_SUPPORTS_KEXEC_FILE diff --git a/lib/Makefile b/lib/Makefile index 392ff808c9b9..46616577caf3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -316,6 +316,8 @@ obj-$(CONFIG_PARMAN) +=3D parman.o =20 obj-y +=3D group_cpus.o =20 +obj-$(CONFIG_KSTATE_TEST) +=3D test_kstate.o + # GCC library routines obj-$(CONFIG_GENERIC_LIB_ASHLDI3) +=3D ashldi3.o obj-$(CONFIG_GENERIC_LIB_ASHRDI3) +=3D ashrdi3.o diff --git a/lib/test_kstate.c b/lib/test_kstate.c new file mode 100644 index 000000000000..70534e8c718f --- /dev/null +++ b/lib/test_kstate.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) "kstate test: " fmt +#include +#include +#include +#include +#include + +static unsigned long ulong_val; +struct kstate_test_data { + int i; + unsigned long *p_ulong; + char s[10]; + struct folio *folio; +}; + +#define KSTATE_TEST_DATA_ID 123 + +struct kstate_description test_state_v2 =3D { + .name =3D "test_v2", + .version_id =3D 1, + .id =3D KSTATE_TEST_ID_V2, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(i, struct kstate_test_data, int), + KSTATE_END_OF_LIST() + }, +}; + +struct kstate_description test_state =3D { + .name =3D "test", + .version_id =3D 2, + .id =3D KSTATE_TEST_ID, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(s, struct kstate_test_data, char [10]), + KSTATE_POINTER(p_ulong, struct kstate_test_data), + KSTATE_FOLIO(folio, struct kstate_test_data), + KSTATE_BASE_TYPE_DEPRECATED(k, u16, 1), + KSTATE_END_OF_LIST() + }, + .subsections =3D (const struct kstate_description *[]){ + &test_state_v2, + NULL + }, +}; + +static struct kstate_test_data test_data; + +static int init_test_data(void) +{ + struct folio *folio; + int i; + + test_data.i =3D 10; + ulong_val =3D 20; + memcpy(test_data.s, "abcdefghk", sizeof(test_data.s)); + folio =3D folio_alloc(GFP_KERNEL, 0); + if (!folio) + return -ENOMEM; + + for (i =3D 0; i < folio_size(folio)/sizeof(u32); i +=3D 4) + *((u32 *)folio_address(folio) + i) =3D 0xdeadbeef; + test_data.folio =3D folio; + return 0; +} + +static void validate_test_data(void) +{ + int i; + + if (WARN_ON(test_data.i !=3D 10)) + return; + if (WARN_ON(*test_data.p_ulong !=3D 20)) + return; + if (WARN_ON(strcmp(test_data.s, "abcdefghk") !=3D 0)) + return; + + for (i =3D 0; i < folio_size(test_data.folio)/4; i +=3D 4) { + u32 val =3D *((u32 *)folio_address(test_data.folio) + i); + + if (WARN_ON_ONCE(val !=3D 0xdeadbeef)) + return; + } +} + +static int __init test_kstate_init(void) +{ + int ret =3D 0; + + test_data.p_ulong =3D &ulong_val; + + ret =3D kstate_register(&test_state, &test_data, KSTATE_TEST_DATA_ID); + if (ret) { + pr_err("register failed %d\n", ret); + goto out; + } + + if (!is_kho_boot()) { + ret =3D init_test_data(); + if (ret) + goto out; + } else { + pr_info("restoring data\n"); + ret =3D kstate_restore(&test_state, &test_data, KSTATE_TEST_DATA_ID); + if (ret) { + pr_err("restore failed %d\n", ret); + goto out; + } + + } + + validate_test_data(); + +out: + return ret; +} +late_initcall(test_kstate_init); --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 87612362087; Tue, 9 Sep 2025 20:15:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448952; cv=none; b=pooqKSzzAWEkn2IKeNDZpXgjJyAlYnKKg3nGcM2BXCUMUwMXSGYT2kt6zwy28397DRvZdBnId3O6aASue3RRWof1vAOi261cIR/m/gI3c3NkP1TxBKXsa01pp7PemgQinaJMtktxtVakt47ak2bC4ZQK/ddDPinuEtb0i1qUWyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448952; c=relaxed/simple; bh=61JY3aGOKt4Zvihy3nw1Me6reibz0TIE5UHSRp7WR/Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=coWUVqlDgX4Bb24mFIz+8t0E5aNymYGFIh/uABAkc8viGqifZExQHyOjgY5LvYP6r65Ji008LbsD+YJiFdLvtkNxGUj3Jf3FVh4Suxb90WxfROvdlY4gR058V76hAxG3OpvXExfAOnXND4nauOQWnkubPC7g9PHbTIcOKac3tgA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=ypBkuNKW; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="ypBkuNKW" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id EE366C01AB; Tue, 09 Sep 2025 23:15:48 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-QszPafCr; Tue, 09 Sep 2025 23:15:48 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448948; bh=m8A5M9SWZ4FduOae9/W265UdgKKBYKII8mDM46+RgEM=; h=Message-ID:Date:In-Reply-To:Cc:Subject:References:To:From; b=ypBkuNKW+ERXO+vB9wi+MesqhXDJci+Z58snBgpV01YxOdZZSgyD0EiGOS1d6ChRM hs4mlstpXx3wA2LGBMtVkU4uQU0M8JpfQTx3pK+DQa36tE3REBAz6GdNN58l44rh9q GyjRrorPLSUoRNijR571xdfTd90C7ZpqX08mC7Vk= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 6/7] mm/memblock: Use KSTATE instead of kho to preserve preserved_mem_table Date: Tue, 9 Sep 2025 22:14:41 +0200 Message-ID: <20250909201446.13138-7-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.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 preserved_mem_table serialized/deserialized using fdt. Use KSTATE instead as it makes code simpler and more compact. Signed-off-by: Andrey Ryabinin --- include/linux/kstate.h | 1 + mm/memblock.c | 158 +++++++++++++---------------------------- 2 files changed, 49 insertions(+), 110 deletions(-) diff --git a/include/linux/kstate.h b/include/linux/kstate.h index 0ced0da37c8f..db8ba07e2319 100644 --- a/include/linux/kstate.h +++ b/include/linux/kstate.h @@ -97,6 +97,7 @@ enum kstate_ids { KSTATE_KHO_FDT_ID, KSTATE_TEST_ID, KSTATE_TEST_ID_V2, + KSTATE_RESERVED_MEM_ID, KSTATE_LAST_ID =3D -1, }; =20 diff --git a/mm/memblock.c b/mm/memblock.c index 6af0b51b1bb7..b9d84d1ffd83 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -14,11 +14,13 @@ #include #include #include +#include #include #include #include =20 #ifdef CONFIG_KEXEC_HANDOVER +#include #include #include #endif /* CONFIG_KEXEC_HANDOVER */ @@ -2498,140 +2500,76 @@ int reserve_mem_release_by_name(const char *name) } =20 #ifdef CONFIG_KEXEC_HANDOVER -#define MEMBLOCK_KHO_FDT "memblock" -#define MEMBLOCK_KHO_NODE_COMPATIBLE "memblock-v1" -#define RESERVE_MEM_KHO_NODE_COMPATIBLE "reserve-mem-v1" - -static int __init prepare_kho_fdt(void) -{ - int err =3D 0, i; - struct page *fdt_page; - void *fdt; - - fdt_page =3D alloc_page(GFP_KERNEL); - if (!fdt_page) - return -ENOMEM; - - fdt =3D page_to_virt(fdt_page); - - err |=3D fdt_create(fdt, PAGE_SIZE); - err |=3D fdt_finish_reservemap(fdt); - - err |=3D fdt_begin_node(fdt, ""); - err |=3D fdt_property_string(fdt, "compatible", MEMBLOCK_KHO_NODE_COMPATI= BLE); - for (i =3D 0; i < reserved_mem_count; i++) { - struct reserve_mem_table *map =3D &reserved_mem_table[i]; - - err |=3D kho_preserve_phys(map->start, map->size); - err |=3D fdt_begin_node(fdt, map->name); - err |=3D fdt_property_string(fdt, "compatible", RESERVE_MEM_KHO_NODE_COM= PATIBLE); - err |=3D fdt_property(fdt, "start", &map->start, sizeof(map->start)); - err |=3D fdt_property(fdt, "size", &map->size, sizeof(map->size)); - err |=3D fdt_end_node(fdt); - } - err |=3D fdt_end_node(fdt); - err |=3D fdt_finish(fdt); - - err |=3D kho_preserve_folio(page_folio(fdt_page)); - err |=3D kho_add_subtree(MEMBLOCK_KHO_FDT, fdt); - - if (err) { - pr_err("failed to prepare memblock FDT for KHO: %d\n", err); - put_page(fdt_page); - } - - return err; -} +static int kstate_preserve_phys(struct kstate_stream *stream, void *obj, + const struct kstate_field *field) +{ + struct reserve_mem_table *map =3D obj; + + return kho_preserve_phys(map->start, map->size); +} + +struct kstate_description kstate_reserve_mem =3D { + .name =3D "reserved_mem", + .id =3D KSTATE_RESERVED_MEM_ID, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(name, struct reserve_mem_table, + char[RESERVE_MEM_NAME_SIZE]), + KSTATE_BASE_TYPE(start, struct reserve_mem_table, phys_addr_t), + KSTATE_BASE_TYPE(size, struct reserve_mem_table, phys_addr_t), + { + .name =3D "phys_range", + .flags =3D KS_CUSTOM, + .save =3D kstate_preserve_phys, + }, + KSTATE_END_OF_LIST(), + }, +}; =20 static int __init reserve_mem_init(void) { int err; + int i; =20 if (!kho_is_enabled() || !reserved_mem_count) return 0; =20 - err =3D prepare_kho_fdt(); - if (err) - return err; - return err; -} -late_initcall(reserve_mem_init); - -static void *__init reserve_mem_kho_retrieve_fdt(void) -{ - phys_addr_t fdt_phys; - static void *fdt; - int err; - - if (fdt) - return fdt; - - err =3D kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys); - if (err) { - if (err !=3D -ENOENT) - pr_warn("failed to retrieve FDT '%s' from KHO: %d\n", - MEMBLOCK_KHO_FDT, err); - return NULL; - } - - fdt =3D phys_to_virt(fdt_phys); + for (i =3D 0; i < reserved_mem_count; i++) { + struct reserve_mem_table *map =3D &reserved_mem_table[i]; =20 - err =3D fdt_node_check_compatible(fdt, 0, MEMBLOCK_KHO_NODE_COMPATIBLE); - if (err) { - pr_warn("FDT '%s' is incompatible with '%s': %d\n", - MEMBLOCK_KHO_FDT, MEMBLOCK_KHO_NODE_COMPATIBLE, err); - fdt =3D NULL; + err =3D kstate_register(&kstate_reserve_mem, + map, crc32(~0, map->name, RESERVE_MEM_NAME_SIZE)); + if (err) + goto out; } - - return fdt; +out: + return err; } +late_initcall(reserve_mem_init); =20 static bool __init reserve_mem_kho_revive(const char *name, phys_addr_t si= ze, phys_addr_t align) { - int err, len_start, len_size, offset; - const phys_addr_t *p_start, *p_size; - const void *fdt; + struct reserve_mem_table *map =3D &reserved_mem_table[reserved_mem_count]; =20 - fdt =3D reserve_mem_kho_retrieve_fdt(); - if (!fdt) + if (kstate_restore(&kstate_reserve_mem, map, + crc32(~0, name, RESERVE_MEM_NAME_SIZE))) return false; =20 - offset =3D fdt_subnode_offset(fdt, 0, name); - if (offset < 0) { - pr_warn("FDT '%s' has no child '%s': %d\n", - MEMBLOCK_KHO_FDT, name, offset); - return false; - } - err =3D fdt_node_check_compatible(fdt, offset, RESERVE_MEM_KHO_NODE_COMPA= TIBLE); - if (err) { - pr_warn("Node '%s' is incompatible with '%s': %d\n", - name, RESERVE_MEM_KHO_NODE_COMPATIBLE, err); + if (map->start & (align - 1)) { + pr_warn("KHO reserve-mem '%s' has wrong alignment (0x%pa, 0x%pa)\n", + name, &align, &map->start); return false; } =20 - p_start =3D fdt_getprop(fdt, offset, "start", &len_start); - p_size =3D fdt_getprop(fdt, offset, "size", &len_size); - if (!p_start || len_start !=3D sizeof(*p_start) || !p_size || - len_size !=3D sizeof(*p_size)) { + if (map->size !=3D size) { + pr_warn("KHO reserve-mem '%s' has wrong size (0x%pa !=3D 0x%pa)\n", + name, &map->size, &size); return false; } =20 - if (*p_start & (align - 1)) { - pr_warn("KHO reserve-mem '%s' has wrong alignment (0x%lx, 0x%lx)\n", - name, (long)align, (long)*p_start); - return false; - } - - if (*p_size !=3D size) { - pr_warn("KHO reserve-mem '%s' has wrong size (0x%lx !=3D 0x%lx)\n", - name, (long)*p_size, (long)size); - return false; - } - - reserved_mem_add(*p_start, size, name); - pr_info("Revived memory reservation '%s' from KHO\n", name); - + pr_info("Revived memory reservation '%s' %pa %pa from KHO\n", + name, &map->start, &map->size); + reserved_mem_count++; return true; } #else --=20 2.49.1 From nobody Thu Oct 2 22:53:07 2025 Received: from forwardcorp1a.mail.yandex.net (forwardcorp1a.mail.yandex.net [178.154.239.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D96E36299D; Tue, 9 Sep 2025 20:15:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=178.154.239.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448956; cv=none; b=RvT7hv6AeGvYvHU5R6MuzUgXAtisUxKgxdEXGhcJ7jdGG3jPWjVI+94CbCkdOpUCI8HoXz2kSphbNR9kemZkcCSh5nDA+jOwk8NCIjdjjrj4gMWlpYUrKUS+PPzdhUbBOIumM/tglB7X6YMQA4JuiDX4SbOpGdkLdHW93ObiYaQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757448956; c=relaxed/simple; bh=XkMZUEEuvqVXTBl3/z30gLJ4zDEgFIOF6Z1PfW5frv0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Mznpb4auLo8pxMusw8qH02nfMMe6WEPNEemYBKbDPoR2bztlx5J3HZ2o9Hs8z5nkO/kU792q6XUEsdBNoi9UKFWiOnihKHacvLbZ9gtFqqWVsXHCZCpO0YgpyJx9j1qFZ564H4tRI0GNYZqtmk3JO5IgfhkqGlFS1/ZYtqaInoI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com; spf=pass smtp.mailfrom=yandex-team.com; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b=l4FamUPe; arc=none smtp.client-ip=178.154.239.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=yandex-team.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=yandex-team.com header.i=@yandex-team.com header.b="l4FamUPe" Received: from mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net [IPv6:2a02:6b8:c1f:3a87:0:640:845c:0]) by forwardcorp1a.mail.yandex.net (Yandex) with ESMTPS id B39CEC01AD; Tue, 09 Sep 2025 23:15:52 +0300 (MSK) Received: from localhost.localdomain (172.31.115.73-vpn.dhcp.yndx.net [172.31.115.73]) by mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net (smtpcorp/Yandex) with ESMTPSA id IFQqlX5GteA0-oPNV4RHQ; Tue, 09 Sep 2025 23:15:52 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex-team.com; s=default; t=1757448952; bh=XK7bLUEvESp+m8RqUZl4D2oF/6AN2tNxcr+b8tCSt7Y=; h=Cc:Message-ID:References:Date:In-Reply-To:Subject:To:From; b=l4FamUPeJH/2K/fFm2QGMJdf/TMdhmYBjUawutzSIHby3gBQ+B3lL6Wpkipkd3fLk sO7XVN91XoNbPqj1vqL3ON/aoQC2y44btVzcMZvR9PS7PeLW6Skpxltn0hYiO0//xU NrpO3fwIt5K0uRDNB2l2en+/J4pNiYdBXZUszQRk= Authentication-Results: mail-nwsmtp-smtp-corp-main-69.vla.yp-c.yandex.net; dkim=pass header.i=@yandex-team.com From: Andrey Ryabinin To: linux-kernel@vger.kernel.org Cc: Alexander Graf , Mike Rapoport , James Gowans , Andrew Morton , linux-mm@kvack.org, Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Baoquan He , kexec@lists.infradead.org, Pratyush Yadav , Jason Gunthorpe , Pasha Tatashin , David Rientjes , Pratyush Yadav , Changyuan Lyu , Jonathan Corbet , linux-doc@vger.kernel.org, Andrey Ryabinin , Chris Li , Ashish.Kalra@amd.com, William Tu , David Matlack , Andrey Ryabinin Subject: [PATCH v3 7/7] Documentation, kstate: Add KSTATE documentation Date: Tue, 9 Sep 2025 22:14:42 +0200 Message-ID: <20250909201446.13138-8-arbn@yandex-team.com> X-Mailer: git-send-email 2.49.1 In-Reply-To: <20250909201446.13138-1-arbn@yandex-team.com> References: <20250909201446.13138-1-arbn@yandex-team.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add KSTATE doc. Describe 'struct kstate_description' and information about versioning fields. Signed-off-by: Andrey Ryabinin --- Documentation/core-api/index.rst | 1 + Documentation/core-api/kstate.rst | 117 ++++++++++++++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 119 insertions(+) create mode 100644 Documentation/core-api/kstate.rst diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/inde= x.rst index a8b7d1417f0a..6c0466e0bb35 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -136,6 +136,7 @@ Documents that don't fit elsewhere or which have yet to= be categorized. .. toctree:: :maxdepth: 1 =20 + kstate librs liveupdate netlink diff --git a/Documentation/core-api/kstate.rst b/Documentation/core-api/kst= ate.rst new file mode 100644 index 000000000000..981ba162109c --- /dev/null +++ b/Documentation/core-api/kstate.rst @@ -0,0 +1,117 @@ +.. SPDX-License-Identifier: GPL-2.0 + +KSTATE: Kernel state preservation framework +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +KSTATE (kernel state) is framework to migrate some part of the internal +kernel state (device driver, memory, etc) from one kernel to another across +kexec reboot. + +kstate_description +------------------ + +Most kernel's state is in structs and structs could be described by +kstate_description. E.g. + +struct kstate_test_data { + int i; + unsigned long *p_ulong; + char s[10]; + struct folio *folio; +}; + +struct kstate_description test_state =3D { + .name =3D "test", + .version_id =3D 1, + .id =3D KSTATE_TEST_ID, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(s, struct kstate_test_data, char [10]), + KSTATE_POINTER(p_ulong, struct kstate_test_data), + KSTATE_FOLIO(folio, struct kstate_test_data), + KSTATE_END_OF_LIST() + }, +}; + +Changing data structures +------------------------ + +KSTATE saves/restores structs as a series of fields. When the kernel struc= ts +are changed we may need to change the state to store more/different inform= ation. + +Versions +-------- + +Version numbers are intended for major incompatible changes, that are not +backward compatible. + +Each version is associated with a series of fields saved. The state is alw= ays +saved as the newest version specified by ->version_id. +But loading state sometimes is able to load state from an older version. + +There are two version fields: + + - version_id: the maximum version_id supported by kstate_description. + - min_version_id: the minimum version_id that given kstate_description= is able to understand. + +KSTATE is able to read versions from minimum_version_id to version_id. + +There are _V forms of many KSTATE_ macros to load fields for version depen= dent fields, e.g. + + KSTATE_BASE_TYPE_V(i, struct kstate_test_data, int, 2), + +only loads that field for versions 2 and newer. + +Saving state will always create a section with the =E2=80=98version_id=E2= =80=99 value and thus can=E2=80=99t +be loaded by any older kernel. + +Removing field +-------------- +If field is no longer needed it could be marked deprecated using +KSTATE_*_DEPRECATED macro and bumping ->version_id of kstate_description: + + KSTATE_BASE_TYPE_DEPRECATED(k, u16, 1), + +The last parameter of the macro is the last version number that have this = field. +Old kernel will save such field, but new kernel will skip it on load. Also +the new kernel will not save such field (as there is nothing to save). +Such change is not backward compatible. + +Adding new field +---------------- + +Addition of new field can be done as version dependent field by using _V f= orm of +KSTATE_ macro: + KSTATE_BASE_TYPE_V(i, struct kstate_test_data, int, 2), + +This indicates that 'test_state' only from version 2 and above have field = '->i'. +If new kernel sees incoming 'test_state' of version 1 it will skip restori= ng '->i' +as nothing was saved. This is not backward compatible, as old kernel doesn= 't +understand the new V2 'test_state'. + +Subsections +----------- +Another option is adding subsection to kstate_description. A subsection is +additional kstate_description which linked to the main one: + +struct kstate_description test_state_v2 =3D { + .name =3D "test_v2", + .id =3D KSTATE_TEST_ID_V2, + .fields =3D (const struct kstate_field[]) { + KSTATE_BASE_TYPE(i, struct kstate_test_data, int), + KSTATE_END_OF_LIST() + }, +}; + +struct kstate_description test_state =3D { + ...... + .subsections =3D (const struct kstate_description *[]){ + &test_state_v2, + NULL + }, +}; + + +Subsection must have a unique ->id. If the receiving side finds a subsecti= on +with unknown id it will be ignored. This make subsections suitable for bac= kward +compatible changes (migrate from N+1 to N kernel) assuming old kernel is o= k without +information in subsection. diff --git a/MAINTAINERS b/MAINTAINERS index e96da6d97e75..a9baf49cdbeb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13721,6 +13721,7 @@ F: include/linux/ks0108.h KSTATE M: Andrey Ryabinin S: Maintained +F: Documentation/core-api/kstate.rst F: include/linux/kstate.h F: kernel/livupdate/kstate.c F: lib/test_kstate.c --=20 2.49.1