EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_event_group.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 *
34 * EM internal event group functions
35 */
36
37#ifndef EM_EVENT_GROUP_H_
38#define EM_EVENT_GROUP_H_
39
40#include <stdbool.h>
41#include <stdint.h>
42
43#include <odp_api.h>
44
45#include <event_machine.h>
46
47#include "em_error.h"
49#include "em_internal_event.h"
50#include "em_mem.h"
51
52#ifdef __cplusplus
53extern "C" {
54#endif
55
56#if EM_EVENT_GROUP_SAFE_MODE
57/** Error reporting macro for event group counters in safe mode (.pre_cnt & .post_cnt) */
58#define EGRP_CNT_ERROR(err, escope, egrp_cnt, fmt, ...) \
59 INTERNAL_ERROR((err), (escope), \
60 fmt "\n pre_cnt:%u post_cnt:%u gen:%u applied:%s\n", \
61 ##__VA_ARGS__, \
62 (uint32_t)(egrp_cnt).pre_cnt, (uint32_t)(egrp_cnt).post_cnt, \
63 (uint32_t)(egrp_cnt).gen, (egrp_cnt).applied ? "true" : "false")
64#else
65/** Error reporting macro for event group counters in non-safe mode (only .post_cnt) */
66#define EGRP_CNT_ERROR(err, escope, egrp_cnt, fmt, ...) \
67 INTERNAL_ERROR((err), (escope), \
68 fmt "\n post_cnt:%u gen:%u applied:%s\n", \
69 ##__VA_ARGS__, \
70 (uint32_t)(egrp_cnt).post_cnt, (uint32_t)(egrp_cnt).gen, \
71 (egrp_cnt).applied ? "true" : "false")
72#endif
73
74#define invalid_egrp(event_group) \
75 ((unsigned int)egrp_hdl2idx((event_group)) >= EM_MAX_EVENT_GROUPS)
76
77em_status_t event_group_init(event_group_tbl_t *const event_group_tbl,
78 odp_stash_t *const event_group_pool);
79em_status_t event_group_term(void);
80
81em_event_group_t event_group_alloc(void);
82em_status_t event_group_free(em_event_group_t event_group);
83
84static inline int
85event_group_allocated(const event_group_elem_t *egrp_elem)
86{
87 return !egrp_elem->in_stash;
88}
89
90static inline int
91egrp_hdl2idx(const em_event_group_t event_group)
92{
93 return (int)((uintptr_t)event_group - 1);
94}
95
96static inline em_event_group_t
97egrp_idx2hdl(const int event_group_idx)
98{
99 return (em_event_group_t)(uintptr_t)(event_group_idx + 1);
100}
101
102static inline event_group_elem_t *
103event_group_elem_get(const em_event_group_t event_group)
104{
105 const int egrp_idx = egrp_hdl2idx(event_group);
106 event_group_elem_t *egrp_elem;
107
108 if (unlikely((uint32_t)egrp_idx > EM_MAX_EVENT_GROUPS - 1))
109 return NULL;
110
111 egrp_elem = &em_shm->event_group_tbl.egrp_elem[egrp_idx];
112
113 return egrp_elem;
114}
115
116static inline bool valid_pre_cnt(const event_group_elem_t *egrp_elem,
117 const egrp_counter_t egrp_cnt, const unsigned int decr,
118 const uint32_t event_egrp_gen)
119{
120 if (unlikely(egrp_cnt.pre_cnt < decr || !egrp_cnt.applied ||
121 egrp_cnt.gen != event_egrp_gen)) {
122 EGRP_CNT_ERROR(EM_ERR_BAD_ID, EM_ESCOPE_EVENT_GROUP_UPDATE_PRECNT, egrp_cnt,
123 "Expired event(s) or inv. decr in event group:%" PRI_EGRP ":\n"
124 " event-egrp-gen:%u, decr:%u",
125 egrp_elem->event_group, event_egrp_gen, decr);
126 return false;
127 }
128
129 return true;
130}
131
132/**
133 * Decrement event group pre-counter atomically.
134 *
135 * Returns true on success, false on failure (invalid pre-count or generation).
136 * Only used when EM_EVENT_GROUP_SAFE_MODE is enabled.
137 */
138static inline bool
139pre_count_decrement(event_group_elem_t *const egrp_elem, unsigned int decr,
140 uint32_t event_egrp_gen)
141{
142 egrp_counter_t egrp_cnt;
143 egrp_counter_t new_cnt;
144
145 do {
146 /* Fully relaxed: just a value check & arithmetic */
147 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
148 __ATOMIC_RELAXED);
149
150 if (unlikely(!valid_pre_cnt(egrp_elem, egrp_cnt, decr, event_egrp_gen)))
151 return false;
152
153 new_cnt.all_u64 = egrp_cnt.all_u64;
154 new_cnt.pre_cnt -= decr;
155 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
156 &egrp_cnt.all_u64, new_cnt.all_u64, false,
157 __ATOMIC_RELAXED /*success*/,
158 __ATOMIC_RELAXED /*failure*/));
159 return true; /* success */
160}
161
162/**
163 * Set core local event group.
164 *
165 * Verifies event group state and updates pre-count before setting core local
166 * event group. Sets evgroup to undefined for excess and expired evgroup events.
167 *
168 * Only called by the EM-dispatcher before receive function.
169 */
170static inline void
171event_group_set_local(em_event_group_t egrp, uint32_t event_egrp_gen, unsigned int decr)
172{
173 if (egrp == EM_EVENT_GROUP_UNDEF)
174 return;
175
176 /* Group is validated before setting */
177 em_locm_t *const locm = &em_locm;
178 event_group_elem_t *const egrp_elem = event_group_elem_get(egrp);
179
180 if (unlikely(!egrp_elem))
181 return;
182
184 bool valid_precnt = pre_count_decrement(egrp_elem, decr, event_egrp_gen);
185
186 if (unlikely(!valid_precnt)) {
187 locm->current.egrp_elem = NULL;
188 return;
189 }
190 }
191
192 locm->current.egrp_elem = egrp_elem;
193 locm->current.egrp_gen = event_egrp_gen;
194}
195
196static inline bool valid_post_cnt(const event_group_elem_t *egrp_elem,
197 const egrp_counter_t egrp_cnt,
198 const unsigned int decr, const uint32_t egrp_gen)
199{
200 if (unlikely(egrp_cnt.post_cnt < decr || egrp_cnt.gen != egrp_gen)) {
201 if (egrp_cnt.applied) {
202 EGRP_CNT_ERROR(EM_ERR_BAD_ID, EM_ESCOPE_EVENT_GROUP_UPDATE_POSTCNT,
203 egrp_cnt,
204 "Expired event(s) or inv. decr in event group:%" PRI_EGRP ":\n"
205 " current-egrp-gen:%u, decr:%u",
206 egrp_elem->event_group, egrp_gen, decr);
207 }
208 return false;
209 }
210
211 return true;
212}
213
214/**
215 * Updates event group post-counter. Generation and count must be valid.
216 */
217static inline int /* num_notifs when evgrp is done */
218post_count_decrement(event_group_elem_t *const egrp_elem,
219 const unsigned int decr,
220 em_notif_t notif_tbl[static EM_EVENT_GROUP_MAX_NOTIF /*out*/])
221{
222 const em_locm_t *locm = &em_locm;
223 const uint32_t egrp_gen = locm->current.egrp_gen;
224
225 egrp_counter_t egrp_cnt;
226 egrp_counter_t new_cnt;
227 int succ_mo;
228 int num_notif; /* return value: the num notifs to send when evgrp done */
229
230 do {
231 num_notif = 0;
232 succ_mo = __ATOMIC_RELAXED;
233 /*
234 * Need acquire here so that if this turns out to be the last event
235 * (reads notif_tbl[]), we see prior initialization (published with a
236 * release when applied).
237 */
238 egrp_cnt.all_u64 = __atomic_load_n(&egrp_elem->counts.atomic_u64,
239 __ATOMIC_ACQUIRE);
240
241 if (unlikely(!valid_post_cnt(egrp_elem, egrp_cnt, decr, egrp_gen)))
242 return 0;
243
244 new_cnt.all_u64 = egrp_cnt.all_u64;
245 new_cnt.post_cnt -= decr;
246
247 if (new_cnt.post_cnt == 0) {
248 /*
249 * All events in event group handled!
250 */
251 num_notif = MIN(egrp_elem->num_notif, EM_EVENT_GROUP_MAX_NOTIF);
252 /* Copy notifs before clearing '.applied' */
253 for (int i = 0; i < num_notif; i++) {
254 notif_tbl[i].event = egrp_elem->notif_tbl[i].event;
255 notif_tbl[i].queue = egrp_elem->notif_tbl[i].queue;
256 notif_tbl[i].egroup = egrp_elem->notif_tbl[i].egroup;
257 }
258 /* Clear '.applied' before sending notifications */
259 new_cnt.applied = 0;
260 /*
261 * Use 'release' when we clear applied (last event) so a re-user
262 * 'acquiring' the flag sees all prior writes. Otherwise 'relaxed'.
263 */
264 succ_mo = __ATOMIC_RELEASE;
265 }
266 } while (!__atomic_compare_exchange_n(&egrp_elem->counts.atomic_u64,
267 &egrp_cnt.all_u64, new_cnt.all_u64, false,
268 succ_mo /*success*/,
269 __ATOMIC_RELAXED /*failure*/));
270
271 return num_notif;
272}
273
274/**
275 * Decrements the event group (post-)count and sends notif events when group is done
276 *
277 * Called by the EM-dispatcher after the EO-receive function.
278 *
279 * Only called when the core-local current event group is set, e.g.,
280 * if (em_locm.current.egrp_elem != NULL)
281 * event_group_count_decrement(1, em_locm.current.egrp_elem);
282 */
283static inline void
284event_group_count_decrement(const unsigned int decr, event_group_elem_t *const egrp_elem)
285{
287
288 /* Validates group before updating counters */
289 const int num_notif = post_count_decrement(egrp_elem, decr, notif_tbl /*out*/);
290
291 /* Last event in the group, send notifications */
292 if (num_notif > 0) {
293 em_status_t ret = send_notifs(num_notif, notif_tbl);
294
295 if (unlikely(ret != EM_OK))
296 INTERNAL_ERROR(ret, EM_ESCOPE_EVENT_GROUP_UPDATE,
297 "send notifs failed");
298 }
299}
300
301static inline void
302save_current_evgrp(event_group_elem_t **save_egrp_elem /*out*/,
303 uint32_t *save_egrp_gen /*out*/)
304{
305 const em_locm_t *locm = &em_locm;
306
307 *save_egrp_elem = locm->current.egrp_elem;
308 *save_egrp_gen = locm->current.egrp_gen;
309}
310
311static inline void
312restore_current_evgrp(event_group_elem_t *const saved_egrp_elem,
313 const uint32_t saved_egrp_gen)
314{
315 em_locm_t *const locm = &em_locm;
316
317 locm->current.egrp_elem = saved_egrp_elem;
318 locm->current.egrp_gen = saved_egrp_gen;
319}
320
321unsigned int event_group_count(void);
322
323/**
324 * Send an internal control event to an EM unscheduled control queue, tagged to
325 * an event group.
326 *
327 * Internal counterpart to em_send_group() for EM's own control-event plumbing:
328 * the per-core/shared unscheduled ctrl queues are dispatched by EM, so the
329 * event group count is decremented normally. Unlike em_send_group() this skips
330 * the user-facing argument checks, the unscheduled-queue restriction and the
331 * user API send-hooks. The caller must pass valid internal arguments.
332 *
333 * @return EM_OK if successful.
334 */
335em_status_t send_ctrl_queue(em_event_t event, em_queue_t queue,
336 em_event_group_t event_group);
337
338/** Print information about all event groups */
339void event_group_info_print(void);
340
341#ifdef __cplusplus
342}
343#endif
344
345#endif /* EM_EVENT_GROUP_H_ */
#define INTERNAL_ERROR(error, escope, fmt,...)
Definition em_error.h:58
void event_group_info_print(void)
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,...)
em_status_t send_notifs(const int num_notif, const em_notif_t notif_tbl[])
Helper func to send notifications events.
ENV_LOCAL em_locm_t em_locm
em_shm_t * em_shm
#define EM_EVENT_GROUP_SAFE_MODE
#define EM_EVENT_GROUP_MAX_NOTIF
#define EM_MAX_EVENT_GROUPS
#define EM_EVENT_GROUP_UNDEF
#define PRI_EGRP
#define EM_OK
uint32_t em_status_t
@ EM_ERR_BAD_ID
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
em_event_group_t egroup
em_notif_t notif_tbl[EM_EVENT_GROUP_MAX_NOTIF]