devices: add support for pseudo header checksum
[vpp.git] / src / plugins / unittest / ip_psh_cksum_test.c
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/ip/ip_psh_cksum.h>
13
14 static_always_inline void
15 compute_ip_phc (void *p)
16 {
17   if ((((u8 *) p)[0] & 0xf0) == 0x40)
18     ip4_pseudo_header_cksum (p);
19   else if ((((u8 *) p)[0] & 0xf0) == 0x60)
20     ip6_pseudo_header_cksum (p);
21 }
22
23 void
24 compute_ip_phc_func (void **p, u32 n_packets)
25 {
26   u32 n_left_from = n_packets;
27
28   while (n_left_from >= 8)
29     {
30       clib_prefetch_load (p[4]);
31       clib_prefetch_load (p[5]);
32       clib_prefetch_load (p[6]);
33       clib_prefetch_load (p[7]);
34
35       compute_ip_phc (p[0]);
36       compute_ip_phc (p[1]);
37       compute_ip_phc (p[2]);
38       compute_ip_phc (p[3]);
39
40       n_left_from -= 4;
41       p += 4;
42     }
43
44   while (n_left_from > 0)
45     {
46       compute_ip_phc (p[0]);
47
48       n_left_from -= 1;
49       p += 1;
50     }
51 }
52
53 typedef struct _phc_test_data
54 {
55   const char *name;
56   const char *description;
57   u8 *data;
58   u32 data_size;
59   struct _phc_test_data *next;
60 } phc_test_data_t;
61
62 typedef struct
63 {
64   int verbose;
65
66   char *phc_name;
67   u32 warmup_rounds;
68   u32 rounds;
69   u32 n_buffers;
70   u32 buffer_size;
71   phc_test_data_t *phc_test_data;
72 } phc_test_main_t;
73
74 phc_test_main_t phc_test_main;
75
76 #define PHC_TEST_REGISTER_DATA(x, ...)                                        \
77   __VA_ARGS__ phc_test_data_t __phc_test_data_##x;                            \
78   static void __clib_constructor __phc_test_data_fn_##x (void)                \
79   {                                                                           \
80     phc_test_main_t *ptm = &phc_test_main;                                    \
81     __phc_test_data_##x.next = ptm->phc_test_data;                            \
82     ptm->phc_test_data = &__phc_test_data_##x;                                \
83   }                                                                           \
84   __VA_ARGS__ phc_test_data_t __phc_test_data_##x
85
86 // ipv4
87 u8 phc_ipv4_tcp_data[50] = {
88   0x45, 0x00, 0x05, 0xdc, 0xdb, 0x42, 0x40, 0x00, 0x40, 0x06, 0xc4, 0x85, 0xc0,
89   0xa8, 0x0a, 0x02, 0xc0, 0xa8, 0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34, 0x93,
90   0xa8, 0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03, 0x00,
91   0x00, 0x01, 0x01, 0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
92 };
93
94 PHC_TEST_REGISTER_DATA (ipv4_tcp, static) = {
95   .name = "ipv4-tcp",
96   .description = "IPv4 TCP",
97   .data = phc_ipv4_tcp_data,
98   .data_size = sizeof (phc_ipv4_tcp_data),
99 };
100
101 // ip6
102 u8 phc_ipv6_udp_data[65] = {
103   0x60, 0x0d, 0xf4, 0x97, 0x00, 0x40, 0x3a, 0x40, 0xfd, 0x01, 0x00, 0x00, 0x00,
104   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfd, 0x01,
105   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
106   0x01, 0x80, 0x00, 0x10, 0x84, 0xb1, 0x25, 0x00, 0x01, 0x22, 0x57, 0xf0, 0x60,
107   0x00, 0x00, 0x00, 0x00, 0xcb, 0x4a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
108 };
109
110 PHC_TEST_REGISTER_DATA (ipv6_udp, static) = {
111   .name = "ipv6-udp",
112   .description = "IPv6 UDP",
113   .data = phc_ipv6_udp_data,
114   .data_size = sizeof (phc_ipv6_udp_data),
115 };
116
117 static void
118 fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size,
119               u32 n_buffers, u32 buffer_size)
120 {
121   int i, j;
122   u64 seed = clib_cpu_time_now ();
123   for (i = 0; i < n_buffers; i++)
124     {
125       vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
126       clib_memcpy_fast (b->data, data, data_size);
127       b->current_data = 0;
128       for (j = data_size; j < buffer_size; j += 8)
129         *(u64 *) (b->data + j) = 1 + random_u64 (&seed);
130       b->current_length = buffer_size;
131     }
132 }
133
134 static clib_error_t *
135 test_phc_perf (vlib_main_t *vm, phc_test_main_t *ptm)
136 {
137   clib_error_t *err = 0;
138   u32 buffer_size = vlib_buffer_get_default_data_size (vm);
139   u32 n_buffers, n_alloc = 0, warmup_rounds, rounds;
140   u32 *buffer_indices = 0;
141   u64 t0[5], t1[5];
142   phc_test_data_t *phc_test_data = ptm->phc_test_data;
143   void **p = 0;
144   int i, j;
145
146   if (ptm->buffer_size > buffer_size)
147     return clib_error_return (0, "buffer size must be <= %u", buffer_size);
148
149   rounds = ptm->rounds ? ptm->rounds : 100;
150   n_buffers = ptm->n_buffers ? ptm->n_buffers : 256;
151   warmup_rounds = ptm->warmup_rounds ? ptm->warmup_rounds : 100;
152   buffer_size = ptm->buffer_size ? ptm->buffer_size : buffer_size;
153
154   vec_validate_aligned (p, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
155   vec_validate_aligned (buffer_indices, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
156   n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_buffers);
157   if (n_alloc != n_buffers)
158     {
159       err = clib_error_return (0, "buffer alloc failure");
160       goto done;
161     }
162
163   vlib_cli_output (
164     vm,
165     "pseudo header checksum: buffer-size %u, n_buffers %u rounds %u "
166     "warmup-rounds %u",
167     buffer_size, n_buffers, rounds, warmup_rounds);
168   vlib_cli_output (vm, "   cpu-freq %.2f GHz",
169                    (f64) vm->clib_time.clocks_per_second * 1e-9);
170
171   while (phc_test_data)
172     {
173       fill_buffers (vm, buffer_indices, phc_test_data->data,
174                     phc_test_data->data_size, n_buffers, buffer_size);
175
176       for (i = 0; i < n_buffers; i++)
177         {
178           vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
179           p[i] = vlib_buffer_get_current (b);
180         }
181
182       for (i = 0; i < 5; i++)
183         {
184           for (j = 0; j < warmup_rounds; j++)
185             {
186               compute_ip_phc_func (p, n_buffers);
187             }
188
189           t0[i] = clib_cpu_time_now ();
190           for (j = 0; j < rounds; j++)
191             compute_ip_phc_func (p, n_buffers);
192           t1[i] = clib_cpu_time_now ();
193         }
194
195       vlib_cli_output (
196         vm, "===========================================================");
197       vlib_cli_output (vm, " Test: %s", phc_test_data->description);
198       vlib_cli_output (
199         vm, "===========================================================");
200       for (i = 0; i < 5; i++)
201         {
202           f64 tpp1 = (f64) (t1[i] - t0[i]) / (n_buffers * rounds);
203           f64 Mpps1 = vm->clib_time.clocks_per_second * 1e-6 / tpp1;
204
205           vlib_cli_output (vm, "%-2u: %.03f ticks/packet, %.02f Mpps\n", i + 1,
206                            tpp1, Mpps1);
207         }
208       phc_test_data = phc_test_data->next;
209     }
210
211 done:
212   if (n_alloc)
213     vlib_buffer_free (vm, buffer_indices, n_alloc);
214
215   vec_free (p);
216   vec_free (buffer_indices);
217   return err;
218 }
219
220 static clib_error_t *
221 test_phc_command_fn (vlib_main_t *vm, unformat_input_t *input,
222                      vlib_cli_command_t *cmd)
223 {
224   phc_test_main_t *ptm = &phc_test_main;
225   clib_error_t *err = 0;
226
227   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
228     {
229       if (unformat (input, "verbose"))
230         ptm->verbose = 1;
231       else if (unformat (input, "detail"))
232         ptm->verbose = 2;
233       else if (unformat (input, "buffers %u", &ptm->n_buffers))
234         ;
235       else if (unformat (input, "buffer-size %u", &ptm->buffer_size))
236         ;
237       else if (unformat (input, "rounds %u", &ptm->rounds))
238         ;
239       else if (unformat (input, "warmup-rounds %u", &ptm->warmup_rounds))
240         ;
241       else
242         {
243           return clib_error_return (0, "unknown input '%U'",
244                                     format_unformat_error, input);
245         }
246     }
247
248   test_phc_perf (vm, ptm);
249
250   return err;
251 }
252
253 VLIB_CLI_COMMAND (test_phc_command, static) = {
254   .path = "test phc",
255   .short_help = "test phc [buffers <n>] [buffer-size <size>] [rounds <n>] "
256                 "[warmup-rounds <n>]",
257   .function = test_phc_command_fn,
258 };
259
260 static clib_error_t *
261 phc_test_init (vlib_main_t *vm)
262 {
263   return (0);
264 }
265
266 VLIB_INIT_FUNCTION (phc_test_init);