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);
 	}

Reply via email to