EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_event_inline.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_INLINE_H_
38#define EM_EVENT_INLINE_H_
39
40#include <stdbool.h>
41#include <stdint.h>
42#include <string.h>
43
44#include <odp_api.h>
45
46#include <event_machine.h>
47
48#include "em_event_state.h"
49#include "em_event_types.h"
50#include "em_mem.h"
51#include "em_pool_inline.h"
52#include "em_pool_types.h"
53
54#ifdef __cplusplus
55extern "C" {
56#endif
57
58/** Convert an EM-event into an ODP-event */
59static inline odp_event_t
60event_em2odp(em_event_t event)
61{
62 /* Valid for both ESV enabled and disabled */
63 evhdl_t evhdl = {.event = event};
64
65 return (odp_event_t)(uintptr_t)evhdl.evptr;
66}
67
68/**
69 * Convert an ODP-event into an EM-event
70 *
71 * @note The returned EM-event does NOT contain the ESV event-generation-count
72 * evhdl_t::evgen! This must be set separately when using ESV.
73 */
74static inline em_event_t
75event_odp2em(odp_event_t odp_event)
76{
77 /* Valid for both ESV enabled and disabled */
78
79 /*
80 * Setting 'evhdl.event = odp_event' is equal to
81 * 'evhdl.evptr = odp_event, evhdl.evgen = 0'
82 * (evhdl.evgen still needs to be set when using ESV)
83 */
84 evhdl_t evhdl = {.event = (em_event_t)(uintptr_t)odp_event};
85
86 return evhdl.event;
87}
88
89/** Convert an array of EM-events into an array ODP-events */
90static inline void
91events_em2odp(const em_event_t events[/*in*/],
92 odp_event_t odp_events[/*out*/], const unsigned int num)
93{
94 /* Valid for both ESV enabled and disabled */
95 const evhdl_t *const evhdls = (const evhdl_t *)events;
96
97 for (unsigned int i = 0; i < num; i++)
98 odp_events[i] = (odp_event_t)(uintptr_t)evhdls[i].evptr;
99}
100
101/**
102 * Convert an array of ODP-events into an array of EM-events
103 *
104 * @note The output EM-events do NOT contain the ESV event-generation-count
105 * evhdl_t::evgen! This must be set separately when using ESV.
106 */
107static inline void
108events_odp2em(const odp_event_t odp_events[/*in*/],
109 em_event_t events[/*out*/], const unsigned int num)
110{
111 /*
112 * Valid for both ESV enabled and disabled.
113 *
114 * Bit-cast odp_event_t -> em_event_t. Copying the full 64-bit value
115 * into events[i] is equivalent to setting evhdl_t::evptr to the
116 * odp_event_t value and evhdl_t::evgen to 0 (evhdl_t::evgen still
117 * needs to be set separately when using ESV).
118 *
119 * sizeof(em_event_t) == sizeof(odp_event_t) guaranteed by a COMPILE_TIME_ASSERT(),
120 * so the raw byte copy is equivalent to the per-element bit-cast.
121 * Copy for(each i): events[i] = (em_event_t)(uintptr_t)odp_events[i]
122 */
123 memcpy(events, odp_events, num * sizeof(em_event_t));
124}
125
126/**
127 * Convert an array of EM-events into an array of ODP-packets.
128 * The content must be known to be packets.
129 */
130static inline void
131events_em2pkt(const em_event_t events[/*in*/],
132 odp_packet_t odp_pkts[/*out*/], const unsigned int num)
133{
134 /* Valid for both ESV enabled and disabled */
135 const evhdl_t *const evhdls = (const evhdl_t *)events;
136
137 for (unsigned int i = 0; i < num; i++)
138 odp_pkts[i] = odp_packet_from_event((odp_event_t)(uintptr_t)evhdls[i].evptr);
139}
140
141/**
142 * Convert an array of EM-events into an array of ODP-events in-place (i.e.
143 * convert using the same memory area for output). Be careful!
144 *
145 * @return Pointer to odp event table: odp_event_t odp_events[num]
146 * Uses the same memory area for the output of 'odp_events[num]' as for
147 * the input 'events[num]' (thus overwrites 'events[num]' with
148 * 'odp_events[num]').
149 */
150static inline odp_event_t *
151events_em2odp_inplace(em_event_t events[/*in(/out)*/], const unsigned int num)
152{
153 /* Valid for both ESV enabled and disabled */
154 const evhdl_t *const evhdls = (evhdl_t *)events;
155 odp_event_t *const odp_events = (odp_event_t *)events; /* careful! */
156
157 /* Careful! Overwrites events[num] with odp_events[num] */
158 for (unsigned int i = 0; i < num; i++)
159 odp_events[i] = (odp_event_t)(uintptr_t)evhdls[i].evptr;
160
161 return odp_events;
162}
163
164/**
165 * Convert an array of EM-events into an array of ODP-packets in-place (i.e.
166 * convert using the same memory area for output) when the event type is known
167 * for sure to be packets. Be careful!
168 *
169 * @return Pointer to odp packet table: odp_packet_t pkts[num]
170 * Uses the same memory area for the output of 'pkts[num]' as for
171 * the input 'events[num]' (thus overwrites 'events[num]' with
172 * 'pkts[num]').
173 */
174static inline odp_packet_t *
175events_em2pkt_inplace(em_event_t events[/*in(/out)*/], const unsigned int num)
176{
177 /* Valid for both ESV enabled and disabled */
178 evhdl_t *const evhdls = (evhdl_t *)events;
179 odp_packet_t *const pkts = (odp_packet_t *)events; /* careful! */
180
181 /* Careful! Overwrites events[num] with pkts[num] */
182 for (unsigned int i = 0; i < num; i++)
183 pkts[i] = odp_packet_from_event((odp_event_t)(uintptr_t)evhdls[i].evptr);
184
185 return pkts;
186}
187
188/**
189 * @brief Convert event vector table content to odp events in-place.
190 *
191 * Convert an EM event vector table, containing em_event_t:s with
192 * esv-info (evgen), to a table of odp events (remove handles' evgen in-place).
193 *
194 * Done before enqueuing a vector to a scheduled or unscheduled queue so the
195 * inner-event table is in odp-form when later iterated on dispatch/dequeue.
196 */
197static inline void
198evvec_tbl2odp(odp_event_t odp_event_vec)
199{
200 odp_event_vector_t odp_evv = odp_event_vector_from_event(odp_event_vec);
201 odp_event_t *odp_evtbl = NULL;
202 const int num = odp_event_vector_tbl(odp_evv, &odp_evtbl/*out*/);
203
204 if (likely(num > 0)) {
205 /* Careful! Points to same table (aliasing) */
206 em_event_t *event_tbl = (em_event_t *)odp_evtbl;
207
208 /* Drop ESV event generation (evgen) from event handle */
209 (void)events_em2odp_inplace(event_tbl, num);
210 }
211}
212
213/**
214 * @brief Convert event vector table content to odp packets in-place.
215 *
216 * Convert an EM event vector table, containing em_event_t:s with
217 * esv-info (evgen), to a table of odp packets (remove handles' evgen in-place).
218 */
219static inline void
220pktvec_tbl2odp(odp_event_t odp_event_pktvec)
221{
222 odp_packet_vector_t pkt_vec = odp_packet_vector_from_event(odp_event_pktvec);
223 odp_packet_t *pkt_tbl = NULL;
224 const int pkts = odp_packet_vector_tbl(pkt_vec, &pkt_tbl/*out*/);
225
226 if (likely(pkts > 0)) {
227 /* Careful! Points to same table (aliasing) */
228 em_event_t *event_tbl = (em_event_t *)pkt_tbl;
229
230 /* Drop ESV event generation (evgen) from event handle */
231 (void)events_em2pkt_inplace(event_tbl, pkts);
232 }
233}
234
235/**
236 * @brief Convert from a packet-event to event header
237 *
238 * It has to be known that the event is a packet before calling this function,
239 * otherwise use event_to_hdr().
240 *
241 * @param pkt_event EM event based on odp_packet_t
242 * @return event_hdr_t* Pointer to the event header
243 *
244 * Does NOT initialize the event header.
245 */
246static inline event_hdr_t *
247eventpkt_to_hdr(em_event_t pkt_event)
248{
249 odp_event_t odp_event = event_em2odp(pkt_event);
250 odp_packet_t odp_pkt = odp_packet_from_event(odp_event);
251 event_hdr_t *ev_hdr = odp_packet_user_area(odp_pkt);
252
253 return ev_hdr;
254}
255
256/**
257 * @brief Convert from a buffer-event to event header
258 *
259 * It has to be known that the event is a buffer before calling this function,
260 * otherwise use event_to_hdr().
261 *
262 * @param buf_event EM event based on odp_buffer_t
263 * @return event_hdr_t* Pointer to the event header
264 *
265 * Does NOT initialize the event header.
266 */
267static inline event_hdr_t *
268eventbuf_to_hdr(em_event_t buf_event)
269{
270 odp_event_t odp_event = event_em2odp(buf_event);
271 odp_buffer_t odp_buf = odp_buffer_from_event(odp_event);
272 event_hdr_t *ev_hdr = odp_buffer_user_area(odp_buf);
273
274 return ev_hdr;
275}
276
277/**
278 * @brief Convert from an event vector to event header
279 *
280 * It has to be known that the event is a vector before calling this function,
281 * otherwise use event_to_hdr().
282 *
283 * @param vector_event EM event of major type EM_EVENT_TYPE_VECTOR
284 * @return event_hdr_t* Pointer to the event header
285 *
286 * Does NOT initialize the event header.
287 */
288static inline event_hdr_t *
289eventvec_to_hdr(em_event_t vector_event)
290{
291 odp_event_t odp_event = event_em2odp(vector_event);
292 odp_event_type_t odp_etype = odp_event_type(odp_event);
293 event_hdr_t *ev_hdr;
294
295 if (odp_etype == ODP_EVENT_VECTOR) {
296 odp_event_vector_t odp_evvec = odp_event_vector_from_event(odp_event);
297
298 ev_hdr = odp_event_vector_user_area(odp_evvec);
299 } else /* ODP_EVENT_PACKET_VECTOR */ {
300 odp_packet_vector_t odp_pktvec = odp_packet_vector_from_event(odp_event);
301
302 ev_hdr = odp_packet_vector_user_area(odp_pktvec);
303 }
304
305 return ev_hdr;
306}
307
308/**
309 * Convert from EM event to event header.
310 *
311 * Does NOT initialize the event header.
312 */
313static inline event_hdr_t *
314event_to_hdr(em_event_t event)
315{
316 odp_event_t odp_event = event_em2odp(event);
317 odp_packet_t odp_pkt;
318 odp_buffer_t odp_buf;
319 odp_event_vector_t odp_evvec;
320 odp_packet_vector_t odp_pktvec;
321 odp_timeout_t odp_tmo;
322 event_hdr_t *ev_hdr;
323
324 odp_event_type_t evtype = odp_event_type(odp_event);
325
326 switch (evtype) {
327 case ODP_EVENT_PACKET:
328 odp_pkt = odp_packet_from_event(odp_event);
329 ev_hdr = odp_packet_user_area(odp_pkt);
330 break;
331 case ODP_EVENT_BUFFER:
332 odp_buf = odp_buffer_from_event(odp_event);
333 ev_hdr = odp_buffer_user_area(odp_buf);
334 break;
335 case ODP_EVENT_VECTOR:
336 odp_evvec = odp_event_vector_from_event(odp_event);
337 ev_hdr = odp_event_vector_user_area(odp_evvec);
338 break;
339 case ODP_EVENT_PACKET_VECTOR:
340 odp_pktvec = odp_packet_vector_from_event(odp_event);
341 ev_hdr = odp_packet_vector_user_area(odp_pktvec);
342 break;
343 case ODP_EVENT_TIMEOUT:
344 odp_tmo = odp_timeout_from_event(odp_event);
345 ev_hdr = odp_timeout_user_area(odp_tmo);
346 break;
347 default:
348 /*
349 * Non-EM events don't have an event header of their own,
350 * use a temporary core-local event header instead.
351 */
352 ev_hdr = &em_locm.evhdr_nonem;
353 break;
354 }
355
356 return ev_hdr;
357}
358
359/**
360 * Convert from EM events to event headers.
361 *
362 * Does NOT initialize the event headers.
363 *
364 * @param[in] events Input array of 'num' valid events
365 * @param[out] ev_hdrs Output array with room to store 'num' pointers to the
366 * corresponding event headers
367 * @param num Number of entries in 'events[]' and 'ev_hdrs[]'
368 */
369static inline void
370event_to_hdr_multi(const em_event_t events[], event_hdr_t *ev_hdrs[/*out*/],
371 const int num)
372{
373 for (int i = 0; i < num; i++)
374 ev_hdrs[i] = event_to_hdr(events[i]);
375}
376
377/** Convert from event header to EM event */
378static inline em_event_t
379event_hdr_to_event(const event_hdr_t *const event_hdr)
380{
381 return event_hdr->event;
382}
383
384/**
385 * Initialize the event header of a packet allocated outside of EM.
386 */
387static inline em_event_t
388evhdr_init_pkt(event_hdr_t *ev_hdr, em_event_t event,
389 odp_packet_t odp_pkt, bool is_extev)
390{
391 const int user_flag_set = odp_packet_user_flag(odp_pkt);
392 const bool esv_ena = esv_enabled();
393
394 if (user_flag_set) {
395 /* Event already initialized by EM */
396 if (esv_ena) {
397 event = ev_hdr->event;
398 if (is_extev)
399 event = evstate_em2usr(event, ev_hdr, EVSTATE__DISPATCH);
400 }
401
402 return event;
403 }
404
405 /*
406 * ODP pkt from outside of EM - not allocated by EM & needs init
407 */
408 odp_packet_user_flag_set(odp_pkt, USER_FLAG_SET);
410 ev_hdr->egrp = EM_EVENT_GROUP_UNDEF;
411 ev_hdr->user_area.all = 0; /* uarea fields init when used */
412
413 if (!esv_ena) {
414 ev_hdr->flags.all = 0;
415 ev_hdr->event = event;
416 return event;
417 }
418
419 /*
420 * ESV enabled:
421 */
422 if (!em_shm->opt.esv.prealloc_pools || ev_hdr->flags.refs_used) {
423 /* No prealloc OR pkt was a ref before being freed into the pool */
424 event = evstate_init(event, ev_hdr, is_extev);
425 } else {
426 /* esv.prealloc_pools == true: */
427 const odp_pool_t odp_pool = odp_packet_pool(odp_pkt);
428 const em_pool_t pool = pool_odp2em(odp_pool);
429
430 if (pool == EM_POOL_UNDEF) {
431 /* External odp pkt originates from an ODP-pool */
432 event = evstate_init(event, ev_hdr, is_extev);
433 } else {
434 /* External odp pkt originates from an EM-pool */
435 event = evstate_update(event, ev_hdr, is_extev);
436 }
437 }
438 ev_hdr->flags.all = 0; /* clear only after evstate_...() */
439
440 return event;
441}
442
443/**
444 * Initialize the event headers of packets allocated outside of EM.
445 *
446 * Note that events[] and odp_pkts[] may refer to the same memory locations,
447 * i.e. an in-place update is possible (they may alias each other).
448 */
449static inline void
450evhdr_init_pkt_multi(event_hdr_t *const ev_hdrs[],
451 em_event_t events[/*in,out*/],
452 const odp_packet_t odp_pkts[/*in*/] /* may alias events[] */,
453 const int num, bool is_extev)
454{
455 const bool esv_ena = esv_enabled();
456 int user_flag_set;
457
458 int needs_init_idx[num];
459 int needs_init_num = 0;
460 int idx;
461
462 for (int i = 0; i < num; i++) {
463 user_flag_set = odp_packet_user_flag(odp_pkts[i]);
464 if (!user_flag_set) {
465 /* ODP pkt not yet initialized by EM - defer init below */
466 odp_packet_user_flag_set(odp_pkts[i], USER_FLAG_SET);
467 needs_init_idx[needs_init_num++] = i;
468 continue;
469 }
470 /* Event already initialized by EM */
471 if (esv_ena) {
472 events[i] = ev_hdrs[i]->event;
473 if (is_extev)
474 events[i] = evstate_em2usr(events[i], ev_hdrs[i],
475 EVSTATE__DISPATCH_MULTI);
476 }
477 /* else: events[i] = events[i], no update needed */
478 }
479
480 if (needs_init_num == 0)
481 return;
482
483 /*
484 * ODP pkt from outside of EM - not allocated by EM & needs init
485 */
486
487 if (!esv_ena) {
488 for (int i = 0; i < needs_init_num; i++) {
489 idx = needs_init_idx[i];
490 ev_hdrs[idx]->flags.all = 0;
491 ev_hdrs[idx]->event_type = EM_EVENT_TYPE_PACKET;
492 ev_hdrs[idx]->event = events[idx];
493 ev_hdrs[idx]->egrp = EM_EVENT_GROUP_UNDEF;
494 ev_hdrs[idx]->user_area.all = 0; /* uarea fields init when used */
495 }
496
497 return;
498 }
499
500 /*
501 * ESV enabled:
502 */
503 if (!em_shm->opt.esv.prealloc_pools) {
504 for (int i = 0; i < needs_init_num; i++) {
505 idx = needs_init_idx[i];
506 events[idx] = evstate_init(events[idx], ev_hdrs[idx], is_extev);
507 }
508 } else {
509 /* em_shm->opt.esv.prealloc_pools == true */
510 for (int i = 0; i < needs_init_num; i++) {
511 idx = needs_init_idx[i];
512
513 odp_pool_t odp_pool = odp_packet_pool(odp_pkts[idx]);
514 em_pool_t pool = pool_odp2em(odp_pool);
515
516 if (pool == EM_POOL_UNDEF || ev_hdrs[idx]->flags.refs_used) {
517 /*
518 * External odp pkt originates from an ODP-pool,
519 * or pkt was a ref before being freed into the pool.
520 */
521 events[idx] = evstate_init(events[idx], ev_hdrs[idx], is_extev);
522 } else {
523 /* External odp pkt originates from an EM-pool */
524 events[idx] = evstate_update(events[idx], ev_hdrs[idx], is_extev);
525 }
526 }
527 }
528
529 for (int i = 0; i < needs_init_num; i++) {
530 idx = needs_init_idx[i];
531 ev_hdrs[idx]->flags.all = 0; /* clear only after evstate_...() */
532 ev_hdrs[idx]->event_type = EM_EVENT_TYPE_PACKET;
533 ev_hdrs[idx]->egrp = EM_EVENT_GROUP_UNDEF;
534 ev_hdrs[idx]->user_area.all = 0; /* uarea fields init when used */
535 }
536}
537
538/**
539 * Apply the missing evstate_em2usr() for one inner event from an
540 * aggregation-produced vector: non-ref sends set aggr_sent in send_aggr();
541 * ref sends keep aggr_sent clear but set refs_used — still enter here.
542 * Skip inner slots that are neither (e.g. user-built vectors).
543 */
544static inline void
545esv_aggr_inner_em2usr_one(odp_event_t odp_ev)
546{
547 event_hdr_t *inner_hdr = event_to_hdr(event_odp2em(odp_ev));
548
549 /*
550 * Non-ref aggregator sends set aggr_sent in send_aggr[_multi]().
551 * Ref sends must not touch aggr_sent (single flags byte vs refs);
552 * identify them via refs_used and still apply em2usr here.
553 * Skip when neither applies — inner slot is not from send_aggr (e.g.
554 * user-built vector).
555 */
556 if (!inner_hdr->flags.refs_used && !inner_hdr->flags.aggr_sent)
557 return;
558 /* Use the stored handle (correct evgen from send). */
559 (void)evstate_em2usr(inner_hdr->event, inner_hdr,
560 EVSTATE__DISPATCH_AGGR);
561 if (!inner_hdr->flags.refs_used)
562 inner_hdr->flags.aggr_sent = 0;
563}
564
565/**
566 * Apply the missing em2usr ESV-transition for inner events of an
567 * aggregation-produced vector.
568 *
569 * Events sent to EM_QUEUE_TYPE_AGGR go through evstate_usr2em() at send time
570 * but never receive evstate_em2usr() at dispatch because ODP bundles them into
571 * a vector transparently. Non-ref sends set aggr_sent in send_aggr(); ref
572 * sends do not touch aggr_sent but refs_used is set — esv_aggr_inner_em2usr_one()
573 * handles both. User-constructed vectors have neither bit set so inner slots
574 * are skipped.
575 *
576 * Pktio-originated packets (user_flag=0) have never been seen by EM and may
577 * have uninitialized user area - skip them before reading aggr_sent.
578 * ODP buffers are always EM-allocated and need no such guard.
579 *
580 * Called only when ESV is enabled and is_extev=true.
581 *
582 * @param disp_q_elem Queue the vector is being dispatched from, or NULL when
583 * the caller is not a normal dispatch (e.g. freeing dropped
584 * events). See the in-body note for the skip condition.
585 */
586static inline void
587esv_aggr_inner_em2usr(odp_event_vector_t odp_evvec,
588 const queue_elem_t *disp_q_elem)
589{
590 /*
591 * Aggregation-produced vectors are only ever delivered on the parent
592 * queue that owns the event aggregators (flags.has_aggr==1), so when
593 * dispatching from a queue without aggregators no inner slot can carry
594 * ev_hdr->flags.aggr_sent = 1 and the per-inner scan is unnecessary.
595 * disp_q_elem==NULL marks a non-dispatch caller (e.g. freeing dropped
596 * events): keep the scan so inner events are moved to user-side state
597 * before em_free().
598 */
599 if (disp_q_elem && !disp_q_elem->flags.has_aggr)
600 return;
601
602 odp_event_t *odp_evtbl = NULL;
603 uint32_t sz = odp_event_vector_tbl(odp_evvec, &odp_evtbl);
604 odp_event_type_t odp_vtype = odp_event_vector_type(odp_evvec);
605
606 /*
607 * Type-dispatched once per vector (ODP guarantees the vector type
608 * reflects all inner events or is ODP_EVENT_ANY when mixed/unknown).
609 */
610 if (odp_vtype == ODP_EVENT_PACKET) {
611 for (uint32_t i = 0; i < sz; i++) {
612 /* Skip pktio packets not yet initialized by EM */
613 if (!odp_packet_user_flag(odp_packet_from_event(odp_evtbl[i])))
614 continue;
615 esv_aggr_inner_em2usr_one(odp_evtbl[i]);
616 }
617 } else if (odp_vtype == ODP_EVENT_BUFFER) {
618 /* All EM-allocated buffers, no pktio guard needed */
619 for (uint32_t i = 0; i < sz; i++)
620 esv_aggr_inner_em2usr_one(odp_evtbl[i]);
621 } else {
622 /* ODP_EVENT_ANY: mixed or unknown, per-element type check */
623 for (uint32_t i = 0; i < sz; i++) {
624 /* Skip pktio packets not yet initialized by EM */
625 if (odp_event_type(odp_evtbl[i]) == ODP_EVENT_PACKET &&
626 !odp_packet_user_flag(odp_packet_from_event(odp_evtbl[i])))
627 continue;
628 esv_aggr_inner_em2usr_one(odp_evtbl[i]);
629 }
630 }
631}
632
633/**
634 * Initialize the event header of an event vector allocated outside of EM.
635 */
636static inline em_event_t
637evhdr_init_evvec(event_hdr_t *ev_hdr, em_event_t event,
638 odp_event_vector_t odp_evvec, bool is_extev,
639 const queue_elem_t *disp_q_elem)
640{
641 const int user_flag = odp_event_vector_user_flag(odp_evvec);
642 const bool esv_ena = esv_enabled();
643
644 if (user_flag == USER_FLAG_SET) {
645 /* Event already initialized by EM */
646 if (esv_ena) {
647 event = ev_hdr->event;
648 if (is_extev) {
649 event = evstate_em2usr(event, ev_hdr,
650 EVSTATE__DISPATCH);
651 esv_aggr_inner_em2usr(odp_evvec, disp_q_elem);
652 }
653 }
654
655 return event;
656 }
657
658 /*
659 * ODP event vector from outside of EM - not allocated by EM & needs init
660 *
661 * Note: ODP resets the user_flag when allocating from a pool even if the
662 * pool was pre-initialized by EM (prealloc_pools=true). Hence aggregation-
663 * produced vectors from an EM pool also enter this path.
664 */
665 odp_event_vector_user_flag_set(odp_evvec, USER_FLAG_SET);
666 /* can clear flags before evstate_...(), flags.refs_used not set for vecs */
667 ev_hdr->flags.all = 0;
669 ev_hdr->egrp = EM_EVENT_GROUP_UNDEF;
670 ev_hdr->user_area.all = 0; /* uarea fields init when used */
671
672 if (!esv_ena) {
673 ev_hdr->event = event;
674 return event;
675 }
676
677 /*
678 * ESV enabled:
679 */
680 if (!em_shm->opt.esv.prealloc_pools) {
681 event = evstate_init(event, ev_hdr, is_extev);
682 } else {
683 /* esv.prealloc_pools == true: */
684 odp_pool_t odp_pool = odp_event_vector_pool(odp_evvec);
685 em_pool_t pool = pool_odp2em(odp_pool);
686
687 if (pool == EM_POOL_UNDEF) {
688 /* External odp event vector originates from an ODP-pool */
689 event = evstate_init(event, ev_hdr, is_extev);
690 } else {
691 /* External odp event vector originates from an EM-pool */
692 event = evstate_update(event, ev_hdr, is_extev);
693 }
694 }
695
696 if (!is_extev)
697 return event;
698
699 /* is_extev=true: apply em2usr for aggr-sent inner events */
700 esv_aggr_inner_em2usr(odp_evvec, disp_q_elem);
701 return event;
702}
703
704/**
705 * Initialize the event header of a packet vector allocated outside of EM.
706 */
707static inline em_event_t
708evhdr_init_pktvec(event_hdr_t *ev_hdr, em_event_t event,
709 odp_packet_vector_t odp_pktvec, bool is_extev)
710{
711 const int user_flag = odp_packet_vector_user_flag(odp_pktvec);
712 const bool esv_ena = esv_enabled();
713
714 if (user_flag == USER_FLAG_SET) {
715 /* Event already initialized by EM */
716 if (esv_ena) {
717 event = ev_hdr->event;
718 if (is_extev)
719 event = evstate_em2usr(event, ev_hdr, EVSTATE__DISPATCH);
720 }
721
722 return event;
723 }
724
725 /*
726 * ODP pkt vector from outside of EM - not allocated by EM & needs init
727 */
728 odp_packet_vector_user_flag_set(odp_pktvec, USER_FLAG_SET);
729 /* can clear flags before evstate_...(), flags.refs_used not set for vecs */
730 ev_hdr->flags.all = 0;
732 ev_hdr->egrp = EM_EVENT_GROUP_UNDEF;
733 ev_hdr->user_area.all = 0; /* uarea fields init when used */
734
735 if (!esv_ena) {
736 ev_hdr->event = event;
737 return event;
738 }
739
740 /*
741 * ESV enabled:
742 */
743 if (!em_shm->opt.esv.prealloc_pools) {
744 event = evstate_init(event, ev_hdr, is_extev);
745 } else {
746 /* esv.prealloc_pools == true: */
747 odp_pool_t odp_pool = odp_packet_vector_pool(odp_pktvec);
748 em_pool_t pool = pool_odp2em(odp_pool);
749
750 if (pool == EM_POOL_UNDEF) {
751 /* External odp pkt originates from an ODP-pool */
752 event = evstate_init(event, ev_hdr, is_extev);
753 } else {
754 /* External odp pkt originates from an EM-pool */
755 event = evstate_update(event, ev_hdr, is_extev);
756 }
757 }
758
759 return event;
760}
761
762/**
763 * Initialize an external ODP event that have been input into EM.
764 *
765 * Initialize the event header if needed, i.e. if event originated from outside
766 * of EM from pktio or other input and was not allocated by EM via em_alloc().
767 * The odp pkt-user-ptr is used to determine whether the header has been
768 * initialized or not.
769 *
770 * @param disp_q_elem Queue the event is being dispatched from, or NULL for
771 * non-dispatch callers. Only used to gate the inner-event
772 * ESV pass for aggregation-produced vectors, see
773 * esv_aggr_inner_em2usr().
774 */
775static inline em_event_t
776event_init_odp(odp_event_t odp_event, bool is_extev,
777 const queue_elem_t *disp_q_elem, event_hdr_t **ev_hdr__out)
778{
779 const odp_event_type_t odp_type = odp_event_type(odp_event);
780 em_event_t event = event_odp2em(odp_event); /* return value, updated by ESV */
781
782 switch (odp_type) {
783 case ODP_EVENT_PACKET: {
784 odp_packet_t odp_pkt = odp_packet_from_event(odp_event);
785 event_hdr_t *ev_hdr = odp_packet_user_area(odp_pkt);
786
787 /* init event-hdr if needed (also ESV-state if used) */
788 event = evhdr_init_pkt(ev_hdr, event, odp_pkt, is_extev);
789 if (ev_hdr__out)
790 *ev_hdr__out = ev_hdr;
791 return event;
792 }
793 case ODP_EVENT_BUFFER: {
794 const bool esv_ena = esv_enabled();
795
796 if (!ev_hdr__out && !esv_ena)
797 return event;
798
799 odp_buffer_t odp_buf = odp_buffer_from_event(odp_event);
800 event_hdr_t *ev_hdr = odp_buffer_user_area(odp_buf);
801
802 if (esv_ena) {
803 /* event_odp2em() zeroed .evgen; refresh it.
804 * is_extev: state machine (re)writes .evgen for
805 * !refs_used; refs ignore evgen.
806 * else: load EM-tracked handle from ev_hdr.
807 */
808 if (is_extev)
809 event = evstate_em2usr(event, ev_hdr, EVSTATE__DISPATCH);
810 else
811 event = ev_hdr->event;
812 }
813 if (ev_hdr__out)
814 *ev_hdr__out = ev_hdr;
815 return event;
816 }
817 case ODP_EVENT_VECTOR: {
818 odp_event_vector_t odp_evvec = odp_event_vector_from_event(odp_event);
819 event_hdr_t *ev_hdr = odp_event_vector_user_area(odp_evvec);
820
821 /* init event-hdr if needed (also ESV-state if used) */
822 event = evhdr_init_evvec(ev_hdr, event, odp_evvec, is_extev,
823 disp_q_elem);
824 if (ev_hdr__out)
825 *ev_hdr__out = ev_hdr;
826 return event;
827 }
828 case ODP_EVENT_PACKET_VECTOR: {
829 odp_packet_vector_t odp_pktvec = odp_packet_vector_from_event(odp_event);
830 event_hdr_t *ev_hdr = odp_packet_vector_user_area(odp_pktvec);
831
832 /* init event-hdr if needed (also ESV-state if used) */
833 event = evhdr_init_pktvec(ev_hdr, event, odp_pktvec, is_extev);
834 if (ev_hdr__out)
835 *ev_hdr__out = ev_hdr;
836 return event;
837 }
838 case ODP_EVENT_TIMEOUT: {
839 odp_timeout_t odp_tmo = odp_timeout_from_event(odp_event);
840 event_hdr_t *ev_hdr = odp_timeout_user_area(odp_tmo);
841 const bool esv_ena = esv_enabled();
842
843 if (esv_ena) {
844 /*
845 * Update event handle, no other ESV checks done.
846 * Some timers might send a copy of the original event
847 * in tear-down, thus keep ptr but update evgen.
848 */
849 evhdl_t evhdl = {.event = event}; /* .evptr from here */
850 evhdl_t evhdr_hdl = {.event = ev_hdr->event}; /* .evgen from here */
851
852 evhdl.evgen = evhdr_hdl.evgen; /* update .evgen */
853 ev_hdr->event = evhdl.event; /* store updated hdl in hdr */
854 event = evhdl.event; /* return updated event */
855 }
856
857 if (ev_hdr__out)
858 *ev_hdr__out = ev_hdr;
859 return event;
860 }
861 default:
862 /*
863 * Non-EM events don't have an event header of their own,
864 * use a temporary core-local event header instead.
865 */
866 if (ev_hdr__out)
867 *ev_hdr__out = &em_locm.evhdr_nonem;
868 return event;
869 }
870}
871
872/**
873 * Helper to event_init_odp_multi()
874 * Initialize the event headers of packets allocated outside of EM.
875 *
876 * Note that events[] and odp_pkts[] may refer to the same memory locations,
877 * i.e. an in-place update is possible (they may alias each other).
878 *
879 * The implementation minimizes copies and preserves correctness even when
880 * events[] and odp_pkts[] alias, by sequencing reads before writes and only
881 * touching entries that need init.
882 * The only practical risk is strict aliasing but that has been disabled by
883 * compiler flags in this project (-fno-strict-aliasing).
884 */
885static inline void
886event_init_pkt_multi(const odp_packet_t odp_pkts[/*in*/] /* may alias events[] */,
887 em_event_t events[/*in,out*/],
888 event_hdr_t *ev_hdrs[/*out*/],
889 const int num, bool is_extev)
890{
891 for (int i = 0; i < num; i++)
892 ev_hdrs[i] = odp_packet_user_area(odp_pkts[i]);
893
894 evhdr_init_pkt_multi(ev_hdrs, events, odp_pkts, num, is_extev);
895}
896
897/**
898 * Helper to event_init_odp_multi()
899 * Initialize the event headers of a batch of ODP buffers.
900 *
901 * ODP buffers reaching EM are always EM-allocated, so unlike the packet
902 * helper there is no needs-init split: every entry is "already initialized".
903 *
904 * Caller must pre-populate events[] with events_odp2em() bit-casts; this
905 * supplies the correct .evptr (and is the final value for the ESV-off path).
906 */
907static inline void
908event_init_buf_multi(const odp_buffer_t odp_bufs[/*in*/],
909 em_event_t events[/*in,out*/],
910 event_hdr_t *ev_hdrs[/*out*/],
911 const int num, bool is_extev)
912{
913 for (int i = 0; i < num; i++)
914 ev_hdrs[i] = odp_buffer_user_area(odp_bufs[i]);
915
916 if (!esv_enabled())
917 return; /* bit-cast events[] already correct */
918
919 if (is_extev) {
920 /* evstate_em2usr_multi() sets events[i].evgen for !refs_used;
921 * refs ignore evgen. .evptr already correct from bit-cast.
922 */
923 evstate_em2usr_multi(events, ev_hdrs, num, EVSTATE__DISPATCH_MULTI);
924 return;
925 }
926
927 /* !is_extev: no state update -> load EM-tracked handle for the EO. */
928 for (int i = 0; i < num; i++)
929 events[i] = ev_hdrs[i]->event;
930}
931
932/**
933 * Convert from EM events to event headers and initialize the headers as needed
934 * (e.g. for events entering EM from pktio that were not allocated via em_alloc).
935 *
936 * Fast path: a leading run of same-type ODP_EVENT_PACKET or ODP_EVENT_BUFFER
937 * events is dispatched to a batched helper, avoiding the per-event
938 * odp_event_type() switch in event_init_odp(). Any tail (different type) and
939 * non-packet/non-buffer leading types fall back to the per-event loop.
940 *
941 * @param disp_q_elem Queue the events are being dispatched from, or NULL for
942 * non-dispatch callers. Forwarded to event_init_odp() to
943 * gate the inner-event ESV pass for aggregation-produced
944 * vectors, see esv_aggr_inner_em2usr().
945 */
946static inline void
947event_init_odp_multi(const odp_event_t odp_events[/*in*/],
948 em_event_t events[/*out*/], event_hdr_t *ev_hdrs[/*out*/],
949 const int num, bool is_extev,
950 const queue_elem_t *disp_q_elem)
951{
952 if (unlikely(num <= 0))
953 return;
954
955 odp_event_type_t type;
956 const int same = odp_event_type_multi(odp_events, num, &type);
957 int handled_num = 0;
958
959 switch (type) {
960 case ODP_EVENT_PACKET: {
961 odp_packet_t odp_pkts[same];
962
963 odp_packet_from_event_multi(odp_pkts, odp_events, same);
964 /* Pre-populate events[] so evhdr_init_pkt_multi() can use it
965 * for needs-init slots (evstate_init/update inputs and the
966 * !esv_ena 'ev_hdr->event = events[i]' assignment).
967 */
968 events_odp2em(odp_events, events, same);
969 event_init_pkt_multi(odp_pkts, events, ev_hdrs, same, is_extev);
970 handled_num = same;
971 break;
972 }
973 case ODP_EVENT_BUFFER: {
974 odp_buffer_t odp_bufs[same];
975
976 odp_buffer_from_event_multi(odp_bufs, odp_events, same);
977 /* Pre-populate events[] so the !esv_ena branch of
978 * event_init_buf_multi() can keep the bit-cast handle.
979 */
980 events_odp2em(odp_events, events, same);
981 event_init_buf_multi(odp_bufs, events, ev_hdrs, same, is_extev);
982 handled_num = same;
983 break;
984 }
985 default:
986 /* No batched fast path for vectors / packet-vectors / timeouts
987 * / non-EM events: keep the per-event init below.
988 */
989 break;
990 }
991
992 if (likely(handled_num == num))
993 return;
994
995 for (int i = handled_num; i < num; i++)
996 events[i] = event_init_odp(odp_events[i], is_extev, disp_q_elem,
997 &ev_hdrs[i]);
998}
999
1000/**
1001 * @brief Revert conversion of event vector table content to odp events in-place.
1002 *
1003 * Revert operation done by evvec_tbl2odp().
1004 * Convert an ODP event vector table to a table of EM events.
1005 * The content must be known to be odp events.
1006 *
1007 * For recovery purposes only (e.g. a vector returned to the user after a
1008 * failed enqueue, see send_sched() / queue_unsched_enqueue()).
1009 */
1010static inline void
1011evvec_tbl2odp_revert(odp_event_t odp_event_vec)
1012{
1013 odp_event_vector_t odp_evv = odp_event_vector_from_event(odp_event_vec);
1014 odp_event_t *odp_evtbl = NULL;
1015 const int num = odp_event_vector_tbl(odp_evv, &odp_evtbl/*out*/);
1016
1017 if (likely(num > 0)) {
1018 /* Careful! Points to same table (aliasing) */
1019 em_event_t *const ev_tbl = (em_event_t *const)odp_evtbl;
1020 event_hdr_t *ev_hdr_tbl[num];
1021
1022 event_init_odp_multi(odp_evtbl /*in*/, ev_tbl /*in,out*/,
1023 ev_hdr_tbl /*out*/, num, false,
1024 NULL/*disp_q_elem*/);
1025 }
1026}
1027
1028/**
1029 * @brief Revert conversion of event vector table content to odp packets in-place.
1030 *
1031 * Revert operation done by pktvec_tbl2odp().
1032 * Convert an ODP packet vector table to a table of EM events.
1033 * The content must be known to be raw odp packets.
1034 *
1035 * For recovery purposes only (e.g. a vector returned to the user after a
1036 * failed enqueue, see send_sched() / queue_unsched_enqueue()).
1037 */
1038static inline void
1039pktvec_tbl2odp_revert(odp_event_t odp_event_pktvec)
1040{
1041 odp_packet_vector_t pkt_vec = odp_packet_vector_from_event(odp_event_pktvec);
1042 odp_packet_t *pkt_tbl = NULL;
1043 const int pkts = odp_packet_vector_tbl(pkt_vec, &pkt_tbl/*out*/);
1044
1045 if (likely(pkts > 0)) {
1046 /* Careful! Points to same table (aliasing) */
1047 em_event_t *const ev_tbl = (em_event_t *const)pkt_tbl;
1048 event_hdr_t *ev_hdr_tbl[pkts];
1049
1050 event_init_pkt_multi(pkt_tbl /*in*/, ev_tbl /*in,out*/,
1051 ev_hdr_tbl /*out*/, pkts, false);
1052 }
1053}
1054
1055/**
1056 * Allocate an event based on an odp-buf.
1057 */
1058static inline event_hdr_t *
1059event_alloc_buf(const mpool_elem_t *const pool_elem, uint32_t size)
1060{
1061 odp_buffer_t odp_buf = ODP_BUFFER_INVALID;
1062 int subpool;
1063
1064 /*
1065 * Allocate from the 'best fit' subpool, or if that is full, from the
1066 * next subpool that has buffers available of a bigger size.
1067 */
1068 subpool = pool_find_subpool(pool_elem, size);
1069 if (unlikely(subpool < 0))
1070 return NULL;
1071
1072 for (; subpool < pool_elem->num_subpools; subpool++) {
1073 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1074
1075 if (EM_CHECK_LEVEL >= 3 &&
1076 unlikely(odp_pool == ODP_POOL_INVALID))
1077 return NULL;
1078
1079 odp_buf = odp_buffer_alloc(odp_pool);
1080 if (likely(odp_buf != ODP_BUFFER_INVALID))
1081 break;
1082 }
1083
1084 if (unlikely(odp_buf == ODP_BUFFER_INVALID))
1085 return NULL;
1086
1087 /*
1088 * odp buffer now allocated - init the EM event header
1089 * in the odp user area.
1090 */
1091 event_hdr_t *const ev_hdr = odp_buffer_user_area(odp_buf);
1092 odp_event_t odp_event = odp_buffer_to_event(odp_buf);
1093 em_event_t event = event_odp2em(odp_event);
1094
1095 ev_hdr->event = event; /* store this event handle */
1096 ev_hdr->align_offset = (uint8_t)pool_elem->align_offset;
1097
1098 /* init common ev_hdr fields in the caller */
1099
1100 return ev_hdr;
1101}
1102
1103/**
1104 * Allocate & initialize multiple events based on odp-bufs.
1105 */
1106static inline int
1107event_alloc_buf_multi(em_event_t events[/*out*/], const int num,
1108 const mpool_elem_t *pool_elem, uint32_t size,
1109 em_event_type_t type)
1110{
1111 odp_buffer_t odp_bufs[num];
1112 odp_event_t odp_event;
1113 event_hdr_t *ev_hdrs[num];
1114 int subpool;
1115 const bool esv_ena = esv_enabled();
1116
1117 /*
1118 * Allocate from the 'best fit' subpool, or if that is full, from the
1119 * next subpool that has buffers available of a bigger size.
1120 */
1121 subpool = pool_find_subpool(pool_elem, size);
1122 if (unlikely(subpool < 0))
1123 return 0;
1124
1125 int num_req = num;
1126 int num_bufs = 0;
1127 int i;
1128
1129 for (; subpool < pool_elem->num_subpools; subpool++) {
1130 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1131
1132 if (EM_CHECK_LEVEL >= 3 &&
1133 unlikely(odp_pool == ODP_POOL_INVALID))
1134 return 0;
1135
1136 int ret = odp_buffer_alloc_multi(odp_pool, &odp_bufs[num_bufs],
1137 num_req);
1138 if (unlikely(ret <= 0))
1139 continue; /* try next subpool */
1140
1141 /* store the allocated events[] */
1142 for (i = num_bufs; i < num_bufs + ret; i++) {
1143 odp_event = odp_buffer_to_event(odp_bufs[i]);
1144 events[i] = event_odp2em(odp_event);
1145 }
1146
1147 /* Init 'ret' ev-hdrs from this 'subpool'=='odp-pool' */
1148 for (i = num_bufs; i < num_bufs + ret; i++)
1149 ev_hdrs[i] = odp_buffer_user_area(odp_bufs[i]);
1150
1151 if (esv_ena) {
1152 /* reads ev_hdrs[i]->flags if prealloc_pools used */
1153 evstate_alloc_multi(&events[num_bufs] /*in/out*/,
1154 &ev_hdrs[num_bufs], ret);
1155 }
1156
1157 for (i = num_bufs; i < num_bufs + ret; i++) {
1158 ev_hdrs[i]->flags.all = 0;
1159 ev_hdrs[i]->event_type = type;
1160 if (!esv_ena)
1161 ev_hdrs[i]->event = events[i];
1162 ev_hdrs[i]->event_size = size;
1163 ev_hdrs[i]->egrp = EM_EVENT_GROUP_UNDEF;
1164
1165 ev_hdrs[i]->user_area.all = 0;
1166 ev_hdrs[i]->user_area.size = pool_elem->user_area.size;
1167 ev_hdrs[i]->user_area.isinit = 1;
1168
1169 ev_hdrs[i]->align_offset = (uint8_t)pool_elem->align_offset;
1170 }
1171
1172 num_bufs += ret;
1173 if (likely(num_bufs == num))
1174 break; /* all allocated */
1175 num_req -= ret;
1176 }
1177
1178 return num_bufs; /* number of allocated bufs (0 ... num) */
1179}
1180
1181/**
1182 * Allocate & initialize an event based on an odp-pkt.
1183 */
1184static inline event_hdr_t *
1185event_alloc_pkt(const mpool_elem_t *pool_elem, uint32_t size)
1186{
1187 const uint32_t push_len = pool_elem->align_offset;
1188 uint32_t pull_len;
1189 uint32_t alloc_size;
1190 odp_packet_t odp_pkt = ODP_PACKET_INVALID;
1191 int subpool;
1192
1193 if (size > push_len) {
1194 alloc_size = size - push_len;
1195 pull_len = 0;
1196 } else {
1197 alloc_size = 1; /* min allowed */
1198 pull_len = push_len + 1 - size;
1199 }
1200
1201 /*
1202 * Allocate from the 'best fit' subpool, or if that is full, from the
1203 * next subpool that has pkts available of a bigger size.
1204 */
1205 subpool = pool_find_subpool(pool_elem, size);
1206 if (unlikely(subpool < 0))
1207 return NULL;
1208
1209 for (; subpool < pool_elem->num_subpools; subpool++) {
1210 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1211
1212 if (EM_CHECK_LEVEL >= 3 &&
1213 unlikely(odp_pool == ODP_POOL_INVALID))
1214 return NULL;
1215
1216 odp_pkt = odp_packet_alloc(odp_pool, alloc_size);
1217 if (likely(odp_pkt != ODP_PACKET_INVALID))
1218 break;
1219 }
1220
1221 if (unlikely(odp_pkt == ODP_PACKET_INVALID))
1222 return NULL;
1223
1224 /*
1225 * odp packet now allocated - adjust the payload start address and
1226 * init the EM event header in the odp-pkt user-area
1227 */
1228
1229 /* Adjust event payload start-address based on alignment config */
1230 const void *ptr;
1231
1232 if (push_len) {
1233 ptr = odp_packet_push_head(odp_pkt, push_len);
1234 if (EM_CHECK_LEVEL >= 3 && unlikely(!ptr))
1235 goto err_pktalloc;
1236 }
1237 if (pull_len) {
1238 ptr = odp_packet_pull_tail(odp_pkt, pull_len);
1239 if (EM_CHECK_LEVEL >= 3 && unlikely(!ptr))
1240 goto err_pktalloc;
1241 }
1242
1243 /*
1244 * Set the pkt user ptr to be able to recognize pkt-events that
1245 * EM has created vs pkts from pkt-input that needs their
1246 * ev-hdrs to be initialized.
1247 */
1248 odp_packet_user_flag_set(odp_pkt, USER_FLAG_SET);
1249
1250 event_hdr_t *const ev_hdr = odp_packet_user_area(odp_pkt);
1251 odp_event_t odp_event = odp_packet_to_event(odp_pkt);
1252 em_event_t event = event_odp2em(odp_event);
1253
1254 if (EM_CHECK_LEVEL >= 3 && unlikely(ev_hdr == NULL))
1255 goto err_pktalloc;
1256
1257 /* store this event handle */
1258 ev_hdr->event = event;
1259
1260 /* init common ev_hdr fields in the caller */
1261
1262 return ev_hdr;
1263
1264err_pktalloc:
1265 odp_packet_free(odp_pkt);
1266 return NULL;
1267}
1268
1269/*
1270 * Helper for event_alloc_pkt_multi()
1271 */
1272static inline int
1273pktalloc_multi(odp_packet_t odp_pkts[/*out*/], int num,
1274 odp_pool_t odp_pool, uint32_t size,
1275 uint32_t push_len, uint32_t pull_len)
1276{
1277 int ret = odp_packet_alloc_multi(odp_pool, size, odp_pkts, num);
1278
1279 if (unlikely(ret <= 0))
1280 return 0;
1281
1282 const int num_pkts = ret; /* return value > 0 */
1283 const void *ptr = NULL;
1284 int i;
1285
1286 /* Adjust payload start-address based on alignment config */
1287 if (push_len) {
1288 for (i = 0; i < num_pkts; i++) {
1289 ptr = odp_packet_push_head(odp_pkts[i], push_len);
1290 if (EM_CHECK_LEVEL >= 3 && unlikely(!ptr))
1291 goto err_pktalloc_multi;
1292 }
1293 }
1294 if (pull_len) {
1295 for (i = 0; i < num_pkts; i++) {
1296 ptr = odp_packet_pull_tail(odp_pkts[i], pull_len);
1297 if (EM_CHECK_LEVEL >= 3 && unlikely(!ptr))
1298 goto err_pktalloc_multi; /* only before esv */
1299 }
1300 }
1301
1302 /*
1303 * Set the pkt user ptr to be able to recognize pkt-events that
1304 * EM has created vs pkts from pkt-input that needs their
1305 * ev-hdrs to be initialized.
1306 */
1307 for (i = 0; i < num_pkts; i++)
1308 odp_packet_user_flag_set(odp_pkts[i], USER_FLAG_SET);
1309
1310 return num_pkts;
1311
1312err_pktalloc_multi:
1313 odp_packet_free_sp(odp_pkts, num_pkts);
1314 return 0;
1315}
1316
1317/**
1318 * Allocate & initialize multiple events based on odp-pkts.
1319 */
1320static inline int
1321event_alloc_pkt_multi(em_event_t events[/*out*/], const int num,
1322 const mpool_elem_t *pool_elem, uint32_t size,
1323 em_event_type_t type)
1324{
1325 const uint32_t push_len = pool_elem->align_offset;
1326 uint32_t pull_len;
1327 odp_packet_t odp_pkts[num];
1328 /* use same output-array: odp_events[] = events[] */
1329 odp_event_t *const odp_events = (odp_event_t *)events;
1330 event_hdr_t *ev_hdrs[num];
1331 uint32_t alloc_size;
1332 int subpool;
1333 const bool esv_ena = esv_enabled();
1334
1335 if (size > push_len) {
1336 alloc_size = size - push_len;
1337 pull_len = 0;
1338 } else {
1339 alloc_size = 1; /* min allowed */
1340 pull_len = push_len + 1 - size;
1341 }
1342
1343 /*
1344 * Allocate from the 'best fit' subpool, or if that is full, from the
1345 * next subpool that has pkts available of a bigger size.
1346 */
1347 subpool = pool_find_subpool(pool_elem, size);
1348 if (unlikely(subpool < 0))
1349 return 0;
1350
1351 int num_req = num;
1352 int num_pkts = 0;
1353 int i;
1354
1355 for (; subpool < pool_elem->num_subpools; subpool++) {
1356 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1357
1358 if (EM_CHECK_LEVEL >= 3 &&
1359 unlikely(odp_pool == ODP_POOL_INVALID))
1360 return 0;
1361
1362 int ret = pktalloc_multi(&odp_pkts[num_pkts], num_req,
1363 odp_pool, alloc_size,
1364 push_len, pull_len);
1365 if (unlikely(ret <= 0))
1366 continue; /* try next subpool */
1367
1368 /*
1369 * Init 'ret' ev-hdrs from this 'subpool'=='odp-pool'.
1370 * Note: odp_events[] points&writes into events[out]
1371 */
1372 odp_packet_to_event_multi(&odp_pkts[num_pkts],
1373 &odp_events[num_pkts], ret);
1374
1375 for (i = num_pkts; i < num_pkts + ret; i++)
1376 ev_hdrs[i] = odp_packet_user_area(odp_pkts[i]);
1377
1378 /*
1379 * Note: events[] == odp_events[] before ESV init.
1380 * Don't touch odp_events[] during this loop-round anymore.
1381 */
1382 if (esv_ena) {
1383 /* reads ev_hdrs[i]->flags if prealloc_pools used */
1384 evstate_alloc_multi(&events[num_pkts] /*in/out*/,
1385 &ev_hdrs[num_pkts], ret);
1386 }
1387
1388 for (i = num_pkts; i < num_pkts + ret; i++) {
1389 ev_hdrs[i]->flags.all = 0;
1390 ev_hdrs[i]->event_type = type;
1391 if (!esv_ena)
1392 ev_hdrs[i]->event = events[i];
1393 ev_hdrs[i]->event_size = size; /* original size */
1394 ev_hdrs[i]->egrp = EM_EVENT_GROUP_UNDEF;
1395
1396 ev_hdrs[i]->user_area.all = 0;
1397 ev_hdrs[i]->user_area.size = pool_elem->user_area.size;
1398 ev_hdrs[i]->user_area.isinit = 1;
1399 /*ev_hdrs[i]->align_offset = needed by odp bufs only*/
1400 }
1401
1402 num_pkts += ret;
1403 if (likely(num_pkts == num))
1404 break; /* all allocated */
1405 num_req -= ret;
1406 }
1407
1408 return num_pkts; /* number of allocated pkts */
1409}
1410
1411static inline event_hdr_t *
1412event_alloc_vector__odp_pktvec(const mpool_elem_t *pool_elem, uint32_t size)
1413{
1414 odp_packet_vector_t odp_pktvec = ODP_PACKET_VECTOR_INVALID;
1415 int subpool;
1416
1417 /*
1418 * Allocate from the 'best fit' subpool, or if that is full, from the
1419 * next subpool that has pkts available of a bigger size.
1420 */
1421 subpool = pool_find_subpool(pool_elem, size);
1422 if (unlikely(subpool < 0))
1423 return NULL;
1424
1425 for (; subpool < pool_elem->num_subpools; subpool++) {
1426 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1427
1428 if (EM_CHECK_LEVEL >= 3 &&
1429 unlikely(odp_pool == ODP_POOL_INVALID))
1430 return NULL;
1431
1432 odp_pktvec = odp_packet_vector_alloc(odp_pool);
1433 if (likely(odp_pktvec != ODP_PACKET_VECTOR_INVALID))
1434 break;
1435 }
1436
1437 if (unlikely(odp_pktvec == ODP_PACKET_VECTOR_INVALID))
1438 return NULL;
1439
1440 /*
1441 * Packet vector now allocated:
1442 * Init the EM event header in the odp-pkt-vector user-area.
1443 */
1444
1445 /*
1446 * Set the pktvec user flag to be able to recognize vectors that
1447 * EM has created vs. vectors from pkt-input that needs their
1448 * ev-hdrs to be initialized.
1449 */
1450 odp_packet_vector_user_flag_set(odp_pktvec, USER_FLAG_SET);
1451
1452 event_hdr_t *const ev_hdr = odp_packet_vector_user_area(odp_pktvec);
1453 odp_event_t odp_event = odp_packet_vector_to_event(odp_pktvec);
1454 em_event_t event = event_odp2em(odp_event);
1455
1456 if (EM_CHECK_LEVEL >= 3 && unlikely(ev_hdr == NULL))
1457 goto err_vecalloc;
1458
1459 ev_hdr->event = event; /* store this event handle */
1461 /* init common ev_hdr fields in the caller */
1462
1463 return ev_hdr;
1464
1465err_vecalloc:
1466 odp_packet_vector_free(odp_pktvec);
1467 return NULL;
1468}
1469
1470static inline event_hdr_t *
1471event_alloc_vector__odp_evvec(const mpool_elem_t *pool_elem, uint32_t size)
1472{
1473 odp_event_vector_t odp_evvec = ODP_EVENT_VECTOR_INVALID;
1474 int subpool;
1475
1476 /*
1477 * Allocate from the 'best fit' subpool, or if that is full, from the
1478 * next subpool that has pkts available of a bigger size.
1479 */
1480 subpool = pool_find_subpool(pool_elem, size);
1481 if (unlikely(subpool < 0))
1482 return NULL;
1483
1484 for (; subpool < pool_elem->num_subpools; subpool++) {
1485 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1486
1487 if (EM_CHECK_LEVEL >= 3 &&
1488 unlikely(odp_pool == ODP_POOL_INVALID))
1489 return NULL;
1490
1491 odp_evvec = odp_event_vector_alloc(odp_pool);
1492 if (likely(odp_evvec != ODP_EVENT_VECTOR_INVALID))
1493 break;
1494 }
1495
1496 if (unlikely(odp_evvec == ODP_EVENT_VECTOR_INVALID))
1497 return NULL;
1498
1499 /*
1500 * ODP Event vector now allocated:
1501 * Init the EM event header in the odp-event-vector user-area.
1502 */
1503
1504 /*
1505 * Set the event vector user flag to be able to recognize vectors that
1506 * EM has created vs. vectors that needs their ev-hdrs to be initialized.
1507 */
1508 odp_event_vector_user_flag_set(odp_evvec, USER_FLAG_SET);
1509
1510 event_hdr_t *const ev_hdr = odp_event_vector_user_area(odp_evvec);
1511 odp_event_t odp_event = odp_event_vector_to_event(odp_evvec);
1512 em_event_t event = event_odp2em(odp_event);
1513
1514 if (EM_CHECK_LEVEL >= 3 && unlikely(ev_hdr == NULL))
1515 goto err_vecalloc;
1516
1517 ev_hdr->event = event; /* store this event handle */
1519 /* init common ev_hdr fields in the caller */
1520
1521 return ev_hdr;
1522
1523err_vecalloc:
1524 odp_event_vector_free(odp_evvec);
1525 return NULL;
1526}
1527
1528static inline event_hdr_t *
1529event_alloc_vector(const mpool_elem_t *pool_elem, uint32_t size)
1530{
1531 if (em_shm->opt.vector.backend == EM_VECTOR_BACKEND_PACKET)
1532 return event_alloc_vector__odp_pktvec(pool_elem, size);
1533 else
1534 return event_alloc_vector__odp_evvec(pool_elem, size);
1535}
1536
1537/*
1538 * Helper for event_alloc_vector_multi__odp_pktvec()
1539 */
1540static inline int
1541pktvec_alloc_multi(odp_packet_vector_t odp_pktvecs[/*out*/], int num,
1542 odp_pool_t odp_pool)
1543{
1544 int i;
1545
1546 for (i = 0; i < num; i++) {
1547 odp_pktvecs[i] = odp_packet_vector_alloc(odp_pool);
1548 if (unlikely(odp_pktvecs[i] == ODP_PACKET_VECTOR_INVALID))
1549 break;
1550 }
1551
1552 const int num_vecs = i;
1553
1554 if (unlikely(num_vecs == 0))
1555 return 0;
1556
1557 /*
1558 * Set the pkt vector user flag to be able to recognize vector-events
1559 * that EM has created vs. vectors from pkt-input that needs their
1560 * ev-hdrs to be initialized.
1561 */
1562 for (i = 0; i < num_vecs; i++)
1563 odp_packet_vector_user_flag_set(odp_pktvecs[i], USER_FLAG_SET);
1564
1565 return num_vecs;
1566}
1567
1568/*
1569 * Helper for event_alloc_vector_multi__odp_evvec()
1570 */
1571static inline int
1572evvec_alloc_multi(odp_event_vector_t odp_evvecs[/*out*/], int num,
1573 odp_pool_t odp_pool)
1574{
1575 int i;
1576
1577 for (i = 0; i < num; i++) {
1578 odp_evvecs[i] = odp_event_vector_alloc(odp_pool);
1579 if (unlikely(odp_evvecs[i] == ODP_EVENT_VECTOR_INVALID))
1580 break;
1581 }
1582
1583 const int num_vecs = i;
1584
1585 if (unlikely(num_vecs == 0))
1586 return 0;
1587
1588 /*
1589 * Set the event vector user flag to be able to recognize vector-events
1590 * that EM has created vs. vectors that need their ev-hdrs to be initialized.
1591 */
1592 for (i = 0; i < num_vecs; i++)
1593 odp_event_vector_user_flag_set(odp_evvecs[i], USER_FLAG_SET);
1594
1595 return num_vecs;
1596}
1597
1598static inline int
1599event_alloc_vector_multi__odp_pktvec(em_event_t events[/*out*/], const int num,
1600 const mpool_elem_t *pool_elem, uint32_t size,
1601 em_event_type_t type)
1602{
1603 odp_packet_vector_t odp_pktvecs[num];
1604 /* use same output-array: odp_events[] = events[] */
1605 odp_event_t *const odp_events = (odp_event_t *)events;
1606 event_hdr_t *ev_hdrs[num];
1607 int subpool;
1608 const bool esv_ena = esv_enabled();
1609
1610 /*
1611 * Allocate from the 'best fit' subpool, or if that is full, from the
1612 * next subpool that has pkts available of a bigger size.
1613 */
1614 subpool = pool_find_subpool(pool_elem, size);
1615 if (unlikely(subpool < 0))
1616 return 0;
1617
1618 int num_req = num;
1619 int num_vecs = 0;
1620 int i;
1621
1622 for (; subpool < pool_elem->num_subpools; subpool++) {
1623 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1624
1625 if (EM_CHECK_LEVEL >= 3 &&
1626 unlikely(odp_pool == ODP_POOL_INVALID))
1627 return 0;
1628
1629 int ret = pktvec_alloc_multi(&odp_pktvecs[num_vecs], num_req, odp_pool);
1630
1631 if (unlikely(ret <= 0))
1632 continue; /* try next subpool */
1633
1634 /*
1635 * Init 'ret' ev-hdrs from this 'subpool'=='odp-pool'.
1636 * Note: odp_events[] points&writes into events[out]
1637 */
1638 for (i = num_vecs; i < num_vecs + ret; i++) {
1639 odp_events[i] = odp_packet_vector_to_event(odp_pktvecs[i]);
1640 ev_hdrs[i] = odp_packet_vector_user_area(odp_pktvecs[i]);
1641 }
1642
1643 /*
1644 * Note: events[] == odp_events[] before ESV init.
1645 * Don't touch odp_events[] during this loop-round anymore.
1646 */
1647 if (esv_ena) {
1648 /* reads ev_hdrs[i]->flags if prealloc_pools used */
1649 evstate_alloc_multi(&events[num_vecs] /*in/out*/,
1650 &ev_hdrs[num_vecs], ret);
1651 }
1652
1653 for (i = num_vecs; i < num_vecs + ret; i++) {
1654 ev_hdrs[i]->flags.all = 0;
1655 /* ev_hdrs[i]->align_offset = needed by odp bufs only */
1656 ev_hdrs[i]->event_type = type;
1657 if (!esv_ena)
1658 ev_hdrs[i]->event = events[i];
1659 ev_hdrs[i]->event_size = size; /* original vec size */
1660 ev_hdrs[i]->egrp = EM_EVENT_GROUP_UNDEF;
1661
1662 ev_hdrs[i]->user_area.all = 0;
1663 ev_hdrs[i]->user_area.size = pool_elem->user_area.size;
1664 ev_hdrs[i]->user_area.isinit = 1;
1665 ev_hdrs[i]->vector_type = EM_EVENT_TYPE_PACKET;
1666 }
1667
1668 num_vecs += ret;
1669 if (likely(num_vecs == num))
1670 break; /* all allocated */
1671 num_req -= ret;
1672 }
1673
1674 return num_vecs; /* number of allocated pkts */
1675}
1676
1677static inline int
1678event_alloc_vector_multi__odp_evvec(em_event_t events[/*out*/], const int num,
1679 const mpool_elem_t *pool_elem, uint32_t size,
1680 em_event_type_t type)
1681{
1682 odp_event_vector_t odp_evvecs[num];
1683 /* use same output-array: odp_events[] = events[] */
1684 odp_event_t *const odp_events = (odp_event_t *)events;
1685 event_hdr_t *ev_hdrs[num];
1686 int subpool;
1687 const bool esv_ena = esv_enabled();
1688
1689 /*
1690 * Allocate from the 'best fit' subpool, or if that is full, from the
1691 * next subpool that has pkts available of a bigger size.
1692 */
1693 subpool = pool_find_subpool(pool_elem, size);
1694 if (unlikely(subpool < 0))
1695 return 0;
1696
1697 int num_req = num;
1698 int num_vecs = 0;
1699 int i;
1700
1701 for (; subpool < pool_elem->num_subpools; subpool++) {
1702 odp_pool_t odp_pool = pool_elem->odp_pool[subpool];
1703
1704 if (EM_CHECK_LEVEL >= 3 &&
1705 unlikely(odp_pool == ODP_POOL_INVALID))
1706 return 0;
1707
1708 int ret = evvec_alloc_multi(&odp_evvecs[num_vecs], num_req, odp_pool);
1709
1710 if (unlikely(ret <= 0))
1711 continue; /* try next subpool */
1712
1713 /*
1714 * Init 'ret' ev-hdrs from this 'subpool'=='odp-pool'.
1715 * Note: odp_events[] points&writes into events[out]
1716 */
1717 for (i = num_vecs; i < num_vecs + ret; i++) {
1718 odp_events[i] = odp_event_vector_to_event(odp_evvecs[i]);
1719 ev_hdrs[i] = odp_event_vector_user_area(odp_evvecs[i]);
1720 }
1721
1722 /*
1723 * Note: events[] == odp_events[] before ESV init.
1724 * Don't touch odp_events[] during this loop-round anymore.
1725 */
1726 if (esv_ena) {
1727 /* reads ev_hdrs[i]->flags if prealloc_pools used */
1728 evstate_alloc_multi(&events[num_vecs] /*in/out*/,
1729 &ev_hdrs[num_vecs], ret);
1730 }
1731
1732 for (i = num_vecs; i < num_vecs + ret; i++) {
1733 ev_hdrs[i]->flags.all = 0;
1734 /* ev_hdrs[i]->align_offset = needed by odp bufs only */
1735 ev_hdrs[i]->event_type = type;
1736 if (!esv_ena)
1737 ev_hdrs[i]->event = events[i];
1738 ev_hdrs[i]->event_size = size; /* original vec size */
1739 ev_hdrs[i]->egrp = EM_EVENT_GROUP_UNDEF;
1740
1741 ev_hdrs[i]->user_area.all = 0;
1742 ev_hdrs[i]->user_area.size = pool_elem->user_area.size;
1743 ev_hdrs[i]->user_area.isinit = 1;
1744 ev_hdrs[i]->vector_type = EM_EVENT_TYPE_ANY;
1745 }
1746
1747 num_vecs += ret;
1748 if (likely(num_vecs == num))
1749 break; /* all allocated */
1750 num_req -= ret;
1751 }
1752
1753 return num_vecs; /* number of allocated events */
1754}
1755
1756/**
1757 * Allocate & initialize multiple events based on odp-pkt-vectors.
1758 */
1759static inline int
1760event_alloc_vector_multi(em_event_t events[/*out*/], const int num,
1761 const mpool_elem_t *pool_elem, uint32_t size,
1762 em_event_type_t type)
1763{
1764 if (em_shm->opt.vector.backend == EM_VECTOR_BACKEND_PACKET)
1765 return event_alloc_vector_multi__odp_pktvec(events /*out*/, num,
1766 pool_elem, size, type);
1767 else
1768 return event_alloc_vector_multi__odp_evvec(events /*out*/, num,
1769 pool_elem, size, type);
1770}
1771
1772/**
1773 * Helper for em_alloc() and em_event_clone()
1774 */
1775static inline em_event_t
1776event_alloc(const mpool_elem_t *pool_elem, uint32_t size, em_event_type_t type,
1777 const uint16_t api_op)
1778{
1779 /*
1780 * EM event pools created with type=PKT can support:
1781 * - SW events (bufs)
1782 * - pkt events.
1783 *
1784 * EM event pools created with type=SW can support:
1785 * - SW events (bufs) only
1786 */
1787 event_hdr_t *ev_hdr = NULL;
1788
1789 if (pool_elem->event_type == EM_EVENT_TYPE_PACKET)
1790 ev_hdr = event_alloc_pkt(pool_elem, size);
1791 else if (pool_elem->event_type == EM_EVENT_TYPE_SW)
1792 ev_hdr = event_alloc_buf(pool_elem, size);
1793 else if (pool_elem->event_type == EM_EVENT_TYPE_VECTOR)
1794 ev_hdr = event_alloc_vector(pool_elem, size);
1795
1796 if (unlikely(!ev_hdr))
1797 return EM_EVENT_UNDEF;
1798
1799 /*
1800 * event now allocated:
1801 * ev_hdr->event = stored by event_alloc_pkt/buf/vector()
1802 */
1803 /* Update event ESV state for alloc */
1804 if (esv_enabled())
1805 (void)evstate_alloc(ev_hdr->event, ev_hdr, api_op);
1806
1807 ev_hdr->flags.all = 0; /* clear only after evstate_alloc() */
1808 ev_hdr->event_type = type; /* store the event type */
1809 ev_hdr->event_size = size; /* store requested size */
1810 ev_hdr->egrp = EM_EVENT_GROUP_UNDEF;
1811
1812 ev_hdr->user_area.all = 0;
1813 ev_hdr->user_area.size = pool_elem->user_area.size;
1814 ev_hdr->user_area.isinit = 1;
1815 /* ev_hdr->align_offset = init by event_alloc_buf() when needed */
1816
1817 return ev_hdr->event;
1818}
1819
1820/**
1821 * Return a pointer to the EM event user payload.
1822 * Helper to e.g. EM API em_event_pointer()
1823 */
1824static inline void *
1825event_pointer(em_event_t event)
1826{
1827 const odp_event_t odp_event = event_em2odp(event);
1828 const odp_event_type_t odp_etype = odp_event_type(odp_event);
1829 void *ev_ptr = NULL; /* return value */
1830
1831 if (odp_etype == ODP_EVENT_PACKET) {
1832 const odp_packet_t odp_pkt = odp_packet_from_event(odp_event);
1833
1834 ev_ptr = odp_packet_data(odp_pkt);
1835 } else if (odp_etype == ODP_EVENT_BUFFER) {
1836 const odp_buffer_t odp_buf = odp_buffer_from_event(odp_event);
1837 const event_hdr_t *ev_hdr = odp_buffer_user_area(odp_buf);
1838 const uint32_t align_offset = ev_hdr->align_offset;
1839
1840 ev_ptr = odp_buffer_addr(odp_buf);
1841
1842 if (align_offset)
1843 ev_ptr = (void *)((uintptr_t)ev_ptr + 32 - align_offset);
1844 }
1845
1846 return ev_ptr; /* NULL for unrecognized odp_etype, also for vectors */
1847}
1848
1849static inline bool
1850event_has_ref(em_event_t event)
1851{
1852 odp_event_t odp_event = event_em2odp(event);
1853 odp_event_type_t odp_etype = odp_event_type(odp_event);
1854
1855 if (odp_etype != ODP_EVENT_PACKET)
1856 return false;
1857
1858 odp_packet_t odp_pkt = odp_packet_from_event(odp_event);
1859
1860 return odp_packet_has_ref(odp_pkt) ? true : false;
1861}
1862
1863#ifdef __cplusplus
1864}
1865#endif
1866
1867#endif /* EM_EVENT_INLINE_H_ */
#define USER_FLAG_SET
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
#define EM_CHECK_LEVEL
uint32_t em_event_type_t
#define EM_POOL_UNDEF
#define EM_EVENT_UNDEF
#define EM_EVENT_GROUP_UNDEF
@ EM_EVENT_TYPE_SW
@ EM_EVENT_TYPE_PACKET
@ EM_EVENT_TYPE_ANY
@ EM_EVENT_TYPE_VECTOR
event_hdr_t evhdr_nonem
Definition em_mem.h:306
em_cfgfile_opts_t opt
Definition em_mem.h:99
uint8_t refs_used
em_event_t event
uint8_t align_offset
ev_hdr_user_area_t user_area
uint8_t aggr_sent
union event_hdr::@43 flags
uint32_t event_size
em_event_type_t event_type
em_event_group_t egrp
em_event_type_t vector_type
struct mpool_elem_t::@54 user_area
uint32_t align_offset
odp_pool_t odp_pool[EM_MAX_SUBPOOLS]
uint16_t size
em_event_type_t event_type
queue_elem_flags_t flags