netmap: update unit tests with libnetmap tests

This commit is contained in:
Vincenzo Maffione 2021-04-02 14:39:30 +00:00
parent ab639bb287
commit 36d6e65722
2 changed files with 309 additions and 13 deletions

View File

@ -10,5 +10,6 @@ PLAIN_TESTS_C+= ctrl-api-test
CFLAGS+= -I${SRCTOP}/tests CFLAGS+= -I${SRCTOP}/tests
LIBADD+= pthread LIBADD+= pthread
LIBADD+= netmap
.include <bsd.test.mk> .include <bsd.test.mk>

View File

@ -46,6 +46,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <libnetmap.h>
#include <net/if.h> #include <net/if.h>
#include <net/netmap.h> #include <net/netmap.h>
#include <pthread.h> #include <pthread.h>
@ -57,6 +58,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
#include <stddef.h>
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include "freebsd_test_suite/macros.h" #include "freebsd_test_suite/macros.h"
@ -71,6 +73,8 @@ eventfd(int x __unused, int y __unused)
#include <sys/eventfd.h> #include <sys/eventfd.h>
#endif #endif
#define NM_IFNAMSZ 64
static int static int
exec_command(int argc, const char *const argv[]) 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) #define THRET_FAILURE ((void *)0)
struct TestContext { struct TestContext {
char ifname[64]; char ifname[NM_IFNAMSZ];
char ifname_ext[128]; char ifname_ext[NM_IFNAMSZ];
char bdgname[64]; char bdgname[NM_IFNAMSZ];
uint32_t nr_tx_slots; /* slots in tx rings */ uint32_t nr_tx_slots; /* slots in tx rings */
uint32_t nr_rx_slots; /* slots in rx rings */ uint32_t nr_rx_slots; /* slots in rx rings */
uint16_t nr_tx_rings; /* number of tx rings */ uint16_t nr_tx_rings; /* number of tx rings */
@ -166,6 +170,10 @@ struct TestContext {
void *csb; /* CSB entries (atok and ktoa) */ void *csb; /* CSB entries (atok and ktoa) */
struct nmreq_option *nr_opt; /* list of options */ struct nmreq_option *nr_opt; /* list of options */
sem_t *sem; /* for thread synchronization */ sem_t *sem; /* for thread synchronization */
struct nmctx *nmctx;
const char *ifparse;
struct nmport_d *nmport; /* nmport descriptor from libnetmap */
}; };
static struct TestContext ctx_; static struct TestContext ctx_;
@ -177,7 +185,8 @@ nmreq_hdr_init(struct nmreq_header *hdr, const char *ifname)
{ {
memset(hdr, 0, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr));
hdr->nr_version = NETMAP_API; 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. */ /* Single NETMAP_REQ_PORT_INFO_GET. */
@ -522,16 +531,30 @@ port_register_hwall_rx(struct TestContext *ctx)
return port_register(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 */ /* NETMAP_REQ_VALE_ATTACH */
static int static int
vale_attach(struct TestContext *ctx) vale_attach(struct TestContext *ctx)
{ {
struct nmreq_vale_attach req; struct nmreq_vale_attach req;
struct nmreq_header hdr; struct nmreq_header hdr;
char vpname[sizeof(ctx->bdgname) + 1 + sizeof(ctx->ifname_ext)]; char vpname[NM_IFNAMSZ];
int ret; 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); printf("Testing NETMAP_REQ_VALE_ATTACH on '%s'\n", vpname);
nmreq_hdr_init(&hdr, vpname); nmreq_hdr_init(&hdr, vpname);
@ -563,10 +586,11 @@ vale_detach(struct TestContext *ctx)
{ {
struct nmreq_header hdr; struct nmreq_header hdr;
struct nmreq_vale_detach req; struct nmreq_vale_detach req;
char vpname[256]; char vpname[NM_IFNAMSZ];
int ret; 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); printf("Testing NETMAP_REQ_VALE_DETACH on '%s'\n", vpname);
nmreq_hdr_init(&hdr, 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 /* 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 static int
pipe_port_info_get(struct TestContext *ctx) pipe_port_info_get(struct TestContext *ctx)
{ {
@ -841,10 +865,12 @@ vale_polling_enable(struct TestContext *ctx)
{ {
struct nmreq_vale_polling req; struct nmreq_vale_polling req;
struct nmreq_header hdr; struct nmreq_header hdr;
char vpname[256]; char vpname[NM_IFNAMSZ];
int ret; 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); printf("Testing NETMAP_REQ_VALE_POLLING_ENABLE on '%s'\n", vpname);
nmreq_hdr_init(&hdr, vpname); nmreq_hdr_init(&hdr, vpname);
@ -873,10 +899,12 @@ vale_polling_disable(struct TestContext *ctx)
{ {
struct nmreq_vale_polling req; struct nmreq_vale_polling req;
struct nmreq_header hdr; struct nmreq_header hdr;
char vpname[256]; char vpname[NM_IFNAMSZ];
int ret; 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); printf("Testing NETMAP_REQ_VALE_POLLING_DISABLE on '%s'\n", vpname);
nmreq_hdr_init(&hdr, vpname); nmreq_hdr_init(&hdr, vpname);
@ -1715,6 +1743,271 @@ null_port_sync(struct TestContext *ctx)
return 0; 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(&reg, 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, &reg) < 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 static void
usage(const char *prog) usage(const char *prog)
{ {
@ -1783,6 +2076,8 @@ static struct mytest tests[] = {
decltest(legacy_regif_extra_bufs), decltest(legacy_regif_extra_bufs),
decltest(legacy_regif_extra_bufs_pipe), decltest(legacy_regif_extra_bufs_pipe),
decltest(legacy_regif_extra_bufs_pipe_vale), decltest(legacy_regif_extra_bufs_pipe_vale),
decltest(nmreq_parsing),
decltest(binarycomp),
}; };
static void static void