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