vcl: cleanup children that use _exit()
[vpp.git] / src / vcl / vppcom.c
1 /*
2  * Copyright (c) 2017 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 <stdio.h>
17 #include <stdlib.h>
18 #include <svm/svm_fifo_segment.h>
19 #include <vcl/vppcom.h>
20 #include <vcl/vcl_debug.h>
21 #include <vcl/vcl_private.h>
22
23 __thread uword __vcl_worker_index = ~0;
24
25
26 static int
27 vcl_wait_for_segment (u64 segment_handle)
28 {
29   vcl_worker_t *wrk = vcl_worker_get_current ();
30   u32 wait_for_seconds = 10, segment_index;
31   f64 timeout;
32
33   if (segment_handle == VCL_INVALID_SEGMENT_HANDLE)
34     return 1;
35
36   timeout = clib_time_now (&wrk->clib_time) + wait_for_seconds;
37   while (clib_time_now (&wrk->clib_time) < timeout)
38     {
39       segment_index = vcl_segment_table_lookup (segment_handle);
40       if (segment_index != VCL_INVALID_SEGMENT_INDEX)
41         return 0;
42       usleep (10);
43     }
44   return 1;
45 }
46
47 const char *
48 vppcom_session_state_str (session_state_t state)
49 {
50   char *st;
51
52   switch (state)
53     {
54     case STATE_START:
55       st = "STATE_START";
56       break;
57
58     case STATE_CONNECT:
59       st = "STATE_CONNECT";
60       break;
61
62     case STATE_LISTEN:
63       st = "STATE_LISTEN";
64       break;
65
66     case STATE_ACCEPT:
67       st = "STATE_ACCEPT";
68       break;
69
70     case STATE_CLOSE_ON_EMPTY:
71       st = "STATE_CLOSE_ON_EMPTY";
72       break;
73
74     case STATE_DISCONNECT:
75       st = "STATE_DISCONNECT";
76       break;
77
78     case STATE_FAILED:
79       st = "STATE_FAILED";
80       break;
81
82     default:
83       st = "UNKNOWN_STATE";
84       break;
85     }
86
87   return st;
88 }
89
90 u8 *
91 format_ip4_address (u8 * s, va_list * args)
92 {
93   u8 *a = va_arg (*args, u8 *);
94   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
95 }
96
97 u8 *
98 format_ip6_address (u8 * s, va_list * args)
99 {
100   ip6_address_t *a = va_arg (*args, ip6_address_t *);
101   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
102
103   i_max_n_zero = ARRAY_LEN (a->as_u16);
104   max_n_zeros = 0;
105   i_first_zero = i_max_n_zero;
106   n_zeros = 0;
107   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
108     {
109       u32 is_zero = a->as_u16[i] == 0;
110       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
111         {
112           i_first_zero = i;
113           n_zeros = 0;
114         }
115       n_zeros += is_zero;
116       if ((!is_zero && n_zeros > max_n_zeros)
117           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
118         {
119           i_max_n_zero = i_first_zero;
120           max_n_zeros = n_zeros;
121           i_first_zero = ARRAY_LEN (a->as_u16);
122           n_zeros = 0;
123         }
124     }
125
126   last_double_colon = 0;
127   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
128     {
129       if (i == i_max_n_zero && max_n_zeros > 1)
130         {
131           s = format (s, "::");
132           i += max_n_zeros - 1;
133           last_double_colon = 1;
134         }
135       else
136         {
137           s = format (s, "%s%x",
138                       (last_double_colon || i == 0) ? "" : ":",
139                       clib_net_to_host_u16 (a->as_u16[i]));
140           last_double_colon = 0;
141         }
142     }
143
144   return s;
145 }
146
147 /* Format an IP46 address. */
148 u8 *
149 format_ip46_address (u8 * s, va_list * args)
150 {
151   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
152   ip46_type_t type = va_arg (*args, ip46_type_t);
153   int is_ip4 = 1;
154
155   switch (type)
156     {
157     case IP46_TYPE_ANY:
158       is_ip4 = ip46_address_is_ip4 (ip46);
159       break;
160     case IP46_TYPE_IP4:
161       is_ip4 = 1;
162       break;
163     case IP46_TYPE_IP6:
164       is_ip4 = 0;
165       break;
166     }
167
168   return is_ip4 ?
169     format (s, "%U", format_ip4_address, &ip46->ip4) :
170     format (s, "%U", format_ip6_address, &ip46->ip6);
171 }
172
173 /*
174  * VPPCOM Utility Functions
175  */
176
177
178 static svm_msg_q_t *
179 vcl_session_vpp_evt_q (vcl_worker_t * wrk, vcl_session_t * s)
180 {
181   if (vcl_session_is_ct (s))
182     return wrk->vpp_event_queues[0];
183   else
184     return wrk->vpp_event_queues[s->tx_fifo->master_thread_index];
185 }
186
187 static void
188 vcl_send_session_accepted_reply (svm_msg_q_t * mq, u32 context,
189                                  session_handle_t handle, int retval)
190 {
191   app_session_evt_t _app_evt, *app_evt = &_app_evt;
192   session_accepted_reply_msg_t *rmp;
193   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_ACCEPTED_REPLY);
194   rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
195   rmp->handle = handle;
196   rmp->context = context;
197   rmp->retval = retval;
198   app_send_ctrl_evt_to_vpp (mq, app_evt);
199 }
200
201 static void
202 vcl_send_session_disconnected_reply (svm_msg_q_t * mq, u32 context,
203                                      session_handle_t handle, int retval)
204 {
205   app_session_evt_t _app_evt, *app_evt = &_app_evt;
206   session_disconnected_reply_msg_t *rmp;
207   app_alloc_ctrl_evt_to_vpp (mq, app_evt,
208                              SESSION_CTRL_EVT_DISCONNECTED_REPLY);
209   rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
210   rmp->handle = handle;
211   rmp->context = context;
212   rmp->retval = retval;
213   app_send_ctrl_evt_to_vpp (mq, app_evt);
214 }
215
216 static void
217 vcl_send_session_reset_reply (svm_msg_q_t * mq, u32 context,
218                               session_handle_t handle, int retval)
219 {
220   app_session_evt_t _app_evt, *app_evt = &_app_evt;
221   session_reset_reply_msg_t *rmp;
222   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_RESET_REPLY);
223   rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
224   rmp->handle = handle;
225   rmp->context = context;
226   rmp->retval = retval;
227   app_send_ctrl_evt_to_vpp (mq, app_evt);
228 }
229
230 static u32
231 vcl_session_accepted_handler (vcl_worker_t * wrk, session_accepted_msg_t * mp)
232 {
233   vcl_session_t *session, *listen_session;
234   svm_fifo_t *rx_fifo, *tx_fifo;
235   u32 vpp_wrk_index;
236   svm_msg_q_t *evt_q;
237
238   session = vcl_session_alloc (wrk);
239
240   listen_session = vcl_session_table_lookup_listener (wrk,
241                                                       mp->listener_handle);
242   if (!listen_session)
243     {
244       svm_msg_q_t *evt_q;
245       evt_q = uword_to_pointer (mp->vpp_event_queue_address, svm_msg_q_t *);
246       clib_warning ("VCL<%d>: ERROR: couldn't find listen session: "
247                     "unknown vpp listener handle %llx",
248                     getpid (), mp->listener_handle);
249       vcl_send_session_accepted_reply (evt_q, mp->context, mp->handle,
250                                        VNET_API_ERROR_INVALID_ARGUMENT);
251       vcl_session_free (wrk, session);
252       return VCL_INVALID_SESSION_INDEX;
253     }
254
255   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
256   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
257
258   if (mp->server_event_queue_address)
259     {
260       session->vpp_evt_q = uword_to_pointer (mp->client_event_queue_address,
261                                              svm_msg_q_t *);
262       session->our_evt_q = uword_to_pointer (mp->server_event_queue_address,
263                                              svm_msg_q_t *);
264       if (vcl_wait_for_segment (mp->segment_handle))
265         {
266           clib_warning ("segment for session %u couldn't be mounted!",
267                         session->session_index);
268           return VCL_INVALID_SESSION_INDEX;
269         }
270       rx_fifo->master_session_index = session->session_index;
271       tx_fifo->master_session_index = session->session_index;
272       rx_fifo->master_thread_index = vcl_get_worker_index ();
273       tx_fifo->master_thread_index = vcl_get_worker_index ();
274       vec_validate (wrk->vpp_event_queues, 0);
275       evt_q = uword_to_pointer (mp->vpp_event_queue_address, svm_msg_q_t *);
276       wrk->vpp_event_queues[0] = evt_q;
277     }
278   else
279     {
280       session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
281                                              svm_msg_q_t *);
282       rx_fifo->client_session_index = session->session_index;
283       tx_fifo->client_session_index = session->session_index;
284       rx_fifo->client_thread_index = vcl_get_worker_index ();
285       tx_fifo->client_thread_index = vcl_get_worker_index ();
286       vpp_wrk_index = tx_fifo->master_thread_index;
287       vec_validate (wrk->vpp_event_queues, vpp_wrk_index);
288       wrk->vpp_event_queues[vpp_wrk_index] = session->vpp_evt_q;
289     }
290
291   session->vpp_handle = mp->handle;
292   session->client_context = mp->context;
293   session->rx_fifo = rx_fifo;
294   session->tx_fifo = tx_fifo;
295
296   session->session_state = STATE_ACCEPT;
297   session->transport.rmt_port = mp->port;
298   session->transport.is_ip4 = mp->is_ip4;
299   clib_memcpy_fast (&session->transport.rmt_ip, mp->ip,
300                     sizeof (ip46_address_t));
301
302   vcl_session_table_add_vpp_handle (wrk, mp->handle, session->session_index);
303   session->transport.lcl_port = listen_session->transport.lcl_port;
304   session->transport.lcl_ip = listen_session->transport.lcl_ip;
305   session->session_type = listen_session->session_type;
306   session->is_dgram = session->session_type == VPPCOM_PROTO_UDP;
307
308   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: client accept request from %s"
309         " address %U port %d queue %p!", getpid (), mp->handle,
310         session->session_index,
311         mp->is_ip4 ? "IPv4" : "IPv6", format_ip46_address, &mp->ip,
312         mp->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
313         clib_net_to_host_u16 (mp->port), session->vpp_evt_q);
314   vcl_evt (VCL_EVT_ACCEPT, session, listen_session, session_index);
315
316   return session->session_index;
317 }
318
319 static u32
320 vcl_session_connected_handler (vcl_worker_t * wrk,
321                                session_connected_msg_t * mp)
322 {
323   u32 session_index, vpp_wrk_index;
324   svm_fifo_t *rx_fifo, *tx_fifo;
325   vcl_session_t *session = 0;
326   svm_msg_q_t *evt_q;
327
328   session_index = mp->context;
329   session = vcl_session_get (wrk, session_index);
330   if (!session)
331     {
332       clib_warning ("[%s] ERROR: vpp handle 0x%llx, sid %u: "
333                     "Invalid session index (%u)!",
334                     getpid (), mp->handle, session_index);
335       return VCL_INVALID_SESSION_INDEX;
336     }
337   if (mp->retval)
338     {
339       clib_warning ("VCL<%d>: ERROR: sid %u: connect failed! %U", getpid (),
340                     session_index, format_api_error, ntohl (mp->retval));
341       session->session_state = STATE_FAILED;
342       session->vpp_handle = mp->handle;
343       return session_index;
344     }
345
346   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
347   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
348   if (vcl_wait_for_segment (mp->segment_handle))
349     {
350       clib_warning ("segment for session %u couldn't be mounted!",
351                     session->session_index);
352       return VCL_INVALID_SESSION_INDEX;
353     }
354
355   rx_fifo->client_session_index = session_index;
356   tx_fifo->client_session_index = session_index;
357   rx_fifo->client_thread_index = vcl_get_worker_index ();
358   tx_fifo->client_thread_index = vcl_get_worker_index ();
359
360   if (mp->client_event_queue_address)
361     {
362       session->vpp_evt_q = uword_to_pointer (mp->server_event_queue_address,
363                                              svm_msg_q_t *);
364       session->our_evt_q = uword_to_pointer (mp->client_event_queue_address,
365                                              svm_msg_q_t *);
366
367       vec_validate (wrk->vpp_event_queues, 0);
368       evt_q = uword_to_pointer (mp->vpp_event_queue_address, svm_msg_q_t *);
369       wrk->vpp_event_queues[0] = evt_q;
370     }
371   else
372     {
373       session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
374                                              svm_msg_q_t *);
375       vpp_wrk_index = tx_fifo->master_thread_index;
376       vec_validate (wrk->vpp_event_queues, vpp_wrk_index);
377       wrk->vpp_event_queues[vpp_wrk_index] = session->vpp_evt_q;
378     }
379
380   session->rx_fifo = rx_fifo;
381   session->tx_fifo = tx_fifo;
382   session->vpp_handle = mp->handle;
383   session->transport.is_ip4 = mp->is_ip4;
384   clib_memcpy_fast (&session->transport.lcl_ip, mp->lcl_ip,
385                     sizeof (session->transport.lcl_ip));
386   session->transport.lcl_port = mp->lcl_port;
387   session->session_state = STATE_CONNECT;
388
389   /* Add it to lookup table */
390   hash_set (wrk->session_index_by_vpp_handles, mp->handle, session_index);
391
392   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: connect succeeded! "
393         "session_rx_fifo %p, refcnt %d, session_tx_fifo %p, refcnt %d",
394         getpid (), mp->handle, session_index, session->rx_fifo,
395         session->rx_fifo->refcnt, session->tx_fifo, session->tx_fifo->refcnt);
396
397   return session_index;
398 }
399
400 static u32
401 vcl_session_reset_handler (vcl_worker_t * wrk,
402                            session_reset_msg_t * reset_msg)
403 {
404   vcl_session_t *session;
405   u32 sid;
406
407   sid = vcl_session_index_from_vpp_handle (wrk, reset_msg->handle);
408   session = vcl_session_get (wrk, sid);
409   if (!session)
410     {
411       VDBG (0, "request to reset unknown handle 0x%llx", reset_msg->handle);
412       return VCL_INVALID_SESSION_INDEX;
413     }
414   session->session_state = STATE_CLOSE_ON_EMPTY;
415   VDBG (0, "reset handle 0x%llx, sid %u ", reset_msg->handle, sid);
416   vcl_send_session_reset_reply (vcl_session_vpp_evt_q (wrk, session),
417                                 wrk->my_client_index, reset_msg->handle, 0);
418   return sid;
419 }
420
421 static u32
422 vcl_session_bound_handler (vcl_worker_t * wrk, session_bound_msg_t * mp)
423 {
424   vcl_session_t *session;
425   u32 sid = mp->context;
426
427   session = vcl_session_get (wrk, sid);
428   if (mp->retval)
429     {
430       VERR ("vpp handle 0x%llx, sid %u: bind failed: %U", mp->handle, sid,
431             format_api_error, mp->retval);
432       if (session)
433         {
434           session->session_state = STATE_FAILED;
435           session->vpp_handle = mp->handle;
436           return sid;
437         }
438       else
439         {
440           clib_warning ("[%s] ERROR: vpp handle 0x%llx, sid %u: "
441                         "Invalid session index (%u)!",
442                         getpid (), mp->handle, sid);
443           return VCL_INVALID_SESSION_INDEX;
444         }
445     }
446
447   session->vpp_handle = mp->handle;
448   session->transport.is_ip4 = mp->lcl_is_ip4;
449   clib_memcpy_fast (&session->transport.lcl_ip, mp->lcl_ip,
450                     sizeof (ip46_address_t));
451   session->transport.lcl_port = mp->lcl_port;
452   vcl_session_table_add_listener (wrk, mp->handle, sid);
453   session->session_state = STATE_LISTEN;
454
455   if (session->is_dgram)
456     {
457       svm_fifo_t *rx_fifo, *tx_fifo;
458       session->vpp_evt_q = uword_to_pointer (mp->vpp_evt_q, svm_msg_q_t *);
459       rx_fifo = uword_to_pointer (mp->rx_fifo, svm_fifo_t *);
460       rx_fifo->client_session_index = sid;
461       tx_fifo = uword_to_pointer (mp->tx_fifo, svm_fifo_t *);
462       tx_fifo->client_session_index = sid;
463       session->rx_fifo = rx_fifo;
464       session->tx_fifo = tx_fifo;
465     }
466
467   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: bind succeeded!",
468         getpid (), mp->handle, sid);
469   return sid;
470 }
471
472 static int
473 vcl_handle_mq_event (vcl_worker_t * wrk, session_event_t * e)
474 {
475   session_accepted_msg_t *accepted_msg;
476   session_disconnected_msg_t *disconnected_msg;
477   vcl_session_msg_t *vcl_msg;
478   vcl_session_t *session;
479   u64 handle;
480   u32 sid;
481
482   switch (e->event_type)
483     {
484     case FIFO_EVENT_APP_RX:
485     case FIFO_EVENT_APP_TX:
486     case SESSION_IO_EVT_CT_RX:
487     case SESSION_IO_EVT_CT_TX:
488       vec_add1 (wrk->unhandled_evts_vector, *e);
489       break;
490     case SESSION_CTRL_EVT_ACCEPTED:
491       accepted_msg = (session_accepted_msg_t *) e->data;
492       handle = accepted_msg->listener_handle;
493       session = vcl_session_table_lookup_listener (wrk, handle);
494       if (!session)
495         {
496           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
497                         "listener handle %llx", getpid (), handle);
498           break;
499         }
500
501       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
502       vcl_msg->accepted_msg = *accepted_msg;
503       break;
504     case SESSION_CTRL_EVT_CONNECTED:
505       vcl_session_connected_handler (wrk,
506                                      (session_connected_msg_t *) e->data);
507       break;
508     case SESSION_CTRL_EVT_DISCONNECTED:
509       disconnected_msg = (session_disconnected_msg_t *) e->data;
510       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
511       session = vcl_session_get (wrk, sid);
512       if (!session)
513         {
514           VDBG (0, "request to disconnect unknown handle 0x%llx",
515                 disconnected_msg->handle);
516           break;
517         }
518       session->session_state = STATE_DISCONNECT;
519       VDBG (0, "disconnected handle 0x%llx, sid %u", disconnected_msg->handle,
520             sid);
521       break;
522     case SESSION_CTRL_EVT_RESET:
523       vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
524       break;
525     case SESSION_CTRL_EVT_BOUND:
526       vcl_session_bound_handler (wrk, (session_bound_msg_t *) e->data);
527       break;
528     default:
529       clib_warning ("unhandled %u", e->event_type);
530     }
531   return VPPCOM_OK;
532 }
533
534 static inline int
535 vppcom_wait_for_session_state_change (u32 session_index,
536                                       session_state_t state,
537                                       f64 wait_for_time)
538 {
539   vcl_worker_t *wrk = vcl_worker_get_current ();
540   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
541   vcl_session_t *volatile session;
542   svm_msg_q_msg_t msg;
543   session_event_t *e;
544
545   do
546     {
547       session = vcl_session_get (wrk, session_index);
548       if (PREDICT_FALSE (!session))
549         {
550           return VPPCOM_EBADFD;
551         }
552       if (session->session_state & state)
553         {
554           return VPPCOM_OK;
555         }
556       if (session->session_state & STATE_FAILED)
557         {
558           return VPPCOM_ECONNREFUSED;
559         }
560
561       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0))
562         {
563           usleep (100);
564           continue;
565         }
566       e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
567       vcl_handle_mq_event (wrk, e);
568       svm_msg_q_free_msg (wrk->app_event_queue, &msg);
569     }
570   while (clib_time_now (&wrk->clib_time) < timeout);
571
572   VDBG (0, "VCL<%d>: timeout waiting for state 0x%x (%s)", getpid (), state,
573         vppcom_session_state_str (state));
574   vcl_evt (VCL_EVT_SESSION_TIMEOUT, session, session_state);
575
576   return VPPCOM_ETIMEDOUT;
577 }
578
579 static int
580 vppcom_app_session_enable (void)
581 {
582   int rv;
583
584   if (vcm->app_state != STATE_APP_ENABLED)
585     {
586       vppcom_send_session_enable_disable (1 /* is_enabled == TRUE */ );
587       rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
588       if (PREDICT_FALSE (rv))
589         {
590           VDBG (0, "VCL<%d>: application session enable timed out! "
591                 "returning %d (%s)", getpid (), rv, vppcom_retval_str (rv));
592           return rv;
593         }
594     }
595   return VPPCOM_OK;
596 }
597
598 static int
599 vppcom_app_attach (void)
600 {
601   int rv;
602
603   vppcom_app_send_attach ();
604   rv = vcl_wait_for_app_state_change (STATE_APP_ATTACHED);
605   if (PREDICT_FALSE (rv))
606     {
607       VDBG (0, "VCL<%d>: application attach timed out! returning %d (%s)",
608             getpid (), rv, vppcom_retval_str (rv));
609       return rv;
610     }
611
612   return VPPCOM_OK;
613 }
614
615 static int
616 vppcom_session_unbind (u32 session_handle)
617 {
618   vcl_worker_t *wrk = vcl_worker_get_current ();
619   vcl_session_t *session = 0;
620   u64 vpp_handle;
621
622   session = vcl_session_get_w_handle (wrk, session_handle);
623   if (!session)
624     return VPPCOM_EBADFD;
625
626   vpp_handle = session->vpp_handle;
627   vcl_session_table_del_listener (wrk, vpp_handle);
628   session->vpp_handle = ~0;
629   session->session_state = STATE_DISCONNECT;
630
631   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending unbind msg! new state"
632         " 0x%x (%s)", getpid (), vpp_handle, session_handle, STATE_DISCONNECT,
633         vppcom_session_state_str (STATE_DISCONNECT));
634   vcl_evt (VCL_EVT_UNBIND, session);
635   vppcom_send_unbind_sock (vpp_handle);
636
637   return VPPCOM_OK;
638 }
639
640 static int
641 vppcom_session_disconnect (u32 session_handle)
642 {
643   vcl_worker_t *wrk = vcl_worker_get_current ();
644   svm_msg_q_t *vpp_evt_q;
645   vcl_session_t *session;
646   session_state_t state;
647   u64 vpp_handle;
648
649   session = vcl_session_get_w_handle (wrk, session_handle);
650   if (!session)
651     return VPPCOM_EBADFD;
652
653   vpp_handle = session->vpp_handle;
654   state = session->session_state;
655
656   VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u state 0x%x (%s)", getpid (),
657         vpp_handle, session_handle, state, vppcom_session_state_str (state));
658
659   if (PREDICT_FALSE (state & STATE_LISTEN))
660     {
661       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
662                     "Cannot disconnect a listen socket!",
663                     getpid (), vpp_handle, session_handle);
664       return VPPCOM_EBADFD;
665     }
666
667   if (state & STATE_CLOSE_ON_EMPTY)
668     {
669       vpp_evt_q = vcl_session_vpp_evt_q (wrk, session);
670       vcl_send_session_disconnected_reply (vpp_evt_q, wrk->my_client_index,
671                                            vpp_handle, 0);
672       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending disconnect "
673             "REPLY...", getpid (), vpp_handle, session_handle);
674     }
675   else
676     {
677       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: sending disconnect...",
678             getpid (), vpp_handle, session_handle);
679       vppcom_send_disconnect_session (vpp_handle);
680     }
681
682   return VPPCOM_OK;
683 }
684
685 static void
686 vcl_cleanup_bapi (void)
687 {
688   socket_client_main_t *scm = &socket_client_main;
689   api_main_t *am = &api_main;
690
691   am->my_client_index = ~0;
692   am->my_registration = 0;
693   am->vl_input_queue = 0;
694   am->msg_index_by_name_and_crc = 0;
695   scm->socket_fd = 0;
696
697   vl_client_api_unmap ();
698 }
699
700 static void
701 vcl_cleanup_forked_child (vcl_worker_t * wrk, vcl_worker_t * child_wrk)
702 {
703   vcl_worker_t *sub_child;
704   int tries = 0;
705
706   if (child_wrk->forked_child != ~0)
707     {
708       sub_child = vcl_worker_get_if_valid (child_wrk->forked_child);
709       if (sub_child)
710         {
711           /* Wait a bit, maybe the process is going away */
712           while (kill (sub_child->current_pid, 0) >= 0 && tries++ < 50)
713             usleep (1e3);
714           if (kill (sub_child->current_pid, 0) < 0)
715             vcl_cleanup_forked_child (child_wrk, sub_child);
716         }
717     }
718   vcl_worker_cleanup (child_wrk, 1 /* notify vpp */ );
719   VDBG (0, "Cleaned up wrk %u", child_wrk->wrk_index);
720   wrk->forked_child = ~0;
721 }
722
723 static struct sigaction old_sa;
724
725 static void
726 vcl_intercept_sigchld_handler (int signum, siginfo_t * si, void *uc)
727 {
728   vcl_worker_t *wrk, *child_wrk;
729
730   if (vcl_get_worker_index () == ~0)
731     return;
732
733   sigaction (SIGCHLD, &old_sa, 0);
734
735   wrk = vcl_worker_get_current ();
736   if (wrk->forked_child == ~0)
737     return;
738
739   child_wrk = vcl_worker_get_if_valid (wrk->forked_child);
740   if (si->si_pid != child_wrk->current_pid)
741     {
742       VDBG (0, "unexpected child pid %u", si->si_pid);
743       return;
744     }
745   if (child_wrk)
746     vcl_cleanup_forked_child (wrk, child_wrk);
747
748   if (old_sa.sa_flags & SA_SIGINFO)
749     {
750       void (*fn) (int, siginfo_t *, void *) = old_sa.sa_sigaction;
751       fn (signum, si, uc);
752     }
753   else
754     {
755       void (*fn) (int) = old_sa.sa_handler;
756       if (fn)
757         fn (signum);
758     }
759 }
760
761 static void
762 vcl_incercept_sigchld ()
763 {
764   struct sigaction sa;
765   clib_memset (&sa, 0, sizeof (sa));
766   sa.sa_sigaction = vcl_intercept_sigchld_handler;
767   sa.sa_flags = SA_SIGINFO;
768   if (sigaction (SIGCHLD, &sa, &old_sa))
769     {
770       VERR ("couldn't intercept sigchld");
771       exit (-1);
772     }
773 }
774
775 static void
776 vcl_app_pre_fork (void)
777 {
778   vcl_incercept_sigchld ();
779 }
780
781 static void
782 vcl_app_fork_child_handler (void)
783 {
784   int rv, parent_wrk_index;
785   vcl_worker_t *parent_wrk;
786   u8 *child_name;
787
788   parent_wrk_index = vcl_get_worker_index ();
789   VDBG (0, "initializing forked child with parent wrk %u", parent_wrk_index);
790
791   /*
792    * Allocate worker
793    */
794   vcl_set_worker_index (~0);
795   if (!vcl_worker_alloc_and_init ())
796     VERR ("couldn't allocate new worker");
797
798   /*
799    * Attach to binary api
800    */
801   child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
802   vcl_cleanup_bapi ();
803   vppcom_api_hookup ();
804   vcm->app_state = STATE_APP_START;
805   rv = vppcom_connect_to_vpp ((char *) child_name);
806   vec_free (child_name);
807   if (rv)
808     {
809       VERR ("couldn't connect to VPP!");
810       return;
811     }
812
813   /*
814    * Register worker with vpp and share sessions
815    */
816   vcl_worker_register_with_vpp ();
817   parent_wrk = vcl_worker_get (parent_wrk_index);
818   vcl_worker_share_sessions (parent_wrk);
819   parent_wrk->forked_child = vcl_get_worker_index ();
820
821   VDBG (0, "forked child main worker initialized");
822   vcm->forking = 0;
823 }
824
825 static void
826 vcl_app_fork_parent_handler (void)
827 {
828   vcm->forking = 1;
829   while (vcm->forking)
830     ;
831 }
832
833 /**
834  * Handle app exit
835  *
836  * Notify vpp of the disconnect and mark the worker as free. If we're the
837  * last worker, do a full cleanup otherwise, since we're probably a forked
838  * child, avoid syscalls as much as possible. We might've lost privileges.
839  */
840 void
841 vppcom_app_exit (void)
842 {
843   if (!pool_elts (vcm->workers))
844     return;
845   vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
846   vcl_set_worker_index (~0);
847   vcl_elog_stop (vcm);
848   if (vec_len (vcm->workers) == 1)
849     vl_client_disconnect_from_vlib ();
850   else
851     vl_client_send_disconnect ();
852 }
853
854 /*
855  * VPPCOM Public API functions
856  */
857 int
858 vppcom_app_create (char *app_name)
859 {
860   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
861   int rv;
862
863   if (vcm->is_init)
864     {
865       clib_warning ("vcl<%u> already initialized", getpid ());
866       return -1;
867     }
868
869   vcm->is_init = 1;
870   vppcom_cfg (&vcm->cfg);
871   vcl_cfg = &vcm->cfg;
872
873   vcm->main_cpu = pthread_self ();
874   vcm->main_pid = getpid ();
875   vcm->app_name = format (0, "%s", app_name);
876   vppcom_init_error_string_table ();
877   svm_fifo_segment_main_init (&vcm->segment_main, vcl_cfg->segment_baseva,
878                               20 /* timeout in secs */ );
879   pool_alloc (vcm->workers, vcl_cfg->max_workers);
880   clib_spinlock_init (&vcm->workers_lock);
881   clib_rwlock_init (&vcm->segment_table_lock);
882   pthread_atfork (vcl_app_pre_fork, vcl_app_fork_parent_handler,
883                   vcl_app_fork_child_handler);
884   atexit (vppcom_app_exit);
885
886   /* Allocate default worker */
887   vcl_worker_alloc_and_init ();
888
889   /* API hookup and connect to VPP */
890   vppcom_api_hookup ();
891   vcl_elog_init (vcm);
892   vcm->app_state = STATE_APP_START;
893   rv = vppcom_connect_to_vpp (app_name);
894   if (rv)
895     {
896       VERR ("couldn't connect to VPP!");
897       return rv;
898     }
899   VDBG (0, "sending session enable");
900   rv = vppcom_app_session_enable ();
901   if (rv)
902     {
903       VERR ("vppcom_app_session_enable() failed!");
904       return rv;
905     }
906
907   VDBG (0, "sending app attach");
908   rv = vppcom_app_attach ();
909   if (rv)
910     {
911       VERR ("vppcom_app_attach() failed!");
912       return rv;
913     }
914
915   VDBG (0, "app_name '%s', my_client_index %d (0x%x)", app_name,
916         vcm->workers[0].my_client_index, vcm->workers[0].my_client_index);
917
918   return VPPCOM_OK;
919 }
920
921 void
922 vppcom_app_destroy (void)
923 {
924   int rv;
925   f64 orig_app_timeout;
926
927   if (!pool_elts (vcm->workers))
928     return;
929
930   vcl_evt (VCL_EVT_DETACH, vcm);
931
932   if (pool_elts (vcm->workers) == 1)
933     {
934       vppcom_app_send_detach ();
935       orig_app_timeout = vcm->cfg.app_timeout;
936       vcm->cfg.app_timeout = 2.0;
937       rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
938       vcm->cfg.app_timeout = orig_app_timeout;
939       if (PREDICT_FALSE (rv))
940         VDBG (0, "application detach timed out! returning %d (%s)", rv,
941               vppcom_retval_str (rv));
942       vec_free (vcm->app_name);
943       vcl_worker_cleanup (vcl_worker_get_current (), 0 /* notify vpp */ );
944     }
945   else
946     {
947       vcl_worker_cleanup (vcl_worker_get_current (), 1 /* notify vpp */ );
948     }
949
950   vcl_set_worker_index (~0);
951   vcl_elog_stop (vcm);
952   vl_client_disconnect_from_vlib ();
953 }
954
955 int
956 vppcom_session_create (u8 proto, u8 is_nonblocking)
957 {
958   vcl_worker_t *wrk = vcl_worker_get_current ();
959   vcl_session_t *session;
960
961   session = vcl_session_alloc (wrk);
962
963   session->session_type = proto;
964   session->session_state = STATE_START;
965   session->vpp_handle = ~0;
966   session->is_dgram = proto == VPPCOM_PROTO_UDP;
967
968   if (is_nonblocking)
969     VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
970
971   vcl_evt (VCL_EVT_CREATE, session, session_type, session->session_state,
972            is_nonblocking, session_index);
973
974   VDBG (0, "created sid %u", session->session_index);
975
976   return vcl_session_handle (session);
977 }
978
979 int
980 vppcom_session_close (uint32_t session_handle)
981 {
982   vcl_worker_t *wrk = vcl_worker_get_current ();
983   u8 is_vep, do_disconnect = 1;
984   vcl_session_t *session = 0;
985   session_state_t state;
986   u32 next_sh, vep_sh;
987   int rv = VPPCOM_OK;
988   u64 vpp_handle;
989
990   session = vcl_session_get_w_handle (wrk, session_handle);
991   if (!session)
992     return VPPCOM_EBADFD;
993
994   if (session->shared_index != ~0)
995     do_disconnect = vcl_worker_unshare_session (wrk, session);
996
997   is_vep = session->is_vep;
998   next_sh = session->vep.next_sh;
999   vep_sh = session->vep.vep_sh;
1000   state = session->session_state;
1001   vpp_handle = session->vpp_handle;
1002
1003   VDBG (0, "Closing session handle %u vpp handle %u", session_handle,
1004         vpp_handle);
1005
1006   if (is_vep)
1007     {
1008       while (next_sh != ~0)
1009         {
1010           rv = vppcom_epoll_ctl (session_handle, EPOLL_CTL_DEL, next_sh, 0);
1011           if (PREDICT_FALSE (rv < 0))
1012             VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u"
1013                   " failed! rv %d (%s)", vpp_handle, next_sh, vep_sh, rv,
1014                   vppcom_retval_str (rv));
1015
1016           next_sh = session->vep.next_sh;
1017         }
1018     }
1019   else
1020     {
1021       if (session->is_vep_session)
1022         {
1023           rv = vppcom_epoll_ctl (vep_sh, EPOLL_CTL_DEL, session_handle, 0);
1024           if (rv < 0)
1025             VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u "
1026                   "failed! rv %d (%s)", vpp_handle, session_handle, vep_sh,
1027                   rv, vppcom_retval_str (rv));
1028         }
1029
1030       if (!do_disconnect)
1031         goto cleanup;
1032
1033       if (state & STATE_LISTEN)
1034         {
1035           rv = vppcom_session_unbind (session_handle);
1036           if (PREDICT_FALSE (rv < 0))
1037             VDBG (0, "vpp handle 0x%llx, sid %u: listener unbind failed! "
1038                   "rv %d (%s)", vpp_handle, session_handle, rv,
1039                   vppcom_retval_str (rv));
1040         }
1041       else if (state & STATE_OPEN)
1042         {
1043           rv = vppcom_session_disconnect (session_handle);
1044           if (PREDICT_FALSE (rv < 0))
1045             clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1046                           "session disconnect failed! rv %d (%s)",
1047                           getpid (), vpp_handle, session_handle,
1048                           rv, vppcom_retval_str (rv));
1049         }
1050     }
1051
1052 cleanup:
1053
1054   if (vcl_session_is_ct (session))
1055     {
1056       vcl_cut_through_registration_t *ctr;
1057       uword mq_addr;
1058
1059       mq_addr = pointer_to_uword (session->our_evt_q);
1060       ctr = vcl_ct_registration_lock_and_lookup (wrk, mq_addr);
1061       ASSERT (ctr);
1062       if (ctr->epoll_evt_conn_index != ~0)
1063         vcl_mq_epoll_del_evfd (wrk, ctr->epoll_evt_conn_index);
1064       VDBG (0, "Removing ct registration %u",
1065             vcl_ct_registration_index (wrk, ctr));
1066       vcl_ct_registration_del (wrk, ctr);
1067       vcl_ct_registration_lookup_del (wrk, mq_addr);
1068       vcl_ct_registration_unlock (wrk);
1069     }
1070
1071   if (vpp_handle != ~0)
1072     {
1073       vcl_session_table_del_vpp_handle (wrk, vpp_handle);
1074     }
1075   vcl_session_free (wrk, session);
1076
1077   VDBG (0, "session handle %u vpp handle %u removed", session_handle,
1078         vpp_handle);
1079
1080   vcl_evt (VCL_EVT_CLOSE, session, rv);
1081
1082   return rv;
1083 }
1084
1085 int
1086 vppcom_session_bind (uint32_t session_handle, vppcom_endpt_t * ep)
1087 {
1088   vcl_worker_t *wrk = vcl_worker_get_current ();
1089   vcl_session_t *session = 0;
1090
1091   if (!ep || !ep->ip)
1092     return VPPCOM_EINVAL;
1093
1094   session = vcl_session_get_w_handle (wrk, session_handle);
1095   if (!session)
1096     return VPPCOM_EBADFD;
1097
1098   if (session->is_vep)
1099     {
1100       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
1101                     "bind to an epoll session!", getpid (), session_handle);
1102       return VPPCOM_EBADFD;
1103     }
1104
1105   session->transport.is_ip4 = ep->is_ip4;
1106   if (ep->is_ip4)
1107     clib_memcpy_fast (&session->transport.lcl_ip.ip4, ep->ip,
1108                       sizeof (ip4_address_t));
1109   else
1110     clib_memcpy_fast (&session->transport.lcl_ip.ip6, ep->ip,
1111                       sizeof (ip6_address_t));
1112   session->transport.lcl_port = ep->port;
1113
1114   VDBG (0, "VCL<%d>: sid %u: binding to local %s address %U port %u, "
1115         "proto %s", getpid (), session_handle,
1116         session->transport.is_ip4 ? "IPv4" : "IPv6",
1117         format_ip46_address, &session->transport.lcl_ip,
1118         session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1119         clib_net_to_host_u16 (session->transport.lcl_port),
1120         session->session_type ? "UDP" : "TCP");
1121   vcl_evt (VCL_EVT_BIND, session);
1122
1123   if (session->session_type == VPPCOM_PROTO_UDP)
1124     vppcom_session_listen (session_handle, 10);
1125
1126   return VPPCOM_OK;
1127 }
1128
1129 int
1130 vppcom_session_listen (uint32_t listen_sh, uint32_t q_len)
1131 {
1132   vcl_worker_t *wrk = vcl_worker_get_current ();
1133   vcl_session_t *listen_session = 0;
1134   u64 listen_vpp_handle;
1135   int rv;
1136
1137   listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1138   if (!listen_session)
1139     return VPPCOM_EBADFD;
1140
1141   if (q_len == 0 || q_len == ~0)
1142     q_len = vcm->cfg.listen_queue_size;
1143
1144   if (listen_session->is_vep)
1145     {
1146       clib_warning ("VCL<%d>: ERROR: sid %u: cannot listen on an "
1147                     "epoll session!", getpid (), listen_sh);
1148       return VPPCOM_EBADFD;
1149     }
1150
1151   listen_vpp_handle = listen_session->vpp_handle;
1152   if (listen_session->session_state & STATE_LISTEN)
1153     {
1154       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: already in listen state!",
1155             getpid (), listen_vpp_handle, listen_sh);
1156       return VPPCOM_OK;
1157     }
1158
1159   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: sending VPP bind+listen "
1160         "request...", getpid (), listen_vpp_handle, listen_sh);
1161
1162   /*
1163    * Send listen request to vpp and wait for reply
1164    */
1165   vppcom_send_bind_sock (listen_session);
1166   rv = vppcom_wait_for_session_state_change (listen_session->session_index,
1167                                              STATE_LISTEN,
1168                                              vcm->cfg.session_timeout);
1169
1170   if (PREDICT_FALSE (rv))
1171     {
1172       listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1173       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: bind+listen failed! "
1174             "returning %d (%s)", getpid (), listen_session->vpp_handle,
1175             listen_sh, rv, vppcom_retval_str (rv));
1176       return rv;
1177     }
1178
1179   return VPPCOM_OK;
1180 }
1181
1182 static int
1183 validate_args_session_accept_ (vcl_worker_t * wrk,
1184                                vcl_session_t * listen_session)
1185 {
1186   /* Input validation - expects spinlock on sessions_lockp */
1187   if (listen_session->is_vep)
1188     {
1189       clib_warning ("VCL<%d>: ERROR: sid %u: cannot accept on an "
1190                     "epoll session!", getpid (),
1191                     listen_session->session_index);
1192       return VPPCOM_EBADFD;
1193     }
1194
1195   if (listen_session->session_state != STATE_LISTEN)
1196     {
1197       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1198                     "not in listen state! state 0x%x (%s)", getpid (),
1199                     listen_session->vpp_handle, listen_session->session_index,
1200                     listen_session->session_state,
1201                     vppcom_session_state_str (listen_session->session_state));
1202       return VPPCOM_EBADFD;
1203     }
1204   return VPPCOM_OK;
1205 }
1206
1207 int
1208 vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
1209                        uint32_t flags)
1210 {
1211   u32 client_session_index = ~0, listen_session_index;
1212   vcl_worker_t *wrk = vcl_worker_get_current ();
1213   session_accepted_msg_t accepted_msg;
1214   vcl_session_t *listen_session = 0;
1215   vcl_session_t *client_session = 0;
1216   svm_msg_q_t *vpp_evt_q;
1217   vcl_session_msg_t *evt;
1218   u64 listen_vpp_handle;
1219   svm_msg_q_msg_t msg;
1220   session_event_t *e;
1221   u8 is_nonblocking;
1222   int rv;
1223
1224   listen_session = vcl_session_get_w_handle (wrk, listen_session_handle);
1225   if (!listen_session)
1226     return VPPCOM_EBADFD;
1227
1228   listen_session_index = listen_session->session_index;
1229   if ((rv = validate_args_session_accept_ (wrk, listen_session)))
1230     return rv;
1231
1232   if (clib_fifo_elts (listen_session->accept_evts_fifo))
1233     {
1234       clib_fifo_sub2 (listen_session->accept_evts_fifo, evt);
1235       accepted_msg = evt->accepted_msg;
1236       goto handle;
1237     }
1238
1239   is_nonblocking = VCL_SESS_ATTR_TEST (listen_session->attr,
1240                                        VCL_SESS_ATTR_NONBLOCK);
1241   if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
1242     return VPPCOM_EAGAIN;
1243
1244   while (1)
1245     {
1246       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_WAIT, 0))
1247         return VPPCOM_EAGAIN;
1248
1249       e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
1250       if (e->event_type != SESSION_CTRL_EVT_ACCEPTED)
1251         {
1252           clib_warning ("discarded event: %u", e->event_type);
1253           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1254           continue;
1255         }
1256       clib_memcpy_fast (&accepted_msg, e->data, sizeof (accepted_msg));
1257       svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1258       break;
1259     }
1260
1261 handle:
1262
1263   client_session_index = vcl_session_accepted_handler (wrk, &accepted_msg);
1264   listen_session = vcl_session_get (wrk, listen_session_index);
1265   client_session = vcl_session_get (wrk, client_session_index);
1266
1267   if (flags & O_NONBLOCK)
1268     VCL_SESS_ATTR_SET (client_session->attr, VCL_SESS_ATTR_NONBLOCK);
1269
1270   listen_vpp_handle = listen_session->vpp_handle;
1271   VDBG (0, "vpp handle 0x%llx, sid %u: Got a client request! "
1272         "vpp handle 0x%llx, sid %u, flags %d, is_nonblocking %u",
1273         listen_vpp_handle, listen_session_handle,
1274         client_session->vpp_handle, client_session_index,
1275         flags, VCL_SESS_ATTR_TEST (client_session->attr,
1276                                    VCL_SESS_ATTR_NONBLOCK));
1277
1278   if (ep)
1279     {
1280       ep->is_ip4 = client_session->transport.is_ip4;
1281       ep->port = client_session->transport.rmt_port;
1282       if (client_session->transport.is_ip4)
1283         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip4,
1284                           sizeof (ip4_address_t));
1285       else
1286         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip6,
1287                           sizeof (ip6_address_t));
1288     }
1289
1290   if (accepted_msg.server_event_queue_address)
1291     vpp_evt_q = uword_to_pointer (accepted_msg.vpp_event_queue_address,
1292                                   svm_msg_q_t *);
1293   else
1294     vpp_evt_q = client_session->vpp_evt_q;
1295
1296   vcl_send_session_accepted_reply (vpp_evt_q, client_session->client_context,
1297                                    client_session->vpp_handle, 0);
1298
1299   VDBG (0, "vpp handle 0x%llx, sid %u: accepted vpp handle 0x%llx, "
1300         "sid %u connection from peer %s address %U port %u to local %s "
1301         "address %U port %u", listen_vpp_handle,
1302         listen_session_handle, client_session->vpp_handle,
1303         client_session_index,
1304         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
1305         format_ip46_address, &client_session->transport.rmt_ip,
1306         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1307         clib_net_to_host_u16 (client_session->transport.rmt_port),
1308         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
1309         format_ip46_address, &client_session->transport.lcl_ip,
1310         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1311         clib_net_to_host_u16 (client_session->transport.lcl_port));
1312   vcl_evt (VCL_EVT_ACCEPT, client_session, listen_session,
1313            client_session_index);
1314
1315   return vcl_session_handle (client_session);
1316 }
1317
1318 int
1319 vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
1320 {
1321   vcl_worker_t *wrk = vcl_worker_get_current ();
1322   vcl_session_t *session = 0;
1323   u32 session_index;
1324   int rv;
1325
1326   session = vcl_session_get_w_handle (wrk, session_handle);
1327   if (!session)
1328     return VPPCOM_EBADFD;
1329   session_index = session->session_index;
1330
1331   if (PREDICT_FALSE (session->is_vep))
1332     {
1333       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
1334                     "connect on an epoll session!", getpid (),
1335                     session_handle);
1336       return VPPCOM_EBADFD;
1337     }
1338
1339   if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
1340     {
1341       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: session already "
1342             "connected to %s %U port %d proto %s, state 0x%x (%s)",
1343             getpid (), session->vpp_handle, session_handle,
1344             session->transport.is_ip4 ? "IPv4" : "IPv6",
1345             format_ip46_address,
1346             &session->transport.rmt_ip, session->transport.is_ip4 ?
1347             IP46_TYPE_IP4 : IP46_TYPE_IP6,
1348             clib_net_to_host_u16 (session->transport.rmt_port),
1349             session->session_type ? "UDP" : "TCP", session->session_state,
1350             vppcom_session_state_str (session->session_state));
1351       return VPPCOM_OK;
1352     }
1353
1354   session->transport.is_ip4 = server_ep->is_ip4;
1355   if (session->transport.is_ip4)
1356     clib_memcpy_fast (&session->transport.rmt_ip.ip4, server_ep->ip,
1357                       sizeof (ip4_address_t));
1358   else
1359     clib_memcpy_fast (&session->transport.rmt_ip.ip6, server_ep->ip,
1360                       sizeof (ip6_address_t));
1361   session->transport.rmt_port = server_ep->port;
1362
1363   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connecting to server %s %U "
1364         "port %d proto %s",
1365         getpid (), session->vpp_handle, session_handle,
1366         session->transport.is_ip4 ? "IPv4" : "IPv6",
1367         format_ip46_address,
1368         &session->transport.rmt_ip, session->transport.is_ip4 ?
1369         IP46_TYPE_IP4 : IP46_TYPE_IP6,
1370         clib_net_to_host_u16 (session->transport.rmt_port),
1371         session->session_type ? "UDP" : "TCP");
1372
1373   /*
1374    * Send connect request and wait for reply from vpp
1375    */
1376   vppcom_send_connect_sock (session);
1377   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
1378                                              vcm->cfg.session_timeout);
1379
1380   session = vcl_session_get (wrk, session_index);
1381
1382   if (PREDICT_FALSE (rv))
1383     {
1384       if (VPPCOM_DEBUG > 0)
1385         {
1386           if (session)
1387             clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: connect "
1388                           "failed! returning %d (%s)", getpid (),
1389                           session->vpp_handle, session_handle, rv,
1390                           vppcom_retval_str (rv));
1391           else
1392             clib_warning ("VCL<%d>: no session for sid %u: connect failed! "
1393                           "returning %d (%s)", getpid (),
1394                           session_handle, rv, vppcom_retval_str (rv));
1395         }
1396     }
1397   else
1398     VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connected!",
1399           getpid (), session->vpp_handle, session_handle);
1400
1401   return rv;
1402 }
1403
1404 static u8
1405 vcl_is_rx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1406 {
1407   if (!is_ct)
1408     return (e->event_type == FIFO_EVENT_APP_RX
1409             && e->fifo->client_session_index == sid);
1410   else
1411     return (e->event_type == SESSION_IO_EVT_CT_TX);
1412 }
1413
1414 static inline u8
1415 vcl_session_is_readable (vcl_session_t * s)
1416 {
1417   return ((s->session_state & STATE_OPEN)
1418           || (s->session_state == STATE_LISTEN
1419               && s->session_type == VPPCOM_PROTO_UDP));
1420 }
1421
1422 static inline int
1423 vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
1424                               u8 peek)
1425 {
1426   vcl_worker_t *wrk = vcl_worker_get_current ();
1427   int n_read = 0, rv, is_nonblocking;
1428   vcl_session_t *s = 0;
1429   svm_fifo_t *rx_fifo;
1430   svm_msg_q_msg_t msg;
1431   session_event_t *e;
1432   svm_msg_q_t *mq;
1433   u8 is_ct;
1434
1435   if (PREDICT_FALSE (!buf))
1436     return VPPCOM_EINVAL;
1437
1438   s = vcl_session_get_w_handle (wrk, session_handle);
1439   if (PREDICT_FALSE (!s || s->is_vep))
1440     return VPPCOM_EBADFD;
1441
1442   if (PREDICT_FALSE (!vcl_session_is_readable (s)))
1443     {
1444       session_state_t state = s->session_state;
1445       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1446
1447       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: %s session is not open! "
1448             "state 0x%x (%s), returning %d (%s)",
1449             getpid (), s->vpp_handle, session_handle, state,
1450             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1451       return rv;
1452     }
1453
1454   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1455   is_ct = vcl_session_is_ct (s);
1456   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1457   rx_fifo = s->rx_fifo;
1458   s->has_rx_evt = 0;
1459
1460   if (svm_fifo_is_empty (rx_fifo))
1461     {
1462       if (is_nonblocking)
1463         {
1464           svm_fifo_unset_event (rx_fifo);
1465           return VPPCOM_EWOULDBLOCK;
1466         }
1467       while (svm_fifo_is_empty (rx_fifo))
1468         {
1469           svm_fifo_unset_event (rx_fifo);
1470           svm_msg_q_lock (mq);
1471           if (svm_msg_q_is_empty (mq))
1472             svm_msg_q_wait (mq);
1473
1474           svm_msg_q_sub_w_lock (mq, &msg);
1475           e = svm_msg_q_msg_data (mq, &msg);
1476           svm_msg_q_unlock (mq);
1477           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1478             {
1479               vcl_handle_mq_event (wrk, e);
1480               svm_msg_q_free_msg (mq, &msg);
1481               continue;
1482             }
1483           svm_msg_q_free_msg (mq, &msg);
1484
1485           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1486             return 0;
1487         }
1488     }
1489
1490   if (s->is_dgram)
1491     n_read = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek);
1492   else
1493     n_read = app_recv_stream_raw (rx_fifo, buf, n, 0, peek);
1494
1495   if (svm_fifo_is_empty (rx_fifo))
1496     svm_fifo_unset_event (rx_fifo);
1497
1498   if (is_ct && svm_fifo_want_tx_evt (rx_fifo))
1499     {
1500       svm_fifo_set_want_tx_evt (s->rx_fifo, 0);
1501       app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo, SESSION_IO_EVT_CT_RX,
1502                               SVM_Q_WAIT);
1503     }
1504
1505   VDBG (2, "VCL<%d>: vpp handle 0x%llx, sid %u: read %d bytes from (%p)",
1506         getpid (), s->vpp_handle, session_handle, n_read, rx_fifo);
1507
1508   return n_read;
1509 }
1510
1511 int
1512 vppcom_session_read (uint32_t session_handle, void *buf, size_t n)
1513 {
1514   return (vppcom_session_read_internal (session_handle, buf, n, 0));
1515 }
1516
1517 static int
1518 vppcom_session_peek (uint32_t session_handle, void *buf, int n)
1519 {
1520   return (vppcom_session_read_internal (session_handle, buf, n, 1));
1521 }
1522
1523 int
1524 vppcom_session_read_segments (uint32_t session_handle,
1525                               vppcom_data_segments_t ds)
1526 {
1527   vcl_worker_t *wrk = vcl_worker_get_current ();
1528   int n_read = 0, rv, is_nonblocking;
1529   vcl_session_t *s = 0;
1530   svm_fifo_t *rx_fifo;
1531   svm_msg_q_msg_t msg;
1532   session_event_t *e;
1533   svm_msg_q_t *mq;
1534   u8 is_ct;
1535
1536   s = vcl_session_get_w_handle (wrk, session_handle);
1537   if (PREDICT_FALSE (!s || s->is_vep))
1538     return VPPCOM_EBADFD;
1539
1540   if (PREDICT_FALSE (!vcl_session_is_readable (s)))
1541     {
1542       session_state_t state = s->session_state;
1543       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1544       return rv;
1545     }
1546
1547   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1548   is_ct = vcl_session_is_ct (s);
1549   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1550   rx_fifo = s->rx_fifo;
1551   s->has_rx_evt = 0;
1552
1553   if (svm_fifo_is_empty (rx_fifo))
1554     {
1555       if (is_nonblocking)
1556         {
1557           svm_fifo_unset_event (rx_fifo);
1558           return VPPCOM_EWOULDBLOCK;
1559         }
1560       while (svm_fifo_is_empty (rx_fifo))
1561         {
1562           svm_fifo_unset_event (rx_fifo);
1563           svm_msg_q_lock (mq);
1564           if (svm_msg_q_is_empty (mq))
1565             svm_msg_q_wait (mq);
1566
1567           svm_msg_q_sub_w_lock (mq, &msg);
1568           e = svm_msg_q_msg_data (mq, &msg);
1569           svm_msg_q_unlock (mq);
1570           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1571             {
1572               vcl_handle_mq_event (wrk, e);
1573               svm_msg_q_free_msg (mq, &msg);
1574               continue;
1575             }
1576           svm_msg_q_free_msg (mq, &msg);
1577
1578           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1579             return 0;
1580         }
1581     }
1582
1583   n_read = svm_fifo_segments (rx_fifo, (svm_fifo_segment_t *) ds);
1584   svm_fifo_unset_event (rx_fifo);
1585
1586   if (is_ct && n_read + svm_fifo_max_dequeue (rx_fifo) == rx_fifo->nitems)
1587     {
1588       /* If the peer is not polling send notification */
1589       if (!svm_fifo_has_event (s->rx_fifo))
1590         app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo,
1591                                 SESSION_IO_EVT_CT_RX, SVM_Q_WAIT);
1592     }
1593
1594   return n_read;
1595 }
1596
1597 void
1598 vppcom_session_free_segments (uint32_t session_handle,
1599                               vppcom_data_segments_t ds)
1600 {
1601   vcl_worker_t *wrk = vcl_worker_get_current ();
1602   vcl_session_t *s;
1603
1604   s = vcl_session_get_w_handle (wrk, session_handle);
1605   if (PREDICT_FALSE (!s || s->is_vep))
1606     return;
1607
1608   svm_fifo_segments_free (s->rx_fifo, (svm_fifo_segment_t *) ds);
1609 }
1610
1611 static inline int
1612 vppcom_session_read_ready (vcl_session_t * session)
1613 {
1614   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1615   if (PREDICT_FALSE (session->is_vep))
1616     {
1617       clib_warning ("VCL<%d>: ERROR: sid %u: cannot read from an "
1618                     "epoll session!", getpid (), session->session_index);
1619       return VPPCOM_EBADFD;
1620     }
1621
1622   if (PREDICT_FALSE (!(session->session_state & (STATE_OPEN | STATE_LISTEN))))
1623     {
1624       session_state_t state = session->session_state;
1625       int rv;
1626
1627       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1628
1629       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open!"
1630             " state 0x%x (%s), returning %d (%s)", getpid (),
1631             session->vpp_handle, session->session_index, state,
1632             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1633       return rv;
1634     }
1635
1636   if (session->session_state & STATE_LISTEN)
1637     return clib_fifo_elts (session->accept_evts_fifo);
1638
1639   return svm_fifo_max_dequeue (session->rx_fifo);
1640 }
1641
1642 int
1643 vppcom_data_segment_copy (void *buf, vppcom_data_segments_t ds, u32 max_bytes)
1644 {
1645   u32 first_copy = clib_min (ds[0].len, max_bytes);
1646   clib_memcpy_fast (buf, ds[0].data, first_copy);
1647   if (first_copy < max_bytes)
1648     {
1649       clib_memcpy_fast (buf + first_copy, ds[1].data,
1650                         clib_min (ds[1].len, max_bytes - first_copy));
1651     }
1652   return 0;
1653 }
1654
1655 static u8
1656 vcl_is_tx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1657 {
1658   if (!is_ct)
1659     return (e->event_type == FIFO_EVENT_APP_TX
1660             && e->fifo->client_session_index == sid);
1661   else
1662     return (e->event_type == SESSION_IO_EVT_CT_RX);
1663 }
1664
1665 int
1666 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
1667 {
1668   vcl_worker_t *wrk = vcl_worker_get_current ();
1669   int rv, n_write, is_nonblocking;
1670   vcl_session_t *s = 0;
1671   svm_fifo_t *tx_fifo = 0;
1672   session_evt_type_t et;
1673   svm_msg_q_msg_t msg;
1674   session_event_t *e;
1675   svm_msg_q_t *mq;
1676   u8 is_ct;
1677
1678   if (PREDICT_FALSE (!buf))
1679     return VPPCOM_EINVAL;
1680
1681   s = vcl_session_get_w_handle (wrk, session_handle);
1682   if (PREDICT_FALSE (!s))
1683     return VPPCOM_EBADFD;
1684
1685   if (PREDICT_FALSE (s->is_vep))
1686     {
1687       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1688                     "cannot write to an epoll session!",
1689                     getpid (), s->vpp_handle, session_handle);
1690
1691       return VPPCOM_EBADFD;
1692     }
1693
1694   if (PREDICT_FALSE (!(s->session_state & STATE_OPEN)))
1695     {
1696       session_state_t state = s->session_state;
1697       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1698       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open! "
1699             "state 0x%x (%s)", getpid (), s->vpp_handle, session_handle,
1700             state, vppcom_session_state_str (state));
1701       return rv;
1702     }
1703
1704   tx_fifo = s->tx_fifo;
1705   is_ct = vcl_session_is_ct (s);
1706   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1707   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1708   if (svm_fifo_is_full (tx_fifo))
1709     {
1710       if (is_nonblocking)
1711         {
1712           return VPPCOM_EWOULDBLOCK;
1713         }
1714       while (svm_fifo_is_full (tx_fifo))
1715         {
1716           svm_fifo_set_want_tx_evt (tx_fifo, 1);
1717           svm_msg_q_lock (mq);
1718           if (svm_msg_q_is_empty (mq))
1719             svm_msg_q_wait (mq);
1720
1721           svm_msg_q_sub_w_lock (mq, &msg);
1722           e = svm_msg_q_msg_data (mq, &msg);
1723           svm_msg_q_unlock (mq);
1724
1725           if (!vcl_is_tx_evt_for_session (e, s->session_index, is_ct))
1726             vcl_handle_mq_event (wrk, e);
1727           svm_msg_q_free_msg (mq, &msg);
1728         }
1729     }
1730
1731   ASSERT (FIFO_EVENT_APP_TX + 1 == SESSION_IO_EVT_CT_TX);
1732   et = FIFO_EVENT_APP_TX + vcl_session_is_ct (s);
1733   if (s->is_dgram)
1734     n_write = app_send_dgram_raw (tx_fifo, &s->transport,
1735                                   s->vpp_evt_q, buf, n, et, SVM_Q_WAIT);
1736   else
1737     n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
1738                                    SVM_Q_WAIT);
1739
1740   ASSERT (n_write > 0);
1741
1742   VDBG (2, "VCL<%d>: vpp handle 0x%llx, sid %u: wrote %d bytes", getpid (),
1743         s->vpp_handle, session_handle, n_write);
1744
1745   return n_write;
1746 }
1747
1748 static vcl_session_t *
1749 vcl_ct_session_get_from_fifo (vcl_worker_t * wrk, svm_fifo_t * f, u8 type)
1750 {
1751   vcl_session_t *s;
1752   s = vcl_session_get (wrk, f->client_session_index);
1753   if (s)
1754     {
1755       /* rx fifo */
1756       if (type == 0 && s->rx_fifo == f)
1757         return s;
1758       /* tx fifo */
1759       if (type == 1 && s->tx_fifo == f)
1760         return s;
1761     }
1762   s = vcl_session_get (wrk, f->master_session_index);
1763   if (s)
1764     {
1765       if (type == 0 && s->rx_fifo == f)
1766         return s;
1767       if (type == 1 && s->tx_fifo == f)
1768         return s;
1769     }
1770   return 0;
1771 }
1772
1773 static inline int
1774 vppcom_session_write_ready (vcl_session_t * session)
1775 {
1776   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1777   if (PREDICT_FALSE (session->is_vep))
1778     {
1779       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1780                     "cannot write to an epoll session!",
1781                     getpid (), session->vpp_handle, session->session_index);
1782       return VPPCOM_EBADFD;
1783     }
1784
1785   if (PREDICT_FALSE (session->session_state & STATE_LISTEN))
1786     {
1787       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1788                     "cannot write to a listen session!",
1789                     getpid (), session->vpp_handle, session->session_index);
1790       return VPPCOM_EBADFD;
1791     }
1792
1793   if (PREDICT_FALSE (!(session->session_state & STATE_OPEN)))
1794     {
1795       session_state_t state = session->session_state;
1796       int rv;
1797
1798       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1799       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1800                     "session is not open! state 0x%x (%s), "
1801                     "returning %d (%s)", getpid (), session->vpp_handle,
1802                     session->session_index,
1803                     state, vppcom_session_state_str (state),
1804                     rv, vppcom_retval_str (rv));
1805       return rv;
1806     }
1807
1808   VDBG (3, "VCL<%d>: vpp handle 0x%llx, sid %u: peek %s (%p), ready = %d",
1809         getpid (), session->vpp_handle, session->session_index,
1810         session->tx_fifo, svm_fifo_max_enqueue (session->tx_fifo));
1811
1812   return svm_fifo_max_enqueue (session->tx_fifo);
1813 }
1814
1815 static inline int
1816 vcl_mq_dequeue_batch (vcl_worker_t * wrk, svm_msg_q_t * mq)
1817 {
1818   svm_msg_q_msg_t *msg;
1819   u32 n_msgs;
1820   int i;
1821
1822   n_msgs = svm_msg_q_size (mq);
1823   for (i = 0; i < n_msgs; i++)
1824     {
1825       vec_add2 (wrk->mq_msg_vector, msg, 1);
1826       svm_msg_q_sub_w_lock (mq, msg);
1827     }
1828   return n_msgs;
1829 }
1830
1831 #define vcl_fifo_rx_evt_valid_or_break(_fifo)                   \
1832 if (PREDICT_FALSE (svm_fifo_is_empty (_fifo)))                  \
1833   {                                                             \
1834     svm_fifo_unset_event (_fifo);                               \
1835     if (svm_fifo_is_empty (_fifo))                              \
1836       break;                                                    \
1837   }                                                             \
1838
1839 static void
1840 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
1841                             unsigned long n_bits, unsigned long *read_map,
1842                             unsigned long *write_map,
1843                             unsigned long *except_map, u32 * bits_set)
1844 {
1845   session_disconnected_msg_t *disconnected_msg;
1846   session_connected_msg_t *connected_msg;
1847   session_accepted_msg_t *accepted_msg;
1848   vcl_session_msg_t *vcl_msg;
1849   vcl_session_t *session;
1850   u64 handle;
1851   u32 sid;
1852
1853   switch (e->event_type)
1854     {
1855     case FIFO_EVENT_APP_RX:
1856       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1857       sid = e->fifo->client_session_index;
1858       session = vcl_session_get (wrk, sid);
1859       if (!session)
1860         break;
1861       if (sid < n_bits && read_map)
1862         {
1863           clib_bitmap_set_no_check (read_map, sid, 1);
1864           *bits_set += 1;
1865         }
1866       break;
1867     case FIFO_EVENT_APP_TX:
1868       sid = e->fifo->client_session_index;
1869       session = vcl_session_get (wrk, sid);
1870       if (!session)
1871         break;
1872       if (sid < n_bits && write_map)
1873         {
1874           clib_bitmap_set_no_check (write_map, sid, 1);
1875           *bits_set += 1;
1876         }
1877       break;
1878     case SESSION_IO_EVT_CT_TX:
1879       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1880       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
1881       if (!session)
1882         break;
1883       sid = session->session_index;
1884       if (sid < n_bits && read_map)
1885         {
1886           clib_bitmap_set_no_check (read_map, sid, 1);
1887           *bits_set += 1;
1888         }
1889       break;
1890     case SESSION_IO_EVT_CT_RX:
1891       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
1892       if (!session)
1893         break;
1894       sid = session->session_index;
1895       if (sid < n_bits && write_map)
1896         {
1897           clib_bitmap_set_no_check (write_map, sid, 1);
1898           *bits_set += 1;
1899         }
1900       break;
1901     case SESSION_CTRL_EVT_ACCEPTED:
1902       accepted_msg = (session_accepted_msg_t *) e->data;
1903       handle = accepted_msg->listener_handle;
1904       session = vcl_session_table_lookup_listener (wrk, handle);
1905       if (!session)
1906         {
1907           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
1908                         "listener handle %llx", getpid (), handle);
1909           break;
1910         }
1911
1912       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
1913       vcl_msg->accepted_msg = *accepted_msg;
1914       sid = session->session_index;
1915       if (sid < n_bits && read_map)
1916         {
1917           clib_bitmap_set_no_check (read_map, sid, 1);
1918           *bits_set += 1;
1919         }
1920       break;
1921     case SESSION_CTRL_EVT_CONNECTED:
1922       connected_msg = (session_connected_msg_t *) e->data;
1923       vcl_session_connected_handler (wrk, connected_msg);
1924       break;
1925     case SESSION_CTRL_EVT_DISCONNECTED:
1926       disconnected_msg = (session_disconnected_msg_t *) e->data;
1927       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
1928       if (sid < n_bits && except_map)
1929         {
1930           clib_bitmap_set_no_check (except_map, sid, 1);
1931           *bits_set += 1;
1932         }
1933       break;
1934     case SESSION_CTRL_EVT_RESET:
1935       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
1936       if (sid < n_bits && except_map)
1937         {
1938           clib_bitmap_set_no_check (except_map, sid, 1);
1939           *bits_set += 1;
1940         }
1941       break;
1942     default:
1943       clib_warning ("unhandled: %u", e->event_type);
1944       break;
1945     }
1946 }
1947
1948 static int
1949 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
1950                       unsigned long n_bits, unsigned long *read_map,
1951                       unsigned long *write_map, unsigned long *except_map,
1952                       double time_to_wait, u32 * bits_set)
1953 {
1954   svm_msg_q_msg_t *msg;
1955   session_event_t *e;
1956   u32 i;
1957
1958   svm_msg_q_lock (mq);
1959   if (svm_msg_q_is_empty (mq))
1960     {
1961       if (*bits_set)
1962         {
1963           svm_msg_q_unlock (mq);
1964           return 0;
1965         }
1966
1967       if (!time_to_wait)
1968         {
1969           svm_msg_q_unlock (mq);
1970           return 0;
1971         }
1972       else if (time_to_wait < 0)
1973         {
1974           svm_msg_q_wait (mq);
1975         }
1976       else
1977         {
1978           if (svm_msg_q_timedwait (mq, time_to_wait))
1979             {
1980               svm_msg_q_unlock (mq);
1981               return 0;
1982             }
1983         }
1984     }
1985   vcl_mq_dequeue_batch (wrk, mq);
1986   svm_msg_q_unlock (mq);
1987
1988   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
1989     {
1990       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
1991       e = svm_msg_q_msg_data (mq, msg);
1992       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
1993                                   except_map, bits_set);
1994       svm_msg_q_free_msg (mq, msg);
1995     }
1996   vec_reset_length (wrk->mq_msg_vector);
1997   return *bits_set;
1998 }
1999
2000 static int
2001 vppcom_select_condvar (vcl_worker_t * wrk, unsigned long n_bits,
2002                        unsigned long *read_map, unsigned long *write_map,
2003                        unsigned long *except_map, double time_to_wait,
2004                        u32 * bits_set)
2005 {
2006   double total_wait = 0, wait_slice;
2007   vcl_cut_through_registration_t *cr;
2008
2009   time_to_wait = (time_to_wait == -1) ? 10e9 : time_to_wait;
2010   wait_slice = wrk->cut_through_registrations ? 10e-6 : time_to_wait;
2011   do
2012     {
2013       vcl_ct_registration_lock (wrk);
2014       /* *INDENT-OFF* */
2015       pool_foreach (cr, wrk->cut_through_registrations, ({
2016         vcl_select_handle_mq (wrk, cr->mq, n_bits, read_map, write_map, except_map,
2017                               0, bits_set);
2018       }));
2019       /* *INDENT-ON* */
2020       vcl_ct_registration_unlock (wrk);
2021
2022       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
2023                             write_map, except_map, time_to_wait, bits_set);
2024       total_wait += wait_slice;
2025       if (*bits_set)
2026         return *bits_set;
2027     }
2028   while (total_wait < time_to_wait);
2029
2030   return 0;
2031 }
2032
2033 static int
2034 vppcom_select_eventfd (vcl_worker_t * wrk, unsigned long n_bits,
2035                        unsigned long *read_map, unsigned long *write_map,
2036                        unsigned long *except_map, double time_to_wait,
2037                        u32 * bits_set)
2038 {
2039   vcl_mq_evt_conn_t *mqc;
2040   int __clib_unused n_read;
2041   int n_mq_evts, i;
2042   u64 buf;
2043
2044   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2045   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2046                           vec_len (wrk->mq_events), time_to_wait);
2047   for (i = 0; i < n_mq_evts; i++)
2048     {
2049       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2050       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2051       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
2052                             except_map, 0, bits_set);
2053     }
2054
2055   return (n_mq_evts > 0 ? (int) *bits_set : 0);
2056 }
2057
2058 int
2059 vppcom_select (unsigned long n_bits, unsigned long *read_map,
2060                unsigned long *write_map, unsigned long *except_map,
2061                double time_to_wait)
2062 {
2063   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
2064   vcl_worker_t *wrk = vcl_worker_get_current ();
2065   vcl_session_t *session = 0;
2066   int rv, i;
2067
2068   ASSERT (sizeof (clib_bitmap_t) == sizeof (long int));
2069
2070   if (n_bits && read_map)
2071     {
2072       clib_bitmap_validate (wrk->rd_bitmap, minbits);
2073       clib_memcpy_fast (wrk->rd_bitmap, read_map,
2074                         vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
2075       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
2076     }
2077   if (n_bits && write_map)
2078     {
2079       clib_bitmap_validate (wrk->wr_bitmap, minbits);
2080       clib_memcpy_fast (wrk->wr_bitmap, write_map,
2081                         vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
2082       memset (write_map, 0,
2083               vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
2084     }
2085   if (n_bits && except_map)
2086     {
2087       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2088       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2089                         vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
2090       memset (except_map, 0,
2091               vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
2092     }
2093
2094   if (!n_bits)
2095     return 0;
2096
2097   if (!write_map)
2098     goto check_rd;
2099
2100   /* *INDENT-OFF* */
2101   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
2102     if (!(session = vcl_session_get (wrk, sid)))
2103       {
2104         if (except_map && sid < minbits)
2105           clib_bitmap_set_no_check (except_map, sid, 1);
2106         continue;
2107       }
2108
2109     rv = svm_fifo_is_full (session->tx_fifo);
2110     if (!rv)
2111       {
2112         clib_bitmap_set_no_check (write_map, sid, 1);
2113         bits_set++;
2114       }
2115   }));
2116
2117 check_rd:
2118   if (!read_map)
2119     goto check_mq;
2120
2121   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
2122     if (!(session = vcl_session_get (wrk, sid)))
2123       {
2124         if (except_map && sid < minbits)
2125           clib_bitmap_set_no_check (except_map, sid, 1);
2126         continue;
2127       }
2128
2129     rv = vppcom_session_read_ready (session);
2130     if (rv)
2131       {
2132         clib_bitmap_set_no_check (read_map, sid, 1);
2133         bits_set++;
2134       }
2135   }));
2136   /* *INDENT-ON* */
2137
2138 check_mq:
2139
2140   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2141     {
2142       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2143                                   read_map, write_map, except_map, &bits_set);
2144     }
2145   vec_reset_length (wrk->unhandled_evts_vector);
2146
2147   if (vcm->cfg.use_mq_eventfd)
2148     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2149                            time_to_wait, &bits_set);
2150   else
2151     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2152                            time_to_wait, &bits_set);
2153
2154   return (bits_set);
2155 }
2156
2157 static inline void
2158 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_idx)
2159 {
2160   vcl_session_t *session;
2161   vppcom_epoll_t *vep;
2162   u32 sid = vep_idx;
2163
2164   if (VPPCOM_DEBUG <= 1)
2165     return;
2166
2167   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2168   session = vcl_session_get (wrk, vep_idx);
2169   if (PREDICT_FALSE (!session))
2170     {
2171       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!",
2172                     getpid (), vep_idx);
2173       goto done;
2174     }
2175   if (PREDICT_FALSE (!session->is_vep))
2176     {
2177       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2178                     getpid (), vep_idx);
2179       goto done;
2180     }
2181   vep = &session->vep;
2182   clib_warning ("VCL<%d>: vep_idx (%u): Dumping epoll chain\n"
2183                 "{\n"
2184                 "   is_vep         = %u\n"
2185                 "   is_vep_session = %u\n"
2186                 "   next_sid       = 0x%x (%u)\n"
2187                 "   wait_cont_idx  = 0x%x (%u)\n"
2188                 "}\n", getpid (), vep_idx,
2189                 session->is_vep, session->is_vep_session,
2190                 vep->next_sh, vep->next_sh,
2191                 session->wait_cont_idx, session->wait_cont_idx);
2192
2193   for (sid = vep->next_sh; sid != ~0; sid = vep->next_sh)
2194     {
2195       session = vcl_session_get (wrk, sid);
2196       if (PREDICT_FALSE (!session))
2197         {
2198           clib_warning ("VCL<%d>: ERROR: Invalid sid (%u)!", getpid (), sid);
2199           goto done;
2200         }
2201       if (PREDICT_FALSE (session->is_vep))
2202         clib_warning ("VCL<%d>: ERROR: sid (%u) is a vep!",
2203                       getpid (), vep_idx);
2204       else if (PREDICT_FALSE (!session->is_vep_session))
2205         {
2206           clib_warning ("VCL<%d>: ERROR: session (%u) "
2207                         "is not a vep session!", getpid (), sid);
2208           goto done;
2209         }
2210       vep = &session->vep;
2211       if (PREDICT_FALSE (vep->vep_sh != vep_idx))
2212         clib_warning ("VCL<%d>: ERROR: session (%u) vep_idx (%u) != "
2213                       "vep_idx (%u)!", getpid (),
2214                       sid, session->vep.vep_sh, vep_idx);
2215       if (session->is_vep_session)
2216         {
2217           clib_warning ("vep_idx[%u]: sid 0x%x (%u)\n"
2218                         "{\n"
2219                         "   next_sid       = 0x%x (%u)\n"
2220                         "   prev_sid       = 0x%x (%u)\n"
2221                         "   vep_idx        = 0x%x (%u)\n"
2222                         "   ev.events      = 0x%x\n"
2223                         "   ev.data.u64    = 0x%llx\n"
2224                         "   et_mask        = 0x%x\n"
2225                         "}\n",
2226                         vep_idx, sid, sid,
2227                         vep->next_sh, vep->next_sh,
2228                         vep->prev_sh, vep->prev_sh,
2229                         vep->vep_sh, vep->vep_sh,
2230                         vep->ev.events, vep->ev.data.u64, vep->et_mask);
2231         }
2232     }
2233
2234 done:
2235   clib_warning ("VCL<%d>: vep_idx (%u): Dump complete!\n",
2236                 getpid (), vep_idx);
2237 }
2238
2239 int
2240 vppcom_epoll_create (void)
2241 {
2242   vcl_worker_t *wrk = vcl_worker_get_current ();
2243   vcl_session_t *vep_session;
2244
2245   vep_session = vcl_session_alloc (wrk);
2246
2247   vep_session->is_vep = 1;
2248   vep_session->vep.vep_sh = ~0;
2249   vep_session->vep.next_sh = ~0;
2250   vep_session->vep.prev_sh = ~0;
2251   vep_session->wait_cont_idx = ~0;
2252   vep_session->vpp_handle = ~0;
2253
2254   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_sh);
2255   VDBG (0, "VCL<%d>: Created vep_idx %u / sid %u!",
2256         getpid (), vep_session->session_index, vep_session->session_index);
2257
2258   return vcl_session_handle (vep_session);
2259 }
2260
2261 int
2262 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2263                   struct epoll_event *event)
2264 {
2265   vcl_worker_t *wrk = vcl_worker_get_current ();
2266   vcl_session_t *vep_session;
2267   vcl_session_t *session;
2268   int rv = VPPCOM_OK;
2269
2270   if (vep_handle == session_handle)
2271     {
2272       clib_warning ("VCL<%d>: ERROR: vep_idx == session_index (%u)!",
2273                     getpid (), vep_handle);
2274       return VPPCOM_EINVAL;
2275     }
2276
2277   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2278   if (PREDICT_FALSE (!vep_session))
2279     {
2280       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!", vep_handle);
2281       return VPPCOM_EBADFD;
2282     }
2283   if (PREDICT_FALSE (!vep_session->is_vep))
2284     {
2285       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2286                     getpid (), vep_handle);
2287       return VPPCOM_EINVAL;
2288     }
2289
2290   ASSERT (vep_session->vep.vep_sh == ~0);
2291   ASSERT (vep_session->vep.prev_sh == ~0);
2292
2293   session = vcl_session_get_w_handle (wrk, session_handle);
2294   if (PREDICT_FALSE (!session))
2295     {
2296       VDBG (0, "VCL<%d>: ERROR: Invalid session_handle (%u)!",
2297             getpid (), session_handle);
2298       return VPPCOM_EBADFD;
2299     }
2300   if (PREDICT_FALSE (session->is_vep))
2301     {
2302       clib_warning ("ERROR: session_handle (%u) is a vep!", vep_handle);
2303       return VPPCOM_EINVAL;
2304     }
2305
2306   switch (op)
2307     {
2308     case EPOLL_CTL_ADD:
2309       if (PREDICT_FALSE (!event))
2310         {
2311           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: NULL pointer to "
2312                         "epoll_event structure!", getpid ());
2313           return VPPCOM_EINVAL;
2314         }
2315       if (vep_session->vep.next_sh != ~0)
2316         {
2317           vcl_session_t *next_session;
2318           next_session = vcl_session_get_w_handle (wrk,
2319                                                    vep_session->vep.next_sh);
2320           if (PREDICT_FALSE (!next_session))
2321             {
2322               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: Invalid "
2323                             "vep.next_sid (%u) on vep_idx (%u)!",
2324                             getpid (), vep_session->vep.next_sh, vep_handle);
2325               return VPPCOM_EBADFD;
2326             }
2327           ASSERT (next_session->vep.prev_sh == vep_handle);
2328           next_session->vep.prev_sh = session_handle;
2329         }
2330       session->vep.next_sh = vep_session->vep.next_sh;
2331       session->vep.prev_sh = vep_handle;
2332       session->vep.vep_sh = vep_handle;
2333       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2334       session->vep.ev = *event;
2335       session->is_vep = 0;
2336       session->is_vep_session = 1;
2337       vep_session->vep.next_sh = session_handle;
2338
2339       VDBG (1, "VCL<%d>: EPOLL_CTL_ADD: vep_idx %u, sid %u, events 0x%x, "
2340             "data 0x%llx!", getpid (), vep_handle, session_handle,
2341             event->events, event->data.u64);
2342       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
2343       break;
2344
2345     case EPOLL_CTL_MOD:
2346       if (PREDICT_FALSE (!event))
2347         {
2348           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_MOD: NULL pointer to "
2349                         "epoll_event structure!", getpid ());
2350           rv = VPPCOM_EINVAL;
2351           goto done;
2352         }
2353       else if (PREDICT_FALSE (!session->is_vep_session))
2354         {
2355           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2356                         "not a vep session!", getpid (), session_handle);
2357           rv = VPPCOM_EINVAL;
2358           goto done;
2359         }
2360       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2361         {
2362           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2363                         "vep_idx (%u) != vep_idx (%u)!",
2364                         getpid (), session_handle,
2365                         session->vep.vep_sh, vep_handle);
2366           rv = VPPCOM_EINVAL;
2367           goto done;
2368         }
2369       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2370       session->vep.ev = *event;
2371       VDBG (1, "VCL<%d>: EPOLL_CTL_MOD: vep_idx %u, sid %u, events 0x%x,"
2372             " data 0x%llx!", getpid (), vep_handle, session_handle,
2373             event->events, event->data.u64);
2374       break;
2375
2376     case EPOLL_CTL_DEL:
2377       if (PREDICT_FALSE (!session->is_vep_session))
2378         {
2379           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2380                         "not a vep session!", getpid (), session_handle);
2381           rv = VPPCOM_EINVAL;
2382           goto done;
2383         }
2384       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2385         {
2386           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2387                         "vep_idx (%u) != vep_idx (%u)!",
2388                         getpid (), session_handle,
2389                         session->vep.vep_sh, vep_handle);
2390           rv = VPPCOM_EINVAL;
2391           goto done;
2392         }
2393
2394       vep_session->wait_cont_idx =
2395         (vep_session->wait_cont_idx == session_handle) ?
2396         session->vep.next_sh : vep_session->wait_cont_idx;
2397
2398       if (session->vep.prev_sh == vep_handle)
2399         vep_session->vep.next_sh = session->vep.next_sh;
2400       else
2401         {
2402           vcl_session_t *prev_session;
2403           prev_session = vcl_session_get_w_handle (wrk, session->vep.prev_sh);
2404           if (PREDICT_FALSE (!prev_session))
2405             {
2406               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2407                             "vep.prev_sid (%u) on sid (%u)!",
2408                             getpid (), session->vep.prev_sh, session_handle);
2409               return VPPCOM_EBADFD;
2410             }
2411           ASSERT (prev_session->vep.next_sh == session_handle);
2412           prev_session->vep.next_sh = session->vep.next_sh;
2413         }
2414       if (session->vep.next_sh != ~0)
2415         {
2416           vcl_session_t *next_session;
2417           next_session = vcl_session_get_w_handle (wrk, session->vep.next_sh);
2418           if (PREDICT_FALSE (!next_session))
2419             {
2420               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2421                             "vep.next_sid (%u) on sid (%u)!",
2422                             getpid (), session->vep.next_sh, session_handle);
2423               return VPPCOM_EBADFD;
2424             }
2425           ASSERT (next_session->vep.prev_sh == session_handle);
2426           next_session->vep.prev_sh = session->vep.prev_sh;
2427         }
2428
2429       memset (&session->vep, 0, sizeof (session->vep));
2430       session->vep.next_sh = ~0;
2431       session->vep.prev_sh = ~0;
2432       session->vep.vep_sh = ~0;
2433       session->is_vep_session = 0;
2434       VDBG (1, "VCL<%d>: EPOLL_CTL_DEL: vep_idx %u, sid %u!",
2435             getpid (), vep_handle, session_handle);
2436       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_sh);
2437       break;
2438
2439     default:
2440       clib_warning ("VCL<%d>: ERROR: Invalid operation (%d)!", getpid (), op);
2441       rv = VPPCOM_EINVAL;
2442     }
2443
2444   vep_verify_epoll_chain (wrk, vep_handle);
2445
2446 done:
2447   return rv;
2448 }
2449
2450 static inline void
2451 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2452                                 struct epoll_event *events, u32 * num_ev)
2453 {
2454   session_disconnected_msg_t *disconnected_msg;
2455   session_connected_msg_t *connected_msg;
2456   session_accepted_msg_t *accepted_msg;
2457   u64 session_evt_data = ~0, handle;
2458   u32 sid = ~0, session_events;
2459   vcl_session_msg_t *vcl_msg;
2460   vcl_session_t *session;
2461   u8 add_event = 0;
2462
2463   switch (e->event_type)
2464     {
2465     case FIFO_EVENT_APP_RX:
2466       ASSERT (e->fifo->client_thread_index == vcl_get_worker_index ());
2467       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2468       sid = e->fifo->client_session_index;
2469       session = vcl_session_get (wrk, sid);
2470       session_events = session->vep.ev.events;
2471       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2472         break;
2473       add_event = 1;
2474       events[*num_ev].events |= EPOLLIN;
2475       session_evt_data = session->vep.ev.data.u64;
2476       session->has_rx_evt = 1;
2477       break;
2478     case FIFO_EVENT_APP_TX:
2479       sid = e->fifo->client_session_index;
2480       session = vcl_session_get (wrk, sid);
2481       session_events = session->vep.ev.events;
2482       if (!(EPOLLOUT & session_events))
2483         break;
2484       add_event = 1;
2485       events[*num_ev].events |= EPOLLOUT;
2486       session_evt_data = session->vep.ev.data.u64;
2487       break;
2488     case SESSION_IO_EVT_CT_TX:
2489       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2490       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
2491       sid = session->session_index;
2492       session_events = session->vep.ev.events;
2493       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2494         break;
2495       add_event = 1;
2496       events[*num_ev].events |= EPOLLIN;
2497       session_evt_data = session->vep.ev.data.u64;
2498       session->has_rx_evt = 1;
2499       break;
2500     case SESSION_IO_EVT_CT_RX:
2501       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
2502       sid = session->session_index;
2503       session_events = session->vep.ev.events;
2504       if (!(EPOLLOUT & session_events))
2505         break;
2506       add_event = 1;
2507       events[*num_ev].events |= EPOLLOUT;
2508       session_evt_data = session->vep.ev.data.u64;
2509       break;
2510     case SESSION_CTRL_EVT_ACCEPTED:
2511       accepted_msg = (session_accepted_msg_t *) e->data;
2512       handle = accepted_msg->listener_handle;
2513       session = vcl_session_table_lookup_listener (wrk, handle);
2514       if (!session)
2515         {
2516           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
2517                         "listener handle %llx", getpid (), handle);
2518           break;
2519         }
2520
2521       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
2522       vcl_msg->accepted_msg = *accepted_msg;
2523       session_events = session->vep.ev.events;
2524       if (!(EPOLLIN & session_events))
2525         break;
2526
2527       add_event = 1;
2528       events[*num_ev].events |= EPOLLIN;
2529       session_evt_data = session->vep.ev.data.u64;
2530       break;
2531     case SESSION_CTRL_EVT_CONNECTED:
2532       connected_msg = (session_connected_msg_t *) e->data;
2533       vcl_session_connected_handler (wrk, connected_msg);
2534       /* Generate EPOLLOUT because there's no connected event */
2535       sid = vcl_session_index_from_vpp_handle (wrk, connected_msg->handle);
2536       session = vcl_session_get (wrk, sid);
2537       session_events = session->vep.ev.events;
2538       if (EPOLLOUT & session_events)
2539         {
2540           add_event = 1;
2541           events[*num_ev].events |= EPOLLOUT;
2542           session_evt_data = session->vep.ev.data.u64;
2543         }
2544       break;
2545     case SESSION_CTRL_EVT_DISCONNECTED:
2546       disconnected_msg = (session_disconnected_msg_t *) e->data;
2547       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
2548       if (!(session = vcl_session_get (wrk, sid)))
2549         break;
2550       add_event = 1;
2551       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2552       session_evt_data = session->vep.ev.data.u64;
2553       session_events = session->vep.ev.events;
2554       break;
2555     case SESSION_CTRL_EVT_RESET:
2556       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2557       if (!(session = vcl_session_get (wrk, sid)))
2558         break;
2559       add_event = 1;
2560       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2561       session_evt_data = session->vep.ev.data.u64;
2562       session_events = session->vep.ev.events;
2563       break;
2564     default:
2565       VDBG (0, "unhandled: %u", e->event_type);
2566       break;
2567     }
2568
2569   if (add_event)
2570     {
2571       events[*num_ev].data.u64 = session_evt_data;
2572       if (EPOLLONESHOT & session_events)
2573         {
2574           session = vcl_session_get (wrk, sid);
2575           session->vep.ev.events = 0;
2576         }
2577       *num_ev += 1;
2578     }
2579 }
2580
2581 static int
2582 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2583                           struct epoll_event *events, u32 maxevents,
2584                           double wait_for_time, u32 * num_ev)
2585 {
2586   svm_msg_q_msg_t *msg;
2587   session_event_t *e;
2588   int i;
2589
2590   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
2591     goto handle_dequeued;
2592
2593   svm_msg_q_lock (mq);
2594   if (svm_msg_q_is_empty (mq))
2595     {
2596       if (!wait_for_time)
2597         {
2598           svm_msg_q_unlock (mq);
2599           return 0;
2600         }
2601       else if (wait_for_time < 0)
2602         {
2603           svm_msg_q_wait (mq);
2604         }
2605       else
2606         {
2607           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
2608             {
2609               svm_msg_q_unlock (mq);
2610               return 0;
2611             }
2612         }
2613     }
2614   vcl_mq_dequeue_batch (wrk, mq);
2615   svm_msg_q_unlock (mq);
2616
2617 handle_dequeued:
2618   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2619     {
2620       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2621       e = svm_msg_q_msg_data (mq, msg);
2622       if (*num_ev < maxevents)
2623         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
2624       else
2625         vec_add1 (wrk->unhandled_evts_vector, *e);
2626       svm_msg_q_free_msg (mq, msg);
2627     }
2628   vec_reset_length (wrk->mq_msg_vector);
2629
2630   return *num_ev;
2631 }
2632
2633 static int
2634 vppcom_epoll_wait_condvar (vcl_worker_t * wrk, struct epoll_event *events,
2635                            int maxevents, u32 n_evts, double wait_for_time)
2636 {
2637   vcl_cut_through_registration_t *cr;
2638   double total_wait = 0, wait_slice;
2639   int rv;
2640
2641   wait_for_time = (wait_for_time == -1) ? (double) 10e9 : wait_for_time;
2642   wait_slice = wrk->cut_through_registrations ? 10e-6 : wait_for_time;
2643
2644   do
2645     {
2646       vcl_ct_registration_lock (wrk);
2647       /* *INDENT-OFF* */
2648       pool_foreach (cr, wrk->cut_through_registrations, ({
2649         vcl_epoll_wait_handle_mq (wrk, cr->mq, events, maxevents, 0, &n_evts);
2650       }));
2651       /* *INDENT-ON* */
2652       vcl_ct_registration_unlock (wrk);
2653
2654       rv = vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events,
2655                                      maxevents, n_evts ? 0 : wait_slice,
2656                                      &n_evts);
2657       if (rv)
2658         total_wait += wait_slice;
2659       if (n_evts)
2660         return n_evts;
2661     }
2662   while (total_wait < wait_for_time);
2663   return n_evts;
2664 }
2665
2666 static int
2667 vppcom_epoll_wait_eventfd (vcl_worker_t * wrk, struct epoll_event *events,
2668                            int maxevents, u32 n_evts, double wait_for_time)
2669 {
2670   vcl_mq_evt_conn_t *mqc;
2671   int __clib_unused n_read;
2672   int n_mq_evts, i;
2673   u64 buf;
2674
2675   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2676 again:
2677   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2678                           vec_len (wrk->mq_events), wait_for_time);
2679   for (i = 0; i < n_mq_evts; i++)
2680     {
2681       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2682       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2683       vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0, &n_evts);
2684     }
2685   if (!n_evts && n_mq_evts > 0)
2686     goto again;
2687
2688   return (int) n_evts;
2689 }
2690
2691 int
2692 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
2693                    int maxevents, double wait_for_time)
2694 {
2695   vcl_worker_t *wrk = vcl_worker_get_current ();
2696   vcl_session_t *vep_session;
2697   u32 n_evts = 0;
2698   int i;
2699
2700   if (PREDICT_FALSE (maxevents <= 0))
2701     {
2702       clib_warning ("VCL<%d>: ERROR: Invalid maxevents (%d)!",
2703                     getpid (), maxevents);
2704       return VPPCOM_EINVAL;
2705     }
2706
2707   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2708   if (!vep_session)
2709     return VPPCOM_EBADFD;
2710
2711   if (PREDICT_FALSE (!vep_session->is_vep))
2712     {
2713       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2714                     getpid (), vep_handle);
2715       return VPPCOM_EINVAL;
2716     }
2717
2718   memset (events, 0, sizeof (*events) * maxevents);
2719
2720   if (vec_len (wrk->unhandled_evts_vector))
2721     {
2722       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2723         {
2724           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
2725                                           events, &n_evts);
2726           if (n_evts == maxevents)
2727             {
2728               i += 1;
2729               break;
2730             }
2731         }
2732
2733       vec_delete (wrk->unhandled_evts_vector, i, 0);
2734     }
2735
2736   if (vcm->cfg.use_mq_eventfd)
2737     return vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
2738                                       wait_for_time);
2739
2740   return vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
2741                                     wait_for_time);
2742 }
2743
2744 int
2745 vppcom_session_attr (uint32_t session_handle, uint32_t op,
2746                      void *buffer, uint32_t * buflen)
2747 {
2748   vcl_worker_t *wrk = vcl_worker_get_current ();
2749   vcl_session_t *session;
2750   int rv = VPPCOM_OK;
2751   u32 *flags = buffer;
2752   vppcom_endpt_t *ep = buffer;
2753
2754   session = vcl_session_get_w_handle (wrk, session_handle);
2755   if (!session)
2756     return VPPCOM_EBADFD;
2757
2758   switch (op)
2759     {
2760     case VPPCOM_ATTR_GET_NREAD:
2761       rv = vppcom_session_read_ready (session);
2762       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NREAD: sid %u, nread = %d",
2763             getpid (), rv);
2764       break;
2765
2766     case VPPCOM_ATTR_GET_NWRITE:
2767       rv = vppcom_session_write_ready (session);
2768       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NWRITE: sid %u, nwrite = %d",
2769             getpid (), session_handle, rv);
2770       break;
2771
2772     case VPPCOM_ATTR_GET_FLAGS:
2773       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
2774         {
2775           *flags = O_RDWR | (VCL_SESS_ATTR_TEST (session->attr,
2776                                                  VCL_SESS_ATTR_NONBLOCK));
2777           *buflen = sizeof (*flags);
2778           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_FLAGS: sid %u, flags = 0x%08x, "
2779                 "is_nonblocking = %u", getpid (),
2780                 session_handle, *flags,
2781                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2782         }
2783       else
2784         rv = VPPCOM_EINVAL;
2785       break;
2786
2787     case VPPCOM_ATTR_SET_FLAGS:
2788       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
2789         {
2790           if (*flags & O_NONBLOCK)
2791             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
2792           else
2793             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
2794
2795           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_FLAGS: sid %u, flags = 0x%08x,"
2796                 " is_nonblocking = %u",
2797                 getpid (), session_handle, *flags,
2798                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2799         }
2800       else
2801         rv = VPPCOM_EINVAL;
2802       break;
2803
2804     case VPPCOM_ATTR_GET_PEER_ADDR:
2805       if (PREDICT_TRUE (buffer && buflen &&
2806                         (*buflen >= sizeof (*ep)) && ep->ip))
2807         {
2808           ep->is_ip4 = session->transport.is_ip4;
2809           ep->port = session->transport.rmt_port;
2810           if (session->transport.is_ip4)
2811             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
2812                               sizeof (ip4_address_t));
2813           else
2814             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
2815                               sizeof (ip6_address_t));
2816           *buflen = sizeof (*ep);
2817           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_PEER_ADDR: sid %u, is_ip4 = %u, "
2818                 "addr = %U, port %u", getpid (),
2819                 session_handle, ep->is_ip4, format_ip46_address,
2820                 &session->transport.rmt_ip,
2821                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2822                 clib_net_to_host_u16 (ep->port));
2823         }
2824       else
2825         rv = VPPCOM_EINVAL;
2826       break;
2827
2828     case VPPCOM_ATTR_GET_LCL_ADDR:
2829       if (PREDICT_TRUE (buffer && buflen &&
2830                         (*buflen >= sizeof (*ep)) && ep->ip))
2831         {
2832           ep->is_ip4 = session->transport.is_ip4;
2833           ep->port = session->transport.lcl_port;
2834           if (session->transport.is_ip4)
2835             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
2836                               sizeof (ip4_address_t));
2837           else
2838             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
2839                               sizeof (ip6_address_t));
2840           *buflen = sizeof (*ep);
2841           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_LCL_ADDR: sid %u, is_ip4 = %u,"
2842                 " addr = %U port %d", getpid (),
2843                 session_handle, ep->is_ip4, format_ip46_address,
2844                 &session->transport.lcl_ip,
2845                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2846                 clib_net_to_host_u16 (ep->port));
2847         }
2848       else
2849         rv = VPPCOM_EINVAL;
2850       break;
2851
2852     case VPPCOM_ATTR_GET_LIBC_EPFD:
2853       rv = session->libc_epfd;
2854       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d",
2855             getpid (), rv);
2856       break;
2857
2858     case VPPCOM_ATTR_SET_LIBC_EPFD:
2859       if (PREDICT_TRUE (buffer && buflen &&
2860                         (*buflen == sizeof (session->libc_epfd))))
2861         {
2862           session->libc_epfd = *(int *) buffer;
2863           *buflen = sizeof (session->libc_epfd);
2864
2865           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, "
2866                 "buflen %d", getpid (), session->libc_epfd, *buflen);
2867         }
2868       else
2869         rv = VPPCOM_EINVAL;
2870       break;
2871
2872     case VPPCOM_ATTR_GET_PROTOCOL:
2873       if (buffer && buflen && (*buflen >= sizeof (int)))
2874         {
2875           *(int *) buffer = session->session_type;
2876           *buflen = sizeof (int);
2877
2878           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
2879                 getpid (), *(int *) buffer, *(int *) buffer ? "UDP" : "TCP",
2880                 *buflen);
2881         }
2882       else
2883         rv = VPPCOM_EINVAL;
2884       break;
2885
2886     case VPPCOM_ATTR_GET_LISTEN:
2887       if (buffer && buflen && (*buflen >= sizeof (int)))
2888         {
2889           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2890                                                 VCL_SESS_ATTR_LISTEN);
2891           *buflen = sizeof (int);
2892
2893           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LISTEN: %d, buflen %d",
2894                 getpid (), *(int *) buffer, *buflen);
2895         }
2896       else
2897         rv = VPPCOM_EINVAL;
2898       break;
2899
2900     case VPPCOM_ATTR_GET_ERROR:
2901       if (buffer && buflen && (*buflen >= sizeof (int)))
2902         {
2903           *(int *) buffer = 0;
2904           *buflen = sizeof (int);
2905
2906           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
2907                 getpid (), *(int *) buffer, *buflen);
2908         }
2909       else
2910         rv = VPPCOM_EINVAL;
2911       break;
2912
2913     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
2914       if (buffer && buflen && (*buflen >= sizeof (u32)))
2915         {
2916
2917           /* VPP-TBD */
2918           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
2919                                 session->tx_fifo ? session->tx_fifo->nitems :
2920                                 vcm->cfg.tx_fifo_size);
2921           *buflen = sizeof (u32);
2922
2923           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), "
2924                 "buflen %d, #VPP-TBD#", getpid (),
2925                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2926         }
2927       else
2928         rv = VPPCOM_EINVAL;
2929       break;
2930
2931     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
2932       if (buffer && buflen && (*buflen == sizeof (u32)))
2933         {
2934           /* VPP-TBD */
2935           session->sndbuf_size = *(u32 *) buffer;
2936           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), "
2937                 "buflen %d, #VPP-TBD#", getpid (),
2938                 session->sndbuf_size, session->sndbuf_size, *buflen);
2939         }
2940       else
2941         rv = VPPCOM_EINVAL;
2942       break;
2943
2944     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
2945       if (buffer && buflen && (*buflen >= sizeof (u32)))
2946         {
2947
2948           /* VPP-TBD */
2949           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
2950                                 session->rx_fifo ? session->rx_fifo->nitems :
2951                                 vcm->cfg.rx_fifo_size);
2952           *buflen = sizeof (u32);
2953
2954           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), "
2955                 "buflen %d, #VPP-TBD#", getpid (),
2956                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2957         }
2958       else
2959         rv = VPPCOM_EINVAL;
2960       break;
2961
2962     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
2963       if (buffer && buflen && (*buflen == sizeof (u32)))
2964         {
2965           /* VPP-TBD */
2966           session->rcvbuf_size = *(u32 *) buffer;
2967           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), "
2968                 "buflen %d, #VPP-TBD#", getpid (),
2969                 session->sndbuf_size, session->sndbuf_size, *buflen);
2970         }
2971       else
2972         rv = VPPCOM_EINVAL;
2973       break;
2974
2975     case VPPCOM_ATTR_GET_REUSEADDR:
2976       if (buffer && buflen && (*buflen >= sizeof (int)))
2977         {
2978           /* VPP-TBD */
2979           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2980                                                 VCL_SESS_ATTR_REUSEADDR);
2981           *buflen = sizeof (int);
2982
2983           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEADDR: %d, "
2984                 "buflen %d, #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2985         }
2986       else
2987         rv = VPPCOM_EINVAL;
2988       break;
2989
2990     case VPPCOM_ATTR_SET_REUSEADDR:
2991       if (buffer && buflen && (*buflen == sizeof (int)) &&
2992           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
2993         {
2994           /* VPP-TBD */
2995           if (*(int *) buffer)
2996             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEADDR);
2997           else
2998             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEADDR);
2999
3000           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d,"
3001                 " #VPP-TBD#", getpid (),
3002                 VCL_SESS_ATTR_TEST (session->attr,
3003                                     VCL_SESS_ATTR_REUSEADDR), *buflen);
3004         }
3005       else
3006         rv = VPPCOM_EINVAL;
3007       break;
3008
3009     case VPPCOM_ATTR_GET_REUSEPORT:
3010       if (buffer && buflen && (*buflen >= sizeof (int)))
3011         {
3012           /* VPP-TBD */
3013           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3014                                                 VCL_SESS_ATTR_REUSEPORT);
3015           *buflen = sizeof (int);
3016
3017           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d,"
3018                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3019         }
3020       else
3021         rv = VPPCOM_EINVAL;
3022       break;
3023
3024     case VPPCOM_ATTR_SET_REUSEPORT:
3025       if (buffer && buflen && (*buflen == sizeof (int)) &&
3026           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
3027         {
3028           /* VPP-TBD */
3029           if (*(int *) buffer)
3030             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEPORT);
3031           else
3032             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEPORT);
3033
3034           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d,"
3035                 " #VPP-TBD#", getpid (),
3036                 VCL_SESS_ATTR_TEST (session->attr,
3037                                     VCL_SESS_ATTR_REUSEPORT), *buflen);
3038         }
3039       else
3040         rv = VPPCOM_EINVAL;
3041       break;
3042
3043     case VPPCOM_ATTR_GET_BROADCAST:
3044       if (buffer && buflen && (*buflen >= sizeof (int)))
3045         {
3046           /* VPP-TBD */
3047           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3048                                                 VCL_SESS_ATTR_BROADCAST);
3049           *buflen = sizeof (int);
3050
3051           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d,"
3052                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3053         }
3054       else
3055         rv = VPPCOM_EINVAL;
3056       break;
3057
3058     case VPPCOM_ATTR_SET_BROADCAST:
3059       if (buffer && buflen && (*buflen == sizeof (int)))
3060         {
3061           /* VPP-TBD */
3062           if (*(int *) buffer)
3063             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_BROADCAST);
3064           else
3065             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_BROADCAST);
3066
3067           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, "
3068                 "#VPP-TBD#", getpid (),
3069                 VCL_SESS_ATTR_TEST (session->attr,
3070                                     VCL_SESS_ATTR_BROADCAST), *buflen);
3071         }
3072       else
3073         rv = VPPCOM_EINVAL;
3074       break;
3075
3076     case VPPCOM_ATTR_GET_V6ONLY:
3077       if (buffer && buflen && (*buflen >= sizeof (int)))
3078         {
3079           /* VPP-TBD */
3080           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3081                                                 VCL_SESS_ATTR_V6ONLY);
3082           *buflen = sizeof (int);
3083
3084           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, "
3085                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3086         }
3087       else
3088         rv = VPPCOM_EINVAL;
3089       break;
3090
3091     case VPPCOM_ATTR_SET_V6ONLY:
3092       if (buffer && buflen && (*buflen == sizeof (int)))
3093         {
3094           /* VPP-TBD */
3095           if (*(int *) buffer)
3096             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_V6ONLY);
3097           else
3098             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_V6ONLY);
3099
3100           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, "
3101                 "#VPP-TBD#", getpid (),
3102                 VCL_SESS_ATTR_TEST (session->attr,
3103                                     VCL_SESS_ATTR_V6ONLY), *buflen);
3104         }
3105       else
3106         rv = VPPCOM_EINVAL;
3107       break;
3108
3109     case VPPCOM_ATTR_GET_KEEPALIVE:
3110       if (buffer && buflen && (*buflen >= sizeof (int)))
3111         {
3112           /* VPP-TBD */
3113           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3114                                                 VCL_SESS_ATTR_KEEPALIVE);
3115           *buflen = sizeof (int);
3116
3117           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, "
3118                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3119         }
3120       else
3121         rv = VPPCOM_EINVAL;
3122       break;
3123
3124     case VPPCOM_ATTR_SET_KEEPALIVE:
3125       if (buffer && buflen && (*buflen == sizeof (int)))
3126         {
3127           /* VPP-TBD */
3128           if (*(int *) buffer)
3129             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3130           else
3131             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3132
3133           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, "
3134                 "#VPP-TBD#", getpid (),
3135                 VCL_SESS_ATTR_TEST (session->attr,
3136                                     VCL_SESS_ATTR_KEEPALIVE), *buflen);
3137         }
3138       else
3139         rv = VPPCOM_EINVAL;
3140       break;
3141
3142     case VPPCOM_ATTR_GET_TCP_NODELAY:
3143       if (buffer && buflen && (*buflen >= sizeof (int)))
3144         {
3145           /* VPP-TBD */
3146           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3147                                                 VCL_SESS_ATTR_TCP_NODELAY);
3148           *buflen = sizeof (int);
3149
3150           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, "
3151                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3152         }
3153       else
3154         rv = VPPCOM_EINVAL;
3155       break;
3156
3157     case VPPCOM_ATTR_SET_TCP_NODELAY:
3158       if (buffer && buflen && (*buflen == sizeof (int)))
3159         {
3160           /* VPP-TBD */
3161           if (*(int *) buffer)
3162             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3163           else
3164             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3165
3166           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, "
3167                 "#VPP-TBD#", getpid (),
3168                 VCL_SESS_ATTR_TEST (session->attr,
3169                                     VCL_SESS_ATTR_TCP_NODELAY), *buflen);
3170         }
3171       else
3172         rv = VPPCOM_EINVAL;
3173       break;
3174
3175     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3176       if (buffer && buflen && (*buflen >= sizeof (int)))
3177         {
3178           /* VPP-TBD */
3179           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3180                                                 VCL_SESS_ATTR_TCP_KEEPIDLE);
3181           *buflen = sizeof (int);
3182
3183           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, "
3184                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3185         }
3186       else
3187         rv = VPPCOM_EINVAL;
3188       break;
3189
3190     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3191       if (buffer && buflen && (*buflen == sizeof (int)))
3192         {
3193           /* VPP-TBD */
3194           if (*(int *) buffer)
3195             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3196           else
3197             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3198
3199           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, "
3200                 "#VPP-TBD#", getpid (),
3201                 VCL_SESS_ATTR_TEST (session->attr,
3202                                     VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3203         }
3204       else
3205         rv = VPPCOM_EINVAL;
3206       break;
3207
3208     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3209       if (buffer && buflen && (*buflen >= sizeof (int)))
3210         {
3211           /* VPP-TBD */
3212           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3213                                                 VCL_SESS_ATTR_TCP_KEEPINTVL);
3214           *buflen = sizeof (int);
3215
3216           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, "
3217                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3218         }
3219       else
3220         rv = VPPCOM_EINVAL;
3221       break;
3222
3223     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3224       if (buffer && buflen && (*buflen == sizeof (int)))
3225         {
3226           /* VPP-TBD */
3227           if (*(int *) buffer)
3228             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3229           else
3230             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3231
3232           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, "
3233                 "#VPP-TBD#", getpid (),
3234                 VCL_SESS_ATTR_TEST (session->attr,
3235                                     VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3236         }
3237       else
3238         rv = VPPCOM_EINVAL;
3239       break;
3240
3241     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3242       if (buffer && buflen && (*buflen >= sizeof (u32)))
3243         {
3244           /* VPP-TBD */
3245           *(u32 *) buffer = session->user_mss;
3246           *buflen = sizeof (int);
3247
3248           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d,"
3249                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3250         }
3251       else
3252         rv = VPPCOM_EINVAL;
3253       break;
3254
3255     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3256       if (buffer && buflen && (*buflen == sizeof (u32)))
3257         {
3258           /* VPP-TBD */
3259           session->user_mss = *(u32 *) buffer;
3260
3261           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d, "
3262                 "#VPP-TBD#", getpid (), session->user_mss, *buflen);
3263         }
3264       else
3265         rv = VPPCOM_EINVAL;
3266       break;
3267
3268     case VPPCOM_ATTR_GET_REFCNT:
3269       rv = vcl_session_get_refcnt (session);
3270       break;
3271
3272     default:
3273       rv = VPPCOM_EINVAL;
3274       break;
3275     }
3276
3277   return rv;
3278 }
3279
3280 int
3281 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3282                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3283 {
3284   vcl_worker_t *wrk = vcl_worker_get_current ();
3285   int rv = VPPCOM_OK;
3286   vcl_session_t *session = 0;
3287
3288   if (ep)
3289     {
3290       session = vcl_session_get_w_handle (wrk, session_handle);
3291       if (PREDICT_FALSE (!session))
3292         {
3293           VDBG (0, "VCL<%d>: invalid session, sid (%u) has been closed!",
3294                 getpid (), session_handle);
3295           return VPPCOM_EBADFD;
3296         }
3297       ep->is_ip4 = session->transport.is_ip4;
3298       ep->port = session->transport.rmt_port;
3299     }
3300
3301   if (flags == 0)
3302     rv = vppcom_session_read (session_handle, buffer, buflen);
3303   else if (flags & MSG_PEEK)
3304     rv = vppcom_session_peek (session_handle, buffer, buflen);
3305   else
3306     {
3307       clib_warning ("VCL<%d>: Unsupport flags for recvfrom %d",
3308                     getpid (), flags);
3309       return VPPCOM_EAFNOSUPPORT;
3310     }
3311
3312   if (ep)
3313     {
3314       if (session->transport.is_ip4)
3315         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3316                           sizeof (ip4_address_t));
3317       else
3318         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3319                           sizeof (ip6_address_t));
3320     }
3321
3322   return rv;
3323 }
3324
3325 int
3326 vppcom_session_sendto (uint32_t session_handle, void *buffer,
3327                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3328 {
3329   if (!buffer)
3330     return VPPCOM_EINVAL;
3331
3332   if (ep)
3333     {
3334       // TBD
3335       return VPPCOM_EINVAL;
3336     }
3337
3338   if (flags)
3339     {
3340       // TBD check the flags and do the right thing
3341       VDBG (2, "VCL<%d>: handling flags 0x%u (%d) not implemented yet.",
3342             getpid (), flags, flags);
3343     }
3344
3345   return (vppcom_session_write (session_handle, buffer, buflen));
3346 }
3347
3348 int
3349 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
3350 {
3351   vcl_worker_t *wrk = vcl_worker_get_current ();
3352   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
3353   u32 i, keep_trying = 1;
3354   svm_msg_q_msg_t msg;
3355   session_event_t *e;
3356   int rv, num_ev = 0;
3357
3358   VDBG (3, "VCL<%d>: vp %p, nsids %u, wait_for_time %f",
3359         getpid (), vp, n_sids, wait_for_time);
3360
3361   if (!vp)
3362     return VPPCOM_EFAULT;
3363
3364   do
3365     {
3366       vcl_session_t *session;
3367
3368       /* Dequeue all events and drop all unhandled io events */
3369       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
3370         {
3371           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
3372           vcl_handle_mq_event (wrk, e);
3373           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
3374         }
3375       vec_reset_length (wrk->unhandled_evts_vector);
3376
3377       for (i = 0; i < n_sids; i++)
3378         {
3379           session = vcl_session_get (wrk, vp[i].sid);
3380           if (!session)
3381             {
3382               vp[i].revents = POLLHUP;
3383               num_ev++;
3384               continue;
3385             }
3386
3387           vp[i].revents = 0;
3388
3389           if (POLLIN & vp[i].events)
3390             {
3391               rv = vppcom_session_read_ready (session);
3392               if (rv > 0)
3393                 {
3394                   vp[i].revents |= POLLIN;
3395                   num_ev++;
3396                 }
3397               else if (rv < 0)
3398                 {
3399                   switch (rv)
3400                     {
3401                     case VPPCOM_ECONNRESET:
3402                       vp[i].revents = POLLHUP;
3403                       break;
3404
3405                     default:
3406                       vp[i].revents = POLLERR;
3407                       break;
3408                     }
3409                   num_ev++;
3410                 }
3411             }
3412
3413           if (POLLOUT & vp[i].events)
3414             {
3415               rv = vppcom_session_write_ready (session);
3416               if (rv > 0)
3417                 {
3418                   vp[i].revents |= POLLOUT;
3419                   num_ev++;
3420                 }
3421               else if (rv < 0)
3422                 {
3423                   switch (rv)
3424                     {
3425                     case VPPCOM_ECONNRESET:
3426                       vp[i].revents = POLLHUP;
3427                       break;
3428
3429                     default:
3430                       vp[i].revents = POLLERR;
3431                       break;
3432                     }
3433                   num_ev++;
3434                 }
3435             }
3436
3437           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
3438             {
3439               vp[i].revents = POLLNVAL;
3440               num_ev++;
3441             }
3442         }
3443       if (wait_for_time != -1)
3444         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
3445     }
3446   while ((num_ev == 0) && keep_trying);
3447
3448   if (VPPCOM_DEBUG > 3)
3449     {
3450       clib_warning ("VCL<%d>: returning %d", getpid (), num_ev);
3451       for (i = 0; i < n_sids; i++)
3452         {
3453           clib_warning ("VCL<%d>: vp[%d].sid %d (0x%x), .events 0x%x, "
3454                         ".revents 0x%x", getpid (), i, vp[i].sid, vp[i].sid,
3455                         vp[i].events, vp[i].revents);
3456         }
3457     }
3458   return num_ev;
3459 }
3460
3461 int
3462 vppcom_mq_epoll_fd (void)
3463 {
3464   vcl_worker_t *wrk = vcl_worker_get_current ();
3465   return wrk->mqs_epfd;
3466 }
3467
3468 int
3469 vppcom_session_index (uint32_t session_handle)
3470 {
3471   return session_handle & 0xFFFFFF;
3472 }
3473
3474 int
3475 vppcom_session_handle (uint32_t session_index)
3476 {
3477   return (vcl_get_worker_index () << 24) | session_index;
3478 }
3479
3480 int
3481 vppcom_worker_register (void)
3482 {
3483   if (!vcl_worker_alloc_and_init ())
3484     return VPPCOM_EEXIST;
3485
3486   if (vcl_worker_set_bapi ())
3487     return VPPCOM_EEXIST;
3488
3489   if (vcl_worker_register_with_vpp ())
3490     return VPPCOM_EEXIST;
3491
3492   return VPPCOM_OK;
3493 }
3494
3495 int
3496 vppcom_worker_index (void)
3497 {
3498   return vcl_get_worker_index ();
3499 }
3500
3501 /*
3502  * fd.io coding-style-patch-verification: ON
3503  *
3504  * Local Variables:
3505  * eval: (c-set-style "gnu")
3506  * End:
3507  */