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) ...@@ -336,45 +336,43 @@ static int switch_namespace(const char *nsname)
return nsfd; return nsfd;
} }
static int validate_interfaces(void) static bool validate_interface(struct ifobject *ifobj)
{ {
bool ret = true; if (!strcmp(ifobj->ifname, ""))
return false;
for (int i = 0; i < MAX_INTERFACES; i++) { return true;
if (!strcmp(ifdict[i]->ifname, "")) {
ret = false;
ksft_test_result_fail("ERROR: interfaces: -i <int>,<ns> -i <int>,<ns>.");
}
}
return ret;
} }
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; opterr = 0;
for (;;) { 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) if (c == -1)
break; break;
switch (c) { switch (c) {
case 'i': 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; break;
char *sptr, *token;
sptr = strndupa(optarg, strlen(optarg)); sptr = strndupa(optarg, strlen(optarg));
memcpy(ifdict[interface_index]->ifname, memcpy(ifobj->ifname, strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
strsep(&sptr, ","), MAX_INTERFACE_NAME_CHARS);
token = strsep(&sptr, ","); token = strsep(&sptr, ",");
if (token) if (token)
memcpy(ifdict[interface_index]->nsname, token, memcpy(ifobj->nsname, token, MAX_INTERFACES_NAMESPACE_CHARS);
MAX_INTERFACES_NAMESPACE_CHARS); interface_nb++;
interface_index++;
break; break;
case 'D': case 'D':
opt_pkt_dump = true; opt_pkt_dump = true;
...@@ -387,11 +385,44 @@ static void parse_command_line(int argc, char **argv) ...@@ -387,11 +385,44 @@ static void parse_command_line(int argc, char **argv)
ksft_exit_xfail(); ksft_exit_xfail();
} }
} }
}
if (!validate_interfaces()) { static void __test_spec_init(struct test_spec *test, struct ifobject *ifobj_tx,
usage(basename(argv[0])); struct ifobject *ifobj_rx)
ksft_exit_xfail(); {
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) 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) ...@@ -842,8 +873,10 @@ static void *worker_testapp_validate_rx(void *arg)
pthread_exit(NULL); 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 bidi = test_type == TEST_TYPE_BIDI;
bool bpf = test_type == TEST_TYPE_BPF_RES; bool bpf = test_type == TEST_TYPE_BPF_RES;
struct pkt_stream *pkt_stream; struct pkt_stream *pkt_stream;
...@@ -855,18 +888,18 @@ static void testapp_validate(void) ...@@ -855,18 +888,18 @@ static void testapp_validate(void)
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE); pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, XSK_UMEM__INVALID_FRAME_SIZE);
else else
pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, PKT_SIZE); pkt_stream = pkt_stream_generate(DEFAULT_PKT_CNT, PKT_SIZE);
ifdict_tx->pkt_stream = pkt_stream; ifobj_tx->pkt_stream = pkt_stream;
ifdict_rx->pkt_stream = pkt_stream; ifobj_rx->pkt_stream = pkt_stream;
/*Spawn RX thread */ /*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); pthread_barrier_wait(&barr);
if (pthread_barrier_destroy(&barr)) if (pthread_barrier_destroy(&barr))
exit_with_error(errno); exit_with_error(errno);
/*Spawn TX thread */ /*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(t1, NULL);
pthread_join(t0, NULL); pthread_join(t0, NULL);
...@@ -875,80 +908,82 @@ static void testapp_validate(void) ...@@ -875,80 +908,82 @@ static void testapp_validate(void)
print_ksft_result(); print_ksft_result();
} }
static void testapp_teardown(void) static void testapp_teardown(struct test_spec *test)
{ {
int i; int i;
for (i = 0; i < MAX_TEARDOWN_ITER; i++) { for (i = 0; i < MAX_TEARDOWN_ITER; i++) {
print_verbose("Creating socket\n"); testapp_validate_traffic(test);
testapp_validate(); test_spec_reset(test);
} }
print_ksft_result(); 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; thread_func_t tmp_func_ptr = (*ifobj1)->func_ptr;
enum fvector tmp_vector = ifobj1->fv.vector; enum fvector tmp_vector = (*ifobj1)->fv.vector;
struct ifobject *tmp_ifobj = (*ifobj1);
ifobj1->func_ptr = ifobj2->func_ptr; (*ifobj1)->func_ptr = (*ifobj2)->func_ptr;
ifobj1->fv.vector = ifobj2->fv.vector; (*ifobj1)->fv.vector = (*ifobj2)->fv.vector;
ifobj2->func_ptr = tmp_func_ptr; (*ifobj2)->func_ptr = tmp_func_ptr;
ifobj2->fv.vector = tmp_vector; (*ifobj2)->fv.vector = tmp_vector;
ifdict_tx = ifobj1; *ifobj1 = *ifobj2;
ifdict_rx = 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++) { for (int i = 0; i < MAX_BIDI_ITER; i++) {
print_verbose("Creating socket\n"); print_verbose("Creating socket\n");
testapp_validate(); testapp_validate_traffic(test);
if (!second_step) { if (!second_step) {
print_verbose("Switching Tx/Rx vectors\n"); print_verbose("Switching Tx/Rx vectors\n");
swap_vectors(ifdict[1], ifdict[0]); swap_directions(&test->ifobj_rx, &test->ifobj_tx);
} }
second_step = true; second_step = true;
} }
swap_vectors(ifdict[0], ifdict[1]); swap_directions(&test->ifobj_rx, &test->ifobj_tx);
print_ksft_result(); 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_socket__delete(ifobj_tx->xsk->xsk);
xsk_umem__delete(ifdict_tx->umem->umem); xsk_umem__delete(ifobj_tx->umem->umem);
xsk_socket__delete(ifdict_rx->xsk->xsk); xsk_socket__delete(ifobj_rx->xsk->xsk);
xsk_umem__delete(ifdict_rx->umem->umem); xsk_umem__delete(ifobj_rx->umem->umem);
ifdict_tx->umem = &ifdict_tx->umem_arr[1]; ifobj_tx->umem = &ifobj_tx->umem_arr[1];
ifdict_tx->xsk = &ifdict_tx->xsk_arr[1]; ifobj_tx->xsk = &ifobj_tx->xsk_arr[1];
ifdict_rx->umem = &ifdict_rx->umem_arr[1]; ifobj_rx->umem = &ifobj_rx->umem_arr[1];
ifdict_rx->xsk = &ifdict_rx->xsk_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; int i;
for (i = 0; i < MAX_BPF_ITER; i++) { for (i = 0; i < MAX_BPF_ITER; i++) {
print_verbose("Creating socket\n"); print_verbose("Creating socket\n");
testapp_validate(); testapp_validate_traffic(test);
if (!second_step) if (!second_step)
swap_xsk_res(); swap_xsk_resources(test->ifobj_tx, test->ifobj_rx);
second_step = true; second_step = true;
} }
print_ksft_result(); 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++) { for (int i = 0; i < STAT_TEST_TYPE_MAX; i++) {
test_spec_reset(test);
stat_test_type = i; stat_test_type = i;
/* reset defaults */ /* reset defaults */
...@@ -968,7 +1003,7 @@ static void testapp_stats(void) ...@@ -968,7 +1003,7 @@ static void testapp_stats(void)
default: default:
break; break;
} }
testapp_validate(); testapp_validate_traffic(test);
} }
print_ksft_result(); print_ksft_result();
...@@ -992,16 +1027,11 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char * ...@@ -992,16 +1027,11 @@ static void init_iface(struct ifobject *ifobj, const char *dst_mac, const char *
ifobj->dst_port = dst_port; ifobj->dst_port = dst_port;
ifobj->src_port = src_port; ifobj->src_port = src_port;
if (vector == tx)
ifdict_tx = ifobj;
else
ifdict_rx = ifobj;
ifobj->fv.vector = vector; ifobj->fv.vector = vector;
ifobj->func_ptr = func_ptr; 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; test_type = type;
...@@ -1027,19 +1057,19 @@ static void run_pkt_test(int mode, int type) ...@@ -1027,19 +1057,19 @@ static void run_pkt_test(int mode, int type)
switch (test_type) { switch (test_type) {
case TEST_TYPE_STATS: case TEST_TYPE_STATS:
testapp_stats(); testapp_stats(test);
break; break;
case TEST_TYPE_TEARDOWN: case TEST_TYPE_TEARDOWN:
testapp_teardown(); testapp_teardown(test);
break; break;
case TEST_TYPE_BIDI: case TEST_TYPE_BIDI:
testapp_bidi(); testapp_bidi(test);
break; break;
case TEST_TYPE_BPF_RES: case TEST_TYPE_BPF_RES:
testapp_bpf_res(); testapp_bpf_res(test);
break; break;
default: default:
testapp_validate(); testapp_validate_traffic(test);
break; break;
} }
} }
...@@ -1079,36 +1109,47 @@ static void ifobject_delete(struct ifobject *ifobj) ...@@ -1079,36 +1109,47 @@ static void ifobject_delete(struct ifobject *ifobj)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
struct rlimit _rlim = { RLIM_INFINITY, RLIM_INFINITY }; 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)) if (setrlimit(RLIMIT_MEMLOCK, &_rlim))
exit_with_error(errno); exit_with_error(errno);
for (i = 0; i < MAX_INTERFACES; i++) { ifobj_tx = ifobject_create();
ifdict[i] = ifobject_create(); if (!ifobj_tx)
if (!ifdict[i]) exit_with_error(ENOMEM);
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, ""); 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); 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); worker_testapp_validate_rx);
ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX); ksft_set_plan(TEST_MODE_MAX * TEST_TYPE_MAX);
for (i = 0; i < TEST_MODE_MAX; i++) for (i = 0; i < TEST_MODE_MAX; i++)
for (j = 0; j < TEST_TYPE_MAX; j++) { 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); usleep(USLEEP_MAX);
} }
for (i = 0; i < MAX_INTERFACES; i++) ifobject_delete(ifobj_tx);
ifobject_delete(ifdict[i]); ifobject_delete(ifobj_rx);
ksft_exit_pass(); ksft_exit_pass();
return 0; return 0;
......
...@@ -137,9 +137,10 @@ struct ifobject { ...@@ -137,9 +137,10 @@ struct ifobject {
u8 src_mac[ETH_ALEN]; u8 src_mac[ETH_ALEN];
}; };
static struct ifobject *ifdict[MAX_INTERFACES]; struct test_spec {
static struct ifobject *ifdict_rx; struct ifobject *ifobj_tx;
static struct ifobject *ifdict_tx; struct ifobject *ifobj_rx;
};
/*threads*/ /*threads*/
pthread_barrier_t barr; 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