EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
event_machine_event_group.c
1/*
2 * Copyright (c) 2015-2026, Nokia Solutions and Networks
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifndef _GNU_SOURCE
32#define _GNU_SOURCE
33#endif
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include <stdbool.h>
40#include <stdint.h>
41#include <string.h>
42
43#include <odp_api.h>
44
45#include <event_machine.h>
46
47#include "em_chaining.h"
48#include "em_eo.h"
49#include "em_error.h"
50#include "em_event.h"
51#include "em_event_group.h"
53#include "em_event_inline.h"
54#include "em_event_state.h"
55#include "em_event_types.h"
56#include "em_hooks.h"
57#include "em_internal_event.h"
58#include "em_mem.h"
59#include "em_queue.h"
60#include "em_queue_inline.h"
61#include "em_queue_types.h"
62
63/* per core (thread) state for em_event_group_next() */
64static ENV_LOCAL unsigned int _egrp_tbl_iter_idx;
65
66em_event_group_t em_event_group_create(void)
67{
68 em_event_group_t egrp;
69 event_group_elem_t *egrp_elem;
70 egrp_counter_t egrp_cnt;
71
72 egrp = event_group_alloc();
73 if (unlikely(egrp == EM_EVENT_GROUP_UNDEF)) {
74 unsigned int evgrp_cnt = 0;
75
76 (void)em_event_group_first(&evgrp_cnt);
77 INTERNAL_ERROR(EM_ERR_ALLOC_FAILED, EM_ESCOPE_EVENT_GROUP_CREATE,
78 "Event group alloc failed! evgrps created: %u, max evgrps: %u",
79 evgrp_cnt, EM_MAX_EVENT_GROUPS);
81 }
82
83 egrp_elem = event_group_elem_get(egrp);
84
85 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
86 __ATOMIC_RELAXED);
87 egrp_cnt.pre_cnt = 0;
88 egrp_cnt.post_cnt = 0;
89 egrp_cnt.applied = 0;
90 /* egrp_cnt.gen = don't change */
91
92 /* Set group ready for 'apply' */
93 __atomic_store_n(&egrp_elem->counts.atomic_u64, egrp_cnt.all_u64,
94 __ATOMIC_RELAXED);
95 /* Alloc succeeded, return event group handle */
96 return egrp;
97}
98
99em_status_t em_event_group_delete(em_event_group_t event_group)
100{
101 em_status_t status;
102 event_group_elem_t *const egrp_elem = event_group_elem_get(event_group);
103 egrp_counter_t egrp_cnt;
104
105 RETURN_ERROR_IF(egrp_elem == NULL || !event_group_allocated(egrp_elem),
106 EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_DELETE,
107 "Invalid event group: %" PRI_EGRP "", event_group);
108
109 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
110 __ATOMIC_RELAXED);
111 if (unlikely(egrp_cnt.applied))
112 return EGRP_CNT_ERROR(EM_ERR_BAD_STATE, EM_ESCOPE_EVENT_GROUP_DELETE, egrp_cnt,
113 "Event group:%" PRI_EGRP " not in a state for delete!",
114 event_group);
115
116 egrp_elem->num_notif = 0;
117
118 status = event_group_free(event_group);
119 RETURN_ERROR_IF(status != EM_OK, status, EM_ESCOPE_EVENT_GROUP_DELETE,
120 "Event Group delete failed!");
121
122 return EM_OK;
123}
124
125em_status_t em_event_group_apply(em_event_group_t event_group, int count,
126 int num_notif, const em_notif_t notif_tbl[])
127{
128 event_group_elem_t *const egrp_elem = event_group_elem_get(event_group);
129
130 if (EM_CHECK_LEVEL > 0)
131 RETURN_ERROR_IF(egrp_elem == NULL || count <= 0 || (uint64_t)count > EGRP_CNT_MAX,
132 EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_APPLY,
133 "Invalid args: event group:%" PRI_EGRP ", count:%d [1, %llu]",
134 event_group, count, EGRP_CNT_MAX);
135 if (EM_CHECK_LEVEL >= 2)
136 RETURN_ERROR_IF(!event_group_allocated(egrp_elem),
137 EM_ERR_NOT_CREATED, EM_ESCOPE_EVENT_GROUP_APPLY,
138 "Event group:%" PRI_EGRP " not created!", event_group);
139 if (EM_CHECK_LEVEL >= 3) {
140 em_status_t ret = check_notif_tbl(num_notif, notif_tbl);
141
142 RETURN_ERROR_IF(ret != EM_OK, ret, EM_ESCOPE_EVENT_GROUP_APPLY,
143 "Invalid notif cfg given!");
144 }
145
146 egrp_counter_t egrp_cnt;
147 egrp_counter_t new_cnt;
148
149 do {
150 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
151 __ATOMIC_RELAXED);
152 if (unlikely(egrp_cnt.applied))
154 EM_ESCOPE_EVENT_GROUP_APPLY, egrp_cnt,
155 "Event group %" PRI_EGRP " in use, cannot apply!",
156 event_group);
157
158 egrp_elem->num_notif = num_notif;
159
160 for (int i = 0; i < num_notif; i++) {
161 egrp_elem->notif_tbl[i].event = notif_tbl[i].event;
162 egrp_elem->notif_tbl[i].queue = notif_tbl[i].queue;
163 egrp_elem->notif_tbl[i].egroup = notif_tbl[i].egroup;
164 }
165
166 /* Set the new count */
167 new_cnt.all_u64 = egrp_cnt.all_u64;
169 new_cnt.pre_cnt = count;
170 new_cnt.post_cnt = count;
171 new_cnt.applied = 1;
172 /* Event group generation increments when _apply() is called */
173 new_cnt.gen++;
174 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
175 &egrp_cnt.all_u64, new_cnt.all_u64, false,
176 /*success, publish notif data + counts*/
177 __ATOMIC_RELEASE,
178 __ATOMIC_RELAXED /*failure*/));
179
180 return EM_OK;
181}
182
184{
185 em_locm_t *const locm = &em_locm;
186 em_event_group_t const egrp = em_event_group_current();
187 event_group_elem_t *egrp_elem = NULL;
188 const uint32_t incr = (uint32_t)count;
189
190 if (likely(egrp != EM_EVENT_GROUP_UNDEF))
191 egrp_elem = locm->current.egrp_elem;
192
193 RETURN_ERROR_IF(!egrp_elem, EM_ERR_BAD_ID, EM_ESCOPE_EVENT_GROUP_INCREMENT,
194 "No current event group (%" PRI_EGRP ")", egrp);
195 RETURN_ERROR_IF(count <= 0 || (uint64_t)count > EGRP_CNT_MAX - 1, EM_ERR_BAD_ARG,
196 EM_ESCOPE_EVENT_GROUP_INCREMENT,
197 "Invalid arg: count:%d", count);
198
199 if (EM_CHECK_LEVEL >= 2) {
200 RETURN_ERROR_IF(!event_group_allocated(egrp_elem),
201 EM_ERR_BAD_STATE, EM_ESCOPE_EVENT_GROUP_INCREMENT,
202 "Current event group in a bad state (%" PRI_EGRP ")", egrp);
203 }
204
205 egrp_counter_t egrp_cnt;
206 egrp_counter_t new_cnt;
207
208 /* Add to post counter before count is zero or generation mismatch */
209 do {
210 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
211 __ATOMIC_RELAXED);
212 if (unlikely(egrp_cnt.gen != locm->current.egrp_gen || !egrp_cnt.applied))
214 EM_ESCOPE_EVENT_GROUP_INCREMENT, egrp_cnt,
215 "Expired event group (%" PRI_EGRP ") or not applied:\n"
216 " curr gen:%u", egrp);
217 /* Check for overflow */
218 if (unlikely((EM_EVENT_GROUP_SAFE_MODE && /* safe mode for .pre_cnt */
219 (egrp_cnt.pre_cnt + incr > EGRP_CNT_MAX)) ||
220 (egrp_cnt.post_cnt + incr > EGRP_CNT_MAX))) {
222 EM_ESCOPE_EVENT_GROUP_INCREMENT, egrp_cnt,
223 "Event group %" PRI_EGRP " counter would overflow:\n"
224 " count + increment(%u) > max(%u)",
225 egrp, incr, EGRP_CNT_MAX);
226 }
227
228 new_cnt.all_u64 = egrp_cnt.all_u64;
230 new_cnt.pre_cnt += incr;
231 new_cnt.post_cnt += incr;
232 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
233 &egrp_cnt.all_u64, new_cnt.all_u64, false,
234 __ATOMIC_RELAXED /*success*/,
235 __ATOMIC_RELAXED /*failure*/));
236
237 return EM_OK;
238}
239
240int em_event_group_is_ready(em_event_group_t event_group)
241{
242 const event_group_elem_t *egrp_elem = event_group_elem_get(event_group);
243
244 if (unlikely(EM_CHECK_LEVEL > 0 && egrp_elem == NULL)) {
245 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_IS_READY,
246 "Invalid event group: %" PRI_EGRP "",
247 event_group);
248 return EM_FALSE;
249 }
250
251 if (unlikely(EM_CHECK_LEVEL >= 2 && !event_group_allocated(egrp_elem))) {
252 INTERNAL_ERROR(EM_ERR_NOT_CREATED, EM_ESCOPE_EVENT_GROUP_IS_READY,
253 "Event group: %" PRI_EGRP " not created",
254 event_group);
255 return EM_FALSE;
256 }
257
258 egrp_counter_t egrp_cnt;
259
260 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
261 __ATOMIC_RELAXED);
262 if (egrp_cnt.applied)
263 return EM_FALSE; /* not ready for a new em_event_group_apply() */
264 else
265 return EM_TRUE; /* ready for a new em_event_group_apply() */
266}
267
268em_event_group_t em_event_group_current(void)
269{
270 const event_group_elem_t *egrp_elem = em_locm.current.egrp_elem;
271
272 return egrp_elem ? egrp_elem->event_group : EM_EVENT_GROUP_UNDEF;
273}
274
276{
277 em_locm_t *const locm = &em_locm;
278 event_group_elem_t *const current_egrp_elem = locm->current.egrp_elem;
279
280 if (unlikely(!current_egrp_elem))
281 return;
282
283 /*
284 * Atomically decrement the event group count.
285 * If new count is zero, send notification events.
286 */
287 event_group_count_decrement(locm->current.rcv_multi_cnt, current_egrp_elem);
288
289 locm->current.egrp_elem = NULL;
290}
291
292em_status_t em_event_group_assign(em_event_group_t event_group)
293{
294 em_locm_t *const locm = &em_locm;
295 event_group_elem_t *const egrp_elem = event_group_elem_get(event_group);
296 const int num_events = locm->current.rcv_multi_cnt; /* only valid in EO-receive */
297
298 if (EM_CHECK_LEVEL > 0)
299 RETURN_ERROR_IF(egrp_elem == NULL,
300 EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_ASSIGN,
301 "Invalid event group: %" PRI_EGRP "", event_group);
302 if (EM_CHECK_LEVEL >= 2) {
303 RETURN_ERROR_IF(!event_group_allocated(egrp_elem),
304 EM_ERR_NOT_CREATED, EM_ESCOPE_EVENT_GROUP_ASSIGN,
305 "Invalid event group: %" PRI_EGRP "", event_group);
306 RETURN_ERROR_IF(!locm->current.q_elem || num_events <= 0,
307 EM_ERR_TOO_SMALL, EM_ESCOPE_EVENT_GROUP_ASSIGN,
308 "Cannot assign event group %" PRI_EGRP ",\n"
309 "not called from EO-receive or no events(%d)!",
310 event_group, num_events);
311 }
312 RETURN_ERROR_IF(locm->current.egrp_elem != NULL,
313 EM_ERR_BAD_CONTEXT, EM_ESCOPE_EVENT_GROUP_ASSIGN,
314 "Cannot assign event group %" PRI_EGRP ",\n"
315 "Event group %" PRI_EGRP " already assigned!",
316 event_group, locm->current.egrp_elem->event_group);
317
318 egrp_counter_t egrp_cnt;
319 egrp_counter_t new_cnt;
320
322 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
323 __ATOMIC_RELAXED);
324 if (unlikely(!egrp_cnt.applied))
326 EM_ESCOPE_EVENT_GROUP_ASSIGN, egrp_cnt,
327 "Cannot assign event group %" PRI_EGRP ",\n"
328 "has not been applied!", event_group);
329
330 locm->current.egrp_elem = egrp_elem;
331 locm->current.egrp_gen = egrp_cnt.gen;
332
333 return EM_OK;
334 }
335
336 /*
337 * EM_EVENT_GROUP_SAFE_MODE:
338 */
339 do {
340 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
341 __ATOMIC_RELAXED);
342 if (unlikely(egrp_cnt.pre_cnt < num_events || !egrp_cnt.applied))
344 EM_ESCOPE_EVENT_GROUP_ASSIGN, egrp_cnt,
345 "Cannot assign event group %" PRI_EGRP ",\n"
346 "has not been applied or too many events:%d!",
347 event_group, num_events);
348
349 new_cnt.all_u64 = egrp_cnt.all_u64;
350 new_cnt.pre_cnt -= num_events;
351 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
352 &egrp_cnt.all_u64, new_cnt.all_u64, false,
353 __ATOMIC_RELAXED /*success*/,
354 __ATOMIC_RELAXED /*failure*/));
355 locm->current.egrp_elem = egrp_elem;
356 locm->current.egrp_gen = new_cnt.gen;
357
358 return EM_OK;
359}
360
361em_status_t em_event_group_abort(em_event_group_t event_group)
362{
363 em_locm_t *const locm = &em_locm;
364 event_group_elem_t *const egrp_elem = event_group_elem_get(event_group);
365 egrp_counter_t egrp_cnt;
366 egrp_counter_t new_cnt;
367
368 if (EM_CHECK_LEVEL > 0)
369 RETURN_ERROR_IF(egrp_elem == NULL,
370 EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_ABORT,
371 "Invalid event group: %" PRI_EGRP "", event_group);
372
373 if (EM_CHECK_LEVEL >= 2)
374 RETURN_ERROR_IF(!event_group_allocated(egrp_elem),
375 EM_ERR_NOT_CREATED, EM_ESCOPE_EVENT_GROUP_ABORT,
376 "Event group: %" PRI_EGRP " not created", event_group);
377
378 /* Attempt to set count to zero before count reaches zero */
379 do {
380 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
381 __ATOMIC_RELAXED);
382 if (unlikely(!egrp_cnt.applied)) {
383 /* Don't report as error since aborting during teardown might be normal */
384 DBG_PRINT("%s():Event group:%" PRI_EGRP " abort late, notifs already sent\n"
385 " pre_cnt:%u post_cnt:%u, gen:%u, applied:%s\n",
386 __func__, event_group, (uint32_t)egrp_cnt.pre_cnt,
387 (uint32_t)egrp_cnt.post_cnt, (uint32_t)egrp_cnt.gen,
388 egrp_cnt.applied ? "true" : "false");
389 return EM_ERR_BAD_STATE;
390 }
391
392 new_cnt.all_u64 = egrp_cnt.all_u64;
393 new_cnt.pre_cnt = 0;
394 new_cnt.post_cnt = 0;
395 new_cnt.applied = 0;
396 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
397 &egrp_cnt.all_u64, new_cnt.all_u64, false,
398 __ATOMIC_RELAXED /*success*/,
399 __ATOMIC_RELAXED /*failure*/));
400
401 /* can only modify local event group, other cores might have this set */
402 if (locm->current.egrp_elem == egrp_elem)
403 locm->current.egrp_elem = NULL;
404
405 return EM_OK;
406}
407
408int em_event_group_notif(em_event_group_t event_group,
409 int max_notif, em_notif_t notif_tbl[])
410{
411 const event_group_elem_t *egrp_elem = event_group_elem_get(event_group);
412 int num_notif = 0; /* return value */
413
414 if (EM_CHECK_LEVEL > 0 &&
415 unlikely(egrp_elem == NULL || max_notif < 0)) {
416 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_EVENT_GROUP_NOTIF,
417 "Invalid args: evgrp:%" PRI_EGRP ", notifs:%d",
418 event_group, max_notif);
419 return 0;
420 }
421
422 if (unlikely(EM_CHECK_LEVEL >= 2 && !event_group_allocated(egrp_elem))) {
423 INTERNAL_ERROR(EM_ERR_NOT_CREATED, EM_ESCOPE_EVENT_GROUP_NOTIF,
424 "Event group:%" PRI_EGRP " not created", event_group);
425 return 0;
426 }
427
428 if (unlikely(max_notif == 0))
429 return 0;
430
431 if (unlikely(notif_tbl == NULL)) {
432 INTERNAL_ERROR(EM_ERR_BAD_POINTER, EM_ESCOPE_EVENT_GROUP_NOTIF,
433 "Invalid notif_tbl[] given");
434 return 0;
435 }
436
437 egrp_counter_t egrp_cnt;
438
439 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64, __ATOMIC_ACQUIRE);
440
441 if (egrp_cnt.applied) {
442 num_notif = max_notif < egrp_elem->num_notif ?
443 max_notif : egrp_elem->num_notif;
444
445 for (int i = 0; i < num_notif; i++) {
446 notif_tbl[i].event = egrp_elem->notif_tbl[i].event;
447 notif_tbl[i].queue = egrp_elem->notif_tbl[i].queue;
448 notif_tbl[i].egroup = egrp_elem->notif_tbl[i].egroup;
449 }
450 }
451
452 return num_notif;
453}
454
455/**
456 * Helper to em_send_group().
457 * Send out of EM via event-chaining and a user-provided function
458 * 'event_send_device()' to another device
459 */
460static inline em_status_t
461send_external_egrp(em_event_t event, event_hdr_t *const ev_hdr,
462 em_queue_t queue, em_event_group_t event_group,
463 const event_group_elem_t *egrp_elem)
464{
466 call_api_hooks_send(&event, 1, queue, event_group);
467
468 em_status_t stat = send_chaining_egrp(event, ev_hdr, queue, egrp_elem);
469
470 if (EM_CHECK_LEVEL == 0)
471 return stat;
472
473 RETURN_ERROR_IF(stat != EM_OK, stat, EM_ESCOPE_SEND_GROUP,
474 "send_chaining_egrp: Q:%" PRI_QUEUE "", queue);
475 return EM_OK;
476}
477
478/**
479 * Helper to em_send_multi().
480 * Send out of EM via event-chaining and a user-provided function
481 * 'event_send_device()' to another device
482 */
483static inline int
484send_external_egrp_multi(const em_event_t events[], event_hdr_t *ev_hdrs[], int num,
485 em_queue_t queue, em_event_group_t event_group,
486 const event_group_elem_t *egrp_elem)
487{
489 call_api_hooks_send(events, num, queue, event_group);
490
491 int num_sent = send_chaining_egrp_multi(events, ev_hdrs, num,
492 queue, egrp_elem);
493 if (EM_CHECK_LEVEL > 0 && unlikely(num_sent != num)) {
494 INTERNAL_ERROR(EM_ERR_OPERATION_FAILED, EM_ESCOPE_SEND_GROUP_MULTI,
495 "send_chaining_egrp_multi: req:%d, sent:%d",
496 num, num_sent);
497 }
498
499 return num_sent;
500}
501
502/**
503 * Tag a single event's header with the given event group.
504 * Shared by em_send_group() and send_ctrl_queue().
505 */
506static inline void
507egrp_tag_event(event_hdr_t *ev_hdr, const event_group_elem_t *egrp_elem)
508{
509 if (egrp_elem) {
510 egrp_counter_t egrp_cnt;
511
512 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
513 __ATOMIC_RELAXED);
514 ev_hdr->egrp = egrp_elem->event_group;
515 ev_hdr->egrp_gen = egrp_cnt.gen;
516 } else {
517 ev_hdr->egrp = EM_EVENT_GROUP_UNDEF;
518 }
519}
520
521/**
522 * Tag multiple events' headers with the given event group.
523 * Used by em_send_group_multi().
524 */
525static inline void
526egrp_tag_events_multi(event_hdr_t *const ev_hdrs[], int num,
527 em_event_group_t event_group,
528 const event_group_elem_t *egrp_elem)
529{
530 for (int i = 0; i < num; i++)
531 ev_hdrs[i]->egrp = event_group; /* can be EM_EVENT_GROUP_UNDEF */
532
533 if (egrp_elem) { /* NULL with EM_EVENT_GROUP_UNDEF */
534 egrp_counter_t egrp_cnt;
535
536 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
537 __ATOMIC_RELAXED);
538 uint32_t egrp_gen = egrp_cnt.gen;
539
540 for (int i = 0; i < num; i++)
541 ev_hdrs[i]->egrp_gen = egrp_gen;
542 }
543}
544
545/**
546 * Shared backend for em_send_group() and send_ctrl_queue().
547 *
548 * Sends a single event to a queue on this device and returns the status. The
549 * caller has fetched and validated 'q_elem', tagged the event with the event
550 * group, and is responsible for any send-hooks and for reporting errors (with
551 * its own error scope).
552 */
553static inline em_status_t
554send_egrp_common(em_event_t event, event_hdr_t *ev_hdr,
555 const queue_elem_t *q_elem)
556{
557 em_status_t stat;
558
559 if (esv_enabled())
560 evstate_usr2em(event, ev_hdr, EVSTATE__SEND_EGRP);
561
562 switch (q_elem->type) {
566 stat = send_sched(event, q_elem);
567 break;
569 /*
570 * The user API rejects a real event group here (see
571 * em_send_group()); reached with a real group only via
572 * send_ctrl_queue() for the internal ctrl queues, which EM
573 * dispatches and whose group count it decrements.
574 */
575 stat = queue_unsched_enqueue(event, q_elem);
576 break;
578 stat = send_aggr(event, ev_hdr, q_elem);
579 break;
581 stat = send_local(event, q_elem);
582 break;
583 default:
584 stat = EM_ERR_NOT_FOUND;
585 break;
586 }
587
588 /* Revert the ESV state on failure; the caller reports the error */
589 if (unlikely(stat != EM_OK && esv_enabled()))
590 evstate_usr2em_revert(event, ev_hdr, EVSTATE__SEND_EGRP__FAIL);
591
592 return stat;
593}
594
595/**
596 * Backend for em_send_group_multi().
597 *
598 * Sends multiple events to a queue on this device and returns the number sent.
599 * The caller has fetched and validated 'q_elem', tagged the events with the
600 * event group, and is responsible for any send-hooks and for reporting errors
601 * (with its own error scope).
602 */
603static inline int
604send_egrp_common_multi(const em_event_t events[], event_hdr_t *ev_hdrs[],
605 int num, const queue_elem_t *q_elem)
606{
607 int num_sent;
608
609 if (esv_enabled())
610 evstate_usr2em_multi(events, ev_hdrs, num, EVSTATE__SEND_EGRP_MULTI);
611
612 switch (q_elem->type) {
616 num_sent = send_sched_multi(events, num, q_elem);
617 break;
619 /*
620 * Reached only with EM_EVENT_GROUP_UNDEF: em_send_group_multi()
621 * rejects a real event group on unscheduled queues.
622 */
623 num_sent = queue_unsched_enqueue_multi(events, num, q_elem);
624 break;
626 num_sent = send_aggr_multi(events, ev_hdrs, num, q_elem);
627 break;
629 num_sent = send_local_multi(events, num, q_elem);
630 break;
631 default:
632 num_sent = 0;
633 break;
634 }
635
636 /* Revert the ESV state for the unsent events on failure; caller reports */
637 if (unlikely(num_sent != num && esv_enabled()))
638 evstate_usr2em_revert_multi(&events[num_sent],
639 &ev_hdrs[num_sent],
640 num - num_sent,
641 EVSTATE__SEND_EGRP_MULTI__FAIL);
642
643 return num_sent;
644}
645
646em_status_t em_send_group(em_event_t event, em_queue_t queue,
647 em_event_group_t event_group)
648{
649 const event_group_elem_t *egrp_elem = event_group_elem_get(event_group);
650 const bool is_external = queue_external(queue);
651
653 EM_ERR_BAD_ARG, EM_ESCOPE_SEND_GROUP, "Invalid event");
655 event_group != EM_EVENT_GROUP_UNDEF && !egrp_elem,
656 EM_ERR_NOT_FOUND, EM_ESCOPE_SEND_GROUP,
657 "Invalid event group:%" PRI_EGRP "", event_group);
658 RETURN_ERROR_IF(EM_CHECK_LEVEL >= 2 && event_group != EM_EVENT_GROUP_UNDEF &&
659 !event_group_allocated(egrp_elem),
660 EM_ERR_NOT_CREATED, EM_ESCOPE_SEND_GROUP,
661 "Event group:%" PRI_EGRP " not created", event_group);
662
663 /*
664 * Verify that event references are not used with event groups.
665 * Cannot save the event group into an event header shared between
666 * all the references.
667 */
669 event_group != EM_EVENT_GROUP_UNDEF && event_has_ref(event),
670 EM_ERR_BAD_CONTEXT, EM_ESCOPE_SEND_GROUP,
671 "Event has references: can't use references with event groups");
672
673 event_hdr_t *ev_hdr = event_to_hdr(event);
674
676 (ev_hdr->event_type == EM_EVENT_TYPE_ODP ||
678 EM_ERR_BAD_ARG, EM_ESCOPE_SEND_GROUP,
679 "Event of type %u can't be sent with %s()",
680 ev_hdr->event_type, __func__);
681
682 /* Store the event group information in the event header */
683 egrp_tag_event(ev_hdr, egrp_elem);
684
685 /*
686 * External queue belongs to another EM instance, send out via EMC/BIP
687 */
688 if (is_external)
689 return send_external_egrp(event, ev_hdr, queue,
690 event_group, egrp_elem);
691
692 const queue_elem_t *q_elem = queue_elem_get(queue);
693
694 RETURN_ERROR_IF(EM_CHECK_LEVEL > 0 && !q_elem,
695 EM_ERR_BAD_ARG, EM_ESCOPE_SEND_GROUP,
696 "Invalid queue:%" PRI_QUEUE "", queue);
697 RETURN_ERROR_IF(EM_CHECK_LEVEL >= 2 && !queue_allocated(q_elem),
698 EM_ERR_BAD_STATE, EM_ESCOPE_SEND_GROUP,
699 "Invalid queue:%" PRI_QUEUE "", queue);
700 /*
701 * Event groups are not supported with unscheduled queues: the group
702 * count is decremented only when EO-receive() returns; events on an
703 * unscheduled queue are dequeued directly by the application and are
704 * not dispatched to an EO, so the count would never reach zero.
705 * EM_EVENT_GROUP_UNDEF carries no group and is allowed, behaving like
706 * em_send(). Internal ctrl-queue sends use send_ctrl_queue() instead.
707 */
709 event_group != EM_EVENT_GROUP_UNDEF &&
711 EM_ERR_BAD_CONTEXT, EM_ESCOPE_SEND_GROUP,
712 "Event group not supported with unscheduled queue:%" PRI_QUEUE "",
713 queue);
714
715 /*
716 * em_send_group() called from within an EO-start function: events sent
717 * to scheduled queues are buffered and sent when EO-start completes.
718 */
719 if (unlikely(em_locm.start_eo_elem && q_elem->flags.scheduled)) {
720 if (esv_enabled())
721 evstate_usr2em(event, ev_hdr, EVSTATE__SEND_EGRP);
722
723 if (likely(eo_start_buffer_events(&event, 1, queue) == 1))
724 return EM_OK; /* Success */
725
726 /* Buffering failed: revert ESV state and report */
727 if (esv_enabled())
728 evstate_usr2em_revert(event, ev_hdr, EVSTATE__SEND_EGRP__FAIL);
729 if (EM_CHECK_LEVEL == 0)
731 return INTERNAL_ERROR(EM_ERR_OPERATION_FAILED, EM_ESCOPE_SEND_GROUP,
732 "EO-start event buffering failed, Q:%" PRI_QUEUE "",
733 queue);
734 }
735
737 call_api_hooks_send(&event, 1, queue, event_group);
738
739 /*
740 * Queue belongs to this EM instance
741 */
742 em_status_t stat = send_egrp_common(event, ev_hdr, q_elem);
743
744 if (likely(stat == EM_OK))
745 return EM_OK; /* Success */
746 if (EM_CHECK_LEVEL == 0)
747 return stat;
748 return INTERNAL_ERROR(stat, EM_ESCOPE_SEND_GROUP,
749 "send egrp: Q:%" PRI_QUEUE " type:%" PRI_QTYPE "",
750 queue, q_elem->type);
751}
752
753/*
754 * em_send_group_multi() helper: check function arguments
755 */
756static inline em_status_t
757send_grpmulti_check(const em_event_t events[], int num,
758 em_event_group_t event_group,
759 const event_group_elem_t *egrp_elem)
760{
761 if (EM_CHECK_LEVEL > 0 &&
762 unlikely(!events || num <= 0 ||
763 (event_group != EM_EVENT_GROUP_UNDEF && !egrp_elem)))
764 return EM_ERR_BAD_ARG;
765
766 if (EM_CHECK_LEVEL >= 2 &&
767 unlikely(event_group != EM_EVENT_GROUP_UNDEF &&
768 !event_group_allocated(egrp_elem)))
769 return EM_ERR_NOT_CREATED;
770
771 if (EM_CHECK_LEVEL >= 3) {
772 int i;
773
774 for (i = 0; i < num && events[i] != EM_EVENT_UNDEF; i++)
775 ;
776 if (unlikely(i != num))
777 return EM_ERR_BAD_POINTER;
778 }
779
780 return EM_OK;
781}
782
783/*
784 * em_send_group_multi() helper: buffer events when called from within an
785 * EO-start function - scheduled-queue events are sent once EO-start completes.
786 */
787static inline int
788send_egrp_multi_eo_start(const em_event_t events[], event_hdr_t *ev_hdrs[],
789 int num, em_queue_t queue)
790{
791 if (esv_enabled())
792 evstate_usr2em_multi(events, ev_hdrs, num,
793 EVSTATE__SEND_EGRP_MULTI);
794
795 int num_sent = eo_start_buffer_events(events, num, queue);
796
797 if (likely(num_sent == num))
798 return num_sent; /* Success */
799
800 /* Buffering failed for the tail events: revert ESV and report */
801 if (esv_enabled())
802 evstate_usr2em_revert_multi(&events[num_sent],
803 &ev_hdrs[num_sent],
804 num - num_sent,
805 EVSTATE__SEND_EGRP_MULTI__FAIL);
806 if (EM_CHECK_LEVEL > 0)
807 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_SEND_GROUP_MULTI,
808 "EO-start event buffering failed: req:%d, sent:%d",
809 num, num_sent);
810 return num_sent;
811}
812
813int em_send_group_multi(const em_event_t events[], int num, em_queue_t queue,
814 em_event_group_t event_group)
815{
816 const event_group_elem_t *egrp_elem = event_group_elem_get(event_group);
817 const bool is_external = queue_external(queue);
818 event_hdr_t *ev_hdrs[num];
819
820 em_status_t err = send_grpmulti_check(events, num, event_group, egrp_elem);
821
822 if (unlikely(err != EM_OK)) {
823 INTERNAL_ERROR(err, EM_ESCOPE_SEND_GROUP_MULTI,
824 "Invalid args: events:%p num:%d event_group:%" PRI_EGRP "",
825 events, num, event_group);
826 return 0;
827 }
828
829 /*
830 * Verify that event references are not used with event groups.
831 * Cannot save the event group into an event header shared between
832 * all the references
833 */
834 if (unlikely(EM_CHECK_LEVEL >= 3 && event_group != EM_EVENT_GROUP_UNDEF)) {
835 for (int i = 0; i < num; i++) {
836 if (likely(!event_has_ref(events[i])))
837 continue;
838
839 INTERNAL_ERROR(EM_ERR_BAD_CONTEXT, EM_ESCOPE_SEND_GROUP_MULTI,
840 "event[%d] has references: can't use with event groups", i);
841 return 0;
842 }
843 }
844
845 event_to_hdr_multi(events, ev_hdrs, num);
846
847 /* check for invalid TIMER events and EM_EVENT_TYPE_ODP */
848 if (EM_CHECK_LEVEL > 0) {
849 for (int i = 0; i < num; i++) {
850 if (unlikely(ev_hdrs[i]->event_type == EM_EVENT_TYPE_ODP ||
851 ev_hdrs[i]->event_type == EM_EVENT_TYPE_TIMER_IND)) {
852 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_SEND_GROUP_MULTI,
853 "Event[%d] of type %u can't be sent with %s()",
854 i, ev_hdrs[i]->event_type, __func__);
855 return 0;
856 }
857 }
858 }
859
860 /* Store the event group information in the event header */
861 egrp_tag_events_multi(ev_hdrs, num, event_group, egrp_elem);
862
863 /*
864 * External queue belongs to another EM instance, send out via EMC/BIP
865 */
866 if (is_external)
867 return send_external_egrp_multi(events, ev_hdrs, num, queue,
868 event_group, egrp_elem);
869
870 const queue_elem_t *q_elem = queue_elem_get(queue);
871
872 if (EM_CHECK_LEVEL > 0 && unlikely(!q_elem)) {
873 INTERNAL_ERROR(EM_ERR_BAD_ARG, EM_ESCOPE_SEND_GROUP_MULTI,
874 "Invalid queue:%" PRI_QUEUE "", queue);
875 return 0;
876 }
877 if (EM_CHECK_LEVEL >= 2 && unlikely(!queue_allocated(q_elem))) {
878 INTERNAL_ERROR(EM_ERR_BAD_STATE, EM_ESCOPE_SEND_GROUP_MULTI,
879 "Invalid queue:%" PRI_QUEUE "", queue);
880 return 0;
881 }
882 /*
883 * Event groups are not supported with unscheduled queues: the group
884 * count is decremented only when EO-receive() returns; events on an
885 * unscheduled queue are dequeued directly by the application and are
886 * not dispatched to an EO, so the count would never reach zero.
887 * EM_EVENT_GROUP_UNDEF carries no group and is allowed, behaving like
888 * em_send_multi().
889 */
890 if (EM_CHECK_LEVEL > 1 && unlikely(event_group != EM_EVENT_GROUP_UNDEF &&
891 q_elem->type == EM_QUEUE_TYPE_UNSCHEDULED)) {
892 INTERNAL_ERROR(EM_ERR_BAD_CONTEXT, EM_ESCOPE_SEND_GROUP_MULTI,
893 "Event group not supported with unscheduled queue:%" PRI_QUEUE "",
894 queue);
895 return 0;
896 }
897
898 /*
899 * em_send_group_multi() called from within an EO-start function: events
900 * sent to scheduled queues are buffered and sent when EO-start completes.
901 */
902 if (unlikely(em_locm.start_eo_elem && q_elem->flags.scheduled))
903 return send_egrp_multi_eo_start(events, ev_hdrs, num, queue);
904
906 call_api_hooks_send(events, num, queue, event_group);
907
908 /*
909 * Queue belongs to this EM instance
910 */
911 int num_sent = send_egrp_common_multi(events, ev_hdrs, num, q_elem);
912
913 if (unlikely(num_sent != num && EM_CHECK_LEVEL > 0))
914 INTERNAL_ERROR(EM_ERR_LIB_FAILED, EM_ESCOPE_SEND_GROUP_MULTI,
915 "send-egrp-multi failed: req:%d, sent:%d",
916 num, num_sent);
917 return num_sent;
918}
919
920/* doc in em_event_group.h */
921em_status_t send_ctrl_queue(em_event_t event, em_queue_t queue,
922 em_event_group_t event_group)
923{
924 const event_group_elem_t *egrp_elem = event_group_elem_get(event_group);
925 event_hdr_t *ev_hdr = event_to_hdr(event);
926 const queue_elem_t *q_elem = queue_elem_get(queue);
927
928 /* validate the ctrl queue, egrp checked by caller */
930 (!q_elem || !queue_allocated(q_elem)),
931 EM_ERR_BAD_ARG, EM_ESCOPE_SEND_CTRL_QUEUE,
932 "Invalid ctrl queue:%" PRI_QUEUE "", queue);
933
934 /* Tag with the event group and send via the shared backend (no guard) */
935 egrp_tag_event(ev_hdr, egrp_elem);
936
937 em_status_t stat = send_egrp_common(event, ev_hdr, q_elem);
938
939 if (likely(stat == EM_OK))
940 return EM_OK; /* Success */
941 if (EM_CHECK_LEVEL == 0)
942 return stat;
943 return INTERNAL_ERROR(stat, EM_ESCOPE_SEND_CTRL_QUEUE,
944 "ctrl-queue send: Q:%" PRI_QUEUE " type:%" PRI_QTYPE "",
945 queue, q_elem->type);
946}
947
948em_event_group_t em_event_group_first(unsigned int *num)
949{
950 const event_group_elem_t *const egrp_elem_tbl =
951 em_shm->event_group_tbl.egrp_elem;
952 const event_group_elem_t *egrp_elem = &egrp_elem_tbl[0];
953 const unsigned int egrp_count = event_group_count();
954
955 _egrp_tbl_iter_idx = 0; /* reset iteration */
956
957 if (num)
958 *num = egrp_count;
959
960 if (egrp_count == 0) {
961 _egrp_tbl_iter_idx = EM_MAX_EVENT_GROUPS; /* UNDEF=_next()*/
963 }
964
965 /* find first */
966 while (!event_group_allocated(egrp_elem)) {
967 _egrp_tbl_iter_idx++;
968 if (_egrp_tbl_iter_idx >= EM_MAX_EVENT_GROUPS)
970 egrp_elem = &egrp_elem_tbl[_egrp_tbl_iter_idx];
971 }
972
973 return egrp_idx2hdl(_egrp_tbl_iter_idx);
974}
975
976em_event_group_t em_event_group_next(void)
977{
978 if (_egrp_tbl_iter_idx >= EM_MAX_EVENT_GROUPS - 1)
980
981 _egrp_tbl_iter_idx++;
982
983 const event_group_elem_t *const egrp_elem_tbl =
984 em_shm->event_group_tbl.egrp_elem;
985 const event_group_elem_t *egrp_elem =
986 &egrp_elem_tbl[_egrp_tbl_iter_idx];
987
988 /* find next */
989 while (!event_group_allocated(egrp_elem)) {
990 _egrp_tbl_iter_idx++;
991 if (_egrp_tbl_iter_idx >= EM_MAX_EVENT_GROUPS)
993 egrp_elem = &egrp_elem_tbl[_egrp_tbl_iter_idx];
994 }
995
996 return egrp_idx2hdl(_egrp_tbl_iter_idx);
997}
998
999uint64_t em_event_group_to_u64(em_event_group_t event_group)
1000{
1001 return (uint64_t)event_group;
1002}
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
#define RETURN_ERROR_IF(cond, error, escope, fmt,...)
Definition em_error.h:65
em_status_t send_ctrl_queue(em_event_t event, em_queue_t queue, em_event_group_t event_group)
#define EGRP_CNT_ERROR(err, escope, egrp_cnt, fmt,...)
#define EGRP_CNT_MAX
em_status_t check_notif_tbl(const int num_notif, const em_notif_t notif_tbl[])
Check that the usage of a table of notifications is valid.
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
#define EM_EVENT_GROUP_SAFE_MODE
#define EM_CHECK_LEVEL
#define EM_MAX_EVENT_GROUPS
#define EM_API_HOOKS_ENABLE
#define PRI_QUEUE
#define EM_TRUE
#define EM_EVENT_UNDEF
#define EM_FALSE
#define EM_EVENT_GROUP_UNDEF
#define PRI_EGRP
#define EM_OK
uint32_t em_status_t
@ EM_ERR_NOT_FOUND
@ EM_ERR_OPERATION_FAILED
@ EM_ERR_BAD_ID
@ EM_ERR_NOT_CREATED
@ EM_ERR_TOO_SMALL
@ EM_ERR_BAD_CONTEXT
@ EM_ERR_ALLOC_FAILED
@ EM_ERR_BAD_ARG
@ EM_ERR_BAD_STATE
@ EM_ERR_LIB_FAILED
@ EM_ERR_BAD_POINTER
uint64_t em_event_group_to_u64(em_event_group_t event_group)
em_event_group_t em_event_group_next(void)
void em_event_group_processing_end(void)
em_status_t em_event_group_delete(em_event_group_t event_group)
int em_event_group_is_ready(em_event_group_t event_group)
em_event_group_t em_event_group_create(void)
em_status_t em_event_group_abort(em_event_group_t event_group)
em_status_t em_event_group_apply(em_event_group_t event_group, int count, int num_notif, const em_notif_t notif_tbl[])
em_event_group_t em_event_group_first(unsigned int *num)
int em_send_group_multi(const em_event_t events[], int num, em_queue_t queue, em_event_group_t event_group)
em_event_group_t em_event_group_current(void)
em_status_t em_send_group(em_event_t event, em_queue_t queue, em_event_group_t event_group)
em_status_t em_event_group_increment(int count)
int em_event_group_notif(em_event_group_t event_group, int max_notif, em_notif_t notif_tbl[])
em_status_t em_event_group_assign(em_event_group_t event_group)
@ EM_EVENT_TYPE_ODP
@ EM_EVENT_TYPE_TIMER_IND
#define PRI_QTYPE
@ EM_QUEUE_TYPE_AGGR
@ EM_QUEUE_TYPE_ORDERED
@ EM_QUEUE_TYPE_ATOMIC
@ EM_QUEUE_TYPE_UNSCHEDULED
@ EM_QUEUE_TYPE_PARALLEL
@ EM_QUEUE_TYPE_LOCAL
queue_elem_t * q_elem
Definition em_mem.h:214
int rcv_multi_cnt
Definition em_mem.h:212
event_group_elem_t * egrp_elem
Definition em_mem.h:218
uint32_t egrp_gen
Definition em_mem.h:220
em_locm_current_t current
Definition em_mem.h:228
eo_elem_t * start_eo_elem
Definition em_mem.h:275
em_event_group_t egroup
em_notif_t notif_tbl[EM_EVENT_GROUP_MAX_NOTIF]
em_event_type_t event_type
em_event_group_t egrp
uint32_t egrp_gen
queue_elem_flags_t flags