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(__attribute__((unused)) struct rte_pipeline *p, 78 __attribute__((unused)) struct rte_mbuf **pkts, 79 uint64_t pkts_mask, 80 __attribute__((unused)) struct rte_pipeline_table_entry **entry, 81 __attribute__((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(__attribute__((unused)) struct rte_pipeline *p, 91 __attribute__((unused)) struct rte_mbuf **pkts, 92 uint64_t pkts_mask, 93 __attribute__((unused)) struct rte_pipeline_table_entry **entry, 94 __attribute__((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 __attribute__((unused)) struct rte_mbuf **pkts, 107 uint64_t pkts_mask, 108 __attribute__((unused)) struct rte_pipeline_table_entry *entry, 109 __attribute__((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 p = rte_pipeline_create(&pipeline_params_3); 194 if (p != NULL) { 195 RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with invalid " 196 "socket\n", __func__); 197 goto fail; 198 } 199 200 /* Check pipeline consistency */ 201 if (!rte_pipeline_check(p)) { 202 rte_panic("Pipeline consistency reported as OK\n"); 203 goto fail; 204 } 205 206 207 return 0; 208 fail: 209 return -1; 210 } 211 212 213 static int 214 setup_pipeline(int test_type) 215 { 216 int ret; 217 int i; 218 struct rte_pipeline_params pipeline_params = { 219 .name = "PIPELINE", 220 .socket_id = 0, 221 }; 222 223 RTE_LOG(INFO, PIPELINE, "%s: **** Setting up %s test\n", 224 __func__, pipeline_test_names[test_type]); 225 226 /* Pipeline configuration */ 227 p = rte_pipeline_create(&pipeline_params); 228 if (p == NULL) { 229 RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n", 230 __func__); 231 goto fail; 232 } 233 234 ret = rte_pipeline_free(p); 235 if (ret != 0) { 236 RTE_LOG(INFO, PIPELINE, "%s: Failed to free pipeline\n", 237 __func__); 238 goto fail; 239 } 240 241 /* Pipeline configuration */ 242 p = rte_pipeline_create(&pipeline_params); 243 if (p == NULL) { 244 RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n", 245 __func__); 246 goto fail; 247 } 248 249 250 /* Input port configuration */ 251 for (i = 0; i < N_PORTS; i++) { 252 struct rte_port_ring_reader_params port_ring_params = { 253 .ring = rings_rx[i], 254 }; 255 256 struct rte_pipeline_port_in_params port_params = { 257 .ops = &rte_port_ring_reader_ops, 258 .arg_create = (void *) &port_ring_params, 259 .f_action = NULL, 260 .burst_size = BURST_SIZE, 261 }; 262 263 /* Put in action for some ports */ 264 if (i) 265 port_params.f_action = NULL; 266 267 ret = rte_pipeline_port_in_create(p, &port_params, 268 &port_in_id[i]); 269 if (ret) { 270 rte_panic("Unable to configure input port %d, ret:%d\n", 271 i, ret); 272 goto fail; 273 } 274 } 275 276 /* output Port configuration */ 277 for (i = 0; i < N_PORTS; i++) { 278 struct rte_port_ring_writer_params port_ring_params = { 279 .ring = rings_tx[i], 280 .tx_burst_sz = BURST_SIZE, 281 }; 282 283 struct rte_pipeline_port_out_params port_params = { 284 .ops = &rte_port_ring_writer_ops, 285 .arg_create = (void *) &port_ring_params, 286 .f_action = NULL, 287 .arg_ah = NULL, 288 }; 289 290 if (i) 291 port_params.f_action = port_out_action; 292 293 if (rte_pipeline_port_out_create(p, &port_params, 294 &port_out_id[i])) { 295 rte_panic("Unable to configure output port %d\n", i); 296 goto fail; 297 } 298 } 299 300 /* Table configuration */ 301 for (i = 0; i < N_PORTS; i++) { 302 struct rte_pipeline_table_params table_params = { 303 .ops = &rte_table_stub_ops, 304 .arg_create = NULL, 305 .f_action_hit = action_handler_hit, 306 .f_action_miss = action_handler_miss, 307 .action_data_size = 0, 308 }; 309 310 if (rte_pipeline_table_create(p, &table_params, &table_id[i])) { 311 rte_panic("Unable to configure table %u\n", i); 312 goto fail; 313 } 314 315 if (connect_miss_action_to_table) 316 if (rte_pipeline_table_create(p, &table_params, 317 &table_id[i+2])) { 318 rte_panic("Unable to configure table %u\n", i); 319 goto fail; 320 } 321 } 322 323 for (i = 0; i < N_PORTS; i++) 324 if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i], 325 table_id[i])) { 326 rte_panic("Unable to connect input port %u to " 327 "table %u\n", port_in_id[i], table_id[i]); 328 goto fail; 329 } 330 331 /* Add entries to tables */ 332 for (i = 0; i < N_PORTS; i++) { 333 struct rte_pipeline_table_entry default_entry = { 334 .action = (enum rte_pipeline_action) 335 table_entry_default_action, 336 {.port_id = port_out_id[i^1]}, 337 }; 338 struct rte_pipeline_table_entry *default_entry_ptr; 339 340 if (connect_miss_action_to_table) { 341 printf("Setting first table to output to next table\n"); 342 default_entry.action = RTE_PIPELINE_ACTION_TABLE; 343 default_entry.table_id = table_id[i+2]; 344 } 345 346 /* Add the default action for the table. */ 347 ret = rte_pipeline_table_default_entry_add(p, table_id[i], 348 &default_entry, &default_entry_ptr); 349 if (ret < 0) { 350 rte_panic("Unable to add default entry to table %u " 351 "code %d\n", table_id[i], ret); 352 goto fail; 353 } else 354 printf("Added default entry to table id %d with " 355 "action %x\n", 356 table_id[i], default_entry.action); 357 358 if (connect_miss_action_to_table) { 359 /* We create a second table so the first can pass 360 traffic into it */ 361 struct rte_pipeline_table_entry default_entry = { 362 .action = RTE_PIPELINE_ACTION_PORT, 363 {.port_id = port_out_id[i^1]}, 364 }; 365 printf("Setting secont table to output to port\n"); 366 367 /* Add the default action for the table. */ 368 ret = rte_pipeline_table_default_entry_add(p, 369 table_id[i+2], 370 &default_entry, &default_entry_ptr); 371 if (ret < 0) { 372 rte_panic("Unable to add default entry to " 373 "table %u code %d\n", 374 table_id[i], ret); 375 goto fail; 376 } else 377 printf("Added default entry to table id %d " 378 "with action %x\n", 379 table_id[i], default_entry.action); 380 } 381 } 382 383 /* Enable input ports */ 384 for (i = 0; i < N_PORTS ; i++) 385 if (rte_pipeline_port_in_enable(p, port_in_id[i])) 386 rte_panic("Unable to enable input port %u\n", 387 port_in_id[i]); 388 389 /* Check pipeline consistency */ 390 if (rte_pipeline_check(p) < 0) { 391 rte_panic("Pipeline consistency check failed\n"); 392 goto fail; 393 } else 394 printf("Pipeline Consistency OK!\n"); 395 396 return 0; 397 fail: 398 399 return -1; 400 } 401 402 static int 403 test_pipeline_single_filter(int test_type, int expected_count) 404 { 405 int i; 406 int j; 407 int ret; 408 int tx_count; 409 410 RTE_LOG(INFO, PIPELINE, "%s: **** Running %s test\n", 411 __func__, pipeline_test_names[test_type]); 412 /* Run pipeline once */ 413 for (i = 0; i < N_PORTS; i++) 414 rte_pipeline_run(p); 415 416 417 ret = rte_pipeline_flush(NULL); 418 if (ret != -EINVAL) { 419 RTE_LOG(INFO, PIPELINE, 420 "%s: No pipeline flush error NULL pipeline (%d)\n", 421 __func__, ret); 422 goto fail; 423 } 424 425 /* 426 * Allocate a few mbufs and manually insert into the rings. */ 427 for (i = 0; i < N_PORTS; i++) 428 for (j = 0; j < N_PORTS; j++) { 429 struct rte_mbuf *m; 430 uint8_t *key; 431 uint32_t *k32; 432 433 m = rte_pktmbuf_alloc(pool); 434 if (m == NULL) { 435 rte_panic("Failed to alloc mbuf from pool\n"); 436 return -1; 437 } 438 key = RTE_MBUF_METADATA_UINT8_PTR(m, 439 APP_METADATA_OFFSET(32)); 440 441 k32 = (uint32_t *) key; 442 k32[0] = 0xadadadad >> (j % 2); 443 444 RTE_LOG(INFO, PIPELINE, "%s: Enqueue onto ring %d\n", 445 __func__, i); 446 rte_ring_enqueue(rings_rx[i], m); 447 } 448 449 /* Run pipeline once */ 450 for (i = 0; i < N_PORTS; i++) 451 rte_pipeline_run(p); 452 453 /* 454 * need to flush the pipeline, as there may be less hits than the burst 455 size and they will not have been flushed to the tx rings. */ 456 rte_pipeline_flush(p); 457 458 /* 459 * Now we'll see what we got back on the tx rings. We should see whatever 460 * packets we had hits on that were destined for the output ports. 461 */ 462 tx_count = 0; 463 464 for (i = 0; i < N_PORTS; i++) { 465 void *objs[RING_TX_SIZE]; 466 struct rte_mbuf *mbuf; 467 468 ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10, NULL); 469 if (ret <= 0) 470 printf("Got no objects from ring %d - error code %d\n", 471 i, ret); 472 else { 473 printf("Got %d object(s) from ring %d!\n", ret, i); 474 for (j = 0; j < ret; j++) { 475 mbuf = objs[j]; 476 rte_hexdump(stdout, "Object:", 477 rte_pktmbuf_mtod(mbuf, char *), 478 mbuf->data_len); 479 rte_pktmbuf_free(mbuf); 480 } 481 tx_count += ret; 482 } 483 } 484 485 if (tx_count != expected_count) { 486 RTE_LOG(INFO, PIPELINE, 487 "%s: Unexpected packets out for %s test, expected %d, " 488 "got %d\n", __func__, pipeline_test_names[test_type], 489 expected_count, tx_count); 490 goto fail; 491 } 492 493 cleanup_pipeline(); 494 495 return 0; 496 fail: 497 return -1; 498 499 } 500 501 int 502 test_table_pipeline(void) 503 { 504 /* TEST - All packets dropped */ 505 action_handler_hit = NULL; 506 action_handler_miss = NULL; 507 table_entry_default_action = RTE_PIPELINE_ACTION_DROP; 508 setup_pipeline(e_TEST_STUB); 509 if (test_pipeline_single_filter(e_TEST_STUB, 0) < 0) 510 return -1; 511 512 /* TEST - All packets passed through */ 513 table_entry_default_action = RTE_PIPELINE_ACTION_PORT; 514 setup_pipeline(e_TEST_STUB); 515 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 516 return -1; 517 518 /* TEST - one packet per port */ 519 action_handler_hit = NULL; 520 action_handler_miss = table_action_stub_miss; 521 table_entry_default_action = RTE_PIPELINE_ACTION_PORT; 522 override_miss_mask = 0x01; /* one packet per port */ 523 setup_pipeline(e_TEST_STUB); 524 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 525 return -1; 526 527 /* TEST - one packet per port */ 528 override_miss_mask = 0x02; /*all per port */ 529 setup_pipeline(e_TEST_STUB); 530 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 531 return -1; 532 533 /* TEST - all packets per port */ 534 override_miss_mask = 0x03; /*all per port */ 535 setup_pipeline(e_TEST_STUB); 536 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 537 return -1; 538 539 /* 540 * This test will set up two tables in the pipeline. the first table 541 * will forward to another table on miss, and the second table will 542 * forward to port. 543 */ 544 connect_miss_action_to_table = 1; 545 table_entry_default_action = RTE_PIPELINE_ACTION_TABLE; 546 action_handler_hit = NULL; /* not for stub, hitmask always zero */ 547 action_handler_miss = NULL; 548 setup_pipeline(e_TEST_STUB); 549 if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0) 550 return -1; 551 connect_miss_action_to_table = 0; 552 553 printf("TEST - two tables, hitmask override to 0x01\n"); 554 connect_miss_action_to_table = 1; 555 action_handler_miss = table_action_stub_miss; 556 override_miss_mask = 0x01; 557 setup_pipeline(e_TEST_STUB); 558 if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0) 559 return -1; 560 connect_miss_action_to_table = 0; 561 562 if (check_pipeline_invalid_params()) { 563 RTE_LOG(INFO, PIPELINE, "%s: Check pipeline invalid params " 564 "failed.\n", __func__); 565 return -1; 566 } 567 568 return 0; 569 } 570