session: add support for memfd segments
[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   svm_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   svm_queue_t *our_event_queue;
95
96   /* $$$ single thread only for the moment */
97   svm_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, svm_queue_t *);
237 }
238
239 static void
240 vl_api_application_detach_reply_t_handler (vl_api_application_detach_reply_t *
241                                            mp)
242 {
243   if (mp->retval)
244     clib_warning ("detach returned with err: %d", mp->retval);
245 }
246
247 u8 *
248 format_api_error (u8 * s, va_list * args)
249 {
250   uri_udp_test_main_t *utm = va_arg (*args, uri_udp_test_main_t *);
251   i32 error = va_arg (*args, u32);
252   uword *p;
253
254   p = hash_get (utm->error_string_by_error_number, -error);
255
256   if (p)
257     s = format (s, "%s", p[0]);
258   else
259     s = format (s, "%d", error);
260   return s;
261 }
262
263 int
264 wait_for_state_change (uri_udp_test_main_t * utm, connection_state_t state)
265 {
266 #if CLIB_DEBUG > 0
267 #define TIMEOUT 600.0
268 #else
269 #define TIMEOUT 600.0
270 #endif
271
272   f64 timeout = clib_time_now (&utm->clib_time) + TIMEOUT;
273
274   while (clib_time_now (&utm->clib_time) < timeout)
275     {
276       if (utm->state == state)
277         return 0;
278     }
279   return -1;
280 }
281
282 u64 server_bytes_received, server_bytes_sent;
283
284 static void *
285 cut_through_thread_fn (void *arg)
286 {
287   session_t *s;
288   svm_fifo_t *rx_fifo;
289   svm_fifo_t *tx_fifo;
290   u8 *my_copy_buffer = 0;
291   uri_udp_test_main_t *utm = &uri_udp_test_main;
292   i32 actual_transfer;
293   int rv;
294   u32 buffer_offset;
295
296   while (utm->cut_through_session_index == ~0)
297     ;
298
299   s = pool_elt_at_index (utm->sessions, utm->cut_through_session_index);
300
301   rx_fifo = s->server_rx_fifo;
302   tx_fifo = s->server_tx_fifo;
303
304   vec_validate (my_copy_buffer, 64 * 1024 - 1);
305
306   while (true)
307     {
308       /* We read from the tx fifo and write to the rx fifo */
309       do
310         {
311           actual_transfer = svm_fifo_dequeue_nowait (tx_fifo,
312                                                      vec_len (my_copy_buffer),
313                                                      my_copy_buffer);
314         }
315       while (actual_transfer <= 0);
316
317       server_bytes_received += actual_transfer;
318
319       buffer_offset = 0;
320       while (actual_transfer > 0)
321         {
322           rv = svm_fifo_enqueue_nowait (rx_fifo, actual_transfer,
323                                         my_copy_buffer + buffer_offset);
324           if (rv > 0)
325             {
326               actual_transfer -= rv;
327               buffer_offset += rv;
328               server_bytes_sent += rv;
329             }
330
331         }
332       if (PREDICT_FALSE (utm->time_to_stop))
333         break;
334     }
335
336   pthread_exit (0);
337 }
338
339 static void
340 udp_client_connect (uri_udp_test_main_t * utm)
341 {
342   vl_api_connect_uri_t *cmp;
343   cmp = vl_msg_api_alloc (sizeof (*cmp));
344   memset (cmp, 0, sizeof (*cmp));
345
346   cmp->_vl_msg_id = ntohs (VL_API_CONNECT_URI);
347   cmp->client_index = utm->my_client_index;
348   cmp->context = ntohl (0xfeedface);
349   memcpy (cmp->uri, utm->connect_uri, vec_len (utm->connect_uri));
350   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & cmp);
351 }
352
353 static void
354 client_send_cut_through (uri_udp_test_main_t * utm, session_t * session)
355 {
356   int i;
357   u8 *test_data = 0;
358   u64 bytes_received = 0, bytes_sent = 0;
359   i32 bytes_to_read;
360   int rv;
361   f64 before, after, delta, bytes_per_second;
362   svm_fifo_t *rx_fifo, *tx_fifo;
363   int buffer_offset, bytes_to_send = 0;
364
365   /*
366    * Prepare test data
367    */
368   vec_validate (test_data, 64 * 1024 - 1);
369   for (i = 0; i < vec_len (test_data); i++)
370     test_data[i] = i & 0xff;
371
372   rx_fifo = session->server_rx_fifo;
373   tx_fifo = session->server_tx_fifo;
374
375   before = clib_time_now (&utm->clib_time);
376
377   vec_validate (utm->rx_buf, vec_len (test_data) - 1);
378
379   for (i = 0; i < NITER; i++)
380     {
381       bytes_to_send = vec_len (test_data);
382       buffer_offset = 0;
383       while (bytes_to_send > 0)
384         {
385           rv = svm_fifo_enqueue_nowait (tx_fifo, bytes_to_send,
386                                         test_data + buffer_offset);
387
388           if (rv > 0)
389             {
390               bytes_to_send -= rv;
391               buffer_offset += rv;
392               bytes_sent += rv;
393             }
394         }
395
396       bytes_to_read = svm_fifo_max_dequeue (rx_fifo);
397       bytes_to_read = vec_len (utm->rx_buf) > bytes_to_read ?
398         bytes_to_read : vec_len (utm->rx_buf);
399
400       buffer_offset = 0;
401       while (bytes_to_read > 0)
402         {
403           rv = svm_fifo_dequeue_nowait (rx_fifo,
404                                         bytes_to_read,
405                                         utm->rx_buf + buffer_offset);
406           if (rv > 0)
407             {
408               bytes_to_read -= rv;
409               buffer_offset += rv;
410               bytes_received += rv;
411             }
412         }
413     }
414   while (bytes_received < bytes_sent)
415     {
416       rv =
417         svm_fifo_dequeue_nowait (rx_fifo, vec_len (utm->rx_buf), utm->rx_buf);
418       if (rv > 0)
419         {
420 #if CLIB_DEBUG > 0
421           int j;
422           for (j = 0; j < rv; j++)
423             {
424               if (utm->rx_buf[j] != ((bytes_received + j) & 0xff))
425                 {
426                   clib_warning ("error at byte %lld, 0x%x not 0x%x",
427                                 bytes_received + j,
428                                 utm->rx_buf[j],
429                                 ((bytes_received + j) & 0xff));
430                 }
431             }
432 #endif
433           bytes_received += (u64) rv;
434         }
435     }
436
437   after = clib_time_now (&utm->clib_time);
438   delta = after - before;
439   bytes_per_second = 0.0;
440
441   if (delta > 0.0)
442     bytes_per_second = (f64) bytes_received / delta;
443
444   fformat (stdout,
445            "Done: %lld recv bytes in %.2f seconds, %.2f bytes/sec...\n\n",
446            bytes_received, delta, bytes_per_second);
447   fformat (stdout,
448            "Done: %lld sent bytes in %.2f seconds, %.2f bytes/sec...\n\n",
449            bytes_sent, delta, bytes_per_second);
450   fformat (stdout,
451            "client -> server -> client round trip: %.2f Gbit/sec \n\n",
452            (bytes_per_second * 8.0) / 1e9);
453 }
454
455 static void
456 send_test_chunk (uri_udp_test_main_t * utm, svm_fifo_t * tx_fifo, int mypid,
457                  u32 bytes)
458 {
459   u8 *test_data = utm->connect_test_data;
460   u64 bytes_sent = 0;
461   int test_buf_offset = 0;
462   u32 bytes_to_snd;
463   u32 queue_max_chunk = 128 << 10, actual_write;
464   session_fifo_event_t evt;
465   int rv;
466
467   bytes_to_snd = (bytes == 0) ? vec_len (test_data) : bytes;
468   if (bytes_to_snd > vec_len (test_data))
469     bytes_to_snd = vec_len (test_data);
470
471   while (bytes_to_snd > 0 && !utm->time_to_stop)
472     {
473       actual_write = (bytes_to_snd > queue_max_chunk) ?
474         queue_max_chunk : bytes_to_snd;
475       rv = svm_fifo_enqueue_nowait (tx_fifo, actual_write,
476                                     test_data + test_buf_offset);
477
478       if (rv > 0)
479         {
480           bytes_to_snd -= rv;
481           test_buf_offset += rv;
482           bytes_sent += rv;
483
484           if (svm_fifo_set_event (tx_fifo))
485             {
486               /* Fabricate TX event, send to vpp */
487               evt.fifo = tx_fifo;
488               evt.event_type = FIFO_EVENT_APP_TX;
489
490               svm_queue_add (utm->vpp_event_queue,
491                              (u8 *) & evt, 0 /* do wait for mutex */ );
492             }
493         }
494     }
495 }
496
497 static void
498 recv_test_chunk (uri_udp_test_main_t * utm, session_t * session)
499 {
500   svm_fifo_t *rx_fifo;
501   int buffer_offset, bytes_to_read = 0, rv;
502
503   rx_fifo = session->server_rx_fifo;
504   bytes_to_read = svm_fifo_max_dequeue (rx_fifo);
505   bytes_to_read =
506     vec_len (utm->rx_buf) > bytes_to_read ?
507     bytes_to_read : vec_len (utm->rx_buf);
508
509   buffer_offset = 0;
510   while (bytes_to_read > 0)
511     {
512       rv = svm_fifo_dequeue_nowait (rx_fifo, bytes_to_read,
513                                     utm->rx_buf + buffer_offset);
514       if (rv > 0)
515         {
516           bytes_to_read -= rv;
517           buffer_offset += rv;
518         }
519     }
520 }
521
522 void
523 client_send_data (uri_udp_test_main_t * utm)
524 {
525   u8 *test_data;
526   int mypid = getpid ();
527   session_t *session;
528   svm_fifo_t *tx_fifo;
529   u32 n_iterations;
530   int i;
531
532   vec_validate (utm->connect_test_data, 64 * 1024 - 1);
533   for (i = 0; i < vec_len (utm->connect_test_data); i++)
534     utm->connect_test_data[i] = i & 0xff;
535
536   test_data = utm->connect_test_data;
537   session = pool_elt_at_index (utm->sessions, utm->connected_session);
538   tx_fifo = session->server_tx_fifo;
539
540   ASSERT (vec_len (test_data) > 0);
541
542   vec_validate (utm->rx_buf, vec_len (test_data) - 1);
543   n_iterations = NITER;
544
545   for (i = 0; i < n_iterations; i++)
546     {
547       send_test_chunk (utm, tx_fifo, mypid, 0);
548       recv_test_chunk (utm, session);
549       if (utm->time_to_stop)
550         break;
551     }
552
553   f64 timeout = clib_time_now (&utm->clib_time) + 5;
554   while (clib_time_now (&utm->clib_time) < timeout)
555     {
556       recv_test_chunk (utm, session);
557     }
558
559 }
560
561 static void
562 client_test (uri_udp_test_main_t * utm)
563 {
564   session_t *session;
565
566   application_send_attach (utm);
567   udp_client_connect (utm);
568
569   if (wait_for_state_change (utm, STATE_READY))
570     {
571       clib_warning ("timeout waiting for STATE_READY");
572       return;
573     }
574
575   if (utm->cut_through_session_index != ~0)
576     {
577       session = pool_elt_at_index (utm->sessions,
578                                    utm->cut_through_session_index);
579       client_send_cut_through (utm, session);
580     }
581   else
582     {
583       session = pool_elt_at_index (utm->sessions, utm->connected_session);
584       client_send_data (utm);
585     }
586
587   application_detach (utm);
588 }
589
590 static void
591 vl_api_bind_uri_reply_t_handler (vl_api_bind_uri_reply_t * mp)
592 {
593   uri_udp_test_main_t *utm = &uri_udp_test_main;
594
595   if (mp->retval)
596     {
597       clib_warning ("bind failed: %d", mp->retval);
598       utm->state = STATE_FAILED;
599       return;
600     }
601
602   utm->state = STATE_BOUND;
603 }
604
605 static void
606 vl_api_map_another_segment_t_handler (vl_api_map_another_segment_t * mp)
607 {
608   svm_fifo_segment_create_args_t _a, *a = &_a;
609   int rv;
610
611   memset (a, 0, sizeof (*a));
612   a->segment_name = (char *) mp->segment_name;
613   a->segment_size = mp->segment_size;
614   /* Attach to the segment vpp created */
615   rv = svm_fifo_segment_attach (a);
616   if (rv)
617     {
618       clib_warning ("svm_fifo_segment_attach ('%s') failed",
619                     mp->segment_name);
620       return;
621     }
622   clib_warning ("Mapped new segment '%s' size %d", mp->segment_name,
623                 mp->segment_size);
624 }
625
626 /**
627  * Acting as server for redirected connect requests
628  */
629 static void
630 vl_api_connect_uri_t_handler (vl_api_connect_uri_t * mp)
631 {
632   u32 segment_index;
633   uri_udp_test_main_t *utm = &uri_udp_test_main;
634   svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
635   svm_fifo_segment_create_args_t _a, *a = &_a;
636   svm_fifo_segment_private_t *seg;
637   svm_queue_t *client_q;
638   vl_api_connect_session_reply_t *rmp;
639   session_t *session = 0;
640   int rv = 0;
641
642   /* Create the segment */
643   a->segment_name = (char *) format (0, "%d:segment%d%c", utm->my_pid,
644                                      utm->unique_segment_index++, 0);
645   a->segment_size = utm->configured_segment_size;
646
647   rv = svm_fifo_segment_create (a);
648   if (rv)
649     {
650       clib_warning ("sm_fifo_segment_create ('%s') failed", a->segment_name);
651       rv = VNET_API_ERROR_URI_FIFO_CREATE_FAILED;
652       goto send_reply;
653     }
654
655   vec_add2 (utm->seg, seg, 1);
656
657   segment_index = vec_len (sm->segments) - 1;
658   memcpy (seg, sm->segments + segment_index, sizeof (utm->seg[0]));
659
660   pool_get (utm->sessions, session);
661
662   session->server_rx_fifo = svm_fifo_segment_alloc_fifo
663     (utm->seg, 128 * 1024, FIFO_SEGMENT_RX_FREELIST);
664   ASSERT (session->server_rx_fifo);
665
666   session->server_tx_fifo = svm_fifo_segment_alloc_fifo
667     (utm->seg, 128 * 1024, FIFO_SEGMENT_TX_FREELIST);
668   ASSERT (session->server_tx_fifo);
669
670   session->server_rx_fifo->master_session_index = session - utm->sessions;
671   session->server_tx_fifo->master_session_index = session - utm->sessions;
672   utm->cut_through_session_index = session - utm->sessions;
673
674   rv = pthread_create (&utm->cut_through_thread_handle,
675                        NULL /*attr */ , cut_through_thread_fn, 0);
676   if (rv)
677     {
678       clib_warning ("pthread_create returned %d", rv);
679       rv = VNET_API_ERROR_SYSCALL_ERROR_1;
680     }
681
682 send_reply:
683   rmp = vl_msg_api_alloc (sizeof (*rmp));
684   memset (rmp, 0, sizeof (*rmp));
685
686   rmp->_vl_msg_id = ntohs (VL_API_CONNECT_SESSION_REPLY);
687   rmp->context = mp->context;
688   rmp->retval = ntohl (rv);
689   rmp->segment_name_length = vec_len (a->segment_name);
690   if (session)
691     {
692       rmp->server_rx_fifo = pointer_to_uword (session->server_rx_fifo);
693       rmp->server_tx_fifo = pointer_to_uword (session->server_tx_fifo);
694     }
695
696   memcpy (rmp->segment_name, a->segment_name, vec_len (a->segment_name));
697
698   vec_free (a->segment_name);
699
700   client_q = uword_to_pointer (mp->client_queue_address, svm_queue_t *);
701   vl_msg_api_send_shmem (client_q, (u8 *) & rmp);
702 }
703
704 static void
705 vl_api_unbind_uri_reply_t_handler (vl_api_unbind_uri_reply_t * mp)
706 {
707   uri_udp_test_main_t *utm = &uri_udp_test_main;
708
709   if (mp->retval != 0)
710     clib_warning ("returned %d", ntohl (mp->retval));
711
712   utm->state = STATE_START;
713 }
714
715 static void
716 vl_api_accept_session_t_handler (vl_api_accept_session_t * mp)
717 {
718   uri_udp_test_main_t *utm = &uri_udp_test_main;
719   vl_api_accept_session_reply_t *rmp;
720   svm_fifo_t *rx_fifo, *tx_fifo;
721   session_t *session;
722   static f64 start_time;
723
724   if (start_time == 0.0)
725     start_time = clib_time_now (&utm->clib_time);
726
727   utm->vpp_event_queue =
728     uword_to_pointer (mp->vpp_event_queue_address, svm_queue_t *);
729
730   pool_get (utm->sessions, session);
731
732   rx_fifo = uword_to_pointer (mp->server_rx_fifo, svm_fifo_t *);
733   rx_fifo->client_session_index = session - utm->sessions;
734   tx_fifo = uword_to_pointer (mp->server_tx_fifo, svm_fifo_t *);
735   tx_fifo->client_session_index = session - utm->sessions;
736
737   session->server_rx_fifo = rx_fifo;
738   session->server_tx_fifo = tx_fifo;
739
740   hash_set (utm->session_index_by_vpp_handles, mp->handle,
741             session - utm->sessions);
742
743   if (pool_elts (utm->sessions) && (pool_elts (utm->sessions) % 20000) == 0)
744     {
745       f64 now = clib_time_now (&utm->clib_time);
746       fformat (stdout, "%d active sessions in %.2f seconds, %.2f/sec...\n",
747                pool_elts (utm->sessions), now - start_time,
748                (f64) pool_elts (utm->sessions) / (now - start_time));
749     }
750
751   rmp = vl_msg_api_alloc (sizeof (*rmp));
752   memset (rmp, 0, sizeof (*rmp));
753   rmp->_vl_msg_id = ntohs (VL_API_ACCEPT_SESSION_REPLY);
754   rmp->handle = mp->handle;
755   rmp->context = mp->context;
756   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & rmp);
757
758   CLIB_MEMORY_BARRIER ();
759   utm->state = STATE_READY;
760 }
761
762 static void
763 vl_api_disconnect_session_t_handler (vl_api_disconnect_session_t * mp)
764 {
765   uri_udp_test_main_t *utm = &uri_udp_test_main;
766   session_t *session;
767   vl_api_disconnect_session_reply_t *rmp;
768   uword *p;
769   int rv = 0;
770
771   p = hash_get (utm->session_index_by_vpp_handles, mp->handle);
772
773   if (p)
774     {
775       session = pool_elt_at_index (utm->sessions, p[0]);
776       hash_unset (utm->session_index_by_vpp_handles, mp->handle);
777       pool_put (utm->sessions, session);
778     }
779   else
780     {
781       clib_warning ("couldn't find session key %llx", mp->handle);
782       rv = -11;
783     }
784
785   rmp = vl_msg_api_alloc (sizeof (*rmp));
786   memset (rmp, 0, sizeof (*rmp));
787   rmp->_vl_msg_id = ntohs (VL_API_DISCONNECT_SESSION_REPLY);
788   rmp->retval = rv;
789   rmp->handle = mp->handle;
790   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & rmp);
791 }
792
793 static void
794 vl_api_connect_session_reply_t_handler (vl_api_connect_session_reply_t * mp)
795 {
796   uri_udp_test_main_t *utm = &uri_udp_test_main;
797   session_t *session;
798
799   ASSERT (utm->i_am_master == 0);
800
801   if (mp->retval)
802     {
803       clib_warning ("failed connect");
804       return;
805     }
806
807   /* We've been redirected */
808   if (mp->segment_name_length > 0)
809     {
810       svm_fifo_segment_main_t *sm = &svm_fifo_segment_main;
811       svm_fifo_segment_create_args_t _a, *a = &_a;
812       u32 segment_index;
813       svm_fifo_segment_private_t *seg;
814       int rv;
815
816       memset (a, 0, sizeof (*a));
817       a->segment_name = (char *) mp->segment_name;
818
819       sleep (1);
820
821       rv = svm_fifo_segment_attach (a);
822       if (rv)
823         {
824           clib_warning ("sm_fifo_segment_create ('%v') failed",
825                         mp->segment_name);
826           return;
827         }
828
829       segment_index = a->new_segment_indices[0];
830       vec_add2 (utm->seg, seg, 1);
831       memcpy (seg, sm->segments + segment_index, sizeof (*seg));
832       sleep (1);
833     }
834
835   pool_get (utm->sessions, session);
836   session->server_rx_fifo = uword_to_pointer (mp->server_rx_fifo,
837                                               svm_fifo_t *);
838   ASSERT (session->server_rx_fifo);
839   session->server_tx_fifo = uword_to_pointer (mp->server_tx_fifo,
840                                               svm_fifo_t *);
841   ASSERT (session->server_tx_fifo);
842
843   if (mp->segment_name_length > 0)
844     utm->cut_through_session_index = session - utm->sessions;
845   else
846     {
847       utm->connected_session = session - utm->sessions;
848       utm->vpp_event_queue = uword_to_pointer (mp->vpp_event_queue_address,
849                                                svm_queue_t *);
850     }
851   utm->state = STATE_READY;
852 }
853
854 #define foreach_tcp_echo_msg                            \
855 _(BIND_URI_REPLY, bind_uri_reply)                       \
856 _(CONNECT_URI, connect_uri)                             \
857 _(CONNECT_SESSION_REPLY, connect_session_reply)         \
858 _(UNBIND_URI_REPLY, unbind_uri_reply)                   \
859 _(ACCEPT_SESSION, accept_session)                       \
860 _(DISCONNECT_SESSION, disconnect_session)               \
861 _(MAP_ANOTHER_SEGMENT, map_another_segment)             \
862 _(APPLICATION_ATTACH_REPLY, application_attach_reply)   \
863 _(APPLICATION_DETACH_REPLY, application_detach_reply)   \
864
865 void
866 tcp_echo_api_hookup (uri_udp_test_main_t * utm)
867 {
868 #define _(N,n)                                                  \
869     vl_msg_api_set_handlers(VL_API_##N, #n,                     \
870                            vl_api_##n##_t_handler,              \
871                            vl_noop_handler,                     \
872                            vl_api_##n##_t_endian,               \
873                            vl_api_##n##_t_print,                \
874                            sizeof(vl_api_##n##_t), 1);
875   foreach_tcp_echo_msg;
876 #undef _
877
878 }
879
880 int
881 connect_to_vpp (char *name)
882 {
883   uri_udp_test_main_t *utm = &uri_udp_test_main;
884   api_main_t *am = &api_main;
885
886   if (vl_client_connect_to_vlib ("/vpe-api", name, 32) < 0)
887     return -1;
888
889   utm->vl_input_queue = am->shmem_hdr->vl_input_queue;
890   utm->my_client_index = am->my_client_index;
891
892   return 0;
893 }
894
895 void
896 vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...)
897 {
898   clib_warning ("BUG");
899 }
900
901 static void
902 init_error_string_table (uri_udp_test_main_t * utm)
903 {
904   utm->error_string_by_error_number = hash_create (0, sizeof (uword));
905
906 #define _(n,v,s) hash_set (utm->error_string_by_error_number, -v, s);
907   foreach_vnet_api_error;
908 #undef _
909
910   hash_set (utm->error_string_by_error_number, 99, "Misc");
911 }
912
913 void
914 server_handle_fifo_event_rx (uri_udp_test_main_t * utm,
915                              session_fifo_event_t * e)
916 {
917   svm_fifo_t *rx_fifo, *tx_fifo;
918   int nbytes;
919   session_fifo_event_t evt;
920   svm_queue_t *q;
921   int rv;
922
923   rx_fifo = e->fifo;
924   tx_fifo = utm->sessions[rx_fifo->client_session_index].server_tx_fifo;
925   svm_fifo_unset_event (rx_fifo);
926
927   do
928     {
929       nbytes = svm_fifo_dequeue_nowait (rx_fifo, vec_len (utm->rx_buf),
930                                         utm->rx_buf);
931     }
932   while (nbytes <= 0);
933   do
934     {
935       rv = svm_fifo_enqueue_nowait (tx_fifo, nbytes, utm->rx_buf);
936     }
937   while (rv == -2);
938
939   if (svm_fifo_set_event (tx_fifo))
940     {
941       /* Fabricate TX event, send to vpp */
942       evt.fifo = tx_fifo;
943       evt.event_type = FIFO_EVENT_APP_TX;
944       q = utm->vpp_event_queue;
945       svm_queue_add (q, (u8 *) & evt, 0 /* do wait for mutex */ );
946     }
947 }
948
949 void
950 server_handle_event_queue (uri_udp_test_main_t * utm)
951 {
952   session_fifo_event_t _e, *e = &_e;
953
954   while (utm->state != STATE_READY)
955     sleep (5);
956
957   while (1)
958     {
959       svm_queue_sub (utm->our_event_queue, (u8 *) e, SVM_Q_WAIT, 0);
960       switch (e->event_type)
961         {
962         case FIFO_EVENT_APP_RX:
963           server_handle_fifo_event_rx (utm, e);
964           break;
965
966         case FIFO_EVENT_DISCONNECT:
967           return;
968
969         default:
970           clib_warning ("unknown event type %d", e->event_type);
971           break;
972         }
973       if (PREDICT_FALSE (utm->time_to_stop == 1))
974         return;
975       if (PREDICT_FALSE (utm->time_to_print_stats == 1))
976         {
977           utm->time_to_print_stats = 0;
978           fformat (stdout, "%d connections\n", pool_elts (utm->sessions));
979         }
980     }
981 }
982
983 static void
984 server_unbind (uri_udp_test_main_t * utm)
985 {
986   vl_api_unbind_uri_t *ump;
987
988   ump = vl_msg_api_alloc (sizeof (*ump));
989   memset (ump, 0, sizeof (*ump));
990
991   ump->_vl_msg_id = ntohs (VL_API_UNBIND_URI);
992   ump->client_index = utm->my_client_index;
993   memcpy (ump->uri, utm->uri, vec_len (utm->uri));
994   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & ump);
995 }
996
997 static void
998 server_bind (uri_udp_test_main_t * utm)
999 {
1000   vl_api_bind_uri_t *bmp;
1001
1002   bmp = vl_msg_api_alloc (sizeof (*bmp));
1003   memset (bmp, 0, sizeof (*bmp));
1004
1005   bmp->_vl_msg_id = ntohs (VL_API_BIND_URI);
1006   bmp->client_index = utm->my_client_index;
1007   bmp->context = ntohl (0xfeedface);
1008   memcpy (bmp->uri, utm->uri, vec_len (utm->uri));
1009   vl_msg_api_send_shmem (utm->vl_input_queue, (u8 *) & bmp);
1010 }
1011
1012 void
1013 udp_server_test (uri_udp_test_main_t * utm)
1014 {
1015
1016   application_send_attach (utm);
1017
1018   /* Bind to uri */
1019   server_bind (utm);
1020
1021   if (wait_for_state_change (utm, STATE_BOUND))
1022     {
1023       clib_warning ("timeout waiting for STATE_BOUND");
1024       return;
1025     }
1026
1027   server_handle_event_queue (utm);
1028
1029   /* Cleanup */
1030   server_unbind (utm);
1031
1032   if (wait_for_state_change (utm, STATE_START))
1033     {
1034       clib_warning ("timeout waiting for STATE_START");
1035       return;
1036     }
1037
1038   application_detach (utm);
1039
1040   fformat (stdout, "Test complete...\n");
1041 }
1042
1043 int
1044 main (int argc, char **argv)
1045 {
1046   uri_udp_test_main_t *utm = &uri_udp_test_main;
1047   u8 *bind_name = (u8 *) "udp://0.0.0.0/1234";
1048   unformat_input_t _argv, *a = &_argv;
1049   int i_am_master = 1;
1050   session_t *session;
1051   u8 *chroot_prefix;
1052   char *app_name;
1053   mheap_t *h;
1054   u8 *heap;
1055   u32 tmp;
1056   int i;
1057
1058   clib_mem_init (0, 256 << 20);
1059
1060   heap = clib_mem_get_per_cpu_heap ();
1061   h = mheap_header (heap);
1062
1063   /* make the main heap thread-safe */
1064   h->flags |= MHEAP_FLAG_THREAD_SAFE;
1065
1066   vec_validate (utm->rx_buf, 8192);
1067
1068   utm->session_index_by_vpp_handles = hash_create (0, sizeof (uword));
1069   utm->my_pid = getpid ();
1070   utm->configured_segment_size = 1 << 20;
1071
1072   clib_time_init (&utm->clib_time);
1073   init_error_string_table (utm);
1074   svm_fifo_segment_init (0x200000000ULL, 20);
1075   unformat_init_command_line (a, argv);
1076
1077   while (unformat_check_input (a) != UNFORMAT_END_OF_INPUT)
1078     {
1079       if (unformat (a, "chroot prefix %s", &chroot_prefix))
1080         {
1081           vl_set_memory_root_path ((char *) chroot_prefix);
1082         }
1083       else if (unformat (a, "uri %s", &bind_name))
1084         ;
1085       else if (unformat (a, "segment-size %dM", &tmp))
1086         utm->configured_segment_size = tmp << 20;
1087       else if (unformat (a, "segment-size %dG", &tmp))
1088         utm->configured_segment_size = tmp << 30;
1089       else if (unformat (a, "master"))
1090         i_am_master = 1;
1091       else if (unformat (a, "slave"))
1092         i_am_master = 0;
1093       else
1094         {
1095           fformat (stderr, "%s: usage [master|slave]\n");
1096           exit (1);
1097         }
1098     }
1099
1100   utm->cut_through_session_index = ~0;
1101   utm->uri = format (0, "%s%c", bind_name, 0);
1102   utm->i_am_master = i_am_master;
1103   utm->segment_main = &svm_fifo_segment_main;
1104   utm->connect_uri = format (0, "udp://6.0.1.2/1234%c", 0);
1105
1106   setup_signal_handlers ();
1107   tcp_echo_api_hookup (utm);
1108
1109   app_name = i_am_master ? "udp_echo_master" : "udp_echo_slave";
1110   if (connect_to_vpp (app_name) < 0)
1111     {
1112       svm_region_exit ();
1113       fformat (stderr, "Couldn't connect to vpe, exiting...\n");
1114       exit (1);
1115     }
1116
1117   if (i_am_master == 0)
1118     {
1119       client_test (utm);
1120       exit (0);
1121     }
1122
1123   /* $$$$ hack preallocation */
1124   for (i = 0; i < 200000; i++)
1125     {
1126       pool_get (utm->sessions, session);
1127       memset (session, 0, sizeof (*session));
1128     }
1129   for (i = 0; i < 200000; i++)
1130     pool_put_index (utm->sessions, i);
1131
1132   udp_server_test (utm);
1133
1134   vl_client_disconnect_from_vlib ();
1135   exit (0);
1136 }
1137
1138 #undef vl_api_version
1139 #define vl_api_version(n,v) static u32 vpe_api_version = v;
1140 #include <vpp/api/vpe.api.h>
1141 #undef vl_api_version
1142
1143 void
1144 vl_client_add_api_signatures (vl_api_memclnt_create_t * mp)
1145 {
1146   /*
1147    * Send the main API signature in slot 0. This bit of code must
1148    * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
1149    */
1150   mp->api_versions[0] = clib_host_to_net_u32 (vpe_api_version);
1151 }
1152
1153 u32
1154 vl (void *p)
1155 {
1156   return vec_len (p);
1157 }
1158
1159 /*
1160  * fd.io coding-style-patch-verification: ON
1161  *
1162  * Local Variables:
1163  * eval: (c-set-style "gnu")
1164  * End:
1165  */