From 36d6e65722ea515bf2d122d6e69096a5ff620a92 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Fri, 2 Apr 2021 14:39:30 +0000 Subject: [PATCH] netmap: update unit tests with libnetmap tests --- tests/sys/netmap/Makefile | 1 + tests/sys/netmap/ctrl-api-test.c | 321 +++++++++++++++++++++++++++++-- 2 files changed, 309 insertions(+), 13 deletions(-) diff --git a/tests/sys/netmap/Makefile b/tests/sys/netmap/Makefile index 0228271dd668..a7891f583b3b 100644 --- a/tests/sys/netmap/Makefile +++ b/tests/sys/netmap/Makefile @@ -10,5 +10,6 @@ PLAIN_TESTS_C+= ctrl-api-test CFLAGS+= -I${SRCTOP}/tests LIBADD+= pthread +LIBADD+= netmap .include diff --git a/tests/sys/netmap/ctrl-api-test.c b/tests/sys/netmap/ctrl-api-test.c index cea78141fbe4..7cd4d6ac6d9b 100644 --- a/tests/sys/netmap/ctrl-api-test.c +++ b/tests/sys/netmap/ctrl-api-test.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #ifdef __FreeBSD__ #include "freebsd_test_suite/macros.h" @@ -71,6 +73,8 @@ eventfd(int x __unused, int y __unused) #include #endif +#define NM_IFNAMSZ 64 + static int exec_command(int argc, const char *const argv[]) { @@ -143,9 +147,9 @@ exec_command(int argc, const char *const argv[]) #define THRET_FAILURE ((void *)0) struct TestContext { - char ifname[64]; - char ifname_ext[128]; - char bdgname[64]; + char ifname[NM_IFNAMSZ]; + char ifname_ext[NM_IFNAMSZ]; + char bdgname[NM_IFNAMSZ]; uint32_t nr_tx_slots; /* slots in tx rings */ uint32_t nr_rx_slots; /* slots in rx rings */ uint16_t nr_tx_rings; /* number of tx rings */ @@ -166,6 +170,10 @@ struct TestContext { void *csb; /* CSB entries (atok and ktoa) */ struct nmreq_option *nr_opt; /* list of options */ sem_t *sem; /* for thread synchronization */ + + struct nmctx *nmctx; + const char *ifparse; + struct nmport_d *nmport; /* nmport descriptor from libnetmap */ }; static struct TestContext ctx_; @@ -177,7 +185,8 @@ nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname) { memset(hdr, 0, sizeof(*hdr)); hdr->nr_version = NETMAP_API; - strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name) - 1); + assert(strlen(ifname) < NM_IFNAMSZ); + strncpy(hdr->nr_name, ifname, sizeof(hdr->nr_name)); } /* Single NETMAP_REQ_PORT_INFO_GET. */ @@ -522,16 +531,30 @@ port_register_hwall_rx(struct TestContext *ctx) return port_register(ctx); } + +static int +vale_mkname(char *vpname, struct TestContext *ctx) +{ + if (snprintf(vpname, NM_IFNAMSZ, "%s:%s", ctx->bdgname, ctx->ifname_ext) >= NM_IFNAMSZ) { + fprintf(stderr, "%s:%s too long (max %d chars)\n", ctx->bdgname, ctx->ifname_ext, + NM_IFNAMSZ - 1); + return -1; + } + return 0; +} + + /* NETMAP_REQ_VALE_ATTACH */ static int vale_attach(struct TestContext *ctx) { struct nmreq_vale_attach req; struct nmreq_header hdr; - char vpname[sizeof(ctx->bdgname) + 1 + sizeof(ctx->ifname_ext)]; + char vpname[NM_IFNAMSZ]; int ret; - snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext); + if (vale_mkname(vpname, ctx) < 0) + return -1; printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname); nmreq_hdr_init(&hdr, vpname); @@ -563,10 +586,11 @@ vale_detach(struct TestContext *ctx) { struct nmreq_header hdr; struct nmreq_vale_detach req; - char vpname[256]; + char vpname[NM_IFNAMSZ]; int ret; - snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext); + if (vale_mkname(vpname, ctx) < 0) + return -1; printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname); nmreq_hdr_init(&hdr, vpname); @@ -818,7 +842,7 @@ pipe_slave(struct TestContext *ctx) } /* Test PORT_INFO_GET and POOLS_INFO_GET on a pipe. This is useful to test the - * registration request used internall by netmap. */ + * registration request used internally by netmap. */ static int pipe_port_info_get(struct TestContext *ctx) { @@ -841,10 +865,12 @@ vale_polling_enable(struct TestContext *ctx) { struct nmreq_vale_polling req; struct nmreq_header hdr; - char vpname[256]; + char vpname[NM_IFNAMSZ]; int ret; - snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext); + if (vale_mkname(vpname, ctx) < 0) + return -1; + printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname); nmreq_hdr_init(&hdr, vpname); @@ -873,10 +899,12 @@ vale_polling_disable(struct TestContext *ctx) { struct nmreq_vale_polling req; struct nmreq_header hdr; - char vpname[256]; + char vpname[NM_IFNAMSZ]; int ret; - snprintf(vpname, sizeof(vpname), "%s:%s", ctx->bdgname, ctx->ifname_ext); + if (vale_mkname(vpname, ctx) < 0) + return -1; + printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname); nmreq_hdr_init(&hdr, vpname); @@ -1715,6 +1743,271 @@ null_port_sync(struct TestContext *ctx) return 0; } +struct nmreq_parse_test { + const char *ifname; + const char *exp_port; + const char *exp_suff; + int exp_error; + uint32_t exp_mode; + uint16_t exp_ringid; + uint64_t exp_flags; +}; + +static struct nmreq_parse_test nmreq_parse_tests[] = { + /* port spec is the input. The expected results are as follows: + * - port: what should go into hdr.nr_name + * - suff: the trailing part of the input after parsing (NULL means equal to port spec) + * - err: the expected return value, interpreted as follows + * err > 0 => nmreq_header_parse should fail with the given error + * err < 0 => nrmeq_header_parse should succeed, but nmreq_register_decode should + * fail with error |err| + * err = 0 => should succeed + * - mode, ringid flags: what should go into the corresponding nr_* fields in the + * nmreq_register struct in case of success + */ + + /*port spec*/ /*port*/ /*suff*/ /*err*/ /*mode*/ /*ringid*/ /*flags*/ + { "netmap:eth0", "eth0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:eth0-1", "eth0", "", 0, NR_REG_ONE_NIC, 1, 0 }, + { "netmap:eth0-", "eth0", "-", -EINVAL,0, 0, 0 }, + { "netmap:eth0/x", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_EXCLUSIVE }, + { "netmap:eth0/z", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_ZCOPY_MON }, + { "netmap:eth0/r", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_MONITOR_RX }, + { "netmap:eth0/t", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_MONITOR_TX }, + { "netmap:eth0-2/Tx", "eth0", "", 0, NR_REG_ONE_NIC, 2, NR_TX_RINGS_ONLY|NR_EXCLUSIVE }, + { "netmap:eth0*", "eth0", "", 0, NR_REG_NIC_SW, 0, 0 }, + { "netmap:eth0^", "eth0", "", 0, NR_REG_SW, 0, 0 }, + { "netmap:eth0@2", "eth0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:eth0@2/R", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY }, + { "netmap:eth0@netmap:lo/R", "eth0", "@netmap:lo/R", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:eth0/R@xxx", "eth0", "@xxx", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY }, + { "netmap:eth0@2/R@2", "eth0", "", 0, NR_REG_ALL_NIC, 0, NR_RX_RINGS_ONLY }, + { "netmap:eth0@2/R@3", "eth0", "@2/R@3", -EINVAL,0, 0, 0 }, + { "netmap:eth0@", "eth0", "@", -EINVAL,0, 0, 0 }, + { "netmap:", "", NULL, EINVAL, 0, 0, 0 }, + { "netmap:^", "", NULL, EINVAL, 0, 0, 0 }, + { "netmap:{", "", NULL, EINVAL, 0, 0, 0 }, + { "netmap:vale0:0", NULL, NULL, EINVAL, 0, 0, 0 }, + { "eth0", NULL, NULL, EINVAL, 0, 0, 0 }, + { "vale0:0", "vale0:0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "vale:0", "vale:0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "valeXXX:YYY", "valeXXX:YYY", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "valeXXX:YYY-4", "valeXXX:YYY", "", 0, NR_REG_ONE_NIC, 4, 0 }, + { "netmapXXX:eth0", NULL, NULL, EINVAL, 0, 0, 0 }, + { "netmap:14", "14", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:eth0&", NULL, NULL, EINVAL, 0, 0, 0 }, + { "netmap:pipe{0", "pipe{0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:pipe{in", "pipe{in", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:pipe{in-7", "pipe{in", "", 0, NR_REG_ONE_NIC, 7, 0 }, + { "vale0:0{0", "vale0:0{0", "", 0, NR_REG_ALL_NIC, 0, 0 }, + { "netmap:pipe{1}2", NULL, NULL, EINVAL, 0, 0, 0 }, + { "vale0:0@opt", "vale0:0", "@opt", 0, NR_REG_ALL_NIC, 0, 0 }, + { "vale0:0/Tx@opt", "vale0:0", "@opt", 0, NR_REG_ALL_NIC, 0, NR_TX_RINGS_ONLY|NR_EXCLUSIVE }, + { "vale0:0-3@opt", "vale0:0", "@opt", 0, NR_REG_ONE_NIC, 3, 0 }, + { "vale0:0@", "vale0:0", "@", -EINVAL,0, 0, 0 }, + { "", NULL, NULL, EINVAL, 0, 0, 0 }, + { NULL, NULL, NULL, 0, 0, 0, 0 }, +}; + +static void +randomize(void *dst, size_t n) +{ + size_t i; + char *dst_ = dst; + + for (i = 0; i < n; i++) + dst_[i] = (char)random(); +} + +static int +nmreq_hdr_parsing(struct TestContext *ctx, + struct nmreq_parse_test *t, + struct nmreq_header *hdr) +{ + const char *save; + struct nmreq_header orig_hdr; + + save = ctx->ifparse = t->ifname; + orig_hdr = *hdr; + + printf("nmreq_header: \"%s\"\n", ctx->ifparse); + if (nmreq_header_decode(&ctx->ifparse, hdr, ctx->nmctx) < 0) { + if (t->exp_error > 0) { + if (errno != t->exp_error) { + printf("!!! got errno=%d, want %d\n", + errno, t->exp_error); + return -1; + } + if (ctx->ifparse != save) { + printf("!!! parse error, but first arg changed\n"); + return -1; + } + if (memcmp(&orig_hdr, hdr, sizeof(*hdr))) { + printf("!!! parse error, but header changed\n"); + return -1; + } + return 0; + } + printf ("!!! nmreq_header_decode was expected to succeed, but it failed with error %d\n", errno); + return -1; + } + if (t->exp_error > 0) { + printf("!!! nmreq_header_decode returns 0, but error %d was expected\n", t->exp_error); + return -1; + } + if (strcmp(t->exp_port, hdr->nr_name) != 0) { + printf("!!! got '%s', want '%s'\n", hdr->nr_name, t->exp_port); + return -1; + } + if (hdr->nr_reqtype != orig_hdr.nr_reqtype || + hdr->nr_options != orig_hdr.nr_options || + hdr->nr_body != orig_hdr.nr_body) { + printf("!!! some fields of the nmreq_header where changed unexpectedly\n"); + return -1; + } + return 0; +} + +static int +nmreq_reg_parsing(struct TestContext *ctx, + struct nmreq_parse_test *t, + struct nmreq_register *reg) +{ + const char *save; + struct nmreq_register orig_reg; + + + save = ctx->ifparse; + orig_reg = *reg; + + printf("nmreq_register: \"%s\"\n", ctx->ifparse); + if (nmreq_register_decode(&ctx->ifparse, reg, ctx->nmctx) < 0) { + if (t->exp_error < 0) { + if (errno != -t->exp_error) { + printf("!!! got errno=%d, want %d\n", + errno, -t->exp_error); + return -1; + } + if (ctx->ifparse != save) { + printf("!!! parse error, but first arg changed\n"); + return -1; + } + if (memcmp(&orig_reg, reg, sizeof(*reg))) { + printf("!!! parse error, but nmreq_register changed\n"); + return -1; + } + return 0; + } + printf ("!!! parse failed but it should have succeeded\n"); + return -1; + } + if (t->exp_error < 0) { + printf("!!! nmreq_register_decode returns 0, but error %d was expected\n", -t->exp_error); + return -1; + } + if (reg->nr_mode != t->exp_mode) { + printf("!!! got nr_mode '%d', want '%d'\n", reg->nr_mode, t->exp_mode); + return -1; + } + if (reg->nr_ringid != t->exp_ringid) { + printf("!!! got nr_ringid '%d', want '%d'\n", reg->nr_ringid, t->exp_ringid); + return -1; + } + if (reg->nr_flags != t->exp_flags) { + printf("!!! got nm_flags '%llx', want '%llx\n", (unsigned long long)reg->nr_flags, + (unsigned long long)t->exp_flags); + return -1; + } + if (reg->nr_offset != orig_reg.nr_offset || + reg->nr_memsize != orig_reg.nr_memsize || + reg->nr_tx_slots != orig_reg.nr_tx_slots || + reg->nr_rx_slots != orig_reg.nr_rx_slots || + reg->nr_tx_rings != orig_reg.nr_tx_rings || + reg->nr_rx_rings != orig_reg.nr_rx_rings || + reg->nr_extra_bufs != orig_reg.nr_extra_bufs) + { + printf("!!! some fields of the nmreq_register where changed unexpectedly\n"); + return -1; + } + return 0; +} + +static void +nmctx_parsing_error(struct nmctx *ctx, const char *msg) +{ + (void)ctx; + printf(" got message: %s\n", msg); +} + +static int +nmreq_parsing(struct TestContext *ctx) +{ + struct nmreq_parse_test *t; + struct nmreq_header hdr; + struct nmreq_register reg; + struct nmctx test_nmctx, *nmctx; + int ret = 0; + + nmctx = nmctx_get(); + if (nmctx == NULL) { + printf("Failed to acquire nmctx: %s", strerror(errno)); + return -1; + } + test_nmctx = *nmctx; + test_nmctx.error = nmctx_parsing_error; + ctx->nmctx = &test_nmctx; + for (t = nmreq_parse_tests; t->ifname != NULL; t++) { + const char *exp_suff = t->exp_suff != NULL ? + t->exp_suff : t->ifname; + + randomize(&hdr, sizeof(hdr)); + randomize(®, sizeof(reg)); + reg.nr_mem_id = 0; + if (nmreq_hdr_parsing(ctx, t, &hdr) < 0) { + ret = -1; + } else if (t->exp_error <= 0 && nmreq_reg_parsing(ctx, t, ®) < 0) { + ret = -1; + } + if (strcmp(ctx->ifparse, exp_suff) != 0) { + printf("!!! string suffix after parse is '%s', but it should be '%s'\n", + ctx->ifparse, exp_suff); + ret = -1; + } + } + return ret; +} + +static int +binarycomp(struct TestContext *ctx) +{ +#define ckroff(f, o) do {\ + if (offsetof(struct netmap_ring, f) != (o)) {\ + printf("offset of netmap_ring.%s is %zd, but it should be %d",\ + #f, offsetof(struct netmap_ring, f), (o));\ + return -1;\ + }\ +} while (0) + + (void)ctx; + + ckroff(buf_ofs, 0); + ckroff(num_slots, 8); + ckroff(nr_buf_size, 12); + ckroff(ringid, 16); + ckroff(dir, 18); + ckroff(head, 20); + ckroff(cur, 24); + ckroff(tail, 28); + ckroff(flags, 32); + ckroff(ts, 40); + ckroff(offset_mask, 56); + ckroff(buf_align, 64); + ckroff(sem, 128); + ckroff(slot, 256); + + return 0; +} + static void usage(const char *prog) { @@ -1783,6 +2076,8 @@ static struct mytest tests[] = { decltest(legacy_regif_extra_bufs), decltest(legacy_regif_extra_bufs_pipe), decltest(legacy_regif_extra_bufs_pipe_vale), + decltest(nmreq_parsing), + decltest(binarycomp), }; static void