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