Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/net_backends.c
Show First 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | struct net_backend { | ||||
* Called to serve a guest transmit request. The scatter-gather | * Called to serve a guest transmit request. The scatter-gather | ||||
* vector provided by the caller has 'iovcnt' elements and contains | * vector provided by the caller has 'iovcnt' elements and contains | ||||
* the packet to send. | * the packet to send. | ||||
*/ | */ | ||||
ssize_t (*send)(struct net_backend *be, const struct iovec *iov, | ssize_t (*send)(struct net_backend *be, const struct iovec *iov, | ||||
int iovcnt); | int iovcnt); | ||||
/* | /* | ||||
* Get the length of the next packet that can be received from | |||||
* the backend. If no packets are currently available, this | |||||
* function returns 0. | |||||
*/ | |||||
ssize_t (*peek_recvlen)(struct net_backend *be); | |||||
/* | |||||
* Called to receive a packet from the backend. When the function | * Called to receive a packet from the backend. When the function | ||||
* returns a positive value 'len', the scatter-gather vector | * returns a positive value 'len', the scatter-gather vector | ||||
* provided by the caller contains a packet with such length. | * provided by the caller contains a packet with such length. | ||||
* The function returns 0 if the backend doesn't have a new packet to | * The function returns 0 if the backend doesn't have a new packet to | ||||
* receive. | * receive. | ||||
*/ | */ | ||||
ssize_t (*recv)(struct net_backend *be, const struct iovec *iov, | ssize_t (*recv)(struct net_backend *be, const struct iovec *iov, | ||||
int iovcnt); | int iovcnt); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
#define WPRINTF(params) PRINTLN params | #define WPRINTF(params) PRINTLN params | ||||
/* | /* | ||||
* The tap backend | * The tap backend | ||||
*/ | */ | ||||
struct tap_priv { | struct tap_priv { | ||||
struct mevent *mevp; | struct mevent *mevp; | ||||
/* | |||||
* A bounce buffer that allows us to implement the peek_recvlen | |||||
* callback. In the future we may get the same information from | |||||
* the kevent data. | |||||
*/ | |||||
char bbuf[1 << 16]; | |||||
ssize_t bbuflen; | |||||
}; | }; | ||||
static void | static void | ||||
tap_cleanup(struct net_backend *be) | tap_cleanup(struct net_backend *be) | ||||
{ | { | ||||
struct tap_priv *priv = (struct tap_priv *)be->opaque; | struct tap_priv *priv = (struct tap_priv *)be->opaque; | ||||
if (priv->mevp) { | if (priv->mevp) { | ||||
Show All 40 Lines | #endif | ||||
} | } | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); | cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); | ||||
if (caph_rights_limit(be->fd, &rights) == -1) | if (caph_rights_limit(be->fd, &rights) == -1) | ||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | errx(EX_OSERR, "Unable to apply rights for sandbox"); | ||||
#endif | #endif | ||||
memset(priv->bbuf, 0, sizeof(priv->bbuf)); | |||||
priv->bbuflen = 0; | |||||
priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); | priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); | ||||
if (priv->mevp == NULL) { | if (priv->mevp == NULL) { | ||||
WPRINTF(("Could not register event")); | WPRINTF(("Could not register event")); | ||||
goto error; | goto error; | ||||
} | } | ||||
return (0); | return (0); | ||||
error: | error: | ||||
tap_cleanup(be); | tap_cleanup(be); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* | /* | ||||
* Called to send a buffer chain out to the tap device | * Called to send a buffer chain out to the tap device | ||||
*/ | */ | ||||
static ssize_t | static ssize_t | ||||
tap_send(struct net_backend *be, const struct iovec *iov, int iovcnt) | tap_send(struct net_backend *be, const struct iovec *iov, int iovcnt) | ||||
{ | { | ||||
return (writev(be->fd, iov, iovcnt)); | return (writev(be->fd, iov, iovcnt)); | ||||
} | } | ||||
static ssize_t | static ssize_t | ||||
tap_peek_recvlen(struct net_backend *be) | |||||
{ | |||||
struct tap_priv *priv = (struct tap_priv *)be->opaque; | |||||
ssize_t ret; | |||||
if (priv->bbuflen > 0) { | |||||
/* | |||||
* We already have a packet in the bounce buffer. | |||||
* Just return its length. | |||||
*/ | |||||
return priv->bbuflen; | |||||
} | |||||
/* | |||||
* Read the next packet (if any) into the bounce buffer, so | |||||
* that we get to know its length and we can return that | |||||
* to the caller. | |||||
*/ | |||||
ret = read(be->fd, priv->bbuf, sizeof(priv->bbuf)); | |||||
if (ret < 0 && errno == EWOULDBLOCK) { | |||||
return (0); | |||||
} | |||||
if (ret > 0) | |||||
priv->bbuflen = ret; | |||||
return (ret); | |||||
} | |||||
static ssize_t | |||||
tap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) | tap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) | ||||
{ | { | ||||
struct tap_priv *priv = (struct tap_priv *)be->opaque; | |||||
ssize_t ret; | ssize_t ret; | ||||
/* Should never be called without a valid tap fd */ | if (priv->bbuflen > 0) { | ||||
assert(be->fd != -1); | /* | ||||
* A packet is available in the bounce buffer, so | |||||
* we read it from there. | |||||
*/ | |||||
ssize_t left = priv->bbuflen; | |||||
int i; | |||||
ret = readv(be->fd, iov, iovcnt); | ret = 0; | ||||
for (i = 0; i < iovcnt && left > 0; i++) { | |||||
afedorov: I think we can replace this cycle with call buf_to_iov(priv->bbuf, priv->bbuflen, iov, iovcnt… | |||||
vmaffioneAuthorUnsubmitted Done Inline ActionsGood catch, thanks. vmaffione: Good catch, thanks. | |||||
ssize_t copylen = iov[i].iov_len; | |||||
if (copylen > left) | |||||
copylen = left; | |||||
memcpy(iov[i].iov_base, priv->bbuf + ret, copylen); | |||||
left -= copylen; | |||||
ret += copylen; | |||||
} | |||||
/* Mark the bounce buffer as empty. */ | |||||
priv->bbuflen = 0; | |||||
return (ret); | |||||
} | |||||
ret = readv(be->fd, iov, iovcnt); | |||||
if (ret < 0 && errno == EWOULDBLOCK) { | if (ret < 0 && errno == EWOULDBLOCK) { | ||||
return (0); | return (0); | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static void | static void | ||||
Show All 28 Lines | |||||
} | } | ||||
static struct net_backend tap_backend = { | static struct net_backend tap_backend = { | ||||
.prefix = "tap", | .prefix = "tap", | ||||
.priv_size = sizeof(struct tap_priv), | .priv_size = sizeof(struct tap_priv), | ||||
.init = tap_init, | .init = tap_init, | ||||
.cleanup = tap_cleanup, | .cleanup = tap_cleanup, | ||||
.send = tap_send, | .send = tap_send, | ||||
.peek_recvlen = tap_peek_recvlen, | |||||
.recv = tap_recv, | .recv = tap_recv, | ||||
.recv_enable = tap_recv_enable, | .recv_enable = tap_recv_enable, | ||||
.recv_disable = tap_recv_disable, | .recv_disable = tap_recv_disable, | ||||
.get_cap = tap_get_cap, | .get_cap = tap_get_cap, | ||||
.set_cap = tap_set_cap, | .set_cap = tap_set_cap, | ||||
}; | }; | ||||
/* A clone of the tap backend, with a different prefix. */ | /* A clone of the tap backend, with a different prefix. */ | ||||
static struct net_backend vmnet_backend = { | static struct net_backend vmnet_backend = { | ||||
.prefix = "vmnet", | .prefix = "vmnet", | ||||
.priv_size = sizeof(struct tap_priv), | .priv_size = sizeof(struct tap_priv), | ||||
.init = tap_init, | .init = tap_init, | ||||
.cleanup = tap_cleanup, | .cleanup = tap_cleanup, | ||||
.send = tap_send, | .send = tap_send, | ||||
.peek_recvlen = tap_peek_recvlen, | |||||
.recv = tap_recv, | .recv = tap_recv, | ||||
.recv_enable = tap_recv_enable, | .recv_enable = tap_recv_enable, | ||||
.recv_disable = tap_recv_disable, | .recv_disable = tap_recv_disable, | ||||
.get_cap = tap_get_cap, | .get_cap = tap_get_cap, | ||||
.set_cap = tap_set_cap, | .set_cap = tap_set_cap, | ||||
}; | }; | ||||
DATA_SET(net_backend_set, tap_backend); | DATA_SET(net_backend_set, tap_backend); | ||||
DATA_SET(net_backend_set, vmnet_backend); | DATA_SET(net_backend_set, vmnet_backend); | ||||
/* | /* | ||||
* The netmap backend | * The netmap backend | ||||
*/ | */ | ||||
/* The virtio-net features supported by netmap. */ | /* The virtio-net features supported by netmap. */ | ||||
#define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \ | #define NETMAP_FEATURES (VIRTIO_NET_F_CSUM | VIRTIO_NET_F_HOST_TSO4 | \ | ||||
VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \ | VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_UFO | \ | ||||
VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \ | VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | \ | ||||
VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO | \ | VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_UFO) | ||||
VIRTIO_NET_F_MRG_RXBUF) | |||||
struct netmap_priv { | struct netmap_priv { | ||||
char ifname[IFNAMSIZ]; | char ifname[IFNAMSIZ]; | ||||
struct nm_desc *nmd; | struct nm_desc *nmd; | ||||
uint16_t memid; | uint16_t memid; | ||||
struct netmap_ring *rx; | struct netmap_ring *rx; | ||||
struct netmap_ring *tx; | struct netmap_ring *tx; | ||||
struct mevent *mevp; | struct mevent *mevp; | ||||
▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | netmap_send(struct net_backend *be, const struct iovec *iov, | ||||
ring->head = ring->cur = head; | ring->head = ring->cur = head; | ||||
txsync: | txsync: | ||||
ioctl(be->fd, NIOCTXSYNC, NULL); | ioctl(be->fd, NIOCTXSYNC, NULL); | ||||
return (totlen); | return (totlen); | ||||
} | } | ||||
static ssize_t | static ssize_t | ||||
netmap_peek_recvlen(struct net_backend *be) | |||||
{ | |||||
struct netmap_priv *priv = (struct netmap_priv *)be->opaque; | |||||
struct netmap_ring *ring = priv->rx; | |||||
uint32_t head = ring->head; | |||||
ssize_t totlen = 0; | |||||
while (head != ring->tail) { | |||||
struct netmap_slot *slot = ring->slot + head; | |||||
totlen += slot->len; | |||||
if ((slot->flags & NS_MOREFRAG) == 0) | |||||
break; | |||||
head = nm_ring_next(ring, head); | |||||
} | |||||
return (totlen); | |||||
} | |||||
static ssize_t | |||||
netmap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) | netmap_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) | ||||
{ | { | ||||
struct netmap_priv *priv = (struct netmap_priv *)be->opaque; | struct netmap_priv *priv = (struct netmap_priv *)be->opaque; | ||||
struct netmap_slot *slot = NULL; | struct netmap_slot *slot = NULL; | ||||
struct netmap_ring *ring; | struct netmap_ring *ring; | ||||
void *iov_frag_buf; | void *iov_frag_buf; | ||||
int iov_frag_size; | int iov_frag_size; | ||||
ssize_t totlen = 0; | ssize_t totlen = 0; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static struct net_backend netmap_backend = { | static struct net_backend netmap_backend = { | ||||
.prefix = "netmap", | .prefix = "netmap", | ||||
.priv_size = sizeof(struct netmap_priv), | .priv_size = sizeof(struct netmap_priv), | ||||
.init = netmap_init, | .init = netmap_init, | ||||
.cleanup = netmap_cleanup, | .cleanup = netmap_cleanup, | ||||
.send = netmap_send, | .send = netmap_send, | ||||
.peek_recvlen = netmap_peek_recvlen, | |||||
.recv = netmap_recv, | .recv = netmap_recv, | ||||
.recv_enable = netmap_recv_enable, | .recv_enable = netmap_recv_enable, | ||||
.recv_disable = netmap_recv_disable, | .recv_disable = netmap_recv_disable, | ||||
.get_cap = netmap_get_cap, | .get_cap = netmap_get_cap, | ||||
.set_cap = netmap_set_cap, | .set_cap = netmap_set_cap, | ||||
}; | }; | ||||
/* A clone of the netmap backend, with a different prefix. */ | /* A clone of the netmap backend, with a different prefix. */ | ||||
static struct net_backend vale_backend = { | static struct net_backend vale_backend = { | ||||
.prefix = "vale", | .prefix = "vale", | ||||
.priv_size = sizeof(struct netmap_priv), | .priv_size = sizeof(struct netmap_priv), | ||||
.init = netmap_init, | .init = netmap_init, | ||||
.cleanup = netmap_cleanup, | .cleanup = netmap_cleanup, | ||||
.send = netmap_send, | .send = netmap_send, | ||||
.peek_recvlen = netmap_peek_recvlen, | |||||
.recv = netmap_recv, | .recv = netmap_recv, | ||||
.recv_enable = netmap_recv_enable, | .recv_enable = netmap_recv_enable, | ||||
.recv_disable = netmap_recv_disable, | .recv_disable = netmap_recv_disable, | ||||
.get_cap = netmap_get_cap, | .get_cap = netmap_get_cap, | ||||
.set_cap = netmap_set_cap, | .set_cap = netmap_set_cap, | ||||
}; | }; | ||||
DATA_SET(net_backend_set, netmap_backend); | DATA_SET(net_backend_set, netmap_backend); | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | netbe_set_cap(struct net_backend *be, uint64_t features, | ||||
return (ret); | return (ret); | ||||
} | } | ||||
ssize_t | ssize_t | ||||
netbe_send(struct net_backend *be, const struct iovec *iov, int iovcnt) | netbe_send(struct net_backend *be, const struct iovec *iov, int iovcnt) | ||||
{ | { | ||||
return (be->send(be, iov, iovcnt)); | return (be->send(be, iov, iovcnt)); | ||||
} | |||||
ssize_t | |||||
netbe_peek_recvlen(struct net_backend *be) | |||||
{ | |||||
return (be->peek_recvlen(be)); | |||||
} | } | ||||
/* | /* | ||||
* Try to read a packet from the backend, without blocking. | * Try to read a packet from the backend, without blocking. | ||||
* If no packets are available, return 0. In case of success, return | * If no packets are available, return 0. In case of success, return | ||||
* the length of the packet just read. Return -1 in case of errors. | * the length of the packet just read. Return -1 in case of errors. | ||||
*/ | */ | ||||
ssize_t | ssize_t | ||||
▲ Show 20 Lines • Show All 48 Lines • Show Last 20 Lines |
I think we can replace this cycle with call buf_to_iov(priv->bbuf, priv->bbuflen, iov, iovcnt, 0)
https://svnweb.freebsd.org/base/head/usr.sbin/bhyve/iov.c?view=markup#l122