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