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