Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2017 Intel Corporation
3 : : */
4 : :
5 : : #include <stdbool.h>
6 : : #include <stddef.h>
7 : : #include <stdint.h>
8 : : #include <stdio.h>
9 : :
10 : : #include <rte_string_fns.h>
11 : : #include <rte_branch_prediction.h>
12 : : #include <rte_debug.h>
13 : : #include <rte_lcore.h>
14 : : #include <rte_log.h>
15 : : #include <rte_malloc.h>
16 : : #include <rte_memcpy.h>
17 : : #include <rte_memory.h>
18 : : #include <rte_memzone.h>
19 : : #include <rte_atomic.h>
20 : :
21 : : #include "opdl_ring.h"
22 : : #include "opdl_log.h"
23 : :
24 : : #define LIB_NAME "opdl_ring"
25 : :
26 : : #define OPDL_NAME_SIZE 64
27 : :
28 : :
29 : : #define OPDL_EVENT_MASK (0x00000000000FFFFFULL)
30 : : #define OPDL_FLOWID_MASK (0xFFFFF)
31 : : #define OPDL_OPA_MASK (0xFF)
32 : : #define OPDL_OPA_OFFSET (0x38)
33 : :
34 : : /* Types of dependency between stages */
35 : : enum dep_type {
36 : : DEP_NONE = 0, /* no dependency */
37 : : DEP_DIRECT, /* stage has direct dependency */
38 : : DEP_INDIRECT, /* in-direct dependency through other stage(s) */
39 : : DEP_SELF, /* stage dependency on itself, used to detect loops */
40 : : };
41 : :
42 : : /* Shared section of stage state.
43 : : * Care is needed when accessing and the layout is important, especially to
44 : : * limit the adjacent cache-line HW prefetcher from impacting performance.
45 : : */
46 : : struct shared_state {
47 : : /* Last known minimum sequence number of dependencies, used for multi
48 : : * thread operation
49 : : */
50 : : uint32_t available_seq;
51 : : char _pad1[RTE_CACHE_LINE_SIZE * 3];
52 : : uint32_t head; /* Head sequence number (for multi thread operation) */
53 : : char _pad2[RTE_CACHE_LINE_SIZE * 3];
54 : : struct opdl_stage *stage; /* back pointer */
55 : : uint32_t tail; /* Tail sequence number */
56 : : char _pad3[RTE_CACHE_LINE_SIZE * 2];
57 : : } __rte_cache_aligned;
58 : :
59 : : /* A structure to keep track of "unfinished" claims. This is only used for
60 : : * stages that are threadsafe. Each lcore accesses its own instance of this
61 : : * structure to record the entries it has claimed. This allows one lcore to make
62 : : * multiple claims without being blocked by another. When disclaiming it moves
63 : : * forward the shared tail when the shared tail matches the tail value recorded
64 : : * here.
65 : : */
66 : : struct claim_manager {
67 : : uint32_t num_to_disclaim;
68 : : uint32_t num_claimed;
69 : : uint32_t mgr_head;
70 : : uint32_t mgr_tail;
71 : : struct {
72 : : uint32_t head;
73 : : uint32_t tail;
74 : : } claims[OPDL_DISCLAIMS_PER_LCORE];
75 : : } __rte_cache_aligned;
76 : :
77 : : /* Context for each stage of opdl_ring.
78 : : * Calculations on sequence numbers need to be done with other uint32_t values
79 : : * so that results are modulus 2^32, and not undefined.
80 : : */
81 : : struct opdl_stage {
82 : : struct opdl_ring *t; /* back pointer, set at init */
83 : : uint32_t num_slots; /* Number of slots for entries, set at init */
84 : : uint32_t index; /* ID for this stage, set at init */
85 : : bool threadsafe; /* Set to 1 if this stage supports threadsafe use */
86 : : /* Last known min seq number of dependencies for used for single thread
87 : : * operation
88 : : */
89 : : uint32_t available_seq;
90 : : uint32_t head; /* Current head for single-thread operation */
91 : : uint32_t nb_instance; /* Number of instances */
92 : : uint32_t instance_id; /* ID of this stage instance */
93 : : uint16_t num_claimed; /* Number of slots claimed */
94 : : uint16_t num_event; /* Number of events */
95 : : uint32_t seq; /* sequence number */
96 : : uint32_t num_deps; /* Number of direct dependencies */
97 : : /* Keep track of all dependencies, used during init only */
98 : : enum dep_type *dep_tracking;
99 : : /* Direct dependencies of this stage */
100 : : struct shared_state **deps;
101 : : /* Other stages read this! */
102 : : struct shared_state shared __rte_cache_aligned;
103 : : /* For managing disclaims in multi-threaded processing stages */
104 : : struct claim_manager pending_disclaims[RTE_MAX_LCORE]
105 : : __rte_cache_aligned;
106 : : uint32_t shadow_head; /* Shadow head for single-thread operation */
107 : : uint32_t queue_id; /* ID of Queue which is assigned to this stage */
108 : : uint32_t pos; /* Atomic scan position */
109 : : } __rte_cache_aligned;
110 : :
111 : : /* Context for opdl_ring */
112 : : struct opdl_ring {
113 : : char name[OPDL_NAME_SIZE]; /* OPDL queue instance name */
114 : : int socket; /* NUMA socket that memory is allocated on */
115 : : uint32_t num_slots; /* Number of slots for entries */
116 : : uint32_t mask; /* Mask for sequence numbers (num_slots - 1) */
117 : : uint32_t slot_size; /* Size of each slot in bytes */
118 : : uint32_t num_stages; /* Number of stages that have been added */
119 : : uint32_t max_num_stages; /* Max number of stages */
120 : : /* Stages indexed by ID */
121 : : struct opdl_stage *stages;
122 : : /* Memory for storing slot data */
123 : : uint8_t slots[0] __rte_cache_aligned;
124 : : };
125 : :
126 : :
127 : : /* Return input stage of a opdl_ring */
128 : : static __rte_always_inline struct opdl_stage *
129 : : input_stage(const struct opdl_ring *t)
130 : : {
131 : 0 : return &t->stages[0];
132 : : }
133 : :
134 : : /* Check if a stage is the input stage */
135 : : static __rte_always_inline bool
136 : : is_input_stage(const struct opdl_stage *s)
137 : : {
138 : 0 : return s->index == 0;
139 : : }
140 : :
141 : : /* Get slot pointer from sequence number */
142 : : static __rte_always_inline void *
143 : : get_slot(const struct opdl_ring *t, uint32_t n)
144 : : {
145 : 0 : return (void *)(uintptr_t)&t->slots[(n & t->mask) * t->slot_size];
146 : : }
147 : :
148 : : /* Find how many entries are available for processing */
149 : : static __rte_always_inline uint32_t
150 : : available(const struct opdl_stage *s)
151 : : {
152 [ # # # # : 0 : if (s->threadsafe == true) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
153 : 0 : uint32_t n = __atomic_load_n(&s->shared.available_seq,
154 : : __ATOMIC_ACQUIRE) -
155 : 0 : __atomic_load_n(&s->shared.head,
156 : : __ATOMIC_ACQUIRE);
157 : :
158 : : /* Return 0 if available_seq needs to be updated */
159 [ # # # # : 0 : return (n <= s->num_slots) ? n : 0;
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
160 : : }
161 : :
162 : : /* Single threaded */
163 : 0 : return s->available_seq - s->head;
164 : : }
165 : :
166 : : /* Read sequence number of dependencies and find minimum */
167 : : static __rte_always_inline void
168 : : update_available_seq(struct opdl_stage *s)
169 : : {
170 : : uint32_t i;
171 : 0 : uint32_t this_tail = s->shared.tail;
172 : 0 : uint32_t min_seq = __atomic_load_n(&s->deps[0]->tail, __ATOMIC_ACQUIRE);
173 : : /* Input stage sequence numbers are greater than the sequence numbers of
174 : : * its dependencies so an offset of t->num_slots is needed when
175 : : * calculating available slots and also the condition which is used to
176 : : * determine the dependencies minimum sequence number must be reverted.
177 : : */
178 : : uint32_t wrap;
179 : :
180 [ # # # # : 0 : if (is_input_stage(s)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
181 : 0 : wrap = s->num_slots;
182 [ # # # # : 0 : for (i = 1; i < s->num_deps; i++) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
183 : 0 : uint32_t seq = __atomic_load_n(&s->deps[i]->tail,
184 : : __ATOMIC_ACQUIRE);
185 [ # # # # : 0 : if ((this_tail - seq) > (this_tail - min_seq))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
186 : : min_seq = seq;
187 : : }
188 : : } else {
189 : : wrap = 0;
190 [ # # # # : 0 : for (i = 1; i < s->num_deps; i++) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
191 : 0 : uint32_t seq = __atomic_load_n(&s->deps[i]->tail,
192 : : __ATOMIC_ACQUIRE);
193 [ # # # # : 0 : if ((seq - this_tail) < (min_seq - this_tail))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
194 : : min_seq = seq;
195 : : }
196 : : }
197 : :
198 [ # # # # : 0 : if (s->threadsafe == false)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
199 : 0 : s->available_seq = min_seq + wrap;
200 : : else
201 : 0 : __atomic_store_n(&s->shared.available_seq, min_seq + wrap,
202 : : __ATOMIC_RELEASE);
203 : : }
204 : :
205 : : /* Wait until the number of available slots reaches number requested */
206 : : static __rte_always_inline void
207 : : wait_for_available(struct opdl_stage *s, uint32_t n)
208 : : {
209 [ # # # # : 0 : while (available(s) < n) {
# # # # #
# # # # #
# # # # #
# ]
210 : : rte_pause();
211 : : update_available_seq(s);
212 : : }
213 : : }
214 : :
215 : : /* Return number of slots to process based on number requested and mode */
216 : : static __rte_always_inline uint32_t
217 : : num_to_process(struct opdl_stage *s, uint32_t n, bool block)
218 : : {
219 : : /* Don't read tail sequences of dependencies if not needed */
220 [ # # # # : 0 : if (available(s) >= n)
# # # # #
# # # #
# ]
221 : : return n;
222 : :
223 : : update_available_seq(s);
224 : :
225 [ # # # # : 0 : if (block == false) {
# # # # #
# # # # #
# # # # ]
226 : : uint32_t avail = available(s);
227 : :
228 [ # # # # : 0 : if (avail == 0) {
# # # # #
# # # # #
# # # # ]
229 : : rte_pause();
230 : : return 0;
231 : : }
232 : 0 : return (avail <= n) ? avail : n;
233 : : }
234 : :
235 [ # # # # : 0 : if (unlikely(n > s->num_slots)) {
# # # # #
# # # # #
# # # # ]
236 : 0 : PMD_DRV_LOG(ERR, "%u entries is more than max (%u)",
237 : : n, s->num_slots);
238 : 0 : return 0; /* Avoid infinite loop */
239 : : }
240 : : /* blocking */
241 : : wait_for_available(s, n);
242 : : return n;
243 : : }
244 : :
245 : : /* Copy entries in to slots with wrap-around */
246 : : static __rte_always_inline void
247 : : copy_entries_in(struct opdl_ring *t, uint32_t start, const void *entries,
248 : : uint32_t num_entries)
249 : : {
250 : 0 : uint32_t slot_size = t->slot_size;
251 : 0 : uint32_t slot_index = start & t->mask;
252 : :
253 [ # # # # : 0 : if (slot_index + num_entries <= t->num_slots) {
# # ]
254 : 0 : rte_memcpy(get_slot(t, start), entries,
255 [ # # # # : 0 : num_entries * slot_size);
# # ]
256 : : } else {
257 : 0 : uint32_t split = t->num_slots - slot_index;
258 : :
259 [ # # # # : 0 : rte_memcpy(get_slot(t, start), entries, split * slot_size);
# # ]
260 : 0 : rte_memcpy(get_slot(t, 0),
261 : 0 : RTE_PTR_ADD(entries, split * slot_size),
262 [ # # # # : 0 : (num_entries - split) * slot_size);
# # ]
263 : : }
264 : : }
265 : :
266 : : /* Copy entries out from slots with wrap-around */
267 : : static __rte_always_inline void
268 : : copy_entries_out(struct opdl_ring *t, uint32_t start, void *entries,
269 : : uint32_t num_entries)
270 : : {
271 : 0 : uint32_t slot_size = t->slot_size;
272 : 0 : uint32_t slot_index = start & t->mask;
273 : :
274 [ # # ]: 0 : if (slot_index + num_entries <= t->num_slots) {
275 : 0 : rte_memcpy(entries, get_slot(t, start),
276 [ # # # # : 0 : num_entries * slot_size);
# # ]
277 : : } else {
278 : 0 : uint32_t split = t->num_slots - slot_index;
279 : :
280 [ # # # # : 0 : rte_memcpy(entries, get_slot(t, start), split * slot_size);
# # ]
281 : 0 : rte_memcpy(RTE_PTR_ADD(entries, split * slot_size),
282 : : get_slot(t, 0),
283 [ # # # # : 0 : (num_entries - split) * slot_size);
# # ]
284 : : }
285 : : }
286 : :
287 : : /* Input function optimised for single thread */
288 : : static __rte_always_inline uint32_t
289 : : opdl_ring_input_singlethread(struct opdl_ring *t, const void *entries,
290 : : uint32_t num_entries, bool block)
291 : : {
292 : : struct opdl_stage *s = input_stage(t);
293 [ # # ]: 0 : uint32_t head = s->head;
294 : :
295 : : num_entries = num_to_process(s, num_entries, block);
296 [ # # ]: 0 : if (num_entries == 0)
297 : : return 0;
298 : :
299 : : copy_entries_in(t, head, entries, num_entries);
300 : :
301 : 0 : s->head += num_entries;
302 : 0 : __atomic_store_n(&s->shared.tail, s->head, __ATOMIC_RELEASE);
303 : :
304 : 0 : return num_entries;
305 : : }
306 : :
307 : : /* Convert head and tail of claim_manager into valid index */
308 : : static __rte_always_inline uint32_t
309 : : claim_mgr_index(uint32_t n)
310 : : {
311 : 0 : return n & (OPDL_DISCLAIMS_PER_LCORE - 1);
312 : : }
313 : :
314 : : /* Check if there are available slots in claim_manager */
315 : : static __rte_always_inline bool
316 : : claim_mgr_available(struct claim_manager *mgr)
317 : : {
318 : 0 : return (mgr->mgr_head < (mgr->mgr_tail + OPDL_DISCLAIMS_PER_LCORE)) ?
319 : : true : false;
320 : : }
321 : :
322 : : /* Record a new claim. Only use after first checking an entry is available */
323 : : static __rte_always_inline void
324 : : claim_mgr_add(struct claim_manager *mgr, uint32_t tail, uint32_t head)
325 : : {
326 : 0 : if ((mgr->mgr_head != mgr->mgr_tail) &&
327 [ # # # # ]: 0 : (mgr->claims[claim_mgr_index(mgr->mgr_head - 1)].head ==
328 : : tail)) {
329 : : /* Combine with previous claim */
330 : 0 : mgr->claims[claim_mgr_index(mgr->mgr_head - 1)].head = head;
331 : : } else {
332 : 0 : mgr->claims[claim_mgr_index(mgr->mgr_head)].head = head;
333 : 0 : mgr->claims[claim_mgr_index(mgr->mgr_head)].tail = tail;
334 : 0 : mgr->mgr_head++;
335 : : }
336 : :
337 : 0 : mgr->num_claimed += (head - tail);
338 : 0 : }
339 : :
340 : : /* Read the oldest recorded claim */
341 : : static __rte_always_inline bool
342 : : claim_mgr_read(struct claim_manager *mgr, uint32_t *tail, uint32_t *head)
343 : : {
344 [ # # # # : 0 : if (mgr->mgr_head == mgr->mgr_tail)
# # # # #
# ]
345 : : return false;
346 : :
347 : 0 : *head = mgr->claims[claim_mgr_index(mgr->mgr_tail)].head;
348 : 0 : *tail = mgr->claims[claim_mgr_index(mgr->mgr_tail)].tail;
349 : 0 : return true;
350 : : }
351 : :
352 : : /* Remove the oldest recorded claim. Only use after first reading the entry */
353 : : static __rte_always_inline void
354 : : claim_mgr_remove(struct claim_manager *mgr)
355 : : {
356 : 0 : mgr->num_claimed -= (mgr->claims[claim_mgr_index(mgr->mgr_tail)].head -
357 : 0 : mgr->claims[claim_mgr_index(mgr->mgr_tail)].tail);
358 : 0 : mgr->mgr_tail++;
359 : : }
360 : :
361 : : /* Update tail in the oldest claim. Only use after first reading the entry */
362 : : static __rte_always_inline void
363 : : claim_mgr_move_tail(struct claim_manager *mgr, uint32_t num_entries)
364 : : {
365 : 0 : mgr->num_claimed -= num_entries;
366 : 0 : mgr->claims[claim_mgr_index(mgr->mgr_tail)].tail += num_entries;
367 : : }
368 : :
369 : : static __rte_always_inline void
370 : : opdl_stage_disclaim_multithread_n(struct opdl_stage *s,
371 : : uint32_t num_entries, bool block)
372 : : {
373 : : struct claim_manager *disclaims = &s->pending_disclaims[rte_lcore_id()];
374 : : uint32_t head;
375 : : uint32_t tail;
376 : :
377 [ # # # # : 0 : while (num_entries) {
# # # # #
# ]
378 : : bool ret = claim_mgr_read(disclaims, &tail, &head);
379 : :
380 [ # # # # : 0 : if (ret == false)
# # # # #
# ]
381 : : break; /* nothing is claimed */
382 : : /* There should be no race condition here. If shared.tail
383 : : * matches, no other core can update it until this one does.
384 : : */
385 [ # # # # : 0 : if (__atomic_load_n(&s->shared.tail, __ATOMIC_ACQUIRE) ==
# # # # #
# ]
386 : : tail) {
387 [ # # # # : 0 : if (num_entries >= (head - tail)) {
# # # # #
# ]
388 : : claim_mgr_remove(disclaims);
389 : 0 : __atomic_store_n(&s->shared.tail, head,
390 : : __ATOMIC_RELEASE);
391 : 0 : num_entries -= (head - tail);
392 : : } else {
393 : : claim_mgr_move_tail(disclaims, num_entries);
394 : 0 : __atomic_store_n(&s->shared.tail,
395 : 0 : num_entries + tail,
396 : : __ATOMIC_RELEASE);
397 : : num_entries = 0;
398 : : }
399 [ # # # # ]: 0 : } else if (block == false)
400 : : break; /* blocked by other thread */
401 : : /* Keep going until num_entries are disclaimed. */
402 : : rte_pause();
403 : : }
404 : :
405 : 0 : disclaims->num_to_disclaim = num_entries;
406 : 0 : }
407 : :
408 : : /* Move head atomically, returning number of entries available to process and
409 : : * the original value of head. For non-input stages, the claim is recorded
410 : : * so that the tail can be updated later by opdl_stage_disclaim().
411 : : */
412 : : static __rte_always_inline void
413 : : move_head_atomically(struct opdl_stage *s, uint32_t *num_entries,
414 : : uint32_t *old_head, bool block, bool claim_func)
415 : : {
416 : : uint32_t orig_num_entries = *num_entries;
417 : : uint32_t ret;
418 : : struct claim_manager *disclaims = &s->pending_disclaims[rte_lcore_id()];
419 : :
420 : : /* Attempt to disclaim any outstanding claims */
421 : 0 : opdl_stage_disclaim_multithread_n(s, disclaims->num_to_disclaim,
422 : : false);
423 : :
424 : 0 : *old_head = __atomic_load_n(&s->shared.head, __ATOMIC_ACQUIRE);
425 : : while (true) {
426 : : bool success;
427 : : /* If called by opdl_ring_input(), claim does not need to be
428 : : * recorded, as there will be no disclaim.
429 : : */
430 : : if (claim_func) {
431 : : /* Check that the claim can be recorded */
432 : : ret = claim_mgr_available(disclaims);
433 [ # # # # ]: 0 : if (ret == false) {
434 : : /* exit out if claim can't be recorded */
435 : : *num_entries = 0;
436 : : return;
437 : : }
438 : : }
439 : :
440 : : *num_entries = num_to_process(s, orig_num_entries, block);
441 [ # # # # : 0 : if (*num_entries == 0)
# # ]
442 : : return;
443 : :
444 : 0 : success = __atomic_compare_exchange_n(&s->shared.head, old_head,
445 : 0 : *old_head + *num_entries,
446 : : true, /* may fail spuriously */
447 : : __ATOMIC_RELEASE, /* memory order on success */
448 : : __ATOMIC_ACQUIRE); /* memory order on fail */
449 [ # # # # : 0 : if (likely(success))
# # ]
450 : : break;
451 : : rte_pause();
452 : : }
453 : :
454 : : if (claim_func)
455 : : /* Store the claim record */
456 [ # # # # ]: 0 : claim_mgr_add(disclaims, *old_head, *old_head + *num_entries);
457 : : }
458 : :
459 : : /* Input function that supports multiple threads */
460 : : static __rte_always_inline uint32_t
461 : : opdl_ring_input_multithread(struct opdl_ring *t, const void *entries,
462 : : uint32_t num_entries, bool block)
463 : : {
464 : : struct opdl_stage *s = input_stage(t);
465 : : uint32_t old_head;
466 : :
467 : : move_head_atomically(s, &num_entries, &old_head, block, false);
468 [ # # ]: 0 : if (num_entries == 0)
469 : : return 0;
470 : :
471 : : copy_entries_in(t, old_head, entries, num_entries);
472 : :
473 : : /* If another thread started inputting before this one, but hasn't
474 : : * finished, we need to wait for it to complete to update the tail.
475 : : */
476 : 0 : rte_wait_until_equal_32(&s->shared.tail, old_head, __ATOMIC_ACQUIRE);
477 : :
478 : 0 : __atomic_store_n(&s->shared.tail, old_head + num_entries,
479 : : __ATOMIC_RELEASE);
480 : :
481 : 0 : return num_entries;
482 : : }
483 : :
484 : : static __rte_always_inline uint32_t
485 : : opdl_first_entry_id(uint32_t start_seq, uint8_t nb_p_lcores,
486 : : uint8_t this_lcore)
487 : : {
488 : 0 : return ((nb_p_lcores <= 1) ? 0 :
489 : 0 : (nb_p_lcores - (start_seq % nb_p_lcores) + this_lcore) %
490 : : nb_p_lcores);
491 : : }
492 : :
493 : : /* Claim slots to process, optimised for single-thread operation */
494 : : static __rte_always_inline uint32_t
495 : : opdl_stage_claim_singlethread(struct opdl_stage *s, void *entries,
496 : : uint32_t num_entries, uint32_t *seq, bool block, bool atomic)
497 : : {
498 : : uint32_t i = 0, j = 0, offset;
499 : : uint32_t opa_id = 0;
500 : : uint32_t flow_id = 0;
501 : : uint64_t event = 0;
502 : : void *get_slots;
503 : : struct rte_event *ev;
504 : : RTE_SET_USED(seq);
505 : 0 : struct opdl_ring *t = s->t;
506 : : uint8_t *entries_offset = (uint8_t *)entries;
507 : :
508 [ # # ]: 0 : if (!atomic) {
509 : :
510 : 0 : offset = opdl_first_entry_id(s->seq, s->nb_instance,
511 [ # # ]: 0 : s->instance_id);
512 : :
513 [ # # ]: 0 : num_entries = s->nb_instance * num_entries;
514 : :
515 : : num_entries = num_to_process(s, num_entries, block);
516 : :
517 [ # # ]: 0 : for (; offset < num_entries; offset += s->nb_instance) {
518 : 0 : get_slots = get_slot(t, s->head + offset);
519 : 0 : memcpy(entries_offset, get_slots, t->slot_size);
520 : 0 : entries_offset += t->slot_size;
521 : 0 : i++;
522 : : }
523 : : } else {
524 : : num_entries = num_to_process(s, num_entries, block);
525 : :
526 [ # # ]: 0 : for (j = 0; j < num_entries; j++) {
527 : 0 : ev = (struct rte_event *)get_slot(t, s->head+j);
528 : :
529 : 0 : event = __atomic_load_n(&(ev->event),
530 : : __ATOMIC_ACQUIRE);
531 : :
532 : 0 : opa_id = OPDL_OPA_MASK & (event >> OPDL_OPA_OFFSET);
533 : 0 : flow_id = OPDL_FLOWID_MASK & event;
534 : :
535 [ # # ]: 0 : if (opa_id >= s->queue_id)
536 : 0 : continue;
537 : :
538 [ # # ]: 0 : if ((flow_id % s->nb_instance) == s->instance_id) {
539 : 0 : memcpy(entries_offset, ev, t->slot_size);
540 : 0 : entries_offset += t->slot_size;
541 : 0 : i++;
542 : : }
543 : : }
544 : : }
545 : 0 : s->shadow_head = s->head;
546 : 0 : s->head += num_entries;
547 : 0 : s->num_claimed = num_entries;
548 : 0 : s->num_event = i;
549 : 0 : s->pos = 0;
550 : :
551 : : /* automatically disclaim entries if number of rte_events is zero */
552 [ # # ]: 0 : if (unlikely(i == 0))
553 : 0 : opdl_stage_disclaim(s, 0, false);
554 : :
555 : : return i;
556 : : }
557 : :
558 : : /* Thread-safe version of function to claim slots for processing */
559 : : static __rte_always_inline uint32_t
560 : : opdl_stage_claim_multithread(struct opdl_stage *s, void *entries,
561 : : uint32_t num_entries, uint32_t *seq, bool block)
562 : : {
563 : : uint32_t old_head;
564 : 0 : struct opdl_ring *t = s->t;
565 : : uint32_t i = 0, offset;
566 : : uint8_t *entries_offset = (uint8_t *)entries;
567 : :
568 [ # # ]: 0 : if (seq == NULL) {
569 : 0 : PMD_DRV_LOG(ERR, "Invalid seq PTR");
570 : 0 : return 0;
571 : : }
572 [ # # ]: 0 : offset = opdl_first_entry_id(*seq, s->nb_instance, s->instance_id);
573 : 0 : num_entries = offset + (s->nb_instance * num_entries);
574 : :
575 : : move_head_atomically(s, &num_entries, &old_head, block, true);
576 : :
577 [ # # ]: 0 : for (; offset < num_entries; offset += s->nb_instance) {
578 : 0 : memcpy(entries_offset, get_slot(t, s->head + offset),
579 : 0 : t->slot_size);
580 : 0 : entries_offset += t->slot_size;
581 : 0 : i++;
582 : : }
583 : :
584 : 0 : *seq = old_head;
585 : :
586 : 0 : return i;
587 : : }
588 : :
589 : : /* Claim and copy slot pointers, optimised for single-thread operation */
590 : : static __rte_always_inline uint32_t
591 : : opdl_stage_claim_copy_singlethread(struct opdl_stage *s, void *entries,
592 : : uint32_t num_entries, uint32_t *seq, bool block)
593 : : {
594 : : num_entries = num_to_process(s, num_entries, block);
595 [ # # ]: 0 : if (num_entries == 0)
596 : : return 0;
597 [ # # ]: 0 : copy_entries_out(s->t, s->head, entries, num_entries);
598 [ # # ]: 0 : if (seq != NULL)
599 : 0 : *seq = s->head;
600 : 0 : s->head += num_entries;
601 : 0 : return num_entries;
602 : : }
603 : :
604 : : /* Thread-safe version of function to claim and copy pointers to slots */
605 : : static __rte_always_inline uint32_t
606 : : opdl_stage_claim_copy_multithread(struct opdl_stage *s, void *entries,
607 : : uint32_t num_entries, uint32_t *seq, bool block)
608 : : {
609 : : uint32_t old_head;
610 : :
611 : : move_head_atomically(s, &num_entries, &old_head, block, true);
612 [ # # ]: 0 : if (num_entries == 0)
613 : : return 0;
614 [ # # ]: 0 : copy_entries_out(s->t, old_head, entries, num_entries);
615 [ # # ]: 0 : if (seq != NULL)
616 : 0 : *seq = old_head;
617 : : return num_entries;
618 : : }
619 : :
620 : : static __rte_always_inline void
621 : : opdl_stage_disclaim_singlethread_n(struct opdl_stage *s,
622 : : uint32_t num_entries)
623 : : {
624 : 0 : uint32_t old_tail = s->shared.tail;
625 : :
626 : 0 : if (unlikely(num_entries > (s->head - old_tail))) {
627 : 0 : PMD_DRV_LOG(WARNING, "Attempt to disclaim (%u) more than claimed (%u)",
628 : : num_entries, s->head - old_tail);
629 : 0 : num_entries = s->head - old_tail;
630 : : }
631 : 0 : __atomic_store_n(&s->shared.tail, num_entries + old_tail,
632 : : __ATOMIC_RELEASE);
633 : 0 : }
634 : :
635 : : uint32_t
636 : 0 : opdl_ring_input(struct opdl_ring *t, const void *entries, uint32_t num_entries,
637 : : bool block)
638 : : {
639 [ # # ]: 0 : if (input_stage(t)->threadsafe == false)
640 : 0 : return opdl_ring_input_singlethread(t, entries, num_entries,
641 : : block);
642 : : else
643 : 0 : return opdl_ring_input_multithread(t, entries, num_entries,
644 : : block);
645 : : }
646 : :
647 : : uint32_t
648 : 0 : opdl_ring_copy_from_burst(struct opdl_ring *t, struct opdl_stage *s,
649 : : const void *entries, uint32_t num_entries, bool block)
650 : : {
651 [ # # ]: 0 : uint32_t head = s->head;
652 : :
653 : : num_entries = num_to_process(s, num_entries, block);
654 : :
655 [ # # ]: 0 : if (num_entries == 0)
656 : 0 : return 0;
657 : :
658 : : copy_entries_in(t, head, entries, num_entries);
659 : :
660 : 0 : s->head += num_entries;
661 : 0 : __atomic_store_n(&s->shared.tail, s->head, __ATOMIC_RELEASE);
662 : :
663 : 0 : return num_entries;
664 : :
665 : : }
666 : :
667 : : uint32_t
668 : 0 : opdl_ring_copy_to_burst(struct opdl_ring *t, struct opdl_stage *s,
669 : : void *entries, uint32_t num_entries, bool block)
670 : : {
671 [ # # ]: 0 : uint32_t head = s->head;
672 : :
673 : : num_entries = num_to_process(s, num_entries, block);
674 [ # # ]: 0 : if (num_entries == 0)
675 : 0 : return 0;
676 : :
677 : : copy_entries_out(t, head, entries, num_entries);
678 : :
679 : 0 : s->head += num_entries;
680 : 0 : __atomic_store_n(&s->shared.tail, s->head, __ATOMIC_RELEASE);
681 : :
682 : 0 : return num_entries;
683 : : }
684 : :
685 : : uint32_t
686 [ # # ]: 0 : opdl_stage_find_num_available(struct opdl_stage *s, uint32_t num_entries)
687 : : {
688 : : /* return (num_to_process(s, num_entries, false)); */
689 : :
690 [ # # ]: 0 : if (available(s) >= num_entries)
691 : : return num_entries;
692 : :
693 : : update_available_seq(s);
694 : :
695 : : uint32_t avail = available(s);
696 : :
697 [ # # ]: 0 : if (avail == 0) {
698 : : rte_pause();
699 : 0 : return 0;
700 : : }
701 : 0 : return (avail <= num_entries) ? avail : num_entries;
702 : : }
703 : :
704 : : uint32_t
705 : 0 : opdl_stage_claim(struct opdl_stage *s, void *entries,
706 : : uint32_t num_entries, uint32_t *seq, bool block, bool atomic)
707 : : {
708 [ # # ]: 0 : if (s->threadsafe == false)
709 : 0 : return opdl_stage_claim_singlethread(s, entries, num_entries,
710 : : seq, block, atomic);
711 : : else
712 : 0 : return opdl_stage_claim_multithread(s, entries, num_entries,
713 : : seq, block);
714 : : }
715 : :
716 : : uint32_t
717 : 0 : opdl_stage_claim_copy(struct opdl_stage *s, void *entries,
718 : : uint32_t num_entries, uint32_t *seq, bool block)
719 : : {
720 [ # # ]: 0 : if (s->threadsafe == false)
721 : 0 : return opdl_stage_claim_copy_singlethread(s, entries,
722 : : num_entries, seq, block);
723 : : else
724 : 0 : return opdl_stage_claim_copy_multithread(s, entries,
725 : : num_entries, seq, block);
726 : : }
727 : :
728 : : void
729 : 0 : opdl_stage_disclaim_n(struct opdl_stage *s, uint32_t num_entries,
730 : : bool block)
731 : : {
732 : :
733 [ # # ]: 0 : if (s->threadsafe == false) {
734 [ # # ]: 0 : opdl_stage_disclaim_singlethread_n(s, s->num_claimed);
735 : : } else {
736 : : struct claim_manager *disclaims =
737 : : &s->pending_disclaims[rte_lcore_id()];
738 : :
739 [ # # ]: 0 : if (unlikely(num_entries > s->num_slots)) {
740 : 0 : PMD_DRV_LOG(WARNING, "Attempt to disclaim (%u) more than claimed (%u)",
741 : : num_entries, disclaims->num_claimed);
742 : 0 : num_entries = disclaims->num_claimed;
743 : : }
744 : :
745 : 0 : num_entries = RTE_MIN(num_entries + disclaims->num_to_disclaim,
746 : : disclaims->num_claimed);
747 : : opdl_stage_disclaim_multithread_n(s, num_entries, block);
748 : : }
749 : 0 : }
750 : :
751 : : int
752 : 0 : opdl_stage_disclaim(struct opdl_stage *s, uint32_t num_entries, bool block)
753 : : {
754 [ # # ]: 0 : if (num_entries != s->num_event) {
755 : 0 : rte_errno = EINVAL;
756 : 0 : return 0;
757 : : }
758 [ # # ]: 0 : if (s->threadsafe == false) {
759 : 0 : __atomic_store_n(&s->shared.tail, s->head, __ATOMIC_RELEASE);
760 : 0 : s->seq += s->num_claimed;
761 : 0 : s->shadow_head = s->head;
762 : 0 : s->num_claimed = 0;
763 : : } else {
764 : : struct claim_manager *disclaims =
765 : : &s->pending_disclaims[rte_lcore_id()];
766 : 0 : opdl_stage_disclaim_multithread_n(s, disclaims->num_claimed,
767 : : block);
768 : : }
769 : 0 : return num_entries;
770 : : }
771 : :
772 : : uint32_t
773 : 0 : opdl_ring_available(struct opdl_ring *t)
774 : : {
775 : 0 : return opdl_stage_available(&t->stages[0]);
776 : : }
777 : :
778 : : uint32_t
779 [ # # ]: 0 : opdl_stage_available(struct opdl_stage *s)
780 : : {
781 : : update_available_seq(s);
782 : 0 : return available(s);
783 : : }
784 : :
785 : : void
786 : 0 : opdl_ring_flush(struct opdl_ring *t)
787 : : {
788 : : struct opdl_stage *s = input_stage(t);
789 : :
790 : 0 : wait_for_available(s, s->num_slots);
791 : 0 : }
792 : :
793 : : /******************** Non performance sensitive functions ********************/
794 : :
795 : : /* Initial setup of a new stage's context */
796 : : static int
797 : 0 : init_stage(struct opdl_ring *t, struct opdl_stage *s, bool threadsafe,
798 : : bool is_input)
799 : : {
800 [ # # ]: 0 : uint32_t available = (is_input) ? t->num_slots : 0;
801 : :
802 : 0 : s->t = t;
803 : 0 : s->num_slots = t->num_slots;
804 : 0 : s->index = t->num_stages;
805 : 0 : s->threadsafe = threadsafe;
806 : 0 : s->shared.stage = s;
807 : :
808 : : /* Alloc memory for deps */
809 : 0 : s->dep_tracking = rte_zmalloc_socket(LIB_NAME,
810 : 0 : t->max_num_stages * sizeof(enum dep_type),
811 : : 0, t->socket);
812 [ # # ]: 0 : if (s->dep_tracking == NULL)
813 : : return -ENOMEM;
814 : :
815 : 0 : s->deps = rte_zmalloc_socket(LIB_NAME,
816 : 0 : t->max_num_stages * sizeof(struct shared_state *),
817 : : 0, t->socket);
818 [ # # ]: 0 : if (s->deps == NULL) {
819 : 0 : rte_free(s->dep_tracking);
820 : 0 : return -ENOMEM;
821 : : }
822 : :
823 : 0 : s->dep_tracking[s->index] = DEP_SELF;
824 : :
825 [ # # ]: 0 : if (threadsafe == true)
826 : 0 : s->shared.available_seq = available;
827 : : else
828 : 0 : s->available_seq = available;
829 : :
830 : : return 0;
831 : : }
832 : :
833 : : /* Add direct or indirect dependencies between stages */
834 : : static int
835 : 0 : add_dep(struct opdl_stage *dependent, const struct opdl_stage *dependency,
836 : : enum dep_type type)
837 : : {
838 : 0 : struct opdl_ring *t = dependent->t;
839 : : uint32_t i;
840 : :
841 : : /* Add new direct dependency */
842 [ # # ]: 0 : if ((type == DEP_DIRECT) &&
843 [ # # ]: 0 : (dependent->dep_tracking[dependency->index] ==
844 : : DEP_NONE)) {
845 : 0 : PMD_DRV_LOG(DEBUG, "%s:%u direct dependency on %u",
846 : : t->name, dependent->index, dependency->index);
847 : 0 : dependent->dep_tracking[dependency->index] = DEP_DIRECT;
848 : : }
849 : :
850 : : /* Add new indirect dependency or change direct to indirect */
851 [ # # ]: 0 : if ((type == DEP_INDIRECT) &&
852 [ # # ]: 0 : ((dependent->dep_tracking[dependency->index] ==
853 : : DEP_NONE) ||
854 : : (dependent->dep_tracking[dependency->index] ==
855 : : DEP_DIRECT))) {
856 : 0 : PMD_DRV_LOG(DEBUG, "%s:%u indirect dependency on %u",
857 : : t->name, dependent->index, dependency->index);
858 : 0 : dependent->dep_tracking[dependency->index] = DEP_INDIRECT;
859 : : }
860 : :
861 : : /* Shouldn't happen... */
862 [ # # # # ]: 0 : if ((dependent->dep_tracking[dependency->index] == DEP_SELF) &&
863 : : (dependent != input_stage(t))) {
864 : 0 : PMD_DRV_LOG(ERR, "Loop in dependency graph %s:%u",
865 : : t->name, dependent->index);
866 : 0 : return -EINVAL;
867 : : }
868 : :
869 : : /* Keep going to dependencies of the dependency, until input stage */
870 [ # # ]: 0 : if (dependency != input_stage(t))
871 [ # # ]: 0 : for (i = 0; i < dependency->num_deps; i++) {
872 : 0 : int ret = add_dep(dependent, dependency->deps[i]->stage,
873 : : DEP_INDIRECT);
874 : :
875 [ # # ]: 0 : if (ret < 0)
876 : 0 : return ret;
877 : : }
878 : :
879 : : /* Make list of sequence numbers for direct dependencies only */
880 [ # # ]: 0 : if (type == DEP_DIRECT)
881 [ # # ]: 0 : for (i = 0, dependent->num_deps = 0; i < t->num_stages; i++)
882 [ # # ]: 0 : if (dependent->dep_tracking[i] == DEP_DIRECT) {
883 [ # # # # ]: 0 : if ((i == 0) && (dependent->num_deps > 1))
884 : 0 : rte_panic("%s:%u depends on > input",
885 : : t->name,
886 : : dependent->index);
887 : 0 : dependent->deps[dependent->num_deps++] =
888 : 0 : &t->stages[i].shared;
889 : : }
890 : :
891 : : return 0;
892 : : }
893 : :
894 : : struct opdl_ring *
895 : 0 : opdl_ring_create(const char *name, uint32_t num_slots, uint32_t slot_size,
896 : : uint32_t max_num_stages, int socket)
897 : : {
898 : : struct opdl_ring *t;
899 : : char mz_name[RTE_MEMZONE_NAMESIZE];
900 : : int mz_flags = 0;
901 : : struct opdl_stage *st = NULL;
902 : : const struct rte_memzone *mz = NULL;
903 : 0 : size_t alloc_size = RTE_CACHE_LINE_ROUNDUP(sizeof(*t) +
904 : : (num_slots * slot_size));
905 : :
906 : : /* Compile time checking */
907 : : RTE_BUILD_BUG_ON((sizeof(struct shared_state) & RTE_CACHE_LINE_MASK) !=
908 : : 0);
909 : : RTE_BUILD_BUG_ON((offsetof(struct opdl_stage, shared) &
910 : : RTE_CACHE_LINE_MASK) != 0);
911 : : RTE_BUILD_BUG_ON((offsetof(struct opdl_ring, slots) &
912 : : RTE_CACHE_LINE_MASK) != 0);
913 : : RTE_BUILD_BUG_ON(!rte_is_power_of_2(OPDL_DISCLAIMS_PER_LCORE));
914 : :
915 : : /* Parameter checking */
916 [ # # ]: 0 : if (name == NULL) {
917 : 0 : PMD_DRV_LOG(ERR, "name param is NULL");
918 : 0 : return NULL;
919 : : }
920 : : if (!rte_is_power_of_2(num_slots)) {
921 : 0 : PMD_DRV_LOG(ERR, "num_slots (%u) for %s is not power of 2",
922 : : num_slots, name);
923 : 0 : return NULL;
924 : : }
925 : :
926 : : /* Alloc memory for stages */
927 : 0 : st = rte_zmalloc_socket(LIB_NAME,
928 : : max_num_stages * sizeof(struct opdl_stage),
929 : : RTE_CACHE_LINE_SIZE, socket);
930 [ # # ]: 0 : if (st == NULL)
931 : 0 : goto exit_fail;
932 : :
933 : : snprintf(mz_name, sizeof(mz_name), "%s%s", LIB_NAME, name);
934 : :
935 : : /* Alloc memory for memzone */
936 : 0 : mz = rte_memzone_reserve(mz_name, alloc_size, socket, mz_flags);
937 [ # # ]: 0 : if (mz == NULL)
938 : 0 : goto exit_fail;
939 : :
940 : 0 : t = mz->addr;
941 : :
942 : : /* Initialise opdl_ring queue */
943 : : memset(t, 0, sizeof(*t));
944 : 0 : strlcpy(t->name, name, sizeof(t->name));
945 : 0 : t->socket = socket;
946 : 0 : t->num_slots = num_slots;
947 : 0 : t->mask = num_slots - 1;
948 : 0 : t->slot_size = slot_size;
949 : 0 : t->max_num_stages = max_num_stages;
950 : 0 : t->stages = st;
951 : :
952 : 0 : PMD_DRV_LOG(DEBUG, "Created %s at %p (num_slots=%u,socket=%i,slot_size=%u)",
953 : : t->name, t, num_slots, socket, slot_size);
954 : :
955 : 0 : return t;
956 : :
957 : 0 : exit_fail:
958 : 0 : PMD_DRV_LOG(ERR, "Cannot reserve memory");
959 : 0 : rte_free(st);
960 : 0 : rte_memzone_free(mz);
961 : :
962 : 0 : return NULL;
963 : : }
964 : :
965 : : void *
966 : 0 : opdl_ring_get_slot(const struct opdl_ring *t, uint32_t index)
967 : : {
968 : 0 : return get_slot(t, index);
969 : : }
970 : :
971 : : bool
972 : 0 : opdl_ring_cas_slot(struct opdl_stage *s, const struct rte_event *ev,
973 : : uint32_t index, bool atomic)
974 : : {
975 : : uint32_t i = 0, offset;
976 : 0 : struct opdl_ring *t = s->t;
977 : : struct rte_event *ev_orig = NULL;
978 : : bool ev_updated = false;
979 : : uint64_t ev_temp = 0;
980 : : uint64_t ev_update = 0;
981 : :
982 : : uint32_t opa_id = 0;
983 : : uint32_t flow_id = 0;
984 : : uint64_t event = 0;
985 : :
986 [ # # ]: 0 : if (index > s->num_event) {
987 : 0 : PMD_DRV_LOG(ERR, "index is overflow");
988 : 0 : return ev_updated;
989 : : }
990 : :
991 : 0 : ev_temp = ev->event & OPDL_EVENT_MASK;
992 : :
993 [ # # ]: 0 : if (!atomic) {
994 : 0 : offset = opdl_first_entry_id(s->seq, s->nb_instance,
995 [ # # ]: 0 : s->instance_id);
996 : 0 : offset += index*s->nb_instance;
997 : 0 : ev_orig = get_slot(t, s->shadow_head+offset);
998 [ # # ]: 0 : if ((ev_orig->event&OPDL_EVENT_MASK) != ev_temp) {
999 : 0 : ev_orig->event = ev->event;
1000 : : ev_updated = true;
1001 : : }
1002 [ # # ]: 0 : if (ev_orig->u64 != ev->u64) {
1003 : 0 : ev_orig->u64 = ev->u64;
1004 : : ev_updated = true;
1005 : : }
1006 : :
1007 : : } else {
1008 [ # # ]: 0 : for (i = s->pos; i < s->num_claimed; i++) {
1009 : : ev_orig = (struct rte_event *)
1010 : 0 : get_slot(t, s->shadow_head+i);
1011 : :
1012 : 0 : event = __atomic_load_n(&(ev_orig->event),
1013 : : __ATOMIC_ACQUIRE);
1014 : :
1015 : 0 : opa_id = OPDL_OPA_MASK & (event >> OPDL_OPA_OFFSET);
1016 : 0 : flow_id = OPDL_FLOWID_MASK & event;
1017 : :
1018 [ # # ]: 0 : if (opa_id >= s->queue_id)
1019 : 0 : continue;
1020 : :
1021 [ # # ]: 0 : if ((flow_id % s->nb_instance) == s->instance_id) {
1022 : 0 : ev_update = s->queue_id;
1023 : 0 : ev_update = (ev_update << OPDL_OPA_OFFSET)
1024 : 0 : | ev->event;
1025 : :
1026 : 0 : s->pos = i + 1;
1027 : :
1028 [ # # ]: 0 : if ((event & OPDL_EVENT_MASK) !=
1029 : : ev_temp) {
1030 : 0 : __atomic_store_n(&(ev_orig->event),
1031 : : ev_update,
1032 : : __ATOMIC_RELEASE);
1033 : : ev_updated = true;
1034 : : }
1035 [ # # ]: 0 : if (ev_orig->u64 != ev->u64) {
1036 : 0 : ev_orig->u64 = ev->u64;
1037 : : ev_updated = true;
1038 : : }
1039 : :
1040 : : break;
1041 : : }
1042 : : }
1043 : :
1044 : : }
1045 : :
1046 : : return ev_updated;
1047 : : }
1048 : :
1049 : : int
1050 : 0 : opdl_ring_get_socket(const struct opdl_ring *t)
1051 : : {
1052 : 0 : return t->socket;
1053 : : }
1054 : :
1055 : : uint32_t
1056 : 0 : opdl_ring_get_num_slots(const struct opdl_ring *t)
1057 : : {
1058 : 0 : return t->num_slots;
1059 : : }
1060 : :
1061 : : const char *
1062 : 0 : opdl_ring_get_name(const struct opdl_ring *t)
1063 : : {
1064 : 0 : return t->name;
1065 : : }
1066 : :
1067 : : /* Check dependency list is valid for a given opdl_ring */
1068 : : static int
1069 : 0 : check_deps(struct opdl_ring *t, struct opdl_stage *deps[],
1070 : : uint32_t num_deps)
1071 : : {
1072 : : unsigned int i;
1073 : :
1074 [ # # ]: 0 : for (i = 0; i < num_deps; ++i) {
1075 [ # # ]: 0 : if (!deps[i]) {
1076 : 0 : PMD_DRV_LOG(ERR, "deps[%u] is NULL", i);
1077 : 0 : return -EINVAL;
1078 : : }
1079 [ # # ]: 0 : if (t != deps[i]->t) {
1080 : 0 : PMD_DRV_LOG(ERR, "deps[%u] is in opdl_ring %s, not %s",
1081 : : i, deps[i]->t->name, t->name);
1082 : 0 : return -EINVAL;
1083 : : }
1084 : : }
1085 : :
1086 : : return 0;
1087 : : }
1088 : :
1089 : : struct opdl_stage *
1090 : 0 : opdl_stage_add(struct opdl_ring *t, bool threadsafe, bool is_input)
1091 : : {
1092 : : struct opdl_stage *s;
1093 : :
1094 : : /* Parameter checking */
1095 [ # # ]: 0 : if (!t) {
1096 : 0 : PMD_DRV_LOG(ERR, "opdl_ring is NULL");
1097 : 0 : return NULL;
1098 : : }
1099 [ # # ]: 0 : if (t->num_stages == t->max_num_stages) {
1100 : 0 : PMD_DRV_LOG(ERR, "%s has max number of stages (%u)",
1101 : : t->name, t->max_num_stages);
1102 : 0 : return NULL;
1103 : : }
1104 : :
1105 : 0 : s = &t->stages[t->num_stages];
1106 : :
1107 [ # # ]: 0 : if (((uintptr_t)&s->shared & RTE_CACHE_LINE_MASK) != 0)
1108 : 0 : PMD_DRV_LOG(WARNING, "Tail seq num (%p) of %s stage not cache aligned",
1109 : : &s->shared, t->name);
1110 : :
1111 [ # # ]: 0 : if (init_stage(t, s, threadsafe, is_input) < 0) {
1112 : 0 : PMD_DRV_LOG(ERR, "Cannot reserve memory");
1113 : 0 : return NULL;
1114 : : }
1115 : 0 : t->num_stages++;
1116 : :
1117 : 0 : return s;
1118 : : }
1119 : :
1120 : : uint32_t
1121 : 0 : opdl_stage_deps_add(struct opdl_ring *t, struct opdl_stage *s,
1122 : : uint32_t nb_instance, uint32_t instance_id,
1123 : : struct opdl_stage *deps[],
1124 : : uint32_t num_deps)
1125 : : {
1126 : : uint32_t i;
1127 : : int ret = 0;
1128 : :
1129 [ # # ]: 0 : if ((num_deps > 0) && (!deps)) {
1130 : 0 : PMD_DRV_LOG(ERR, "%s stage has NULL dependencies", t->name);
1131 : 0 : return -1;
1132 : : }
1133 : 0 : ret = check_deps(t, deps, num_deps);
1134 [ # # ]: 0 : if (ret < 0)
1135 : 0 : return ret;
1136 : :
1137 [ # # ]: 0 : for (i = 0; i < num_deps; i++) {
1138 : 0 : ret = add_dep(s, deps[i], DEP_DIRECT);
1139 [ # # ]: 0 : if (ret < 0)
1140 : 0 : return ret;
1141 : : }
1142 : :
1143 : 0 : s->nb_instance = nb_instance;
1144 : 0 : s->instance_id = instance_id;
1145 : :
1146 : 0 : return ret;
1147 : : }
1148 : :
1149 : : struct opdl_stage *
1150 : 0 : opdl_ring_get_input_stage(const struct opdl_ring *t)
1151 : : {
1152 : 0 : return input_stage(t);
1153 : : }
1154 : :
1155 : : int
1156 : 0 : opdl_stage_set_deps(struct opdl_stage *s, struct opdl_stage *deps[],
1157 : : uint32_t num_deps)
1158 : : {
1159 : : unsigned int i;
1160 : : int ret;
1161 : :
1162 [ # # ]: 0 : if ((num_deps == 0) || (!deps)) {
1163 : 0 : PMD_DRV_LOG(ERR, "cannot set NULL dependencies");
1164 : 0 : return -EINVAL;
1165 : : }
1166 : :
1167 : 0 : ret = check_deps(s->t, deps, num_deps);
1168 [ # # ]: 0 : if (ret < 0)
1169 : : return ret;
1170 : :
1171 : : /* Update deps */
1172 [ # # ]: 0 : for (i = 0; i < num_deps; i++)
1173 : 0 : s->deps[i] = &deps[i]->shared;
1174 : 0 : s->num_deps = num_deps;
1175 : :
1176 : 0 : return 0;
1177 : : }
1178 : :
1179 : : struct opdl_ring *
1180 : 0 : opdl_stage_get_opdl_ring(const struct opdl_stage *s)
1181 : : {
1182 : 0 : return s->t;
1183 : : }
1184 : :
1185 : : void
1186 : 0 : opdl_stage_set_queue_id(struct opdl_stage *s,
1187 : : uint32_t queue_id)
1188 : : {
1189 : 0 : s->queue_id = queue_id;
1190 : 0 : }
1191 : :
1192 : : void
1193 : 0 : opdl_ring_dump(const struct opdl_ring *t, FILE *f)
1194 : : {
1195 : : uint32_t i;
1196 : :
1197 [ # # ]: 0 : if (t == NULL) {
1198 : : fprintf(f, "NULL OPDL!\n");
1199 : 0 : return;
1200 : : }
1201 : : fprintf(f, "OPDL \"%s\": num_slots=%u; mask=%#x; slot_size=%u; num_stages=%u; socket=%i\n",
1202 : 0 : t->name, t->num_slots, t->mask, t->slot_size,
1203 : 0 : t->num_stages, t->socket);
1204 [ # # ]: 0 : for (i = 0; i < t->num_stages; i++) {
1205 : : uint32_t j;
1206 : 0 : const struct opdl_stage *s = &t->stages[i];
1207 : :
1208 [ # # # # ]: 0 : fprintf(f, " %s[%u]: threadsafe=%s; head=%u; available_seq=%u; tail=%u; deps=%u",
1209 : : t->name, i, (s->threadsafe) ? "true" : "false",
1210 : : (s->threadsafe) ? s->shared.head : s->head,
1211 : 0 : (s->threadsafe) ? s->shared.available_seq :
1212 : : s->available_seq,
1213 [ # # # # ]: 0 : s->shared.tail, (s->num_deps > 0) ?
1214 : 0 : s->deps[0]->stage->index : 0);
1215 [ # # ]: 0 : for (j = 1; j < s->num_deps; j++)
1216 : 0 : fprintf(f, ",%u", s->deps[j]->stage->index);
1217 : : fprintf(f, "\n");
1218 : : }
1219 : 0 : fflush(f);
1220 : : }
1221 : :
1222 : : void
1223 : 0 : opdl_ring_free(struct opdl_ring *t)
1224 : : {
1225 : : uint32_t i;
1226 : : const struct rte_memzone *mz;
1227 : : char mz_name[RTE_MEMZONE_NAMESIZE];
1228 : :
1229 [ # # ]: 0 : if (t == NULL) {
1230 : 0 : PMD_DRV_LOG(DEBUG, "Freeing NULL OPDL Ring!");
1231 : 0 : return;
1232 : : }
1233 : :
1234 : 0 : PMD_DRV_LOG(DEBUG, "Freeing %s opdl_ring at %p", t->name, t);
1235 : :
1236 [ # # ]: 0 : for (i = 0; i < t->num_stages; ++i) {
1237 : 0 : rte_free(t->stages[i].deps);
1238 : 0 : rte_free(t->stages[i].dep_tracking);
1239 : : }
1240 : :
1241 : 0 : rte_free(t->stages);
1242 : :
1243 : : snprintf(mz_name, sizeof(mz_name), "%s%s", LIB_NAME, t->name);
1244 : 0 : mz = rte_memzone_lookup(mz_name);
1245 [ # # ]: 0 : if (rte_memzone_free(mz) != 0)
1246 : 0 : PMD_DRV_LOG(ERR, "Cannot free memzone for %s", t->name);
1247 : : }
1248 : :
1249 : : /* search a opdl_ring from its name */
1250 : : struct opdl_ring *
1251 : 0 : opdl_ring_lookup(const char *name)
1252 : : {
1253 : : const struct rte_memzone *mz;
1254 : : char mz_name[RTE_MEMZONE_NAMESIZE];
1255 : :
1256 : : snprintf(mz_name, sizeof(mz_name), "%s%s", LIB_NAME, name);
1257 : :
1258 : 0 : mz = rte_memzone_lookup(mz_name);
1259 [ # # ]: 0 : if (mz == NULL)
1260 : : return NULL;
1261 : :
1262 : 0 : return mz->addr;
1263 : : }
1264 : :
1265 : : void
1266 : 0 : opdl_ring_set_stage_threadsafe(struct opdl_stage *s, bool threadsafe)
1267 : : {
1268 : 0 : s->threadsafe = threadsafe;
1269 : 0 : }
|