Author: sephe
Date: Mon Dec 12 05:04:55 2016
New Revision: 309874
URL: https://svnweb.freebsd.org/changeset/base/309874

Log:
  hyperv/vmbus: Add channel polling support.
  
  MFC after:    1 week
  Sponsored by: Microsoft
  Differential Revision:        https://reviews.freebsd.org/D8738

Modified:
  head/sys/dev/hyperv/include/vmbus.h
  head/sys/dev/hyperv/vmbus/vmbus_chan.c
  head/sys/dev/hyperv/vmbus/vmbus_chanvar.h

Modified: head/sys/dev/hyperv/include/vmbus.h
==============================================================================
--- head/sys/dev/hyperv/include/vmbus.h Mon Dec 12 03:46:40 2016        
(r309873)
+++ head/sys/dev/hyperv/include/vmbus.h Mon Dec 12 05:04:55 2016        
(r309874)
@@ -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: head/sys/dev/hyperv/vmbus/vmbus_chan.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/vmbus_chan.c      Mon Dec 12 03:46:40 2016        
(r309873)
+++ head/sys/dev/hyperv/vmbus/vmbus_chan.c      Mon Dec 12 05:04:55 2016        
(r309874)
@@ -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: head/sys/dev/hyperv/vmbus/vmbus_chanvar.h
==============================================================================
--- head/sys/dev/hyperv/vmbus/vmbus_chanvar.h   Mon Dec 12 03:46:40 2016        
(r309873)
+++ head/sys/dev/hyperv/vmbus/vmbus_chanvar.h   Mon Dec 12 05:04:55 2016        
(r309874)
@@ -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-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to