apps: refactor uri and update build infra
[vpp.git] / src / tests / vnet / session / udp_echo.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
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 <setjmp.h>
18 #include <signal.h>
19 #include <vppinfra/clib.h>
20 #include <vppinfra/format.h>
21 #include <vppinfra/error.h>
22 #include <vppinfra/time.h>
23 #include <vppinfra/macros.h>
24 #include <vnet/vnet.h>
25 #include <vlib/vlib.h>
26 #include <vlib/unix/unix.h>
27 #include <vlibapi/api.h>
28 #include <vlibmemory/api.h>
29 #include <vpp/api/vpe_msg_enum.h>
30 #include <svm/svm_fifo_segment.h>
31 #include <pthread.h>
32 #include <vnet/session/application_interface.h>
33
34 #define vl_typedefs             /* define message structures */
35 #include <vpp/api/vpe_all_api_h.h>
36 #undef vl_typedefs
37
38 /* declare message handlers for each api */
39
40 #define vl_endianfun            /* define message structures */
41 #include <vpp/api/vpe_all_api_h.h>
42 #undef vl_endianfun
43
44 /* instantiate all the print functions we know about */
45 #define vl_print(handle, ...)
46 #define vl_printfun
47 #include <vpp/api/vpe_all_api_h.h>
48 #undef vl_printfun
49
50 typedef enum
51 {
52   STATE_START,
53   STATE_BOUND,
54   STATE_READY,
55   STATE_FAILED,
56   STATE_DISCONNECTING,
57 } connection_state_t;
58
59 typedef struct
60 {
61   svm_fifo_t *server_rx_fifo;
62   svm_fifo_t *server_tx_fifo;
63 } session_t;
64
65 typedef struct
66 {
67   /* vpe input queue */
68   unix_shared_memory_queue_t *vl_input_queue;
69
70   /* API client handle */
71   u32 my_client_index;
72
73   /* The URI we're playing with */
74   u8 *uri;
75
76   /* Session pool */
77   session_t *sessions;
78
79   /* Hash table for disconnect processing */
80   uword *session_index_by_vpp_handles;
81
82   /* fifo segment */
83   svm_fifo_segment_private_t *seg;
84
85   /* intermediate rx buffer */
86   u8 *rx_buf;
87
88   /* URI for connect */
89   u8 *connect_uri;
90
91   int i_am_master;
92
93   /* Our event queue */
94   unix_shared_memory_queue_t *our_event_queue;
95
96   /* $$$ single thread only for the moment */
97   unix_shared_memory_queue_t *vpp_event_queue;
98
99   /* $$$$ hack: cut-through session index */
100   volatile u32 cut_through_session_index;
101   volatile u32 connected_session;
102
103   /* unique segment name counter */
104   u32 unique_segment_index;
105
106   pid_t my_pid;
107
108   /* pthread handle */
109   pthread_t cut_through_thread_handle;
110
111   /* For deadman timers */
112   clib_time_t clib_time;
113
114   /* State of the connection, shared between msg RX thread and main thread */
115   volatile connection_state_t state;
116
117   volatile int time_to_stop;
118   volatile int time_to_print_stats;
119
120   u32 configured_segment_size;
121
122   /* VNET_API_ERROR_FOO -> "Foo" hash table */
123   uword *error_string_by_error_number;
124
125   /* convenience */
126   svm_fifo_segment_main_t *segment_main;
127
128   u8 *connect_test_data;
129 } uri_udp_test_main_t;
130
131 #if CLIB_DEBUG > 0
132 #define NITER 10000
133 #else
134 #define NITER 4000000
135 #endif
136
137 uri_udp_test_main_t uri_udp_test_main;
138
139 static void
140 stop_signal (int signum)
141 {
142   uri_udp_test_main_t *um = &uri_udp_test_main;
143
144   um->time_to_stop = 1;
145 }
146
147 static void
148 stats_signal (int signum)
149 {
150   uri_udp_test_main_t *um = &uri_udp_test_main;
151
152   um->time_to_print_stats = 1;
153 }
154
155 static clib_error_t *
156 setup_signal_handlers (void)
157 {
158   signal (SIGINT, stats_signal);
159   signal (SIGQUIT, stop_signal);
160   signal (SIGTERM, stop_signal);
161
162   return 0;
163 }
164
165 void
166 application_send_attach (uri_udp_test_main_t * utm)
167 {
168   vl_api_application_attach_t *bmp;
169   u32 fifo_size = 1 << 20;
170   bmp = vl_msg_api_alloc (sizeof (*bmp));
171   memset (bmp, 0, sizeof (*bmp));
172
173   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_ATTACH);
174   bmp->client_index = utm->my_client_index;
175   bmp->context = ntohl (0xfeedface);
176   bmp->options[APP_OPTIONS_FLAGS] =
177     APP_OPTIONS_FLAGS_ACCEPT_REDIRECT | APP_OPTIONS_FLAGS_ADD_SEGMENT;
178   bmp->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = 2;
179   bmp->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
180   bmp->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
181   bmp->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = 128 << 20;
182   bmp->options[APP_OPTIONS_SEGMENT_SIZE] = 256 << 20;
183   bmp->options[APP_OPTIONS_EVT_QUEUE_SIZE] = 16768;
184   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & bmp);
185 }
186
187 void
188 application_detach (uri_udp_test_main_t * utm)
189 {
190   vl_api_application_detach_t *bmp;
191   bmp = vl_msg_api_alloc (sizeof (*bmp));
192   memset (bmp, 0, sizeof (*bmp));
193
194   bmp->_vl_msg_id = ntohs (VL_API_APPLICATION_DETACH);
195   bmp->client_index = utm->my_client_index;
196   bmp->context = ntohl (0xfeedface);
197   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & bmp);
198 }
199
200 static void
201 vl_api_application_attach_reply_t_handler (vl_api_application_attach_reply_t *
202                                            mp)
203 {
204   uri_udp_test_main_t *utm = &uri_udp_test_main;
205   svm_fifo_segment_create_args_t _a, *a = &_a;
206   int rv;
207
208   if (mp->retval)
209     {
210       clib_warning ("attach failed: %d", mp->retval);
211       utm->state = STATE_FAILED;
212       return;
213     }
214
215   if (mp->segment_name_length == 0)
216     {
217       clib_warning ("segment_name_length zero");
218       return;
219     }
220
221   a->segment_name = (char *) mp->segment_name;
222   a->segment_size = mp->segment_size;
223
224   ASSERT (mp->app_event_queue_address);
225
226   /* Attach to the segment vpp created */
227   rv = svm_fifo_segment_attach (a);
228   if (rv)
229     {
230       clib_warning ("svm_fifo_segment_attach ('%s') failed",
231                     mp->segment_name);
232       return;
233     }
234
235   utm->our_event_queue =
236     uword_to_pointer (mp->app_event_queue_address,
237                       unix_shared_memory_queue_t *);
238 }
239
240 static void
241 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
242                                            mp)
243 {
244   if (mp->retval)
245     clib_warning ("detach returned with err: %d", mp->retval);
246 }
247
248 u8 *
249 format_api_error (u8 * s, va_list * args)
250 {
251   uri_udp_test_main_t *utm = va_arg (*args, uri_udp_test_main_t *);
252   i32 error = va_arg (*args, u32);
253   uword *p;
254
255   p = hash_get (utm->error_string_by_error_number, -error);
256
257   if (p)
258     s = format (s, "%s", p[0]);
259   else
260     s = format (s, "%d", error);
261   return s;
262 }
263
264 int
265 wait_for_state_change (uri_udp_test_main_t * utm, connection_state_t state)
266 {
267 #if CLIB_DEBUG > 0
268 #define TIMEOUT 600.0
269 #else
270 #define TIMEOUT 600.0
271 #endif
272
273   f64 timeout = clib_time_now (&utm->clib_time) + TIMEOUT;
274
275   while (clib_time_now (&utm->clib_time) < timeout)
276     {
277       if (utm->state == state)
278         return 0;
279     }
280   return -1;
281 }
282
283 u64 server_bytes_received, server_bytes_sent;
284
285 static void *
286 cut_through_thread_fn (void *arg)
287 {
288   session_t *s;
289   svm_fifo_t *rx_fifo;
290   svm_fifo_t *tx_fifo;
291   u8 *my_copy_buffer = 0;
292   uri_udp_test_main_t *utm = &uri_udp_test_main;
293   i32 actual_transfer;
294   int rv;
295   u32 buffer_offset;
296
297   while (utm->cut_through_session_index == ~0)
298     ;
299
300   s = pool_elt_at_index (utm->sessions, utm->cut_through_session_index);
301
302   rx_fifo = s->server_rx_fifo;
303   tx_fifo = s->server_tx_fifo;
304
305   vec_validate (my_copy_buffer, 64 * 1024 - 1);
306
307   while (true)
308     {
309       /* We read from the tx fifo and write to the rx fifo */
310       do
311         {
312           actual_transfer = svm_fifo_dequeue_nowait (tx_fifo,
313                                                      vec_len (my_copy_buffer),
314                                                      my_copy_buffer);
315         }
316       while (actual_transfer <= 0);
317
318       server_bytes_received += actual_transfer;
319
320       buffer_offset = 0;
321       while (actual_transfer > 0)
322         {
323           rv = svm_fifo_enqueue_nowait (rx_fifo, actual_transfer,
324                                         my_copy_buffer + buffer_offset);
325           if (rv > 0)
326             {
327               actual_transfer -= rv;
328               buffer_offset += rv;
329               server_bytes_sent += rv;
330             }
331
332         }
333       if (PREDICT_FALSE (utm->time_to_stop))
334         break;
335     }
336
337   pthread_exit (0);
338 }
339
340 static void
341 udp_client_connect (uri_udp_test_main_t * utm)
342 {
343   vl_api_connect_uri_t *cmp;
344   cmp = vl_msg_api_alloc (sizeof (*cmp));
345   memset (cmp, 0, sizeof (*cmp));
346
347   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_URI);
348   cmp->client_index = utm->my_client_index;
349   cmp->context = ntohl (0xfeedface);
350   memcpy (cmp->uri, utm->connect_uri, vec_len (utm->connect_uri));
351   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & cmp);
352 }
353
354 static void
355 client_send_cut_through (uri_udp_test_main_t * utm, session_t * session)
356 {
357   int i;
358   u8 *test_data = 0;
359   u64 bytes_received = 0, bytes_sent = 0;
360   i32 bytes_to_read;
361   int rv;
362   f64 before, after, delta, bytes_per_second;
363   svm_fifo_t *rx_fifo, *tx_fifo;
364   int buffer_offset, bytes_to_send = 0;
365
366   /*
367    * Prepare test data
368    */
369   vec_validate (test_data, 64 * 1024 - 1);
370   for (i = 0; i < vec_len (test_data); i++)
371     test_data[i] = i & 0xff;
372
373   rx_fifo = session->server_rx_fifo;
374   tx_fifo = session->server_tx_fifo;
375
376   before = clib_time_now (&utm->clib_time);
377
378   vec_validate (utm->rx_buf, vec_len (test_data) - 1);
379
380   for (i = 0; i < NITER; i++)
381     {
382       bytes_to_send = vec_len (test_data);
383       buffer_offset = 0;
384       while (bytes_to_send > 0)
385         {
386           rv = svm_fifo_enqueue_nowait (tx_fifo, bytes_to_send,
387                                         test_data + buffer_offset);
388
389           if (rv > 0)
390             {
391               bytes_to_send -= rv;
392               buffer_offset += rv;
393               bytes_sent += rv;
394             }
395         }
396
397       bytes_to_read = svm_fifo_max_dequeue (rx_fifo);
398       bytes_to_read = vec_len (utm->rx_buf) > bytes_to_read ?
399         bytes_to_read : vec_len (utm->rx_buf);
400
401       buffer_offset = 0;
402       while (bytes_to_read > 0)
403         {
404           rv = svm_fifo_dequeue_nowait (rx_fifo,
405                                         bytes_to_read,
406                                         utm->rx_buf + buffer_offset);
407           if (rv > 0)
408             {
409               bytes_to_read -= rv;
410               buffer_offset += rv;
411               bytes_received += rv;
412             }
413         }
414     }
415   while (bytes_received < bytes_sent)
416     {
417       rv =
418         svm_fifo_dequeue_nowait (rx_fifo, vec_len (utm->rx_buf), utm->rx_buf);
419       if (rv > 0)
420         {
421 #if CLIB_DEBUG > 0
422           int j;
423           for (j = 0; j < rv; j++)
424             {
425               if (utm->rx_buf[j] != ((bytes_received + j) & 0xff))
426                 {
427                   clib_warning ("error at byte %lld, 0x%x not 0x%x",
428                                 bytes_received + j,
429                                 utm->rx_buf[j],
430                                 ((bytes_received + j) & 0xff));
431                 }
432             }
433 #endif
434           bytes_received += (u64) rv;
435         }
436     }
437
438   after = clib_time_now (&utm->clib_time);
439   delta = after - before;
440   bytes_per_second = 0.0;
441
442   if (delta > 0.0)
443     bytes_per_second = (f64) bytes_received / delta;
444
445   fformat (stdout,
446            "Done: %lld recv bytes in %.2f seconds, %.2f bytes/sec...\n\n",
447            bytes_received, delta, bytes_per_second);
448   fformat (stdout,
449            "Done: %lld sent bytes in %.2f seconds, %.2f bytes/sec...\n\n",
450            bytes_sent, delta, bytes_per_second);
451   fformat (stdout,
452            "client -> server -> client round trip: %.2f Gbit/sec \n\n",
453            (bytes_per_second * 8.0) / 1e9);
454 }
455
456 static void
457 send_test_chunk (uri_udp_test_main_t * utm, svm_fifo_t * tx_fifo, int mypid,
458                  u32 bytes)
459 {
460   u8 *test_data = utm->connect_test_data;
461   u64 bytes_sent = 0;
462   int test_buf_offset = 0;
463   u32 bytes_to_snd;
464   u32 queue_max_chunk = 128 << 10, actual_write;
465   session_fifo_event_t evt;
466   int rv;
467
468   bytes_to_snd = (bytes == 0) ? vec_len (test_data) : bytes;
469   if (bytes_to_snd > vec_len (test_data))
470     bytes_to_snd = vec_len (test_data);
471
472   while (bytes_to_snd > 0 && !utm->time_to_stop)
473     {
474       actual_write = (bytes_to_snd > queue_max_chunk) ?
475         queue_max_chunk : bytes_to_snd;
476       rv = svm_fifo_enqueue_nowait (tx_fifo, actual_write,
477                                     test_data + test_buf_offset);
478
479       if (rv > 0)
480         {
481           bytes_to_snd -= rv;
482           test_buf_offset += rv;
483           bytes_sent += rv;
484
485           if (svm_fifo_set_event (tx_fifo))
486             {
487               /* Fabricate TX event, send to vpp */
488               evt.fifo = tx_fifo;
489               evt.event_type = FIFO_EVENT_APP_TX;
490
491               unix_shared_memory_queue_add (utm->vpp_event_queue,
492                                             (u8 *) & evt,
493                                             0 /* do wait for mutex */ );
494             }
495         }
496     }
497 }
498
499 static void
500 recv_test_chunk (uri_udp_test_main_t * utm, session_t * session)
501 {
502   svm_fifo_t *rx_fifo;
503   int buffer_offset, bytes_to_read = 0, rv;
504
505   rx_fifo = session->server_rx_fifo;
506   bytes_to_read = svm_fifo_max_dequeue (rx_fifo);
507   bytes_to_read =
508     vec_len (utm->rx_buf) > bytes_to_read ?
509     bytes_to_read : vec_len (utm->rx_buf);
510
511   buffer_offset = 0;
512   while (bytes_to_read > 0)
513     {
514       rv = svm_fifo_dequeue_nowait (rx_fifo, bytes_to_read,
515                                     utm->rx_buf + buffer_offset);
516       if (rv > 0)
517         {
518           bytes_to_read -= rv;
519           buffer_offset += rv;
520         }
521     }
522 }
523
524 void
525 client_send_data (uri_udp_test_main_t * utm)
526 {
527   u8 *test_data;
528   int mypid = getpid ();
529   session_t *session;
530   svm_fifo_t *tx_fifo;
531   u32 n_iterations;
532   int i;
533
534   vec_validate (utm->connect_test_data, 64 * 1024 - 1);
535   for (i = 0; i < vec_len (utm->connect_test_data); i++)
536     utm->connect_test_data[i] = i & 0xff;
537
538   test_data = utm->connect_test_data;
539   session = pool_elt_at_index (utm->sessions, utm->connected_session);
540   tx_fifo = session->server_tx_fifo;
541
542   ASSERT (vec_len (test_data) > 0);
543
544   vec_validate (utm->rx_buf, vec_len (test_data) - 1);
545   n_iterations = NITER;
546
547   for (i = 0; i < n_iterations; i++)
548     {
549       send_test_chunk (utm, tx_fifo, mypid, 0);
550       recv_test_chunk (utm, session);
551       if (utm->time_to_stop)
552         break;
553     }
554
555   f64 timeout = clib_time_now (&utm->clib_time) + 5;
556   while (clib_time_now (&utm->clib_time) < timeout)
557     {
558       recv_test_chunk (utm, session);
559     }
560
561 }
562
563 static void
564 client_test (uri_udp_test_main_t * utm)
565 {
566   session_t *session;
567
568   application_send_attach (utm);
569   udp_client_connect (utm);
570
571   if (wait_for_state_change (utm, STATE_READY))
572     {
573       clib_warning ("timeout waiting for STATE_READY");
574       return;
575     }
576
577   if (utm->cut_through_session_index != ~0)
578     {
579       session = pool_elt_at_index (utm->sessions,
580                                    utm->cut_through_session_index);
581       client_send_cut_through (utm, session);
582     }
583   else
584     {
585       session = pool_elt_at_index (utm->sessions, utm->connected_session);
586       client_send_data (utm);
587     }
588
589   application_detach (utm);
590 }
591
592 static void
593 vl_api_bind_uri_reply_t_handler (vl_api_bind_uri_reply_t * mp)
594 {
595   uri_udp_test_main_t *utm = &uri_udp_test_main;
596
597   if (mp->retval)
598     {
599       clib_warning ("bind failed: %d", mp->retval);
600       utm->state = STATE_FAILED;
601       return;
602     }
603
604   utm->state = STATE_BOUND;
605 }
606
607 static void
608 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
609 {
610   svm_fifo_segment_create_args_t _a, *a = &_a;
611   int rv;
612
613   memset (a, 0, sizeof (*a));
614   a->segment_name = (char *) mp->segment_name;
615   a->segment_size = mp->segment_size;
616   /* Attach to the segment vpp created */
617   rv = svm_fifo_segment_attach (a);
618   if (rv)
619     {
620       clib_warning ("svm_fifo_segment_attach ('%s') failed",
621                     mp->segment_name);
622       return;
623     }
624   clib_warning ("Mapped new segment '%s' size %d", mp->segment_name,
625                 mp->segment_size);
626 }
627
628 /**
629  * Acting as server for redirected connect requests
630  */
631 static void
632 vl_api_connect_uri_t_handler (vl_api_connect_uri_t * mp)
633 {
634   u32 segment_index;
635   uri_udp_test_main_t *utm = &uri_udp_test_main;
636   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
637   svm_fifo_segment_create_args_t _a, *a = &_a;
638   svm_fifo_segment_private_t *seg;
639   unix_shared_memory_queue_t *client_q;
640   vl_api_connect_session_reply_t *rmp;
641   session_t *session = 0;
642   int rv = 0;
643
644   /* Create the segment */
645   a->segment_name = (char *) format (0, "%d:segment%d%c", utm->my_pid,
646                                      utm->unique_segment_index++, 0);
647   a->segment_size = utm->configured_segment_size;
648
649   rv = svm_fifo_segment_create (a);
650   if (rv)
651     {
652       clib_warning ("sm_fifo_segment_create ('%s') failed", a->segment_name);
653       rv = VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
654       goto send_reply;
655     }
656
657   vec_add2 (utm->seg, seg, 1);
658
659   segment_index = vec_len (sm->segments) - 1;
660   memcpy (seg, sm->segments + segment_index, sizeof (utm->seg[0]));
661
662   pool_get (utm->sessions, session);
663
664   session->server_rx_fifo = svm_fifo_segment_alloc_fifo
665     (utm->seg, 128 * 1024, FIFO_SEGMENT_RX_FREELIST);
666   ASSERT (session->server_rx_fifo);
667
668   session->server_tx_fifo = svm_fifo_segment_alloc_fifo
669     (utm->seg, 128 * 1024, FIFO_SEGMENT_TX_FREELIST);
670   ASSERT (session->server_tx_fifo);
671
672   session->server_rx_fifo->master_session_index = session - utm->sessions;
673   session->server_tx_fifo->master_session_index = session - utm->sessions;
674   utm->cut_through_session_index = session - utm->sessions;
675
676   rv = pthread_create (&utm->cut_through_thread_handle,
677                        NULL /*attr */ , cut_through_thread_fn, 0);
678   if (rv)
679     {
680       clib_warning ("pthread_create returned %d", rv);
681       rv = VNET_API_ERROR_SYSCALL_ERROR_1;
682     }
683
684 send_reply:
685   rmp = vl_msg_api_alloc (sizeof (*rmp));
686   memset (rmp, 0, sizeof (*rmp));
687
688   rmp->_vl_msg_id = ntohs (VL_API_CONNECT_SESSION_REPLY);
689   rmp->context = mp->context;
690   rmp->retval = ntohl (rv);
691   rmp->segment_name_length = vec_len (a->segment_name);
692   if (session)
693     {
694       rmp->server_rx_fifo = pointer_to_uword (session->server_rx_fifo);
695       rmp->server_tx_fifo = pointer_to_uword (session->server_tx_fifo);
696     }
697
698   memcpy (rmp->segment_name, a->segment_name, vec_len (a->segment_name));
699
700   vec_free (a->segment_name);
701
702   client_q =
703     uword_to_pointer (mp->client_queue_address, unix_shared_memory_queue_t *);
704   vl_msg_api_send_shmem (client_q, (u8 *) & rmp);
705 }
706
707 static void
708 vl_api_unbind_uri_reply_t_handler (vl_api_unbind_uri_reply_t * mp)
709 {
710   uri_udp_test_main_t *utm = &uri_udp_test_main;
711
712   if (mp->retval != 0)
713     clib_warning ("returned %d", ntohl (mp->retval));
714
715   utm->state = STATE_START;
716 }
717
718 static void
719 vl_api_accept_session_t_handler (vl_api_accept_session_t * mp)
720 {
721   uri_udp_test_main_t *utm = &uri_udp_test_main;
722   vl_api_accept_session_reply_t *rmp;
723   svm_fifo_t *rx_fifo, *tx_fifo;
724   session_t *session;
725   static f64 start_time;
726
727   if (start_time == 0.0)
728     start_time = clib_time_now (&utm->clib_time);
729
730   utm->vpp_event_queue =
731     uword_to_pointer (mp->vpp_event_queue_address,
732                       unix_shared_memory_queue_t *);
733
734   pool_get (utm->sessions, session);
735
736   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
737   rx_fifo->client_session_index = session - utm->sessions;
738   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
739   tx_fifo->client_session_index = session - utm->sessions;
740
741   session->server_rx_fifo = rx_fifo;
742   session->server_tx_fifo = tx_fifo;
743
744   hash_set (utm->session_index_by_vpp_handles, mp->handle,
745             session - utm->sessions);
746
747   if (pool_elts (utm->sessions) && (pool_elts (utm->sessions) % 20000) == 0)
748     {
749       f64 now = clib_time_now (&utm->clib_time);
750       fformat (stdout, "%d active sessions in %.2f seconds, %.2f/sec...\n",
751                pool_elts (utm->sessions), now - start_time,
752                (f64) pool_elts (utm->sessions) / (now - start_time));
753     }
754
755   rmp = vl_msg_api_alloc (sizeof (*rmp));
756   memset (rmp, 0, sizeof (*rmp));
757   rmp->_vl_msg_id = ntohs (VL_API_ACCEPT_SESSION_REPLY);
758   rmp->handle = mp->handle;
759   rmp->context = mp->context;
760   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & rmp);
761
762   CLIB_MEMORY_BARRIER ();
763   utm->state = STATE_READY;
764 }
765
766 static void
767 vl_api_disconnect_session_t_handler (vl_api_disconnect_session_t * mp)
768 {
769   uri_udp_test_main_t *utm = &uri_udp_test_main;
770   session_t *session;
771   vl_api_disconnect_session_reply_t *rmp;
772   uword *p;
773   int rv = 0;
774
775   p = hash_get (utm->session_index_by_vpp_handles, mp->handle);
776
777   if (p)
778     {
779       session = pool_elt_at_index (utm->sessions, p[0]);
780       hash_unset (utm->session_index_by_vpp_handles, mp->handle);
781       pool_put (utm->sessions, session);
782     }
783   else
784     {
785       clib_warning ("couldn't find session key %llx", mp->handle);
786       rv = -11;
787     }
788
789   rmp = vl_msg_api_alloc (sizeof (*rmp));
790   memset (rmp, 0, sizeof (*rmp));
791   rmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION_REPLY);
792   rmp->retval = rv;
793   rmp->handle = mp->handle;
794   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & rmp);
795 }
796
797 static void
798 vl_api_connect_session_reply_t_handler (vl_api_connect_session_reply_t * mp)
799 {
800   uri_udp_test_main_t *utm = &uri_udp_test_main;
801   session_t *session;
802
803   ASSERT (utm->i_am_master == 0);
804
805   if (mp->retval)
806     {
807       clib_warning ("failed connect");
808       return;
809     }
810
811   /* We've been redirected */
812   if (mp->segment_name_length > 0)
813     {
814       svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
815       svm_fifo_segment_create_args_t _a, *a = &_a;
816       u32 segment_index;
817       svm_fifo_segment_private_t *seg;
818       int rv;
819
820       memset (a, 0, sizeof (*a));
821       a->segment_name = (char *) mp->segment_name;
822
823       sleep (1);
824
825       rv = svm_fifo_segment_attach (a);
826       if (rv)
827         {
828           clib_warning ("sm_fifo_segment_create ('%v') failed",
829                         mp->segment_name);
830           return;
831         }
832
833       segment_index = a->new_segment_indices[0];
834       vec_add2 (utm->seg, seg, 1);
835       memcpy (seg, sm->segments + segment_index, sizeof (*seg));
836       sleep (1);
837     }
838
839   pool_get (utm->sessions, session);
840   session->server_rx_fifo = uword_to_pointer (mp->server_rx_fifo,
841                                               svm_fifo_t *);
842   ASSERT (session->server_rx_fifo);
843   session->server_tx_fifo = uword_to_pointer (mp->server_tx_fifo,
844                                               svm_fifo_t *);
845   ASSERT (session->server_tx_fifo);
846
847   if (mp->segment_name_length > 0)
848     utm->cut_through_session_index = session - utm->sessions;
849   else
850     {
851       utm->connected_session = session - utm->sessions;
852       utm->vpp_event_queue = uword_to_pointer (mp->vpp_event_queue_address,
853                                                unix_shared_memory_queue_t *);
854     }
855   utm->state = STATE_READY;
856 }
857
858 #define foreach_uri_msg                                 \
859 _(BIND_URI_REPLY, bind_uri_reply)                       \
860 _(CONNECT_URI, connect_uri)                             \
861 _(CONNECT_SESSION_REPLY, connect_session_reply)         \
862 _(UNBIND_URI_REPLY, unbind_uri_reply)                   \
863 _(ACCEPT_SESSION, accept_session)                       \
864 _(DISCONNECT_SESSION, disconnect_session)               \
865 _(MAP_ANOTHER_SEGMENT, map_another_segment)             \
866 _(APPLICATION_ATTACH_REPLY, application_attach_reply)   \
867 _(APPLICATION_DETACH_REPLY, application_detach_reply)   \
868
869 void
870 uri_api_hookup (uri_udp_test_main_t * utm)
871 {
872 #define _(N,n)                                                  \
873     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
874                            vl_api_##n##_t_handler,              \
875                            vl_noop_handler,                     \
876                            vl_api_##n##_t_endian,               \
877                            vl_api_##n##_t_print,                \
878                            sizeof(vl_api_##n##_t), 1);
879   foreach_uri_msg;
880 #undef _
881
882 }
883
884 int
885 connect_to_vpp (char *name)
886 {
887   uri_udp_test_main_t *utm = &uri_udp_test_main;
888   api_main_t *am = &api_main;
889
890   if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
891     return -1;
892
893   utm->vl_input_queue = am->shmem_hdr->vl_input_queue;
894   utm->my_client_index = am->my_client_index;
895
896   return 0;
897 }
898
899 void
900 vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
901 {
902   clib_warning ("BUG");
903 }
904
905 static void
906 init_error_string_table (uri_udp_test_main_t * utm)
907 {
908   utm->error_string_by_error_number = hash_create (0, sizeof (uword));
909
910 #define _(n,v,s) hash_set (utm->error_string_by_error_number, -v, s);
911   foreach_vnet_api_error;
912 #undef _
913
914   hash_set (utm->error_string_by_error_number, 99, "Misc");
915 }
916
917 void
918 server_handle_fifo_event_rx (uri_udp_test_main_t * utm,
919                              session_fifo_event_t * e)
920 {
921   svm_fifo_t *rx_fifo, *tx_fifo;
922   int nbytes;
923   session_fifo_event_t evt;
924   unix_shared_memory_queue_t *q;
925   int rv;
926
927   rx_fifo = e->fifo;
928   tx_fifo = utm->sessions[rx_fifo->client_session_index].server_tx_fifo;
929   svm_fifo_unset_event (rx_fifo);
930
931   do
932     {
933       nbytes = svm_fifo_dequeue_nowait (rx_fifo, vec_len (utm->rx_buf),
934                                         utm->rx_buf);
935     }
936   while (nbytes <= 0);
937   do
938     {
939       rv = svm_fifo_enqueue_nowait (tx_fifo, nbytes, utm->rx_buf);
940     }
941   while (rv == -2);
942
943   if (svm_fifo_set_event (tx_fifo))
944     {
945       /* Fabricate TX event, send to vpp */
946       evt.fifo = tx_fifo;
947       evt.event_type = FIFO_EVENT_APP_TX;
948       q = utm->vpp_event_queue;
949       unix_shared_memory_queue_add (q, (u8 *) & evt,
950                                     0 /* do wait for mutex */ );
951     }
952 }
953
954 void
955 server_handle_event_queue (uri_udp_test_main_t * utm)
956 {
957   session_fifo_event_t _e, *e = &_e;
958
959   while (utm->state != STATE_READY)
960     sleep (5);
961
962   while (1)
963     {
964       unix_shared_memory_queue_sub (utm->our_event_queue, (u8 *) e,
965                                     0 /* nowait */ );
966       switch (e->event_type)
967         {
968         case FIFO_EVENT_APP_RX:
969           server_handle_fifo_event_rx (utm, e);
970           break;
971
972         case FIFO_EVENT_DISCONNECT:
973           return;
974
975         default:
976           clib_warning ("unknown event type %d", e->event_type);
977           break;
978         }
979       if (PREDICT_FALSE (utm->time_to_stop == 1))
980         return;
981       if (PREDICT_FALSE (utm->time_to_print_stats == 1))
982         {
983           utm->time_to_print_stats = 0;
984           fformat (stdout, "%d connections\n", pool_elts (utm->sessions));
985         }
986     }
987 }
988
989 static void
990 server_unbind (uri_udp_test_main_t * utm)
991 {
992   vl_api_unbind_uri_t *ump;
993
994   ump = vl_msg_api_alloc (sizeof (*ump));
995   memset (ump, 0, sizeof (*ump));
996
997   ump->_vl_msg_id = ntohs (VL_API_UNBIND_URI);
998   ump->client_index = utm->my_client_index;
999   memcpy (ump->uri, utm->uri, vec_len (utm->uri));
1000   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & ump);
1001 }
1002
1003 static void
1004 server_bind (uri_udp_test_main_t * utm)
1005 {
1006   vl_api_bind_uri_t *bmp;
1007
1008   bmp = vl_msg_api_alloc (sizeof (*bmp));
1009   memset (bmp, 0, sizeof (*bmp));
1010
1011   bmp->_vl_msg_id = ntohs (VL_API_BIND_URI);
1012   bmp->client_index = utm->my_client_index;
1013   bmp->context = ntohl (0xfeedface);
1014   memcpy (bmp->uri, utm->uri, vec_len (utm->uri));
1015   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & bmp);
1016 }
1017
1018 void
1019 udp_server_test (uri_udp_test_main_t * utm)
1020 {
1021
1022   application_send_attach (utm);
1023
1024   /* Bind to uri */
1025   server_bind (utm);
1026
1027   if (wait_for_state_change (utm, STATE_BOUND))
1028     {
1029       clib_warning ("timeout waiting for STATE_BOUND");
1030       return;
1031     }
1032
1033   server_handle_event_queue (utm);
1034
1035   /* Cleanup */
1036   server_unbind (utm);
1037
1038   if (wait_for_state_change (utm, STATE_START))
1039     {
1040       clib_warning ("timeout waiting for STATE_START");
1041       return;
1042     }
1043
1044   application_detach (utm);
1045
1046   fformat (stdout, "Test complete...\n");
1047 }
1048
1049 int
1050 main (int argc, char **argv)
1051 {
1052   uri_udp_test_main_t *utm = &uri_udp_test_main;
1053   unformat_input_t _argv, *a = &_argv;
1054   u8 *chroot_prefix;
1055   u8 *heap;
1056   u8 *bind_name = (u8 *) "udp://0.0.0.0/1234";
1057   u32 tmp;
1058   mheap_t *h;
1059   session_t *session;
1060   int i;
1061   int i_am_master = 1;
1062
1063   clib_mem_init (0, 256 << 20);
1064
1065   heap = clib_mem_get_per_cpu_heap ();
1066   h = mheap_header (heap);
1067
1068   /* make the main heap thread-safe */
1069   h->flags |= MHEAP_FLAG_THREAD_SAFE;
1070
1071   vec_validate (utm->rx_buf, 8192);
1072
1073   utm->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
1074
1075   utm->my_pid = getpid ();
1076   utm->configured_segment_size = 1 << 20;
1077
1078   clib_time_init (&utm->clib_time);
1079   init_error_string_table (utm);
1080   svm_fifo_segment_init (0x200000000ULL, 20);
1081   unformat_init_command_line (a, argv);
1082
1083   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
1084     {
1085       if (unformat (a, "chroot prefix %s", &chroot_prefix))
1086         {
1087           vl_set_memory_root_path ((char *) chroot_prefix);
1088         }
1089       else if (unformat (a, "uri %s", &bind_name))
1090         ;
1091       else if (unformat (a, "segment-size %dM", &tmp))
1092         utm->configured_segment_size = tmp << 20;
1093       else if (unformat (a, "segment-size %dG", &tmp))
1094         utm->configured_segment_size = tmp << 30;
1095       else if (unformat (a, "master"))
1096         i_am_master = 1;
1097       else if (unformat (a, "slave"))
1098         i_am_master = 0;
1099       else
1100         {
1101           fformat (stderr, "%s: usage [master|slave]\n");
1102           exit (1);
1103         }
1104     }
1105
1106   utm->cut_through_session_index = ~0;
1107   utm->uri = format (0, "%s%c", bind_name, 0);
1108   utm->i_am_master = i_am_master;
1109   utm->segment_main = &svm_fifo_segment_main;
1110
1111   utm->connect_uri = format (0, "udp://6.0.1.2/1234%c", 0);
1112
1113   setup_signal_handlers ();
1114
1115   uri_api_hookup (utm);
1116
1117   if (connect_to_vpp (i_am_master ? "uri_udp_master" : "uri_udp_slave") < 0)
1118     {
1119       svm_region_exit ();
1120       fformat (stderr, "Couldn't connect to vpe, exiting...\n");
1121       exit (1);
1122     }
1123
1124   if (i_am_master == 0)
1125     {
1126       client_test (utm);
1127       exit (0);
1128     }
1129
1130   /* $$$$ hack preallocation */
1131   for (i = 0; i < 200000; i++)
1132     {
1133       pool_get (utm->sessions, session);
1134       memset (session, 0, sizeof (*session));
1135     }
1136   for (i = 0; i < 200000; i++)
1137     pool_put_index (utm->sessions, i);
1138
1139   udp_server_test (utm);
1140
1141   vl_client_disconnect_from_vlib ();
1142   exit (0);
1143 }
1144
1145 #undef vl_api_version
1146 #define vl_api_version(n,v) static u32 vpe_api_version = v;
1147 #include <vpp/api/vpe.api.h>
1148 #undef vl_api_version
1149
1150 void
1151 vl_client_add_api_signatures (vl_api_memclnt_create_t * mp)
1152 {
1153   /*
1154    * Send the main API signature in slot 0. This bit of code must
1155    * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
1156    */
1157   mp->api_versions[0] = clib_host_to_net_u32 (vpe_api_version);
1158 }
1159
1160 u32
1161 vl (void *p)
1162 {
1163   return vec_len (p);
1164 }
1165
1166 /*
1167  * fd.io coding-style-patch-verification: ON
1168  *
1169  * Local Variables:
1170  * eval: (c-set-style "gnu")
1171  * End:
1172  */