FD.io VPP  v21.01.1
Vector Packet Processing
callback_data.h
Go to the documentation of this file.
1 /*
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_) \
29 typedef 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_) \
42 do { \
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_) \
58 do { \
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_) \
157 do { \
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_) \
190 do { \
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_, ...) \
277 do { \
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_, ...) \
292 do { \
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_) \
301 do { \
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  */