1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <string.h> 6 #include <rte_pipeline.h> 7 #include <rte_log.h> 8 #include <inttypes.h> 9 #include <rte_hexdump.h> 10 #include "test_table.h" 11 #include "test_table_pipeline.h" 12 13 #if 0 14 15 static rte_pipeline_port_out_action_handler port_action_0x00 16 (struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg); 17 static rte_pipeline_port_out_action_handler port_action_0xFF 18 (struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg); 19 static rte_pipeline_port_out_action_handler port_action_stub 20 (struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg); 21 22 23 rte_pipeline_port_out_action_handler port_action_0x00(struct rte_mbuf **pkts, 24 uint32_t n, 25 uint64_t *pkts_mask, 26 void *arg) 27 { 28 RTE_SET_USED(pkts); 29 RTE_SET_USED(n); 30 RTE_SET_USED(arg); 31 printf("Port Action 0x00\n"); 32 *pkts_mask = 0x00; 33 return 0; 34 } 35 36 rte_pipeline_port_out_action_handler port_action_0xFF(struct rte_mbuf **pkts, 37 uint32_t n, 38 uint64_t *pkts_mask, 39 void *arg) 40 { 41 RTE_SET_USED(pkts); 42 RTE_SET_USED(n); 43 RTE_SET_USED(arg); 44 printf("Port Action 0xFF\n"); 45 *pkts_mask = 0xFF; 46 return 0; 47 } 48 49 rte_pipeline_port_out_action_handler port_action_stub(struct rte_mbuf **pkts, 50 uint32_t n, 51 uint64_t *pkts_mask, 52 void *arg) 53 { 54 RTE_SET_USED(pkts); 55 RTE_SET_USED(n); 56 RTE_SET_USED(pkts_mask); 57 RTE_SET_USED(arg); 58 printf("Port Action stub\n"); 59 return 0; 60 } 61 62 #endif 63 64 rte_pipeline_table_action_handler_hit 65 table_action_0x00(struct rte_pipeline *p, struct rte_mbuf **pkts, 66 uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg); 67 68 rte_pipeline_table_action_handler_hit 69 table_action_stub_hit(struct rte_pipeline *p, struct rte_mbuf **pkts, 70 uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg); 71 72 static int 73 table_action_stub_miss(struct rte_pipeline *p, struct rte_mbuf **pkts, 74 uint64_t pkts_mask, struct rte_pipeline_table_entry *entry, void *arg); 75 76 rte_pipeline_table_action_handler_hit 77 table_action_0x00(__rte_unused struct rte_pipeline *p, 78 __rte_unused struct rte_mbuf **pkts, 79 uint64_t pkts_mask, 80 __rte_unused struct rte_pipeline_table_entry **entry, 81 __rte_unused void *arg) 82 { 83 printf("Table Action, setting pkts_mask to 0x00\n"); 84 pkts_mask = ~0x00; 85 rte_pipeline_ah_packet_drop(p, pkts_mask); 86 return 0; 87 } 88 89 rte_pipeline_table_action_handler_hit 90 table_action_stub_hit(__rte_unused struct rte_pipeline *p, 91 __rte_unused struct rte_mbuf **pkts, 92 uint64_t pkts_mask, 93 __rte_unused struct rte_pipeline_table_entry **entry, 94 __rte_unused void *arg) 95 { 96 printf("STUB Table Action Hit - doing nothing\n"); 97 printf("STUB Table Action Hit - setting mask to 0x%"PRIx64"\n", 98 override_hit_mask); 99 pkts_mask = (~override_hit_mask) & 0x3; 100 rte_pipeline_ah_packet_drop(p, pkts_mask); 101 return 0; 102 } 103 104 static int 105 table_action_stub_miss(struct rte_pipeline *p, 106 __rte_unused struct rte_mbuf **pkts, 107 uint64_t pkts_mask, 108 __rte_unused struct rte_pipeline_table_entry *entry, 109 __rte_unused void *arg) 110 { 111 printf("STUB Table Action Miss - setting mask to 0x%"PRIx64"\n", 112 override_miss_mask); 113 pkts_mask = (~override_miss_mask) & 0x3; 114 rte_pipeline_ah_packet_drop(p, pkts_mask); 115 return 0; 116 } 117 118 enum e_test_type { 119 e_TEST_STUB = 0, 120 e_TEST_LPM, 121 e_TEST_LPM6, 122 e_TEST_HASH_LRU_8, 123 e_TEST_HASH_LRU_16, 124 e_TEST_HASH_LRU_32, 125 e_TEST_HASH_EXT_8, 126 e_TEST_HASH_EXT_16, 127 e_TEST_HASH_EXT_32 128 }; 129 130 char pipeline_test_names[][64] = { 131 "Stub", 132 "LPM", 133 "LPMv6", 134 "8-bit LRU Hash", 135 "16-bit LRU Hash", 136 "32-bit LRU Hash", 137 "16-bit Ext Hash", 138 "8-bit Ext Hash", 139 "32-bit Ext Hash", 140 "" 141 }; 142 143 144 static int 145 cleanup_pipeline(void) 146 { 147 148 rte_pipeline_free(p); 149 150 return 0; 151 } 152 153 154 static int check_pipeline_invalid_params(void); 155 156 static int 157 check_pipeline_invalid_params(void) 158 { 159 struct rte_pipeline_params pipeline_params_1 = { 160 .name = NULL, 161 .socket_id = 0, 162 }; 163 struct rte_pipeline_params pipeline_params_2 = { 164 .name = "PIPELINE", 165 .socket_id = -1, 166 }; 167 struct rte_pipeline_params pipeline_params_3 = { 168 .name = "PIPELINE", 169 .socket_id = 127, 170 }; 171 172 p = rte_pipeline_create(NULL); 173 if (p != NULL) { 174 RTE_LOG(INFO, PIPELINE, 175 "%s: configured pipeline with null params\n", 176 __func__); 177 goto fail; 178 } 179 p = rte_pipeline_create(&pipeline_params_1); 180 if (p != NULL) { 181 RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with NULL " 182 "name\n", __func__); 183 goto fail; 184 } 185 186 p = rte_pipeline_create(&pipeline_params_2); 187 if (p != NULL) { 188 RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with invalid " 189 "socket\n", __func__); 190 goto fail; 191 } 192 193 if (rte_eal_has_hugepages()) { 194 p = rte_pipeline_create(&pipeline_params_3); 195 if (p != NULL) { 196 RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with " 197 "invalid socket\n", __func__); 198 goto fail; 199 } 200 } 201 202 /* Check pipeline consistency */ 203 if (!rte_pipeline_check(p)) { 204 rte_panic("Pipeline consistency reported as OK\n"); 205 goto fail; 206 } 207 208 209 return 0; 210 fail: 211 return -1; 212 } 213 214 215 static int 216 setup_pipeline(int test_type) 217 { 218 int ret; 219 int i; 220 struct rte_pipeline_params pipeline_params = { 221 .name = "PIPELINE", 222 .socket_id = 0, 223 }; 224 225 RTE_LOG(INFO, PIPELINE, "%s: **** Setting up %s test\n", 226 __func__, pipeline_test_names[test_type]); 227 228 /* Pipeline configuration */ 229 p = rte_pipeline_create(&pipeline_params); 230 if (p == NULL) { 231 RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n", 232 __func__); 233 goto fail; 234 } 235 236 ret = rte_pipeline_free(p); 237 if (ret != 0) { 238 RTE_LOG(INFO, PIPELINE, "%s: Failed to free pipeline\n", 239 __func__); 240 goto fail; 241 } 242 243 /* Pipeline configuration */ 244 p = rte_pipeline_create(&pipeline_params); 245 if (p == NULL) { 246 RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n", 247 __func__); 248 goto fail; 249 } 250 251 252 /* Input port configuration */ 253 for (i = 0; i < N_PORTS; i++) { 254 struct rte_port_ring_reader_params port_ring_params = { 255 .ring = rings_rx[i], 256 }; 257 258 struct rte_pipeline_port_in_params port_params = { 259 .ops = &rte_port_ring_reader_ops, 260 .arg_create = (void *) &port_ring_params, 261 .f_action = NULL, 262 .burst_size = BURST_SIZE, 263 }; 264 265 /* Put in action for some ports */ 266 if (i) 267 port_params.f_action = NULL; 268 269 ret = rte_pipeline_port_in_create(p, &port_params, 270 &port_in_id[i]); 271 if (ret) { 272 rte_panic("Unable to configure input port %d, ret:%d\n", 273 i, ret); 274 goto fail; 275 } 276 } 277 278 /* output Port configuration */ 279 for (i = 0; i < N_PORTS; i++) { 280 struct rte_port_ring_writer_params port_ring_params = { 281 .ring = rings_tx[i], 282 .tx_burst_sz = BURST_SIZE, 283 }; 284 285 struct rte_pipeline_port_out_params port_params = { 286 .ops = &rte_port_ring_writer_ops, 287 .arg_create = (void *) &port_ring_params, 288 .f_action = NULL, 289 .arg_ah = NULL, 290 }; 291 292 if (i) 293 port_params.f_action = port_out_action; 294 295 if (rte_pipeline_port_out_create(p, &port_params, 296 &port_out_id[i])) { 297 rte_panic("Unable to configure output port %d\n", i); 298 goto fail; 299 } 300 } 301 302 /* Table configuration */ 303 for (i = 0; i < N_PORTS; i++) { 304 struct rte_pipeline_table_params table_params = { 305 .ops = &rte_table_stub_ops, 306 .arg_create = NULL, 307 .f_action_hit = action_handler_hit, 308 .f_action_miss = action_handler_miss, 309 .action_data_size = 0, 310 }; 311 312 if (rte_pipeline_table_create(p, &table_params, &table_id[i])) { 313 rte_panic("Unable to configure table %u\n", i); 314 goto fail; 315 } 316 317 if (connect_miss_action_to_table) 318 if (rte_pipeline_table_create(p, &table_params, 319 &table_id[i+2])) { 320 rte_panic("Unable to configure table %u\n", i); 321 goto fail; 322 } 323 } 324 325 for (i = 0; i < N_PORTS; i++) 326 if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i], 327 table_id[i])) { 328 rte_panic("Unable to connect input port %u to " 329 "table %u\n", port_in_id[i], table_id[i]); 330 goto fail; 331 } 332 333 /* Add entries to tables */ 334 for (i = 0; i < N_PORTS; i++) { 335 struct rte_pipeline_table_entry default_entry = { 336 .action = (enum rte_pipeline_action) 337 table_entry_default_action, 338 {.port_id = port_out_id[i^1]}, 339 }; 340 struct rte_pipeline_table_entry *default_entry_ptr; 341 342 if (connect_miss_action_to_table) { 343 printf("Setting first table to output to next table\n"); 344 default_entry.action = RTE_PIPELINE_ACTION_TABLE; 345 default_entry.table_id = table_id[i+2]; 346 } 347 348 /* Add the default action for the table. */ 349 ret = rte_pipeline_table_default_entry_add(p, table_id[i], 350 &default_entry, &default_entry_ptr); 351 if (ret < 0) { 352 rte_panic("Unable to add default entry to table %u " 353 "code %d\n", table_id[i], ret); 354 goto fail; 355 } else 356 printf("Added default entry to table id %d with " 357 "action %x\n", 358 table_id[i], default_entry.action); 359 360 if (connect_miss_action_to_table) { 361 /* We create a second table so the first can pass 362 traffic into it */ 363 struct rte_pipeline_table_entry default_entry = { 364 .action = RTE_PIPELINE_ACTION_PORT, 365 {.port_id = port_out_id[i^1]}, 366 }; 367 printf("Setting secont table to output to port\n"); 368 369 /* Add the default action for the table. */ 370 ret = rte_pipeline_table_default_entry_add(p, 371 table_id[i+2], 372 &default_entry, &default_entry_ptr); 373 if (ret < 0) { 374 rte_panic("Unable to add default entry to " 375 "table %u code %d\n", 376 table_id[i], ret); 377 goto fail; 378 } else 379 printf("Added default entry to table id %d " 380 "with action %x\n", 381 table_id[i], default_entry.action); 382 } 383 } 384 385 /* Enable input ports */ 386 for (i = 0; i < N_PORTS ; i++) 387 if (rte_pipeline_port_in_enable(p, port_in_id[i])) 388 rte_panic("Unable to enable input port %u\n", 389 port_in_id[i]); 390 391 /* Check pipeline consistency */ 392 if (rte_pipeline_check(p) < 0) { 393 rte_panic("Pipeline consistency check failed\n"); 394 goto fail; 395 } else 396 printf("Pipeline Consistency OK!\n"); 397 398 return 0; 399 fail: 400 401 return -1; 402 } 403 404 static int 405 test_pipeline_single_filter(int test_type, int expected_count) 406 { 407 int i; 408 int j; 409 int ret; 410 int tx_count; 411 412 RTE_LOG(INFO, PIPELINE, "%s: **** Running %s test\n", 413 __func__, pipeline_test_names[test_type]); 414 /* Run pipeline once */ 415 for (i = 0; i < N_PORTS; i++) 416 rte_pipeline_run(p); 417 418 419 ret = rte_pipeline_flush(NULL); 420 if (ret != -EINVAL) { 421 RTE_LOG(INFO, PIPELINE, 422 "%s: No pipeline flush error NULL pipeline (%d)\n", 423 __func__, ret); 424 goto fail; 425 } 426 427 /* 428 * Allocate a few mbufs and manually insert into the rings. */ 429 for (i = 0; i < N_PORTS; i++) 430 for (j = 0; j < N_PORTS; j++) { 431 struct rte_mbuf *m; 432 uint8_t *key; 433 uint32_t *k32; 434 435 m = rte_pktmbuf_alloc(pool); 436 if (m == NULL) { 437 rte_panic("Failed to alloc mbuf from pool\n"); 438 return -1; 439 } 440 key = RTE_MBUF_METADATA_UINT8_PTR(m, 441 APP_METADATA_OFFSET(32)); 442 443 k32 = (uint32_t *) key; 444 k32[0] = 0xadadadad >> (j % 2); 445 446 RTE_LOG(INFO, PIPELINE, "%s: Enqueue onto ring %d\n", 447 __func__, i); 448 rte_ring_enqueue(rings_rx[i], m); 449 } 450 451 /* Run pipeline once */ 452 for (i = 0; i < N_PORTS; i++) 453 rte_pipeline_run(p); 454 455 /* 456 * need to flush the pipeline, as there may be less hits than the burst 457 size and they will not have been flushed to the tx rings. */ 458 rte_pipeline_flush(p); 459 460 /* 461 * Now we'll see what we got back on the tx rings. We should see whatever 462 * packets we had hits on that were destined for the output ports. 463 */ 464 tx_count = 0; 465 466 for (i = 0; i < N_PORTS; i++) { 467 void *objs[RING_TX_SIZE]; 468 struct rte_mbuf *mbuf; 469 470 ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10, NULL); 471 if (ret <= 0) 472 printf("Got no objects from ring %d - error code %d\n", 473 i, ret); 474 else { 475 printf("Got %d object(s) from ring %d!\n", ret, i); 476 for (j = 0; j < ret; j++) { 477 mbuf = objs[j]; 478 rte_hexdump(stdout, "Object:", 479 rte_pktmbuf_mtod(mbuf, char *), 480 mbuf->data_len); 481 rte_pktmbuf_free(mbuf); 482 } 483 tx_count += ret; 484 } 485 } 486 487 if (tx_count != expected_count) { 488 RTE_LOG(INFO, PIPELINE, 489 "%s: Unexpected packets out for %s test, expected %d, " 490 "got %d\n", __func__, pipeline_test_names[test_type], 491 expected_count, tx_count); 492 goto fail; 493 } 494 495 cleanup_pipeline(); 496 497 return 0; 498 fail: 499 return -1; 500 501 } 502 503 int 504 test_table_pipeline(void) 505 { 506 /* TEST - All packets dropped */ 507 action_handler_hit = NULL; 508 action_handler_miss = NULL; 509 table_entry_default_action = RTE_PIPELINE_ACTION_DROP; 510 setup_pipeline(e_TEST_STUB); 511 if (test_pipeline_single_filter(e_TEST_STUB, 0) < 0) 512 return -1; 513 514 /* TEST - All packets passed through */ 515 table_entry_default_action = RTE_PIPELINE_ACTION_PORT; 516 setup_pipeline(e_TEST_STUB); 517 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 518 return -1; 519 520 /* TEST - one packet per port */ 521 action_handler_hit = NULL; 522 action_handler_miss = table_action_stub_miss; 523 table_entry_default_action = RTE_PIPELINE_ACTION_PORT; 524 override_miss_mask = 0x01; /* one packet per port */ 525 setup_pipeline(e_TEST_STUB); 526 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 527 return -1; 528 529 /* TEST - one packet per port */ 530 override_miss_mask = 0x02; /*all per port */ 531 setup_pipeline(e_TEST_STUB); 532 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 533 return -1; 534 535 /* TEST - all packets per port */ 536 override_miss_mask = 0x03; /*all per port */ 537 setup_pipeline(e_TEST_STUB); 538 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 539 return -1; 540 541 /* 542 * This test will set up two tables in the pipeline. the first table 543 * will forward to another table on miss, and the second table will 544 * forward to port. 545 */ 546 connect_miss_action_to_table = 1; 547 table_entry_default_action = RTE_PIPELINE_ACTION_TABLE; 548 action_handler_hit = NULL; /* not for stub, hitmask always zero */ 549 action_handler_miss = NULL; 550 setup_pipeline(e_TEST_STUB); 551 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 552 return -1; 553 connect_miss_action_to_table = 0; 554 555 printf("TEST - two tables, hitmask override to 0x01\n"); 556 connect_miss_action_to_table = 1; 557 action_handler_miss = table_action_stub_miss; 558 override_miss_mask = 0x01; 559 setup_pipeline(e_TEST_STUB); 560 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 561 return -1; 562 connect_miss_action_to_table = 0; 563 564 if (check_pipeline_invalid_params()) { 565 RTE_LOG(INFO, PIPELINE, "%s: Check pipeline invalid params " 566 "failed.\n", __func__); 567 return -1; 568 } 569 570 return 0; 571 } 572