hsa: Refactor quic_echo to allow other protocols
[vpp.git] / src / plugins / hs_apps / sapi / vpp_echo_common.c
1 /*
2  * Copyright (c) 2019 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 <signal.h>
18
19 #include <hs_apps/sapi/vpp_echo_common.h>
20
21 /*
22  *
23  *  Format functions
24  *
25  */
26
27 u8 *
28 format_ip4_address (u8 * s, va_list * args)
29 {
30   u8 *a = va_arg (*args, u8 *);
31   return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
32 }
33
34 u8 *
35 format_ip6_address (u8 * s, va_list * args)
36 {
37   ip6_address_t *a = va_arg (*args, ip6_address_t *);
38   u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;
39
40   i_max_n_zero = ARRAY_LEN (a->as_u16);
41   max_n_zeros = 0;
42   i_first_zero = i_max_n_zero;
43   n_zeros = 0;
44   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
45     {
46       u32 is_zero = a->as_u16[i] == 0;
47       if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
48         {
49           i_first_zero = i;
50           n_zeros = 0;
51         }
52       n_zeros += is_zero;
53       if ((!is_zero && n_zeros > max_n_zeros)
54           || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
55         {
56           i_max_n_zero = i_first_zero;
57           max_n_zeros = n_zeros;
58           i_first_zero = ARRAY_LEN (a->as_u16);
59           n_zeros = 0;
60         }
61     }
62
63   last_double_colon = 0;
64   for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
65     {
66       if (i == i_max_n_zero && max_n_zeros > 1)
67         {
68           s = format (s, "::");
69           i += max_n_zeros - 1;
70           last_double_colon = 1;
71         }
72       else
73         {
74           s = format (s, "%s%x",
75                       (last_double_colon || i == 0) ? "" : ":",
76                       clib_net_to_host_u16 (a->as_u16[i]));
77           last_double_colon = 0;
78         }
79     }
80
81   return s;
82 }
83
84 /* Format an IP46 address. */
85 u8 *
86 format_ip46_address (u8 * s, va_list * args)
87 {
88   ip46_address_t *ip46 = va_arg (*args, ip46_address_t *);
89   ip46_type_t type = va_arg (*args, ip46_type_t);
90   int is_ip4 = 1;
91
92   switch (type)
93     {
94     case IP46_TYPE_ANY:
95       is_ip4 = ip46_address_is_ip4 (ip46);
96       break;
97     case IP46_TYPE_IP4:
98       is_ip4 = 1;
99       break;
100     case IP46_TYPE_IP6:
101       is_ip4 = 0;
102       break;
103     }
104
105   return is_ip4 ?
106     format (s, "%U", format_ip4_address, &ip46->ip4) :
107     format (s, "%U", format_ip6_address, &ip46->ip6);
108 }
109
110 uword
111 unformat_data (unformat_input_t * input, va_list * args)
112 {
113   u64 _a;
114   u64 *a = va_arg (*args, u64 *);
115   if (unformat (input, "%lluGb", &_a))
116     *a = _a << 30;
117   else if (unformat (input, "%lluG", &_a))
118     *a = _a << 30;
119   else if (unformat (input, "%lluMb", &_a))
120     *a = _a << 20;
121   else if (unformat (input, "%lluM", &_a))
122     *a = _a << 20;
123   else if (unformat (input, "%lluKb", &_a))
124     *a = _a << 10;
125   else if (unformat (input, "%lluK", &_a))
126     *a = _a << 10;
127   else if (unformat (input, "%llu", a))
128     ;
129   else
130     return 0;
131   return 1;
132 }
133
134 u8 *
135 format_api_error (u8 * s, va_list * args)
136 {
137   echo_main_t *em = &echo_main;
138   i32 error = va_arg (*args, u32);
139   uword *p;
140
141   p = hash_get (em->error_string_by_error_number, -error);
142
143   if (p)
144     s = format (s, "%s", p[0]);
145   else
146     s = format (s, "%d", error);
147   return s;
148 }
149
150 void
151 init_error_string_table ()
152 {
153   echo_main_t *em = &echo_main;
154   em->error_string_by_error_number = hash_create (0, sizeof (uword));
155
156 #define _(n,v,s) hash_set (em->error_string_by_error_number, -v, s);
157   foreach_vnet_api_error;
158 #undef _
159
160   hash_set (em->error_string_by_error_number, 99, "Misc");
161 }
162
163 u8 *
164 echo_format_app_state (u8 * s, va_list * args)
165 {
166   u32 state = va_arg (*args, u32);
167   if (state == STATE_START)
168     return format (s, "STATE_START");
169   if (state == STATE_ATTACHED)
170     return format (s, "STATE_ATTACHED");
171   if (state == STATE_LISTEN)
172     return format (s, "STATE_LISTEN");
173   if (state == STATE_READY)
174     return format (s, "STATE_READY");
175   if (state == STATE_DATA_DONE)
176     return format (s, "STATE_DATA_DONE");
177   if (state == STATE_DISCONNECTED)
178     return format (s, "STATE_DISCONNECTED");
179   if (state == STATE_DETACHED)
180     return format (s, "STATE_DETACHED");
181   else
182     return format (s, "unknown state");
183 }
184
185 uword
186 echo_unformat_close (unformat_input_t * input, va_list * args)
187 {
188   u8 *a = va_arg (*args, u8 *);
189   if (unformat (input, "Y"))
190     *a = ECHO_CLOSE_F_ACTIVE;
191   else if (unformat (input, "N"))
192     *a = ECHO_CLOSE_F_NONE;
193   else if (unformat (input, "W"))
194     *a = ECHO_CLOSE_F_PASSIVE;
195   else
196     return 0;
197   return 1;
198 }
199
200 uword
201 echo_unformat_timing_event (unformat_input_t * input, va_list * args)
202 {
203   u8 *a = va_arg (*args, u8 *);
204   if (unformat (input, "start"))
205     *a = ECHO_EVT_START;
206   else if (unformat (input, "qconnected"))
207     *a = ECHO_EVT_LAST_QCONNECTED;
208   else if (unformat (input, "qconnect"))
209     *a = ECHO_EVT_FIRST_QCONNECT;
210   else if (unformat (input, "sconnected"))
211     *a = ECHO_EVT_LAST_SCONNECTED;
212   else if (unformat (input, "sconnect"))
213     *a = ECHO_EVT_FIRST_SCONNECT;
214   else if (unformat (input, "lastbyte"))
215     *a = ECHO_EVT_LAST_BYTE;
216   else if (unformat (input, "exit"))
217     *a = ECHO_EVT_EXIT;
218   else
219     return 0;
220   return 1;
221 }
222
223 u8 *
224 echo_format_timing_event (u8 * s, va_list * args)
225 {
226   u32 timing_event = va_arg (*args, u32);
227   if (timing_event == ECHO_EVT_START)
228     return format (s, "start");
229   if (timing_event == ECHO_EVT_FIRST_QCONNECT)
230     return format (s, "qconnect");
231   if (timing_event == ECHO_EVT_LAST_QCONNECTED)
232     return format (s, "qconnected");
233   if (timing_event == ECHO_EVT_FIRST_SCONNECT)
234     return format (s, "sconnect");
235   if (timing_event == ECHO_EVT_LAST_SCONNECTED)
236     return format (s, "sconnected");
237   if (timing_event == ECHO_EVT_LAST_BYTE)
238     return format (s, "lastbyte");
239   if (timing_event == ECHO_EVT_EXIT)
240     return format (s, "exit");
241   else
242     return format (s, "unknown timing event");
243 }
244
245 uword
246 unformat_transport_proto (unformat_input_t * input, va_list * args)
247 {
248   u32 *proto = va_arg (*args, u32 *);
249   if (unformat (input, "tcp"))
250     *proto = TRANSPORT_PROTO_TCP;
251   else if (unformat (input, "TCP"))
252     *proto = TRANSPORT_PROTO_TCP;
253   else if (unformat (input, "udpc"))
254     *proto = TRANSPORT_PROTO_UDPC;
255   else if (unformat (input, "UDPC"))
256     *proto = TRANSPORT_PROTO_UDPC;
257   else if (unformat (input, "udp"))
258     *proto = TRANSPORT_PROTO_UDP;
259   else if (unformat (input, "UDP"))
260     *proto = TRANSPORT_PROTO_UDP;
261   else if (unformat (input, "sctp"))
262     *proto = TRANSPORT_PROTO_SCTP;
263   else if (unformat (input, "SCTP"))
264     *proto = TRANSPORT_PROTO_SCTP;
265   else if (unformat (input, "tls"))
266     *proto = TRANSPORT_PROTO_TLS;
267   else if (unformat (input, "TLS"))
268     *proto = TRANSPORT_PROTO_TLS;
269   else if (unformat (input, "quic"))
270     *proto = TRANSPORT_PROTO_QUIC;
271   else if (unformat (input, "QUIC"))
272     *proto = TRANSPORT_PROTO_QUIC;
273   else
274     return 0;
275   return 1;
276 }
277
278 u8 *
279 format_transport_proto (u8 * s, va_list * args)
280 {
281   u32 transport_proto = va_arg (*args, u32);
282   switch (transport_proto)
283     {
284     case TRANSPORT_PROTO_TCP:
285       s = format (s, "TCP");
286       break;
287     case TRANSPORT_PROTO_UDP:
288       s = format (s, "UDP");
289       break;
290     case TRANSPORT_PROTO_SCTP:
291       s = format (s, "SCTP");
292       break;
293     case TRANSPORT_PROTO_NONE:
294       s = format (s, "NONE");
295       break;
296     case TRANSPORT_PROTO_TLS:
297       s = format (s, "TLS");
298       break;
299     case TRANSPORT_PROTO_UDPC:
300       s = format (s, "UDPC");
301       break;
302     case TRANSPORT_PROTO_QUIC:
303       s = format (s, "QUIC");
304       break;
305     default:
306       s = format (s, "UNKNOWN");
307       break;
308     }
309   return s;
310 }
311
312 uword
313 unformat_ip4_address (unformat_input_t * input, va_list * args)
314 {
315   u8 *result = va_arg (*args, u8 *);
316   unsigned a[4];
317
318   if (!unformat (input, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]))
319     return 0;
320
321   if (a[0] >= 256 || a[1] >= 256 || a[2] >= 256 || a[3] >= 256)
322     return 0;
323
324   result[0] = a[0];
325   result[1] = a[1];
326   result[2] = a[2];
327   result[3] = a[3];
328
329   return 1;
330 }
331
332 uword
333 unformat_ip6_address (unformat_input_t * input, va_list * args)
334 {
335   ip6_address_t *result = va_arg (*args, ip6_address_t *);
336   u16 hex_quads[8];
337   uword hex_quad, n_hex_quads, hex_digit, n_hex_digits;
338   uword c, n_colon, double_colon_index;
339
340   n_hex_quads = hex_quad = n_hex_digits = n_colon = 0;
341   double_colon_index = ARRAY_LEN (hex_quads);
342   while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
343     {
344       hex_digit = 16;
345       if (c >= '0' && c <= '9')
346         hex_digit = c - '0';
347       else if (c >= 'a' && c <= 'f')
348         hex_digit = c + 10 - 'a';
349       else if (c >= 'A' && c <= 'F')
350         hex_digit = c + 10 - 'A';
351       else if (c == ':' && n_colon < 2)
352         n_colon++;
353       else
354         {
355           unformat_put_input (input);
356           break;
357         }
358
359       /* Too many hex quads. */
360       if (n_hex_quads >= ARRAY_LEN (hex_quads))
361         return 0;
362
363       if (hex_digit < 16)
364         {
365           hex_quad = (hex_quad << 4) | hex_digit;
366
367           /* Hex quad must fit in 16 bits. */
368           if (n_hex_digits >= 4)
369             return 0;
370
371           n_colon = 0;
372           n_hex_digits++;
373         }
374
375       /* Save position of :: */
376       if (n_colon == 2)
377         {
378           /* More than one :: ? */
379           if (double_colon_index < ARRAY_LEN (hex_quads))
380             return 0;
381           double_colon_index = n_hex_quads;
382         }
383
384       if (n_colon > 0 && n_hex_digits > 0)
385         {
386           hex_quads[n_hex_quads++] = hex_quad;
387           hex_quad = 0;
388           n_hex_digits = 0;
389         }
390     }
391
392   if (n_hex_digits > 0)
393     hex_quads[n_hex_quads++] = hex_quad;
394
395   {
396     word i;
397
398     /* Expand :: to appropriate number of zero hex quads. */
399     if (double_colon_index < ARRAY_LEN (hex_quads))
400       {
401         word n_zero = ARRAY_LEN (hex_quads) - n_hex_quads;
402
403         for (i = n_hex_quads - 1; i >= (signed) double_colon_index; i--)
404           hex_quads[n_zero + i] = hex_quads[i];
405
406         for (i = 0; i < n_zero; i++)
407           hex_quads[double_colon_index + i] = 0;
408
409         n_hex_quads = ARRAY_LEN (hex_quads);
410       }
411
412     /* Too few hex quads given. */
413     if (n_hex_quads < ARRAY_LEN (hex_quads))
414       return 0;
415
416     for (i = 0; i < ARRAY_LEN (hex_quads); i++)
417       result->as_u16[i] = clib_host_to_net_u16 (hex_quads[i]);
418
419     return 1;
420   }
421 }
422
423 /*
424  *
425  *  End of format functions
426  *
427  */
428
429 void
430 echo_session_handle_add_del (echo_main_t * em, u64 handle, u32 sid)
431 {
432   clib_spinlock_lock (&em->sid_vpp_handles_lock);
433   if (sid == SESSION_INVALID_INDEX)
434     hash_unset (em->session_index_by_vpp_handles, handle);
435   else
436     hash_set (em->session_index_by_vpp_handles, handle, sid);
437   clib_spinlock_unlock (&em->sid_vpp_handles_lock);
438 }
439
440 echo_session_t *
441 echo_session_new (echo_main_t * em)
442 {
443   /* thread safe new prealloced session */
444   return pool_elt_at_index (em->sessions,
445                             clib_atomic_fetch_add (&em->nxt_available_sidx,
446                                                    1));
447 }
448
449 int
450 echo_send_rpc (echo_main_t * em, void *fp, void *arg, u32 opaque)
451 {
452   svm_msg_q_msg_t msg;
453   echo_rpc_msg_t *evt;
454   if (PREDICT_FALSE (svm_msg_q_lock (em->rpc_msq_queue)))
455     {
456       ECHO_LOG (1, "RPC lock failed");
457       return -1;
458     }
459   if (PREDICT_FALSE (svm_msg_q_ring_is_full (em->rpc_msq_queue, 0)))
460     {
461       svm_msg_q_unlock (em->rpc_msq_queue);
462       ECHO_LOG (1, "RPC ring is full");
463       return -2;
464     }
465   msg = svm_msg_q_alloc_msg_w_ring (em->rpc_msq_queue, 0);
466   if (PREDICT_FALSE (svm_msg_q_msg_is_invalid (&msg)))
467     {
468       ECHO_LOG (1, "RPC msg is invalid");
469       svm_msg_q_unlock (em->rpc_msq_queue);
470       return -2;
471     }
472   evt = (echo_rpc_msg_t *) svm_msg_q_msg_data (em->rpc_msq_queue, &msg);
473   evt->arg = arg;
474   evt->opaque = opaque;
475   evt->fp = fp;
476
477   svm_msg_q_add_and_unlock (em->rpc_msq_queue, &msg);
478   return 0;
479 }
480
481 echo_session_t *
482 echo_get_session_from_handle (echo_main_t * em, u64 handle)
483 {
484   uword *p;
485   clib_spinlock_lock (&em->sid_vpp_handles_lock);
486   p = hash_get (em->session_index_by_vpp_handles, handle);
487   clib_spinlock_unlock (&em->sid_vpp_handles_lock);
488   if (!p)
489     {
490       ECHO_FAIL ("unknown handle 0x%lx", handle);
491       return 0;
492     }
493   return pool_elt_at_index (em->sessions, p[0]);
494 }
495
496 int
497 wait_for_segment_allocation (u64 segment_handle)
498 {
499   echo_main_t *em = &echo_main;
500   f64 timeout;
501   timeout = clib_time_now (&em->clib_time) + TIMEOUT;
502   uword *segment_present;
503   ECHO_LOG (1, "Waiting for segment 0x%lx...", segment_handle);
504   while (clib_time_now (&em->clib_time) < timeout)
505     {
506       clib_spinlock_lock (&em->segment_handles_lock);
507       segment_present = hash_get (em->shared_segment_handles, segment_handle);
508       clib_spinlock_unlock (&em->segment_handles_lock);
509       if (segment_present != 0)
510         return 0;
511       if (em->time_to_stop == 1)
512         return 0;
513     }
514   ECHO_LOG (1, "timeout wait_for_segment_allocation (0x%lx)", segment_handle);
515   return -1;
516 }
517
518 int
519 wait_for_state_change (echo_main_t * em, connection_state_t state,
520                        f64 timeout)
521 {
522   f64 end_time = clib_time_now (&em->clib_time) + timeout;
523   while (!timeout || clib_time_now (&em->clib_time) < end_time)
524     {
525       if (em->state == state)
526         return 0;
527       if (em->time_to_stop)
528         return 1;
529     }
530   ECHO_LOG (1, "timeout waiting for %U", echo_format_app_state, state);
531   return -1;
532 }
533
534 void
535 echo_notify_event (echo_main_t * em, echo_test_evt_t e)
536 {
537   if (em->timing.events_sent & e)
538     return;
539   if (em->timing.start_event == e)
540     em->timing.start_time = clib_time_now (&em->clib_time);
541   else if (em->timing.end_event == e)
542     em->timing.end_time = clib_time_now (&em->clib_time);
543   em->timing.events_sent |= e;
544 }
545
546 void
547 echo_session_print_stats (echo_main_t * em, echo_session_t * session)
548 {
549   f64 deltat = clib_time_now (&em->clib_time) - session->start;
550   ECHO_LOG (0, "Session 0x%x done in %.6fs RX[%.4f] TX[%.4f] Gbit/s\n",
551             session->vpp_session_handle, deltat,
552             (session->bytes_received * 8.0) / deltat / 1e9,
553             (session->bytes_sent * 8.0) / deltat / 1e9);
554 }
555
556 /*
557  * fd.io coding-style-patch-verification: ON
558  *
559  * Local Variables:
560  * eval: (c-set-style "gnu")
561  * End:
562  */