EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_dispatcher_inline.h
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * Copyright (c) 2023-2026, Nokia Solutions and Networks
4 */
5
6/**
7 * @file
8 * EM internal dispatcher functions
9 */
10
11#ifndef EM_DISPATCHER_INLINE_H_
12#define EM_DISPATCHER_INLINE_H_
13
14#include <stdbool.h>
15#include <stdint.h>
16
17#include <odp_api.h>
18
19#include <event_machine.h>
21
22#include "em_atomic_group.h"
24#include "em_dispatcher_types.h"
25#include "em_eo.h"
26#include "em_eo_types.h"
27#include "em_error.h"
28#include "em_event.h"
29#include "em_event_group.h"
31#include "em_event_inline.h"
32#include "em_event_state.h"
33#include "em_event_types.h"
34#include "em_hook_types.h"
35#include "em_hooks.h"
36#include "em_mem.h"
37#include "em_queue.h"
38#include "em_queue_inline.h"
39#include "em_queue_types.h"
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44
45/*
46 * Max number of events handled in a single dispatch_events() burst.
47 * dispatch_events() is reached both from the scheduler burst (bounded by
48 * EM_SCHED_MULTI_MAX_BURST) and from the atomic-group burst (bounded by
49 * EM_SCHED_AG_MULTI_MAX_BURST). Use the larger of the two to size the fixed
50 * (non-VLA) event tables on the dispatch fast path.
51 */
52#define DISPATCH_MULTI_MAX_BURST \
53 MAX(EM_SCHED_MULTI_MAX_BURST, EM_SCHED_AG_MULTI_MAX_BURST)
54
55static inline void
56dispatch_eo_multircv_single(em_event_t event, event_hdr_t *ev_hdr,
57 queue_elem_t *const q_elem, const bool check_local_qs);
58static inline void
59dispatch_eo_multircv_burst(em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
60 const int num_events, queue_elem_t *const q_elem,
61 const bool check_local_qs);
62static inline void
63dispatch_eo_rcv_single(em_event_t event, event_hdr_t *ev_hdr,
64 queue_elem_t *const q_elem, const bool check_local_qs);
65static inline void
66dispatch_eo_rcv_burst(em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
67 const int num_events, queue_elem_t *const q_elem,
68 const bool check_local_qs);
69
70/**
71 * Helper: Remove undef-event entries from ev_tbl[]
72 */
73static inline int pack_ev_tbl(em_event_t ev_tbl[/*in,out*/], const int num)
74{
75 if (num == 1) {
76 if (ev_tbl[0] != EM_EVENT_UNDEF)
77 return 1;
78 else
79 return 0;
80 }
81
82 int pack = 0;
83
84 for (int i = 0; i < num; i++) {
85 if (ev_tbl[i] != EM_EVENT_UNDEF) {
86 if (pack < i)
87 ev_tbl[pack] = ev_tbl[i];
88 pack++;
89 }
90 }
91
92 return pack;
93}
94
95static inline uint64_t debug_timestamp(void)
96{
97 /* compile time selection */
98 return EM_DEBUG_TIMESTAMP_ENABLE == 1 ? odp_time_global_ns() : odp_time_global_strict_ns();
99}
100
101/**
102 * Run all dispatch enter-callback functions.
103 *
104 * @note Neither EO-receive nor any further enter-callbacks will be called if
105 * all events have been dropped by the callbacks already run, i.e.
106 * no callback or EO-receive will be called with 'num=0'.
107 *
108 * @param eo EO handle
109 * @param eo_ctx EO context data
110 * @param[in,out] ev_tbl Event table
111 * @param num_events Number of events in the event table
112 * @param queue Queue from which this event came from
113 * @param q_ctx Queue context data
114 *
115 * @return The number of events in ev_tbl[] after all dispatch enter callbacks
116 */
117static inline int
118dispatch_enter_cb(em_eo_t eo, void **eo_ctx,
119 em_event_t ev_tbl[/*in,out*/], const int num_events,
120 em_queue_t *queue, void **q_ctx)
121{
122 const hook_tbl_t *cb_tbl = em_shm->dispatch_enter_cb_tbl;
123 em_dispatch_enter_func_t dispatch_enter_fn;
124 int num = num_events;
125
126 for (int i = 0; i < EM_CALLBACKS_MAX && num > 0; i++) {
127 dispatch_enter_fn = cb_tbl->tbl[i].disp_enter;
128 if (dispatch_enter_fn == NULL)
129 break;
130 dispatch_enter_fn(eo, eo_ctx, ev_tbl, num, queue, q_ctx);
131 num = pack_ev_tbl(ev_tbl, num);
132 }
133
134 return num;
135}
136
137/**
138 * Run all dispatch exit-callback functions.
139 *
140 * @param eo EO handle
141 */
142static inline void
143dispatch_exit_cb(em_eo_t eo)
144{
145 const hook_tbl_t *dispatch_exit_cb_tbl = em_shm->dispatch_exit_cb_tbl;
146 em_dispatch_exit_func_t dispatch_exit_fn;
147
148 for (int i = 0; i < EM_CALLBACKS_MAX; i++) {
149 dispatch_exit_fn = dispatch_exit_cb_tbl->tbl[i].disp_exit;
150 if (dispatch_exit_fn == NULL)
151 return;
152 dispatch_exit_fn(eo);
153 }
154}
155
156/**
157 * Returns true if any dispatch enter- or exit-callback is registered.
158 *
159 * Compiles to a constant 'false' when EM_DISPATCH_CALLBACKS_ENABLE is 0,
160 * letting callers dead-code-eliminate the callback path entirely.
161 */
162static inline bool
163dispatch_callbacks_registered(void)
164{
165 return EM_DISPATCH_CALLBACKS_ENABLE &&
166 (em_shm->dispatch_enter_cb_tbl->tbl[0].disp_enter != NULL ||
167 em_shm->dispatch_exit_cb_tbl->tbl[0].disp_exit != NULL);
168}
169
170static inline void
171call_eo_rcv_fn(const em_eo_t eo, const em_receive_func_t eo_receive_func,
172 em_event_t event, event_hdr_t *ev_hdr, queue_elem_t *const q_elem)
173{
174 em_locm_t *const locm = &em_locm;
175 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
176 void *queue_ctx = q_elem->context;
177 void *eo_ctx = q_elem->eo_ctx;
178 int num = 1;
179
180 locm->current.rcv_multi_cnt = 1;
181 /* Check and set core local event group (before dispatch callback(s)) */
182 event_group_set_local(ev_hdr->egrp, ev_hdr->egrp_gen, 1);
183
184 if (EM_DISPATCH_CALLBACKS_ENABLE) {
185 em_event_t ev_tbl[1] = {event};
186
187 num = dispatch_enter_cb(eo, &eo_ctx, ev_tbl/*in,out*/, 1,
188 &queue, &queue_ctx);
189 if (num && ev_tbl[0] != event) {
190 /* user-callback changed event: update event & hdr */
191 event = ev_tbl[0];
192 ev_hdr = event_to_hdr(event);
193 }
194 }
195
196 if (likely(num == 1)) {
197 em_event_type_t event_type = ev_hdr->event_type;
198 /*
199 * Call the EO receive function
200 * (only if the dispatch callback(s) did not free the event)
201 */
202 eo_receive_func(eo_ctx, event, event_type,
203 queue, queue_ctx);
204 }
205
206 if (EM_DISPATCH_CALLBACKS_ENABLE)
207 dispatch_exit_cb(eo);
208
209 /*
210 * Event belongs to an event_group, update the count and
211 * if requested send notifications
212 */
213 event_group_elem_t *const current_egrp_elem = locm->current.egrp_elem;
214
215 if (current_egrp_elem) {
216 /*
217 * Atomically decrease the event group count.
218 * If the new count is zero, send notification events.
219 */
220 event_group_count_decrement(1, current_egrp_elem);
221 locm->current.egrp_elem = NULL;
222 }
223}
224
225/**
226 * Fast-path equivalent of call_eo_rcv_fn() for when no dispatch callbacks are
227 * registered.
228 *
229 * The caller must have pre-extracted q_elem->queue, q_elem->context and
230 * q_elem->eo_ctx into locals so they can be passed by value to the
231 * receive function (and, when called from a burst loop, reused across
232 * the loop iterations).
233 *
234 * Marked always_inline because GCC's heuristics may decline to inline this
235 * helper when called from multiple sites with a function pointer call
236 * inside; we rely on inlining for the dispatch fast path to remain fast.
237 */
238__attribute__((always_inline))
239static inline void
240call_eo_rcv_fn__no_cb(const em_receive_func_t eo_receive_func,
241 em_event_t event, event_hdr_t *const ev_hdr,
242 const em_queue_t queue, void *const queue_ctx,
243 void *const eo_ctx)
244{
245 em_locm_t *const locm = &em_locm;
246
247 locm->current.rcv_multi_cnt = 1;
248 event_group_set_local(ev_hdr->egrp, ev_hdr->egrp_gen, 1);
249
250 eo_receive_func(eo_ctx, event, ev_hdr->event_type,
251 queue, queue_ctx);
252
253 event_group_elem_t *const current_egrp_elem = locm->current.egrp_elem;
254
255 if (current_egrp_elem) {
256 event_group_count_decrement(1, current_egrp_elem);
257 locm->current.egrp_elem = NULL;
258 }
259}
260
261/**
262 * @note All events belong to the same event group
263 * @note Event type dropped from multi-event receive - use em_event_type()
264 */
265static inline void
266call_eo_multircv_fn(const em_eo_t eo,
267 const em_receive_multi_func_t eo_receive_multi_func,
268 em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
269 const int num_events, queue_elem_t *const q_elem)
270{
271 em_locm_t *const locm = &em_locm;
272 em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
273 void *queue_ctx = q_elem->context;
274 void *eo_ctx = q_elem->eo_ctx;
275 int num = num_events;
276
277 locm->current.rcv_multi_cnt = num_events;
278 /* Check and set core local event group (before dispatch callback(s)) */
279 event_group_set_local(ev_hdr_tbl[0]->egrp, ev_hdr_tbl[0]->egrp_gen,
280 num_events);
281
282 if (EM_DISPATCH_CALLBACKS_ENABLE)
283 num = dispatch_enter_cb(eo, &eo_ctx,
284 ev_tbl/*in,out*/, num_events,
285 &queue, &queue_ctx);
286 if (likely(num > 0)) {
287 /*
288 * Call the EO multi-event receive function
289 * (only if the dispatch callback(s) did not free all events)
290 */
291 eo_receive_multi_func(eo_ctx, ev_tbl, num, queue, queue_ctx);
292 }
293
294 if (EM_DISPATCH_CALLBACKS_ENABLE)
295 dispatch_exit_cb(eo);
296
297 /*
298 * Event belongs to an event_group, update the count and
299 * if requested send notifications
300 */
301 event_group_elem_t *const current_egrp_elem = locm->current.egrp_elem;
302
303 if (current_egrp_elem) {
304 /*
305 * Atomically decrease the event group count.
306 * If the new count is zero, send notification events.
307 */
308 event_group_count_decrement(num_events, current_egrp_elem);
309 locm->current.egrp_elem = NULL;
310 }
311}
312
313/**
314 * Fast-path equivalent of call_eo_multircv_fn() for when no dispatch callbacks
315 * are registered.
316 *
317 * The caller must have pre-extracted q_elem->queue, q_elem->context and
318 * q_elem->eo_ctx into locals so they can be passed by value to the
319 * receive function (and, when called from a burst loop, reused across
320 * the burst's batches).
321 *
322 * Marked always_inline because GCC's heuristics may decline to inline this
323 * helper when called from multiple sites with a function pointer call
324 * inside; we rely on inlining for the dispatch fast path to remain fast.
325 */
326__attribute__((always_inline))
327static inline void
328call_eo_multircv_fn__no_cb(const em_receive_multi_func_t eo_receive_multi_func,
329 em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
330 const int num_events,
331 const em_queue_t queue, void *const queue_ctx,
332 void *const eo_ctx)
333{
334 em_locm_t *const locm = &em_locm;
335
336 locm->current.rcv_multi_cnt = num_events;
337 event_group_set_local(ev_hdr_tbl[0]->egrp, ev_hdr_tbl[0]->egrp_gen,
338 num_events);
339
340 eo_receive_multi_func(eo_ctx, ev_tbl, num_events, queue, queue_ctx);
341
342 event_group_elem_t *const current_egrp_elem = locm->current.egrp_elem;
343
344 if (current_egrp_elem) {
345 event_group_count_decrement(num_events, current_egrp_elem);
346 locm->current.egrp_elem = NULL;
347 }
348}
349
350/**
351 * @brief Helper to dispatch_local_queues() for a single event
352 */
353static inline void
354dispatch_local_single(stash_entry_t entry)
355{
356 em_locm_t *const locm = &em_locm;
357 odp_event_t odp_event;
358 em_event_t event;
359 event_hdr_t *ev_hdr;
360
361 /* dst local queue */
362 const int qidx = entry.qidx;
363 const em_queue_t queue = queue_idx2hdl(qidx);
364 queue_elem_t *const q_elem = queue_elem_get(queue);
365
366 locm->current.q_elem = q_elem; /* before event_init_... for ESV error prints */
367
368 odp_event = (odp_event_t)(uintptr_t)entry.evptr;
369 /* Event might originate from outside (via polled pktio) of EM and need init */
370 event = event_init_odp(odp_event, true/*is_extev*/, q_elem, &ev_hdr/*out*/);
371
372 if (unlikely(!q_elem || q_elem->state != EM_QUEUE_STATE_READY)) {
373 em_free(event);
374 /* Consider removing the logging */
375 EM_LOG(EM_LOG_PRINT,
376 "EM info: %s(): localQ:%" PRI_QUEUE ":\n"
377 "Not ready - state:%d drop:1 event\n",
378 __func__, queue, q_elem ? q_elem->state : 0);
379 return;
380 }
381
382 if (q_elem->flags.use_multi_rcv)
383 dispatch_eo_multircv_single(event, ev_hdr, q_elem, false);
384 else
385 dispatch_eo_rcv_single(event, ev_hdr, q_elem, false);
386}
387
388/**
389 * @brief Helper to dispatch_local_queues() for multiple events
390 */
391static inline void
392dispatch_local_burst(const stash_entry_t entry_tbl[], const int num)
393{
394 em_locm_t *const locm = &em_locm;
395 int idx = 0; /* index into ev_tbl[] & ev_hdr_tbl[] */
396 int ev_cnt; /* number of events to the same local-queue */
397
398 /* num <= EM_QUEUE_LOCAL_MULTI_MAX_BURST, use fixed-size (non-VLA) tables */
399 odp_event_t odp_evtbl[EM_QUEUE_LOCAL_MULTI_MAX_BURST];
400 em_event_t ev_tbl[EM_QUEUE_LOCAL_MULTI_MAX_BURST];
402
403 for (int i = 0; i < num; i++)
404 odp_evtbl[i] = (odp_event_t)(uintptr_t)entry_tbl[i].evptr;
405
406 /* Loop through 'num' events and dispatch in batches to local queues */
407 do {
408 /* dst local queue */
409 const int qidx = entry_tbl[idx].qidx;
410 const em_queue_t queue = queue_idx2hdl(qidx);
411 queue_elem_t *const q_elem = queue_elem_get(queue);
412 int i;
413
414 locm->current.q_elem = q_elem; /* before event_init_... for ESV error prints */
415
416 /*
417 * Count events sent to the same local queue,
418 * i < num <= EM_QUEUE_LOCAL_MULTI_MAX_BURST
419 */
420 for (i = idx + 1; i < num && entry_tbl[i].qidx == qidx; i++)
421 ;
422
423 ev_cnt = i - idx; /* '1 to num' events */
424
425 /* Events might originate from outside (via polled pktio) of EM and need init */
426 event_init_odp_multi(&odp_evtbl[idx], ev_tbl/*out*/, evhdr_tbl/*out*/,
427 ev_cnt, true/*is_extev*/, q_elem);
428
429 if (unlikely(!q_elem || q_elem->state != EM_QUEUE_STATE_READY)) {
430 em_free_multi(ev_tbl, ev_cnt);
431 /* Consider removing the logging */
432 EM_LOG(EM_LOG_PRINT,
433 "EM info: %s(): localQ:%" PRI_QUEUE ":\n"
434 "Not ready - state:%d drop:%d events\n",
435 __func__, queue, q_elem ? q_elem->state : 0, ev_cnt);
436 idx += ev_cnt;
437 continue;
438 }
439
440 if (q_elem->flags.use_multi_rcv)
441 dispatch_eo_multircv_burst(ev_tbl, evhdr_tbl, ev_cnt, q_elem, false);
442 else
443 dispatch_eo_rcv_burst(ev_tbl, evhdr_tbl, ev_cnt, q_elem, false);
444
445 idx += ev_cnt;
446 } while (idx < num);
447}
448
449static inline void
450dispatch_local_queues(const stash_entry_t entry_tbl[], const int num)
451{
452 if (num == 1)
453 dispatch_local_single(entry_tbl[0]);
454 else
455 dispatch_local_burst(entry_tbl, num);
456}
457
458static inline void
459check_local_queues(void)
460{
461 em_locm_t *const locm = &em_locm;
462
463 if (locm->local_queues.empty)
464 return;
465
466 /*
467 * Check if the previous EO receive function sent events to a
468 * local queue ('EM_QUEUE_TYPE_LOCAL') - and if so, dispatch
469 * those events immediately.
470 */
472
473 for (;;) {
475 locm->debug_ts[EM_DEBUG_TSP_SCHED_ENTRY] = debug_timestamp();
476
477 int num = next_local_queue_events(entry_tbl /*[out]*/,
480 locm->debug_ts[EM_DEBUG_TSP_SCHED_RETURN] = debug_timestamp();
481
482 if (num <= 0)
483 break;
484
485 locm->local_event_cnt += num;
486 dispatch_local_queues(entry_tbl, num);
487 }
488
489 /* Restore */
490 locm->current.q_elem = locm->current.sched_q_elem;
491}
492
493/**
494 * Count events (hdrs) sent/tagged with the same event group
495 */
496static inline int
497count_same_evgroup(event_hdr_t *ev_hdr_tbl[], const unsigned int num)
498{
499 if (unlikely(num < 2))
500 return num;
501
502 const em_event_group_t egrp = ev_hdr_tbl[0]->egrp;
503 unsigned int i = 1; /* 2nd hdr */
504
505 if (egrp == EM_EVENT_GROUP_UNDEF) {
506 for (; i < num &&
507 ev_hdr_tbl[i]->egrp == EM_EVENT_GROUP_UNDEF; i++)
508 ;
509 } else {
510 const uint32_t egrp_gen = ev_hdr_tbl[0]->egrp_gen;
511
512 for (; i < num &&
513 egrp == ev_hdr_tbl[i]->egrp &&
514 egrp_gen == ev_hdr_tbl[i]->egrp_gen; i++)
515 ;
516 }
517
518 return i;
519}
520
521/**
522 * Dispatch a single event to an EO multi-receive function
523 *
524 * Optimizes the case of a single event.
525 *
526 * When no dispatch callbacks are registered (the common case), uses
527 * call_eo_multircv_fn__no_cb() to skip the callback-table NULL checks
528 * that call_eo_multircv_fn() would otherwise perform. Falls back to
529 * call_eo_multircv_fn() when any dispatch callback is registered.
530 */
531static inline void
532dispatch_eo_multircv_single(em_event_t event, event_hdr_t *ev_hdr,
533 queue_elem_t *const q_elem, const bool check_local_qs)
534{
535 const em_eo_t eo = (em_eo_t)(uintptr_t)q_elem->eo;
536 const em_receive_multi_func_t eo_rcv_multi_fn = q_elem->receive_multi_func;
537
538 if (dispatch_callbacks_registered()) {
539 /*
540 * Dispatch callbacks are registered - use call_eo_multircv_fn()
541 * which handles callback invocation including event/context
542 * modification by callbacks.
543 */
544 if (check_local_qs) {
545 em_locm_t *const locm = &em_locm;
546
547 locm->event_burst_cnt--;
548 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
549 &event, &ev_hdr, 1, q_elem);
550 check_local_queues();
551 } else {
552 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
553 &event, &ev_hdr, 1, q_elem);
554 }
555 return;
556 }
557
558 /*
559 * No dispatch callbacks - fast path: skip the callback-table lookups
560 * and pass q_elem fields directly to the receive function.
561 */
562 const em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
563 void *const queue_ctx = q_elem->context;
564 void *const eo_ctx = q_elem->eo_ctx;
565
566 if (check_local_qs) {
567 em_locm_t *const locm = &em_locm;
568
569 locm->event_burst_cnt--;
570 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn, &event, &ev_hdr, 1,
571 queue, queue_ctx, eo_ctx);
572 check_local_queues();
573 } else {
574 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn, &event, &ev_hdr, 1,
575 queue, queue_ctx, eo_ctx);
576 }
577}
578
579/**
580 * Dispatch a burst of events to an EO multi-receive function
581 *
582 * When no dispatch callbacks are registered (the common case), uses
583 * call_eo_multircv_fn__no_cb() with q_elem fields (queue, context, eo_ctx)
584 * read once for the whole burst, eliminating per-batch q_elem loads and
585 * callback-table NULL checks. Falls back to call_eo_multircv_fn() per
586 * batch when any dispatch callback is registered.
587 */
588static inline void
589dispatch_eo_multircv_burst(em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
590 const int num_events, queue_elem_t *const q_elem,
591 const bool check_local_qs)
592{
593 em_locm_t *const locm = &em_locm;
594 const em_eo_t eo = (em_eo_t)(uintptr_t)q_elem->eo;
595 const em_receive_multi_func_t eo_rcv_multi_fn =
596 q_elem->receive_multi_func;
597 int idx = 0; /* index into ev_hdr_tbl[] */
598
599 if (dispatch_callbacks_registered()) {
600 /*
601 * Dispatch callbacks are registered - fall back to the
602 * per-batch call_eo_multircv_fn() which handles callback
603 * invocation including event/context modification by
604 * callbacks.
605 */
606 do {
607 /* count same event groups: 1 to (num_events - idx) */
608 const int egrp_cnt = count_same_evgroup(&ev_hdr_tbl[idx],
609 num_events - idx);
610 /*
611 * Clamp the egrp-end index to num_events to make the
612 * array bound explicit for static analyzers.
613 * count_same_evgroup() returns '<= (num_events - idx)'
614 * by contract, so the MIN() is a no-op at runtime.
615 */
616 const int egrp_end = MIN(idx + egrp_cnt, num_events);
617 const int max = q_elem->max_events;
618 const int num = MIN(egrp_cnt, max);
619
620 if (check_local_qs) {
621 int j = idx;
622
623 /* Full batches of 'num' events */
624 while (egrp_end - j >= num) {
625 locm->event_burst_cnt -= num;
626 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
627 &ev_tbl[j], &ev_hdr_tbl[j],
628 num, q_elem);
629 check_local_queues();
630 j += num;
631 }
632 /* Trailing partial batch, if any */
633 if (j < egrp_end) {
634 locm->event_burst_cnt = 0;
635 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
636 &ev_tbl[j], &ev_hdr_tbl[j],
637 egrp_end - j, q_elem);
638 check_local_queues();
639 }
640 } else {
641 int j = idx;
642
643 while (egrp_end - j >= num) {
644 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
645 &ev_tbl[j], &ev_hdr_tbl[j],
646 num, q_elem);
647 j += num;
648 }
649 if (j < egrp_end) {
650 call_eo_multircv_fn(eo, eo_rcv_multi_fn,
651 &ev_tbl[j], &ev_hdr_tbl[j],
652 egrp_end - j, q_elem);
653 }
654 }
655
656 idx = egrp_end;
657 } while (idx < num_events);
658 return;
659 }
660
661 /*
662 * No dispatch callbacks - fast path for the whole burst.
663 * Read q_elem fields once and reuse across all batches to skip the
664 * per-batch q_elem loads and callback-table lookups that
665 * call_eo_multircv_fn() would otherwise perform.
666 */
667 const em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
668 void *const queue_ctx = q_elem->context;
669 void *const eo_ctx = q_elem->eo_ctx;
670
671 do {
672 const int egrp_cnt = count_same_evgroup(&ev_hdr_tbl[idx],
673 num_events - idx);
674 /* See note above about the egrp_end clamp */
675 const int egrp_end = MIN(idx + egrp_cnt, num_events);
676 const int max = q_elem->max_events;
677 const int num = MIN(egrp_cnt, max);
678
679 if (check_local_qs) {
680 int j = idx;
681
682 /* Full batches of 'num' events */
683 while (egrp_end - j >= num) {
684 locm->event_burst_cnt -= num;
685 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn,
686 &ev_tbl[j], &ev_hdr_tbl[j],
687 num,
688 queue, queue_ctx, eo_ctx);
689 check_local_queues();
690 j += num;
691 }
692 /* Trailing partial batch, if any */
693 if (j < egrp_end) {
694 locm->event_burst_cnt = 0;
695 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn,
696 &ev_tbl[j], &ev_hdr_tbl[j],
697 egrp_end - j,
698 queue, queue_ctx, eo_ctx);
699 check_local_queues();
700 }
701 } else {
702 int j = idx;
703
704 while (egrp_end - j >= num) {
705 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn,
706 &ev_tbl[j], &ev_hdr_tbl[j],
707 num,
708 queue, queue_ctx, eo_ctx);
709 j += num;
710 }
711 if (j < egrp_end) {
712 call_eo_multircv_fn__no_cb(eo_rcv_multi_fn,
713 &ev_tbl[j], &ev_hdr_tbl[j],
714 egrp_end - j,
715 queue, queue_ctx, eo_ctx);
716 }
717 }
718
719 idx = egrp_end;
720 } while (idx < num_events);
721}
722
723/**
724 * Dispatch a single event to an EO receive function
725 *
726 * Optimizes the case of a single event.
727 *
728 * When no dispatch callbacks are registered (the common case), uses
729 * call_eo_rcv_fn__no_cb() to skip the callback-table NULL checks that
730 * call_eo_rcv_fn() would otherwise perform. Falls back to call_eo_rcv_fn()
731 * when any dispatch callback is registered.
732 */
733static inline void
734dispatch_eo_rcv_single(em_event_t event, event_hdr_t *ev_hdr,
735 queue_elem_t *const q_elem, const bool check_local_qs)
736{
737 const em_eo_t eo = (em_eo_t)(uintptr_t)q_elem->eo;
738 const em_receive_func_t eo_rcv_fn = q_elem->receive_func;
739
740 if (dispatch_callbacks_registered()) {
741 /*
742 * Dispatch callbacks are registered - use call_eo_rcv_fn()
743 * which handles callback invocation including event
744 * modification by callbacks.
745 */
746 if (check_local_qs) {
747 em_locm_t *const locm = &em_locm;
748
749 locm->event_burst_cnt--;
750 call_eo_rcv_fn(eo, eo_rcv_fn, event, ev_hdr, q_elem);
751 check_local_queues();
752 } else {
753 call_eo_rcv_fn(eo, eo_rcv_fn, event, ev_hdr, q_elem);
754 }
755 return;
756 }
757
758 /*
759 * No dispatch callbacks - fast path: skip the callback-table lookups
760 * and pass q_elem fields directly to the receive function.
761 */
762 const em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
763 void *const queue_ctx = q_elem->context;
764 void *const eo_ctx = q_elem->eo_ctx;
765
766 if (check_local_qs) {
767 em_locm_t *const locm = &em_locm;
768
769 locm->event_burst_cnt--;
770 call_eo_rcv_fn__no_cb(eo_rcv_fn, event, ev_hdr,
771 queue, queue_ctx, eo_ctx);
772 check_local_queues();
773 } else {
774 call_eo_rcv_fn__no_cb(eo_rcv_fn, event, ev_hdr,
775 queue, queue_ctx, eo_ctx);
776 }
777}
778
779/**
780 * Dispatch an event burst to an EO receive function one by one in a loop.
781 *
782 * When no dispatch callbacks are registered (the common case), uses
783 * call_eo_rcv_fn__no_cb() with q_elem fields (queue, context, eo_ctx)
784 * read once for the whole burst, eliminating per-event q_elem loads and
785 * callback-table NULL checks. Falls back to call_eo_rcv_fn() per event
786 * when any dispatch callback is registered.
787 */
788static inline void
789dispatch_eo_rcv_burst(em_event_t ev_tbl[], event_hdr_t *ev_hdr_tbl[],
790 const int num_events, queue_elem_t *const q_elem,
791 const bool check_local_qs)
792{
793 em_locm_t *const locm = &em_locm;
794 const em_eo_t eo = (em_eo_t)(uintptr_t)q_elem->eo;
795 const em_receive_func_t eo_rcv_fn = q_elem->receive_func;
796
797 if (dispatch_callbacks_registered()) {
798 /*
799 * Dispatch callbacks are registered - fall back to the
800 * per-event call_eo_rcv_fn() which handles callback
801 * invocation including event modification by callbacks.
802 */
803 if (check_local_qs) {
804 for (int i = 0; i < num_events; i++) {
805 locm->event_burst_cnt--;
806 call_eo_rcv_fn(eo, eo_rcv_fn,
807 ev_tbl[i], ev_hdr_tbl[i],
808 q_elem);
809 check_local_queues();
810 }
811 } else {
812 for (int i = 0; i < num_events; i++)
813 call_eo_rcv_fn(eo, eo_rcv_fn,
814 ev_tbl[i], ev_hdr_tbl[i],
815 q_elem);
816 }
817 return;
818 }
819
820 /*
821 * No dispatch callbacks - fast path for the whole burst.
822 * Read q_elem fields once and reuse across all events to skip the
823 * per-event q_elem loads and callback-table lookups that
824 * call_eo_rcv_fn() would otherwise perform.
825 */
826 const em_queue_t queue = (em_queue_t)(uintptr_t)q_elem->queue;
827 void *const queue_ctx = q_elem->context;
828 void *const eo_ctx = q_elem->eo_ctx;
829
830 if (check_local_qs) {
831 for (int i = 0; i < num_events; i++) {
832 locm->event_burst_cnt--;
833 call_eo_rcv_fn__no_cb(eo_rcv_fn,
834 ev_tbl[i], ev_hdr_tbl[i],
835 queue, queue_ctx, eo_ctx);
836 check_local_queues();
837 }
838 } else {
839 for (int i = 0; i < num_events; i++)
840 call_eo_rcv_fn__no_cb(eo_rcv_fn,
841 ev_tbl[i], ev_hdr_tbl[i],
842 queue, queue_ctx, eo_ctx);
843 }
844}
845
846/**
847 * Helper to dispatch_events() for a single event
848 *
849 * Prepare for calling the EO single- or multi-receive function by converting
850 * (and initializing) the single ODP-event received to an EM-event.
851 *
852 * Optimizes the case of a single event to avoid the overhead of
853 * setting up arrays and loops for multiple events.
854 */
855static inline void
856dispatch_event_single_prepare(odp_event_t odp_event, queue_elem_t *const q_elem)
857{
858 event_hdr_t *ev_hdr;
859 /* Events might originate from outside of EM and need init */
860 em_event_t event = event_init_odp(odp_event, true/*is_extev*/, q_elem, &ev_hdr);
861
862 /*
863 * Call the Execution Object (EO) receive function.
864 * Scheduling context may be released during this.
865 */
866 if (q_elem->flags.use_multi_rcv)
867 dispatch_eo_multircv_single(event, ev_hdr, q_elem, true);
868 else
869 dispatch_eo_rcv_single(event, ev_hdr, q_elem, true);
870}
871
872/**
873 * Helper to dispatch_events() for a burst of events
874 *
875 * Prepare for calling the EO single- or multi-receive function by converting
876 * (and initializing) the burst of ODP-events received to EM-events.
877 */
878static inline void
879dispatch_event_burst_prepare(odp_event_t odp_evtbl[], const int num_events,
880 queue_elem_t *const q_elem)
881{
882 /* num_events <= DISPATCH_MULTI_MAX_BURST, use fixed-size (non-VLA) tables */
883 event_hdr_t *evhdr_tbl[DISPATCH_MULTI_MAX_BURST];
884 em_event_t ev_tbl[DISPATCH_MULTI_MAX_BURST];
885
886 /* Events might originate from outside of EM and need init */
887 event_init_odp_multi(odp_evtbl, ev_tbl/*out*/, evhdr_tbl/*out*/,
888 num_events, true/*is_extev*/, q_elem);
889 /*
890 * Call the Execution Object (EO) receive function.
891 * Scheduling context may be released during this.
892 */
893 if (q_elem->flags.use_multi_rcv)
894 dispatch_eo_multircv_burst(ev_tbl, evhdr_tbl, num_events,
895 q_elem, true);
896 else
897 dispatch_eo_rcv_burst(ev_tbl, evhdr_tbl, num_events,
898 q_elem, true);
899 }
900
901/**
902 * Dispatch events
903 *
904 * Prepare and convert the received ODP-event(s) to EM-event(s) and finally call
905 * the EO receive function (multi- or single-receive variant) with the EM-events
906 *
907 * dispatch_events()
908 * ├─ if num_events == 1
909 * │ └─ dispatch_event_single_prepare(ODP-event)
910 * │ ├─ if use_multi_rcv
911 * │ │ └─ dispatch_eo_multircv_single(EM-event)
912 * │ │ └─ call_eo_multircv_fn(EM-event)
913 * │ └─ else
914 * │ └─ dispatch_eo_rcv_single(EM-event)
915 * │ └─ call_eo_rcv_fn(EM-event)
916 * └─ else (num_events > 1)
917 * └─ dispatch_event_burst_prepare(ODP-events)
918 * ├─ if use_multi_rcv
919 * │ └─ dispatch_eo_multircv_burst(EM-events) (per evgrp batch)
920 * │ ├─ cb path: call_eo_multircv_fn()
921 * │ └─ no-cb path: call_eo_multircv_fn__no_cb()
922 * └─ else
923 * └─ dispatch_eo_rcv_burst(EM-events) (per event)
924 * ├─ cb path: call_eo_rcv_fn()
925 * └─ no-cb path: call_eo_rcv_fn__no_cb()
926 */
927static inline void
928dispatch_events(odp_event_t odp_evtbl[], const int num_events,
929 queue_elem_t *const q_elem)
930{
931 em_locm_t *const locm = &em_locm;
932 const em_queue_type_t q_type = q_elem->type;
934
935 if (q_type == EM_QUEUE_TYPE_ATOMIC)
936 sched_ctx_type = EM_SCHED_CONTEXT_TYPE_ATOMIC;
937 else if (q_type == EM_QUEUE_TYPE_ORDERED)
938 sched_ctx_type = EM_SCHED_CONTEXT_TYPE_ORDERED;
939
940 locm->current.sched_context_type = sched_ctx_type;
941 locm->current.sched_q_elem = q_elem;
942 locm->current.q_elem = q_elem; /* before event_init_... for ESV error prints */
943 /* here: locm->current.egrp_elem == NULL */
944
945 if (num_events == 1)
946 dispatch_event_single_prepare(odp_evtbl[0], q_elem);
947 else /* num_events > 1 */
948 dispatch_event_burst_prepare(odp_evtbl, num_events, q_elem);
949
950 /*
951 * Check for buffered events sent to output queues during the previous
952 * dispatch rounds. Currently buffered only for ordered sched context,
953 * use local var 'sched_ctx_type' since the type might have been changed
954 * from _ORDERED by 'em_ordered_processing_end()'.
955 */
957 sched_ctx_type == EM_SCHED_CONTEXT_TYPE_ORDERED &&
958 locm->output_queue_track.idx_cnt > 0)
959 output_queue_buffering_drain();
960
961 locm->current.q_elem = NULL;
962 locm->current.sched_q_elem = NULL;
964}
965
966static inline void
967dispatch_poll_ctrl_queue(void)
968{
969 const unsigned int poll_interval = em_shm->opt.dispatch.poll_ctrl_interval;
970
971 /*
972 * Rate limit how often this core checks the unsched ctrl queue.
973 */
974
975 if (poll_interval > 1) {
976 em_locm_t *const locm = &em_locm;
977
978 locm->dispatch_cnt--;
979 if (locm->dispatch_cnt > 0)
980 return;
981 locm->dispatch_cnt = poll_interval;
982
983 odp_time_t now = odp_time_local();
984 odp_time_t period = odp_time_diff(now, locm->dispatch_last_run);
985 odp_time_t poll_period = locm->poll_ctrl_interval_time;
986
987 if (odp_time_cmp(period, poll_period) < 0)
988 return;
989 locm->dispatch_last_run = now;
990 }
991
992 /* Poll internal unscheduled ctrl queues */
994}
995
996/*
997 * Change the core state to idle and call idle hooks. If the core state changes,
998 * call to_idle hooks. If the core state is already idle, call while_idle hooks.
999 */
1000static inline void
1001to_idle(const em_dispatch_opt_t *opt)
1002{
1004 em_locm_t *const locm = &em_locm;
1005
1006 if (locm->idle_state == IDLE_STATE_ACTIVE) {
1007 uint64_t to_idle_delay_ns = 0;
1008
1010 to_idle_delay_ns = debug_timestamp() -
1011 locm->debug_ts[EM_DEBUG_TSP_SCHED_ENTRY];
1012 } else if (EM_SCHED_WAIT_ENABLE) {
1013 to_idle_delay_ns = opt ? opt->wait_ns :
1014 em_shm->opt.dispatch.sched_wait_ns;
1015 } else if (opt) {
1016 to_idle_delay_ns = opt->wait_ns;
1017 }
1018
1019 call_idle_hooks_to_idle(to_idle_delay_ns);
1021 } else if (locm->idle_state == IDLE_STATE_IDLE) {
1022 call_idle_hooks_while_idle();
1023 }
1024 }
1025}
1026
1027/*
1028 * Change the core state to active and call idle hooks. If the core state
1029 * changes call to_active hooks. If the core state is already active no idle
1030 * hooks will be called.
1031 */
1032static inline void
1033to_active(void)
1034{
1036 em_locm_t *const locm = &em_locm;
1037
1038 if (locm->idle_state == IDLE_STATE_IDLE) {
1039 call_idle_hooks_to_active();
1041 }
1042 }
1043}
1044
1045/**
1046 * @brief Dispatcher calls the scheduler and requests events for processing
1047 */
1048static inline int
1049dispatch_schedule(odp_queue_t *odp_queue /*out*/, uint64_t sched_wait,
1050 odp_event_t odp_evtbl[/*out*/], int num)
1051{
1052 int ret;
1053
1055 em_locm.debug_ts[EM_DEBUG_TSP_SCHED_ENTRY] = debug_timestamp();
1056
1057 ret = odp_schedule_multi(odp_queue, sched_wait, odp_evtbl, num);
1058
1060 em_locm.debug_ts[EM_DEBUG_TSP_SCHED_RETURN] = debug_timestamp();
1061
1062 return ret;
1063}
1064
1065static inline bool
1066is_invalid_queue(const queue_elem_t *const q_elem)
1067{
1068 const bool not_emq = !q_elem || (EM_CHECK_LEVEL > 2 &&
1069 q_elem->valid_check != QUEUE_ELEM_VALID);
1070
1071 if (unlikely(not_emq || q_elem->state != EM_QUEUE_STATE_READY)) {
1072 if (not_emq)
1073 INTERNAL_ERROR(EM_ERR_BAD_POINTER, EM_ESCOPE_DISPATCH,
1074 "Drop event(s) from non-EM Q");
1075 else
1076 INTERNAL_ERROR(EM_ERR_BAD_STATE, EM_ESCOPE_DISPATCH,
1077 "Drop event(s) from Q:%" PRI_QUEUE ": not ready, state=%d",
1078 q_elem->queue, q_elem->state);
1079 return true; /* invalid queue */
1080 }
1081
1082 return false; /* not invalid, i.e. a valid queue */
1083}
1084
1085static inline void
1086free_invalid_events(odp_event_t odp_evtbl[], int num)
1087{
1089 em_event_t ev_tbl[EM_SCHED_MULTI_MAX_BURST];
1090
1091 event_init_odp_multi(odp_evtbl, ev_tbl/*out*/, ev_hdr_tbl/*out*/,
1092 num, true/*is_extev*/, NULL/*not a dispatch: always scan*/);
1093 em_free_multi(ev_tbl, num);
1094}
1095
1096/*
1097 * Run a dispatch round - query the scheduler for events and dispatch
1098 */
1099static inline int
1100dispatch_round(uint64_t sched_wait, uint16_t burst_size,
1101 const em_dispatch_opt_t *opt /*optional, can be NULL*/)
1102{
1103 odp_queue_t odp_queue;
1104 odp_event_t odp_evtbl[EM_SCHED_MULTI_MAX_BURST];
1105 int num;
1106
1107 dispatch_poll_ctrl_queue();
1108
1109 /*
1110 * burst_size is validated to be <= EM_SCHED_MULTI_MAX_BURST at the API
1111 * level, but only when EM_CHECK_LEVEL > 0. Clamp defensively so that the
1112 * fixed-size odp_evtbl[] can never be overflowed by odp_schedule_multi()
1113 * if the validation is compiled out (EM_CHECK_LEVEL == 0).
1114 */
1115 const int num_req = MIN((int)burst_size, EM_SCHED_MULTI_MAX_BURST);
1116
1117 num = dispatch_schedule(&odp_queue/*out*/, sched_wait,
1118 odp_evtbl/*out[]*/, num_req);
1119 if (unlikely(num <= 0)) {
1120 /*
1121 * No scheduled events available, check if the local queues
1122 * contain anything on this core - e.g. pktio or something
1123 * outside the dispatch-context might have sent to a local queue
1124 * Update the EM_IDLE_STATE and call idle hooks if they are
1125 * enabled
1126 */
1127 if (em_locm.local_queues.empty) {
1128 to_idle(opt);
1129 } else {
1130 to_active();
1131 check_local_queues();
1132 }
1133 return 0;
1134 }
1135
1136 queue_elem_t *const q_elem = odp_queue_context(odp_queue);
1137
1138 if (unlikely(is_invalid_queue(q_elem))) {
1139 /* Free all events from an invalid queue */
1140 free_invalid_events(odp_evtbl, num);
1141 return 0;
1142 }
1143
1144 /*
1145 * If scheduled events are available, update the EM_IDLE_STATE and
1146 * call idle hooks if they are enabled.
1147 */
1148 to_active();
1149
1150 if (q_elem->flags.in_atomic_group) {
1151 atomic_group_dispatch(odp_evtbl, num, q_elem);
1152 } else {
1154 dispatch_events(odp_evtbl, num, q_elem);
1155 }
1156
1157 return num;
1158}
1159
1160/*
1161 * em_dispatch() helper: check if the user provided callback functions
1162 * 'input_poll' and 'output_drain' should be called in
1163 * this dispatch round
1164 */
1165static inline bool
1166check_poll_drain_round(unsigned int interval, odp_time_t poll_drain_period)
1167{
1168 if (interval > 1) {
1169 em_locm_t *const locm = &em_locm;
1170
1172 if (locm->poll_drain_dispatch_cnt == 0) {
1173 odp_time_t now = odp_time_local();
1174 odp_time_t period;
1175
1176 period = odp_time_diff(now, locm->poll_drain_dispatch_last_run);
1177 locm->poll_drain_dispatch_cnt = interval;
1178
1179 if (odp_time_cmp(poll_drain_period, period) < 0) {
1180 locm->poll_drain_dispatch_last_run = now;
1181 return true;
1182 }
1183 }
1184 } else {
1185 return true;
1186 }
1187 return false;
1188}
1189
1190/*
1191 * em_dispatch() helper: dispatch and call the user provided callback functions
1192 * 'input_poll' and 'output_drain'
1193 */
1194static inline uint64_t
1195dispatch_with_userfn(const uint64_t rounds,
1196 const em_input_poll_func_t input_poll_fn,
1197 const em_output_drain_func_t output_drain_fn)
1198{
1199 const bool do_forever = rounds == 0 ? true : false;
1200 const unsigned int poll_interval = em_shm->opt.dispatch.poll_drain_interval;
1201 const odp_time_t poll_period = em_locm.poll_drain_interval_time;
1202 const uint64_t sched_wait = EM_SCHED_WAIT_ENABLE ?
1203 em_shm->opt_cache.dispatch.sched_wait : ODP_SCHED_NO_WAIT;
1204 int rx_events = 0;
1205 uint64_t events = 0;
1206 int dispatched_events;
1207 int round_events;
1208 bool do_poll_drain_round;
1209
1210 for (uint64_t i = 0; do_forever || i < rounds;) {
1211 dispatched_events = 0;
1212
1213 do_poll_drain_round = check_poll_drain_round(poll_interval, poll_period);
1214
1215 if (input_poll_fn && do_poll_drain_round)
1216 rx_events = input_poll_fn();
1217
1218 do {
1219 round_events = dispatch_round(sched_wait, EM_SCHED_MULTI_MAX_BURST, NULL);
1220 dispatched_events += round_events;
1221 i++; /* inc rounds */
1222 } while (dispatched_events < rx_events &&
1223 round_events > 0 && (do_forever || i < rounds));
1224
1225 events += dispatched_events; /* inc ret value*/
1226 if (output_drain_fn && do_poll_drain_round)
1227 (void)output_drain_fn();
1228 }
1229
1230 return events;
1231}
1232
1233/*
1234 * em_dispatch() helper: dispatch without calling any user provided callbacks
1235 */
1236static inline uint64_t
1237dispatch_no_userfn(uint64_t rounds)
1238{
1239 const bool do_forever = rounds == 0 ? true : false;
1240 const uint64_t sched_wait = EM_SCHED_WAIT_ENABLE ?
1241 em_shm->opt_cache.dispatch.sched_wait : ODP_SCHED_NO_WAIT;
1242 uint64_t events = 0;
1243
1244 if (do_forever) {
1245 for (;/*ever*/;)
1246 dispatch_round(sched_wait, EM_SCHED_MULTI_MAX_BURST, NULL);
1247 } else {
1248 for (uint64_t i = 0; i < rounds; i++)
1249 events += dispatch_round(sched_wait, EM_SCHED_MULTI_MAX_BURST, NULL);
1250 }
1251
1252 return events;
1253}
1254
1255/*
1256 * em_dispatch() helper: dispatch and call the user provided callback functions
1257 * 'input_poll' and 'output_drain'
1258 */
1259static inline uint64_t
1260dispatch_duration_with_userfn(const em_dispatch_duration_t *duration,
1261 const em_dispatch_opt_t *opt,
1262 const em_input_poll_func_t input_poll_fn,
1263 const em_output_drain_func_t output_drain_fn,
1264 em_dispatch_results_t *results /*out*/)
1265{
1266 const unsigned int poll_interval = em_shm->opt.dispatch.poll_drain_interval;
1267 const odp_time_t poll_period = em_locm.poll_drain_interval_time;
1268 const uint64_t sched_wait = opt->wait_ns == 0 ? ODP_SCHED_NO_WAIT :
1269 odp_schedule_wait_time(opt->wait_ns);
1270 const uint16_t burst_size = opt->burst_size;
1271 bool do_poll_drain_round = false;
1272 em_locm_t *const locm = &em_locm;
1273
1274 const bool duration_forever =
1275 duration->select == EM_DISPATCH_DURATION_FOREVER ? true : false;
1276 const bool duration_rounds =
1277 duration->select & EM_DISPATCH_DURATION_ROUNDS ? true : false;
1278 const bool duration_ns =
1279 duration->select & EM_DISPATCH_DURATION_NS ? true : false;
1280 const bool duration_events =
1281 duration->select & EM_DISPATCH_DURATION_EVENTS ? true : false;
1282 const bool duration_noev_rounds =
1283 duration->select & EM_DISPATCH_DURATION_NO_EVENTS_ROUNDS ? true : false;
1284 const bool duration_noev_ns =
1285 duration->select & EM_DISPATCH_DURATION_NO_EVENTS_NS ? true : false;
1286
1287 if (unlikely(duration_forever)) {
1288 for (;/*ever*/;) {
1289 /* check if callback functions should be called */
1290 do_poll_drain_round = check_poll_drain_round(poll_interval, poll_period);
1291
1292 if (input_poll_fn && do_poll_drain_round)
1293 (void)input_poll_fn();
1294
1295 /* dispatch one round */
1296 (void)dispatch_round(sched_wait, burst_size, opt);
1297
1298 if (output_drain_fn && do_poll_drain_round)
1299 (void)output_drain_fn();
1300 }
1301 /* never return */
1302 }
1303
1304 uint64_t events = 0;
1305 uint64_t rounds = 0;
1306 uint64_t noev_rounds = 0;
1307
1308 uint64_t start_ns = 0;
1309 uint64_t stop_ns = 0;
1310 uint64_t noev_stop_ns = 0;
1311 uint64_t time_ns = 0;
1312
1313 if (duration_ns || duration_noev_ns) {
1314 start_ns = odp_time_local_ns();
1315 stop_ns = start_ns + duration->ns;
1316 noev_stop_ns = start_ns + duration->no_events.ns;
1317 time_ns = start_ns;
1318 }
1319
1320 while ((!duration_rounds || rounds < duration->rounds) &&
1321 (!duration_ns || time_ns < stop_ns) &&
1322 (!duration_events || events + locm->local_event_cnt < duration->events) &&
1323 (!duration_noev_rounds || noev_rounds < duration->no_events.rounds) &&
1324 (!duration_noev_ns || time_ns < noev_stop_ns)) {
1325 /* check if callback functions should be called */
1326 do_poll_drain_round = check_poll_drain_round(poll_interval, poll_period);
1327
1328 if (input_poll_fn && do_poll_drain_round)
1329 (void)input_poll_fn();
1330
1331 /* dispatch one round */
1332 int round_events = dispatch_round(sched_wait, burst_size, opt);
1333
1334 events += round_events;
1335 rounds++;
1336
1337 if (output_drain_fn && do_poll_drain_round)
1338 (void)output_drain_fn();
1339
1340 if (duration_noev_rounds) {
1341 if (round_events == 0)
1342 noev_rounds++;
1343 else
1344 noev_rounds = 0;
1345 }
1346
1347 if (duration_ns || duration_noev_ns) {
1348 time_ns = odp_time_local_ns();
1349
1350 if (duration_noev_ns && round_events > 0)
1351 noev_stop_ns = time_ns + duration->no_events.ns;
1352 }
1353 }
1354
1355 if (results) {
1356 results->rounds = rounds;
1357 if (duration_ns || duration_noev_ns)
1358 results->ns = time_ns - start_ns;
1359 else
1360 results->ns = 0;
1361 results->events = events;
1362 }
1363
1364 return events;
1365}
1366
1367static inline uint64_t
1368dispatch_duration_no_userfn(const em_dispatch_duration_t *duration,
1369 const em_dispatch_opt_t *opt,
1370 em_dispatch_results_t *results/*out*/)
1371{
1372 const uint64_t sched_wait = opt->wait_ns == 0 ? ODP_SCHED_NO_WAIT :
1373 odp_schedule_wait_time(opt->wait_ns);
1374 const uint16_t burst_size = opt->burst_size;
1375 em_locm_t *const locm = &em_locm;
1376
1377 const bool duration_forever =
1378 duration->select == EM_DISPATCH_DURATION_FOREVER ? true : false;
1379 const bool duration_rounds =
1380 duration->select & EM_DISPATCH_DURATION_ROUNDS ? true : false;
1381 const bool duration_ns =
1382 duration->select & EM_DISPATCH_DURATION_NS ? true : false;
1383 const bool duration_events =
1384 duration->select & EM_DISPATCH_DURATION_EVENTS ? true : false;
1385 const bool duration_noev_rounds =
1386 duration->select & EM_DISPATCH_DURATION_NO_EVENTS_ROUNDS ? true : false;
1387 const bool duration_noev_ns =
1388 duration->select & EM_DISPATCH_DURATION_NO_EVENTS_NS ? true : false;
1389
1390 if (unlikely(duration_forever)) {
1391 for (;/*ever*/;)
1392 (void)dispatch_round(sched_wait, burst_size, opt);
1393 /* never return */
1394 }
1395
1396 uint64_t events = 0;
1397 uint64_t rounds = 0;
1398 uint64_t noev_rounds = 0;
1399
1400 uint64_t start_ns = 0;
1401 uint64_t stop_ns = 0;
1402 uint64_t noev_stop_ns = 0;
1403 uint64_t time_ns = 0;
1404
1405 if (duration_ns || duration_noev_ns) {
1406 start_ns = odp_time_local_ns();
1407 stop_ns = start_ns + duration->ns;
1408 noev_stop_ns = start_ns + duration->no_events.ns;
1409 time_ns = start_ns;
1410 }
1411
1412 while ((!duration_rounds || rounds < duration->rounds) &&
1413 (!duration_ns || time_ns < stop_ns) &&
1414 (!duration_events || events + locm->local_event_cnt < duration->events) &&
1415 (!duration_noev_rounds || noev_rounds < duration->no_events.rounds) &&
1416 (!duration_noev_ns || time_ns < noev_stop_ns)) {
1417 /* dispatch one round */
1418 int round_events = dispatch_round(sched_wait, burst_size, opt);
1419
1420 events += round_events;
1421 rounds++;
1422
1423 if (duration_noev_rounds) {
1424 if (round_events == 0)
1425 noev_rounds++;
1426 else
1427 noev_rounds = 0;
1428 }
1429
1430 if (duration_ns || duration_noev_ns) {
1431 time_ns = odp_time_local_ns();
1432 if (duration_noev_ns && round_events > 0)
1433 noev_stop_ns = time_ns + duration->no_events.ns;
1434 }
1435 }
1436
1437 if (results) {
1438 results->rounds = rounds;
1439 if (duration_ns || duration_noev_ns)
1440 results->ns = time_ns - start_ns;
1441 else
1442 results->ns = 0;
1443 results->events = events;
1444 }
1445
1446 return events;
1447}
1448
1449#ifdef __cplusplus
1450}
1451#endif
1452
1453#endif /* EM_DISPATCHER_INLINE_H_ */
__attribute__((always_inline)) static inline void call_eo_rcv_fn__no_cb(const em_receive_func_t eo_receive_func
@ IDLE_STATE_ACTIVE
@ IDLE_STATE_IDLE
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
void poll_unsched_ctrl_queue(void)
Poll EM's internal unscheduled control queues during dispatch.
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
@ EM_QUEUE_STATE_READY
#define EM_DEBUG_TIMESTAMP_ENABLE
#define EM_QUEUE_LOCAL_MULTI_MAX_BURST
#define EM_OUTPUT_QUEUE_IMMEDIATE
#define EM_SCHED_MULTI_MAX_BURST
#define EM_IDLE_HOOKS_ENABLE
#define EM_CHECK_LEVEL
#define EM_CALLBACKS_MAX
#define EM_SCHED_WAIT_ENABLE
#define PRI_QUEUE
uint32_t em_event_type_t
#define EM_EVENT_UNDEF
#define EM_EVENT_GROUP_UNDEF
void(* em_dispatch_enter_func_t)(em_eo_t eo, void **eo_ctx, em_event_t events[], int num, em_queue_t *queue, void **q_ctx)
void(* em_dispatch_exit_func_t)(em_eo_t eo)
@ EM_DISPATCH_DURATION_ROUNDS
@ EM_DISPATCH_DURATION_NO_EVENTS_NS
@ EM_DISPATCH_DURATION_EVENTS
@ EM_DISPATCH_DURATION_NS
@ EM_DISPATCH_DURATION_FOREVER
@ EM_DISPATCH_DURATION_NO_EVENTS_ROUNDS
void(* em_receive_func_t)(void *eo_ctx, em_event_t event, em_event_type_t type, em_queue_t queue, void *q_ctx)
void(* em_receive_multi_func_t)(void *eo_ctx, em_event_t events[], int num, em_queue_t queue, void *q_ctx)
@ EM_ERR_BAD_STATE
@ EM_ERR_BAD_POINTER
void em_free_multi(em_event_t events[], int num)
void em_free(em_event_t event)
uint32_t em_queue_type_t
@ EM_QUEUE_TYPE_ORDERED
@ EM_QUEUE_TYPE_ATOMIC
em_sched_context_type_t
@ EM_SCHED_CONTEXT_TYPE_ORDERED
@ EM_SCHED_CONTEXT_TYPE_NONE
@ EM_SCHED_CONTEXT_TYPE_ATOMIC
int(* em_input_poll_func_t)(void)
int(* em_output_drain_func_t)(void)
queue_elem_t * q_elem
Definition em_mem.h:214
em_sched_context_type_t sched_context_type
Definition em_mem.h:210
int rcv_multi_cnt
Definition em_mem.h:212
event_group_elem_t * egrp_elem
Definition em_mem.h:218
queue_elem_t * sched_q_elem
Definition em_mem.h:216
em_dispatch_duration_select_t select
em_locm_current_t current
Definition em_mem.h:228
local_queues_t local_queues
Definition em_mem.h:241
unsigned int dispatch_cnt
Definition em_mem.h:233
unsigned int poll_drain_dispatch_cnt
Definition em_mem.h:235
uint64_t local_event_cnt
Definition em_mem.h:243
odp_time_t poll_ctrl_interval_time
Definition em_mem.h:265
uint64_t debug_ts[EM_DEBUG_TSP_LAST]
Definition em_mem.h:289
odp_time_t dispatch_last_run
Definition em_mem.h:263
int event_burst_cnt
Definition em_mem.h:237
idle_state_t idle_state
Definition em_mem.h:231
output_queue_track_t output_queue_track
Definition em_mem.h:292
odp_time_t poll_drain_dispatch_last_run
Definition em_mem.h:267
odp_time_t poll_drain_interval_time
Definition em_mem.h:269
cfgfile_opt_cache_t opt_cache
Definition em_mem.h:97
hook_tbl_t * dispatch_exit_cb_tbl
Definition em_mem.h:138
em_cfgfile_opts_t opt
Definition em_mem.h:99
em_event_type_t event_type
em_event_group_t egrp
uint32_t egrp_gen
uint16_t max_events
queue_state_t state
uint16_t valid_check
em_receive_multi_func_t receive_multi_func
queue_elem_flags_t flags
em_receive_func_t receive_func