Author: sephe
Date: Thu Jan  5 06:25:16 2017
New Revision: 311375
URL: https://svnweb.freebsd.org/changeset/base/311375

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/11/sys/dev/hyperv/include/vmbus.h
  stable/11/sys/dev/hyperv/netvsc/if_hn.c
  stable/11/sys/dev/hyperv/netvsc/if_hnvar.h
  stable/11/sys/dev/hyperv/vmbus/vmbus_chan.c
  stable/11/sys/dev/hyperv/vmbus/vmbus_chanvar.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- stable/11/sys/dev/hyperv/include/vmbus.h    Thu Jan  5 06:21:14 2017        
(r311374)
+++ stable/11/sys/dev/hyperv/include/vmbus.h    Thu Jan  5 06:25:16 2017        
(r311375)
@@ -49,6 +49,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.
  */
@@ -220,4 +223,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/11/sys/dev/hyperv/netvsc/if_hn.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hn.c     Thu Jan  5 06:21:14 2017        
(r311374)
+++ stable/11/sys/dev/hyperv/netvsc/if_hn.c     Thu Jan  5 06:25:16 2017        
(r311375)
@@ -293,6 +293,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 *);
@@ -319,6 +320,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 *);
@@ -1117,6 +1120,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.
@@ -2360,6 +2367,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.
@@ -2405,6 +2415,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;
 
@@ -2531,6 +2548,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);
@@ -2568,6 +2588,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
@@ -2875,6 +2899,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/11/sys/dev/hyperv/netvsc/if_hnvar.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hnvar.h  Thu Jan  5 06:21:14 2017        
(r311374)
+++ stable/11/sys/dev/hyperv/netvsc/if_hnvar.h  Thu Jan  5 06:25:16 2017        
(r311375)
@@ -212,6 +212,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/11/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- stable/11/sys/dev/hyperv/vmbus/vmbus_chan.c Thu Jan  5 06:21:14 2017        
(r311374)
+++ stable/11/sys/dev/hyperv/vmbus/vmbus_chan.c Thu Jan  5 06:25:16 2017        
(r311375)
@@ -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/11/sys/dev/hyperv/vmbus/vmbus_chanvar.h
==============================================================================
--- stable/11/sys/dev/hyperv/vmbus/vmbus_chanvar.h      Thu Jan  5 06:21:14 
2017        (r311374)
+++ stable/11/sys/dev/hyperv/vmbus/vmbus_chanvar.h      Thu Jan  5 06:25:16 
2017        (r311375)
@@ -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;
 
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to