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