hash: add support for hashing infra
[vpp.git] / src / plugins / unittest / hash_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/hash/hash.h>
11 #include <vnet/ethernet/ethernet.h>
12
13 #define HASH_TEST_DATA_SIZE 2048
14
15 typedef struct _hash_test_data
16 {
17   const char *name;
18   const char *description;
19   u8 *data;
20   u32 data_size;
21   vnet_hash_fn_type_t ftype;
22   struct _hash_test_data *next;
23 } hash_test_data_t;
24
25 typedef struct
26 {
27   int verbose;
28
29   char *hash_name;
30   u32 warmup_rounds;
31   u32 rounds;
32   u32 n_buffers;
33
34   hash_test_data_t *hash_test_data;
35 } hash_test_main_t;
36
37 hash_test_main_t hash_test_main;
38
39 #define HASH_TEST_REGISTER_DATA(x, ...)                                       \
40   __VA_ARGS__ hash_test_data_t __hash_test_data_##x;                          \
41   static void __clib_constructor __hash_test_data_fn_##x (void)               \
42   {                                                                           \
43     hash_test_main_t *htm = &hash_test_main;                                  \
44     __hash_test_data_##x.next = htm->hash_test_data;                          \
45     htm->hash_test_data = &__hash_test_data_##x;                              \
46   }                                                                           \
47   __VA_ARGS__ hash_test_data_t __hash_test_data_##x
48
49 // qinq
50 u8 eth_qinq_ipv4_tcp_data[72] = {
51   0x02, 0xfe, 0x39, 0xe5, 0x09, 0x8f, 0x02, 0xfe, 0x2d, 0x18, 0x63, 0x18,
52   0x88, 0xa8, 0x03, 0xe8, 0x81, 0x00, 0x03, 0xe8, 0x08, 0x00, 0x45, 0x00,
53   0x05, 0xdc, 0xdb, 0x42, 0x40, 0x00, 0x40, 0x06, 0xc4, 0x85, 0xc0, 0xa8,
54   0x0a, 0x02, 0xc0, 0xa8, 0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34, 0x93,
55   0xa8, 0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03,
56   0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
57 };
58
59 HASH_TEST_REGISTER_DATA (eth_qinq_ipv4_tcp, static) = {
60   .name = "eth-qinq-ipv4-tcp",
61   .description = "Ethernet QinQ IPv4 TCP",
62   .data = eth_qinq_ipv4_tcp_data,
63   .data_size = sizeof (eth_qinq_ipv4_tcp_data),
64   .ftype = VNET_HASH_FN_TYPE_ETHERNET,
65 };
66
67 // vlan
68 u8 eth_vlan_ipv4_tcp_data[68] = {
69   0x02, 0xfe, 0x39, 0xe5, 0x09, 0x8f, 0x02, 0xfe, 0x2d, 0x18, 0x63, 0x18,
70   0x81, 0x00, 0x03, 0xe8, 0x08, 0x00, 0x45, 0x00, 0x05, 0xdc, 0xdb, 0x42,
71   0x40, 0x00, 0x40, 0x06, 0xc4, 0x85, 0xc0, 0xa8, 0x0a, 0x02, 0xc0, 0xa8,
72   0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34, 0x93, 0xa8, 0x1b, 0x7b, 0xef,
73   0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03, 0x00, 0x00, 0x01, 0x01,
74   0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
75 };
76
77 HASH_TEST_REGISTER_DATA (eth_vlan_ipv4_tcp, static) = {
78   .name = "eth-vlan-ipv4-tcp",
79   .description = "Ethernet Vlan IPv4 TCP",
80   .data = eth_vlan_ipv4_tcp_data,
81   .data_size = sizeof (eth_vlan_ipv4_tcp_data),
82   .ftype = VNET_HASH_FN_TYPE_ETHERNET,
83 };
84
85 // ethernet
86 u8 eth_ipv4_tcp_data[64] = {
87   0x02, 0xfe, 0x39, 0xe5, 0x09, 0x8f, 0x02, 0xfe, 0x2d, 0x18, 0x63, 0x18, 0x08,
88   0x00, 0x45, 0x00, 0x05, 0xdc, 0xdb, 0x42, 0x40, 0x00, 0x40, 0x06, 0xc4, 0x85,
89   0xc0, 0xa8, 0x0a, 0x02, 0xc0, 0xa8, 0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34,
90   0x93, 0xa8, 0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5, 0xc7, 0x03,
91   0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xce, 0xaa, 0x00, 0x2f, 0xf2, 0xc3
92 };
93
94 HASH_TEST_REGISTER_DATA (eth_ipv4_tcp, static) = {
95   .name = "eth-ipv4-tcp",
96   .description = "Ethernet IPv4 TCP",
97   .data = eth_ipv4_tcp_data,
98   .data_size = sizeof (eth_ipv4_tcp_data),
99   .ftype = VNET_HASH_FN_TYPE_ETHERNET,
100 };
101
102 // udp
103 u8 eth_ipv4_udp_data[42] = { 0x62, 0x36, 0xbe, 0xff, 0x91, 0x20, 0x5e,
104                              0x2c, 0xaf, 0x2e, 0x1e, 0x51, 0x08, 0x00,
105                              0x45, 0x00, 0x05, 0xc4, 0x9d, 0xc3, 0x40,
106                              0x00, 0x33, 0x11, 0x49, 0x61, 0x3e, 0xd2,
107                              0x12, 0x28, 0x0a, 0x09, 0x00, 0x02, 0x14,
108                              0x58, 0xc0, 0xd8, 0x05, 0xb0, 0x75, 0xbd };
109
110 HASH_TEST_REGISTER_DATA (eth_ipv4_udp, static) = {
111   .name = "eth-ipv4-udp",
112   .description = "Ethernet IPv4 UDP",
113   .data = eth_ipv4_udp_data,
114   .data_size = sizeof (eth_ipv4_udp_data),
115   .ftype = VNET_HASH_FN_TYPE_ETHERNET,
116 };
117
118 // ipv4
119 u8 ipv4_tcp_data[50] = { 0x45, 0x00, 0x05, 0xdc, 0xdb, 0x42, 0x40, 0x00, 0x40,
120                          0x06, 0xc4, 0x85, 0xc0, 0xa8, 0x0a, 0x02, 0xc0, 0xa8,
121                          0x0a, 0x01, 0xd8, 0xde, 0x14, 0x51, 0x34, 0x93, 0xa8,
122                          0x1b, 0x7b, 0xef, 0x2e, 0x7e, 0x80, 0x10, 0x00, 0xe5,
123                          0xc7, 0x03, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0xce,
124                          0xaa, 0x00, 0x2f, 0xf2, 0xc3 };
125
126 HASH_TEST_REGISTER_DATA (ipv4_tcp, static) = {
127   .name = "ipv4-tcp",
128   .description = "IPv4 TCP",
129   .data = ipv4_tcp_data,
130   .data_size = sizeof (ipv4_tcp_data),
131   .ftype = VNET_HASH_FN_TYPE_IP,
132 };
133
134 u8 ipv4_icmp_data[84] = {
135   0x45, 0x00, 0x00, 0x54, 0xb7, 0xe6, 0x40, 0x00, 0x40, 0x01, 0xed, 0x6e,
136   0xc0, 0xa8, 0x0a, 0x01, 0xc0, 0xa8, 0x0a, 0x02, 0x08, 0x00, 0xc7, 0x84,
137   0x00, 0x16, 0x00, 0x92, 0xfd, 0xdb, 0xd9, 0x60, 0x00, 0x00, 0x00, 0x00,
138   0x91, 0xc3, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13,
139   0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
140   0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
141   0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
142
143 };
144
145 HASH_TEST_REGISTER_DATA (ipv4_icmp, static) = {
146   .name = "ipv4-icmp",
147   .description = "IPv4 ICMP",
148   .data = ipv4_icmp_data,
149   .data_size = sizeof (ipv4_icmp_data),
150   .ftype = VNET_HASH_FN_TYPE_IP,
151 };
152
153 // ip6
154 u8 ipv6_icmp6_data[104] = {
155   0x60, 0x0d, 0xf4, 0x97, 0x00, 0x40, 0x3a, 0x40, 0xfd, 0x01, 0x00, 0x00, 0x00,
156   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xfd, 0x01,
157   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
158   0x01, 0x80, 0x00, 0x10, 0x84, 0xb1, 0x25, 0x00, 0x01, 0x22, 0x57, 0xf0, 0x60,
159   0x00, 0x00, 0x00, 0x00, 0xcb, 0x4a, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
160   0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
161   0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
162   0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37
163 };
164
165 HASH_TEST_REGISTER_DATA (ipv6_icmp6, static) = {
166   .name = "ipv6-icmp6",
167   .description = "IPv6 ICMP6",
168   .data = ipv6_icmp6_data,
169   .data_size = sizeof (ipv6_icmp6_data),
170   .ftype = VNET_HASH_FN_TYPE_IP,
171 };
172
173 void
174 fill_buffers (vlib_main_t *vm, u32 *buffer_indices, u8 *data, u32 data_size,
175               u32 n_buffers)
176 {
177   int i, j;
178   u64 seed = clib_cpu_time_now ();
179   for (i = 0; i < n_buffers; i++)
180     {
181       vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
182       clib_memcpy_fast (b->data, data, data_size);
183       b->current_data = 0;
184       for (j = data_size; j < HASH_TEST_DATA_SIZE; j += 8)
185         *(u64 *) (b->data + j) = 1 + random_u64 (&seed);
186       b->current_length = HASH_TEST_DATA_SIZE;
187     }
188 }
189
190 static clib_error_t *
191 test_hash_perf (vlib_main_t *vm, hash_test_main_t *htm)
192 {
193   clib_error_t *err = 0;
194   u32 n_buffers, n_alloc = 0, warmup_rounds, rounds;
195   u32 *buffer_indices = 0;
196   u64 t0[5], t1[5];
197   vnet_hash_fn_t hf;
198   hash_test_data_t *hash_test_data = htm->hash_test_data;
199   void **p = 0;
200   int i, j;
201
202   rounds = htm->rounds ? htm->rounds : 100;
203   n_buffers = htm->n_buffers ? htm->n_buffers : 256;
204   warmup_rounds = htm->warmup_rounds ? htm->warmup_rounds : 100;
205
206   vec_validate_aligned (p, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
207   vec_validate_aligned (buffer_indices, n_buffers - 1, CLIB_CACHE_LINE_BYTES);
208   n_alloc = vlib_buffer_alloc (vm, buffer_indices, n_buffers);
209   if (n_alloc != n_buffers)
210     {
211       err = clib_error_return (0, "buffer alloc failure");
212       goto done;
213     }
214
215   vlib_cli_output (vm,
216                    "%s: n_buffers %u rounds %u "
217                    "warmup-rounds %u",
218                    htm->hash_name, n_buffers, rounds, warmup_rounds);
219   vlib_cli_output (vm, "   cpu-freq %.2f GHz",
220                    (f64) vm->clib_time.clocks_per_second * 1e-9);
221
222   while (hash_test_data)
223     {
224       fill_buffers (vm, buffer_indices, hash_test_data->data,
225                     hash_test_data->data_size, n_buffers);
226
227       for (i = 0; i < n_buffers; i++)
228         {
229           vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
230           p[i] = vlib_buffer_get_current (b);
231         }
232
233       hf =
234         vnet_hash_function_from_name (htm->hash_name, hash_test_data->ftype);
235
236       if (!hf)
237         {
238           err = clib_error_return (0, "wrong hash name");
239           goto done;
240         }
241
242       for (i = 0; i < 5; i++)
243         {
244           u32 h[n_buffers];
245           for (j = 0; j < warmup_rounds; j++)
246             {
247               hf (p, h, n_buffers);
248             }
249
250           t0[i] = clib_cpu_time_now ();
251           for (j = 0; j < rounds; j++)
252             hf (p, h, n_buffers);
253           t1[i] = clib_cpu_time_now ();
254         }
255
256       vlib_cli_output (
257         vm, "===========================================================");
258       vlib_cli_output (vm, " Test: %s", hash_test_data->description);
259       vlib_cli_output (
260         vm, "===========================================================");
261       for (i = 0; i < 5; i++)
262         {
263           f64 tpp1 = (f64) (t1[i] - t0[i]) / (n_buffers * rounds);
264           f64 Mpps1 = vm->clib_time.clocks_per_second * 1e-6 / tpp1;
265
266           vlib_cli_output (vm, "%-2u: %.03f ticks/packet, %.02f Mpps\n", i + 1,
267                            tpp1, Mpps1);
268         }
269       hash_test_data = hash_test_data->next;
270     }
271
272 done:
273   if (n_alloc)
274     vlib_buffer_free (vm, buffer_indices, n_alloc);
275
276   vec_free (p);
277   vec_free (buffer_indices);
278   return err;
279 }
280
281 static clib_error_t *
282 test_hash_command_fn (vlib_main_t *vm, unformat_input_t *input,
283                       vlib_cli_command_t *cmd)
284 {
285   hash_test_main_t *tm = &hash_test_main;
286   clib_error_t *err = 0;
287
288   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
289     {
290       if (unformat (input, "verbose"))
291         tm->verbose = 1;
292       else if (unformat (input, "detail"))
293         tm->verbose = 2;
294       else if (unformat (input, "perf %s", &tm->hash_name))
295         ;
296       else if (unformat (input, "buffers %u", &tm->n_buffers))
297         ;
298       else if (unformat (input, "rounds %u", &tm->rounds))
299         ;
300       else if (unformat (input, "warmup-rounds %u", &tm->warmup_rounds))
301         ;
302       else
303         {
304           err = clib_error_return (0, "unknown input '%U'",
305                                    format_unformat_error, input);
306           goto error;
307         }
308     }
309
310   err = test_hash_perf (vm, tm);
311
312 error:
313   vec_free (tm->hash_name);
314
315   return err;
316 }
317
318 VLIB_CLI_COMMAND (test_hash_command, static) = {
319   .path = "test hash",
320   .short_help = "test hash [perf <hash-name>] [buffers <n>] [rounds <n>] "
321                 "[warmup-rounds <n>]",
322   .function = test_hash_command_fn,
323 };
324
325 static clib_error_t *
326 hash_test_init (vlib_main_t *vm)
327 {
328   return (0);
329 }
330
331 VLIB_INIT_FUNCTION (hash_test_init);