All of lore.kernel.org
 help / color / mirror / Atom feed
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



  reply	other threads:[~2026-11-11 15:15 UTC|newest]

Thread overview: 18+ 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-11 21:08     ` Pasha Tatashin
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-11 21:13     ` Pasha Tatashin
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.