Module Name:    src
Committed By:   yamaguchi
Date:           Mon May 25 08:16:23 UTC 2020

Modified Files:
        src/sys/dev/pci: if_vioif.c

Log Message:
Introduce packet handling in softint or kthread for vioif(4)


To generate a diff of this commit:
cvs rdiff -u -r1.54 -r1.55 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.54 src/sys/dev/pci/if_vioif.c:1.55
--- src/sys/dev/pci/if_vioif.c:1.54	Mon May 25 07:52:16 2020
+++ src/sys/dev/pci/if_vioif.c	Mon May 25 08:16:23 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_vioif.c,v 1.54 2020/05/25 07:52:16 yamaguchi Exp $	*/
+/*	$NetBSD: if_vioif.c,v 1.55 2020/05/25 08:16:23 yamaguchi Exp $	*/
 
 /*
  * Copyright (c) 2010 Minoura Makoto.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.54 2020/05/25 07:52:16 yamaguchi Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v 1.55 2020/05/25 08:16:23 yamaguchi Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -35,6 +35,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/atomic.h>
 #include <sys/bus.h>
 #include <sys/condvar.h>
 #include <sys/device.h>
@@ -46,6 +47,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_vioif.c,v
 #include <sys/cpu.h>
 #include <sys/module.h>
 #include <sys/pcq.h>
+#include <sys/workqueue.h>
 
 #include <dev/pci/virtioreg.h>
 #include <dev/pci/virtiovar.h>
@@ -205,6 +207,13 @@ struct vioif_ctrl_cmdspec {
  *      - txq_lock or rxq_lock cannot be held along with ctrlq_wait_lock
  */
 
+struct vioif_work {
+	struct work	 cookie;
+	void		(*func)(void *);
+	void		*arg;
+	unsigned int	 added;
+};
+
 struct vioif_txqueue {
 	kmutex_t		*txq_lock;	/* lock for tx operations */
 
@@ -220,6 +229,10 @@ struct vioif_txqueue {
 	bus_dmamap_t		*txq_dmamaps;
 
 	void			*txq_deferred_transmit;
+	void			*txq_handle_si;
+	struct vioif_work	 txq_work;
+	bool			 txq_workqueue;
+	bool			 txq_active;
 };
 
 struct vioif_rxqueue {
@@ -235,6 +248,10 @@ struct vioif_rxqueue {
 	bus_dmamap_t		*rxq_dmamaps;
 
 	void			*rxq_softint;
+	void			*rxq_handle_si;
+	struct vioif_work	 rxq_work;
+	bool			 rxq_workqueue;
+	bool			 rxq_active;
 };
 
 struct vioif_ctrlqueue {
@@ -263,6 +280,7 @@ struct vioif_ctrlqueue {
 
 struct vioif_softc {
 	device_t		sc_dev;
+	struct sysctllog	*sc_sysctllog;
 
 	struct virtio_softc	*sc_virtio;
 	struct virtqueue	*sc_vqs;
@@ -287,14 +305,29 @@ struct vioif_softc {
 	void			*sc_kmem;
 
 	void			*sc_ctl_softint;
+
+	struct workqueue	*sc_txrx_workqueue;
+	bool			 sc_txrx_workqueue_sysctl;
+	u_int			 sc_tx_intr_process_limit;
+	u_int			 sc_tx_process_limit;
+	u_int			 sc_rx_intr_process_limit;
+	u_int			 sc_rx_process_limit;
 };
 #define VIRTIO_NET_TX_MAXNSEGS		(16) /* XXX */
 #define VIRTIO_NET_CTRL_MAC_MAXENTRIES	(64) /* XXX */
 
+#define VIOIF_TX_INTR_PROCESS_LIMIT	256
+#define VIOIF_TX_PROCESS_LIMIT		256
+#define VIOIF_RX_INTR_PROCESS_LIMIT	0U
+#define VIOIF_RX_PROCESS_LIMIT		256
+
+#define VIOIF_WORKQUEUE_PRI		PRI_SOFTNET
+
 /* cfattach interface functions */
 static int	vioif_match(device_t, cfdata_t, void *);
 static void	vioif_attach(device_t, device_t, void *);
 static void	vioif_deferred_init(device_t);
+static int	vioif_finalize_teardown(device_t);
 
 /* ifnet interface functions */
 static int	vioif_init(struct ifnet *);
@@ -311,18 +344,36 @@ static int	vioif_add_rx_mbuf(struct vioi
 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 void	vioif_rx_queue_clear(struct vioif_rxqueue *);
+static bool	vioif_rx_deq_locked(struct vioif_softc *, struct virtio_softc *,
+		    struct vioif_rxqueue *, u_int);
 static int	vioif_rx_intr(void *);
+static void	vioif_rx_handle(void *);
+static void	vioif_rx_sched_handle(struct vioif_softc *,
+		    struct vioif_rxqueue *);
 static void	vioif_rx_softint(void *);
 static void	vioif_rx_drain(struct vioif_rxqueue *);
 
 /* tx */
 static int	vioif_tx_intr(void *);
-static int	vioif_tx_deq_locked(struct virtqueue *);
+static void	vioif_tx_handle(void *);
+static void	vioif_tx_sched_handle(struct vioif_softc *,
+		    struct vioif_txqueue *);
+static void	vioif_tx_queue_clear(struct vioif_txqueue *);
+static bool	vioif_tx_deq_locked(struct vioif_softc *, struct virtio_softc *,
+		    struct vioif_txqueue *, u_int);
 static void	vioif_tx_drain(struct vioif_txqueue *);
 static void	vioif_deferred_transmit(void *);
 
+/* workqueue */
+static struct workqueue*
+		vioif_workq_create(const char *, pri_t, int, int);
+static void	vioif_workq_destroy(struct workqueue *);
+static void	vioif_workq_work(struct work *, void *);
+static void	vioif_work_set(struct vioif_work *, void(*)(void *), void *);
+static void	vioif_work_add(struct workqueue *, struct vioif_work *);
+static void	vioif_work_wait(struct workqueue *, struct vioif_work *);
+
 /* other control */
 static bool	vioif_is_link_up(struct vioif_softc *);
 static void	vioif_update_link_status(struct vioif_softc *);
@@ -337,6 +388,7 @@ 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 *);
+static int	vioif_setup_sysctl(struct vioif_softc *);
 
 CFATTACH_DECL_NEW(vioif, sizeof(struct vioif_softc),
 		  vioif_match, vioif_attach, NULL, NULL);
@@ -672,6 +724,7 @@ vioif_attach(device_t parent, device_t s
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 	u_int softint_flags;
 	int r, i, nvqs=0, req_flags;
+	char xnamebuf[MAXCOMLEN];
 
 	if (virtio_child(vsc) != NULL) {
 		aprint_normal(": child already attached for %s; "
@@ -686,6 +739,17 @@ vioif_attach(device_t parent, device_t s
 	sc->sc_max_nvq_pairs = 1;
 	sc->sc_req_nvq_pairs = 1;
 	sc->sc_act_nvq_pairs = 1;
+	sc->sc_txrx_workqueue_sysctl = true;
+	sc->sc_tx_intr_process_limit = VIOIF_TX_INTR_PROCESS_LIMIT;
+	sc->sc_tx_process_limit = VIOIF_TX_PROCESS_LIMIT;
+	sc->sc_rx_intr_process_limit = VIOIF_RX_INTR_PROCESS_LIMIT;
+	sc->sc_rx_process_limit = VIOIF_RX_PROCESS_LIMIT;
+
+	snprintf(xnamebuf, sizeof(xnamebuf), "%s_txrx", device_xname(self));
+	sc->sc_txrx_workqueue = vioif_workq_create(xnamebuf, VIOIF_WORKQUEUE_PRI,
+	    IPL_NET, WQ_PERCPU | WQ_MPSAFE);
+	if (sc->sc_txrx_workqueue == NULL)
+		goto err;
 
 	req_flags = 0;
 
@@ -774,6 +838,13 @@ vioif_attach(device_t parent, device_t s
 			aprint_error_dev(self, "cannot establish rx softint\n");
 			goto err;
 		}
+		rxq->rxq_handle_si = softint_establish(softint_flags,
+		    vioif_rx_handle, rxq);
+		if (rxq->rxq_handle_si == 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);
@@ -783,14 +854,23 @@ vioif_attach(device_t parent, device_t s
 		rxq->rxq_vq->vq_intrhand = vioif_rx_intr;
 		rxq->rxq_vq->vq_intrhand_arg = (void *)rxq;
 		rxq->rxq_stopping = true;
+		vioif_work_set(&rxq->rxq_work, vioif_rx_handle, rxq);
 
 		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;
 		}
+		txq->txq_handle_si = softint_establish(softint_flags,
+		    vioif_tx_handle, txq);
+		if (txq->txq_handle_si == 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)
@@ -804,6 +884,7 @@ vioif_attach(device_t parent, device_t s
 		txq->txq_link_active = sc->sc_link_active;
 		txq->txq_stopping = false;
 		txq->txq_intrq = pcq_create(txq->txq_vq->vq_num, KM_SLEEP);
+		vioif_work_set(&txq->txq_work, vioif_tx_handle, txq);
 	}
 
 	if (sc->sc_has_ctrl) {
@@ -840,6 +921,11 @@ vioif_attach(device_t parent, device_t s
 	if (virtio_child_attach_finish(vsc) != 0)
 		goto err;
 
+	if (vioif_setup_sysctl(sc) != 0) {
+		aprint_error_dev(self, "unable to create sysctl node\n");
+		/* continue */
+	}
+
 	strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
 	ifp->if_softc = sc;
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -881,11 +967,21 @@ err:
 			rxq->rxq_softint = NULL;
 		}
 
+		if (rxq->rxq_handle_si) {
+			softint_disestablish(rxq->rxq_handle_si);
+			rxq->rxq_handle_si = NULL;
+		}
+
 		if (txq->txq_lock) {
 			mutex_obj_free(txq->txq_lock);
 			txq->txq_lock = NULL;
 		}
 
+		if (txq->txq_handle_si) {
+			softint_disestablish(txq->txq_handle_si);
+			txq->txq_handle_si = NULL;
+		}
+
 		if (txq->txq_deferred_transmit) {
 			softint_disestablish(txq->txq_deferred_transmit);
 			txq->txq_deferred_transmit = NULL;
@@ -906,11 +1002,25 @@ err:
 		virtio_free_vq(vsc, &sc->sc_vqs[--nvqs]);
 
 	vioif_free_queues(sc);
-
 	virtio_child_attach_failed(vsc);
+	config_finalize_register(self, vioif_finalize_teardown);
+
 	return;
 }
 
+static int
+vioif_finalize_teardown(device_t self)
+{
+	struct vioif_softc *sc = device_private(self);
+
+	if (sc->sc_txrx_workqueue != NULL) {
+		vioif_workq_destroy(sc->sc_txrx_workqueue);
+		sc->sc_txrx_workqueue = NULL;
+	}
+
+	return 0;
+}
+
 /* we need interrupts to make promiscuous mode off */
 static void
 vioif_deferred_init(device_t self)
@@ -1049,8 +1159,26 @@ vioif_stop(struct ifnet *ifp, int disabl
 
 	/* only way to stop I/O and DMA is resetting... */
 	virtio_reset(vsc);
-	for (i = 0; i < sc->sc_act_nvq_pairs; i++)
-		vioif_rx_deq(&sc->sc_rxq[i]);
+
+	/* rendezvous for finish of handlers */
+	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);
+		mutex_exit(txq->txq_lock);
+
+		mutex_enter(rxq->rxq_lock);
+		mutex_exit(rxq->rxq_lock);
+
+		vioif_work_wait(sc->sc_txrx_workqueue, &txq->txq_work);
+		vioif_work_wait(sc->sc_txrx_workqueue, &rxq->rxq_work);
+	}
+
+	for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+		vioif_rx_queue_clear(&sc->sc_rxq[i]);
+		vioif_tx_queue_clear(&sc->sc_txq[i]);
+	}
 
 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
 	sc->sc_link_active = false;
@@ -1289,8 +1417,9 @@ vioif_watchdog(struct ifnet *ifp)
 	int i;
 
 	if (ifp->if_flags & IFF_RUNNING) {
-		for (i = 0; i < sc->sc_act_nvq_pairs; i++)
-			vioif_tx_intr(sc->sc_txq[i].txq_vq);
+		for (i = 0; i < sc->sc_act_nvq_pairs; i++) {
+			vioif_tx_queue_clear(&sc->sc_txq[i]);
+		}
 	}
 }
 
@@ -1396,41 +1525,54 @@ vioif_populate_rx_mbufs_locked(struct vi
 		virtio_enqueue_commit(vsc, vq, -1, true);
 }
 
-/* dequeue received packets */
-static int
-vioif_rx_deq(struct vioif_rxqueue *rxq)
+static void
+vioif_rx_queue_clear(struct vioif_rxqueue *rxq)
 {
-	int r;
+	struct virtqueue *vq = rxq->rxq_vq;
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = device_private(virtio_child(vsc));
+	u_int limit = UINT_MAX;
+	bool more;
 
 	KASSERT(rxq->rxq_stopping);
 
 	mutex_enter(rxq->rxq_lock);
-	r = vioif_rx_deq_locked(rxq);
+	for (;;) {
+		more = vioif_rx_deq_locked(sc, vsc, rxq, limit);
+		if (more == false)
+			break;
+	}
 	mutex_exit(rxq->rxq_lock);
-
-	return r;
 }
 
 /* dequeue received packets */
-static int
-vioif_rx_deq_locked(struct vioif_rxqueue *rxq)
+static bool
+vioif_rx_deq_locked(struct vioif_softc *sc, struct virtio_softc *vsc,
+    struct vioif_rxqueue *rxq, u_int limit)
 {
 	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;
+	bool more = false, dequeued = false;
 
 	KASSERT(mutex_owned(rxq->rxq_lock));
 
 	if (virtio_vq_is_enqueued(vsc, vq) == false)
-		return r;
+		return false;
+
+	for (;;) {
+		if (limit-- == 0) {
+			more = true;
+			break;
+		}
+
+		if (virtio_dequeue(vsc, vq, &slot, &len) != 0)
+			break;
+
+		dequeued = true;
 
-	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
 		len -= sizeof(struct virtio_net_hdr);
-		r = 1;
 		bus_dmamap_sync(virtio_dmat(vsc), rxq->rxq_hdr_dmamaps[slot],
 		    0, sizeof(struct virtio_net_hdr), BUS_DMASYNC_POSTREAD);
 		bus_dmamap_sync(virtio_dmat(vsc), rxq->rxq_dmamaps[slot],
@@ -1451,7 +1593,10 @@ vioif_rx_deq_locked(struct vioif_rxqueue
 			break;
 	}
 
-	return r;
+	if (dequeued)
+		softint_schedule(rxq->rxq_softint);
+
+	return more;
 }
 
 /* rx interrupt; call _dequeue above and schedule a softint */
@@ -1459,20 +1604,73 @@ static int
 vioif_rx_intr(void *arg)
 {
 	struct vioif_rxqueue *rxq = arg;
-	int r = 0;
+	struct virtqueue *vq = rxq->rxq_vq;
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = device_private(virtio_child(vsc));
+	u_int limit;
+	bool more;
+
+	limit = sc->sc_rx_intr_process_limit;
+
+	if (atomic_load_relaxed(&rxq->rxq_active) == true)
+		return 1;
 
 	mutex_enter(rxq->rxq_lock);
 
-	if (rxq->rxq_stopping)
-		goto out;
+	if (!rxq->rxq_stopping) {
+		rxq->rxq_workqueue = sc->sc_txrx_workqueue_sysctl;
 
-	r = vioif_rx_deq_locked(rxq);
-	if (r)
-		softint_schedule(rxq->rxq_softint);
+		virtio_stop_vq_intr(vsc, vq);
+		atomic_store_relaxed(&rxq->rxq_active, true);
+
+		more = vioif_rx_deq_locked(sc, vsc, rxq, limit);
+		if (more) {
+			vioif_rx_sched_handle(sc, rxq);
+		} else {
+			atomic_store_relaxed(&rxq->rxq_active, false);
+			virtio_start_vq_intr(vsc, vq);
+		}
+	}
 
-out:
 	mutex_exit(rxq->rxq_lock);
-	return r;
+	return 1;
+}
+
+static void
+vioif_rx_handle(void *xrxq)
+{
+	struct vioif_rxqueue *rxq = xrxq;
+	struct virtqueue *vq = rxq->rxq_vq;
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct vioif_softc *sc = device_private(virtio_child(vsc));
+	u_int limit;
+	bool more;
+
+	limit = sc->sc_rx_process_limit;
+
+	mutex_enter(rxq->rxq_lock);
+
+	if (!rxq->rxq_stopping) {
+		more = vioif_rx_deq_locked(sc, vsc, rxq, limit);
+		if (more) {
+			vioif_rx_sched_handle(sc, rxq);
+		} else {
+			atomic_store_relaxed(&rxq->rxq_active, false);
+			virtio_start_vq_intr(vsc, rxq->rxq_vq);
+		}
+	}
+
+	mutex_exit(rxq->rxq_lock);
+}
+
+static void
+vioif_rx_sched_handle(struct vioif_softc *sc, struct vioif_rxqueue *rxq)
+{
+
+	if (rxq->rxq_workqueue)
+		vioif_work_add(sc->sc_txrx_workqueue, &rxq->rxq_work);
+	else
+		softint_schedule(rxq->rxq_handle_si);
 }
 
 /* softint: enqueue receive requests for new incoming packets */
@@ -1507,6 +1705,7 @@ vioif_rx_drain(struct vioif_rxqueue *rxq
  * tx interrupt is actually disabled; this should be called upon
  * tx vq full and watchdog
  */
+
 static int
 vioif_tx_intr(void *arg)
 {
@@ -1515,44 +1714,132 @@ vioif_tx_intr(void *arg)
 	struct virtio_softc *vsc = vq->vq_owner;
 	struct vioif_softc *sc = device_private(virtio_child(vsc));
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
-	int r = 0;
+	bool more;
+	u_int limit;
+
+	limit = sc->sc_tx_intr_process_limit;
+
+	if (atomic_load_relaxed(&txq->txq_active) == true)
+		return 1;
 
 	mutex_enter(txq->txq_lock);
 
-	if (txq->txq_stopping)
-		goto out;
+	if (!txq->txq_stopping) {
+		txq->txq_workqueue = sc->sc_txrx_workqueue_sysctl;
 
-	r = vioif_tx_deq_locked(vq);
+		virtio_stop_vq_intr(vsc, vq);
+		atomic_store_relaxed(&txq->txq_active, true);
+
+		more = vioif_tx_deq_locked(sc, vsc, txq, limit);
+		if (more) {
+			vioif_tx_sched_handle(sc, txq);
+		} else {
+			atomic_store_relaxed(&txq->txq_active, false);
+
+			/* for ALTQ */
+			if (txq == &sc->sc_txq[0]) {
+				if_schedule_deferred_start(ifp);
+				ifp->if_flags &= ~IFF_OACTIVE;
+			}
+			softint_schedule(txq->txq_deferred_transmit);
+
+			virtio_start_vq_intr(vsc, vq);
+		}
+	}
 
-out:
 	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 1;
+}
+
+static void
+vioif_tx_handle(void *xtxq)
+{
+	struct vioif_txqueue *txq = xtxq;
+	struct virtqueue *vq = txq->txq_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;
+	u_int limit;
+	bool more;
+
+	limit = sc->sc_tx_process_limit;
+
+	mutex_enter(txq->txq_lock);
+
+	if (!txq->txq_stopping) {
+		more = vioif_tx_deq_locked(sc, vsc, txq, limit);
+		if (more) {
+			vioif_tx_sched_handle(sc, txq);
+		} else {
+			atomic_store_relaxed(&txq->txq_active, false);
+
+			/* for ALTQ */
+			if (txq == &sc->sc_txq[0]) {
+				if_schedule_deferred_start(ifp);
+				ifp->if_flags &= ~IFF_OACTIVE;
+			}
+			softint_schedule(txq->txq_deferred_transmit);
+
+			virtio_start_vq_intr(vsc, txq->txq_vq);
+		}
 	}
-	return r;
+
+	mutex_exit(txq->txq_lock);
 }
 
-static int
-vioif_tx_deq_locked(struct virtqueue *vq)
+static void
+vioif_tx_sched_handle(struct vioif_softc *sc, struct vioif_txqueue *txq)
 {
+
+	if (txq->txq_workqueue)
+		vioif_work_add(sc->sc_txrx_workqueue, &txq->txq_work);
+	else
+		softint_schedule(txq->txq_handle_si);
+}
+
+static void
+vioif_tx_queue_clear(struct vioif_txqueue *txq)
+{
+	struct virtqueue *vq = txq->txq_vq;
 	struct virtio_softc *vsc = vq->vq_owner;
 	struct vioif_softc *sc = device_private(virtio_child(vsc));
-	struct vioif_txqueue *txq = vq->vq_intrhand_arg;
+	u_int limit = UINT_MAX;
+	bool more;
+
+	mutex_enter(txq->txq_lock);
+	for (;;) {
+		more = vioif_tx_deq_locked(sc, vsc, txq, limit);
+		if (more == false)
+			break;
+	}
+	mutex_exit(txq->txq_lock);
+}
+
+static bool
+vioif_tx_deq_locked(struct vioif_softc *sc, struct virtio_softc *vsc,
+    struct vioif_txqueue *txq, u_int limit)
+{
+	struct virtqueue *vq = txq->txq_vq;
 	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
 	struct mbuf *m;
-	int r = 0;
 	int slot, len;
+	bool more = false;
 
 	KASSERT(mutex_owned(txq->txq_lock));
 
 	if (virtio_vq_is_enqueued(vsc, vq) == false)
-		return r;
+		return false;
+
+	for (;;) {
+		if (limit-- == 0) {
+			more = true;
+			break;
+		}
+
+		if (virtio_dequeue(vsc, vq, &slot, &len) != 0)
+			break;
 
-	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
-		r++;
 		bus_dmamap_sync(virtio_dmat(vsc), txq->txq_hdr_dmamaps[slot],
 		    0, sizeof(struct virtio_net_hdr), BUS_DMASYNC_POSTWRITE);
 		bus_dmamap_sync(virtio_dmat(vsc), txq->txq_dmamaps[slot],
@@ -1566,9 +1853,7 @@ vioif_tx_deq_locked(struct virtqueue *vq
 		m_freem(m);
 	}
 
-	if (r)
-		ifp->if_flags &= ~IFF_OACTIVE;
-	return r;
+	return more;
 }
 
 /* free all the mbufs already put on vq; called from if_stop(disable) */
@@ -2019,6 +2304,140 @@ vioif_ctl_softint(void *arg)
 	vioif_start(ifp);
 }
 
+static struct workqueue *
+vioif_workq_create(const char *name, pri_t prio, int ipl, int flags)
+{
+	struct workqueue *wq;
+	int error;
+
+	error = workqueue_create(&wq, name, vioif_workq_work, NULL,
+	    prio, ipl, flags);
+
+	if (error)
+		return NULL;
+
+	return wq;
+}
+
+static void
+vioif_workq_destroy(struct workqueue *wq)
+{
+
+	workqueue_destroy(wq);
+}
+
+static void
+vioif_workq_work(struct work *wk, void *context)
+{
+	struct vioif_work *work;
+
+	work = container_of(wk, struct vioif_work, cookie);
+
+	atomic_store_relaxed(&work->added, 0);
+	work->func(work->arg);
+}
+
+static void
+vioif_work_set(struct vioif_work *work, void (*func)(void *), void *arg)
+{
+
+	memset(work, 0, sizeof(*work));
+	work->func = func;
+	work->arg = arg;
+}
+
+static void
+vioif_work_add(struct workqueue *wq, struct vioif_work *work)
+{
+
+	if (atomic_load_relaxed(&work->added) != 0)
+		return;
+
+	atomic_store_relaxed(&work->added, 1);
+	kpreempt_disable();
+	workqueue_enqueue(wq, &work->cookie, NULL);
+	kpreempt_enable();
+}
+
+static void
+vioif_work_wait(struct workqueue *wq, struct vioif_work *work)
+{
+
+	workqueue_wait(wq, &work->cookie);
+}
+
+static int
+vioif_setup_sysctl(struct vioif_softc *sc)
+{
+	const char *devname;
+	struct sysctllog **log;
+	const struct sysctlnode *rnode, *rxnode, *txnode;
+	int error;
+
+	log = &sc->sc_sysctllog;
+	devname = device_xname(sc->sc_dev);
+
+	error = sysctl_createv(log, 0, NULL, &rnode,
+	    0, CTLTYPE_NODE, devname,
+	    SYSCTL_DESCR("virtio-net information and settings"),
+	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &rnode, NULL,
+	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "txrx_workqueue",
+	    SYSCTL_DESCR("Use workqueue for packet processing"),
+	    NULL, 0, &sc->sc_txrx_workqueue_sysctl, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &rnode, &rxnode,
+	    0, CTLTYPE_NODE, "rx",
+	    SYSCTL_DESCR("virtio-net information and settings for Rx"),
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &rxnode, NULL,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "intr_process_limit",
+	    SYSCTL_DESCR("max number of Rx packets to process for interrupt processing"),
+	    NULL, 0, &sc->sc_rx_intr_process_limit, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &rxnode, NULL,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "process_limit",
+	    SYSCTL_DESCR("max number of Rx packets to process for deferred processing"),
+	    NULL, 0, &sc->sc_rx_process_limit, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &rnode, &txnode,
+	    0, CTLTYPE_NODE, "tx",
+	    SYSCTL_DESCR("virtio-net information and settings for Tx"),
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &txnode, NULL,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "intr_process_limit",
+	    SYSCTL_DESCR("max number of Tx packets to process for interrupt processing"),
+	    NULL, 0, &sc->sc_tx_intr_process_limit, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto out;
+
+	error = sysctl_createv(log, 0, &txnode, NULL,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "process_limit",
+	    SYSCTL_DESCR("max number of Tx packets to process for deferred processing"),
+	    NULL, 0, &sc->sc_tx_process_limit, 0, CTL_CREATE, CTL_EOL);
+
+out:
+	if (error)
+		sysctl_teardown(log);
+
+	return error;
+}
+
 MODULE(MODULE_CLASS_DRIVER, if_vioif, "virtio");
 
 #ifdef _MODULE

Reply via email to