Commit 328aa08a authored by John Fastabend's avatar John Fastabend Committed by Daniel Borkmann

bpf: Selftests, break down test_sockmap into subtests

At the moment test_sockmap runs all 800+ tests ungrouped which is not
ideal because it makes it hard to see what is failing but also more
importantly its hard to confirm all cases are tested. Additionally,
after inspecting we noticed the runtime is bloated because we run
many duplicate tests. Worse some of these tests are known error cases
that wait for the recvmsg handler to timeout which creats long delays.
Also we noted some tests were not clearing their options and as a
result the following tests would run with extra and incorrect options.

Fix this by reorganizing test code so its clear what tests are running
and when. Then it becomes easy to remove duplication and run tests with
only the set of send/recv patterns that are relavent.

To accomplish this break test_sockmap into subtests and remove
unnecessary duplication. The output is more readable now and
the runtime reduced.

Now default output prints subtests like this,

 $ ./test_sockmap
 # 1/ 6  sockmap:txmsg test passthrough:OK
 ...
 #22/ 1 sockhash:txmsg test push/pop data:OK
 Pass: 22 Fail: 0
Signed-off-by: default avatarJohn Fastabend <john.fastabend@gmail.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Reviewed-by: default avatarJakub Sitnicki <jakub@cloudflare.com>
Link: https://lore.kernel.org/bpf/158939728384.15176.13601520183665880762.stgit@john-Precision-5820-Tower
parent 18d4e900
......@@ -54,7 +54,7 @@ static void running_handler(int a);
#define S1_PORT 10000
#define S2_PORT 10001
#define BPF_SOCKMAP_FILENAME "test_sockmap_kern.o"
#define BPF_SOCKMAP_FILENAME "test_sockmap_kern.o"
#define BPF_SOCKHASH_FILENAME "test_sockhash_kern.o"
#define CG_PATH "/sockmap"
......@@ -110,6 +110,76 @@ static const struct option long_options[] = {
{0, 0, NULL, 0 }
};
struct test_env {
const char *type;
const char *subtest;
int test_num;
int subtest_num;
int succ_cnt;
int fail_cnt;
int fail_last;
};
struct test_env env;
static void test_start(void)
{
env.subtest_num++;
}
static void test_fail(void)
{
env.fail_cnt++;
}
static void test_pass(void)
{
env.succ_cnt++;
}
static void test_reset(void)
{
txmsg_start = txmsg_end = 0;
txmsg_start_pop = txmsg_pop = 0;
txmsg_start_push = txmsg_end_push = 0;
txmsg_pass = txmsg_drop = txmsg_redir = 0;
txmsg_apply = txmsg_cork = 0;
txmsg_ingress = txmsg_skb = 0;
}
static int test_start_subtest(const char *name, const char *type)
{
env.type = type;
env.subtest = name;
env.test_num++;
env.subtest_num = 0;
env.fail_last = env.fail_cnt;
test_reset();
return 0;
}
static void test_end_subtest(void)
{
int error = env.fail_cnt - env.fail_last;
int type = strcmp(env.type, BPF_SOCKMAP_FILENAME);
if (!error)
test_pass();
fprintf(stdout, "#%2d/%2d %8s:%s:%s\n",
env.test_num, env.subtest_num,
!type ? "sockmap" : "sockhash",
env.subtest, error ? "FAIL" : "OK");
}
static void test_print_results(void)
{
fprintf(stdout, "Pass: %d Fail: %d\n",
env.succ_cnt, env.fail_cnt);
}
static void usage(char *argv[])
{
int i;
......@@ -316,6 +386,7 @@ struct sockmap_options {
int iov_count;
int iov_length;
int rate;
char *map;
};
static int msg_loop_sendpage(int fd, int iov_length, int cnt,
......@@ -1169,416 +1240,305 @@ static int __test_exec(int cgrp, int test, struct sockmap_options *opt)
test_options(options);
fprintf(stdout,
"[TEST %i]: (%i, %i, %i, %s, %s): ",
test_cnt, opt->rate, opt->iov_count, opt->iov_length,
test_to_str(test), options);
fflush(stdout);
if (opt->verbose) {
fprintf(stdout,
"[TEST %i]: (%i, %i, %i, %s, %s): ",
test_cnt, opt->rate, opt->iov_count, opt->iov_length,
test_to_str(test), options);
fflush(stdout);
}
err = run_options(opt, cgrp, test);
fprintf(stdout, "%s\n", !err ? "PASS" : "FAILED");
if (opt->verbose)
fprintf(stdout, "%s\n", !err ? "PASS" : "FAILED");
test_cnt++;
!err ? passed++ : failed++;
free(options);
return err;
}
static int test_exec(int cgrp, struct sockmap_options *opt)
{
int err = __test_exec(cgrp, SENDMSG, opt);
if (err)
goto out;
err = __test_exec(cgrp, SENDPAGE, opt);
out:
return err;
}
static int test_loop(int cgrp)
{
struct sockmap_options opt;
int err, i, l, r;
opt.verbose = 0;
opt.base = false;
opt.sendpage = false;
opt.data_test = false;
opt.drop_expected = false;
opt.iov_count = 0;
opt.iov_length = 0;
opt.rate = 0;
r = 1;
for (i = 1; i < 100; i += 33) {
for (l = 1; l < 100; l += 33) {
opt.rate = r;
opt.iov_count = i;
opt.iov_length = l;
err = test_exec(cgrp, &opt);
if (err)
goto out;
}
}
sched_yield();
out:
return err;
}
static int test_txmsg(int cgrp)
static void test_exec(int cgrp, struct sockmap_options *opt)
{
int type = strcmp(opt->map, BPF_SOCKMAP_FILENAME);
int err;
txmsg_pass = txmsg_drop = 0;
txmsg_apply = txmsg_cork = 0;
txmsg_ingress = txmsg_skb = 0;
txmsg_pass = 1;
err = test_loop(cgrp);
txmsg_pass = 0;
if (err)
goto out;
txmsg_redir = 1;
err = test_loop(cgrp);
txmsg_redir = 0;
if (err)
goto out;
txmsg_drop = 1;
err = test_loop(cgrp);
txmsg_drop = 0;
if (err)
goto out;
txmsg_redir = 1;
txmsg_ingress = 1;
err = test_loop(cgrp);
txmsg_redir = 0;
txmsg_ingress = 0;
if (err)
goto out;
out:
txmsg_pass = 0;
txmsg_redir = 0;
txmsg_drop = 0;
return err;
if (type == 0) {
test_start();
err = __test_exec(cgrp, SENDMSG, opt);
if (err)
test_fail();
} else {
test_start();
err = __test_exec(cgrp, SENDPAGE, opt);
if (err)
test_fail();
}
}
static int test_send(struct sockmap_options *opt, int cgrp)
static void test_send_one(struct sockmap_options *opt, int cgrp)
{
int err;
opt->iov_length = 1;
opt->iov_count = 1;
opt->rate = 1;
err = test_exec(cgrp, opt);
if (err)
goto out;
test_exec(cgrp, opt);
opt->iov_length = 1;
opt->iov_count = 1024;
opt->rate = 1;
err = test_exec(cgrp, opt);
if (err)
goto out;
test_exec(cgrp, opt);
opt->iov_length = 1024;
opt->iov_count = 1;
opt->rate = 1;
err = test_exec(cgrp, opt);
if (err)
goto out;
test_exec(cgrp, opt);
opt->iov_length = 1;
}
static void test_send_many(struct sockmap_options *opt, int cgrp)
{
opt->iov_length = 3;
opt->iov_count = 1;
opt->rate = 512;
err = test_exec(cgrp, opt);
if (err)
goto out;
test_exec(cgrp, opt);
opt->rate = 100;
opt->iov_count = 1;
opt->iov_length = 5;
test_exec(cgrp, opt);
}
static void test_send_large(struct sockmap_options *opt, int cgrp)
{
opt->iov_length = 256;
opt->iov_count = 1024;
opt->rate = 2;
err = test_exec(cgrp, opt);
if (err)
goto out;
test_exec(cgrp, opt);
}
opt->rate = 100;
opt->iov_count = 1;
opt->iov_length = 5;
err = test_exec(cgrp, opt);
if (err)
goto out;
out:
static void test_send(struct sockmap_options *opt, int cgrp)
{
test_send_one(opt, cgrp);
test_send_many(opt, cgrp);
test_send_large(opt, cgrp);
sched_yield();
return err;
}
static int test_mixed(int cgrp)
static void test_txmsg_pass(int cgrp, char *map)
{
struct sockmap_options opt = {0};
int err;
txmsg_pass = txmsg_drop = 0;
txmsg_apply = txmsg_cork = 0;
txmsg_start = txmsg_end = 0;
txmsg_start_push = txmsg_end_push = 0;
txmsg_start_pop = txmsg_pop = 0;
struct sockmap_options opt = {.map = map};
/* Test small and large iov_count values with pass/redir/apply/cork */
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1;
txmsg_cork = 0;
err = test_send(&opt, cgrp);
if (err)
goto out;
test_send(&opt, cgrp);
}
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 0;
txmsg_cork = 1;
err = test_send(&opt, cgrp);
if (err)
goto out;
static void test_txmsg_redir(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1;
txmsg_cork = 1;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_redir = 1;
test_send(&opt, cgrp);
}
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1024;
txmsg_cork = 0;
err = test_send(&opt, cgrp);
if (err)
goto out;
static void test_txmsg_drop(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 0;
txmsg_cork = 1024;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_drop = 1;
test_send(&opt, cgrp);
}
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1024;
txmsg_cork = 1024;
err = test_send(&opt, cgrp);
if (err)
goto out;
static void test_txmsg_ingress_redir(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
txmsg_pass = txmsg_drop = 0;
txmsg_ingress = txmsg_redir = 1;
test_send(&opt, cgrp);
}
/* Test cork with hung data. This tests poor usage patterns where
* cork can leave data on the ring if user program is buggy and
* doesn't flush them somehow. They do take some time however
* because they wait for a timeout. Test pass, redir and cork with
* apply logic. Use cork size of 4097 with send_large to avoid
* aligning cork size with send size.
*/
static void test_txmsg_cork_hangs(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_cork = 4096;
txmsg_apply = 4096;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_pass = 0;
txmsg_redir = 1;
txmsg_apply = 1;
txmsg_cork = 0;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_cork = 4097;
txmsg_apply = 4097;
test_send_large(&opt, cgrp);
txmsg_pass = 0;
txmsg_redir = 1;
txmsg_apply = 0;
txmsg_cork = 1;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_cork = 4097;
test_send_large(&opt, cgrp);
txmsg_pass = 0;
txmsg_redir = 1;
txmsg_apply = 1024;
txmsg_cork = 0;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_apply = 4097;
txmsg_cork = 4097;
test_send_large(&opt, cgrp);
}
txmsg_pass = 0;
static void test_txmsg_pull(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
/* Test basic start/end */
txmsg_start = 1;
txmsg_end = 2;
test_send(&opt, cgrp);
/* Test >4k pull */
txmsg_start = 4096;
txmsg_end = 9182;
test_send_large(&opt, cgrp);
/* Test pull + redirect */
txmsg_redir = 0;
txmsg_start = 1;
txmsg_end = 2;
test_send(&opt, cgrp);
/* Test pull + cork */
txmsg_redir = 0;
txmsg_cork = 512;
txmsg_start = 1;
txmsg_end = 2;
test_send_many(&opt, cgrp);
/* Test pull + cork + redirect */
txmsg_redir = 1;
txmsg_apply = 0;
txmsg_cork = 1024;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_cork = 512;
txmsg_start = 1;
txmsg_end = 2;
test_send_many(&opt, cgrp);
}
txmsg_pass = 0;
static void test_txmsg_pop(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
/* Test basic pop */
txmsg_start_pop = 1;
txmsg_pop = 2;
test_send_many(&opt, cgrp);
/* Test pop with >4k */
txmsg_start_pop = 4096;
txmsg_pop = 4096;
test_send_large(&opt, cgrp);
/* Test pop + redirect */
txmsg_redir = 1;
txmsg_apply = 1024;
txmsg_cork = 1024;
err = test_send(&opt, cgrp);
if (err)
goto out;
txmsg_start_pop = 1;
txmsg_pop = 2;
test_send_many(&opt, cgrp);
txmsg_pass = 0;
/* Test pop + cork */
txmsg_redir = 0;
txmsg_cork = 512;
txmsg_start_pop = 1;
txmsg_pop = 2;
test_send_many(&opt, cgrp);
/* Test pop + redirect + cork */
txmsg_redir = 1;
txmsg_cork = 4096;
txmsg_apply = 4096;
err = test_send(&opt, cgrp);
if (err)
goto out;
out:
return err;
txmsg_cork = 4;
txmsg_start_pop = 1;
txmsg_pop = 2;
test_send_many(&opt, cgrp);
}
static int test_start_end(int cgrp)
static void test_txmsg_push(int cgrp, char *map)
{
struct sockmap_options opt = {0};
int err, i;
struct sockmap_options opt = {.map = map};
/* Test basic start/end with lots of iov_count and iov_lengths */
txmsg_start = 1;
txmsg_end = 2;
/* Test basic push */
txmsg_start_push = 1;
txmsg_end_push = 1;
test_send(&opt, cgrp);
/* Test push 4kB >4k */
txmsg_start_push = 4096;
txmsg_end_push = 4096;
test_send_large(&opt, cgrp);
/* Test push + redirect */
txmsg_redir = 1;
txmsg_start_push = 1;
txmsg_end_push = 2;
txmsg_start_pop = 1;
txmsg_pop = 1;
err = test_txmsg(cgrp);
if (err)
goto out;
test_send_many(&opt, cgrp);
/* Cut a byte of pushed data but leave reamining in place */
txmsg_start = 1;
txmsg_end = 2;
/* Test push + cork */
txmsg_redir = 0;
txmsg_cork = 512;
txmsg_start_push = 1;
txmsg_end_push = 3;
txmsg_start_pop = 1;
txmsg_pop = 1;
err = test_txmsg(cgrp);
if (err)
goto out;
txmsg_end_push = 2;
test_send_many(&opt, cgrp);
}
/* Test start/end with cork */
opt.rate = 16;
opt.iov_count = 1;
opt.iov_length = 100;
txmsg_cork = 1600;
txmsg_start_pop = 0;
txmsg_pop = 0;
for (i = 99; i <= 1600; i += 500) {
txmsg_start = 0;
txmsg_end = i;
txmsg_start_push = 0;
txmsg_end_push = i;
err = test_exec(cgrp, &opt);
if (err)
goto out;
}
static void test_txmsg_push_pop(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
/* Test pop data in middle of cork */
for (i = 99; i <= 1600; i += 500) {
txmsg_start_pop = 10;
txmsg_pop = i;
err = test_exec(cgrp, &opt);
if (err)
goto out;
}
txmsg_start_pop = 0;
txmsg_pop = 0;
/* Test start/end with cork but pull data in middle */
for (i = 199; i <= 1600; i += 500) {
txmsg_start = 100;
txmsg_end = i;
txmsg_start_push = 100;
txmsg_end_push = i;
err = test_exec(cgrp, &opt);
if (err)
goto out;
}
txmsg_start_push = 1;
txmsg_end_push = 10;
txmsg_start_pop = 5;
txmsg_pop = 4;
test_send_large(&opt, cgrp);
}
/* Test start/end with cork pulling last sg entry */
txmsg_start = 1500;
txmsg_end = 1600;
txmsg_start_push = 1500;
txmsg_end_push = 1600;
err = test_exec(cgrp, &opt);
if (err)
goto out;
static void test_txmsg_apply(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
/* Test pop with cork pulling last sg entry */
txmsg_start_pop = 1500;
txmsg_pop = 1600;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_start_pop = 0;
txmsg_pop = 0;
/* Test start/end pull of single byte in last page */
txmsg_start = 1111;
txmsg_end = 1112;
txmsg_start_push = 1111;
txmsg_end_push = 1112;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1;
txmsg_cork = 0;
test_send_one(&opt, cgrp);
/* Test pop of single byte in last page */
txmsg_start_pop = 1111;
txmsg_pop = 1112;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_pass = 0;
txmsg_redir = 1;
txmsg_apply = 1;
txmsg_cork = 0;
test_send_one(&opt, cgrp);
/* Test start/end with end < start */
txmsg_start = 1111;
txmsg_end = 0;
txmsg_start_push = 1111;
txmsg_end_push = 0;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1024;
txmsg_cork = 0;
test_send_large(&opt, cgrp);
/* Test start/end with end > data */
txmsg_start = 0;
txmsg_end = 1601;
txmsg_start_push = 0;
txmsg_end_push = 1601;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_pass = 0;
txmsg_redir = 1;
txmsg_apply = 1024;
txmsg_cork = 0;
test_send_large(&opt, cgrp);
}
/* Test start/end with start > data */
txmsg_start = 1601;
txmsg_end = 1600;
txmsg_start_push = 1601;
txmsg_end_push = 1600;
err = test_exec(cgrp, &opt);
if (err)
goto out;
static void test_txmsg_cork(int cgrp, char *map)
{
struct sockmap_options opt = {.map = map};
/* Test pop with start > data */
txmsg_start_pop = 1601;
txmsg_pop = 1;
err = test_exec(cgrp, &opt);
if (err)
goto out;
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 0;
txmsg_cork = 1;
test_send(&opt, cgrp);
/* Test pop with pop range > data */
txmsg_start_pop = 1599;
txmsg_pop = 10;
err = test_exec(cgrp, &opt);
out:
txmsg_start = 0;
txmsg_end = 0;
sched_yield();
return err;
txmsg_pass = 1;
txmsg_redir = 0;
txmsg_apply = 1;
txmsg_cork = 1;
test_send(&opt, cgrp);
}
char *map_names[] = {
......@@ -1663,16 +1623,59 @@ static int populate_progs(char *bpf_file)
return 0;
}
static int __test_suite(int cg_fd, char *bpf_file)
struct _test {
char *title;
void (*tester)(int cg_fd, char *map);
};
struct _test test[] = {
{"txmsg test passthrough", test_txmsg_pass},
{"txmsg test redirect", test_txmsg_redir},
{"txmsg test drop", test_txmsg_drop},
{"txmsg test ingress redirect", test_txmsg_ingress_redir},
{"txmsg test apply", test_txmsg_apply},
{"txmsg test cork", test_txmsg_cork},
{"txmsg test hanging corks", test_txmsg_cork_hangs},
{"txmsg test push_data", test_txmsg_push},
{"txmsg test pull-data", test_txmsg_pull},
{"txmsg test pop-data", test_txmsg_pop},
{"txmsg test push/pop data", test_txmsg_push_pop},
};
static int __test_selftests(int cg_fd, char *map)
{
int err, cleanup = cg_fd;
int i, err;
err = populate_progs(bpf_file);
err = populate_progs(map);
if (err < 0) {
fprintf(stderr, "ERROR: (%i) load bpf failed\n", err);
return err;
}
/* Tests basic commands and APIs */
for (i = 0; i < sizeof(test)/sizeof(struct _test); i++) {
struct _test t = test[i];
test_start_subtest(t.title, map);
t.tester(cg_fd, map);
test_end_subtest();
}
return err;
}
static void test_selftests_sockmap(int cg_fd)
{
__test_selftests(cg_fd, BPF_SOCKMAP_FILENAME);
}
static void test_selftests_sockhash(int cg_fd)
{
__test_selftests(cg_fd, BPF_SOCKHASH_FILENAME);
}
static int test_selftest(int cg_fd)
{
if (cg_fd < 0) {
if (setup_cgroup_environment()) {
fprintf(stderr, "ERROR: cgroup env failed\n");
......@@ -1693,43 +1696,12 @@ static int __test_suite(int cg_fd, char *bpf_file)
}
}
/* Tests basic commands and APIs with range of iov values */
txmsg_start = txmsg_end = txmsg_start_push = txmsg_end_push = 0;
err = test_txmsg(cg_fd);
if (err)
goto out;
/* Tests interesting combinations of APIs used together */
err = test_mixed(cg_fd);
if (err)
goto out;
/* Tests pull_data API using start/end API */
err = test_start_end(cg_fd);
if (err)
goto out;
out:
printf("Summary: %i PASSED %i FAILED\n", passed, failed);
if (cleanup < 0) {
cleanup_cgroup_environment();
close(cg_fd);
}
return err;
}
static int test_suite(int cg_fd)
{
int err;
err = __test_suite(cg_fd, BPF_SOCKMAP_FILENAME);
if (err)
goto out;
err = __test_suite(cg_fd, BPF_SOCKHASH_FILENAME);
out:
if (cg_fd > -1)
close(cg_fd);
return err;
test_selftests_sockmap(cg_fd);
test_selftests_sockhash(cg_fd);
cleanup_cgroup_environment();
close(cg_fd);
test_print_results();
return 0;
}
int main(int argc, char **argv)
......@@ -1741,8 +1713,9 @@ int main(int argc, char **argv)
int test = PING_PONG;
bool cg_created = 0;
if (argc < 2)
return test_suite(-1);
if (argc < 2) {
return test_selftest(-1);
}
while ((opt = getopt_long(argc, argv, ":dhvc:r:i:l:t:p:q:",
long_options, &longindex)) != -1) {
......
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