LCOV - code coverage report
Current view: top level - plugins/unittest - gso_test.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 6 154 3.9 %
Date: 2023-07-05 22:20:52 Functions: 7 11 63.6 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: Apache-2.0
       3             :  * Copyright(c) 2021 Cisco Systems, Inc.
       4             :  */
       5             : 
       6             : #include <vlib/vlib.h>
       7             : #include <vppinfra/time.h>
       8             : #include <vppinfra/cache.h>
       9             : #include <vppinfra/error.h>
      10             : #include <vnet/ethernet/ethernet.h>
      11             : #include <vnet/ip/ip.h>
      12             : #include <vnet/gso/gso.h>
      13             : #include <vnet/gso/hdr_offset_parser.h>
      14             : #include <vnet/tcp/tcp_packet.h>
      15             : 
      16             : #define MAX_GSO_PACKET_SIZE      (TCP_MAX_GSO_SZ - 1)
      17             : #define MIN_GSO_SEGMENT_SIZE     128
      18             : #define MAX_GSO_SEGMENT_SIZE     2048
      19             : #define DEFAULT_GSO_SEGMENT_SIZE 1448
      20             : 
      21             : typedef struct _gso_test_data
      22             : {
      23             :   const char *name;
      24             :   const char *description;
      25             :   u8 *data;
      26             :   u32 data_size;
      27             :   u32 l4_hdr_len;
      28             :   u8 is_l2;
      29             :   u8 is_ip6;
      30             :   struct _gso_test_data *next;
      31             : } gso_test_data_t;
      32             : 
      33             : typedef struct
      34             : {
      35             :   int verbose;
      36             : 
      37             :   char *gso_name;
      38             :   u32 warmup_rounds;
      39             :   u32 rounds;
      40             :   u32 n_buffers;
      41             :   u32 buffer_size;
      42             :   u32 packet_size;
      43             :   u32 gso_size;
      44             :   gso_test_data_t *gso_test_data;
      45             : } gso_test_main_t;
      46             : 
      47             : gso_test_main_t gso_test_main;
      48             : 
      49             : #define GSO_TEST_REGISTER_DATA(x, ...)                                        \
      50             :   __VA_ARGS__ gso_test_data_t __gso_test_data_##x;                            \
      51             :   static void __clib_constructor __gso_test_data_fn_##x (void)                \
      52             :   {                                                                           \
      53             :     gso_test_main_t *gtm = &gso_test_main;                                    \
      54             :     __gso_test_data_##x.next = gtm->gso_test_data;                            \
      55             :     gtm->gso_test_data = &__gso_test_data_##x;                                \
      56             :   }                                                                           \
      57             :   __VA_ARGS__ gso_test_data_t __gso_test_data_##x
      58             : 
      59             : // ipv4
      60             : u8 gso_ipv4_tcp_data[64] = {
      61             :   0x02, 0xfe, 0x39, 0xe5, 0x09, 0x8f, 0x02, 0xfe, 0x2d, 0x18, 0x63, 0x18, 0x08,
      62             :   0x00, 0x45, 0x00, 0x05, 0xdc, 0xdb, 0x42, 0x40, 0x00, 0x40, 0x06, 0xc4, 0x85,
      63             :   0xc0, 0xa8, 0x0a, 0x02, 0xc0, 0xa8, 0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34,
      64             :   0x93, 0xa8, 0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03,
      65             :   0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
      66             : };
      67             : 
      68         559 : GSO_TEST_REGISTER_DATA (gso_ipv4_tcp, static) = {
      69             :   .name = "ipv4-tcp",
      70             :   .description = "IPv4 TCP",
      71             :   .data = gso_ipv4_tcp_data,
      72             :   .data_size = sizeof (gso_ipv4_tcp_data),
      73             :   .l4_hdr_len = sizeof (tcp_header_t),
      74             :   .is_l2 = 1,
      75             :   .is_ip6 = 0,
      76             : };
      77             : 
      78             : // ipv6
      79             : u8 gso_ipv6_tcp_data[] = {
      80             :   0x02, 0xfe, 0x39, 0xe5, 0x09, 0x8f, 0x02, 0xfe, 0x2d, 0x18, 0x63, 0x18,
      81             :   0x08, 0x00, 0x60, 0x0d, 0xf4, 0x97, 0x00, 0x40, 0x06, 0x40, 0xfd, 0x01,
      82             :   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      83             :   0x10, 0x00, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      84             :   0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34, 0x93,
      85             :   0xa8, 0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03,
      86             :   0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
      87             : };
      88             : 
      89         559 : GSO_TEST_REGISTER_DATA (gso_ipv6_tcp, static) = {
      90             :   .name = "ipv6-tcp",
      91             :   .description = "IPv6 TCP",
      92             :   .data = gso_ipv6_tcp_data,
      93             :   .data_size = sizeof (gso_ipv6_tcp_data),
      94             :   .l4_hdr_len = sizeof (tcp_header_t),
      95             :   .is_l2 = 1,
      96             :   .is_ip6 = 1,
      97             : };
      98             : 
      99             : static u32
     100           0 : fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size,
     101             :               u32 n_buffers, u32 buffer_size, u32 packet_size, u32 gso_size,
     102             :               u32 l4_hdr_len)
     103             : {
     104             :   u32 i;
     105             : 
     106           0 :   for (i = 0; i < n_buffers; i++)
     107             :     {
     108           0 :       u64 seed = clib_cpu_time_now ();
     109           0 :       vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
     110           0 :       u32 len = 0;
     111           0 :       u32 remaining_data =
     112           0 :         (packet_size > buffer_size) ? (packet_size - buffer_size) : 0;
     113             : 
     114           0 :       clib_memcpy_fast (b->data, data, data_size);
     115           0 :       b->current_data = 0;
     116             : 
     117           0 :       for (u32 j = data_size; j < buffer_size; j += 8)
     118           0 :         *(u64 *) (b->data + j) = 1 + random_u64 (&seed);
     119           0 :       b->current_length = buffer_size;
     120             : 
     121           0 :       if (remaining_data)
     122             :         {
     123           0 :           vlib_buffer_t *pb = b;
     124             :           u32 n_alloc,
     125           0 :             n_bufs = ((remaining_data + buffer_size - 1) / buffer_size);
     126           0 :           u32 *buffers = 0;
     127             :           u32 fill_data_size;
     128           0 :           u32 k = 0;
     129             : 
     130           0 :           vec_validate (buffers, n_bufs - 1);
     131           0 :           n_alloc = vlib_buffer_alloc (vm, buffers, n_bufs);
     132           0 :           if (n_alloc < n_bufs)
     133             :             {
     134           0 :               vlib_buffer_free (vm, buffers, n_alloc);
     135           0 :               vlib_cli_output (
     136             :                 vm, "vlib buffer alloc failed at %u requested %u actual %u", i,
     137             :                 n_bufs, n_alloc);
     138           0 :               return i;
     139             :             }
     140             : 
     141             :           do
     142             :             {
     143           0 :               pb->next_buffer = buffers[k];
     144           0 :               pb->flags |= VLIB_BUFFER_NEXT_PRESENT;
     145           0 :               pb = vlib_get_buffer (vm, buffers[k]);
     146           0 :               pb->current_data = 0;
     147           0 :               fill_data_size = clib_min (buffer_size, remaining_data);
     148           0 :               remaining_data -= fill_data_size;
     149           0 :               for (u32 l = 0; l < fill_data_size; l += 8)
     150           0 :                 *(u64 *) (pb->data + l) = 1 + random_u64 (&seed);
     151           0 :               pb->current_length = fill_data_size;
     152           0 :               k++;
     153           0 :               len += fill_data_size;
     154             :             }
     155           0 :           while (k < n_bufs);
     156           0 :           b->flags |= VNET_BUFFER_F_GSO;
     157           0 :           vnet_buffer2 (b)->gso_size = gso_size;
     158           0 :           vnet_buffer2 (b)->gso_l4_hdr_sz = l4_hdr_len;
     159             :         }
     160           0 :       b->total_length_not_including_first_buffer = len;
     161           0 :       b->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
     162             :     }
     163           0 :   return i;
     164             : }
     165             : 
     166             : static_always_inline u32
     167           0 : gso_segment_buffer_test (vlib_main_t *vm, u32 bi,
     168             :                          vnet_interface_per_thread_data_t *ptd, u8 is_l2,
     169             :                          u8 is_ip6)
     170             : {
     171           0 :   vlib_buffer_t *b = vlib_get_buffer (vm, bi);
     172           0 :   generic_header_offset_t gho = { 0 };
     173           0 :   u32 n_tx_bytes = 0;
     174             : 
     175           0 :   if (PREDICT_TRUE (b->flags & VNET_BUFFER_F_GSO))
     176             :     {
     177           0 :       vnet_generic_header_offset_parser (b, &gho, is_l2, !is_ip6, is_ip6);
     178           0 :       n_tx_bytes = gso_segment_buffer_inline (vm, ptd, b, &gho, is_l2, is_ip6);
     179             :     }
     180             : 
     181           0 :   return n_tx_bytes;
     182             : }
     183             : 
     184             : static clib_error_t *
     185           0 : test_gso_perf (vlib_main_t *vm, gso_test_main_t *gtm)
     186             : {
     187           0 :   clib_error_t *err = 0;
     188           0 :   vnet_interface_per_thread_data_t *ptd = 0;
     189           0 :   u32 packet_size = MAX_GSO_PACKET_SIZE;
     190           0 :   u32 buffer_size = vlib_buffer_get_default_data_size (vm);
     191             :   u32 gso_size;
     192             :   u32 n_buffers, warmup_rounds, rounds;
     193           0 :   u32 *buffer_indices = 0;
     194           0 :   u64 t0, t1, t2[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
     195           0 :   gso_test_data_t *gso_test_data = gtm->gso_test_data;
     196             :   int i, j, k;
     197             : 
     198           0 :   if (gtm->buffer_size > buffer_size)
     199           0 :     return clib_error_return (0, "buffer size must be <= %u", buffer_size);
     200             : 
     201           0 :   if (gtm->packet_size > packet_size)
     202           0 :     return clib_error_return (0, "gso packet size must be <= %u", packet_size);
     203             : 
     204           0 :   if ((gtm->gso_size > MAX_GSO_SEGMENT_SIZE) ||
     205           0 :       (gtm->gso_size < MIN_GSO_SEGMENT_SIZE))
     206           0 :     return clib_error_return (
     207             :       0, "gso segment size must be in between %u >= and <= %u",
     208             :       MIN_GSO_SEGMENT_SIZE, MAX_GSO_SEGMENT_SIZE);
     209             : 
     210           0 :   rounds = gtm->rounds ? gtm->rounds : 256;
     211           0 :   n_buffers = gtm->n_buffers ? gtm->n_buffers : 256;
     212           0 :   warmup_rounds = gtm->warmup_rounds ? gtm->warmup_rounds : 256;
     213           0 :   buffer_size = gtm->buffer_size ? gtm->buffer_size : buffer_size;
     214           0 :   gso_size = gtm->gso_size;
     215           0 :   packet_size = gtm->packet_size ? gtm->packet_size : packet_size;
     216             : 
     217           0 :   vec_validate_aligned (ptd, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
     218           0 :   vec_validate_aligned (buffer_indices, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
     219             : 
     220           0 :   vlib_cli_output (vm,
     221             :                    "GSO Segmentation: packet-size %u gso-size %u buffer-size "
     222             :                    "%u n_buffers %u rounds %u "
     223             :                    "warmup-rounds %u",
     224             :                    packet_size, gso_size, buffer_size, n_buffers, rounds,
     225             :                    warmup_rounds);
     226           0 :   vlib_cli_output (vm, "   cpu-freq %.2f GHz",
     227           0 :                    (f64) vm->clib_time.clocks_per_second * 1e-9);
     228             : 
     229           0 :   while (gso_test_data)
     230             :     {
     231           0 :       u32 n_filled = 0;
     232           0 :       u32 n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_buffers);
     233           0 :       if (n_alloc != n_buffers)
     234             :         {
     235           0 :           vlib_cli_output (vm, " Test: %s FAILED", gso_test_data->description);
     236           0 :           err = clib_error_return (0, "buffer alloc failure");
     237           0 :           vlib_buffer_free (vm, buffer_indices, n_alloc);
     238           0 :           goto done;
     239             :         }
     240             :       n_filled =
     241           0 :         fill_buffers (vm, buffer_indices, gso_test_data->data,
     242             :                       gso_test_data->data_size, n_buffers, buffer_size,
     243             :                       packet_size, gso_size, gso_test_data->l4_hdr_len);
     244             : 
     245           0 :       u8 is_l2 = gso_test_data->is_l2;
     246           0 :       u8 is_ip6 = gso_test_data->is_ip6;
     247             : 
     248           0 :       for (k = 0; k < warmup_rounds; k++)
     249             :         {
     250           0 :           for (j = 0; j < n_filled; j++)
     251           0 :             gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], is_l2,
     252             :                                      is_ip6);
     253           0 :           for (j = 0; j < n_filled; j++)
     254             :             {
     255           0 :               vlib_buffer_free (vm, ptd[j].split_buffers,
     256           0 :                                 vec_len (ptd[j].split_buffers));
     257           0 :               vec_free (ptd[j].split_buffers);
     258             :             }
     259             :         }
     260             : 
     261           0 :       for (i = 0; i < 10; i++)
     262             :         {
     263           0 :           for (k = 0; k < rounds; k++)
     264             :             {
     265           0 :               t0 = clib_cpu_time_now ();
     266           0 :               for (j = 0; j < n_filled; j++)
     267           0 :                 gso_segment_buffer_test (vm, buffer_indices[j], &ptd[j], is_l2,
     268             :                                          is_ip6);
     269           0 :               t1 = clib_cpu_time_now ();
     270           0 :               t2[i] += (t1 - t0);
     271           0 :               for (j = 0; j < n_filled; j++)
     272             :                 {
     273           0 :                   vlib_buffer_free (vm, ptd[j].split_buffers,
     274           0 :                                     vec_len (ptd[j].split_buffers));
     275           0 :                   vec_free (ptd[j].split_buffers);
     276             :                 }
     277             :             }
     278             :         }
     279             : 
     280           0 :       vlib_cli_output (
     281             :         vm, "===========================================================");
     282           0 :       vlib_cli_output (vm, " Test: %s", gso_test_data->description);
     283           0 :       vlib_cli_output (
     284             :         vm, "===========================================================");
     285           0 :       for (i = 0; i < 10; i++)
     286             :         {
     287             :           // ticks per packet
     288           0 :           f64 tpp1 = (f64) (t2[i]) / (n_filled * rounds);
     289             :           // ticks per Byte
     290           0 :           f64 tpB1 = (f64) (t2[i]) / (n_filled * rounds * packet_size);
     291             :           // Packets per second
     292           0 :           f64 Kpps1 = vm->clib_time.clocks_per_second * 1e-3 / tpp1;
     293             :           // Throughput Giga-bits per second
     294           0 :           f64 Gbps1 = vm->clib_time.clocks_per_second * 8 * 1e-9 / tpB1;
     295             : 
     296           0 :           vlib_cli_output (
     297             :             vm, "%-2u: %.03f ticks/packet, %.02f Kpps, %.02f Gbps\n", i + 1,
     298             :             tpp1, Kpps1, Gbps1);
     299             :         }
     300           0 :       if (n_alloc)
     301           0 :         vlib_buffer_free (vm, buffer_indices, n_alloc);
     302           0 :       clib_memset (t2, 0, sizeof (t2));
     303           0 :       gso_test_data = gso_test_data->next;
     304             :     }
     305             : 
     306           0 : done:
     307             : 
     308           0 :   vec_free (ptd);
     309           0 :   vec_free (buffer_indices);
     310           0 :   return err;
     311             : }
     312             : 
     313             : static clib_error_t *
     314           0 : test_gso_command_fn (vlib_main_t *vm, unformat_input_t *input,
     315             :                      vlib_cli_command_t *cmd)
     316             : {
     317           0 :   gso_test_main_t *gtm = &gso_test_main;
     318           0 :   clib_error_t *err = 0;
     319             :   f64 end, start, total_time;
     320             : 
     321           0 :   gtm->gso_size = DEFAULT_GSO_SEGMENT_SIZE;
     322           0 :   gtm->warmup_rounds = 0;
     323           0 :   gtm->rounds = 0;
     324           0 :   gtm->n_buffers = 0;
     325           0 :   gtm->buffer_size = 0;
     326           0 :   gtm->packet_size = 0;
     327             : 
     328           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     329             :     {
     330           0 :       if (unformat (input, "verbose"))
     331           0 :         gtm->verbose = 1;
     332           0 :       else if (unformat (input, "detail"))
     333           0 :         gtm->verbose = 2;
     334           0 :       else if (unformat (input, "buffers %u", &gtm->n_buffers))
     335             :         ;
     336           0 :       else if (unformat (input, "buffer-size %u", &gtm->buffer_size))
     337             :         ;
     338           0 :       else if (unformat (input, "packet-size %u", &gtm->packet_size))
     339             :         ;
     340           0 :       else if (unformat (input, "gso-size %u", &gtm->gso_size))
     341             :         ;
     342           0 :       else if (unformat (input, "rounds %u", &gtm->rounds))
     343             :         ;
     344           0 :       else if (unformat (input, "warmup-rounds %u", &gtm->warmup_rounds))
     345             :         ;
     346             :       else
     347             :         {
     348           0 :           return clib_error_return (0, "unknown input '%U'",
     349             :                                     format_unformat_error, input);
     350             :         }
     351             :     }
     352             : 
     353           0 :   start = clib_cpu_time_now ();
     354           0 :   err = test_gso_perf (vm, gtm);
     355           0 :   end = clib_cpu_time_now ();
     356             : 
     357           0 :   total_time = (f64) (end - start) / vm->clib_time.clocks_per_second;
     358           0 :   vlib_cli_output (vm, "Total Time Test Took %.02f seconds", total_time);
     359             : 
     360           0 :   return err;
     361             : }
     362             : 
     363       16239 : VLIB_CLI_COMMAND (test_gso_command, static) = {
     364             :   .path = "test gso",
     365             :   .short_help = "test gso [buffers <n>] [buffer-size <size>] [packet-size "
     366             :                 "<size>] [gso-size <size>] [rounds <n>] "
     367             :                 "[warmup-rounds <n>]",
     368             :   .function = test_gso_command_fn,
     369             : };
     370             : 
     371             : static clib_error_t *
     372         559 : gso_test_init (vlib_main_t *vm)
     373             : {
     374         559 :   return (0);
     375             : }
     376             : 
     377        2799 : VLIB_INIT_FUNCTION (gso_test_init);

Generated by: LCOV version 1.14