Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/valectl/valectl.c
Context not available. | |||||
/* $FreeBSD$ */ | /* $FreeBSD$ */ | ||||
#define LIBNETMAP_NOTHREADSAFE | #define NETMAP_WITH_LIBS | ||||
#include <libnetmap.h> | #include <net/netmap_user.h> | ||||
#include <net/netmap.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
Context not available. | |||||
#include <libgen.h> /* basename */ | #include <libgen.h> /* basename */ | ||||
#include <stdlib.h> /* atoi, free */ | #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 | |||||
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 | static void | ||||
dump_newif(struct nmreq_vale_newif *v) | parse_nmr_config(const char* conf, struct nmreq *nmr) | ||||
{ | |||||
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; | char *w, *tok; | ||||
int i, v; | int i, v; | ||||
*nr_tx_rings = *nr_rx_rings = 0; | nmr->nr_tx_rings = nmr->nr_rx_rings = 0; | ||||
*nr_tx_slots = *nr_rx_slots = 0; | nmr->nr_tx_slots = nmr->nr_rx_slots = 0; | ||||
if (conf == NULL || ! *conf) | if (conf == NULL || ! *conf) | ||||
return; | return; | ||||
w = strdup(conf); | w = strdup(conf); | ||||
Context not available. | |||||
v = atoi(tok); | v = atoi(tok); | ||||
switch (i) { | switch (i) { | ||||
case 0: | case 0: | ||||
*nr_tx_slots = *nr_rx_slots = v; | nmr->nr_tx_slots = nmr->nr_rx_slots = v; | ||||
break; | break; | ||||
case 1: | case 1: | ||||
*nr_rx_slots = v; | nmr->nr_rx_slots = v; | ||||
break; | break; | ||||
case 2: | case 2: | ||||
*nr_tx_rings = *nr_rx_rings = v; | nmr->nr_tx_rings = nmr->nr_rx_rings = v; | ||||
break; | break; | ||||
case 3: | case 3: | ||||
*nr_rx_rings = v; | nmr->nr_rx_rings = v; | ||||
break; | break; | ||||
default: | default: | ||||
fprintf(stderr, "ignored config: %s", tok); | D("ignored config: %s", tok); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
ND("txr %d txd %d rxr %d rxd %d", | D("txr %d txd %d rxr %d rxd %d", | ||||
*nr_tx_rings, *nr_tx_slots, | nmr->nr_tx_rings, nmr->nr_tx_slots, | ||||
*nr_rx_rings, *nr_rx_slots); | nmr->nr_rx_rings, nmr->nr_rx_slots); | ||||
free(w); | free(w); | ||||
} | } | ||||
static int | static int | ||||
parse_poll_config(const char *conf, struct nmreq_vale_polling *v) | bdg_ctl(const char *name, int nr_cmd, int nr_arg, char *nmr_config, int nr_arg2) | ||||
{ | { | ||||
char *w, *tok; | struct nmreq nmr; | ||||
int i, p; | |||||
if (conf == NULL || ! *conf) { | |||||
fprintf(stderr, "invalid null/empty config\n"); | |||||
return -1; | |||||
} | |||||
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; | |||||
} | |||||
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 error = 0; | ||||
int fd; | int fd = open("/dev/netmap", O_RDWR); | ||||
int32_t mem_id; | |||||
const char *action = NULL; | |||||
fd = open("/dev/netmap", O_RDWR); | |||||
if (fd == -1) { | if (fd == -1) { | ||||
perror("/dev/netmap"); | D("Unable to open /dev/netmap"); | ||||
return 1; | 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_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; | |||||
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: | bzero(&nmr, sizeof(nmr)); | ||||
memset(&vale_detach, 0, sizeof(vale_detach)); | nmr.nr_version = NETMAP_API; | ||||
hdr.nr_body = (uintptr_t)&vale_detach; | if (name != NULL) /* might be NULL */ | ||||
action = "detach"; | strncpy(nmr.nr_name, name, sizeof(nmr.nr_name)-1); | ||||
break; | nmr.nr_cmd = nr_cmd; | ||||
parse_nmr_config(nmr_config, &nmr); | |||||
case NETMAP_REQ_VALE_LIST: | nmr.nr_arg2 = nr_arg2; | ||||
memset(&vale_list, 0, sizeof(vale_list)); | |||||
hdr.nr_body = (uintptr_t)&vale_list; | switch (nr_cmd) { | ||||
if (a->name == NULL) { | case NETMAP_BDG_DELIF: | ||||
return list_all(fd, &hdr); | 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); | |||||
} | } | ||||
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; | break; | ||||
case NETMAP_BDG_ATTACH: | |||||
case NETMAP_REQ_PORT_INFO_GET: | case NETMAP_BDG_DETACH: | ||||
memset(&port_info_get, 0, sizeof(port_info_get)); | nmr.nr_flags = NR_REG_ALL_NIC; | ||||
hdr.nr_body = (uintptr_t)&port_info_get; | if (nr_arg && nr_arg != NETMAP_BDG_HOST) { | ||||
action = "obtain info for"; | nmr.nr_flags = NR_REG_NIC_SW; | ||||
break; | nr_arg = 0; | ||||
} | |||||
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); | |||||
} | } | ||||
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; | break; | ||||
case NETMAP_REQ_VALE_ATTACH: | case NETMAP_BDG_LIST: | ||||
if (verbose) { | if (strlen(nmr.nr_name)) { /* name to bridge/port info */ | ||||
printf("port_index: %"PRIu32"\n", vale_attach.port_index); | 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); | |||||
break; | |||||
} | } | ||||
break; | |||||
case NETMAP_REQ_VALE_DETACH: | /* scan all the bridges and ports */ | ||||
if (verbose) { | nmr.nr_arg1 = nmr.nr_arg2 = 0; | ||||
printf("port_index: %"PRIu32"\n", vale_detach.port_index); | 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'; | |||||
} | } | ||||
break; | break; | ||||
case NETMAP_REQ_VALE_LIST: | case NETMAP_BDG_POLLING_ON: | ||||
dump_vale_list(&vale_list); | 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); | |||||
break; | break; | ||||
case NETMAP_REQ_PORT_INFO_GET: | default: /* GINFO */ | ||||
dump_port_info(&port_info_get); | 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); | |||||
break; | break; | ||||
} | } | ||||
close(fd); | close(fd); | ||||
Context not available. | |||||
{ | { | ||||
fprintf(stderr, | fprintf(stderr, | ||||
"Usage:\n" | "Usage:\n" | ||||
"vale-ctl [arguments]\n" | "valectl arguments\n" | ||||
"\t-g interface interface name to get info\n" | "\t-g interface interface name to get info\n" | ||||
"\t-d interface interface name to be detached\n" | "\t-d interface interface name to be detached\n" | ||||
"\t-a interface interface name to be attached\n" | "\t-a interface interface name to be attached\n" | ||||
"\t-h interface interface name to be attached with the host stack\n" | "\t-h interface interface name to be attached with the host stack\n" | ||||
"\t-n interface interface name to be created\n" | "\t-n interface interface name to be created\n" | ||||
"\t-r interface interface name to be deleted\n" | "\t-r interface interface name to be deleted\n" | ||||
"\t-l vale-port show bridge and port indices\n" | "\t-l list all or specified bridge's interfaces (default)\n" | ||||
"\t-C string ring/slot setting of an interface creating by -n\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-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 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 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\t z: (ONE_NIC only) num of total cores/rings\n" | ||||
"\t-P interface stop polling\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); | exit(errcode); | ||||
} | } | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
int ch; | int ch, nr_cmd = 0, nr_arg = 0; | ||||
struct args a = { | char *name = NULL, *nmr_config = NULL; | ||||
.name = NULL, | int nr_arg2 = 0; | ||||
.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:v")) != -1) { | 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 */ | |||||
switch (ch) { | switch (ch) { | ||||
default: | default: | ||||
fprintf(stderr, "bad option %c %s", ch, optarg); | fprintf(stderr, "bad option %c %s", ch, optarg); | ||||
usage(1); | usage(-1); | ||||
break; | break; | ||||
case 'd': | case 'd': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_DETACH; | nr_cmd = NETMAP_BDG_DETACH; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'a': | case 'a': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; | nr_cmd = NETMAP_BDG_ATTACH; | ||||
a.nr_mode = NR_REG_ALL_NIC; | |||||
a.name = optarg; | |||||
break; | break; | ||||
case 'h': | case 'h': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; | nr_cmd = NETMAP_BDG_ATTACH; | ||||
a.nr_mode = NR_REG_NIC_SW; | nr_arg = NETMAP_BDG_HOST; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'n': | case 'n': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_NEWIF; | nr_cmd = NETMAP_BDG_NEWIF; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'r': | case 'r': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_DELIF; | nr_cmd = NETMAP_BDG_DELIF; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'g': | case 'g': | ||||
a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET; | nr_cmd = 0; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'l': | case 'l': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_LIST; | nr_cmd = NETMAP_BDG_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; | break; | ||||
case 'C': | case 'C': | ||||
a.config = optarg; | nmr_config = strdup(optarg); | ||||
break; | break; | ||||
case 'p': | case 'p': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE; | nr_cmd = NETMAP_BDG_POLLING_ON; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'P': | case 'P': | ||||
a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE; | nr_cmd = NETMAP_BDG_POLLING_OFF; | ||||
a.name = optarg; | |||||
break; | break; | ||||
case 'm': | case 'm': | ||||
a.mem_id = optarg; | nr_arg2 = atoi(optarg); | ||||
break; | |||||
case 'v': | |||||
verbose++; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (optind != argc) { | if (optind != argc) { | ||||
usage(1); | // fprintf(stderr, "optind %d argc %d\n", optind, argc); | ||||
usage(-1); | |||||
} | } | ||||
if (argc == 1) { | if (argc == 1) { | ||||
a.nr_reqtype = NETMAP_REQ_VALE_LIST; | nr_cmd = NETMAP_BDG_LIST; | ||||
a.name = NULL; | name = NULL; | ||||
} | |||||
if (!a.nr_reqtype) { | |||||
usage(1); | |||||
} | } | ||||
return bdg_ctl(&a); | return bdg_ctl(name, nr_cmd, nr_arg, nmr_config, nr_arg2) ? 1 : 0; | ||||
} | } | ||||
Context not available. |