EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_event.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 <string.h>
42
43#include <odp_api.h>
44
45#include <event_machine.h>
47
48#include "em_chaining.h"
49#include "em_error.h"
50#include "em_event.h"
51#include "em_event_inline.h"
52#include "em_event_state.h"
53#include "em_event_types.h"
54#include "em_libconfig.h"
55#include "em_mem.h"
56#include "em_pool.h"
57#include "em_pool_inline.h"
58#include "em_pool_types.h"
59#include "em_queue.h"
60#include "em_queue_inline.h"
61#include "em_queue_types.h"
62#include "misc/list.h"
63
64/*
65 * Sanity check that no extra padding is added to the event_hdr_t by
66 * alignment directives etc.
67 */
68typedef event_hdr_t _ev_hdr__size_check__arr_t[3];
69COMPILE_TIME_ASSERT(sizeof(_ev_hdr__size_check__arr_t) ==
70 3 * sizeof(event_hdr_t), EVENT_HDR_SIZE_ERROR2);
71
72/*
73 * Verify the value set for EM_CHECK_LEVEL - this define is set either from
74 * the include/event_machine/api/event_machine_config.h file or by the
75 * configure.ac option --enable-check-level=N.
76 */
77COMPILE_TIME_ASSERT(EM_CHECK_LEVEL >= 0 && EM_CHECK_LEVEL <= 3,
78 EM_CHECK_LEVEL__BAD_VALUE);
79
80static int read_config_file(void)
81{
82 EM_PRINT("EM event vector config:\n");
83
84 const char *conf_str = "vector.backend";
85 int ret = em_libconfig_lookup_string(&em_shm->libconfig, conf_str,
86 &em_shm->opt.vector.backend_str);
87 if (unlikely(!ret)) {
88 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", conf_str);
89 return -1;
90 }
91
92 const char *backend_str = em_shm->opt.vector.backend_str;
93
94 /* store & print the value */
95 if (strcmp(backend_str, "packet") == 0) {
96 em_shm->opt.vector.backend = EM_VECTOR_BACKEND_PACKET;
97 } else if (strcmp(backend_str, "event") == 0) {
98 const odp_pool_capability_t *capa = &em_shm->mpool_tbl.odp_pool_capability;
99
100 if (unlikely(capa->event_vector.max_pools == 0)) {
101 EM_LOG(EM_LOG_ERR,
102 "Config option '%s = \"event\"' not supported by ODP\n",
103 conf_str);
104 return -1;
105 }
106 em_shm->opt.vector.backend = EM_VECTOR_BACKEND_EVENT;
107 } else {
108 EM_LOG(EM_LOG_ERR,
109 "Invalid value '%s' for %s (expected \"packet\" or \"event\")\n",
110 backend_str, conf_str);
111 return -1;
112 }
113
114 EM_PRINT(" %s: %s\n", conf_str, backend_str);
115
116 return 0;
117}
118
119em_status_t event_init(void)
120{
121 if (read_config_file())
122 return EM_ERR_LIB_FAILED;
123
124 return EM_OK;
125}
126
127void print_event_info(void)
128{
129 EM_PRINT("\n"
130 "EM Events\n"
131 "---------\n"
132 "event-hdr size: %zu B\n",
133 sizeof(event_hdr_t));
134
135 DBG_PRINT("\t\toffset\tsize\n"
136 "\t\t------\t----\n"
137 "esv.state_cnt:\t%3zu B\t%2zu B\n"
138 "esv.state:\t%3zu B\t%2zu B\n"
139 "flags:\t\t%3zu B\t%2zu B\n"
140 "align_offset:\t%3zu B\t%2zu B\n"
141 "event_type:\t%3zu B\t%2zu B\n"
142 "event:\t\t%3zu B\t%2zu B\n"
143 "event_size:\t%3zu B\t%2zu B\n"
144 "egrp_gen:\t%3zu B\t%2zu B\n"
145 "egrp:\t\t%3zu B\t%2zu B\n"
146 "tmo:\t\t%3zu B\t%2zu B\n"
147 "user_area info:\t%3zu B\t%2zu B\n"
148 "vector_type:\t%3zu B\t%2zu B\n"
149 "end_hdr_data:\t%3zu B\t%2zu B\n"
150 " <pad>\t\t%3zu B\n"
151 "end:\t\t%3zu B\t%2zu B\n",
152 offsetof(event_hdr_t, state_cnt), sizeof_field(event_hdr_t, state_cnt),
153 offsetof(event_hdr_t, state), sizeof_field(event_hdr_t, state),
154 offsetof(event_hdr_t, flags), sizeof_field(event_hdr_t, flags),
155 offsetof(event_hdr_t, align_offset), sizeof_field(event_hdr_t, align_offset),
156 offsetof(event_hdr_t, event_type), sizeof_field(event_hdr_t, event_type),
157 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
158 offsetof(event_hdr_t, event), sizeof_field(event_hdr_t, event),
159 offsetof(event_hdr_t, event_size), sizeof_field(event_hdr_t, event_size),
160 offsetof(event_hdr_t, egrp_gen), sizeof_field(event_hdr_t, egrp_gen),
161 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
162 offsetof(event_hdr_t, egrp), sizeof_field(event_hdr_t, egrp),
163 /* NOLINTNEXTLINE(bugprone-sizeof-expression) */
164 offsetof(event_hdr_t, tmo), sizeof_field(event_hdr_t, tmo),
165 offsetof(event_hdr_t, user_area), sizeof_field(event_hdr_t, user_area),
166 offsetof(event_hdr_t, vector_type), sizeof_field(event_hdr_t, vector_type),
167 offsetof(event_hdr_t, end_hdr_data), sizeof_field(event_hdr_t, end_hdr_data),
168 offsetof(event_hdr_t, end) - offsetof(event_hdr_t, end_hdr_data),
169 offsetof(event_hdr_t, end), sizeof_field(event_hdr_t, end));
170 EM_PRINT("\n");
171}
172
173/**
174 * Start-up helper for pool preallocation
175 */
176event_prealloc_hdr_t *event_prealloc(const mpool_elem_t *pool_elem, uint32_t size)
177{
178 /*
179 * EM event pools created with type=PKT can support:
180 * - SW events (bufs)
181 * - pkt events.
182 *
183 * EM event pools created with type=SW can support:
184 * - SW events (bufs) only
185 */
186 event_hdr_t *ev_hdr = NULL;
187
188 if (pool_elem->event_type == EM_EVENT_TYPE_PACKET)
189 ev_hdr = event_alloc_pkt(pool_elem, size);
190 else if (pool_elem->event_type == EM_EVENT_TYPE_SW)
191 ev_hdr = event_alloc_buf(pool_elem, size);
192 else if (pool_elem->event_type == EM_EVENT_TYPE_VECTOR)
193 ev_hdr = event_alloc_vector(pool_elem, size);
194
195 if (unlikely(ev_hdr == NULL))
196 return NULL;
197
198 /* event now allocated */
199
200 if (esv_enabled()) {
201 em_event_t event = ev_hdr->event;
202
203 (void)evstate_prealloc(event, ev_hdr);
204 }
205 ev_hdr->flags.all = 0; /* clear only after evstate_alloc() */
206 ev_hdr->user_area.all = 0;
207
208 event_prealloc_hdr_t *prealloc_hdr = (event_prealloc_hdr_t *)ev_hdr;
209
210 return prealloc_hdr;
211}
212
213event_prealloc_hdr_t *list_node_to_prealloc_hdr(list_node_t *const list_node)
214{
215 event_prealloc_hdr_t *const ev_hdr = (event_prealloc_hdr_t *)(uintptr_t)
216 ((uint8_t *)list_node - offsetof(event_prealloc_hdr_t, list_node));
217
218 return likely(list_node != NULL) ? ev_hdr : NULL;
219}
220
221/**
222 * Helper for em_event_clone().
223 *
224 * Clone an event originating from an external odp pkt-pool.
225 * Initialize the new cloned event as an EM event and return it.
226 *
227 * Alloc and copy content via ODP.
228 * Also the ev_hdr in the odp-pkt user_area is copied.
229 */
230em_event_t pkt_clone_odp(odp_packet_t pkt, odp_pool_t pkt_pool,
231 uint32_t offset, uint32_t size,
232 bool clone_uarea, bool is_clone_part)
233{
234 odp_packet_t clone_pkt;
235
236 if (is_clone_part) {
237 /* only data is copied, ODP-uarea isn't */
238 clone_pkt = odp_packet_copy_part(pkt, offset, size, pkt_pool);
239 if (unlikely(clone_pkt == ODP_PACKET_INVALID))
240 return EM_EVENT_UNDEF;
241
242 const void *src_odp_uarea = odp_packet_user_area(pkt);
243 void *dst_odp_uarea = odp_packet_user_area(clone_pkt);
244 size_t cpy_size = sizeof(event_hdr_t);
245
246 if (clone_uarea) {
247 /* copy ODP-uarea (EM-hdr + EM-uarea) */
248 uint32_t src_uarea_size = odp_packet_user_area_size(pkt);
249 uint32_t dst_uarea_size = odp_packet_user_area_size(clone_pkt);
250
251 if (unlikely(dst_uarea_size < src_uarea_size)) {
252 odp_packet_free(clone_pkt);
253 return EM_EVENT_UNDEF;
254 }
255 /* update 'cpy_size' to include the whole ODP-uarea (EM-hdr + EM-uarea) */
256 cpy_size = src_uarea_size;
257 }
258 /* copy the EM-hdr and possibly also the EM-uarea if requested */
259 memcpy(dst_odp_uarea, src_odp_uarea, cpy_size);
260 } else {
261 /* identical clone, also ODP-uarea (EM-hdr + EM-uarea) is copied */
262 clone_pkt = odp_packet_copy(pkt, pkt_pool);
263 if (unlikely(clone_pkt == ODP_PACKET_INVALID))
264 return EM_EVENT_UNDEF;
265 }
266
267 odp_packet_user_flag_set(clone_pkt, USER_FLAG_SET);
268
269 odp_event_t odp_clone_event = odp_packet_to_event(clone_pkt);
270 event_hdr_t *clone_hdr = odp_packet_user_area(clone_pkt);
271 em_event_t clone_event = event_odp2em(odp_clone_event);
272
273 /*
274 * Init hdr of event, also ESV init if needed.
275 * The clone_hdr is a copy of parent's, update only relevant fields.
276 */
277 if (esv_enabled())
278 clone_event = evstate_init(clone_event, clone_hdr, false);
279 else
280 clone_hdr->event = clone_event;
281
282 clone_hdr->flags.all = 0;
283 clone_hdr->egrp = EM_EVENT_GROUP_UNDEF;
284 /* other fields: use parent's values as is */
285
286 return clone_event;
287}
288
289void
290output_queue_track(queue_elem_t *const output_q_elem)
291{
292 output_queue_track_t *const track =
294 const uint32_t qidx = output_q_elem->output.idx;
295
296 if (track->used_queues[qidx] == NULL) {
297 track->used_queues[qidx] = output_q_elem;
298 track->idx[track->idx_cnt++] = qidx;
299 }
300}
301
302void
303output_queue_drain(const queue_elem_t *output_q_elem)
304{
305 const em_queue_t output_queue = (em_queue_t)(uintptr_t)output_q_elem->queue;
306 const em_output_func_t output_fn =
307 output_q_elem->output.output_conf.output_fn;
308 void *const output_fn_args =
309 output_q_elem->output.output_conf.output_fn_args;
310
311 const int deq_max = 32;
312
313 em_event_t output_ev_tbl[deq_max];
314 /* use same event-tbl, dequeue odp events into the EM event-tbl */
315 odp_event_t *const odp_deq_events = (odp_event_t *)output_ev_tbl;
316
317 const odp_queue_t odp_queue = output_q_elem->odp_queue;
318 unsigned int output_num;
319 int deq;
320 int ret;
321
322 const bool esv_ena = esv_enabled();
323
324 do {
325 deq = odp_queue_deq_multi(odp_queue,
326 odp_deq_events/*out=output_ev_tbl[]*/,
327 deq_max);
328 if (unlikely(deq <= 0))
329 return;
330
331 output_num = (unsigned int)deq;
332 /* odp_deq_events[] == output_ev_tbl[] */
333 if (esv_ena) {
334 event_hdr_t *ev_hdrs[output_num];
335
336 /* Update EM hdls from hdrs, odp-ev conv lost evgen (non-EM events OK) */
337 event_to_hdr_multi(output_ev_tbl, ev_hdrs, output_num);
338 for (unsigned int i = 0; i < output_num; i++)
339 if (likely(ev_hdrs[i]->event_type != EM_EVENT_TYPE_ODP))
340 output_ev_tbl[i] = ev_hdrs[i]->event;
341 }
342
343 ret = output_fn(output_ev_tbl, output_num,
344 output_queue, output_fn_args);
345
346 if (unlikely((unsigned int)ret != output_num))
347 em_free_multi(&output_ev_tbl[ret], output_num - ret);
348 } while (deq > 0);
349}
350
351void
352output_queue_buffering_drain(void)
353{
355
356 for (unsigned int i = 0; i < track->idx_cnt; i++) {
357 int qidx = track->idx[i];
358 queue_elem_t *output_q_elem = track->used_queues[qidx];
359 odp_ticketlock_t *lock = &output_q_elem->output.lock;
360
361 /*
362 * drain if lock available, otherwise another core is already
363 * draining so no need to do anything.
364 */
365 if (odp_ticketlock_trylock(lock)) {
366 output_queue_drain(output_q_elem);
367 odp_ticketlock_unlock(lock);
368 }
369
370 track->idx[i] = 0;
371 track->used_queues[qidx] = NULL;
372 }
373 track->idx_cnt = 0;
374}
375
376/** Helper to event_vector_tbl() */
377uint32_t event_vector_tbl__odp_evvec(odp_event_vector_t evvec,
378 em_event_t **event_tbl /*out*/)
379{
380 odp_event_t *odp_evtbl = NULL;
381 uint32_t num = odp_event_vector_tbl(evvec, &odp_evtbl/*out*/);
382
383 /* set output arg 'event_tbl' even if there are no events in the vector */
384 *event_tbl = (em_event_t *)odp_evtbl; /* Careful! Points to same table (aliasing) */
385
386 if (!num)
387 return 0;
388
389 /*
390 * Init the event-table as needed, might contain EM events or
391 * ODP events depending on source.
392 */
393 if (esv_enabled()) {
394 /*
395 * Drop ESV generation from event handles by converting to
396 * odp-events, then init as needed as EM events.
397 */
398 (void)events_em2odp_inplace(*event_tbl, num);
399 }
400
401 event_hdr_t *ev_hdr_tbl[num];
402
403 /* odp_evtbl[] and event_tbl[] alias each other! */
404 event_init_odp_multi(odp_evtbl /*in*/, *event_tbl /*in,out*/,
405 ev_hdr_tbl /*out*/, num, false, NULL/*disp_q_elem*/);
406
407 return num;
408}
409
410/** Helper to event_vector_tbl() */
411uint32_t event_vector_tbl__odp_pktvec(odp_packet_vector_t pkt_vec,
412 em_event_t **event_tbl /*out*/)
413{
414 odp_packet_t *pkt_tbl = NULL;
415 uint32_t num = odp_packet_vector_tbl(pkt_vec, &pkt_tbl/*out*/);
416
417 /* set output arg 'event_tbl' even if there are no pkts in the vector */
418 *event_tbl = (em_event_t *)pkt_tbl; /* Careful! Points to same table (aliasing)*/
419
420 if (!num)
421 return 0;
422
423 /*
424 * Init the event-table as needed, might contain EM events or
425 * ODP packets depending on source.
426 */
427 if (esv_enabled()) {
428 /*
429 * Drop ESV generation from event handles by converting to
430 * odp-packets, then init as needed as EM events.
431 */
432 (void)events_em2pkt_inplace(*event_tbl, num);
433 }
434
435 event_hdr_t *ev_hdr_tbl[num];
436
437 /* pkt_tbl[] and event_tbl[] may alias each other! */
438 event_init_pkt_multi(pkt_tbl /*in*/, *event_tbl /*in,out*/,
439 ev_hdr_tbl /*out*/, num, false);
440
441 return num;
442}
443
444uint32_t event_vector_tbl(em_event_t vector_event,
445 em_event_t **event_tbl /*out*/)
446{
447 odp_event_t odp_event = event_em2odp(vector_event);
448 odp_event_type_t odp_etype = odp_event_type(odp_event);
449 uint32_t num;
450
451 if (odp_etype == ODP_EVENT_VECTOR) {
452 odp_event_vector_t evvec = odp_event_vector_from_event(odp_event);
453
454 num = event_vector_tbl__odp_evvec(evvec, event_tbl /*out*/);
455 } else {
456 /* odp_etype == ODP_EVENT_PACKET_VECTOR */
457 odp_packet_vector_t pkt_vec = odp_packet_vector_from_event(odp_event);
458
459 num = event_vector_tbl__odp_pktvec(pkt_vec, event_tbl /*out*/);
460 }
461
462 return num;
463}
464
465em_status_t event_vector_max_size(em_event_t vector_event, uint32_t *max_size /*out*/,
466 em_escope_t escope)
467{
468 odp_event_t odp_event = event_em2odp(vector_event);
469 odp_event_type_t odp_etype = odp_event_type(odp_event);
470 odp_pool_t odp_pool = ODP_POOL_INVALID;
471
472 if (odp_etype == ODP_EVENT_VECTOR) {
473 odp_event_vector_t evvec = odp_event_vector_from_event(odp_event);
474
475 odp_pool = odp_event_vector_pool(evvec);
476 } else {
477 /* odp_etype == ODP_EVENT_PACKET_VECTOR */
478 odp_packet_vector_t pktvec = odp_packet_vector_from_event(odp_event);
479
480 odp_pool = odp_packet_vector_pool(pktvec);
481 }
482
483 pool_subpool_t pool_subpool = pool_subpool_odp2em(odp_pool);
484 em_pool_t pool = (em_pool_t)(uintptr_t)pool_subpool.pool;
485 int subpool = pool_subpool.subpool;
486
487 if (unlikely(pool == EM_POOL_UNDEF)) {
488 /*
489 * Don't report an error if 'pool == EM_POOL_UNDEF' since that
490 * might happen if the vector is input from pktio that is using
491 * external (to EM) odp vector pools.
492 */
493 *max_size = 0;
494 return EM_OK; /* EM does not have the max_size info */
495 }
496
497 const mpool_elem_t *pool_elem = pool_elem_get(pool);
498
499 if (unlikely(!pool_elem ||
500 (EM_CHECK_LEVEL > 2 && !pool_allocated(pool_elem)))) {
501 *max_size = 0;
502 return INTERNAL_ERROR(EM_ERR_BAD_STATE, escope,
503 "Invalid pool:%" PRI_POOL "", pool);
504 }
505
506 if (unlikely(subpool >= pool_elem->num_subpools)) {
507 /* not found */
508 *max_size = 0;
509 return INTERNAL_ERROR(EM_ERR_NOT_FOUND, escope,
510 "Subpool not found, pool:%" PRI_POOL "", pool);
511 }
512
513 /* subpool index found, store corresponding size */
514 *max_size = pool_elem->size[subpool];
515
516 return EM_OK;
517}
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
#define USER_FLAG_SET
struct event_hdr event_hdr_t
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
#define EM_CHECK_LEVEL
#define PRI_POOL
#define EM_POOL_UNDEF
#define EM_EVENT_UNDEF
#define EM_EVENT_GROUP_UNDEF
#define EM_OK
uint32_t em_escope_t
uint32_t em_status_t
@ EM_ERR_NOT_FOUND
@ EM_ERR_BAD_STATE
@ EM_ERR_LIB_FAILED
void em_free_multi(em_event_t events[], int num)
@ EM_EVENT_TYPE_SW
@ EM_EVENT_TYPE_ODP
@ EM_EVENT_TYPE_PACKET
@ EM_EVENT_TYPE_VECTOR
int(* em_output_func_t)(const em_event_t events[], const unsigned int num, const em_queue_t output_queue, void *output_fn_args)
output_queue_track_t output_queue_track
Definition em_mem.h:292
em_cfgfile_opts_t opt
Definition em_mem.h:99
em_event_t event
ev_hdr_user_area_t user_area
union event_hdr::@43 flags
em_event_group_t egrp
uint16_t size
em_event_type_t event_type
odp_ticketlock_t lock
em_output_queue_conf_t output_conf
odp_queue_t odp_queue