1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2015-2019 Vladimir Medvedkin <medvedkinv@gmail.com>
5 #include <rte_common.h>
8 #include <rte_random.h>
12 #include <rte_thash.h>
14 #define HASH_MSK(reta_sz) ((1 << reta_sz) - 1)
15 #define TUPLE_SZ (RTE_THASH_V4_L4_LEN * 4)
17 struct test_thash_v4 {
26 struct test_thash_v6 {
35 /*From 82599 Datasheet 7.1.2.8.3 RSS Verification Suite*/
36 struct test_thash_v4 v4_tbl[] = {
37 {RTE_IPV4(161, 142, 100, 80), RTE_IPV4(66, 9, 149, 187),
38 1766, 2794, 0x323e8fc2, 0x51ccc178},
39 {RTE_IPV4(65, 69, 140, 83), RTE_IPV4(199, 92, 111, 2),
40 4739, 14230, 0xd718262a, 0xc626b0ea},
41 {RTE_IPV4(12, 22, 207, 184), RTE_IPV4(24, 19, 198, 95),
42 38024, 12898, 0xd2d0a5de, 0x5c2b394a},
43 {RTE_IPV4(209, 142, 163, 6), RTE_IPV4(38, 27, 205, 30),
44 2217, 48228, 0x82989176, 0xafc7327f},
45 {RTE_IPV4(202, 188, 127, 2), RTE_IPV4(153, 39, 163, 191),
46 1303, 44251, 0x5d1809c5, 0x10e828a2},
49 struct test_thash_v6 v6_tbl[] = {
50 /*3ffe:2501:200:3::1*/
51 {{0x3f, 0xfe, 0x25, 0x01, 0x02, 0x00, 0x00, 0x03,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,},
53 /*3ffe:2501:200:1fff::7*/
54 {0x3f, 0xfe, 0x25, 0x01, 0x02, 0x00, 0x1f, 0xff,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,},
56 1766, 2794, 0x2cc18cd5, 0x40207d3d},
58 {{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,},
60 /*3ffe:501:8::260:97ff:fe40:efab*/
61 {0x3f, 0xfe, 0x05, 0x01, 0x00, 0x08, 0x00, 0x00,
62 0x02, 0x60, 0x97, 0xff, 0xfe, 0x40, 0xef, 0xab,},
63 4739, 14230, 0x0f0c461c, 0xdde51bbf},
64 /*fe80::200:f8ff:fe21:67cf*/
65 {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x02, 0x00, 0xf8, 0xff, 0xfe, 0x21, 0x67, 0xcf,},
67 /*3ffe:1900:4545:3:200:f8ff:fe21:67cf*/
68 {0x3f, 0xfe, 0x19, 0x00, 0x45, 0x45, 0x00, 0x03,
69 0x02, 0x00, 0xf8, 0xff, 0xfe, 0x21, 0x67, 0xcf,},
70 38024, 44251, 0x4b61e985, 0x02d1feef},
73 uint8_t default_rss_key[] = {
74 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
75 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
76 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
77 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
78 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
82 test_toeplitz_hash_calc(void)
85 union rte_thash_tuple tuple;
86 uint32_t rss_l3, rss_l3l4;
87 uint8_t rss_key_be[RTE_DIM(default_rss_key)];
88 struct rte_ipv6_hdr ipv6_hdr;
91 rte_convert_rss_key((uint32_t *)&default_rss_key,
92 (uint32_t *)rss_key_be, RTE_DIM(default_rss_key));
95 for (i = 0; i < RTE_DIM(v4_tbl); i++) {
96 tuple.v4.src_addr = v4_tbl[i].src_ip;
97 tuple.v4.dst_addr = v4_tbl[i].dst_ip;
98 tuple.v4.sport = v4_tbl[i].src_port;
99 tuple.v4.dport = v4_tbl[i].dst_port;
100 /*Calculate hash with original key*/
101 rss_l3 = rte_softrss((uint32_t *)&tuple,
102 RTE_THASH_V4_L3_LEN, default_rss_key);
103 rss_l3l4 = rte_softrss((uint32_t *)&tuple,
104 RTE_THASH_V4_L4_LEN, default_rss_key);
105 if ((rss_l3 != v4_tbl[i].hash_l3) ||
106 (rss_l3l4 != v4_tbl[i].hash_l3l4))
108 /*Calculate hash with converted key*/
109 rss_l3 = rte_softrss_be((uint32_t *)&tuple,
110 RTE_THASH_V4_L3_LEN, rss_key_be);
111 rss_l3l4 = rte_softrss_be((uint32_t *)&tuple,
112 RTE_THASH_V4_L4_LEN, rss_key_be);
113 if ((rss_l3 != v4_tbl[i].hash_l3) ||
114 (rss_l3l4 != v4_tbl[i].hash_l3l4))
117 for (i = 0; i < RTE_DIM(v6_tbl); i++) {
119 for (j = 0; j < RTE_DIM(ipv6_hdr.src_addr); j++)
120 ipv6_hdr.src_addr[j] = v6_tbl[i].src_ip[j];
121 for (j = 0; j < RTE_DIM(ipv6_hdr.dst_addr); j++)
122 ipv6_hdr.dst_addr[j] = v6_tbl[i].dst_ip[j];
123 /*Load and convert ipv6 address into tuple*/
124 rte_thash_load_v6_addrs(&ipv6_hdr, &tuple);
125 tuple.v6.sport = v6_tbl[i].src_port;
126 tuple.v6.dport = v6_tbl[i].dst_port;
127 /*Calculate hash with original key*/
128 rss_l3 = rte_softrss((uint32_t *)&tuple,
129 RTE_THASH_V6_L3_LEN, default_rss_key);
130 rss_l3l4 = rte_softrss((uint32_t *)&tuple,
131 RTE_THASH_V6_L4_LEN, default_rss_key);
132 if ((rss_l3 != v6_tbl[i].hash_l3) ||
133 (rss_l3l4 != v6_tbl[i].hash_l3l4))
135 /*Calculate hash with converted key*/
136 rss_l3 = rte_softrss_be((uint32_t *)&tuple,
137 RTE_THASH_V6_L3_LEN, rss_key_be);
138 rss_l3l4 = rte_softrss_be((uint32_t *)&tuple,
139 RTE_THASH_V6_L4_LEN, rss_key_be);
140 if ((rss_l3 != v6_tbl[i].hash_l3) ||
141 (rss_l3l4 != v6_tbl[i].hash_l3l4))
148 test_create_invalid(void)
150 struct rte_thash_ctx *ctx;
154 ctx = rte_thash_init_ctx(NULL, key_len, reta_sz, NULL, 0);
155 RTE_TEST_ASSERT(ctx == NULL,
156 "Call succeeded with invalid parameters\n");
158 ctx = rte_thash_init_ctx("test", 0, reta_sz, NULL, 0);
159 RTE_TEST_ASSERT(ctx == NULL,
160 "Call succeeded with invalid parameters\n");
162 ctx = rte_thash_init_ctx(NULL, key_len, 1, NULL, 0);
163 RTE_TEST_ASSERT(ctx == NULL,
164 "Call succeeded with invalid parameters\n");
166 ctx = rte_thash_init_ctx(NULL, key_len, 17, NULL, 0);
167 RTE_TEST_ASSERT(ctx == NULL,
168 "Call succeeded with invalid parameters\n");
174 test_multiple_create(void)
176 struct rte_thash_ctx *ctx;
181 for (i = 0; i < 100; i++) {
182 ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
183 RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n");
185 rte_thash_free_ctx(ctx);
194 struct rte_thash_ctx *ctx;
196 ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
197 RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n");
199 rte_thash_free_ctx(ctx);
200 rte_thash_free_ctx(NULL);
206 test_add_invalid_helper(void)
208 struct rte_thash_ctx *ctx;
209 const int key_len = 40;
213 ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
214 RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
216 ret = rte_thash_add_helper(NULL, "test", reta_sz, 0);
217 RTE_TEST_ASSERT(ret == -EINVAL,
218 "Call succeeded with invalid parameters\n");
220 ret = rte_thash_add_helper(ctx, NULL, reta_sz, 0);
221 RTE_TEST_ASSERT(ret == -EINVAL,
222 "Call succeeded with invalid parameters\n");
224 ret = rte_thash_add_helper(ctx, "test", reta_sz - 1, 0);
225 RTE_TEST_ASSERT(ret == -EINVAL,
226 "Call succeeded with invalid parameters\n");
228 ret = rte_thash_add_helper(ctx, "test", reta_sz, key_len * 8);
229 RTE_TEST_ASSERT(ret == -EINVAL,
230 "Call succeeded with invalid parameters\n");
232 ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0);
233 RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
235 ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0);
236 RTE_TEST_ASSERT(ret == -EEXIST,
237 "Call succeeded with duplicated name\n");
240 * Create second helper with offset 3 * reta_sz.
241 * Note first_range helper created range in key:
242 * [0, 32 + length{= reta_sz} - 1), i.e [0, 37).
243 * second range is [44, 81)
245 ret = rte_thash_add_helper(ctx, "second_range", reta_sz,
247 RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
250 * Try to create overlapping with first_ and second_ ranges,
253 ret = rte_thash_add_helper(ctx, "third_range", 2 * reta_sz, reta_sz);
254 RTE_TEST_ASSERT(ret == -EEXIST,
255 "Call succeeded with overlapping ranges\n");
257 rte_thash_free_ctx(ctx);
263 test_find_existing(void)
265 struct rte_thash_ctx *ctx, *ret_ctx;
267 ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
268 RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
270 ret_ctx = rte_thash_find_existing("test");
271 RTE_TEST_ASSERT(ret_ctx != NULL, "can not find existing ctx\n");
273 rte_thash_free_ctx(ctx);
279 test_get_helper(void)
281 struct rte_thash_ctx *ctx;
282 struct rte_thash_subtuple_helper *h;
285 ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
286 RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
288 h = rte_thash_get_helper(NULL, "first_range");
289 RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n");
291 h = rte_thash_get_helper(ctx, NULL);
292 RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n");
294 ret = rte_thash_add_helper(ctx, "first_range", 8, 0);
295 RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
297 h = rte_thash_get_helper(ctx, "first_range");
298 RTE_TEST_ASSERT(h != NULL, "Can not find helper\n");
300 rte_thash_free_ctx(ctx);
306 test_period_overflow(void)
308 struct rte_thash_ctx *ctx;
309 int reta_sz = 7; /* reflects polynomial degree */
312 /* first create without RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */
313 ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL, 0);
314 RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
316 /* requested range > (2^reta_sz) - 1 */
317 ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz), 0);
318 RTE_TEST_ASSERT(ret == -ENOSPC,
319 "Call succeeded with invalid parameters\n");
321 /* requested range == len + 32 - 1, smaller than (2^reta_sz) - 1 */
322 ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) - 32, 0);
323 RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
325 rte_thash_free_ctx(ctx);
327 /* create with RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */
328 ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL,
329 RTE_THASH_IGNORE_PERIOD_OVERFLOW);
330 RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
332 /* requested range > (2^reta_sz - 1) */
333 ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) + 10, 0);
334 RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
336 rte_thash_free_ctx(ctx);
342 test_predictable_rss_min_seq(void)
344 struct rte_thash_ctx *ctx;
345 struct rte_thash_subtuple_helper *h;
346 const int key_len = 40;
348 uint8_t initial_key[key_len];
349 const uint8_t *new_key;
351 union rte_thash_tuple tuple;
352 uint32_t orig_hash, adj_hash, adj;
353 unsigned int desired_value = 27 & HASH_MSK(reta_sz);
354 uint16_t port_value = 22;
356 memset(initial_key, 0, key_len);
358 ctx = rte_thash_init_ctx("test", key_len, reta_sz, initial_key,
359 RTE_THASH_MINIMAL_SEQ);
360 RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
362 ret = rte_thash_add_helper(ctx, "snat", sizeof(uint16_t) * 8,
363 offsetof(union rte_thash_tuple, v4.sport) * 8);
364 RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret);
366 h = rte_thash_get_helper(ctx, "snat");
367 RTE_TEST_ASSERT(h != NULL, "can not find helper\n");
369 new_key = rte_thash_get_key(ctx);
370 tuple.v4.src_addr = RTE_IPV4(0, 0, 0, 0);
371 tuple.v4.dst_addr = RTE_IPV4(0, 0, 0, 0);
373 tuple.v4.sport = rte_cpu_to_be_16(port_value);
375 tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag);
377 orig_hash = rte_softrss((uint32_t *)&tuple,
378 RTE_THASH_V4_L4_LEN, new_key);
379 adj = rte_thash_get_complement(h, orig_hash, desired_value);
381 tuple.v4.sctp_tag = rte_cpu_to_be_32(tuple.v4.sctp_tag);
382 tuple.v4.sport ^= rte_cpu_to_be_16(adj);
383 tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag);
385 adj_hash = rte_softrss((uint32_t *)&tuple,
386 RTE_THASH_V4_L4_LEN, new_key);
387 RTE_TEST_ASSERT((adj_hash & HASH_MSK(reta_sz)) ==
388 desired_value, "bad desired value\n");
390 rte_thash_free_ctx(ctx);
396 * This test creates 7 subranges in the following order:
397 * range_one = [56, 95), len = 8, offset = 56
398 * range_two = [64, 103), len = 8, offset = 64
399 * range_three = [120, 159), len = 8, offset = 120
400 * range_four = [48, 87), len = 8, offset = 48
401 * range_five = [57, 95), len = 7, offset = 57
402 * range_six = [40, 111), len = 40, offset = 40
403 * range_seven = [0, 39), len = 8, offset = 0
412 struct range rng_arr[] = {
415 {"three", 8, 120, 15},
423 test_predictable_rss_multirange(void)
425 struct rte_thash_ctx *ctx;
426 struct rte_thash_subtuple_helper *h[RTE_DIM(rng_arr)];
427 const uint8_t *new_key;
428 const int key_len = 40;
430 unsigned int i, j, k;
432 uint32_t desired_value = rte_rand() & HASH_MSK(reta_sz);
433 uint8_t tuples[RTE_DIM(rng_arr)][16] = { {0} };
435 uint32_t hashes[RTE_DIM(rng_arr)];
436 uint32_t adj_hashes[RTE_DIM(rng_arr)];
439 ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
440 RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
442 for (i = 0; i < RTE_DIM(rng_arr); i++) {
443 ret = rte_thash_add_helper(ctx, rng_arr[i].name,
444 rng_arr[i].len, rng_arr[i].offset);
445 RTE_TEST_ASSERT(ret == 0, "can not add helper\n");
447 h[i] = rte_thash_get_helper(ctx, rng_arr[i].name);
448 RTE_TEST_ASSERT(h[i] != NULL, "can not find helper\n");
450 new_key = rte_thash_get_key(ctx);
453 * calculate hashes, complements, then adjust keys with
454 * complements and recalsulate hashes
456 for (i = 0; i < RTE_DIM(rng_arr); i++) {
457 for (k = 0; k < 100; k++) {
458 /* init with random keys */
459 ptr = (uint32_t *)&tuples[i][0];
460 for (j = 0; j < 4; j++)
462 /* convert keys from BE to CPU byte order */
463 for (j = 0; j < 4; j++)
464 ptr[j] = rte_be_to_cpu_32(ptr[j]);
466 hashes[i] = rte_softrss(ptr, 4, new_key);
467 adj = rte_thash_get_complement(h[i], hashes[i],
469 /* convert back to BE to adjust the value */
470 for (j = 0; j < 4; j++)
471 ptr[j] = rte_cpu_to_be_32(ptr[j]);
473 tuples[i][rng_arr[i].byte_idx] ^= adj;
475 for (j = 0; j < 4; j++)
476 ptr[j] = rte_be_to_cpu_32(ptr[j]);
478 adj_hashes[i] = rte_softrss(ptr, 4, new_key);
479 RTE_TEST_ASSERT((adj_hashes[i] & HASH_MSK(reta_sz)) ==
481 "bad desired value for %d tuple\n", i);
485 rte_thash_free_ctx(ctx);
491 cmp_tuple_eq(void *userdata, uint8_t *tuple)
493 return memcmp(userdata, tuple, TUPLE_SZ);
497 test_adjust_tuple(void)
499 struct rte_thash_ctx *ctx;
500 struct rte_thash_subtuple_helper *h;
501 const int key_len = 40;
502 const uint8_t *new_key;
503 uint8_t tuple[TUPLE_SZ];
504 uint32_t tmp_tuple[TUPLE_SZ / sizeof(uint32_t)];
505 uint32_t tuple_copy[TUPLE_SZ / sizeof(uint32_t)];
507 int reta_sz = CHAR_BIT;
509 unsigned int i, desired_value = rte_rand() & HASH_MSK(reta_sz);
511 memset(tuple, 0xab, TUPLE_SZ);
513 ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
514 RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
517 * set offset to be in the middle of a byte
518 * set size of the subtuple to be 2 * rets_sz
519 * to have the room for random bits
521 ret = rte_thash_add_helper(ctx, "test", reta_sz * 2,
523 RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret);
525 new_key = rte_thash_get_key(ctx);
527 h = rte_thash_get_helper(ctx, "test");
528 RTE_TEST_ASSERT(h != NULL, "can not find helper\n");
530 ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
532 RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret);
534 for (i = 0; i < (TUPLE_SZ / 4); i++)
536 rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]);
538 hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key);
539 RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) ==
540 desired_value, "bad desired value\n");
543 /* Pass previously calculated tuple to callback function */
544 memcpy(tuple_copy, tuple, TUPLE_SZ);
546 memset(tuple, 0xab, TUPLE_SZ);
547 ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
548 1, cmp_tuple_eq, tuple_copy);
549 RTE_TEST_ASSERT(ret == -EEXIST,
550 "adjust tuple didn't indicate collision\n");
553 * Make the function to generate random bits into subtuple
554 * after first adjustment attempt.
556 memset(tuple, 0xab, TUPLE_SZ);
557 ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
558 2, cmp_tuple_eq, tuple_copy);
559 RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret);
561 for (i = 0; i < (TUPLE_SZ / 4); i++)
563 rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]);
565 hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key);
566 RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) ==
567 desired_value, "bad desired value\n");
569 rte_thash_free_ctx(ctx);
574 static struct unit_test_suite thash_tests = {
575 .suite_name = "thash autotest",
579 TEST_CASE(test_toeplitz_hash_calc),
580 TEST_CASE(test_create_invalid),
581 TEST_CASE(test_multiple_create),
582 TEST_CASE(test_free_null),
583 TEST_CASE(test_add_invalid_helper),
584 TEST_CASE(test_find_existing),
585 TEST_CASE(test_get_helper),
586 TEST_CASE(test_period_overflow),
587 TEST_CASE(test_predictable_rss_min_seq),
588 TEST_CASE(test_predictable_rss_multirange),
589 TEST_CASE(test_adjust_tuple),
597 return unit_test_suite_runner(&thash_tests);
600 REGISTER_TEST_COMMAND(thash_autotest, test_thash);