diff options
Diffstat (limited to 'usr.sbin/valectl')
| -rw-r--r-- | usr.sbin/valectl/Makefile | 9 | ||||
| -rw-r--r-- | usr.sbin/valectl/Makefile.depend | 16 | ||||
| -rw-r--r-- | usr.sbin/valectl/valectl.8 | 161 | ||||
| -rw-r--r-- | usr.sbin/valectl/valectl.c | 444 |
4 files changed, 630 insertions, 0 deletions
diff --git a/usr.sbin/valectl/Makefile b/usr.sbin/valectl/Makefile new file mode 100644 index 000000000000..c9f1288c76db --- /dev/null +++ b/usr.sbin/valectl/Makefile @@ -0,0 +1,9 @@ +PACKAGE= netmap +PROG= valectl +MAN= valectl.8 + +WARNS?= 3 + +LIBADD= netmap + +.include <bsd.prog.mk> diff --git a/usr.sbin/valectl/Makefile.depend b/usr.sbin/valectl/Makefile.depend new file mode 100644 index 000000000000..d28ce8e5adde --- /dev/null +++ b/usr.sbin/valectl/Makefile.depend @@ -0,0 +1,16 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libnetmap \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/valectl/valectl.8 b/usr.sbin/valectl/valectl.8 new file mode 100644 index 000000000000..805d785f52cf --- /dev/null +++ b/usr.sbin/valectl/valectl.8 @@ -0,0 +1,161 @@ +.\" Copyright (c) 2016 Michio Honda. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd August 30, 2024 +.Dt VALECTL 8 +.Os +.Sh NAME +.Nm valectl +.Nd manage VALE network switches provided by netmap +.Sh SYNOPSIS +.Bk -words +.Bl -tag -width "valectl" +.It Nm +.Op Fl g Ar valeSSS:PPP +.Op Fl a Ar valeSSS:interface +.Op Fl h Ar valeSSS:interface +.Op Fl d Ar valeSSS:interface +.Op Fl n Ar interface +.Op Fl r Ar interface +.Op Fl l Ar valeSSS:PPP +.Op Fl l +.Op Fl p Ar valeSSS:PPP +.Op Fl P Ar valeSSS:PPP +.Op Fl C Ar spec +.Op Fl m Ar memid +.El +.Ek +.Sh DESCRIPTION +.Nm +manages and inspects +.Xr vale 4 +network switches, for instance attaching and detaching interfaces, creating +and deleting persistent VALE ports, or listing the existing switches +and their ports. +In the following, +.Ar valeSSS +is the name of a VALE switch, while +.Ar valeSSS:PPP +is the name of a VALE port of +.Ar valeSSS . +.Pp +When issued without options it lists all the existing switch ports together +with their internal bridge number and port number. +.Bl -tag -width Ds +.It Fl g Ar valeSSS:PPP +Print the number of receive rings of +.Ar valeSSS:PPP . +.It Fl a Ar valeSSS:interface +Attach +.Ar interface +(which must be an existing network interface) to +.Ar valeSSS +and detach it from the host stack. +.It Fl h Ar valeSSS:interface +Attach +.Ar interface +(which must be an existing network interface) to +.Ar valeSSS +while keeping it attached to the host stack. +More precisely, packets coming from +the host stack and directed to the interface will go through the switch, where +they can still reach the interface if the switch rules allow it. +Conversely, packets coming from the interface will go through the switch and, +if appropriate, will reach the host stack. +.It Fl d Ar valeSSS:interface +Detach +.Ar interface +from +.Ar valeSSS . +.It Fl n Ar interface +Create a new persistent VALE port with name +.Ar interface . +The name must be different from any other network interface +already present in the system. +.It Fl r Ar interface +Destroy the persistent VALE port with name +.Ar interface . +.It Fl l Ar valeSSS:PPP +Show the internal bridge number and port number of the given switch port. +.It Fl p Ar valeSSS:PPP +Enable polling mode for +.Ar valeSSS:PPP . +In polling mode, a dedicated kernel thread is spawned to handle packets +received from +.Ar valeSSS:PPP +and push them into the switch. +The kernel thread busy waits on the switch port rather than relying on +interrupts or notifications. +Polling mode can only be used on physical NICs attached to a VALE switch. +.It Fl P Ar valeSSS:PPP +Disable polling mode for +.Ar valeSSS:PPP . +.It Fl C Ar x | Ar x,y | Ar x,y,z | Ar x,y,z,w +When used in conjunction with +.Fl n +it supplies the number of tx and rx rings and slots. +The full format with four numbers gives, in order, number of tx slots, number +of rx slots, number of tx rings and number of rx rings. +The form with three numbers uses +.Ar z +for both the number of tx and the number of rx rings. +The forms with less than two numbers use the default values for the number +of rings. +The form with two numbers supplies the numbers of tx and rx slots. +The form with only one number uses +.Ar x +for both the number of tx and the number of rx slots. +.Pp +When used in conjunction with +.Fl p +only the first three forms are used. +The first number may be either 0 or 1. +If 0, then all interface rings will be polled by a single thread, running +on the core id given by the second number (the third number, if present, +must be 1). +If the first number is 1, then the ring identified by the second number will +be polled by the core with the same id. +If a third number is given, then this is repeated for as many consecutive +rings and cores. +.It Fl m Ar memid +Used in conjunction with +.Fl n +supplies the netmap memory region identifier to use together with the newly +created persistent VALE port. +These ports use a private memory region by default. +Using this option you can let them share memory with other ports. +Pass 1 as +.Ar memid +to use the global memory region already shared by all +hardware netmap ports. +.El +.Sh SEE ALSO +.Xr netmap 4 , +.Xr vale 4 +.Sh AUTHORS +.An -nosplit +.Nm +has been written by +.An Michio Honda +at NetApp. diff --git a/usr.sbin/valectl/valectl.c b/usr.sbin/valectl/valectl.c new file mode 100644 index 000000000000..0e4dffd34290 --- /dev/null +++ b/usr.sbin/valectl/valectl.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2013-2014 Michio Honda. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +#define LIBNETMAP_NOTHREADSAFE +#include <libnetmap.h> + +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> /* PRI* macros */ +#include <string.h> /* strcmp */ +#include <fcntl.h> /* open */ +#include <unistd.h> /* close */ +#include <sys/ioctl.h> /* ioctl */ +#include <sys/param.h> +#include <sys/socket.h> /* apple needs sockaddr */ +#include <net/if.h> /* ifreq */ +#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 +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; + + *nr_tx_rings = *nr_rx_rings = 0; + *nr_tx_slots = *nr_rx_slots = 0; + if (conf == NULL || ! *conf) + return; + w = strdup(conf); + for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { + v = atoi(tok); + switch (i) { + case 0: + *nr_tx_slots = *nr_rx_slots = v; + break; + case 1: + *nr_rx_slots = v; + break; + case 2: + *nr_tx_rings = *nr_rx_rings = v; + break; + case 3: + *nr_rx_rings = v; + break; + default: + fprintf(stderr, "ignored config: %s", tok); + break; + } + } + 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 +parse_poll_config(const char *conf, struct nmreq_vale_polling *v) +{ + char *w, *tok; + 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 *)(uintptr_t)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_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: + 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); + return error; +} + +static void +usage(int errcode) +{ + fprintf(stderr, + "Usage:\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 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-v increase verbosity\n" + "with no arguments: list all existing vale ports\n"); + exit(errcode); +} + +int +main(int argc, char *argv[]) +{ + 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:v")) != -1) { + switch (ch) { + default: + fprintf(stderr, "bad option %c %s", ch, optarg); + usage(1); + break; + case 'd': + a.nr_reqtype = NETMAP_REQ_VALE_DETACH; + a.name = optarg; + break; + case 'a': + a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; + a.nr_mode = NR_REG_ALL_NIC; + a.name = optarg; + break; + case 'h': + a.nr_reqtype = NETMAP_REQ_VALE_ATTACH; + a.nr_mode = NR_REG_NIC_SW; + a.name = optarg; + break; + case 'n': + a.nr_reqtype = NETMAP_REQ_VALE_NEWIF; + a.name = optarg; + break; + case 'r': + a.nr_reqtype = NETMAP_REQ_VALE_DELIF; + a.name = optarg; + break; + case 'g': + a.nr_reqtype = NETMAP_REQ_PORT_INFO_GET; + a.name = optarg; + break; + case 'l': + 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': + a.config = optarg; + break; + case 'p': + a.nr_reqtype = NETMAP_REQ_VALE_POLLING_ENABLE; + a.name = optarg; + break; + case 'P': + a.nr_reqtype = NETMAP_REQ_VALE_POLLING_DISABLE; + a.name = optarg; + break; + case 'm': + a.mem_id = optarg; + break; + case 'v': + verbose++; + break; + } + } + if (optind != argc) { + usage(1); + } + if (argc == 1) { + a.nr_reqtype = NETMAP_REQ_VALE_LIST; + a.name = NULL; + } + if (!a.nr_reqtype) { + usage(1); + } + return bdg_ctl(&a); +} |
