blob: 9a1ad0a9778e20ebf4b8f6240cd0e56076c81c99 [file] [log] [blame]
Tom Seidenberg6c81f5a2020-07-10 15:49:03 +00001/*
2 * Copyright (c) 2020 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/** @file
17 * @brief Callback multiplex scheme
18 */
19
20#ifndef included_callback_data_h
21#define included_callback_data_h
22#include <vppinfra/clib.h>
23
24/** @brief Declare and define a callback set type
25 * @param set_t_ The set type to define
26 * @param cb_t_ The callback type to use
27 */
28#define clib_callback_data_typedef(set_t_, cb_t_) \
29typedef struct set_t_ \
30{ \
31 cb_t_* curr; \
32 cb_t_* volatile next; \
33 cb_t_* spare; \
34 clib_spinlock_t* lock; \
35} set_t_
36
37/** @brief Initialize a callback set
38 * @param set_ The callback set to initialize
39 * @param lock_ The lock to use, if any
40 */
41#define clib_callback_data_init(set_,lock_) \
42do { \
43 (set_)->lock = (lock_); \
44 (set_)->curr = 0; \
45 (set_)->next = 0; \
46 (set_)->spare = 0; \
47} while (0)
48
49/** @brief Add a callback to the specified callback set
50 * @param set_ The callback set
51 * @param value_ The value_ to assign the callback
52 *
53 * Add a callback from the indicated callback set. If the set is
54 * currently being iterated, then the change will be applied after the
55 * current full iteration, and prior to the next full iteration.
56 */
57#define clib_callback_data_add(set_,value_) \
58do { \
59 clib_spinlock_lock_if_init ((set_)->lock); \
60 typeof ((set_)->next) next_ = (set_)->next; \
61 if (PREDICT_TRUE (next_ == 0)) \
62 { \
63 next_ = (set_)->spare; \
64 (set_)->spare = 0; \
65 vec_append (next_, (set_)->curr); \
66 } \
67 u32 sz_ = vec_len (next_); \
68 vec_validate (next_, sz_); \
69 next_[sz_] = (value_); \
70 (set_)->next = next_; \
71 clib_spinlock_unlock_if_init ((set_)->lock); \
72} while (0)
73
74/** @brief Remove a callback from the specified callback set
75 * @param set_ The callback set
76 * @param fp_ The current callback function
77 * @return 1 if the function was removed, 0 if not
78 *
79 * Remove a callback from the indicated callback set. Idempotent. If
80 * the set is currently being iterated, then the change will be applied
81 * after the current full iteration, and prior to the next full
82 * iteration.
83 */
84#define clib_callback_data_remove(set_,fp_) \
85({ \
86 int found_ = 0; \
87 clib_spinlock_lock_if_init ((set_)->lock); \
88 typeof ((set_)->next) next_ = (set_)->next; \
89 if (PREDICT_TRUE (next_ == 0)) \
90 { \
91 next_ = (set_)->spare; \
92 (set_)->spare = 0; \
93 vec_append (next_, (set_)->curr); \
94 } \
95 u32 sz_ = vec_len (next_); \
96 u32 i_; \
97 for (i_ = 0; i_ < sz_; i_++) \
98 if (next_[i_].fp == (fp_)) \
99 { \
100 vec_delete (next_, 1, i_); \
101 found_ = 1; \
102 break; \
103 } \
104 (set_)->next = next_; \
105 clib_spinlock_unlock_if_init ((set_)->lock); \
106 found_; \
107})
108
109/** @brief Swap a callback in the specified callback set
110 * @param set_ The callback set
111 * @param fp_ The current callback function
112 * @param value_ The value_ to assign the callback
113 * @return 1 if the function was swapped, 0 if not
114 *
115 * Swap a callback in the indicated callback set. If the callback is
116 * not found, then nothing is done. If the set is currently being
117 * iterated, then the change will be applied after the current full
118 * iteration, and prior to the next full iteration.
119 */
120#define clib_callback_data_swap(set_,fp_,value_) \
121({ \
122 int found_ = 0; \
123 clib_spinlock_lock_if_init ((set_)->lock); \
124 typeof ((set_)->next) next_ = (set_)->next; \
125 if (PREDICT_TRUE (next_ == 0)) \
126 { \
127 next_ = (set_)->spare; \
128 (set_)->spare = 0; \
129 vec_append (next_, (set_)->curr); \
130 } \
131 u32 sz_ = vec_len (next_); \
132 u32 i_; \
133 for (i_ = 0; i_ < sz_; i_++) \
134 if (next_[i_].fp == (fp_)) \
135 { \
136 next_[i_] = (value_); \
137 found_ = 1; \
138 break; \
139 } \
140 (set_)->next = next_; \
141 clib_spinlock_unlock_if_init ((set_)->lock); \
142 found_; \
143})
144
145/** @brief Ensure a callback is in the specified callback set
146 * @param set_ The callback set
147 * @param value_ The value_ to assign the callback
148 * @return 1 if the function was swapped, 0 if not
149 *
150 * Add or swap a callback in the indicated callback set. If the
151 * callback is already in the set, it is replaced. If the callback is
152 * not found, then it is added. If the set is currently being
153 * iterated, then the change will be applied after the current full
154 * iteration, and prior to the next full iteration.
155 */
156#define clib_callback_data_ensure(set_,value_) \
157do { \
158 int found_ = 0; \
159 clib_spinlock_lock_if_init ((set_)->lock); \
160 typeof ((set_)->next) next_ = (set_)->next; \
161 if (PREDICT_TRUE (next_ == 0)) \
162 { \
163 next_ = (set_)->spare; \
164 (set_)->spare = 0; \
165 vec_append (next_, (set_)->curr); \
166 } \
167 u32 sz_ = vec_len (next_); \
168 u32 i_; \
169 for (i_ = 0; i_ < sz_; i_++) \
170 if (next_[i_].fp == (value_).fp) \
171 { \
172 found_ = 1; \
173 break; \
174 } \
175 if (!found_) \
176 vec_validate (next_, i_); \
177 next_[i_] = (value_); \
178 (set_)->next = next_; \
179 clib_spinlock_unlock_if_init ((set_)->lock); \
180} while(0)
181
182/** @brief Enable/Disable the specified callback
183 * @param set_ The callback set
184 * @param fp_ The callback function
185 * @param ena_ 1 to enable, 0 to disable
186 *
187 * Enable or disable a callback function, with no data.
188 */
189#define clib_callback_data_enable_disable(set_,fp_,ena_) \
190do { \
191 if (ena_) \
192 { \
193 typeof ((set_)->next[0]) data_ = { .fp = (fp_) }; \
194 clib_callback_data_add ((set_), data_); \
195 } \
196 else \
197 clib_callback_data_remove ((set_), (fp_)); \
198} while (0)
199
200/** @brief Get the value of a callback, if set.
201 * @param set_ The callback set
202 * @param fp_ The callback function
203 * @param v_ Set to the callback's current value
204 * @return 1 if the function is in the set, 0 if not
205 */
206#define clib_callback_data_get_value(set_,fp_,v_) \
207({ \
208 int found_ = 0; \
209 clib_spinlock_lock_if_init ((set_)->lock); \
210 typeof ((set_)->next) search_ = (set_)->next; \
211 if (PREDICT_TRUE (search_ == 0)) \
212 search_ = (set_)->curr; \
213 u32 sz_ = vec_len (search_); \
214 u32 i_; \
215 for (i_ = 0; i_ < sz_; i_++) \
216 if (search_[i_].fp == (fp_)) \
217 { \
218 (v_) = search_[i]; \
219 found_ = 1; \
220 break; \
221 } \
222 clib_spinlock_unlock_if_init ((set_)->lock); \
223 found_; \
224})
225
226/** @brief Check if callback is set
227 * @param set_ The callback set
228 * @param fp_ The callback function
229 * @return 1 if the function is in the set, 0 if not
230 */
231#define clib_callback_data_is_set(set_,fp_) \
232({ \
233 int found_ = 0; \
234 clib_spinlock_lock_if_init ((set_)->lock); \
235 typeof ((set_)->next) search_ = (set_)->next; \
236 if (PREDICT_TRUE (search_ == 0)) \
237 search_ = (set_)->curr; \
238 u32 sz_ = vec_len (search_); \
239 u32 i_; \
240 for (i_ = 0; i_ < sz_; i_++) \
241 if (search_[i_].fp == (fp_)) \
242 { \
243 found_ = 1; \
244 break; \
245 } \
246 clib_spinlock_unlock_if_init ((set_)->lock); \
247 found_; \
248})
249
250/** @brief Check for and get current callback set
251 * @param set_ the callback set
252 * @param varargs additional callback parameters
253 */
254#define clib_callback_data_check_and_get(set_) \
255({ \
256 typeof ((set_)->curr) curr_ = (set_)->curr; \
257 if (PREDICT_FALSE ((set_)->next != 0)) \
258 { \
259 clib_spinlock_lock_if_init ((set_)->lock); \
260 vec_reset_length (curr_); \
261 (set_)->spare = curr_; \
262 curr_ = (set_)->next; \
263 (set_)->next = 0; \
264 if (PREDICT_FALSE (0 == vec_len (curr_))) \
265 vec_free (curr_); \
266 (set_)->curr = curr_; \
267 clib_spinlock_unlock_if_init ((set_)->lock); \
268 } \
269 curr_; \
270})
271
272/** @brief Iterate and call a callback vector
273 * @param vec_ the callback vector
274 * @param varargs additional callback parameters
275 */
276#define clib_callback_data_call_vec(vec_, ...) \
277do { \
278 u32 sz_ = vec_len (vec_); \
279 u32 i_; \
280 for (i_ = 0; i_ < sz_; i_++) \
281 { \
282 CLIB_PREFETCH (&vec_[i_+1], CLIB_CACHE_LINE_BYTES, STORE); \
283 (vec_[i_].fp) (&vec_[i_], __VA_ARGS__); \
284 } \
285} while (0)
286
287/** @brief Call the specified callback set
288 * @param set_ the callback set
289 * @param varargs additional callback parameters
290 */
291#define clib_callback_data_call(set_, ...) \
292do { \
293 typeof ((set_)->curr) v_ = clib_callback_data_check_and_get(set_); \
294 clib_callback_data_iterate (v_, __VA_ARGS__); \
295} while (0)
296
297/** @brief prefetch the callback set
298 * @param set_ The callback set
299 */
300#define clib_callback_data_prefetch(set_) \
301do { \
302 if (PREDICT_FALSE ((set_)->curr)) \
303 CLIB_PREFETCH ((set_)->curr, CLIB_CACHE_LINE_BYTES, STORE); \
304} while (0)
305
306
307#endif /* included_callback_data_h */
308
309/*
310 * fd.io coding-style-patch-verification: ON
311 *
312 * Local Variables:
313 * eval: (c-set-style "gnu")
314 * End:
315 */