EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_queue_group.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 <stdint.h>
41#include <stdio.h>
42#include <string.h>
43
44#include <odp_api.h>
45
46#include <event_machine.h>
49
50#include "em_core.h"
51#include "em_core_types.h"
52#include "em_error.h"
53#include "em_event_group.h"
54#include "em_init.h"
55#include "em_internal_event.h"
57#include "em_libconfig.h"
58#include "em_mem.h"
59#include "em_queue.h"
60#include "em_queue_group.h"
62#include "em_queue_inline.h"
63#include "em_queue_types.h"
64#include "misc/list.h"
65#include "misc/objpool.h"
66
67/**
68 * em_queue_group_modify() triggers an internal 'Done'-notification event
69 * that updates the queue group mask. This struct contains the callback args.
70 */
71typedef struct {
72 queue_group_elem_t *qgrp_elem;
73 em_core_mask_t new_mask;
75
76/*
77 * Assert that the value of EM_MAX_QUEUE_GROUPS is large enough to support
78 * per-core queue groups (up to EM_MAX_CORES), the default queue group
79 * as well as potential further queue groups the user might want to create.
80 */
81COMPILE_TIME_ASSERT(EM_MAX_QUEUE_GROUPS > EM_MAX_CORES,
82 EM_MAX_QUEUE_GROUPS__TOO_SMALL);
83
84static em_status_t core_queue_group_create(void);
85static em_status_t core_queue_group_join(void);
86static em_queue_group_t default_queue_group_create(void);
87static em_queue_group_t default_queue_group_join(void);
88
89static em_queue_group_t
90queue_group_create_escope(const char *name, const em_core_mask_t *mask,
91 int num_notif, const em_notif_t notif_tbl[],
92 em_queue_group_t requested_queue_group,
93 em_escope_t escope);
94
95static void q_grp_add_core(const queue_group_elem_t *qgrp_elem);
96static void q_grp_rem_core(const queue_group_elem_t *qgrp_elem);
97
98static void q_grp_create_done_callback(void *arg_ptr);
99static void q_grp_create_sync_done_callback(void *arg_ptr);
100static void q_grp_create_done(const queue_group_elem_t *const qgrp_elem,
101 const em_core_mask_t *const new_mask);
102static void q_grp_create_sync_done(const queue_group_elem_t *const qgrp_elem,
103 const em_core_mask_t *const new_mask);
104
105static void q_grp_modify_done_callback(void *arg_ptr);
106static void q_grp_modify_sync_done_callback(void *arg_ptr);
107static void q_grp_modify_done(const queue_group_elem_t *const qgrp_elem,
108 const em_core_mask_t *const new_mask);
109
110static void q_grp_delete_done_callback(void *arg_ptr);
111static void q_grp_delete_sync_done_callback(void *arg_ptr);
112static void q_grp_delete_done(queue_group_elem_t *const qgrp_elem,
113 const em_core_mask_t *const new_mask);
114
115static em_status_t
116send_qgrp_addrem_reqs(queue_group_elem_t *qgrp_elem,
117 const em_core_mask_t *new_mask,
118 const em_core_mask_t *add_mask,
119 const em_core_mask_t *rem_mask,
120 int num_notif, const em_notif_t notif_tbl[],
121 em_escope_t escope);
122
123/**
124 * Return the queue group elem that includes the given objpool_elem_t
125 */
126static inline queue_group_elem_t *
127queue_group_poolelem2qgrpelem(objpool_elem_t *const queue_group_pool_elem)
128{
129 return (queue_group_elem_t *)((uintptr_t)queue_group_pool_elem -
130 offsetof(queue_group_elem_t, queue_group_pool_elem));
131}
132
133static int
134read_config_file(void)
135{
136 const char *conf_str;
137 bool val_bool = false;
138 int ret;
139
140 EM_PRINT("EM queue group config:\n");
141
142 /*
143 * Option: queue_group.create_core_queue_groups
144 */
145 conf_str = "queue_group.create_core_queue_groups";
146 ret = em_libconfig_lookup_bool(&em_shm->libconfig, conf_str, &val_bool);
147 if (unlikely(!ret)) {
148 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
149 return -1;
150 }
151 /* store & print the value */
152 em_shm->opt.queue_group.create_core_queue_groups = val_bool;
153 EM_PRINT(" %s: %s(%d)\n", conf_str, val_bool ? "true" : "false",
154 val_bool);
155
156 /*
157 * Option: queue_group.term_local_flush_orphans
158 */
159 conf_str = "queue_group.term_local_flush_orphans";
160 ret = em_libconfig_lookup_bool(&em_shm->libconfig, conf_str, &val_bool);
161 if (unlikely(!ret)) {
162 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
163 return -1;
164 }
165 /* store & print the value */
166 em_shm->opt.queue_group.term_local_flush_orphans = val_bool;
167 EM_PRINT(" %s: %s(%d)\n", conf_str, val_bool ? "true" : "false",
168 val_bool);
169
170 return 0;
171}
172
173/**
174 * Queue group inits done at global init (once at startup on one core)
175 */
176em_status_t queue_group_init(queue_group_tbl_t *const queue_group_tbl,
177 queue_group_pool_t *const queue_group_pool)
178{
179 const uint32_t num_subpools = MIN(4, OBJSUBPOOLS_MAX);
180 queue_group_elem_t *queue_group_elem;
181 int ret;
182
183 if (read_config_file())
184 return EM_ERR_LIB_FAILED;
185
186 memset(queue_group_tbl, 0, sizeof(queue_group_tbl_t));
187 memset(queue_group_pool, 0, sizeof(queue_group_pool_t));
188 odp_atomic_init_u32(&em_shm->queue_group_count, 0);
189
190 for (int i = 0; i < EM_MAX_QUEUE_GROUPS; i++) {
191 queue_group_elem = &queue_group_tbl->queue_group_elem[i];
192 queue_group_elem->queue_group = qgrp_idx2hdl(i);
193 /* Initialize empty queue list */
194 odp_ticketlock_init(&queue_group_elem->lock);
195 list_init(&queue_group_elem->queue_list);
196 }
197
198 /*
199 * objpool for core specific queue group elems (1 -> EM_MAX_CORES)
200 */
201 ret = objpool_init(&queue_group_pool->objpool_rsvd, num_subpools);
202 if (ret != 0)
203 return EM_ERR_LIB_FAILED;
204
205 for (uint32_t i = 0; i < EM_MAX_CORES; i++) {
206 queue_group_elem = &queue_group_tbl->queue_group_elem[i];
207 objpool_add(&queue_group_pool->objpool_rsvd, i % num_subpools,
208 &queue_group_elem->queue_group_pool_elem);
209 }
210
211 /*
212 * objpool for the rest of the queue group elems
213 * (EM_MAX_CORES + 1 -> EM_MAX_QUEUE_GROUPS)
214 */
215 ret = objpool_init(&queue_group_pool->objpool_dyn, num_subpools);
216 if (ret != 0)
217 return EM_ERR_LIB_FAILED;
218
219 for (uint32_t i = EM_MAX_CORES; i < EM_MAX_QUEUE_GROUPS; i++) {
220 queue_group_elem = &queue_group_tbl->queue_group_elem[i];
221 objpool_add(&queue_group_pool->objpool_dyn, i % num_subpools,
222 &queue_group_elem->queue_group_pool_elem);
223 }
224
225 /*
226 * Create the EM default queue group: EM_QUEUE_GROUP_DEFAULT, "default"
227 */
228 em_queue_group_t default_queue_group = default_queue_group_create();
229
230 if (default_queue_group != EM_QUEUE_GROUP_DEFAULT) {
231 EM_LOG(EM_LOG_ERR, "default_queue_group_create() failed!\n");
232 return EM_ERR_LIB_FAILED;
233 }
234
235 return EM_OK;
236}
237
238em_status_t queue_group_init_local(void)
239{
240 /*
241 * Update the EM default queue group with this cores information
242 */
243 em_queue_group_t def_qgrp = default_queue_group_join();
244
245 if (def_qgrp != EM_QUEUE_GROUP_DEFAULT) {
246 EM_LOG(EM_LOG_ERR, "default_queue_group_join() failed!\n");
247 return EM_ERR_LIB_FAILED;
248 }
249
250 /*
251 * Create EM single-core queue group if enabled by config.
252 */
253 if (em_shm->opt.queue_group.create_core_queue_groups) {
254 em_status_t stat = core_queue_group_create();
255
256 if (stat != EM_OK) {
257 EM_LOG(EM_LOG_ERR, "core_queue_group_create():%" PRI_STAT "\n", stat);
258 return stat;
259 }
260
261 /*
262 * Update the single-core queue group with this core's information
263 */
264 stat = core_queue_group_join();
265
266 if (stat != EM_OK) {
267 EM_LOG(EM_LOG_ERR, "core_queue_group_join():%" PRI_STAT "\n", stat);
268 return stat;
269 }
270 }
271
272 return EM_OK;
273}
274
275em_status_t queue_group_term(void)
276{
277 em_status_t stat = EM_OK;
278 em_queue_group_t qgrp = em_queue_group_first(NULL);
279
280 /*
281 * Destroy all ODP schedule groups to prevent resource leaks
282 * and allow proper re-initialization.
283 */
284 while (qgrp != EM_QUEUE_GROUP_UNDEF) {
285 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
286
287 if (unlikely(!qgrp_elem)) {
288 EM_LOG(EM_LOG_ERR, "%s(): qgrp_elem NULL\n", __func__);
289 continue;
290 }
291
292 if (unlikely(qgrp_elem->ongoing_delete)) {
293 stat = EM_ERR_BAD_STATE;
294 EM_LOG(EM_LOG_ERR,
295 "Bad state qgrp:%" PRI_QGRP " delete is ongoing\n", qgrp);
296 /* Continue cleanup even if delete is ongoing */
297 }
298
299 if (unlikely(odp_ticketlock_is_locked(&qgrp_elem->lock))) {
300 stat = EM_ERR_BAD_STATE;
301 EM_LOG(EM_LOG_ERR, "Bad state qgrp:%" PRI_QGRP " - lock is locked\n", qgrp);
302 /* Continue cleanup even if lock is locked */
303 }
304
305 if (qgrp_elem->odp_sched_group != ODP_SCHED_GROUP_INVALID) {
306 int ret = odp_schedule_group_destroy(qgrp_elem->odp_sched_group);
307
308 if (unlikely(ret != 0)) {
309 EM_LOG(EM_LOG_ERR,
310 "ODP schedule group destroy failed qgrp:%"
311 PRI_QGRP " ret:%d\n", qgrp, ret);
312 stat = EM_ERR_LIB_FAILED;
313 /* Continue cleanup even if destroy fails */
314 }
315
316 qgrp_elem->odp_sched_group = ODP_SCHED_GROUP_INVALID;
317 }
318
319 qgrp = em_queue_group_next();
320 }
321
322 return stat;
323}
324
325/**
326 * Allocate a new EM queue group
327 *
328 * @param queue_group EM queue group handle if a specific EM queue group is
329 * requested, EM_QUEUE_GROUP_UNDEF if any EM queue group
330 * will do.
331 *
332 * @return EM queue group handle
333 * @retval EM_QUEUE_GROUP_UNDEF on failure
334 */
335static em_queue_group_t
336queue_group_alloc(em_queue_group_t queue_group)
337{
338 queue_group_elem_t *qgrp_elem;
339 objpool_elem_t *qgrp_pool_elem;
340 queue_group_pool_t *const qgrp_pools = &em_shm->queue_group_pool;
341
342 if (queue_group == EM_QUEUE_GROUP_UNDEF) {
343 /*
344 * Allocate any queue group, i.e. take next available
345 */
346 int core = em_core_id();
347
348 qgrp_pool_elem = objpool_rem(&qgrp_pools->objpool_dyn, core);
349
350 if (unlikely(qgrp_pool_elem == NULL)) {
351 /* try the core specific queue group pool next */
352 qgrp_pool_elem = objpool_rem(&qgrp_pools->objpool_rsvd, core);
353 if (unlikely(qgrp_pool_elem == NULL))
355 }
356
357 qgrp_elem = queue_group_poolelem2qgrpelem(qgrp_pool_elem);
358 } else {
359 /*
360 * Allocate a specific queue group, handle given as argument
361 */
362 objpool_t *const objpool = qgrp_hdl2idx(queue_group) < EM_MAX_CORES ?
363 &qgrp_pools->objpool_rsvd : &qgrp_pools->objpool_dyn;
364
365 qgrp_elem = queue_group_elem_get(queue_group);
366 if (unlikely(qgrp_elem == NULL))
368
369 odp_ticketlock_lock(&qgrp_elem->lock);
370 /* Verify that the queue group is not allocated */
371 if (queue_group_allocated(qgrp_elem)) {
372 odp_ticketlock_unlock(&qgrp_elem->lock);
374 }
375
376 /* Remove the queue group from the pool */
377 int ret = objpool_rem_elem(objpool, &qgrp_elem->queue_group_pool_elem);
378
379 odp_ticketlock_unlock(&qgrp_elem->lock);
380 if (unlikely(ret != 0))
382 }
383
384 odp_atomic_inc_u32(&em_shm->queue_group_count);
385 return qgrp_elem->queue_group;
386}
387
388/**
389 * Free an EM queue group
390 *
391 * @param queue_group EM queue group handle
392 *
393 * @return EM status
394 * @retval EM_QUEUE_GROUP_UNDEF on failure
395 */
396static em_status_t
397queue_group_free(em_queue_group_t queue_group)
398{
399 queue_group_elem_t *const queue_group_elem =
400 queue_group_elem_get(queue_group);
401 objpool_t *const objpool = qgrp_hdl2idx(queue_group) < EM_MAX_CORES ?
402 &em_shm->queue_group_pool.objpool_rsvd :
403 &em_shm->queue_group_pool.objpool_dyn;
404
405 if (unlikely(queue_group_elem == NULL))
406 return EM_ERR_BAD_ID;
407
408 objpool_add(objpool,
409 queue_group_elem->queue_group_pool_elem.subpool_idx,
410 &queue_group_elem->queue_group_pool_elem);
411
412 odp_atomic_dec_u32(&em_shm->queue_group_count);
413 return EM_OK;
414}
415
416/**
417 * Create a new ODP schedule group
418 *
419 * @return ODP schedule group handle
420 */
421static odp_schedule_group_t
422create_odp_schedule_group(const char *name, const odp_thrmask_t *thrmask)
423{
424 odp_schedule_group_t odp_sched_group = ODP_SCHED_GROUP_INVALID;
425
426 odp_schedule_group_param_t param;
427
428 odp_schedule_group_param_init(&param);
429
430 /* Optimize cache stashing hints for EM */
431 param.cache_stash_hints.common.regions.event_data_l2 = 1; /* EM event data */
432 param.cache_stash_hints.common.regions.event_user_area_l2 = 1; /* EM evhdr (+ EM uarea) */
433 param.cache_stash_hints.common.regions.queue_context_l2 = 1; /* EM queue elem */
434
435 /* EM event data: */
436 param.cache_stash_hints.common.event_data.l2.offset = 0;
437 param.cache_stash_hints.common.event_data.l2.len = ODP_CACHE_LINE_SIZE;
438 /* EM evhdr + uarea: */
439 param.cache_stash_hints.common.event_user_area.l2.offset = 0;
440 param.cache_stash_hints.common.event_user_area.l2.len = sizeof(event_hdr_t);
441 /* EM queue elem: */
442 param.cache_stash_hints.common.queue_context.l2.offset = 0;
443 param.cache_stash_hints.common.queue_context.l2.len = MIN(sizeof(queue_elem_t),
444 ODP_CACHE_LINE_SIZE);
445 odp_sched_group = odp_schedule_group_create_2(name, thrmask, &param);
446 return odp_sched_group;
447}
448
449/**
450 * Create the EM default queue group 'EM_QUEUE_GROUP_DEFAULT'
451 */
452static em_queue_group_t default_queue_group_create(void)
453{
454 em_queue_group_t default_qgrp;
455 queue_group_elem_t *default_qgrp_elem;
456 em_core_mask_t *mask;
457 odp_thrmask_t zero_thrmask;
458 odp_schedule_group_t odp_sched_group;
459
460 default_qgrp = queue_group_alloc(EM_QUEUE_GROUP_DEFAULT);
461 if (unlikely(default_qgrp != EM_QUEUE_GROUP_DEFAULT))
462 return EM_QUEUE_GROUP_UNDEF; /* sanity check */
463
464 default_qgrp_elem = queue_group_elem_get(EM_QUEUE_GROUP_DEFAULT);
465 if (unlikely(default_qgrp_elem == NULL))
466 return EM_QUEUE_GROUP_UNDEF; /* sanity check */
467
468 mask = &default_qgrp_elem->core_mask;
469 em_core_mask_zero(mask);
470
471 odp_thrmask_zero(&zero_thrmask);
472
473 /*
474 * Create a new odp schedule group for the EM default queue group.
475 * Don't use the ODP_SCHED_GROUP_WORKER or other predefined ODP groups
476 * since those groups can't be modified.
477 * Create the group without any cores/threads and update it during
478 * em_init_local/core() -> default_queue_group_join() calls for each
479 * EM core.
480 */
481 default_qgrp_elem->odp_sched_group = ODP_SCHED_GROUP_INVALID;
482
483 odp_sched_group = create_odp_schedule_group(EM_QUEUE_GROUP_DEFAULT_NAME, &zero_thrmask);
484 if (unlikely(odp_sched_group == ODP_SCHED_GROUP_INVALID)) {
485 DBG_PRINT("ODP schedule group creation failed for 'EM_QUEUE_GROUP_DEFAULT'\n");
487 }
488
489 /* Store the created odp sched group as the EM default queue group */
490 default_qgrp_elem->odp_sched_group = odp_sched_group;
491
493}
494
495/**
496 * Update the EM default queue group with valid group information for each
497 * core local init and add the ODP thread-id to the scheduling mask.
498 * Run by each call to em_init_local/core() for EM worker and control cores.
499 */
500static em_queue_group_t default_queue_group_join(void)
501{
502 queue_group_elem_t *default_qgrp_elem;
503 odp_thrmask_t odp_joinmask;
504 const int core_id = em_locm.core_id;
505 const int odp_thr = odp_thread_id();
506 int ret;
507
508 default_qgrp_elem = queue_group_elem_get(EM_QUEUE_GROUP_DEFAULT);
509 if (unlikely(!default_qgrp_elem))
511
512 /* Set this thread in the odp schedule group join-mask */
513 odp_thrmask_zero(&odp_joinmask);
514 odp_thrmask_set(&odp_joinmask, odp_thr);
515
516 odp_ticketlock_lock(&default_qgrp_elem->lock);
517 em_core_mask_set(core_id, &default_qgrp_elem->core_mask);
518 /* Join this thread into the "EM default" schedule group */
519 ret = odp_schedule_group_join(default_qgrp_elem->odp_sched_group,
520 &odp_joinmask);
521 odp_ticketlock_unlock(&default_qgrp_elem->lock);
522
523 if (unlikely(ret))
525
527}
528
529/**
530 * @brief The calling core joins all available queue groups
531 *
532 * Main use case for em_term(): to be able to flush the scheduler with only the
533 * last EM-core running we need to modify all queue groups to include this last
534 * core in the queue groups' core masks
535 */
536void queue_group_join_all(void)
537{
538 em_queue_group_t qgrp = em_queue_group_first(NULL);
539 const int core_id = em_locm.core_id;
540
541 while (qgrp != EM_QUEUE_GROUP_UNDEF) {
542 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
543
544 odp_ticketlock_lock(&qgrp_elem->lock);
545
546 int allocated = queue_group_allocated(qgrp_elem);
547 bool ongoing_delete = qgrp_elem->ongoing_delete;
548
549 if (allocated && !ongoing_delete &&
550 !em_core_mask_isset(core_id, &qgrp_elem->core_mask)) {
551 em_core_mask_set(core_id, &qgrp_elem->core_mask);
552 q_grp_add_core(qgrp_elem);
553 }
554 odp_ticketlock_unlock(&qgrp_elem->lock);
555
556 qgrp = em_queue_group_next();
557 }
558}
559
560static inline queue_group_elem_t *
561list_node_to_qgrp_elem(const list_node_t *const list_node)
562{
563 queue_group_elem_t *qgrp_elem = (queue_group_elem_t *)((uintptr_t)list_node
564 - offsetof(queue_group_elem_t, qgrp_list_node));
565
566 return likely(list_node != NULL) ? qgrp_elem : NULL;
567}
568
569/**
570 * @brief The calling core leaves all queue groups it is part of
571 *
572 * Also flushes the scheduler from events from queues that belong to
573 * queue groups that would only be services by the terminating core.
574 *
575 * Main use case for em_term_local()/em_term_core()
576 *
577 * @param flush_orphan_qgrps Flush the scheduler from events from queues that
578 * belong to queue groups that only this core is
579 * handling queues for.
580 */
581void queue_group_leave_all(bool flush_orphan_qgrps)
582{
583 em_queue_group_t qgrp = em_queue_group_first(NULL);
584
585 if (qgrp == EM_QUEUE_GROUP_UNDEF)
586 return;
587
588 const int core_id = em_locm.core_id;
589 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
590 em_core_mask_t this_core_only;
591 list_node_t qgrp_list;
592
593 em_core_mask_zero(&this_core_only);
594 em_core_mask_set(core_id, &this_core_only);
595 list_init(&qgrp_list);
596
597 while (qgrp_elem) {
598 odp_ticketlock_lock(&qgrp_elem->lock);
599
600 int allocated = queue_group_allocated(qgrp_elem);
601 bool ongoing_delete = qgrp_elem->ongoing_delete;
602 bool do_unlock = true;
603
604 if (allocated && !ongoing_delete &&
605 em_core_mask_isset(core_id, &qgrp_elem->core_mask)) {
606 if (flush_orphan_qgrps &&
607 em_core_mask_equal(&this_core_only, &qgrp_elem->core_mask) &&
608 !list_is_empty(&qgrp_elem->queue_list)) {
609 /* No other cores handling queues in this qgrp - add to a list */
610 list_add(&qgrp_list, &qgrp_elem->qgrp_list_node);
611 /* keep the qgrp elems in the list locked */
612 do_unlock = false;
613 } else {
614 /* Remove the EM-core from the queue group */
615 q_grp_rem_core(qgrp_elem);
616 em_core_mask_clr(core_id, &qgrp_elem->core_mask);
617 }
618 }
619 if (do_unlock)
620 odp_ticketlock_unlock(&qgrp_elem->lock);
621
622 qgrp = em_queue_group_next();
623 /* qgrp_elem is NULL for EM_QUEUE_GROUP_UNDEF */
624 qgrp_elem = queue_group_elem_get(qgrp);
625 }
626
627 /* can contain qgrps only if 'flush_orphan_qgrps = true' */
628 if (!list_is_empty(&qgrp_list)) {
629 em_locm_t *const locm = &em_locm;
630 /*
631 * Flush the scheduler from events from queues that belong to
632 * queue groups that only this core is handling queues for.
633 * This is needed to avoid events being stuck in the scheduler
634 * when an EM-core is terminating.
635 */
636 odp_schedule_resume();
637 locm->is_sched_paused = false;
638 flush_scheduler_events();
639 /*
640 * Flush the scheduler from locally stashed events.
641 */
642 odp_schedule_pause();
643 locm->is_sched_paused = true;
644 flush_scheduler_events();
645
646 /*
647 * Drain the list of qgrp-elems that only this core is
648 * handling: remove this core from them and unlock them.
649 *
650 * Use list_rem_first() to properly unlink each node from
651 * the stack-local 'qgrp_list', ensuring no global
652 * qgrp_list_node retains a back-pointer to the stack.
653 */
654 const list_node_t *list_node;
655
656 while ((list_node = list_rem_first(&qgrp_list)) != NULL) {
657 qgrp_elem = list_node_to_qgrp_elem(list_node);
658 /* Remove the EM-core from the queue group */
659 q_grp_rem_core(qgrp_elem);
660 em_core_mask_clr(core_id, &qgrp_elem->core_mask);
661 /* unlock the qgrp-elem that was locked earlier */
662 odp_ticketlock_unlock(&qgrp_elem->lock);
663 }
664 }
665}
666
667static em_status_t core_queue_group_create(void)
668{
669 em_queue_group_t qgrp;
670 em_queue_group_t qgrp_req;
671 queue_group_elem_t *qgrp_elem;
672 em_core_mask_t *mask;
673 odp_thrmask_t zero_thrmask;
674 odp_schedule_group_t odp_sched_group;
675 const int core = em_locm.core_id;
676 char qgrp_name[EM_QUEUE_GROUP_NAME_LEN];
677
678 core_queue_grp_name(core, qgrp_name/*out*/, sizeof(qgrp_name));
679
680 qgrp = em_queue_group_find(qgrp_name);
681 if (qgrp != EM_QUEUE_GROUP_UNDEF) {
682 DBG_PRINT("%s(): core:%d, %s already created - reusing\n",
683 __func__, core, qgrp_name);
684
685 qgrp_elem = queue_group_elem_get(qgrp);
686
687 if (unlikely(!qgrp_elem || !queue_group_allocated(qgrp_elem))) {
688 DBG_PRINT("%s(): qgrp_elem invalid for core-qgrp:%d\n",
689 __func__, core);
690 return EM_ERR_BAD_POINTER;
691 }
692
693 return EM_OK;
694 }
695
696 qgrp_req = qgrp_idx2hdl(core);
697 qgrp = queue_group_alloc(qgrp_req);
698 if (unlikely(qgrp == EM_QUEUE_GROUP_UNDEF || qgrp != qgrp_req)) {
699 DBG_PRINT("queue_group_alloc() fails for core-qgrp:%d\n", core);
700 return EM_ERR_ALLOC_FAILED;
701 }
702
703 qgrp_elem = queue_group_elem_get(qgrp);
704 if (unlikely(qgrp_elem == NULL)) {
705 DBG_PRINT("qgrp_elem NULL for core-qgrp:%d\n", core);
706 return EM_ERR_BAD_POINTER;
707 }
708
709 mask = &qgrp_elem->core_mask;
710 em_core_mask_zero(mask);
711 em_core_mask_set(core, mask);
712
713 odp_thrmask_zero(&zero_thrmask);
714
715 /*
716 * Create a new odp schedule group for each EM core.
717 * Create the group without the core/thread set and update it
718 * during em_init_local/core() -> core_queue_group_join()
719 * calls for each EM core.
720 */
721 qgrp_elem->odp_sched_group = ODP_SCHED_GROUP_INVALID;
722
723 odp_sched_group = create_odp_schedule_group(qgrp_name, &zero_thrmask);
724 if (unlikely(odp_sched_group == ODP_SCHED_GROUP_INVALID)) {
725 DBG_PRINT("ODP schedule group creation failed for core-qgrp:%d\n", core);
726 return EM_ERR_LIB_FAILED;
727 }
728 /* Store the created odp sched group for this EM queue group */
729 qgrp_elem->odp_sched_group = odp_sched_group;
730
731 return EM_OK;
732}
733
734static em_status_t core_queue_group_join(void)
735{
736 char qgrp_name[EM_QUEUE_GROUP_NAME_LEN];
737 int core = em_core_id();
738 const int odp_thr = odp_thread_id();
739
740 core_queue_grp_name(core, qgrp_name/*out*/, sizeof(qgrp_name));
741
742 em_queue_group_t qgrp = em_queue_group_find(qgrp_name);
743
744 if (unlikely(qgrp == EM_QUEUE_GROUP_UNDEF)) {
745 DBG_PRINT("%s(): core:%d, %s not found\n", __func__, core, qgrp_name);
746 return EM_ERR_NOT_FOUND;
747 }
748
749 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
750
751 if (unlikely(!qgrp_elem)) {
752 DBG_PRINT("%s(): qgrp_elem NULL for core-qgrp:%d\n",
753 __func__, core);
754 return EM_ERR_BAD_POINTER;
755 }
756
757 /* Set this thread in the odp schedule group join-mask */
758 odp_thrmask_t odp_joinmask;
759
760 odp_thrmask_zero(&odp_joinmask);
761 odp_thrmask_set(&odp_joinmask, odp_thr);
762
763 odp_ticketlock_lock(&qgrp_elem->lock);
764 /* Join this thread into the core-local schedule group */
765 int ret = odp_schedule_group_join(qgrp_elem->odp_sched_group,
766 &odp_joinmask);
767 odp_ticketlock_unlock(&qgrp_elem->lock);
768
769 if (unlikely(ret)) {
770 DBG_PRINT("%s(): odp_schedule_group_join():%d, core-qgrp:%d\n",
771 __func__, ret, core);
772 return EM_ERR_LIB_FAILED;
773 }
774
775 return EM_OK;
776}
777
778/**
779 * Allow creating a queue group with a specific handle if requested and
780 * available, use EM_QUEUE_GROUP_UNDEF to take any free handle.
781 * Called from queue_group_create() and queue_group_create_sync() with an
782 * appropriate escope.
783 */
784static em_queue_group_t
785queue_group_create_escope(const char *name, const em_core_mask_t *mask,
786 int num_notif, const em_notif_t notif_tbl[],
787 em_queue_group_t requested_queue_group,
788 em_escope_t escope)
789{
790 core_map_t *const core_map = &em_shm->core_map;
791 em_queue_group_t queue_group;
792 queue_group_elem_t *qgrp_elem;
793 odp_schedule_group_t odp_sched_group;
794 odp_thrmask_t zero_thrmask;
795 em_status_t stat;
796 em_core_mask_t add_mask;
797 em_core_mask_t rem_zero_mask;
798 const int core = em_core_id();
799
800 odp_thrmask_zero(&zero_thrmask);
801 em_core_mask_zero(&rem_zero_mask);
802 em_core_mask_zero(&add_mask);
803
804 /*
805 * Allocate the queue group element,
806 * if 'requested_queue_group' == EM_QUEUE_GROUP_UNDEF take any handle.
807 */
808 queue_group = queue_group_alloc(requested_queue_group);
809 qgrp_elem = queue_group_elem_get(queue_group);
810 if (unlikely(qgrp_elem == NULL)) {
812 "Queue group alloc failed!");
813 /* No free queue group found */
815 }
816
817 /* Create empty schedule group, each core adds itself via an add-req */
818 odp_sched_group = create_odp_schedule_group(name, &zero_thrmask);
819 if (unlikely(odp_sched_group == ODP_SCHED_GROUP_INVALID)) {
820 queue_group_free(queue_group);
822 "ODP sched group creation for EM queue group (\"%s\") failed!",
823 name);
825 }
826
827 /*
828 * Take the core_map rwlock for reading - can afford to keep it for a
829 * "long" time since blocking write-access is only needed in core
830 * add/rem scenarios (via em_init/term_local() and the older
831 * em_init/term_core()). The core ctrl events should be sent before
832 * releasing the lock to prevent add/rem-core actions while sending
833 * these ctrl events.
834 */
835 odp_rwlock_read_lock(&core_map->rwlock);
836
837 stat = queue_group_check_mask(mask);
838 if (unlikely(stat != EM_OK)) {
839 odp_rwlock_read_unlock(&core_map->rwlock);
840 odp_schedule_group_destroy(odp_sched_group);
841 queue_group_free(queue_group);
842
843 /* use mstr len > EM_CORE_MASK_STRLEN on mask-error */
844 char mstr[ODP_CPUMASK_STR_SIZE];
845
846 em_core_mask_tostr(mstr, sizeof(mstr), mask);
848 "Invalid mask given:%s", mstr);
849
851 }
852
853 em_core_mask_copy(&add_mask, mask);
854
855 odp_ticketlock_lock(&qgrp_elem->lock);
856
857 /* Initialize the data of the newly allocated queue group */
858 qgrp_elem->odp_sched_group = odp_sched_group;
859 em_core_mask_copy(&qgrp_elem->core_mask, mask); /* set new mask */
860 list_init(&qgrp_elem->queue_list);
861 odp_atomic_init_u32(&qgrp_elem->num_queues, 0);
862 qgrp_elem->ongoing_delete = false;
863
864 if (em_core_mask_isset(core, &add_mask)) {
865 em_core_mask_clr(core, &add_mask);
866 q_grp_add_core(qgrp_elem);
867 }
868
869 if (em_core_mask_iszero(&add_mask)) {
870 if (escope == EM_ESCOPE_QUEUE_GROUP_CREATE_SYNC)
871 q_grp_create_sync_done(qgrp_elem, mask);
872 else
873 q_grp_create_done(qgrp_elem, mask);
874
875 odp_ticketlock_unlock(&qgrp_elem->lock);
876 odp_rwlock_read_unlock(&core_map->rwlock);
877
878 stat = send_notifs(num_notif, notif_tbl);
879 if (unlikely(stat != EM_OK))
880 INTERNAL_ERROR(stat, escope, "Sending notifs failed!");
881
882 return queue_group;
883 }
884
885 odp_ticketlock_unlock(&qgrp_elem->lock);
886
887 stat = send_qgrp_addrem_reqs(qgrp_elem, mask, &add_mask, &rem_zero_mask,
888 num_notif, notif_tbl, escope);
889
890 odp_rwlock_read_unlock(&core_map->rwlock);
891
892 if (unlikely(stat != EM_OK))
893 INTERNAL_ERROR(stat, escope, "qgrp add/rem-req(s) send failed");
894
895 return queue_group;
896}
897
898/**
899 * Allow creating a queue group with a specific handle
900 * if requested and available.
901 */
902em_queue_group_t
903queue_group_create(const char *name, const em_core_mask_t *mask,
904 int num_notif, const em_notif_t notif_tbl[],
905 em_queue_group_t requested_queue_group)
906{
907 return queue_group_create_escope(name, mask, num_notif, notif_tbl,
908 requested_queue_group,
909 EM_ESCOPE_QUEUE_GROUP_CREATE);
910}
911
912/**
913 * Allow creating a queue group synchronously with a specific handle
914 * if requested and available.
915 * No need for sync blocking when creating a new queue group.
916 */
917em_queue_group_t
918queue_group_create_sync(const char *name, const em_core_mask_t *mask,
919 em_queue_group_t requested_queue_group)
920{
921 return queue_group_create_escope(name, mask, 0, NULL,
922 requested_queue_group,
923 EM_ESCOPE_QUEUE_GROUP_CREATE_SYNC);
924}
925
926/*
927 * queue_group_create/modify/_sync() helper:
928 * Can only set core mask bits for running cores - verify this.
929 * Must be called with em_shm->core_map.rwlock locked for reads.
930 */
931em_status_t queue_group_check_mask(const em_core_mask_t *mask)
932{
933 const core_map_t *const core_map = &em_shm->core_map;
934 em_core_mask_t check_mask;
935
936 em_core_mask_and(&check_mask, mask, &core_map->rwlocked.logic_mask);
937
938 if (unlikely(!em_core_mask_equal(&check_mask, mask)))
939 return EM_ERR_TOO_LARGE;
940
941 return EM_OK;
942}
943
944/*
945 * queue_group_modify/_sync() helper: check Queue Group state
946 */
947static em_status_t
948check_qgrp_state(const queue_group_elem_t *qgrp_elem, bool is_delete,
949 const char **err_str/*out*/)
950{
951 if (unlikely(!queue_group_allocated(qgrp_elem))) {
952 *err_str = "Queue group not allocated";
953 return EM_ERR_BAD_ID;
954 }
955 if (unlikely(qgrp_elem->ongoing_delete)) {
956 *err_str = "Contending queue group delete ongoing";
957 return EM_ERR_BAD_STATE;
958 }
959 if (unlikely(is_delete && !list_is_empty(&qgrp_elem->queue_list))) {
960 *err_str = "Queue group contains queues, cannot delete group";
961 return EM_ERR_NOT_FREE;
962 }
963
964 return EM_OK;
965}
966
967/*
968 * queue_group_modify/_sync() helper: determine cores to be added to the queue group
969 */
970static int count_qgrp_adds(const em_core_mask_t *old_mask,
971 const em_core_mask_t *new_mask,
972 em_core_mask_t *add_mask /*out*/)
973{
974 em_core_mask_t changed_mask;
975
976 /* get changed cores (added and removed ones) */
977 em_core_mask_xor(&changed_mask, old_mask, new_mask);
978 /* cores that were added in the new mask */
979 em_core_mask_and(add_mask, &changed_mask, new_mask);
980
981 int adds = em_core_mask_count(add_mask);
982
983 return adds;
984}
985
986/*
987 * queue_group_modify/_sync() helper: determine cores to be removed from the queue group
988 */
989static int count_qgrp_rems(const em_core_mask_t *old_mask,
990 const em_core_mask_t *new_mask,
991 em_core_mask_t *rem_mask /*out*/)
992{
993 em_core_mask_t changed_mask;
994
995 /* get changed cores (added and removed ones) */
996 em_core_mask_xor(&changed_mask, old_mask, new_mask);
997 /* cores that were removed from the old mask */
998 em_core_mask_and(rem_mask, &changed_mask, old_mask);
999
1000 int rems = em_core_mask_count(rem_mask);
1001
1002 return rems;
1003}
1004
1005/**
1006 * @brief send_qgrp_addrem_reqs() helper: free unsent add/rem req events
1007 */
1008static void addrem_events_free(em_event_t add_events[], int add_count,
1009 em_event_t rem_events[], int rem_count)
1010{
1011 for (int i = 0; i < add_count; i++) {
1012 if (add_events[i] != EM_EVENT_UNDEF)
1013 em_free(add_events[i]);
1014 }
1015 for (int i = 0; i < rem_count; i++) {
1016 if (rem_events[i] != EM_EVENT_UNDEF)
1017 em_free(rem_events[i]);
1018 }
1019}
1020
1021/**
1022 * @brief send_qgrp_addrem_reqs() helper: send add or rem req events to cores
1023 * in 'mask', send with an event group to trigger 'done' notification.
1024 *
1025 * Mark each sent event in the array as 'undef' to help detect sent vs. unsent
1026 *
1027 * Converts the given EM core mask to an ODP thread mask and thus requires that
1028 * this function has been called with the core map rwlock taken:
1029 * odp_rwlock_read_lock(&em_shm->core_map.rwlock)
1030 */
1031static em_status_t send_addrem_events(em_event_t addrem_events[], int count,
1032 const em_core_mask_t *mask,
1033 em_event_group_t event_group)
1034{
1035 const int first_qidx = queue_id2idx(em_shm->queue_tbl.first_internal_queue_id);
1036 int ev_idx = 0;
1037 em_status_t err;
1038
1039 odp_thrmask_t thr_mask;
1040 int thr;
1041
1042 /*
1043 * Assumes lock is already taken:
1044 * odp_rwlock_read_lock(&em_shm->core_map.rwlock)
1045 * Caller locks and unlocks.
1046 */
1047 mask_em2odp__rwlocked(mask, &thr_mask);
1048
1049 thr = odp_thrmask_first(&thr_mask);
1050 while (thr >= 0 && ev_idx < count) {
1051 /*
1052 * Send an add/rem-req to each core-specific unscheduled ctrl
1053 * queue, track completion using an event group.
1054 */
1055 err = send_ctrl_queue(addrem_events[ev_idx],
1056 queue_idx2hdl(first_qidx + thr),
1057 event_group);
1058 if (unlikely(err != EM_OK))
1059 return err;
1060 addrem_events[ev_idx] = EM_EVENT_UNDEF;
1061 ev_idx++;
1062
1063 thr = odp_thrmask_next(&thr_mask, thr);
1064 }
1065
1066 return EM_OK;
1067}
1068
1069/**
1070 * @brief send_qgrp_addrem_reqs() helper: create the add/rem req events
1071 */
1072static int create_addrem_events(em_event_t addrem_events[/*out*/], int count,
1073 uint64_t ev_id, em_queue_group_t queue_group)
1074{
1075 internal_event_t *i_event;
1076
1077 if (unlikely(count < 1))
1078 return 0;
1079
1080 addrem_events[0] = em_alloc(sizeof(internal_event_t),
1082 if (unlikely(addrem_events[0] == EM_EVENT_UNDEF))
1083 return 0;
1084
1085 /* Init the QUEUE_GROUP_ADD_REQ internal ctrl event(s) */
1086 i_event = em_event_pointer(addrem_events[0]);
1087 i_event->id = ev_id;
1088 i_event->q_grp.queue_group = queue_group;
1089
1090 for (int i = 1; i < count; i++) {
1091 addrem_events[i] = em_event_clone(addrem_events[0],
1093 if (unlikely(addrem_events[i] == EM_EVENT_UNDEF))
1094 return i;
1095 }
1096
1097 return count;
1098}
1099
1100/**
1101 * @brief send_qgrp_addrem_reqs() helper: set callback based on err-scope (=id)
1102 */
1103static int
1104set_qgrp_done_func(em_escope_t escope,
1105 void (**f_done_callback)(void *arg_ptr) /*out*/,
1106 bool *sync_operation /*out*/)
1107{
1108 *sync_operation = false;
1109
1110 switch (escope) {
1111 case EM_ESCOPE_QUEUE_GROUP_CREATE:
1112 *f_done_callback = q_grp_create_done_callback;
1113 break;
1114 case EM_ESCOPE_QUEUE_GROUP_CREATE_SYNC:
1115 *f_done_callback = q_grp_create_sync_done_callback;
1116 *sync_operation = true;
1117 break;
1118 case EM_ESCOPE_QUEUE_GROUP_MODIFY:
1119 *f_done_callback = q_grp_modify_done_callback;
1120 break;
1121 case EM_ESCOPE_QUEUE_GROUP_MODIFY_SYNC:
1122 *f_done_callback = q_grp_modify_sync_done_callback;
1123 *sync_operation = true;
1124 break;
1125 case EM_ESCOPE_QUEUE_GROUP_DELETE:
1126 *f_done_callback = q_grp_delete_done_callback;
1127 break;
1128 case EM_ESCOPE_QUEUE_GROUP_DELETE_SYNC:
1129 *f_done_callback = q_grp_delete_sync_done_callback;
1130 *sync_operation = true;
1131 break;
1132 default:
1133 *f_done_callback = NULL;
1134 return -1;
1135 }
1136
1137 return 0;
1138}
1139
1140/**
1141 * @brief queue_group_create/modify/_sync() helper: send qgrp addrem-req events to cores
1142 */
1143static em_status_t
1144send_qgrp_addrem_reqs(queue_group_elem_t *qgrp_elem,
1145 const em_core_mask_t *new_mask,
1146 const em_core_mask_t *add_mask,
1147 const em_core_mask_t *rem_mask,
1148 int num_notif, const em_notif_t notif_tbl[],
1149 em_escope_t escope)
1150{
1151 em_event_t callback_args_event =
1154 if (unlikely(callback_args_event == EM_EVENT_UNDEF))
1155 return EM_ERR_ALLOC_FAILED;
1156
1157 /*
1158 * Set the 'qgrp operation done'-callback func based on given
1159 * escope (identifies operation).
1160 * f_done_callback(f_done_arg_ptr)
1161 */
1162 void (*f_done_callback)(void *arg_ptr);
1163 void *f_done_arg_ptr = callback_args_event;
1164 bool sync_operation = false;
1165
1166 int ret = set_qgrp_done_func(escope, &f_done_callback/*out*/,
1167 &sync_operation/*out*/);
1168 if (unlikely(ret)) {
1169 em_free(callback_args_event);
1170 return EM_ERR_NOT_FOUND;
1171 }
1172
1173 const em_queue_group_t queue_group = qgrp_elem->queue_group;
1174 const int add_count = em_core_mask_count(add_mask);
1175 const int rem_count = em_core_mask_count(rem_mask);
1176 const int addrem_count = add_count + rem_count; /* Subset of cores*/
1177 em_event_t add_events[add_count];
1178 em_event_t rem_events[rem_count];
1179 em_event_group_t event_group;
1180 em_status_t err;
1181 int cnt;
1182
1183 /* Init the 'done'-callback function arguments */
1184 q_grp_done_callback_args_t *callback_args =
1185 em_event_pointer(callback_args_event);
1186 callback_args->qgrp_elem = qgrp_elem;
1187 em_core_mask_copy(&callback_args->new_mask, new_mask);
1188
1189 /*
1190 * Create an event group to track completion of all sent add/rem-reqs.
1191 * Set up notifications to be sent when all cores are done handling the
1192 * queue group add/rem-reqs.
1193 */
1194 event_group = internal_done_w_notif_req(addrem_count,
1195 f_done_callback, f_done_arg_ptr,
1196 num_notif, notif_tbl,
1197 sync_operation);
1198 if (unlikely(event_group == EM_EVENT_GROUP_UNDEF)) {
1199 em_free(callback_args_event);
1200 return EM_ERR_NOT_FREE;
1201 }
1202
1203 for (int i = 0; i < add_count; i++)
1204 add_events[i] = EM_EVENT_UNDEF;
1205 for (int i = 0; i < rem_count; i++)
1206 rem_events[i] = EM_EVENT_UNDEF;
1207
1208 /* Create internal events for queue group add-reqs */
1209 if (add_count) {
1210 cnt = create_addrem_events(add_events /*out*/, add_count,
1211 QUEUE_GROUP_ADD_REQ, queue_group);
1212 if (unlikely(cnt != add_count))
1213 goto err_free_resources;
1214 }
1215 /* Create internal events for queue group rem-reqs */
1216 if (rem_count) {
1217 cnt = create_addrem_events(rem_events /*out*/, rem_count,
1218 QUEUE_GROUP_REM_REQ, queue_group);
1219 if (unlikely(cnt != rem_count))
1220 goto err_free_resources;
1221 }
1222
1223 /*
1224 * Send rem-req events to the concerned cores
1225 */
1226 err = send_addrem_events(rem_events, rem_count, rem_mask, event_group);
1227 if (unlikely(err != EM_OK))
1228 goto err_free_resources;
1229 /*
1230 * Send add-req events to the concerned cores
1231 */
1232 err = send_addrem_events(add_events, add_count, add_mask, event_group);
1233
1234 if (unlikely(err != EM_OK))
1235 goto err_free_resources;
1236
1237 return EM_OK;
1238
1239err_free_resources:
1240 addrem_events_free(add_events, add_count,
1241 rem_events, rem_count);
1242 evgrp_abort_delete(event_group);
1243 em_free(callback_args_event);
1244
1246}
1247
1248/**
1249 * Called by em_queue_group_modify with flag is_delete=0 and by
1250 * em_queue_group_delete() with flag is_delete=1
1251 *
1252 * @param qgrp_elem Queue group element
1253 * @param new_mask New core mask
1254 * @param num_notif Number of entries in notif_tbl (0 for no notification)
1255 * @param notif_tbl Array of notifications to send as the operation completes
1256 * @param is_delete Is this modify triggered by em_queue_group_delete()?
1257 */
1259queue_group_modify(queue_group_elem_t *const qgrp_elem,
1260 const em_core_mask_t *new_mask,
1261 int num_notif, const em_notif_t notif_tbl[],
1262 bool is_delete)
1263{
1264 core_map_t *const core_map = &em_shm->core_map;
1265 const em_queue_group_t queue_group = qgrp_elem->queue_group;
1266 em_status_t err;
1267 const char *err_str = "";
1268 const em_escope_t escope = is_delete ? EM_ESCOPE_QUEUE_GROUP_DELETE :
1269 EM_ESCOPE_QUEUE_GROUP_MODIFY;
1270 const int core = em_core_id();
1271
1272 /*
1273 * Take the core_map rwlock for reading - can afford to keep it for a
1274 * "long" time since blocking write-access is only needed in core
1275 * add/rem scenarios (via em_init/term_local() and the older
1276 * em_init/term_core()). The core ctrl events should be sent before
1277 * releasing the lock to prevent add/rem-core actions while sending
1278 * these ctrl events.
1279 */
1280 odp_rwlock_read_lock(&core_map->rwlock);
1281
1282 err = queue_group_check_mask(new_mask);
1283 if (unlikely(err != EM_OK)) {
1284 odp_rwlock_read_unlock(&core_map->rwlock);
1285
1286 /* use mstr len > EM_CORE_MASK_STRLEN on mask-error */
1287 char mstr[ODP_CPUMASK_STR_SIZE];
1288
1289 em_core_mask_tostr(mstr, sizeof(mstr), new_mask);
1290 return INTERNAL_ERROR(EM_ERR_BAD_ARG, escope,
1291 "Queue group:%" PRI_QGRP ", invalid mask given:%s",
1292 queue_group, mstr);
1293 }
1294
1295 odp_ticketlock_lock(&qgrp_elem->lock);
1296
1297 /* Check Queue Group state */
1298 err = check_qgrp_state(qgrp_elem, is_delete, &err_str/*out*/);
1299 if (unlikely(err != EM_OK)) {
1300 odp_ticketlock_unlock(&qgrp_elem->lock);
1301 odp_rwlock_read_unlock(&core_map->rwlock);
1302 return INTERNAL_ERROR(err, escope, err_str);
1303 }
1304
1305 em_core_mask_t old_mask;
1306
1307 /* store previous mask */
1308 em_core_mask_copy(&old_mask, &qgrp_elem->core_mask);
1309 /* update with new_mask */
1310 em_core_mask_copy(&qgrp_elem->core_mask, new_mask);
1311
1312 /* Count added & removed cores */
1313 em_core_mask_t add_mask;
1314 em_core_mask_t rem_mask;
1315 int adds = count_qgrp_adds(&old_mask, new_mask, &add_mask /*out*/);
1316 int rems = count_qgrp_rems(&old_mask, new_mask, &rem_mask /*out*/);
1317 /*
1318 * Remove the calling core from the add-mask. The core adds itself.
1319 * Don't do the same for the rem-mask: we want to send a rem-event to
1320 * this core to ensure the core is not currently processing from that
1321 * queue-group.
1322 */
1323 if (adds > 0 && em_core_mask_isset(core, &add_mask)) {
1324 em_core_mask_clr(core, &add_mask);
1325 adds--;
1326 q_grp_add_core(qgrp_elem);
1327 }
1328
1329 /*
1330 * If the new mask is equal to the one in use:
1331 * send notifs immediately and return.
1332 */
1333 if (em_core_mask_equal(&old_mask, new_mask) || (adds == 0 && rems == 0)) {
1334 /* New mask == curr mask, or both zero, send notifs & return */
1335 if (is_delete)
1336 q_grp_delete_done(qgrp_elem, new_mask);
1337 else
1338 q_grp_modify_done(qgrp_elem, new_mask);
1339
1340 odp_ticketlock_unlock(&qgrp_elem->lock);
1341 odp_rwlock_read_unlock(&core_map->rwlock);
1342
1343 err = send_notifs(num_notif, notif_tbl);
1344 RETURN_ERROR_IF(err != EM_OK, err, escope,
1345 "notif sending failed");
1346 return EM_OK;
1347 }
1348
1349 /* Catch contending queue group operations while delete is ongoing */
1350 if (is_delete)
1351 qgrp_elem->ongoing_delete = true;
1352
1353 odp_ticketlock_unlock(&qgrp_elem->lock);
1354
1355 /*
1356 * Send add/rem-req events to all other concerned cores.
1357 * Note: if .ongoing_delete = true:
1358 * Treat errors as EM_FATAL because failures will leave
1359 * .ongoing_delete = true for the group until restart of EM.
1360 */
1361 err = send_qgrp_addrem_reqs(qgrp_elem, new_mask, &add_mask, &rem_mask,
1362 num_notif, notif_tbl, escope);
1363
1364 odp_rwlock_read_unlock(&core_map->rwlock);
1365
1366 RETURN_ERROR_IF(err != EM_OK, is_delete ? EM_FATAL(err) : err, escope,
1367 "qgrp rem req(s) sending failed");
1368
1369 return EM_OK;
1370}
1371
1372/**
1373 * Called by em_queue_group_modify_sync with flag is_delete=0 and by
1374 * em_queue_group_delete_sync() with flag is_delete=1
1375 *
1376 * @param qgrp_elem Queue group element
1377 * @param new_mask New core mask
1378 * @param is_delete Is this modify triggered by em_queue_group_delete_sync()?
1379 */
1381queue_group_modify_sync(queue_group_elem_t *const qgrp_elem,
1382 const em_core_mask_t *new_mask, bool is_delete)
1383{
1384 core_map_t *const core_map = &em_shm->core_map;
1385 em_locm_t *const locm = &em_locm;
1386 const em_queue_group_t queue_group = qgrp_elem->queue_group;
1387 em_status_t err = EM_OK;
1388 const char *err_str = "";
1389 const em_escope_t escope = is_delete ? EM_ESCOPE_QUEUE_GROUP_DELETE_SYNC
1390 : EM_ESCOPE_QUEUE_GROUP_MODIFY_SYNC;
1391 const int core = em_core_id();
1392
1393 /*
1394 * Take the core_map rwlock for reading - can afford to keep it for a
1395 * "long" time since blocking write-access is only needed in core
1396 * add/rem scenarios (via em_init/term_local() and the older
1397 * em_init/term_core()). The core ctrl events should be sent before
1398 * releasing the lock to prevent add/rem-core actions while sending
1399 * these ctrl events.
1400 */
1401 odp_rwlock_read_lock(&core_map->rwlock);
1402
1403 err = queue_group_check_mask(new_mask);
1404 if (unlikely(err != EM_OK)) {
1405 odp_rwlock_read_unlock(&core_map->rwlock);
1406
1407 /* use mstr len > EM_CORE_MASK_STRLEN on mask-error */
1408 char mstr[ODP_CPUMASK_STR_SIZE];
1409
1410 em_core_mask_tostr(mstr, sizeof(mstr), new_mask);
1411 return INTERNAL_ERROR(EM_ERR_BAD_ARG, escope,
1412 "Queue group:%" PRI_QGRP ", invalid core mask given:%s",
1413 queue_group, mstr);
1414 }
1415
1416 /* Mark that a sync-API call is in progress */
1417 locm->sync_api.in_progress = true;
1418
1419 odp_ticketlock_lock(&qgrp_elem->lock);
1420
1421 /* Check Queue Group state */
1422 err = check_qgrp_state(qgrp_elem, is_delete, &err_str/*out*/);
1423 if (unlikely(err != EM_OK)) {
1424 odp_ticketlock_unlock(&qgrp_elem->lock);
1425 goto queue_group_modify_sync_error;
1426 }
1427
1428 em_core_mask_t old_mask;
1429
1430 /* store previous mask */
1431 em_core_mask_copy(&old_mask, &qgrp_elem->core_mask);
1432 /* update with new_mask */
1433 em_core_mask_copy(&qgrp_elem->core_mask, new_mask);
1434
1435 if (em_core_mask_equal(&old_mask, new_mask)) {
1436 /* New mask == curr mask, or both zero */
1437 if (is_delete)
1438 q_grp_delete_done(qgrp_elem, new_mask);
1439
1440 odp_ticketlock_unlock(&qgrp_elem->lock);
1441
1442 err = EM_OK;
1443 goto queue_group_modify_sync_error; /* no error, just return */
1444 }
1445
1446 /* Catch contending queue group operations while delete is ongoing */
1447 if (is_delete)
1448 qgrp_elem->ongoing_delete = true;
1449
1450 /* Count added & removed cores */
1451 em_core_mask_t add_mask;
1452 em_core_mask_t rem_mask;
1453 int adds = count_qgrp_adds(&old_mask, new_mask, &add_mask /*out*/);
1454 int rems = count_qgrp_rems(&old_mask, new_mask, &rem_mask /*out*/);
1455
1456 /*
1457 * Remove the calling core from the add/rem-mask and -count since no
1458 * add/rem-req event should be sent to it during this _sync operation.
1459 */
1460 if (adds > 0 && em_core_mask_isset(core, &add_mask)) {
1461 em_core_mask_clr(core, &add_mask);
1462 adds--;
1463 q_grp_add_core(qgrp_elem);
1464 }
1465 if (rems > 0 && em_core_mask_isset(core, &rem_mask)) {
1466 em_core_mask_clr(core, &rem_mask);
1467 rems--;
1468 q_grp_rem_core(qgrp_elem);
1469 }
1470
1471 /* No cores to send rem-reqs to, mark operation done and return */
1472 if (adds == 0 && rems == 0) {
1473 if (is_delete)
1474 q_grp_delete_done(qgrp_elem, new_mask);
1475 else
1476 q_grp_modify_done(qgrp_elem, new_mask);
1477
1478 odp_ticketlock_unlock(&qgrp_elem->lock);
1479 err = EM_OK;
1480 goto queue_group_modify_sync_error; /* no error, just return */
1481 }
1482
1483 odp_ticketlock_unlock(&qgrp_elem->lock);
1484
1485 /*
1486 * Send add/rem-req events to all other concerned cores.
1487 * Note: if .ongoing_delete = true:
1488 * Treat errors as EM_FATAL because failures will leave
1489 * .ongoing_delete = true for the group until restart of EM.
1490 */
1491 err = send_qgrp_addrem_reqs(qgrp_elem, new_mask, &add_mask, &rem_mask,
1492 0, NULL, escope);
1493
1494 odp_rwlock_read_unlock(&core_map->rwlock);
1495
1496 if (unlikely(err != EM_OK)) {
1497 if (is_delete)
1498 err = EM_FATAL(err);
1499 goto queue_group_modify_sync_error;
1500 }
1501
1502 /*
1503 * Poll the core-local unscheduled control-queue for events.
1504 * These events request the core to do a core-local operation (or not).
1505 * Poll and handle events until 'locm->sync_api.in_progress == false'
1506 * indicating that this sync-API is 'done' on all concerned cores.
1507 */
1508 while (locm->sync_api.in_progress)
1510
1511 return EM_OK;
1512
1513queue_group_modify_sync_error:
1514 odp_rwlock_read_unlock(&core_map->rwlock);
1515 locm->sync_api.in_progress = false;
1516 RETURN_ERROR_IF(err != EM_OK, err, escope,
1517 "Failure: Modify sync QGrp:%" PRI_QGRP ":%s",
1518 queue_group, err_str);
1519 return EM_OK;
1520}
1521
1522/**
1523 * @brief Add the calling core to the odp schedule group that is used by
1524 * the given EM queue group.
1525 *
1526 * @param qgrp_elem Queue group element
1527 */
1528static void q_grp_add_core(const queue_group_elem_t *qgrp_elem)
1529{
1530 int odp_thr = odp_thread_id();
1531 odp_thrmask_t odp_joinmask;
1532
1533 odp_thrmask_zero(&odp_joinmask);
1534 odp_thrmask_set(&odp_joinmask, odp_thr);
1535
1536 int ret = odp_schedule_group_join(qgrp_elem->odp_sched_group,
1537 &odp_joinmask);
1538 if (unlikely(ret)) {
1539 char mask_str[EM_CORE_MASK_STRLEN];
1540 em_queue_group_t queue_group = qgrp_elem->queue_group;
1541
1543 &qgrp_elem->core_mask);
1544 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_QUEUE_GROUP_ADD_CORE,
1545 "QGrp ADD core%02d: odp_schedule_group_join(thr:%d):%d\n"
1546 "QueueGroup:%" PRI_QGRP " core-mask:%s",
1547 em_core_id(), odp_thr, ret, queue_group, mask_str);
1548 }
1549}
1550
1551/**
1552 * @brief Remove the calling core from the odp schedule group that is used by
1553 * the given EM queue group.
1554 *
1555 * @param qgrp_elem Queue group element
1556 */
1557static void q_grp_rem_core(const queue_group_elem_t *qgrp_elem)
1558{
1559 int odp_thr = odp_thread_id();
1560 odp_thrmask_t odp_leavemask;
1561
1562 odp_thrmask_zero(&odp_leavemask);
1563 odp_thrmask_set(&odp_leavemask, odp_thr);
1564
1565 int ret = odp_schedule_group_leave(qgrp_elem->odp_sched_group,
1566 &odp_leavemask);
1567 if (unlikely(ret)) {
1568 char mask_str[EM_CORE_MASK_STRLEN];
1569 em_queue_group_t queue_group = qgrp_elem->queue_group;
1570
1572 &qgrp_elem->core_mask);
1573 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_QUEUE_GROUP_REM_CORE,
1574 "QGrp REM core%02d: odp_schedule_group_leave(thr:%d):%d\n"
1575 "QueueGroup:%" PRI_QGRP " core-mask:%s",
1576 em_core_id(), odp_thr, ret, queue_group, mask_str);
1577 }
1578}
1579
1580void i_event__qgrp_add_core_req(const internal_event_t *i_ev)
1581{
1582 em_queue_group_t qgrp = i_ev->q_grp.queue_group;
1583 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
1584
1585 if (unlikely(!qgrp_elem))
1586 return;
1587
1588 odp_ticketlock_lock(&qgrp_elem->lock);
1589 q_grp_add_core(qgrp_elem);
1590 odp_ticketlock_unlock(&qgrp_elem->lock);
1591}
1592
1593void i_event__qgrp_rem_core_req(const internal_event_t *i_ev)
1594{
1595 em_queue_group_t qgrp = i_ev->q_grp.queue_group;
1596 queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
1597
1598 if (unlikely(!qgrp_elem))
1599 return;
1600
1601 odp_ticketlock_lock(&qgrp_elem->lock);
1602 q_grp_rem_core(qgrp_elem);
1603 odp_ticketlock_unlock(&qgrp_elem->lock);
1604}
1605
1606/**
1607 * Callback function when a em_queue_group_create()
1608 * completes with the internal DONE-event
1609 */
1610static void q_grp_create_done_callback(void *arg_ptr)
1611{
1612 em_event_t event = (em_event_t)arg_ptr;
1613 const q_grp_done_callback_args_t *args = em_event_pointer(event);
1614 queue_group_elem_t *const qgrp_elem = args->qgrp_elem;
1615
1616 odp_ticketlock_lock(&qgrp_elem->lock);
1617 q_grp_create_done(qgrp_elem, &args->new_mask);
1618 odp_ticketlock_unlock(&qgrp_elem->lock);
1619
1620 em_free(event);
1621}
1622
1623/**
1624 * Callback function when a em_queue_group_create_sync()
1625 * completes with the internal DONE-event
1626 */
1627static void q_grp_create_sync_done_callback(void *arg_ptr)
1628{
1629 em_event_t event = (em_event_t)arg_ptr;
1630 const q_grp_done_callback_args_t *args = em_event_pointer(event);
1631 queue_group_elem_t *const qgrp_elem = args->qgrp_elem;
1632
1633 odp_ticketlock_lock(&qgrp_elem->lock);
1634 q_grp_create_sync_done(qgrp_elem, &args->new_mask);
1635 odp_ticketlock_unlock(&qgrp_elem->lock);
1636
1637 em_free(event);
1638}
1639
1640static void q_grp_create_done(const queue_group_elem_t *const qgrp_elem,
1641 const em_core_mask_t *const new_mask)
1642{
1643 (void)qgrp_elem;
1644 (void)new_mask;
1645}
1646
1647static void q_grp_create_sync_done(const queue_group_elem_t *const qgrp_elem,
1648 const em_core_mask_t *const new_mask)
1649{
1650 (void)qgrp_elem;
1651 (void)new_mask;
1652}
1653
1654/**
1655 * Callback function when a em_queue_group_modify()
1656 * completes with the internal DONE-event
1657 */
1658static void q_grp_modify_done_callback(void *arg_ptr)
1659{
1660 em_event_t event = (em_event_t)arg_ptr;
1661 const q_grp_done_callback_args_t *args = em_event_pointer(event);
1662 queue_group_elem_t *const qgrp_elem = args->qgrp_elem;
1663
1664 odp_ticketlock_lock(&qgrp_elem->lock);
1665 q_grp_modify_done(qgrp_elem, &args->new_mask);
1666 odp_ticketlock_unlock(&qgrp_elem->lock);
1667
1668 em_free(event);
1669}
1670
1671/**
1672 * Callback function when a em_queue_group_modify_sync()
1673 * completes with the internal DONE-event
1674 */
1675static void q_grp_modify_sync_done_callback(void *arg_ptr)
1676{
1677 em_locm_t *const locm = &em_locm;
1678
1679 q_grp_modify_done_callback(arg_ptr);
1680
1681 /* Enable the caller of the sync API func to proceed (on this core) */
1682 locm->sync_api.in_progress = false;
1683}
1684
1685static void q_grp_modify_done(const queue_group_elem_t *const qgrp_elem,
1686 const em_core_mask_t *const new_mask)
1687{
1688 (void)qgrp_elem;
1689 (void)new_mask;
1690}
1691
1692/**
1693 * Callback function when a em_queue_group_modify(delete flag set)
1694 * completes with the internal DONE-event
1695 */
1696static void q_grp_delete_done_callback(void *arg_ptr)
1697{
1698 em_event_t event = (em_event_t)arg_ptr;
1699 const q_grp_done_callback_args_t *args = em_event_pointer(event);
1700 queue_group_elem_t *const qgrp_elem = args->qgrp_elem;
1701
1702 odp_ticketlock_lock(&qgrp_elem->lock);
1703 q_grp_delete_done(qgrp_elem, &args->new_mask);
1704 odp_ticketlock_unlock(&qgrp_elem->lock);
1705
1706 em_free(event);
1707}
1708
1709/**
1710 * Callback function when a em_queue_group_modify_sync(delete flag set)
1711 * completes with the internal DONE-event
1712 */
1713static void q_grp_delete_sync_done_callback(void *arg_ptr)
1714{
1715 em_locm_t *const locm = &em_locm;
1716
1717 q_grp_delete_done_callback(arg_ptr);
1718
1719 /* Enable the caller of the sync API func to proceed (on this core) */
1720 locm->sync_api.in_progress = false;
1721}
1722
1723static void q_grp_delete_done(queue_group_elem_t *const qgrp_elem,
1724 const em_core_mask_t *const new_mask)
1725{
1726 const unsigned int num_queues = odp_atomic_load_u32(&qgrp_elem->num_queues);
1727 const em_queue_group_t queue_group = qgrp_elem->queue_group;
1728
1729 /* Sanity check: new core mask for delete is always zero */
1730 if (unlikely(!em_core_mask_iszero(new_mask))) {
1731 char mstr[EM_CORE_MASK_STRLEN];
1732
1733 em_core_mask_tostr(mstr, EM_CORE_MASK_STRLEN, new_mask);
1734 INTERNAL_ERROR(EM_FATAL(EM_ERR_BAD_STATE), EM_ESCOPE_QUEUE_GROUP_DELETE,
1735 "Delete QGrp:%" PRI_QGRP " mask not zero:%s",
1736 queue_group, mstr);
1737 }
1738 /* Sanity check: grp must not have been modified since start of delete */
1739 if (unlikely(!em_core_mask_equal(&qgrp_elem->core_mask, new_mask))) {
1740 char mstr1[EM_CORE_MASK_STRLEN];
1741 char mstr2[EM_CORE_MASK_STRLEN];
1742
1744 em_core_mask_tostr(mstr2, EM_CORE_MASK_STRLEN, new_mask);
1745 INTERNAL_ERROR(EM_FATAL(EM_ERR_BAD_STATE), EM_ESCOPE_QUEUE_GROUP_DELETE,
1746 "Delete QGrp:%" PRI_QGRP ", masks modified during delete:%s vs. %s",
1747 queue_group, mstr1, mstr2);
1748 }
1749
1750 if (unlikely(!list_is_empty(&qgrp_elem->queue_list) || num_queues))
1751 INTERNAL_ERROR(EM_FATAL(EM_ERR_NOT_FREE), EM_ESCOPE_QUEUE_GROUP_DELETE,
1752 "Delete QGrp:%" PRI_QGRP ", contains %u queues, cannot delete!",
1753 queue_group, num_queues);
1754
1755 int ret = odp_schedule_group_destroy(qgrp_elem->odp_sched_group);
1756
1757 if (unlikely(ret != 0))
1758 INTERNAL_ERROR(EM_FATAL(EM_ERR_LIB_FAILED), EM_ESCOPE_QUEUE_GROUP_DELETE,
1759 "Delete QGrp:%" PRI_QGRP ", ODP sched grp destroy fails:%d",
1760 queue_group, ret);
1761
1762 qgrp_elem->odp_sched_group = ODP_SCHED_GROUP_INVALID;
1763 qgrp_elem->ongoing_delete = false;
1764
1765 /* Free the queue group */
1766 queue_group_free(qgrp_elem->queue_group);
1767}
1768
1769void queue_group_add_queue_list(queue_group_elem_t *const queue_group_elem,
1770 queue_elem_t *const queue_elem)
1771{
1772 odp_ticketlock_lock(&queue_group_elem->lock);
1773 list_add(&queue_group_elem->queue_list, &queue_elem->qgrp_node);
1774 odp_atomic_inc_u32(&queue_group_elem->num_queues);
1775 odp_ticketlock_unlock(&queue_group_elem->lock);
1776}
1777
1778void queue_group_rem_queue_list(queue_group_elem_t *const queue_group_elem,
1779 queue_elem_t *const queue_elem)
1780{
1781 odp_ticketlock_lock(&queue_group_elem->lock);
1782 if (!list_is_empty(&queue_group_elem->queue_list)) {
1783 list_rem(&queue_group_elem->queue_list, &queue_elem->qgrp_node);
1784 odp_atomic_dec_u32(&queue_group_elem->num_queues);
1785 }
1786 odp_ticketlock_unlock(&queue_group_elem->lock);
1787}
1788
1789unsigned int queue_group_count(void)
1790{
1791 return odp_atomic_load_u32(&em_shm->queue_group_count);
1792}
1793
1794#define QGRP_INFO_HDR_STR \
1795"EM Queue group(s):%2u\n" \
1796"ID Name EM-mask Cpumask " \
1797" ODP-mask Q-num\n" \
1798"------------------------------------------------------------------------------" \
1799"------------------------------\n" \
1800"%s\n"
1801
1802/* Info len (in bytes) per queue group, calculated from QGRP_INFO_FMT */
1803#define QGRP_INFO_LEN (108 + 1 /* Terminating null byte */)
1804#define QGRP_INFO_FMT "%-10" PRI_QGRP "%-32s%-20s%-20s%-20s%-5d\n" /*108 bytes*/
1805
1806static void queue_group_info_str(em_queue_group_t queue_group,
1807 char qgrp_info_str[/*out*/])
1808{
1809 em_core_mask_t core_mask;
1810 odp_thrmask_t odp_thrmask;
1811 em_core_mask_t phys_mask;
1812 char qgrp_name[EM_QUEUE_GROUP_NAME_LEN];
1813 char em_mask_str[EM_CORE_MASK_STRLEN];
1814 char odp_thrmask_str[ODP_THRMASK_STR_SIZE];
1815 char phys_mask_str[EM_CORE_MASK_STRLEN];
1816 em_status_t err;
1817 int ret;
1818 int len = 0;
1819
1820 queue_group_elem_t *qgrp_elem = queue_group_elem_get(queue_group);
1821
1822 if (unlikely(!qgrp_elem || !queue_group_allocated(qgrp_elem)))
1823 goto info_print_err;
1824
1825 em_queue_group_name(queue_group, qgrp_name, sizeof(qgrp_name));
1826 err = em_queue_group_mask(queue_group, &core_mask);
1827 if (unlikely(err != EM_OK))
1828 goto info_print_err;
1829 em_core_mask_tostr(em_mask_str, sizeof(em_mask_str), &core_mask);
1830
1831 /* ODP thread mask */
1832 ret = odp_schedule_group_thrmask(qgrp_elem->odp_sched_group,
1833 &odp_thrmask /*out*/);
1834 if (unlikely(ret))
1835 goto info_print_err;
1836 ret = odp_thrmask_to_str(&odp_thrmask, odp_thrmask_str,
1837 sizeof(odp_thrmask_str));
1838 if (unlikely(ret <= 0))
1839 goto info_print_err;
1840 odp_thrmask_str[ret - 1] = '\0';
1841
1842 /* Physical mask */
1843 em_core_mask_physical(&phys_mask /*out*/, &core_mask);
1844 em_core_mask_tostr(phys_mask_str, sizeof(phys_mask_str), &phys_mask);
1845 phys_mask_str[EM_CORE_MASK_STRLEN - 1] = '\0';
1846
1847 len = snprintf(qgrp_info_str, QGRP_INFO_LEN, QGRP_INFO_FMT,
1848 queue_group, qgrp_name, em_mask_str,
1849 phys_mask_str, odp_thrmask_str,
1850 odp_atomic_load_u32(&qgrp_elem->num_queues));
1851
1852 qgrp_info_str[len] = '\0';
1853 return;
1854
1855info_print_err:
1856 len = snprintf(qgrp_info_str, QGRP_INFO_LEN, QGRP_INFO_FMT,
1857 queue_group, "err:n/a", "n/a", "n/a", "n/a", 0);
1858 qgrp_info_str[len] = '\0';
1859}
1860
1861void queue_group_info_print_all(void)
1862{
1863 em_queue_group_t qgrp;
1864 unsigned int qgrp_num;
1865 char single_qgrp_info_str[QGRP_INFO_LEN];
1866 int len = 0;
1867 int n_print = 0;
1868
1869 qgrp = em_queue_group_first(&qgrp_num);
1870
1871 /*
1872 * qgrp_num may not match the amount of queue groups actually returned
1873 * by iterating using em_queue_group_next() if queue groups are added
1874 * or removed in parallel by another core. Thus space for 10 extra queue
1875 * groups is reserved. If more than 10 queue groups are added by other
1876 * cores in parallel, we print only information of the (qgrp_num + 10)
1877 * queue groups.
1878 *
1879 * The extra 1 byte is reserved for the terminating null byte.
1880 */
1881 const int all_qgrp_info_str_len = (qgrp_num + 10) * QGRP_INFO_LEN + 1;
1882 char all_qgrp_info_str[all_qgrp_info_str_len];
1883
1884 while (qgrp != EM_QUEUE_GROUP_UNDEF) {
1885 queue_group_info_str(qgrp, single_qgrp_info_str);
1886
1887 n_print = snprintf(all_qgrp_info_str + len,
1888 all_qgrp_info_str_len - len,
1889 "%s", single_qgrp_info_str);
1890
1891 /* Not enough space to hold more queue group info */
1892 if (n_print >= all_qgrp_info_str_len - len)
1893 break;
1894
1895 len += n_print;
1896 qgrp = em_queue_group_next();
1897 }
1898
1899 /* No EM queue group */
1900 if (len == 0) {
1901 EM_PRINT("No EM queue group!\n");
1902 return;
1903 }
1904
1905 /*
1906 * To prevent printing incomplete information of the last queue group
1907 * when there is not enough space to hold all queue group info.
1908 */
1909 all_qgrp_info_str[len] = '\0';
1910 EM_PRINT(QGRP_INFO_HDR_STR, qgrp_num, all_qgrp_info_str);
1911}
1912
1913#define QGRO_QUEUE_INFO_HDR_STR \
1914"Queue group %" PRI_QGRP "(%s) has %d queue(s):\n\n" \
1915"Id Name Priority Type State Ctx\n" \
1916"--------------------------------------------------------------------------\n" \
1917"%s\n"
1918
1919/* Info len (in bytes) per queue group queue, calculated from QGRP_Q_INFO_FMT */
1920#define QGRP_Q_LEN 75
1921#define QGRP_Q_INFO_FMT "%-10" PRI_QUEUE "%-32s%-10d%-10s%-9s%-3c\n" /*75 bytes*/
1922
1923void queue_group_queues_print(em_queue_group_t qgrp)
1924{
1925 unsigned int q_num;
1926 em_queue_t qgrp_queue;
1927 const queue_elem_t *q_elem;
1928 char qgrp_name[EM_QUEUE_GROUP_NAME_LEN];
1929 char q_name[EM_QUEUE_NAME_LEN];
1930 int len = 0;
1931 int n_print = 0;
1932
1933 const queue_group_elem_t *qgrp_elem = queue_group_elem_get(qgrp);
1934
1935 if (unlikely(!qgrp_elem || !queue_group_allocated(qgrp_elem))) {
1936 EM_PRINT("Queue group %" PRI_QGRP " is not created!\n", qgrp);
1937 return;
1938 }
1939
1940 em_queue_group_name(qgrp, qgrp_name, sizeof(qgrp_name));
1941 qgrp_queue = em_queue_group_queue_first(&q_num, qgrp);
1942
1943 /*
1944 * q_num may not match the amount of queues actually returned by iterating
1945 * using em_queue_group_queue_next() if queues are added or removed
1946 * in parallel by another core. Thus space for 10 extra queues is reserved.
1947 * If more than 10 extra queues are added to this queue group by other
1948 * cores in parallel, we print only information of the (q_num + 10) queues.
1949 *
1950 * The extra 1 byte is reserved for the terminating null byte.
1951 */
1952 const int q_info_len = (q_num + 10) * QGRP_Q_LEN + 1;
1953 char q_info_str[q_info_len];
1954
1955 while (qgrp_queue != EM_QUEUE_UNDEF) {
1956 q_elem = queue_elem_get(qgrp_queue);
1957
1958 if (unlikely(q_elem == NULL || !queue_allocated(q_elem))) {
1959 qgrp_queue = em_queue_group_queue_next();
1960 continue;
1961 }
1962
1963 queue_name(q_elem, q_name, EM_QUEUE_NAME_LEN - 1);
1964
1965 n_print = snprintf(q_info_str + len, q_info_len - len,
1966 QGRP_Q_INFO_FMT, qgrp_queue, q_name,
1967 q_elem->priority,
1968 queue_type_str(q_elem->type),
1969 queue_state_str(q_elem->state),
1970 q_elem->context ? 'Y' : 'N');
1971
1972 /* Not enough space to hold more queue info */
1973 if (n_print >= q_info_len - len)
1974 break;
1975
1976 len += n_print;
1977 qgrp_queue = em_queue_group_queue_next();
1978 }
1979
1980 /* No queue belonging to the queue group */
1981 if (!len) {
1982 EM_PRINT("Queue group %" PRI_QGRP "(%s) has no queue!\n",
1983 qgrp, qgrp_name);
1984 return;
1985 }
1986
1987 /*
1988 * To prevent printing incomplete information of the last queue when
1989 * there is not enough space to hold all queue info.
1990 */
1991 q_info_str[len] = '\0';
1992 EM_PRINT(QGRO_QUEUE_INFO_HDR_STR, qgrp, qgrp_name, q_num, q_info_str);
1993}
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
#define RETURN_ERROR_IF(cond, error, escope, fmt,...)
Definition em_error.h:65
em_status_t send_ctrl_queue(em_event_t event, em_queue_t queue, em_event_group_t event_group)
struct event_hdr event_hdr_t
em_event_group_t internal_done_w_notif_req(int event_group_count, void(*f_done_callback)(void *arg_ptr), void *f_done_arg_ptr, int num_notif, const em_notif_t notif_tbl[], bool sync_operation)
Helper func: Allocate & set up the internal 'done' event with function callbacks and notification eve...
void poll_unsched_ctrl_queue(void)
Poll EM's internal unscheduled control queues during dispatch.
void evgrp_abort_delete(em_event_group_t event_group)
internal_done_w_notif_req() 'companion' to abort and delete the event group created by the mentioned ...
em_status_t send_notifs(const int num_notif, const em_notif_t notif_tbl[])
Helper func to send notifications events.
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
#define EM_QUEUE_NAME_LEN
#define EM_MAX_QUEUE_GROUPS
#define EM_QUEUE_GROUP_NAME_LEN
#define EM_POOL_DEFAULT
#define EM_QUEUE_GROUP_DEFAULT_NAME
#define EM_MAX_CORES
#define EM_QUEUE_GROUP_DEFAULT
int em_core_mask_equal(const em_core_mask_t *mask1, const em_core_mask_t *mask2)
int em_core_mask_count(const em_core_mask_t *mask)
void em_core_mask_tostr(char *mask_str, int len, const em_core_mask_t *mask)
int em_core_mask_iszero(const em_core_mask_t *mask)
void em_core_mask_copy(em_core_mask_t *dst, const em_core_mask_t *src)
void em_core_mask_set(int core, em_core_mask_t *mask)
void em_core_mask_clr(int core, em_core_mask_t *mask)
int em_core_mask_isset(int core, const em_core_mask_t *mask)
void em_core_mask_and(em_core_mask_t *dst, const em_core_mask_t *src1, const em_core_mask_t *src2)
#define EM_CORE_MASK_STRLEN
void em_core_mask_zero(em_core_mask_t *mask)
void em_core_mask_xor(em_core_mask_t *dst, const em_core_mask_t *src1, const em_core_mask_t *src2)
void em_core_mask_physical(em_core_mask_t *phys, const em_core_mask_t *logic)
#define EM_QUEUE_GROUP_UNDEF
#define EM_POOL_UNDEF
#define EM_EVENT_UNDEF
#define PRI_QGRP
#define EM_EVENT_GROUP_UNDEF
#define EM_QUEUE_UNDEF
int em_core_id(void)
#define EM_OK
uint32_t em_escope_t
#define EM_FATAL(error)
uint32_t em_status_t
@ EM_ERR_NOT_FOUND
@ EM_ERR_OPERATION_FAILED
@ EM_ERR_TOO_LARGE
@ EM_ERR_BAD_ID
@ EM_ERR_NOT_FREE
@ EM_ERR_ALLOC_FAILED
@ EM_ERR_BAD_ARG
@ EM_ERR_BAD_STATE
@ EM_ERR_LIB_FAILED
@ EM_ERR_BAD_POINTER
em_event_t em_event_clone(em_event_t event, em_pool_t pool)
Clone an event.
em_event_t em_alloc(uint32_t size, em_event_type_t type, em_pool_t pool)
void em_free(em_event_t event)
void * em_event_pointer(em_event_t event)
@ EM_EVENT_TYPE_SW
em_queue_group_t em_queue_group_find(const char *name)
em_queue_group_t em_queue_group_first(unsigned int *num)
size_t em_queue_group_name(em_queue_group_t queue_group, char *name, size_t maxlen)
em_queue_group_t em_queue_group_next(void)
em_queue_t em_queue_group_queue_first(unsigned int *num, em_queue_group_t queue_group)
em_queue_t em_queue_group_queue_next(void)
em_status_t em_queue_group_mask(em_queue_group_t queue_group, em_core_mask_t *mask)
#define OBJSUBPOOLS_MAX
Definition objpool.h:62
odp_rwlock_t rwlock
em_core_mask_t logic_mask
bool is_sched_paused
Definition em_mem.h:252
int core_id
Definition em_mem.h:239
sync_api_t sync_api
Definition em_mem.h:286
odp_atomic_u32_t queue_group_count
Definition em_mem.h:177
em_cfgfile_opts_t opt
Definition em_mem.h:99
uint32_t subpool_idx
Definition objpool.h:74
queue_state_t state
list_node_t qgrp_node
objpool_elem_t queue_group_pool_elem
em_queue_group_t queue_group
odp_atomic_u32_t num_queues
odp_schedule_group_t odp_sched_group
queue_group_elem_t queue_group_elem[EM_MAX_QUEUE_GROUPS]
struct internal_event_t::@50 q_grp