netmap: valectl: switch to libnetmap

Use the newer libnetmap (included in base) rather than the older
nm_open()/nm_close() defined in netmap_user.h

MFC after:      3 days
This commit is contained in:
Vincenzo Maffione 2020-11-22 10:02:56 +00:00
parent 91c60ce828
commit 9fd3f663da
2 changed files with 303 additions and 136 deletions

View File

@ -5,4 +5,6 @@ MAN= valectl.8
WARNS?= 3
LIBADD= netmap
.include <bsd.prog.mk>

View File

@ -25,9 +25,8 @@
/* $FreeBSD$ */
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
#include <net/netmap.h>
#define LIBNETMAP_NOTHREADSAFE
#include <libnetmap.h>
#include <errno.h>
#include <stdio.h>
@ -42,14 +41,58 @@
#include <libgen.h> /* basename */
#include <stdlib.h> /* atoi, free */
int verbose;
struct args {
const char *name;
const char *config;
const char *mem_id;
uint16_t nr_reqtype;
uint32_t nr_mode;
};
static void
parse_nmr_config(const char* conf, struct nmreq *nmr)
dump_port_info(struct nmreq_port_info_get *v)
{
printf("memsize: %"PRIu64"\n", v->nr_memsize);
printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots);
printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots);
printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings);
printf("rx_rings %"PRIu16"\n", v->nr_rx_rings);
printf("mem_id: %"PRIu16"\n", v->nr_mem_id);
}
static void
dump_newif(struct nmreq_vale_newif *v)
{
printf("tx_slots: %"PRIu32"\n", v->nr_tx_slots);
printf("rx_slots: %"PRIu32"\n", v->nr_rx_slots);
printf("tx_rings: %"PRIu16"\n", v->nr_tx_rings);
printf("rx_ring: %"PRIu16"\n", v->nr_rx_rings);
printf("mem_id: %"PRIu16"\n", v->nr_mem_id);
}
static void
dump_vale_list(struct nmreq_vale_list *v)
{
printf("bridge_idx: %"PRIu16"\n", v->nr_bridge_idx);
printf("port_idx: %"PRIu16"\n", v->nr_port_idx);
}
static void
parse_ring_config(const char* conf,
uint32_t *nr_tx_slots,
uint32_t *nr_rx_slots,
uint16_t *nr_tx_rings,
uint16_t *nr_rx_rings)
{
char *w, *tok;
int i, v;
nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
*nr_tx_rings = *nr_rx_rings = 0;
*nr_tx_slots = *nr_rx_slots = 0;
if (conf == NULL || ! *conf)
return;
w = strdup(conf);
@ -57,137 +100,235 @@ parse_nmr_config(const char* conf, struct nmreq *nmr)
v = atoi(tok);
switch (i) {
case 0:
nmr->nr_tx_slots = nmr->nr_rx_slots = v;
*nr_tx_slots = *nr_rx_slots = v;
break;
case 1:
nmr->nr_rx_slots = v;
*nr_rx_slots = v;
break;
case 2:
nmr->nr_tx_rings = nmr->nr_rx_rings = v;
*nr_tx_rings = *nr_rx_rings = v;
break;
case 3:
nmr->nr_rx_rings = v;
*nr_rx_rings = v;
break;
default:
D("ignored config: %s", tok);
fprintf(stderr, "ignored config: %s", tok);
break;
}
}
D("txr %d txd %d rxr %d rxd %d",
nmr->nr_tx_rings, nmr->nr_tx_slots,
nmr->nr_rx_rings, nmr->nr_rx_slots);
ND("txr %d txd %d rxr %d rxd %d",
*nr_tx_rings, *nr_tx_slots,
*nr_rx_rings, *nr_rx_slots);
free(w);
}
static int
bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config, int nr_arg2)
parse_poll_config(const char *conf, struct nmreq_vale_polling *v)
{
struct nmreq nmr;
int error = 0;
int fd = open("/dev/netmap", O_RDWR);
char *w, *tok;
int i, p;
if (fd == -1) {
D("Unable to open /dev/netmap");
if (conf == NULL || ! *conf) {
fprintf(stderr, "invalid null/empty config\n");
return -1;
}
bzero(&nmr, sizeof(nmr));
nmr.nr_version = NETMAP_API;
if (name != NULL) /* might be NULL */
strncpy(nmr.nr_name, name, sizeof(nmr.nr_name)-1);
nmr.nr_cmd = nr_cmd;
parse_nmr_config(nmr_config, &nmr);
nmr.nr_arg2 = nr_arg2;
switch (nr_cmd) {
case NETMAP_BDG_DELIF:
case NETMAP_BDG_NEWIF:
error = ioctl(fd, NIOCREGIF, &nmr);
if (error == -1) {
ND("Unable to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
perror(name);
} else {
ND("Success to %s %s", nr_cmd == NETMAP_BDG_DELIF ? "delete":"create", name);
}
break;
case NETMAP_BDG_ATTACH:
case NETMAP_BDG_DETACH:
nmr.nr_flags = NR_REG_ALL_NIC;
if (nr_arg && nr_arg != NETMAP_BDG_HOST) {
nmr.nr_flags = NR_REG_NIC_SW;
nr_arg = 0;
}
nmr.nr_arg1 = nr_arg;
error = ioctl(fd, NIOCREGIF, &nmr);
if (error == -1) {
ND("Unable to %s %s to the bridge", nr_cmd ==
NETMAP_BDG_DETACH?"detach":"attach", name);
perror(name);
} else
ND("Success to %s %s to the bridge", nr_cmd ==
NETMAP_BDG_DETACH?"detach":"attach", name);
break;
case NETMAP_BDG_LIST:
if (strlen(nmr.nr_name)) { /* name to bridge/port info */
error = ioctl(fd, NIOCGINFO, &nmr);
if (error) {
ND("Unable to obtain info for %s", name);
perror(name);
} else
D("%s at bridge:%d port:%d", name, nmr.nr_arg1,
nmr.nr_arg2);
w = strdup(conf);
for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
p = atoi(tok);
switch (i) {
case 0:
v->nr_mode = p ? NETMAP_POLLING_MODE_MULTI_CPU :
NETMAP_POLLING_MODE_SINGLE_CPU;
break;
case 1:
v->nr_first_cpu_id = p;
break;
case 2:
if (v->nr_mode != NETMAP_POLLING_MODE_MULTI_CPU) {
fprintf(stderr, "too many numbers in '%s'\n", conf);
return -1;
}
v->nr_num_polling_cpus = p;
break;
case 3:
fprintf(stderr, "too many numbers in '%s'\n", conf);
return -1;
}
}
free(w);
return 0;
}
/* scan all the bridges and ports */
nmr.nr_arg1 = nmr.nr_arg2 = 0;
for (; !ioctl(fd, NIOCGINFO, &nmr); nmr.nr_arg2++) {
D("bridge:%d port:%d %s", nmr.nr_arg1, nmr.nr_arg2,
nmr.nr_name);
nmr.nr_name[0] = '\0';
static int32_t
parse_mem_id(const char *mem_id)
{
int32_t id;
if (mem_id == NULL)
return 0;
if (isdigit(*mem_id))
return atoi(mem_id);
id = nmreq_get_mem_id(&mem_id, nmctx_get());
if (id == 0) {
fprintf(stderr, "invalid format in '-m %s' (missing 'netmap:'?)\n", mem_id);
return -1;
}
return id;
}
static int
list_all(int fd, struct nmreq_header *hdr)
{
int error;
struct nmreq_vale_list *vale_list =
(struct nmreq_vale_list *)hdr->nr_body;
for (;;) {
hdr->nr_name[0] = '\0';
error = ioctl(fd, NIOCCTRL, hdr);
if (error < 0) {
if (errno == ENOENT)
break;
fprintf(stderr, "failed to list all: %s\n", strerror(errno));
return 1;
}
printf("%s bridge_idx %"PRIu16" port_idx %"PRIu32"\n", hdr->nr_name,
vale_list->nr_bridge_idx, vale_list->nr_port_idx);
vale_list->nr_port_idx++;
}
return 1;
}
static int
bdg_ctl(struct args *a)
{
struct nmreq_header hdr;
struct nmreq_vale_attach vale_attach;
struct nmreq_vale_detach vale_detach;
struct nmreq_vale_newif vale_newif;
struct nmreq_vale_list vale_list;
struct nmreq_vale_polling vale_polling;
struct nmreq_port_info_get port_info_get;
int error = 0;
int fd;
int32_t mem_id;
const char *action = NULL;
fd = open("/dev/netmap", O_RDWR);
if (fd == -1) {
perror("/dev/netmap");
return 1;
}
bzero(&hdr, sizeof(hdr));
hdr.nr_version = NETMAP_API;
if (a->name != NULL) { /* might be NULL */
strncpy(hdr.nr_name, a->name, NETMAP_REQ_IFNAMSIZ - 1);
hdr.nr_name[NETMAP_REQ_IFNAMSIZ - 1] = '\0';
}
hdr.nr_reqtype = a->nr_reqtype;
switch (a->nr_reqtype) {
case NETMAP_REQ_VALE_DELIF:
/* no body */
action = "remove";
break;
case NETMAP_BDG_POLLING_ON:
case NETMAP_BDG_POLLING_OFF:
/* We reuse nmreq fields as follows:
* nr_tx_slots: 0 and non-zero indicate REG_ALL_NIC
* REG_ONE_NIC, respectively.
* nr_rx_slots: CPU core index. This also indicates the
* first queue in the case of REG_ONE_NIC
* nr_tx_rings: (REG_ONE_NIC only) indicates the
* number of CPU cores or the last queue
*/
nmr.nr_flags |= nmr.nr_tx_slots ?
NR_REG_ONE_NIC : NR_REG_ALL_NIC;
nmr.nr_ringid = nmr.nr_rx_slots;
/* number of cores/rings */
if (nmr.nr_flags == NR_REG_ALL_NIC)
nmr.nr_arg1 = 1;
else
nmr.nr_arg1 = nmr.nr_tx_rings;
error = ioctl(fd, NIOCREGIF, &nmr);
if (!error)
D("polling on %s %s", nmr.nr_name,
nr_cmd == NETMAP_BDG_POLLING_ON ?
"started" : "stopped");
else
D("polling on %s %s (err %d)", nmr.nr_name,
nr_cmd == NETMAP_BDG_POLLING_ON ?
"couldn't start" : "couldn't stop", error);
case NETMAP_REQ_VALE_NEWIF:
memset(&vale_newif, 0, sizeof(vale_newif));
hdr.nr_body = (uintptr_t)&vale_newif;
parse_ring_config(a->config,
&vale_newif.nr_tx_slots,
&vale_newif.nr_rx_slots,
&vale_newif.nr_tx_rings,
&vale_newif.nr_rx_rings);
mem_id = parse_mem_id(a->mem_id);
if (mem_id < 0)
return 1;
vale_newif.nr_mem_id = mem_id;
action = "create";
break;
default: /* GINFO */
nmr.nr_cmd = nmr.nr_arg1 = nmr.nr_arg2 = 0;
error = ioctl(fd, NIOCGINFO, &nmr);
if (error) {
ND("Unable to get if info for %s", name);
perror(name);
} else
D("%s: %d queues.", name, nmr.nr_rx_rings);
case NETMAP_REQ_VALE_ATTACH:
memset(&vale_attach, 0, sizeof(vale_attach));
hdr.nr_body = (uintptr_t)&vale_attach;
vale_attach.reg.nr_mode = a->nr_mode;
parse_ring_config(a->config,
&vale_attach.reg.nr_tx_slots,
&vale_attach.reg.nr_rx_slots,
&vale_attach.reg.nr_tx_rings,
&vale_attach.reg.nr_rx_rings);
mem_id = parse_mem_id(a->mem_id);
if (mem_id < 0)
return 1;
vale_attach.reg.nr_mem_id = mem_id;
action = "attach";
break;
case NETMAP_REQ_VALE_DETACH:
memset(&vale_detach, 0, sizeof(vale_detach));
hdr.nr_body = (uintptr_t)&vale_detach;
action = "detach";
break;
case NETMAP_REQ_VALE_LIST:
memset(&vale_list, 0, sizeof(vale_list));
hdr.nr_body = (uintptr_t)&vale_list;
if (a->name == NULL) {
return list_all(fd, &hdr);
}
action = "list";
break;
case NETMAP_REQ_VALE_POLLING_ENABLE:
action = "enable polling on";
/* fall through */
case NETMAP_REQ_VALE_POLLING_DISABLE:
memset(&vale_polling, 0, sizeof(vale_polling));
hdr.nr_body = (uintptr_t)&vale_polling;
parse_poll_config(a->config, &vale_polling);
if (action == NULL)
action ="disable polling on";
break;
case NETMAP_REQ_PORT_INFO_GET:
memset(&port_info_get, 0, sizeof(port_info_get));
hdr.nr_body = (uintptr_t)&port_info_get;
action = "obtain info for";
break;
}
error = ioctl(fd, NIOCCTRL, &hdr);
if (error < 0) {
fprintf(stderr, "failed to %s %s: %s\n",
action, a->name, strerror(errno));
return 1;
}
switch (hdr.nr_reqtype) {
case NETMAP_REQ_VALE_NEWIF:
if (verbose) {
dump_newif(&vale_newif);
}
break;
case NETMAP_REQ_VALE_ATTACH:
if (verbose) {
printf("port_index: %"PRIu32"\n", vale_attach.port_index);
}
break;
case NETMAP_REQ_VALE_DETACH:
if (verbose) {
printf("port_index: %"PRIu32"\n", vale_detach.port_index);
}
break;
case NETMAP_REQ_VALE_LIST:
dump_vale_list(&vale_list);
break;
case NETMAP_REQ_PORT_INFO_GET:
dump_port_info(&port_info_get);
break;
}
close(fd);
@ -199,82 +340,106 @@ usage(int errcode)
{
fprintf(stderr,
"Usage:\n"
"valectl arguments\n"
"vale-ctl [arguments]\n"
"\t-g interface interface name to get info\n"
"\t-d interface interface name to be detached\n"
"\t-a interface interface name to be attached\n"
"\t-h interface interface name to be attached with the host stack\n"
"\t-n interface interface name to be created\n"
"\t-r interface interface name to be deleted\n"
"\t-l list all or specified bridge's interfaces (default)\n"
"\t-l vale-port show bridge and port indices\n"
"\t-C string ring/slot setting of an interface creating by -n\n"
"\t-p interface start polling. Additional -C x,y,z configures\n"
"\t\t x: 0 (REG_ALL_NIC) or 1 (REG_ONE_NIC),\n"
"\t\t y: CPU core id for ALL_NIC and core/ring for ONE_NIC\n"
"\t\t z: (ONE_NIC only) num of total cores/rings\n"
"\t-P interface stop polling\n"
"\t-m memid to use when creating a new interface\n");
"\t-m memid to use when creating a new interface\n"
"\t-v increase verbosity\n"
"with no arguments: list all existing vale ports\n");
exit(errcode);
}
int
main(int argc, char *argv[])
{
int ch, nr_cmd = 0, nr_arg = 0;
char *name = NULL, *nmr_config = NULL;
int nr_arg2 = 0;
int ch;
struct args a = {
.name = NULL,
.config = NULL,
.mem_id = NULL,
.nr_reqtype = 0,
.nr_mode = NR_REG_ALL_NIC,
};
while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:")) != -1) {
if (ch != 'C' && ch != 'm')
name = optarg; /* default */
while ((ch = getopt(argc, argv, "d:a:h:g:l:n:r:C:p:P:m:v")) != -1) {
switch (ch) {
default:
fprintf(stderr, "bad option %c %s", ch, optarg);
usage(-1);
usage(1);
break;
case 'd':
nr_cmd = NETMAP_BDG_DETACH;
a.nr_reqtype = NETMAP_REQ_VALE_DETACH;
a.name = optarg;
break;
case 'a':
nr_cmd = NETMAP_BDG_ATTACH;
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
a.nr_mode = NR_REG_ALL_NIC;
a.name = optarg;
break;
case 'h':
nr_cmd = NETMAP_BDG_ATTACH;
nr_arg = NETMAP_BDG_HOST;
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH;
a.nr_mode = NR_REG_NIC_SW;
a.name = optarg;
break;
case 'n':
nr_cmd = NETMAP_BDG_NEWIF;
a.nr_reqtype = NETMAP_REQ_VALE_NEWIF;
a.name = optarg;
break;
case 'r':
nr_cmd = NETMAP_BDG_DELIF;
a.nr_reqtype = NETMAP_REQ_VALE_DELIF;
a.name = optarg;
break;
case 'g':
nr_cmd = 0;
a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
a.name = optarg;
break;
case 'l':
nr_cmd = NETMAP_BDG_LIST;
a.nr_reqtype = NETMAP_REQ_VALE_LIST;
a.name = optarg;
if (strncmp(a.name, NM_BDG_NAME, strlen(NM_BDG_NAME))) {
fprintf(stderr, "invalid vale port name: '%s'\n", a.name);
usage(1);
}
break;
case 'C':
nmr_config = strdup(optarg);
a.config = optarg;
break;
case 'p':
nr_cmd = NETMAP_BDG_POLLING_ON;
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE;
a.name = optarg;
break;
case 'P':
nr_cmd = NETMAP_BDG_POLLING_OFF;
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE;
a.name = optarg;
break;
case 'm':
nr_arg2 = atoi(optarg);
a.mem_id = optarg;
break;
case 'v':
verbose++;
break;
}
}
if (optind != argc) {
// fprintf(stderr, "optind %d argc %d\n", optind, argc);
usage(-1);
usage(1);
}
if (argc == 1) {
nr_cmd = NETMAP_BDG_LIST;
name = NULL;
a.nr_reqtype = NETMAP_REQ_VALE_LIST;
a.name = NULL;
}
return bdg_ctl(name, nr_cmd, nr_arg, nmr_config, nr_arg2) ? 1 : 0;
if (!a.nr_reqtype) {
usage(1);
}
return bdg_ctl(&a);
}