xref: /linux/kernel/liveupdate/kho_block.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*0349ff28SPasha Tatashin // SPDX-License-Identifier: GPL-2.0
2*0349ff28SPasha Tatashin 
3*0349ff28SPasha Tatashin /*
4*0349ff28SPasha Tatashin  * Copyright (c) 2026, Google LLC.
5*0349ff28SPasha Tatashin  * Pasha Tatashin <pasha.tatashin@soleen.com>
6*0349ff28SPasha Tatashin  */
7*0349ff28SPasha Tatashin 
8*0349ff28SPasha Tatashin /**
9*0349ff28SPasha Tatashin  * DOC: KHO Serialization Blocks
10*0349ff28SPasha Tatashin  *
11*0349ff28SPasha Tatashin  * KHO provides a mechanism to preserve stateful data across a kexec handover
12*0349ff28SPasha Tatashin  * by serializing it into memory blocks, and provides the common
13*0349ff28SPasha Tatashin  * infrastructure for managing these blocks.
14*0349ff28SPasha Tatashin  *
15*0349ff28SPasha Tatashin  * Each block consists of a header (struct kho_block_header_ser) followed by an
16*0349ff28SPasha Tatashin  * array of serialized entries. Multiple blocks are linked together via a
17*0349ff28SPasha Tatashin  * physical pointer in the header, forming a linked list that can be easily
18*0349ff28SPasha Tatashin  * traversed in both the current and the next kernel.
19*0349ff28SPasha Tatashin  */
20*0349ff28SPasha Tatashin 
21*0349ff28SPasha Tatashin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22*0349ff28SPasha Tatashin 
23*0349ff28SPasha Tatashin #include <linux/io.h>
24*0349ff28SPasha Tatashin #include <linux/kexec_handover.h>
25*0349ff28SPasha Tatashin #include <linux/kho/abi/block.h>
26*0349ff28SPasha Tatashin #include <linux/kho_block.h>
27*0349ff28SPasha Tatashin #include <linux/slab.h>
28*0349ff28SPasha Tatashin 
29*0349ff28SPasha Tatashin /*
30*0349ff28SPasha Tatashin  * Safeguard limit for the number of serialization blocks. This is used to
31*0349ff28SPasha Tatashin  * prevent infinite loops and excessive memory allocation in case of memory
32*0349ff28SPasha Tatashin  * corruption in the preserved state.
33*0349ff28SPasha Tatashin  *
34*0349ff28SPasha Tatashin  * With a 4KB page size, 10k blocks is about 40MB. For 32-byte entries
35*0349ff28SPasha Tatashin  * (e.g. 4 u64s), each block holds up to 127 entries (accounting for the
36*0349ff28SPasha Tatashin  * 16-byte header), allowing the block set to hold up to 1.27M entries.
37*0349ff28SPasha Tatashin  */
38*0349ff28SPasha Tatashin #define KHO_MAX_BLOCKS 10000
39*0349ff28SPasha Tatashin 
40*0349ff28SPasha Tatashin /**
41*0349ff28SPasha Tatashin  * kho_block_set_init - Initialize a block set.
42*0349ff28SPasha Tatashin  * @bs:         The block set to initialize.
43*0349ff28SPasha Tatashin  * @entry_size: The size of each entry in the blocks.
44*0349ff28SPasha Tatashin  */
45*0349ff28SPasha Tatashin void kho_block_set_init(struct kho_block_set *bs, size_t entry_size)
46*0349ff28SPasha Tatashin {
47*0349ff28SPasha Tatashin 	*bs = (struct kho_block_set)KHO_BLOCK_SET_INIT(*bs, entry_size);
48*0349ff28SPasha Tatashin 	WARN_ON_ONCE(!bs->count_per_block);
49*0349ff28SPasha Tatashin }
50*0349ff28SPasha Tatashin 
51*0349ff28SPasha Tatashin /* Serialized entries start immediately after the block header */
52*0349ff28SPasha Tatashin static void *kho_block_entries(struct kho_block *block)
53*0349ff28SPasha Tatashin {
54*0349ff28SPasha Tatashin 	return (void *)(block->ser + 1);
55*0349ff28SPasha Tatashin }
56*0349ff28SPasha Tatashin 
57*0349ff28SPasha Tatashin /* Get the address of the serialized entry at the specified index */
58*0349ff28SPasha Tatashin static void *kho_block_entry(struct kho_block_set_it *it, u64 index)
59*0349ff28SPasha Tatashin {
60*0349ff28SPasha Tatashin 	return kho_block_entries(it->block) + (index * it->bs->entry_size);
61*0349ff28SPasha Tatashin }
62*0349ff28SPasha Tatashin 
63*0349ff28SPasha Tatashin /* Free serialized data */
64*0349ff28SPasha Tatashin static void kho_block_free_ser(struct kho_block_set *bs,
65*0349ff28SPasha Tatashin 			       struct kho_block_header_ser *ser)
66*0349ff28SPasha Tatashin {
67*0349ff28SPasha Tatashin 	if (bs->incoming)
68*0349ff28SPasha Tatashin 		kho_restore_free(ser);
69*0349ff28SPasha Tatashin 	else
70*0349ff28SPasha Tatashin 		kho_unpreserve_free(ser);
71*0349ff28SPasha Tatashin }
72*0349ff28SPasha Tatashin 
73*0349ff28SPasha Tatashin static struct kho_block_header_ser *kho_block_alloc_ser(struct kho_block_set *bs)
74*0349ff28SPasha Tatashin {
75*0349ff28SPasha Tatashin 	WARN_ON_ONCE(bs->incoming);
76*0349ff28SPasha Tatashin 	return kho_alloc_preserve(KHO_BLOCK_SIZE);
77*0349ff28SPasha Tatashin }
78*0349ff28SPasha Tatashin 
79*0349ff28SPasha Tatashin static int kho_block_add(struct kho_block_set *bs,
80*0349ff28SPasha Tatashin 			 struct kho_block_header_ser *ser)
81*0349ff28SPasha Tatashin {
82*0349ff28SPasha Tatashin 	struct kho_block *block, *last;
83*0349ff28SPasha Tatashin 
84*0349ff28SPasha Tatashin 	if (bs->nblocks >= KHO_MAX_BLOCKS)
85*0349ff28SPasha Tatashin 		return -ENOSPC;
86*0349ff28SPasha Tatashin 
87*0349ff28SPasha Tatashin 	block = kzalloc_obj(*block);
88*0349ff28SPasha Tatashin 	if (!block)
89*0349ff28SPasha Tatashin 		return -ENOMEM;
90*0349ff28SPasha Tatashin 
91*0349ff28SPasha Tatashin 	block->ser = ser;
92*0349ff28SPasha Tatashin 	last = list_last_entry_or_null(&bs->blocks, struct kho_block, list);
93*0349ff28SPasha Tatashin 	list_add_tail(&block->list, &bs->blocks);
94*0349ff28SPasha Tatashin 	bs->nblocks++;
95*0349ff28SPasha Tatashin 
96*0349ff28SPasha Tatashin 	if (last)
97*0349ff28SPasha Tatashin 		last->ser->next = virt_to_phys(ser);
98*0349ff28SPasha Tatashin 	else
99*0349ff28SPasha Tatashin 		bs->head_pa = virt_to_phys(ser);
100*0349ff28SPasha Tatashin 
101*0349ff28SPasha Tatashin 	return 0;
102*0349ff28SPasha Tatashin }
103*0349ff28SPasha Tatashin 
104*0349ff28SPasha Tatashin static int kho_block_set_grow_one(struct kho_block_set *bs)
105*0349ff28SPasha Tatashin {
106*0349ff28SPasha Tatashin 	struct kho_block_header_ser *ser;
107*0349ff28SPasha Tatashin 	int err;
108*0349ff28SPasha Tatashin 
109*0349ff28SPasha Tatashin 	ser = kho_block_alloc_ser(bs);
110*0349ff28SPasha Tatashin 	if (IS_ERR(ser))
111*0349ff28SPasha Tatashin 		return PTR_ERR(ser);
112*0349ff28SPasha Tatashin 
113*0349ff28SPasha Tatashin 	err = kho_block_add(bs, ser);
114*0349ff28SPasha Tatashin 	if (err) {
115*0349ff28SPasha Tatashin 		kho_block_free_ser(bs, ser);
116*0349ff28SPasha Tatashin 		return err;
117*0349ff28SPasha Tatashin 	}
118*0349ff28SPasha Tatashin 
119*0349ff28SPasha Tatashin 	return 0;
120*0349ff28SPasha Tatashin }
121*0349ff28SPasha Tatashin 
122*0349ff28SPasha Tatashin static void kho_block_set_shrink_one(struct kho_block_set *bs)
123*0349ff28SPasha Tatashin {
124*0349ff28SPasha Tatashin 	struct kho_block *last, *new_last;
125*0349ff28SPasha Tatashin 
126*0349ff28SPasha Tatashin 	if (list_empty(&bs->blocks))
127*0349ff28SPasha Tatashin 		return;
128*0349ff28SPasha Tatashin 
129*0349ff28SPasha Tatashin 	last = list_last_entry(&bs->blocks, struct kho_block, list);
130*0349ff28SPasha Tatashin 	list_del(&last->list);
131*0349ff28SPasha Tatashin 	bs->nblocks--;
132*0349ff28SPasha Tatashin 	kho_block_free_ser(bs, last->ser);
133*0349ff28SPasha Tatashin 	kfree(last);
134*0349ff28SPasha Tatashin 
135*0349ff28SPasha Tatashin 	new_last = list_last_entry_or_null(&bs->blocks, struct kho_block, list);
136*0349ff28SPasha Tatashin 	if (new_last)
137*0349ff28SPasha Tatashin 		new_last->ser->next = 0;
138*0349ff28SPasha Tatashin 	else
139*0349ff28SPasha Tatashin 		bs->head_pa = 0;
140*0349ff28SPasha Tatashin }
141*0349ff28SPasha Tatashin 
142*0349ff28SPasha Tatashin /**
143*0349ff28SPasha Tatashin  * kho_block_set_grow - Expand the block set to accommodate the target count.
144*0349ff28SPasha Tatashin  * @bs:    The block set.
145*0349ff28SPasha Tatashin  * @count: The target number of valid entries to accommodate.
146*0349ff28SPasha Tatashin  *
147*0349ff28SPasha Tatashin  * Dynamically preallocates and links preserved memory blocks if the target
148*0349ff28SPasha Tatashin  * entry count exceeds the current total capacity of the set, ensuring they
149*0349ff28SPasha Tatashin  * are available during serialization/deserialization.
150*0349ff28SPasha Tatashin  *
151*0349ff28SPasha Tatashin  * Context: Caller must hold a lock protecting the block set.
152*0349ff28SPasha Tatashin  * Return: 0 on success, or a negative errno on failure.
153*0349ff28SPasha Tatashin  */
154*0349ff28SPasha Tatashin int kho_block_set_grow(struct kho_block_set *bs, u64 count)
155*0349ff28SPasha Tatashin {
156*0349ff28SPasha Tatashin 	long orig_nblocks = bs->nblocks;
157*0349ff28SPasha Tatashin 	int err;
158*0349ff28SPasha Tatashin 
159*0349ff28SPasha Tatashin 	if (WARN_ON_ONCE(bs->incoming))
160*0349ff28SPasha Tatashin 		return -EINVAL;
161*0349ff28SPasha Tatashin 
162*0349ff28SPasha Tatashin 	while (count > bs->nblocks * bs->count_per_block) {
163*0349ff28SPasha Tatashin 		err = kho_block_set_grow_one(bs);
164*0349ff28SPasha Tatashin 		if (err)
165*0349ff28SPasha Tatashin 			goto err_shrink;
166*0349ff28SPasha Tatashin 	}
167*0349ff28SPasha Tatashin 
168*0349ff28SPasha Tatashin 	return 0;
169*0349ff28SPasha Tatashin 
170*0349ff28SPasha Tatashin err_shrink:
171*0349ff28SPasha Tatashin 	while (bs->nblocks > orig_nblocks)
172*0349ff28SPasha Tatashin 		kho_block_set_shrink_one(bs);
173*0349ff28SPasha Tatashin 	return err;
174*0349ff28SPasha Tatashin }
175*0349ff28SPasha Tatashin 
176*0349ff28SPasha Tatashin /**
177*0349ff28SPasha Tatashin  * kho_block_set_shrink - Shrink the block set to accommodate the target count.
178*0349ff28SPasha Tatashin  * @bs:              The block set.
179*0349ff28SPasha Tatashin  * @count:           The target number of valid entries to accommodate.
180*0349ff28SPasha Tatashin  *
181*0349ff28SPasha Tatashin  * Releases and unallocates redundant preserved memory blocks. Checks if the
182*0349ff28SPasha Tatashin  * last block in the set can be removed because the remaining entry count is
183*0349ff28SPasha Tatashin  * fully accommodated by the preceding blocks.
184*0349ff28SPasha Tatashin  *
185*0349ff28SPasha Tatashin  * Note: It is the caller's responsibility to ensure that entries are removed
186*0349ff28SPasha Tatashin  * in the reverse order of their insertion. Because shrinking destroys the last
187*0349ff28SPasha Tatashin  * block in the set, removing entries in any other order would corrupt active
188*0349ff28SPasha Tatashin  * data.
189*0349ff28SPasha Tatashin  *
190*0349ff28SPasha Tatashin  * Context: Caller must hold a lock protecting the block set.
191*0349ff28SPasha Tatashin  */
192*0349ff28SPasha Tatashin void kho_block_set_shrink(struct kho_block_set *bs, u64 count)
193*0349ff28SPasha Tatashin {
194*0349ff28SPasha Tatashin 	while (bs->nblocks > 0 && count <= (bs->nblocks - 1) * bs->count_per_block)
195*0349ff28SPasha Tatashin 		kho_block_set_shrink_one(bs);
196*0349ff28SPasha Tatashin }
197*0349ff28SPasha Tatashin 
198*0349ff28SPasha Tatashin /*
199*0349ff28SPasha Tatashin  * kho_block_set_is_cyclic - Check for cycles in a linked list of blocks.
200*0349ff28SPasha Tatashin  * Uses Floyd's cycle-finding algorithm to ensure sanity of the incoming list.
201*0349ff28SPasha Tatashin  *
202*0349ff28SPasha Tatashin  * Return: true if a cycle or corruption is detected, false otherwise.
203*0349ff28SPasha Tatashin  */
204*0349ff28SPasha Tatashin static bool kho_block_set_is_cyclic(struct kho_block_set *bs)
205*0349ff28SPasha Tatashin {
206*0349ff28SPasha Tatashin 	struct kho_block_header_ser *fast;
207*0349ff28SPasha Tatashin 	struct kho_block_header_ser *slow;
208*0349ff28SPasha Tatashin 	int count = 0;
209*0349ff28SPasha Tatashin 
210*0349ff28SPasha Tatashin 	fast = phys_to_virt(bs->head_pa);
211*0349ff28SPasha Tatashin 	slow = fast;
212*0349ff28SPasha Tatashin 
213*0349ff28SPasha Tatashin 	while (fast) {
214*0349ff28SPasha Tatashin 		if (count++ >= KHO_MAX_BLOCKS) {
215*0349ff28SPasha Tatashin 			pr_err("Block set is corrupted\n");
216*0349ff28SPasha Tatashin 			return true;
217*0349ff28SPasha Tatashin 		}
218*0349ff28SPasha Tatashin 
219*0349ff28SPasha Tatashin 		if (!fast->next)
220*0349ff28SPasha Tatashin 			break;
221*0349ff28SPasha Tatashin 
222*0349ff28SPasha Tatashin 		fast = phys_to_virt(fast->next);
223*0349ff28SPasha Tatashin 		if (!fast->next)
224*0349ff28SPasha Tatashin 			break;
225*0349ff28SPasha Tatashin 
226*0349ff28SPasha Tatashin 		fast = phys_to_virt(fast->next);
227*0349ff28SPasha Tatashin 		slow = phys_to_virt(slow->next);
228*0349ff28SPasha Tatashin 
229*0349ff28SPasha Tatashin 		if (slow == fast) {
230*0349ff28SPasha Tatashin 			pr_err("Block set is corrupted\n");
231*0349ff28SPasha Tatashin 			return true;
232*0349ff28SPasha Tatashin 		}
233*0349ff28SPasha Tatashin 	}
234*0349ff28SPasha Tatashin 
235*0349ff28SPasha Tatashin 	return false;
236*0349ff28SPasha Tatashin }
237*0349ff28SPasha Tatashin 
238*0349ff28SPasha Tatashin /**
239*0349ff28SPasha Tatashin  * kho_block_set_restore - Restore a block set from a physical address.
240*0349ff28SPasha Tatashin  * @bs:      The block set to restore.
241*0349ff28SPasha Tatashin  * @head_pa: Physical address of the first block header.
242*0349ff28SPasha Tatashin  *
243*0349ff28SPasha Tatashin  * Restores a serialized block set from a given physical address. The caller is
244*0349ff28SPasha Tatashin  * responsible for ensuring that the block set @bs has been allocated and
245*0349ff28SPasha Tatashin  * initialized prior to calling this function.
246*0349ff28SPasha Tatashin  *
247*0349ff28SPasha Tatashin  * Return: 0 on success, or a negative errno on failure.
248*0349ff28SPasha Tatashin  */
249*0349ff28SPasha Tatashin int kho_block_set_restore(struct kho_block_set *bs, u64 head_pa)
250*0349ff28SPasha Tatashin {
251*0349ff28SPasha Tatashin 	struct kho_block_header_ser *ser;
252*0349ff28SPasha Tatashin 	u64 next_pa = head_pa;
253*0349ff28SPasha Tatashin 	int err;
254*0349ff28SPasha Tatashin 
255*0349ff28SPasha Tatashin 	/* Restored block sets use size from the previous kernel */
256*0349ff28SPasha Tatashin 	bs->incoming = true;
257*0349ff28SPasha Tatashin 	if (!head_pa)
258*0349ff28SPasha Tatashin 		return 0;
259*0349ff28SPasha Tatashin 
260*0349ff28SPasha Tatashin 	bs->head_pa = head_pa;
261*0349ff28SPasha Tatashin 	if (kho_block_set_is_cyclic(bs)) {
262*0349ff28SPasha Tatashin 		bs->head_pa = 0;
263*0349ff28SPasha Tatashin 		return -EINVAL;
264*0349ff28SPasha Tatashin 	}
265*0349ff28SPasha Tatashin 
266*0349ff28SPasha Tatashin 	while (next_pa) {
267*0349ff28SPasha Tatashin 		ser = phys_to_virt(next_pa);
268*0349ff28SPasha Tatashin 		if (!ser->count || ser->count > bs->count_per_block) {
269*0349ff28SPasha Tatashin 			pr_warn("Block contains invalid entry count: %llu\n",
270*0349ff28SPasha Tatashin 				ser->count);
271*0349ff28SPasha Tatashin 			err = -EINVAL;
272*0349ff28SPasha Tatashin 			goto err_destroy;
273*0349ff28SPasha Tatashin 		}
274*0349ff28SPasha Tatashin 		err = kho_block_add(bs, ser);
275*0349ff28SPasha Tatashin 		if (err)
276*0349ff28SPasha Tatashin 			goto err_destroy;
277*0349ff28SPasha Tatashin 		next_pa = ser->next;
278*0349ff28SPasha Tatashin 	}
279*0349ff28SPasha Tatashin 
280*0349ff28SPasha Tatashin 	return 0;
281*0349ff28SPasha Tatashin 
282*0349ff28SPasha Tatashin err_destroy:
283*0349ff28SPasha Tatashin 	kho_block_set_destroy(bs);
284*0349ff28SPasha Tatashin 
285*0349ff28SPasha Tatashin 	/* Free the remaining un-restored blocks in the physical chain */
286*0349ff28SPasha Tatashin 	while (next_pa) {
287*0349ff28SPasha Tatashin 		struct kho_block_header_ser *next_ser = phys_to_virt(next_pa);
288*0349ff28SPasha Tatashin 
289*0349ff28SPasha Tatashin 		next_pa = next_ser->next;
290*0349ff28SPasha Tatashin 		kho_block_free_ser(bs, next_ser);
291*0349ff28SPasha Tatashin 	}
292*0349ff28SPasha Tatashin 	return err;
293*0349ff28SPasha Tatashin }
294*0349ff28SPasha Tatashin 
295*0349ff28SPasha Tatashin /**
296*0349ff28SPasha Tatashin  * kho_block_set_destroy - Destroy all blocks in a block set.
297*0349ff28SPasha Tatashin  * @bs:          The block set.
298*0349ff28SPasha Tatashin  */
299*0349ff28SPasha Tatashin void kho_block_set_destroy(struct kho_block_set *bs)
300*0349ff28SPasha Tatashin {
301*0349ff28SPasha Tatashin 	struct kho_block *block, *tmp;
302*0349ff28SPasha Tatashin 
303*0349ff28SPasha Tatashin 	list_for_each_entry_safe(block, tmp, &bs->blocks, list) {
304*0349ff28SPasha Tatashin 		list_del(&block->list);
305*0349ff28SPasha Tatashin 		kho_block_free_ser(bs, block->ser);
306*0349ff28SPasha Tatashin 		kfree(block);
307*0349ff28SPasha Tatashin 	}
308*0349ff28SPasha Tatashin 	bs->nblocks = 0;
309*0349ff28SPasha Tatashin 	bs->head_pa = 0;
310*0349ff28SPasha Tatashin }
311*0349ff28SPasha Tatashin 
312*0349ff28SPasha Tatashin /**
313*0349ff28SPasha Tatashin  * kho_block_set_clear - Clear all serialized data in a block set.
314*0349ff28SPasha Tatashin  * @bs: The block set to clear.
315*0349ff28SPasha Tatashin  */
316*0349ff28SPasha Tatashin void kho_block_set_clear(struct kho_block_set *bs)
317*0349ff28SPasha Tatashin {
318*0349ff28SPasha Tatashin 	struct kho_block *block;
319*0349ff28SPasha Tatashin 
320*0349ff28SPasha Tatashin 	list_for_each_entry(block, &bs->blocks, list) {
321*0349ff28SPasha Tatashin 		block->ser->count = 0;
322*0349ff28SPasha Tatashin 		memset(block->ser + 1, 0, KHO_BLOCK_SIZE - sizeof(*block->ser));
323*0349ff28SPasha Tatashin 	}
324*0349ff28SPasha Tatashin }
325*0349ff28SPasha Tatashin 
326*0349ff28SPasha Tatashin /**
327*0349ff28SPasha Tatashin  * kho_block_set_it_init - Initialize a block set iterator.
328*0349ff28SPasha Tatashin  * @it:         The iterator to initialize.
329*0349ff28SPasha Tatashin  * @bs:         The block set to iterate over.
330*0349ff28SPasha Tatashin  */
331*0349ff28SPasha Tatashin void kho_block_set_it_init(struct kho_block_set_it *it, struct kho_block_set *bs)
332*0349ff28SPasha Tatashin {
333*0349ff28SPasha Tatashin 	it->bs = bs;
334*0349ff28SPasha Tatashin 	it->block = list_first_entry_or_null(&bs->blocks, struct kho_block, list);
335*0349ff28SPasha Tatashin 	it->i = 0;
336*0349ff28SPasha Tatashin }
337*0349ff28SPasha Tatashin 
338*0349ff28SPasha Tatashin /**
339*0349ff28SPasha Tatashin  * kho_block_set_it_reserve_entry - Reserve and return the next available slot for writing.
340*0349ff28SPasha Tatashin  * @it: The block iterator.
341*0349ff28SPasha Tatashin  *
342*0349ff28SPasha Tatashin  * Reserves a slot in the current block during state serialization to add a new
343*0349ff28SPasha Tatashin  * entry, advancing the internal index. If the current block is full, it
344*0349ff28SPasha Tatashin  * automatically moves to the next block in the set.
345*0349ff28SPasha Tatashin  *
346*0349ff28SPasha Tatashin  * Return: A pointer to the reserved entry slot, or NULL if the block set's
347*0349ff28SPasha Tatashin  * capacity is fully exhausted.
348*0349ff28SPasha Tatashin  */
349*0349ff28SPasha Tatashin void *kho_block_set_it_reserve_entry(struct kho_block_set_it *it)
350*0349ff28SPasha Tatashin {
351*0349ff28SPasha Tatashin 	void *entry;
352*0349ff28SPasha Tatashin 
353*0349ff28SPasha Tatashin 	if (!it->block)
354*0349ff28SPasha Tatashin 		return NULL;
355*0349ff28SPasha Tatashin 
356*0349ff28SPasha Tatashin 	if (it->i == it->bs->count_per_block) {
357*0349ff28SPasha Tatashin 		if (list_is_last(&it->block->list, &it->bs->blocks))
358*0349ff28SPasha Tatashin 			return NULL;
359*0349ff28SPasha Tatashin 		it->block = list_next_entry(it->block, list);
360*0349ff28SPasha Tatashin 		it->i = 0;
361*0349ff28SPasha Tatashin 	}
362*0349ff28SPasha Tatashin 
363*0349ff28SPasha Tatashin 	entry = kho_block_entry(it, it->i++);
364*0349ff28SPasha Tatashin 	it->block->ser->count = it->i;
365*0349ff28SPasha Tatashin 	return entry;
366*0349ff28SPasha Tatashin }
367*0349ff28SPasha Tatashin 
368*0349ff28SPasha Tatashin /**
369*0349ff28SPasha Tatashin  * kho_block_set_it_read_entry - Read the next serialized entry from the block set.
370*0349ff28SPasha Tatashin  * @it: The block iterator.
371*0349ff28SPasha Tatashin  *
372*0349ff28SPasha Tatashin  * Iterates through previously written entries during state deserialization,
373*0349ff28SPasha Tatashin  * respecting the actual count stored in each block's header.
374*0349ff28SPasha Tatashin  *
375*0349ff28SPasha Tatashin  * Return: A pointer to the next serialized entry, or NULL if all serialized
376*0349ff28SPasha Tatashin  * entries have been read.
377*0349ff28SPasha Tatashin  */
378*0349ff28SPasha Tatashin void *kho_block_set_it_read_entry(struct kho_block_set_it *it)
379*0349ff28SPasha Tatashin {
380*0349ff28SPasha Tatashin 	if (!it->block)
381*0349ff28SPasha Tatashin 		return NULL;
382*0349ff28SPasha Tatashin 
383*0349ff28SPasha Tatashin 	if (it->i == it->block->ser->count) {
384*0349ff28SPasha Tatashin 		if (list_is_last(&it->block->list, &it->bs->blocks))
385*0349ff28SPasha Tatashin 			return NULL;
386*0349ff28SPasha Tatashin 		it->block = list_next_entry(it->block, list);
387*0349ff28SPasha Tatashin 		it->i = 0;
388*0349ff28SPasha Tatashin 	}
389*0349ff28SPasha Tatashin 
390*0349ff28SPasha Tatashin 	return kho_block_entry(it, it->i++);
391*0349ff28SPasha Tatashin }
392*0349ff28SPasha Tatashin 
393*0349ff28SPasha Tatashin /**
394*0349ff28SPasha Tatashin  * kho_block_set_it_prev - Return the previous entry slot in the block set.
395*0349ff28SPasha Tatashin  * @it: The block iterator.
396*0349ff28SPasha Tatashin  *
397*0349ff28SPasha Tatashin  * If the current index is at the start of a block, it automatically moves to
398*0349ff28SPasha Tatashin  * the end of the previous block.
399*0349ff28SPasha Tatashin  *
400*0349ff28SPasha Tatashin  * Return: A pointer to the previous entry slot, or NULL if at the very
401*0349ff28SPasha Tatashin  * beginning of the block set.
402*0349ff28SPasha Tatashin  */
403*0349ff28SPasha Tatashin void *kho_block_set_it_prev(struct kho_block_set_it *it)
404*0349ff28SPasha Tatashin {
405*0349ff28SPasha Tatashin 	if (!it->block)
406*0349ff28SPasha Tatashin 		return NULL;
407*0349ff28SPasha Tatashin 
408*0349ff28SPasha Tatashin 	if (it->i == 0) {
409*0349ff28SPasha Tatashin 		if (list_is_first(&it->block->list, &it->bs->blocks))
410*0349ff28SPasha Tatashin 			return NULL;
411*0349ff28SPasha Tatashin 		it->block = list_prev_entry(it->block, list);
412*0349ff28SPasha Tatashin 		it->i = it->bs->count_per_block;
413*0349ff28SPasha Tatashin 	}
414*0349ff28SPasha Tatashin 
415*0349ff28SPasha Tatashin 	return kho_block_entry(it, --it->i);
416*0349ff28SPasha Tatashin }
417