blob: d8bd8c74e369cfb124ab96fc63c2ad34e14df524 [file] [log] [blame]
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -08001/*
2 * Copyright (c) 2019 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this
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#include <vppinfra/fifo.h>
17#include <vppinfra/pool.h>
18#include <vppinfra/hash.h>
19#include <vnet/api_errno.h>
20
21#include <vcl/vcl_event.h>
22/**
23 * @file
24 * @brief VPP Communications Library (VCL) event handler.
25 *
26 * Definitions for generic event handling in VCL.
27 */
28
29int
30vce_generate_event (vce_event_thread_t *evt, u32 ev_idx)
31{
32 int elts, rv = 0;
33 vce_event_t *p;
34
35 pthread_mutex_lock (&(evt->generator_lock));
36
37 /* Check there is event data for this event */
38
39 clib_spinlock_lock (&(evt->events_lockp));
40 p = pool_elt_at_index (evt->vce_events, ev_idx);
41 ASSERT(p);
42
43 elts = (int) clib_fifo_free_elts (evt->event_index_fifo);
44 if (PREDICT_TRUE (elts))
45 {
46 /* Add event to queue */
47 clib_fifo_add1 (evt->event_index_fifo, ev_idx);
48 pthread_cond_signal (&(evt->generator_cond));
49 }
50 else
51 {
52 rv = VNET_API_ERROR_QUEUE_FULL;
53 }
54
55 clib_spinlock_unlock (&(evt->events_lockp));
56 pthread_mutex_unlock (&(evt->generator_lock));
57
58 return rv;
59}
60
61void
62vce_clear_event (vce_event_thread_t *evt, vce_event_t *ev)
63{
64 clib_spinlock_lock (&(evt->events_lockp));
65 pool_put (evt->vce_events, ev);
66 clib_spinlock_unlock (&(evt->events_lockp));
67}
68
69vce_event_t *
70vce_get_event_from_index(vce_event_thread_t *evt, u32 ev_idx)
71{
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -080072 vce_event_t *ev = 0;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -080073
74 clib_spinlock_lock (&(evt->events_lockp));
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -080075 if ( ! pool_is_free_index (evt->vce_events, ev_idx))
76 ev = pool_elt_at_index (evt->vce_events, ev_idx);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -080077 clib_spinlock_unlock (&(evt->events_lockp));
78
79 return ev;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -080080}
81
82vce_event_handler_reg_t *
Keith Burns (alagalah)7cf80e02018-03-08 16:46:25 -080083vce_get_event_handler (vce_event_thread_t *evt, vce_event_key_t *evk)
84{
85 vce_event_handler_reg_t *handler = 0;
86 uword *p;
87
88 clib_spinlock_lock (&evt->handlers_lockp);
89 p = hash_get (evt->handlers_index_by_event_key, evk->as_u64);
90 if (p)
91 handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
92 clib_spinlock_unlock (&evt->handlers_lockp);
93
94 return handler;
95}
96
97vce_event_handler_reg_t *
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -080098vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk,
Keith Burns (alagalah)0d2b0d52018-03-06 15:55:22 -080099 vce_event_callback_t cb, void *cb_args)
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800100{
101 vce_event_handler_reg_t *handler;
102 vce_event_handler_reg_t *old_handler = 0;
103 uword *p;
104 u32 handler_index;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800105
106 /* TODO - multiple handler support. For now we can replace
107 * and re-instate, which is useful for event recycling */
108
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800109 clib_spinlock_lock (&evt->handlers_lockp);
110
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800111 p = hash_get (evt->handlers_index_by_event_key, evk->as_u64);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800112 if (p)
113 {
114 old_handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
115 /* If we are just re-registering, ignore and move on
116 * else store the old handler_fn for unregister to re-instate */
117 if (old_handler->handler_fn == cb)
118 {
119
120 clib_spinlock_unlock (&evt->handlers_lockp);
121
122 /* Signal event thread that a handler exists in case any
123 * recycled events requiring this handler are pending */
124 pthread_mutex_lock (&(evt->generator_lock));
125 pthread_cond_signal (&(evt->generator_cond));
126 pthread_mutex_unlock (&(evt->generator_lock));
127 return old_handler;
128 }
129 }
130
131 pool_get (evt->vce_event_handlers, handler);
132 handler_index = (u32) (handler - evt->vce_event_handlers);
133
134 handler->handler_fn = cb;
135 handler->replaced_handler_idx = (p) ? p[0] : ~0;
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800136 handler->ev_idx = ~0; //This will be set by the event thread if event happens
137 handler->evk = evk->as_u64;
Keith Burns (alagalah)0d2b0d52018-03-06 15:55:22 -0800138 handler->handler_fn_args = cb_args;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800139
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800140 hash_set (evt->handlers_index_by_event_key, evk->as_u64, handler_index);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800141
142 pthread_cond_init (&(handler->handler_cond), NULL);
143 pthread_mutex_init (&(handler->handler_lock), NULL);
144
145 clib_spinlock_unlock (&evt->handlers_lockp);
146
147 /* Signal event thread that a new handler exists in case any
148 * recycled events requiring this handler are pending */
149 pthread_mutex_lock (&(evt->generator_lock));
150 pthread_cond_signal (&(evt->generator_cond));
151 pthread_mutex_unlock (&(evt->generator_lock));
152
153 return handler;
154}
155
156int
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800157vce_unregister_handler (vce_event_thread_t *evt,
158 vce_event_handler_reg_t *handler)
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800159{
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800160 uword *p;
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800161 u64 evk = handler->evk;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800162 u8 generate_signal = 0;
163
164 clib_spinlock_lock (&evt->handlers_lockp);
165
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800166 p = hash_get (evt->handlers_index_by_event_key, evk);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800167 if (!p)
168 {
169 clib_spinlock_unlock (&evt->handlers_lockp);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800170 return VNET_API_ERROR_NO_SUCH_ENTRY;
171 }
172
173 handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
174
175 /* If this handler replaced another handler, re-instate it */
176 if (handler->replaced_handler_idx != ~0)
177 {
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800178 hash_set (evt->handlers_index_by_event_key, evk,
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800179 handler->replaced_handler_idx);
180 generate_signal = 1;
181 }
182 else
183 {
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800184 hash_unset (evt->handlers_index_by_event_key, evk);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800185 }
186
187 pthread_mutex_destroy (&(handler->handler_lock));
188 pthread_cond_destroy (&(handler->handler_cond));
189 pool_put (evt->vce_event_handlers, handler);
190
191 clib_spinlock_unlock (&evt->handlers_lockp);
192
193 if (generate_signal)
194 {
195 /* Signal event thread that a new handler exists in case any
196 * recycled events requiring this handler are pending */
197 pthread_mutex_lock (&(evt->generator_lock));
198 pthread_cond_signal (&(evt->generator_cond));
199 pthread_mutex_unlock (&(evt->generator_lock));
200 }
201
202 return 0;
203}
204
205void *
206vce_event_thread_fn (void *arg)
207{
208 vce_event_thread_t *evt = (vce_event_thread_t *) arg;
209 vce_event_t *ev;
210 u32 ev_idx;
211 vce_event_handler_reg_t *handler;
212 uword *p;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800213
Dave Wallace49c13c72018-03-13 19:42:02 -0400214 pthread_mutex_lock (&(evt->generator_lock));
215 clib_spinlock_lock (&(evt->events_lockp));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800216 evt->recycle_event = 1; // Used for recycling events with no handlers
Dave Wallace49c13c72018-03-13 19:42:02 -0400217 clib_spinlock_unlock (&(evt->events_lockp));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800218
219 do
220 {
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800221 while ( (clib_fifo_elts (evt->event_index_fifo) == 0) ||
222 evt->recycle_event)
223 {
Dave Wallace49c13c72018-03-13 19:42:02 -0400224 clib_spinlock_lock (&(evt->events_lockp));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800225 evt->recycle_event = 0;
Dave Wallace49c13c72018-03-13 19:42:02 -0400226 clib_spinlock_unlock (&(evt->events_lockp));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800227 pthread_cond_wait (&(evt->generator_cond),
228 &(evt->generator_lock));
229 }
230
231 /* Remove event */
232 clib_spinlock_lock (&(evt->events_lockp));
233
234 clib_fifo_sub1 (evt->event_index_fifo, ev_idx);
235 ev = pool_elt_at_index (evt->vce_events, ev_idx);
236
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800237 ASSERT(ev);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800238
239 clib_spinlock_lock (&evt->handlers_lockp);
240
Keith Burns (alagalah)00f44cc2018-03-07 09:26:38 -0800241 p = hash_get (evt->handlers_index_by_event_key, ev->evk.as_u64);
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800242 if (!p)
243 {
244 /* If an event falls in the woods, and there is no handler to hear it,
245 * does it make any sound?
246 * I don't know either, so lets try recycling the event */
247 clib_fifo_add1 (evt->event_index_fifo, ev_idx);
248 evt->recycle_event = 1;
Dave Wallace49c13c72018-03-13 19:42:02 -0400249 clib_spinlock_unlock (&(evt->events_lockp));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800250 clib_spinlock_unlock (&evt->handlers_lockp);
Dave Wallace49c13c72018-03-13 19:42:02 -0400251 pthread_mutex_unlock (&(evt->generator_lock));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800252 }
Dave Wallace49c13c72018-03-13 19:42:02 -0400253 else
254 {
255 handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
256 handler->ev_idx = ev_idx;
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800257
Dave Wallace49c13c72018-03-13 19:42:02 -0400258 clib_spinlock_unlock (&(evt->events_lockp));
259 clib_spinlock_unlock (&evt->handlers_lockp);
260 pthread_mutex_unlock (&(evt->generator_lock));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800261
Dave Wallace49c13c72018-03-13 19:42:02 -0400262 (handler->handler_fn)(handler);
263 }
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800264
Dave Wallace49c13c72018-03-13 19:42:02 -0400265 pthread_mutex_lock (&(evt->generator_lock));
Keith Burns (alagalah)3cf2d642018-02-23 10:17:01 -0800266 }
267 while (1);
268 return NULL;
269}
270
271int
272vce_start_event_thread (vce_event_thread_t *evt, u8 max_events)
273{
274 clib_fifo_validate (evt->event_index_fifo, max_events);
275 evt->handlers_index_by_event_key = hash_create (0, sizeof (uword));
276
277 pthread_cond_init (&(evt->generator_cond), NULL);
278 pthread_mutex_init (&(evt->generator_lock), NULL);
279
280 clib_spinlock_init (&(evt->events_lockp));
281 clib_spinlock_init (&(evt->handlers_lockp));
282
283 return pthread_create (&(evt->thread), NULL /* attr */ ,
284 vce_event_thread_fn, evt);
Keith Burns (alagalah)0d2b0d52018-03-06 15:55:22 -0800285}