Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_virtio_net.c
Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
#include <pthread_np.h> | #include <pthread_np.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "mevent.h" | #include "mevent.h" | ||||
#include "virtio.h" | #include "virtio.h" | ||||
#include "net_utils.h" | #include "net_utils.h" | ||||
#include "net_backends.h" | #include "net_backends.h" | ||||
#include "iov.h" | |||||
#define VTNET_RINGSZ 1024 | #define VTNET_RINGSZ 1024 | ||||
#define VTNET_MAXSEGS 256 | #define VTNET_MAXSEGS 256 | ||||
#define VTNET_MAX_PKT_LEN (65536 + 64) | |||||
#define VTNET_S_HOSTCAPS \ | #define VTNET_S_HOSTCAPS \ | ||||
( VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | \ | ( VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | \ | ||||
VIRTIO_F_NOTIFY_ON_EMPTY | VIRTIO_RING_F_INDIRECT_DESC) | VIRTIO_F_NOTIFY_ON_EMPTY | VIRTIO_RING_F_INDIRECT_DESC) | ||||
/* | /* | ||||
* PCI config-space "registers" | * PCI config-space "registers" | ||||
*/ | */ | ||||
struct virtio_net_config { | struct virtio_net_config { | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | pci_vtnet_reset(void *vsc) | ||||
*/ | */ | ||||
vi_reset_dev(&sc->vsc_vs); | vi_reset_dev(&sc->vsc_vs); | ||||
sc->resetting = 0; | sc->resetting = 0; | ||||
pthread_mutex_unlock(&sc->tx_mtx); | pthread_mutex_unlock(&sc->tx_mtx); | ||||
pthread_mutex_unlock(&sc->rx_mtx); | pthread_mutex_unlock(&sc->rx_mtx); | ||||
} | } | ||||
struct virtio_mrg_rxbuf_info { | |||||
uint16_t idx; | |||||
uint16_t pad; | |||||
uint32_t len; | |||||
}; | |||||
static void | static void | ||||
pci_vtnet_rx(struct pci_vtnet_softc *sc) | pci_vtnet_rx(struct pci_vtnet_softc *sc) | ||||
{ | { | ||||
struct virtio_mrg_rxbuf_info info[VTNET_MAXSEGS]; | |||||
struct iovec iov[VTNET_MAXSEGS + 1]; | struct iovec iov[VTNET_MAXSEGS + 1]; | ||||
struct vqueue_info *vq; | struct vqueue_info *vq; | ||||
int len, n; | uint32_t cur_iov_bytes; | ||||
uint16_t idx; | struct iovec *cur_iov; | ||||
uint16_t cur_iov_len; | |||||
uint32_t ulen; | |||||
int n_chains; | |||||
int len; | |||||
vq = &sc->vsc_queues[VTNET_RXQ]; | vq = &sc->vsc_queues[VTNET_RXQ]; | ||||
for (;;) { | for (;;) { | ||||
/* | /* | ||||
* Check for available rx buffers. | * Get a descriptor chain to store the next ingress | ||||
* packet. In case of mergeable rx buffers, get as | |||||
* many chains as necessary in order to make room | |||||
* for a maximum sized LRO packet. | |||||
*/ | */ | ||||
if (!vq_has_descs(vq)) { | cur_iov_bytes = 0; | ||||
/* No rx buffers. Enable RX kicks and double check. */ | cur_iov_len = 0; | ||||
cur_iov = iov; | |||||
n_chains = 0; | |||||
do { | |||||
int n = vq_getchain(vq, &info[n_chains].idx, cur_iov, | |||||
VTNET_MAXSEGS - cur_iov_len, NULL); | |||||
if (n == 0) { | |||||
/* | |||||
* No rx buffers. Enable RX kicks and double | |||||
* check. | |||||
*/ | |||||
vq_kick_enable(vq); | vq_kick_enable(vq); | ||||
if (!vq_has_descs(vq)) { | if (!vq_has_descs(vq)) { | ||||
/* | /* | ||||
* Still no buffers. Interrupt if needed | * Still no buffers. Return the unused | ||||
* chains (if any), interrupt if needed | |||||
* (including for NOTIFY_ON_EMPTY), and | * (including for NOTIFY_ON_EMPTY), and | ||||
* disable the backend until the next kick. | * disable the backend until the next | ||||
* kick. | |||||
*/ | */ | ||||
vq_retchains(vq, n_chains); | |||||
vq_endchains(vq, /*used_all_avail=*/1); | vq_endchains(vq, /*used_all_avail=*/1); | ||||
netbe_rx_disable(sc->vsc_be); | netbe_rx_disable(sc->vsc_be); | ||||
return; | return; | ||||
} | } | ||||
/* More rx buffers found, so keep going. */ | /* More rx buffers found, so keep going. */ | ||||
vq_kick_disable(vq); | vq_kick_disable(vq); | ||||
continue; | |||||
} | } | ||||
assert(n >= 1 && cur_iov_len + n <= VTNET_MAXSEGS); | |||||
cur_iov_len += n; | |||||
if (!sc->rx_merge) { | |||||
n_chains = 1; | |||||
break; | |||||
} | |||||
info[n_chains].len = (uint32_t)count_iov(cur_iov, n); | |||||
cur_iov_bytes += info[n_chains].len; | |||||
cur_iov += n; | |||||
n_chains++; | |||||
} while (cur_iov_bytes < VTNET_MAX_PKT_LEN && | |||||
cur_iov_len < VTNET_MAXSEGS); | |||||
/* | len = netbe_recv(sc->vsc_be, iov, cur_iov_len); | ||||
* Get descriptor chain. | |||||
*/ | |||||
n = vq_getchain(vq, &idx, iov, VTNET_MAXSEGS, NULL); | |||||
assert(n >= 1 && n <= VTNET_MAXSEGS); | |||||
len = netbe_recv(sc->vsc_be, iov, n); | |||||
if (len <= 0) { | if (len <= 0) { | ||||
/* | /* | ||||
* No more packets (len == 0), or backend errored | * No more packets (len == 0), or backend errored | ||||
* (err < 0). Return unused available buffers | * (err < 0). Return unused available buffers | ||||
* and stop. | * and stop. | ||||
*/ | */ | ||||
vq_retchain(vq); | vq_retchains(vq, n_chains); | ||||
/* Interrupt if needed/appropriate and stop. */ | /* Interrupt if needed/appropriate and stop. */ | ||||
afedorov: I catched this assert(n==0) with my tests - two ubuntu 16.04 VM + vale switch. It seems, there… | |||||
Done Inline ActionsThank you, this helps. I forgot to check that chains after the first one are indeed available. vmaffione: Thank you, this helps. I forgot to check that chains after the first one are indeed available. | |||||
vq_endchains(vq, /*used_all_avail=*/0); | vq_endchains(vq, /*used_all_avail=*/0); | ||||
return; | return; | ||||
} | } | ||||
/* Publish the info to the guest */ | ulen = (uint32_t)len; /* avoid too many casts below */ | ||||
vq_relchain(vq, idx, (uint32_t)len); | |||||
/* Publish the used buffers to the guest. */ | |||||
if (!sc->rx_merge) { | |||||
vq_relchain(vq, info[0].idx, ulen); | |||||
} else { | |||||
struct virtio_net_rxhdr *hdr = iov[0].iov_base; | |||||
uint32_t iolen; | |||||
int i = 0; | |||||
assert(iov[0].iov_len >= sizeof(*hdr)); | |||||
do { | |||||
iolen = info[i].len; | |||||
if (iolen > ulen) { | |||||
iolen = ulen; | |||||
} | |||||
vq_relchain_prepare(vq, info[i].idx, iolen); | |||||
ulen -= iolen; | |||||
i++; | |||||
assert(i <= n_chains); | |||||
} while (ulen > 0); | |||||
hdr->vrh_bufs = i; | |||||
vq_relchain_publish(vq); | |||||
vq_retchains(vq, n_chains - i); | |||||
} | |||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Called when there is read activity on the backend file descriptor. | * Called when there is read activity on the backend file descriptor. | ||||
* Each buffer posted by the guest is assumed to be able to contain | * Each buffer posted by the guest is assumed to be able to contain | ||||
* an entire ethernet frame + rx header. | * an entire ethernet frame + rx header. | ||||
▲ Show 20 Lines • Show All 295 Lines • Show Last 20 Lines |
I catched this assert(n==0) with my tests - two ubuntu 16.04 VM + vale switch. It seems, there are nothing to prevent vq_getchain() return 0.