hyperv/vmbus: Add channel polling support.

MFC after:	1 week
Sponsored by:	Microsoft
Differential Revision:	https://reviews.freebsd.org/D8738
This commit is contained in:
sephe 2016-12-12 05:04:55 +00:00
parent b4359fc5ce
commit 39150ad6fb
3 changed files with 220 additions and 0 deletions

View File

@ -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 vmbus_channel *chan);
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_ */

View File

@ -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(struct vmbus_channel *);
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(struct vmbus_channel *,
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_channel *chan)
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)
{
@ -817,6 +847,11 @@ vmbus_chan_close_internal(struct vmbus_channel *chan)
*/
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,
@ -1185,6 +1220,9 @@ vmbus_chan_task(void *xchan, int pending __unused)
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 pending __unused)
{
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 *chan)
("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_channel *chan,
}
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);
}

View File

@ -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;