From: "Pasha Tatashin" <pasha.tatashin@soleen.com>
To: "Mike Rapoport" <rppt@kernel.org>,
"Pasha Tatashin" <pasha.tatashin@soleen.com>
Cc: <linux-kselftest@vger.kernel.org>, <shuah@kernel.org>,
<linux-mm@kvack.org>, <skhan@linuxfoundation.org>,
<linux-doc@vger.kernel.org>, <vipinsh@google.com>,
<linux-kernel@vger.kernel.org>, <corbet@lwn.net>,
<dmatlack@google.com>, <kexec@lists.infradead.org>,
<pratyush@kernel.org>, <rdunlap@infradead.org>, <graf@amazon.com>
Subject: Re: [PATCH v5 2/6] kho: support incremental entry publication in kho_block
Date: Wed, 11 Nov 2026 10:15:36 -0500 [thread overview]
Message-ID: <DMSHB0JBS5V8.1IZD7WPEQGPMA@soleen.com> (raw)
In-Reply-To: <179439592040.425130.12391503339294194908.b4-review@b4>
On Wed Nov 11, 2026 at 6:18 AM EST, Mike Rapoport wrote:
> Hi,
>
>> To allow LUO sessions to be frozen and committed into the outgoing KHO
>> subtree from userspace ahead of reboot, kho_block needs to support
>> publishing entries one by one into a live serialized block chain while
>> new blocks may still be preallocated or shrunk for uncommitted sessions.
>>
>> Currently, kho_block assumes outgoing block sets are populated in a
>> single batch during reboot() before the chain is exposed: preallocated
>> blocks are linked onto the chain immediately even while empty
>> (ser->count == 0), and reserving a slot increments ser->count before the
>> entry is populated.
>>
>> Address this by keeping preallocated outgoing blocks unlinked in
>> kho_block_add(), linking blocks lazily only as entries are written. Add
>> kho_block_set_it_append() to copy each entry into its slot before
>> publishing the updated ser->count and next-block link via
>> smp_store_release(), paired with smp_load_acquire() in
>> kho_block_set_it_read_entry().
>>
>> Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
>> ---
>> include/linux/kho_block.h | 1 +
>> kernel/liveupdate/kho_block.c | 86 +++++++++++++++++++++++++++++++----
>> 2 files changed, 77 insertions(+), 10 deletions(-)
>>
>> diff --git a/include/linux/kho_block.h b/include/linux/kho_block.h
>> index 93a7cc2be5f51..a9e696ef3e08e 100644
>> --- a/include/linux/kho_block.h
>> +++ b/include/linux/kho_block.h
>> @@ -100,6 +100,7 @@ static inline bool kho_block_set_is_empty(struct kho_block_set *bs)
>>
>> void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs);
>> void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it);
>> +void *kho_block_set_it_append(struct kho_block_set_it *it, const void *src);
>> void *kho_block_set_it_read_entry(struct kho_block_set_it *it);
>> void *kho_block_set_it_prev(struct kho_block_set_it *it);
>>
>> diff --git a/kernel/liveupdate/kho_block.c b/kernel/liveupdate/kho_block.c
>> index 0d2a342ef4228..2813c6fd593c4 100644
>> --- a/kernel/liveupdate/kho_block.c
>> +++ b/kernel/liveupdate/kho_block.c
>> @@ -16,6 +16,12 @@
>> * 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.
>> + *
>> + * Outgoing blocks preallocated via kho_block_set_grow() remain unlinked until
>> + * entries are written into them. Callers publishing entries incrementally into
>> + * a live KHO subtree can use kho_block_set_it_append(), which populates each
>> + * entry before exposing the updated block count or next-block link via
>> + * release-store ordering.
>
>
>> */
>>
>> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> @@ -93,9 +99,7 @@ static int kho_block_add(struct kho_block_set *bs,
>> list_add_tail(&block->list, &bs->blocks);
>> bs->nblocks++;
>>
>> - if (last)
>> - last->ser->next = virt_to_phys(ser);
>> - else
>> + if (!last)
>> bs->head_pa = virt_to_phys(ser);
>
>
>>
>> return 0;
>> @@ -129,14 +133,15 @@ static void kho_block_set_shrink_one(struct kho_block_set *bs)
>> 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;
>> + WRITE_ONCE(new_last->ser->next, 0);
>
> Why this is needed? Docs explicitly state that "Synchronization and
> locking are the responsibility of the caller".
This was done as prep for the kho-ro changes: when an uncommitted
session closes and shrinks an unused trailing block, new_last may
already be published and read concurrently via kho-ro. I will move it
into a separate patch in the kho-ro series to clearly explain the
intent.
>
>> else
>> bs->head_pa = 0;
>> +
>> + kho_block_free_ser(bs, last->ser);
>> + kfree(last);
>
> This change seems unnecessary.
Leftover from the earlier pop support (unlinking last->ser before
unpreserving/freeing it). Will drop this change in v6 as well; I have
since simplified the logic so adding to KHO is always a one-way trip.
>> }
>>
>> /**
>> @@ -144,9 +149,9 @@ static void kho_block_set_shrink_one(struct kho_block_set *bs)
>> * @bs: The block set.
>> * @count: The target number of valid entries to accommodate.
>> *
>> - * Dynamically preallocates and links preserved memory blocks if the target
>> - * entry count exceeds the current total capacity of the set, ensuring they
>> - * are available during serialization/deserialization.
>> + * Dynamically preallocates preserved memory blocks if the target entry count
>> + * exceeds the current total capacity of the set, ensuring they are available
>> + * during serialization/deserialization.
>> *
>> * Context: Caller must hold a lock protecting the block set.
>> * Return: 0 on success, or a negative errno on failure.
>> @@ -318,6 +323,7 @@ void kho_block_set_clear(struct kho_block_set *bs)
>> struct kho_block *block;
>>
>> list_for_each_entry(block, &bs->blocks, list) {
>> + block->ser->next = 0;
>> block->ser->count = 0;
>> memset(block->ser + 1, 0, KHO_BLOCK_SIZE - sizeof(*block->ser));
>> }
>> @@ -348,6 +354,7 @@ void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs
>> */
>> void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it)
>> {
>> + struct kho_block *prev = NULL;
>> void *entry;
>>
>> if (!it->block)
>> @@ -356,15 +363,73 @@ void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it)
>> if (it->i == it->bs->count_per_block) {
>> if (list_is_last(&it->block->list, &it->bs->blocks))
>> return NULL;
>> + prev = it->block;
>> it->block = list_next_entry(it->block, list);
>> it->i = 0;
>> }
>>
>> + if (!it->i)
>> + it->block->ser->next = 0;
>> +
>> entry = kho_block_entry(it, it->i++);
>> it->block->ser->count = it->i;
>> + if (prev)
>> + prev->ser->next = virt_to_phys(it->block->ser);
>
> kernel-doc should mention that this function now links blocks.
>
> And I believe there should be a paragraph about the new flow in the DOC:
> section.
+1, I will update the kernel-doc.
>
>> +
>> return entry;
>> }
>>
>> +/**
>> + * kho_block_set_it_append - Write and publish an entry at the next slot.
>> + * @it: The block iterator.
>> + * @src: Entry data of size @it->bs->entry_size to copy into the slot.
>> + *
>> + * Copies @src into the next available slot before updating the block's entry
>> + * count or linking a newly occupied block to the previous block, so concurrent
>> + * readers never observe an uninitialized slot or an empty block.
>> + *
>> + * Return: A pointer to the written entry slot, or NULL if the block set's
>> + * capacity is fully exhausted.
>> + */
>> +void *kho_block_set_it_append(struct kho_block_set_it *it, const void *src)
>> +{
>> + struct kho_block *prev = NULL;
>> + void *dst;
>> +
>> + if (!it->block)
>> + return NULL;
>> +
>> + if (it->i == it->bs->count_per_block) {
>> + if (list_is_last(&it->block->list, &it->bs->blocks))
>> + return NULL;
>> + prev = it->block;
>> + it->block = list_next_entry(it->block, list);
>> + it->i = 0;
>> + }
>> +
>> + if (!it->i)
>> + WRITE_ONCE(it->block->ser->next, 0);
>> +
>> + dst = kho_block_entry(it, it->i++);
>> + memcpy(dst, src, it->bs->entry_size);
>> +
>> + /*
>> + * Ensure the serialized entry is visible before exposing the
>> + * incremented count to concurrent readers.
>> + */
>> + smp_store_release(&it->block->ser->count, it->i);
>> + if (prev) {
>> + /*
>> + * Ensure the new block's first entry and header are visible
>> + * before linking prev->ser->next for concurrent readers.
>> + */
>> + smp_store_release(&prev->ser->next,
>> + virt_to_phys(it->block->ser));
>
> Until now the locking model was "the caller is responsible to hold a
> lock".
>
> Using WRITE_ONCE() and smp_store_release() instead of plain assignments
> implies that it is not anymore, or at least not always.
>
> This should be a well documented separate change.
Agreed. I wrote this patch as preparation for both early freeze and
kho-ro: mutations to struct kho_block_set itself still require caller
locking, while the WRITE_ONCE() and release/acquire ordering allow a
concurrent kho-ro reader to walk the live serialized physical block
chain while the host continues committing entries under its lock.
In v6, I will remove the kho-ro parts, and submit them together with the
next iteration of that series.
--
Pasha
next prev parent reply other threads:[~2026-11-11 15:15 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 5:00 [PATCH v5 0/6] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl Pasha Tatashin
2026-11-05 5:00 ` [PATCH v5 1/6] liveupdate: Move file_set_ser handling to luo_session_serialize() Pasha Tatashin
2026-11-05 5:00 ` [PATCH v5 2/6] kho: support incremental entry publication in kho_block Pasha Tatashin
2026-11-11 11:18 ` Mike Rapoport
2026-11-11 15:15 ` Pasha Tatashin [this message]
2026-11-05 5:00 ` [PATCH v5 3/6] liveupdate: luo: serialize sessions via per-session publish helper Pasha Tatashin
2026-11-11 11:18 ` Mike Rapoport
2026-11-11 15:37 ` Pasha Tatashin
2026-11-05 5:00 ` [PATCH v5 4/6] liveupdate: luo_flb: support incremental outgoing FLB publication Pasha Tatashin
2026-11-11 11:18 ` Mike Rapoport
2026-11-05 5:00 ` [PATCH v5 5/6] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl to freeze sessions early Pasha Tatashin
2026-11-11 11:18 ` Mike Rapoport
2026-11-05 5:00 ` [PATCH v5 6/6] selftests: liveupdate: Add tests for LIVEUPDATE_SESSION_FREEZE Pasha Tatashin
2026-11-11 9:56 ` [PATCH v5 0/6] liveupdate: Add LIVEUPDATE_SESSION_FREEZE ioctl Mike Rapoport
2026-11-11 11:18 ` Mike Rapoport
2026-11-11 11:45 ` Pasha Tatashin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DMSHB0JBS5V8.1IZD7WPEQGPMA@soleen.com \
--to=pasha.tatashin@soleen.com \
--cc=corbet@lwn.net \
--cc=dmatlack@google.com \
--cc=graf@amazon.com \
--cc=kexec@lists.infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=pratyush@kernel.org \
--cc=rdunlap@infradead.org \
--cc=rppt@kernel.org \
--cc=shuah@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=vipinsh@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®