EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_eo.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>
48
49#include "em_atomic_group.h"
51#include "em_eo.h"
52#include "em_eo_types.h"
53#include "em_error.h"
54#include "em_event.h"
55#include "em_event_inline.h"
56#include "em_event_state.h"
57#include "em_event_types.h"
58#include "em_internal_event.h"
59#include "em_libconfig.h"
61#include "em_mem.h"
62#include "em_queue.h"
63#include "em_queue_inline.h"
64#include "em_queue_types.h"
65#include "misc/list.h"
66#include "misc/objpool.h"
67
68/**
69 * Params for eo_local_func_call_req().
70 * Init params with eo_local_func_call_param_init() before usage.
71 */
72typedef struct {
73 eo_elem_t *eo_elem;
74 queue_elem_t *q_elem;
75 int delete_queues;
76 uint64_t ev_id;
77 void (*f_done_callback)(void *arg_ptr);
78 int num_notif;
79 const em_notif_t *notif_tbl; /* notif_tbl[num_notif] */
80 bool sync_operation;
82
83static void
84eo_local_func_call_param_init(eo_local_func_call_param_t *param);
85static int
86eo_local_func_call_req(const eo_local_func_call_param_t *param);
87
88static em_status_t
89check_eo_local_status(const loc_func_retval_t *loc_func_retvals);
90
91static void
92eo_start_done_callback(void *args);
93static void
94eo_start_sync_done_callback(void *args);
95
96static void
97eo_stop_done_callback(void *args);
98static void
99eo_stop_sync_done_callback(void *args);
100
101static em_status_t
102eo_remove_queue_local(const eo_elem_t *eo_elem, const queue_elem_t *q_elem);
103static void
104eo_remove_queue_done_callback(void *args);
105
106static em_status_t
107eo_remove_queue_sync_local(const eo_elem_t *eo_elem,
108 const queue_elem_t *q_elem);
109static void
110eo_remove_queue_sync_done_callback(void *args);
111
112static em_status_t
113eo_remove_queue_all_local(const eo_elem_t *eo_elem, int delete_queues);
114static void
115eo_remove_queue_all_done_callback(void *args);
116
117static em_status_t
118eo_remove_queue_all_sync_local(const eo_elem_t *eo_elem, int delete_queues);
119static void
120eo_remove_queue_all_sync_done_callback(void *args);
121
122static int read_config_file(void)
123{
124 const char *conf_str;
125 bool val_bool = false;
126 int ret;
127
128 EM_PRINT("EM EO config:\n");
129
130 /*
131 * Option: eo.start_local_fn_at_init
132 */
133 conf_str = "eo.start_local_fn_at_init";
134 ret = em_libconfig_lookup_bool(&em_shm->libconfig, conf_str, &val_bool);
135 if (unlikely(!ret)) {
136 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
137 return -1;
138 }
139 /* store & print the value */
140 em_shm->opt.eo.start_local_fn_at_init = val_bool;
141 EM_PRINT(" %s: %s(%d)\n", conf_str, val_bool ? "true" : "false",
142 val_bool);
143
144 /*
145 * Option: eo.stop_local_fn_at_term
146 */
147 conf_str = "eo.stop_local_fn_at_term";
148 ret = em_libconfig_lookup_bool(&em_shm->libconfig, conf_str, &val_bool);
149 if (unlikely(!ret)) {
150 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
151 return -1;
152 }
153 /* store & print the value */
154 em_shm->opt.eo.stop_local_fn_at_term = val_bool;
155 EM_PRINT(" %s: %s(%d)\n", conf_str, val_bool ? "true" : "false",
156 val_bool);
157
158 return 0;
159}
160
161static inline eo_elem_t *
162eo_poolelem2eo(const objpool_elem_t *const eo_pool_elem)
163{
164 return (eo_elem_t *)((uintptr_t)eo_pool_elem -
165 offsetof(eo_elem_t, eo_pool_elem));
166}
167
169eo_init(eo_tbl_t eo_tbl[], eo_pool_t *eo_pool)
170{
171 int ret;
172 const uint32_t objpool_subpools = MIN(4, OBJSUBPOOLS_MAX);
173
174 memset(eo_tbl, 0, sizeof(eo_tbl_t));
175 memset(eo_pool, 0, sizeof(eo_pool_t));
176
177 if (read_config_file())
178 return EM_ERR_LIB_FAILED;
179
180 for (int i = 0; i < EM_MAX_EOS; i++) {
181 eo_elem_t *const eo_elem = &eo_tbl->eo_elem[i];
182 /* Store EO handle */
183 eo_elem->eo = eo_idx2hdl(i);
184 /* Initialize empty EO-queue list */
185 odp_ticketlock_init(&eo_elem->lock);
186 list_init(&eo_elem->queue_list);
187 eo_elem->stash = ODP_STASH_INVALID;
188 }
189
190 ret = objpool_init(&eo_pool->objpool, objpool_subpools);
191 if (ret != 0)
192 return EM_ERR_LIB_FAILED;
193
194 for (uint32_t i = 0; i < EM_MAX_EOS; i++)
195 objpool_add(&eo_pool->objpool, i % objpool_subpools,
196 &eo_tbl->eo_elem[i].eo_pool_elem);
197
198 odp_atomic_init_u32(&em_shm->eo_count, 0);
199
200 return EM_OK;
201}
202
203em_eo_t
204eo_alloc(void)
205{
206 const eo_elem_t *eo_elem;
207 const objpool_elem_t *eo_pool_elem;
208
209 eo_pool_elem = objpool_rem(&em_shm->eo_pool.objpool, em_core_id());
210 if (unlikely(eo_pool_elem == NULL))
211 return EM_EO_UNDEF;
212
213 eo_elem = eo_poolelem2eo(eo_pool_elem);
214 odp_atomic_inc_u32(&em_shm->eo_count);
215
216 return eo_elem->eo;
217}
218
220eo_free(em_eo_t eo)
221{
222 eo_elem_t *eo_elem = eo_elem_get(eo);
223
224 if (unlikely(eo_elem == NULL))
225 return EM_ERR_BAD_ID;
226
227 eo_elem->state = EM_EO_STATE_UNDEF;
228
229 objpool_add(&em_shm->eo_pool.objpool,
230 eo_elem->eo_pool_elem.subpool_idx, &eo_elem->eo_pool_elem);
231 odp_atomic_dec_u32(&em_shm->eo_count);
232
233 return EM_OK;
234}
235
236/**
237 * Add a queue to an EO
238 */
240eo_add_queue(eo_elem_t *const eo_elem, queue_elem_t *const q_elem)
241{
242 queue_state_t old_state = q_elem->state;
244 em_status_t err;
245
246 err = queue_state_change__check(old_state, new_state, 1/*is_setup*/);
247 if (unlikely(err != EM_OK))
248 return err;
249
250 q_elem->max_events = (uint16_t)eo_elem->max_events;
251
252 q_elem->flags.use_multi_rcv = eo_elem->use_multi_rcv ? true : false;
253 if (eo_elem->use_multi_rcv)
254 q_elem->receive_multi_func = eo_elem->receive_multi_func;
255 else
256 q_elem->receive_func = eo_elem->receive_func;
257
258 q_elem->eo = (uint16_t)(uintptr_t)eo_elem->eo;
259 q_elem->eo_ctx = eo_elem->eo_ctx;
260 q_elem->eo_elem = eo_elem;
261 q_elem->state = new_state;
262
263 /* Link the new queue into the EO's queue-list */
264 odp_ticketlock_lock(&eo_elem->lock);
265 list_add(&eo_elem->queue_list, &q_elem->eo_queue_node);
266 odp_atomic_inc_u32(&eo_elem->num_queues);
267 odp_ticketlock_unlock(&eo_elem->lock);
268
269 return EM_OK;
270}
271
272static inline em_status_t
273eo_rem_queue_locked(eo_elem_t *const eo_elem, queue_elem_t *const q_elem)
274{
275 queue_state_t old_state = q_elem->state;
277 em_status_t err;
278
279 err = queue_state_change__check(old_state, new_state, 0/*!is_setup*/);
280 if (unlikely(err != EM_OK))
281 return err;
282
283 list_rem(&eo_elem->queue_list, &q_elem->eo_queue_node);
284 odp_atomic_dec_u32(&eo_elem->num_queues);
285
286 q_elem->state = new_state;
287 q_elem->eo = (uint16_t)(uintptr_t)EM_EO_UNDEF;
288 q_elem->eo_elem = NULL;
289
290 return EM_OK;
291}
292
293/**
294 * Remove a queue from an EO
295 */
297eo_rem_queue(eo_elem_t *const eo_elem, queue_elem_t *const q_elem)
298{
299 em_status_t err;
300
301 odp_ticketlock_lock(&eo_elem->lock);
302 err = eo_rem_queue_locked(eo_elem, q_elem);
303 odp_ticketlock_unlock(&eo_elem->lock);
304
305 if (unlikely(err != EM_OK))
306 return err;
307
308 return EM_OK;
309}
310
311/*
312 * Remove all queues associated with the EO.
313 * Note: does not delete the queues.
314 */
316eo_rem_queue_all(eo_elem_t *const eo_elem)
317{
318 em_status_t err = EM_OK;
319 queue_elem_t *q_elem;
320 const list_node_t *list_node;
321
322 odp_ticketlock_lock(&eo_elem->lock);
323
324 /* Loop through all queues associated with the EO */
325 list_for_each(&eo_elem->queue_list, list_node) {
326 q_elem = eo_queue_node_to_queue_elem(list_node);
327 /* remove the queue from the EO */
328 err = eo_rem_queue_locked(eo_elem, q_elem);
329 if (unlikely(err != EM_OK))
330 break;
331 } /* end loop */
332
333 odp_ticketlock_unlock(&eo_elem->lock);
334
335 return err;
336}
337
338/*
339 * Delete all queues associated with the EO.
340 * The queue needs to be removed from the EO before the actual delete.
341 */
343eo_delete_queue_all(eo_elem_t *const eo_elem)
344{
345 em_status_t err = EM_OK;
346 queue_elem_t *q_elem;
347 const list_node_t *list_node;
348 const char *fail_op = "eo_rem_queue_locked()";
349 const char *err_str = "remove queue from EO failed";
350
351 odp_ticketlock_lock(&eo_elem->lock);
352
353 /* Loop through all queues associated with the EO */
354 list_for_each(&eo_elem->queue_list, list_node) {
355 q_elem = eo_queue_node_to_queue_elem(list_node);
356 /* remove the queue from the EO */
357 err = eo_rem_queue_locked(eo_elem, q_elem);
358 if (unlikely(err != EM_OK)) {
359 /* uses default fail_op and err_str */
360 break;
361 }
362 /* delete the queue */
363 err = queue_delete(q_elem, &err_str);
364 if (unlikely(err != EM_OK)) {
365 fail_op = "queue_delete()";
366 break;
367 }
368 } /* end loop */
369
370 odp_ticketlock_unlock(&eo_elem->lock);
371
372 if (unlikely(err != EM_OK))
373 EM_LOG(EM_LOG_ERR,
374 "EO:%" PRI_EO " %s(): %s failed: %s (0x%08X(%s))\n",
375 eo_elem->eo, __func__, fail_op, err_str, err, err_status_str(err));
376
377 return err;
378}
379
380/**
381 * Request all EM-cores (worker or control) to run an EO's local start function
382 * as triggered from em_eo_start()
383 *
384 * @param eo_elem EO element pointer
385 * @param num_notif Number of notifications in notif_tbl
386 * @param notif_tbl Notification table, can be NULL if num_notif is 0
387 *
388 * @return the number of requests sent or a negative value on error
389 * @retval 0 if no requests were sent
390 * @retval >0 the number of requests sent
391 * @retval <0 on error
392 */
393int eo_start_local_req(eo_elem_t *const eo_elem,
394 int num_notif, const em_notif_t notif_tbl[])
395{
397
398 eo_local_func_call_param_init(&param);
399 param.eo_elem = eo_elem;
400 param.q_elem = NULL; /* no q_elem */
401 param.delete_queues = EM_FALSE;
402 param.ev_id = EO_START_LOCAL_REQ;
403 param.f_done_callback = eo_start_done_callback;
404 param.num_notif = num_notif;
405 param.notif_tbl = notif_tbl;
406 param.sync_operation = false;
407
408 return eo_local_func_call_req(&param);
409}
410
411/**
412 * Callback function run when all start_local functions are finished,
413 * triggered by calling em_eo_start() when using local-start functions
414 */
415static void
416eo_start_done_callback(void *args)
417{
418 const loc_func_retval_t *loc_func_retvals = args;
419 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
420 em_status_t ret;
421
422 if (unlikely(eo_elem == NULL)) {
424 EM_ESCOPE_EO_START_DONE_CB,
425 "eo_elem is NULL!");
426 return;
427 }
428
429 if (check_eo_local_status(loc_func_retvals) == EM_OK) {
430 ret = queue_enable_all(eo_elem); /* local starts OK */
431 if (ret == EM_OK)
432 eo_elem->state = EM_EO_STATE_RUNNING;
433 }
434
435 /* free the storage for local func return values */
436 em_free(loc_func_retvals->event);
437
438 /* Send events buffered during the EO-start/local-start functions */
439 eo_start_send_buffered_events(eo_elem);
440}
441
442/**
443 * Request all EM-cores (worker or control) to run an EO's local start function
444 * as triggered from em_eo_start_sync()
445 *
446 * @param eo_elem EO element pointer
447 *
448 * @return the number of requests sent or a negative value on error
449 * @retval 0 if no requests were sent
450 * @retval >0 the number of requests sent
451 * @retval <0 on error
452 */
453int eo_start_sync_local_req(eo_elem_t *const eo_elem)
454{
456
457 eo_local_func_call_param_init(&param);
458 param.eo_elem = eo_elem;
459 param.q_elem = NULL; /* no q_elem */
460 param.delete_queues = EM_FALSE;
461 param.ev_id = EO_START_SYNC_LOCAL_REQ;
462 param.f_done_callback = eo_start_sync_done_callback;
463 param.num_notif = 0;
464 param.notif_tbl = NULL;
465 param.sync_operation = true;
466
467 return eo_local_func_call_req(&param);
468}
469
470/**
471 * Callback function run when all start_local functions are finished,
472 * triggered by calling em_eo_start_sync() when using local-start functions
473 */
474static void
475eo_start_sync_done_callback(void *args)
476{
477 em_locm_t *const locm = &em_locm;
478 const loc_func_retval_t *loc_func_retvals = args;
479 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
480 em_status_t ret;
481
482 if (unlikely(eo_elem == NULL)) {
484 EM_ESCOPE_EO_START_SYNC_DONE_CB,
485 "eo_elem is NULL!");
486 return;
487 }
488
489 if (check_eo_local_status(loc_func_retvals) == EM_OK) {
490 ret = queue_enable_all(eo_elem); /* local starts OK */
491 if (ret == EM_OK)
492 eo_elem->state = EM_EO_STATE_RUNNING;
493 }
494
495 /* free the storage for local func return values */
496 em_free(loc_func_retvals->event);
497
498 /* Enable the caller of the sync API func to proceed (on this core) */
499 locm->sync_api.in_progress = false;
500
501 /*
502 * Events buffered during the EO-start/local-start functions are sent
503 * from em_eo_start_sync() after this.
504 */
505}
506
507/**
508 * Called by em_send() & variants during an EO start-function.
509 *
510 * Events sent from within the EO-start functions are buffered and sent
511 * after the start-operation has completed. Otherwise it would not be
512 * possible to reliably send events from the start-functions to the
513 * EO's own queues.
514 */
515int eo_start_buffer_events(const em_event_t events[], int num, em_queue_t queue)
516{
517 eo_elem_t *const eo_elem = em_locm.start_eo_elem;
518 const uint16_t qidx = queue_hdl2idx(queue);
519 const evhdl_t *const evhdl_tbl = (const evhdl_t *const)events;
520 stash_entry_t entry_tbl[num];
521
522 if (unlikely(eo_elem == NULL))
523 return 0;
524
525 odp_ticketlock_lock(&eo_elem->lock);
526
527 for (int i = 0; i < num; i++) {
528 /* ESV evgen dropped */
529 entry_tbl[i] = (stash_entry_t){.qidx = qidx,
530 .evptr = evhdl_tbl[i].evptr};
531 }
532
533 /* Enqueue events to internal queue */
534 int ret = odp_stash_put_u64(eo_elem->stash, &entry_tbl[0].u64, num);
535
536 if (unlikely(ret < 0))
537 ret = 0;
538
539 odp_ticketlock_unlock(&eo_elem->lock);
540
541 return ret;
542}
543
544/**
545 * @brief Helper to eo_start_send_buffered_events()
546 */
547static void eo_start_send_multi(em_event_t ev_tbl[], int num,
548 em_queue_t queue, em_event_group_t event_group)
549{
550 int num_sent = 0;
551
552 /* send events with same destination queue and event group */
553 if (event_group == EM_EVENT_GROUP_UNDEF)
554 num_sent = em_send_multi(ev_tbl, num, queue);
555 else
556 num_sent = em_send_group_multi(ev_tbl, num, queue, event_group);
557
558 if (unlikely(num_sent != num)) {
559 /* User's eo-start saw successful em_send, free here */
560 em_free_multi(&ev_tbl[num_sent], num - num_sent);
561 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_EO_START,
562 "Q:%" PRI_QUEUE " req:%u sent:%u",
563 queue, num, num_sent);
564 }
565}
566
567/**
568 * Send the buffered events at the end of the EO-start operation.
569 *
570 * Events sent from within the EO-start functions are buffered and sent
571 * after the start-operation has completed. Otherwise it would not be
572 * possible to reliably send events from the start-functions to the
573 * EO's own queues.
574 */
575void eo_start_send_buffered_events(eo_elem_t *const eo_elem)
576{
577 /* max events to send in a burst */
578 const unsigned int max_ev = 32;
579 stash_entry_t entry_tbl[max_ev];
580 em_event_t ev_tbl[max_ev];
581 event_hdr_t *ev_hdr_tbl[max_ev];
582
583 odp_ticketlock_lock(&eo_elem->lock);
584
585 /*
586 * Send the buffered events in bursts into the destination queue.
587 *
588 * This is startup: we can use some extra cycles to create the
589 * event-arrays to send in bursts.
590 */
591 int err = 0;
592 int num = 0;
593
594 do {
595 num = odp_stash_get_u64(eo_elem->stash, &entry_tbl[0].u64 /*[out]*/, max_ev);
596 if (num <= 0) {
597 if (unlikely(num < 0))
598 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_EO_START,
599 "odp_stash_get_u64() fails: %d", num);
600 goto buffered_send_exit;
601 }
602
603 for (int i = 0; i < num; i++)
604 ev_tbl[i] = (em_event_t)(uintptr_t)entry_tbl[i].evptr;
605
606 event_to_hdr_multi(ev_tbl, ev_hdr_tbl, num);
607
608 if (esv_enabled())
609 evstate_em2usr_multi(ev_tbl/*in/out*/, ev_hdr_tbl, num,
610 EVSTATE__EO_START_SEND_BUFFERED);
611
612 int tbl_idx = 0; /* index into ..._tbl[] */
613
614 /*
615 * Send in batches of 'batch_cnt' events.
616 * Each batch contains events from the same queue & evgrp.
617 */
618 do {
619 const int qidx = entry_tbl[tbl_idx].qidx;
620 const em_queue_t queue = queue_idx2hdl(qidx);
621 const em_event_group_t event_group = ev_hdr_tbl[tbl_idx]->egrp;
622 int batch_cnt = 1;
623
624 for (int i = tbl_idx + 1; i < num &&
625 entry_tbl[i].qidx == qidx &&
626 ev_hdr_tbl[i]->egrp == event_group; i++) {
627 batch_cnt++;
628 }
629
630 /* send events with same destination queue and event group */
631 eo_start_send_multi(&ev_tbl[tbl_idx], batch_cnt, queue, event_group);
632
633 tbl_idx += batch_cnt;
634 } while (tbl_idx < num);
635 } while (num > 0);
636
637buffered_send_exit:
638 err = odp_stash_destroy(eo_elem->stash);
639
640 eo_elem->stash = ODP_STASH_INVALID;
641 odp_ticketlock_unlock(&eo_elem->lock);
642
643 if (unlikely(err)) {
644 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_EO_START,
645 "odp_stash_destroy() fails: %d", err);
646 }
647}
648
649/**
650 * Request all EM-cores (worker or control) to run an EO's local stop function
651 * as triggered from em_eo_stop()
652 *
653 * @param eo_elem EO element pointer
654 * @param num_notif Number of notifications to send
655 * @param notif_tbl Array of notifications to send
656 *
657 * @return the number of requests sent or a negative value on error
658 * @retval 0 if no requests were sent
659 * @retval >0 the number of requests sent
660 * @retval <0 on error
661 */
662int eo_stop_local_req(eo_elem_t *const eo_elem,
663 int num_notif, const em_notif_t notif_tbl[])
664{
666
667 eo_local_func_call_param_init(&param);
668 param.eo_elem = eo_elem;
669 param.q_elem = NULL; /* no q_elem */
670 param.delete_queues = EM_FALSE;
671 param.ev_id = EO_STOP_LOCAL_REQ;
672 param.f_done_callback = eo_stop_done_callback;
673 param.num_notif = num_notif;
674 param.notif_tbl = notif_tbl;
675 param.sync_operation = false;
676
677 return eo_local_func_call_req(&param);
678}
679
680/**
681 * Helper to eo_stop_done_callback() and em_eo_stop()
682 *
683 * @return EO global stop function return value
684 */
685em_status_t eo_stop_done(eo_elem_t *const eo_elem)
686{
687 em_locm_t *const locm = &em_locm;
688 queue_elem_t *const save_q_elem = locm->current.q_elem;
689 queue_elem_t tmp_q_elem;
690 em_status_t ret;
691 em_eo_t eo = eo_elem->eo;
692 void *const eo_ctx = eo_elem->eo_ctx;
693
694 /* Change state here to allow em_eo_delete() from EO global stop */
695 eo_elem->state = EM_EO_STATE_CREATED; /* == EO_STATE_STOPPED */
696
697 /*
698 * Use a tmp q_elem as the 'current q_elem' to enable calling
699 * em_eo_current() from the EO stop functions.
700 * Before returning, restore the original 'current q_elem' from
701 * 'save_q_elem'.
702 */
703 memset(&tmp_q_elem, 0, sizeof(tmp_q_elem));
704 tmp_q_elem.eo = (uint16_t)(uintptr_t)eo;
705
706 locm->current.q_elem = &tmp_q_elem;
707 /*
708 * Call the Global EO stop function now that all
709 * EO local stop functions are done.
710 */
711 ret = eo_elem->stop_func(eo_ctx, eo);
712 /* Restore the original 'current q_elem' */
713 locm->current.q_elem = save_q_elem;
714
715 return ret;
716}
717
718/**
719 * Callback function run when all stop_local functions are finished,
720 * triggered by calling eo_eo_stop().
721 */
722static void
723eo_stop_done_callback(void *args)
724{
725 const loc_func_retval_t *loc_func_retvals = args;
726 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
727
728 if (unlikely(eo_elem == NULL)) {
730 EM_ESCOPE_EO_STOP_DONE_CB,
731 "eo_elem is NULL!");
732 return;
733 }
734
735 em_eo_t eo = eo_elem->eo;
736
737 (void)check_eo_local_status(loc_func_retvals);
738
739 em_status_t ret = eo_stop_done(eo_elem);
740
741 /*
742 * Note: the EO might not be available after this if the EO global stop
743 * called em_eo_delete()!
744 */
745
746 if (unlikely(ret != EM_OK))
747 INTERNAL_ERROR(EM_ERR, EM_ESCOPE_EO_STOP_DONE_CB,
748 "EO:%" PRI_EO " stop-func failed:%" PRIxSTAT "", eo, ret);
749
750 /* free the storage for local func return values */
751 em_free(loc_func_retvals->event);
752}
753
754/**
755 * Request all EM-cores (worker or control) to run an EO's local stop function
756 * as triggered from em_eo_stop_sync()
757 *
758 * @param eo_elem EO element pointer
759 *
760 * @return the number of requests sent or a negative value on error
761 * @retval 0 if no requests were sent
762 * @retval >0 the number of requests sent
763 * @retval <0 on error
764 */
765int eo_stop_sync_local_req(eo_elem_t *const eo_elem)
766{
768
769 eo_local_func_call_param_init(&param);
770 param.eo_elem = eo_elem;
771 param.q_elem = NULL; /* no q_elem */
772 param.delete_queues = EM_FALSE;
773 param.ev_id = EO_STOP_SYNC_LOCAL_REQ;
774 param.f_done_callback = eo_stop_sync_done_callback;
775 param.num_notif = 0;
776 param.notif_tbl = NULL;
777 param.sync_operation = true;
778
779 return eo_local_func_call_req(&param);
780}
781
782/**
783 * Callback function run when all stop_local functions are finished,
784 * triggered by calling eo_eo_stop_sync().
785 */
786static void
787eo_stop_sync_done_callback(void *args)
788{
789 em_locm_t *const locm = &em_locm;
790 const loc_func_retval_t *loc_func_retvals = args;
791 const eo_elem_t *eo_elem = loc_func_retvals->eo_elem;
792
793 if (unlikely(eo_elem == NULL)) {
795 EM_ESCOPE_EO_STOP_SYNC_DONE_CB,
796 "eo_elem is NULL!");
797 /* Enable the caller of the sync API func to proceed */
798 locm->sync_api.in_progress = false;
799 return;
800 }
801
802 (void)check_eo_local_status(loc_func_retvals);
803
804 /* free the storage for local func return values */
805 em_free(loc_func_retvals->event);
806
807 /* Enable the caller of the sync API func to proceed (on this core) */
808 locm->sync_api.in_progress = false;
809}
810
811int eo_remove_queue_local_req(eo_elem_t *const eo_elem, queue_elem_t *const q_elem,
812 int num_notif, const em_notif_t notif_tbl[])
813{
815
816 eo_local_func_call_param_init(&param);
817 param.eo_elem = eo_elem;
818 param.q_elem = q_elem;
819 param.delete_queues = EM_FALSE;
820 param.ev_id = EO_REM_QUEUE_LOCAL_REQ;
821 param.f_done_callback = eo_remove_queue_done_callback;
822 param.num_notif = num_notif;
823 param.notif_tbl = notif_tbl;
824 param.sync_operation = false;
825
826 return eo_local_func_call_req(&param);
827}
828
829static em_status_t
830eo_remove_queue_local(const eo_elem_t *eo_elem, const queue_elem_t *q_elem)
831{
832 (void)eo_elem;
833 (void)q_elem;
834
835 return EM_OK;
836}
837
838static void
839eo_remove_queue_done_callback(void *args)
840{
841 const loc_func_retval_t *loc_func_retvals = args;
842 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
843 queue_elem_t *const q_elem = loc_func_retvals->q_elem;
844 em_status_t ret;
845
846 if (unlikely(eo_elem == NULL || q_elem == NULL)) {
848 EM_ESCOPE_EO_REMOVE_QUEUE_DONE_CB,
849 "eo_elem/q_elem is NULL!");
850 return;
851 }
852
853 (void)check_eo_local_status(loc_func_retvals);
854
855 /* Remove the queue from the EO */
856 ret = eo_rem_queue(eo_elem, q_elem);
857
858 if (unlikely(ret != EM_OK))
859 INTERNAL_ERROR(ret, EM_ESCOPE_EO_REMOVE_QUEUE_DONE_CB,
860 "EO:%" PRI_EO " remove Q:%" PRI_QUEUE " failed",
861 eo_elem->eo, q_elem->queue);
862
863 /* free the storage for local func return values */
864 em_free(loc_func_retvals->event);
865}
866
867int eo_remove_queue_sync_local_req(eo_elem_t *const eo_elem,
868 queue_elem_t *const q_elem)
869{
871
872 eo_local_func_call_param_init(&param);
873 param.eo_elem = eo_elem;
874 param.q_elem = q_elem;
875 param.delete_queues = EM_FALSE;
876 param.ev_id = EO_REM_QUEUE_SYNC_LOCAL_REQ;
877 param.f_done_callback = eo_remove_queue_sync_done_callback;
878 param.num_notif = 0;
879 param.notif_tbl = NULL;
880 param.sync_operation = true;
881
882 return eo_local_func_call_req(&param);
883}
884
885static em_status_t
886eo_remove_queue_sync_local(const eo_elem_t *eo_elem, const queue_elem_t *q_elem)
887{
888 (void)eo_elem;
889 (void)q_elem;
890
891 return EM_OK;
892}
893
894static void
895eo_remove_queue_sync_done_callback(void *args)
896{
897 em_locm_t *const locm = &em_locm;
898 const loc_func_retval_t *loc_func_retvals = args;
899 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
900 queue_elem_t *const q_elem = loc_func_retvals->q_elem;
901 em_status_t ret;
902
903 if (unlikely(eo_elem == NULL || q_elem == NULL)) {
905 EM_ESCOPE_EO_REMOVE_QUEUE_SYNC_DONE_CB,
906 "eo_elem/q_elem is NULL!");
907 /* Enable the caller of the sync API func to proceed */
908 locm->sync_api.in_progress = false;
909 return;
910 }
911
912 (void)check_eo_local_status(loc_func_retvals);
913
914 /* Remove the queue from the EO */
915 ret = eo_rem_queue(eo_elem, q_elem);
916
917 if (unlikely(ret != EM_OK))
918 INTERNAL_ERROR(ret,
919 EM_ESCOPE_EO_REMOVE_QUEUE_SYNC_DONE_CB,
920 "EO:%" PRI_EO " remove Q:%" PRI_QUEUE " failed",
921 eo_elem->eo, q_elem->queue);
922
923 /* free the storage for local func return values */
924 em_free(loc_func_retvals->event);
925
926 /* Enable the caller of the sync API func to proceed (on this core) */
927 locm->sync_api.in_progress = false;
928}
929
930int eo_remove_queue_all_local_req(eo_elem_t *const eo_elem, int delete_queues,
931 int num_notif, const em_notif_t notif_tbl[])
932{
934
935 eo_local_func_call_param_init(&param);
936 param.eo_elem = eo_elem;
937 param.q_elem = NULL; /* no q_elem */
938 param.delete_queues = delete_queues;
939 param.ev_id = EO_REM_QUEUE_ALL_LOCAL_REQ;
940 param.f_done_callback = eo_remove_queue_all_done_callback;
941 param.num_notif = num_notif;
942 param.notif_tbl = notif_tbl;
943 param.sync_operation = false;
944
945 return eo_local_func_call_req(&param);
946}
947
948static em_status_t
949eo_remove_queue_all_local(const eo_elem_t *eo_elem, int delete_queues)
950{
951 (void)eo_elem;
952 (void)delete_queues;
953
954 return EM_OK;
955}
956
957static void
958eo_remove_queue_all_done_callback(void *args)
959{
960 const loc_func_retval_t *loc_func_retvals = args;
961 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
962 int delete_queues = loc_func_retvals->delete_queues;
963 em_status_t ret;
964
965 if (unlikely(eo_elem == NULL)) {
967 EM_ESCOPE_EO_REMOVE_QUEUE_ALL_DONE_CB,
968 "eo_elem is NULL!");
969 return;
970 }
971
972 (void)check_eo_local_status(loc_func_retvals);
973
974 /* Remove or delete all the EO's queues */
975 if (delete_queues)
976 ret = eo_delete_queue_all(eo_elem);
977 else
978 ret = eo_rem_queue_all(eo_elem);
979
980 if (unlikely(ret != EM_OK))
981 INTERNAL_ERROR(ret, EM_ESCOPE_EO_REMOVE_QUEUE_ALL_DONE_CB,
982 "EO:%" PRI_EO " removing all queues failed",
983 eo_elem->eo);
984
985 /* free the storage for local func return values */
986 em_free(loc_func_retvals->event);
987}
988
989int eo_remove_queue_all_sync_local_req(eo_elem_t *const eo_elem, int delete_queues)
990{
992
993 eo_local_func_call_param_init(&param);
994 param.eo_elem = eo_elem;
995 param.q_elem = NULL; /* no q_elem */
996 param.delete_queues = delete_queues;
997 param.ev_id = EO_REM_QUEUE_ALL_SYNC_LOCAL_REQ;
998 param.f_done_callback = eo_remove_queue_all_sync_done_callback;
999 param.num_notif = 0;
1000 param.notif_tbl = NULL;
1001 param.sync_operation = true;
1002
1003 return eo_local_func_call_req(&param);
1004}
1005
1006static em_status_t
1007eo_remove_queue_all_sync_local(const eo_elem_t *eo_elem, int delete_queues)
1008{
1009 (void)eo_elem;
1010 (void)delete_queues;
1011
1012 return EM_OK;
1013}
1014
1015static void
1016eo_remove_queue_all_sync_done_callback(void *args)
1017{
1018 em_locm_t *const locm = &em_locm;
1019 const loc_func_retval_t *loc_func_retvals = args;
1020 eo_elem_t *const eo_elem = loc_func_retvals->eo_elem;
1021 int delete_queues = loc_func_retvals->delete_queues;
1022 em_status_t ret;
1023
1024 if (unlikely(eo_elem == NULL)) {
1026 EM_ESCOPE_EO_REMOVE_QUEUE_ALL_SYNC_DONE_CB,
1027 "eo_elem is NULL!");
1028 /* Enable the caller of the sync API func to proceed */
1029 locm->sync_api.in_progress = false;
1030 return;
1031 }
1032
1033 (void)check_eo_local_status(loc_func_retvals);
1034
1035 /* Remove or delete all the EO's queues */
1036 if (delete_queues)
1037 ret = eo_delete_queue_all(eo_elem);
1038 else
1039 ret = eo_rem_queue_all(eo_elem);
1040
1041 if (unlikely(ret != EM_OK))
1042 INTERNAL_ERROR(ret,
1043 EM_ESCOPE_EO_REMOVE_QUEUE_ALL_SYNC_DONE_CB,
1044 "EO:%" PRI_EO " removing all queues failed",
1045 eo_elem->eo);
1046
1047 /* free the storage for local func return values */
1048 em_free(loc_func_retvals->event);
1049
1050 /* Enable the caller of the sync API func to proceed (on this core) */
1051 locm->sync_api.in_progress = false;
1052}
1053
1054static em_status_t
1055check_eo_local_status(const loc_func_retval_t *loc_func_retvals)
1056{
1057 const em_core_mask_t *core_mask = &loc_func_retvals->core_mask;
1058 bool local_fail = false;
1059 em_status_t err;
1060
1061 int idx = em_core_mask_first(core_mask);
1062
1063 while (idx >= 0) {
1064 err = loc_func_retvals->core[idx];
1065 if (err != EM_OK) {
1066 local_fail = true;
1067 break;
1068 }
1069 idx = em_core_mask_next(core_mask, idx);
1070 }
1071
1072 /* All OK - return */
1073 if (!local_fail)
1074 return EM_OK;
1075
1076 /*
1077 * Errors found - report them
1078 */
1079 static const char core_err[] = "coreXX:0x12345678 ";
1080 const int core_count = em_core_mask_count(core_mask);
1081 const size_t err_sz = core_count * sizeof(core_err);
1082 char errmsg[err_sz];
1083 int c = 0;
1084
1085 idx = em_core_mask_first(core_mask);
1086
1087 while (idx >= 0) {
1088 err = loc_func_retvals->core[idx];
1089 if (err != EM_OK) {
1090 int n = snprintf(&errmsg[c], sizeof(core_err),
1091 "core%02d:0x%08X ", idx, err);
1092 if ((unsigned int)n >= sizeof(core_err))
1093 break;
1094 c += n;
1095 }
1096 idx = em_core_mask_next(core_mask, idx);
1097 }
1098 errmsg[err_sz - 1] = '\0';
1099
1100 INTERNAL_ERROR(EM_ERR, EM_ESCOPE_EVENT_INTERNAL_LFUNC_CALL,
1101 "\nLocal start function failed on cores:\n"
1102 "%s", errmsg);
1103 return EM_ERR;
1104}
1105
1106static void
1107eo_local_func_call_param_init(eo_local_func_call_param_t *param)
1108{
1109 memset(param, 0, sizeof(*param));
1110}
1111
1112/**
1113 * Request a function to be run on each EM-core (worker or control) and call
1114 * 'f_done_callback(arg_ptr)' when all those functions have completed.
1115 *
1116 * @param param EO local function call parameters
1117 *
1118 * @return the number of requests sent or a negative value on error
1119 * @retval 0 if no requests were sent
1120 * @retval >0 the number of requests sent
1121 * @retval <0 on error
1122 */
1123static int
1124eo_local_func_call_req(const eo_local_func_call_param_t *param)
1125{
1126 core_map_t *const core_map = &em_shm->core_map;
1127
1128 em_event_t event = em_alloc(sizeof(internal_event_t),
1130 if (unlikely(event == EM_EVENT_UNDEF)) {
1131 INTERNAL_ERROR(EM_ERR_ALLOC_FAILED, EM_ESCOPE_EO_LOCAL_FUNC_CALL_REQ,
1132 "Internal event (%u) allocation failed", param->ev_id);
1133 return -1;
1134 }
1135
1136 em_event_t tmp = em_alloc(sizeof(loc_func_retval_t),
1138 if (unlikely(tmp == EM_EVENT_UNDEF)) {
1139 em_free(event);
1140 INTERNAL_ERROR(EM_ERR_ALLOC_FAILED, EM_ESCOPE_EO_LOCAL_FUNC_CALL_REQ,
1141 "Internal loc_func_retval_t allocation failed");
1142 return -1;
1143 }
1144
1145 /*
1146 * Take the core_map rwlock for reading - can afford to keep it for a
1147 * "long" time since blocking write-access is only needed in core
1148 * add/rem scenarios (via em_init/term_local() and the older
1149 * em_init/term_core()). The core ctrl events should be sent before
1150 * releasing the lock to prevent add/rem-core actions while sending
1151 * these ctrl events.
1152 */
1153 odp_rwlock_read_lock(&core_map->rwlock);
1154
1155 uint32_t core_count = odp_atomic_load_u32(&core_map->current_core_count);
1156
1157 if (core_count == 0) {
1158 /* No EM-cores available, free resources and return */
1159 odp_rwlock_read_unlock(&core_map->rwlock);
1160 em_free(event);
1161 em_free(tmp);
1162 return 0;
1163 }
1164
1165 uint32_t num_reqs = core_count;
1166 uint32_t free_count = num_reqs + 1; /* all cores + 'done' event */
1167 em_core_mask_t core_mask;
1168 odp_thrmask_t thr_mask;
1169
1170 odp_thrmask_copy(&thr_mask, &core_map->rwlocked.thrmask_emcores);
1171 em_core_mask_copy(&core_mask, &core_map->rwlocked.logic_mask);
1172
1173 if (unlikely(core_count != (uint32_t)em_core_mask_count(&core_mask) ||
1174 core_count != (uint32_t)odp_thrmask_count(&thr_mask))) {
1175 odp_rwlock_read_unlock(&core_map->rwlock);
1176 em_free(event);
1177 em_free(tmp);
1178 INTERNAL_ERROR(EM_FATAL(EM_ERR_BAD_STATE), EM_ESCOPE_EO_LOCAL_FUNC_CALL_REQ,
1179 "Core count mismatch: core_count=%" PRIu32 "\n"
1180 " count:core_mask=%" PRIu32 "\n"
1181 " count:thr_mask=%" PRIu32 "",
1182 core_count, em_core_mask_count(&core_mask),
1183 odp_thrmask_count(&thr_mask));
1184 return -1;
1185 }
1186
1187 internal_event_t *i_event = em_event_pointer(event);
1188
1189 i_event->id = param->ev_id;
1190 i_event->loc_func.eo_elem = param->eo_elem;
1191 i_event->loc_func.q_elem = param->q_elem;
1192 i_event->loc_func.delete_queues = param->delete_queues;
1193
1194 loc_func_retval_t *loc_func_retvals = em_event_pointer(tmp);
1195
1196 loc_func_retvals->eo_elem = param->eo_elem;
1197 loc_func_retvals->q_elem = param->q_elem;
1198 loc_func_retvals->delete_queues = param->delete_queues;
1199 loc_func_retvals->event = tmp; /* store event handle for em_free() */
1200 odp_atomic_init_u32(&loc_func_retvals->free_at_zero, free_count);
1201 em_core_mask_copy(&loc_func_retvals->core_mask, &core_mask);
1202 memset(&loc_func_retvals->core[0], EM_OK, sizeof(loc_func_retvals->core));
1203
1204 /* ptr to retval storage so loc func calls can record retval there */
1205 i_event->loc_func.retvals = loc_func_retvals;
1206
1207 /* Give ptr to retval storage also to 'done' function */
1208 void *f_done_arg_ptr = loc_func_retvals;
1209
1210 int err = send_core_ctrl_events(&thr_mask, event,
1211 param->f_done_callback, f_done_arg_ptr,
1212 param->num_notif, param->notif_tbl,
1213 param->sync_operation);
1214
1215 odp_rwlock_read_unlock(&core_map->rwlock);
1216
1217 if (unlikely(err)) {
1218 char mask_str[ODP_THRMASK_STR_SIZE];
1219 uint32_t unsent_cnt = err;
1220 uint32_t cnt;
1221
1222 em_free(event);
1223 cnt = odp_atomic_fetch_sub_u32(&loc_func_retvals->free_at_zero,
1224 unsent_cnt + 1);
1225 if (cnt == unsent_cnt + 1)
1226 em_free(tmp);
1227
1228 odp_thrmask_to_str(&thr_mask, mask_str, ODP_THRMASK_STR_SIZE);
1229 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_EO_LOCAL_FUNC_CALL_REQ,
1230 "send_core_ctrl_events(mask=%s) failed", mask_str);
1231 return -1;
1232 }
1233
1234 return num_reqs;
1235}
1236
1237/**
1238 * EM internal event handler (see em_internal_event.c&h)
1239 * Handle the internal event requesting a local function call.
1240 */
1241void i_event__eo_local_func_call_req(const internal_event_t *i_ev)
1242{
1243 em_locm_t *const locm = &em_locm;
1244 const uint64_t f_type = i_ev->loc_func.id;
1245 eo_elem_t *eo_elem = i_ev->loc_func.eo_elem;
1246 const queue_elem_t *q_elem = i_ev->loc_func.q_elem;
1247 int delete_queues = i_ev->loc_func.delete_queues;
1248 loc_func_retval_t *const loc_func_retvals = i_ev->loc_func.retvals;
1249 em_status_t status = EM_ERR;
1250 queue_elem_t *const save_q_elem = locm->current.q_elem;
1251 queue_elem_t tmp_q_elem;
1252
1253 switch (f_type) {
1254 case EO_START_SYNC_LOCAL_REQ:
1255 case EO_START_LOCAL_REQ:
1256 if (!eo_elem->start_local_func) {
1257 /* No local start func given */
1258 status = EM_ERR_BAD_POINTER;
1259 break;
1260 }
1261 /*
1262 * Use a tmp q_elem as the 'current q_elem' to enable calling
1263 * em_eo_current() from the EO start functions.
1264 * Before returning, restore the original 'current q_elem' from
1265 * 'save_q_elem'.
1266 */
1267 memset(&tmp_q_elem, 0, sizeof(tmp_q_elem));
1268 tmp_q_elem.eo = (uint16_t)(uintptr_t)eo_elem->eo;
1269 locm->current.q_elem = &tmp_q_elem;
1270
1271 locm->start_eo_elem = eo_elem;
1272 status = eo_elem->start_local_func(eo_elem->eo_ctx,
1273 eo_elem->eo);
1274 locm->start_eo_elem = NULL;
1275 /* Restore the original 'current q_elem' */
1276 locm->current.q_elem = save_q_elem;
1277 break;
1278
1279 case EO_STOP_SYNC_LOCAL_REQ:
1280 case EO_STOP_LOCAL_REQ:
1281 if (eo_elem->stop_local_func) {
1282 /*
1283 * Use a tmp q_elem as the 'current q_elem' to enable
1284 * calling em_eo_current() from the EO start functions.
1285 * Before returning, restore the original 'current
1286 * q_elem' from 'save_q_elem'.
1287 */
1288 memset(&tmp_q_elem, 0, sizeof(tmp_q_elem));
1289 tmp_q_elem.eo = (uint16_t)(uintptr_t)eo_elem->eo;
1290 locm->current.q_elem = &tmp_q_elem;
1291
1292 status = eo_elem->stop_local_func(eo_elem->eo_ctx,
1293 eo_elem->eo);
1294 /* Restore the original 'current q_elem' */
1295 locm->current.q_elem = save_q_elem;
1296 } else {
1297 status = EM_OK; /* No local stop func given */
1298 }
1299 break;
1300
1301 case EO_REM_QUEUE_LOCAL_REQ:
1302 status = eo_remove_queue_local(eo_elem, q_elem);
1303 break;
1304 case EO_REM_QUEUE_SYNC_LOCAL_REQ:
1305 status = eo_remove_queue_sync_local(eo_elem, q_elem);
1306 break;
1307 case EO_REM_QUEUE_ALL_LOCAL_REQ:
1308 status = eo_remove_queue_all_local(eo_elem, delete_queues);
1309 break;
1310 case EO_REM_QUEUE_ALL_SYNC_LOCAL_REQ:
1311 status = eo_remove_queue_all_sync_local(eo_elem, delete_queues);
1312 break;
1313 default:
1314 status = EM_FATAL(EM_ERR_BAD_ID);
1315 break;
1316 }
1317
1318 if (status != EM_OK) {
1319 /* store failing status, egrp 'done' can check if all ok */
1320 loc_func_retvals->core[em_core_id()] = status;
1321
1322 INTERNAL_ERROR(status, EM_ESCOPE_EVENT_INTERNAL_LFUNC_CALL,
1323 "EO:%" PRI_EO "-%s:Local func(%" PRIx64 ")fail",
1324 eo_elem->eo, eo_elem->name, f_type);
1325 }
1326
1327 /*
1328 * In case of setup error, determine if 'loc_func_retvals' should be
1329 * freed here, in the setup code in eo_local_func_call_req() or
1330 * normally in a successful case in the
1331 * eo_start/stop_local__done_callback() function when the event group
1332 * completion notif is handled.
1333 */
1334 uint32_t cnt = odp_atomic_fetch_sub_u32(&loc_func_retvals->free_at_zero, 1);
1335
1336 if (unlikely(cnt == 1)) {
1337 (void)check_eo_local_status(loc_func_retvals);
1338 em_free(loc_func_retvals->event);
1339 }
1340}
1341
1342unsigned int eo_count(void)
1343{
1344 return odp_atomic_load_u32(&em_shm->eo_count);
1345}
1346
1347size_t eo_name(const eo_elem_t *const eo_elem,
1348 char name[/*out*/], const size_t maxlen)
1349{
1350 size_t len;
1351
1352 len = strnlen(eo_elem->name, sizeof(eo_elem->name) - 1);
1353 if (maxlen - 1 < len)
1354 len = maxlen - 1;
1355
1356 memcpy(name, eo_elem->name, len);
1357 name[len] = '\0';
1358
1359 return len;
1360}
1361
1362static const char *state_to_str(em_eo_state_t state)
1363{
1364 const char *state_str;
1365
1366 switch (state) {
1367 case EM_EO_STATE_UNDEF:
1368 state_str = "UNDEF";
1369 break;
1371 state_str = "CREATED";
1372 break;
1374 state_str = "STARTING";
1375 break;
1377 state_str = "RUNNING";
1378 break;
1380 state_str = "STOPPING";
1381 break;
1382 case EM_EO_STATE_ERROR:
1383 state_str = "ERROR";
1384 break;
1385 default:
1386 state_str = "UNKNOWN";
1387 break;
1388 }
1389
1390 return state_str;
1391}
1392
1393#define EO_INFO_HDR_FMT \
1394"Number of EOs: %d\n\n" \
1395"ID Name State Start-local Stop-local" \
1396" Multi-rcv Max-events Err-hdl Q-num EO-ctx\n" \
1397"---------------------------------------------------------------------------" \
1398"-----------------------------------------------\n%s\n"
1399
1400#define EO_INFO_LEN 123
1401#define EO_INFO_FMT "%-10" PRI_EO "%-32s%-10s%-13c%-12c%-11c%-12d%-9c%-7d%-6c\n"
1402
1403void eo_info_print_all(void)
1404{
1405 unsigned int num_eo;
1406 eo_elem_t *eo_elem;
1407 int len = 0;
1408 int n_print = 0;
1409 em_eo_t eo = em_eo_first(&num_eo);
1410
1411 /*
1412 * num_eo may not match the amount of EOs actually returned by iterating
1413 * using em_eo_next() if EOs are added or removed in parallel by
1414 * another core. Thus space for 10 extra EOs is reserved. If more than 10
1415 * EOs are added by other cores in parallel, we only print information of
1416 * the (num_eo + 10) EOs.
1417 *
1418 * The extra 1 byte is reserved for the terminating null byte.
1419 */
1420 const int eo_info_str_len = (num_eo + 10) * EO_INFO_LEN + 1;
1421 char eo_info_str[eo_info_str_len];
1422
1423 while (eo != EM_EO_UNDEF) {
1424 eo_elem = eo_elem_get(eo);
1425 if (unlikely(eo_elem == NULL || !eo_allocated(eo_elem))) {
1426 eo = em_eo_next();
1427 continue;
1428 }
1429
1430 n_print = snprintf(eo_info_str + len,
1431 eo_info_str_len - len,
1432 EO_INFO_FMT, eo, eo_elem->name,
1433 state_to_str(eo_elem->state),
1434 eo_elem->start_local_func ? 'Y' : 'N',
1435 eo_elem->stop_local_func ? 'Y' : 'N',
1436 eo_elem->use_multi_rcv ? 'Y' : 'N',
1437 eo_elem->max_events,
1438 eo_elem->error_handler_func ? 'Y' : 'N',
1439 odp_atomic_load_u32(&eo_elem->num_queues),
1440 eo_elem->eo_ctx ? 'Y' : 'N');
1441
1442 /* Not enough space to hold more eo info */
1443 if (n_print >= eo_info_str_len - len)
1444 break;
1445
1446 len += n_print;
1447 eo = em_eo_next();
1448 }
1449
1450 /* No EO */
1451 if (!len) {
1452 EM_PRINT("No EO has been created!\n");
1453 return;
1454 }
1455
1456 /*
1457 * To prevent printing incomplete information of the last eo when there
1458 * is not enough space to hold all eo info.
1459 */
1460 eo_info_str[len] = '\0';
1461 EM_PRINT(EO_INFO_HDR_FMT, num_eo, eo_info_str);
1462}
1463
1464#define EO_Q_INFO_HDR_FMT \
1465"EO %" PRI_EO "(%s) has %d queue(s):\n\n" \
1466"Handle Name Priority Type State Qgrp" \
1467" Ctx\n" \
1468"---------------------------------------------------------------------------" \
1469"---------\n" \
1470"%s\n"
1471
1472#define EO_Q_INFO_LEN 85
1473#define EO_Q_INFO_FMT \
1474"%-10" PRI_QUEUE "%-32s%-10d%-10s%-9s%-10" PRI_QGRP "%-3c\n" /*85 characters*/
1475
1476void eo_queue_info_print(em_eo_t eo)
1477{
1478 unsigned int q_num;
1479 em_queue_t q;
1480 const queue_elem_t *q_elem;
1481 char q_name[EM_QUEUE_NAME_LEN];
1482 int len = 0;
1483 int n_print = 0;
1484 const eo_elem_t *eo_elem = eo_elem_get(eo);
1485
1486 if (unlikely(eo_elem == NULL || !eo_allocated(eo_elem))) {
1487 EM_PRINT("EO %" PRI_EO " is not created!\n", eo);
1488 return;
1489 }
1490
1491 q = em_eo_queue_first(&q_num, eo);
1492
1493 /*
1494 * q_num may not match the amount of queues actually returned by iterating
1495 * using em_eo_queue_next() if queues are added or removed in parallel
1496 * by another core. Thus space for 10 extra queues is reserved. If more
1497 * than 10 queues are added to this EO by other cores, we only print info
1498 * of the (q_num + 10) queues.
1499 *
1500 * The extra 1 byte is reserved for the terminating null byte.
1501 */
1502 const int eo_q_info_str_len = (q_num + 10) * EO_Q_INFO_LEN + 1;
1503 char eo_q_info_str[eo_q_info_str_len];
1504
1505 while (q != EM_QUEUE_UNDEF) {
1506 q_elem = queue_elem_get(q);
1507 if (unlikely(q_elem == NULL || !queue_allocated(q_elem))) {
1508 q = em_eo_queue_next();
1509 continue;
1510 }
1511
1512 queue_name(q_elem, q_name, EM_QUEUE_NAME_LEN - 1);
1513
1514 n_print = snprintf(eo_q_info_str + len,
1515 eo_q_info_str_len - len,
1516 EO_Q_INFO_FMT,
1517 q, q_name, q_elem->priority,
1518 queue_type_str(q_elem->type),
1519 queue_state_str(q_elem->state),
1520 q_elem->queue_group,
1521 q_elem->context ? 'Y' : 'N');
1522
1523 /* Not enough space to hold more queue info */
1524 if (n_print >= eo_q_info_str_len - len)
1525 break;
1526
1527 len += n_print;
1528 q = em_eo_queue_next();
1529 }
1530
1531 /* EO has no queue */
1532 if (!len) {
1533 EM_PRINT("EO %" PRI_EO "(%s) has no queue!\n", eo, eo_elem->name);
1534 return;
1535 }
1536
1537 /*
1538 * To prevent printing incomplete information of the last queue when
1539 * there is not enough space to hold all queue info.
1540 */
1541 eo_q_info_str[len] = '\0';
1542 EM_PRINT(EO_Q_INFO_HDR_FMT, eo, eo_elem->name, q_num, eo_q_info_str);
1543}
1544
1545/**
1546 * @brief Create a stash used to buffer events sent during EO-start
1547 */
1548odp_stash_t eo_start_stash_create(void)
1549{
1550 unsigned int num_obj = 0;
1551 odp_stash_capability_t stash_capa;
1552 odp_stash_param_t stash_param;
1553 odp_stash_t stash = ODP_STASH_INVALID;
1554
1555 int ret = odp_stash_capability(&stash_capa, ODP_STASH_TYPE_FIFO);
1556
1557 if (ret != 0)
1558 return ODP_STASH_INVALID;
1559
1560 odp_stash_param_init(&stash_param);
1561
1562 stash_param.type = ODP_STASH_TYPE_FIFO;
1563 stash_param.put_mode = ODP_STASH_OP_MT;
1564 stash_param.get_mode = ODP_STASH_OP_MT;
1565
1566 /* Stash size: use EM default queue size value from config file: */
1567 num_obj = em_shm->opt.queue.min_events_default;
1568 if (num_obj != 0)
1569 stash_param.num_obj = num_obj;
1570 /* else: use odp default as set by odp_stash_param_init() */
1571
1572 stash_param.obj_size = sizeof(uint64_t);
1573 if (stash_param.num_obj > stash_capa.max_num.u64) {
1574 EM_LOG(EM_LOG_PRINT,
1575 "%s(): req stash.num_obj(%" PRIu64 ") > capa.max_num.u64(%" PRIu64 ").\n"
1576 " ==> using max value:%" PRIu64 "\n", __func__,
1577 stash_param.num_obj, stash_capa.max_num.u64, stash_capa.max_num.u64);
1578 stash_param.num_obj = stash_capa.max_num.u64;
1579 }
1580
1581 stash_param.cache_size = 0; /* No core local caching */
1582
1583 stash = odp_stash_create(NULL, &stash_param);
1584 if (unlikely(stash == ODP_STASH_INVALID))
1585 return ODP_STASH_INVALID;
1586
1587 return stash;
1588}
1589
1590/**
1591 * @brief Run EO local-start functions for all EOs on a new core (if configured)
1592 *
1593 * If the EM-core is added after initial startup and EO:s are already
1594 * created and running: check all running EO's and call the local start
1595 * functions (if available).
1596 */
1597void eo_start_local_fn_at_init(void)
1598{
1599 unsigned int num = 0;
1600 em_eo_t eo = em_eo_first(&num);
1601
1602 if (num == 0)
1603 return;
1604
1605 DBG_PRINT("%s():%d EOs available, EM-core:%02d\n", __func__, num, em_core_id());
1606
1607 while (eo != EM_EO_UNDEF) {
1608 eo_elem_t *eo_elem = eo_elem_get(eo);
1609
1610 if (likely(eo_elem)) {
1611 odp_ticketlock_lock(&eo_elem->lock);
1612 bool is_locked = true;
1613 bool cfg_file_opt = em_shm->opt.eo.start_local_fn_at_init;
1615
1616 bool has_local_fn = eo_elem->start_local_func &&
1617 eo_elem->state == EM_EO_STATE_RUNNING;
1618 bool has_run_mode = mode == EM_EO_START_LOCAL_MODE_INIT_RUN ||
1620 cfg_file_opt);
1621
1622 if (has_local_fn && has_run_mode) {
1623 em_start_local_func_t loc_fn = eo_elem->start_local_func;
1624 void *eo_ctx = eo_elem->eo_ctx;
1625
1626 odp_ticketlock_unlock(&eo_elem->lock);
1627 is_locked = false;
1628
1629 /* Run the EO local start function (unlocked) */
1630 loc_fn(eo_ctx, eo);
1631 }
1632
1633 if (is_locked)
1634 odp_ticketlock_unlock(&eo_elem->lock);
1635 }
1636 eo = em_eo_next();
1637 }
1638 DBG_PRINT("%s(): EO local starts done, EM-core:%02d\n", __func__, em_core_id());
1639}
1640
1641/**
1642 * @brief Run EO local-stop funcs for all EOs during term-core (if configured)
1643 *
1644 * If the EM-core is removed while EOs are running:
1645 * check all running EO's and call the local stop function (if available).
1646 */
1647void eo_stop_local_fn_at_term(void)
1648{
1649 unsigned int num = 0;
1650 em_eo_t eo = em_eo_first(&num);
1651
1652 if (num == 0)
1653 return;
1654
1655 DBG_PRINT("%s():%d EOs available, EM-core:%02d\n", __func__, num, em_core_id());
1656
1657 while (eo != EM_EO_UNDEF) {
1658 eo_elem_t *eo_elem = eo_elem_get(eo);
1659
1660 if (likely(eo_elem)) {
1661 odp_ticketlock_lock(&eo_elem->lock);
1662 bool is_locked = true;
1663 bool cfg_file_opt = em_shm->opt.eo.stop_local_fn_at_term;
1665
1666 bool has_local_fn = eo_elem->stop_local_func &&
1667 eo_elem->state == EM_EO_STATE_RUNNING;
1668 bool has_run_mode = mode == EM_EO_STOP_LOCAL_MODE_TERM_RUN ||
1670 cfg_file_opt);
1671
1672 if (has_local_fn && has_run_mode) {
1673 em_stop_local_func_t loc_fn = eo_elem->stop_local_func;
1674 void *eo_ctx = eo_elem->eo_ctx;
1675
1676 odp_ticketlock_unlock(&eo_elem->lock);
1677 is_locked = false;
1678
1679 /* Run the EO local stop function (unlocked) */
1680 loc_fn(eo_ctx, eo);
1681 }
1682
1683 if (is_locked)
1684 odp_ticketlock_unlock(&eo_elem->lock);
1685 }
1686 eo = em_eo_next();
1687 }
1688 DBG_PRINT("%s(): EO local stops done, EM-core:%02d\n", __func__, em_core_id());
1689}
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
int send_core_ctrl_events(const odp_thrmask_t *const thr_mask, em_event_t ctrl_event, void(*f_done_callback)(void *arg_ptr), void *f_done_arg_ptr, int num_notif, const em_notif_t notif_tbl[], bool sync_operation)
Sends an internal control event to each thread set in 'mask'.
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
uint8_t queue_state_t
@ EM_QUEUE_STATE_INIT
@ EM_QUEUE_STATE_BIND
#define EM_QUEUE_NAME_LEN
#define EM_MAX_EOS
#define EM_POOL_DEFAULT
int em_core_mask_count(const em_core_mask_t *mask)
int em_core_mask_first(const em_core_mask_t *mask)
void em_core_mask_copy(em_core_mask_t *dst, const em_core_mask_t *src)
int em_core_mask_next(const em_core_mask_t *mask, int idx)
#define PRI_QUEUE
#define PRI_EO
#define EM_EVENT_UNDEF
#define EM_FALSE
#define EM_EVENT_GROUP_UNDEF
#define EM_QUEUE_UNDEF
#define EM_EO_UNDEF
int em_core_id(void)
em_eo_t em_eo_first(unsigned int *num)
em_eo_start_local_mode_t
em_eo_state_t
em_status_t(* em_start_local_func_t)(void *eo_ctx, em_eo_t eo)
em_eo_stop_local_mode_t
em_status_t(* em_stop_local_func_t)(void *eo_ctx, em_eo_t eo)
em_eo_t em_eo_next(void)
em_queue_t em_eo_queue_next(void)
em_queue_t em_eo_queue_first(unsigned int *num, em_eo_t eo)
@ EM_EO_START_LOCAL_MODE_CONFIG_FILE
@ EM_EO_START_LOCAL_MODE_INIT_RUN
@ EM_EO_STATE_STOPPING
@ EM_EO_STATE_CREATED
@ EM_EO_STATE_RUNNING
@ EM_EO_STATE_ERROR
@ EM_EO_STATE_UNDEF
@ EM_EO_STATE_STARTING
@ EM_EO_STOP_LOCAL_MODE_TERM_RUN
@ EM_EO_STOP_LOCAL_MODE_CONFIG_FILE
#define EM_OK
#define EM_FATAL(error)
uint32_t em_status_t
@ EM_ERR_BAD_ID
@ EM_ERR_ALLOC_FAILED
@ EM_ERR_BAD_STATE
@ EM_ERR_LIB_FAILED
@ EM_ERR_BAD_POINTER
int em_send_group_multi(const em_event_t events[], int num, em_queue_t queue, em_event_group_t event_group)
int em_send_multi(const em_event_t events[], int num, em_queue_t queue)
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
#define OBJSUBPOOLS_MAX
Definition objpool.h:62
queue_elem_t * q_elem
Definition em_mem.h:214
odp_rwlock_t rwlock
odp_atomic_u32_t current_core_count
odp_thrmask_t thrmask_emcores
em_core_mask_t logic_mask
em_locm_current_t current
Definition em_mem.h:228
eo_elem_t * start_eo_elem
Definition em_mem.h:275
sync_api_t sync_api
Definition em_mem.h:286
em_cfgfile_opts_t opt
Definition em_mem.h:99
objpool_elem_t eo_pool_elem
Definition em_eo_types.h:96
em_error_handler_t error_handler_func
Definition em_eo_types.h:82
em_eo_stop_local_mode_t stop_local_mode
Definition em_eo_types.h:71
em_receive_func_t receive_func
Definition em_eo_types.h:77
em_stop_func_t stop_func
Definition em_eo_types.h:65
odp_ticketlock_t lock
Definition em_eo_types.h:86
em_eo_state_t state
Definition em_eo_types.h:59
em_stop_local_func_t stop_local_func
Definition em_eo_types.h:67
int max_events
Definition em_eo_types.h:75
list_node_t queue_list
Definition em_eo_types.h:88
em_receive_multi_func_t receive_multi_func
Definition em_eo_types.h:79
odp_atomic_u32_t num_queues
Definition em_eo_types.h:90
em_eo_start_local_mode_t start_local_mode
Definition em_eo_types.h:69
odp_stash_t stash
Definition em_eo_types.h:92
em_start_local_func_t start_local_func
Definition em_eo_types.h:63
int use_multi_rcv
Definition em_eo_types.h:73
em_eo_t eo
Definition em_eo_types.h:94
void * eo_ctx
Definition em_eo_types.h:84
eo_elem_t eo_elem[EM_MAX_EOS]
em_event_group_t egrp
em_status_t core[EM_MAX_CORES]
odp_atomic_u32_t free_at_zero
uint32_t subpool_idx
Definition objpool.h:74
uint16_t max_events
queue_state_t state
list_node_t eo_queue_node
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
struct internal_event_t::@51 loc_func