EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
event_machine_atomic_group.c
1/*
2 * Copyright (c) 2014-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 /* for strnlen() */
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_error.h"
52#include "em_internal_event.h"
54#include "em_mem.h"
55#include "em_queue.h"
56#include "em_queue_group.h"
57#include "em_queue_inline.h"
58#include "em_queue_types.h"
59#include "misc/objpool.h"
60
61/* per core (thread) state for em_atomic_group_next() */
62static ENV_LOCAL unsigned int _agrp_tbl_iter_idx;
63/* Per core (thread) state of em_atomic_group_queue_next() */
64static ENV_LOCAL unsigned int _agrp_q_iter_idx;
65static ENV_LOCAL em_atomic_group_t _agrp_q_iter_agrp;
66
67em_atomic_group_t
68em_atomic_group_create(const char *name, em_queue_group_t queue_group)
69{
70 em_atomic_group_t atomic_group = EM_ATOMIC_GROUP_UNDEF;
71 atomic_group_elem_t *ag_elem = NULL;
72 const char *err_str = "";
73 em_status_t error = EM_OK;
74 int ret = 0;
75
76 if (unlikely(invalid_qgrp(queue_group))) {
77 error = EM_ERR_BAD_ARG;
78 err_str = "Invalid queue group!";
79 goto error;
80 }
81
82 /* New Atomic group */
83 atomic_group = atomic_group_alloc();
84
85 if (unlikely(atomic_group == EM_ATOMIC_GROUP_UNDEF)) {
86 error = EM_ERR_ALLOC_FAILED;
87 err_str = "Atomic group allocation failed!";
88 goto error;
89 }
90
91 /* Initialize the atomic group */
92 ag_elem = atomic_group_elem_get(atomic_group);
93 if (unlikely(!ag_elem)) {
94 /* Fatal since atomic_group_alloc() returned 'ok', should never happen */
95 error = EM_FATAL(EM_ERR_BAD_ID);
96 err_str = "Atomic group allocation failed: ag_elem NULL!";
97 goto error;
98 }
99
100 odp_ticketlock_init(&ag_elem->lock);
101 ag_elem->dispatch_lock.lock_and_evcnt = 0;
102 ag_elem->dispatch_lock.parts.wrap = WRAP_PROTECT;
103 odp_atomic_init_u32(&ag_elem->num_queues, 0);
104 odp_atomic_init_u32(&ag_elem->num_hi_prio_queues, 0);
105
106 /* Store the related queue group */
107 ag_elem->queue_group = queue_group;
108
109 if (name != NULL) {
110 strncpy(ag_elem->name, name, sizeof(ag_elem->name) - 1);
111 ag_elem->name[sizeof(ag_elem->name) - 1] = '\0';
112 } else {
113 ag_elem->name[0] = '\0';
114 }
115
116 /*
117 * Create the AG internal stashes
118 */
119 unsigned int num_obj = 0;
120 odp_stash_capability_t stash_capa;
121 odp_stash_param_t stash_param;
122
123 ret = odp_stash_capability(&stash_capa, ODP_STASH_TYPE_FIFO);
124 if (ret != 0) {
125 error = EM_ERR_LIB_FAILED;
126 err_str = "odp_stash_capability() failed!";
127 goto error;
128 }
129
130 odp_stash_param_init(&stash_param);
131
132 stash_param.type = ODP_STASH_TYPE_FIFO;
133 stash_param.put_mode = ODP_STASH_OP_MT;
134 /* 'get' protected by ag_elem->dispatch_lock */
135 stash_param.get_mode = ODP_STASH_OP_ST;
136
137 /* Stash size: use EM default queue size value from config file: */
138 num_obj = em_shm->opt.queue.min_events_default;
139 if (num_obj != 0)
140 stash_param.num_obj = num_obj;
141 /* else: use odp default as set by odp_stash_param_init() */
142
143 stash_param.obj_size = sizeof(uint64_t);
144 if (stash_param.num_obj > stash_capa.max_num.u64) {
145 EM_LOG(EM_LOG_PRINT,
146 "%s(): req stash.num_obj(%" PRIu64 ") > capa.max_num.u64(%" PRIu64 ").\n"
147 " ==> using max value:%" PRIu64 "\n", __func__,
148 stash_param.num_obj, stash_capa.max_num.u64, stash_capa.max_num.u64);
149 stash_param.num_obj = stash_capa.max_num.u64;
150 }
151
152 stash_param.cache_size = 0; /* No core local caching */
153
154 ag_elem->stashes.hi_prio = odp_stash_create(ag_elem->name, &stash_param);
155 ag_elem->stashes.lo_prio = odp_stash_create(ag_elem->name, &stash_param);
156 if (unlikely(ag_elem->stashes.hi_prio == ODP_STASH_INVALID ||
157 ag_elem->stashes.lo_prio == ODP_STASH_INVALID)) {
158 error = EM_ERR_LIB_FAILED;
159 err_str = "odp_stash_create() failed!";
160 goto error;
161 }
162
163 __atomic_thread_fence(__ATOMIC_RELEASE);
164 return atomic_group;
165
166error:
167 INTERNAL_ERROR(error, EM_ESCOPE_ATOMIC_GROUP_CREATE, err_str);
168 if (atomic_group != EM_ATOMIC_GROUP_UNDEF)
169 em_atomic_group_delete(atomic_group);
170
172}
173
174/*
175 * Helper for em_atomic_group_delete()
176 * Flush the atomic group's internal queues and then destroy them.
177 */
178static int
179ag_stash_destroy(odp_stash_t stash)
180{
182 odp_event_t odp_evtbl[EM_SCHED_AG_MULTI_MAX_BURST];
183 em_event_t ev_tbl[EM_SCHED_AG_MULTI_MAX_BURST];
185 int32_t cnt = 0;
186 bool esv_ena = esv_enabled();
187
188 if (stash == ODP_STASH_INVALID)
189 return -1;
190
191 do {
192 cnt = odp_stash_get_u64(stash, &entry_tbl[0].u64 /*[out]*/,
194 if (cnt <= 0)
195 break;
196 for (int32_t i = 0; i < cnt; i++)
197 odp_evtbl[i] = (odp_event_t)(uintptr_t)entry_tbl[i].evptr;
198
199 events_odp2em(odp_evtbl, ev_tbl/*out*/, cnt);
200
201 if (esv_ena) {
202 event_to_hdr_multi(ev_tbl, ev_hdr_tbl/*out*/, cnt);
203 evstate_em2usr_multi(ev_tbl/*in/out*/, ev_hdr_tbl,
204 cnt, EVSTATE__AG_DELETE);
205 }
206
207 em_free_multi(ev_tbl, cnt);
208 } while (cnt > 0);
209
210 return odp_stash_destroy(stash);
211}
212
214em_atomic_group_delete(em_atomic_group_t atomic_group)
215{
216 atomic_group_elem_t *const ag_elem =
217 atomic_group_elem_get(atomic_group);
218 em_status_t error = EM_OK;
219 int err = 0;
220
221 RETURN_ERROR_IF(ag_elem == NULL,
222 EM_ERR_BAD_ARG, EM_ESCOPE_ATOMIC_GROUP_DELETE,
223 "Invalid atomic group - cannot delete!");
224
225 odp_ticketlock_lock(&ag_elem->lock);
226
227 /* Error checks */
228 err = !list_is_empty(&ag_elem->qlist_head);
229 err |= !atomic_group_allocated(ag_elem);
230
231 if (unlikely(err)) {
232 odp_ticketlock_unlock(&ag_elem->lock);
234 EM_ESCOPE_ATOMIC_GROUP_DELETE,
235 "Atomic group in bad state - cannot delete!");
236 }
237
238 /* Flush the atomic group's internal queues and destroy them */
239 err = ag_stash_destroy(ag_elem->stashes.hi_prio);
240 err |= ag_stash_destroy(ag_elem->stashes.lo_prio);
241
243 ag_elem->name[0] = '\0';
244
245 odp_ticketlock_unlock(&ag_elem->lock);
246
247 /* Free the atomic group (elem) back into the AG-pool */
248 error = atomic_group_free(atomic_group);
249 RETURN_ERROR_IF(error != EM_OK || err != 0,
250 error, EM_ESCOPE_ATOMIC_GROUP_DELETE,
251 "Atomic group free failed(%d)!", err);
252
253 return EM_OK;
254}
255
256em_queue_t
257em_queue_create_ag(const char *name, em_queue_prio_t prio,
258 em_atomic_group_t atomic_group, const em_queue_conf_t *conf)
259{
260 em_queue_t queue;
261 queue_elem_t *q_elem;
262 em_queue_group_t queue_group;
263 em_queue_param_t param;
264 atomic_group_elem_t *const ag_elem =
265 atomic_group_elem_get(atomic_group);
266 const char *err_str = "";
267
268 if (unlikely(!ag_elem || !atomic_group_allocated(ag_elem))) {
269 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_QUEUE_CREATE_AG,
270 "Invalid Atomic Group:%" PRI_AGRP "",
271 atomic_group);
272 return EM_QUEUE_UNDEF;
273 }
274
275 queue_group = ag_elem->queue_group;
276
277 em_queue_param_init(&param);
279 param.prio = prio;
280 param.queue_group = queue_group;
281 param.atomic_group = atomic_group;
282 queue_param_apply_conf(&param, conf);
283
284 queue = queue_create_param(name, &param, &err_str);
285
286 if (unlikely(queue == EM_QUEUE_UNDEF)) {
287 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_QUEUE_CREATE_AG,
288 "Atomic Group queue creation failed! (%s)",
289 err_str);
290 return EM_QUEUE_UNDEF;
291 }
292
293 q_elem = queue_elem_get(queue);
294 if (unlikely(!q_elem)) {
295 INTERNAL_ERROR(EM_ERR_BAD_POINTER, EM_ESCOPE_QUEUE_CREATE_AG,
296 "Atomic Group Q:%" PRI_QUEUE " - q_elem = NULL",
297 queue);
298 queue_free(queue);
299 return EM_QUEUE_UNDEF;
300 }
301
302 /* Add queue to atomic group list */
303 atomic_group_add_queue_list(ag_elem, q_elem);
304
305 return queue;
306}
307
310 em_atomic_group_t atomic_group, em_queue_t queue,
311 const em_queue_conf_t *conf)
312{
313 em_queue_t queue_static;
314 em_queue_group_t queue_group;
315 em_queue_param_t param;
316 atomic_group_elem_t *const ag_elem =
317 atomic_group_elem_get(atomic_group);
318 const char *err_str = "";
319
320 RETURN_ERROR_IF(!ag_elem || !atomic_group_allocated(ag_elem),
321 EM_ERR_BAD_ARG, EM_ESCOPE_QUEUE_CREATE_STATIC_AG,
322 "Invalid Atomic Group:%" PRI_AGRP "", atomic_group);
323
324 queue_group = ag_elem->queue_group;
325
326 em_queue_param_init(&param);
327 param.queue = queue;
329 param.prio = prio;
330 param.queue_group = queue_group;
331 param.atomic_group = atomic_group;
332 queue_param_apply_conf(&param, conf);
333
334 queue_static = queue_create_param(name, &param, &err_str);
335
336 RETURN_ERROR_IF(queue_static == EM_QUEUE_UNDEF,
337 EM_ERR_NOT_FREE, EM_ESCOPE_QUEUE_CREATE_STATIC_AG,
338 "Atomic Group static queue:%" PRI_QUEUE " creation failed! (%s)",
339 queue, err_str);
340
341 queue_elem_t *q_elem = queue_elem_get(queue_static);
342
343 /* Fatal error if q_elem == NULL, should never happen if queue_static != UNDEF */
345 EM_ESCOPE_QUEUE_CREATE_STATIC_AG,
346 "Queue elem NULL - req:%" PRI_QUEUE " vs. %" PRI_QUEUE "(=NULL)",
347 queue, queue_static);
348
349 if (unlikely(queue_static != queue)) {
350 const char *qdel_err = "queue cleanup ok";
351
352 (void)queue_delete(q_elem, &qdel_err);
353 return INTERNAL_ERROR(EM_ERR_BAD_ID, EM_ESCOPE_QUEUE_CREATE_STATIC_AG,
354 "Queue req:%" PRI_QUEUE " vs. %" PRI_QUEUE " (deleted:%s)",
355 queue, queue_static, qdel_err);
356 }
357
358 /* Add queue to atomic group list */
359 atomic_group_add_queue_list(ag_elem, q_elem);
360
361 return EM_OK;
362}
363
364size_t em_atomic_group_name(em_atomic_group_t atomic_group,
365 char *name, size_t maxlen)
366{
367 const atomic_group_elem_t *ag_elem =
368 atomic_group_elem_get(atomic_group);
369 size_t len = 0;
370
371 if (unlikely(name == NULL || maxlen == 0)) {
372 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_ATOMIC_GROUP_NAME,
373 "Invalid args: name=0x%" PRIx64 ", maxlen=%zu",
374 name, maxlen);
375 return 0;
376 }
377
378 if (unlikely(ag_elem == NULL || !atomic_group_allocated(ag_elem))) {
379 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_ATOMIC_GROUP_NAME,
380 "Invalid Atomic Group:%" PRI_AGRP "",
381 atomic_group);
382 name[0] = '\0';
383 return 0;
384 }
385
386 len = strnlen(ag_elem->name, sizeof(ag_elem->name) - 1);
387 if (maxlen - 1 < len)
388 len = maxlen - 1;
389
390 memcpy(name, ag_elem->name, len);
391 name[len] = '\0';
392
393 return len;
394}
395
396em_atomic_group_t
397em_atomic_group_find(const char *name)
398{
399 if (name && *name) {
400 for (int i = 0; i < EM_MAX_ATOMIC_GROUPS; i++) {
401 const atomic_group_elem_t *ag_elem =
402 &em_shm->atomic_group_tbl.ag_elem[i];
403
404 if (atomic_group_allocated(ag_elem) &&
405 !strncmp(name, ag_elem->name,
407 return ag_elem->atomic_group;
408 }
409 }
411}
412
413em_atomic_group_t em_atomic_group_first(unsigned int *num)
414{
415 const atomic_group_elem_t *const agrp_elem_tbl =
416 em_shm->atomic_group_tbl.ag_elem;
417 const atomic_group_elem_t *ag_elem = &agrp_elem_tbl[0];
418 const unsigned int agrp_count = atomic_group_count();
419
420 _agrp_tbl_iter_idx = 0; /* reset iteration */
421
422 if (num)
423 *num = agrp_count;
424
425 if (agrp_count == 0) {
426 _agrp_tbl_iter_idx = EM_MAX_ATOMIC_GROUPS; /*UNDEF=_next()*/
428 }
429
430 /* find first */
431 while (!atomic_group_allocated(ag_elem)) {
432 _agrp_tbl_iter_idx++;
433 if (_agrp_tbl_iter_idx >= EM_MAX_ATOMIC_GROUPS)
435 ag_elem = &agrp_elem_tbl[_agrp_tbl_iter_idx];
436 }
437
438 return agrp_idx2hdl(_agrp_tbl_iter_idx);
439}
440
441em_atomic_group_t em_atomic_group_next(void)
442{
443 if (_agrp_tbl_iter_idx >= EM_MAX_ATOMIC_GROUPS - 1)
445
446 _agrp_tbl_iter_idx++;
447
448 const atomic_group_elem_t *const agrp_elem_tbl =
449 em_shm->atomic_group_tbl.ag_elem;
450 const atomic_group_elem_t *ag_elem = &agrp_elem_tbl[_agrp_tbl_iter_idx];
451
452 /* find next */
453 while (!atomic_group_allocated(ag_elem)) {
454 _agrp_tbl_iter_idx++;
455 if (_agrp_tbl_iter_idx >= EM_MAX_ATOMIC_GROUPS)
457 ag_elem = &agrp_elem_tbl[_agrp_tbl_iter_idx];
458 }
459
460 return agrp_idx2hdl(_agrp_tbl_iter_idx);
461}
462
463em_queue_t em_atomic_group_queue_first(unsigned int *num,
464 em_atomic_group_t atomic_group)
465{
466 atomic_group_elem_t *const agrp_elem = atomic_group_elem_get(atomic_group);
467
468 const unsigned int max_queues = em_shm->queue_tbl.max_queue_num;
469
470 if (unlikely(agrp_elem == NULL || !atomic_group_allocated(agrp_elem))) {
472 EM_ESCOPE_ATOMIC_GROUP_QUEUE_FIRST,
473 "Invalid atomic group:%" PRI_AGRP "",
474 atomic_group);
475 if (num)
476 *num = 0;
477 return EM_QUEUE_UNDEF;
478 }
479
480 const unsigned int num_queues =
481 odp_atomic_load_u32(&agrp_elem->num_queues);
482
483 if (num)
484 *num = num_queues;
485
486 if (num_queues == 0) {
487 _agrp_q_iter_idx = max_queues; /* UNDEF = _next() */
488 return EM_QUEUE_UNDEF;
489 }
490
491 /*
492 * A 'agrp_elem' contains a linked list with all it's queues. That list
493 * might be modified while processing this iteration, so instead we just
494 * go through the whole queue table.
495 * This is potentially a slow implementation and perhaps worth
496 * re-thinking?
497 */
498 const queue_elem_t *const q_elem_tbl = em_shm->queue_tbl.queue_elem;
499 const queue_elem_t *q_elem = &q_elem_tbl[0];
500
501 _agrp_q_iter_idx = 0; /* reset list */
502 _agrp_q_iter_agrp = atomic_group;
503
504 /* find first */
505 while (!queue_allocated(q_elem) ||
506 !q_elem->flags.in_atomic_group ||
507 q_elem->agrp.atomic_group != _agrp_q_iter_agrp) {
508 _agrp_q_iter_idx++;
509 if (_agrp_q_iter_idx >= max_queues)
510 return EM_QUEUE_UNDEF;
511 q_elem = &q_elem_tbl[_agrp_q_iter_idx];
512 }
513
514 return queue_idx2hdl(_agrp_q_iter_idx);
515}
516
518{
519 const unsigned int max_queues = em_shm->queue_tbl.max_queue_num;
520
521 if (_agrp_q_iter_idx >= max_queues - 1)
522 return EM_QUEUE_UNDEF;
523
524 _agrp_q_iter_idx++;
525
526 const queue_elem_t *const q_elem_tbl = em_shm->queue_tbl.queue_elem;
527 const queue_elem_t *q_elem = &q_elem_tbl[_agrp_q_iter_idx];
528
529 /* find next */
530 while (!queue_allocated(q_elem) ||
531 !q_elem->flags.in_atomic_group ||
532 q_elem->agrp.atomic_group != _agrp_q_iter_agrp) {
533 _agrp_q_iter_idx++;
534 if (_agrp_q_iter_idx >= max_queues)
535 return EM_QUEUE_UNDEF;
536 q_elem = &q_elem_tbl[_agrp_q_iter_idx];
537 }
538
539 return queue_idx2hdl(_agrp_q_iter_idx);
540}
541
542uint64_t em_atomic_group_to_u64(em_atomic_group_t atomic_group)
543{
544 return (uint64_t)atomic_group;
545}
#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_shm_t * em_shm
#define EM_MAX_ATOMIC_GROUPS
#define EM_ATOMIC_GROUP_NAME_LEN
#define EM_SCHED_AG_MULTI_MAX_BURST
#define PRI_QUEUE
#define EM_QUEUE_GROUP_UNDEF
#define EM_ATOMIC_GROUP_UNDEF
#define PRI_AGRP
#define EM_QUEUE_UNDEF
em_queue_t em_queue_create_ag(const char *name, em_queue_prio_t prio, em_atomic_group_t atomic_group, const em_queue_conf_t *conf)
em_atomic_group_t em_atomic_group_find(const char *name)
em_queue_t em_atomic_group_queue_next(void)
uint64_t em_atomic_group_to_u64(em_atomic_group_t atomic_group)
em_queue_t em_atomic_group_queue_first(unsigned int *num, em_atomic_group_t atomic_group)
size_t em_atomic_group_name(em_atomic_group_t atomic_group, char *name, size_t maxlen)
em_status_t em_queue_create_static_ag(const char *name, em_queue_prio_t prio, em_atomic_group_t atomic_group, em_queue_t queue, const em_queue_conf_t *conf)
em_atomic_group_t em_atomic_group_next(void)
em_atomic_group_t em_atomic_group_first(unsigned int *num)
em_status_t em_atomic_group_delete(em_atomic_group_t atomic_group)
em_atomic_group_t em_atomic_group_create(const char *name, em_queue_group_t queue_group)
#define EM_OK
#define EM_FATAL(error)
uint32_t em_status_t
@ 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
void em_free_multi(em_event_t events[], int num)
uint32_t em_queue_prio_t
void em_queue_param_init(em_queue_param_t *param)
@ EM_QUEUE_TYPE_ATOMIC
odp_atomic_u32_t num_queues
em_queue_group_t queue_group
char name[EM_ATOMIC_GROUP_NAME_LEN]
em_atomic_group_t atomic_group
struct atomic_group_elem_t::@30 stashes
odp_atomic_u32_t num_hi_prio_queues
em_atomic_group_t atomic_group
em_queue_group_t queue_group
em_cfgfile_opts_t opt
Definition em_mem.h:99
em_atomic_group_t atomic_group
q_elem_atomic_group_t agrp
queue_elem_flags_t flags