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