EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_event.h
Go to the documentation of this file.
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 /**
32 * @file
33 * EM internal event functions
34 *
35 */
36
37#ifndef EM_EVENT_H_
38#define EM_EVENT_H_
39
40#include <stdbool.h>
41#include <stdint.h>
42
43#include <odp_api.h>
44
45#include <event_machine.h>
47
48#include "em_event_inline.h"
49#include "em_event_state.h"
50#include "em_event_types.h"
51#include "em_mem.h"
52#include "em_pool_types.h"
53#include "em_queue_inline.h"
54#include "em_queue_types.h"
55#include "misc/list.h"
56
57#ifdef __cplusplus
58extern "C" {
59#endif
60
61#ifndef __clang__
62COMPILE_TIME_ASSERT((uintptr_t)EM_EVENT_UNDEF == (uintptr_t)ODP_EVENT_INVALID,
63 EM_EVENT_NOT_EQUAL_TO_ODP_EVENT);
64COMPILE_TIME_ASSERT(EM_TMO_TYPE_NONE == 0,
65 "EM_TMO_TYPE_NONE must be 0");
66#endif
67
68em_status_t event_init(void);
69void print_event_info(void);
70em_event_t pkt_clone_odp(odp_packet_t pkt, odp_pool_t pkt_pool,
71 uint32_t offset, uint32_t size,
72 bool clone_uarea, bool is_clone_part);
73void output_queue_track(queue_elem_t *const output_q_elem);
74void output_queue_drain(const queue_elem_t *output_q_elem);
75void output_queue_buffering_drain(void);
76
77uint32_t event_vector_tbl(em_event_t vector_event, em_event_t **event_tbl/*out*/);
78uint32_t event_vector_tbl__odp_pktvec(odp_packet_vector_t pkt_vec,
79 em_event_t **event_tbl /*out*/);
80uint32_t event_vector_tbl__odp_evvec(odp_event_vector_t evvec,
81 em_event_t **event_tbl /*out*/);
82
83em_status_t event_vector_max_size(em_event_t vector_event, uint32_t *max_size /*out*/,
84 em_escope_t escope);
85
86/** Start-up helper for pool preallocation */
87event_prealloc_hdr_t *event_prealloc(const mpool_elem_t *pool_elem, uint32_t size);
88event_prealloc_hdr_t *list_node_to_prealloc_hdr(list_node_t *const list_node);
89
90/*
91 * Send an event to a scheduled queue.
92 *
93 * @param event The event to send.
94 * @param q_elem The queue element of the destination queue.
95 * @return EM_OK on success, EM_ERR_LIB_FAILED on failure.
96 */
97static inline em_status_t
98send_sched(em_event_t event, const queue_elem_t *q_elem)
99{
100 odp_event_t odp_event = event_em2odp(event);
101 odp_queue_t odp_queue = q_elem->odp_queue;
102
103 if (unlikely(EM_CHECK_LEVEL > 1 &&
104 (odp_event == ODP_EVENT_INVALID ||
105 odp_queue == ODP_QUEUE_INVALID)))
106 return EM_ERR_NOT_FOUND;
107
108 if (unlikely(EM_CHECK_LEVEL > 0 &&
109 q_elem->state != EM_QUEUE_STATE_READY)) {
110 return EM_ERR_BAD_STATE;
111 }
112
113 /* ESV enabled */
114 if (esv_enabled()) {
115 odp_event_type_t odp_etype = odp_event_type(odp_event);
116
117 /*
118 * Check if the event is a vector event.
119 * is vector: convert the EM event vector table to a table of odp packets/events
120 * (in-place) before passing the vector and contents to the scheduler.
121 */
122 if (odp_etype == ODP_EVENT_PACKET_VECTOR)
123 pktvec_tbl2odp(odp_event);
124 else if (odp_etype == ODP_EVENT_VECTOR)
125 evvec_tbl2odp(odp_event);
126
127 /* Enqueue event for scheduling */
128 int ret = odp_queue_enq(odp_queue, odp_event);
129
130 if (unlikely(EM_CHECK_LEVEL > 0 && ret != 0)) {
131 /* Restore EM vector event-table before returning vector to user */
132 if (odp_etype == ODP_EVENT_PACKET_VECTOR)
133 pktvec_tbl2odp_revert(odp_event);
134 else if (odp_etype == ODP_EVENT_VECTOR)
135 evvec_tbl2odp_revert(odp_event);
136
137 return EM_ERR_LIB_FAILED;
138 }
139
140 return EM_OK;
141 }
142
143 /* ESV disabled: */
144
145 /* Enqueue event for scheduling */
146 int ret = odp_queue_enq(odp_queue, odp_event);
147
148 if (unlikely(EM_CHECK_LEVEL > 0 && ret != 0))
149 return EM_ERR_LIB_FAILED;
150
151 return EM_OK;
152}
153
154static inline int
155send_sched_multi(const em_event_t events[], const int num,
156 const queue_elem_t *q_elem)
157{
158 const bool esv_ena = esv_enabled();
159 odp_event_t odp_events[num];
160 odp_queue_t odp_queue = q_elem->odp_queue;
161
162 if (unlikely(EM_CHECK_LEVEL > 1 && odp_queue == ODP_QUEUE_INVALID))
163 return 0;
164
165 if (unlikely(EM_CHECK_LEVEL > 0 &&
166 q_elem->state != EM_QUEUE_STATE_READY)) {
167 return 0;
168 }
169
170 events_em2odp(events, odp_events/*out*/, num);
171
172 /*
173 * Vector: convert the event vector table to a table of odp packets
174 * (in-place) before passing the vector and contents to the scheduler.
175 */
176 if (esv_ena) {
177 odp_event_type_t odp_etypes[num];
178
179 for (int i = 0; i < num; i++)
180 odp_etypes[i] = odp_event_type(odp_events[i]);
181
182 for (int i = 0; i < num; i++) {
183 if (odp_etypes[i] == ODP_EVENT_PACKET_VECTOR)
184 pktvec_tbl2odp(odp_events[i]);
185 else if (odp_etypes[i] == ODP_EVENT_VECTOR)
186 evvec_tbl2odp(odp_events[i]);
187 }
188
189 /* Enqueue events for scheduling */
190 int ret = odp_queue_enq_multi(odp_queue, odp_events, num);
191
192 if (likely(ret == num))
193 return num; /* Success! */
194
195 /*
196 * Fail: could not enqueue all events (ret != num)
197 */
198 int enq = ret < 0 ? 0 : ret;
199
200 /* Restore EM vector event-table before returning vector to user */
201 for (int i = enq; i < num; i++) {
202 if (odp_etypes[i] == ODP_EVENT_PACKET_VECTOR)
203 pktvec_tbl2odp_revert(odp_events[i]);
204 else if (odp_etypes[i] == ODP_EVENT_VECTOR)
205 evvec_tbl2odp_revert(odp_events[i]);
206 }
207 return enq; /* enq < num */
208 }
209
210 /* ESV disabled: */
211
212 /* Enqueue events for scheduling */
213 int ret = odp_queue_enq_multi(odp_queue, odp_events, num);
214
215 if (likely(ret == num))
216 return num; /* Success! */
217
218 /*
219 * Fail: could not enqueue all events (ret != num)
220 */
221 int enq = ret < 0 ? 0 : ret;
222
223 return enq; /* enq < num */
224}
225
226static inline em_status_t
227send_local(em_event_t event, const queue_elem_t *q_elem)
228{
229 em_locm_t *const locm = &em_locm;
230 const em_queue_prio_t prio = q_elem->priority;
231 evhdl_t evhdl = {.event = event};
232 int ret;
233
234 if (unlikely(EM_CHECK_LEVEL > 0 &&
235 q_elem->state != EM_QUEUE_STATE_READY))
236 return EM_ERR_BAD_STATE;
237
238 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
239 stash_entry_t entry = {.qidx = queue_hdl2idx(queue),
240 .evptr = evhdl.evptr};
241
242 ret = odp_stash_put_u64(locm->local_queues.prio[prio].stash,
243 &entry.u64, 1);
244 if (likely(ret == 1)) {
245 locm->local_queues.empty = 0;
246 locm->local_queues.prio[prio].empty_prio = 0;
247 return EM_OK;
248 }
249
250 return EM_ERR_LIB_FAILED;
251}
252
253static inline int
254send_local_multi(const em_event_t events[], const int num,
255 const queue_elem_t *q_elem)
256{
257 em_locm_t *const locm = &em_locm;
258 const em_queue_prio_t prio = q_elem->priority;
259 const evhdl_t *const evhdl_tbl = (const evhdl_t *const)events;
260
261 if (unlikely(EM_CHECK_LEVEL > 0 &&
262 q_elem->state != EM_QUEUE_STATE_READY))
263 return 0;
264
265 stash_entry_t entry_tbl[num];
266 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
267 const uint16_t qidx = (uint16_t)queue_hdl2idx(queue);
268
269 for (int i = 0; i < num; i++)
270 entry_tbl[i] = (stash_entry_t){.qidx = qidx,
271 .evptr = evhdl_tbl[i].evptr};
272
273 int ret = odp_stash_put_u64(locm->local_queues.prio[prio].stash,
274 &entry_tbl[0].u64, num);
275 if (likely(ret > 0)) {
276 locm->local_queues.empty = 0;
277 locm->local_queues.prio[prio].empty_prio = 0;
278 return ret;
279 }
280
281 return 0;
282}
283
284/**
285 * Send one event to a queue of type EM_QUEUE_TYPE_OUTPUT
286 */
287static inline em_status_t
288send_output(em_event_t event, queue_elem_t *const output_q_elem)
289{
290 const em_sched_context_type_t sched_ctx_type =
292
293 if (unlikely(EM_CHECK_LEVEL > 0 &&
294 output_q_elem->state != EM_QUEUE_STATE_UNSCHEDULED))
295 return EM_ERR_BAD_STATE;
296
297 /*
298 * An event sent to an output queue from an ordered context needs to
299 * be 're-ordered' before calling the user provided output-function.
300 * Order is maintained by enqueuing and dequeuing into an odp-queue
301 * that takes care of order.
302 */
303 if (sched_ctx_type == EM_SCHED_CONTEXT_TYPE_ORDERED) {
304 const odp_queue_t odp_queue = output_q_elem->odp_queue;
305 odp_event_t odp_event = event_em2odp(event);
306 int ret;
307
308 if (unlikely(EM_CHECK_LEVEL > 1 &&
309 (odp_event == ODP_EVENT_INVALID ||
310 odp_queue == ODP_QUEUE_INVALID)))
311 return EM_ERR_NOT_FOUND;
312
314 output_queue_track(output_q_elem);
315
316 /* enqueue to enforce odp to handle ordering */
317 ret = odp_queue_enq(odp_queue, odp_event);
318 if (unlikely(ret != 0))
319 return EM_ERR_LIB_FAILED;
320
321 /* return value must be EM_OK after this since event enqueued */
322
324 odp_ticketlock_t *const lock =
325 &output_q_elem->output.lock;
326
327 if (!odp_ticketlock_trylock(lock))
328 return EM_OK;
329 output_queue_drain(output_q_elem);
330 odp_ticketlock_unlock(lock);
331 }
332
333 return EM_OK;
334 }
335
336 /*
337 * No ordered context - call output_fn() directly
338 */
339 const em_queue_t output_queue = (em_queue_t)(uintptr_t)output_q_elem->queue;
340 const em_output_func_t output_fn =
341 output_q_elem->output.output_conf.output_fn;
342 void *const output_fn_args =
343 output_q_elem->output.output_conf.output_fn_args;
344 int sent;
345
346 sent = output_fn(&event, 1, output_queue, output_fn_args);
347 if (unlikely(sent != 1))
349
350 return EM_OK;
351}
352
353/**
354 * Send events to a queue of type EM_QUEUE_TYPE_OUTPUT
355 */
356static inline int
357send_output_multi(const em_event_t events[], const unsigned int num,
358 queue_elem_t *const output_q_elem)
359{
360 const em_sched_context_type_t sched_ctx_type =
362 int sent;
363
364 if (unlikely(EM_CHECK_LEVEL > 0 &&
365 output_q_elem->state != EM_QUEUE_STATE_UNSCHEDULED))
366 return 0;
367
368 /*
369 * Event sent to an output queue from an ordered context needs to
370 * be 're-ordered' before calling the user provided output-function.
371 * Order is maintained by enqueuing and dequeuing into an odp-queue
372 * that takes care of order.
373 */
374 if (sched_ctx_type == EM_SCHED_CONTEXT_TYPE_ORDERED) {
375 const odp_queue_t odp_queue = output_q_elem->odp_queue;
376 odp_event_t odp_events[num];
377
378 if (unlikely(EM_CHECK_LEVEL > 1 &&
379 odp_queue == ODP_QUEUE_INVALID))
380 return 0;
381
383 output_queue_track(output_q_elem);
384
385 events_em2odp(events, odp_events/*out*/, num);
386
387 /* enqueue to enforce odp to handle ordering */
388 sent = odp_queue_enq_multi(odp_queue, odp_events, num);
389 if (unlikely(sent <= 0))
390 return 0;
391
392 /* the return value must be the number of enqueued events */
393
395 odp_ticketlock_t *const lock =
396 &output_q_elem->output.lock;
397
398 if (!odp_ticketlock_trylock(lock))
399 return sent;
400 output_queue_drain(output_q_elem);
401 odp_ticketlock_unlock(lock);
402 }
403
404 return sent;
405 }
406
407 /*
408 * No ordered context - call output_fn() directly
409 */
410 const em_queue_t output_queue = (em_queue_t)(uintptr_t)output_q_elem->queue;
411 const em_output_func_t output_fn = output_q_elem->output.output_conf.output_fn;
412 void *const output_fn_args = output_q_elem->output.output_conf.output_fn_args;
413
414 sent = output_fn(events, num, output_queue, output_fn_args);
415
416 return sent;
417}
418
419/*
420 * Send an event to an aggregator queue.
421 *
422 * @param event The event to send.
423 * @param ev_hdr The event header (ESV: aggr_sent flag managed here).
424 * @param q_elem The queue element of the destination queue.
425 * @return EM_OK on success, EM_ERR_LIB_FAILED on failure.
426 */
427static inline em_status_t
428send_aggr(em_event_t event, event_hdr_t *ev_hdr, const queue_elem_t *q_elem)
429{
430 odp_event_t odp_event = event_em2odp(event);
431 odp_queue_t odp_queue = q_elem->odp_queue;
432
433 if (unlikely(EM_CHECK_LEVEL > 1 &&
434 (odp_event == ODP_EVENT_INVALID ||
435 odp_queue == ODP_QUEUE_INVALID)))
436 return EM_ERR_NOT_FOUND;
437
438 /*
439 * Aggregator children inherit the parent's operational state: READY for
440 * scheduled parents, UNSCHEDULED for unscheduled parents. Accept both.
441 */
442 if (unlikely(EM_CHECK_LEVEL > 0 &&
443 q_elem->state != EM_QUEUE_STATE_READY &&
444 q_elem->state != EM_QUEUE_STATE_UNSCHEDULED)) {
445 return EM_ERR_BAD_STATE;
446 }
447
448 if (EM_CHECK_LEVEL >= 3) {
449 odp_event_type_t odp_etype = odp_event_type(odp_event);
450
451 if (unlikely(odp_etype == ODP_EVENT_VECTOR ||
452 odp_etype == ODP_EVENT_PACKET_VECTOR)) {
453 return EM_ERR_BAD_TYPE;
454 }
455 }
456
457 /*
458 * Set flag before enqueue: another core may dispatch the event from
459 * the aggregation vector before odp_queue_enq() returns.
460 */
461 if (esv_enabled() && !ev_hdr->flags.refs_used)
462 ev_hdr->flags.aggr_sent = 1;
463
464 /* Enqueue event */
465 int ret = odp_queue_enq(odp_queue, odp_event);
466
467 if (unlikely(ret != 0)) {
468 if (esv_enabled() && !ev_hdr->flags.refs_used)
469 ev_hdr->flags.aggr_sent = 0;
470 return EM_ERR_LIB_FAILED;
471 }
472
473 return EM_OK;
474}
475
476static inline int
477send_aggr_multi(const em_event_t events[], event_hdr_t *const ev_hdrs[],
478 const int num, const queue_elem_t *q_elem)
479{
480 odp_event_t odp_events[num];
481 odp_queue_t odp_queue = q_elem->odp_queue;
482
483 if (unlikely(EM_CHECK_LEVEL > 1 && odp_queue == ODP_QUEUE_INVALID))
484 return 0;
485
486 /* Accept both scheduled (READY) and unscheduled aggregator parents */
487 if (unlikely(EM_CHECK_LEVEL > 0 &&
488 q_elem->state != EM_QUEUE_STATE_READY &&
489 q_elem->state != EM_QUEUE_STATE_UNSCHEDULED))
490 return 0;
491
492 events_em2odp(events, odp_events/*out*/, num);
493
494 if (EM_CHECK_LEVEL >= 3) {
495 odp_event_type_t odp_etypes[num];
496
497 for (int i = 0; i < num; i++)
498 odp_etypes[i] = odp_event_type(odp_events[i]);
499
500 for (int i = 0; i < num; i++) {
501 if (unlikely(odp_etypes[i] == ODP_EVENT_VECTOR ||
502 odp_etypes[i] == ODP_EVENT_PACKET_VECTOR))
503 return 0;
504 }
505 }
506
507 /*
508 * Set flags before enqueue: another core may dispatch events from
509 * the aggregation vector before odp_queue_enq_multi() returns.
510 */
511 if (esv_enabled()) {
512 for (int i = 0; i < num; i++) {
513 if (!ev_hdrs[i]->flags.refs_used)
514 ev_hdrs[i]->flags.aggr_sent = 1;
515 }
516 }
517
518 /* Enqueue events */
519 int ret = odp_queue_enq_multi(odp_queue, odp_events, num);
520
521 if (likely(ret == num))
522 return num; /* Success! */
523
524 /*
525 * Fail: could not enqueue all events (ret != num)
526 */
527 int enq = ret < 0 ? 0 : ret;
528
529 /* Clear flag for events that failed to enqueue */
530 if (esv_enabled()) {
531 for (int i = enq; i < num; i++) {
532 if (!ev_hdrs[i]->flags.refs_used)
533 ev_hdrs[i]->flags.aggr_sent = 0;
534 }
535 }
536
537 return enq; /* enq < num */
538}
539
540#ifdef __cplusplus
541}
542#endif
543
544#endif /* EM_EVENT_H_ */
event_prealloc_hdr_t * event_prealloc(const mpool_elem_t *pool_elem, uint32_t size)
Definition em_event.c:176
uint32_t event_vector_tbl__odp_pktvec(odp_packet_vector_t pkt_vec, em_event_t **event_tbl)
Definition em_event.c:411
em_event_t pkt_clone_odp(odp_packet_t pkt, odp_pool_t pkt_pool, uint32_t offset, uint32_t size, bool clone_uarea, bool is_clone_part)
Definition em_event.c:230
uint32_t event_vector_tbl__odp_evvec(odp_event_vector_t evvec, em_event_t **event_tbl)
Definition em_event.c:377
ENV_LOCAL em_locm_t em_locm
@ EM_QUEUE_STATE_READY
#define EM_OUTPUT_QUEUE_IMMEDIATE
#define EM_CHECK_LEVEL
#define EM_EVENT_UNDEF
#define EM_OK
uint32_t em_escope_t
uint32_t em_status_t
@ EM_ERR_NOT_FOUND
@ EM_ERR_OPERATION_FAILED
@ EM_ERR_BAD_STATE
@ EM_ERR_BAD_TYPE
@ EM_ERR_LIB_FAILED
uint32_t em_queue_prio_t
int(* em_output_func_t)(const em_event_t events[], const unsigned int num, const em_queue_t output_queue, void *output_fn_args)
em_sched_context_type_t
@ EM_SCHED_CONTEXT_TYPE_ORDERED
em_sched_context_type_t sched_context_type
Definition em_mem.h:210
em_locm_current_t current
Definition em_mem.h:228
local_queues_t local_queues
Definition em_mem.h:241
uint8_t refs_used
uint8_t aggr_sent
union event_hdr::@43 flags
odp_ticketlock_t lock
em_output_queue_conf_t output_conf
odp_queue_t odp_queue
queue_state_t state