Module Name: src
Committed By: yamaguchi
Date: Mon Jan 14 15:00:27 UTC 2019
Modified Files:
src/sys/dev/pci: if_vioif.c
Log Message:
Add multiqueue support, vioif(4)
To generate a diff of this commit:
cvs rdiff -u -r1.45 -r1.46 src/sys/dev/pci/if_vioif.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/if_vioif.c
diff -u src/sys/dev/pci/if_vioif.c:1.45 src/sys/dev/pci/if_vioif.c:1.46
--- src/sys/dev/pci/if_vioif.c:1.45 Mon Jan 14 14:57:25 2019
+++ src/sys/dev/pci/if_vioif.c Mon Jan 14 15:00:27 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: if_vioif.c,v 1.45 2019/01/14 14:57:25 yamaguchi Exp $ */
+/* $NetBSD: if_vioif.c,v 1.46 2019/01/14 15:00:27 yamaguchi Exp $ */
/*
* Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.45 2019/01/14 14:57:25 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.46 2019/01/14 15:00:27 yamaguchi Exp $");
#ifdef _KERNEL_OPT
#include "opt_net_mpsafe.h"
@@ -45,6 +45,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v
#include <sys/sockio.h>
#include <sys/cpu.h>
#include <sys/module.h>
+#include <sys/pcq.h>
#include <dev/pci/virtioreg.h>
#include <dev/pci/virtiovar.h>
@@ -59,6 +60,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v
#ifdef NET_MPSAFE
#define VIOIF_MPSAFE 1
+#define VIOIF_MULTIQ 1
#endif
#ifdef SOFTINT_INTR
@@ -71,28 +73,35 @@ __KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v
/* Configuration registers */
#define VIRTIO_NET_CONFIG_MAC 0 /* 8bit x 6byte */
#define VIRTIO_NET_CONFIG_STATUS 6 /* 16bit */
+#define VIRTIO_NET_CONFIG_MAX_VQ_PAIRS 8 /* 16bit */
/* Feature bits */
-#define VIRTIO_NET_F_CSUM (1<<0)
-#define VIRTIO_NET_F_GUEST_CSUM (1<<1)
-#define VIRTIO_NET_F_MAC (1<<5)
-#define VIRTIO_NET_F_GSO (1<<6)
-#define VIRTIO_NET_F_GUEST_TSO4 (1<<7)
-#define VIRTIO_NET_F_GUEST_TSO6 (1<<8)
-#define VIRTIO_NET_F_GUEST_ECN (1<<9)
-#define VIRTIO_NET_F_GUEST_UFO (1<<10)
-#define VIRTIO_NET_F_HOST_TSO4 (1<<11)
-#define VIRTIO_NET_F_HOST_TSO6 (1<<12)
-#define VIRTIO_NET_F_HOST_ECN (1<<13)
-#define VIRTIO_NET_F_HOST_UFO (1<<14)
-#define VIRTIO_NET_F_MRG_RXBUF (1<<15)
-#define VIRTIO_NET_F_STATUS (1<<16)
-#define VIRTIO_NET_F_CTRL_VQ (1<<17)
-#define VIRTIO_NET_F_CTRL_RX (1<<18)
-#define VIRTIO_NET_F_CTRL_VLAN (1<<19)
+#define VIRTIO_NET_F_CSUM __BIT(0)
+#define VIRTIO_NET_F_GUEST_CSUM __BIT(1)
+#define VIRTIO_NET_F_MAC __BIT(5)
+#define VIRTIO_NET_F_GSO __BIT(6)
+#define VIRTIO_NET_F_GUEST_TSO4 __BIT(7)
+#define VIRTIO_NET_F_GUEST_TSO6 __BIT(8)
+#define VIRTIO_NET_F_GUEST_ECN __BIT(9)
+#define VIRTIO_NET_F_GUEST_UFO __BIT(10)
+#define VIRTIO_NET_F_HOST_TSO4 __BIT(11)
+#define VIRTIO_NET_F_HOST_TSO6 __BIT(12)
+#define VIRTIO_NET_F_HOST_ECN __BIT(13)
+#define VIRTIO_NET_F_HOST_UFO __BIT(14)
+#define VIRTIO_NET_F_MRG_RXBUF __BIT(15)
+#define VIRTIO_NET_F_STATUS __BIT(16)
+#define VIRTIO_NET_F_CTRL_VQ __BIT(17)
+#define VIRTIO_NET_F_CTRL_RX __BIT(18)
+#define VIRTIO_NET_F_CTRL_VLAN __BIT(19)
+#define VIRTIO_NET_F_CTRL_RX_EXTRA __BIT(20)
+#define VIRTIO_NET_F_GUEST_ANNOUNCE __BIT(21)
+#define VIRTIO_NET_F_MQ __BIT(22)
#define VIRTIO_NET_FLAG_BITS \
VIRTIO_COMMON_FLAG_BITS \
+ "\x17""MQ" \
+ "\x16""GUEST_ANNOUNCE" \
+ "\x15""CTRL_RX_EXTRA" \
"\x14""CTRL_VLAN" \
"\x13""CTRL_RX" \
"\x12""CTRL_VQ" \
@@ -152,6 +161,11 @@ struct virtio_net_ctrl_cmd {
# define VIRTIO_NET_CTRL_VLAN_ADD 0
# define VIRTIO_NET_CTRL_VLAN_DEL 1
+#define VIRTIO_NET_CTRL_MQ 4
+# define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
+# define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
+# define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
+
struct virtio_net_ctrl_status {
uint8_t ack;
} __packed;
@@ -171,6 +185,10 @@ struct virtio_net_ctrl_vlan {
uint16_t id;
} __packed;
+struct virtio_net_ctrl_mq {
+ uint16_t virtqueue_pairs;
+} __packed;
+
struct vioif_ctrl_cmdspec {
bus_dmamap_t dmamap;
void *buf;
@@ -197,12 +215,15 @@ struct vioif_txqueue {
struct virtqueue *txq_vq;
bool txq_stopping;
bool txq_link_active;
+ pcq_t *txq_intrq;
struct virtio_net_hdr *txq_hdrs;
bus_dmamap_t *txq_hdr_dmamaps;
struct mbuf **txq_mbufs;
bus_dmamap_t *txq_dmamaps;
+
+ void *txq_deferred_transmit;
};
struct vioif_rxqueue {
@@ -234,30 +255,33 @@ struct vioif_ctrlqueue {
struct virtio_net_ctrl_rx *ctrlq_rx;
struct virtio_net_ctrl_mac_tbl *ctrlq_mac_tbl_uc;
struct virtio_net_ctrl_mac_tbl *ctrlq_mac_tbl_mc;
+ struct virtio_net_ctrl_mq *ctrlq_mq;
bus_dmamap_t ctrlq_cmd_dmamap;
bus_dmamap_t ctrlq_status_dmamap;
bus_dmamap_t ctrlq_rx_dmamap;
bus_dmamap_t ctrlq_tbl_uc_dmamap;
bus_dmamap_t ctrlq_tbl_mc_dmamap;
+ bus_dmamap_t ctrlq_mq_dmamap;
};
struct vioif_softc {
device_t sc_dev;
struct virtio_softc *sc_virtio;
- struct virtqueue sc_vq[3];
-#define VQ_RX 0
-#define VQ_TX 1
-#define VQ_CTRL 2
+ struct virtqueue *sc_vqs;
+
+ int sc_max_nvq_pairs;
+ int sc_req_nvq_pairs;
+ int sc_act_nvq_pairs;
uint8_t sc_mac[ETHER_ADDR_LEN];
struct ethercom sc_ethercom;
short sc_deferred_init_done;
bool sc_link_active;
- struct vioif_txqueue sc_txq;
- struct vioif_rxqueue sc_rxq;
+ struct vioif_txqueue *sc_txq;
+ struct vioif_rxqueue *sc_rxq;
bool sc_has_ctrl;
struct vioif_ctrlqueue sc_ctrlq;
@@ -271,14 +295,6 @@ struct vioif_softc {
#define VIRTIO_NET_TX_MAXNSEGS (16) /* XXX */
#define VIRTIO_NET_CTRL_MAC_MAXENTRIES (64) /* XXX */
-#define VIOIF_TXQ_LOCK(_q) mutex_enter((_q)->txq_lock)
-#define VIOIF_TXQ_UNLOCK(_q) mutex_exit((_q)->txq_lock)
-#define VIOIF_TXQ_LOCKED(_q) mutex_owned((_q)->txq_lock)
-
-#define VIOIF_RXQ_LOCK(_q) mutex_enter((_q)->rxq_lock)
-#define VIOIF_RXQ_UNLOCK(_q) mutex_exit((_q)->rxq_lock)
-#define VIOIF_RXQ_LOCKED(_q) mutex_owned((_q)->rxq_lock)
-
/* cfattach interface functions */
static int vioif_match(device_t, cfdata_t, void *);
static void vioif_attach(device_t, device_t, void *);
@@ -288,24 +304,28 @@ static void vioif_deferred_init(device_t
static int vioif_init(struct ifnet *);
static void vioif_stop(struct ifnet *, int);
static void vioif_start(struct ifnet *);
+static void vioif_start_locked(struct ifnet *, struct vioif_txqueue *);
+static int vioif_transmit(struct ifnet *, struct mbuf *);
+static void vioif_transmit_locked(struct ifnet *, struct vioif_txqueue *);
static int vioif_ioctl(struct ifnet *, u_long, void *);
static void vioif_watchdog(struct ifnet *);
/* rx */
-static int vioif_add_rx_mbuf(struct vioif_softc *, int);
-static void vioif_free_rx_mbuf(struct vioif_softc *, int);
-static void vioif_populate_rx_mbufs(struct vioif_softc *);
-static void vioif_populate_rx_mbufs_locked(struct vioif_softc *);
-static int vioif_rx_deq(struct vioif_softc *);
-static int vioif_rx_deq_locked(struct vioif_softc *);
+static int vioif_add_rx_mbuf(struct vioif_rxqueue *, int);
+static void vioif_free_rx_mbuf(struct vioif_rxqueue *, int);
+static void vioif_populate_rx_mbufs(struct vioif_rxqueue *);
+static void vioif_populate_rx_mbufs_locked(struct vioif_rxqueue *);
+static int vioif_rx_deq(struct vioif_rxqueue *);
+static int vioif_rx_deq_locked(struct vioif_rxqueue *);
static int vioif_rx_vq_done(struct virtqueue *);
static void vioif_rx_softint(void *);
-static void vioif_rx_drain(struct vioif_softc *);
+static void vioif_rx_drain(struct vioif_rxqueue *);
/* tx */
static int vioif_tx_vq_done(struct virtqueue *);
static int vioif_tx_vq_done_locked(struct virtqueue *);
-static void vioif_tx_drain(struct vioif_softc *);
+static void vioif_tx_drain(struct vioif_txqueue *);
+static void vioif_deferred_transmit(void *);
/* other control */
static bool vioif_is_link_up(struct vioif_softc *);
@@ -318,6 +338,9 @@ static int vioif_rx_filter(struct vioif_
static int vioif_ctrl_vq_done(struct virtqueue *);
static int vioif_config_change(struct virtio_softc *);
static void vioif_ctl_softint(void *);
+static int vioif_ctrl_mq_vq_pairs_set(struct vioif_softc *, int);
+static void vioif_enable_interrupt_vqpairs(struct vioif_softc *);
+static void vioif_disable_interrupt_vqpairs(struct vioif_softc *);
CFATTACH_DECL_NEW(vioif, sizeof(struct vioif_softc),
vioif_match, vioif_attach, NULL, NULL);
@@ -333,6 +356,69 @@ vioif_match(device_t parent, cfdata_t ma
return 0;
}
+static int
+vioif_alloc_queues(struct vioif_softc *sc)
+{
+ int nvq_pairs = sc->sc_max_nvq_pairs;
+ int nvqs = nvq_pairs * 2;
+ int i;
+
+ KASSERT(nvq_pairs <= VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX);
+
+ sc->sc_rxq = kmem_zalloc(sizeof(sc->sc_rxq[0]) * nvq_pairs,
+ KM_NOSLEEP);
+ if (sc->sc_rxq == NULL)
+ return -1;
+
+ sc->sc_txq = kmem_zalloc(sizeof(sc->sc_txq[0]) * nvq_pairs,
+ KM_NOSLEEP);
+ if (sc->sc_txq == NULL)
+ return -1;
+
+ if (sc->sc_has_ctrl)
+ nvqs++;
+
+ sc->sc_vqs = kmem_zalloc(sizeof(sc->sc_vqs[0]) * nvqs, KM_NOSLEEP);
+ if (sc->sc_vqs == NULL)
+ return -1;
+
+ nvqs = 0;
+ for (i = 0; i < nvq_pairs; i++) {
+ sc->sc_rxq[i].rxq_vq = &sc->sc_vqs[nvqs++];
+ sc->sc_txq[i].txq_vq = &sc->sc_vqs[nvqs++];
+ }
+
+ if (sc->sc_has_ctrl)
+ sc->sc_ctrlq.ctrlq_vq = &sc->sc_vqs[nvqs++];
+
+ return 0;
+}
+
+static void
+vioif_free_queues(struct vioif_softc *sc)
+{
+ int nvq_pairs = sc->sc_max_nvq_pairs;
+ int nvqs = nvq_pairs * 2;
+
+ if (sc->sc_ctrlq.ctrlq_vq)
+ nvqs++;
+
+ if (sc->sc_txq) {
+ kmem_free(sc->sc_txq, sizeof(sc->sc_txq[0]) * nvq_pairs);
+ sc->sc_txq = NULL;
+ }
+
+ if (sc->sc_rxq) {
+ kmem_free(sc->sc_rxq, sizeof(sc->sc_rxq[0]) * nvq_pairs);
+ sc->sc_rxq = NULL;
+ }
+
+ if (sc->sc_vqs) {
+ kmem_free(sc->sc_vqs, sizeof(sc->sc_vqs[0]) * nvqs);
+ sc->sc_vqs = NULL;
+ }
+}
+
/* allocate memory */
/*
* dma memory is used for:
@@ -362,19 +448,23 @@ static int
vioif_alloc_mems(struct vioif_softc *sc)
{
struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_txqueue *txq = &sc->sc_txq;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct vioif_txqueue *txq;
+ struct vioif_rxqueue *rxq;
struct vioif_ctrlqueue *ctrlq = &sc->sc_ctrlq;
- int allocsize, allocsize2, r, rsegs, i;
+ int allocsize, allocsize2, r, rsegs, i, qid;
void *vaddr;
intptr_t p;
- int rxqsize, txqsize;
- rxqsize = rxq->rxq_vq->vq_num;
- txqsize = txq->txq_vq->vq_num;
-
- allocsize = sizeof(struct virtio_net_hdr) * rxqsize;
- allocsize += sizeof(struct virtio_net_hdr) * txqsize;
+ allocsize = 0;
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+
+ allocsize +=
+ sizeof(struct virtio_net_hdr) * rxq->rxq_vq->vq_num;
+ allocsize +=
+ sizeof(struct virtio_net_hdr) * txq->txq_vq->vq_num;
+ }
if (sc->sc_has_ctrl) {
allocsize += sizeof(struct virtio_net_ctrl_cmd) * 1;
allocsize += sizeof(struct virtio_net_ctrl_status) * 1;
@@ -382,6 +472,7 @@ vioif_alloc_mems(struct vioif_softc *sc)
allocsize += sizeof(struct virtio_net_ctrl_mac_tbl)
+ sizeof(struct virtio_net_ctrl_mac_tbl)
+ ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_MAXENTRIES;
+ allocsize += sizeof(struct virtio_net_ctrl_mq) * 1;
}
r = bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 0, 0,
&sc->sc_hdr_segs[0], 1, &rsegs, BUS_DMA_NOWAIT);
@@ -407,8 +498,15 @@ vioif_alloc_mems(struct vioif_softc *sc)
sc->sc_dmamem = vaddr;
p = (intptr_t) vaddr;
- P(p, rxq->rxq_hdrs, sizeof(rxq->rxq_hdrs[0]) * rxqsize);
- P(p, txq->txq_hdrs, sizeof(txq->txq_hdrs[0]) * txqsize);
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+
+ P(p, rxq->rxq_hdrs,
+ sizeof(rxq->rxq_hdrs[0]) * rxq->rxq_vq->vq_num);
+ P(p, txq->txq_hdrs,
+ sizeof(txq->txq_hdrs[0]) * txq->txq_vq->vq_num);
+ }
if (sc->sc_has_ctrl) {
P(p, ctrlq->ctrlq_cmd, sizeof(*ctrlq->ctrlq_cmd));
P(p, ctrlq->ctrlq_status, sizeof(*ctrlq->ctrlq_status));
@@ -417,21 +515,44 @@ vioif_alloc_mems(struct vioif_softc *sc)
P(p, ctrlq->ctrlq_mac_tbl_mc,
(sizeof(*ctrlq->ctrlq_mac_tbl_mc)
+ ETHER_ADDR_LEN * VIRTIO_NET_CTRL_MAC_MAXENTRIES));
+ P(p, ctrlq->ctrlq_mq, sizeof(*ctrlq->ctrlq_mq));
}
- allocsize2 = sizeof(bus_dmamap_t) * (rxqsize + txqsize);
- allocsize2 += sizeof(bus_dmamap_t) * (rxqsize + txqsize);
- allocsize2 += sizeof(struct mbuf*) * (rxqsize + txqsize);
+ allocsize2 = 0;
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ int rxqsize, txqsize;
+
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+ rxqsize = rxq->rxq_vq->vq_num;
+ txqsize = txq->txq_vq->vq_num;
+
+ allocsize2 += sizeof(rxq->rxq_dmamaps[0]) * rxqsize;
+ allocsize2 += sizeof(rxq->rxq_hdr_dmamaps[0]) * rxqsize;
+ allocsize2 += sizeof(rxq->rxq_mbufs[0]) * rxqsize;
+
+ allocsize2 += sizeof(txq->txq_dmamaps[0]) * txqsize;
+ allocsize2 += sizeof(txq->txq_hdr_dmamaps[0]) * txqsize;
+ allocsize2 += sizeof(txq->txq_mbufs[0]) * txqsize;
+ }
vaddr = kmem_zalloc(allocsize2, KM_SLEEP);
sc->sc_kmem = vaddr;
p = (intptr_t) vaddr;
- P(p, rxq->rxq_hdr_dmamaps, sizeof(rxq->rxq_hdr_dmamaps[0]) * rxqsize);
- P(p, txq->txq_hdr_dmamaps, sizeof(txq->txq_hdr_dmamaps[0]) * txqsize);
- P(p, rxq->rxq_dmamaps, sizeof(rxq->rxq_dmamaps[0]) * rxqsize);
- P(p, txq->txq_dmamaps, sizeof(txq->txq_dmamaps[0]) * txqsize);
- P(p, rxq->rxq_mbufs, sizeof(rxq->rxq_mbufs[0]) * rxqsize);
- P(p, txq->txq_mbufs, sizeof(txq->txq_mbufs[0]) * txqsize);
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ int rxqsize, txqsize;
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+ rxqsize = rxq->rxq_vq->vq_num;
+ txqsize = txq->txq_vq->vq_num;
+
+ P(p, rxq->rxq_hdr_dmamaps, sizeof(rxq->rxq_hdr_dmamaps[0]) * rxqsize);
+ P(p, txq->txq_hdr_dmamaps, sizeof(txq->txq_hdr_dmamaps[0]) * txqsize);
+ P(p, rxq->rxq_dmamaps, sizeof(rxq->rxq_dmamaps[0]) * rxqsize);
+ P(p, txq->txq_dmamaps, sizeof(txq->txq_dmamaps[0]) * txqsize);
+ P(p, rxq->rxq_mbufs, sizeof(rxq->rxq_mbufs[0]) * rxqsize);
+ P(p, txq->txq_mbufs, sizeof(txq->txq_mbufs[0]) * txqsize);
+ }
#undef P
#define C(map, size, nsegs, usage) \
@@ -459,16 +580,25 @@ vioif_alloc_mems(struct vioif_softc *sc)
goto err_reqs; \
} \
} while (0)
- for (i = 0; i < rxqsize; i++) {
- C_L(rxq->rxq_hdr_dmamaps[i], &rxq->rxq_hdrs[i], sizeof(rxq->rxq_hdrs[0]), 1,
- BUS_DMA_READ, "rx header");
- C(rxq->rxq_dmamaps[i], MCLBYTES, 1, "rx payload");
- }
- for (i = 0; i < txqsize; i++) {
- C_L(txq->txq_hdr_dmamaps[i], &txq->txq_hdrs[i], sizeof(txq->txq_hdrs[0]), 1,
- BUS_DMA_READ, "tx header");
- C(txq->txq_dmamaps[i], ETHER_MAX_LEN, VIRTIO_NET_TX_MAXNSEGS, "tx payload");
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+
+ for (i = 0; i < rxq->rxq_vq->vq_num; i++) {
+ C_L(rxq->rxq_hdr_dmamaps[i], &rxq->rxq_hdrs[i],
+ sizeof(rxq->rxq_hdrs[0]), 1,
+ BUS_DMA_READ, "rx header");
+ C(rxq->rxq_dmamaps[i], MCLBYTES, 1, "rx payload");
+ }
+
+ for (i = 0; i < txq->txq_vq->vq_num; i++) {
+ C_L(txq->txq_hdr_dmamaps[i], &txq->txq_hdrs[i],
+ sizeof(txq->txq_hdrs[0]), 1,
+ BUS_DMA_READ, "tx header");
+ C(txq->txq_dmamaps[i], ETHER_MAX_LEN,
+ VIRTIO_NET_TX_MAXNSEGS, "tx payload");
+ }
}
if (sc->sc_has_ctrl) {
@@ -485,6 +615,11 @@ vioif_alloc_mems(struct vioif_softc *sc)
ctrlq->ctrlq_rx, sizeof(*ctrlq->ctrlq_rx), 1,
BUS_DMA_WRITE, "rx mode control command");
+ /* multiqueue set command */
+ C_L(ctrlq->ctrlq_mq_dmamap,
+ ctrlq->ctrlq_mq, sizeof(*ctrlq->ctrlq_mq), 1,
+ BUS_DMA_WRITE, "multiqueue set command");
+
/* control vq MAC filter table for unicast */
/* do not load now since its length is variable */
C(ctrlq->ctrlq_tbl_uc_dmamap,
@@ -515,13 +650,18 @@ err_reqs:
D(ctrlq->ctrlq_rx_dmamap);
D(ctrlq->ctrlq_status_dmamap);
D(ctrlq->ctrlq_cmd_dmamap);
- for (i = 0; i < txqsize; i++) {
- D(txq->txq_dmamaps[i]);
- D(txq->txq_hdr_dmamaps[i]);
- }
- for (i = 0; i < rxqsize; i++) {
- D(rxq->rxq_dmamaps[i]);
- D(rxq->rxq_hdr_dmamaps[i]);
+ for (qid = 0; qid < sc->sc_max_nvq_pairs; qid++) {
+ rxq = &sc->sc_rxq[qid];
+ txq = &sc->sc_txq[qid];
+
+ for (i = 0; i < txq->txq_vq->vq_num; i++) {
+ D(txq->txq_dmamaps[i]);
+ D(txq->txq_hdr_dmamaps[i]);
+ }
+ for (i = 0; i < rxq->rxq_vq->vq_num; i++) {
+ D(rxq->rxq_dmamaps[i]);
+ D(rxq->rxq_hdr_dmamaps[i]);
+ }
}
#undef D
if (sc->sc_kmem) {
@@ -540,13 +680,13 @@ vioif_attach(device_t parent, device_t s
{
struct vioif_softc *sc = device_private(self);
struct virtio_softc *vsc = device_private(parent);
- struct vioif_txqueue *txq = &sc->sc_txq;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
struct vioif_ctrlqueue *ctrlq = &sc->sc_ctrlq;
- uint32_t features;
+ struct vioif_txqueue *txq;
+ struct vioif_rxqueue *rxq;
+ uint32_t features, req_features;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
u_int softint_flags;
- int r, nvqs=0, req_flags;
+ int r, i, nvqs=0, req_flags;
if (virtio_child(vsc) != NULL) {
aprint_normal(": child already attached for %s; "
@@ -559,6 +699,10 @@ vioif_attach(device_t parent, device_t s
sc->sc_virtio = vsc;
sc->sc_link_active = false;
+ sc->sc_max_nvq_pairs = 1;
+ sc->sc_req_nvq_pairs = 1;
+ sc->sc_act_nvq_pairs = 1;
+
req_flags = 0;
#ifdef VIOIF_MPSAFE
@@ -569,11 +713,15 @@ vioif_attach(device_t parent, device_t s
#endif
req_flags |= VIRTIO_F_PCI_INTR_MSIX;
- virtio_child_attach_start(vsc, self, IPL_NET, sc->sc_vq,
+ req_features =
+ VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_NET_F_CTRL_VQ |
+ VIRTIO_NET_F_CTRL_RX | VIRTIO_F_NOTIFY_ON_EMPTY;
+#ifdef VIOIF_MULTIQ
+ req_features |= VIRTIO_NET_F_MQ;
+#endif
+ virtio_child_attach_start(vsc, self, IPL_NET, NULL,
vioif_config_change, virtio_vq_intr, req_flags,
- (VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_NET_F_CTRL_VQ |
- VIRTIO_NET_F_CTRL_RX | VIRTIO_F_NOTIFY_ON_EMPTY),
- VIRTIO_NET_FLAG_BITS);
+ req_features, VIRTIO_NET_FLAG_BITS);
features = virtio_features(vsc);
@@ -619,6 +767,34 @@ vioif_attach(device_t parent, device_t s
aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(sc->sc_mac));
+ if ((features & VIRTIO_NET_F_CTRL_VQ) &&
+ (features & VIRTIO_NET_F_CTRL_RX)) {
+ sc->sc_has_ctrl = true;
+
+ cv_init(&ctrlq->ctrlq_wait, "ctrl_vq");
+ mutex_init(&ctrlq->ctrlq_wait_lock, MUTEX_DEFAULT, IPL_NET);
+ ctrlq->ctrlq_inuse = FREE;
+ } else {
+ sc->sc_has_ctrl = false;
+ }
+
+ if (sc->sc_has_ctrl && (features & VIRTIO_NET_F_MQ)) {
+ sc->sc_max_nvq_pairs = virtio_read_device_config_2(vsc,
+ VIRTIO_NET_CONFIG_MAX_VQ_PAIRS);
+
+ if (sc->sc_max_nvq_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX)
+ goto err;
+
+ /* Limit the number of queue pairs to use */
+ sc->sc_req_nvq_pairs = MIN(sc->sc_max_nvq_pairs, ncpu);
+ }
+
+ r = vioif_alloc_queues(sc);
+ if (r != 0)
+ goto err;
+
+ virtio_child_attach_set_vqs(vsc, sc->sc_vqs, sc->sc_req_nvq_pairs);
+
#ifdef VIOIF_MPSAFE
softint_flags = SOFTINT_NET | SOFTINT_MPSAFE;
#else
@@ -626,66 +802,72 @@ vioif_attach(device_t parent, device_t s
#endif
/*
- * Allocating a virtqueue for Rx
+ * Allocating a virtqueues
*/
- rxq->rxq_vq = &sc->sc_vq[VQ_RX];
- rxq->rxq_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
-
- rxq->rxq_softint = softint_establish(softint_flags, vioif_rx_softint, sc);
- if (rxq->rxq_softint == NULL) {
- aprint_error_dev(self, "cannot establish rx softint\n");
- goto err;
+ for (i = 0; i < sc->sc_max_nvq_pairs; i++) {
+ rxq = &sc->sc_rxq[i];
+ txq = &sc->sc_txq[i];
+ char qname[32];
+
+ rxq->rxq_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+
+ rxq->rxq_softint = softint_establish(softint_flags, vioif_rx_softint, rxq);
+ if (rxq->rxq_softint == NULL) {
+ aprint_error_dev(self, "cannot establish rx softint\n");
+ goto err;
+ }
+ snprintf(qname, sizeof(qname), "rx%d", i);
+ r = virtio_alloc_vq(vsc, rxq->rxq_vq, nvqs,
+ MCLBYTES+sizeof(struct virtio_net_hdr), nvqs, qname);
+ if (r != 0)
+ goto err;
+ nvqs++;
+ rxq->rxq_vq->vq_done = vioif_rx_vq_done;
+ rxq->rxq_vq->vq_done_ctx = (void *)rxq;
+ rxq->rxq_stopping = true;
+
+ txq->txq_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+ txq->txq_deferred_transmit = softint_establish(softint_flags,
+ vioif_deferred_transmit, txq);
+ if (txq->txq_deferred_transmit == NULL) {
+ aprint_error_dev(self, "cannot establish tx softint\n");
+ goto err;
+ }
+ snprintf(qname, sizeof(qname), "tx%d", i);
+ r = virtio_alloc_vq(vsc, txq->txq_vq, nvqs,
+ (sizeof(struct virtio_net_hdr) + (ETHER_MAX_LEN - ETHER_HDR_LEN)),
+ VIRTIO_NET_TX_MAXNSEGS + 1, qname);
+ if (r != 0)
+ goto err;
+ nvqs++;
+ txq->txq_vq->vq_done = vioif_tx_vq_done;
+ txq->txq_vq->vq_done_ctx = (void *)txq;
+ txq->txq_link_active = sc->sc_link_active;
+ txq->txq_stopping = false;
+ txq->txq_intrq = pcq_create(txq->txq_vq->vq_num, KM_NOSLEEP);
+ if (txq->txq_intrq == NULL)
+ goto err;
}
- r = virtio_alloc_vq(vsc, rxq->rxq_vq, VQ_RX,
- MCLBYTES+sizeof(struct virtio_net_hdr), 2, "rx");
- if (r != 0)
- goto err;
- nvqs = 1;
- rxq->rxq_vq->vq_done = vioif_rx_vq_done;
- rxq->rxq_stopping = true;
-
- /*
- * Allocating a virtqueue for Tx
- */
- txq->txq_vq = &sc->sc_vq[VQ_TX];
- txq->txq_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
- r = virtio_alloc_vq(vsc, txq->txq_vq, VQ_TX,
- (sizeof(struct virtio_net_hdr) + (ETHER_MAX_LEN - ETHER_HDR_LEN)),
- VIRTIO_NET_TX_MAXNSEGS + 1, "tx");
- if (r != 0)
- goto err;
- nvqs = 2;
- txq->txq_vq->vq_done = vioif_tx_vq_done;
- txq->txq_link_active = sc->sc_link_active;
- txq->txq_stopping = false;
-
- virtio_start_vq_intr(vsc, rxq->rxq_vq);
- virtio_stop_vq_intr(vsc, txq->txq_vq); /* not urgent; do it later */
- ctrlq->ctrlq_vq = &sc->sc_vq[VQ_CTRL];
- if ((features & VIRTIO_NET_F_CTRL_VQ) &&
- (features & VIRTIO_NET_F_CTRL_RX)) {
+ if (sc->sc_has_ctrl) {
/*
* Allocating a virtqueue for control channel
*/
- r = virtio_alloc_vq(vsc, ctrlq->ctrlq_vq, VQ_CTRL,
+ r = virtio_alloc_vq(vsc, ctrlq->ctrlq_vq, nvqs,
NBPG, 1, "control");
if (r != 0) {
aprint_error_dev(self, "failed to allocate "
"a virtqueue for control channel\n");
- goto skip;
- }
- ctrlq->ctrlq_vq->vq_done = vioif_ctrl_vq_done;
- cv_init(&ctrlq->ctrlq_wait, "ctrl_vq");
- mutex_init(&ctrlq->ctrlq_wait_lock, MUTEX_DEFAULT, IPL_NET);
- ctrlq->ctrlq_inuse = FREE;
- virtio_start_vq_intr(vsc, ctrlq->ctrlq_vq);
- sc->sc_has_ctrl = true;
- nvqs = 3;
+ sc->sc_has_ctrl = false;
+ cv_destroy(&ctrlq->ctrlq_wait);
+ mutex_destroy(&ctrlq->ctrlq_wait_lock);
+ } else {
+ nvqs++;
+ ctrlq->ctrlq_vq->vq_done = vioif_ctrl_vq_done;
+ ctrlq->ctrlq_vq->vq_done_ctx = (void *) ctrlq;
+ }
}
-skip:
-
sc->sc_ctl_softint = softint_establish(softint_flags, vioif_ctl_softint, sc);
if (sc->sc_ctl_softint == NULL) {
@@ -706,11 +888,14 @@ skip:
ifp->if_extflags = IFEF_MPSAFE;
#endif
ifp->if_start = vioif_start;
+ if (sc->sc_req_nvq_pairs > 1)
+ ifp->if_transmit = vioif_transmit;
ifp->if_ioctl = vioif_ioctl;
ifp->if_init = vioif_init;
ifp->if_stop = vioif_stop;
ifp->if_capabilities = 0;
ifp->if_watchdog = vioif_watchdog;
+ txq = &sc->sc_txq[0];
IFQ_SET_MAXLEN(&ifp->if_snd, MAX(txq->txq_vq->vq_num, IFQ_MAXLEN));
IFQ_SET_READY(&ifp->if_snd);
@@ -723,19 +908,34 @@ skip:
return;
err:
- if (rxq->rxq_lock) {
- mutex_obj_free(rxq->rxq_lock);
- rxq->rxq_lock = NULL;
- }
+ for (i = 0; i < sc->sc_max_nvq_pairs; i++) {
+ rxq = &sc->sc_rxq[i];
+ txq = &sc->sc_txq[i];
+
+ if (rxq->rxq_lock) {
+ mutex_obj_free(rxq->rxq_lock);
+ rxq->rxq_lock = NULL;
+ }
- if (rxq->rxq_softint) {
- softint_disestablish(rxq->rxq_softint);
- rxq->rxq_softint = NULL;
- }
+ if (rxq->rxq_softint) {
+ softint_disestablish(rxq->rxq_softint);
+ rxq->rxq_softint = NULL;
+ }
- if (txq->txq_lock) {
- mutex_obj_free(txq->txq_lock);
- txq->txq_lock = NULL;
+ if (txq->txq_lock) {
+ mutex_obj_free(txq->txq_lock);
+ txq->txq_lock = NULL;
+ }
+
+ if (txq->txq_deferred_transmit) {
+ softint_disestablish(txq->txq_deferred_transmit);
+ txq->txq_deferred_transmit = NULL;
+ }
+
+ if (txq->txq_intrq) {
+ pcq_destroy(txq->txq_intrq);
+ txq->txq_intrq = NULL;
+ }
}
if (sc->sc_has_ctrl) {
@@ -744,7 +944,9 @@ err:
}
while (nvqs > 0)
- virtio_free_vq(vsc, &sc->sc_vq[--nvqs]);
+ virtio_free_vq(vsc, &sc->sc_vqs[--nvqs]);
+
+ vioif_free_queues(sc);
virtio_child_attach_failed(vsc);
return;
@@ -767,6 +969,40 @@ vioif_deferred_init(device_t self)
"errror code %d\n", r);
}
+static void
+vioif_enable_interrupt_vqpairs(struct vioif_softc *sc)
+{
+ struct virtio_softc *vsc = sc->sc_virtio;
+ struct vioif_txqueue *txq;
+ struct vioif_rxqueue *rxq;
+ int i;
+
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+ txq = &sc->sc_txq[i];
+ rxq = &sc->sc_rxq[i];
+
+ virtio_start_vq_intr(vsc, txq->txq_vq);
+ virtio_start_vq_intr(vsc, rxq->rxq_vq);
+ }
+}
+
+static void
+vioif_disable_interrupt_vqpairs(struct vioif_softc *sc)
+{
+ struct virtio_softc *vsc = sc->sc_virtio;
+ struct vioif_txqueue *txq;
+ struct vioif_rxqueue *rxq;
+ int i;
+
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+ txq = &sc->sc_txq[i];
+ rxq = &sc->sc_rxq[i];
+
+ virtio_stop_vq_intr(vsc, txq->txq_vq);
+ virtio_stop_vq_intr(vsc, rxq->rxq_vq);
+ }
+}
+
/*
* Interface functions for ifnet
*/
@@ -775,19 +1011,36 @@ vioif_init(struct ifnet *ifp)
{
struct vioif_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_txqueue *txq = &sc->sc_txq;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct vioif_rxqueue *rxq;
struct vioif_ctrlqueue *ctrlq = &sc->sc_ctrlq;
+ int r, i;
vioif_stop(ifp, 0);
virtio_reinit_start(vsc);
virtio_negotiate_features(vsc, virtio_features(vsc));
- virtio_start_vq_intr(vsc, rxq->rxq_vq);
- virtio_stop_vq_intr(vsc, txq->txq_vq);
- if (sc->sc_has_ctrl)
- virtio_start_vq_intr(vsc, ctrlq->ctrlq_vq);
+
+ for (i = 0; i < sc->sc_req_nvq_pairs; i++) {
+ rxq = &sc->sc_rxq[i];
+
+ /* Have to set false before vioif_populate_rx_mbufs */
+ rxq->rxq_stopping = false;
+ vioif_populate_rx_mbufs(rxq);
+ }
+
virtio_reinit_end(vsc);
+ virtio_start_vq_intr(vsc, ctrlq->ctrlq_vq);
+
+ r = vioif_ctrl_mq_vq_pairs_set(sc, sc->sc_req_nvq_pairs);
+ if (r == 0)
+ sc->sc_act_nvq_pairs = sc->sc_req_nvq_pairs;
+ else
+ sc->sc_act_nvq_pairs = 1;
+
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++)
+ sc->sc_txq[i].txq_stopping = false;
+
+ vioif_enable_interrupt_vqpairs(sc);
if (!sc->sc_deferred_init_done) {
sc->sc_deferred_init_done = 1;
@@ -795,12 +1048,6 @@ vioif_init(struct ifnet *ifp)
vioif_deferred_init(sc->sc_dev);
}
- /* Have to set false before vioif_populate_rx_mbufs */
- rxq->rxq_stopping = false;
- txq->txq_stopping = false;
-
- vioif_populate_rx_mbufs(sc);
-
vioif_update_link_status(sc);
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
@@ -814,60 +1061,80 @@ vioif_stop(struct ifnet *ifp, int disabl
{
struct vioif_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_txqueue *txq = &sc->sc_txq;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct vioif_txqueue *txq;
+ struct vioif_rxqueue *rxq;
struct vioif_ctrlqueue *ctrlq = &sc->sc_ctrlq;
+ int i;
/* Take the locks to ensure that ongoing TX/RX finish */
- VIOIF_TXQ_LOCK(txq);
- txq->txq_stopping = true;
- VIOIF_TXQ_UNLOCK(txq);
-
- VIOIF_RXQ_LOCK(rxq);
- rxq->rxq_stopping = true;
- VIOIF_RXQ_UNLOCK(rxq);
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+ txq = &sc->sc_txq[i];
+ rxq = &sc->sc_rxq[i];
+
+ mutex_enter(txq->txq_lock);
+ txq->txq_stopping = true;
+ mutex_exit(txq->txq_lock);
+
+ mutex_enter(rxq->rxq_lock);
+ rxq->rxq_stopping = true;
+ mutex_exit(rxq->rxq_lock);
+ }
/* disable interrupts */
- virtio_stop_vq_intr(vsc, rxq->rxq_vq);
- virtio_stop_vq_intr(vsc, txq->txq_vq);
+ vioif_disable_interrupt_vqpairs(sc);
+
if (sc->sc_has_ctrl)
virtio_stop_vq_intr(vsc, ctrlq->ctrlq_vq);
/* only way to stop I/O and DMA is resetting... */
virtio_reset(vsc);
- vioif_rx_deq(sc);
- vioif_tx_drain(sc);
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++)
+ vioif_rx_deq(&sc->sc_rxq[i]);
+
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
sc->sc_link_active = false;
- txq->txq_link_active = false;
- if (disable)
- vioif_rx_drain(sc);
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+ txq = &sc->sc_txq[i];
+ rxq = &sc->sc_rxq[i];
+
+ txq->txq_link_active = false;
+
+ if (disable)
+ vioif_rx_drain(rxq);
+
+ vioif_tx_drain(txq);
+ }
}
static void
-vioif_start(struct ifnet *ifp)
+vioif_send_common_locked(struct ifnet *ifp, struct vioif_txqueue *txq, bool is_transmit)
{
struct vioif_softc *sc = ifp->if_softc;
struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_txqueue *txq = &sc->sc_txq;
struct virtqueue *vq = txq->txq_vq;
struct mbuf *m;
int queued = 0;
- VIOIF_TXQ_LOCK(txq);
+ KASSERT(mutex_owned(txq->txq_lock));
- if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING ||
- !txq->txq_link_active)
- goto out;
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return;
- if (txq->txq_stopping)
- goto out;
+ if (!txq->txq_link_active || txq->txq_stopping)
+ return;
+
+ if ((ifp->if_flags & IFF_OACTIVE) != 0 && !is_transmit)
+ return;
for (;;) {
int slot, r;
- IFQ_DEQUEUE(&ifp->if_snd, m);
+ if (is_transmit)
+ m = pcq_get(txq->txq_intrq);
+ else
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+
if (m == NULL)
break;
@@ -944,9 +1211,91 @@ skip:
virtio_enqueue_commit(vsc, vq, -1, true);
ifp->if_timer = 5;
}
+}
-out:
- VIOIF_TXQ_UNLOCK(txq);
+static void
+vioif_start_locked(struct ifnet *ifp, struct vioif_txqueue *txq)
+{
+
+ /*
+ * ifp->if_obytes and ifp->if_omcasts are added in if_transmit()@if.c.
+ */
+ vioif_send_common_locked(ifp, txq, false);
+
+}
+
+static void
+vioif_start(struct ifnet *ifp)
+{
+ struct vioif_softc *sc = ifp->if_softc;
+ struct vioif_txqueue *txq = &sc->sc_txq[0];
+
+#ifdef VIOIF_MPSAFE
+ KASSERT(if_is_mpsafe(ifp));
+#endif
+
+ mutex_enter(txq->txq_lock);
+ if (!txq->txq_stopping)
+ vioif_start_locked(ifp, txq);
+ mutex_exit(txq->txq_lock);
+}
+
+static inline int
+vioif_select_txqueue(struct ifnet *ifp, struct mbuf *m)
+{
+ struct vioif_softc *sc = ifp->if_softc;
+ u_int cpuid = cpu_index(curcpu());
+
+ return cpuid % sc->sc_act_nvq_pairs;
+}
+
+static void
+vioif_transmit_locked(struct ifnet *ifp, struct vioif_txqueue *txq)
+{
+
+ vioif_send_common_locked(ifp, txq, true);
+}
+
+static int
+vioif_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ struct vioif_softc *sc = ifp->if_softc;
+ struct vioif_txqueue *txq;
+ int qid;
+
+ qid = vioif_select_txqueue(ifp, m);
+ txq = &sc->sc_txq[qid];
+
+ if (__predict_false(!pcq_put(txq->txq_intrq, m))) {
+ m_freem(m);
+ return ENOBUFS;
+ }
+
+ ifp->if_obytes += m->m_pkthdr.len;
+ if (m->m_flags & M_MCAST)
+ ifp->if_omcasts++;
+
+ if (mutex_tryenter(txq->txq_lock)) {
+ if (!txq->txq_stopping)
+ vioif_transmit_locked(ifp, txq);
+ mutex_exit(txq->txq_lock);
+ }
+
+ return 0;
+}
+
+static void
+vioif_deferred_transmit(void *arg)
+{
+ struct vioif_txqueue *txq = arg;
+ struct virtio_softc *vsc = txq->txq_vq->vq_owner;
+ struct vioif_softc *sc = device_private(virtio_child(vsc));
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+
+ if (mutex_tryenter(txq->txq_lock)) {
+ vioif_send_common_locked(ifp, txq, true);
+ mutex_exit(txq->txq_lock);
+ }
}
static int
@@ -974,10 +1323,12 @@ void
vioif_watchdog(struct ifnet *ifp)
{
struct vioif_softc *sc = ifp->if_softc;
- struct vioif_txqueue *txq = &sc->sc_txq;
+ int i;
- if (ifp->if_flags & IFF_RUNNING)
- vioif_tx_vq_done(txq->txq_vq);
+ if (ifp->if_flags & IFF_RUNNING) {
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++)
+ vioif_tx_vq_done(sc->sc_txq[i].txq_vq);
+ }
}
@@ -986,9 +1337,9 @@ vioif_watchdog(struct ifnet *ifp)
*/
/* allocate and initialize a mbuf for receive */
static int
-vioif_add_rx_mbuf(struct vioif_softc *sc, int i)
+vioif_add_rx_mbuf(struct vioif_rxqueue *rxq, int i)
{
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct virtio_softc *vsc = rxq->rxq_vq->vq_owner;
struct mbuf *m;
int r;
@@ -1002,12 +1353,12 @@ vioif_add_rx_mbuf(struct vioif_softc *sc
}
rxq->rxq_mbufs[i] = m;
m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
- r = bus_dmamap_load_mbuf(virtio_dmat(sc->sc_virtio),
+ r = bus_dmamap_load_mbuf(virtio_dmat(vsc),
rxq->rxq_dmamaps[i],
m, BUS_DMA_READ|BUS_DMA_NOWAIT);
if (r) {
m_freem(m);
- rxq->rxq_mbufs[i] = 0;
+ rxq->rxq_mbufs[i] = NULL;
return r;
}
@@ -1016,35 +1367,34 @@ vioif_add_rx_mbuf(struct vioif_softc *sc
/* free a mbuf for receive */
static void
-vioif_free_rx_mbuf(struct vioif_softc *sc, int i)
+vioif_free_rx_mbuf(struct vioif_rxqueue *rxq, int i)
{
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct virtio_softc *vsc = rxq->rxq_vq->vq_owner;
- bus_dmamap_unload(virtio_dmat(sc->sc_virtio), rxq->rxq_dmamaps[i]);
+ bus_dmamap_unload(virtio_dmat(vsc), rxq->rxq_dmamaps[i]);
m_freem(rxq->rxq_mbufs[i]);
rxq->rxq_mbufs[i] = NULL;
}
/* add mbufs for all the empty receive slots */
static void
-vioif_populate_rx_mbufs(struct vioif_softc *sc)
+vioif_populate_rx_mbufs(struct vioif_rxqueue *rxq)
{
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
- VIOIF_RXQ_LOCK(rxq);
- vioif_populate_rx_mbufs_locked(sc);
- VIOIF_RXQ_UNLOCK(rxq);
+ mutex_enter(rxq->rxq_lock);
+ vioif_populate_rx_mbufs_locked(rxq);
+ mutex_exit(rxq->rxq_lock);
}
static void
-vioif_populate_rx_mbufs_locked(struct vioif_softc *sc)
+vioif_populate_rx_mbufs_locked(struct vioif_rxqueue *rxq)
{
- struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
- int i, r, ndone = 0;
struct virtqueue *vq = rxq->rxq_vq;
+ struct virtio_softc *vsc = vq->vq_owner;
+ struct vioif_softc *sc = device_private(virtio_child(vsc));
+ int i, r, ndone = 0;
- KASSERT(VIOIF_RXQ_LOCKED(rxq));
+ KASSERT(mutex_owned(rxq->rxq_lock));
if (rxq->rxq_stopping)
return;
@@ -1057,7 +1407,7 @@ vioif_populate_rx_mbufs_locked(struct vi
if (r != 0)
panic("enqueue_prep for rx buffers");
if (rxq->rxq_mbufs[slot] == NULL) {
- r = vioif_add_rx_mbuf(sc, slot);
+ r = vioif_add_rx_mbuf(rxq, slot);
if (r != 0) {
printf("%s: rx mbuf allocation failed, "
"error code %d\n",
@@ -1068,7 +1418,7 @@ vioif_populate_rx_mbufs_locked(struct vi
r = virtio_enqueue_reserve(vsc, vq, slot,
rxq->rxq_dmamaps[slot]->dm_nsegs + 1);
if (r != 0) {
- vioif_free_rx_mbuf(sc, slot);
+ vioif_free_rx_mbuf(rxq, slot);
break;
}
bus_dmamap_sync(virtio_dmat(vsc), rxq->rxq_hdr_dmamaps[slot],
@@ -1086,33 +1436,32 @@ vioif_populate_rx_mbufs_locked(struct vi
/* dequeue received packets */
static int
-vioif_rx_deq(struct vioif_softc *sc)
+vioif_rx_deq(struct vioif_rxqueue *rxq)
{
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
int r;
KASSERT(rxq->rxq_stopping);
- VIOIF_RXQ_LOCK(rxq);
- r = vioif_rx_deq_locked(sc);
- VIOIF_RXQ_UNLOCK(rxq);
+ mutex_enter(rxq->rxq_lock);
+ r = vioif_rx_deq_locked(rxq);
+ mutex_exit(rxq->rxq_lock);
return r;
}
/* dequeue received packets */
static int
-vioif_rx_deq_locked(struct vioif_softc *sc)
+vioif_rx_deq_locked(struct vioif_rxqueue *rxq)
{
- struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
struct virtqueue *vq = rxq->rxq_vq;
+ struct virtio_softc *vsc = vq->vq_owner;
+ struct vioif_softc *sc = device_private(virtio_child(vsc));
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct mbuf *m;
int r = 0;
int slot, len;
- KASSERT(VIOIF_RXQ_LOCKED(rxq));
+ KASSERT(mutex_owned(rxq->rxq_lock));
while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
len -= sizeof(struct virtio_net_hdr);
@@ -1126,14 +1475,14 @@ vioif_rx_deq_locked(struct vioif_softc *
m = rxq->rxq_mbufs[slot];
KASSERT(m != NULL);
bus_dmamap_unload(virtio_dmat(vsc), rxq->rxq_dmamaps[slot]);
- rxq->rxq_mbufs[slot] = 0;
+ rxq->rxq_mbufs[slot] = NULL;
virtio_dequeue_commit(vsc, vq, slot);
m_set_rcvif(m, ifp);
m->m_len = m->m_pkthdr.len = len;
- VIOIF_RXQ_UNLOCK(rxq);
+ mutex_exit(rxq->rxq_lock);
if_percpuq_enqueue(ifp->if_percpuq, m);
- VIOIF_RXQ_LOCK(rxq);
+ mutex_enter(rxq->rxq_lock);
if (rxq->rxq_stopping)
break;
@@ -1146,30 +1495,28 @@ vioif_rx_deq_locked(struct vioif_softc *
static int
vioif_rx_vq_done(struct virtqueue *vq)
{
- struct virtio_softc *vsc = vq->vq_owner;
- struct vioif_softc *sc = device_private(virtio_child(vsc));
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
+ struct vioif_rxqueue *rxq = vq->vq_done_ctx;
int r = 0;
#ifdef VIOIF_SOFTINT_INTR
KASSERT(!cpu_intr_p());
#endif
- VIOIF_RXQ_LOCK(rxq);
+ mutex_enter(rxq->rxq_lock);
if (rxq->rxq_stopping)
goto out;
- r = vioif_rx_deq_locked(sc);
+ r = vioif_rx_deq_locked(rxq);
if (r)
#ifdef VIOIF_SOFTINT_INTR
- vioif_populate_rx_mbufs_locked(sc);
+ vioif_populate_rx_mbufs_locked(rxq);
#else
softint_schedule(rxq->rxq_softint);
#endif
out:
- VIOIF_RXQ_UNLOCK(rxq);
+ mutex_exit(rxq->rxq_lock);
return r;
}
@@ -1177,23 +1524,22 @@ out:
static void
vioif_rx_softint(void *arg)
{
- struct vioif_softc *sc = arg;
+ struct vioif_rxqueue *rxq = arg;
- vioif_populate_rx_mbufs(sc);
+ vioif_populate_rx_mbufs(rxq);
}
/* free all the mbufs; called from if_stop(disable) */
static void
-vioif_rx_drain(struct vioif_softc *sc)
+vioif_rx_drain(struct vioif_rxqueue *rxq)
{
- struct vioif_rxqueue *rxq = &sc->sc_rxq;
struct virtqueue *vq = rxq->rxq_vq;
int i;
for (i = 0; i < vq->vq_num; i++) {
if (rxq->rxq_mbufs[i] == NULL)
continue;
- vioif_free_rx_mbuf(sc, i);
+ vioif_free_rx_mbuf(rxq, i);
}
}
@@ -1213,10 +1559,10 @@ vioif_tx_vq_done(struct virtqueue *vq)
struct virtio_softc *vsc = vq->vq_owner;
struct vioif_softc *sc = device_private(virtio_child(vsc));
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
- struct vioif_txqueue *txq = &sc->sc_txq;
+ struct vioif_txqueue *txq = vq->vq_done_ctx;
int r = 0;
- VIOIF_TXQ_LOCK(txq);
+ mutex_enter(txq->txq_lock);
if (txq->txq_stopping)
goto out;
@@ -1224,9 +1570,13 @@ vioif_tx_vq_done(struct virtqueue *vq)
r = vioif_tx_vq_done_locked(vq);
out:
- VIOIF_TXQ_UNLOCK(txq);
- if (r)
+ mutex_exit(txq->txq_lock);
+ if (r) {
if_schedule_deferred_start(ifp);
+
+ KASSERT(txq->txq_deferred_transmit != NULL);
+ softint_schedule(txq->txq_deferred_transmit);
+ }
return r;
}
@@ -1235,13 +1585,13 @@ vioif_tx_vq_done_locked(struct virtqueue
{
struct virtio_softc *vsc = vq->vq_owner;
struct vioif_softc *sc = device_private(virtio_child(vsc));
- struct vioif_txqueue *txq = &sc->sc_txq;
+ struct vioif_txqueue *txq = vq->vq_done_ctx;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct mbuf *m;
int r = 0;
int slot, len;
- KASSERT(VIOIF_TXQ_LOCKED(txq));
+ KASSERT(mutex_owned(txq->txq_lock));
while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
r++;
@@ -1253,7 +1603,7 @@ vioif_tx_vq_done_locked(struct virtqueue
BUS_DMASYNC_POSTWRITE);
m = txq->txq_mbufs[slot];
bus_dmamap_unload(virtio_dmat(vsc), txq->txq_dmamaps[slot]);
- txq->txq_mbufs[slot] = 0;
+ txq->txq_mbufs[slot] = NULL;
virtio_dequeue_commit(vsc, vq, slot);
ifp->if_opackets++;
m_freem(m);
@@ -1266,11 +1616,10 @@ vioif_tx_vq_done_locked(struct virtqueue
/* free all the mbufs already put on vq; called from if_stop(disable) */
static void
-vioif_tx_drain(struct vioif_softc *sc)
+vioif_tx_drain(struct vioif_txqueue *txq)
{
- struct virtio_softc *vsc = sc->sc_virtio;
- struct vioif_txqueue *txq = &sc->sc_txq;
struct virtqueue *vq = txq->txq_vq;
+ struct virtio_softc *vsc = vq->vq_owner;
int i;
KASSERT(txq->txq_stopping);
@@ -1311,6 +1660,7 @@ vioif_ctrl_release(struct vioif_softc *s
mutex_enter(&ctrlq->ctrlq_wait_lock);
ctrlq->ctrlq_inuse = FREE;
+ ctrlq->ctrlq_owner = NULL;
cv_signal(&ctrlq->ctrlq_wait);
mutex_exit(&ctrlq->ctrlq_wait_lock);
}
@@ -1513,13 +1863,41 @@ out:
return r;
}
+static int
+vioif_ctrl_mq_vq_pairs_set(struct vioif_softc *sc, int nvq_pairs)
+{
+ struct virtio_net_ctrl_mq *mq = sc->sc_ctrlq.ctrlq_mq;
+ struct vioif_ctrl_cmdspec specs[1];
+ int r;
+
+ if (!sc->sc_has_ctrl)
+ return ENOTSUP;
+
+ if (nvq_pairs <= 1)
+ return EINVAL;
+
+ vioif_ctrl_acquire(sc);
+
+ mq->virtqueue_pairs = nvq_pairs;
+ specs[0].dmamap = sc->sc_ctrlq.ctrlq_mq_dmamap;
+ specs[0].buf = mq;
+ specs[0].bufsize = sizeof(*mq);
+
+ r = vioif_ctrl_send_command(sc,
+ VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET,
+ specs, __arraycount(specs));
+
+ vioif_ctrl_release(sc);
+
+ return r;
+}
+
/* ctrl vq interrupt; wake up the command issuer */
static int
vioif_ctrl_vq_done(struct virtqueue *vq)
{
struct virtio_softc *vsc = vq->vq_owner;
- struct vioif_softc *sc = device_private(virtio_child(vsc));
- struct vioif_ctrlqueue *ctrlq = &sc->sc_ctrlq;
+ struct vioif_ctrlqueue *ctrlq = vq->vq_done_ctx;
int r, slot;
r = virtio_dequeue(vsc, vq, &slot, NULL);
@@ -1642,9 +2020,9 @@ static void
vioif_update_link_status(struct vioif_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
- struct vioif_txqueue *txq = &sc->sc_txq;
+ struct vioif_txqueue *txq;
bool active, changed;
- int link;
+ int link, i;
active = vioif_is_link_up(sc);
changed = false;
@@ -1664,9 +2042,13 @@ vioif_update_link_status(struct vioif_so
}
if (changed) {
- VIOIF_TXQ_LOCK(txq);
- txq->txq_link_active = sc->sc_link_active;
- VIOIF_TXQ_UNLOCK(txq);
+ for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+ txq = &sc->sc_txq[i];
+
+ mutex_enter(txq->txq_lock);
+ txq->txq_link_active = sc->sc_link_active;
+ mutex_exit(txq->txq_lock);
+ }
if_link_state_change(ifp, link);
}