Path: blob/main/usr.sbin/bhyve/net_backend_netmap.c
103517 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 Vincenzo Maffione <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR17* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS18* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,19* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT20* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR21* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,22* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE23* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,24* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.25*/2627#include <net/if.h>28#include <net/netmap.h>29#include <net/netmap_virt.h>30#define NETMAP_WITH_LIBS31#include <net/netmap_user.h>3233#include <assert.h>3435#include "debug.h"36#include "iov.h"37#include "mevent.h"38#include "net_backends.h"39#include "net_backends_priv.h"4041/* The virtio-net features supported by netmap. */42#define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \43VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \44VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \45VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO)4647struct netmap_priv {48char ifname[IFNAMSIZ];49struct nm_desc *nmd;50uint16_t memid;51struct netmap_ring *rx;52struct netmap_ring *tx;53struct mevent *mevp;54net_be_rxeof_t cb;55void *cb_param;56};5758static void59nmreq_init(struct nmreq *req, char *ifname)60{6162memset(req, 0, sizeof(*req));63strlcpy(req->nr_name, ifname, sizeof(req->nr_name));64req->nr_version = NETMAP_API;65}6667static int68netmap_set_vnet_hdr_len(struct net_backend *be, int vnet_hdr_len)69{70int err;71struct nmreq req;72struct netmap_priv *priv = NET_BE_PRIV(be);7374nmreq_init(&req, priv->ifname);75req.nr_cmd = NETMAP_BDG_VNET_HDR;76req.nr_arg1 = vnet_hdr_len;77err = ioctl(be->fd, NIOCREGIF, &req);78if (err) {79EPRINTLN("Unable to set vnet header length %d", vnet_hdr_len);80return (err);81}8283be->be_vnet_hdr_len = vnet_hdr_len;8485return (0);86}8788static int89netmap_has_vnet_hdr_len(struct net_backend *be, unsigned vnet_hdr_len)90{91unsigned prev_hdr_len = be->be_vnet_hdr_len;92int ret;9394if (vnet_hdr_len == prev_hdr_len) {95return (1);96}9798ret = netmap_set_vnet_hdr_len(be, vnet_hdr_len);99if (ret) {100return (0);101}102103netmap_set_vnet_hdr_len(be, prev_hdr_len);104105return (1);106}107108static uint64_t109netmap_get_cap(struct net_backend *be)110{111112return (netmap_has_vnet_hdr_len(be, VNET_HDR_LEN) ?113NETMAP_FEATURES : 0);114}115116static int117netmap_set_cap(struct net_backend *be, uint64_t features __unused,118unsigned vnet_hdr_len)119{120121return (netmap_set_vnet_hdr_len(be, vnet_hdr_len));122}123124static int125netmap_init(struct net_backend *be, const char *devname,126nvlist_t *nvl __unused, net_be_rxeof_t cb, void *param)127{128struct netmap_priv *priv = NET_BE_PRIV(be);129130strlcpy(priv->ifname, devname, sizeof(priv->ifname));131priv->ifname[sizeof(priv->ifname) - 1] = '\0';132133priv->nmd = nm_open(priv->ifname, NULL, NETMAP_NO_TX_POLL, NULL);134if (priv->nmd == NULL) {135EPRINTLN("Unable to nm_open(): interface '%s', errno (%s)",136devname, strerror(errno));137return (-1);138}139140priv->memid = priv->nmd->req.nr_arg2;141priv->tx = NETMAP_TXRING(priv->nmd->nifp, 0);142priv->rx = NETMAP_RXRING(priv->nmd->nifp, 0);143priv->cb = cb;144priv->cb_param = param;145be->fd = priv->nmd->fd;146147priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);148if (priv->mevp == NULL) {149EPRINTLN("Could not register event");150return (-1);151}152153return (0);154}155156static void157netmap_cleanup(struct net_backend *be)158{159struct netmap_priv *priv = NET_BE_PRIV(be);160161if (priv->mevp) {162mevent_delete(priv->mevp);163}164if (priv->nmd) {165nm_close(priv->nmd);166}167be->fd = -1;168}169170static ssize_t171netmap_send(struct net_backend *be, const struct iovec *iov,172int iovcnt)173{174struct netmap_priv *priv = NET_BE_PRIV(be);175struct netmap_ring *ring;176ssize_t totlen = 0;177int nm_buf_size;178int nm_buf_len;179uint32_t head;180uint8_t *nm_buf;181int j;182183ring = priv->tx;184head = ring->head;185if (head == ring->tail) {186EPRINTLN("No space, drop %zu bytes", count_iov(iov, iovcnt));187goto txsync;188}189nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);190nm_buf_size = ring->nr_buf_size;191nm_buf_len = 0;192193for (j = 0; j < iovcnt; j++) {194uint8_t *iov_frag_buf = iov[j].iov_base;195int iov_frag_size = iov[j].iov_len;196197totlen += iov_frag_size;198199/*200* Split each iovec fragment over more netmap slots, if201* necessary.202*/203for (;;) {204int copylen;205206copylen = iov_frag_size < nm_buf_size ? iov_frag_size : nm_buf_size;207memcpy(nm_buf, iov_frag_buf, copylen);208209iov_frag_buf += copylen;210iov_frag_size -= copylen;211nm_buf += copylen;212nm_buf_size -= copylen;213nm_buf_len += copylen;214215if (iov_frag_size == 0) {216break;217}218219ring->slot[head].len = nm_buf_len;220ring->slot[head].flags = NS_MOREFRAG;221head = nm_ring_next(ring, head);222if (head == ring->tail) {223/*224* We ran out of netmap slots while225* splitting the iovec fragments.226*/227EPRINTLN("No space, drop %zu bytes",228count_iov(iov, iovcnt));229goto txsync;230}231nm_buf = NETMAP_BUF(ring, ring->slot[head].buf_idx);232nm_buf_size = ring->nr_buf_size;233nm_buf_len = 0;234}235}236237/* Complete the last slot, which must not have NS_MOREFRAG set. */238ring->slot[head].len = nm_buf_len;239ring->slot[head].flags = 0;240head = nm_ring_next(ring, head);241242/* Now update ring->head and ring->cur. */243ring->head = ring->cur = head;244txsync:245ioctl(be->fd, NIOCTXSYNC, NULL);246247return (totlen);248}249250static ssize_t251netmap_peek_recvlen(struct net_backend *be)252{253struct netmap_priv *priv = NET_BE_PRIV(be);254struct netmap_ring *ring = priv->rx;255uint32_t head = ring->head;256ssize_t totlen = 0;257258while (head != ring->tail) {259struct netmap_slot *slot = ring->slot + head;260261totlen += slot->len;262if ((slot->flags & NS_MOREFRAG) == 0)263break;264head = nm_ring_next(ring, head);265}266267return (totlen);268}269270static ssize_t271netmap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)272{273struct netmap_priv *priv = NET_BE_PRIV(be);274struct netmap_slot *slot = NULL;275struct netmap_ring *ring;276uint8_t *iov_frag_buf;277int iov_frag_size;278ssize_t totlen = 0;279uint32_t head;280281assert(iovcnt);282283ring = priv->rx;284head = ring->head;285iov_frag_buf = iov->iov_base;286iov_frag_size = iov->iov_len;287288do {289uint8_t *nm_buf;290int nm_buf_len;291292if (head == ring->tail) {293return (0);294}295296slot = ring->slot + head;297nm_buf = NETMAP_BUF(ring, slot->buf_idx);298nm_buf_len = slot->len;299300for (;;) {301int copylen = nm_buf_len < iov_frag_size ?302nm_buf_len : iov_frag_size;303304memcpy(iov_frag_buf, nm_buf, copylen);305nm_buf += copylen;306nm_buf_len -= copylen;307iov_frag_buf += copylen;308iov_frag_size -= copylen;309totlen += copylen;310311if (nm_buf_len == 0) {312break;313}314315iov++;316iovcnt--;317if (iovcnt == 0) {318/* No space to receive. */319EPRINTLN("Short iov, drop %zd bytes",320totlen);321return (-ENOSPC);322}323iov_frag_buf = iov->iov_base;324iov_frag_size = iov->iov_len;325}326327head = nm_ring_next(ring, head);328329} while (slot->flags & NS_MOREFRAG);330331/* Release slots to netmap. */332ring->head = ring->cur = head;333334return (totlen);335}336337static void338netmap_recv_enable(struct net_backend *be)339{340struct netmap_priv *priv = NET_BE_PRIV(be);341342mevent_enable(priv->mevp);343}344345static void346netmap_recv_disable(struct net_backend *be)347{348struct netmap_priv *priv = NET_BE_PRIV(be);349350mevent_disable(priv->mevp);351}352353static struct net_backend netmap_backend = {354.prefix = "netmap",355.priv_size = sizeof(struct netmap_priv),356.init = netmap_init,357.cleanup = netmap_cleanup,358.send = netmap_send,359.peek_recvlen = netmap_peek_recvlen,360.recv = netmap_recv,361.recv_enable = netmap_recv_enable,362.recv_disable = netmap_recv_disable,363.get_cap = netmap_get_cap,364.set_cap = netmap_set_cap,365};366367/* A clone of the netmap backend, with a different prefix. */368static struct net_backend vale_backend = {369.prefix = "vale",370.priv_size = sizeof(struct netmap_priv),371.init = netmap_init,372.cleanup = netmap_cleanup,373.send = netmap_send,374.peek_recvlen = netmap_peek_recvlen,375.recv = netmap_recv,376.recv_enable = netmap_recv_enable,377.recv_disable = netmap_recv_disable,378.get_cap = netmap_get_cap,379.set_cap = netmap_set_cap,380};381382DATA_SET(net_backend_set, netmap_backend);383DATA_SET(net_backend_set, vale_backend);384385386