svn commit: r310802 - in stable/10/sys/dev/hyperv: include netvsc vmbus

Sepherosa Ziehau sephe at FreeBSD.org
Fri Dec 30 02:18:36 UTC 2016


Author: sephe
Date: Fri Dec 30 02:18:34 2016
New Revision: 310802
URL: https://svnweb.freebsd.org/changeset/base/310802

Log:
  MFC 309874,309875
  
  309874
      hyperv/vmbus: Add channel polling support.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8738
  
  309875
      hyperv/hn: Add polling support
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D8739

Modified:
  stable/10/sys/dev/hyperv/include/vmbus.h
  stable/10/sys/dev/hyperv/netvsc/if_hn.c
  stable/10/sys/dev/hyperv/netvsc/if_hnvar.h
  stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
  stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- stable/10/sys/dev/hyperv/include/vmbus.h	Fri Dec 30 02:13:21 2016	(r310801)
+++ stable/10/sys/dev/hyperv/include/vmbus.h	Fri Dec 30 02:18:34 2016	(r310802)
@@ -50,6 +50,9 @@
 #define VMBUS_VERSION_MAJOR(ver)	(((uint32_t)(ver)) >> 16)
 #define VMBUS_VERSION_MINOR(ver)	(((uint32_t)(ver)) & 0xffff)
 
+#define VMBUS_CHAN_POLLHZ_MIN		100	/* 10ms interval */
+#define VMBUS_CHAN_POLLHZ_MAX		1000000	/* 1us interval */
+
 /*
  * GPA stuffs.
  */
@@ -221,4 +224,8 @@ bool		vmbus_chan_tx_empty(const struct v
 struct taskqueue *
 		vmbus_chan_mgmt_tq(const struct vmbus_channel *chan);
 
+void		vmbus_chan_poll_enable(struct vmbus_channel *chan,
+		    u_int pollhz);
+void		vmbus_chan_poll_disable(struct vmbus_channel *chan);
+
 #endif	/* !_VMBUS_H_ */

Modified: stable/10/sys/dev/hyperv/netvsc/if_hn.c
==============================================================================
--- stable/10/sys/dev/hyperv/netvsc/if_hn.c	Fri Dec 30 02:13:21 2016	(r310801)
+++ stable/10/sys/dev/hyperv/netvsc/if_hn.c	Fri Dec 30 02:18:34 2016	(r310802)
@@ -286,6 +286,7 @@ static int			hn_txagg_size_sysctl(SYSCTL
 static int			hn_txagg_pkts_sysctl(SYSCTL_HANDLER_ARGS);
 static int			hn_txagg_pktmax_sysctl(SYSCTL_HANDLER_ARGS);
 static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
+static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
 
 static void			hn_stop(struct hn_softc *);
 static void			hn_init_locked(struct hn_softc *);
@@ -312,6 +313,8 @@ static void			hn_resume_mgmt(struct hn_s
 static void			hn_suspend_mgmt_taskfunc(void *, int);
 static void			hn_chan_drain(struct hn_softc *,
 				    struct vmbus_channel *);
+static void			hn_polling(struct hn_softc *, u_int);
+static void			hn_chan_polling(struct vmbus_channel *, u_int);
 
 static void			hn_update_link_status(struct hn_softc *);
 static void			hn_change_network(struct hn_softc *);
@@ -1095,6 +1098,10 @@ hn_attach(device_t dev)
 	    hn_txagg_pkts_sysctl, "I",
 	    "Packet transmission aggregation packets, "
 	    "0 -- disable, -1 -- auto");
+	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "polling",
+	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
+	    hn_polling_sysctl, "I",
+	    "Polling frequency: [100,1000000], 0 disable polling");
 
 	/*
 	 * Setup the ifmedia, which has been initialized earlier.
@@ -2347,6 +2354,9 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 			break;
 		}
 
+		/* Disable polling. */
+		hn_polling(sc, 0);
+
 		/*
 		 * Suspend this interface before the synthetic parts
 		 * are ripped.
@@ -2392,6 +2402,13 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 		 */
 		hn_resume(sc);
 
+		/*
+		 * Re-enable polling if this interface is running and
+		 * the polling is requested.
+		 */
+		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->hn_pollhz > 0)
+			hn_polling(sc, sc->hn_pollhz);
+
 		HN_UNLOCK(sc);
 		break;
 
@@ -2518,6 +2535,9 @@ hn_stop(struct hn_softc *sc)
 	KASSERT(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED,
 	    ("synthetic parts were not attached"));
 
+	/* Disable polling. */
+	hn_polling(sc, 0);
+
 	/* Clear RUNNING bit _before_ hn_suspend_data() */
 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
 	hn_suspend_data(sc);
@@ -2555,6 +2575,10 @@ hn_init_locked(struct hn_softc *sc)
 
 	/* Everything is ready; unleash! */
 	atomic_set_int(&ifp->if_drv_flags, IFF_DRV_RUNNING);
+
+	/* Re-enable polling if requested. */
+	if (sc->hn_pollhz > 0)
+		hn_polling(sc, sc->hn_pollhz);
 }
 
 static void
@@ -2862,6 +2886,61 @@ hn_txagg_align_sysctl(SYSCTL_HANDLER_ARG
 	return (sysctl_handle_int(oidp, &align, 0, req));
 }
 
+static void
+hn_chan_polling(struct vmbus_channel *chan, u_int pollhz)
+{
+	if (pollhz == 0)
+		vmbus_chan_poll_disable(chan);
+	else
+		vmbus_chan_poll_enable(chan, pollhz);
+}
+
+static void
+hn_polling(struct hn_softc *sc, u_int pollhz)
+{
+	int nsubch = sc->hn_rx_ring_inuse - 1;
+
+	HN_LOCK_ASSERT(sc);
+
+	if (nsubch > 0) {
+		struct vmbus_channel **subch;
+		int i;
+
+		subch = vmbus_subchan_get(sc->hn_prichan, nsubch);
+		for (i = 0; i < nsubch; ++i)
+			hn_chan_polling(subch[i], pollhz);
+		vmbus_subchan_rel(subch, nsubch);
+	}
+	hn_chan_polling(sc->hn_prichan, pollhz);
+}
+
+static int
+hn_polling_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	struct hn_softc *sc = arg1;
+	int pollhz, error;
+
+	pollhz = sc->hn_pollhz;
+	error = sysctl_handle_int(oidp, &pollhz, 0, req);
+	if (error || req->newptr == NULL)
+		return (error);
+
+	if (pollhz != 0 &&
+	    (pollhz < VMBUS_CHAN_POLLHZ_MIN || pollhz > VMBUS_CHAN_POLLHZ_MAX))
+		return (EINVAL);
+
+	HN_LOCK(sc);
+	if (sc->hn_pollhz != pollhz) {
+		sc->hn_pollhz = pollhz;
+		if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+		    (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
+			hn_polling(sc, sc->hn_pollhz);
+	}
+	HN_UNLOCK(sc);
+
+	return (0);
+}
+
 static int
 hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS)
 {

Modified: stable/10/sys/dev/hyperv/netvsc/if_hnvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/netvsc/if_hnvar.h	Fri Dec 30 02:13:21 2016	(r310801)
+++ stable/10/sys/dev/hyperv/netvsc/if_hnvar.h	Fri Dec 30 02:18:34 2016	(r310802)
@@ -213,6 +213,8 @@ struct hn_softc {
 
 	uint32_t		hn_caps;	/* HN_CAP_ */
 	uint32_t		hn_flags;	/* HN_FLAG_ */
+	u_int			hn_pollhz;
+
 	void			*hn_rxbuf;
 	uint32_t		hn_rxbuf_gpadl;
 	struct hyperv_dma	hn_rxbuf_dma;

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Fri Dec 30 02:13:21 2016	(r310801)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chan.c	Fri Dec 30 02:18:34 2016	(r310802)
@@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/bus.h>
+#include <sys/callout.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
@@ -50,6 +51,11 @@ __FBSDID("$FreeBSD$");
 #include <dev/hyperv/vmbus/vmbus_brvar.h>
 #include <dev/hyperv/vmbus/vmbus_chanvar.h>
 
+struct vmbus_chan_pollarg {
+	struct vmbus_channel	*poll_chan;
+	u_int			poll_hz;
+};
+
 static void			vmbus_chan_update_evtflagcnt(
 				    struct vmbus_softc *,
 				    const struct vmbus_channel *);
@@ -68,6 +74,10 @@ static void			vmbus_chan_clear_chmap(str
 static void			vmbus_chan_detach(struct vmbus_channel *);
 static bool			vmbus_chan_wait_revoke(
 				    const struct vmbus_channel *, bool);
+static void			vmbus_chan_poll_timeout(void *);
+static bool			vmbus_chan_poll_cancel_intq(
+				    struct vmbus_channel *);
+static void			vmbus_chan_poll_cancel(struct vmbus_channel *);
 
 static void			vmbus_chan_ins_prilist(struct vmbus_softc *,
 				    struct vmbus_channel *);
@@ -84,7 +94,11 @@ static void			vmbus_chan_rem_sublist(str
 
 static void			vmbus_chan_task(void *, int);
 static void			vmbus_chan_task_nobatch(void *, int);
+static void			vmbus_chan_poll_task(void *, int);
 static void			vmbus_chan_clrchmap_task(void *, int);
+static void			vmbus_chan_pollcfg_task(void *, int);
+static void			vmbus_chan_polldis_task(void *, int);
+static void			vmbus_chan_poll_cancel_task(void *, int);
 static void			vmbus_prichan_attach_task(void *, int);
 static void			vmbus_subchan_attach_task(void *, int);
 static void			vmbus_prichan_detach_task(void *, int);
@@ -782,6 +796,22 @@ vmbus_chan_set_chmap(struct vmbus_channe
 	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = chan;
 }
 
+static void
+vmbus_chan_poll_cancel_task(void *xchan, int pending __unused)
+{
+
+	vmbus_chan_poll_cancel_intq(xchan);
+}
+
+static void
+vmbus_chan_poll_cancel(struct vmbus_channel *chan)
+{
+	struct task poll_cancel;
+
+	TASK_INIT(&poll_cancel, 0, vmbus_chan_poll_cancel_task, chan);
+	vmbus_chan_run_task(chan, &poll_cancel);
+}
+
 static int
 vmbus_chan_close_internal(struct vmbus_channel *chan)
 {
@@ -818,6 +848,11 @@ vmbus_chan_close_internal(struct vmbus_c
 	sysctl_ctx_free(&chan->ch_sysctl_ctx);
 
 	/*
+	 * Cancel polling, if it is enabled.
+	 */
+	vmbus_chan_poll_cancel(chan);
+
+	/*
 	 * NOTE:
 	 * Order is critical.  This channel _must_ be uninstalled first,
 	 * else the channel task may be enqueued by the IDT after it has
@@ -1185,6 +1220,9 @@ vmbus_chan_task(void *xchan, int pending
 	vmbus_chan_callback_t cb = chan->ch_cb;
 	void *cbarg = chan->ch_cbarg;
 
+	KASSERT(chan->ch_poll_intvl == 0,
+	    ("chan%u: interrupted in polling mode", chan->ch_id));
+
 	/*
 	 * Optimize host to guest signaling by ensuring:
 	 * 1. While reading the channel, we disable interrupts from
@@ -1216,9 +1254,145 @@ vmbus_chan_task_nobatch(void *xchan, int
 {
 	struct vmbus_channel *chan = xchan;
 
+	KASSERT(chan->ch_poll_intvl == 0,
+	    ("chan%u: interrupted in polling mode", chan->ch_id));
+	chan->ch_cb(chan, chan->ch_cbarg);
+}
+
+static void
+vmbus_chan_poll_timeout(void *xchan)
+{
+	struct vmbus_channel *chan = xchan;
+
+	KASSERT(chan->ch_poll_intvl != 0,
+	    ("chan%u: polling timeout in interrupt mode", chan->ch_id));
+	taskqueue_enqueue(chan->ch_tq, &chan->ch_poll_task);
+}
+
+static void
+vmbus_chan_poll_task(void *xchan, int pending __unused)
+{
+	struct vmbus_channel *chan = xchan;
+
+	KASSERT(chan->ch_poll_intvl != 0,
+	    ("chan%u: polling in interrupt mode", chan->ch_id));
+	callout_reset_sbt_curcpu(&chan->ch_poll_timeo, chan->ch_poll_intvl, 0,
+	    vmbus_chan_poll_timeout, chan, chan->ch_poll_flags);
 	chan->ch_cb(chan, chan->ch_cbarg);
 }
 
+static void
+vmbus_chan_pollcfg_task(void *xarg, int pending __unused)
+{
+	const struct vmbus_chan_pollarg *arg = xarg;
+	struct vmbus_channel *chan = arg->poll_chan;
+	sbintime_t intvl;
+	int poll_flags;
+
+	/*
+	 * Save polling interval.
+	 */
+	intvl = SBT_1S / arg->poll_hz;
+	if (intvl == 0)
+		intvl = 1;
+	if (intvl == chan->ch_poll_intvl) {
+		/* Nothing changes; done */
+		return;
+	}
+	chan->ch_poll_intvl = intvl;
+
+	/* Adjust callout flags. */
+	poll_flags = C_DIRECT_EXEC;
+	if (arg->poll_hz <= hz)
+		poll_flags |= C_HARDCLOCK;
+	chan->ch_poll_flags = poll_flags;
+
+	/*
+	 * Disable interrupt from the RX bufring (TX bufring does not
+	 * generate interrupt to VM), and disconnect this channel from
+	 * the channel map to make sure that ISR can not enqueue this
+	 * channel task anymore.
+	 */
+	critical_enter();
+	vmbus_rxbr_intr_mask(&chan->ch_rxbr);
+	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = NULL;
+	critical_exit();
+
+	/*
+	 * NOTE:
+	 * At this point, this channel task will not be enqueued by
+	 * the ISR anymore, time to cancel the pending one.
+	 */
+	taskqueue_cancel(chan->ch_tq, &chan->ch_task, NULL);
+
+	/* Kick start! */
+	taskqueue_enqueue(chan->ch_tq, &chan->ch_poll_task);
+}
+
+static bool
+vmbus_chan_poll_cancel_intq(struct vmbus_channel *chan)
+{
+
+	if (chan->ch_poll_intvl == 0) {
+		/* Not enabled. */
+		return (false);
+	}
+
+	/*
+	 * Stop polling callout, so that channel polling task
+	 * will not be enqueued anymore.
+	 */
+	callout_drain(&chan->ch_poll_timeo);
+
+	/*
+	 * Disable polling by resetting polling interval.
+	 *
+	 * NOTE:
+	 * The polling interval resetting MUST be conducted
+	 * after the callout is drained; mainly to keep the
+	 * proper assertion in place.
+	 */
+	chan->ch_poll_intvl = 0;
+
+	/*
+	 * NOTE:
+	 * At this point, this channel polling task will not be
+	 * enqueued by the callout anymore, time to cancel the
+	 * pending one.
+	 */
+	taskqueue_cancel(chan->ch_tq, &chan->ch_poll_task, NULL);
+
+	/* Polling was enabled. */
+	return (true);
+}
+
+static void
+vmbus_chan_polldis_task(void *xchan, int pending __unused)
+{
+	struct vmbus_channel *chan = xchan;
+
+	if (!vmbus_chan_poll_cancel_intq(chan)) {
+		/* Already disabled; done. */
+		return;
+	}
+
+	/*
+	 * Plug this channel back to the channel map and unmask
+	 * the RX bufring interrupt.
+	 */
+	critical_enter();
+	chan->ch_vmbus->vmbus_chmap[chan->ch_id] = chan;
+	__compiler_membar();
+	vmbus_rxbr_intr_unmask(&chan->ch_rxbr);
+	critical_exit();
+
+	/*
+	 * Kick start the interrupt task, just in case unmasking
+	 * interrupt races ISR.
+	 */
+	taskqueue_enqueue(chan->ch_tq, &chan->ch_task);
+}
+
 static __inline void
 vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags,
     int flag_cnt)
@@ -1333,6 +1507,9 @@ vmbus_chan_alloc(struct vmbus_softc *sc)
 	vmbus_rxbr_init(&chan->ch_rxbr);
 	vmbus_txbr_init(&chan->ch_txbr);
 
+	TASK_INIT(&chan->ch_poll_task, 0, vmbus_chan_poll_task, chan);
+	callout_init(&chan->ch_poll_timeo, 1);
+
 	return chan;
 }
 
@@ -1351,6 +1528,8 @@ vmbus_chan_free(struct vmbus_channel *ch
 	    ("still has orphan xact installed"));
 	KASSERT(chan->ch_refs == 0, ("chan%u: invalid refcnt %d",
 	    chan->ch_id, chan->ch_refs));
+	KASSERT(chan->ch_poll_intvl == 0, ("chan%u: polling is activated",
+	    chan->ch_id));
 
 	hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
 	mtx_destroy(&chan->ch_subchan_lock);
@@ -1998,3 +2177,32 @@ vmbus_chan_xact_wait(const struct vmbus_
 	}
 	return (ret);
 }
+
+void
+vmbus_chan_poll_enable(struct vmbus_channel *chan, u_int pollhz)
+{
+	struct vmbus_chan_pollarg arg;
+	struct task poll_cfg;
+
+	KASSERT(chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD,
+	    ("enable polling on non-batch chan%u", chan->ch_id));
+	KASSERT(pollhz >= VMBUS_CHAN_POLLHZ_MIN &&
+	    pollhz <= VMBUS_CHAN_POLLHZ_MAX, ("invalid pollhz %u", pollhz));
+
+	arg.poll_chan = chan;
+	arg.poll_hz = pollhz;
+	TASK_INIT(&poll_cfg, 0, vmbus_chan_pollcfg_task, &arg);
+	vmbus_chan_run_task(chan, &poll_cfg);
+}
+
+void
+vmbus_chan_poll_disable(struct vmbus_channel *chan)
+{
+	struct task poll_dis;
+
+	KASSERT(chan->ch_flags & VMBUS_CHAN_FLAG_BATCHREAD,
+	    ("disable polling on non-batch chan%u", chan->ch_id));
+
+	TASK_INIT(&poll_dis, 0, vmbus_chan_polldis_task, chan);
+	vmbus_chan_run_task(chan, &poll_dis);
+}

Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h
==============================================================================
--- stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h	Fri Dec 30 02:13:21 2016	(r310801)
+++ stable/10/sys/dev/hyperv/vmbus/vmbus_chanvar.h	Fri Dec 30 02:18:34 2016	(r310802)
@@ -30,6 +30,7 @@
 #define _VMBUS_CHANVAR_H_
 
 #include <sys/param.h>
+#include <sys/callout.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
 #include <sys/queue.h>
@@ -49,6 +50,7 @@ struct vmbus_channel {
 	 * target CPU.
 	 */
 	uint32_t			ch_flags;	/* VMBUS_CHAN_FLAG_ */
+	int				ch_poll_flags;	/* callout flags */
 
 	/*
 	 * RX bufring; immediately following ch_txbr.
@@ -57,6 +59,9 @@ struct vmbus_channel {
 
 	struct taskqueue		*ch_tq;
 	struct task			ch_task;
+	struct task			ch_poll_task;
+	sbintime_t			ch_poll_intvl;
+	struct callout			ch_poll_timeo;
 	vmbus_chan_callback_t		ch_cb;
 	void				*ch_cbarg;
 


More information about the svn-src-all mailing list