DPDK 26.11.0-rc2
rte_stack_pile.h
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2026 SmartShare Systems
3 */
4
5#ifndef _RTE_STACK_PILE_H_
6#define _RTE_STACK_PILE_H_
7
8#include <rte_memcpy.h>
9
10#include "rte_stack_lf.h"
11#ifdef RTE_STACK_LF_SUPPORTED
15#define RTE_STACK_PILE_SUPPORTED
16#endif
17
18static __rte_always_inline unsigned int
19__rte_stack_pile_count(struct rte_stack *s)
20{
21 /* stack_lf_push() and stack_lf_pop() do not update the list's contents
22 * and stack_lf->len atomically, which can cause the list to appear
23 * shorter than it actually is if this function is called while other
24 * threads are modifying the list.
25 *
26 * However, given the inherently approximate nature of the get_count
27 * callback -- even if the list and its size were updated atomically,
28 * the size could change between when get_count executes and when the
29 * value is returned to the caller -- this is acceptable.
30 *
31 * The stack_lf->len updates are placed such that the list may appear to
32 * have fewer elements than it does, but will never appear to have more
33 * elements. If the mempool is near-empty to the point that this is a
34 * concern, the user should consider increasing the mempool size.
35 */
36 return RTE_MIN((unsigned int)s->capacity,
37 __rte_stack_lf_elems_count(&s->stack_pile.bulk) * RTE_STACK_PILE_BULK_SIZE +
38 __rte_stack_lf_elems_count(&s->stack_pile.solo));
39}
40
41static __rte_always_inline void
42__rte_stack_pile_bulk_push_elems(struct rte_stack_lf_list *list,
43 struct rte_stack_pile_bulk_elem *first,
44 struct rte_stack_pile_bulk_elem *last,
45 unsigned int num)
46{
47 __rte_stack_lf_push_elems(list,
48 (struct rte_stack_lf_elem *)first,
49 (struct rte_stack_lf_elem *)last,
50 num);
51}
52
53static __rte_always_inline struct rte_stack_pile_bulk_elem *
54__rte_stack_pile_bulk_pop_elems(struct rte_stack_lf_list *list,
55 unsigned int num,
56 void **obj_table,
57 struct rte_stack_pile_bulk_elem **last)
58{
59 struct rte_stack_lf_elem *first =
60 __rte_stack_lf_pop_elems(list, num, NULL,
61 (struct rte_stack_lf_elem **)last);
62 if (first == NULL)
63 return NULL;
64
65 if (obj_table != NULL) {
66 /*
67 * Traverse the list to copy the bulks.
68 * Note:
69 * Done here to minimize the time spent in the retry loop in
70 * __rte_stack_lf_pop_elems(),
71 * and to avoid modifying __rte_stack_lf_pop_elems().
72 */
73 struct rte_stack_lf_elem *tmp = first;
74 for (unsigned int i = 0; i < num; i++, tmp = tmp->next)
75 rte_memcpy(&obj_table[i * RTE_STACK_PILE_BULK_SIZE],
76 ((struct rte_stack_pile_bulk_elem *)tmp)->objs,
77 sizeof(void *) * RTE_STACK_PILE_BULK_SIZE);
78 }
79
80 return (struct rte_stack_pile_bulk_elem *)first;
81}
82
95static __rte_always_inline unsigned int
96__rte_stack_pile_push(struct rte_stack *s,
97 void * const *obj_table,
98 unsigned int n)
99{
100 RTE_ASSERT(s != NULL);
101 RTE_ASSERT(obj_table != NULL);
102
103 struct rte_stack_pile *pile = &s->stack_pile;
104 struct rte_stack_pile_bulk_elem *bulk_first = NULL, *bulk_last = NULL;
105 struct rte_stack_lf_elem *solo_first = NULL, *solo_last = NULL;
106 struct rte_stack_lf_elem *tmp;
107 unsigned int n_bulk = n / RTE_STACK_PILE_BULK_SIZE;
108 unsigned int n_solo = n & (RTE_STACK_PILE_BULK_SIZE - 1);
109 unsigned int i;
110
111 if (unlikely(n_bulk == 0)) {
112 if (unlikely(n_solo == 0))
113 return 0;
114 goto solo;
115 }
116
117 /* Allocate n_bulk elements from the free list. */
118 bulk_first = __rte_stack_pile_bulk_pop_elems(&pile->free_bulk, n_bulk, NULL, &bulk_last);
119 if (unlikely(bulk_first == NULL))
120 return 0; /* Failed. */
121
122 if (likely(n_solo == 0))
123 goto bulk;
124
125solo:
126 /* Allocate n_solo elements from the free list. */
127 solo_first = __rte_stack_lf_pop_elems(&pile->free_solo, n_solo, NULL, &solo_last);
128 if (unlikely(solo_first == NULL)) {
129 /* Failed. Roll back. */
130 if (n_bulk > 0)
131 __rte_stack_pile_bulk_push_elems(&pile->free_bulk,
132 bulk_first, bulk_last, n_bulk);
133 return 0;
134 }
135
136 /*
137 * Construct the solo elements.
138 * Copy objects in reverse order.
139 */
140 tmp = solo_first;
141 __rte_assume(n_solo > 0);
142 __rte_assume(n_solo < RTE_STACK_PILE_BULK_SIZE);
143 for (i = 0; i < n_solo; i++, tmp = tmp->next)
144 tmp->data = obj_table[n_bulk * RTE_STACK_PILE_BULK_SIZE + n_solo - i - 1];
145
146 /* Push them to the solo list. */
147 __rte_stack_lf_push_elems(&pile->solo, solo_first, solo_last, n_solo);
148
149 if (unlikely(n_bulk == 0))
150 return n; /* Done. */
151
152bulk:
153 /*
154 * Construct the bulk elements.
155 * Copy bulks in reverse order, but ignore the object order within each bulk.
156 */
157 tmp = (struct rte_stack_lf_elem *)bulk_first;
158 __rte_assume(n_bulk > 0);
159 for (i = 0; i < n_bulk; i++, tmp = tmp->next)
160 rte_memcpy(((struct rte_stack_pile_bulk_elem *)tmp)->objs,
161 &obj_table[(n_bulk - i - 1) * RTE_STACK_PILE_BULK_SIZE],
162 sizeof(void *) * RTE_STACK_PILE_BULK_SIZE);
163
164 /* Push them to the bulk list. */
165 __rte_stack_pile_bulk_push_elems(&pile->bulk, bulk_first, bulk_last, n_bulk);
166
167 return n;
168}
169
183static __rte_noinline unsigned int
184__rte_stack_pile_pop_frag(struct rte_stack_pile * const pile,
185 void **obj_table,
186 unsigned int n)
187{
188 alignas(RTE_CACHE_LINE_SIZE) void *obj_frag[RTE_STACK_PILE_BULK_SIZE];
189 struct rte_stack_pile_bulk_elem *frag = NULL;
190 struct rte_stack_lf_elem *solo_first = NULL, *solo_last = NULL, *tmp;
191 unsigned int i;
192
193 RTE_ASSERT(n > 0);
194 RTE_ASSERT(n < RTE_STACK_PILE_BULK_SIZE);
195
196 /* Fetch a bulk element for fragmentation. */
197 frag = __rte_stack_pile_bulk_pop_elems(&pile->bulk, 1, obj_frag, NULL);
198 if (unlikely(frag == NULL))
199 return 0; /* Not available. */
200
201 /* Get n objects from the bulk element. */
202 __rte_assume(n > 0);
203 __rte_assume(n < RTE_STACK_PILE_BULK_SIZE);
204 for (i = 0; i < n; i++)
205 obj_table[i] = obj_frag[i];
206
207 /* Fetch free solo elements for the excess objects. */
208 __rte_assume(RTE_STACK_PILE_BULK_SIZE - n > 0);
209 __rte_assume(RTE_STACK_PILE_BULK_SIZE - n < RTE_STACK_PILE_BULK_SIZE);
210 solo_first = __rte_stack_lf_pop_elems(&pile->free_solo,
211 RTE_STACK_PILE_BULK_SIZE - n, NULL, &solo_last);
212 if (unlikely(solo_first == NULL)) {
213 /*
214 * Failed. Roll back.
215 * No further action is required to roll the bulk element
216 * back into the pile of bulk elements, as the objects in
217 * the bulk element are intact.
218 */
219 __rte_stack_pile_bulk_push_elems(&pile->bulk, frag, frag, 1);
220 return 0;
221 }
222
223 /* Construct the solo elements from the excess objects. */
224 tmp = solo_first;
225 __rte_assume(n > 0);
226 __rte_assume(n < RTE_STACK_PILE_BULK_SIZE);
227 for (i = n; i < RTE_STACK_PILE_BULK_SIZE; i++, tmp = tmp->next)
228 tmp->data = obj_frag[i];
229
230 /* Push the excess objects as solo elements. */
231 __rte_stack_lf_push_elems(&pile->solo, solo_first, solo_last,
232 RTE_STACK_PILE_BULK_SIZE - n);
233
234 /* Free the bulk element. */
235 __rte_stack_pile_bulk_push_elems(&pile->free_bulk, frag, frag, 1);
236
237 return n;
238}
239
252static __rte_always_inline unsigned int
253__rte_stack_pile_pop(struct rte_stack *s,
254 void **obj_table,
255 unsigned int n)
256{
257 RTE_ASSERT(s != NULL);
258 RTE_ASSERT(obj_table != NULL);
259
260 struct rte_stack_pile *pile = &s->stack_pile;
261 struct rte_stack_pile_bulk_elem *bulk_first = NULL, *bulk_last = NULL;
262 struct rte_stack_lf_elem *solo_first = NULL, *solo_last = NULL;
263 unsigned int n_bulk = n / RTE_STACK_PILE_BULK_SIZE;
264 unsigned int n_solo = n & (RTE_STACK_PILE_BULK_SIZE - 1);
265
266 if (unlikely(n_bulk == 0)) {
267 if (unlikely(n_solo == 0))
268 return 0;
269 goto solo;
270 }
271
272bulk:
273 /* Fetch n_bulk * RTE_STACK_PILE_BULK_SIZE objects as bulk elements. */
274 bulk_first = __rte_stack_pile_bulk_pop_elems(&pile->bulk, n_bulk, obj_table, &bulk_last);
275 if (unlikely(bulk_first == NULL)) {
276 /*
277 * Not available.
278 * Determine how many are available, and retry with fewer than before.
279 * Remaining objects are to be fetched as solo elements instead.
280 * TODO: Retry could be avoided if pop_elems() had a burst variant.
281 */
282 unsigned int delta_bulk = n_bulk - __rte_stack_lf_elems_count(&pile->bulk);
283
284 if (unlikely((int)delta_bulk <= 0)) {
285 /*
286 * Another thread raced to add bulk elements; more are available now.
287 * Retry with one less than before.
288 * Note: By only retrying with fewer, progress is guaranteed.
289 */
290 delta_bulk = 1;
291 }
292 n_bulk -= delta_bulk;
293 n_solo += RTE_STACK_PILE_BULK_SIZE * delta_bulk;
294
295 if (n_bulk == 0)
296 goto solo; /* No bulk elements available. Stop retrying. */
297
298 goto bulk;
299 }
300
301 obj_table += n_bulk * RTE_STACK_PILE_BULK_SIZE;
302
303 if (likely(n_solo == 0))
304 goto done;
305
306solo:
307 /* Fetch n_solo objects as solo elements. */
308 solo_first = __rte_stack_lf_pop_elems(&pile->solo, n_solo,
309 obj_table, &solo_last);
310 if (solo_first != NULL)
311 goto done;
312
313 /* Solo elements not available. Try fetching objects by fragmentation of a bulk element. */
314 if (unlikely(n_solo >= RTE_STACK_PILE_BULK_SIZE))
315 goto fail; /* Ran out of bulk elements above. Don't try to fetch one more. */
316
317 if (unlikely(__rte_stack_pile_pop_frag(pile, obj_table, n_solo) == 0))
318 goto fail; /* Fragmentation failed. */
319
320done:
321 /* Success. Free the elements. */
322 if (bulk_first != NULL)
323 __rte_stack_pile_bulk_push_elems(&pile->free_bulk, bulk_first, bulk_last, n_bulk);
324 if (solo_first != NULL)
325 __rte_stack_lf_push_elems(&pile->free_solo, solo_first, solo_last, n_solo);
326
327 return n;
328
329fail:
330 /* Failed. Roll back. */
331 if (bulk_first != NULL)
332 __rte_stack_pile_bulk_push_elems(&pile->bulk, bulk_first, bulk_last, n_bulk);
333
334 return 0;
335}
336
345void
346rte_stack_pile_init(struct rte_stack *s, unsigned int count);
347
356ssize_t
357rte_stack_pile_get_memsize(unsigned int count);
358
359#endif /* _RTE_STACK_PILE_H_ */
#define likely(x)
#define unlikely(x)
#define __rte_assume(condition)
Definition: rte_common.h:595
#define RTE_MIN(a, b)
Definition: rte_common.h:999
#define __rte_noinline
Definition: rte_common.h:547
#define __rte_always_inline
Definition: rte_common.h:534
static void * rte_memcpy(void *dst, const void *src, size_t n)