a9de470cc7
Since all other apps have been moved to the "app" folder, the autotest app remains alone in the test folder. Rather than having an entire top-level folder for this, we can move it back to where it all started in early versions of DPDK - the "app/" folder. This move has a couple of advantages: * This reduces clutter at the top level of the project, due to one less folder. * It eliminates the separate build task necessary for building the autotests using make "make test-build" which means that developers are less likely to miss something in their own compilation tests * It re-aligns the final location of the test binary in the app folder when building with make with it's location in the source tree. For meson builds, the autotest app is different from the other apps in that it needs a series of different test cases defined for it for use by "meson test". Therefore, it does not get built as part of the main loop in the app folder, but gets built separately at the end. Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
570 lines
14 KiB
C
570 lines
14 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2014 Intel Corporation
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <rte_pipeline.h>
|
|
#include <rte_log.h>
|
|
#include <inttypes.h>
|
|
#include <rte_hexdump.h>
|
|
#include "test_table.h"
|
|
#include "test_table_pipeline.h"
|
|
|
|
#if 0
|
|
|
|
static rte_pipeline_port_out_action_handler port_action_0x00
|
|
(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
|
|
static rte_pipeline_port_out_action_handler port_action_0xFF
|
|
(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
|
|
static rte_pipeline_port_out_action_handler port_action_stub
|
|
(struct rte_mbuf **pkts, uint32_t n, uint64_t *pkts_mask, void *arg);
|
|
|
|
|
|
rte_pipeline_port_out_action_handler port_action_0x00(struct rte_mbuf **pkts,
|
|
uint32_t n,
|
|
uint64_t *pkts_mask,
|
|
void *arg)
|
|
{
|
|
RTE_SET_USED(pkts);
|
|
RTE_SET_USED(n);
|
|
RTE_SET_USED(arg);
|
|
printf("Port Action 0x00\n");
|
|
*pkts_mask = 0x00;
|
|
return 0;
|
|
}
|
|
|
|
rte_pipeline_port_out_action_handler port_action_0xFF(struct rte_mbuf **pkts,
|
|
uint32_t n,
|
|
uint64_t *pkts_mask,
|
|
void *arg)
|
|
{
|
|
RTE_SET_USED(pkts);
|
|
RTE_SET_USED(n);
|
|
RTE_SET_USED(arg);
|
|
printf("Port Action 0xFF\n");
|
|
*pkts_mask = 0xFF;
|
|
return 0;
|
|
}
|
|
|
|
rte_pipeline_port_out_action_handler port_action_stub(struct rte_mbuf **pkts,
|
|
uint32_t n,
|
|
uint64_t *pkts_mask,
|
|
void *arg)
|
|
{
|
|
RTE_SET_USED(pkts);
|
|
RTE_SET_USED(n);
|
|
RTE_SET_USED(pkts_mask);
|
|
RTE_SET_USED(arg);
|
|
printf("Port Action stub\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
rte_pipeline_table_action_handler_hit
|
|
table_action_0x00(struct rte_pipeline *p, struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg);
|
|
|
|
rte_pipeline_table_action_handler_hit
|
|
table_action_stub_hit(struct rte_pipeline *p, struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask, struct rte_pipeline_table_entry **entry, void *arg);
|
|
|
|
static int
|
|
table_action_stub_miss(struct rte_pipeline *p, struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask, struct rte_pipeline_table_entry *entry, void *arg);
|
|
|
|
rte_pipeline_table_action_handler_hit
|
|
table_action_0x00(__attribute__((unused)) struct rte_pipeline *p,
|
|
__attribute__((unused)) struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask,
|
|
__attribute__((unused)) struct rte_pipeline_table_entry **entry,
|
|
__attribute__((unused)) void *arg)
|
|
{
|
|
printf("Table Action, setting pkts_mask to 0x00\n");
|
|
pkts_mask = ~0x00;
|
|
rte_pipeline_ah_packet_drop(p, pkts_mask);
|
|
return 0;
|
|
}
|
|
|
|
rte_pipeline_table_action_handler_hit
|
|
table_action_stub_hit(__attribute__((unused)) struct rte_pipeline *p,
|
|
__attribute__((unused)) struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask,
|
|
__attribute__((unused)) struct rte_pipeline_table_entry **entry,
|
|
__attribute__((unused)) void *arg)
|
|
{
|
|
printf("STUB Table Action Hit - doing nothing\n");
|
|
printf("STUB Table Action Hit - setting mask to 0x%"PRIx64"\n",
|
|
override_hit_mask);
|
|
pkts_mask = (~override_hit_mask) & 0x3;
|
|
rte_pipeline_ah_packet_drop(p, pkts_mask);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
table_action_stub_miss(struct rte_pipeline *p,
|
|
__attribute__((unused)) struct rte_mbuf **pkts,
|
|
uint64_t pkts_mask,
|
|
__attribute__((unused)) struct rte_pipeline_table_entry *entry,
|
|
__attribute__((unused)) void *arg)
|
|
{
|
|
printf("STUB Table Action Miss - setting mask to 0x%"PRIx64"\n",
|
|
override_miss_mask);
|
|
pkts_mask = (~override_miss_mask) & 0x3;
|
|
rte_pipeline_ah_packet_drop(p, pkts_mask);
|
|
return 0;
|
|
}
|
|
|
|
enum e_test_type {
|
|
e_TEST_STUB = 0,
|
|
e_TEST_LPM,
|
|
e_TEST_LPM6,
|
|
e_TEST_HASH_LRU_8,
|
|
e_TEST_HASH_LRU_16,
|
|
e_TEST_HASH_LRU_32,
|
|
e_TEST_HASH_EXT_8,
|
|
e_TEST_HASH_EXT_16,
|
|
e_TEST_HASH_EXT_32
|
|
};
|
|
|
|
char pipeline_test_names[][64] = {
|
|
"Stub",
|
|
"LPM",
|
|
"LPMv6",
|
|
"8-bit LRU Hash",
|
|
"16-bit LRU Hash",
|
|
"32-bit LRU Hash",
|
|
"16-bit Ext Hash",
|
|
"8-bit Ext Hash",
|
|
"32-bit Ext Hash",
|
|
""
|
|
};
|
|
|
|
|
|
static int
|
|
cleanup_pipeline(void)
|
|
{
|
|
|
|
rte_pipeline_free(p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int check_pipeline_invalid_params(void);
|
|
|
|
static int
|
|
check_pipeline_invalid_params(void)
|
|
{
|
|
struct rte_pipeline_params pipeline_params_1 = {
|
|
.name = NULL,
|
|
.socket_id = 0,
|
|
};
|
|
struct rte_pipeline_params pipeline_params_2 = {
|
|
.name = "PIPELINE",
|
|
.socket_id = -1,
|
|
};
|
|
struct rte_pipeline_params pipeline_params_3 = {
|
|
.name = "PIPELINE",
|
|
.socket_id = 127,
|
|
};
|
|
|
|
p = rte_pipeline_create(NULL);
|
|
if (p != NULL) {
|
|
RTE_LOG(INFO, PIPELINE,
|
|
"%s: configured pipeline with null params\n",
|
|
__func__);
|
|
goto fail;
|
|
}
|
|
p = rte_pipeline_create(&pipeline_params_1);
|
|
if (p != NULL) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with NULL "
|
|
"name\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
p = rte_pipeline_create(&pipeline_params_2);
|
|
if (p != NULL) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with invalid "
|
|
"socket\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
p = rte_pipeline_create(&pipeline_params_3);
|
|
if (p != NULL) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Configure pipeline with invalid "
|
|
"socket\n", __func__);
|
|
goto fail;
|
|
}
|
|
|
|
/* Check pipeline consistency */
|
|
if (!rte_pipeline_check(p)) {
|
|
rte_panic("Pipeline consistency reported as OK\n");
|
|
goto fail;
|
|
}
|
|
|
|
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
setup_pipeline(int test_type)
|
|
{
|
|
int ret;
|
|
int i;
|
|
struct rte_pipeline_params pipeline_params = {
|
|
.name = "PIPELINE",
|
|
.socket_id = 0,
|
|
};
|
|
|
|
RTE_LOG(INFO, PIPELINE, "%s: **** Setting up %s test\n",
|
|
__func__, pipeline_test_names[test_type]);
|
|
|
|
/* Pipeline configuration */
|
|
p = rte_pipeline_create(&pipeline_params);
|
|
if (p == NULL) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n",
|
|
__func__);
|
|
goto fail;
|
|
}
|
|
|
|
ret = rte_pipeline_free(p);
|
|
if (ret != 0) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Failed to free pipeline\n",
|
|
__func__);
|
|
goto fail;
|
|
}
|
|
|
|
/* Pipeline configuration */
|
|
p = rte_pipeline_create(&pipeline_params);
|
|
if (p == NULL) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n",
|
|
__func__);
|
|
goto fail;
|
|
}
|
|
|
|
|
|
/* Input port configuration */
|
|
for (i = 0; i < N_PORTS; i++) {
|
|
struct rte_port_ring_reader_params port_ring_params = {
|
|
.ring = rings_rx[i],
|
|
};
|
|
|
|
struct rte_pipeline_port_in_params port_params = {
|
|
.ops = &rte_port_ring_reader_ops,
|
|
.arg_create = (void *) &port_ring_params,
|
|
.f_action = NULL,
|
|
.burst_size = BURST_SIZE,
|
|
};
|
|
|
|
/* Put in action for some ports */
|
|
if (i)
|
|
port_params.f_action = NULL;
|
|
|
|
ret = rte_pipeline_port_in_create(p, &port_params,
|
|
&port_in_id[i]);
|
|
if (ret) {
|
|
rte_panic("Unable to configure input port %d, ret:%d\n",
|
|
i, ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* output Port configuration */
|
|
for (i = 0; i < N_PORTS; i++) {
|
|
struct rte_port_ring_writer_params port_ring_params = {
|
|
.ring = rings_tx[i],
|
|
.tx_burst_sz = BURST_SIZE,
|
|
};
|
|
|
|
struct rte_pipeline_port_out_params port_params = {
|
|
.ops = &rte_port_ring_writer_ops,
|
|
.arg_create = (void *) &port_ring_params,
|
|
.f_action = NULL,
|
|
.arg_ah = NULL,
|
|
};
|
|
|
|
if (i)
|
|
port_params.f_action = port_out_action;
|
|
|
|
if (rte_pipeline_port_out_create(p, &port_params,
|
|
&port_out_id[i])) {
|
|
rte_panic("Unable to configure output port %d\n", i);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* Table configuration */
|
|
for (i = 0; i < N_PORTS; i++) {
|
|
struct rte_pipeline_table_params table_params = {
|
|
.ops = &rte_table_stub_ops,
|
|
.arg_create = NULL,
|
|
.f_action_hit = action_handler_hit,
|
|
.f_action_miss = action_handler_miss,
|
|
.action_data_size = 0,
|
|
};
|
|
|
|
if (rte_pipeline_table_create(p, &table_params, &table_id[i])) {
|
|
rte_panic("Unable to configure table %u\n", i);
|
|
goto fail;
|
|
}
|
|
|
|
if (connect_miss_action_to_table)
|
|
if (rte_pipeline_table_create(p, &table_params,
|
|
&table_id[i+2])) {
|
|
rte_panic("Unable to configure table %u\n", i);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < N_PORTS; i++)
|
|
if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i],
|
|
table_id[i])) {
|
|
rte_panic("Unable to connect input port %u to "
|
|
"table %u\n", port_in_id[i], table_id[i]);
|
|
goto fail;
|
|
}
|
|
|
|
/* Add entries to tables */
|
|
for (i = 0; i < N_PORTS; i++) {
|
|
struct rte_pipeline_table_entry default_entry = {
|
|
.action = (enum rte_pipeline_action)
|
|
table_entry_default_action,
|
|
{.port_id = port_out_id[i^1]},
|
|
};
|
|
struct rte_pipeline_table_entry *default_entry_ptr;
|
|
|
|
if (connect_miss_action_to_table) {
|
|
printf("Setting first table to output to next table\n");
|
|
default_entry.action = RTE_PIPELINE_ACTION_TABLE;
|
|
default_entry.table_id = table_id[i+2];
|
|
}
|
|
|
|
/* Add the default action for the table. */
|
|
ret = rte_pipeline_table_default_entry_add(p, table_id[i],
|
|
&default_entry, &default_entry_ptr);
|
|
if (ret < 0) {
|
|
rte_panic("Unable to add default entry to table %u "
|
|
"code %d\n", table_id[i], ret);
|
|
goto fail;
|
|
} else
|
|
printf("Added default entry to table id %d with "
|
|
"action %x\n",
|
|
table_id[i], default_entry.action);
|
|
|
|
if (connect_miss_action_to_table) {
|
|
/* We create a second table so the first can pass
|
|
traffic into it */
|
|
struct rte_pipeline_table_entry default_entry = {
|
|
.action = RTE_PIPELINE_ACTION_PORT,
|
|
{.port_id = port_out_id[i^1]},
|
|
};
|
|
printf("Setting secont table to output to port\n");
|
|
|
|
/* Add the default action for the table. */
|
|
ret = rte_pipeline_table_default_entry_add(p,
|
|
table_id[i+2],
|
|
&default_entry, &default_entry_ptr);
|
|
if (ret < 0) {
|
|
rte_panic("Unable to add default entry to "
|
|
"table %u code %d\n",
|
|
table_id[i], ret);
|
|
goto fail;
|
|
} else
|
|
printf("Added default entry to table id %d "
|
|
"with action %x\n",
|
|
table_id[i], default_entry.action);
|
|
}
|
|
}
|
|
|
|
/* Enable input ports */
|
|
for (i = 0; i < N_PORTS ; i++)
|
|
if (rte_pipeline_port_in_enable(p, port_in_id[i]))
|
|
rte_panic("Unable to enable input port %u\n",
|
|
port_in_id[i]);
|
|
|
|
/* Check pipeline consistency */
|
|
if (rte_pipeline_check(p) < 0) {
|
|
rte_panic("Pipeline consistency check failed\n");
|
|
goto fail;
|
|
} else
|
|
printf("Pipeline Consistency OK!\n");
|
|
|
|
return 0;
|
|
fail:
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
test_pipeline_single_filter(int test_type, int expected_count)
|
|
{
|
|
int i;
|
|
int j;
|
|
int ret;
|
|
int tx_count;
|
|
|
|
RTE_LOG(INFO, PIPELINE, "%s: **** Running %s test\n",
|
|
__func__, pipeline_test_names[test_type]);
|
|
/* Run pipeline once */
|
|
for (i = 0; i < N_PORTS; i++)
|
|
rte_pipeline_run(p);
|
|
|
|
|
|
ret = rte_pipeline_flush(NULL);
|
|
if (ret != -EINVAL) {
|
|
RTE_LOG(INFO, PIPELINE,
|
|
"%s: No pipeline flush error NULL pipeline (%d)\n",
|
|
__func__, ret);
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Allocate a few mbufs and manually insert into the rings. */
|
|
for (i = 0; i < N_PORTS; i++)
|
|
for (j = 0; j < N_PORTS; j++) {
|
|
struct rte_mbuf *m;
|
|
uint8_t *key;
|
|
uint32_t *k32;
|
|
|
|
m = rte_pktmbuf_alloc(pool);
|
|
if (m == NULL) {
|
|
rte_panic("Failed to alloc mbuf from pool\n");
|
|
return -1;
|
|
}
|
|
key = RTE_MBUF_METADATA_UINT8_PTR(m,
|
|
APP_METADATA_OFFSET(32));
|
|
|
|
k32 = (uint32_t *) key;
|
|
k32[0] = 0xadadadad >> (j % 2);
|
|
|
|
RTE_LOG(INFO, PIPELINE, "%s: Enqueue onto ring %d\n",
|
|
__func__, i);
|
|
rte_ring_enqueue(rings_rx[i], m);
|
|
}
|
|
|
|
/* Run pipeline once */
|
|
for (i = 0; i < N_PORTS; i++)
|
|
rte_pipeline_run(p);
|
|
|
|
/*
|
|
* need to flush the pipeline, as there may be less hits than the burst
|
|
size and they will not have been flushed to the tx rings. */
|
|
rte_pipeline_flush(p);
|
|
|
|
/*
|
|
* Now we'll see what we got back on the tx rings. We should see whatever
|
|
* packets we had hits on that were destined for the output ports.
|
|
*/
|
|
tx_count = 0;
|
|
|
|
for (i = 0; i < N_PORTS; i++) {
|
|
void *objs[RING_TX_SIZE];
|
|
struct rte_mbuf *mbuf;
|
|
|
|
ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10, NULL);
|
|
if (ret <= 0)
|
|
printf("Got no objects from ring %d - error code %d\n",
|
|
i, ret);
|
|
else {
|
|
printf("Got %d object(s) from ring %d!\n", ret, i);
|
|
for (j = 0; j < ret; j++) {
|
|
mbuf = objs[j];
|
|
rte_hexdump(stdout, "Object:",
|
|
rte_pktmbuf_mtod(mbuf, char *),
|
|
mbuf->data_len);
|
|
rte_pktmbuf_free(mbuf);
|
|
}
|
|
tx_count += ret;
|
|
}
|
|
}
|
|
|
|
if (tx_count != expected_count) {
|
|
RTE_LOG(INFO, PIPELINE,
|
|
"%s: Unexpected packets out for %s test, expected %d, "
|
|
"got %d\n", __func__, pipeline_test_names[test_type],
|
|
expected_count, tx_count);
|
|
goto fail;
|
|
}
|
|
|
|
cleanup_pipeline();
|
|
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
|
|
}
|
|
|
|
int
|
|
test_table_pipeline(void)
|
|
{
|
|
/* TEST - All packets dropped */
|
|
action_handler_hit = NULL;
|
|
action_handler_miss = NULL;
|
|
table_entry_default_action = RTE_PIPELINE_ACTION_DROP;
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 0) < 0)
|
|
return -1;
|
|
|
|
/* TEST - All packets passed through */
|
|
table_entry_default_action = RTE_PIPELINE_ACTION_PORT;
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
|
|
return -1;
|
|
|
|
/* TEST - one packet per port */
|
|
action_handler_hit = NULL;
|
|
action_handler_miss = table_action_stub_miss;
|
|
table_entry_default_action = RTE_PIPELINE_ACTION_PORT;
|
|
override_miss_mask = 0x01; /* one packet per port */
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
|
|
return -1;
|
|
|
|
/* TEST - one packet per port */
|
|
override_miss_mask = 0x02; /*all per port */
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
|
|
return -1;
|
|
|
|
/* TEST - all packets per port */
|
|
override_miss_mask = 0x03; /*all per port */
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
* This test will set up two tables in the pipeline. the first table
|
|
* will forward to another table on miss, and the second table will
|
|
* forward to port.
|
|
*/
|
|
connect_miss_action_to_table = 1;
|
|
table_entry_default_action = RTE_PIPELINE_ACTION_TABLE;
|
|
action_handler_hit = NULL; /* not for stub, hitmask always zero */
|
|
action_handler_miss = NULL;
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 4) < 0)
|
|
return -1;
|
|
connect_miss_action_to_table = 0;
|
|
|
|
printf("TEST - two tables, hitmask override to 0x01\n");
|
|
connect_miss_action_to_table = 1;
|
|
action_handler_miss = table_action_stub_miss;
|
|
override_miss_mask = 0x01;
|
|
setup_pipeline(e_TEST_STUB);
|
|
if (test_pipeline_single_filter(e_TEST_STUB, 2) < 0)
|
|
return -1;
|
|
connect_miss_action_to_table = 0;
|
|
|
|
if (check_pipeline_invalid_params()) {
|
|
RTE_LOG(INFO, PIPELINE, "%s: Check pipeline invalid params "
|
|
"failed.\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|