vcl: test refactor and improvements
[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 void
701 vcl_app_fork_child_handler (void)
702 {
703   u8 *child_name;
704   int rv, parent_wrk;
705
706   parent_wrk = vcl_get_worker_index ();
707   VDBG (0, "initializing forked child with parent wrk %u", parent_wrk);
708
709   /*
710    * Allocate worker
711    */
712   vcl_set_worker_index (~0);
713   if (!vcl_worker_alloc_and_init ())
714     VERR ("couldn't allocate new worker");
715
716   /*
717    * Attach to binary api
718    */
719   child_name = format (0, "%v-child-%u%c", vcm->app_name, getpid (), 0);
720   vcl_cleanup_bapi ();
721   vppcom_api_hookup ();
722   vcm->app_state = STATE_APP_START;
723   rv = vppcom_connect_to_vpp ((char *) child_name);
724   vec_free (child_name);
725   if (rv)
726     {
727       VERR ("couldn't connect to VPP!");
728       return;
729     }
730
731   /*
732    * Register worker with vpp and share sessions
733    */
734   vcl_worker_register_with_vpp ();
735   vcl_worker_share_sessions (parent_wrk);
736
737   VDBG (0, "forked child main worker initialized");
738   vcm->forking = 0;
739 }
740
741 void
742 vcl_app_fork_parent_handler (void)
743 {
744   vcm->forking = 1;
745
746   while (vcm->forking)
747     ;
748 }
749
750 /**
751  * Handle app exit
752  *
753  * Notify vpp of the disconnect and mark the worker as free. If we're the
754  * last worker, do a full cleanup otherwise, since we're probably a forked
755  * child, avoid syscalls as much as possible. We might've lost privileges.
756  */
757 void
758 vppcom_app_exit (void)
759 {
760   if (!pool_elts (vcm->workers))
761     return;
762
763   vcl_worker_cleanup (1 /* notify vpp */ );
764   vcl_elog_stop (vcm);
765   if (vec_len (vcm->workers) == 1)
766     vl_client_disconnect_from_vlib ();
767   else
768     vl_client_send_disconnect ();
769 }
770
771 /*
772  * VPPCOM Public API functions
773  */
774 int
775 vppcom_app_create (char *app_name)
776 {
777   vppcom_cfg_t *vcl_cfg = &vcm->cfg;
778   int rv;
779
780   if (vcm->is_init)
781     {
782       clib_warning ("vcl<%u> already initialized", getpid ());
783       return -1;
784     }
785
786   vcm->is_init = 1;
787   vppcom_cfg (&vcm->cfg);
788   vcl_cfg = &vcm->cfg;
789
790   vcm->main_cpu = pthread_self ();
791   vcm->main_pid = getpid ();
792   vcm->app_name = format (0, "%s", app_name);
793   vppcom_init_error_string_table ();
794   svm_fifo_segment_main_init (&vcm->segment_main, vcl_cfg->segment_baseva,
795                               20 /* timeout in secs */ );
796   pool_alloc (vcm->workers, vcl_cfg->max_workers);
797   clib_spinlock_init (&vcm->workers_lock);
798   clib_rwlock_init (&vcm->segment_table_lock);
799   pthread_atfork (NULL, vcl_app_fork_parent_handler,
800                   vcl_app_fork_child_handler);
801   atexit (vppcom_app_exit);
802
803   /* Allocate default worker */
804   vcl_worker_alloc_and_init ();
805
806   /* API hookup and connect to VPP */
807   vppcom_api_hookup ();
808   vcl_elog_init (vcm);
809   vcm->app_state = STATE_APP_START;
810   rv = vppcom_connect_to_vpp (app_name);
811   if (rv)
812     {
813       VERR ("couldn't connect to VPP!");
814       return rv;
815     }
816   VDBG (0, "sending session enable");
817   rv = vppcom_app_session_enable ();
818   if (rv)
819     {
820       VERR ("vppcom_app_session_enable() failed!");
821       return rv;
822     }
823
824   VDBG (0, "sending app attach");
825   rv = vppcom_app_attach ();
826   if (rv)
827     {
828       VERR ("vppcom_app_attach() failed!");
829       return rv;
830     }
831
832   VDBG (0, "app_name '%s', my_client_index %d (0x%x)", app_name,
833         vcm->workers[0].my_client_index, vcm->workers[0].my_client_index);
834
835   return VPPCOM_OK;
836 }
837
838 void
839 vppcom_app_destroy (void)
840 {
841   int rv;
842   f64 orig_app_timeout;
843
844   if (!pool_elts (vcm->workers))
845     return;
846
847   vcl_evt (VCL_EVT_DETACH, vcm);
848
849   if (pool_elts (vcm->workers) == 1)
850     {
851       vppcom_app_send_detach ();
852       orig_app_timeout = vcm->cfg.app_timeout;
853       vcm->cfg.app_timeout = 2.0;
854       rv = vcl_wait_for_app_state_change (STATE_APP_ENABLED);
855       vcm->cfg.app_timeout = orig_app_timeout;
856       if (PREDICT_FALSE (rv))
857         VDBG (0, "application detach timed out! returning %d (%s)", rv,
858               vppcom_retval_str (rv));
859       vec_free (vcm->app_name);
860       vcl_worker_cleanup (0 /* notify vpp */ );
861     }
862   else
863     {
864       vcl_worker_cleanup (1 /* notify vpp */ );
865     }
866
867   vcl_elog_stop (vcm);
868   vl_client_disconnect_from_vlib ();
869 }
870
871 int
872 vppcom_session_create (u8 proto, u8 is_nonblocking)
873 {
874   vcl_worker_t *wrk = vcl_worker_get_current ();
875   vcl_session_t *session;
876
877   session = vcl_session_alloc (wrk);
878
879   session->session_type = proto;
880   session->session_state = STATE_START;
881   session->vpp_handle = ~0;
882   session->is_dgram = proto == VPPCOM_PROTO_UDP;
883
884   if (is_nonblocking)
885     VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
886
887   vcl_evt (VCL_EVT_CREATE, session, session_type, session->session_state,
888            is_nonblocking, session_index);
889
890   VDBG (0, "created sid %u", session->session_index);
891
892   return vcl_session_handle (session);
893 }
894
895 int
896 vppcom_session_close (uint32_t session_handle)
897 {
898   vcl_worker_t *wrk = vcl_worker_get_current ();
899   u8 is_vep, do_disconnect = 1;
900   vcl_session_t *session = 0;
901   session_state_t state;
902   u32 next_sh, vep_sh;
903   int rv = VPPCOM_OK;
904   u64 vpp_handle;
905
906   session = vcl_session_get_w_handle (wrk, session_handle);
907   if (!session)
908     return VPPCOM_EBADFD;
909
910   if (session->shared_index != ~0)
911     do_disconnect = vcl_worker_unshare_session (wrk, session);
912
913   is_vep = session->is_vep;
914   next_sh = session->vep.next_sh;
915   vep_sh = session->vep.vep_sh;
916   state = session->session_state;
917   vpp_handle = session->vpp_handle;
918
919   VDBG (0, "Closing session handle %u vpp handle %u", session_handle,
920         vpp_handle);
921
922   if (is_vep)
923     {
924       while (next_sh != ~0)
925         {
926           rv = vppcom_epoll_ctl (session_handle, EPOLL_CTL_DEL, next_sh, 0);
927           if (PREDICT_FALSE (rv < 0))
928             VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u"
929                   " failed! rv %d (%s)", vpp_handle, next_sh, vep_sh, rv,
930                   vppcom_retval_str (rv));
931
932           next_sh = session->vep.next_sh;
933         }
934     }
935   else
936     {
937       if (session->is_vep_session)
938         {
939           rv = vppcom_epoll_ctl (vep_sh, EPOLL_CTL_DEL, session_handle, 0);
940           if (rv < 0)
941             VDBG (0, "vpp handle 0x%llx, sid %u: EPOLL_CTL_DEL vep_idx %u "
942                   "failed! rv %d (%s)", vpp_handle, session_handle, vep_sh,
943                   rv, vppcom_retval_str (rv));
944         }
945
946       if (!do_disconnect)
947         goto cleanup;
948
949       if (state & STATE_LISTEN)
950         {
951           rv = vppcom_session_unbind (session_handle);
952           if (PREDICT_FALSE (rv < 0))
953             VDBG (0, "vpp handle 0x%llx, sid %u: listener unbind failed! "
954                   "rv %d (%s)", vpp_handle, session_handle, rv,
955                   vppcom_retval_str (rv));
956         }
957       else if (state & STATE_OPEN)
958         {
959           rv = vppcom_session_disconnect (session_handle);
960           if (PREDICT_FALSE (rv < 0))
961             clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
962                           "session disconnect failed! rv %d (%s)",
963                           getpid (), vpp_handle, session_handle,
964                           rv, vppcom_retval_str (rv));
965         }
966     }
967
968 cleanup:
969
970   if (vcl_session_is_ct (session))
971     {
972       vcl_cut_through_registration_t *ctr;
973       uword mq_addr;
974
975       mq_addr = pointer_to_uword (session->our_evt_q);
976       ctr = vcl_ct_registration_lock_and_lookup (wrk, mq_addr);
977       ASSERT (ctr);
978       if (ctr->epoll_evt_conn_index != ~0)
979         vcl_mq_epoll_del_evfd (wrk, ctr->epoll_evt_conn_index);
980       VDBG (0, "Removing ct registration %u",
981             vcl_ct_registration_index (wrk, ctr));
982       vcl_ct_registration_del (wrk, ctr);
983       vcl_ct_registration_lookup_del (wrk, mq_addr);
984       vcl_ct_registration_unlock (wrk);
985     }
986
987   if (vpp_handle != ~0)
988     {
989       vcl_session_table_del_vpp_handle (wrk, vpp_handle);
990     }
991   vcl_session_free (wrk, session);
992
993   VDBG (0, "session handle %u vpp handle %u removed", session_handle,
994         vpp_handle);
995
996   vcl_evt (VCL_EVT_CLOSE, session, rv);
997
998   return rv;
999 }
1000
1001 int
1002 vppcom_session_bind (uint32_t session_handle, vppcom_endpt_t * ep)
1003 {
1004   vcl_worker_t *wrk = vcl_worker_get_current ();
1005   vcl_session_t *session = 0;
1006
1007   if (!ep || !ep->ip)
1008     return VPPCOM_EINVAL;
1009
1010   session = vcl_session_get_w_handle (wrk, session_handle);
1011   if (!session)
1012     return VPPCOM_EBADFD;
1013
1014   if (session->is_vep)
1015     {
1016       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
1017                     "bind to an epoll session!", getpid (), session_handle);
1018       return VPPCOM_EBADFD;
1019     }
1020
1021   session->transport.is_ip4 = ep->is_ip4;
1022   if (ep->is_ip4)
1023     clib_memcpy_fast (&session->transport.lcl_ip.ip4, ep->ip,
1024                       sizeof (ip4_address_t));
1025   else
1026     clib_memcpy_fast (&session->transport.lcl_ip.ip6, ep->ip,
1027                       sizeof (ip6_address_t));
1028   session->transport.lcl_port = ep->port;
1029
1030   VDBG (0, "VCL<%d>: sid %u: binding to local %s address %U port %u, "
1031         "proto %s", getpid (), session_handle,
1032         session->transport.is_ip4 ? "IPv4" : "IPv6",
1033         format_ip46_address, &session->transport.lcl_ip,
1034         session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1035         clib_net_to_host_u16 (session->transport.lcl_port),
1036         session->session_type ? "UDP" : "TCP");
1037   vcl_evt (VCL_EVT_BIND, session);
1038
1039   if (session->session_type == VPPCOM_PROTO_UDP)
1040     vppcom_session_listen (session_handle, 10);
1041
1042   return VPPCOM_OK;
1043 }
1044
1045 int
1046 vppcom_session_listen (uint32_t listen_sh, uint32_t q_len)
1047 {
1048   vcl_worker_t *wrk = vcl_worker_get_current ();
1049   vcl_session_t *listen_session = 0;
1050   u64 listen_vpp_handle;
1051   int rv;
1052
1053   listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1054   if (!listen_session)
1055     return VPPCOM_EBADFD;
1056
1057   if (q_len == 0 || q_len == ~0)
1058     q_len = vcm->cfg.listen_queue_size;
1059
1060   if (listen_session->is_vep)
1061     {
1062       clib_warning ("VCL<%d>: ERROR: sid %u: cannot listen on an "
1063                     "epoll session!", getpid (), listen_sh);
1064       return VPPCOM_EBADFD;
1065     }
1066
1067   listen_vpp_handle = listen_session->vpp_handle;
1068   if (listen_session->session_state & STATE_LISTEN)
1069     {
1070       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: already in listen state!",
1071             getpid (), listen_vpp_handle, listen_sh);
1072       return VPPCOM_OK;
1073     }
1074
1075   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: sending VPP bind+listen "
1076         "request...", getpid (), listen_vpp_handle, listen_sh);
1077
1078   /*
1079    * Send listen request to vpp and wait for reply
1080    */
1081   vppcom_send_bind_sock (listen_session);
1082   rv = vppcom_wait_for_session_state_change (listen_session->session_index,
1083                                              STATE_LISTEN,
1084                                              vcm->cfg.session_timeout);
1085
1086   if (PREDICT_FALSE (rv))
1087     {
1088       listen_session = vcl_session_get_w_handle (wrk, listen_sh);
1089       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: bind+listen failed! "
1090             "returning %d (%s)", getpid (), listen_session->vpp_handle,
1091             listen_sh, rv, vppcom_retval_str (rv));
1092       return rv;
1093     }
1094
1095   return VPPCOM_OK;
1096 }
1097
1098 static int
1099 validate_args_session_accept_ (vcl_worker_t * wrk,
1100                                vcl_session_t * listen_session)
1101 {
1102   /* Input validation - expects spinlock on sessions_lockp */
1103   if (listen_session->is_vep)
1104     {
1105       clib_warning ("VCL<%d>: ERROR: sid %u: cannot accept on an "
1106                     "epoll session!", getpid (),
1107                     listen_session->session_index);
1108       return VPPCOM_EBADFD;
1109     }
1110
1111   if (listen_session->session_state != STATE_LISTEN)
1112     {
1113       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1114                     "not in listen state! state 0x%x (%s)", getpid (),
1115                     listen_session->vpp_handle, listen_session->session_index,
1116                     listen_session->session_state,
1117                     vppcom_session_state_str (listen_session->session_state));
1118       return VPPCOM_EBADFD;
1119     }
1120   return VPPCOM_OK;
1121 }
1122
1123 int
1124 vppcom_session_accept (uint32_t listen_session_handle, vppcom_endpt_t * ep,
1125                        uint32_t flags)
1126 {
1127   u32 client_session_index = ~0, listen_session_index;
1128   vcl_worker_t *wrk = vcl_worker_get_current ();
1129   session_accepted_msg_t accepted_msg;
1130   vcl_session_t *listen_session = 0;
1131   vcl_session_t *client_session = 0;
1132   svm_msg_q_t *vpp_evt_q;
1133   vcl_session_msg_t *evt;
1134   u64 listen_vpp_handle;
1135   svm_msg_q_msg_t msg;
1136   session_event_t *e;
1137   u8 is_nonblocking;
1138   int rv;
1139
1140   listen_session = vcl_session_get_w_handle (wrk, listen_session_handle);
1141   if (!listen_session)
1142     return VPPCOM_EBADFD;
1143
1144   listen_session_index = listen_session->session_index;
1145   if ((rv = validate_args_session_accept_ (wrk, listen_session)))
1146     return rv;
1147
1148   if (clib_fifo_elts (listen_session->accept_evts_fifo))
1149     {
1150       clib_fifo_sub2 (listen_session->accept_evts_fifo, evt);
1151       accepted_msg = evt->accepted_msg;
1152       goto handle;
1153     }
1154
1155   is_nonblocking = VCL_SESS_ATTR_TEST (listen_session->attr,
1156                                        VCL_SESS_ATTR_NONBLOCK);
1157   if (svm_msg_q_is_empty (wrk->app_event_queue) && is_nonblocking)
1158     return VPPCOM_EAGAIN;
1159
1160   while (1)
1161     {
1162       if (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_WAIT, 0))
1163         return VPPCOM_EAGAIN;
1164
1165       e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
1166       if (e->event_type != SESSION_CTRL_EVT_ACCEPTED)
1167         {
1168           clib_warning ("discarded event: %u", e->event_type);
1169           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1170           continue;
1171         }
1172       clib_memcpy_fast (&accepted_msg, e->data, sizeof (accepted_msg));
1173       svm_msg_q_free_msg (wrk->app_event_queue, &msg);
1174       break;
1175     }
1176
1177 handle:
1178
1179   client_session_index = vcl_session_accepted_handler (wrk, &accepted_msg);
1180   listen_session = vcl_session_get (wrk, listen_session_index);
1181   client_session = vcl_session_get (wrk, client_session_index);
1182
1183   if (flags & O_NONBLOCK)
1184     VCL_SESS_ATTR_SET (client_session->attr, VCL_SESS_ATTR_NONBLOCK);
1185
1186   listen_vpp_handle = listen_session->vpp_handle;
1187   VDBG (0, "vpp handle 0x%llx, sid %u: Got a client request! "
1188         "vpp handle 0x%llx, sid %u, flags %d, is_nonblocking %u",
1189         listen_vpp_handle, listen_session_handle,
1190         client_session->vpp_handle, client_session_index,
1191         flags, VCL_SESS_ATTR_TEST (client_session->attr,
1192                                    VCL_SESS_ATTR_NONBLOCK));
1193
1194   if (ep)
1195     {
1196       ep->is_ip4 = client_session->transport.is_ip4;
1197       ep->port = client_session->transport.rmt_port;
1198       if (client_session->transport.is_ip4)
1199         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip4,
1200                           sizeof (ip4_address_t));
1201       else
1202         clib_memcpy_fast (ep->ip, &client_session->transport.rmt_ip.ip6,
1203                           sizeof (ip6_address_t));
1204     }
1205
1206   if (accepted_msg.server_event_queue_address)
1207     vpp_evt_q = uword_to_pointer (accepted_msg.vpp_event_queue_address,
1208                                   svm_msg_q_t *);
1209   else
1210     vpp_evt_q = client_session->vpp_evt_q;
1211
1212   vcl_send_session_accepted_reply (vpp_evt_q, client_session->client_context,
1213                                    client_session->vpp_handle, 0);
1214
1215   VDBG (0, "vpp handle 0x%llx, sid %u: accepted vpp handle 0x%llx, "
1216         "sid %u connection from peer %s address %U port %u to local %s "
1217         "address %U port %u", listen_vpp_handle,
1218         listen_session_handle, client_session->vpp_handle,
1219         client_session_index,
1220         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
1221         format_ip46_address, &client_session->transport.rmt_ip,
1222         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1223         clib_net_to_host_u16 (client_session->transport.rmt_port),
1224         client_session->transport.is_ip4 ? "IPv4" : "IPv6",
1225         format_ip46_address, &client_session->transport.lcl_ip,
1226         client_session->transport.is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
1227         clib_net_to_host_u16 (client_session->transport.lcl_port));
1228   vcl_evt (VCL_EVT_ACCEPT, client_session, listen_session,
1229            client_session_index);
1230
1231   return vcl_session_handle (client_session);
1232 }
1233
1234 int
1235 vppcom_session_connect (uint32_t session_handle, vppcom_endpt_t * server_ep)
1236 {
1237   vcl_worker_t *wrk = vcl_worker_get_current ();
1238   vcl_session_t *session = 0;
1239   u32 session_index;
1240   int rv;
1241
1242   session = vcl_session_get_w_handle (wrk, session_handle);
1243   if (!session)
1244     return VPPCOM_EBADFD;
1245   session_index = session->session_index;
1246
1247   if (PREDICT_FALSE (session->is_vep))
1248     {
1249       clib_warning ("VCL<%d>: ERROR: sid %u: cannot "
1250                     "connect on an epoll session!", getpid (),
1251                     session_handle);
1252       return VPPCOM_EBADFD;
1253     }
1254
1255   if (PREDICT_FALSE (session->session_state & CLIENT_STATE_OPEN))
1256     {
1257       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: session already "
1258             "connected to %s %U port %d proto %s, state 0x%x (%s)",
1259             getpid (), session->vpp_handle, session_handle,
1260             session->transport.is_ip4 ? "IPv4" : "IPv6",
1261             format_ip46_address,
1262             &session->transport.rmt_ip, session->transport.is_ip4 ?
1263             IP46_TYPE_IP4 : IP46_TYPE_IP6,
1264             clib_net_to_host_u16 (session->transport.rmt_port),
1265             session->session_type ? "UDP" : "TCP", session->session_state,
1266             vppcom_session_state_str (session->session_state));
1267       return VPPCOM_OK;
1268     }
1269
1270   session->transport.is_ip4 = server_ep->is_ip4;
1271   if (session->transport.is_ip4)
1272     clib_memcpy_fast (&session->transport.rmt_ip.ip4, server_ep->ip,
1273                       sizeof (ip4_address_t));
1274   else
1275     clib_memcpy_fast (&session->transport.rmt_ip.ip6, server_ep->ip,
1276                       sizeof (ip6_address_t));
1277   session->transport.rmt_port = server_ep->port;
1278
1279   VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connecting to server %s %U "
1280         "port %d proto %s",
1281         getpid (), session->vpp_handle, session_handle,
1282         session->transport.is_ip4 ? "IPv4" : "IPv6",
1283         format_ip46_address,
1284         &session->transport.rmt_ip, session->transport.is_ip4 ?
1285         IP46_TYPE_IP4 : IP46_TYPE_IP6,
1286         clib_net_to_host_u16 (session->transport.rmt_port),
1287         session->session_type ? "UDP" : "TCP");
1288
1289   /*
1290    * Send connect request and wait for reply from vpp
1291    */
1292   vppcom_send_connect_sock (session);
1293   rv = vppcom_wait_for_session_state_change (session_index, STATE_CONNECT,
1294                                              vcm->cfg.session_timeout);
1295
1296   session = vcl_session_get (wrk, session_index);
1297
1298   if (PREDICT_FALSE (rv))
1299     {
1300       if (VPPCOM_DEBUG > 0)
1301         {
1302           if (session)
1303             clib_warning ("VCL<%d>: vpp handle 0x%llx, sid %u: connect "
1304                           "failed! returning %d (%s)", getpid (),
1305                           session->vpp_handle, session_handle, rv,
1306                           vppcom_retval_str (rv));
1307           else
1308             clib_warning ("VCL<%d>: no session for sid %u: connect failed! "
1309                           "returning %d (%s)", getpid (),
1310                           session_handle, rv, vppcom_retval_str (rv));
1311         }
1312     }
1313   else
1314     VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: connected!",
1315           getpid (), session->vpp_handle, session_handle);
1316
1317   return rv;
1318 }
1319
1320 static u8
1321 vcl_is_rx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1322 {
1323   if (!is_ct)
1324     return (e->event_type == FIFO_EVENT_APP_RX
1325             && e->fifo->client_session_index == sid);
1326   else
1327     return (e->event_type == SESSION_IO_EVT_CT_TX);
1328 }
1329
1330 static inline u8
1331 vcl_session_is_readable (vcl_session_t * s)
1332 {
1333   return ((s->session_state & STATE_OPEN)
1334           || (s->session_state == STATE_LISTEN
1335               && s->session_type == VPPCOM_PROTO_UDP));
1336 }
1337
1338 static inline int
1339 vppcom_session_read_internal (uint32_t session_handle, void *buf, int n,
1340                               u8 peek)
1341 {
1342   vcl_worker_t *wrk = vcl_worker_get_current ();
1343   int n_read = 0, rv, is_nonblocking;
1344   vcl_session_t *s = 0;
1345   svm_fifo_t *rx_fifo;
1346   svm_msg_q_msg_t msg;
1347   session_event_t *e;
1348   svm_msg_q_t *mq;
1349   u8 is_ct;
1350
1351   if (PREDICT_FALSE (!buf))
1352     return VPPCOM_EINVAL;
1353
1354   s = vcl_session_get_w_handle (wrk, session_handle);
1355   if (PREDICT_FALSE (!s || s->is_vep))
1356     return VPPCOM_EBADFD;
1357
1358   if (PREDICT_FALSE (!vcl_session_is_readable (s)))
1359     {
1360       session_state_t state = s->session_state;
1361       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1362
1363       VDBG (0, "VCL<%d>: vpp handle 0x%llx, sid %u: %s session is not open! "
1364             "state 0x%x (%s), returning %d (%s)",
1365             getpid (), s->vpp_handle, session_handle, state,
1366             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1367       return rv;
1368     }
1369
1370   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1371   is_ct = vcl_session_is_ct (s);
1372   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1373   rx_fifo = s->rx_fifo;
1374   s->has_rx_evt = 0;
1375
1376   if (svm_fifo_is_empty (rx_fifo))
1377     {
1378       if (is_nonblocking)
1379         {
1380           svm_fifo_unset_event (rx_fifo);
1381           return VPPCOM_EWOULDBLOCK;
1382         }
1383       while (svm_fifo_is_empty (rx_fifo))
1384         {
1385           svm_fifo_unset_event (rx_fifo);
1386           svm_msg_q_lock (mq);
1387           if (svm_msg_q_is_empty (mq))
1388             svm_msg_q_wait (mq);
1389
1390           svm_msg_q_sub_w_lock (mq, &msg);
1391           e = svm_msg_q_msg_data (mq, &msg);
1392           svm_msg_q_unlock (mq);
1393           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1394             {
1395               vcl_handle_mq_event (wrk, e);
1396               svm_msg_q_free_msg (mq, &msg);
1397               continue;
1398             }
1399           svm_msg_q_free_msg (mq, &msg);
1400
1401           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1402             return 0;
1403         }
1404     }
1405
1406   if (s->is_dgram)
1407     n_read = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek);
1408   else
1409     n_read = app_recv_stream_raw (rx_fifo, buf, n, 0, peek);
1410
1411   if (svm_fifo_is_empty (rx_fifo))
1412     svm_fifo_unset_event (rx_fifo);
1413
1414   if (is_ct && svm_fifo_want_tx_evt (rx_fifo))
1415     {
1416       svm_fifo_set_want_tx_evt (s->rx_fifo, 0);
1417       app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo, SESSION_IO_EVT_CT_RX,
1418                               SVM_Q_WAIT);
1419     }
1420
1421   VDBG (2, "VCL<%d>: vpp handle 0x%llx, sid %u: read %d bytes from (%p)",
1422         getpid (), s->vpp_handle, session_handle, n_read, rx_fifo);
1423
1424   return n_read;
1425 }
1426
1427 int
1428 vppcom_session_read (uint32_t session_handle, void *buf, size_t n)
1429 {
1430   return (vppcom_session_read_internal (session_handle, buf, n, 0));
1431 }
1432
1433 static int
1434 vppcom_session_peek (uint32_t session_handle, void *buf, int n)
1435 {
1436   return (vppcom_session_read_internal (session_handle, buf, n, 1));
1437 }
1438
1439 int
1440 vppcom_session_read_segments (uint32_t session_handle,
1441                               vppcom_data_segments_t ds)
1442 {
1443   vcl_worker_t *wrk = vcl_worker_get_current ();
1444   int n_read = 0, rv, is_nonblocking;
1445   vcl_session_t *s = 0;
1446   svm_fifo_t *rx_fifo;
1447   svm_msg_q_msg_t msg;
1448   session_event_t *e;
1449   svm_msg_q_t *mq;
1450   u8 is_ct;
1451
1452   s = vcl_session_get_w_handle (wrk, session_handle);
1453   if (PREDICT_FALSE (!s || s->is_vep))
1454     return VPPCOM_EBADFD;
1455
1456   if (PREDICT_FALSE (!vcl_session_is_readable (s)))
1457     {
1458       session_state_t state = s->session_state;
1459       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1460       return rv;
1461     }
1462
1463   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1464   is_ct = vcl_session_is_ct (s);
1465   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1466   rx_fifo = s->rx_fifo;
1467   s->has_rx_evt = 0;
1468
1469   if (svm_fifo_is_empty (rx_fifo))
1470     {
1471       if (is_nonblocking)
1472         {
1473           svm_fifo_unset_event (rx_fifo);
1474           return VPPCOM_EWOULDBLOCK;
1475         }
1476       while (svm_fifo_is_empty (rx_fifo))
1477         {
1478           svm_fifo_unset_event (rx_fifo);
1479           svm_msg_q_lock (mq);
1480           if (svm_msg_q_is_empty (mq))
1481             svm_msg_q_wait (mq);
1482
1483           svm_msg_q_sub_w_lock (mq, &msg);
1484           e = svm_msg_q_msg_data (mq, &msg);
1485           svm_msg_q_unlock (mq);
1486           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1487             {
1488               vcl_handle_mq_event (wrk, e);
1489               svm_msg_q_free_msg (mq, &msg);
1490               continue;
1491             }
1492           svm_msg_q_free_msg (mq, &msg);
1493
1494           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1495             return 0;
1496         }
1497     }
1498
1499   n_read = svm_fifo_segments (rx_fifo, (svm_fifo_segment_t *) ds);
1500   svm_fifo_unset_event (rx_fifo);
1501
1502   if (is_ct && n_read + svm_fifo_max_dequeue (rx_fifo) == rx_fifo->nitems)
1503     {
1504       /* If the peer is not polling send notification */
1505       if (!svm_fifo_has_event (s->rx_fifo))
1506         app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo,
1507                                 SESSION_IO_EVT_CT_RX, SVM_Q_WAIT);
1508     }
1509
1510   return n_read;
1511 }
1512
1513 void
1514 vppcom_session_free_segments (uint32_t session_handle,
1515                               vppcom_data_segments_t ds)
1516 {
1517   vcl_worker_t *wrk = vcl_worker_get_current ();
1518   vcl_session_t *s;
1519
1520   s = vcl_session_get_w_handle (wrk, session_handle);
1521   if (PREDICT_FALSE (!s || s->is_vep))
1522     return;
1523
1524   svm_fifo_segments_free (s->rx_fifo, (svm_fifo_segment_t *) ds);
1525 }
1526
1527 static inline int
1528 vppcom_session_read_ready (vcl_session_t * session)
1529 {
1530   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1531   if (PREDICT_FALSE (session->is_vep))
1532     {
1533       clib_warning ("VCL<%d>: ERROR: sid %u: cannot read from an "
1534                     "epoll session!", getpid (), session->session_index);
1535       return VPPCOM_EBADFD;
1536     }
1537
1538   if (PREDICT_FALSE (!(session->session_state & (STATE_OPEN | STATE_LISTEN))))
1539     {
1540       session_state_t state = session->session_state;
1541       int rv;
1542
1543       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1544
1545       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open!"
1546             " state 0x%x (%s), returning %d (%s)", getpid (),
1547             session->vpp_handle, session->session_index, state,
1548             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1549       return rv;
1550     }
1551
1552   if (session->session_state & STATE_LISTEN)
1553     return clib_fifo_elts (session->accept_evts_fifo);
1554
1555   return svm_fifo_max_dequeue (session->rx_fifo);
1556 }
1557
1558 int
1559 vppcom_data_segment_copy (void *buf, vppcom_data_segments_t ds, u32 max_bytes)
1560 {
1561   u32 first_copy = clib_min (ds[0].len, max_bytes);
1562   clib_memcpy_fast (buf, ds[0].data, first_copy);
1563   if (first_copy < max_bytes)
1564     {
1565       clib_memcpy_fast (buf + first_copy, ds[1].data,
1566                         clib_min (ds[1].len, max_bytes - first_copy));
1567     }
1568   return 0;
1569 }
1570
1571 static u8
1572 vcl_is_tx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1573 {
1574   if (!is_ct)
1575     return (e->event_type == FIFO_EVENT_APP_TX
1576             && e->fifo->client_session_index == sid);
1577   else
1578     return (e->event_type == SESSION_IO_EVT_CT_RX);
1579 }
1580
1581 int
1582 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
1583 {
1584   vcl_worker_t *wrk = vcl_worker_get_current ();
1585   int rv, n_write, is_nonblocking;
1586   vcl_session_t *s = 0;
1587   svm_fifo_t *tx_fifo = 0;
1588   session_evt_type_t et;
1589   svm_msg_q_msg_t msg;
1590   session_event_t *e;
1591   svm_msg_q_t *mq;
1592   u8 is_ct;
1593
1594   if (PREDICT_FALSE (!buf))
1595     return VPPCOM_EINVAL;
1596
1597   s = vcl_session_get_w_handle (wrk, session_handle);
1598   if (PREDICT_FALSE (!s))
1599     return VPPCOM_EBADFD;
1600
1601   if (PREDICT_FALSE (s->is_vep))
1602     {
1603       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1604                     "cannot write to an epoll session!",
1605                     getpid (), s->vpp_handle, session_handle);
1606
1607       return VPPCOM_EBADFD;
1608     }
1609
1610   if (PREDICT_FALSE (!(s->session_state & STATE_OPEN)))
1611     {
1612       session_state_t state = s->session_state;
1613       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1614       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open! "
1615             "state 0x%x (%s)", getpid (), s->vpp_handle, session_handle,
1616             state, vppcom_session_state_str (state));
1617       return rv;
1618     }
1619
1620   tx_fifo = s->tx_fifo;
1621   is_ct = vcl_session_is_ct (s);
1622   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1623   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1624   if (svm_fifo_is_full (tx_fifo))
1625     {
1626       if (is_nonblocking)
1627         {
1628           return VPPCOM_EWOULDBLOCK;
1629         }
1630       while (svm_fifo_is_full (tx_fifo))
1631         {
1632           svm_fifo_set_want_tx_evt (tx_fifo, 1);
1633           svm_msg_q_lock (mq);
1634           if (svm_msg_q_is_empty (mq))
1635             svm_msg_q_wait (mq);
1636
1637           svm_msg_q_sub_w_lock (mq, &msg);
1638           e = svm_msg_q_msg_data (mq, &msg);
1639           svm_msg_q_unlock (mq);
1640
1641           if (!vcl_is_tx_evt_for_session (e, s->session_index, is_ct))
1642             vcl_handle_mq_event (wrk, e);
1643           svm_msg_q_free_msg (mq, &msg);
1644         }
1645     }
1646
1647   ASSERT (FIFO_EVENT_APP_TX + 1 == SESSION_IO_EVT_CT_TX);
1648   et = FIFO_EVENT_APP_TX + vcl_session_is_ct (s);
1649   if (s->is_dgram)
1650     n_write = app_send_dgram_raw (tx_fifo, &s->transport,
1651                                   s->vpp_evt_q, buf, n, et, SVM_Q_WAIT);
1652   else
1653     n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
1654                                    SVM_Q_WAIT);
1655
1656   ASSERT (n_write > 0);
1657
1658   VDBG (2, "VCL<%d>: vpp handle 0x%llx, sid %u: wrote %d bytes", getpid (),
1659         s->vpp_handle, session_handle, n_write);
1660
1661   return n_write;
1662 }
1663
1664 static vcl_session_t *
1665 vcl_ct_session_get_from_fifo (vcl_worker_t * wrk, svm_fifo_t * f, u8 type)
1666 {
1667   vcl_session_t *s;
1668   s = vcl_session_get (wrk, f->client_session_index);
1669   if (s)
1670     {
1671       /* rx fifo */
1672       if (type == 0 && s->rx_fifo == f)
1673         return s;
1674       /* tx fifo */
1675       if (type == 1 && s->tx_fifo == f)
1676         return s;
1677     }
1678   s = vcl_session_get (wrk, f->master_session_index);
1679   if (s)
1680     {
1681       if (type == 0 && s->rx_fifo == f)
1682         return s;
1683       if (type == 1 && s->tx_fifo == f)
1684         return s;
1685     }
1686   return 0;
1687 }
1688
1689 static inline int
1690 vppcom_session_write_ready (vcl_session_t * session)
1691 {
1692   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1693   if (PREDICT_FALSE (session->is_vep))
1694     {
1695       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1696                     "cannot write to an epoll session!",
1697                     getpid (), session->vpp_handle, session->session_index);
1698       return VPPCOM_EBADFD;
1699     }
1700
1701   if (PREDICT_FALSE (session->session_state & STATE_LISTEN))
1702     {
1703       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1704                     "cannot write to a listen session!",
1705                     getpid (), session->vpp_handle, session->session_index);
1706       return VPPCOM_EBADFD;
1707     }
1708
1709   if (PREDICT_FALSE (!(session->session_state & STATE_OPEN)))
1710     {
1711       session_state_t state = session->session_state;
1712       int rv;
1713
1714       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1715       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1716                     "session is not open! state 0x%x (%s), "
1717                     "returning %d (%s)", getpid (), session->vpp_handle,
1718                     session->session_index,
1719                     state, vppcom_session_state_str (state),
1720                     rv, vppcom_retval_str (rv));
1721       return rv;
1722     }
1723
1724   VDBG (3, "VCL<%d>: vpp handle 0x%llx, sid %u: peek %s (%p), ready = %d",
1725         getpid (), session->vpp_handle, session->session_index,
1726         session->tx_fifo, svm_fifo_max_enqueue (session->tx_fifo));
1727
1728   return svm_fifo_max_enqueue (session->tx_fifo);
1729 }
1730
1731 static inline int
1732 vcl_mq_dequeue_batch (vcl_worker_t * wrk, svm_msg_q_t * mq)
1733 {
1734   svm_msg_q_msg_t *msg;
1735   u32 n_msgs;
1736   int i;
1737
1738   n_msgs = svm_msg_q_size (mq);
1739   for (i = 0; i < n_msgs; i++)
1740     {
1741       vec_add2 (wrk->mq_msg_vector, msg, 1);
1742       svm_msg_q_sub_w_lock (mq, msg);
1743     }
1744   return n_msgs;
1745 }
1746
1747 #define vcl_fifo_rx_evt_valid_or_break(_fifo)                   \
1748 if (PREDICT_FALSE (svm_fifo_is_empty (_fifo)))                  \
1749   {                                                             \
1750     svm_fifo_unset_event (_fifo);                               \
1751     if (svm_fifo_is_empty (_fifo))                              \
1752       break;                                                    \
1753   }                                                             \
1754
1755 static void
1756 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
1757                             unsigned long n_bits, unsigned long *read_map,
1758                             unsigned long *write_map,
1759                             unsigned long *except_map, u32 * bits_set)
1760 {
1761   session_disconnected_msg_t *disconnected_msg;
1762   session_connected_msg_t *connected_msg;
1763   session_accepted_msg_t *accepted_msg;
1764   vcl_session_msg_t *vcl_msg;
1765   vcl_session_t *session;
1766   u64 handle;
1767   u32 sid;
1768
1769   switch (e->event_type)
1770     {
1771     case FIFO_EVENT_APP_RX:
1772       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1773       sid = e->fifo->client_session_index;
1774       session = vcl_session_get (wrk, sid);
1775       if (!session)
1776         break;
1777       if (sid < n_bits && read_map)
1778         {
1779           clib_bitmap_set_no_check (read_map, sid, 1);
1780           *bits_set += 1;
1781         }
1782       break;
1783     case FIFO_EVENT_APP_TX:
1784       sid = e->fifo->client_session_index;
1785       session = vcl_session_get (wrk, sid);
1786       if (!session)
1787         break;
1788       if (sid < n_bits && write_map)
1789         {
1790           clib_bitmap_set_no_check (write_map, sid, 1);
1791           *bits_set += 1;
1792         }
1793       break;
1794     case SESSION_IO_EVT_CT_TX:
1795       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1796       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
1797       if (!session)
1798         break;
1799       sid = session->session_index;
1800       if (sid < n_bits && read_map)
1801         {
1802           clib_bitmap_set_no_check (read_map, sid, 1);
1803           *bits_set += 1;
1804         }
1805       break;
1806     case SESSION_IO_EVT_CT_RX:
1807       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
1808       if (!session)
1809         break;
1810       sid = session->session_index;
1811       if (sid < n_bits && write_map)
1812         {
1813           clib_bitmap_set_no_check (write_map, sid, 1);
1814           *bits_set += 1;
1815         }
1816       break;
1817     case SESSION_CTRL_EVT_ACCEPTED:
1818       accepted_msg = (session_accepted_msg_t *) e->data;
1819       handle = accepted_msg->listener_handle;
1820       session = vcl_session_table_lookup_listener (wrk, handle);
1821       if (!session)
1822         {
1823           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
1824                         "listener handle %llx", getpid (), handle);
1825           break;
1826         }
1827
1828       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
1829       vcl_msg->accepted_msg = *accepted_msg;
1830       sid = session->session_index;
1831       if (sid < n_bits && read_map)
1832         {
1833           clib_bitmap_set_no_check (read_map, sid, 1);
1834           *bits_set += 1;
1835         }
1836       break;
1837     case SESSION_CTRL_EVT_CONNECTED:
1838       connected_msg = (session_connected_msg_t *) e->data;
1839       vcl_session_connected_handler (wrk, connected_msg);
1840       break;
1841     case SESSION_CTRL_EVT_DISCONNECTED:
1842       disconnected_msg = (session_disconnected_msg_t *) e->data;
1843       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
1844       if (sid < n_bits && except_map)
1845         {
1846           clib_bitmap_set_no_check (except_map, sid, 1);
1847           *bits_set += 1;
1848         }
1849       break;
1850     case SESSION_CTRL_EVT_RESET:
1851       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
1852       if (sid < n_bits && except_map)
1853         {
1854           clib_bitmap_set_no_check (except_map, sid, 1);
1855           *bits_set += 1;
1856         }
1857       break;
1858     default:
1859       clib_warning ("unhandled: %u", e->event_type);
1860       break;
1861     }
1862 }
1863
1864 static int
1865 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
1866                       unsigned long n_bits, unsigned long *read_map,
1867                       unsigned long *write_map, unsigned long *except_map,
1868                       double time_to_wait, u32 * bits_set)
1869 {
1870   svm_msg_q_msg_t *msg;
1871   session_event_t *e;
1872   u32 i;
1873
1874   svm_msg_q_lock (mq);
1875   if (svm_msg_q_is_empty (mq))
1876     {
1877       if (*bits_set)
1878         {
1879           svm_msg_q_unlock (mq);
1880           return 0;
1881         }
1882
1883       if (!time_to_wait)
1884         {
1885           svm_msg_q_unlock (mq);
1886           return 0;
1887         }
1888       else if (time_to_wait < 0)
1889         {
1890           svm_msg_q_wait (mq);
1891         }
1892       else
1893         {
1894           if (svm_msg_q_timedwait (mq, time_to_wait))
1895             {
1896               svm_msg_q_unlock (mq);
1897               return 0;
1898             }
1899         }
1900     }
1901   vcl_mq_dequeue_batch (wrk, mq);
1902   svm_msg_q_unlock (mq);
1903
1904   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
1905     {
1906       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
1907       e = svm_msg_q_msg_data (mq, msg);
1908       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
1909                                   except_map, bits_set);
1910       svm_msg_q_free_msg (mq, msg);
1911     }
1912   vec_reset_length (wrk->mq_msg_vector);
1913   return *bits_set;
1914 }
1915
1916 static int
1917 vppcom_select_condvar (vcl_worker_t * wrk, unsigned long n_bits,
1918                        unsigned long *read_map, unsigned long *write_map,
1919                        unsigned long *except_map, double time_to_wait,
1920                        u32 * bits_set)
1921 {
1922   double total_wait = 0, wait_slice;
1923   vcl_cut_through_registration_t *cr;
1924
1925   time_to_wait = (time_to_wait == -1) ? 10e9 : time_to_wait;
1926   wait_slice = wrk->cut_through_registrations ? 10e-6 : time_to_wait;
1927   do
1928     {
1929       vcl_ct_registration_lock (wrk);
1930       /* *INDENT-OFF* */
1931       pool_foreach (cr, wrk->cut_through_registrations, ({
1932         vcl_select_handle_mq (wrk, cr->mq, n_bits, read_map, write_map, except_map,
1933                               0, bits_set);
1934       }));
1935       /* *INDENT-ON* */
1936       vcl_ct_registration_unlock (wrk);
1937
1938       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
1939                             write_map, except_map, time_to_wait, bits_set);
1940       total_wait += wait_slice;
1941       if (*bits_set)
1942         return *bits_set;
1943     }
1944   while (total_wait < time_to_wait);
1945
1946   return 0;
1947 }
1948
1949 static int
1950 vppcom_select_eventfd (vcl_worker_t * wrk, unsigned long n_bits,
1951                        unsigned long *read_map, unsigned long *write_map,
1952                        unsigned long *except_map, double time_to_wait,
1953                        u32 * bits_set)
1954 {
1955   vcl_mq_evt_conn_t *mqc;
1956   int __clib_unused n_read;
1957   int n_mq_evts, i;
1958   u64 buf;
1959
1960   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
1961   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
1962                           vec_len (wrk->mq_events), time_to_wait);
1963   for (i = 0; i < n_mq_evts; i++)
1964     {
1965       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
1966       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
1967       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
1968                             except_map, 0, bits_set);
1969     }
1970
1971   return (n_mq_evts > 0 ? (int) *bits_set : 0);
1972 }
1973
1974 int
1975 vppcom_select (unsigned long n_bits, unsigned long *read_map,
1976                unsigned long *write_map, unsigned long *except_map,
1977                double time_to_wait)
1978 {
1979   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
1980   vcl_worker_t *wrk = vcl_worker_get_current ();
1981   vcl_session_t *session = 0;
1982   int rv, i;
1983
1984   ASSERT (sizeof (clib_bitmap_t) == sizeof (long int));
1985
1986   if (n_bits && read_map)
1987     {
1988       clib_bitmap_validate (wrk->rd_bitmap, minbits);
1989       clib_memcpy_fast (wrk->rd_bitmap, read_map,
1990                         vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
1991       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
1992     }
1993   if (n_bits && write_map)
1994     {
1995       clib_bitmap_validate (wrk->wr_bitmap, minbits);
1996       clib_memcpy_fast (wrk->wr_bitmap, write_map,
1997                         vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
1998       memset (write_map, 0,
1999               vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
2000     }
2001   if (n_bits && except_map)
2002     {
2003       clib_bitmap_validate (wrk->ex_bitmap, minbits);
2004       clib_memcpy_fast (wrk->ex_bitmap, except_map,
2005                         vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
2006       memset (except_map, 0,
2007               vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
2008     }
2009
2010   if (!n_bits)
2011     return 0;
2012
2013   if (!write_map)
2014     goto check_rd;
2015
2016   /* *INDENT-OFF* */
2017   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
2018     if (!(session = vcl_session_get (wrk, sid)))
2019       {
2020         if (except_map && sid < minbits)
2021           clib_bitmap_set_no_check (except_map, sid, 1);
2022         continue;
2023       }
2024
2025     rv = svm_fifo_is_full (session->tx_fifo);
2026     if (!rv)
2027       {
2028         clib_bitmap_set_no_check (write_map, sid, 1);
2029         bits_set++;
2030       }
2031   }));
2032
2033 check_rd:
2034   if (!read_map)
2035     goto check_mq;
2036
2037   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
2038     if (!(session = vcl_session_get (wrk, sid)))
2039       {
2040         if (except_map && sid < minbits)
2041           clib_bitmap_set_no_check (except_map, sid, 1);
2042         continue;
2043       }
2044
2045     rv = vppcom_session_read_ready (session);
2046     if (rv)
2047       {
2048         clib_bitmap_set_no_check (read_map, sid, 1);
2049         bits_set++;
2050       }
2051   }));
2052   /* *INDENT-ON* */
2053
2054 check_mq:
2055
2056   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2057     {
2058       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
2059                                   read_map, write_map, except_map, &bits_set);
2060     }
2061   vec_reset_length (wrk->unhandled_evts_vector);
2062
2063   if (vcm->cfg.use_mq_eventfd)
2064     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
2065                            time_to_wait, &bits_set);
2066   else
2067     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
2068                            time_to_wait, &bits_set);
2069
2070   return (bits_set);
2071 }
2072
2073 static inline void
2074 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_idx)
2075 {
2076   vcl_session_t *session;
2077   vppcom_epoll_t *vep;
2078   u32 sid = vep_idx;
2079
2080   if (VPPCOM_DEBUG <= 1)
2081     return;
2082
2083   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2084   session = vcl_session_get (wrk, vep_idx);
2085   if (PREDICT_FALSE (!session))
2086     {
2087       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!",
2088                     getpid (), vep_idx);
2089       goto done;
2090     }
2091   if (PREDICT_FALSE (!session->is_vep))
2092     {
2093       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2094                     getpid (), vep_idx);
2095       goto done;
2096     }
2097   vep = &session->vep;
2098   clib_warning ("VCL<%d>: vep_idx (%u): Dumping epoll chain\n"
2099                 "{\n"
2100                 "   is_vep         = %u\n"
2101                 "   is_vep_session = %u\n"
2102                 "   next_sid       = 0x%x (%u)\n"
2103                 "   wait_cont_idx  = 0x%x (%u)\n"
2104                 "}\n", getpid (), vep_idx,
2105                 session->is_vep, session->is_vep_session,
2106                 vep->next_sh, vep->next_sh,
2107                 session->wait_cont_idx, session->wait_cont_idx);
2108
2109   for (sid = vep->next_sh; sid != ~0; sid = vep->next_sh)
2110     {
2111       session = vcl_session_get (wrk, sid);
2112       if (PREDICT_FALSE (!session))
2113         {
2114           clib_warning ("VCL<%d>: ERROR: Invalid sid (%u)!", getpid (), sid);
2115           goto done;
2116         }
2117       if (PREDICT_FALSE (session->is_vep))
2118         clib_warning ("VCL<%d>: ERROR: sid (%u) is a vep!",
2119                       getpid (), vep_idx);
2120       else if (PREDICT_FALSE (!session->is_vep_session))
2121         {
2122           clib_warning ("VCL<%d>: ERROR: session (%u) "
2123                         "is not a vep session!", getpid (), sid);
2124           goto done;
2125         }
2126       vep = &session->vep;
2127       if (PREDICT_FALSE (vep->vep_sh != vep_idx))
2128         clib_warning ("VCL<%d>: ERROR: session (%u) vep_idx (%u) != "
2129                       "vep_idx (%u)!", getpid (),
2130                       sid, session->vep.vep_sh, vep_idx);
2131       if (session->is_vep_session)
2132         {
2133           clib_warning ("vep_idx[%u]: sid 0x%x (%u)\n"
2134                         "{\n"
2135                         "   next_sid       = 0x%x (%u)\n"
2136                         "   prev_sid       = 0x%x (%u)\n"
2137                         "   vep_idx        = 0x%x (%u)\n"
2138                         "   ev.events      = 0x%x\n"
2139                         "   ev.data.u64    = 0x%llx\n"
2140                         "   et_mask        = 0x%x\n"
2141                         "}\n",
2142                         vep_idx, sid, sid,
2143                         vep->next_sh, vep->next_sh,
2144                         vep->prev_sh, vep->prev_sh,
2145                         vep->vep_sh, vep->vep_sh,
2146                         vep->ev.events, vep->ev.data.u64, vep->et_mask);
2147         }
2148     }
2149
2150 done:
2151   clib_warning ("VCL<%d>: vep_idx (%u): Dump complete!\n",
2152                 getpid (), vep_idx);
2153 }
2154
2155 int
2156 vppcom_epoll_create (void)
2157 {
2158   vcl_worker_t *wrk = vcl_worker_get_current ();
2159   vcl_session_t *vep_session;
2160
2161   vep_session = vcl_session_alloc (wrk);
2162
2163   vep_session->is_vep = 1;
2164   vep_session->vep.vep_sh = ~0;
2165   vep_session->vep.next_sh = ~0;
2166   vep_session->vep.prev_sh = ~0;
2167   vep_session->wait_cont_idx = ~0;
2168   vep_session->vpp_handle = ~0;
2169
2170   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_sh);
2171   VDBG (0, "VCL<%d>: Created vep_idx %u / sid %u!",
2172         getpid (), vep_session->session_index, vep_session->session_index);
2173
2174   return vcl_session_handle (vep_session);
2175 }
2176
2177 int
2178 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2179                   struct epoll_event *event)
2180 {
2181   vcl_worker_t *wrk = vcl_worker_get_current ();
2182   vcl_session_t *vep_session;
2183   vcl_session_t *session;
2184   int rv = VPPCOM_OK;
2185
2186   if (vep_handle == session_handle)
2187     {
2188       clib_warning ("VCL<%d>: ERROR: vep_idx == session_index (%u)!",
2189                     getpid (), vep_handle);
2190       return VPPCOM_EINVAL;
2191     }
2192
2193   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2194   if (PREDICT_FALSE (!vep_session))
2195     {
2196       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!", vep_handle);
2197       return VPPCOM_EBADFD;
2198     }
2199   if (PREDICT_FALSE (!vep_session->is_vep))
2200     {
2201       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2202                     getpid (), vep_handle);
2203       return VPPCOM_EINVAL;
2204     }
2205
2206   ASSERT (vep_session->vep.vep_sh == ~0);
2207   ASSERT (vep_session->vep.prev_sh == ~0);
2208
2209   session = vcl_session_get_w_handle (wrk, session_handle);
2210   if (PREDICT_FALSE (!session))
2211     {
2212       VDBG (0, "VCL<%d>: ERROR: Invalid session_handle (%u)!",
2213             getpid (), session_handle);
2214       return VPPCOM_EBADFD;
2215     }
2216   if (PREDICT_FALSE (session->is_vep))
2217     {
2218       clib_warning ("ERROR: session_handle (%u) is a vep!", vep_handle);
2219       return VPPCOM_EINVAL;
2220     }
2221
2222   switch (op)
2223     {
2224     case EPOLL_CTL_ADD:
2225       if (PREDICT_FALSE (!event))
2226         {
2227           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: NULL pointer to "
2228                         "epoll_event structure!", getpid ());
2229           return VPPCOM_EINVAL;
2230         }
2231       if (vep_session->vep.next_sh != ~0)
2232         {
2233           vcl_session_t *next_session;
2234           next_session = vcl_session_get_w_handle (wrk,
2235                                                    vep_session->vep.next_sh);
2236           if (PREDICT_FALSE (!next_session))
2237             {
2238               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: Invalid "
2239                             "vep.next_sid (%u) on vep_idx (%u)!",
2240                             getpid (), vep_session->vep.next_sh, vep_handle);
2241               return VPPCOM_EBADFD;
2242             }
2243           ASSERT (next_session->vep.prev_sh == vep_handle);
2244           next_session->vep.prev_sh = session_handle;
2245         }
2246       session->vep.next_sh = vep_session->vep.next_sh;
2247       session->vep.prev_sh = vep_handle;
2248       session->vep.vep_sh = vep_handle;
2249       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2250       session->vep.ev = *event;
2251       session->is_vep = 0;
2252       session->is_vep_session = 1;
2253       vep_session->vep.next_sh = session_handle;
2254
2255       VDBG (1, "VCL<%d>: EPOLL_CTL_ADD: vep_idx %u, sid %u, events 0x%x, "
2256             "data 0x%llx!", getpid (), vep_handle, session_handle,
2257             event->events, event->data.u64);
2258       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
2259       break;
2260
2261     case EPOLL_CTL_MOD:
2262       if (PREDICT_FALSE (!event))
2263         {
2264           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_MOD: NULL pointer to "
2265                         "epoll_event structure!", getpid ());
2266           rv = VPPCOM_EINVAL;
2267           goto done;
2268         }
2269       else if (PREDICT_FALSE (!session->is_vep_session))
2270         {
2271           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2272                         "not a vep session!", getpid (), session_handle);
2273           rv = VPPCOM_EINVAL;
2274           goto done;
2275         }
2276       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2277         {
2278           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2279                         "vep_idx (%u) != vep_idx (%u)!",
2280                         getpid (), session_handle,
2281                         session->vep.vep_sh, vep_handle);
2282           rv = VPPCOM_EINVAL;
2283           goto done;
2284         }
2285       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2286       session->vep.ev = *event;
2287       VDBG (1, "VCL<%d>: EPOLL_CTL_MOD: vep_idx %u, sid %u, events 0x%x,"
2288             " data 0x%llx!", getpid (), vep_handle, session_handle,
2289             event->events, event->data.u64);
2290       break;
2291
2292     case EPOLL_CTL_DEL:
2293       if (PREDICT_FALSE (!session->is_vep_session))
2294         {
2295           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2296                         "not a vep session!", getpid (), session_handle);
2297           rv = VPPCOM_EINVAL;
2298           goto done;
2299         }
2300       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2301         {
2302           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2303                         "vep_idx (%u) != vep_idx (%u)!",
2304                         getpid (), session_handle,
2305                         session->vep.vep_sh, vep_handle);
2306           rv = VPPCOM_EINVAL;
2307           goto done;
2308         }
2309
2310       vep_session->wait_cont_idx =
2311         (vep_session->wait_cont_idx == session_handle) ?
2312         session->vep.next_sh : vep_session->wait_cont_idx;
2313
2314       if (session->vep.prev_sh == vep_handle)
2315         vep_session->vep.next_sh = session->vep.next_sh;
2316       else
2317         {
2318           vcl_session_t *prev_session;
2319           prev_session = vcl_session_get_w_handle (wrk, session->vep.prev_sh);
2320           if (PREDICT_FALSE (!prev_session))
2321             {
2322               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2323                             "vep.prev_sid (%u) on sid (%u)!",
2324                             getpid (), session->vep.prev_sh, session_handle);
2325               return VPPCOM_EBADFD;
2326             }
2327           ASSERT (prev_session->vep.next_sh == session_handle);
2328           prev_session->vep.next_sh = session->vep.next_sh;
2329         }
2330       if (session->vep.next_sh != ~0)
2331         {
2332           vcl_session_t *next_session;
2333           next_session = vcl_session_get_w_handle (wrk, session->vep.next_sh);
2334           if (PREDICT_FALSE (!next_session))
2335             {
2336               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2337                             "vep.next_sid (%u) on sid (%u)!",
2338                             getpid (), session->vep.next_sh, session_handle);
2339               return VPPCOM_EBADFD;
2340             }
2341           ASSERT (next_session->vep.prev_sh == session_handle);
2342           next_session->vep.prev_sh = session->vep.prev_sh;
2343         }
2344
2345       memset (&session->vep, 0, sizeof (session->vep));
2346       session->vep.next_sh = ~0;
2347       session->vep.prev_sh = ~0;
2348       session->vep.vep_sh = ~0;
2349       session->is_vep_session = 0;
2350       VDBG (1, "VCL<%d>: EPOLL_CTL_DEL: vep_idx %u, sid %u!",
2351             getpid (), vep_handle, session_handle);
2352       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_sh);
2353       break;
2354
2355     default:
2356       clib_warning ("VCL<%d>: ERROR: Invalid operation (%d)!", getpid (), op);
2357       rv = VPPCOM_EINVAL;
2358     }
2359
2360   vep_verify_epoll_chain (wrk, vep_handle);
2361
2362 done:
2363   return rv;
2364 }
2365
2366 static inline void
2367 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2368                                 struct epoll_event *events, u32 * num_ev)
2369 {
2370   session_disconnected_msg_t *disconnected_msg;
2371   session_connected_msg_t *connected_msg;
2372   session_accepted_msg_t *accepted_msg;
2373   u64 session_evt_data = ~0, handle;
2374   u32 sid = ~0, session_events;
2375   vcl_session_msg_t *vcl_msg;
2376   vcl_session_t *session;
2377   u8 add_event = 0;
2378
2379   switch (e->event_type)
2380     {
2381     case FIFO_EVENT_APP_RX:
2382       ASSERT (e->fifo->client_thread_index == vcl_get_worker_index ());
2383       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2384       sid = e->fifo->client_session_index;
2385       session = vcl_session_get (wrk, sid);
2386       session_events = session->vep.ev.events;
2387       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2388         break;
2389       add_event = 1;
2390       events[*num_ev].events |= EPOLLIN;
2391       session_evt_data = session->vep.ev.data.u64;
2392       session->has_rx_evt = 1;
2393       break;
2394     case FIFO_EVENT_APP_TX:
2395       sid = e->fifo->client_session_index;
2396       session = vcl_session_get (wrk, sid);
2397       session_events = session->vep.ev.events;
2398       if (!(EPOLLOUT & session_events))
2399         break;
2400       add_event = 1;
2401       events[*num_ev].events |= EPOLLOUT;
2402       session_evt_data = session->vep.ev.data.u64;
2403       break;
2404     case SESSION_IO_EVT_CT_TX:
2405       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2406       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
2407       sid = session->session_index;
2408       session_events = session->vep.ev.events;
2409       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2410         break;
2411       add_event = 1;
2412       events[*num_ev].events |= EPOLLIN;
2413       session_evt_data = session->vep.ev.data.u64;
2414       session->has_rx_evt = 1;
2415       break;
2416     case SESSION_IO_EVT_CT_RX:
2417       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
2418       sid = session->session_index;
2419       session_events = session->vep.ev.events;
2420       if (!(EPOLLOUT & session_events))
2421         break;
2422       add_event = 1;
2423       events[*num_ev].events |= EPOLLOUT;
2424       session_evt_data = session->vep.ev.data.u64;
2425       break;
2426     case SESSION_CTRL_EVT_ACCEPTED:
2427       accepted_msg = (session_accepted_msg_t *) e->data;
2428       handle = accepted_msg->listener_handle;
2429       session = vcl_session_table_lookup_listener (wrk, handle);
2430       if (!session)
2431         {
2432           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
2433                         "listener handle %llx", getpid (), handle);
2434           break;
2435         }
2436
2437       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
2438       vcl_msg->accepted_msg = *accepted_msg;
2439       session_events = session->vep.ev.events;
2440       if (!(EPOLLIN & session_events))
2441         break;
2442
2443       add_event = 1;
2444       events[*num_ev].events |= EPOLLIN;
2445       session_evt_data = session->vep.ev.data.u64;
2446       break;
2447     case SESSION_CTRL_EVT_CONNECTED:
2448       connected_msg = (session_connected_msg_t *) e->data;
2449       vcl_session_connected_handler (wrk, connected_msg);
2450       /* Generate EPOLLOUT because there's no connected event */
2451       sid = vcl_session_index_from_vpp_handle (wrk, connected_msg->handle);
2452       session = vcl_session_get (wrk, sid);
2453       session_events = session->vep.ev.events;
2454       if (EPOLLOUT & session_events)
2455         {
2456           add_event = 1;
2457           events[*num_ev].events |= EPOLLOUT;
2458           session_evt_data = session->vep.ev.data.u64;
2459         }
2460       break;
2461     case SESSION_CTRL_EVT_DISCONNECTED:
2462       disconnected_msg = (session_disconnected_msg_t *) e->data;
2463       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
2464       if (!(session = vcl_session_get (wrk, sid)))
2465         break;
2466       add_event = 1;
2467       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2468       session_evt_data = session->vep.ev.data.u64;
2469       session_events = session->vep.ev.events;
2470       break;
2471     case SESSION_CTRL_EVT_RESET:
2472       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2473       if (!(session = vcl_session_get (wrk, sid)))
2474         break;
2475       add_event = 1;
2476       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2477       session_evt_data = session->vep.ev.data.u64;
2478       session_events = session->vep.ev.events;
2479       break;
2480     default:
2481       VDBG (0, "unhandled: %u", e->event_type);
2482       break;
2483     }
2484
2485   if (add_event)
2486     {
2487       events[*num_ev].data.u64 = session_evt_data;
2488       if (EPOLLONESHOT & session_events)
2489         {
2490           session = vcl_session_get (wrk, sid);
2491           session->vep.ev.events = 0;
2492         }
2493       *num_ev += 1;
2494     }
2495 }
2496
2497 static int
2498 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2499                           struct epoll_event *events, u32 maxevents,
2500                           double wait_for_time, u32 * num_ev)
2501 {
2502   svm_msg_q_msg_t *msg;
2503   session_event_t *e;
2504   int i;
2505
2506   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
2507     goto handle_dequeued;
2508
2509   svm_msg_q_lock (mq);
2510   if (svm_msg_q_is_empty (mq))
2511     {
2512       if (!wait_for_time)
2513         {
2514           svm_msg_q_unlock (mq);
2515           return 0;
2516         }
2517       else if (wait_for_time < 0)
2518         {
2519           svm_msg_q_wait (mq);
2520         }
2521       else
2522         {
2523           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
2524             {
2525               svm_msg_q_unlock (mq);
2526               return 0;
2527             }
2528         }
2529     }
2530   vcl_mq_dequeue_batch (wrk, mq);
2531   svm_msg_q_unlock (mq);
2532
2533 handle_dequeued:
2534   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
2535     {
2536       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
2537       e = svm_msg_q_msg_data (mq, msg);
2538       if (*num_ev < maxevents)
2539         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
2540       else
2541         vec_add1 (wrk->unhandled_evts_vector, *e);
2542       svm_msg_q_free_msg (mq, msg);
2543     }
2544   vec_reset_length (wrk->mq_msg_vector);
2545
2546   return *num_ev;
2547 }
2548
2549 static int
2550 vppcom_epoll_wait_condvar (vcl_worker_t * wrk, struct epoll_event *events,
2551                            int maxevents, u32 n_evts, double wait_for_time)
2552 {
2553   vcl_cut_through_registration_t *cr;
2554   double total_wait = 0, wait_slice;
2555   int rv;
2556
2557   wait_for_time = (wait_for_time == -1) ? (double) 10e9 : wait_for_time;
2558   wait_slice = wrk->cut_through_registrations ? 10e-6 : wait_for_time;
2559
2560   do
2561     {
2562       vcl_ct_registration_lock (wrk);
2563       /* *INDENT-OFF* */
2564       pool_foreach (cr, wrk->cut_through_registrations, ({
2565         vcl_epoll_wait_handle_mq (wrk, cr->mq, events, maxevents, 0, &n_evts);
2566       }));
2567       /* *INDENT-ON* */
2568       vcl_ct_registration_unlock (wrk);
2569
2570       rv = vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events,
2571                                      maxevents, n_evts ? 0 : wait_slice,
2572                                      &n_evts);
2573       if (rv)
2574         total_wait += wait_slice;
2575       if (n_evts)
2576         return n_evts;
2577     }
2578   while (total_wait < wait_for_time);
2579   return n_evts;
2580 }
2581
2582 static int
2583 vppcom_epoll_wait_eventfd (vcl_worker_t * wrk, struct epoll_event *events,
2584                            int maxevents, u32 n_evts, double wait_for_time)
2585 {
2586   vcl_mq_evt_conn_t *mqc;
2587   int __clib_unused n_read;
2588   int n_mq_evts, i;
2589   u64 buf;
2590
2591   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2592 again:
2593   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2594                           vec_len (wrk->mq_events), wait_for_time);
2595   for (i = 0; i < n_mq_evts; i++)
2596     {
2597       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2598       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2599       vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0, &n_evts);
2600     }
2601   if (!n_evts && n_mq_evts > 0)
2602     goto again;
2603
2604   return (int) n_evts;
2605 }
2606
2607 int
2608 vppcom_epoll_wait (uint32_t vep_handle, struct epoll_event *events,
2609                    int maxevents, double wait_for_time)
2610 {
2611   vcl_worker_t *wrk = vcl_worker_get_current ();
2612   vcl_session_t *vep_session;
2613   u32 n_evts = 0;
2614   int i;
2615
2616   if (PREDICT_FALSE (maxevents <= 0))
2617     {
2618       clib_warning ("VCL<%d>: ERROR: Invalid maxevents (%d)!",
2619                     getpid (), maxevents);
2620       return VPPCOM_EINVAL;
2621     }
2622
2623   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2624   if (!vep_session)
2625     return VPPCOM_EBADFD;
2626
2627   if (PREDICT_FALSE (!vep_session->is_vep))
2628     {
2629       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2630                     getpid (), vep_handle);
2631       return VPPCOM_EINVAL;
2632     }
2633
2634   memset (events, 0, sizeof (*events) * maxevents);
2635
2636   if (vec_len (wrk->unhandled_evts_vector))
2637     {
2638       for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
2639         {
2640           vcl_epoll_wait_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i],
2641                                           events, &n_evts);
2642           if (n_evts == maxevents)
2643             {
2644               i += 1;
2645               break;
2646             }
2647         }
2648
2649       vec_delete (wrk->unhandled_evts_vector, i, 0);
2650     }
2651
2652   if (vcm->cfg.use_mq_eventfd)
2653     return vppcom_epoll_wait_eventfd (wrk, events, maxevents, n_evts,
2654                                       wait_for_time);
2655
2656   return vppcom_epoll_wait_condvar (wrk, events, maxevents, n_evts,
2657                                     wait_for_time);
2658 }
2659
2660 int
2661 vppcom_session_attr (uint32_t session_handle, uint32_t op,
2662                      void *buffer, uint32_t * buflen)
2663 {
2664   vcl_worker_t *wrk = vcl_worker_get_current ();
2665   vcl_session_t *session;
2666   int rv = VPPCOM_OK;
2667   u32 *flags = buffer;
2668   vppcom_endpt_t *ep = buffer;
2669
2670   session = vcl_session_get_w_handle (wrk, session_handle);
2671   if (!session)
2672     return VPPCOM_EBADFD;
2673
2674   switch (op)
2675     {
2676     case VPPCOM_ATTR_GET_NREAD:
2677       rv = vppcom_session_read_ready (session);
2678       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NREAD: sid %u, nread = %d",
2679             getpid (), rv);
2680       break;
2681
2682     case VPPCOM_ATTR_GET_NWRITE:
2683       rv = vppcom_session_write_ready (session);
2684       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_NWRITE: sid %u, nwrite = %d",
2685             getpid (), session_handle, rv);
2686       break;
2687
2688     case VPPCOM_ATTR_GET_FLAGS:
2689       if (PREDICT_TRUE (buffer && buflen && (*buflen >= sizeof (*flags))))
2690         {
2691           *flags = O_RDWR | (VCL_SESS_ATTR_TEST (session->attr,
2692                                                  VCL_SESS_ATTR_NONBLOCK));
2693           *buflen = sizeof (*flags);
2694           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_FLAGS: sid %u, flags = 0x%08x, "
2695                 "is_nonblocking = %u", getpid (),
2696                 session_handle, *flags,
2697                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2698         }
2699       else
2700         rv = VPPCOM_EINVAL;
2701       break;
2702
2703     case VPPCOM_ATTR_SET_FLAGS:
2704       if (PREDICT_TRUE (buffer && buflen && (*buflen == sizeof (*flags))))
2705         {
2706           if (*flags & O_NONBLOCK)
2707             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_NONBLOCK);
2708           else
2709             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_NONBLOCK);
2710
2711           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_FLAGS: sid %u, flags = 0x%08x,"
2712                 " is_nonblocking = %u",
2713                 getpid (), session_handle, *flags,
2714                 VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_NONBLOCK));
2715         }
2716       else
2717         rv = VPPCOM_EINVAL;
2718       break;
2719
2720     case VPPCOM_ATTR_GET_PEER_ADDR:
2721       if (PREDICT_TRUE (buffer && buflen &&
2722                         (*buflen >= sizeof (*ep)) && ep->ip))
2723         {
2724           ep->is_ip4 = session->transport.is_ip4;
2725           ep->port = session->transport.rmt_port;
2726           if (session->transport.is_ip4)
2727             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
2728                               sizeof (ip4_address_t));
2729           else
2730             clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
2731                               sizeof (ip6_address_t));
2732           *buflen = sizeof (*ep);
2733           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_PEER_ADDR: sid %u, is_ip4 = %u, "
2734                 "addr = %U, port %u", getpid (),
2735                 session_handle, ep->is_ip4, format_ip46_address,
2736                 &session->transport.rmt_ip,
2737                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2738                 clib_net_to_host_u16 (ep->port));
2739         }
2740       else
2741         rv = VPPCOM_EINVAL;
2742       break;
2743
2744     case VPPCOM_ATTR_GET_LCL_ADDR:
2745       if (PREDICT_TRUE (buffer && buflen &&
2746                         (*buflen >= sizeof (*ep)) && ep->ip))
2747         {
2748           ep->is_ip4 = session->transport.is_ip4;
2749           ep->port = session->transport.lcl_port;
2750           if (session->transport.is_ip4)
2751             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip4,
2752                               sizeof (ip4_address_t));
2753           else
2754             clib_memcpy_fast (ep->ip, &session->transport.lcl_ip.ip6,
2755                               sizeof (ip6_address_t));
2756           *buflen = sizeof (*ep);
2757           VDBG (1, "VCL<%d>: VPPCOM_ATTR_GET_LCL_ADDR: sid %u, is_ip4 = %u,"
2758                 " addr = %U port %d", getpid (),
2759                 session_handle, ep->is_ip4, format_ip46_address,
2760                 &session->transport.lcl_ip,
2761                 ep->is_ip4 ? IP46_TYPE_IP4 : IP46_TYPE_IP6,
2762                 clib_net_to_host_u16 (ep->port));
2763         }
2764       else
2765         rv = VPPCOM_EINVAL;
2766       break;
2767
2768     case VPPCOM_ATTR_GET_LIBC_EPFD:
2769       rv = session->libc_epfd;
2770       VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LIBC_EPFD: libc_epfd %d",
2771             getpid (), rv);
2772       break;
2773
2774     case VPPCOM_ATTR_SET_LIBC_EPFD:
2775       if (PREDICT_TRUE (buffer && buflen &&
2776                         (*buflen == sizeof (session->libc_epfd))))
2777         {
2778           session->libc_epfd = *(int *) buffer;
2779           *buflen = sizeof (session->libc_epfd);
2780
2781           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_LIBC_EPFD: libc_epfd %d, "
2782                 "buflen %d", getpid (), session->libc_epfd, *buflen);
2783         }
2784       else
2785         rv = VPPCOM_EINVAL;
2786       break;
2787
2788     case VPPCOM_ATTR_GET_PROTOCOL:
2789       if (buffer && buflen && (*buflen >= sizeof (int)))
2790         {
2791           *(int *) buffer = session->session_type;
2792           *buflen = sizeof (int);
2793
2794           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_PROTOCOL: %d (%s), buflen %d",
2795                 getpid (), *(int *) buffer, *(int *) buffer ? "UDP" : "TCP",
2796                 *buflen);
2797         }
2798       else
2799         rv = VPPCOM_EINVAL;
2800       break;
2801
2802     case VPPCOM_ATTR_GET_LISTEN:
2803       if (buffer && buflen && (*buflen >= sizeof (int)))
2804         {
2805           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2806                                                 VCL_SESS_ATTR_LISTEN);
2807           *buflen = sizeof (int);
2808
2809           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_LISTEN: %d, buflen %d",
2810                 getpid (), *(int *) buffer, *buflen);
2811         }
2812       else
2813         rv = VPPCOM_EINVAL;
2814       break;
2815
2816     case VPPCOM_ATTR_GET_ERROR:
2817       if (buffer && buflen && (*buflen >= sizeof (int)))
2818         {
2819           *(int *) buffer = 0;
2820           *buflen = sizeof (int);
2821
2822           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_ERROR: %d, buflen %d, #VPP-TBD#",
2823                 getpid (), *(int *) buffer, *buflen);
2824         }
2825       else
2826         rv = VPPCOM_EINVAL;
2827       break;
2828
2829     case VPPCOM_ATTR_GET_TX_FIFO_LEN:
2830       if (buffer && buflen && (*buflen >= sizeof (u32)))
2831         {
2832
2833           /* VPP-TBD */
2834           *(size_t *) buffer = (session->sndbuf_size ? session->sndbuf_size :
2835                                 session->tx_fifo ? session->tx_fifo->nitems :
2836                                 vcm->cfg.tx_fifo_size);
2837           *buflen = sizeof (u32);
2838
2839           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TX_FIFO_LEN: %u (0x%x), "
2840                 "buflen %d, #VPP-TBD#", getpid (),
2841                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2842         }
2843       else
2844         rv = VPPCOM_EINVAL;
2845       break;
2846
2847     case VPPCOM_ATTR_SET_TX_FIFO_LEN:
2848       if (buffer && buflen && (*buflen == sizeof (u32)))
2849         {
2850           /* VPP-TBD */
2851           session->sndbuf_size = *(u32 *) buffer;
2852           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TX_FIFO_LEN: %u (0x%x), "
2853                 "buflen %d, #VPP-TBD#", getpid (),
2854                 session->sndbuf_size, session->sndbuf_size, *buflen);
2855         }
2856       else
2857         rv = VPPCOM_EINVAL;
2858       break;
2859
2860     case VPPCOM_ATTR_GET_RX_FIFO_LEN:
2861       if (buffer && buflen && (*buflen >= sizeof (u32)))
2862         {
2863
2864           /* VPP-TBD */
2865           *(size_t *) buffer = (session->rcvbuf_size ? session->rcvbuf_size :
2866                                 session->rx_fifo ? session->rx_fifo->nitems :
2867                                 vcm->cfg.rx_fifo_size);
2868           *buflen = sizeof (u32);
2869
2870           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_RX_FIFO_LEN: %u (0x%x), "
2871                 "buflen %d, #VPP-TBD#", getpid (),
2872                 *(size_t *) buffer, *(size_t *) buffer, *buflen);
2873         }
2874       else
2875         rv = VPPCOM_EINVAL;
2876       break;
2877
2878     case VPPCOM_ATTR_SET_RX_FIFO_LEN:
2879       if (buffer && buflen && (*buflen == sizeof (u32)))
2880         {
2881           /* VPP-TBD */
2882           session->rcvbuf_size = *(u32 *) buffer;
2883           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_RX_FIFO_LEN: %u (0x%x), "
2884                 "buflen %d, #VPP-TBD#", getpid (),
2885                 session->sndbuf_size, session->sndbuf_size, *buflen);
2886         }
2887       else
2888         rv = VPPCOM_EINVAL;
2889       break;
2890
2891     case VPPCOM_ATTR_GET_REUSEADDR:
2892       if (buffer && buflen && (*buflen >= sizeof (int)))
2893         {
2894           /* VPP-TBD */
2895           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2896                                                 VCL_SESS_ATTR_REUSEADDR);
2897           *buflen = sizeof (int);
2898
2899           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEADDR: %d, "
2900                 "buflen %d, #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2901         }
2902       else
2903         rv = VPPCOM_EINVAL;
2904       break;
2905
2906     case VPPCOM_ATTR_SET_REUSEADDR:
2907       if (buffer && buflen && (*buflen == sizeof (int)) &&
2908           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
2909         {
2910           /* VPP-TBD */
2911           if (*(int *) buffer)
2912             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEADDR);
2913           else
2914             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEADDR);
2915
2916           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEADDR: %d, buflen %d,"
2917                 " #VPP-TBD#", getpid (),
2918                 VCL_SESS_ATTR_TEST (session->attr,
2919                                     VCL_SESS_ATTR_REUSEADDR), *buflen);
2920         }
2921       else
2922         rv = VPPCOM_EINVAL;
2923       break;
2924
2925     case VPPCOM_ATTR_GET_REUSEPORT:
2926       if (buffer && buflen && (*buflen >= sizeof (int)))
2927         {
2928           /* VPP-TBD */
2929           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2930                                                 VCL_SESS_ATTR_REUSEPORT);
2931           *buflen = sizeof (int);
2932
2933           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_REUSEPORT: %d, buflen %d,"
2934                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2935         }
2936       else
2937         rv = VPPCOM_EINVAL;
2938       break;
2939
2940     case VPPCOM_ATTR_SET_REUSEPORT:
2941       if (buffer && buflen && (*buflen == sizeof (int)) &&
2942           !VCL_SESS_ATTR_TEST (session->attr, VCL_SESS_ATTR_LISTEN))
2943         {
2944           /* VPP-TBD */
2945           if (*(int *) buffer)
2946             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_REUSEPORT);
2947           else
2948             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_REUSEPORT);
2949
2950           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_REUSEPORT: %d, buflen %d,"
2951                 " #VPP-TBD#", getpid (),
2952                 VCL_SESS_ATTR_TEST (session->attr,
2953                                     VCL_SESS_ATTR_REUSEPORT), *buflen);
2954         }
2955       else
2956         rv = VPPCOM_EINVAL;
2957       break;
2958
2959     case VPPCOM_ATTR_GET_BROADCAST:
2960       if (buffer && buflen && (*buflen >= sizeof (int)))
2961         {
2962           /* VPP-TBD */
2963           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2964                                                 VCL_SESS_ATTR_BROADCAST);
2965           *buflen = sizeof (int);
2966
2967           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_BROADCAST: %d, buflen %d,"
2968                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
2969         }
2970       else
2971         rv = VPPCOM_EINVAL;
2972       break;
2973
2974     case VPPCOM_ATTR_SET_BROADCAST:
2975       if (buffer && buflen && (*buflen == sizeof (int)))
2976         {
2977           /* VPP-TBD */
2978           if (*(int *) buffer)
2979             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_BROADCAST);
2980           else
2981             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_BROADCAST);
2982
2983           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_BROADCAST: %d, buflen %d, "
2984                 "#VPP-TBD#", getpid (),
2985                 VCL_SESS_ATTR_TEST (session->attr,
2986                                     VCL_SESS_ATTR_BROADCAST), *buflen);
2987         }
2988       else
2989         rv = VPPCOM_EINVAL;
2990       break;
2991
2992     case VPPCOM_ATTR_GET_V6ONLY:
2993       if (buffer && buflen && (*buflen >= sizeof (int)))
2994         {
2995           /* VPP-TBD */
2996           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
2997                                                 VCL_SESS_ATTR_V6ONLY);
2998           *buflen = sizeof (int);
2999
3000           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_V6ONLY: %d, buflen %d, "
3001                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3002         }
3003       else
3004         rv = VPPCOM_EINVAL;
3005       break;
3006
3007     case VPPCOM_ATTR_SET_V6ONLY:
3008       if (buffer && buflen && (*buflen == sizeof (int)))
3009         {
3010           /* VPP-TBD */
3011           if (*(int *) buffer)
3012             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_V6ONLY);
3013           else
3014             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_V6ONLY);
3015
3016           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_V6ONLY: %d, buflen %d, "
3017                 "#VPP-TBD#", getpid (),
3018                 VCL_SESS_ATTR_TEST (session->attr,
3019                                     VCL_SESS_ATTR_V6ONLY), *buflen);
3020         }
3021       else
3022         rv = VPPCOM_EINVAL;
3023       break;
3024
3025     case VPPCOM_ATTR_GET_KEEPALIVE:
3026       if (buffer && buflen && (*buflen >= sizeof (int)))
3027         {
3028           /* VPP-TBD */
3029           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3030                                                 VCL_SESS_ATTR_KEEPALIVE);
3031           *buflen = sizeof (int);
3032
3033           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_KEEPALIVE: %d, buflen %d, "
3034                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3035         }
3036       else
3037         rv = VPPCOM_EINVAL;
3038       break;
3039
3040     case VPPCOM_ATTR_SET_KEEPALIVE:
3041       if (buffer && buflen && (*buflen == sizeof (int)))
3042         {
3043           /* VPP-TBD */
3044           if (*(int *) buffer)
3045             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3046           else
3047             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_KEEPALIVE);
3048
3049           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_KEEPALIVE: %d, buflen %d, "
3050                 "#VPP-TBD#", getpid (),
3051                 VCL_SESS_ATTR_TEST (session->attr,
3052                                     VCL_SESS_ATTR_KEEPALIVE), *buflen);
3053         }
3054       else
3055         rv = VPPCOM_EINVAL;
3056       break;
3057
3058     case VPPCOM_ATTR_GET_TCP_NODELAY:
3059       if (buffer && buflen && (*buflen >= sizeof (int)))
3060         {
3061           /* VPP-TBD */
3062           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3063                                                 VCL_SESS_ATTR_TCP_NODELAY);
3064           *buflen = sizeof (int);
3065
3066           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_NODELAY: %d, buflen %d, "
3067                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3068         }
3069       else
3070         rv = VPPCOM_EINVAL;
3071       break;
3072
3073     case VPPCOM_ATTR_SET_TCP_NODELAY:
3074       if (buffer && buflen && (*buflen == sizeof (int)))
3075         {
3076           /* VPP-TBD */
3077           if (*(int *) buffer)
3078             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3079           else
3080             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_NODELAY);
3081
3082           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_NODELAY: %d, buflen %d, "
3083                 "#VPP-TBD#", getpid (),
3084                 VCL_SESS_ATTR_TEST (session->attr,
3085                                     VCL_SESS_ATTR_TCP_NODELAY), *buflen);
3086         }
3087       else
3088         rv = VPPCOM_EINVAL;
3089       break;
3090
3091     case VPPCOM_ATTR_GET_TCP_KEEPIDLE:
3092       if (buffer && buflen && (*buflen >= sizeof (int)))
3093         {
3094           /* VPP-TBD */
3095           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3096                                                 VCL_SESS_ATTR_TCP_KEEPIDLE);
3097           *buflen = sizeof (int);
3098
3099           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPIDLE: %d, buflen %d, "
3100                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3101         }
3102       else
3103         rv = VPPCOM_EINVAL;
3104       break;
3105
3106     case VPPCOM_ATTR_SET_TCP_KEEPIDLE:
3107       if (buffer && buflen && (*buflen == sizeof (int)))
3108         {
3109           /* VPP-TBD */
3110           if (*(int *) buffer)
3111             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3112           else
3113             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPIDLE);
3114
3115           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPIDLE: %d, buflen %d, "
3116                 "#VPP-TBD#", getpid (),
3117                 VCL_SESS_ATTR_TEST (session->attr,
3118                                     VCL_SESS_ATTR_TCP_KEEPIDLE), *buflen);
3119         }
3120       else
3121         rv = VPPCOM_EINVAL;
3122       break;
3123
3124     case VPPCOM_ATTR_GET_TCP_KEEPINTVL:
3125       if (buffer && buflen && (*buflen >= sizeof (int)))
3126         {
3127           /* VPP-TBD */
3128           *(int *) buffer = VCL_SESS_ATTR_TEST (session->attr,
3129                                                 VCL_SESS_ATTR_TCP_KEEPINTVL);
3130           *buflen = sizeof (int);
3131
3132           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_KEEPINTVL: %d, buflen %d, "
3133                 "#VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3134         }
3135       else
3136         rv = VPPCOM_EINVAL;
3137       break;
3138
3139     case VPPCOM_ATTR_SET_TCP_KEEPINTVL:
3140       if (buffer && buflen && (*buflen == sizeof (int)))
3141         {
3142           /* VPP-TBD */
3143           if (*(int *) buffer)
3144             VCL_SESS_ATTR_SET (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3145           else
3146             VCL_SESS_ATTR_CLR (session->attr, VCL_SESS_ATTR_TCP_KEEPINTVL);
3147
3148           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_KEEPINTVL: %d, buflen %d, "
3149                 "#VPP-TBD#", getpid (),
3150                 VCL_SESS_ATTR_TEST (session->attr,
3151                                     VCL_SESS_ATTR_TCP_KEEPINTVL), *buflen);
3152         }
3153       else
3154         rv = VPPCOM_EINVAL;
3155       break;
3156
3157     case VPPCOM_ATTR_GET_TCP_USER_MSS:
3158       if (buffer && buflen && (*buflen >= sizeof (u32)))
3159         {
3160           /* VPP-TBD */
3161           *(u32 *) buffer = session->user_mss;
3162           *buflen = sizeof (int);
3163
3164           VDBG (2, "VCL<%d>: VPPCOM_ATTR_GET_TCP_USER_MSS: %d, buflen %d,"
3165                 " #VPP-TBD#", getpid (), *(int *) buffer, *buflen);
3166         }
3167       else
3168         rv = VPPCOM_EINVAL;
3169       break;
3170
3171     case VPPCOM_ATTR_SET_TCP_USER_MSS:
3172       if (buffer && buflen && (*buflen == sizeof (u32)))
3173         {
3174           /* VPP-TBD */
3175           session->user_mss = *(u32 *) buffer;
3176
3177           VDBG (2, "VCL<%d>: VPPCOM_ATTR_SET_TCP_USER_MSS: %u, buflen %d, "
3178                 "#VPP-TBD#", getpid (), session->user_mss, *buflen);
3179         }
3180       else
3181         rv = VPPCOM_EINVAL;
3182       break;
3183
3184     case VPPCOM_ATTR_GET_REFCNT:
3185       rv = vcl_session_get_refcnt (session);
3186       break;
3187
3188     default:
3189       rv = VPPCOM_EINVAL;
3190       break;
3191     }
3192
3193   return rv;
3194 }
3195
3196 int
3197 vppcom_session_recvfrom (uint32_t session_handle, void *buffer,
3198                          uint32_t buflen, int flags, vppcom_endpt_t * ep)
3199 {
3200   vcl_worker_t *wrk = vcl_worker_get_current ();
3201   int rv = VPPCOM_OK;
3202   vcl_session_t *session = 0;
3203
3204   if (ep)
3205     {
3206       session = vcl_session_get_w_handle (wrk, session_handle);
3207       if (PREDICT_FALSE (!session))
3208         {
3209           VDBG (0, "VCL<%d>: invalid session, sid (%u) has been closed!",
3210                 getpid (), session_handle);
3211           return VPPCOM_EBADFD;
3212         }
3213       ep->is_ip4 = session->transport.is_ip4;
3214       ep->port = session->transport.rmt_port;
3215     }
3216
3217   if (flags == 0)
3218     rv = vppcom_session_read (session_handle, buffer, buflen);
3219   else if (flags & MSG_PEEK)
3220     rv = vppcom_session_peek (session_handle, buffer, buflen);
3221   else
3222     {
3223       clib_warning ("VCL<%d>: Unsupport flags for recvfrom %d",
3224                     getpid (), flags);
3225       return VPPCOM_EAFNOSUPPORT;
3226     }
3227
3228   if (ep)
3229     {
3230       if (session->transport.is_ip4)
3231         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip4,
3232                           sizeof (ip4_address_t));
3233       else
3234         clib_memcpy_fast (ep->ip, &session->transport.rmt_ip.ip6,
3235                           sizeof (ip6_address_t));
3236     }
3237
3238   return rv;
3239 }
3240
3241 int
3242 vppcom_session_sendto (uint32_t session_handle, void *buffer,
3243                        uint32_t buflen, int flags, vppcom_endpt_t * ep)
3244 {
3245   if (!buffer)
3246     return VPPCOM_EINVAL;
3247
3248   if (ep)
3249     {
3250       // TBD
3251       return VPPCOM_EINVAL;
3252     }
3253
3254   if (flags)
3255     {
3256       // TBD check the flags and do the right thing
3257       VDBG (2, "VCL<%d>: handling flags 0x%u (%d) not implemented yet.",
3258             getpid (), flags, flags);
3259     }
3260
3261   return (vppcom_session_write (session_handle, buffer, buflen));
3262 }
3263
3264 int
3265 vppcom_poll (vcl_poll_t * vp, uint32_t n_sids, double wait_for_time)
3266 {
3267   vcl_worker_t *wrk = vcl_worker_get_current ();
3268   f64 timeout = clib_time_now (&wrk->clib_time) + wait_for_time;
3269   u32 i, keep_trying = 1;
3270   svm_msg_q_msg_t msg;
3271   session_event_t *e;
3272   int rv, num_ev = 0;
3273
3274   VDBG (3, "VCL<%d>: vp %p, nsids %u, wait_for_time %f",
3275         getpid (), vp, n_sids, wait_for_time);
3276
3277   if (!vp)
3278     return VPPCOM_EFAULT;
3279
3280   do
3281     {
3282       vcl_session_t *session;
3283
3284       /* Dequeue all events and drop all unhandled io events */
3285       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
3286         {
3287           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
3288           vcl_handle_mq_event (wrk, e);
3289           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
3290         }
3291       vec_reset_length (wrk->unhandled_evts_vector);
3292
3293       for (i = 0; i < n_sids; i++)
3294         {
3295           session = vcl_session_get (wrk, vp[i].sid);
3296           if (!session)
3297             {
3298               vp[i].revents = POLLHUP;
3299               num_ev++;
3300               continue;
3301             }
3302
3303           vp[i].revents = 0;
3304
3305           if (POLLIN & vp[i].events)
3306             {
3307               rv = vppcom_session_read_ready (session);
3308               if (rv > 0)
3309                 {
3310                   vp[i].revents |= POLLIN;
3311                   num_ev++;
3312                 }
3313               else if (rv < 0)
3314                 {
3315                   switch (rv)
3316                     {
3317                     case VPPCOM_ECONNRESET:
3318                       vp[i].revents = POLLHUP;
3319                       break;
3320
3321                     default:
3322                       vp[i].revents = POLLERR;
3323                       break;
3324                     }
3325                   num_ev++;
3326                 }
3327             }
3328
3329           if (POLLOUT & vp[i].events)
3330             {
3331               rv = vppcom_session_write_ready (session);
3332               if (rv > 0)
3333                 {
3334                   vp[i].revents |= POLLOUT;
3335                   num_ev++;
3336                 }
3337               else if (rv < 0)
3338                 {
3339                   switch (rv)
3340                     {
3341                     case VPPCOM_ECONNRESET:
3342                       vp[i].revents = POLLHUP;
3343                       break;
3344
3345                     default:
3346                       vp[i].revents = POLLERR;
3347                       break;
3348                     }
3349                   num_ev++;
3350                 }
3351             }
3352
3353           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
3354             {
3355               vp[i].revents = POLLNVAL;
3356               num_ev++;
3357             }
3358         }
3359       if (wait_for_time != -1)
3360         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
3361     }
3362   while ((num_ev == 0) && keep_trying);
3363
3364   if (VPPCOM_DEBUG > 3)
3365     {
3366       clib_warning ("VCL<%d>: returning %d", getpid (), num_ev);
3367       for (i = 0; i < n_sids; i++)
3368         {
3369           clib_warning ("VCL<%d>: vp[%d].sid %d (0x%x), .events 0x%x, "
3370                         ".revents 0x%x", getpid (), i, vp[i].sid, vp[i].sid,
3371                         vp[i].events, vp[i].revents);
3372         }
3373     }
3374   return num_ev;
3375 }
3376
3377 int
3378 vppcom_mq_epoll_fd (void)
3379 {
3380   vcl_worker_t *wrk = vcl_worker_get_current ();
3381   return wrk->mqs_epfd;
3382 }
3383
3384 int
3385 vppcom_session_index (uint32_t session_handle)
3386 {
3387   return session_handle & 0xFFFFFF;
3388 }
3389
3390 int
3391 vppcom_session_handle (uint32_t session_index)
3392 {
3393   return (vcl_get_worker_index () << 24) | session_index;
3394 }
3395
3396 int
3397 vppcom_worker_register (void)
3398 {
3399   if (!vcl_worker_alloc_and_init ())
3400     return VPPCOM_EEXIST;
3401
3402   if (vcl_worker_set_bapi ())
3403     return VPPCOM_EEXIST;
3404
3405   if (vcl_worker_register_with_vpp ())
3406     return VPPCOM_EEXIST;
3407
3408   return VPPCOM_OK;
3409 }
3410
3411 int
3412 vppcom_worker_index (void)
3413 {
3414   return vcl_get_worker_index ();
3415 }
3416
3417 /*
3418  * fd.io coding-style-patch-verification: ON
3419  *
3420  * Local Variables:
3421  * eval: (c-set-style "gnu")
3422  * End:
3423  */