EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_queue.c
1/*
2 * Copyright (c) 2015-2026, Nokia Solutions and Networks
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef _GNU_SOURCE
32#define _GNU_SOURCE
33#endif
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include <stdbool.h>
40#include <stddef.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include <odp_api.h>
47
48#include <event_machine.h>
50
51#include "em_atomic_group.h"
53#include "em_chaining.h"
54#include "em_eo.h"
55#include "em_eo_types.h"
56#include "em_error.h"
57#include "em_event.h"
58#include "em_event_inline.h"
59#include "em_event_state.h"
60#include "em_event_types.h"
61#include "em_internal_event.h"
62#include "em_libconfig.h"
63#include "em_mem.h"
64#include "em_queue.h"
65#include "em_queue_group.h"
67#include "em_queue_inline.h"
68#include "em_queue_types.h"
69#include "misc/list.h"
70#include "misc/objpool.h"
71
72#define EM_Q_BASENAME "EM_Q_"
73
74static int queue_init_prio_map(int minp, int maxp, int nump);
75static void queue_init_prio_legacy(int minp, int maxp);
76static void queue_init_prio_adaptive(int minp, int maxp, int nump);
77static int queue_init_prio_custom(int minp, int maxp);
78
79static inline int
80queue_create_check_sched(const em_queue_param_t *param, const char **err_str);
81
82static int queue_setup(const char *name, const em_queue_param_t *param,
83 queue_elem_t *q_elem /*in/out*/,
84 const char **err_str /*out*/);
85static void queue_setup_odp_common(const em_queue_param_t *param,
86 odp_queue_param_t *odp_queue_param /*out*/);
87static int queue_setup_scheduled(const em_queue_param_t *param,
88 queue_elem_t *q_elem /*out*/,
89 const char **err_str /*out*/);
90static int queue_setup_unscheduled(const em_queue_param_t *param,
91 queue_elem_t *q_elem /*out*/,
92 const char **err_str /*out*/);
93static int queue_setup_local(const em_queue_param_t *param,
94 queue_elem_t *q_elem /*out*/,
95 const char **err_str /*out*/);
96static int queue_setup_output(const em_queue_param_t *param,
97 queue_elem_t *q_elem /*out*/,
98 const char **err_str /*out*/);
99
100static inline queue_elem_t *
101queue_poolelem2queue(objpool_elem_t *const queue_pool_elem)
102{
103 return (queue_elem_t *)((uintptr_t)queue_pool_elem -
104 offsetof(queue_elem_t, queue_pool_elem));
105}
106
107/*
108 * Helper for read_config_file(): read and validate queue.num_aggr.
109 *
110 * The option is only applicable when vector.backend == EVENT; with the packet
111 * backend num_aggr is forced to 0, and any other backend value is an error.
112 * Assumes opt.vector.backend has already been parsed.
113 */
114static int read_num_aggr_config(void)
115{
116 const unsigned int max_config_queues = UINT16_MAX - MAX_INTERNAL_QUEUES;
117 const char *conf_str;
118 int val;
119 int ret;
120
121 if (em_shm->opt.vector.backend == EM_VECTOR_BACKEND_EVENT) {
122 conf_str = "queue.num_aggr";
123 ret = em_libconfig_lookup_int(&em_shm->libconfig, conf_str, &val);
124 if (unlikely(!ret)) {
125 EM_LOG(EM_LOG_ERR, "Config option '%s' not found.\n", conf_str);
126 return -1;
127 }
128 if (val < 0 || (unsigned int)val > max_config_queues) {
129 EM_LOG(EM_LOG_ERR, "Bad config value '%s = %d', value must be\n"
130 ">= 0 and <= %u\n", conf_str, val, max_config_queues);
131 return -1;
132 }
133 em_shm->opt.queue.num_aggr = (unsigned int)val;
134 EM_PRINT(" %s: %d\n", conf_str, val);
135 } else if (em_shm->opt.vector.backend == EM_VECTOR_BACKEND_PACKET) {
136 /* Aggregated queues are not supported with packet vectors */
137 em_shm->opt.queue.num_aggr = 0;
138 } else {
139 EM_LOG(EM_LOG_ERR, "Invalid vector backend %d\n", em_shm->opt.vector.backend);
140 return -1;
141 }
142
143 return 0;
144}
145
146static int read_config_file(void)
147{
148 const char *conf_str;
149 int val = 0;
150 int ret;
151 unsigned int max_queues;
152 const unsigned int max_config_queues = UINT16_MAX - MAX_INTERNAL_QUEUES;
153
154 EM_PRINT("EM queue config:\n");
155
156 /*
157 * Option: queue.num_static
158 */
159 conf_str = "queue.num_static";
160 ret = em_libconfig_lookup_int(&em_shm->libconfig, conf_str, &val);
161 if (unlikely(!ret)) {
162 EM_LOG(EM_LOG_ERR, "Config option '%s' not found.\n", conf_str);
163 return -1;
164 }
165
166 if (val < 0 || (unsigned int)val > max_config_queues) {
167 EM_LOG(EM_LOG_ERR, "Bad config value '%s = %d', value must be\n"
168 ">= 0 and <= %u\n", conf_str, val, max_config_queues);
169 return -1;
170 }
171 em_shm->opt.queue.num_static = (unsigned int)val;
172 EM_PRINT(" %s: %d\n", conf_str, val);
173
174 /*
175 * Option: queue.num_dynamic
176 */
177 conf_str = "queue.num_dynamic";
178 ret = em_libconfig_lookup_int(&em_shm->libconfig, conf_str, &val);
179 if (unlikely(!ret)) {
180 EM_LOG(EM_LOG_ERR, "Config option '%s' not found.\n", conf_str);
181 return -1;
182 }
183
184 if (val < 0 || (unsigned int)val > max_config_queues) {
185 EM_LOG(EM_LOG_ERR, "Bad config value '%s = %d', value must be\n"
186 ">= 0 and <= %u\n", conf_str, val, max_config_queues);
187 return -1;
188 }
189 em_shm->opt.queue.num_dynamic = (unsigned int)val;
190 EM_PRINT(" %s: %d\n", conf_str, val);
191
192 if (em_shm->opt.queue.num_static == 0 && em_shm->opt.queue.num_dynamic == 0) {
193 EM_LOG(EM_LOG_ERR, "At least one of num_static or num_dynamic must be > 0\n");
194 return -1;
195 }
196
197 /*
198 * Option: queue.num_aggr
199 * Only applicable when 'vector.backend = event', otherwise interpreted
200 * as '0' (no aggregator queues).
201 * Assumes the option 'vector.backend' has been parsed before this option.
202 */
203 if (read_num_aggr_config() != 0)
204 return -1;
205
206 max_queues = em_shm->opt.queue.num_static +
207 em_shm->opt.queue.num_dynamic +
208 em_shm->opt.queue.num_aggr + MAX_INTERNAL_QUEUES;
209
210 if (max_queues > UINT16_MAX) {
211 EM_LOG(EM_LOG_ERR,
212 "Bad config value: num_static(%u) + num_dynamic(%u) +\n"
213 "num_aggr(%u) + MAX_INTERNAL_QUEUES(%u) = %u > UINT16_MAX(%u)\n",
214 em_shm->opt.queue.num_static, em_shm->opt.queue.num_dynamic,
215 em_shm->opt.queue.num_aggr, MAX_INTERNAL_QUEUES, max_queues, UINT16_MAX);
216 return -1;
217 }
218
219 if (max_queues > em_shm->queue_tbl.odp_queue_capability.max_queues) {
220 EM_LOG(EM_LOG_ERR,
221 "Bad config values: num_static(%u) + num_dynamic(%u) +\n"
222 "num_aggr(%u) + MAX_INTERNAL_QUEUES(%u) = %u > odp-max-queues:%u\n",
223 em_shm->opt.queue.num_static, em_shm->opt.queue.num_dynamic,
224 em_shm->opt.queue.num_aggr, MAX_INTERNAL_QUEUES, max_queues,
225 em_shm->queue_tbl.odp_queue_capability.max_queues);
226 return -1;
227 }
228
229 /* store the maximum number of EM queues that can be created */
230 em_shm->queue_tbl.max_queue_num = (uint16_t)max_queues;
231
232 /*
233 * Option: queue.min_events_default
234 */
235 conf_str = "queue.min_events_default";
236 ret = em_libconfig_lookup_int(&em_shm->libconfig, conf_str, &val);
237 if (unlikely(!ret)) {
238 EM_LOG(EM_LOG_ERR, "Config option '%s' not found.\n", conf_str);
239 return -1;
240 }
241 if (val < 0) {
242 EM_LOG(EM_LOG_ERR, "Bad config value '%s = %d'\n",
243 conf_str, val);
244 return -1;
245 }
246 /* store & print the value */
247 em_shm->opt.queue.min_events_default = val;
248 EM_PRINT(" %s: %d\n", conf_str, val);
249
250 /*
251 * Option: queue.prio_map_mode
252 */
253 conf_str = "queue.priority.map_mode";
254 ret = em_libconfig_lookup_int(&em_shm->libconfig, conf_str, &val);
255 if (unlikely(!ret)) {
256 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
257 return -1;
258 }
259 if (val < 0 || val > 2) {
260 EM_LOG(EM_LOG_ERR, "Bad config value '%s = %d'\n", conf_str, val);
261 return -1;
262 }
263 em_shm->opt.queue.priority.map_mode = val;
264 EM_PRINT(" %s: %d\n", conf_str, val);
265
266 if (val == 2) { /* custom map */
267 conf_str = "queue.priority.custom_map";
268 ret = em_libconfig_lookup_array(&em_shm->libconfig, conf_str,
269 em_shm->opt.queue.priority.custom_map,
271 if (unlikely(!ret)) {
272 EM_LOG(EM_LOG_ERR, "Config option '%s' not found or invalid\n", conf_str);
273 return -1;
274 }
275 EM_PRINT(" %s: [", conf_str);
276 for (int i = 0; i < EM_QUEUE_PRIO_NUM; i++) {
277 EM_PRINT("%d", em_shm->opt.queue.priority.custom_map[i]);
278 if (i < (EM_QUEUE_PRIO_NUM - 1))
279 EM_PRINT(",");
280 }
281 EM_PRINT("]\n");
282 }
283 return 0;
284}
285
286/**
287 * Helper: initialize a queue pool (set as empty)
288 */
289static int queue_pool_init(queue_pool_t *const queue_pool)
290{
291 const uint32_t num_subpools = MIN(4, OBJSUBPOOLS_MAX);
292
293 if (objpool_init(&queue_pool->objpool, num_subpools) != 0)
294 return -1;
295
296 return 0;
297}
298
299/**
300 * Helper: populate a queue pool with queue elements after init.
301 */
302static int queue_pool_populate(queue_tbl_t *const queue_tbl,
303 queue_pool_t *const queue_pool,
304 int min_qidx, int max_qidx)
305{
306 const uint32_t num_subpools = objpool_subpools(&queue_pool->objpool);
307 const int qs_per_pool = (max_qidx - min_qidx + 1);
308 int qs_per_subpool = qs_per_pool / num_subpools;
309 int qs_leftover = qs_per_pool % num_subpools;
310 uint32_t subpool_idx = 0;
311 int add_cnt = 0;
312
313 for (int i = min_qidx; i <= max_qidx; i++) {
314 objpool_add(&queue_pool->objpool, subpool_idx,
315 &queue_tbl->queue_elem[i].queue_pool_elem);
316 add_cnt++;
317 if (add_cnt == qs_per_subpool + qs_leftover) {
318 subpool_idx++; /* add to next subpool */
319 qs_leftover = 0; /* added leftovers to subpool 0 */
320 add_cnt = 0;
321 }
322 }
323
324 return 0;
325}
326
327/**
328 * Initialize the EM queues
329 */
330em_status_t queue_init(queue_tbl_t *const queue_tbl,
331 queue_pool_t *const queue_pool,
332 queue_pool_t *const queue_pool_static,
333 queue_pool_t *const queue_pool_aggr)
334{
335 odp_queue_capability_t *const odp_queue_capa =
336 &queue_tbl->odp_queue_capability;
337 odp_schedule_capability_t *const odp_sched_capa =
338 &queue_tbl->odp_schedule_capability;
339 int min;
340 int max;
341 int ret;
342
343 memset(queue_tbl, 0, sizeof(queue_tbl_t));
344 memset(queue_pool, 0, sizeof(queue_pool_t));
345 memset(queue_pool_static, 0, sizeof(queue_pool_t));
346 memset(queue_pool_aggr, 0, sizeof(queue_pool_t));
347 odp_atomic_init_u32(&em_shm->queue_count, 0);
348 odp_atomic_init_u32(&queue_tbl->output_queue_count, 0);
349
350 /* init the queue-pools as empty */
351 if (queue_pool_init(queue_pool) ||
352 queue_pool_init(queue_pool_static) ||
353 queue_pool_init(queue_pool_aggr))
354 return EM_ERR_LIB_FAILED;
355
356 /* Retrieve and store the ODP queue capabilities into 'queue_tbl' */
357 ret = odp_queue_capability(odp_queue_capa);
358 RETURN_ERROR_IF(ret != 0, EM_ERR_LIB_FAILED, EM_ESCOPE_INIT,
359 "odp_queue_capability():%d failed", ret);
360
361 /* Retrieve and store the ODP schedule capabilities into 'queue_tbl' */
362 ret = odp_schedule_capability(odp_sched_capa);
363 RETURN_ERROR_IF(ret != 0, EM_ERR_LIB_FAILED, EM_ESCOPE_INIT,
364 "odp_schedule_capability():%d failed", ret);
365
366 if (read_config_file())
367 return EM_ERR_LIB_FAILED;
368
369 const unsigned int max_queues = em_shm->queue_tbl.max_queue_num;
370
371 if (em_shm->opt.queue.num_static) {
372 queue_tbl->first_static_queue_id = queue_idx2id(0);
373 queue_tbl->last_static_queue_id = queue_tbl->first_static_queue_id +
374 (uint16_t)em_shm->opt.queue.num_static - 1;
375 queue_tbl->first_internal_queue_id = queue_tbl->last_static_queue_id + 1;
376 } else {
377 queue_tbl->first_static_queue_id = INVALID_QUEUE_ID;
378 queue_tbl->last_static_queue_id = INVALID_QUEUE_ID;
379 queue_tbl->first_internal_queue_id = queue_idx2id(0);
380 }
381
382 queue_tbl->last_internal_queue_id = queue_tbl->first_internal_queue_id +
383 MAX_INTERNAL_QUEUES - 1;
384 queue_tbl->shared_internal_queue_id = queue_tbl->last_internal_queue_id;
385
386 if (em_shm->opt.queue.num_dynamic) {
387 queue_tbl->first_dyn_queue_id = queue_tbl->last_internal_queue_id + 1;
388 queue_tbl->last_dyn_queue_id = queue_tbl->first_dyn_queue_id +
389 (uint16_t)em_shm->opt.queue.num_dynamic - 1;
390 } else {
391 queue_tbl->first_dyn_queue_id = INVALID_QUEUE_ID;
392 queue_tbl->last_dyn_queue_id = INVALID_QUEUE_ID;
393 }
394
395 if (em_shm->opt.queue.num_aggr) {
396 if (em_shm->opt.queue.num_dynamic) {
397 queue_tbl->first_aggr_queue_id = queue_tbl->last_dyn_queue_id + 1;
398 queue_tbl->last_aggr_queue_id = queue_tbl->first_aggr_queue_id +
399 (uint16_t)em_shm->opt.queue.num_aggr - 1;
400 } else {
401 queue_tbl->first_aggr_queue_id = queue_tbl->last_internal_queue_id + 1;
402 queue_tbl->last_aggr_queue_id = queue_tbl->first_aggr_queue_id +
403 (uint16_t)em_shm->opt.queue.num_aggr - 1;
404 }
405 } else {
406 queue_tbl->first_aggr_queue_id = INVALID_QUEUE_ID;
407 queue_tbl->last_aggr_queue_id = INVALID_QUEUE_ID;
408 }
409
410 size_t qelem_tbl_sz = sizeof(queue_elem_t) * max_queues;
411 size_t qname_tbl_sz = sizeof(char) * EM_QUEUE_NAME_LEN * max_queues;
412 size_t shm_sz = qelem_tbl_sz + qname_tbl_sz;
413
414 void *shm_tbl = env_shared_reserve("EM q_elem tbl and names", shm_sz);
415
416 RETURN_ERROR_IF(!shm_tbl, EM_ERR_LIB_FAILED, EM_ESCOPE_INIT,
417 "env_shared_reserve() failed when reserving \"EM q_elem tbl and names\"");
418 memset(shm_tbl, 0, shm_sz);
419
420 /* Store the q_elem tbl and q_name tbl pointers, points into the allocated shared mem */
421 queue_tbl->queue_elem = shm_tbl;
422 queue_tbl->name = (char(*)[EM_QUEUE_NAME_LEN])((uintptr_t)shm_tbl + qelem_tbl_sz);
423
424 /* Initialize the queue element table */
425 for (unsigned int i = 0; i < max_queues; i++)
426 queue_tbl->queue_elem[i].queue = (uint32_t)(uintptr_t)queue_idx2hdl(i);
427
428 /* Initialize the static queue pool */
429 if (em_shm->opt.queue.num_static)
430 min = queue_id2idx(queue_tbl->first_static_queue_id);
431 else
432 min = queue_id2idx(queue_tbl->first_internal_queue_id);
433
434 max = queue_id2idx(queue_tbl->last_internal_queue_id);
435 if (queue_pool_populate(queue_tbl, queue_pool_static, min, max) != 0)
436 return EM_ERR_LIB_FAILED;
437
438 /* Initialize the dynamic queue pool */
439 if (em_shm->opt.queue.num_dynamic) {
440 min = queue_id2idx(queue_tbl->first_dyn_queue_id);
441 max = queue_id2idx(queue_tbl->last_dyn_queue_id);
442 if (queue_pool_populate(queue_tbl, queue_pool, min, max) != 0)
443 return EM_ERR_LIB_FAILED;
444 }
445
446 /* Initialize the aggregator queue pool */
447 if (em_shm->opt.queue.num_aggr) {
448 min = queue_id2idx(queue_tbl->first_aggr_queue_id);
449 max = queue_id2idx(queue_tbl->last_aggr_queue_id);
450 if (queue_pool_populate(queue_tbl, queue_pool_aggr, min, max) != 0)
451 return EM_ERR_LIB_FAILED;
452 }
453
454 /* Initialize priority mapping, adapt to values from ODP */
455 min = odp_schedule_min_prio();
456 max = odp_schedule_max_prio();
457 em_shm->queue_prio.num_runtime = max - min + 1;
458 ret = queue_init_prio_map(min, max, em_shm->queue_prio.num_runtime);
459 RETURN_ERROR_IF(ret != 0, EM_ERR_LIB_FAILED, EM_ESCOPE_INIT,
460 "mapping odp priorities failed: %d", ret);
461
462 /* Initialize output queue free indexes */
463 for (unsigned int i = 0; i < EM_MAX_OUTPUT_QUEUES; i++)
464 queue_tbl->output_queue_idx_free[i] = true;
465
466 return EM_OK;
467}
468
469/**
470 * Queue inits done during EM core local init (once at startup on each core).
471 *
472 * Initialize event storage for queues of type 'EM_QUEUE_TYPE_LOCAL'.
473 */
474em_status_t queue_init_local(void)
475{
476 em_locm_t *const locm = &em_locm;
477 odp_stash_capability_t stash_capa;
478 odp_stash_param_t stash_param;
479 unsigned int num_obj = 0;
480 int core = em_core_id();
481 char name[ODP_STASH_NAME_LEN];
482
483 int ret = odp_stash_capability(&stash_capa, ODP_STASH_TYPE_FIFO);
484
485 if (ret != 0)
486 return EM_ERR_LIB_FAILED;
487
488 odp_stash_param_init(&stash_param);
489
490 stash_param.type = ODP_STASH_TYPE_FIFO;
491 /*
492 * Each local-queue stash lives in core-local memory and is only ever
493 * accessed (put and get) by the owning core's own thread, so the
494 * strongest single-thread mode applies.
495 */
496 stash_param.put_mode = ODP_STASH_OP_LOCAL;
497 stash_param.get_mode = ODP_STASH_OP_LOCAL;
498
499 /* Stash size: use EM default queue size value from config file: */
500 num_obj = em_shm->opt.queue.min_events_default;
501 if (num_obj != 0)
502 stash_param.num_obj = num_obj;
503 /* else: use odp default as set by odp_stash_param_init() */
504
505 stash_param.obj_size = sizeof(uint64_t);
506 if (stash_param.num_obj > stash_capa.max_num.u64) {
507 EM_LOG(EM_LOG_PRINT,
508 "%s(): req stash.num_obj(%" PRIu64 ") > capa.max_num.u64(%" PRIu64 ").\n"
509 " ==> using max value:%" PRIu64 "\n", __func__,
510 stash_param.num_obj, stash_capa.max_num.u64, stash_capa.max_num.u64);
511 stash_param.num_obj = stash_capa.max_num.u64;
512 }
513
514 stash_param.cache_size = 0; /* No core local caching */
515
516 locm->local_queues.empty = 1;
517
518 for (int prio = 0; prio < EM_QUEUE_PRIO_NUM; prio++) {
519 snprintf(name, sizeof(name),
520 "local-q:c%02d:prio%d", core, prio);
521 name[sizeof(name) - 1] = '\0';
522
523 locm->local_queues.prio[prio].empty_prio = 1;
524 locm->local_queues.prio[prio].stash =
525 odp_stash_create(name, &stash_param);
526 if (unlikely(locm->local_queues.prio[prio].stash ==
527 ODP_STASH_INVALID))
528 return EM_ERR_ALLOC_FAILED;
529 }
530
531 memset(&locm->output_queue_track, 0,
532 sizeof(locm->output_queue_track));
533
534 return EM_OK;
535}
536
537/**
538 * Queue termination done during em_term_core().
539 *
540 * Flush & destroy event storage for queues of type 'EM_QUEUE_TYPE_LOCAL'.
541 */
542em_status_t queue_term_local(void)
543{
545 em_event_t ev_tbl[EM_QUEUE_LOCAL_MULTI_MAX_BURST];
547 em_status_t stat = EM_OK;
548
549 for (;;) {
550 int num = next_local_queue_events(entry_tbl /*[out]*/,
552 if (num <= 0)
553 break;
554
555 for (int i = 0; i < num; i++)
556 ev_tbl[i] = (em_event_t)(uintptr_t)entry_tbl[i].evptr;
557
558 event_to_hdr_multi(ev_tbl, ev_hdr_tbl, num);
559
560 if (esv_enabled())
561 evstate_em2usr_multi(ev_tbl, ev_hdr_tbl, num,
562 EVSTATE__TERM_CORE__QUEUE_LOCAL);
563 em_free_multi(ev_tbl, num);
564 }
565
566 for (int prio = 0; prio < EM_QUEUE_PRIO_NUM; prio++) {
567 int ret = odp_stash_destroy(em_locm.local_queues.prio[prio].stash);
568
569 if (unlikely(ret != 0))
570 stat = EM_ERR_LIB_FAILED;
571 }
572
573 return stat;
574}
575
576static inline bool id_in_queue_pool_static(uint16_t qid)
577{
578 if (em_shm->opt.queue.num_static == 0)
579 return qid >= em_shm->queue_tbl.first_internal_queue_id &&
580 qid <= em_shm->queue_tbl.last_internal_queue_id;
581 else
582 return qid >= em_shm->queue_tbl.first_static_queue_id &&
583 qid <= em_shm->queue_tbl.last_internal_queue_id;
584}
585
586/**
587 * Allocate a new static or dynamic EM queue
588 *
589 * Aggregator queue allocation uses queue_aggr_alloc() instead.
590 *
591 * @param queue EM queue handle if a specific EM queue is requested,
592 * EM_QUEUE_UNDEF if any EM queue will do.
593 * @param[out] err_str Output var for error message in case of failure.
594 *
595 * @return EM queue handle
596 * @retval EM_QUEUE_UNDEF on failure
597 */
598em_queue_t queue_alloc(em_queue_t queue, const char **err_str /*out*/)
599{
600 queue_elem_t *queue_elem;
601 objpool_elem_t *queue_pool_elem;
602
603 if (queue == EM_QUEUE_UNDEF) {
604 /*
605 * Allocate a dynamic queue, i.e. take next available
606 */
607 queue_pool_elem = objpool_rem(&em_shm->queue_pool.objpool,
608 odp_thread_id());
609 if (unlikely(queue_pool_elem == NULL)) {
610 *err_str = "queue pool element alloc failed!";
611 return EM_QUEUE_UNDEF;
612 }
613 queue_elem = queue_poolelem2queue(queue_pool_elem);
614 } else {
615 /*
616 * Allocate a specific static-handle queue, handle given
617 */
619
620 iq.queue = queue;
621 if (iq.device_id != em_shm->conf.device_id ||
622 !id_in_queue_pool_static(iq.queue_id)) {
623 *err_str = "Invalid queue requested or handle not from static range!";
624 return EM_QUEUE_UNDEF;
625 }
626
627 queue_elem = queue_elem_get(queue);
628 if (unlikely(queue_elem == NULL)) {
629 *err_str = "queue_elem ptr NULL!";
630 return EM_QUEUE_UNDEF;
631 }
632 /* Verify that the queue is not allocated */
633 if (queue_allocated(queue_elem)) {
634 *err_str = "queue already allocated!";
635 return EM_QUEUE_UNDEF;
636 }
637 /* Remove the queue from the pool */
638 int ret = objpool_rem_elem(&em_shm->queue_pool_static.objpool,
639 &queue_elem->queue_pool_elem);
640 if (unlikely(ret != 0)) {
641 *err_str = "static queue pool element alloc failed!";
642 return EM_QUEUE_UNDEF;
643 }
644 }
645
646 odp_atomic_inc_u32(&em_shm->queue_count);
647 return (em_queue_t)(uintptr_t)queue_elem->queue;
648}
649
650em_status_t queue_free(em_queue_t queue)
651{
652 queue_elem_t *const queue_elem = queue_elem_get(queue);
653 objpool_t *objpool;
655
656 iq.queue = queue;
657
658 if (unlikely(queue_elem == NULL))
659 return EM_ERR_BAD_ID;
660
661 if (id_in_queue_pool_static(iq.queue_id))
662 objpool = &em_shm->queue_pool_static.objpool;
663 else
664 objpool = &em_shm->queue_pool.objpool;
665
666 queue_elem->state = EM_QUEUE_STATE_INVALID;
667
668 objpool_add(objpool,
669 queue_elem->queue_pool_elem.subpool_idx,
670 &queue_elem->queue_pool_elem);
671
672 odp_atomic_dec_u32(&em_shm->queue_count);
673 return EM_OK;
674}
675
676static em_queue_t queue_aggr_alloc(const char **err_str /*out*/)
677{
678 const queue_elem_t *queue_elem;
679 objpool_elem_t *queue_pool_elem;
680
681 /*
682 * Allocate an aggregator queue, i.e. take next available
683 */
684 queue_pool_elem = objpool_rem(&em_shm->queue_pool_aggr.objpool,
685 odp_thread_id());
686 if (unlikely(queue_pool_elem == NULL)) {
687 *err_str = "aggregator queue pool element alloc failed!";
688 return EM_QUEUE_UNDEF;
689 }
690 queue_elem = queue_poolelem2queue(queue_pool_elem);
691
692 odp_atomic_inc_u32(&em_shm->queue_count);
693 return (em_queue_t)(uintptr_t)queue_elem->queue;
694}
695
696static void queue_aggr_free(queue_elem_t *aggr_qelem)
697{
698 aggr_qelem->state = EM_QUEUE_STATE_INVALID;
699 aggr_qelem->aggr.parent_queue = EM_QUEUE_UNDEF;
700 aggr_qelem->aggr.pool = EM_POOL_UNDEF;
701 aggr_qelem->aggr.max_tmo_ns = 0;
702 aggr_qelem->aggr.max_size = 0;
703 aggr_qelem->aggr.event_type = EM_EVENT_TYPE_UNDEF;
704
705 objpool_t *objpool = &em_shm->queue_pool_aggr.objpool;
706
707 objpool_add(objpool, aggr_qelem->queue_pool_elem.subpool_idx,
708 &aggr_qelem->queue_pool_elem);
709
710 odp_atomic_dec_u32(&em_shm->queue_count);
711}
712
713static int queue_free_aggr_all(queue_elem_t *q_elem)
714{
715 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
716 uint32_t num_aggr = q_elem->aggr_parent.num_aggr;
717 const uint32_t aggr_undef = (uint32_t)(uintptr_t)EM_QUEUE_UNDEF;
718 bool err_occurred = false;
719
720 for (uint32_t i = 0; i < num_aggr; i++) {
721 em_queue_t aggr_queue =
722 (em_queue_t)(uintptr_t)q_elem->aggr_parent.aggr_queues[i];
723 queue_elem_t *aggr_qelem = queue_elem_get(aggr_queue);
724
725 q_elem->aggr_parent.aggr_queues[i] = aggr_undef;
726
727 if (unlikely(!aggr_qelem || !queue_allocated(aggr_qelem))) {
728 err_occurred = true;
729 EM_LOG(EM_LOG_ERR,
730 "Q:%" PRI_QUEUE " - Invalid aggr-Q:%" PRI_QUEUE " at idx:%u\n",
731 queue, aggr_queue, i);
732 continue;
733 }
734
735 if (unlikely(aggr_qelem->type != EM_QUEUE_TYPE_AGGR ||
736 aggr_qelem->aggr.parent_queue != queue)) {
737 err_occurred = true;
738 EM_LOG(EM_LOG_ERR,
739 "Q:%" PRI_QUEUE " - Invalid aggr-Q:%" PRI_QUEUE " at idx:%u\n"
740 "aggr-Q:{type=%u, parent-Q=%" PRI_QUEUE "}\n",
741 queue, aggr_queue, i,
742 aggr_qelem->type, aggr_qelem->aggr.parent_queue);
743 continue;
744 }
745
746 /* Free the aggregator queue */
747 queue_aggr_free(aggr_qelem);
748 }
749
750 q_elem->aggr_parent.num_aggr = 0;
751 q_elem->flags.has_aggr = 0;
752
753 if (unlikely(err_occurred))
754 return -1;
755
756 return 0;
757}
758
759static int queue_create_check_aggr(const em_queue_param_t *param,
760 const odp_event_aggr_capability_t *odp_aggr_capa,
761 const char **err_str)
762{
763 if (param->num_aggr == 0)
764 return 0; /* No event aggregation, no need to check further */
765
766 /* param->num_aggr > 0: */
767 if (unlikely(em_shm->opt.vector.backend == EM_VECTOR_BACKEND_PACKET)) {
768 *err_str = "Event aggregation not supported with packet vector backend!";
769 return -1;
770 }
771
772 /* Check maximum number of event aggregators */
773 if (unlikely(param->num_aggr > MIN(odp_aggr_capa->max_num,
774 odp_aggr_capa->max_num_per_queue) ||
775 param->num_aggr > EM_QUEUE_MAX_AGGR)) {
776 *err_str = "Invalid number of event aggregators, check limits!";
777 return -1;
778 }
779
780 if (unlikely(!param->aggr_conf)) {
781 *err_str = "Event aggregator config missing!";
782 return -1;
783 }
784
785 for (uint32_t i = 0; i < param->num_aggr; i++) {
786 const em_queue_aggr_conf_t *aggr_conf = &param->aggr_conf[i];
787
788 if (unlikely(aggr_conf->__internal_check != EM_CHECK_INIT_CALLED)) {
789 *err_str = "Use em_queue_aggr_conf_init() before create";
790 return -1;
791 }
792
793 if (unlikely(aggr_conf->pool == EM_POOL_UNDEF)) {
794 *err_str = "Event aggregator pool not defined!";
795 return -1;
796 }
797
798 const mpool_elem_t *pool_elem = pool_elem_get(aggr_conf->pool);
799
800 if (unlikely(!pool_elem || !pool_allocated(pool_elem) ||
801 pool_elem->event_type != EM_EVENT_TYPE_VECTOR)) {
802 *err_str = "Invalid pool for event aggregator!";
803 return -1;
804 }
805
806 /* Check max & min number of events that can be aggregated into an event vector */
807 if (unlikely(aggr_conf->max_size > odp_aggr_capa->max_size ||
808 aggr_conf->max_size < odp_aggr_capa->min_size)) {
809 *err_str = "Invalid max_size for event aggregator!";
810 return -1;
811 }
812
813 /* Check max & min allowed value of max_tmo_ns */
814 if (aggr_conf->max_tmo_ns > 0 /* 0 == no limit */ &&
815 (aggr_conf->max_tmo_ns > odp_aggr_capa->max_tmo_ns ||
816 aggr_conf->max_tmo_ns < odp_aggr_capa->min_tmo_ns)) {
817 *err_str = "Invalid max_tmo_ns for event aggregator!";
818 return -1;
819 }
820 }
821
822 return 0;
823}
824
825static int queue_setup_odp_aggr(const em_queue_aggr_conf_t aggr_conf[/*in:num_aggr*/],
826 odp_event_aggr_config_t odp_aggr_config[/*out:num_aggr*/],
827 uint32_t num_aggr, const char **err_str)
828{
829 /*
830 * Set queue params for event aggregation,
831 * values already checked in queue_create_check_aggr().
832 */
833 for (uint32_t i = 0; i < num_aggr; i++) {
834 const mpool_elem_t *pool_elem = pool_elem_get(aggr_conf[i].pool);
835
836 if (unlikely(!pool_elem || !pool_allocated(pool_elem) ||
837 pool_elem->event_type != EM_EVENT_TYPE_VECTOR)) {
838 *err_str = "Invalid pool for event aggregator!";
839 return -1;
840 }
841
842 int subpool = pool_find_subpool(pool_elem, aggr_conf[i].max_size);
843
844 if (unlikely(subpool < 0)) {
845 *err_str = "No suitable subpool found for event aggregator!";
846 return -1;
847 }
848
849 odp_aggr_config[i].pool = pool_elem->odp_pool[subpool];
850 odp_aggr_config[i].max_tmo_ns = aggr_conf[i].max_tmo_ns;
851 odp_aggr_config[i].max_size = aggr_conf[i].max_size;
852
853 /* Convert EM event type to ODP event type */
854 em_event_type_t evtype_major = em_event_type_major(aggr_conf[i].event_type);
855
856 switch (evtype_major) {
857 case EM_EVENT_TYPE_SW:
858 odp_aggr_config[i].event_type = ODP_EVENT_BUFFER;
859 break;
861 odp_aggr_config[i].event_type = ODP_EVENT_PACKET;
862 break;
864 odp_aggr_config[i].event_type = ODP_EVENT_TIMEOUT;
865 break;
866 case EM_EVENT_TYPE_TIMER: /* fallthrough */
867 case EM_EVENT_TYPE_CRYPTO: /* fallthrough */
868 case EM_EVENT_TYPE_ODP: /* fallthrough */
870 odp_aggr_config[i].event_type = ODP_EVENT_ANY;
871 break;
872 default:
873 /* also EM_EVENT_TYPE_VECTOR */
874 *err_str = "Unsupported event type for event aggregator!";
875 return -1;
876 }
877 }
878
879 return 0;
880}
881
882static int queue_create_check_sched(const em_queue_param_t *param, const char **err_str)
883{
884 const queue_group_elem_t *queue_group_elem = queue_group_elem_get(param->queue_group);
885
886 /* scheduled queues are always associated with a queue group */
887 if (unlikely(queue_group_elem == NULL || !queue_group_allocated(queue_group_elem))) {
888 *err_str = "Invalid queue group!";
889 return -1;
890 }
891
892 if (param->atomic_group != EM_ATOMIC_GROUP_UNDEF) {
893 const atomic_group_elem_t *ag_elem = atomic_group_elem_get(param->atomic_group);
894
895 if (unlikely(!ag_elem || !atomic_group_allocated(ag_elem))) {
896 *err_str = "Invalid atomic group!";
897 return -1;
898 }
899 }
900
901 if (unlikely(param->prio >= EM_QUEUE_PRIO_NUM)) {
902 *err_str = "Invalid queue priority!";
903 return -1;
904 }
905
906 const odp_event_aggr_capability_t *odp_aggr_capa =
907 &em_shm->queue_tbl.odp_schedule_capability.aggr;
908
909 int err = queue_create_check_aggr(param, odp_aggr_capa, err_str /*out*/);
910
911 if (unlikely(err)) /* 'err_str' set by queue_create_check_aggr() */
912 return -1;
913
914 return 0;
915}
916
917static int queue_create_check_unsched(const em_queue_param_t *param, const char **err_str)
918{
919 /* API arg checks for unscheduled queues */
920 if (unlikely(param->prio != EM_QUEUE_PRIO_UNDEF)) {
921 *err_str = "Invalid priority for unsched queue!";
922 return -1;
923 }
924 if (unlikely(param->queue_group != EM_QUEUE_GROUP_UNDEF)) {
925 *err_str = "Queue group not used with unsched queues!";
926 return -1;
927 }
928 if (unlikely(param->atomic_group != EM_ATOMIC_GROUP_UNDEF)) {
929 *err_str = "Atomic group not used with unsched queues!";
930 return -1;
931 }
932
933 const odp_event_aggr_capability_t *odp_aggr_capa =
934 &em_shm->queue_tbl.odp_queue_capability.plain.aggr;
935
936 int err = queue_create_check_aggr(param, odp_aggr_capa, err_str /*out*/);
937
938 if (unlikely(err)) /* 'err_str' set by queue_create_check_aggr() */
939 return -1;
940
941 return 0;
942}
943
944static int queue_create_check_local(const em_queue_param_t *param, const char **err_str)
945{
946 /* API arg checks for local queues */
947 if (unlikely(param->queue_group != EM_QUEUE_GROUP_UNDEF)) {
948 *err_str = "Queue group not used with local queues!";
949 return -1;
950 }
951 if (unlikely(param->atomic_group != EM_ATOMIC_GROUP_UNDEF)) {
952 *err_str = "Atomic group not used with local queues!";
953 return -1;
954 }
955 if (unlikely(param->prio >= EM_QUEUE_PRIO_NUM)) {
956 *err_str = "Invalid queue priority!";
957 return -1;
958 }
959 if (unlikely(param->num_aggr > 0)) {
960 *err_str = "Event aggregation not supported with local queues!";
961 return -1;
962 }
963
964 return 0;
965}
966
967static int queue_create_check_output(const em_queue_param_t *param, const char **err_str)
968{
969 /* API arg checks for output queues */
970 if (unlikely(param->queue_group != EM_QUEUE_GROUP_UNDEF)) {
971 *err_str = "Queue group not used with output queues!";
972 return -1;
973 }
974 if (unlikely(param->atomic_group != EM_ATOMIC_GROUP_UNDEF)) {
975 *err_str = "Atomic group not used with output queues!";
976 return -1;
977 }
978 if (unlikely(param->num_aggr > 0)) {
979 *err_str = "Event aggregation not supported with output queues!";
980 return -1;
981 }
982 if (unlikely(param->output_conf.output_fn == NULL)) {
983 *err_str = "Invalid output queue conf: output function missing!";
984 return -1;
985 }
986
987 return 0;
988}
989
990static int queue_create_check_args(const em_queue_param_t *param, const char **err_str)
991{
992 if (param->atomic_group != EM_ATOMIC_GROUP_UNDEF &&
993 param->type != EM_QUEUE_TYPE_ATOMIC) {
994 *err_str = "Atomic group can only be used with atomic queues!";
995 return -1;
996 }
997
998 /* scheduled queue */
999 if (param->type == EM_QUEUE_TYPE_ATOMIC ||
1000 param->type == EM_QUEUE_TYPE_PARALLEL ||
1001 param->type == EM_QUEUE_TYPE_ORDERED)
1002 return queue_create_check_sched(param, err_str/*out*/);
1003
1004 /* other queue types */
1005 switch (param->type) {
1007 return queue_create_check_unsched(param, err_str/*out*/);
1009 return queue_create_check_local(param, err_str/*out*/);
1011 return queue_create_check_output(param, err_str/*out*/);
1012 default:
1013 *err_str = "Unknown queue type";
1014 return -1;
1015 }
1016
1017 return 0;
1018}
1019
1020void queue_param_apply_conf(em_queue_param_t *param /*in,out*/,
1021 const em_queue_conf_t *conf)
1022{
1023 if (conf == NULL)
1024 return;
1025
1026 param->flags = conf->flags;
1027 param->min_events = conf->min_events;
1028 if (conf->conf &&
1029 conf->conf_len == sizeof(em_output_queue_conf_t) &&
1030 param->type == EM_QUEUE_TYPE_OUTPUT) {
1031 param->output_conf = *((const em_output_queue_conf_t *)conf->conf);
1032 }
1033}
1034
1035em_queue_t queue_create_param(const char *name, const em_queue_param_t *param,
1036 const char **err_str /*out*/)
1037{
1038 int err = queue_create_check_args(param, err_str /*out*/);
1039
1040 if (unlikely(err)) /* 'err_str' set by queue_create_check_args() */
1041 return EM_QUEUE_UNDEF;
1042
1043 /*
1044 * Allocate the queue handle and obtain the corresponding queue-element
1045 */
1046 em_queue_t queue_req = param->queue;
1047 em_queue_t queue = queue_alloc(queue_req, err_str /*out*/);
1048
1049 if (unlikely(queue == EM_QUEUE_UNDEF)) {
1050 /* 'err_str' set by queue_alloc() */
1051 return EM_QUEUE_UNDEF;
1052 }
1053 if (unlikely(queue_req != EM_QUEUE_UNDEF && queue_req != queue)) {
1054 queue_free(queue);
1055 *err_str = "Failed to allocate the requested queue!";
1056 return EM_QUEUE_UNDEF;
1057 }
1058
1059 queue_elem_t *queue_elem = queue_elem_get(queue);
1060
1061 if (unlikely(!queue_elem)) {
1062 queue_free(queue);
1063 *err_str = "Queue elem NULL!";
1064 return EM_QUEUE_UNDEF;
1065 }
1066
1067 /*
1068 * Setup/configure the queue
1069 */
1070 err = queue_setup(name, param, queue_elem, err_str);
1071 if (unlikely(err)) {
1072 queue_free(queue);
1073 /* 'err_str' set by queue_setup() */
1074 return EM_QUEUE_UNDEF;
1075 }
1076
1077 return queue;
1078}
1079
1080em_status_t queue_delete(queue_elem_t *const queue_elem, const char **err_str/*out*/)
1081{
1082 queue_state_t old_state;
1083 queue_state_t new_state;
1084 em_status_t ret;
1085 em_queue_t queue = (em_queue_t)(uintptr_t)queue_elem->queue;
1086 em_queue_type_t type = queue_elem->type;
1087
1088 if (unlikely(!queue_allocated(queue_elem))) {
1089 *err_str = "Invalid queue (not allocated)";
1090 return EM_ERR_BAD_STATE;
1091 }
1092 /*
1093 * Cannot explicitly delete aggregator queues,
1094 * they are deleted as part of the parent queue deletion.
1095 */
1096 if (unlikely(type == EM_QUEUE_TYPE_AGGR)) {
1097 *err_str = "Cannot delete aggregator queue, delete parent queue instead";
1098 return EM_ERR_NOT_SUPPORTED;
1099 }
1100
1101 old_state = queue_elem->state;
1102 new_state = EM_QUEUE_STATE_INVALID;
1103
1104 if (type != EM_QUEUE_TYPE_UNSCHEDULED &&
1105 type != EM_QUEUE_TYPE_OUTPUT) {
1106 /* verify scheduled queue state transition */
1107 ret = queue_state_change__check(old_state, new_state,
1108 0/*!is_setup*/);
1109 if (unlikely(ret != EM_OK)) {
1110 *err_str = "Invalid queue state transition for delete";
1111 return ret;
1112 }
1113 }
1114
1115 if (type != EM_QUEUE_TYPE_UNSCHEDULED &&
1116 type != EM_QUEUE_TYPE_LOCAL &&
1117 type != EM_QUEUE_TYPE_OUTPUT) {
1118 queue_group_elem_t *const queue_group_elem =
1119 queue_group_elem_get(queue_elem->queue_group);
1120
1121 if (unlikely(queue_group_elem == NULL ||
1122 !queue_group_allocated(queue_group_elem))) {
1123 *err_str = "Invalid queue group";
1124 return EM_ERR_BAD_ID;
1125 }
1126
1127 /* Remove the queue from the queue group list */
1128 queue_group_rem_queue_list(queue_group_elem, queue_elem);
1129 }
1130
1131 if (type == EM_QUEUE_TYPE_OUTPUT) {
1132 odp_ticketlock_t *const lock = &queue_elem->output.lock;
1133 q_elem_output_t *const q_out = &queue_elem->output;
1134
1135 odp_ticketlock_lock(lock);
1136 /* Drain any remaining events from the output queue */
1137 output_queue_drain(queue_elem);
1138 odp_ticketlock_unlock(lock);
1139
1140 /* delete the fn-args storage if allocated in create */
1141 if (q_out->output_fn_args_event != EM_EVENT_UNDEF) {
1144 }
1145
1146 odp_ticketlock_lock(&em_shm->queue_tbl.output_queue_lock);
1147 em_shm->queue_tbl.output_queue_idx_free[q_out->idx] = true;
1148 odp_atomic_dec_u32(&em_shm->queue_tbl.output_queue_count);
1149 odp_ticketlock_unlock(&em_shm->queue_tbl.output_queue_lock);
1150 }
1151
1152 if (queue_elem->odp_queue != ODP_QUEUE_INVALID &&
1153 !queue_elem->flags.is_pktin) {
1154 /*
1155 * Destroy the underlying ODP queue.
1156 * Also destroys all ODP event aggregators used by this queue
1157 * (for scheduled and unscheduled queues only).
1158 */
1159 int err = odp_queue_destroy(queue_elem->odp_queue);
1160
1161 if (unlikely(err)) {
1162 *err_str = "odp_queue_destroy() failed";
1163 return EM_ERR_LIB_FAILED;
1164 }
1165 }
1166
1167 queue_elem->odp_queue = ODP_QUEUE_INVALID;
1168
1169 if (queue_elem->flags.has_aggr) {
1170 /* Free all aggregator queue elems linked to this queue (if any) */
1171 int err = queue_free_aggr_all(queue_elem);
1172
1173 if (unlikely(err)) {
1174 /* Log error, but continue with queue deletion */
1175 EM_LOG(EM_LOG_ERR,
1176 "Failed to free aggregator queues of EM-Q:%" PRI_QUEUE " err:%d",
1177 queue, err);
1178 }
1179 }
1180
1181 /* Zero queue name */
1182 em_shm->queue_tbl.name[queue_hdl2idx(queue)][0] = '\0';
1183
1184 /* Remove the queue from the atomic group it belongs to, if any */
1185 atomic_group_remove_queue(queue_elem);
1186
1187 ret = queue_free(queue);
1188 if (unlikely(ret != EM_OK)) {
1189 *err_str = "queue_free() failed";
1190 return ret;
1191 }
1192
1193 return EM_OK;
1194}
1195
1196/**
1197 * Setup an allocated/created queue before use.
1198 */
1199static int queue_setup(const char *name, const em_queue_param_t *param,
1200 queue_elem_t *q_elem /*in/out*/,
1201 const char **err_str /*out*/)
1202{
1203 int ret;
1204
1205 /* Set common queue-elem fields based on setup */
1206 queue_setup_common(name, param, q_elem);
1207
1208 switch (param->type) {
1209 case EM_QUEUE_TYPE_ATOMIC: /* fallthrough */
1210 case EM_QUEUE_TYPE_PARALLEL: /* fallthrough */
1212 ret = queue_setup_scheduled(param, q_elem, err_str);
1213 break;
1215 ret = queue_setup_unscheduled(param, q_elem, err_str);
1216 break;
1218 ret = queue_setup_local(param, q_elem, err_str);
1219 break;
1221 ret = queue_setup_output(param, q_elem, err_str);
1222 break;
1223 default:
1224 *err_str = "Queue setup: unknown queue type";
1225 ret = -1;
1226 break;
1227 }
1228
1229 if (unlikely(ret))
1230 return -1;
1231
1232 env_sync_mem();
1233 return 0;
1234}
1235
1236/**
1237 * Helper function to queue_setup()
1238 *
1239 * Set EM queue params common to all EM queues based on EM config
1240 */
1241void queue_setup_common(const char *name, const em_queue_param_t *param,
1242 queue_elem_t *q_elem /*in/out*/)
1243{
1244 const em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
1245 char *const qname = &em_shm->queue_tbl.name[queue_hdl2idx(queue)][0];
1246
1247 /* checks that the odp queue context points to an EM queue elem */
1248 q_elem->valid_check = QUEUE_ELEM_VALID;
1249
1250 /* Store queue name */
1251 if (name)
1252 strncpy(qname, name, EM_QUEUE_NAME_LEN - 1);
1253 else /* default unique name: "EM_Q_" + Q-id = e.g. EM_Q_1234 */
1254 snprintf(qname, EM_QUEUE_NAME_LEN,
1255 "%s%" PRI_QUEUE "", EM_Q_BASENAME, queue);
1256 qname[EM_QUEUE_NAME_LEN - 1] = '\0';
1257
1258 q_elem->flags.all = 0;
1259 /* Init q_elem fields based on setup params and clear the rest */
1260 q_elem->type = (uint8_t)param->type;
1261 q_elem->priority = (uint8_t)param->prio;
1262 q_elem->queue_group = param->queue_group;
1263
1264 /* Does this queue belong to an EM Atomic Group? */
1265 if (param->atomic_group == EM_ATOMIC_GROUP_UNDEF) {
1266 q_elem->flags.in_atomic_group = false;
1268 } else {
1269 q_elem->flags.in_atomic_group = true;
1270 q_elem->agrp.atomic_group = param->atomic_group;
1271 }
1272
1273 /* Clear the rest */
1274 q_elem->odp_queue = ODP_QUEUE_INVALID;
1275 q_elem->state = EM_QUEUE_STATE_INVALID;
1276 q_elem->context = NULL;
1277 q_elem->eo = (uint16_t)(uintptr_t)EM_EO_UNDEF;
1278 q_elem->eo_elem = NULL;
1279 q_elem->eo_ctx = NULL;
1280 q_elem->max_events = 0;
1281 q_elem->receive_func = NULL;
1282 q_elem->receive_multi_func = NULL; /* union */
1283 q_elem->aggr_parent.num_aggr = 0;
1284}
1285
1286/**
1287 * Helper function to queue_setup_...()
1288 *
1289 * Set common ODP queue params based on EM config
1290 */
1291static void queue_setup_odp_common(const em_queue_param_t *param,
1292 odp_queue_param_t *odp_queue_param /*out*/)
1293{
1294 /*
1295 * Set ODP queue params according to EM queue conf flags
1296 */
1297 em_queue_flag_t flags = param->flags & EM_QUEUE_FLAG_MASK;
1298
1299 if (flags != EM_QUEUE_FLAG_DEFAULT) {
1300 if (flags & EM_QUEUE_FLAG_NONBLOCKING_WF)
1301 odp_queue_param->nonblocking = ODP_NONBLOCKING_WF;
1302 else if (flags & EM_QUEUE_FLAG_NONBLOCKING_LF)
1303 odp_queue_param->nonblocking = ODP_NONBLOCKING_LF;
1304
1305 if (flags & EM_QUEUE_FLAG_ENQ_NOT_MTSAFE)
1306 odp_queue_param->enq_mode = ODP_QUEUE_OP_MT_UNSAFE;
1307 if (flags & EM_QUEUE_FLAG_DEQ_NOT_MTSAFE)
1308 odp_queue_param->deq_mode = ODP_QUEUE_OP_MT_UNSAFE;
1309 }
1310
1311 /*
1312 * Set minimum queue size if other than 'default'(0)
1313 */
1314 if (param->min_events == 0) {
1315 /* use EM default value from config file: */
1316 unsigned int size = em_shm->opt.queue.min_events_default;
1317
1318 if (size != 0)
1319 odp_queue_param->size = size;
1320 /* else: use odp default as set by odp_queue_param_init() */
1321 } else {
1322 /* use user provided value: */
1323 odp_queue_param->size = param->min_events;
1324 }
1325}
1326
1327/**
1328 * Create an ODP queue for the newly created EM queue
1329 */
1330static int create_odp_queue(queue_elem_t *q_elem,
1331 const odp_queue_param_t *odp_queue_param)
1332{
1333 char odp_name[ODP_QUEUE_NAME_LEN];
1334 odp_queue_t odp_queue;
1335
1336 (void)queue_name(q_elem, odp_name/*out*/, sizeof(odp_name));
1337
1338 odp_queue = odp_queue_create(odp_name, odp_queue_param);
1339 if (unlikely(odp_queue == ODP_QUEUE_INVALID))
1340 return -1;
1341
1342 /* Store the corresponding ODP Queue */
1343 q_elem->odp_queue = odp_queue;
1344
1345 return 0;
1346}
1347
1348/**
1349 * Helper function to queue_setup_scheduled() and queue_setup_unscheduled().
1350 *
1351 * Set up (child) aggregator queues for the parent queue. Retrieves the
1352 * ODP aggregator queue handles, allocates EM aggregator queue elements and
1353 * initializes them with names derived from the parent queue name.
1354 *
1355 * @param q_elem Parent queue element, updated with aggregator parent data.
1356 * @param num_aggr Number of event aggregators to set up.
1357 * @param err_str [out] Error message string on failure.
1358 *
1359 * @return 0 on success, -1 on failure.
1360 */
1361static int queue_setup_aggr(queue_elem_t *q_elem /*in,out*/,
1362 const em_queue_aggr_conf_t aggr_conf[/*in:num_aggr*/],
1363 uint32_t num_aggr, const char **err_str /*out*/)
1364{
1365 odp_queue_t odp_aggr_queues[num_aggr];
1366 em_queue_t aggr_queues[num_aggr];
1367 queue_elem_t *aggr_qelems[num_aggr];
1368
1369 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
1370 char *const qname = &em_shm->queue_tbl.name[queue_hdl2idx(queue)][0];
1371
1372 /* Get the ODP queue handles for the event aggregators */
1373 for (uint32_t i = 0; i < num_aggr; i++) {
1374 odp_aggr_queues[i] = odp_queue_aggr(q_elem->odp_queue, i);
1375 if (unlikely(odp_aggr_queues[i] == ODP_QUEUE_INVALID)) {
1376 *err_str = "Q-setup-aggr: failed to get ODP event aggregator!";
1377 return -1;
1378 }
1379 }
1380
1381 /* Allocate queue elems for the EM queue aggregators */
1382 for (uint32_t i = 0; i < num_aggr; i++) {
1383 aggr_queues[i] = queue_aggr_alloc(err_str /*out*/);
1384 aggr_qelems[i] = queue_elem_get(aggr_queues[i]);
1385
1386 if (unlikely(aggr_queues[i] == EM_QUEUE_UNDEF ||
1387 aggr_qelems[i] == NULL)) {
1388 *err_str = "Q-setup-aggr: failed to allocate aggregator queue!";
1389 for (uint32_t j = 0; j < i; j++)
1390 queue_aggr_free(aggr_qelems[j]);
1391 return -1;
1392 }
1393 }
1394
1395 q_elem->flags.has_aggr = 1;
1396 q_elem->aggr_parent.num_aggr = num_aggr;
1397
1398 /* Setup the EM aggregator queue elems */
1399 for (uint32_t i = 0; i < num_aggr; i++) {
1400 const em_queue_t aggr_queue = aggr_queues[i];
1401 queue_elem_t *aggr_qelem = aggr_qelems[i];
1402 int aggr_idx = queue_hdl2idx(aggr_queue);
1403 char *const aggr_name = &em_shm->queue_tbl.name[aggr_idx][0];
1404
1405 /*
1406 * Store aggregator queue name:
1407 * "AGGR01-<parent_qname>" e.g. "AGGR01-EM_Q_1234"
1408 * "AGGRnn-" prefix (4+2+1) + '\0' = 8 bytes overhead
1409 */
1410 const int aggr_name_maxlen = EM_QUEUE_NAME_LEN - 8;
1411
1412 snprintf(aggr_name, EM_QUEUE_NAME_LEN,
1413 "AGGR%02u-%.*s",
1414 (i + 1) % (EM_QUEUE_MAX_AGGR + 1) /* modulo informs compiler: fits %02u */,
1415 aggr_name_maxlen, qname);
1416 aggr_name[EM_QUEUE_NAME_LEN - 1] = '\0';
1417
1418 /* checks that the odp queue context points to an EM queue elem */
1419 aggr_qelem->valid_check = QUEUE_ELEM_VALID;
1420 /* Set the queue type */
1421 aggr_qelem->type = EM_QUEUE_TYPE_AGGR;
1422 /* Store the corresponding ODP aggregator queue */
1423 aggr_qelem->odp_queue = odp_aggr_queues[i];
1424
1425 /* Clear the rest */
1426 aggr_qelem->flags.all = 0;
1427 aggr_qelem->flags.scheduled = q_elem->flags.scheduled;
1428 aggr_qelem->state = q_elem->state; /* same as parent queue state */
1429 aggr_qelem->priority = EM_QUEUE_PRIO_UNDEF;
1430 aggr_qelem->max_events = 0;
1431 aggr_qelem->eo = (uint16_t)(uintptr_t)EM_EO_UNDEF;
1432 aggr_qelem->context = NULL;
1433 aggr_qelem->receive_func = NULL;
1434 aggr_qelem->receive_multi_func = NULL; /* union */
1435 aggr_qelem->eo_ctx = NULL;
1436 aggr_qelem->eo_elem = NULL;
1437 aggr_qelem->queue_group = EM_QUEUE_GROUP_UNDEF;
1438
1439 /* store aggregator specific data */
1440 aggr_qelem->aggr.parent_queue = queue;
1441 aggr_qelem->aggr.pool = aggr_conf[i].pool;
1442 aggr_qelem->aggr.max_tmo_ns = aggr_conf[i].max_tmo_ns;
1443 aggr_qelem->aggr.max_size = aggr_conf[i].max_size;
1444 aggr_qelem->aggr.event_type = aggr_conf[i].event_type;
1445 }
1446
1447 /* store the aggregator queue handles in the parent's queue elem */
1448 for (uint32_t i = 0; i < num_aggr; i++)
1449 q_elem->aggr_parent.aggr_queues[i] = (uint32_t)(uintptr_t)aggr_queues[i];
1450
1451 return 0;
1452}
1453
1454/**
1455 * Helper function to queue_setup()
1456 *
1457 * Set EM and ODP queue params for scheduled queues
1458 */
1459static int queue_setup_scheduled(const em_queue_param_t *param,
1460 queue_elem_t *q_elem /*out*/,
1461 const char **err_str /*out*/)
1462{
1463 /* validity checks done earlier for queue_group */
1464 queue_group_elem_t *qgrp_elem = queue_group_elem_get(param->queue_group);
1465 int err;
1466
1467 if (unlikely(qgrp_elem == NULL)) {
1468 *err_str = "Q-setup-sched: invalid queue group!";
1469 return -1;
1470 }
1471
1472 /* q_elem common fields already set by queue_setup_common() */
1473 q_elem->flags.scheduled = true;
1474 q_elem->state = EM_QUEUE_STATE_INIT;
1475
1476 /*
1477 * Set up a scheduled ODP queue for the EM scheduled queue
1478 */
1479 odp_queue_param_t odp_queue_param;
1480 /* Default values. Always changed unless error: */
1481 odp_schedule_sync_t odp_schedule_sync = ODP_SCHED_SYNC_PARALLEL;
1482 odp_schedule_prio_t odp_prio = odp_schedule_min_prio();
1483
1484 /* Init odp queue params to default values */
1485 odp_queue_param_init(&odp_queue_param);
1486 /* Set common ODP queue params based on the EM Queue config */
1487 queue_setup_odp_common(param, &odp_queue_param /*out*/);
1488
1489 err = scheduled_queue_type_em2odp(param->type, &odp_schedule_sync /*out*/);
1490 if (unlikely(err)) {
1491 *err_str = "Q-setup-sched: invalid queue type!";
1492 return -1;
1493 }
1494
1495 err = prio_em2odp(param->prio, &odp_prio /*out*/);
1496 if (unlikely(err)) {
1497 *err_str = "Q-setup-sched: invalid queue priority!";
1498 return -1;
1499 }
1500
1501 odp_queue_param.type = ODP_QUEUE_TYPE_SCHED;
1502 odp_queue_param.sched.prio = odp_prio;
1503 odp_queue_param.sched.sync = odp_schedule_sync;
1504 odp_queue_param.sched.group = qgrp_elem->odp_sched_group;
1505
1506 /* Retrieve previously stored ODP scheduler capabilities */
1507 const odp_schedule_capability_t *odp_sched_capa =
1508 &em_shm->queue_tbl.odp_schedule_capability;
1509
1510 /*
1511 * Check nonblocking level against sched queue capabilities.
1512 * Related ODP queue params set earlier in queue_setup_common().
1513 */
1514 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_LF &&
1515 odp_sched_capa->lockfree_queues == ODP_SUPPORT_NO) {
1516 *err_str = "Q-setup-sched: non-blocking, lock-free sched queues unavailable";
1517 return -1;
1518 }
1519 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_WF &&
1520 odp_sched_capa->waitfree_queues == ODP_SUPPORT_NO) {
1521 *err_str = "Q-setup-sched: non-blocking, wait-free sched queues unavailable";
1522 return -1;
1523 }
1524 if (odp_queue_param.enq_mode != ODP_QUEUE_OP_MT ||
1525 odp_queue_param.deq_mode != ODP_QUEUE_OP_MT) {
1526 *err_str = "Q-setup-sched: invalid flag: scheduled queues must be MT-safe";
1527 return -1;
1528 }
1529
1530 /*
1531 * Note: The ODP queue context points to the EM queue elem.
1532 * The EM queue context set by the user using the API function
1533 * em_queue_set_context() is accessed through the queue_elem_t::context
1534 * and retrieved with em_queue_context() or passed by EM to the
1535 * EO-receive function for scheduled queues.
1536 */
1537 odp_queue_param.context = q_elem;
1538 /*
1539 * Set the context data length (in bytes) for potential prefetching.
1540 * The ODP implementation may use this value as a hint for the number
1541 * of context data bytes to prefetch.
1542 */
1543 odp_queue_param.context_len = sizeof(*q_elem);
1544
1545 /*
1546 * Set event aggregation params for the queue if any,
1547 * values already checked in queue_create_check_aggr().
1548 */
1549 const uint32_t num_aggr = MIN(param->num_aggr, EM_QUEUE_MAX_AGGR);
1550 odp_event_aggr_config_t odp_aggr_config[EM_QUEUE_MAX_AGGR];
1551
1552 if (num_aggr > 0) {
1553 err = queue_setup_odp_aggr(param->aggr_conf /* [ in:num_aggr] */,
1554 odp_aggr_config /* [out:num_aggr] */,
1555 num_aggr, err_str /*out*/);
1556 if (unlikely(err)) /* err_str set */
1557 return -1;
1558 odp_queue_param.num_aggr = num_aggr;
1559 odp_queue_param.aggr = &odp_aggr_config[0];
1560 }
1561
1562 /* Create the underlying ODP queue, stored in q_elem->odp_queue when successful */
1563 err = create_odp_queue(q_elem, &odp_queue_param);
1564 if (unlikely(err)) {
1565 *err_str = "Q-setup-sched: scheduled odp queue creation failed!";
1566 return -1;
1567 }
1568
1569 if (num_aggr > 0) {
1570 err = queue_setup_aggr(q_elem, param->aggr_conf, num_aggr, err_str /*out*/);
1571 if (unlikely(err)) { /* err_str set by queue_setup_aggr() */
1572 err = odp_queue_destroy(q_elem->odp_queue);
1573 if (unlikely(err))
1574 EM_LOG(EM_LOG_ERR, "ODP queue destroy failed:%d", err);
1575 q_elem->odp_queue = ODP_QUEUE_INVALID;
1576 return -1;
1577 }
1578 }
1579
1580 /*
1581 * Add the scheduled queue to the queue group
1582 */
1583 queue_group_add_queue_list(qgrp_elem, q_elem);
1584
1585 return 0;
1586}
1587
1588/*
1589 * Helper function to queue_setup()
1590 *
1591 * Set EM and ODP queue params for unscheduled queues
1592 */
1593static int queue_setup_unscheduled(const em_queue_param_t *param,
1594 queue_elem_t *q_elem /*out*/,
1595 const char **err_str /*out*/)
1596{
1597 int err;
1598
1599 q_elem->priority = EM_QUEUE_PRIO_UNDEF;
1602 /* unscheduled queues are not scheduled */
1603 q_elem->flags.scheduled = false;
1604 q_elem->state = EM_QUEUE_STATE_UNSCHEDULED;
1605
1606 /*
1607 * Set up a plain ODP queue for the EM unscheduled queue.
1608 */
1609 odp_queue_param_t odp_queue_param;
1610 /* Retrieve previously stored ODP queue capabilities */
1611 const odp_queue_capability_t *odp_queue_capa =
1612 &em_shm->queue_tbl.odp_queue_capability;
1613
1614 /* Init odp queue params to default values */
1615 odp_queue_param_init(&odp_queue_param);
1616 /* Set common ODP queue params based on the EM Queue config */
1617 queue_setup_odp_common(param, &odp_queue_param);
1618
1619 odp_queue_param.type = ODP_QUEUE_TYPE_PLAIN;
1620 /* don't order events enqueued into unsched queues */
1621 odp_queue_param.order = ODP_QUEUE_ORDER_IGNORE;
1622
1623 /*
1624 * Check nonblocking level against plain queue capabilities.
1625 * Related ODP queue params set earlier in queue_setup_common().
1626 */
1627 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_LF &&
1628 odp_queue_capa->plain.lockfree.max_num == 0) {
1629 *err_str = "Q-setup-unsched: non-blocking, lock-free unsched queues unavailable";
1630 return -1;
1631 }
1632 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_WF &&
1633 odp_queue_capa->plain.waitfree.max_num == 0) {
1634 *err_str = "Q-setup-unsched: non-blocking, wait-free unsched queues unavailable";
1635 return -2;
1636 }
1637
1638 /*
1639 * Note: The ODP queue context points to the EM queue elem.
1640 * The EM queue context set by the user using the API function
1641 * em_queue_set_context() is accessed through the queue_elem_t::context
1642 * and retrieved with em_queue_context().
1643 */
1644 odp_queue_param.context = q_elem;
1645 /*
1646 * Set the context data length (in bytes) for potential prefetching.
1647 * The ODP implementation may use this value as a hint for the number
1648 * of context data bytes to prefetch.
1649 */
1650 odp_queue_param.context_len = sizeof(*q_elem);
1651
1652 /*
1653 * Set event aggregation params for the queue if any,
1654 * values already checked in queue_create_check_aggr().
1655 */
1656 const uint32_t num_aggr = MIN(param->num_aggr, EM_QUEUE_MAX_AGGR);
1657 odp_event_aggr_config_t odp_aggr_config[EM_QUEUE_MAX_AGGR];
1658
1659 if (num_aggr > 0) {
1660 err = queue_setup_odp_aggr(param->aggr_conf /* [ in:num_aggr] */,
1661 odp_aggr_config /* [out:num_aggr] */,
1662 num_aggr, err_str /*out*/);
1663 if (unlikely(err)) /* err_str set */
1664 return -1;
1665 odp_queue_param.num_aggr = num_aggr;
1666 odp_queue_param.aggr = &odp_aggr_config[0];
1667 }
1668
1669 /* Create the underlying ODP queue, stored in q_elem->odp_queue when successful */
1670 err = create_odp_queue(q_elem, &odp_queue_param);
1671 if (unlikely(err)) {
1672 *err_str = "Q-setup-unsched: plain odp queue creation failed!";
1673 return -1;
1674 }
1675
1676 if (num_aggr > 0) {
1677 err = queue_setup_aggr(q_elem, param->aggr_conf, num_aggr, err_str /*out*/);
1678 if (unlikely(err)) { /* err_str set by queue_setup_aggr() */
1679 err = odp_queue_destroy(q_elem->odp_queue);
1680 if (unlikely(err))
1681 EM_LOG(EM_LOG_ERR, "ODP queue destroy failed:%d", err);
1682 q_elem->odp_queue = ODP_QUEUE_INVALID;
1683 return -1;
1684 }
1685 }
1686
1687 return 0;
1688}
1689
1690/*
1691 * Helper function to queue_setup()
1692 *
1693 * Set EM queue params for (core-)local queues
1694 */
1695static int queue_setup_local(const em_queue_param_t *param,
1696 queue_elem_t *q_elem /*out*/,
1697 const char **err_str /*out*/)
1698{
1699 (void)err_str;
1700
1701 q_elem->priority = (uint8_t)param->prio;
1702 q_elem->type = EM_QUEUE_TYPE_LOCAL;
1704 /* local queues are not scheduled */
1705 q_elem->flags.scheduled = false;
1706 q_elem->state = EM_QUEUE_STATE_INIT;
1707
1708 return 0;
1709}
1710
1711/*
1712 * Helper function to queue_setup()
1713 *
1714 * Set EM queue params for output queues
1715 */
1716static int queue_setup_output(const em_queue_param_t *param,
1717 queue_elem_t *q_elem /*out*/,
1718 const char **err_str /*out*/)
1719{
1720 queue_tbl_t *const queue_tbl = &em_shm->queue_tbl;
1721 const em_output_queue_conf_t *output_conf = &param->output_conf;
1722 uint32_t nbr_output_queues;
1723
1724 /* Double check */
1725 nbr_output_queues = odp_atomic_fetch_add_u32(&queue_tbl->output_queue_count, 1) + 1;
1726
1727 if (unlikely(nbr_output_queues > EM_MAX_OUTPUT_QUEUES)) {
1728 *err_str = "Q-setup-output: too many output queues";
1729 goto error_dec_cnt;
1730 }
1731
1732 q_elem->priority = EM_QUEUE_PRIO_UNDEF;
1733 q_elem->type = EM_QUEUE_TYPE_OUTPUT;
1735 /* output queues are not scheduled */
1736 q_elem->flags.scheduled = false;
1737 /* use unsched state for output queues */
1738 q_elem->state = EM_QUEUE_STATE_UNSCHEDULED;
1739
1740 if (unlikely(output_conf->output_fn == NULL)) {
1741 *err_str = "Q-setup-output: invalid output function";
1742 goto error_dec_cnt;
1743 }
1744
1745 odp_ticketlock_lock(&em_shm->queue_tbl.output_queue_lock);
1746 for (unsigned int i = 0; i < EM_MAX_OUTPUT_QUEUES; i++) {
1747 if (em_shm->queue_tbl.output_queue_idx_free[i]) {
1748 em_shm->queue_tbl.output_queue_idx_free[i] = false;
1749 q_elem->output.idx = i;
1750 break;
1751 }
1752 }
1753 odp_ticketlock_unlock(&em_shm->queue_tbl.output_queue_lock);
1754
1755 /* copy whole output conf */
1756 q_elem->output.output_conf = *output_conf;
1757 q_elem->output.output_fn_args_event = EM_EVENT_UNDEF;
1758 if (output_conf->args_len == 0) {
1759 /* 'output_fn_args' is ignored, if 'args_len' is 0 */
1760 q_elem->output.output_conf.output_fn_args = NULL;
1761 } else {
1762 em_event_t args_event;
1763 void *args_storage;
1764
1765 /* alloc an event to copy the given fn-args into */
1766 args_event = em_alloc((uint32_t)output_conf->args_len,
1768 if (unlikely(args_event == EM_EVENT_UNDEF)) {
1769 *err_str = "Q-setup-output: alloc output_fn_args fails";
1770 goto error_dec_cnt;
1771 }
1772 /* store the event handle for em_free() later */
1773 q_elem->output.output_fn_args_event = args_event;
1774 args_storage = em_event_pointer(args_event);
1775 memcpy(args_storage, output_conf->output_fn_args,
1776 output_conf->args_len);
1777 /* update the args ptr to point to the copied content */
1778 q_elem->output.output_conf.output_fn_args = args_storage;
1779 }
1780 odp_ticketlock_init(&q_elem->output.lock);
1781
1782 /*
1783 * Set up a plain ODP queue for EM output queue (re-)ordering.
1784 *
1785 * EM output-queues need an odp-queue to ensure re-ordering if
1786 * events are sent into it from within an ordered context.
1787 */
1788 odp_queue_param_t odp_queue_param;
1789 /* Retrieve previously stored ODP queue capabilities */
1790 const odp_queue_capability_t *odp_queue_capa =
1791 &em_shm->queue_tbl.odp_queue_capability;
1792
1793 /* Init odp queue params to default values */
1794 odp_queue_param_init(&odp_queue_param);
1795 /* Set common ODP queue params based on the EM Queue config */
1796 queue_setup_odp_common(param, &odp_queue_param);
1797
1798 odp_queue_param.type = ODP_QUEUE_TYPE_PLAIN;
1799 odp_queue_param.order = ODP_QUEUE_ORDER_KEEP;
1800
1801 /* check nonblocking level against plain queue capabilities */
1802 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_LF &&
1803 odp_queue_capa->plain.lockfree.max_num == 0) {
1804 *err_str = "Q-setup-output: non-blocking, lock-free unsched queues unavailable";
1805 goto error_dec_cnt;
1806 }
1807 if (odp_queue_param.nonblocking == ODP_NONBLOCKING_WF &&
1808 odp_queue_capa->plain.waitfree.max_num == 0) {
1809 *err_str = "Q-setup-output: non-blocking, wait-free unsched queues unavailable";
1810 goto error_dec_cnt;
1811 }
1812
1813 /* output-queue dequeue protected by q_elem->output.lock */
1814 odp_queue_param.deq_mode = ODP_QUEUE_OP_MT_UNSAFE;
1815
1816 /* explicitly show here that output queues should not set odp-context */
1817 odp_queue_param.context = NULL;
1818
1819 int err = create_odp_queue(q_elem, &odp_queue_param);
1820
1821 if (unlikely(err)) {
1822 *err_str = "Q-setup-output: plain odp queue creation failed!";
1823 goto error_dec_cnt;
1824 }
1825
1826 return 0;
1827
1828error_dec_cnt:
1829 /*
1830 * Setup error: decrement the output queue count if it was incremented
1831 * before the error was detected.
1832 */
1833 odp_atomic_dec_u32(&queue_tbl->output_queue_count);
1834 return -1;
1835}
1836
1837/**
1838 * Helper func for queue_state_change() - check that state change is valid
1839 *
1840 * Valid state transitions:
1841 * ---------------------------------
1842 * | |new-state|new-state |
1843 * |old_state|is_setup |is_teardown|
1844 * |---------|---------|-----------|
1845 * |INVALID | INIT | (NULL) |
1846 * |INIT | BIND | INVALID |
1847 * |BIND | READY | INIT |
1848 * |READY | (NULL) | BIND |
1849 * ---------------------------------
1850 * State change check is made easy because the following condition is true
1851 * for valid state transitions: abs(old-new)=1
1852 */
1853em_status_t queue_state_change__check(queue_state_t old_state,
1854 queue_state_t new_state,
1855 int is_setup /* vs. is_teardown */)
1856{
1857 uint32_t state_diff;
1858
1859 if (is_setup)
1860 state_diff = new_state - old_state;
1861 else
1862 state_diff = old_state - new_state;
1863
1864 return (state_diff == 1) ? EM_OK : EM_ERR_BAD_STATE;
1865}
1866
1867static inline em_status_t
1868queue_state_set(queue_elem_t *const q_elem, queue_state_t new_state)
1869{
1870 const queue_state_t old_state = q_elem->state;
1871 const int is_setup = (new_state == EM_QUEUE_STATE_READY);
1872 em_status_t err;
1873
1874 /* allow multiple queue_enable/disable() calls */
1875 if (new_state == old_state &&
1876 (new_state == EM_QUEUE_STATE_READY ||
1877 new_state == EM_QUEUE_STATE_BIND))
1878 return EM_OK;
1879
1880 err = queue_state_change__check(old_state, new_state, is_setup);
1881 if (unlikely(err != EM_OK))
1882 return err;
1883
1884 q_elem->state = new_state;
1885
1886 /* Propagate state change to aggregator child queues (if any) */
1887 if (q_elem->flags.has_aggr) {
1888 uint32_t num = q_elem->aggr_parent.num_aggr;
1889
1890 for (uint32_t i = 0; i < num; i++) {
1891 em_queue_t aq = (em_queue_t)(uintptr_t)q_elem->aggr_parent.aggr_queues[i];
1892 queue_elem_t *aggr_qelem = queue_elem_get(aq);
1893
1894 if (likely(aggr_qelem != NULL))
1895 aggr_qelem->state = new_state;
1896 }
1897 }
1898
1899 return EM_OK;
1900}
1901
1902/**
1903 * Change the queue state
1904 */
1905em_status_t queue_state_change(queue_elem_t *const q_elem, queue_state_t new_state)
1906{
1907 em_status_t err = queue_state_set(q_elem, new_state);
1908
1909 RETURN_ERROR_IF(err != EM_OK, err, EM_ESCOPE_QUEUE_STATE_CHANGE,
1910 "EM-Q:%" PRI_QUEUE " inv. state: %d=>%d",
1911 q_elem->queue, q_elem->state, new_state);
1912 return EM_OK;
1913}
1914
1915/**
1916 * Change the queue state for all queues associated with the given EO
1917 */
1918em_status_t queue_state_change_all(eo_elem_t *const eo_elem, queue_state_t new_state)
1919{
1920 em_status_t err = EM_OK;
1921 queue_elem_t *q_elem = NULL;
1922 const list_node_t *list_node;
1923
1924 /*
1925 * Loop through all queues associated with the EO
1926 */
1927 odp_ticketlock_lock(&eo_elem->lock);
1928
1929 list_for_each(&eo_elem->queue_list, list_node) {
1930 q_elem = eo_queue_node_to_queue_elem(list_node);
1931 err = queue_state_set(q_elem, new_state);
1932 if (unlikely(err != EM_OK))
1933 break;
1934 } /* end loop */
1935
1936 odp_ticketlock_unlock(&eo_elem->lock);
1937
1938 if (unlikely(err != EM_OK)) {
1939 uint32_t queue_u32 = q_elem ? q_elem->queue : 0;
1940 queue_state_t state = q_elem ? q_elem->state : EM_QUEUE_STATE_INVALID;
1941
1942 return INTERNAL_ERROR(err, EM_ESCOPE_QUEUE_STATE_CHANGE,
1943 "EM-Q:%" PRIx32 " inv. state: %d=>%d",
1944 queue_u32, state, new_state);
1945 }
1946
1947 return EM_OK;
1948}
1949
1950/**
1951 * Enable event reception of an EM queue
1952 */
1953em_status_t queue_enable(queue_elem_t *const q_elem)
1954{
1955 em_status_t ret;
1956
1957 RETURN_ERROR_IF(q_elem == NULL || !queue_allocated(q_elem),
1958 EM_ERR_BAD_ID, EM_ESCOPE_QUEUE_ENABLE,
1959 "Invalid queue");
1960
1961 ret = queue_state_change(q_elem, EM_QUEUE_STATE_READY);
1962
1963 RETURN_ERROR_IF(ret != EM_OK, ret, EM_ESCOPE_QUEUE_ENABLE,
1964 "queue_state_change()->READY fails EM-Q:%" PRI_QUEUE "",
1965 q_elem->queue);
1966
1967 return EM_OK;
1968}
1969
1970/**
1971 * Enable event reception of ALL queues belonging to an EO
1972 */
1973em_status_t queue_enable_all(eo_elem_t *const eo_elem)
1974{
1975 em_status_t ret;
1976
1977 RETURN_ERROR_IF(eo_elem == NULL || !eo_allocated(eo_elem),
1978 EM_ERR_BAD_ID, EM_ESCOPE_QUEUE_ENABLE_ALL,
1979 "Invalid EO");
1980
1981 ret = queue_state_change_all(eo_elem, EM_QUEUE_STATE_READY);
1982 RETURN_ERROR_IF(ret != EM_OK, ret, EM_ESCOPE_QUEUE_ENABLE_ALL,
1983 "queue_state_change_all()->READY fails EO:%" PRI_EO "",
1984 eo_elem->eo);
1985
1986 return EM_OK;
1987}
1988
1989/**
1990 * Disable event reception of an EM queue
1991 */
1992em_status_t queue_disable(queue_elem_t *const q_elem)
1993{
1994 em_status_t ret;
1995
1996 RETURN_ERROR_IF(q_elem == NULL || !queue_allocated(q_elem),
1997 EM_ERR_BAD_ID, EM_ESCOPE_QUEUE_DISABLE,
1998 "Invalid queue");
1999
2000 /* Change the state of the queue */
2001 ret = queue_state_change(q_elem, EM_QUEUE_STATE_BIND);
2002 RETURN_ERROR_IF(ret != EM_OK, ret, EM_ESCOPE_QUEUE_DISABLE,
2003 "queue_state_change()->BIND fails, Q:%" PRI_QUEUE "",
2004 q_elem->queue);
2005
2006 return EM_OK;
2007}
2008
2009/**
2010 * Disable event reception of ALL queues belonging to an EO
2011 */
2012em_status_t queue_disable_all(eo_elem_t *const eo_elem)
2013{
2014 em_status_t ret;
2015
2016 RETURN_ERROR_IF(eo_elem == NULL || !eo_allocated(eo_elem),
2017 EM_ERR_BAD_ID, EM_ESCOPE_QUEUE_DISABLE_ALL,
2018 "Invalid EO");
2019
2020 ret = queue_state_change_all(eo_elem, EM_QUEUE_STATE_BIND);
2021 RETURN_ERROR_IF(ret != EM_OK, ret, EM_ESCOPE_QUEUE_DISABLE_ALL,
2022 "queue_state_change_all()->BIND: EO:%" PRI_EO "",
2023 eo_elem->eo);
2024
2025 return EM_OK;
2026}
2027
2028void print_queue_elem_info(void)
2029{
2030 EM_PRINT("queue-elem size: %zu B\n",
2031 sizeof(queue_elem_t));
2032
2033 DBG_PRINT("\t\t\toffset\tsize\n"
2034 "\t\t\t------\t----\n"
2035 "valid_check:\t\t%3zu B\t%2zu B\n"
2036 "flags:\t\t\t%3zu B\t%2zu B\n"
2037 "state:\t\t\t%3zu B\t%2zu B\n"
2038 "priority:\t\t%3zu B\t%2zu B\n"
2039 "type:\t\t\t%3zu B\t%2zu B\n"
2040 "max_events:\t\t%3zu B\t%2zu B\n"
2041 "eo:\t\t\t%3zu B\t%2zu B\n"
2042 "queue:\t\t\t%3zu B\t%2zu B\n"
2043 "odp_queue:\t\t%3zu B\t%2zu B\n"
2044 "context:\t\t%3zu B\t%2zu B\n"
2045 "union {\n"
2046 " rcv_fn:\t\t%3zu B\t%2zu B\n"
2047 " rcv_multi_fn:\t\t%3zu B\t%2zu B\n"
2048 "}\n"
2049 "eo_ctx:\t\t\t%3zu B\t%2zu B\n"
2050 "agrp {\t\t\t%3zu B\t%2zu B\n"
2051 " .atomic_grp:\t\t%3zu B\t%2zu B\n"
2052 " .agrp_node:\t\t%3zu B\t%2zu B\n"
2053 "}\n"
2054 "union {\n"
2055 " aggr_parent {\t\t%3zu B\t%2zu B\n"
2056 " .num_aggr:\t\t%3zu B\t%2zu B\n"
2057 " .aggr_queues[]:\t%3zu B\t%2zu B\n"
2058 " }\n"
2059 " aggr {\t\t%3zu B\t%2zu B\n"
2060 " .parent_queue:\t%3zu B\t%2zu B\n"
2061 " .pool:\t\t%3zu B\t%2zu B\n"
2062 " .max_tmo_ns:\t%3zu B\t%2zu B\n"
2063 " .max_size:\t\t%3zu B\t%2zu B\n"
2064 " .event_type:\t%3zu B\t%2zu B\n"
2065 " }\n"
2066 " output {\t\t%3zu B\t%2zu B\n"
2067 " .conf:\t\t%3zu B\t%2zu B\n"
2068 " .args_event:\t%3zu B\t%2zu B\n"
2069 " .idx:\t\t%3zu B\t%2zu B\n"
2070 " .lock:\t\t%3zu B\t%2zu B\n"
2071 " }\n"
2072 "}\n"
2073 "eo_elem:\t\t%3zu B\t%2zu B\n"
2074 "queue_group:\t\t%3zu B\t%2zu B\n"
2075 "eo_queue_node:\t\t%3zu B\t%2zu B\n"
2076 "qgrp_node:\t\t%3zu B\t%2zu B\n"
2077 "queue_pool_elem:\t%3zu B\t%2zu B\n"
2078 " <pad>\t\t\t%3zu B\n"
2079 "end:\t\t\t%3zu B\t%2zu B\n",
2080 offsetof(queue_elem_t, valid_check), sizeof_field(queue_elem_t, valid_check),
2081 offsetof(queue_elem_t, flags), sizeof_field(queue_elem_t, flags),
2082 offsetof(queue_elem_t, state), sizeof_field(queue_elem_t, state),
2083 offsetof(queue_elem_t, priority), sizeof_field(queue_elem_t, priority),
2084 offsetof(queue_elem_t, type), sizeof_field(queue_elem_t, type),
2085 offsetof(queue_elem_t, max_events), sizeof_field(queue_elem_t, max_events),
2086 offsetof(queue_elem_t, eo), sizeof_field(queue_elem_t, eo),
2087 offsetof(queue_elem_t, queue), sizeof_field(queue_elem_t, queue),
2088 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2089 offsetof(queue_elem_t, odp_queue), sizeof_field(queue_elem_t, odp_queue),
2090 offsetof(queue_elem_t, context), sizeof_field(queue_elem_t, context),
2091 offsetof(queue_elem_t, receive_func),
2092 sizeof_field(queue_elem_t, receive_func),
2093 offsetof(queue_elem_t, receive_multi_func),
2094 sizeof_field(queue_elem_t, receive_multi_func),
2095 offsetof(queue_elem_t, eo_ctx), sizeof_field(queue_elem_t, eo_ctx),
2096 offsetof(queue_elem_t, agrp), sizeof_field(queue_elem_t, agrp),
2097 offsetof(queue_elem_t, agrp.atomic_group),
2098 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2099 sizeof_field(queue_elem_t, agrp.atomic_group),
2100 offsetof(queue_elem_t, agrp.agrp_node),
2101 sizeof_field(queue_elem_t, agrp.agrp_node),
2102 offsetof(queue_elem_t, aggr_parent), sizeof_field(queue_elem_t, aggr_parent),
2103 offsetof(queue_elem_t, aggr_parent.num_aggr),
2104 sizeof_field(queue_elem_t, aggr_parent.num_aggr),
2105 offsetof(queue_elem_t, aggr_parent.aggr_queues),
2106 sizeof_field(queue_elem_t, aggr_parent.aggr_queues),
2107 offsetof(queue_elem_t, aggr), sizeof_field(queue_elem_t, aggr),
2108 offsetof(queue_elem_t, aggr.parent_queue),
2109 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2110 sizeof_field(queue_elem_t, aggr.parent_queue),
2111 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2112 offsetof(queue_elem_t, aggr.pool), sizeof_field(queue_elem_t, aggr.pool),
2113 offsetof(queue_elem_t, aggr.max_tmo_ns),
2114 sizeof_field(queue_elem_t, aggr.max_tmo_ns),
2115 offsetof(queue_elem_t, aggr.max_size), sizeof_field(queue_elem_t, aggr.max_size),
2116 offsetof(queue_elem_t, aggr.event_type),
2117 sizeof_field(queue_elem_t, aggr.event_type),
2118 offsetof(queue_elem_t, output), sizeof_field(queue_elem_t, output),
2119 offsetof(queue_elem_t, output.output_conf),
2120 sizeof_field(queue_elem_t, output.output_conf),
2121 offsetof(queue_elem_t, output.output_fn_args_event),
2122 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2123 sizeof_field(queue_elem_t, output.output_fn_args_event),
2124 offsetof(queue_elem_t, output.idx), sizeof_field(queue_elem_t, output.idx),
2125 offsetof(queue_elem_t, output.lock), sizeof_field(queue_elem_t, output.lock),
2126 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2127 offsetof(queue_elem_t, eo_elem), sizeof_field(queue_elem_t, eo_elem),
2128 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
2129 offsetof(queue_elem_t, queue_group), sizeof_field(queue_elem_t, queue_group),
2130 offsetof(queue_elem_t, eo_queue_node), sizeof_field(queue_elem_t, eo_queue_node),
2131 offsetof(queue_elem_t, qgrp_node), sizeof_field(queue_elem_t, qgrp_node),
2132 offsetof(queue_elem_t, queue_pool_elem),
2133 sizeof_field(queue_elem_t, queue_pool_elem),
2134 offsetof(queue_elem_t, end) - offsetof(queue_elem_t, end_data),
2135 offsetof(queue_elem_t, end), sizeof_field(queue_elem_t, end));
2136
2137 EM_PRINT("\n");
2138}
2139
2140/*
2141 * Format a queue-ID range as decimal and hex strings for printing.
2142 * Buffers are left unchanged (caller pre-fills with "n/a") when the range
2143 * is not configured (num_queues == 0) or the ID equals INVALID_QUEUE_ID.
2144 */
2145static void
2146format_qid_range(unsigned int num_queues, uint16_t first_id, uint16_t last_id,
2147 char first_dec[24], char last_dec[24],
2148 char first_hex[24], char last_hex[24])
2149{
2150 if (!num_queues)
2151 return;
2152 if (first_id != INVALID_QUEUE_ID) {
2153 snprintf(first_dec, 24, "%u", first_id);
2154 snprintf(first_hex, 24, "0x%x", first_id);
2155 }
2156 if (last_id != INVALID_QUEUE_ID) {
2157 snprintf(last_dec, 24, "%u", last_id);
2158 snprintf(last_hex, 24, "0x%x", last_id);
2159 }
2160}
2161
2162void print_queue_capa(void)
2163{
2164 const odp_queue_capability_t *queue_capa =
2165 &em_shm->queue_tbl.odp_queue_capability;
2166 const odp_schedule_capability_t *sched_capa =
2167 &em_shm->queue_tbl.odp_schedule_capability;
2168 const queue_tbl_t *queue_tbl = &em_shm->queue_tbl;
2169 char plain_sz[24] = "n/a";
2170 char plain_lf_sz[24] = "n/a";
2171 char plain_wf_sz[24] = "n/a";
2172 char sched_sz[24] = "nolimit";
2173
2174 char first_static_qid[24] = "n/a";
2175 char last_static_qid[24] = "n/a";
2176 char first_dyn_qid[24] = "n/a";
2177 char last_dyn_qid[24] = "n/a";
2178 char first_aggr_qid[24] = "n/a";
2179 char last_aggr_qid[24] = "n/a";
2180 char first_static_qid_hex[24] = "n/a";
2181 char last_static_qid_hex[24] = "n/a";
2182 char first_dyn_qid_hex[24] = "n/a";
2183 char last_dyn_qid_hex[24] = "n/a";
2184 char first_aggr_qid_hex[24] = "n/a";
2185 char last_aggr_qid_hex[24] = "n/a";
2186
2187 const unsigned int max_queues = em_shm->queue_tbl.max_queue_num;
2188
2189 if (queue_capa->plain.max_size > 0)
2190 snprintf(plain_sz, sizeof(plain_sz), "%u",
2191 queue_capa->plain.max_size);
2192 if (queue_capa->plain.lockfree.max_size > 0)
2193 snprintf(plain_lf_sz, sizeof(plain_lf_sz), "%u",
2194 queue_capa->plain.lockfree.max_size);
2195 if (queue_capa->plain.waitfree.max_size > 0)
2196 snprintf(plain_wf_sz, sizeof(plain_wf_sz), "%u",
2197 queue_capa->plain.waitfree.max_size);
2198
2199 if (sched_capa->max_queue_size > 0)
2200 snprintf(sched_sz, sizeof(sched_sz), "%u",
2201 sched_capa->max_queue_size);
2202
2203 format_qid_range(em_shm->opt.queue.num_static,
2204 queue_tbl->first_static_queue_id,
2205 queue_tbl->last_static_queue_id,
2206 first_static_qid, last_static_qid,
2207 first_static_qid_hex, last_static_qid_hex);
2208
2209 format_qid_range(em_shm->opt.queue.num_dynamic,
2210 queue_tbl->first_dyn_queue_id,
2211 queue_tbl->last_dyn_queue_id,
2212 first_dyn_qid, last_dyn_qid,
2213 first_dyn_qid_hex, last_dyn_qid_hex);
2214
2215 format_qid_range(em_shm->opt.queue.num_aggr,
2216 queue_tbl->first_aggr_queue_id,
2217 queue_tbl->last_aggr_queue_id,
2218 first_aggr_qid, last_aggr_qid,
2219 first_aggr_qid_hex, last_aggr_qid_hex);
2220 /* snprintf() has '\0'-terminated all strings above */
2221
2222 /* The first EM queue id depends if static queues are configured or not */
2223 uint16_t queue_range_first = em_shm->opt.queue.num_static ?
2224 queue_tbl->first_static_queue_id :
2225 queue_tbl->first_internal_queue_id;
2226
2227 /* The last EM queue id depends on which queue ranges are configured */
2228 uint16_t queue_range_last = queue_tbl->last_internal_queue_id;
2229
2230 if (em_shm->opt.queue.num_dynamic)
2231 queue_range_last = queue_tbl->last_dyn_queue_id;
2232 if (em_shm->opt.queue.num_aggr)
2233 queue_range_last = queue_tbl->last_aggr_queue_id;
2234
2235 EM_PRINT("ODP Queue Capabilities\n"
2236 "----------------------\n"
2237 " Max number of ODP queues: %u\n"
2238 " Max number of ODP ordered locks per queue: %u\n"
2239 " Max number of ODP scheduling groups: %u\n"
2240 " Max number of ODP scheduling priorities: %u\n"
2241 " PLAIN queues:\n"
2242 " blocking: count: %6u size: %6s\n"
2243 " nonblocking-lf: count: %6u size: %6s\n"
2244 " nonblocking-wf: count: %6u size: %6s\n"
2245 " SCHED queues:\n"
2246 " blocking: count: %6u size: %6s\n"
2247 " nonblocking-lf: %ssupported\n"
2248 " nonblocking-wf: %ssupported\n\n",
2249 queue_capa->max_queues, sched_capa->max_ordered_locks,
2250 sched_capa->max_groups, sched_capa->max_prios,
2251 queue_capa->plain.max_num, plain_sz,
2252 queue_capa->plain.lockfree.max_num, plain_lf_sz,
2253 queue_capa->plain.waitfree.max_num, plain_wf_sz,
2254 sched_capa->max_queues, sched_sz,
2255 sched_capa->lockfree_queues == ODP_SUPPORT_NO ? "not " : "",
2256 sched_capa->waitfree_queues == ODP_SUPPORT_NO ? "not " : "");
2257
2258 EM_PRINT("EM Queues\n"
2259 "---------\n"
2260 " Max number of EM queues: %d (0x%x)\n"
2261 " EM queue handle offset: %d (0x%x)\n"
2262 " EM queue range: [%u - %u] ([0x%x - 0x%x])\n"
2263 " static range: [%s - %s] ([%s - %s])\n"
2264 " internal range: [%d - %d] ([0x%x - 0x%x])\n"
2265 " dynamic range: [%s - %s] ([%s - %s])\n"
2266 " aggreg. range: [%s - %s] ([%s - %s])\n"
2267 "\n",
2268 max_queues, max_queues,
2270 queue_range_first, queue_range_last,
2271 queue_range_first, queue_range_last,
2272 first_static_qid, last_static_qid,
2273 first_static_qid_hex, last_static_qid_hex,
2274 queue_tbl->first_internal_queue_id, queue_tbl->last_internal_queue_id,
2275 queue_tbl->first_internal_queue_id, queue_tbl->last_internal_queue_id,
2276 first_dyn_qid, last_dyn_qid,
2277 first_dyn_qid_hex, last_dyn_qid_hex,
2278 first_aggr_qid, last_aggr_qid,
2279 first_aggr_qid_hex, last_aggr_qid_hex);
2280}
2281
2282void print_queue_prio_info(void)
2283{
2284 #define MAXPRIOBUF 128
2285 char buf[MAXPRIOBUF];
2286 int pos = 0;
2287
2288 for (int i = 0; i < EM_QUEUE_PRIO_NUM; i++) {
2289 /* comma separated list of priorities */
2290 int num = snprintf(&buf[pos], MAXPRIOBUF - pos, "%d%c",
2291 em_shm->queue_prio.map[i],
2292 i < (EM_QUEUE_PRIO_NUM - 1) ? ',' : '\0');
2293 if (num < 0 || num >= (MAXPRIOBUF - pos))
2294 break;
2295 pos += num;
2296 }
2297
2298 buf[MAXPRIOBUF - 1] = 0;
2299 EM_PRINT(" Current queue priority map: [%s]\n", buf);
2300}
2301
2302unsigned int queue_count(void)
2303{
2304 return odp_atomic_load_u32(&em_shm->queue_count);
2305}
2306
2307size_t queue_name(const queue_elem_t *const q_elem,
2308 char name[/*out*/], const size_t maxlen)
2309{
2310 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
2311 const char *queue_name = &em_shm->queue_tbl.name[queue_hdl2idx(queue)][0];
2312 size_t len = strnlen(queue_name, EM_QUEUE_NAME_LEN - 1);
2313
2314 if (maxlen - 1 < len)
2315 len = maxlen - 1;
2316
2317 if (len)
2318 memcpy(name, queue_name, len);
2319 name[len] = '\0';
2320
2321 return len;
2322}
2323
2324static void queue_init_prio_legacy(int minp, int maxp)
2325{
2326 /* legacy mode - match the previous simple 3-level implementation */
2327
2328 int def = odp_schedule_default_prio();
2329
2330 /* needs to be synced with queue_prio_e values. Due to enum this can't be #if */
2331 COMPILE_TIME_ASSERT(EM_QUEUE_PRIO_HIGHEST < EM_QUEUE_PRIO_NUM,
2332 "queue_prio_e values / EM_QUEUE_PRIO_NUM mismatch!\n");
2333
2334 /* init both ends first */
2335 for (int i = 0; i < EM_QUEUE_PRIO_NUM; i++)
2336 em_shm->queue_prio.map[i] = i < (EM_QUEUE_PRIO_NUM / 2) ? minp : maxp;
2337
2338 /* then add NORMAL in the middle */
2340 /* if room: widen the normal range a bit */
2341 if (EM_QUEUE_PRIO_NORMAL - EM_QUEUE_PRIO_LOW > 1) /* legacy 4-2 */
2343 if (EM_QUEUE_PRIO_HIGH - EM_QUEUE_PRIO_NORMAL > 1) /* legacy 6-4 */
2345}
2346
2347static void queue_init_prio_adaptive(int minp, int maxp, int nump)
2348{
2349 double step = (double)nump / EM_QUEUE_PRIO_NUM;
2350 double cur = (double)minp;
2351
2352 /* simple linear fit to available levels */
2353
2354 for (int i = 0; i < EM_QUEUE_PRIO_NUM; i++) {
2355 em_shm->queue_prio.map[i] = (int)cur;
2356 cur += step;
2357 }
2358
2359 /* last EM prio always highest ODP level */
2360 if (em_shm->queue_prio.map[EM_QUEUE_PRIO_NUM - 1] != maxp)
2362}
2363
2364static int queue_init_prio_custom(int minp, int maxp)
2365{
2366 for (int i = 0; i < EM_QUEUE_PRIO_NUM; i++) {
2367 em_shm->queue_prio.map[i] = minp + em_shm->opt.queue.priority.custom_map[i];
2368 if (em_shm->queue_prio.map[i] > maxp || em_shm->queue_prio.map[i] < minp) {
2369 EM_PRINT("Invalid odp priority %d!\n", em_shm->queue_prio.map[i]);
2370 return -1;
2371 }
2372 }
2373 return 0;
2374}
2375
2376static int queue_init_prio_map(int minp, int maxp, int nump)
2377{
2378 /* EM normally uses 8 priority levels (EM_QUEUE_PRIO_NUM).
2379 * These are mapped to ODP runtime values depending on selected map mode
2380 */
2381
2382 switch (em_shm->opt.queue.priority.map_mode) {
2383 case 0: /* legacy mode, use only 3 levels */
2384 queue_init_prio_legacy(minp, maxp);
2385 break;
2386 case 1: /* adapt to runtime (full spread) */
2387 queue_init_prio_adaptive(minp, maxp, nump);
2388 break;
2389 case 2: /** custom */
2390 if (queue_init_prio_custom(minp, maxp) != 0)
2391 return -1;
2392 break;
2393 default:
2394 EM_PRINT("Unknown map_mode %d!\n", em_shm->opt.queue.priority.map_mode);
2395 return -1;
2396 }
2397
2398 EM_PRINT(" EM uses %d priorities, runtime %d (%d-%d)\n",
2399 EM_QUEUE_PRIO_NUM, nump, minp, nump - minp - 1);
2400 print_queue_prio_info();
2401 return 0;
2402}
2403
2404const char *queue_state_str(queue_state_t state)
2405{
2406 const char *str;
2407
2408 switch (state) {
2410 str = "INVALID";
2411 break;
2413 str = "INIT";
2414 break;
2416 str = "BIND";
2417 break;
2419 str = "READY";
2420 break;
2421 case EM_QUEUE_STATE_UNSCHEDULED:
2422 str = "UNSCH";
2423 break;
2424 default:
2425 str = "UNKNOWN";
2426 break;
2427 }
2428
2429 return str;
2430}
2431
2432const char *queue_type_str(em_queue_type_t type)
2433{
2434 const char *type_str;
2435
2436 switch (type) {
2438 type_str = "UNDEF";
2439 break;
2441 type_str = "ATOMIC";
2442 break;
2444 type_str = "PARALLEL";
2445 break;
2447 type_str = "ORDERED";
2448 break;
2450 type_str = "UNSCH";
2451 break;
2453 type_str = "LOCAL";
2454 break;
2456 type_str = "OUTPUT";
2457 break;
2458 default:
2459 type_str = "UNKNOWN";
2460 break;
2461 }
2462
2463 return type_str;
2464}
2465
2466#define QUEUE_INFO_HDR_STR \
2467"Number of queues: %d\n\n" \
2468"Handle Name Priority Type State Qgrp" \
2469" Agrp EO Multi-rcv Max-events Ctx\n" \
2470"---------------------------------------------------------------------------" \
2471"----------------------------------------------------\n" \
2472"%s\n"
2473
2474#define QUEUE_INFO_LEN 128
2475
2476#define QUEUE_INFO_FMT \
2477"%-10" PRI_QUEUE "%-32s%-10" PRI_QPRIO "%-10s%-9s%-10" PRI_QGRP "%-10" PRI_AGRP \
2478"%-10" PRI_EO "%-11c%-12d%-3c\n" /*128 bytes per queue*/
2479
2480void print_queue_info(void)
2481{
2482 unsigned int q_num;
2483 const queue_elem_t *q_elem;
2484 char q_name[EM_QUEUE_NAME_LEN];
2485 int len = 0;
2486 int n_print = 0;
2487
2488 em_queue_t q = em_queue_first(&q_num);
2489
2490 /* q_num may not match the amount of queues actually returned by iterating
2491 * using em_queue_next() if queues are added or removed in parallel
2492 * by another core. Thus space for 10 extra queues is reserved. If more
2493 * than 10 queues are added by other cores in parallel, we print only info
2494 * of the (q_num + 10) queues.
2495 */
2496 const int q_info_buf_len = (q_num + 10) * QUEUE_INFO_LEN + 1/*Terminating null byte*/;
2497 char q_info_buf[q_info_buf_len];
2498
2499 while (q != EM_QUEUE_UNDEF) {
2500 q_elem = queue_elem_get(q);
2501
2502 if (unlikely(q_elem == NULL || !queue_allocated(q_elem))) {
2503 q = em_queue_next();
2504 continue;
2505 }
2506
2507 em_atomic_group_t atomic_group = EM_ATOMIC_GROUP_UNDEF;
2508 em_eo_t eo = (em_eo_t)(uintptr_t)q_elem->eo;
2509
2510 if (q_elem->flags.in_atomic_group)
2511 atomic_group = q_elem->agrp.atomic_group;
2512
2513 queue_name(q_elem, q_name, EM_QUEUE_NAME_LEN - 1);
2514 n_print = snprintf(q_info_buf + len,
2515 q_info_buf_len - len,
2516 QUEUE_INFO_FMT,
2517 q, q_name, q_elem->priority,
2518 queue_type_str(q_elem->type),
2519 queue_state_str(q_elem->state),
2520 q_elem->queue_group, atomic_group, eo,
2521 q_elem->flags.use_multi_rcv ? 'Y' : 'N',
2522 q_elem->max_events,
2523 q_elem->context ? 'Y' : 'N');
2524
2525 /* Not enough space to hold more queue info */
2526 if (n_print >= q_info_buf_len - len)
2527 break;
2528
2529 len += n_print;
2530 q = em_queue_next();
2531 }
2532
2533 /* No queue */
2534 if (len == 0) {
2535 EM_PRINT("No EM queue!\n");
2536 return;
2537 }
2538
2539 /*
2540 * To prevent printing incomplete information of the last queue when
2541 * there is not enough space to hold all queue info.
2542 */
2543 q_info_buf[len] = '\0';
2544 EM_PRINT(QUEUE_INFO_HDR_STR, q_num, q_info_buf);
2545}
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
#define RETURN_ERROR_IF(cond, error, escope, fmt,...)
Definition em_error.h:65
#define EM_CHECK_INIT_CALLED
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
uint8_t queue_state_t
@ EM_QUEUE_STATE_READY
@ EM_QUEUE_STATE_INIT
@ EM_QUEUE_STATE_INVALID
@ EM_QUEUE_STATE_BIND
#define EM_QUEUE_LOCAL_MULTI_MAX_BURST
#define EM_QUEUE_MAX_AGGR
#define EM_QUEUE_PRIO_NUM
#define EM_QUEUE_NAME_LEN
#define EM_MAX_OUTPUT_QUEUES
#define EM_POOL_DEFAULT
#define EM_QUEUE_RANGE_OFFSET
#define PRI_QUEUE
#define PRI_EO
#define EM_QUEUE_GROUP_UNDEF
uint32_t em_event_type_t
#define EM_POOL_UNDEF
#define EM_EVENT_UNDEF
#define EM_ATOMIC_GROUP_UNDEF
#define EM_QUEUE_UNDEF
#define EM_EO_UNDEF
int em_core_id(void)
#define EM_OK
uint32_t em_status_t
@ EM_ERR_BAD_ID
@ EM_ERR_ALLOC_FAILED
@ EM_ERR_NOT_SUPPORTED
@ EM_ERR_BAD_STATE
@ EM_ERR_LIB_FAILED
em_event_t em_alloc(uint32_t size, em_event_type_t type, em_pool_t pool)
void em_free_multi(em_event_t events[], int num)
void em_free(em_event_t event)
void * em_event_pointer(em_event_t event)
@ EM_EVENT_TYPE_SW
@ EM_EVENT_TYPE_UNDEF
@ EM_EVENT_TYPE_ODP
@ EM_EVENT_TYPE_PACKET
@ EM_EVENT_TYPE_TIMER_IND
@ EM_EVENT_TYPE_ANY
@ EM_EVENT_TYPE_TIMER
@ EM_EVENT_TYPE_CRYPTO
@ EM_EVENT_TYPE_VECTOR
#define EM_QUEUE_FLAG_ENQ_NOT_MTSAFE
#define EM_QUEUE_PRIO_UNDEF
uint32_t em_queue_type_t
#define EM_QUEUE_FLAG_MASK
#define EM_QUEUE_FLAG_NONBLOCKING_WF
#define EM_QUEUE_FLAG_DEFAULT
#define EM_QUEUE_FLAG_DEQ_NOT_MTSAFE
#define EM_QUEUE_FLAG_NONBLOCKING_LF
em_queue_t em_queue_first(unsigned int *num)
uint32_t em_queue_flag_t
em_queue_t em_queue_next(void)
@ EM_QUEUE_TYPE_AGGR
@ EM_QUEUE_TYPE_ORDERED
@ EM_QUEUE_TYPE_ATOMIC
@ EM_QUEUE_TYPE_UNSCHEDULED
@ EM_QUEUE_TYPE_PARALLEL
@ EM_QUEUE_TYPE_UNDEF
@ EM_QUEUE_TYPE_LOCAL
@ EM_QUEUE_TYPE_OUTPUT
@ EM_QUEUE_PRIO_LOW
@ EM_QUEUE_PRIO_NORMAL
@ EM_QUEUE_PRIO_HIGHEST
@ EM_QUEUE_PRIO_HIGH
#define OBJSUBPOOLS_MAX
Definition objpool.h:62
local_queues_t local_queues
Definition em_mem.h:241
output_queue_track_t output_queue_track
Definition em_mem.h:292
em_queue_flag_t flags
em_queue_aggr_conf_t * aggr_conf
em_output_queue_conf_t output_conf
em_queue_flag_t flags
em_atomic_group_t atomic_group
em_queue_group_t queue_group
struct em_shm_t::@53 queue_prio
int map[EM_QUEUE_PRIO_NUM]
Definition em_mem.h:190
odp_atomic_u32_t queue_count
Definition em_mem.h:175
em_conf_t conf
Definition em_mem.h:93
em_cfgfile_opts_t opt
Definition em_mem.h:99
odp_ticketlock_t lock
Definition em_eo_types.h:86
list_node_t queue_list
Definition em_eo_types.h:88
em_eo_t eo
Definition em_eo_types.h:94
odp_pool_t odp_pool[EM_MAX_SUBPOOLS]
em_event_type_t event_type
uint32_t subpool_idx
Definition objpool.h:74
uint64_t max_tmo_ns
uint32_t max_size
em_queue_t parent_queue
em_event_type_t event_type
uint32_t aggr_queues[EM_QUEUE_MAX_AGGR]
em_atomic_group_t atomic_group
odp_ticketlock_t lock
em_output_queue_conf_t output_conf
em_event_t output_fn_args_event
odp_queue_t odp_queue
uint16_t max_events
queue_state_t state
uint16_t valid_check
q_elem_atomic_group_t agrp
objpool_elem_t queue_pool_elem
em_receive_multi_func_t receive_multi_func
em_queue_group_t queue_group
queue_elem_flags_t flags
em_receive_func_t receive_func
eo_elem_t * eo_elem
odp_schedule_group_t odp_sched_group
uint16_t last_internal_queue_id
uint16_t shared_internal_queue_id
odp_schedule_capability_t odp_schedule_capability
odp_queue_capability_t odp_queue_capability
uint16_t first_aggr_queue_id
uint16_t first_internal_queue_id
char(* name)[EM_QUEUE_NAME_LEN]
bool output_queue_idx_free[EM_MAX_OUTPUT_QUEUES]
uint16_t last_static_queue_id
uint16_t first_dyn_queue_id
uint16_t last_aggr_queue_id
uint16_t first_static_queue_id
uint16_t last_dyn_queue_id