* Re: [PATCH v7 08/43] fscrypt: add documentation about extent encryption
From: Eric Biggers @ 2026-06-01 23:43 UTC (permalink / raw)
To: Daniel Vacek
Cc: Chris Mason, Josef Bacik, Theodore Y. Ts'o, Jaegeuk Kim,
Jens Axboe, David Sterba, Jonathan Corbet, Shuah Khan,
linux-block, linux-fscrypt, linux-btrfs, linux-kernel, linux-doc
In-Reply-To: <20260513085340.3673127-9-neelx@suse.com>
On Wed, May 13, 2026 at 10:52:42AM +0200, Daniel Vacek wrote:
> From: Josef Bacik <josef@toxicpanda.com>
>
> Add a couple of sections to the fscrypt documentation about per-extent
> encryption.
A few things that were missed:
- "Contents encryption" section should be updated to document how
extents are encrypted
- btrfs should be added to the list of filesystems in the introduction
section and in the "Tests" section
- "Read-only kernel memory compromise" should be updated to mention the
new limitation which extent-based encryption creates.
Maybe after the sentence "Per-file keys for in-use files will *not* be
removed or wiped.", insert "On filesystems that use per-extent
encryption, master keys for in-use files will not be wiped either."
The list of filesystems can also be found in the help text for the
CONFIG_FS_ENCRYPTION kconfig. That should be updated too.
> +For certain file systems, such as btrfs, it's desired to derive a
> +per-extent encryption key. This is to enable features such as snapshots
It's necessary, not just "desired". The per-file keys just don't work
when multiple files can share the same extents.
> +Currently the inode's master key and encryption policy must match the
> +extent, so you cannot share extents between inodes that were encrypted
> +differently.
Here's another instance of "the inode". It should say something like:
Currently, each extent's master key and encryption mode always match the
corresponding values from each inode that shares the extent.
> +The extent encryption context mirrors the important parts of the above
> +`Encryption context`_, with a few omissions. The struct is defined as
> +follows::
> +
> + struct fscrypt_extent_context {
> + u8 version;
> + u8 encryption_mode;
> + u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
> + u8 nonce[FSCRYPT_FILE_NONCE_SIZE];
> + };
> +
> +Currently all fields much match the containing inode's encryption
> +context, with the exception of the nonce.
> +
> +Additionally extent encryption is only supported with
> +FSCRYPT_EXTENT_CONTEXT_V2 using the standard policy; all other policies
> +are disallowed.
FSCRYPT_EXTENT_CONTEXT_V2 should say FSCRYPT_EXTENT_CONTEXT_V1.
Both version and nonce are exceptions, not just nonce.
Unclear what "the standard policy" is.
- Eric
^ permalink raw reply
* Re: [PATCH v3 1/4] mm/zswap: Make shrink_worker writeback cursor per-memcg
From: Yosry Ahmed @ 2026-06-02 0:31 UTC (permalink / raw)
To: Hao Jia
Cc: akpm, tj, hannes, shakeel.butt, mhocko, mkoutny, nphamcs,
chengming.zhou, muchun.song, roman.gushchin, cgroups, linux-mm,
linux-kernel, linux-doc, Hao Jia
In-Reply-To: <8c0e60e1-5713-69f0-a687-088c87e75764@gmail.com>
On Mon, Jun 01, 2026 at 07:07:45PM +0800, Hao Jia wrote:
>
>
> On 2026/5/30 09:24, Yosry Ahmed wrote:
> > On Tue, May 26, 2026 at 07:45:58PM +0800, Hao Jia wrote:
> > > From: Hao Jia <jiahao1@lixiang.com>
> > >
> > > The zswap background writeback worker shrink_worker() uses a global
> > > cursor zswap_next_shrink, protected by zswap_shrink_lock, to round-robin
> > > across the online memcgs under root_mem_cgroup.
> > >
> > > Proactive writeback also wants a similar per-memcg cursor that is
> > > scoped to the specified memcg, so that repeated invocations against
> > > the same memcg make forward progress across its descendant memcgs
> > > instead of restarting from the first child memcg each time.
> >
> > Is this a problem in practice?
> >
> > Is the concern the overhead of scanning memcgs repeatedly, or lack of
> > fairness? I wonder if we should just do writeback in batches from all
> > memcgs, similar to how reclaim does it, then evaluate at the end if we
> > need to start over?
> >
>
> Not using a per-cgroup cursor will cause issues for "repeated small-budget
> calls" cases. For example, repeatedly triggering a 2MB writeback might
> result in only writing back pages from the first few child memcgs every
> time. In the worst-case scenario (where the writeback amount is less than
> WB_BATCH), it might only ever write back from the first child memcg.
Right, so a fairness concern?
I wonder if we should just reclaim a batch from each memcg, then check
if we reached the goal, otherwise start over. If the batch size is small
enough that should work?
>
> Similar to how memory reclaim uses mem_cgroup_iter() (via struct
> mem_cgroup_reclaim_iter) and the old shrink_worker() used zswap_next_shrink,
> we need a shared cursor here.
Right, I understand that in theory we need a cursor. I am just wondering
if the complexity is justified in practice. Reclaim is a much larger
beast than zswap writeback. I wonder if we can just get away with
scanning a batch from each child memcg -- for per-memcg reclaim, not
global.
We can always improve it later with a cursor if there's an actual need.
>
>
> > >
> > > Naturally, group the cursor and its protecting spinlock into a
> > > zswap_wb_iter struct, and make it a member of struct mem_cgroup to
> > > realize per-memcg cursor management. Accordingly, shrink_worker() now
> > > uses the lock and cursor in root_mem_cgroup->zswap_wb_iter.
> >
> > If we really need to have per-memcg cursors (I am not a big fan), I
> > think we can minimize the overhead by making the cursor updates use
> > atomic cmpxchg instead of having a per-memcg lock.
> >
>
> Because mem_cgroup_iter() always calls css_put(&prev->css), we cannot simply
> update zswap_wb_iter.pos via cmpxchg() after calling it. Doing so could lead
> to a double css_put() issue on prev->css.
>
> Therefore, if we switch to the cmpxchg() approach, we wouldn't be able to
> reuse the existing mem_cgroup_iter() logic. We would have to write a new
> function similar to cgroup_iter(), and its implementation might end up
> looking a bit obscure/complex.
What if we do something like this (for the global cursor):
do {
memcg = xchg(zswap_next_shrink, NULL);
memcg = mem_cgroup_iter(NULL, memcg, NULL);
/* If the cursor was advanced from under us, try again */
if (!try_cmpxchg(zswap_next_shrink, NULL, memcg))
continue;
} while (..);
There is a window where a racing shrinker will see the cursor as NULL
and start over, but that should be fine. We can generalize this for the
per-memcg cursor.
That being said..
>
> Currently, this lock is only used in shrink_memcg(), proactive writeback,
> and mem_cgroup_css_offline(). Note that shrink_memcg() only acquires the
> lock of the root cgroup, and mem_cgroup_css_offline() is unlikely to be a
> hot path.
..this made me realize it's probably fine to just use a global lock for
now?
IIUC the only additional contention to the existing lock will be from
userspace proactive writeback, and that shouldn't be a big deal
especially with the critical section being short?
>
> So, should we keep the spin_lock or go with the cmpxchg() approach?
> Yosry and Nhat, what are your thoughts on this?
I think we should experiment with the global lock first. See if you
observe any regressions with workloads that put a lot of pressure on the
lock (a lot of threads in reclaim doing writeback + a few userspace
threads doing proactive writeback). See if the userspace threads
actually cause a meaningful regression.
>
>
>
> > >
> > > Because the cursor is now per-memcg, the offline cleanup must visit
> > > every ancestor that could be holding a reference to the dying memcg.
> > > Factor out __zswap_memcg_offline_cleanup() and walk from dead_memcg up
> > > to the root.
> >
> > Another reason why I don't like per-memcg cursors. There is too much
> > complexity and I wonder if it's warranted. If we stick with per-memcg
> > cursors please do the refactoring in separate patches to make the
> > patches easier to review.
>
>
> Sorry about that. I will try to keep each patch as simple as possible in the
> next version.
No worries, thanks!
>
>
> Thanks,
> Hao
>
>
^ permalink raw reply
* Re: [PATCH 00/11] net: wwan: t9xx: Add MediaTek T9XX WWAN driver
From: Jakub Kicinski @ 2026-06-02 0:34 UTC (permalink / raw)
To: Jack Wu via B4 Relay
Cc: jackbb_wu, Loic Poulain, Sergey Ryazanov, Johannes Berg,
Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Wen-Zhi Huang, Shi-Wei Yeh, Minano Tseng, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, Jonathan Corbet,
Shuah Khan, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, linux-doc
In-Reply-To: <20260529-t9xx_driver_v1-v1-0-bdbfe2c01e57@compal.com>
On Fri, 29 May 2026 18:31:39 +0800 Jack Wu via B4 Relay wrote:
> 43 files changed, 14761 insertions(+)
Please try to cut this down to ~5kLoC for the initial submission.
Whatever the absolute minimum sensible chunk of code is.
Each patch must build cleanly with W=1
^ permalink raw reply
* Re: [PATCH v4 2/5] tools/lib/mm: add shared file helpers
From: Andrew Morton @ 2026-06-02 0:39 UTC (permalink / raw)
To: Sarthak Sharma
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Jason Gunthorpe, John Hubbard,
Peter Xu, Leon Romanovsky, Jonathan Corbet, Shuah Khan,
Mark Brown, linux-mm, linux-kselftest, linux-doc, linux-kernel
In-Reply-To: <2fb753d0-a9cd-48f3-a24d-ea0b54330279@arm.com>
On Mon, 1 Jun 2026 11:40:52 +0530 Sarthak Sharma <sarthak.sharma@arm.com> wrote:
> On 5/27/26 7:54 PM, Sarthak Sharma wrote:
> > Move read_file(), write_file(), read_num(), and write_num() out of
> > tools/testing/selftests/mm/vm_util.c into a new shared helper under
> > tools/lib/mm/.
> >
> > These helpers are used by mm selftests today and will also be needed by
> > shared hugepage helpers in subsequent patches. Move them to a generic
> > location so they can be reused outside selftests as well.
> >
> > Keep the helpers exposed to mm selftests through vm_util.h by including
> > the new shared header there, and link the new helper into the
> > selftests/mm build.
> >
> > Add tools/lib/mm/ to the MEMORY MANAGEMENT - MISC entry in MAINTAINERS.
> >
> > Signed-off-by: Sarthak Sharma <sarthak.sharma@arm.com>
> > ---
>
> Hi Andrew!
>
> Can you please fold the below fixlet into patch 2, as suggested by
> Sashiko.
Thanks. Please incorporate this (and the bisection fixlet and anything
else) into a v5 series, after 7.2-rc1?
Because it's late in 7.1-rcX and there is still much reviewing and
testing to be done (please).
^ permalink raw reply
* Re: [PATCH v6 01/22] mm/swap: decouple swap cache from physical swap infrastructure
From: Yosry Ahmed @ 2026-06-02 0:56 UTC (permalink / raw)
To: Nhat Pham
Cc: kasong, Liam.Howlett, akpm, apopple, axelrasmussen, baohua,
baolin.wang, bhe, byungchul, cgroups, chengming.zhou, chrisl,
corbet, david, dev.jain, gourry, hannes, hughd, jannh,
joshua.hahnjy, lance.yang, lenb, linux-doc, linux-kernel,
linux-mm, linux-pm, lorenzo.stoakes, matthew.brost, mhocko,
muchun.song, npache, pavel, peterx, peterz, pfalcato, rafael,
rakie.kim, roman.gushchin, rppt, ryan.roberts, shakeel.butt,
shikemeng, surenb, tglx, vbabka, weixugc, ying.huang, yosry.ahmed,
yuanchu, zhengqi.arch, ziy, kernel-team, riel, haowenchao22
In-Reply-To: <CAKEwX=PZnKqfriUsPV2whZyqxfCRNy67z7gyrHObEvztDF0_zg@mail.gmail.com>
On Thu, May 28, 2026 at 02:42:19PM -0700, Nhat Pham wrote:
> On Mon, May 11, 2026 at 3:46 PM Yosry Ahmed <yosry@kernel.org> wrote:
> >
> > On Tue, May 05, 2026 at 08:38:30AM -0700, Nhat Pham wrote:
> > > When we virtualize the swap space, we will manage swap cache at the
> > > virtual swap layer. To prepare for this, decouple swap cache from
> > > physical swap infrastructure.
> > >
> > > We will also remove all the swap cache related helpers of swap table. We
> > > will keep the rest of the swap table infrastructure, which will be
> > > repurposed to serve as the rmap (physical -> virtual swap mapping)
> > > later.
> >
> > I didn't look through the entire series, but let me ask the same
> > high-level question I asked before. Instead of moving things out of the
> > swap table, why not reuse the swap table as the representation of the
> > virtual swap space? Seems like most/all metadata is already moved there
> > in a nice concise format.
>
> The honest answer is I wasn't sure it would work, so I was hacking
> quietly a prototype on my own time :)
>
> I finally got something that survives stress-ng and constant
> memory.reclaim thrown at it though. I figured I should send it out to
> get feedback before digging myself deeper into that hole:
>
> https://lore.kernel.org/all/20260528212955.1912856-1-nphamcs@gmail.com/
>
> There is still a small problem left (the metadata duplication issue
> that Johannes brought up). It is potentially fixable, but I haven't
> actually tried it out yet, so I don't want to overstate here. But take
> a look at it and let me know how you feel about this alternative
> approach!
Thank you! I will take a look!
^ permalink raw reply
* Re: [PATCH] MAINTAINERS: nvdimm: Include maintainer profile
From: Alison Schofield @ 2026-06-02 1:18 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Dan Williams, Vishal Verma, Dave Jiang, Ira Weiny,
Jonathan Corbet, Shuah Khan, nvdimm, linux-doc, linux-kernel
In-Reply-To: <20260518104306.39289-2-krzysztof.kozlowski@oss.qualcomm.com>
On Mon, May 18, 2026 at 12:43:07PM +0200, Krzysztof Kozlowski wrote:
> No dedicated NVDIMM maintainers are returned by get_maintainers.pl for
> the subsystem maintainer profile, thus patches changing that file miss
> the actual owners of the file.
>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Thanks!
Applied to nvdimm/nvdimm.git (libnvdimm-for-next)
https://git.kernel.org/nvdimm/nvdimm/c/86e411b6ec27
>
^ permalink raw reply
* Re: [PATCH v15 00/23] arm64/riscv: Add support for crashkernel CMA reservation
From: Jinjie Ruan @ 2026-06-02 1:43 UTC (permalink / raw)
To: Baoquan He
Cc: corbet, skhan, catalin.marinas, will, chenhuacai, kernel, maddy,
mpe, npiggin, chleroy, pjw, palmer, aou, alex, tglx, mingo, bp,
dave.hansen, hpa, robh, saravanak, akpm, bhe, rppt,
pasha.tatashin, pratyush, ruirui.yang, rdunlap, feng.tang,
dapeng1.mi, kees, elver, kuba, lirongqing, ebiggers, paulmck,
sourabhjain, thuth, ardb, masahiroy, gshan, james.morse, maz,
leitao, yeoreum.yun, coxu, suzuki.poulose, cfsworks, osandov,
jbohac, ryan.roberts, tangyouling, ritesh.list, adityag, hbathini,
bjorn, songshuaishuai, vishal.moola, junhui.liu,
djordje.todorovic, austin.kim, namcao, djbw, chao.gao, seanjc,
fuqiang.wang, liaoyuanhong, makb, graf, piliu, rafael.j.wysocki,
mario.limonciello, jbouron, chenjiahao16, guoren, bauerman, bgwin,
takahiro.akashi, x86, linux-doc, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, devicetree, kexec
In-Reply-To: <ah2Lx7KHI60tzd0v@MiWiFi-R3L-srv>
On 6/1/2026 9:40 PM, Baoquan He wrote:
> Hi Jinjie,
>
> On 06/01/26 at 05:47pm, Jinjie Ruan wrote:
> ...snip...
>> Changes in v15:
>> - Unify the subject prefix formats as Huacai suggested.
>> - Fix powerpc pre-existing NULL pointer dereference [Sashiko [1]]
>> - Fix powerpc pre-existing __merge_memory_ranges() memory range
>> truncation [Sashiko [1]].
>> - Fix pre-existing arm64 CMA page leaks [Sashiko[2]].
>> - Fix pre-existing crash_load_dm_crypt_keys() Use-After-Free and
>> Double Free issue [Sashiko[3]].
>> - Fix vfree(headers) and uninitialized variables issue
>> and simplify the fix [Sashiko[2]].
>> - As walk_system_ram_res() and for_each_mem_range() use different
>> lock, unify and simplify the fix of TOCTOU buffer overflow via memory
>> region padding [Sashiko[4]].
>> - Fix the arm64 crash dump issues in Sashiko[5].
>> - Link to v14: https://lore.kernel.org/all/20260525084932.934910-1-ruanjinjie@huawei.com/
>
> Do these Fixes have anything with the main target of this patch series
> you mentioned in cover-letter:"arm64/riscv: Add support for crashkernel CMA"?
> The patches become more and more in each new version, I am wondering if
> it relies on these Fixes patches to implement your adding support for
> crashkernel CMA on arm64/risc-v.
>
> If not relying on them, could you split them into different patchset
> on different purpose?
Hi Baoquan,
Thank you for your valuable guidance.
You are absolutely right. Most of these fix patches are indeed not
strictly related to the core implementation of the crashkernel CMA
support. They are pre-existing bugs in the surrounding kexec/crash code
that were flagged during our review.
Previously, Andrew suggested taking a look at the code review comments
from the Sashiko AI system, which is why these fixes kept expanding. I
completely agree with your advice that there is no need to keep them
together. I will split them into two completely different patchsets
based on their purpose:
1. A cleaner version of this series, strictly focused on adding the core
crashkernel CMA support for arm64/riscv.
2. One standalone bugfix patchset dedicated entirely to fixing these
pre-existing issues.
By the way, I would also appreciate some advice on how to handle further
AI reviews. It seems that the more code we touch or refactor to fix
these pre-existing issues, the more tangential bugs the AI flags in the
newly exposed areas, making the series extremely difficult to converge.
Should I continue to address all AI-reported bugs associated with the
surrounding code in this series, or should we draw a strict line
and only focus on the core CMA logic moving forward?
I will prepare the split patchsets shortly. Thanks again for
straightening this out!
Best regards,
Jinjie Ruan
>
> Thanks
> Baoquan
>
>>
>> [1]: https://lore.kernel.org/all/20260525092207.96B9D1F000E9@smtp.kernel.org/
>> [2]: https://lore.kernel.org/all/20260525091149.1A1E01F00A3D@smtp.kernel.org/
>> [3]: https://lore.kernel.org/all/20260525105227.3C2421F000E9@smtp.kernel.org/
>> [4]: https://lore.kernel.org/all/20260525095447.944E11F000E9@smtp.kernel.org/
>> [5]: https://lore.kernel.org/all/20260525101746.9959D1F000E9@smtp.kernel.org/
>>
>> Changes in v14:
>> - Fix image->elf_headers memory leak during retry loop for arm64 as Sashiko
>> AI code review pointed out.
>> - Solve the hotplug notifier arch_crash_handle_hotplug_event() AA
>> self-deadlock problem as Sashiko AI code review pointed out.
>> - Fix the TOCTOU issue in prepare_elf_headers() by get_online_mems().
>> - -ENOMEM -> -EAGAIN as Breno suggested.
>> - Add support for arm64 crash hotplug.
>> - Link to v13: https://lore.kernel.org/all/20260511030454.1730881-1-ruanjinjie@huawei.com/
>>
[...]
>> 24 files changed, 430 insertions(+), 338 deletions(-)
>> create mode 100644 arch/arm64/kernel/crash.c
>>
>> --
>> 2.34.1
>>
^ permalink raw reply
* Re: [PATCH mm-hotfixes-unstable v18 00/14] khugepaged: add mTHP collapse support
From: Lance Yang @ 2026-06-02 1:53 UTC (permalink / raw)
To: Lorenzo Stoakes, Alexander Gordeev
Cc: Andrew Morton, Gerald Schaefer, Nico Pache, linux-doc,
linux-kernel, linux-mm, linux-trace-kernel, aarcange,
anshuman.khandual, apopple, baohua, baolin.wang, byungchul,
catalin.marinas, cl, corbet, dave.hansen, david, dev.jain, gourry,
hannes, hughd, jack, jackmanb, jannh, jglisse, joshua.hahnjy, kas,
liam, mathieu.desnoyers, matthew.brost, mhiramat, mhocko, peterx,
pfalcato, rakie.kim, raquini, rdunlap, richard.weiyang, rientjes,
rostedt, rppt, ryan.roberts, shivankg, sunnanyong, surenb,
thomas.hellstrom, tiwai, usamaarif642, vbabka, vishal.moola,
wangkefeng.wang, will, willy, yang, ying.huang, ziy, zokeefe,
linux-s390, linux-next
In-Reply-To: <ah2z26OzPktchVeT@lucifer>
On 2026/6/2 01:08, Lorenzo Stoakes wrote:
> On Mon, Jun 01, 2026 at 05:58:08PM +0200, Alexander Gordeev wrote:
>> On Fri, May 22, 2026 at 01:47:24PM -0700, Andrew Morton wrote:
>>
>> Hi Andrew et al,
>>
>>> On Fri, 22 May 2026 08:59:55 -0600 Nico Pache <npache@redhat.com> wrote:
>>>
>>>> The following series provides khugepaged with the capability to collapse
>>>> anonymous memory regions to mTHPs.
>>>
>>> Thanks, I've update mm.git's mm-unstable branch to this version.
>>>
>>> It sounds like I might be dropping it soon, haven't started looking at
>>> that yet. But let's at least eyeball the latest version at this time.
>>>
>>> Sashiko was able to apply this, so the base-it-on-hotfixes thing worked
>>> well, thanks. The AI checking made a few allegations:
>>
>> This series appears to cause hangs on s390 in linux-next.
>> The issue is not easily reproducible, so it is not yet confirmed.
>> Any ideas for a reliable reproducer that exercises the code path below?
>>
>> [ 2749.385719] sysrq: Show Blocked State
>> [ 2749.385730] task:khugepaged state:D stack:0 pid:209 tgid:209 ppid:2 task_flags:0x200040 flags:0x00000000
>> [ 2749.385735] Call Trace:
>> [ 2749.385736] [<0000017f63c8b226>] __schedule+0x316/0x890
>> [ 2749.385740] [<0000017f63c8b7dc>] schedule+0x3c/0xc0
>> [ 2749.385743] [<0000017f63c8b888>] schedule_preempt_disabled+0x28/0x40
>> [ 2749.385746] [<0000017f63c902ea>] rwsem_down_write_slowpath+0x2fa/0x8b0
>> [ 2749.385749] [<0000017f63c90910>] down_write+0x70/0x80
>> [ 2749.385752] [<0000017f6313407a>] collapse_huge_page+0x2ea/0x9e0
>> [ 2749.385755] [<0000017f6313491e>] mthp_collapse+0x1ae/0x1f0
>> [ 2749.385757] [<0000017f63134fda>] collapse_scan_pmd+0x67a/0x8f0
>> [ 2749.385760] [<0000017f6313751a>] collapse_single_pmd+0x15a/0x260
>> [ 2749.385762] [<0000017f6313792c>] collapse_scan_mm_slot.constprop.0+0x30c/0x470
>> [ 2749.385765] [<0000017f63137cb6>] khugepaged+0x226/0x240
>> [ 2749.385768] [<0000017f62db3128>] kthread+0x148/0x170
>> [ 2749.385770] [<0000017f62d2c238>] __ret_from_fork+0x48/0x220
>> [ 2749.385772] [<0000017f63c95d0a>] ret_from_fork+0xa/0x30
>>
>> Thanks!
>
> Hi Alexander,
>
> Thanks for the report.
>
> It's a pity it's non-repro, I had Claude have a look at it and it couldn't find
> a definite issue with the code at v18, all the locks seem balanced internally.
>
> Things it highlighted FWIW:
>
> - Far more mmap_write_lock()'s being taken - the stack-based approach calls
> colapse_huge_page() multiple times per-PMD each of which entails an mmap read
> lock/unlock and mmap write lock.
>
> - anon_vma write lock held for a much longer period over partial collapse.
>
> So maybe these are triggering issues rather than being the cause of them per-se?
>
> If you happen to see it again could you give the output for:
>
> 'echo t > /proc/sysrq-trigger' so we can track who holds the contended lock and
> get more details on it?
>
> Also the .config would be useful.
>
> I'm guessing you've also not enabled mTHP in any way on the system?
>
> Repro-wise you could also:
>
> # echo 1 > /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs
> # echo 1 > /sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs
>
> To get khugepaged going a more aggressively:
>
> $ for f in /sys/kernel/mm/transparent_hugepage/hugepages-*; do echo always | sudo tee $f/enabled; done
>
> Then maybe some stress-ng like sudo stress-ng --vm 4 --vm-bytes 2G --vm-method
> all --timeout 5m (or maybe something more refined :)?
>
> Maybe some of this will help repro more reliably?
>
Cool!
Maybe also worth trying with CONFIG_DETECT_HUNG_TASK=y and
CONFIG_DETECT_HUNG_TASK_BLOCKER=y.
# detect after 10s in D state instead of default 120s
echo 10 > /proc/sys/kernel/hung_task_timeout_secs
# optional: check more often; 0 means same as timeout
echo 0 > /proc/sys/kernel/hung_task_check_interval_secs
With that enabled, the kernel should hopefully tell us which task likely
owns the rwsem. If it is writer-owned, I would expect that to be fairly
reliable.
Cheers, Lance
^ permalink raw reply
* Re: [PATCH] PM: sleep: Allow disabling DPM watchdog by default
From: Tzung-Bi Shih @ 2026-06-02 2:08 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Jonathan Corbet, Greg Kroah-Hartman, Danilo Krummrich, Shuah Khan,
Pavel Machek, Len Brown, linux-doc, linux-kernel, linux-pm,
driver-core, tfiga
In-Reply-To: <CAJZ5v0gc7AtOjWd+cg6tFgzPBovu=wmYmavjaRRjTdNNd8q-0g@mail.gmail.com>
On Mon, Jun 01, 2026 at 08:39:42PM +0200, Rafael J. Wysocki wrote:
> On Thu, May 28, 2026 at 12:32 PM Tzung-Bi Shih <tzungbi@kernel.org> wrote:
> > diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
> > index e1b550664bab..4f92905f3edf 100644
> > --- a/drivers/base/power/main.c
> > +++ b/drivers/base/power/main.c
> > @@ -527,6 +527,20 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
> > MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
> > "Backtrace all CPUs on DPM watchdog timeout");
> >
> > +#ifdef CONFIG_DPM_WATCHDOG_DEFAULT_ENABLED
> > +static unsigned int __read_mostly dpm_watchdog_enabled = 1;
> > +#else
> > +static unsigned int __read_mostly dpm_watchdog_enabled;
> > +#endif
> > +
> > +static int __init dpm_watchdog_setup(char *str)
> > +{
> > + if (kstrtouint(str, 0, &dpm_watchdog_enabled) == 0)
> > + return 1;
> > + return 0;
> > +}
> > +__setup("dpm_watchdog_enabled=", dpm_watchdog_setup);
>
> You might as well use a module parameter to allow this to be set or
> clear at run time. Is there a particular reason why you only want it
> to be enabled or disabled via the kernel command line?
Thanks for the suggestion. Mainly because in our use cases, we only need
to set it once at boot time.
Also, I was wondering if we need to consider potential races if the flag
can be set at runtime. E.g.:
1) The flag is set.
2) dpm_watchdog_set() is called and the timer is started.
3) The flag is then unset.
4) The subsequent dpm_watchdog_clear() isn't stop the timer.
Given this, would you still suggest providing the module parameter for
completeness?
>
> > +
> > /**
> > * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
> > * @t: The timer that PM watchdog depends on.
> > @@ -570,6 +584,9 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
> > {
> > struct timer_list *timer = &wd->timer;
> >
> > + if (!dpm_watchdog_enabled)
> > + return;
> > +
> > wd->dev = dev;
> > wd->tsk = current;
> > wd->fatal = CONFIG_DPM_WATCHDOG_TIMEOUT == CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
> > @@ -588,6 +605,9 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
> > {
> > struct timer_list *timer = &wd->timer;
> >
> > + if (!dpm_watchdog_enabled)
> > + return;
> > +
> > timer_delete_sync(timer);
> > timer_destroy_on_stack(timer);
> > }
^ permalink raw reply
* Re: [PATCH v1] net: Remove orphaned ax25_ptr references
From: patchwork-bot+netdevbpf @ 2026-06-02 2:10 UTC (permalink / raw)
To: Costa Shulyupin
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, corbet,
skhan, rdunlap, netdev, linux-doc, linux-kernel
In-Reply-To: <20260531134837.4111349-1-costa.shul@redhat.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Sun, 31 May 2026 16:48:36 +0300 you wrote:
> The AX.25 subsystem was removed in commit dd8d4bc28ad7
> ("net: remove ax25 and amateur radio (hamradio) subsystem"),
> which removed the ax25_ptr field from struct net_device but
> left behind the kdoc comment and documentation.
>
> Assisted-by: Claude:claude-opus-4-6
> Signed-off-by: Costa Shulyupin <costa.shul@redhat.com>
>
> [...]
Here is the summary with links:
- [v1] net: Remove orphaned ax25_ptr references
https://git.kernel.org/netdev/net-next/c/7745f1978a0f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v15 11/12] docs: iio: add documentation for adf41513 driver
From: Randy Dunlap @ 2026-06-02 2:58 UTC (permalink / raw)
To: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc
Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
Lars-Peter Clausen, Michael Hennerich, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
Petr Mladek, Steven Rostedt, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Shuah Khan
In-Reply-To: <20260531-adf41513-iio-driver-v15-11-da09adf1c0dd@analog.com>
On 5/31/26 1:30 AM, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add documentation for ADF41513 driver, which describes the device
> driver files and shows how userspace may consume the ABI for various
> tasks.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
> ---
> Documentation/iio/adf41513.rst | 199 +++++++++++++++++++++++++++++++++++++++++
> Documentation/iio/index.rst | 1 +
> MAINTAINERS | 1 +
> 3 files changed, 201 insertions(+)
--
~Randy
^ permalink raw reply
* Re: [PATCH v15 12/12] Documentation: ABI: testing: add common ABI file for iio/frequency
From: Randy Dunlap @ 2026-06-02 2:58 UTC (permalink / raw)
To: rodrigo.alencar, linux-kernel, linux-iio, devicetree, linux-doc
Cc: Jonathan Cameron, David Lechner, Andy Shevchenko,
Lars-Peter Clausen, Michael Hennerich, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jonathan Corbet, Andrew Morton,
Petr Mladek, Steven Rostedt, Andy Shevchenko, Rasmus Villemoes,
Sergey Senozhatsky, Shuah Khan
In-Reply-To: <20260531-adf41513-iio-driver-v15-12-da09adf1c0dd@analog.com>
On 5/31/26 1:30 AM, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add ABI documentation file for PLL/DDS devices with frequency_resolution
> sysfs entry attribute used by both ADF4350 and ADF41513.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> Documentation/ABI/testing/sysfs-bus-iio-frequency | 11 +++++++++++
> Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 | 10 ----------
> 2 files changed, 11 insertions(+), 10 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency b/Documentation/ABI/testing/sysfs-bus-iio-frequency
> new file mode 100644
> index 000000000000..5af31b5b3a19
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency
> @@ -0,0 +1,11 @@
> +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
> +KernelVersion: 3.4.0
> +Contact: linux-iio@vger.kernel.org
> +Description:
> + Stores channel Y frequency resolution/channel spacing in Hz for PLL
> + devices. The given value directly influences the operating mode when
> + fractional-N synthesis is required, as it derives values for
> + configurable modulus parameters used in the calculation of the output
> + frequency. It is assumed that the algorithm that is used to compute
> + the various dividers, is able to generate proper values for multiples
Unneeded comma ^
although you can find many varying opinions on the internet.
I agree with https://www.grammarly.com/blog/punctuation-capitalization/comma/
> + of channel spacing.
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
> index 1254457a726e..76987a119feb 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
> @@ -1,13 +1,3 @@
> -What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resolution
> -KernelVersion: 3.4.0
> -Contact: linux-iio@vger.kernel.org
> -Description:
> - Stores channel Y frequency resolution/channel spacing in Hz.
> - The value given directly influences the MODULUS used by
> - the fractional-N PLL. It is assumed that the algorithm
> - that is used to compute the various dividers, is able to
> - generate proper values for multiples of channel spacing.
> -
> What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency
> KernelVersion: 3.4.0
> Contact: linux-iio@vger.kernel.org
>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
--
~Randy
^ permalink raw reply
* Re: [PATCH v15 00/23] arm64/riscv: Add support for crashkernel CMA reservation
From: Baoquan He @ 2026-06-02 3:06 UTC (permalink / raw)
To: Jinjie Ruan
Cc: corbet, skhan, catalin.marinas, will, chenhuacai, kernel, maddy,
mpe, npiggin, chleroy, pjw, palmer, aou, alex, tglx, mingo, bp,
dave.hansen, hpa, robh, saravanak, akpm, bhe, rppt,
pasha.tatashin, pratyush, ruirui.yang, rdunlap, feng.tang,
dapeng1.mi, kees, elver, kuba, lirongqing, ebiggers, paulmck,
sourabhjain, thuth, ardb, masahiroy, gshan, james.morse, maz,
leitao, yeoreum.yun, coxu, suzuki.poulose, cfsworks, osandov,
jbohac, ryan.roberts, tangyouling, ritesh.list, adityag, hbathini,
bjorn, songshuaishuai, vishal.moola, junhui.liu,
djordje.todorovic, austin.kim, namcao, djbw, chao.gao, seanjc,
fuqiang.wang, liaoyuanhong, makb, graf, piliu, rafael.j.wysocki,
mario.limonciello, jbouron, chenjiahao16, guoren, bauerman, bgwin,
takahiro.akashi, x86, linux-doc, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, devicetree, kexec
In-Reply-To: <1a459706-80db-43d8-b163-76fc09da338d@huawei.com>
On 06/02/26 at 09:43am, Jinjie Ruan wrote:
>
>
> On 6/1/2026 9:40 PM, Baoquan He wrote:
> > Hi Jinjie,
> >
> > On 06/01/26 at 05:47pm, Jinjie Ruan wrote:
> > ...snip...
> >> Changes in v15:
> >> - Unify the subject prefix formats as Huacai suggested.
> >> - Fix powerpc pre-existing NULL pointer dereference [Sashiko [1]]
> >> - Fix powerpc pre-existing __merge_memory_ranges() memory range
> >> truncation [Sashiko [1]].
> >> - Fix pre-existing arm64 CMA page leaks [Sashiko[2]].
> >> - Fix pre-existing crash_load_dm_crypt_keys() Use-After-Free and
> >> Double Free issue [Sashiko[3]].
> >> - Fix vfree(headers) and uninitialized variables issue
> >> and simplify the fix [Sashiko[2]].
> >> - As walk_system_ram_res() and for_each_mem_range() use different
> >> lock, unify and simplify the fix of TOCTOU buffer overflow via memory
> >> region padding [Sashiko[4]].
> >> - Fix the arm64 crash dump issues in Sashiko[5].
> >> - Link to v14: https://lore.kernel.org/all/20260525084932.934910-1-ruanjinjie@huawei.com/
> >
> > Do these Fixes have anything with the main target of this patch series
> > you mentioned in cover-letter:"arm64/riscv: Add support for crashkernel CMA"?
> > The patches become more and more in each new version, I am wondering if
> > it relies on these Fixes patches to implement your adding support for
> > crashkernel CMA on arm64/risc-v.
> >
> > If not relying on them, could you split them into different patchset
> > on different purpose?
>
> Hi Baoquan,
>
> Thank you for your valuable guidance.
>
> You are absolutely right. Most of these fix patches are indeed not
> strictly related to the core implementation of the crashkernel CMA
> support. They are pre-existing bugs in the surrounding kexec/crash code
> that were flagged during our review.
>
> Previously, Andrew suggested taking a look at the code review comments
> from the Sashiko AI system, which is why these fixes kept expanding. I
> completely agree with your advice that there is no need to keep them
> together. I will split them into two completely different patchsets
> based on their purpose:
>
> 1. A cleaner version of this series, strictly focused on adding the core
> crashkernel CMA support for arm64/riscv.
>
> 2. One standalone bugfix patchset dedicated entirely to fixing these
> pre-existing issues.
>
> By the way, I would also appreciate some advice on how to handle further
> AI reviews. It seems that the more code we touch or refactor to fix
> these pre-existing issues, the more tangential bugs the AI flags in the
> newly exposed areas, making the series extremely difficult to converge.
>
> Should I continue to address all AI-reported bugs associated with the
> surrounding code in this series, or should we draw a strict line
> and only focus on the core CMA logic moving forward?
Then please post patches to focus on the core implementation of the
crashkernel CMA support. If any AI reported bugs are raised but not
relatd to it, you can add note in cover-letter or explain somewhere
to tell whehter it's caused by the core code and how you want
to deal with it. Otherwise, you could go round and round of new posting
and still can't see when it ends up.
^ permalink raw reply
* Re: [PATCH v8 2/6] mm/memory-failure: surface unhandlable kernel pages as -ENOTRECOVERABLE
From: Miaohe Lin @ 2026-06-02 3:08 UTC (permalink / raw)
To: David Hildenbrand (Arm), Breno Leitao
Cc: linux-mm, linux-kernel, linux-doc, linux-kselftest,
linux-trace-kernel, kernel-team, Lance Yang, Andrew Morton,
Lorenzo Stoakes, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Naoya Horiguchi,
Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet, Shuah Khan, Liam R. Howlett
In-Reply-To: <e3d023f1-ab6e-4424-b304-55f1294480c3@kernel.org>
On 2026/6/1 21:22, David Hildenbrand (Arm) wrote:
> On 6/1/26 14:28, Miaohe Lin wrote:
>> On 2026/5/27 22:06, Breno Leitao wrote:
>>> get_any_page() collapses every HWPoisonHandlable() rejection into a
>>> single -EIO via the __get_hwpoison_page() -> -EBUSY -> shake_page()
>>> -> retry path. That is correct for the transient case (a userspace
>>> folio briefly off LRU during migration or compaction, which a later
>>> shake can drag back), but wrong for stable kernel-owned pages: slab,
>>> page-table, large-kmalloc and PG_reserved pages will never become
>>> HWPoisonHandlable(), so the retry loop is wasted work and the final
>>> -EIO loses the "this is structurally unrecoverable" information.
>>> memory_failure() then maps -EIO into MF_MSG_GET_HWPOISON, which the
>>> panic-on-unrecoverable sysctl deliberately does not act on.
>>>
>>> Introduce HWPoisonKernelOwned(), a small predicate that positively
>>> identifies pages the hwpoison handler cannot recover from:
>>>
>>> HWPoisonKernelOwned(p, flags) :=
>>> !(MF_SOFT_OFFLINE && page_has_movable_ops(p)) &&
>>> (PageReserved(p) || PageSlab(p) ||
>>> PageTable(p) || PageLargeKmalloc(p))
>>>
>>> The MF_SOFT_OFFLINE / page_has_movable_ops() opt-out mirrors the
>>> same exception in HWPoisonHandlable(): soft-offline is allowed to
>>> migrate movable_ops pages even though they are not on the LRU, and
>>> we must not pre-empt that with an unrecoverable verdict.
>>>
>>> The list is intentionally not exhaustive. vmalloc and kernel-stack
>>> pages, for example, do not carry a page_type bit and would need a
>>> different oracle; they keep going through the existing retry path
>>> unchanged. This is the smallest set we can identify with certainty
>>> by page type.
>>>
>>> Wire the helper into the top of get_any_page() to short-circuit
>>> those pages before the retry loop runs. On a hit, drop the caller's
>>> MF_COUNT_INCREASED reference (if any) and return -ENOTRECOVERABLE
>>> straight away. Pages outside the helper's positive list still take
>>> the existing retry path and return -EIO, leaving operator-visible
>>> behaviour for those cases unchanged.
>>>
>>> Extend the unhandlable-page pr_err() to fire for either errno and
>>> update the get_hwpoison_page() kerneldoc to document the new return.
>>>
>>> memory_failure() still folds every negative return into
>>> MF_MSG_GET_HWPOISON via its existing "else if (res < 0)" branch, so
>>> this patch on its own only changes the errno that soft_offline_page()
>>> can propagate to its callers. A follow-up wires -ENOTRECOVERABLE
>>> through memory_failure() and reports MF_MSG_KERNEL for the
>>> unrecoverable cases, which is what the
>>> panic_on_unrecoverable_memory_failure sysctl observes.
>>
>> Thanks for your patch.
>>
>>>
>>> Suggested-by: David Hildenbrand <david@kernel.org>
>>> Suggested-by: Lance Yang <lance.yang@linux.dev>
>>> Signed-off-by: Breno Leitao <leitao@debian.org>
>>> ---
>>> mm/memory-failure.c | 42 ++++++++++++++++++++++++++++++++++++++++--
>>> 1 file changed, 40 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
>>> index f4d3e6e20e13..8f63bdfeff8f 100644
>>> --- a/mm/memory-failure.c
>>> +++ b/mm/memory-failure.c
>>> @@ -1325,6 +1325,28 @@ static inline bool HWPoisonHandlable(struct page *page, unsigned long flags)
>>> return PageLRU(page) || is_free_buddy_page(page);
>>> }
>>>
>>> +/*
>>> + * Positive identification of pages the hwpoison handler cannot recover.
>>> + * These page types are owned by kernel internals (no userspace mapping
>>> + * to unmap, no file mapping to invalidate, no migration target), so the
>>> + * shake_page() / retry loop in get_any_page() can never turn them into
>>> + * something HWPoisonHandlable() will accept. Short-circuit them to
>>> + * -ENOTRECOVERABLE so callers can panic on operator request instead of
>>> + * spinning through retries that exit as a transient-looking -EIO.
>>> + *
>>> + * The MF_SOFT_OFFLINE / page_has_movable_ops() opt-out mirrors
>>> + * HWPoisonHandlable(): soft-offline is allowed to migrate movable_ops
>>> + * pages even though they are not on the LRU.
>>> + */
>>> +static inline bool HWPoisonKernelOwned(struct page *page, unsigned long flags)
>>> +{
>>> + if ((flags & MF_SOFT_OFFLINE) && page_has_movable_ops(page))
>>> + return false;
>>> +
>>> + return PageReserved(page) || PageSlab(page) ||
>>
>> Once shake_page finds a lightweight range-based way to shrink slab, slab pages could be freed
>> into buddy and above PageSlab test should be removed then. Maybe add a TODO or XXX here?
>>
>>> + PageTable(page) || PageLargeKmalloc(page);
>>
>> I'm not sure but is it safe or a common way to test PageReserved, PageSlab,
>> PageTable and PageLargeKmalloc without extra page refcnt?
>
> Checking typed pages in a racy fashion is fine (PageSlab, PageTable,
> PageLargeKmalloc).
Got it. Thanks.
> Checking PageReserved in a racy fashion is fine as well. TESTPAGEFLAG() will
> allow checking it on compound pages.
It seems PageReserved is not intended to be set on compound pages. I see there are PF_NO_COMPOUND
in its definition: PAGEFLAG(Reserved, reserved, PF_NO_COMPOUND).
>
> For PageLargeKmalloc, we would want to check the head page, though. The page
> type is only stored for the head page.
Maybe we should check the head page for PageSlab and PageTable too? alloc_slab_page only
set PageSlab on the head page and __pagetable_ctor uses __folio_set_pgtable to set PageTable
on folio.
>
> So maybe we want to lookup the compound head (if any) and perform the type
> checks against that?
Maybe we should or we might miss some pages that could have been handled. And
if compound head is required, should we hold an extra page refcnt to guard against
possible folio split race?
Thanks.
.
^ permalink raw reply
* [PATCH] kdoc: xforms: ignore special static/inline macros
From: Randy Dunlap @ 2026-06-02 3:12 UTC (permalink / raw)
To: linux-doc
Cc: Randy Dunlap, Jonathan Corbet, Shuah Khan, Mauro Carvalho Chehab,
Harry Wentland, Alex Hung, Ivan Lipski, Dan Wheeler, Alex Deucher,
Christian König, amd-gfx
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c contains 7 (for
now) functions that use STATIC_IFN_KUNIT or INLINE_IFN_KUNIT macros for
function qualifiers (static or not, inline or not).
These cause parse warnings from kernel-doc:
Invalid C declaration: Expected identifier in nested name, got keyword:
struct [error at 29]
STATIC_IFN_KUNIT const struct drm_color_lut * __extract_blob_lut (const
struct drm_property_blob *blob, uint32_t *size)
Handle these in kernel-doc to prevent multiple warnings.
Fixes: 647d1fd04652 ("drm/amd/display: Add KUnit test for color helpers")
Signed-off-by: Randy Dunlap <rdunlap@infradead.org>
---
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: Harry Wentland <harry.wentland@amd.com>
Cc: Alex Hung <alex.hung@amd.com>
Cc: Ivan Lipski <ivan.lipski@amd.com>
Cc: Dan Wheeler <daniel.wheeler@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: amd-gfx@lists.freedesktop.org
tools/lib/python/kdoc/xforms_lists.py | 2 ++
1 file changed, 2 insertions(+)
--- linux-next-20260601.orig/tools/lib/python/kdoc/xforms_lists.py
+++ linux-next-20260601/tools/lib/python/kdoc/xforms_lists.py
@@ -104,6 +104,8 @@ class CTransforms:
(CMatch("__context_unsafe"), ""),
(CMatch("__attribute_const__"), ""),
(CMatch("__attribute__"), ""),
+ (CMatch("STATIC_IFN_KUNIT"), ""),
+ (CMatch("INLINE_IFN_KUNIT"), ""),
#
# HACK: this is similar to process_export() hack. It is meant to
^ permalink raw reply
* [PATCH v5 00/13] liveupdate: Remove limits on sessions and files
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
Hi all,
This series removes the fixed limits on the number of files that can
be preserved within a single session, and the total number of sessions
managed by the Live Update Orchestrator (LUO).
The core of the change is a transition from single contiguous memory
blocks for metadata serialization to a chain of linked blocks. This
allows LUO to scale dynamically.
1. ABI Evolution:
- Introduced linked-block headers for both file and session
serialization.
- Bumped session ABI version to v4.
2. Memory Management & Security:
- Implemented a dynamic block allocation and reuse strategy. Blocks
are allocated only when existing ones are exhausted and are reused
during session/file removal cycles.
- Introduced KHO_MAX_BLOCKS (10000) as a safeguard against stupid
excessive allocations or corrupted cyclic lists during restore.
3. Expanded Selftests:
- Added new kexec-based tests verifying preservation of
2000 sessions and 500 files per session.
- Added self-tests for many sessions and many files management.
Tree: git.kernel.org/pub/scm/linux/kernel/git/tatashin/linux.git
Branch: luo-remove-max-files-sessions-limits/v5
Changes v5:
- Addressed comments from Pratyush:
- Renamed kho_block_restore -> kho_block_set_restore, kho_block_destroy -> kho_block_set_destroy.
- Renamed block iterator next/read functions to reserve_entry/read_entry.
- Added public helpers kho_block_set_head_pa() and kho_block_set_is_empty().
- Added validation to treat zero-count blocks as errors during restoration.
- Simplified block iterator reading loop from a while to an if statement.
- Changed standard WARN_ON macros to WARN_ON_ONCE on iterator allocation checks, and added warning details.
- Simplified session serialization by removing a redundant NULL check on sessions_pa.
Please review.
Thanks,
Pasha
Pasha Tatashin (13):
liveupdate: change file_set->count type to u64 for type safety
liveupdate: avoid mixing cleanup guards with goto in
luo_session_retrieve_fd
liveupdate: centralize state management into struct luo_ser
liveupdate: register luo_ser as KHO subtree
liveupdate: Extract luo_file_deserialize_one helper
liveupdate: Extract luo_session_deserialize_one helper
kho: add support for linked-block serialization
liveupdate: defer session block allocation and PA setting
liveupdate: Remove limit on the number of sessions
liveupdate: Remove limit on the number of files per session
selftests/liveupdate: Test session and file limit removal
selftests/liveupdate: Add stress-sessions kexec test
selftests/liveupdate: Add stress-files kexec test
Documentation/core-api/kho/abi.rst | 5 +
Documentation/core-api/kho/index.rst | 11 +
MAINTAINERS | 1 +
include/linux/kho/abi/block.h | 56 +++
include/linux/kho/abi/luo.h | 149 ++-----
include/linux/kho_block.h | 101 +++++
kernel/liveupdate/Makefile | 1 +
kernel/liveupdate/kho_block.c | 390 ++++++++++++++++++
kernel/liveupdate/luo_core.c | 99 ++---
kernel/liveupdate/luo_file.c | 206 ++++-----
kernel/liveupdate/luo_flb.c | 65 +--
kernel/liveupdate/luo_internal.h | 16 +-
kernel/liveupdate/luo_session.c | 241 +++++------
tools/testing/selftests/liveupdate/Makefile | 2 +
.../testing/selftests/liveupdate/liveupdate.c | 75 ++++
.../selftests/liveupdate/luo_stress_files.c | 97 +++++
.../liveupdate/luo_stress_sessions.c | 102 +++++
.../selftests/liveupdate/luo_test_utils.c | 24 ++
.../selftests/liveupdate/luo_test_utils.h | 2 +
19 files changed, 1184 insertions(+), 459 deletions(-)
create mode 100644 include/linux/kho/abi/block.h
create mode 100644 include/linux/kho_block.h
create mode 100644 kernel/liveupdate/kho_block.c
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_files.c
create mode 100644 tools/testing/selftests/liveupdate/luo_stress_sessions.c
base-commit: 2935777b418d2bfcbfe96705bb2c0fa6c0d94e18
--
2.53.0
^ permalink raw reply
* [PATCH v5 01/13] liveupdate: change file_set->count type to u64 for type safety
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
This improves type safety and aligns the in-memory file_set->count with
the serialized count type. It avoids potential truncation or sign
conversion mismatch issues.
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_internal.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index dd53d4a7277e..ae58206f14ac 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -52,7 +52,7 @@ static inline int luo_ucmd_respond(struct luo_ucmd *ucmd,
struct luo_file_set {
struct list_head files_list;
struct luo_file_ser *files;
- long count;
+ u64 count;
};
/**
--
2.53.0
^ permalink raw reply related
* [PATCH v5 02/13] liveupdate: avoid mixing cleanup guards with goto in luo_session_retrieve_fd
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Refactoring luo_session_retrieve_fd() to avoid mixing automated
cleanup-style guards with goto-based resource release, which is not
recommended under the Linux kernel coding style.
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_session.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 5c6cebc6e326..7b2f9cbabb05 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -291,25 +291,24 @@ static int luo_session_retrieve_fd(struct luo_session *session,
if (argp->fd < 0)
return argp->fd;
- guard(mutex)(&session->mutex);
- err = luo_retrieve_file(&session->file_set, argp->token, &file);
- if (err < 0)
- goto err_put_fd;
+ scoped_guard(mutex, &session->mutex) {
+ err = luo_retrieve_file(&session->file_set, argp->token, &file);
+ if (err < 0) {
+ put_unused_fd(argp->fd);
+ return err;
+ }
+ }
err = luo_ucmd_respond(ucmd, sizeof(*argp));
- if (err)
- goto err_put_file;
+ if (err) {
+ fput(file);
+ put_unused_fd(argp->fd);
+ return err;
+ }
fd_install(argp->fd, file);
return 0;
-
-err_put_file:
- fput(file);
-err_put_fd:
- put_unused_fd(argp->fd);
-
- return err;
}
static int luo_session_finish(struct luo_session *session,
--
2.53.0
^ permalink raw reply related
* [PATCH v5 03/13] liveupdate: centralize state management into struct luo_ser
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Transition the LUO to ABI v2, which centralizes state management into a
single struct luo_ser header.
Previously, LUO state was spread across multiple FDT properties and
subnodes. ABI v2 simplifies this by placing all core state, including
the liveupdate number and physical addresses for sessions and FLB
headers into a centralized struct luo_ser.
Note that this change introduces a semantic difference: the sessions
and FLB serialization formats are no longer completely independent of
the core LUO. Their metadata (such as physical addresses for sessions
and FLB headers) is now coupled to and managed via the centralized
struct luo_ser.
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 91 +++++++++++---------------------
kernel/liveupdate/luo_core.c | 64 +++++++++++++++-------
kernel/liveupdate/luo_flb.c | 65 +++--------------------
kernel/liveupdate/luo_internal.h | 8 +--
kernel/liveupdate/luo_session.c | 64 ++++------------------
5 files changed, 98 insertions(+), 194 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 46750a0ddf88..1b2f865a771a 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -30,52 +30,25 @@
* .. code-block:: none
*
* / {
- * compatible = "luo-v1";
- * liveupdate-number = <...>;
- *
- * luo-session {
- * compatible = "luo-session-v1";
- * luo-session-header = <phys_addr_of_session_header_ser>;
- * };
- *
- * luo-flb {
- * compatible = "luo-flb-v1";
- * luo-flb-header = <phys_addr_of_flb_header_ser>;
- * };
+ * compatible = "luo-v2";
+ * luo-abi-header = <phys_addr_of_luo_ser>;
* };
*
* Main LUO Node (/):
*
- * - compatible: "luo-v1"
+ * - compatible: "luo-v2"
* Identifies the overall LUO ABI version.
- * - liveupdate-number: u64
- * A counter tracking the number of successful live updates performed.
- *
- * Session Node (luo-session):
- * This node describes all preserved user-space sessions.
- *
- * - compatible: "luo-session-v1"
- * Identifies the session ABI version.
- * - luo-session-header: u64
- * The physical address of a `struct luo_session_header_ser`. This structure
- * is the header for a contiguous block of memory containing an array of
- * `struct luo_session_ser`, one for each preserved session.
- *
- * File-Lifecycle-Bound Node (luo-flb):
- * This node describes all preserved global objects whose lifecycle is bound
- * to that of the preserved files (e.g., shared IOMMU state).
- *
- * - compatible: "luo-flb-v1"
- * Identifies the FLB ABI version.
- * - luo-flb-header: u64
- * The physical address of a `struct luo_flb_header_ser`. This structure is
- * the header for a contiguous block of memory containing an array of
- * `struct luo_flb_ser`, one for each preserved global object.
+ * - luo-abi-header: u64
+ * The physical address of `struct luo_ser`.
*
* Serialization Structures:
* The FDT properties point to memory regions containing arrays of simple,
* `__packed` structures. These structures contain the actual preserved state.
*
+ * - struct luo_ser:
+ * The central ABI structure that contains the overall state of the LUO.
+ * It includes the liveupdate-number and pointers to sessions and FLBs.
+ *
* - struct luo_session_header_ser:
* Header for the session array. Contains the total page count of the
* preserved memory block and the number of `struct luo_session_ser`
@@ -109,13 +82,26 @@
/*
* The LUO FDT hooks all LUO state for sessions, fds, etc.
- * In the root it also carries "liveupdate-number" 64-bit property that
- * corresponds to the number of live-updates performed on this machine.
*/
#define LUO_FDT_SIZE PAGE_SIZE
#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v1"
-#define LUO_FDT_LIVEUPDATE_NUM "liveupdate-number"
+#define LUO_FDT_COMPATIBLE "luo-v2"
+#define LUO_FDT_ABI_HEADER "luo-abi-header"
+
+/**
+ * struct luo_ser - Centralized LUO ABI header.
+ * @liveupdate_num: A counter tracking the number of successful live updates.
+ * @sessions_pa: Physical address of the first session block header.
+ * @flbs_pa: Physical address of the FLB header.
+ *
+ * This structure is the root of all preserved LUO state. It is pointed to by
+ * the "luo-abi-header" property in the LUO FDT.
+ */
+struct luo_ser {
+ u64 liveupdate_num;
+ u64 sessions_pa;
+ u64 flbs_pa;
+} __packed;
#define LIVEUPDATE_HNDL_COMPAT_LENGTH 48
@@ -147,15 +133,6 @@ struct luo_file_set_ser {
u64 count;
} __packed;
-/*
- * LUO FDT session node
- * LUO_FDT_SESSION_HEADER: is a u64 physical address of struct
- * luo_session_header_ser
- */
-#define LUO_FDT_SESSION_NODE_NAME "luo-session"
-#define LUO_FDT_SESSION_COMPATIBLE "luo-session-v2"
-#define LUO_FDT_SESSION_HEADER "luo-session-header"
-
/**
* struct luo_session_header_ser - Header for the serialized session data block.
* @count: The number of `struct luo_session_ser` entries that immediately
@@ -165,7 +142,7 @@ struct luo_file_set_ser {
* physical memory preserved across the kexec. It provides the necessary
* metadata to interpret the array of session entries that follow.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_header_ser {
u64 count;
@@ -182,7 +159,7 @@ struct luo_session_header_ser {
* session) is created and passed to the new kernel, allowing it to reconstruct
* the session context.
*
- * If this structure is modified, `LUO_FDT_SESSION_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_session_ser {
char name[LIVEUPDATE_SESSION_NAME_LENGTH];
@@ -192,10 +169,6 @@ struct luo_session_ser {
/* The max size is set so it can be reliably used during in serialization */
#define LIVEUPDATE_FLB_COMPAT_LENGTH 48
-#define LUO_FDT_FLB_NODE_NAME "luo-flb"
-#define LUO_FDT_FLB_COMPATIBLE "luo-flb-v1"
-#define LUO_FDT_FLB_HEADER "luo-flb-header"
-
/**
* struct luo_flb_header_ser - Header for the serialized FLB data block.
* @pgcnt: The total number of pages occupied by the entire preserved memory
@@ -205,11 +178,9 @@ struct luo_session_ser {
* in the memory block.
*
* This structure is located at the physical address specified by the
- * `LUO_FDT_FLB_HEADER` FDT property. It provides the new kernel with the
- * necessary information to find and iterate over the array of preserved
- * File-Lifecycle-Bound objects and to manage the underlying memory.
+ * flbs_pa in luo_ser.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_header_ser {
u64 pgcnt;
@@ -231,7 +202,7 @@ struct luo_flb_header_ser {
* passed to the new kernel. Each entry allows the LUO core to restore one
* global, shared object.
*
- * If this structure is modified, LUO_FDT_FLB_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
*/
struct luo_flb_ser {
char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 5d5827ced73c..085c0dfc1ef1 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -61,7 +61,6 @@
#include <linux/rwsem.h>
#include <linux/sizes.h>
#include <linux/string.h>
-#include <linux/unaligned.h>
#include "kexec_handover_internal.h"
#include "luo_internal.h"
@@ -86,9 +85,11 @@ early_param("liveupdate", early_liveupdate_param);
static int __init luo_early_startup(void)
{
+ struct luo_ser *luo_ser;
+ int err, header_size;
phys_addr_t fdt_phys;
- int err, ln_size;
const void *ptr;
+ u64 luo_ser_pa;
if (!kho_is_enabled()) {
if (liveupdate_enabled())
@@ -119,26 +120,32 @@ static int __init luo_early_startup(void)
return -EINVAL;
}
- ln_size = 0;
- ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_LIVEUPDATE_NUM,
- &ln_size);
- if (!ptr || ln_size != sizeof(luo_global.liveupdate_num)) {
- pr_err("Unable to get live update number '%s' [%d]\n",
- LUO_FDT_LIVEUPDATE_NUM, ln_size);
+ header_size = 0;
+ ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size);
+ if (!ptr || header_size != sizeof(u64)) {
+ pr_err("Unable to get ABI header '%s' [%d]\n",
+ LUO_FDT_ABI_HEADER, header_size);
return -EINVAL;
}
- luo_global.liveupdate_num = get_unaligned((u64 *)ptr);
+ luo_ser_pa = get_unaligned((u64 *)ptr);
+ luo_ser = phys_to_virt(luo_ser_pa);
+
+ luo_global.liveupdate_num = luo_ser->liveupdate_num;
pr_info("Retrieved live update data, liveupdate number: %lld\n",
luo_global.liveupdate_num);
- err = luo_session_setup_incoming(luo_global.fdt_in);
+ err = luo_session_setup_incoming(luo_ser->sessions_pa);
if (err)
- return err;
+ goto out_free_ser;
+
+ luo_flb_setup_incoming(luo_ser->flbs_pa);
- err = luo_flb_setup_incoming(luo_global.fdt_in);
+ err = 0;
+out_free_ser:
+ kho_restore_free(luo_ser);
return err;
}
@@ -160,7 +167,8 @@ early_initcall(liveupdate_early_init);
/* Called during boot to create outgoing LUO fdt tree */
static int __init luo_fdt_setup(void)
{
- const u64 ln = luo_global.liveupdate_num + 1;
+ struct luo_ser *luo_ser;
+ u64 luo_ser_pa;
void *fdt_out;
int err;
@@ -170,27 +178,45 @@ static int __init luo_fdt_setup(void)
return PTR_ERR(fdt_out);
}
+ luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
+ if (IS_ERR(luo_ser)) {
+ err = PTR_ERR(luo_ser);
+ goto exit_free_fdt;
+ }
+ luo_ser_pa = virt_to_phys(luo_ser);
+
err = fdt_create(fdt_out, LUO_FDT_SIZE);
err |= fdt_finish_reservemap(fdt_out);
err |= fdt_begin_node(fdt_out, "");
err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_LIVEUPDATE_NUM, &ln, sizeof(ln));
- err |= luo_session_setup_outgoing(fdt_out);
- err |= luo_flb_setup_outgoing(fdt_out);
+ err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa,
+ sizeof(luo_ser_pa));
err |= fdt_end_node(fdt_out);
err |= fdt_finish(fdt_out);
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
+
+ err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
+ if (err)
+ goto exit_free_luo_ser;
+
+ luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out,
fdt_totalsize(fdt_out));
if (err)
- goto exit_free;
+ goto exit_free_luo_ser;
luo_global.fdt_out = fdt_out;
return 0;
-exit_free:
+exit_free_luo_ser:
+ kho_unpreserve_free(luo_ser);
+exit_free_fdt:
kho_unpreserve_free(fdt_out);
pr_err("failed to prepare LUO FDT: %d\n", err);
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 8f5c5dd01cd0..c8dd30b41238 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -44,13 +44,11 @@
#include <linux/io.h>
#include <linux/kexec_handover.h>
#include <linux/kho/abi/luo.h>
-#include <linux/libfdt.h>
#include <linux/list_private.h>
#include <linux/liveupdate.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/unaligned.h>
#include "luo_internal.h"
#define LUO_FLB_PGCNT 1ul
@@ -551,27 +549,15 @@ int liveupdate_flb_get_outgoing(struct liveupdate_flb *flb, void **objp)
return 0;
}
-int __init luo_flb_setup_outgoing(void *fdt_out)
+int __init luo_flb_setup_outgoing(u64 *flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_FLB_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_FLB_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_FLB_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_FLB_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
-
- if (err)
- goto err_unpreserve;
+ *flbs_pa = virt_to_phys(header_ser);
header_ser->pgcnt = LUO_FLB_PGCNT;
luo_flb_global.outgoing.header_ser = header_ser;
@@ -579,53 +565,18 @@ int __init luo_flb_setup_outgoing(void *fdt_out)
luo_flb_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
-
- return err;
}
-int __init luo_flb_setup_incoming(void *fdt_in)
+void __init luo_flb_setup_incoming(u64 flbs_pa)
{
struct luo_flb_header_ser *header_ser;
- int err, header_size, offset;
- const void *ptr;
- u64 header_ser_pa;
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_FLB_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get FLB node [%s]\n", LUO_FDT_FLB_NODE_NAME);
-
- return -ENOENT;
+ if (flbs_pa) {
+ header_ser = phys_to_virt(flbs_pa);
+ luo_flb_global.incoming.header_ser = header_ser;
+ luo_flb_global.incoming.ser = (void *)(header_ser + 1);
+ luo_flb_global.incoming.active = true;
}
-
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_FLB_COMPATIBLE);
- if (err) {
- pr_err("FLB node is incompatible with '%s' [%d]\n",
- LUO_FDT_FLB_COMPATIBLE, err);
-
- return -EINVAL;
- }
-
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_FLB_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get FLB header property '%s' [%d]\n",
- LUO_FDT_FLB_HEADER, header_size);
-
- return -EINVAL;
- }
-
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
-
- luo_flb_global.incoming.header_ser = header_ser;
- luo_flb_global.incoming.ser = (void *)(header_ser + 1);
- luo_flb_global.incoming.active = true;
-
- return 0;
}
/**
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index ae58206f14ac..fe22086bfbeb 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -79,8 +79,8 @@ extern struct rw_semaphore luo_register_rwlock;
int luo_session_create(const char *name, struct file **filep);
int luo_session_retrieve(const char *name, struct file **filep);
-int __init luo_session_setup_outgoing(void *fdt);
-int __init luo_session_setup_incoming(void *fdt);
+int __init luo_session_setup_outgoing(u64 *sessions_pa);
+int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
@@ -102,8 +102,8 @@ int luo_flb_file_preserve(struct liveupdate_file_handler *fh);
void luo_flb_file_unpreserve(struct liveupdate_file_handler *fh);
void luo_flb_file_finish(struct liveupdate_file_handler *fh);
void luo_flb_unregister_all(struct liveupdate_file_handler *fh);
-int __init luo_flb_setup_outgoing(void *fdt);
-int __init luo_flb_setup_incoming(void *fdt);
+int __init luo_flb_setup_outgoing(u64 *flbs_pa);
+void __init luo_flb_setup_incoming(u64 flbs_pa);
void luo_flb_serialize(void);
#ifdef CONFIG_LIVEUPDATE_TEST
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 7b2f9cbabb05..3b255ffd1bf1 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -25,9 +25,8 @@
*
* - Serialization: Session metadata is preserved using the KHO framework. When
* a live update is triggered via kexec, an array of `struct luo_session_ser`
- * is populated and placed in a preserved memory region. An FDT node is also
- * created, containing the count of sessions and the physical address of this
- * array.
+ * is populated and placed in a preserved memory region. The physical address
+ * of this array is stored in the centralized `struct luo_ser` structure.
*
* Session Lifecycle:
*
@@ -91,13 +90,11 @@
#include <linux/io.h>
#include <linux/kexec_handover.h>
#include <linux/kho/abi/luo.h>
-#include <linux/libfdt.h>
#include <linux/list.h>
#include <linux/liveupdate.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
-#include <linux/unaligned.h>
#include <uapi/linux/liveupdate.h>
#include "luo_internal.h"
@@ -525,75 +522,34 @@ int luo_session_retrieve(const char *name, struct file **filep)
return err;
}
-int __init luo_session_setup_outgoing(void *fdt_out)
+int __init luo_session_setup_outgoing(u64 *sessions_pa)
{
struct luo_session_header_ser *header_ser;
- u64 header_ser_pa;
- int err;
header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
if (IS_ERR(header_ser))
return PTR_ERR(header_ser);
- header_ser_pa = virt_to_phys(header_ser);
-
- err = fdt_begin_node(fdt_out, LUO_FDT_SESSION_NODE_NAME);
- err |= fdt_property_string(fdt_out, "compatible",
- LUO_FDT_SESSION_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_SESSION_HEADER, &header_ser_pa,
- sizeof(header_ser_pa));
- err |= fdt_end_node(fdt_out);
- if (err)
- goto err_unpreserve;
+ *sessions_pa = virt_to_phys(header_ser);
luo_session_global.outgoing.header_ser = header_ser;
luo_session_global.outgoing.ser = (void *)(header_ser + 1);
luo_session_global.outgoing.active = true;
return 0;
-
-err_unpreserve:
- kho_unpreserve_free(header_ser);
- return err;
}
-int __init luo_session_setup_incoming(void *fdt_in)
+int __init luo_session_setup_incoming(u64 sessions_pa)
{
struct luo_session_header_ser *header_ser;
- int err, header_size, offset;
- u64 header_ser_pa;
- const void *ptr;
-
- offset = fdt_subnode_offset(fdt_in, 0, LUO_FDT_SESSION_NODE_NAME);
- if (offset < 0) {
- pr_err("Unable to get session node: [%s]\n",
- LUO_FDT_SESSION_NODE_NAME);
- return -EINVAL;
- }
- err = fdt_node_check_compatible(fdt_in, offset,
- LUO_FDT_SESSION_COMPATIBLE);
- if (err) {
- pr_err("Session node incompatible [%s]\n",
- LUO_FDT_SESSION_COMPATIBLE);
- return -EINVAL;
+ if (sessions_pa) {
+ header_ser = phys_to_virt(sessions_pa);
+ luo_session_global.incoming.header_ser = header_ser;
+ luo_session_global.incoming.ser = (void *)(header_ser + 1);
+ luo_session_global.incoming.active = true;
}
- header_size = 0;
- ptr = fdt_getprop(fdt_in, offset, LUO_FDT_SESSION_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get session header '%s' [%d]\n",
- LUO_FDT_SESSION_HEADER, header_size);
- return -EINVAL;
- }
-
- header_ser_pa = get_unaligned((u64 *)ptr);
- header_ser = phys_to_virt(header_ser_pa);
-
- luo_session_global.incoming.header_ser = header_ser;
- luo_session_global.incoming.ser = (void *)(header_ser + 1);
- luo_session_global.incoming.active = true;
-
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v5 04/13] liveupdate: register luo_ser as KHO subtree
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Entirely remove the LUO FDT wrapper since the FDT only carries the
compatible string and the pointer to the centralized struct luo_ser.
Instead, register the struct luo_ser via the KHO raw subtree
API, placing the compatibility string inside the structure itself.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 57 +++++++++---------------
kernel/liveupdate/luo_core.c | 85 +++++++++++-------------------------
2 files changed, 46 insertions(+), 96 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 1b2f865a771a..9a4fe491812b 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -10,11 +10,11 @@
*
* Live Update Orchestrator uses the stable Application Binary Interface
* defined below to pass state from a pre-update kernel to a post-update
- * kernel. The ABI is built upon the Kexec HandOver framework and uses a
- * Flattened Device Tree to describe the preserved data.
+ * kernel. The ABI is built upon the Kexec HandOver framework and registers
+ * the central `struct luo_ser` via the KHO raw subtree API.
*
- * This interface is a contract. Any modification to the FDT structure, node
- * properties, compatible strings, or the layout of the `__packed` serialization
+ * This interface is a contract. Any modification to the structure fields,
+ * compatible strings, or the layout of the `__packed` serialization
* structures defined here constitutes a breaking change. Such changes require
* incrementing the version number in the relevant `_COMPATIBLE` string to
* prevent a new kernel from misinterpreting data from an old kernel.
@@ -23,31 +23,15 @@
* however, backward/forward compatibility is only guaranteed for kernels
* supporting the same ABI version.
*
- * FDT Structure Overview:
+ * KHO Structure Overview:
* The entire LUO state is encapsulated within a single KHO entry named "LUO".
- * This entry contains an FDT with the following layout:
- *
- * .. code-block:: none
- *
- * / {
- * compatible = "luo-v2";
- * luo-abi-header = <phys_addr_of_luo_ser>;
- * };
- *
- * Main LUO Node (/):
- *
- * - compatible: "luo-v2"
- * Identifies the overall LUO ABI version.
- * - luo-abi-header: u64
- * The physical address of `struct luo_ser`.
+ * This entry contains the `struct luo_ser` structure.
*
* Serialization Structures:
- * The FDT properties point to memory regions containing arrays of simple,
- * `__packed` structures. These structures contain the actual preserved state.
- *
* - struct luo_ser:
* The central ABI structure that contains the overall state of the LUO.
- * It includes the liveupdate-number and pointers to sessions and FLBs.
+ * It includes the compatibility string, the liveupdate-number, and pointers
+ * to sessions and FLBs.
*
* - struct luo_session_header_ser:
* Header for the session array. Contains the total page count of the
@@ -78,26 +62,27 @@
#ifndef _LINUX_KHO_ABI_LUO_H
#define _LINUX_KHO_ABI_LUO_H
+#include <linux/align.h>
#include <uapi/linux/liveupdate.h>
/*
- * The LUO FDT hooks all LUO state for sessions, fds, etc.
+ * The LUO state is registered under this KHO entry name.
*/
-#define LUO_FDT_SIZE PAGE_SIZE
-#define LUO_FDT_KHO_ENTRY_NAME "LUO"
-#define LUO_FDT_COMPATIBLE "luo-v2"
-#define LUO_FDT_ABI_HEADER "luo-abi-header"
+#define LUO_KHO_ENTRY_NAME "LUO"
+#define LUO_ABI_COMPATIBLE "luo-v3"
+#define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8)
/**
* struct luo_ser - Centralized LUO ABI header.
+ * @compatible: Compatibility string identifying the LUO ABI version.
* @liveupdate_num: A counter tracking the number of successful live updates.
* @sessions_pa: Physical address of the first session block header.
* @flbs_pa: Physical address of the FLB header.
*
- * This structure is the root of all preserved LUO state. It is pointed to by
- * the "luo-abi-header" property in the LUO FDT.
+ * This structure is the root of all preserved LUO state.
*/
struct luo_ser {
+ char compatible[LUO_ABI_COMPAT_LEN];
u64 liveupdate_num;
u64 sessions_pa;
u64 flbs_pa;
@@ -111,7 +96,7 @@ struct luo_ser {
* @data: Private data
* @token: User provided token for this file
*
- * If this structure is modified, LUO_SESSION_COMPATIBLE must be updated.
+ * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_file_ser {
char compatible[LIVEUPDATE_HNDL_COMPAT_LENGTH];
@@ -142,7 +127,7 @@ struct luo_file_set_ser {
* physical memory preserved across the kexec. It provides the necessary
* metadata to interpret the array of session entries that follow.
*
- * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_session_header_ser {
u64 count;
@@ -159,7 +144,7 @@ struct luo_session_header_ser {
* session) is created and passed to the new kernel, allowing it to reconstruct
* the session context.
*
- * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_session_ser {
char name[LIVEUPDATE_SESSION_NAME_LENGTH];
@@ -180,7 +165,7 @@ struct luo_session_ser {
* This structure is located at the physical address specified by the
* flbs_pa in luo_ser.
*
- * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_flb_header_ser {
u64 pgcnt;
@@ -202,7 +187,7 @@ struct luo_flb_header_ser {
* passed to the new kernel. Each entry allows the LUO core to restore one
* global, shared object.
*
- * If this structure is modified, `LUO_FDT_COMPATIBLE` must be updated.
+ * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
*/
struct luo_flb_ser {
char name[LIVEUPDATE_FLB_COMPAT_LENGTH];
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 085c0dfc1ef1..69b00e7d0f8f 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -54,7 +54,6 @@
#include <linux/kexec_handover.h>
#include <linux/kho/abi/luo.h>
#include <linux/kobject.h>
-#include <linux/libfdt.h>
#include <linux/liveupdate.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -67,8 +66,7 @@
static struct {
bool enabled;
- void *fdt_out;
- void *fdt_in;
+ struct luo_ser *luo_ser_out;
u64 liveupdate_num;
} luo_global;
@@ -85,11 +83,10 @@ early_param("liveupdate", early_liveupdate_param);
static int __init luo_early_startup(void)
{
+ phys_addr_t luo_ser_phys;
struct luo_ser *luo_ser;
- int err, header_size;
- phys_addr_t fdt_phys;
- const void *ptr;
- u64 luo_ser_pa;
+ size_t len;
+ int err;
if (!kho_is_enabled()) {
if (liveupdate_enabled())
@@ -98,40 +95,29 @@ static int __init luo_early_startup(void)
return 0;
}
- /* Retrieve LUO subtree, and verify its format. */
- err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL);
+ /* Retrieve LUO state from KHO. */
+ err = kho_retrieve_subtree(LUO_KHO_ENTRY_NAME, &luo_ser_phys, &len);
if (err) {
if (err != -ENOENT) {
- pr_err("failed to retrieve FDT '%s' from KHO: %pe\n",
- LUO_FDT_KHO_ENTRY_NAME, ERR_PTR(err));
+ pr_err("failed to retrieve LUO state '%s' from KHO: %pe\n",
+ LUO_KHO_ENTRY_NAME, ERR_PTR(err));
return err;
}
return 0;
}
- luo_global.fdt_in = phys_to_virt(fdt_phys);
- err = fdt_node_check_compatible(luo_global.fdt_in, 0,
- LUO_FDT_COMPATIBLE);
- if (err) {
- pr_err("FDT '%s' is incompatible with '%s' [%d]\n",
- LUO_FDT_KHO_ENTRY_NAME, LUO_FDT_COMPATIBLE, err);
-
+ if (len < sizeof(*luo_ser)) {
+ pr_err("LUO state is too small (%zu < %zu)\n", len, sizeof(*luo_ser));
return -EINVAL;
}
- header_size = 0;
- ptr = fdt_getprop(luo_global.fdt_in, 0, LUO_FDT_ABI_HEADER, &header_size);
- if (!ptr || header_size != sizeof(u64)) {
- pr_err("Unable to get ABI header '%s' [%d]\n",
- LUO_FDT_ABI_HEADER, header_size);
-
+ luo_ser = phys_to_virt(luo_ser_phys);
+ if (strncmp(luo_ser->compatible, LUO_ABI_COMPATIBLE, LUO_ABI_COMPAT_LEN)) {
+ pr_err("LUO state is incompatible with '%s'\n", LUO_ABI_COMPATIBLE);
return -EINVAL;
}
- luo_ser_pa = get_unaligned((u64 *)ptr);
- luo_ser = phys_to_virt(luo_ser_pa);
-
luo_global.liveupdate_num = luo_ser->liveupdate_num;
pr_info("Retrieved live update data, liveupdate number: %lld\n",
luo_global.liveupdate_num);
@@ -164,37 +150,20 @@ static int __init liveupdate_early_init(void)
}
early_initcall(liveupdate_early_init);
-/* Called during boot to create outgoing LUO fdt tree */
-static int __init luo_fdt_setup(void)
+/* Called during boot to create outgoing LUO state */
+static int __init luo_state_setup(void)
{
struct luo_ser *luo_ser;
- u64 luo_ser_pa;
- void *fdt_out;
int err;
- fdt_out = kho_alloc_preserve(LUO_FDT_SIZE);
- if (IS_ERR(fdt_out)) {
- pr_err("failed to allocate/preserve FDT memory\n");
- return PTR_ERR(fdt_out);
- }
-
luo_ser = kho_alloc_preserve(sizeof(*luo_ser));
if (IS_ERR(luo_ser)) {
- err = PTR_ERR(luo_ser);
- goto exit_free_fdt;
+ pr_err("failed to allocate/preserve LUO state memory\n");
+ return PTR_ERR(luo_ser);
}
- luo_ser_pa = virt_to_phys(luo_ser);
-
- err = fdt_create(fdt_out, LUO_FDT_SIZE);
- err |= fdt_finish_reservemap(fdt_out);
- err |= fdt_begin_node(fdt_out, "");
- err |= fdt_property_string(fdt_out, "compatible", LUO_FDT_COMPATIBLE);
- err |= fdt_property(fdt_out, LUO_FDT_ABI_HEADER, &luo_ser_pa,
- sizeof(luo_ser_pa));
- err |= fdt_end_node(fdt_out);
- err |= fdt_finish(fdt_out);
- if (err)
- goto exit_free_luo_ser;
+
+ strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
+ luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
if (err)
@@ -204,21 +173,17 @@ static int __init luo_fdt_setup(void)
if (err)
goto exit_free_luo_ser;
- luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
-
- err = kho_add_subtree(LUO_FDT_KHO_ENTRY_NAME, fdt_out,
- fdt_totalsize(fdt_out));
+ err = kho_add_subtree(LUO_KHO_ENTRY_NAME, luo_ser, sizeof(*luo_ser));
if (err)
goto exit_free_luo_ser;
- luo_global.fdt_out = fdt_out;
+
+ luo_global.luo_ser_out = luo_ser;
return 0;
exit_free_luo_ser:
kho_unpreserve_free(luo_ser);
-exit_free_fdt:
- kho_unpreserve_free(fdt_out);
- pr_err("failed to prepare LUO FDT: %d\n", err);
+ pr_err("failed to prepare LUO state: %d\n", err);
return err;
}
@@ -234,7 +199,7 @@ static int __init luo_late_startup(void)
if (!liveupdate_enabled())
return 0;
- err = luo_fdt_setup();
+ err = luo_state_setup();
if (err)
luo_global.enabled = false;
--
2.53.0
^ permalink raw reply related
* [PATCH v5 05/13] liveupdate: Extract luo_file_deserialize_one helper
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Extract the logic for deserializing single entries for files into
separate helper functions. In preparation to a linked-block
serialization for files.
This is a pure code movement, no other changes intended.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_file.c | 77 ++++++++++++++++++++----------------
1 file changed, 44 insertions(+), 33 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index 208987502f73..9eec07a9e9fc 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -753,6 +753,46 @@ int luo_file_finish(struct luo_file_set *file_set)
return 0;
}
+static int luo_file_deserialize_one(struct luo_file_set *file_set,
+ struct luo_file_ser *ser)
+{
+ struct liveupdate_file_handler *fh;
+ bool handler_found = false;
+ struct luo_file *luo_file;
+
+ down_read(&luo_register_rwlock);
+ list_private_for_each_entry(fh, &luo_file_handler_list, list) {
+ if (!strcmp(fh->compatible, ser->compatible)) {
+ if (try_module_get(fh->ops->owner))
+ handler_found = true;
+ break;
+ }
+ }
+ up_read(&luo_register_rwlock);
+
+ if (!handler_found) {
+ pr_warn("No registered handler for compatible '%.*s'\n",
+ (int)sizeof(ser->compatible),
+ ser->compatible);
+ return -ENOENT;
+ }
+
+ luo_file = kzalloc_obj(*luo_file);
+ if (!luo_file) {
+ module_put(fh->ops->owner);
+ return -ENOMEM;
+ }
+
+ luo_file->fh = fh;
+ luo_file->file = NULL;
+ luo_file->serialized_data = ser->data;
+ luo_file->token = ser->token;
+ mutex_init(&luo_file->mutex);
+ list_add_tail(&luo_file->list, &file_set->files_list);
+
+ return 0;
+}
+
/**
* luo_file_deserialize - Reconstructs the list of preserved files in the new kernel.
* @file_set: The incoming file_set to fill with deserialized data.
@@ -782,6 +822,7 @@ int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_set_ser *file_set_ser)
{
struct luo_file_ser *file_ser;
+ int err;
u64 i;
if (!file_set_ser->files) {
@@ -809,39 +850,9 @@ int luo_file_deserialize(struct luo_file_set *file_set,
*/
file_ser = file_set->files;
for (i = 0; i < file_set->count; i++) {
- struct liveupdate_file_handler *fh;
- bool handler_found = false;
- struct luo_file *luo_file;
-
- down_read(&luo_register_rwlock);
- list_private_for_each_entry(fh, &luo_file_handler_list, list) {
- if (!strcmp(fh->compatible, file_ser[i].compatible)) {
- if (try_module_get(fh->ops->owner))
- handler_found = true;
- break;
- }
- }
- up_read(&luo_register_rwlock);
-
- if (!handler_found) {
- pr_warn("No registered handler for compatible '%.*s'\n",
- (int)sizeof(file_ser[i].compatible),
- file_ser[i].compatible);
- return -ENOENT;
- }
-
- luo_file = kzalloc_obj(*luo_file);
- if (!luo_file) {
- module_put(fh->ops->owner);
- return -ENOMEM;
- }
-
- luo_file->fh = fh;
- luo_file->file = NULL;
- luo_file->serialized_data = file_ser[i].data;
- luo_file->token = file_ser[i].token;
- mutex_init(&luo_file->mutex);
- list_add_tail(&luo_file->list, &file_set->files_list);
+ err = luo_file_deserialize_one(file_set, &file_ser[i]);
+ if (err)
+ return err;
}
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH v5 06/13] liveupdate: Extract luo_session_deserialize_one helper
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Extract the logic for deserializing single entries for sessions into
separate helper functions. In preparation to a linked-block
serialization for sessions.
This is a pure code movement, no other changes intended.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
kernel/liveupdate/luo_session.c | 63 +++++++++++++++++++--------------
1 file changed, 36 insertions(+), 27 deletions(-)
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 3b255ffd1bf1..9f72a8b0a9a8 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -553,6 +553,40 @@ int __init luo_session_setup_incoming(u64 sessions_pa)
return 0;
}
+static int luo_session_deserialize_one(struct luo_session_header *sh,
+ struct luo_session_ser *ser)
+{
+ struct luo_session *session;
+ int err;
+
+ session = luo_session_alloc(ser->name);
+ if (IS_ERR(session)) {
+ pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n",
+ (int)sizeof(ser->name), ser->name, session);
+ return PTR_ERR(session);
+ }
+
+ err = luo_session_insert(sh, session);
+ if (err) {
+ pr_warn("Failed to insert session [%s] %pe\n",
+ session->name, ERR_PTR(err));
+ luo_session_free(session);
+ return err;
+ }
+
+ scoped_guard(mutex, &session->mutex) {
+ err = luo_file_deserialize(&session->file_set,
+ &ser->file_set_ser);
+ }
+ if (err) {
+ pr_warn("Failed to deserialize files for session [%s] %pe\n",
+ session->name, ERR_PTR(err));
+ return err;
+ }
+
+ return 0;
+}
+
int luo_session_deserialize(void)
{
struct luo_session_header *sh = &luo_session_global.incoming;
@@ -584,34 +618,9 @@ int luo_session_deserialize(void)
* reliably reset devices and reclaim memory.
*/
for (int i = 0; i < sh->header_ser->count; i++) {
- struct luo_session *session;
-
- session = luo_session_alloc(sh->ser[i].name);
- if (IS_ERR(session)) {
- pr_warn("Failed to allocate session [%.*s] during deserialization %pe\n",
- (int)sizeof(sh->ser[i].name),
- sh->ser[i].name, session);
- err = PTR_ERR(session);
- goto save_err;
- }
-
- err = luo_session_insert(sh, session);
- if (err) {
- pr_warn("Failed to insert session [%s] %pe\n",
- session->name, ERR_PTR(err));
- luo_session_free(session);
- goto save_err;
- }
-
- scoped_guard(mutex, &session->mutex) {
- err = luo_file_deserialize(&session->file_set,
- &sh->ser[i].file_set_ser);
- }
- if (err) {
- pr_warn("Failed to deserialize files for session [%s] %pe\n",
- session->name, ERR_PTR(err));
+ err = luo_session_deserialize_one(sh, &sh->ser[i]);
+ if (err)
goto save_err;
- }
}
kho_restore_free(sh->header_ser);
--
2.53.0
^ permalink raw reply related
* [PATCH v5 07/13] kho: add support for linked-block serialization
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Introduce a linked-block serialization mechanism for state handover.
Previously, LUO used contiguous memory blocks for serializing sessions
and files, which imposed limits on the total number of items that could
be preserved across a live update.
This commit adds the infrastructure for a more flexible, block-based
approach where serialized data is stored in a chain of linked blocks.
This is a generic KHO serialization block infrastructure that can be
used by multiple subsystems.
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
Documentation/core-api/kho/abi.rst | 5 +
Documentation/core-api/kho/index.rst | 11 +
MAINTAINERS | 1 +
include/linux/kho/abi/block.h | 56 ++++
include/linux/kho_block.h | 79 ++++++
kernel/liveupdate/Makefile | 1 +
kernel/liveupdate/kho_block.c | 390 +++++++++++++++++++++++++++
7 files changed, 543 insertions(+)
create mode 100644 include/linux/kho/abi/block.h
create mode 100644 include/linux/kho_block.h
create mode 100644 kernel/liveupdate/kho_block.c
diff --git a/Documentation/core-api/kho/abi.rst b/Documentation/core-api/kho/abi.rst
index 799d743105a6..edeb5b311963 100644
--- a/Documentation/core-api/kho/abi.rst
+++ b/Documentation/core-api/kho/abi.rst
@@ -28,6 +28,11 @@ KHO persistent memory tracker ABI
.. kernel-doc:: include/linux/kho/abi/kexec_handover.h
:doc: KHO persistent memory tracker
+KHO serialization block ABI
+===========================
+
+.. kernel-doc:: include/linux/kho/abi/block.h
+
See Also
========
diff --git a/Documentation/core-api/kho/index.rst b/Documentation/core-api/kho/index.rst
index 0a2dee4f8e7d..320914a42178 100644
--- a/Documentation/core-api/kho/index.rst
+++ b/Documentation/core-api/kho/index.rst
@@ -83,6 +83,17 @@ Public API
.. kernel-doc:: kernel/liveupdate/kexec_handover.c
:export:
+KHO Serialization Blocks API
+============================
+
+.. kernel-doc:: kernel/liveupdate/kho_block.c
+ :doc: KHO Serialization Blocks
+
+.. kernel-doc:: include/linux/kho_block.h
+
+.. kernel-doc:: kernel/liveupdate/kho_block.c
+ :internal:
+
See Also
========
diff --git a/MAINTAINERS b/MAINTAINERS
index 9ec290e38b44..920ba7622afa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14208,6 +14208,7 @@ F: Documentation/admin-guide/mm/kho.rst
F: Documentation/core-api/kho/*
F: include/linux/kexec_handover.h
F: include/linux/kho/
+F: include/linux/kho_block.h
F: kernel/liveupdate/kexec_handover*
F: lib/test_kho.c
F: tools/testing/selftests/kho/
diff --git a/include/linux/kho/abi/block.h b/include/linux/kho/abi/block.h
new file mode 100644
index 000000000000..8641c20b379b
--- /dev/null
+++ b/include/linux/kho/abi/block.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+/**
+ * DOC: KHO Serialization Blocks ABI
+ *
+ * Subsystems using the KHO Serialization Blocks framework rely on the stable
+ * Application Binary Interface defined below to pass serialized state from a
+ * pre-update kernel to a post-update kernel.
+ *
+ * This interface is a contract. Any modification to the structure fields,
+ * compatible strings, or the layout of the `__packed` serialization
+ * structures defined here constitutes a breaking change. Such changes require
+ * incrementing the version number in the `KHO_BLOCK_ABI_COMPATIBLE` string to
+ * prevent a new kernel from misinterpreting data from an old kernel.
+ *
+ * Changes are allowed provided the compatibility version is incremented;
+ * however, backward/forward compatibility is only guaranteed for kernels
+ * supporting the same ABI version.
+ */
+
+#ifndef _LINUX_KHO_ABI_BLOCK_H
+#define _LINUX_KHO_ABI_BLOCK_H
+
+#include <asm/page.h>
+#include <linux/types.h>
+
+#define KHO_BLOCK_ABI_COMPATIBLE "kho-block-v1"
+
+/**
+ * KHO_BLOCK_SIZE - The size of each serialization block.
+ *
+ * This is defined as PAGE_SIZE. PAGE_SIZE is ABI compliant because live
+ * update between kernels with different page sizes is not supported by KHO.
+ */
+#define KHO_BLOCK_SIZE PAGE_SIZE
+
+/**
+ * struct kho_block_header_ser - Header for the serialized data block.
+ * @next: Physical address of the next struct kho_block_header_ser.
+ * @count: The number of entries that immediately follow this header in the
+ * memory block.
+ *
+ * This structure is located at the beginning of a block of physical memory
+ * preserved across a kexec. It provides the necessary metadata to interpret
+ * the array of entries that follow.
+ */
+struct kho_block_header_ser {
+ u64 next;
+ u64 count;
+} __packed;
+
+#endif /* _LINUX_KHO_ABI_BLOCK_H */
diff --git a/include/linux/kho_block.h b/include/linux/kho_block.h
new file mode 100644
index 000000000000..505bf78409f2
--- /dev/null
+++ b/include/linux/kho_block.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+#ifndef _LINUX_KHO_BLOCK_H
+#define _LINUX_KHO_BLOCK_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/kho/abi/block.h>
+
+/**
+ * struct kho_block - Internal representation of a serialization block.
+ * @list: List head for linking blocks in memory.
+ * @ser: Pointer to the serialized header in preserved memory.
+ */
+struct kho_block {
+ struct list_head list;
+ struct kho_block_header_ser *ser;
+};
+
+/**
+ * struct kho_block_set - A set of blocks that belong to the same object.
+ * @blocks: The list of serialization blocks (struct kho_block).
+ * @nblocks: The number of allocated serialization blocks.
+ * @head_pa: Physical address of the first block header.
+ * @entry_size: The size of each entry in the blocks.
+ * @count_per_block: The maximum number of entries each block can hold.
+ * @incoming: True if this block set was restored from the previous kernel.
+ */
+struct kho_block_set {
+ struct list_head blocks;
+ long nblocks;
+ u64 head_pa;
+ size_t entry_size;
+ u64 count_per_block;
+ bool incoming;
+};
+
+/**
+ * struct kho_block_it - Iterator for serializing entries into blocks.
+ * @bs: The block set being iterated.
+ * @block: The current block.
+ * @i: The current entry index within @block.
+ */
+struct kho_block_it {
+ struct kho_block_set *bs;
+ struct kho_block *block;
+ u64 i;
+};
+
+/**
+ * KHO_BLOCK_SET_INIT - Initialize a static kho_block_set.
+ * @_name: Name of the kho_block_set variable.
+ * @_entry_size: The size of each entry in the block set.
+ */
+#define KHO_BLOCK_SET_INIT(_name, _entry_size) { \
+ .blocks = LIST_HEAD_INIT((_name).blocks), \
+ .entry_size = _entry_size, \
+}
+
+void kho_block_set_init(struct kho_block_set *bs, size_t entry_size);
+
+int kho_block_grow(struct kho_block_set *bs, u64 count);
+void kho_block_shrink(struct kho_block_set *bs, u64 count);
+
+int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa);
+void kho_block_set_destroy(struct kho_block_set *bs);
+void kho_block_set_clear(struct kho_block_set *bs);
+
+void kho_block_it_init(struct kho_block_it *it, struct kho_block_set *bs);
+void *kho_block_it_reserve_entry(struct kho_block_it *it);
+void *kho_block_it_read_entry(struct kho_block_it *it);
+void *kho_block_it_prev(struct kho_block_it *it);
+void kho_block_it_finalize(struct kho_block_it *it);
+
+#endif /* _LINUX_KHO_BLOCK_H */
diff --git a/kernel/liveupdate/Makefile b/kernel/liveupdate/Makefile
index d2f779cbe279..eec9d3ae07eb 100644
--- a/kernel/liveupdate/Makefile
+++ b/kernel/liveupdate/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
luo-y := \
+ kho_block.o \
luo_core.o \
luo_file.o \
luo_flb.o \
diff --git a/kernel/liveupdate/kho_block.c b/kernel/liveupdate/kho_block.c
new file mode 100644
index 000000000000..01978c6aea1a
--- /dev/null
+++ b/kernel/liveupdate/kho_block.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2026, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+/**
+ * DOC: KHO Serialization Blocks
+ *
+ * KHO provides a mechanism to preserve stateful data across a kexec handover
+ * by serializing it into memory blocks. This file provides the common
+ * infrastructure for managing these blocks.
+ *
+ * Each block consists of a header (struct kho_block_header_ser) followed by an
+ * array of serialized entries. Multiple blocks are linked together via a
+ * physical pointer in the header, forming a linked list that can be easily
+ * traversed in both the current and the next kernel.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/kexec_handover.h>
+#include <linux/kho/abi/block.h>
+#include <linux/kho_block.h>
+#include <linux/slab.h>
+
+/*
+ * Safeguard limit for the number of serialization blocks. This is used to
+ * prevent infinite loops and excessive memory allocation in case of memory
+ * corruption in the preserved state.
+ */
+#define KHO_MAX_BLOCKS 10000
+
+/**
+ * kho_block_set_init - Initialize a block set.
+ * @bs: The block set to initialize.
+ * @entry_size: The size of each entry in the blocks.
+ */
+void kho_block_set_init(struct kho_block_set *bs, size_t entry_size)
+{
+ *bs = (struct kho_block_set)KHO_BLOCK_SET_INIT(*bs, entry_size);
+}
+
+static inline u64 kho_block_count_per_block(struct kho_block_set *bs)
+{
+ if (unlikely(!bs->count_per_block)) {
+ bs->count_per_block = (KHO_BLOCK_SIZE -
+ sizeof(struct kho_block_header_ser)) /
+ bs->entry_size;
+ WARN_ON_ONCE(!bs->count_per_block);
+ }
+ return bs->count_per_block;
+}
+
+/* Free serialized data */
+static void kho_block_free_ser(struct kho_block_set *bs,
+ struct kho_block_header_ser *ser)
+{
+ if (bs->incoming)
+ kho_restore_free(ser);
+ else
+ kho_unpreserve_free(ser);
+}
+
+static struct kho_block_header_ser *kho_block_alloc_ser(struct kho_block_set *bs)
+{
+ WARN_ON_ONCE(bs->incoming);
+ return kho_alloc_preserve(KHO_BLOCK_SIZE);
+}
+
+static int kho_block_add(struct kho_block_set *bs,
+ struct kho_block_header_ser *ser)
+{
+ struct kho_block *block, *last;
+
+ if (bs->nblocks >= KHO_MAX_BLOCKS)
+ return -ENOSPC;
+
+ block = kzalloc_obj(*block);
+ if (!block)
+ return -ENOMEM;
+
+ block->ser = ser;
+ last = list_last_entry_or_null(&bs->blocks, struct kho_block, list);
+ list_add_tail(&block->list, &bs->blocks);
+ bs->nblocks++;
+
+ if (last)
+ last->ser->next = virt_to_phys(ser);
+ else
+ bs->head_pa = virt_to_phys(ser);
+
+ return 0;
+}
+
+/**
+ * kho_block_grow - Create a new block if the current capacity is reached.
+ * @bs: The block set.
+ * @count: The current number of entries.
+ *
+ * This function handles the dynamic expansion of a block set. It allocates
+ * and links a new serialization block if the provided entry count matches
+ * the current total capacity of the set.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int kho_block_grow(struct kho_block_set *bs, u64 count)
+{
+ struct kho_block_header_ser *ser;
+ int err;
+
+ if (WARN_ON_ONCE(bs->incoming))
+ return -EINVAL;
+
+ if (count != bs->nblocks * kho_block_count_per_block(bs))
+ return 0;
+
+ ser = kho_block_alloc_ser(bs);
+ if (IS_ERR(ser))
+ return PTR_ERR(ser);
+
+ err = kho_block_add(bs, ser);
+ if (err) {
+ kho_block_free_ser(bs, ser);
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * kho_block_shrink - Conditionally destroy the last block in a block set.
+ * @bs: The block set.
+ * @count: The current number of entries across all blocks.
+ *
+ * This function checks if the last block in the set is redundant based on the
+ * total entry count and the capacity of the preceding blocks. If the entry
+ * count can be accommodated by the blocks that come before the last one, the
+ * last block is destroyed and removed from the set.
+ */
+void kho_block_shrink(struct kho_block_set *bs, u64 count)
+{
+ struct kho_block *last, *new_last;
+
+ if (count > (bs->nblocks - 1) * kho_block_count_per_block(bs))
+ return;
+
+ if (list_empty(&bs->blocks))
+ return;
+
+ last = list_last_entry(&bs->blocks, struct kho_block, list);
+ list_del(&last->list);
+ bs->nblocks--;
+ kho_block_free_ser(bs, last->ser);
+ kfree(last);
+
+ new_last = list_last_entry_or_null(&bs->blocks, struct kho_block, list);
+ if (new_last)
+ new_last->ser->next = 0;
+ else
+ bs->head_pa = 0;
+}
+
+/*
+ * kho_cyclic_blocks_check - Check for cycles in a linked list of blocks.
+ * Uses Floyd's cycle-finding algorithm to ensure sanity of the incoming list.
+ */
+static bool kho_cyclic_blocks_check(struct kho_block_set *bs)
+{
+ struct kho_block_header_ser *fast;
+ struct kho_block_header_ser *slow;
+ int count = 0;
+
+ fast = phys_to_virt(bs->head_pa);
+ slow = fast;
+
+ while (fast) {
+ if (count++ >= KHO_MAX_BLOCKS) {
+ pr_err("Linked list too long\n");
+ return false;
+ }
+
+ if (!fast->next)
+ break;
+
+ fast = phys_to_virt(fast->next);
+ if (!fast->next)
+ break;
+
+ fast = phys_to_virt(fast->next);
+ slow = phys_to_virt(slow->next);
+
+ if (slow == fast) {
+ pr_err("Cyclic list detected\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * kho_block_set_restore - Restore a block set from a physical address.
+ * @bs: The block set to restore.
+ * @head_pa: Physical address of the first block header.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa)
+{
+ struct kho_block_header_ser *ser;
+ u64 next_pa = head_pa;
+ int err;
+
+ /* Restored block sets use size from the previous kernel */
+ bs->incoming = true;
+ if (!head_pa)
+ return 0;
+
+ bs->head_pa = head_pa;
+ if (!kho_cyclic_blocks_check(bs)) {
+ bs->head_pa = 0;
+ return -EINVAL;
+ }
+
+ while (next_pa) {
+ ser = phys_to_virt(next_pa);
+ if (!ser->count || ser->count > kho_block_count_per_block(bs)) {
+ pr_warn("Block contains invalid entry count: %llu\n",
+ ser->count);
+ err = -EINVAL;
+ goto err_destroy;
+ }
+ err = kho_block_add(bs, ser);
+ if (err)
+ goto err_destroy;
+ next_pa = ser->next;
+ }
+
+ return 0;
+
+err_destroy:
+ kho_block_set_destroy(bs);
+ return err;
+}
+
+/**
+ * kho_block_set_destroy - Destroy all blocks in a block set.
+ * @bs: The block set.
+ */
+void kho_block_set_destroy(struct kho_block_set *bs)
+{
+ struct kho_block *block, *tmp;
+ u64 head_pa = bs->head_pa;
+
+ list_for_each_entry_safe(block, tmp, &bs->blocks, list) {
+ list_del(&block->list);
+ kfree(block);
+ }
+ bs->nblocks = 0;
+ bs->head_pa = 0;
+
+ /*
+ * bs->blocks may only contain partially restored blocks, but head_pa
+ * still points to the entire chain.
+ */
+ while (head_pa) {
+ struct kho_block_header_ser *ser = phys_to_virt(head_pa);
+
+ head_pa = ser->next;
+ kho_block_free_ser(bs, ser);
+ }
+}
+
+/**
+ * kho_block_set_clear - Clear all serialized data in a block set.
+ * @bs: The block set to clear.
+ */
+void kho_block_set_clear(struct kho_block_set *bs)
+{
+ struct kho_block *block;
+
+ list_for_each_entry(block, &bs->blocks, list) {
+ block->ser->count = 0;
+ memset(block->ser + 1, 0, KHO_BLOCK_SIZE - sizeof(*block->ser));
+ }
+}
+
+/**
+ * kho_block_it_init - Initialize a block set iterator.
+ * @it: The iterator to initialize.
+ * @bs: The block set to iterate over.
+ */
+void kho_block_it_init(struct kho_block_it *it, struct kho_block_set *bs)
+{
+ it->bs = bs;
+ it->block = list_first_entry_or_null(&bs->blocks, struct kho_block, list);
+ it->i = 0;
+}
+
+/**
+ * kho_block_it_reserve_entry - Reserve and return the next available slot for writing.
+ * @it: The block iterator.
+ *
+ * This function is used during state serialization to add a new entry.
+ * It reserves a slot in the current block, advancing the internal index.
+ * If the current block is full, it automatically moves to the next block
+ * in the set.
+ *
+ * Return: A pointer to the reserved entry slot, or NULL if the block set's
+ * capacity is fully exhausted.
+ */
+void *kho_block_it_reserve_entry(struct kho_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ if (it->i == kho_block_count_per_block(it->bs)) {
+ it->block->ser->count = it->i;
+ if (list_is_last(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_next_entry(it->block, list);
+ it->i = 0;
+ }
+
+ return (void *)(it->block->ser + 1) + (it->i++ * it->bs->entry_size);
+}
+
+/**
+ * kho_block_it_read_entry - Read the next serialized entry from the block set.
+ * @it: The block iterator.
+ *
+ * This function is used during state deserialization. It iterates through
+ * entries that were previously written, respecting the actual count stored
+ * in each block's header.
+ *
+ * Return: A pointer to the next serialized entry, or NULL if all serialized
+ * entries have been read.
+ */
+void *kho_block_it_read_entry(struct kho_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ if (it->i == it->block->ser->count) {
+ if (list_is_last(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_next_entry(it->block, list);
+ it->i = 0;
+ }
+
+ return (void *)(it->block->ser + 1) + (it->i++ * it->bs->entry_size);
+}
+
+/**
+ * kho_block_it_prev - Return the previous entry slot in the block set.
+ * @it: The block iterator.
+ *
+ * If the current index is at the start of a block, it automatically moves to
+ * the end of the previous block.
+ *
+ * Return: A pointer to the previous entry slot, or NULL if at the very
+ * beginning of the block set.
+ */
+void *kho_block_it_prev(struct kho_block_it *it)
+{
+ if (!it->block)
+ return NULL;
+
+ if (it->i == 0) {
+ if (list_is_first(&it->block->list, &it->bs->blocks))
+ return NULL;
+ it->block = list_prev_entry(it->block, list);
+ it->i = kho_block_count_per_block(it->bs);
+ }
+
+ return (void *)(it->block->ser + 1) + (--it->i * it->bs->entry_size);
+}
+
+/**
+ * kho_block_it_finalize - Finalize the current block by setting its entry count.
+ * @it: The block iterator.
+ */
+void kho_block_it_finalize(struct kho_block_it *it)
+{
+ if (it->block)
+ it->block->ser->count = it->i;
+}
--
2.53.0
^ permalink raw reply related
* [PATCH v5 08/13] liveupdate: defer session block allocation and PA setting
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Currently, luo_session_setup_outgoing() allocates the session block and
sets its physical address in the header immediately. With upcoming
dynamic block-based session management, this makes the first block
different from the rest. Move the allocation to where it is first needed.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho_block.h | 22 +++++++++++
kernel/liveupdate/luo_core.c | 4 +-
kernel/liveupdate/luo_internal.h | 2 +-
kernel/liveupdate/luo_session.c | 68 ++++++++++++++++++++------------
4 files changed, 67 insertions(+), 29 deletions(-)
diff --git a/include/linux/kho_block.h b/include/linux/kho_block.h
index 505bf78409f2..0a8cda2cbfb5 100644
--- a/include/linux/kho_block.h
+++ b/include/linux/kho_block.h
@@ -70,6 +70,28 @@ int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa);
void kho_block_set_destroy(struct kho_block_set *bs);
void kho_block_set_clear(struct kho_block_set *bs);
+/**
+ * kho_block_set_head_pa - Get the physical address of the first block header.
+ * @bs: The block set.
+ *
+ * Return: The physical address of the first block header, or 0 if empty.
+ */
+static inline u64 kho_block_set_head_pa(struct kho_block_set *bs)
+{
+ return bs->head_pa;
+}
+
+/**
+ * kho_block_set_is_empty - Check if the block set has no allocated blocks.
+ * @bs: The block set.
+ *
+ * Return: True if there are no blocks in the set, false otherwise.
+ */
+static inline bool kho_block_set_is_empty(struct kho_block_set *bs)
+{
+ return list_empty(&bs->blocks);
+}
+
void kho_block_it_init(struct kho_block_it *it, struct kho_block_set *bs);
void *kho_block_it_reserve_entry(struct kho_block_it *it);
void *kho_block_it_read_entry(struct kho_block_it *it);
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 69b00e7d0f8f..1b2bda22902d 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -165,9 +165,7 @@ static int __init luo_state_setup(void)
strscpy(luo_ser->compatible, LUO_ABI_COMPATIBLE, sizeof(luo_ser->compatible));
luo_ser->liveupdate_num = luo_global.liveupdate_num + 1;
- err = luo_session_setup_outgoing(&luo_ser->sessions_pa);
- if (err)
- goto exit_free_luo_ser;
+ luo_session_setup_outgoing(&luo_ser->sessions_pa);
err = luo_flb_setup_outgoing(&luo_ser->flbs_pa);
if (err)
diff --git a/kernel/liveupdate/luo_internal.h b/kernel/liveupdate/luo_internal.h
index fe22086bfbeb..ee18f9a11b91 100644
--- a/kernel/liveupdate/luo_internal.h
+++ b/kernel/liveupdate/luo_internal.h
@@ -79,7 +79,7 @@ extern struct rw_semaphore luo_register_rwlock;
int luo_session_create(const char *name, struct file **filep);
int luo_session_retrieve(const char *name, struct file **filep);
-int __init luo_session_setup_outgoing(u64 *sessions_pa);
+void __init luo_session_setup_outgoing(u64 *sessions_pa);
int __init luo_session_setup_incoming(u64 sessions_pa);
int luo_session_serialize(void);
int luo_session_deserialize(void);
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 9f72a8b0a9a8..43342916d314 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -108,15 +108,16 @@ static DECLARE_RWSEM(luo_session_serialize_rwsem);
/**
* struct luo_session_header - Header struct for managing LUO sessions.
- * @count: The number of sessions currently tracked in the @list.
- * @list: The head of the linked list of `struct luo_session` instances.
- * @rwsem: A read-write semaphore providing synchronized access to the
- * session list and other fields in this structure.
- * @header_ser: The header data of serialization array.
- * @ser: The serialized session data (an array of
- * `struct luo_session_ser`).
- * @active: Set to true when first initialized. If previous kernel did not
- * send session data, active stays false for incoming.
+ * @count: The number of sessions currently tracked in the @list.
+ * @list: The head of the linked list of `struct luo_session` instances.
+ * @rwsem: A read-write semaphore providing synchronized access to the
+ * session list and other fields in this structure.
+ * @header_ser: The header data of serialization array.
+ * @ser: The serialized session data (an array of
+ * `struct luo_session_ser`).
+ * @sessions_pa: Points to the location of sessions_pa within struct luo_ser.
+ * @active: Set to true when first initialized. If previous kernel did not
+ * send session data, active stays false for incoming.
*/
struct luo_session_header {
long count;
@@ -124,6 +125,7 @@ struct luo_session_header {
struct rw_semaphore rwsem;
struct luo_session_header_ser *header_ser;
struct luo_session_ser *ser;
+ u64 *sessions_pa;
bool active;
};
@@ -171,10 +173,30 @@ static void luo_session_free(struct luo_session *session)
kfree(session);
}
+static int luo_session_grow_ser(struct luo_session_header *sh)
+{
+ struct luo_session_header_ser *header_ser;
+
+ if (sh->count == LUO_SESSION_MAX)
+ return -ENOMEM;
+
+ if (sh->header_ser)
+ return 0;
+
+ header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
+ if (IS_ERR(header_ser))
+ return PTR_ERR(header_ser);
+
+ sh->header_ser = header_ser;
+ sh->ser = (void *)(header_ser + 1);
+ return 0;
+}
+
static int luo_session_insert(struct luo_session_header *sh,
struct luo_session *session)
{
struct luo_session *it;
+ int err;
guard(rwsem_write)(&sh->rwsem);
@@ -183,8 +205,9 @@ static int luo_session_insert(struct luo_session_header *sh,
* for new session.
*/
if (sh == &luo_session_global.outgoing) {
- if (sh->count == LUO_SESSION_MAX)
- return -ENOMEM;
+ err = luo_session_grow_ser(sh);
+ if (err)
+ return err;
}
/*
@@ -522,21 +545,10 @@ int luo_session_retrieve(const char *name, struct file **filep)
return err;
}
-int __init luo_session_setup_outgoing(u64 *sessions_pa)
+void __init luo_session_setup_outgoing(u64 *sessions_pa)
{
- struct luo_session_header_ser *header_ser;
-
- header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
- if (IS_ERR(header_ser))
- return PTR_ERR(header_ser);
-
- *sessions_pa = virt_to_phys(header_ser);
-
- luo_session_global.outgoing.header_ser = header_ser;
- luo_session_global.outgoing.ser = (void *)(header_ser + 1);
+ luo_session_global.outgoing.sessions_pa = sessions_pa;
luo_session_global.outgoing.active = true;
-
- return 0;
}
int __init luo_session_setup_incoming(u64 sessions_pa)
@@ -642,6 +654,8 @@ int luo_session_serialize(void)
down_write(&luo_session_serialize_rwsem);
down_write(&sh->rwsem);
+ *sh->sessions_pa = 0;
+
list_for_each_entry(session, &sh->list, list) {
err = luo_session_freeze_one(session, &sh->ser[i]);
if (err)
@@ -651,7 +665,11 @@ int luo_session_serialize(void)
sizeof(sh->ser[i].name));
i++;
}
- sh->header_ser->count = sh->count;
+
+ if (sh->header_ser && sh->count > 0) {
+ sh->header_ser->count = sh->count;
+ *sh->sessions_pa = virt_to_phys(sh->header_ser);
+ }
up_write(&sh->rwsem);
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH v5 09/13] liveupdate: Remove limit on the number of sessions
From: Pasha Tatashin @ 2026-06-02 3:17 UTC (permalink / raw)
To: linux-kselftest, rppt, shuah, akpm, linux-mm, skhan, linux-doc,
linux-kernel, corbet, pasha.tatashin, dmatlack, kexec, pratyush,
skhawaja, graf
In-Reply-To: <20260602031717.197696-1-pasha.tatashin@soleen.com>
Currently, the number of LUO sessions is limited by a fixed number of
pre-allocated pages for serialization (16 pages, allowing for ~819
sessions).
This limitation is problematic if LUO is used to support things such as
systemd file descriptor store, and would be used not just as VM memory
but to save other states on the machine.
Remove this limit by transitioning to a linked-block approach for
session metadata serialization. Instead of a single contiguous block,
session metadata is now stored in a chain of 16-page blocks. Each block
starts with a header containing the physical address of the next block
and the number of session entries in the current block.
Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav (Google) <pratyush@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
include/linux/kho/abi/luo.h | 24 +------
kernel/liveupdate/luo_session.c | 115 +++++++++++++++-----------------
2 files changed, 58 insertions(+), 81 deletions(-)
diff --git a/include/linux/kho/abi/luo.h b/include/linux/kho/abi/luo.h
index 9a4fe491812b..79758d92ed5f 100644
--- a/include/linux/kho/abi/luo.h
+++ b/include/linux/kho/abi/luo.h
@@ -33,11 +33,6 @@
* It includes the compatibility string, the liveupdate-number, and pointers
* to sessions and FLBs.
*
- * - struct luo_session_header_ser:
- * Header for the session array. Contains the total page count of the
- * preserved memory block and the number of `struct luo_session_ser`
- * entries that follow.
- *
* - struct luo_session_ser:
* Metadata for a single session, including its name and a physical pointer
* to another preserved memory block containing an array of
@@ -63,13 +58,15 @@
#define _LINUX_KHO_ABI_LUO_H
#include <linux/align.h>
+#include <linux/kho/abi/block.h>
#include <uapi/linux/liveupdate.h>
/*
* The LUO state is registered under this KHO entry name.
*/
#define LUO_KHO_ENTRY_NAME "LUO"
-#define LUO_ABI_COMPATIBLE "luo-v3"
+#define LUO_COMPAT_BASE "luo-v3"
+#define LUO_ABI_COMPATIBLE LUO_COMPAT_BASE "-" KHO_BLOCK_ABI_COMPATIBLE
#define LUO_ABI_COMPAT_LEN ALIGN(sizeof(LUO_ABI_COMPATIBLE), 8)
/**
@@ -118,21 +115,6 @@ struct luo_file_set_ser {
u64 count;
} __packed;
-/**
- * struct luo_session_header_ser - Header for the serialized session data block.
- * @count: The number of `struct luo_session_ser` entries that immediately
- * follow this header in the memory block.
- *
- * This structure is located at the beginning of a contiguous block of
- * physical memory preserved across the kexec. It provides the necessary
- * metadata to interpret the array of session entries that follow.
- *
- * If this structure is modified, `LUO_ABI_COMPATIBLE` must be updated.
- */
-struct luo_session_header_ser {
- u64 count;
-} __packed;
-
/**
* struct luo_session_ser - Represents the serialized metadata for a LUO session.
* @name: The unique name of the session, provided by the userspace at
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index 43342916d314..f6eeb965b3c1 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -24,9 +24,10 @@
* ioctls on /dev/liveupdate.
*
* - Serialization: Session metadata is preserved using the KHO framework. When
- * a live update is triggered via kexec, an array of `struct luo_session_ser`
- * is populated and placed in a preserved memory region. The physical address
- * of this array is stored in the centralized `struct luo_ser` structure.
+ * a live update is triggered via kexec, session metadata is serialized into
+ * a chain of linked-blocks and placed in a preserved memory region. The
+ * physical address of the first block header is stored in the centralized
+ * `struct luo_ser` structure.
*
* Session Lifecycle:
*
@@ -89,6 +90,7 @@
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kexec_handover.h>
+#include <linux/kho_block.h>
#include <linux/kho/abi/luo.h>
#include <linux/list.h>
#include <linux/liveupdate.h>
@@ -98,23 +100,14 @@
#include <uapi/linux/liveupdate.h>
#include "luo_internal.h"
-/* 16 4K pages, give space for 744 sessions */
-#define LUO_SESSION_PGCNT 16ul
-#define LUO_SESSION_MAX (((LUO_SESSION_PGCNT << PAGE_SHIFT) - \
- sizeof(struct luo_session_header_ser)) / \
- sizeof(struct luo_session_ser))
-
static DECLARE_RWSEM(luo_session_serialize_rwsem);
-
/**
* struct luo_session_header - Header struct for managing LUO sessions.
* @count: The number of sessions currently tracked in the @list.
* @list: The head of the linked list of `struct luo_session` instances.
* @rwsem: A read-write semaphore providing synchronized access to the
* session list and other fields in this structure.
- * @header_ser: The header data of serialization array.
- * @ser: The serialized session data (an array of
- * `struct luo_session_ser`).
+ * @block_set: The set of serialization blocks.
* @sessions_pa: Points to the location of sessions_pa within struct luo_ser.
* @active: Set to true when first initialized. If previous kernel did not
* send session data, active stays false for incoming.
@@ -123,8 +116,7 @@ struct luo_session_header {
long count;
struct list_head list;
struct rw_semaphore rwsem;
- struct luo_session_header_ser *header_ser;
- struct luo_session_ser *ser;
+ struct kho_block_set block_set;
u64 *sessions_pa;
bool active;
};
@@ -143,10 +135,14 @@ static struct luo_session_global luo_session_global = {
.incoming = {
.list = LIST_HEAD_INIT(luo_session_global.incoming.list),
.rwsem = __RWSEM_INITIALIZER(luo_session_global.incoming.rwsem),
+ .block_set = KHO_BLOCK_SET_INIT(luo_session_global.incoming.block_set,
+ sizeof(struct luo_session_ser)),
},
.outgoing = {
.list = LIST_HEAD_INIT(luo_session_global.outgoing.list),
.rwsem = __RWSEM_INITIALIZER(luo_session_global.outgoing.rwsem),
+ .block_set = KHO_BLOCK_SET_INIT(luo_session_global.outgoing.block_set,
+ sizeof(struct luo_session_ser)),
},
};
@@ -173,25 +169,6 @@ static void luo_session_free(struct luo_session *session)
kfree(session);
}
-static int luo_session_grow_ser(struct luo_session_header *sh)
-{
- struct luo_session_header_ser *header_ser;
-
- if (sh->count == LUO_SESSION_MAX)
- return -ENOMEM;
-
- if (sh->header_ser)
- return 0;
-
- header_ser = kho_alloc_preserve(LUO_SESSION_PGCNT << PAGE_SHIFT);
- if (IS_ERR(header_ser))
- return PTR_ERR(header_ser);
-
- sh->header_ser = header_ser;
- sh->ser = (void *)(header_ser + 1);
- return 0;
-}
-
static int luo_session_insert(struct luo_session_header *sh,
struct luo_session *session)
{
@@ -205,7 +182,7 @@ static int luo_session_insert(struct luo_session_header *sh,
* for new session.
*/
if (sh == &luo_session_global.outgoing) {
- err = luo_session_grow_ser(sh);
+ err = kho_block_grow(&sh->block_set, sh->count);
if (err)
return err;
}
@@ -232,6 +209,8 @@ static void luo_session_remove(struct luo_session_header *sh,
guard(rwsem_write)(&sh->rwsem);
list_del(&session->list);
sh->count--;
+ if (sh == &luo_session_global.outgoing)
+ kho_block_shrink(&sh->block_set, sh->count);
}
static int luo_session_finish_one(struct luo_session *session)
@@ -553,15 +532,17 @@ void __init luo_session_setup_outgoing(u64 *sessions_pa)
int __init luo_session_setup_incoming(u64 sessions_pa)
{
- struct luo_session_header_ser *header_ser;
+ struct luo_session_header *sh = &luo_session_global.incoming;
+ int err;
- if (sessions_pa) {
- header_ser = phys_to_virt(sessions_pa);
- luo_session_global.incoming.header_ser = header_ser;
- luo_session_global.incoming.ser = (void *)(header_ser + 1);
- luo_session_global.incoming.active = true;
- }
+ if (!sessions_pa)
+ return 0;
+ err = kho_block_set_restore(&sh->block_set, sessions_pa);
+ if (err)
+ return err;
+
+ sh->active = true;
return 0;
}
@@ -603,6 +584,8 @@ int luo_session_deserialize(void)
{
struct luo_session_header *sh = &luo_session_global.incoming;
static bool is_deserialized;
+ struct luo_session_ser *ser;
+ struct kho_block_it it;
static int saved_err;
int err;
@@ -629,18 +612,19 @@ int luo_session_deserialize(void)
* userspace to detect the failure and trigger a reboot, which will
* reliably reset devices and reclaim memory.
*/
- for (int i = 0; i < sh->header_ser->count; i++) {
- err = luo_session_deserialize_one(sh, &sh->ser[i]);
+ kho_block_it_init(&it, &sh->block_set);
+ while ((ser = kho_block_it_read_entry(&it))) {
+ err = luo_session_deserialize_one(sh, ser);
if (err)
goto save_err;
}
- kho_restore_free(sh->header_ser);
- sh->header_ser = NULL;
- sh->ser = NULL;
+ kho_block_set_destroy(&sh->block_set);
return 0;
+
save_err:
+ kho_block_set_destroy(&sh->block_set);
saved_err = err;
return err;
}
@@ -649,36 +633,47 @@ int luo_session_serialize(void)
{
struct luo_session_header *sh = &luo_session_global.outgoing;
struct luo_session *session;
- int i = 0;
+ struct kho_block_it it;
int err;
down_write(&luo_session_serialize_rwsem);
down_write(&sh->rwsem);
*sh->sessions_pa = 0;
+ kho_block_it_init(&it, &sh->block_set);
+
list_for_each_entry(session, &sh->list, list) {
- err = luo_session_freeze_one(session, &sh->ser[i]);
- if (err)
+ struct luo_session_ser *ser = kho_block_it_reserve_entry(&it);
+
+ /* This should not fail normally as blocks were pre-allocated */
+ if (WARN_ON_ONCE(!ser)) {
+ err = -ENOSPC;
goto err_undo;
+ }
- strscpy(sh->ser[i].name, session->name,
- sizeof(sh->ser[i].name));
- i++;
- }
+ err = luo_session_freeze_one(session, ser);
+ if (err) {
+ kho_block_it_prev(&it);
+ goto err_undo;
+ }
- if (sh->header_ser && sh->count > 0) {
- sh->header_ser->count = sh->count;
- *sh->sessions_pa = virt_to_phys(sh->header_ser);
+ strscpy(ser->name, session->name, sizeof(ser->name));
}
+
+ kho_block_it_finalize(&it);
+
+ if (sh->count > 0)
+ *sh->sessions_pa = kho_block_set_head_pa(&sh->block_set);
up_write(&sh->rwsem);
return 0;
err_undo:
list_for_each_entry_continue_reverse(session, &sh->list, list) {
- i--;
- luo_session_unfreeze_one(session, &sh->ser[i]);
- memset(sh->ser[i].name, 0, sizeof(sh->ser[i].name));
+ struct luo_session_ser *ser = kho_block_it_prev(&it);
+
+ luo_session_unfreeze_one(session, ser);
+ memset(ser->name, 0, sizeof(ser->name));
}
up_write(&sh->rwsem);
up_write(&luo_session_serialize_rwsem);
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox