1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2015-2019 Vladimir Medvedkin <[email protected]> 3 * Copyright(c) 2021 Intel Corporation 4 */ 5 6 #ifndef _RTE_THASH_H 7 #define _RTE_THASH_H 8 9 /** 10 * @file 11 * 12 * Software implementation of the Toeplitz hash function used by RSS. 13 * Can be used either for packet distribution on single queue NIC 14 * or for simulating of RSS computation on specific NIC (for example 15 * after GRE header decapsulating) 16 */ 17 18 #ifdef __cplusplus 19 extern "C" { 20 #endif 21 22 #include <stdint.h> 23 #include <rte_byteorder.h> 24 #include <rte_config.h> 25 #include <rte_ip.h> 26 #include <rte_common.h> 27 #include <rte_thash_gfni.h> 28 29 #if defined(RTE_ARCH_X86) || defined(__ARM_NEON) 30 #include <rte_vect.h> 31 #endif 32 33 #ifdef RTE_ARCH_X86 34 /* Byte swap mask used for converting IPv6 address 35 * 4-byte chunks to CPU byte order 36 */ 37 static const __m128i rte_thash_ipv6_bswap_mask = { 38 0x0405060700010203ULL, 0x0C0D0E0F08090A0BULL}; 39 #endif 40 41 /** 42 * length in dwords of input tuple to 43 * calculate hash of ipv4 header only 44 */ 45 #define RTE_THASH_V4_L3_LEN ((sizeof(struct rte_ipv4_tuple) - \ 46 sizeof(((struct rte_ipv4_tuple *)0)->sctp_tag)) / 4) 47 48 /** 49 * length in dwords of input tuple to 50 * calculate hash of ipv4 header + 51 * transport header 52 */ 53 #define RTE_THASH_V4_L4_LEN ((sizeof(struct rte_ipv4_tuple)) / 4) 54 55 /** 56 * length in dwords of input tuple to 57 * calculate hash of ipv6 header only 58 */ 59 #define RTE_THASH_V6_L3_LEN ((sizeof(struct rte_ipv6_tuple) - \ 60 sizeof(((struct rte_ipv6_tuple *)0)->sctp_tag)) / 4) 61 62 /** 63 * length in dwords of input tuple to 64 * calculate hash of ipv6 header + 65 * transport header 66 */ 67 #define RTE_THASH_V6_L4_LEN ((sizeof(struct rte_ipv6_tuple)) / 4) 68 69 /** 70 * IPv4 tuple 71 * addresses and ports/sctp_tag have to be CPU byte order 72 */ 73 struct rte_ipv4_tuple { 74 uint32_t src_addr; 75 uint32_t dst_addr; 76 RTE_STD_C11 77 union { 78 struct { 79 uint16_t dport; 80 uint16_t sport; 81 }; 82 uint32_t sctp_tag; 83 }; 84 }; 85 86 /** 87 * IPv6 tuple 88 * Addresses have to be filled by rte_thash_load_v6_addr() 89 * ports/sctp_tag have to be CPU byte order 90 */ 91 struct rte_ipv6_tuple { 92 uint8_t src_addr[16]; 93 uint8_t dst_addr[16]; 94 RTE_STD_C11 95 union { 96 struct { 97 uint16_t dport; 98 uint16_t sport; 99 }; 100 uint32_t sctp_tag; 101 }; 102 }; 103 104 union rte_thash_tuple { 105 struct rte_ipv4_tuple v4; 106 struct rte_ipv6_tuple v6; 107 #ifdef RTE_ARCH_X86 108 } __rte_aligned(XMM_SIZE); 109 #else 110 }; 111 #endif 112 113 /** 114 * Prepare special converted key to use with rte_softrss_be() 115 * @param orig 116 * pointer to original RSS key 117 * @param targ 118 * pointer to target RSS key 119 * @param len 120 * RSS key length 121 */ 122 static inline void 123 rte_convert_rss_key(const uint32_t *orig, uint32_t *targ, int len) 124 { 125 int i; 126 127 for (i = 0; i < (len >> 2); i++) 128 targ[i] = rte_be_to_cpu_32(orig[i]); 129 } 130 131 /** 132 * Prepare and load IPv6 addresses (src and dst) 133 * into target tuple 134 * @param orig 135 * Pointer to ipv6 header of the original packet 136 * @param targ 137 * Pointer to rte_ipv6_tuple structure 138 */ 139 static inline void 140 rte_thash_load_v6_addrs(const struct rte_ipv6_hdr *orig, 141 union rte_thash_tuple *targ) 142 { 143 #ifdef RTE_ARCH_X86 144 __m128i ipv6 = _mm_loadu_si128((const __m128i *)orig->src_addr); 145 *(__m128i *)targ->v6.src_addr = 146 _mm_shuffle_epi8(ipv6, rte_thash_ipv6_bswap_mask); 147 ipv6 = _mm_loadu_si128((const __m128i *)orig->dst_addr); 148 *(__m128i *)targ->v6.dst_addr = 149 _mm_shuffle_epi8(ipv6, rte_thash_ipv6_bswap_mask); 150 #elif defined(__ARM_NEON) 151 uint8x16_t ipv6 = vld1q_u8((uint8_t const *)orig->src_addr); 152 vst1q_u8((uint8_t *)targ->v6.src_addr, vrev32q_u8(ipv6)); 153 ipv6 = vld1q_u8((uint8_t const *)orig->dst_addr); 154 vst1q_u8((uint8_t *)targ->v6.dst_addr, vrev32q_u8(ipv6)); 155 #else 156 int i; 157 for (i = 0; i < 4; i++) { 158 *((uint32_t *)targ->v6.src_addr + i) = 159 rte_be_to_cpu_32(*((const uint32_t *)orig->src_addr + i)); 160 *((uint32_t *)targ->v6.dst_addr + i) = 161 rte_be_to_cpu_32(*((const uint32_t *)orig->dst_addr + i)); 162 } 163 #endif 164 } 165 166 /** 167 * Generic implementation. Can be used with original rss_key 168 * @param input_tuple 169 * Pointer to input tuple 170 * @param input_len 171 * Length of input_tuple in 4-bytes chunks 172 * @param rss_key 173 * Pointer to RSS hash key. 174 * @return 175 * Calculated hash value. 176 */ 177 static inline uint32_t 178 rte_softrss(uint32_t *input_tuple, uint32_t input_len, 179 const uint8_t *rss_key) 180 { 181 uint32_t i, j, map, ret = 0; 182 183 for (j = 0; j < input_len; j++) { 184 for (map = input_tuple[j]; map; map &= (map - 1)) { 185 i = rte_bsf32(map); 186 ret ^= rte_cpu_to_be_32(((const uint32_t *)rss_key)[j]) << (31 - i) | 187 (uint32_t)((uint64_t)(rte_cpu_to_be_32(((const uint32_t *)rss_key)[j + 1])) >> 188 (i + 1)); 189 } 190 } 191 return ret; 192 } 193 194 /** 195 * Optimized implementation. 196 * If you want the calculated hash value matches NIC RSS value 197 * you have to use special converted key with rte_convert_rss_key() fn. 198 * @param input_tuple 199 * Pointer to input tuple 200 * @param input_len 201 * Length of input_tuple in 4-bytes chunks 202 * @param *rss_key 203 * Pointer to RSS hash key. 204 * @return 205 * Calculated hash value. 206 */ 207 static inline uint32_t 208 rte_softrss_be(uint32_t *input_tuple, uint32_t input_len, 209 const uint8_t *rss_key) 210 { 211 uint32_t i, j, map, ret = 0; 212 213 for (j = 0; j < input_len; j++) { 214 for (map = input_tuple[j]; map; map &= (map - 1)) { 215 i = rte_bsf32(map); 216 ret ^= ((const uint32_t *)rss_key)[j] << (31 - i) | 217 (uint32_t)((uint64_t)(((const uint32_t *)rss_key)[j + 1]) >> (i + 1)); 218 } 219 } 220 return ret; 221 } 222 223 /** 224 * Indicates if GFNI implementations of the Toeplitz hash are supported. 225 * 226 * @warning 227 * @b EXPERIMENTAL: this API may change without prior notice. 228 * 229 * @return 230 * 1 if GFNI is supported 231 * 0 otherwise 232 */ 233 __rte_experimental 234 int 235 rte_thash_gfni_supported(void); 236 237 /** 238 * Converts Toeplitz hash key (RSS key) into matrixes required 239 * for GFNI implementation 240 * 241 * @warning 242 * @b EXPERIMENTAL: this API may change without prior notice. 243 * 244 * @param matrixes 245 * pointer to the memory where matrices will be written. 246 * Note: the size of this memory must be equal to size * 8 247 * @param rss_key 248 * pointer to the Toeplitz hash key 249 * @param size 250 * Size of the rss_key in bytes. 251 */ 252 __rte_experimental 253 void 254 rte_thash_complete_matrix(uint64_t *matrixes, const uint8_t *rss_key, 255 int size); 256 257 /** @internal Logarithm of minimum size of the RSS ReTa */ 258 #define RTE_THASH_RETA_SZ_MIN 2U 259 /** @internal Logarithm of maximum size of the RSS ReTa */ 260 #define RTE_THASH_RETA_SZ_MAX 16U 261 262 /** 263 * LFSR will ignore if generated m-sequence has more than 2^n -1 bits, 264 * where n is the logarithm of the RSS ReTa size. 265 */ 266 #define RTE_THASH_IGNORE_PERIOD_OVERFLOW 0x1 267 /** 268 * Generate minimal required bit (equal to ReTa LSB) sequence into 269 * the hash_key 270 */ 271 #define RTE_THASH_MINIMAL_SEQ 0x2 272 273 /** @internal thash context structure. */ 274 struct rte_thash_ctx; 275 /** @internal thash helper structure. */ 276 struct rte_thash_subtuple_helper; 277 278 /** 279 * Create a new thash context. 280 * 281 * @warning 282 * @b EXPERIMENTAL: this API may change without prior notice. 283 * 284 * @param name 285 * Context name 286 * @param key_len 287 * Length of the toeplitz hash key 288 * @param reta_sz 289 * Logarithm of the NIC's Redirection Table (ReTa) size, 290 * i.e. number of the LSBs if the hash used to determine 291 * the reta entry. 292 * @param key 293 * Pointer to the key used to init an internal key state. 294 * Could be NULL, in this case internal key will be inited with random. 295 * @param flags 296 * Supported flags are: 297 * RTE_THASH_IGNORE_PERIOD_OVERFLOW 298 * RTE_THASH_MINIMAL_SEQ 299 * @return 300 * A pointer to the created context on success 301 * NULL otherwise 302 */ 303 __rte_experimental 304 struct rte_thash_ctx * 305 rte_thash_init_ctx(const char *name, uint32_t key_len, uint32_t reta_sz, 306 uint8_t *key, uint32_t flags); 307 308 /** 309 * Find an existing thash context and return a pointer to it. 310 * 311 * @warning 312 * @b EXPERIMENTAL: this API may change without prior notice. 313 * 314 * @param name 315 * Name of the thash context 316 * @return 317 * Pointer to the thash context or NULL if it was not found with rte_errno 318 * set appropriately. Possible rte_errno values include: 319 * - ENOENT - required entry not available to return. 320 */ 321 __rte_experimental 322 struct rte_thash_ctx * 323 rte_thash_find_existing(const char *name); 324 325 /** 326 * Free a thash context object 327 * 328 * @warning 329 * @b EXPERIMENTAL: this API may change without prior notice. 330 * 331 * @param ctx 332 * Thash context 333 * @return 334 * None 335 */ 336 __rte_experimental 337 void 338 rte_thash_free_ctx(struct rte_thash_ctx *ctx); 339 340 /** 341 * Add a special properties to the toeplitz hash key inside a thash context. 342 * Creates an internal helper struct which has a complementary table 343 * to calculate toeplitz hash collisions. 344 * This function is not multi-thread safe. 345 * 346 * @warning 347 * @b EXPERIMENTAL: this API may change without prior notice. 348 * 349 * @param ctx 350 * Thash context 351 * @param name 352 * Name of the helper 353 * @param len 354 * Length in bits of the target subtuple 355 * Must be no shorter than reta_sz passed on rte_thash_init_ctx(). 356 * @param offset 357 * Offset in bits of the subtuple 358 * @return 359 * 0 on success 360 * negative on error 361 */ 362 __rte_experimental 363 int 364 rte_thash_add_helper(struct rte_thash_ctx *ctx, const char *name, uint32_t len, 365 uint32_t offset); 366 367 /** 368 * Find a helper in the context by the given name 369 * 370 * @warning 371 * @b EXPERIMENTAL: this API may change without prior notice. 372 * 373 * @param ctx 374 * Thash context 375 * @param name 376 * Name of the helper 377 * @return 378 * Pointer to the thash helper or NULL if it was not found. 379 */ 380 __rte_experimental 381 struct rte_thash_subtuple_helper * 382 rte_thash_get_helper(struct rte_thash_ctx *ctx, const char *name); 383 384 /** 385 * Get a complementary value for the subtuple to produce a 386 * partial toeplitz hash collision. It must be XOR'ed with the 387 * subtuple to produce the hash value with the desired hash LSB's 388 * This function is multi-thread safe. 389 * 390 * @param h 391 * Pointer to the helper struct 392 * @param hash 393 * Toeplitz hash value calculated for the given tuple 394 * @param desired_hash 395 * Desired hash value to find a collision for 396 * @return 397 * A complementary value which must be xored with the corresponding subtuple 398 */ 399 __rte_experimental 400 uint32_t 401 rte_thash_get_complement(struct rte_thash_subtuple_helper *h, 402 uint32_t hash, uint32_t desired_hash); 403 404 /** 405 * Get a pointer to the toeplitz hash contained in the context. 406 * It changes after each addition of a helper. It should be installed to 407 * the NIC. 408 * 409 * @warning 410 * @b EXPERIMENTAL: this API may change without prior notice. 411 * 412 * @param ctx 413 * Thash context 414 * @return 415 * A pointer to the toeplitz hash key 416 */ 417 __rte_experimental 418 const uint8_t * 419 rte_thash_get_key(struct rte_thash_ctx *ctx); 420 421 /** 422 * Get a pointer to the toeplitz hash matrices contained in the context. 423 * These matrices could be used with fast toeplitz hash implementation if 424 * CPU supports GFNI. 425 * Matrices changes after each addition of a helper. 426 * 427 * @warning 428 * @b EXPERIMENTAL: this API may change without prior notice. 429 * 430 * @param ctx 431 * Thash context 432 * @return 433 * A pointer to the toeplitz hash key matrices on success 434 * NULL if GFNI is not supported. 435 */ 436 __rte_experimental 437 const uint64_t * 438 rte_thash_get_gfni_matrices(struct rte_thash_ctx *ctx); 439 440 /** 441 * Function prototype for the rte_thash_adjust_tuple 442 * to check if adjusted tuple could be used. 443 * Generally it is some kind of lookup function to check 444 * if adjusted tuple is already in use. 445 * 446 * @warning 447 * @b EXPERIMENTAL: this API may change without prior notice. 448 * 449 * @param userdata 450 * Pointer to the userdata. It could be a pointer to the 451 * table with used tuples to search. 452 * @param tuple 453 * Pointer to the tuple to check 454 * 455 * @return 456 * 1 on success 457 * 0 otherwise 458 */ 459 typedef int (*rte_thash_check_tuple_t)(void *userdata, uint8_t *tuple); 460 461 /** 462 * Adjusts tuple in the way to make Toeplitz hash has 463 * desired least significant bits. 464 * This function is multi-thread safe. 465 * 466 * @warning 467 * @b EXPERIMENTAL: this API may change without prior notice. 468 * 469 * @param ctx 470 * Thash context 471 * @param h 472 * Pointer to the helper struct 473 * @param tuple 474 * Pointer to the tuple to be adjusted 475 * @param tuple_len 476 * Length of the tuple. Must be multiple of 4. 477 * @param desired_value 478 * Desired value of least significant bits of the hash 479 * @param attempts 480 * Number of attempts to adjust tuple with fn() calling 481 * @param fn 482 * Callback function to check adjusted tuple. Could be NULL 483 * @param userdata 484 * Pointer to the userdata to be passed to fn(). Could be NULL 485 * 486 * @return 487 * 0 on success 488 * negative otherwise 489 */ 490 __rte_experimental 491 int 492 rte_thash_adjust_tuple(struct rte_thash_ctx *ctx, 493 struct rte_thash_subtuple_helper *h, 494 uint8_t *tuple, unsigned int tuple_len, 495 uint32_t desired_value, unsigned int attempts, 496 rte_thash_check_tuple_t fn, void *userdata); 497 498 #ifdef __cplusplus 499 } 500 #endif 501 502 #endif /* _RTE_THASH_H */ 503