Commit ce74acaf authored by Magnus Karlsson's avatar Magnus Karlsson Committed by Daniel Borkmann

selftests: xsk: Introduce test specifications

Introduce a test specification to be able to concisely describe a
test. Currently, a test is implemented by sprinkling test specific if
statements here and there, which is not scalable or easy to
understand. The end goal with this patch set is to come to the point
in which a test is completely specified by a test specification that
can easily be constructed in a single function so that new tests can
be added without too much trouble. This test specification will be run
by a test runner that has no idea about tests. It just executes the
what test specification states.

This patch introduces the test specification and, as a start, puts the
two interface objects in there, one containing the packet stream to be
sent and the other one the packet stream that is supposed to be
received for a test to pass. The global variables containing these can
then be eliminated. The following patches will convert each existing
test into a test specification and add the needed fields into it and
the functionality in the test runner that act on the test
specification. At the end, the test runner should contain no test
specific code and each test should be described in a single simple
function.
Signed-off-by: default avatarMagnus Karlsson <magnus.karlsson@intel.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarMaciej Fijalkowski <maciej.fijalkowski@intel.com>
Link: https://lore.kernel.org/bpf/20210907071928.9750-4-magnus.karlsson@gmail.com
parent 744eb5c8
......@@ -336,45 +336,43 @@ static int switch_namespace(const char *nsname)
return nsfd;
}
static int validate_interfaces(void)
static bool validate_interface(struct ifobject *ifobj)
{
bool ret = true;
for (int i = 0; i < MAX_INTERFACES; i++) {
if (!strcmp(ifdict[i]->ifname, "")) {
ret = false;
ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
}
}
return ret;
if (!strcmp(ifobj->ifname, ""))
return false;
return true;
}
static void parse_command_line(int argc, char **argv)
static void parse_command_line(struct test_spec *test, int argc, char **argv)
{
int option_index, interface_index = 0, c;
struct ifobject *ifobj;
u32 interface_nb = 0;
int option_index, c;
opterr = 0;
for (;;) {
c = getopt_long(argc, argv, "i:Dv", long_options, &option_index);
char *sptr, *token;
c = getopt_long(argc, argv, "i:Dv", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'i':
if (interface_index == MAX_INTERFACES)
if (interface_nb == 0)
ifobj = test->ifobj_tx;
else if (interface_nb == 1)
ifobj = test->ifobj_rx;
else
break;
char *sptr, *token;
sptr = strndupa(optarg, strlen(optarg));
memcpy(ifdict[interface_index]->ifname,
strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
memcpy(ifobj->ifname, strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
token = strsep(&sptr, ",");
if (token)
memcpy(ifdict[interface_index]->nsname, token,
MAX_INTERFACES_NAMESPACE_CHARS);
interface_index++;
memcpy(ifobj->nsname, token, MAX_INTERFACES_NAMESPACE_CHARS);
interface_nb++;
break;
case 'D':
opt_pkt_dump = true;
......@@ -387,11 +385,44 @@ static void parse_command_line(int argc, char **argv)
ksft_exit_xfail();
}
}
}
if (!validate_interfaces()) {
usage(basename(argv[0]));
ksft_exit_xfail();
static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
struct ifobject *ifobj_rx)
{
u32 i, j;
for (i = 0; i < MAX_INTERFACES; i++) {
struct ifobject *ifobj = i ? ifobj_rx : ifobj_tx;
ifobj->umem = &ifobj->umem_arr[0];
ifobj->xsk = &ifobj->xsk_arr[0];
if (i == tx)
ifobj->fv.vector = tx;
else
ifobj->fv.vector = rx;
for (j = 0; j < MAX_SOCKETS; j++) {
memset(&ifobj->umem_arr[j], 0, sizeof(ifobj->umem_arr[j]));
memset(&ifobj->xsk_arr[j], 0, sizeof(ifobj->xsk_arr[j]));
}
}
test->ifobj_tx = ifobj_tx;
test->ifobj_rx = ifobj_rx;
}
static void test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
struct ifobject *ifobj_rx)
{
memset(test, 0, sizeof(*test));
__test_spec_init(test, ifobj_tx, ifobj_rx);
}
static void test_spec_reset(struct test_spec *test)
{
__test_spec_init(test, test->ifobj_tx, test->ifobj_rx);
}
static struct pkt *pkt_stream_get_pkt(struct pkt_stream *pkt_stream, u32 pkt_nb)
......@@ -842,8 +873,10 @@ static void *worker_testapp_validate_rx(void *arg)
pthread_exit(NULL);
}
static void testapp_validate(void)
static void testapp_validate_traffic(struct test_spec *test)
{
struct ifobject *ifobj_tx = test->ifobj_tx;
struct ifobject *ifobj_rx = test->ifobj_rx;
bool bidi = test_type == TEST_TYPE_BIDI;
bool bpf = test_type == TEST_TYPE_BPF_RES;
struct pkt_stream *pkt_stream;
......@@ -855,18 +888,18 @@ static void testapp_validate(void)
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
else
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, PKT_SIZE);
ifdict_tx->pkt_stream = pkt_stream;
ifdict_rx->pkt_stream = pkt_stream;
ifobj_tx->pkt_stream = pkt_stream;
ifobj_rx->pkt_stream = pkt_stream;
/*Spawn RX thread */
pthread_create(&t0, NULL, ifdict_rx->func_ptr, ifdict_rx);
pthread_create(&t0, NULL, ifobj_rx->func_ptr, ifobj_rx);
pthread_barrier_wait(&barr);
if (pthread_barrier_destroy(&barr))
exit_with_error(errno);
/*Spawn TX thread */
pthread_create(&t1, NULL, ifdict_tx->func_ptr, ifdict_tx);
pthread_create(&t1, NULL, ifobj_tx->func_ptr, ifobj_tx);
pthread_join(t1, NULL);
pthread_join(t0, NULL);
......@@ -875,80 +908,82 @@ static void testapp_validate(void)
print_ksft_result();
}
static void testapp_teardown(void)
static void testapp_teardown(struct test_spec *test)
{
int i;
for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
print_verbose("Creating socket\n");
testapp_validate();
testapp_validate_traffic(test);
test_spec_reset(test);
}
print_ksft_result();
}
static void swap_vectors(struct ifobject *ifobj1, struct ifobject *ifobj2)
static void swap_directions(struct ifobject **ifobj1, struct ifobject **ifobj2)
{
void *(*tmp_func_ptr)(void *) = ifobj1->func_ptr;
enum fvector tmp_vector = ifobj1->fv.vector;
thread_func_t tmp_func_ptr = (*ifobj1)->func_ptr;
enum fvector tmp_vector = (*ifobj1)->fv.vector;
struct ifobject *tmp_ifobj = (*ifobj1);
ifobj1->func_ptr = ifobj2->func_ptr;
ifobj1->fv.vector = ifobj2->fv.vector;
(*ifobj1)->func_ptr = (*ifobj2)->func_ptr;
(*ifobj1)->fv.vector = (*ifobj2)->fv.vector;
ifobj2->func_ptr = tmp_func_ptr;
ifobj2->fv.vector = tmp_vector;
(*ifobj2)->func_ptr = tmp_func_ptr;
(*ifobj2)->fv.vector = tmp_vector;
ifdict_tx = ifobj1;
ifdict_rx = ifobj2;
*ifobj1 = *ifobj2;
*ifobj2 = tmp_ifobj;
}
static void testapp_bidi(void)
static void testapp_bidi(struct test_spec *test)
{
for (int i = 0; i < MAX_BIDI_ITER; i++) {
print_verbose("Creating socket\n");
testapp_validate();
testapp_validate_traffic(test);
if (!second_step) {
print_verbose("Switching Tx/Rx vectors\n");
swap_vectors(ifdict[1], ifdict[0]);
swap_directions(&test->ifobj_rx, &test->ifobj_tx);
}
second_step = true;
}
swap_vectors(ifdict[0], ifdict[1]);
swap_directions(&test->ifobj_rx, &test->ifobj_tx);
print_ksft_result();
}
static void swap_xsk_res(void)
static void swap_xsk_resources(struct ifobject *ifobj_tx, struct ifobject *ifobj_rx)
{
xsk_socket__delete(ifdict_tx->xsk->xsk);
xsk_umem__delete(ifdict_tx->umem->umem);
xsk_socket__delete(ifdict_rx->xsk->xsk);
xsk_umem__delete(ifdict_rx->umem->umem);
ifdict_tx->umem = &ifdict_tx->umem_arr[1];
ifdict_tx->xsk = &ifdict_tx->xsk_arr[1];
ifdict_rx->umem = &ifdict_rx->umem_arr[1];
ifdict_rx->xsk = &ifdict_rx->xsk_arr[1];
xsk_socket__delete(ifobj_tx->xsk->xsk);
xsk_umem__delete(ifobj_tx->umem->umem);
xsk_socket__delete(ifobj_rx->xsk->xsk);
xsk_umem__delete(ifobj_rx->umem->umem);
ifobj_tx->umem = &ifobj_tx->umem_arr[1];
ifobj_tx->xsk = &ifobj_tx->xsk_arr[1];
ifobj_rx->umem = &ifobj_rx->umem_arr[1];
ifobj_rx->xsk = &ifobj_rx->xsk_arr[1];
}
static void testapp_bpf_res(void)
static void testapp_bpf_res(struct test_spec *test)
{
int i;
for (i = 0; i < MAX_BPF_ITER; i++) {
print_verbose("Creating socket\n");
testapp_validate();
testapp_validate_traffic(test);
if (!second_step)
swap_xsk_res();
swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
second_step = true;
}
print_ksft_result();
}
static void testapp_stats(void)
static void testapp_stats(struct test_spec *test)
{
for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
test_spec_reset(test);
stat_test_type = i;
/* reset defaults */
......@@ -968,7 +1003,7 @@ static void testapp_stats(void)
default:
break;
}
testapp_validate();
testapp_validate_traffic(test);
}
print_ksft_result();
......@@ -992,16 +1027,11 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *
ifobj->dst_port = dst_port;
ifobj->src_port = src_port;
if (vector == tx)
ifdict_tx = ifobj;
else
ifdict_rx = ifobj;
ifobj->fv.vector = vector;
ifobj->func_ptr = func_ptr;
}
static void run_pkt_test(int mode, int type)
static void run_pkt_test(struct test_spec *test, int mode, int type)
{
test_type = type;
......@@ -1027,19 +1057,19 @@ static void run_pkt_test(int mode, int type)
switch (test_type) {
case TEST_TYPE_STATS:
testapp_stats();
testapp_stats(test);
break;
case TEST_TYPE_TEARDOWN:
testapp_teardown();
testapp_teardown(test);
break;
case TEST_TYPE_BIDI:
testapp_bidi();
testapp_bidi(test);
break;
case TEST_TYPE_BPF_RES:
testapp_bpf_res();
testapp_bpf_res(test);
break;
default:
testapp_validate();
testapp_validate_traffic(test);
break;
}
}
......@@ -1079,36 +1109,47 @@ static void ifobject_delete(struct ifobject *ifobj)
int main(int argc, char **argv)
{
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY };
int i, j;
struct ifobject *ifobj_tx, *ifobj_rx;
struct test_spec test;
u32 i, j;
if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
exit_with_error(errno);
for (i = 0; i < MAX_INTERFACES; i++) {
ifdict[i] = ifobject_create();
if (!ifdict[i])
ifobj_tx = ifobject_create();
if (!ifobj_tx)
exit_with_error(ENOMEM);
ifobj_rx = ifobject_create();
if (!ifobj_rx)
exit_with_error(ENOMEM);
}
test_spec_init(&test, ifobj_tx, ifobj_rx);
setlocale(LC_ALL, "");
parse_command_line(argc, argv);
parse_command_line(&test, argc, argv);
if (!validate_interface(ifobj_tx) || !validate_interface(ifobj_rx)) {
usage(basename(argv[0]));
ksft_exit_xfail();
}
init_iface(ifdict[tx], MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx,
init_iface(ifobj_tx, MAC1, MAC2, IP1, IP2, UDP_PORT1, UDP_PORT2, tx,
worker_testapp_validate_tx);
init_iface(ifdict[rx], MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx,
init_iface(ifobj_rx, MAC2, MAC1, IP2, IP1, UDP_PORT2, UDP_PORT1, rx,
worker_testapp_validate_rx);
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
for (i = 0; i < TEST_MODE_MAX; i++)
for (j = 0; j < TEST_TYPE_MAX; j++) {
run_pkt_test(i, j);
test_spec_init(&test, ifobj_tx, ifobj_rx);
run_pkt_test(&test, i, j);
usleep(USLEEP_MAX);
}
for (i = 0; i < MAX_INTERFACES; i++)
ifobject_delete(ifdict[i]);
ifobject_delete(ifobj_tx);
ifobject_delete(ifobj_rx);
ksft_exit_pass();
return 0;
......
......@@ -137,9 +137,10 @@ struct ifobject {
u8 src_mac[ETH_ALEN];
};
static struct ifobject *ifdict[MAX_INTERFACES];
static struct ifobject *ifdict_rx;
static struct ifobject *ifdict_tx;
struct test_spec {
struct ifobject *ifobj_tx;
struct ifobject *ifobj_rx;
};
/*threads*/
pthread_barrier_t barr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment