1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2015 Intel Corporation 3 */ 4 5 #include <string.h> 6 #include <stdarg.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <stdint.h> 10 #include <inttypes.h> 11 #include <errno.h> 12 #include <rte_cycles.h> 13 #include <sys/queue.h> 14 15 #include <rte_byteorder.h> 16 #include <rte_common.h> 17 #include <rte_debug.h> 18 #include <rte_ethdev.h> 19 #include <rte_log.h> 20 #include <rte_lcore.h> 21 #include <rte_memory.h> 22 #include <rte_bus_vdev.h> 23 24 #include <rte_string_fns.h> 25 #include <rte_errno.h> 26 #include <rte_eth_bond.h> 27 28 #include "test.h" 29 30 #define SLAVE_COUNT (4) 31 32 #define RXTX_RING_SIZE 1024 33 #define RXTX_QUEUE_COUNT 4 34 35 #define BONDED_DEV_NAME ("net_bonding_rss") 36 37 #define SLAVE_DEV_NAME_FMT ("net_null%d") 38 #define SLAVE_RXTX_QUEUE_FMT ("rssconf_slave%d_q%d") 39 40 #define NUM_MBUFS 8191 41 #define MBUF_SIZE (1600 + RTE_PKTMBUF_HEADROOM) 42 #define MBUF_CACHE_SIZE 250 43 #define BURST_SIZE 32 44 45 #define INVALID_SOCKET_ID (-1) 46 #define INVALID_PORT_ID (0xFF) 47 #define INVALID_BONDING_MODE (-1) 48 49 struct slave_conf { 50 uint16_t port_id; 51 struct rte_eth_dev_info dev_info; 52 53 struct rte_eth_rss_conf rss_conf; 54 uint8_t rss_key[40]; 55 struct rte_eth_rss_reta_entry64 reta_conf[512 / RTE_ETH_RETA_GROUP_SIZE]; 56 57 uint8_t is_slave; 58 struct rte_ring *rxtx_queue[RXTX_QUEUE_COUNT]; 59 }; 60 61 struct link_bonding_rssconf_unittest_params { 62 uint8_t bond_port_id; 63 struct rte_eth_dev_info bond_dev_info; 64 struct rte_eth_rss_reta_entry64 bond_reta_conf[512 / RTE_ETH_RETA_GROUP_SIZE]; 65 struct slave_conf slave_ports[SLAVE_COUNT]; 66 67 struct rte_mempool *mbuf_pool; 68 }; 69 70 static struct link_bonding_rssconf_unittest_params test_params = { 71 .bond_port_id = INVALID_PORT_ID, 72 .slave_ports = { 73 [0 ... SLAVE_COUNT - 1] = { .port_id = INVALID_PORT_ID, .is_slave = 0} 74 }, 75 .mbuf_pool = NULL, 76 }; 77 78 /** 79 * Default port configuration with RSS turned off 80 */ 81 static struct rte_eth_conf default_pmd_conf = { 82 .rxmode = { 83 .mq_mode = RTE_ETH_MQ_RX_NONE, 84 .split_hdr_size = 0, 85 }, 86 .txmode = { 87 .mq_mode = RTE_ETH_MQ_TX_NONE, 88 }, 89 .lpbk_mode = 0, 90 }; 91 92 static struct rte_eth_conf rss_pmd_conf = { 93 .rxmode = { 94 .mq_mode = RTE_ETH_MQ_RX_RSS, 95 .split_hdr_size = 0, 96 }, 97 .txmode = { 98 .mq_mode = RTE_ETH_MQ_TX_NONE, 99 }, 100 .rx_adv_conf = { 101 .rss_conf = { 102 .rss_key = NULL, 103 .rss_hf = RTE_ETH_RSS_IPV6, 104 }, 105 }, 106 .lpbk_mode = 0, 107 }; 108 109 #define FOR_EACH(_i, _item, _array, _size) \ 110 for (_i = 0, _item = &_array[0]; _i < _size && (_item = &_array[_i]); _i++) 111 112 /* Macro for iterating over every port that can be used as a slave 113 * in this test. 114 * _i variable used as an index in test_params->slave_ports 115 * _slave pointer to &test_params->slave_ports[_idx] 116 */ 117 #define FOR_EACH_PORT(_i, _port) \ 118 FOR_EACH(_i, _port, test_params.slave_ports, \ 119 RTE_DIM(test_params.slave_ports)) 120 121 static int 122 configure_ethdev(uint16_t port_id, struct rte_eth_conf *eth_conf, 123 uint8_t start) 124 { 125 int rxq, txq; 126 127 TEST_ASSERT(rte_eth_dev_configure(port_id, RXTX_QUEUE_COUNT, 128 RXTX_QUEUE_COUNT, eth_conf) == 0, "Failed to configure device %u", 129 port_id); 130 131 for (rxq = 0; rxq < RXTX_QUEUE_COUNT; rxq++) { 132 TEST_ASSERT(rte_eth_rx_queue_setup(port_id, rxq, RXTX_RING_SIZE, 133 rte_eth_dev_socket_id(port_id), NULL, 134 test_params.mbuf_pool) == 0, "Failed to setup rx queue."); 135 } 136 137 for (txq = 0; txq < RXTX_QUEUE_COUNT; txq++) { 138 TEST_ASSERT(rte_eth_tx_queue_setup(port_id, txq, RXTX_RING_SIZE, 139 rte_eth_dev_socket_id(port_id), NULL) == 0, 140 "Failed to setup tx queue."); 141 } 142 143 if (start) { 144 TEST_ASSERT(rte_eth_dev_start(port_id) == 0, 145 "Failed to start device (%d).", port_id); 146 } 147 148 return 0; 149 } 150 151 /** 152 * Remove all slaves from bonding 153 */ 154 static int 155 remove_slaves(void) 156 { 157 unsigned n; 158 struct slave_conf *port; 159 160 FOR_EACH_PORT(n, port) { 161 port = &test_params.slave_ports[n]; 162 if (port->is_slave) { 163 TEST_ASSERT_SUCCESS(rte_eth_bond_slave_remove( 164 test_params.bond_port_id, port->port_id), 165 "Cannot remove slave %d from bonding", port->port_id); 166 port->is_slave = 0; 167 } 168 } 169 170 return 0; 171 } 172 173 static int 174 remove_slaves_and_stop_bonded_device(void) 175 { 176 TEST_ASSERT_SUCCESS(remove_slaves(), "Removing slaves"); 177 TEST_ASSERT_SUCCESS(rte_eth_dev_stop(test_params.bond_port_id), 178 "Failed to stop port %u", test_params.bond_port_id); 179 return TEST_SUCCESS; 180 } 181 182 /** 183 * Add all slaves to bonding 184 */ 185 static int 186 bond_slaves(void) 187 { 188 unsigned n; 189 struct slave_conf *port; 190 191 FOR_EACH_PORT(n, port) { 192 port = &test_params.slave_ports[n]; 193 if (!port->is_slave) { 194 TEST_ASSERT_SUCCESS(rte_eth_bond_slave_add(test_params.bond_port_id, 195 port->port_id), "Cannot attach slave %d to the bonding", 196 port->port_id); 197 port->is_slave = 1; 198 } 199 } 200 201 return 0; 202 } 203 204 /** 205 * Set all RETA values in port_id to value 206 */ 207 static int 208 reta_set(uint16_t port_id, uint8_t value, int reta_size) 209 { 210 struct rte_eth_rss_reta_entry64 reta_conf[512/RTE_ETH_RETA_GROUP_SIZE]; 211 int i, j; 212 213 for (i = 0; i < reta_size / RTE_ETH_RETA_GROUP_SIZE; i++) { 214 /* select all fields to set */ 215 reta_conf[i].mask = ~0LL; 216 for (j = 0; j < RTE_ETH_RETA_GROUP_SIZE; j++) 217 reta_conf[i].reta[j] = value; 218 } 219 220 return rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size); 221 } 222 223 /** 224 * Check if slaves RETA is synchronized with bonding port. Returns 1 if slave 225 * port is synced with bonding port. 226 */ 227 static int 228 reta_check_synced(struct slave_conf *port) 229 { 230 unsigned i; 231 232 for (i = 0; i < test_params.bond_dev_info.reta_size; 233 i++) { 234 235 int index = i / RTE_ETH_RETA_GROUP_SIZE; 236 int shift = i % RTE_ETH_RETA_GROUP_SIZE; 237 238 if (port->reta_conf[index].reta[shift] != 239 test_params.bond_reta_conf[index].reta[shift]) 240 return 0; 241 242 } 243 244 return 1; 245 } 246 247 /** 248 * Fetch bonding ports RETA 249 */ 250 static int 251 bond_reta_fetch(void) { 252 unsigned j; 253 254 for (j = 0; j < test_params.bond_dev_info.reta_size / RTE_ETH_RETA_GROUP_SIZE; 255 j++) 256 test_params.bond_reta_conf[j].mask = ~0LL; 257 258 TEST_ASSERT_SUCCESS(rte_eth_dev_rss_reta_query(test_params.bond_port_id, 259 test_params.bond_reta_conf, test_params.bond_dev_info.reta_size), 260 "Cannot take bonding ports RSS configuration"); 261 return 0; 262 } 263 264 /** 265 * Fetch slaves RETA 266 */ 267 static int 268 slave_reta_fetch(struct slave_conf *port) { 269 unsigned j; 270 271 for (j = 0; j < port->dev_info.reta_size / RTE_ETH_RETA_GROUP_SIZE; j++) 272 port->reta_conf[j].mask = ~0LL; 273 274 TEST_ASSERT_SUCCESS(rte_eth_dev_rss_reta_query(port->port_id, 275 port->reta_conf, port->dev_info.reta_size), 276 "Cannot take bonding ports RSS configuration"); 277 return 0; 278 } 279 280 /** 281 * Remove and add slave to check if slaves configuration is synced with 282 * the bonding ports values after adding new slave. 283 */ 284 static int 285 slave_remove_and_add(void) 286 { 287 struct slave_conf *port = &(test_params.slave_ports[0]); 288 289 /* 1. Remove first slave from bonding */ 290 TEST_ASSERT_SUCCESS(rte_eth_bond_slave_remove(test_params.bond_port_id, 291 port->port_id), "Cannot remove slave #d from bonding"); 292 293 /* 2. Change removed (ex-)slave and bonding configuration to different 294 * values 295 */ 296 reta_set(test_params.bond_port_id, 1, test_params.bond_dev_info.reta_size); 297 bond_reta_fetch(); 298 299 reta_set(port->port_id, 2, port->dev_info.reta_size); 300 slave_reta_fetch(port); 301 302 TEST_ASSERT(reta_check_synced(port) == 0, 303 "Removed slave didn't should be synchronized with bonding port"); 304 305 /* 3. Add (ex-)slave and check if configuration changed*/ 306 TEST_ASSERT_SUCCESS(rte_eth_bond_slave_add(test_params.bond_port_id, 307 port->port_id), "Cannot add slave"); 308 309 bond_reta_fetch(); 310 slave_reta_fetch(port); 311 312 return reta_check_synced(port); 313 } 314 315 /** 316 * Test configuration propagation over slaves. 317 */ 318 static int 319 test_propagate(void) 320 { 321 unsigned i; 322 uint8_t n; 323 struct slave_conf *port; 324 uint8_t bond_rss_key[40]; 325 struct rte_eth_rss_conf bond_rss_conf; 326 327 int retval = 0; 328 uint64_t rss_hf = 0; 329 uint64_t default_rss_hf = 0; 330 331 retval = rte_eth_dev_info_get(test_params.bond_port_id, 332 &test_params.bond_dev_info); 333 TEST_ASSERT((retval == 0), 334 "Error during getting device (port %u) info: %s\n", 335 test_params.bond_port_id, strerror(-retval)); 336 337 /* 338 * Test hash function propagation 339 */ 340 for (i = 0; i < sizeof(test_params.bond_dev_info.flow_type_rss_offloads)*8; 341 i++) { 342 343 rss_hf = test_params.bond_dev_info.flow_type_rss_offloads & (1<<i); 344 if (rss_hf) { 345 bond_rss_conf.rss_key = NULL; 346 bond_rss_conf.rss_hf = rss_hf; 347 348 retval = rte_eth_dev_rss_hash_update(test_params.bond_port_id, 349 &bond_rss_conf); 350 TEST_ASSERT_SUCCESS(retval, "Cannot set slaves hash function"); 351 352 FOR_EACH_PORT(n, port) { 353 port = &test_params.slave_ports[n]; 354 355 retval = rte_eth_dev_rss_hash_conf_get(port->port_id, 356 &port->rss_conf); 357 TEST_ASSERT_SUCCESS(retval, 358 "Cannot take slaves RSS configuration"); 359 360 TEST_ASSERT(port->rss_conf.rss_hf == rss_hf, 361 "Hash function not propagated for slave %d", 362 port->port_id); 363 } 364 365 default_rss_hf = rss_hf; 366 } 367 368 } 369 370 /* 371 * Test key propagation 372 */ 373 for (i = 1; i < 10; i++) { 374 375 /* Set all keys to zero */ 376 FOR_EACH_PORT(n, port) { 377 port = &test_params.slave_ports[n]; 378 memset(port->rss_conf.rss_key, 0, 40); 379 retval = rte_eth_dev_rss_hash_update(port->port_id, 380 &port->rss_conf); 381 TEST_ASSERT_SUCCESS(retval, "Cannot set slaves RSS keys"); 382 } 383 384 memset(bond_rss_key, i, sizeof(bond_rss_key)); 385 bond_rss_conf.rss_hf = default_rss_hf, 386 bond_rss_conf.rss_key = bond_rss_key; 387 bond_rss_conf.rss_key_len = 40; 388 389 retval = rte_eth_dev_rss_hash_update(test_params.bond_port_id, 390 &bond_rss_conf); 391 TEST_ASSERT_SUCCESS(retval, "Cannot set bonded port RSS keys"); 392 393 FOR_EACH_PORT(n, port) { 394 port = &test_params.slave_ports[n]; 395 396 retval = rte_eth_dev_rss_hash_conf_get(port->port_id, 397 &(port->rss_conf)); 398 399 TEST_ASSERT_SUCCESS(retval, 400 "Cannot take slaves RSS configuration"); 401 402 /* compare keys */ 403 retval = memcmp(port->rss_conf.rss_key, bond_rss_key, 404 sizeof(bond_rss_key)); 405 TEST_ASSERT(retval == 0, "Key value not propagated for slave %d", 406 port->port_id); 407 } 408 } 409 410 /* 411 * Test RETA propagation 412 */ 413 for (i = 0; i < RXTX_QUEUE_COUNT; i++) { 414 415 /* Set all keys to zero */ 416 FOR_EACH_PORT(n, port) { 417 port = &test_params.slave_ports[n]; 418 retval = reta_set(port->port_id, (i + 1) % RXTX_QUEUE_COUNT, 419 port->dev_info.reta_size); 420 TEST_ASSERT_SUCCESS(retval, "Cannot set slaves RETA"); 421 } 422 423 TEST_ASSERT_SUCCESS(reta_set(test_params.bond_port_id, 424 i % RXTX_QUEUE_COUNT, test_params.bond_dev_info.reta_size), 425 "Cannot set bonded port RETA"); 426 427 bond_reta_fetch(); 428 429 FOR_EACH_PORT(n, port) { 430 port = &test_params.slave_ports[n]; 431 432 slave_reta_fetch(port); 433 TEST_ASSERT(reta_check_synced(port) == 1, "RETAs inconsistent"); 434 } 435 } 436 437 return TEST_SUCCESS; 438 } 439 440 /** 441 * Test propagation logic, when RX_RSS mq_mode is turned on for bonding port 442 */ 443 static int 444 test_rss(void) 445 { 446 /** 447 * Configure bonding port in RSS mq mode 448 */ 449 int ret; 450 451 TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id, 452 &rss_pmd_conf, 0), "Failed to configure bonding device\n"); 453 454 ret = rte_eth_dev_info_get(test_params.bond_port_id, 455 &test_params.bond_dev_info); 456 TEST_ASSERT((ret == 0), 457 "Error during getting device (port %u) info: %s\n", 458 test_params.bond_port_id, strerror(-ret)); 459 460 TEST_ASSERT_SUCCESS(bond_slaves(), "Bonding slaves failed"); 461 462 TEST_ASSERT_SUCCESS(rte_eth_dev_start(test_params.bond_port_id), 463 "Failed to start bonding port (%d).", test_params.bond_port_id); 464 465 TEST_ASSERT_SUCCESS(test_propagate(), "Propagation test failed"); 466 467 TEST_ASSERT(slave_remove_and_add() == 1, "New slave should be synced"); 468 469 remove_slaves_and_stop_bonded_device(); 470 471 return TEST_SUCCESS; 472 } 473 474 /** 475 * Test propagation logic, when RX_RSS mq_mode is turned off for bonding port 476 */ 477 static int 478 test_rss_lazy(void) 479 { 480 int ret; 481 482 TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id, 483 &default_pmd_conf, 0), "Failed to configure bonding device\n"); 484 485 ret = rte_eth_dev_info_get(test_params.bond_port_id, 486 &test_params.bond_dev_info); 487 TEST_ASSERT((ret == 0), 488 "Error during getting device (port %u) info: %s\n", 489 test_params.bond_port_id, strerror(-ret)); 490 491 TEST_ASSERT_SUCCESS(bond_slaves(), "Bonding slaves failed"); 492 493 TEST_ASSERT_SUCCESS(rte_eth_dev_start(test_params.bond_port_id), 494 "Failed to start bonding port (%d).", test_params.bond_port_id); 495 496 TEST_ASSERT_SUCCESS(test_propagate(), "Propagation test failed"); 497 498 TEST_ASSERT(slave_remove_and_add() == 0, "New slave shouldn't be synced"); 499 500 remove_slaves_and_stop_bonded_device(); 501 502 return TEST_SUCCESS; 503 } 504 505 static int 506 test_setup(void) 507 { 508 unsigned n; 509 int retval; 510 int port_id; 511 char name[256]; 512 struct slave_conf *port; 513 struct rte_ether_addr mac_addr = { .addr_bytes = {0} }; 514 515 if (test_params.mbuf_pool == NULL) { 516 517 test_params.mbuf_pool = rte_pktmbuf_pool_create( 518 "RSS_MBUF_POOL", NUM_MBUFS * SLAVE_COUNT, 519 MBUF_CACHE_SIZE, 0, MBUF_SIZE, rte_socket_id()); 520 521 TEST_ASSERT(test_params.mbuf_pool != NULL, 522 "rte_pktmbuf_pool_create failed\n"); 523 } 524 525 /* Create / initialize ring eth devs. */ 526 FOR_EACH_PORT(n, port) { 527 port = &test_params.slave_ports[n]; 528 529 port_id = rte_eth_dev_count_avail(); 530 snprintf(name, sizeof(name), SLAVE_DEV_NAME_FMT, port_id); 531 532 retval = rte_vdev_init(name, "size=64,copy=0"); 533 TEST_ASSERT_SUCCESS(retval, "Failed to create null device '%s'\n", 534 name); 535 536 port->port_id = port_id; 537 538 port->rss_conf.rss_key = port->rss_key; 539 port->rss_conf.rss_key_len = 40; 540 541 retval = configure_ethdev(port->port_id, &default_pmd_conf, 0); 542 TEST_ASSERT_SUCCESS(retval, "Failed to configure virtual ethdev %s\n", 543 name); 544 545 /* assign a non-zero MAC */ 546 mac_addr.addr_bytes[5] = 0x10 + port->port_id; 547 rte_eth_dev_default_mac_addr_set(port->port_id, &mac_addr); 548 549 rte_eth_dev_info_get(port->port_id, &port->dev_info); 550 retval = rte_eth_dev_info_get(port->port_id, &port->dev_info); 551 TEST_ASSERT((retval == 0), 552 "Error during getting device (port %u) info: %s\n", 553 test_params.bond_port_id, strerror(-retval)); 554 } 555 556 if (test_params.bond_port_id == INVALID_PORT_ID) { 557 retval = rte_eth_bond_create(BONDED_DEV_NAME, 0, rte_socket_id()); 558 559 TEST_ASSERT(retval >= 0, "Failed to create bonded ethdev %s", 560 BONDED_DEV_NAME); 561 562 test_params.bond_port_id = retval; 563 564 TEST_ASSERT_SUCCESS(configure_ethdev(test_params.bond_port_id, 565 &default_pmd_conf, 0), "Failed to configure bonding device\n"); 566 567 retval = rte_eth_dev_info_get(test_params.bond_port_id, 568 &test_params.bond_dev_info); 569 TEST_ASSERT((retval == 0), 570 "Error during getting device (port %u) info: %s\n", 571 test_params.bond_port_id, strerror(-retval)); 572 } 573 574 return TEST_SUCCESS; 575 } 576 577 static void 578 testsuite_teardown(void) 579 { 580 struct slave_conf *port; 581 uint8_t i; 582 583 /* Only stop ports. 584 * Any cleanup/reset state is done when particular test is 585 * started. */ 586 587 rte_eth_dev_stop(test_params.bond_port_id); 588 589 FOR_EACH_PORT(i, port) 590 rte_eth_dev_stop(port->port_id); 591 } 592 593 static int 594 check_environment(void) 595 { 596 return TEST_SUCCESS; 597 } 598 599 static int 600 test_rssconf_executor(int (*test_func)(void)) 601 { 602 int test_result; 603 604 /* Check if environment is clean. Fail to launch a test if there was 605 * a critical error before that prevented to reset environment. */ 606 TEST_ASSERT_SUCCESS(check_environment(), 607 "Refusing to launch test in dirty environment."); 608 609 RTE_VERIFY(test_func != NULL); 610 test_result = (*test_func)(); 611 612 /* If test succeed check if environment wast left in good condition. */ 613 if (test_result == TEST_SUCCESS) 614 test_result = check_environment(); 615 616 /* Reset environment in case test failed to do that. */ 617 if (test_result != TEST_SUCCESS) { 618 TEST_ASSERT_SUCCESS(remove_slaves_and_stop_bonded_device(), 619 "Failed to stop bonded device"); 620 } 621 622 return test_result; 623 } 624 625 static int 626 test_setup_wrapper(void) 627 { 628 return test_rssconf_executor(&test_setup); 629 } 630 631 static int 632 test_rss_wrapper(void) 633 { 634 return test_rssconf_executor(&test_rss); 635 } 636 637 static int 638 test_rss_lazy_wrapper(void) 639 { 640 return test_rssconf_executor(&test_rss_lazy); 641 } 642 643 static struct unit_test_suite link_bonding_rssconf_test_suite = { 644 .suite_name = "RSS Dynamic Configuration for Bonding Unit Test Suite", 645 .teardown = testsuite_teardown, 646 .unit_test_cases = { 647 TEST_CASE_NAMED("test_setup", test_setup_wrapper), 648 TEST_CASE_NAMED("test_rss", test_rss_wrapper), 649 TEST_CASE_NAMED("test_rss_lazy", test_rss_lazy_wrapper), 650 651 TEST_CASES_END() 652 } 653 }; 654 655 static int 656 test_link_bonding_rssconf(void) 657 { 658 return unit_test_suite_runner(&link_bonding_rssconf_test_suite); 659 } 660 661 REGISTER_TEST_COMMAND(link_bonding_rssconf_autotest, test_link_bonding_rssconf); 662