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