Remove c-11 memcpy checks from perf-critical code
[vpp.git] / src / vcl / vppcom.c
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <svm/svm_fifo_segment.h>
19 #include <vcl/vppcom.h>
20 #include <vcl/vcl_debug.h>
21 #include <vcl/vcl_private.h>
22
23 __thread uword __vcl_worker_index = ~0;
24
25 static u8 not_ready;
26
27 void
28 sigsegv_signal (int signum)
29 {
30   not_ready = 1;
31 }
32
33 static void
34 vcl_wait_for_memory (void *mem)
35 {
36   u8 __clib_unused test;
37   if (vcm->mounting_segment)
38     {
39       while (vcm->mounting_segment)
40         ;
41       return;
42     }
43   if (1 || vcm->debug)
44     {
45       usleep (1e5);
46       return;
47     }
48   if (signal (SIGSEGV, sigsegv_signal))
49     {
50       perror ("signal()");
51       return;
52     }
53   not_ready = 0;
54
55 again:
56   test = *(u8 *) mem;
57   if (not_ready)
58     {
59       not_ready = 0;
60       usleep (1);
61       goto again;
62     }
63
64   signal (SIGSEGV, SIG_DFL);
65 }
66
67 const char *
68 vppcom_session_state_str (session_state_t state)
69 {
70   char *st;
71
72   switch (state)
73     {
74     case STATE_START:
75       st = "STATE_START";
76       break;
77
78     case STATE_CONNECT:
79       st = "STATE_CONNECT";
80       break;
81
82     case STATE_LISTEN:
83       st = "STATE_LISTEN";
84       break;
85
86     case STATE_ACCEPT:
87       st = "STATE_ACCEPT";
88       break;
89
90     case STATE_CLOSE_ON_EMPTY:
91       st = "STATE_CLOSE_ON_EMPTY";
92       break;
93
94     case STATE_DISCONNECT:
95       st = "STATE_DISCONNECT";
96       break;
97
98     case STATE_FAILED:
99       st = "STATE_FAILED";
100       break;
101
102     default:
103       st = "UNKNOWN_STATE";
104       break;
105     }
106
107   return st;
108 }
109
110 u8 *
111 format_ip4_address (u8 * s, va_list * args)
112 {
113   u8 *a = va_arg (*args, u8 *);
114   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
115 }
116
117 u8 *
118 format_ip6_address (u8 * s, va_list * args)
119 {
120   ip6_address_t *a = va_arg (*args, ip6_address_t *);
121   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
122
123   i_max_n_zero = ARRAY_LEN (a->as_u16);
124   max_n_zeros = 0;
125   i_first_zero = i_max_n_zero;
126   n_zeros = 0;
127   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
128     {
129       u32 is_zero = a->as_u16[i] == 0;
130       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
131         {
132           i_first_zero = i;
133           n_zeros = 0;
134         }
135       n_zeros += is_zero;
136       if ((!is_zero && n_zeros > max_n_zeros)
137           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
138         {
139           i_max_n_zero = i_first_zero;
140           max_n_zeros = n_zeros;
141           i_first_zero = ARRAY_LEN (a->as_u16);
142           n_zeros = 0;
143         }
144     }
145
146   last_double_colon = 0;
147   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
148     {
149       if (i == i_max_n_zero && max_n_zeros > 1)
150         {
151           s = format (s, "::");
152           i += max_n_zeros - 1;
153           last_double_colon = 1;
154         }
155       else
156         {
157           s = format (s, "%s%x",
158                       (last_double_colon || i == 0) ? "" : ":",
159                       clib_net_to_host_u16 (a->as_u16[i]));
160           last_double_colon = 0;
161         }
162     }
163
164   return s;
165 }
166
167 /* Format an IP46 address. */
168 u8 *
169 format_ip46_address (u8 * s, va_list * args)
170 {
171   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
172   ip46_type_t type = va_arg (*args, ip46_type_t);
173   int is_ip4 = 1;
174
175   switch (type)
176     {
177     case IP46_TYPE_ANY:
178       is_ip4 = ip46_address_is_ip4 (ip46);
179       break;
180     case IP46_TYPE_IP4:
181       is_ip4 = 1;
182       break;
183     case IP46_TYPE_IP6:
184       is_ip4 = 0;
185       break;
186     }
187
188   return is_ip4 ?
189     format (s, "%U", format_ip4_address, &ip46->ip4) :
190     format (s, "%U", format_ip6_address, &ip46->ip6);
191 }
192
193 /*
194  * VPPCOM Utility Functions
195  */
196
197
198 static svm_msg_q_t *
199 vcl_session_vpp_evt_q (vcl_worker_t * wrk, vcl_session_t * s)
200 {
201   if (vcl_session_is_ct (s))
202     return wrk->vpp_event_queues[0];
203   else
204     return wrk->vpp_event_queues[s->tx_fifo->master_thread_index];
205 }
206
207 static void
208 vcl_send_session_accepted_reply (svm_msg_q_t * mq, u32 context,
209                                  session_handle_t handle, int retval)
210 {
211   app_session_evt_t _app_evt, *app_evt = &_app_evt;
212   session_accepted_reply_msg_t *rmp;
213   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_ACCEPTED_REPLY);
214   rmp = (session_accepted_reply_msg_t *) app_evt->evt->data;
215   rmp->handle = handle;
216   rmp->context = context;
217   rmp->retval = retval;
218   app_send_ctrl_evt_to_vpp (mq, app_evt);
219 }
220
221 static void
222 vcl_send_session_disconnected_reply (svm_msg_q_t * mq, u32 context,
223                                      session_handle_t handle, int retval)
224 {
225   app_session_evt_t _app_evt, *app_evt = &_app_evt;
226   session_disconnected_reply_msg_t *rmp;
227   app_alloc_ctrl_evt_to_vpp (mq, app_evt,
228                              SESSION_CTRL_EVT_DISCONNECTED_REPLY);
229   rmp = (session_disconnected_reply_msg_t *) app_evt->evt->data;
230   rmp->handle = handle;
231   rmp->context = context;
232   rmp->retval = retval;
233   app_send_ctrl_evt_to_vpp (mq, app_evt);
234 }
235
236 static void
237 vcl_send_session_reset_reply (svm_msg_q_t * mq, u32 context,
238                               session_handle_t handle, int retval)
239 {
240   app_session_evt_t _app_evt, *app_evt = &_app_evt;
241   session_reset_reply_msg_t *rmp;
242   app_alloc_ctrl_evt_to_vpp (mq, app_evt, SESSION_CTRL_EVT_RESET_REPLY);
243   rmp = (session_reset_reply_msg_t *) app_evt->evt->data;
244   rmp->handle = handle;
245   rmp->context = context;
246   rmp->retval = retval;
247   app_send_ctrl_evt_to_vpp (mq, app_evt);
248 }
249
250 static u32
251 vcl_session_accepted_handler (vcl_worker_t * wrk, session_accepted_msg_t * mp)
252 {
253   vcl_session_t *session, *listen_session;
254   svm_fifo_t *rx_fifo, *tx_fifo;
255   u32 vpp_wrk_index;
256   svm_msg_q_t *evt_q;
257
258   session = vcl_session_alloc (wrk);
259
260   listen_session = vcl_session_table_lookup_listener (wrk,
261                                                       mp->listener_handle);
262   if (!listen_session)
263     {
264       svm_msg_q_t *evt_q;
265       evt_q = uword_to_pointer (mp->vpp_event_queue_address, svm_msg_q_t *);
266       clib_warning ("VCL<%d>: ERROR: couldn't find listen session: "
267                     "unknown vpp listener handle %llx",
268                     getpid (), mp->listener_handle);
269       vcl_send_session_accepted_reply (evt_q, mp->context, mp->handle,
270                                        VNET_API_ERROR_INVALID_ARGUMENT);
271       vcl_session_free (wrk, session);
272       return VCL_INVALID_SESSION_INDEX;
273     }
274
275   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
276   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
277
278   if (mp->server_event_queue_address)
279     {
280       session->vpp_evt_q = uword_to_pointer (mp->client_event_queue_address,
281                                              svm_msg_q_t *);
282       session->our_evt_q = uword_to_pointer (mp->server_event_queue_address,
283                                              svm_msg_q_t *);
284       vcl_wait_for_memory (session->vpp_evt_q);
285       rx_fifo->master_session_index = session->session_index;
286       tx_fifo->master_session_index = session->session_index;
287       rx_fifo->master_thread_index = vcl_get_worker_index ();
288       tx_fifo->master_thread_index = vcl_get_worker_index ();
289       vec_validate (wrk->vpp_event_queues, 0);
290       evt_q = uword_to_pointer (mp->vpp_event_queue_address, svm_msg_q_t *);
291       wrk->vpp_event_queues[0] = evt_q;
292     }
293   else
294     {
295       session->vpp_evt_q = uword_to_pointer (mp->vpp_event_queue_address,
296                                              svm_msg_q_t *);
297       rx_fifo->client_session_index = session->session_index;
298       tx_fifo->client_session_index = session->session_index;
299       rx_fifo->client_thread_index = vcl_get_worker_index ();
300       tx_fifo->client_thread_index = vcl_get_worker_index ();
301       vpp_wrk_index = tx_fifo->master_thread_index;
302       vec_validate (wrk->vpp_event_queues, vpp_wrk_index);
303       wrk->vpp_event_queues[vpp_wrk_index] = session->vpp_evt_q;
304     }
305
306   session->vpp_handle = mp->handle;
307   session->client_context = mp->context;
308   session->rx_fifo = rx_fifo;
309   session->tx_fifo = tx_fifo;
310
311   session->session_state = STATE_ACCEPT;
312   session->transport.rmt_port = mp->port;
313   session->transport.is_ip4 = mp->is_ip4;
314   clib_memcpy_fast (&session->transport.rmt_ip, mp->ip,
315                     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_fast (&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_fast (&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_fast (&session->transport.lcl_ip.ip4, ep->ip,
947                       sizeof (ip4_address_t));
948   else
949     clib_memcpy_fast (&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_fast (&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_fast (ep->ip, &client_session->transport.rmt_ip.ip4,
1123                           sizeof (ip4_address_t));
1124       else
1125         clib_memcpy_fast (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_fast (&session->transport.rmt_ip.ip4, server_ep->ip,
1196                       sizeof (ip4_address_t));
1197   else
1198     clib_memcpy_fast (&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   s->has_rx_evt = 0;
1298
1299   if (svm_fifo_is_empty (rx_fifo))
1300     {
1301       if (is_nonblocking)
1302         {
1303           svm_fifo_unset_event (rx_fifo);
1304           return VPPCOM_EWOULDBLOCK;
1305         }
1306       while (svm_fifo_is_empty (rx_fifo))
1307         {
1308           svm_fifo_unset_event (rx_fifo);
1309           svm_msg_q_lock (mq);
1310           if (svm_msg_q_is_empty (mq))
1311             svm_msg_q_wait (mq);
1312
1313           svm_msg_q_sub_w_lock (mq, &msg);
1314           e = svm_msg_q_msg_data (mq, &msg);
1315           svm_msg_q_unlock (mq);
1316           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1317             {
1318               vcl_handle_mq_event (wrk, e);
1319               svm_msg_q_free_msg (mq, &msg);
1320               continue;
1321             }
1322           svm_msg_q_free_msg (mq, &msg);
1323
1324           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1325             return 0;
1326         }
1327     }
1328
1329   if (s->is_dgram)
1330     n_read = app_recv_dgram_raw (rx_fifo, buf, n, &s->transport, 0, peek);
1331   else
1332     n_read = app_recv_stream_raw (rx_fifo, buf, n, 0, peek);
1333
1334   if (svm_fifo_is_empty (rx_fifo))
1335     svm_fifo_unset_event (rx_fifo);
1336
1337   if (is_ct && svm_fifo_want_tx_evt (rx_fifo))
1338     {
1339       svm_fifo_set_want_tx_evt (s->rx_fifo, 0);
1340       app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo, SESSION_IO_EVT_CT_RX,
1341                               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   s->has_rx_evt = 0;
1391
1392   if (svm_fifo_is_empty (rx_fifo))
1393     {
1394       if (is_nonblocking)
1395         {
1396           svm_fifo_unset_event (rx_fifo);
1397           return VPPCOM_EWOULDBLOCK;
1398         }
1399       while (svm_fifo_is_empty (rx_fifo))
1400         {
1401           svm_fifo_unset_event (rx_fifo);
1402           svm_msg_q_lock (mq);
1403           if (svm_msg_q_is_empty (mq))
1404             svm_msg_q_wait (mq);
1405
1406           svm_msg_q_sub_w_lock (mq, &msg);
1407           e = svm_msg_q_msg_data (mq, &msg);
1408           svm_msg_q_unlock (mq);
1409           if (!vcl_is_rx_evt_for_session (e, s->session_index, is_ct))
1410             {
1411               vcl_handle_mq_event (wrk, e);
1412               svm_msg_q_free_msg (mq, &msg);
1413               continue;
1414             }
1415           svm_msg_q_free_msg (mq, &msg);
1416
1417           if (PREDICT_FALSE (s->session_state == STATE_CLOSE_ON_EMPTY))
1418             return 0;
1419         }
1420     }
1421
1422   n_read = svm_fifo_segments (rx_fifo, (svm_fifo_segment_t *) ds);
1423   svm_fifo_unset_event (rx_fifo);
1424
1425   if (is_ct && n_read + svm_fifo_max_dequeue (rx_fifo) == rx_fifo->nitems)
1426     {
1427       /* If the peer is not polling send notification */
1428       if (!svm_fifo_has_event (s->rx_fifo))
1429         app_send_io_evt_to_vpp (s->vpp_evt_q, s->rx_fifo,
1430                                 SESSION_IO_EVT_CT_RX, SVM_Q_WAIT);
1431     }
1432
1433   return n_read;
1434 }
1435
1436 void
1437 vppcom_session_free_segments (uint32_t session_handle,
1438                               vppcom_data_segments_t ds)
1439 {
1440   vcl_worker_t *wrk = vcl_worker_get_current ();
1441   vcl_session_t *s;
1442
1443   s = vcl_session_get_w_handle (wrk, session_handle);
1444   if (PREDICT_FALSE (!s || s->is_vep))
1445     return;
1446
1447   svm_fifo_segments_free (s->rx_fifo, (svm_fifo_segment_t *) ds);
1448 }
1449
1450 static inline int
1451 vppcom_session_read_ready (vcl_session_t * session)
1452 {
1453   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1454   if (PREDICT_FALSE (session->is_vep))
1455     {
1456       clib_warning ("VCL<%d>: ERROR: sid %u: cannot read from an "
1457                     "epoll session!", getpid (), session->session_index);
1458       return VPPCOM_EBADFD;
1459     }
1460
1461   if (PREDICT_FALSE (!(session->session_state & (STATE_OPEN | STATE_LISTEN))))
1462     {
1463       session_state_t state = session->session_state;
1464       int rv;
1465
1466       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1467
1468       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open!"
1469             " state 0x%x (%s), returning %d (%s)", getpid (),
1470             session->vpp_handle, session->session_index, state,
1471             vppcom_session_state_str (state), rv, vppcom_retval_str (rv));
1472       return rv;
1473     }
1474
1475   if (session->session_state & STATE_LISTEN)
1476     return clib_fifo_elts (session->accept_evts_fifo);
1477
1478   return svm_fifo_max_dequeue (session->rx_fifo);
1479 }
1480
1481 int
1482 vppcom_data_segment_copy (void *buf, vppcom_data_segments_t ds, u32 max_bytes)
1483 {
1484   u32 first_copy = clib_min (ds[0].len, max_bytes);
1485   clib_memcpy_fast (buf, ds[0].data, first_copy);
1486   if (first_copy < max_bytes)
1487     {
1488       clib_memcpy_fast (buf + first_copy, ds[1].data,
1489                         clib_min (ds[1].len, max_bytes - first_copy));
1490     }
1491   return 0;
1492 }
1493
1494 static u8
1495 vcl_is_tx_evt_for_session (session_event_t * e, u32 sid, u8 is_ct)
1496 {
1497   if (!is_ct)
1498     return (e->event_type == FIFO_EVENT_APP_TX
1499             && e->fifo->client_session_index == sid);
1500   else
1501     return (e->event_type == SESSION_IO_EVT_CT_RX);
1502 }
1503
1504 int
1505 vppcom_session_write (uint32_t session_handle, void *buf, size_t n)
1506 {
1507   vcl_worker_t *wrk = vcl_worker_get_current ();
1508   int rv, n_write, is_nonblocking;
1509   vcl_session_t *s = 0;
1510   svm_fifo_t *tx_fifo = 0;
1511   session_evt_type_t et;
1512   svm_msg_q_msg_t msg;
1513   session_event_t *e;
1514   svm_msg_q_t *mq;
1515   u8 is_ct;
1516
1517   if (PREDICT_FALSE (!buf))
1518     return VPPCOM_EINVAL;
1519
1520   s = vcl_session_get_w_handle (wrk, session_handle);
1521   if (PREDICT_FALSE (!s))
1522     return VPPCOM_EBADFD;
1523
1524   if (PREDICT_FALSE (s->is_vep))
1525     {
1526       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1527                     "cannot write to an epoll session!",
1528                     getpid (), s->vpp_handle, session_handle);
1529
1530       return VPPCOM_EBADFD;
1531     }
1532
1533   if (PREDICT_FALSE (!(s->session_state & STATE_OPEN)))
1534     {
1535       session_state_t state = s->session_state;
1536       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1537       VDBG (1, "VCL<%d>: vpp handle 0x%llx, sid %u: session is not open! "
1538             "state 0x%x (%s)", getpid (), s->vpp_handle, session_handle,
1539             state, vppcom_session_state_str (state));
1540       return rv;
1541     }
1542
1543   tx_fifo = s->tx_fifo;
1544   is_ct = vcl_session_is_ct (s);
1545   is_nonblocking = VCL_SESS_ATTR_TEST (s->attr, VCL_SESS_ATTR_NONBLOCK);
1546   mq = is_ct ? s->our_evt_q : wrk->app_event_queue;
1547   if (svm_fifo_is_full (tx_fifo))
1548     {
1549       if (is_nonblocking)
1550         {
1551           return VPPCOM_EWOULDBLOCK;
1552         }
1553       while (svm_fifo_is_full (tx_fifo))
1554         {
1555           svm_fifo_set_want_tx_evt (tx_fifo, 1);
1556           svm_msg_q_lock (mq);
1557           if (svm_msg_q_is_empty (mq))
1558             svm_msg_q_wait (mq);
1559
1560           svm_msg_q_sub_w_lock (mq, &msg);
1561           e = svm_msg_q_msg_data (mq, &msg);
1562           svm_msg_q_unlock (mq);
1563
1564           if (!vcl_is_tx_evt_for_session (e, s->session_index, is_ct))
1565             vcl_handle_mq_event (wrk, e);
1566           svm_msg_q_free_msg (mq, &msg);
1567         }
1568     }
1569
1570   ASSERT (FIFO_EVENT_APP_TX + 1 == SESSION_IO_EVT_CT_TX);
1571   et = FIFO_EVENT_APP_TX + vcl_session_is_ct (s);
1572   if (s->is_dgram)
1573     n_write = app_send_dgram_raw (tx_fifo, &s->transport,
1574                                   s->vpp_evt_q, buf, n, et, SVM_Q_WAIT);
1575   else
1576     n_write = app_send_stream_raw (tx_fifo, s->vpp_evt_q, buf, n, et,
1577                                    SVM_Q_WAIT);
1578
1579   ASSERT (n_write > 0);
1580
1581   VDBG (2, "VCL<%d>: vpp handle 0x%llx, sid %u: wrote %d bytes", getpid (),
1582         s->vpp_handle, session_handle, n_write);
1583
1584   return n_write;
1585 }
1586
1587 static vcl_session_t *
1588 vcl_ct_session_get_from_fifo (vcl_worker_t * wrk, svm_fifo_t * f, u8 type)
1589 {
1590   vcl_session_t *s;
1591   s = vcl_session_get (wrk, f->client_session_index);
1592   if (s)
1593     {
1594       /* rx fifo */
1595       if (type == 0 && s->rx_fifo == f)
1596         return s;
1597       /* tx fifo */
1598       if (type == 1 && s->tx_fifo == f)
1599         return s;
1600     }
1601   s = vcl_session_get (wrk, f->master_session_index);
1602   if (s)
1603     {
1604       if (type == 0 && s->rx_fifo == f)
1605         return s;
1606       if (type == 1 && s->tx_fifo == f)
1607         return s;
1608     }
1609   return 0;
1610 }
1611
1612 static inline int
1613 vppcom_session_write_ready (vcl_session_t * session)
1614 {
1615   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
1616   if (PREDICT_FALSE (session->is_vep))
1617     {
1618       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1619                     "cannot write to an epoll session!",
1620                     getpid (), session->vpp_handle, session->session_index);
1621       return VPPCOM_EBADFD;
1622     }
1623
1624   if (PREDICT_FALSE (session->session_state & STATE_LISTEN))
1625     {
1626       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1627                     "cannot write to a listen session!",
1628                     getpid (), session->vpp_handle, session->session_index);
1629       return VPPCOM_EBADFD;
1630     }
1631
1632   if (PREDICT_FALSE (!(session->session_state & STATE_OPEN)))
1633     {
1634       session_state_t state = session->session_state;
1635       int rv;
1636
1637       rv = ((state & STATE_DISCONNECT) ? VPPCOM_ECONNRESET : VPPCOM_ENOTCONN);
1638       clib_warning ("VCL<%d>: ERROR: vpp handle 0x%llx, sid %u: "
1639                     "session is not open! state 0x%x (%s), "
1640                     "returning %d (%s)", getpid (), session->vpp_handle,
1641                     session->session_index,
1642                     state, vppcom_session_state_str (state),
1643                     rv, vppcom_retval_str (rv));
1644       return rv;
1645     }
1646
1647   VDBG (3, "VCL<%d>: vpp handle 0x%llx, sid %u: peek %s (%p), ready = %d",
1648         getpid (), session->vpp_handle, session->session_index,
1649         session->tx_fifo, svm_fifo_max_enqueue (session->tx_fifo));
1650
1651   return svm_fifo_max_enqueue (session->tx_fifo);
1652 }
1653
1654 static inline int
1655 vcl_mq_dequeue_batch (vcl_worker_t * wrk, svm_msg_q_t * mq)
1656 {
1657   svm_msg_q_msg_t *msg;
1658   u32 n_msgs;
1659   int i;
1660
1661   n_msgs = svm_msg_q_size (mq);
1662   for (i = 0; i < n_msgs; i++)
1663     {
1664       vec_add2 (wrk->mq_msg_vector, msg, 1);
1665       svm_msg_q_sub_w_lock (mq, msg);
1666     }
1667   return n_msgs;
1668 }
1669
1670 #define vcl_fifo_rx_evt_valid_or_break(_fifo)                   \
1671 if (PREDICT_FALSE (svm_fifo_is_empty (_fifo)))                  \
1672   {                                                             \
1673     svm_fifo_unset_event (_fifo);                               \
1674     if (svm_fifo_is_empty (_fifo))                              \
1675       break;                                                    \
1676   }                                                             \
1677
1678 static void
1679 vcl_select_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
1680                             unsigned long n_bits, unsigned long *read_map,
1681                             unsigned long *write_map,
1682                             unsigned long *except_map, u32 * bits_set)
1683 {
1684   session_disconnected_msg_t *disconnected_msg;
1685   session_connected_msg_t *connected_msg;
1686   session_accepted_msg_t *accepted_msg;
1687   vcl_session_msg_t *vcl_msg;
1688   vcl_session_t *session;
1689   u64 handle;
1690   u32 sid;
1691
1692   switch (e->event_type)
1693     {
1694     case FIFO_EVENT_APP_RX:
1695       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1696       sid = e->fifo->client_session_index;
1697       session = vcl_session_get (wrk, sid);
1698       if (!session)
1699         break;
1700       if (sid < n_bits && read_map)
1701         {
1702           clib_bitmap_set_no_check (read_map, sid, 1);
1703           *bits_set += 1;
1704         }
1705       break;
1706     case FIFO_EVENT_APP_TX:
1707       sid = e->fifo->client_session_index;
1708       session = vcl_session_get (wrk, sid);
1709       if (!session)
1710         break;
1711       if (sid < n_bits && write_map)
1712         {
1713           clib_bitmap_set_no_check (write_map, sid, 1);
1714           *bits_set += 1;
1715         }
1716       break;
1717     case SESSION_IO_EVT_CT_TX:
1718       vcl_fifo_rx_evt_valid_or_break (e->fifo);
1719       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
1720       if (!session)
1721         break;
1722       sid = session->session_index;
1723       if (sid < n_bits && read_map)
1724         {
1725           clib_bitmap_set_no_check (read_map, sid, 1);
1726           *bits_set += 1;
1727         }
1728       break;
1729     case SESSION_IO_EVT_CT_RX:
1730       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
1731       if (!session)
1732         break;
1733       sid = session->session_index;
1734       if (sid < n_bits && write_map)
1735         {
1736           clib_bitmap_set_no_check (write_map, sid, 1);
1737           *bits_set += 1;
1738         }
1739       break;
1740     case SESSION_CTRL_EVT_ACCEPTED:
1741       accepted_msg = (session_accepted_msg_t *) e->data;
1742       handle = accepted_msg->listener_handle;
1743       session = vcl_session_table_lookup_listener (wrk, handle);
1744       if (!session)
1745         {
1746           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
1747                         "listener handle %llx", getpid (), handle);
1748           break;
1749         }
1750
1751       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
1752       vcl_msg->accepted_msg = *accepted_msg;
1753       sid = session->session_index;
1754       if (sid < n_bits && read_map)
1755         {
1756           clib_bitmap_set_no_check (read_map, sid, 1);
1757           *bits_set += 1;
1758         }
1759       break;
1760     case SESSION_CTRL_EVT_CONNECTED:
1761       connected_msg = (session_connected_msg_t *) e->data;
1762       vcl_session_connected_handler (wrk, connected_msg);
1763       break;
1764     case SESSION_CTRL_EVT_DISCONNECTED:
1765       disconnected_msg = (session_disconnected_msg_t *) e->data;
1766       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
1767       if (sid < n_bits && except_map)
1768         {
1769           clib_bitmap_set_no_check (except_map, sid, 1);
1770           *bits_set += 1;
1771         }
1772       break;
1773     case SESSION_CTRL_EVT_RESET:
1774       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
1775       if (sid < n_bits && except_map)
1776         {
1777           clib_bitmap_set_no_check (except_map, sid, 1);
1778           *bits_set += 1;
1779         }
1780       break;
1781     default:
1782       clib_warning ("unhandled: %u", e->event_type);
1783       break;
1784     }
1785 }
1786
1787 static int
1788 vcl_select_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
1789                       unsigned long n_bits, unsigned long *read_map,
1790                       unsigned long *write_map, unsigned long *except_map,
1791                       double time_to_wait, u32 * bits_set)
1792 {
1793   svm_msg_q_msg_t *msg;
1794   session_event_t *e;
1795   u32 i;
1796
1797   svm_msg_q_lock (mq);
1798   if (svm_msg_q_is_empty (mq))
1799     {
1800       if (*bits_set)
1801         {
1802           svm_msg_q_unlock (mq);
1803           return 0;
1804         }
1805
1806       if (!time_to_wait)
1807         {
1808           svm_msg_q_unlock (mq);
1809           return 0;
1810         }
1811       else if (time_to_wait < 0)
1812         {
1813           svm_msg_q_wait (mq);
1814         }
1815       else
1816         {
1817           if (svm_msg_q_timedwait (mq, time_to_wait))
1818             {
1819               svm_msg_q_unlock (mq);
1820               return 0;
1821             }
1822         }
1823     }
1824   vcl_mq_dequeue_batch (wrk, mq);
1825   svm_msg_q_unlock (mq);
1826
1827   for (i = 0; i < vec_len (wrk->mq_msg_vector); i++)
1828     {
1829       msg = vec_elt_at_index (wrk->mq_msg_vector, i);
1830       e = svm_msg_q_msg_data (mq, msg);
1831       vcl_select_handle_mq_event (wrk, e, n_bits, read_map, write_map,
1832                                   except_map, bits_set);
1833       svm_msg_q_free_msg (mq, msg);
1834     }
1835   vec_reset_length (wrk->mq_msg_vector);
1836   return *bits_set;
1837 }
1838
1839 static int
1840 vppcom_select_condvar (vcl_worker_t * wrk, unsigned long n_bits,
1841                        unsigned long *read_map, unsigned long *write_map,
1842                        unsigned long *except_map, double time_to_wait,
1843                        u32 * bits_set)
1844 {
1845   double total_wait = 0, wait_slice;
1846   vcl_cut_through_registration_t *cr;
1847
1848   time_to_wait = (time_to_wait == -1) ? 10e9 : time_to_wait;
1849   wait_slice = wrk->cut_through_registrations ? 10e-6 : time_to_wait;
1850   do
1851     {
1852       vcl_ct_registration_lock (wrk);
1853       /* *INDENT-OFF* */
1854       pool_foreach (cr, wrk->cut_through_registrations, ({
1855         vcl_select_handle_mq (wrk, cr->mq, n_bits, read_map, write_map, except_map,
1856                               0, bits_set);
1857       }));
1858       /* *INDENT-ON* */
1859       vcl_ct_registration_unlock (wrk);
1860
1861       vcl_select_handle_mq (wrk, wrk->app_event_queue, n_bits, read_map,
1862                             write_map, except_map, time_to_wait, bits_set);
1863       total_wait += wait_slice;
1864       if (*bits_set)
1865         return *bits_set;
1866     }
1867   while (total_wait < time_to_wait);
1868
1869   return 0;
1870 }
1871
1872 static int
1873 vppcom_select_eventfd (vcl_worker_t * wrk, unsigned long n_bits,
1874                        unsigned long *read_map, unsigned long *write_map,
1875                        unsigned long *except_map, double time_to_wait,
1876                        u32 * bits_set)
1877 {
1878   vcl_mq_evt_conn_t *mqc;
1879   int __clib_unused n_read;
1880   int n_mq_evts, i;
1881   u64 buf;
1882
1883   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
1884   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
1885                           vec_len (wrk->mq_events), time_to_wait);
1886   for (i = 0; i < n_mq_evts; i++)
1887     {
1888       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
1889       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
1890       vcl_select_handle_mq (wrk, mqc->mq, n_bits, read_map, write_map,
1891                             except_map, 0, bits_set);
1892     }
1893
1894   return (n_mq_evts > 0 ? (int) *bits_set : 0);
1895 }
1896
1897 int
1898 vppcom_select (unsigned long n_bits, unsigned long *read_map,
1899                unsigned long *write_map, unsigned long *except_map,
1900                double time_to_wait)
1901 {
1902   u32 sid, minbits = clib_max (n_bits, BITS (uword)), bits_set = 0;
1903   vcl_worker_t *wrk = vcl_worker_get_current ();
1904   vcl_session_t *session = 0;
1905   int rv, i;
1906
1907   ASSERT (sizeof (clib_bitmap_t) == sizeof (long int));
1908
1909   if (n_bits && read_map)
1910     {
1911       clib_bitmap_validate (wrk->rd_bitmap, minbits);
1912       clib_memcpy_fast (wrk->rd_bitmap, read_map,
1913                         vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
1914       memset (read_map, 0, vec_len (wrk->rd_bitmap) * sizeof (clib_bitmap_t));
1915     }
1916   if (n_bits && write_map)
1917     {
1918       clib_bitmap_validate (wrk->wr_bitmap, minbits);
1919       clib_memcpy_fast (wrk->wr_bitmap, write_map,
1920                         vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
1921       memset (write_map, 0,
1922               vec_len (wrk->wr_bitmap) * sizeof (clib_bitmap_t));
1923     }
1924   if (n_bits && except_map)
1925     {
1926       clib_bitmap_validate (wrk->ex_bitmap, minbits);
1927       clib_memcpy_fast (wrk->ex_bitmap, except_map,
1928                         vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
1929       memset (except_map, 0,
1930               vec_len (wrk->ex_bitmap) * sizeof (clib_bitmap_t));
1931     }
1932
1933   if (!n_bits)
1934     return 0;
1935
1936   if (!write_map)
1937     goto check_rd;
1938
1939   /* *INDENT-OFF* */
1940   clib_bitmap_foreach (sid, wrk->wr_bitmap, ({
1941     if (!(session = vcl_session_get (wrk, sid)))
1942       {
1943         VDBG (0, "VCL<%d>: session %d specified in write_map is closed.",
1944               getpid (), sid);
1945         return VPPCOM_EBADFD;
1946       }
1947
1948     rv = svm_fifo_is_full (session->tx_fifo);
1949     if (!rv)
1950       {
1951         clib_bitmap_set_no_check (write_map, sid, 1);
1952         bits_set++;
1953       }
1954   }));
1955
1956 check_rd:
1957   if (!read_map)
1958     goto check_mq;
1959
1960   clib_bitmap_foreach (sid, wrk->rd_bitmap, ({
1961     if (!(session = vcl_session_get (wrk, sid)))
1962       {
1963         VDBG (0, "VCL<%d>: session %d specified in write_map is closed.",
1964               getpid (), sid);
1965         return VPPCOM_EBADFD;
1966       }
1967
1968     rv = vppcom_session_read_ready (session);
1969     if (rv)
1970       {
1971         clib_bitmap_set_no_check (read_map, sid, 1);
1972         bits_set++;
1973       }
1974   }));
1975   /* *INDENT-ON* */
1976
1977 check_mq:
1978
1979   for (i = 0; i < vec_len (wrk->unhandled_evts_vector); i++)
1980     {
1981       vcl_select_handle_mq_event (wrk, &wrk->unhandled_evts_vector[i], n_bits,
1982                                   read_map, write_map, except_map, &bits_set);
1983     }
1984   vec_reset_length (wrk->unhandled_evts_vector);
1985
1986   if (vcm->cfg.use_mq_eventfd)
1987     vppcom_select_eventfd (wrk, n_bits, read_map, write_map, except_map,
1988                            time_to_wait, &bits_set);
1989   else
1990     vppcom_select_condvar (wrk, n_bits, read_map, write_map, except_map,
1991                            time_to_wait, &bits_set);
1992
1993   return (bits_set);
1994 }
1995
1996 static inline void
1997 vep_verify_epoll_chain (vcl_worker_t * wrk, u32 vep_idx)
1998 {
1999   vcl_session_t *session;
2000   vppcom_epoll_t *vep;
2001   u32 sid = vep_idx;
2002
2003   if (VPPCOM_DEBUG <= 1)
2004     return;
2005
2006   /* Assumes caller has acquired spinlock: vcm->sessions_lockp */
2007   session = vcl_session_get (wrk, vep_idx);
2008   if (PREDICT_FALSE (!session))
2009     {
2010       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!",
2011                     getpid (), vep_idx);
2012       goto done;
2013     }
2014   if (PREDICT_FALSE (!session->is_vep))
2015     {
2016       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2017                     getpid (), vep_idx);
2018       goto done;
2019     }
2020   vep = &session->vep;
2021   clib_warning ("VCL<%d>: vep_idx (%u): Dumping epoll chain\n"
2022                 "{\n"
2023                 "   is_vep         = %u\n"
2024                 "   is_vep_session = %u\n"
2025                 "   next_sid       = 0x%x (%u)\n"
2026                 "   wait_cont_idx  = 0x%x (%u)\n"
2027                 "}\n", getpid (), vep_idx,
2028                 session->is_vep, session->is_vep_session,
2029                 vep->next_sh, vep->next_sh,
2030                 session->wait_cont_idx, session->wait_cont_idx);
2031
2032   for (sid = vep->next_sh; sid != ~0; sid = vep->next_sh)
2033     {
2034       session = vcl_session_get (wrk, sid);
2035       if (PREDICT_FALSE (!session))
2036         {
2037           clib_warning ("VCL<%d>: ERROR: Invalid sid (%u)!", getpid (), sid);
2038           goto done;
2039         }
2040       if (PREDICT_FALSE (session->is_vep))
2041         clib_warning ("VCL<%d>: ERROR: sid (%u) is a vep!",
2042                       getpid (), vep_idx);
2043       else if (PREDICT_FALSE (!session->is_vep_session))
2044         {
2045           clib_warning ("VCL<%d>: ERROR: session (%u) "
2046                         "is not a vep session!", getpid (), sid);
2047           goto done;
2048         }
2049       vep = &session->vep;
2050       if (PREDICT_FALSE (vep->vep_sh != vep_idx))
2051         clib_warning ("VCL<%d>: ERROR: session (%u) vep_idx (%u) != "
2052                       "vep_idx (%u)!", getpid (),
2053                       sid, session->vep.vep_sh, vep_idx);
2054       if (session->is_vep_session)
2055         {
2056           clib_warning ("vep_idx[%u]: sid 0x%x (%u)\n"
2057                         "{\n"
2058                         "   next_sid       = 0x%x (%u)\n"
2059                         "   prev_sid       = 0x%x (%u)\n"
2060                         "   vep_idx        = 0x%x (%u)\n"
2061                         "   ev.events      = 0x%x\n"
2062                         "   ev.data.u64    = 0x%llx\n"
2063                         "   et_mask        = 0x%x\n"
2064                         "}\n",
2065                         vep_idx, sid, sid,
2066                         vep->next_sh, vep->next_sh,
2067                         vep->prev_sh, vep->prev_sh,
2068                         vep->vep_sh, vep->vep_sh,
2069                         vep->ev.events, vep->ev.data.u64, vep->et_mask);
2070         }
2071     }
2072
2073 done:
2074   clib_warning ("VCL<%d>: vep_idx (%u): Dump complete!\n",
2075                 getpid (), vep_idx);
2076 }
2077
2078 int
2079 vppcom_epoll_create (void)
2080 {
2081   vcl_worker_t *wrk = vcl_worker_get_current ();
2082   vcl_session_t *vep_session;
2083
2084   vep_session = vcl_session_alloc (wrk);
2085
2086   vep_session->is_vep = 1;
2087   vep_session->vep.vep_sh = ~0;
2088   vep_session->vep.next_sh = ~0;
2089   vep_session->vep.prev_sh = ~0;
2090   vep_session->wait_cont_idx = ~0;
2091   vep_session->vpp_handle = ~0;
2092
2093   vcl_evt (VCL_EVT_EPOLL_CREATE, vep_session, vep_sh);
2094   VDBG (0, "VCL<%d>: Created vep_idx %u / sid %u!",
2095         getpid (), vep_session->session_index, vep_session->session_index);
2096
2097   return vcl_session_handle (vep_session);
2098 }
2099
2100 int
2101 vppcom_epoll_ctl (uint32_t vep_handle, int op, uint32_t session_handle,
2102                   struct epoll_event *event)
2103 {
2104   vcl_worker_t *wrk = vcl_worker_get_current ();
2105   vcl_session_t *vep_session;
2106   vcl_session_t *session;
2107   int rv = VPPCOM_OK;
2108
2109   if (vep_handle == session_handle)
2110     {
2111       clib_warning ("VCL<%d>: ERROR: vep_idx == session_index (%u)!",
2112                     getpid (), vep_handle);
2113       return VPPCOM_EINVAL;
2114     }
2115
2116   vep_session = vcl_session_get_w_handle (wrk, vep_handle);
2117   if (PREDICT_FALSE (!vep_session))
2118     {
2119       clib_warning ("VCL<%d>: ERROR: Invalid vep_idx (%u)!", vep_handle);
2120       return VPPCOM_EBADFD;
2121     }
2122   if (PREDICT_FALSE (!vep_session->is_vep))
2123     {
2124       clib_warning ("VCL<%d>: ERROR: vep_idx (%u) is not a vep!",
2125                     getpid (), vep_handle);
2126       return VPPCOM_EINVAL;
2127     }
2128
2129   ASSERT (vep_session->vep.vep_sh == ~0);
2130   ASSERT (vep_session->vep.prev_sh == ~0);
2131
2132   session = vcl_session_get_w_handle (wrk, session_handle);
2133   if (PREDICT_FALSE (!session))
2134     {
2135       VDBG (0, "VCL<%d>: ERROR: Invalid session_handle (%u)!",
2136             getpid (), session_handle);
2137       return VPPCOM_EBADFD;
2138     }
2139   if (PREDICT_FALSE (session->is_vep))
2140     {
2141       clib_warning ("ERROR: session_handle (%u) is a vep!", vep_handle);
2142       return VPPCOM_EINVAL;
2143     }
2144
2145   switch (op)
2146     {
2147     case EPOLL_CTL_ADD:
2148       if (PREDICT_FALSE (!event))
2149         {
2150           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: NULL pointer to "
2151                         "epoll_event structure!", getpid ());
2152           return VPPCOM_EINVAL;
2153         }
2154       if (vep_session->vep.next_sh != ~0)
2155         {
2156           vcl_session_t *next_session;
2157           next_session = vcl_session_get_w_handle (wrk,
2158                                                    vep_session->vep.next_sh);
2159           if (PREDICT_FALSE (!next_session))
2160             {
2161               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_ADD: Invalid "
2162                             "vep.next_sid (%u) on vep_idx (%u)!",
2163                             getpid (), vep_session->vep.next_sh, vep_handle);
2164               return VPPCOM_EBADFD;
2165             }
2166           ASSERT (next_session->vep.prev_sh == vep_handle);
2167           next_session->vep.prev_sh = session_handle;
2168         }
2169       session->vep.next_sh = vep_session->vep.next_sh;
2170       session->vep.prev_sh = vep_handle;
2171       session->vep.vep_sh = vep_handle;
2172       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2173       session->vep.ev = *event;
2174       session->is_vep = 0;
2175       session->is_vep_session = 1;
2176       vep_session->vep.next_sh = session_handle;
2177
2178       VDBG (1, "VCL<%d>: EPOLL_CTL_ADD: vep_idx %u, sid %u, events 0x%x, "
2179             "data 0x%llx!", getpid (), vep_handle, session_handle,
2180             event->events, event->data.u64);
2181       vcl_evt (VCL_EVT_EPOLL_CTLADD, session, event->events, event->data.u64);
2182       break;
2183
2184     case EPOLL_CTL_MOD:
2185       if (PREDICT_FALSE (!event))
2186         {
2187           clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_MOD: NULL pointer to "
2188                         "epoll_event structure!", getpid ());
2189           rv = VPPCOM_EINVAL;
2190           goto done;
2191         }
2192       else if (PREDICT_FALSE (!session->is_vep_session))
2193         {
2194           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2195                         "not a vep session!", getpid (), session_handle);
2196           rv = VPPCOM_EINVAL;
2197           goto done;
2198         }
2199       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2200         {
2201           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_MOD: "
2202                         "vep_idx (%u) != vep_idx (%u)!",
2203                         getpid (), session_handle,
2204                         session->vep.vep_sh, vep_handle);
2205           rv = VPPCOM_EINVAL;
2206           goto done;
2207         }
2208       session->vep.et_mask = VEP_DEFAULT_ET_MASK;
2209       session->vep.ev = *event;
2210       VDBG (1, "VCL<%d>: EPOLL_CTL_MOD: vep_idx %u, sid %u, events 0x%x,"
2211             " data 0x%llx!", getpid (), vep_handle, session_handle,
2212             event->events, event->data.u64);
2213       break;
2214
2215     case EPOLL_CTL_DEL:
2216       if (PREDICT_FALSE (!session->is_vep_session))
2217         {
2218           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2219                         "not a vep session!", getpid (), session_handle);
2220           rv = VPPCOM_EINVAL;
2221           goto done;
2222         }
2223       else if (PREDICT_FALSE (session->vep.vep_sh != vep_handle))
2224         {
2225           clib_warning ("VCL<%d>: ERROR: sid %u EPOLL_CTL_DEL: "
2226                         "vep_idx (%u) != vep_idx (%u)!",
2227                         getpid (), session_handle,
2228                         session->vep.vep_sh, vep_handle);
2229           rv = VPPCOM_EINVAL;
2230           goto done;
2231         }
2232
2233       vep_session->wait_cont_idx =
2234         (vep_session->wait_cont_idx == session_handle) ?
2235         session->vep.next_sh : vep_session->wait_cont_idx;
2236
2237       if (session->vep.prev_sh == vep_handle)
2238         vep_session->vep.next_sh = session->vep.next_sh;
2239       else
2240         {
2241           vcl_session_t *prev_session;
2242           prev_session = vcl_session_get_w_handle (wrk, session->vep.prev_sh);
2243           if (PREDICT_FALSE (!prev_session))
2244             {
2245               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2246                             "vep.prev_sid (%u) on sid (%u)!",
2247                             getpid (), session->vep.prev_sh, session_handle);
2248               return VPPCOM_EBADFD;
2249             }
2250           ASSERT (prev_session->vep.next_sh == session_handle);
2251           prev_session->vep.next_sh = session->vep.next_sh;
2252         }
2253       if (session->vep.next_sh != ~0)
2254         {
2255           vcl_session_t *next_session;
2256           next_session = vcl_session_get_w_handle (wrk, session->vep.next_sh);
2257           if (PREDICT_FALSE (!next_session))
2258             {
2259               clib_warning ("VCL<%d>: ERROR: EPOLL_CTL_DEL: Invalid "
2260                             "vep.next_sid (%u) on sid (%u)!",
2261                             getpid (), session->vep.next_sh, session_handle);
2262               return VPPCOM_EBADFD;
2263             }
2264           ASSERT (next_session->vep.prev_sh == session_handle);
2265           next_session->vep.prev_sh = session->vep.prev_sh;
2266         }
2267
2268       memset (&session->vep, 0, sizeof (session->vep));
2269       session->vep.next_sh = ~0;
2270       session->vep.prev_sh = ~0;
2271       session->vep.vep_sh = ~0;
2272       session->is_vep_session = 0;
2273       VDBG (1, "VCL<%d>: EPOLL_CTL_DEL: vep_idx %u, sid %u!",
2274             getpid (), vep_handle, session_handle);
2275       vcl_evt (VCL_EVT_EPOLL_CTLDEL, session, vep_sh);
2276       break;
2277
2278     default:
2279       clib_warning ("VCL<%d>: ERROR: Invalid operation (%d)!", getpid (), op);
2280       rv = VPPCOM_EINVAL;
2281     }
2282
2283   vep_verify_epoll_chain (wrk, vep_handle);
2284
2285 done:
2286   return rv;
2287 }
2288
2289 static inline void
2290 vcl_epoll_wait_handle_mq_event (vcl_worker_t * wrk, session_event_t * e,
2291                                 struct epoll_event *events, u32 * num_ev)
2292 {
2293   session_disconnected_msg_t *disconnected_msg;
2294   session_connected_msg_t *connected_msg;
2295   session_accepted_msg_t *accepted_msg;
2296   u64 session_evt_data = ~0, handle;
2297   u32 sid = ~0, session_events;
2298   vcl_session_msg_t *vcl_msg;
2299   vcl_session_t *session;
2300   u8 add_event = 0;
2301
2302   switch (e->event_type)
2303     {
2304     case FIFO_EVENT_APP_RX:
2305       ASSERT (e->fifo->client_thread_index == vcl_get_worker_index ());
2306       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2307       sid = e->fifo->client_session_index;
2308       session = vcl_session_get (wrk, sid);
2309       session_events = session->vep.ev.events;
2310       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2311         break;
2312       add_event = 1;
2313       events[*num_ev].events |= EPOLLIN;
2314       session_evt_data = session->vep.ev.data.u64;
2315       session->has_rx_evt = 1;
2316       break;
2317     case FIFO_EVENT_APP_TX:
2318       sid = e->fifo->client_session_index;
2319       session = vcl_session_get (wrk, sid);
2320       session_events = session->vep.ev.events;
2321       if (!(EPOLLOUT & session_events))
2322         break;
2323       add_event = 1;
2324       events[*num_ev].events |= EPOLLOUT;
2325       session_evt_data = session->vep.ev.data.u64;
2326       break;
2327     case SESSION_IO_EVT_CT_TX:
2328       vcl_fifo_rx_evt_valid_or_break (e->fifo);
2329       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 0);
2330       sid = session->session_index;
2331       session_events = session->vep.ev.events;
2332       if (!(EPOLLIN & session->vep.ev.events) || session->has_rx_evt)
2333         break;
2334       add_event = 1;
2335       events[*num_ev].events |= EPOLLIN;
2336       session_evt_data = session->vep.ev.data.u64;
2337       session->has_rx_evt = 1;
2338       break;
2339     case SESSION_IO_EVT_CT_RX:
2340       session = vcl_ct_session_get_from_fifo (wrk, e->fifo, 1);
2341       sid = session->session_index;
2342       session_events = session->vep.ev.events;
2343       if (!(EPOLLOUT & session_events))
2344         break;
2345       add_event = 1;
2346       events[*num_ev].events |= EPOLLOUT;
2347       session_evt_data = session->vep.ev.data.u64;
2348       break;
2349     case SESSION_CTRL_EVT_ACCEPTED:
2350       accepted_msg = (session_accepted_msg_t *) e->data;
2351       handle = accepted_msg->listener_handle;
2352       session = vcl_session_table_lookup_listener (wrk, handle);
2353       if (!session)
2354         {
2355           clib_warning ("VCL<%d>: ERROR: couldn't find listen session:"
2356                         "listener handle %llx", getpid (), handle);
2357           break;
2358         }
2359
2360       clib_fifo_add2 (session->accept_evts_fifo, vcl_msg);
2361       vcl_msg->accepted_msg = *accepted_msg;
2362       session_events = session->vep.ev.events;
2363       if (!(EPOLLIN & session_events))
2364         break;
2365
2366       add_event = 1;
2367       events[*num_ev].events |= EPOLLIN;
2368       session_evt_data = session->vep.ev.data.u64;
2369       break;
2370     case SESSION_CTRL_EVT_CONNECTED:
2371       connected_msg = (session_connected_msg_t *) e->data;
2372       vcl_session_connected_handler (wrk, connected_msg);
2373       /* Generate EPOLLOUT because there's no connected event */
2374       sid = vcl_session_index_from_vpp_handle (wrk, connected_msg->handle);
2375       session = vcl_session_get (wrk, sid);
2376       session_events = session->vep.ev.events;
2377       if (EPOLLOUT & session_events)
2378         {
2379           add_event = 1;
2380           events[*num_ev].events |= EPOLLOUT;
2381           session_evt_data = session->vep.ev.data.u64;
2382         }
2383       break;
2384     case SESSION_CTRL_EVT_DISCONNECTED:
2385       disconnected_msg = (session_disconnected_msg_t *) e->data;
2386       sid = vcl_session_index_from_vpp_handle (wrk, disconnected_msg->handle);
2387       if (!(session = vcl_session_get (wrk, sid)))
2388         break;
2389       add_event = 1;
2390       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2391       session_evt_data = session->vep.ev.data.u64;
2392       session_events = session->vep.ev.events;
2393       break;
2394     case SESSION_CTRL_EVT_RESET:
2395       sid = vcl_session_reset_handler (wrk, (session_reset_msg_t *) e->data);
2396       if (!(session = vcl_session_get (wrk, sid)))
2397         break;
2398       add_event = 1;
2399       events[*num_ev].events |= EPOLLHUP | EPOLLRDHUP;
2400       session_evt_data = session->vep.ev.data.u64;
2401       session_events = session->vep.ev.events;
2402       break;
2403     default:
2404       VDBG (0, "unhandled: %u", e->event_type);
2405       break;
2406     }
2407
2408   if (add_event)
2409     {
2410       events[*num_ev].data.u64 = session_evt_data;
2411       if (EPOLLONESHOT & session_events)
2412         {
2413           session = vcl_session_get (wrk, sid);
2414           session->vep.ev.events = 0;
2415         }
2416       *num_ev += 1;
2417     }
2418 }
2419
2420 static int
2421 vcl_epoll_wait_handle_mq (vcl_worker_t * wrk, svm_msg_q_t * mq,
2422                           struct epoll_event *events, u32 maxevents,
2423                           double wait_for_time, u32 * num_ev)
2424 {
2425   svm_msg_q_msg_t *msg;
2426   session_event_t *e;
2427   int i;
2428
2429   if (vec_len (wrk->mq_msg_vector) && svm_msg_q_is_empty (mq))
2430     goto handle_dequeued;
2431
2432   svm_msg_q_lock (mq);
2433   if (svm_msg_q_is_empty (mq))
2434     {
2435       if (!wait_for_time)
2436         {
2437           svm_msg_q_unlock (mq);
2438           return 0;
2439         }
2440       else if (wait_for_time < 0)
2441         {
2442           svm_msg_q_wait (mq);
2443         }
2444       else
2445         {
2446           if (svm_msg_q_timedwait (mq, wait_for_time / 1e3))
2447             {
2448               svm_msg_q_unlock (mq);
2449               return 0;
2450             }
2451         }
2452     }
2453   vcl_mq_dequeue_batch (wrk, mq);
2454   svm_msg_q_unlock (mq);
2455
2456 handle_dequeued:
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       if (*num_ev < maxevents)
2462         vcl_epoll_wait_handle_mq_event (wrk, e, events, num_ev);
2463       else
2464         vec_add1 (wrk->unhandled_evts_vector, *e);
2465       svm_msg_q_free_msg (mq, msg);
2466     }
2467   vec_reset_length (wrk->mq_msg_vector);
2468
2469   return *num_ev;
2470 }
2471
2472 static int
2473 vppcom_epoll_wait_condvar (vcl_worker_t * wrk, struct epoll_event *events,
2474                            int maxevents, u32 n_evts, double wait_for_time)
2475 {
2476   vcl_cut_through_registration_t *cr;
2477   double total_wait = 0, wait_slice;
2478   int rv;
2479
2480   wait_for_time = (wait_for_time == -1) ? (double) 10e9 : wait_for_time;
2481   wait_slice = wrk->cut_through_registrations ? 10e-6 : wait_for_time;
2482
2483   do
2484     {
2485       vcl_ct_registration_lock (wrk);
2486       /* *INDENT-OFF* */
2487       pool_foreach (cr, wrk->cut_through_registrations, ({
2488         vcl_epoll_wait_handle_mq (wrk, cr->mq, events, maxevents, 0, &n_evts);
2489       }));
2490       /* *INDENT-ON* */
2491       vcl_ct_registration_unlock (wrk);
2492
2493       rv = vcl_epoll_wait_handle_mq (wrk, wrk->app_event_queue, events,
2494                                      maxevents, n_evts ? 0 : wait_slice,
2495                                      &n_evts);
2496       if (rv)
2497         total_wait += wait_slice;
2498       if (n_evts)
2499         return n_evts;
2500     }
2501   while (total_wait < wait_for_time);
2502   return n_evts;
2503 }
2504
2505 static int
2506 vppcom_epoll_wait_eventfd (vcl_worker_t * wrk, struct epoll_event *events,
2507                            int maxevents, u32 n_evts, double wait_for_time)
2508 {
2509   vcl_mq_evt_conn_t *mqc;
2510   int __clib_unused n_read;
2511   int n_mq_evts, i;
2512   u64 buf;
2513
2514   vec_validate (wrk->mq_events, pool_elts (wrk->mq_evt_conns));
2515 again:
2516   n_mq_evts = epoll_wait (wrk->mqs_epfd, wrk->mq_events,
2517                           vec_len (wrk->mq_events), wait_for_time);
2518   for (i = 0; i < n_mq_evts; i++)
2519     {
2520       mqc = vcl_mq_evt_conn_get (wrk, wrk->mq_events[i].data.u32);
2521       n_read = read (mqc->mq_fd, &buf, sizeof (buf));
2522       vcl_epoll_wait_handle_mq (wrk, mqc->mq, events, maxevents, 0, &n_evts);
2523     }
2524   if (!n_evts && n_mq_evts > 0)
2525     goto again;
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_fast (ep->ip, &session->transport.rmt_ip.ip4,
2651                               sizeof (ip4_address_t));
2652           else
2653             clib_memcpy_fast (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_fast (ep->ip, &session->transport.lcl_ip.ip4,
2675                               sizeof (ip4_address_t));
2676           else
2677             clib_memcpy_fast (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_fast (ep->ip, &session->transport.rmt_ip.ip4,
3151                           sizeof (ip4_address_t));
3152       else
3153         clib_memcpy_fast (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   svm_msg_q_msg_t msg;
3190   session_event_t *e;
3191   int rv, num_ev = 0;
3192
3193   VDBG (3, "VCL<%d>: vp %p, nsids %u, wait_for_time %f",
3194         getpid (), vp, n_sids, wait_for_time);
3195
3196   if (!vp)
3197     return VPPCOM_EFAULT;
3198
3199   do
3200     {
3201       vcl_session_t *session;
3202
3203       /* Dequeue all events and drop all unhandled io events */
3204       while (svm_msg_q_sub (wrk->app_event_queue, &msg, SVM_Q_NOWAIT, 0) == 0)
3205         {
3206           e = svm_msg_q_msg_data (wrk->app_event_queue, &msg);
3207           vcl_handle_mq_event (wrk, e);
3208           svm_msg_q_free_msg (wrk->app_event_queue, &msg);
3209         }
3210       vec_reset_length (wrk->unhandled_evts_vector);
3211
3212       for (i = 0; i < n_sids; i++)
3213         {
3214           session = vcl_session_get (wrk, vp[i].sid);
3215           if (!session)
3216             {
3217               vp[i].revents = POLLHUP;
3218               num_ev++;
3219               continue;
3220             }
3221
3222           vp[i].revents = 0;
3223
3224           if (POLLIN & vp[i].events)
3225             {
3226               rv = vppcom_session_read_ready (session);
3227               if (rv > 0)
3228                 {
3229                   vp[i].revents |= POLLIN;
3230                   num_ev++;
3231                 }
3232               else if (rv < 0)
3233                 {
3234                   switch (rv)
3235                     {
3236                     case VPPCOM_ECONNRESET:
3237                       vp[i].revents = POLLHUP;
3238                       break;
3239
3240                     default:
3241                       vp[i].revents = POLLERR;
3242                       break;
3243                     }
3244                   num_ev++;
3245                 }
3246             }
3247
3248           if (POLLOUT & vp[i].events)
3249             {
3250               rv = vppcom_session_write_ready (session);
3251               if (rv > 0)
3252                 {
3253                   vp[i].revents |= POLLOUT;
3254                   num_ev++;
3255                 }
3256               else if (rv < 0)
3257                 {
3258                   switch (rv)
3259                     {
3260                     case VPPCOM_ECONNRESET:
3261                       vp[i].revents = POLLHUP;
3262                       break;
3263
3264                     default:
3265                       vp[i].revents = POLLERR;
3266                       break;
3267                     }
3268                   num_ev++;
3269                 }
3270             }
3271
3272           if (0)                // Note "done:" label used by VCL_SESSION_LOCK_AND_GET()
3273             {
3274               vp[i].revents = POLLNVAL;
3275               num_ev++;
3276             }
3277         }
3278       if (wait_for_time != -1)
3279         keep_trying = (clib_time_now (&wrk->clib_time) <= timeout) ? 1 : 0;
3280     }
3281   while ((num_ev == 0) && keep_trying);
3282
3283   if (VPPCOM_DEBUG > 3)
3284     {
3285       clib_warning ("VCL<%d>: returning %d", getpid (), num_ev);
3286       for (i = 0; i < n_sids; i++)
3287         {
3288           clib_warning ("VCL<%d>: vp[%d].sid %d (0x%x), .events 0x%x, "
3289                         ".revents 0x%x", getpid (), i, vp[i].sid, vp[i].sid,
3290                         vp[i].events, vp[i].revents);
3291         }
3292     }
3293   return num_ev;
3294 }
3295
3296 int
3297 vppcom_mq_epoll_fd (void)
3298 {
3299   vcl_worker_t *wrk = vcl_worker_get_current ();
3300   return wrk->mqs_epfd;
3301 }
3302
3303 int
3304 vppcom_session_index (uint32_t session_handle)
3305 {
3306   return session_handle & 0xFFFFFF;
3307 }
3308
3309 int
3310 vppcom_worker_register (void)
3311 {
3312   if (vcl_worker_alloc_and_init ())
3313     return VPPCOM_OK;
3314   return VPPCOM_EEXIST;
3315 }
3316
3317 /*
3318  * fd.io coding-style-patch-verification: ON
3319  *
3320  * Local Variables:
3321  * eval: (c-set-style "gnu")
3322  * End:
3323  */