hyperv/vmbus: Merge hv_channel_mgmt.c into hv_channel.c

MFC after:	1 week
Sponsored by:	Microsoft OSTC
Differential Revision:	https://reviews.freebsd.org/D7126
This commit is contained in:
Sepherosa Ziehau 2016-07-15 04:42:08 +00:00
parent c93042724e
commit 7d590c7345
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302864
5 changed files with 480 additions and 521 deletions

View File

@ -271,7 +271,6 @@ dev/hyperv/utilities/hv_shutdown.c optional hyperv
dev/hyperv/utilities/hv_timesync.c optional hyperv
dev/hyperv/utilities/hv_util.c optional hyperv
dev/hyperv/vmbus/hv_channel.c optional hyperv
dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv
dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv
dev/hyperv/vmbus/hyperv.c optional hyperv
dev/hyperv/vmbus/hyperv_busdma.c optional hyperv

View File

@ -247,7 +247,6 @@ dev/hyperv/utilities/hv_shutdown.c optional hyperv
dev/hyperv/utilities/hv_timesync.c optional hyperv
dev/hyperv/utilities/hv_util.c optional hyperv
dev/hyperv/vmbus/hv_channel.c optional hyperv
dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv
dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv
dev/hyperv/vmbus/hyperv.c optional hyperv
dev/hyperv/vmbus/hyperv_busdma.c optional hyperv

View File

@ -53,8 +53,28 @@ __FBSDID("$FreeBSD$");
static void vmbus_chan_send_event(hv_vmbus_channel* channel);
static void vmbus_chan_update_evtflagcnt(struct vmbus_softc *,
const struct hv_vmbus_channel *);
static void vmbus_chan_task(void *, int);
static void vmbus_chan_task_nobatch(void *, int);
static void vmbus_chan_detach_task(void *, int);
static void vmbus_chan_msgproc_choffer(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
const struct vmbus_message *);
/*
* Vmbus channel message processing.
*/
static const vmbus_chanmsg_proc_t
vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer),
VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind),
VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
};
/**
* @brief Trigger an event notification on the specified channel
@ -984,3 +1004,463 @@ vmbus_chan_update_evtflagcnt(struct vmbus_softc *sc,
}
}
}
static struct hv_vmbus_channel *
vmbus_chan_alloc(struct vmbus_softc *sc)
{
struct hv_vmbus_channel *chan;
chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
&chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
if (chan->ch_monprm == NULL) {
device_printf(sc->vmbus_dev, "monprm alloc failed\n");
free(chan, M_DEVBUF);
return NULL;
}
chan->vmbus_sc = sc;
mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
TAILQ_INIT(&chan->ch_subchans);
TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
return chan;
}
static void
vmbus_chan_free(struct hv_vmbus_channel *chan)
{
/* TODO: assert sub-channel list is empty */
/* TODO: asset no longer on the primary channel's sub-channel list */
/* TODO: asset no longer on the vmbus channel list */
hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
mtx_destroy(&chan->ch_subchan_lock);
free(chan, M_DEVBUF);
}
static int
vmbus_chan_add(struct hv_vmbus_channel *newchan)
{
struct vmbus_softc *sc = newchan->vmbus_sc;
struct hv_vmbus_channel *prichan;
if (newchan->ch_id == 0) {
/*
* XXX
* Chan0 will neither be processed nor should be offered;
* skip it.
*/
device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
return EINVAL;
} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
newchan->ch_id);
return EINVAL;
}
sc->vmbus_chmap[newchan->ch_id] = newchan;
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
newchan->ch_id, newchan->ch_subidx);
}
mtx_lock(&sc->vmbus_prichan_lock);
TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
/*
* Sub-channel will have the same type GUID and instance
* GUID as its primary channel.
*/
if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
sizeof(struct hyperv_guid)) == 0 &&
memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
sizeof(struct hyperv_guid)) == 0)
break;
}
if (VMBUS_CHAN_ISPRIMARY(newchan)) {
if (prichan == NULL) {
/* Install the new primary channel */
TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
ch_prilink);
mtx_unlock(&sc->vmbus_prichan_lock);
return 0;
} else {
mtx_unlock(&sc->vmbus_prichan_lock);
device_printf(sc->vmbus_dev, "duplicated primary "
"chan%u\n", newchan->ch_id);
return EINVAL;
}
} else { /* Sub-channel */
if (prichan == NULL) {
mtx_unlock(&sc->vmbus_prichan_lock);
device_printf(sc->vmbus_dev, "no primary chan for "
"chan%u\n", newchan->ch_id);
return EINVAL;
}
/*
* Found the primary channel for this sub-channel and
* move on.
*
* XXX refcnt prichan
*/
}
mtx_unlock(&sc->vmbus_prichan_lock);
/*
* This is a sub-channel; link it with the primary channel.
*/
KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
("new channel is not sub-channel"));
KASSERT(prichan != NULL, ("no primary channel"));
newchan->ch_prichan = prichan;
newchan->ch_dev = prichan->ch_dev;
mtx_lock(&prichan->ch_subchan_lock);
TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
/*
* Bump up sub-channel count and notify anyone that is
* interested in this sub-channel, after this sub-channel
* is setup.
*/
prichan->ch_subchan_cnt++;
mtx_unlock(&prichan->ch_subchan_lock);
wakeup(prichan);
return 0;
}
void
vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
{
KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
/* Only cpu0 is supported */
cpu = 0;
}
chan->target_cpu = cpu;
chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
if (bootverbose) {
printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
chan->ch_id,
chan->target_cpu, chan->target_vcpu);
}
}
void
vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
{
static uint32_t vmbus_chan_nextcpu;
int cpu;
cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
vmbus_channel_cpu_set(chan, cpu);
}
static void
vmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
{
/*
* By default, pin the channel to cpu0. Devices having
* special channel-cpu mapping requirement should call
* vmbus_channel_cpu_{set,rr}().
*/
vmbus_channel_cpu_set(chan, 0);
}
static void
vmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const struct vmbus_chanmsg_choffer *offer;
struct hv_vmbus_channel *chan;
int error;
offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
chan = vmbus_chan_alloc(sc);
if (chan == NULL) {
device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
offer->chm_chanid);
return;
}
chan->ch_id = offer->chm_chanid;
chan->ch_subidx = offer->chm_subidx;
chan->ch_guid_type = offer->chm_chtype;
chan->ch_guid_inst = offer->chm_chinst;
/* Batch reading is on by default */
chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
if (sc->vmbus_version != VMBUS_VERSION_WS2008)
chan->ch_monprm->mp_connid = offer->chm_connid;
if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
/*
* Setup MNF stuffs.
*/
chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
panic("invalid monitor trigger %u", offer->chm_montrig);
chan->ch_montrig_mask =
1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
}
/* Select default cpu for this channel. */
vmbus_chan_cpu_default(chan);
error = vmbus_chan_add(chan);
if (error) {
device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
chan->ch_id, error);
vmbus_chan_free(chan);
return;
}
if (VMBUS_CHAN_ISPRIMARY(chan)) {
/*
* Add device for this primary channel.
*
* NOTE:
* Error is ignored here; don't have much to do if error
* really happens.
*/
hv_vmbus_child_device_register(chan);
}
}
/*
* XXX pretty broken; need rework.
*/
static void
vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const struct vmbus_chanmsg_chrescind *note;
struct hv_vmbus_channel *chan;
note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
if (note->chm_chanid > VMBUS_CHAN_MAX) {
device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
note->chm_chanid);
return;
}
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u rescinded\n",
note->chm_chanid);
}
chan = sc->vmbus_chmap[note->chm_chanid];
if (chan == NULL)
return;
sc->vmbus_chmap[note->chm_chanid] = NULL;
taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
}
static void
vmbus_chan_detach_task(void *xchan, int pending __unused)
{
struct hv_vmbus_channel *chan = xchan;
if (VMBUS_CHAN_ISPRIMARY(chan)) {
/* Only primary channel owns the device */
hv_vmbus_child_device_unregister(chan);
/* NOTE: DO NOT free primary channel for now */
} else {
struct vmbus_softc *sc = chan->vmbus_sc;
struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
struct vmbus_chanmsg_chfree *req;
struct vmbus_msghc *mh;
int error;
mh = vmbus_msghc_get(sc, sizeof(*req));
if (mh == NULL) {
device_printf(sc->vmbus_dev,
"can not get msg hypercall for chfree(chan%u)\n",
chan->ch_id);
goto remove;
}
req = vmbus_msghc_dataptr(mh);
req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
req->chm_chanid = chan->ch_id;
error = vmbus_msghc_exec_noresult(mh);
vmbus_msghc_put(sc, mh);
if (error) {
device_printf(sc->vmbus_dev,
"chfree(chan%u) failed: %d",
chan->ch_id, error);
/* NOTE: Move on! */
} else {
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u freed\n",
chan->ch_id);
}
}
remove:
mtx_lock(&pri_chan->ch_subchan_lock);
TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
KASSERT(pri_chan->ch_subchan_cnt > 0,
("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
pri_chan->ch_subchan_cnt--;
mtx_unlock(&pri_chan->ch_subchan_lock);
wakeup(pri_chan);
vmbus_chan_free(chan);
}
}
/*
* Detach all devices and destroy the corresponding primary channels.
*/
void
vmbus_chan_destroy_all(struct vmbus_softc *sc)
{
struct hv_vmbus_channel *chan;
mtx_lock(&sc->vmbus_prichan_lock);
while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
mtx_unlock(&sc->vmbus_prichan_lock);
hv_vmbus_child_device_unregister(chan);
vmbus_chan_free(chan);
mtx_lock(&sc->vmbus_prichan_lock);
}
bzero(sc->vmbus_chmap,
sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
mtx_unlock(&sc->vmbus_prichan_lock);
}
/**
* @brief Select the best outgoing channel
*
* The channel whose vcpu binding is closest to the currect vcpu will
* be selected.
* If no multi-channel, always select primary channel
*
* @param primary - primary channel
*/
struct hv_vmbus_channel *
vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
{
hv_vmbus_channel *new_channel = NULL;
hv_vmbus_channel *outgoing_channel = primary;
int old_cpu_distance = 0;
int new_cpu_distance = 0;
int cur_vcpu = 0;
int smp_pro_id = PCPU_GET(cpuid);
if (TAILQ_EMPTY(&primary->ch_subchans)) {
return outgoing_channel;
}
if (smp_pro_id >= MAXCPU) {
return outgoing_channel;
}
cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
/* XXX need lock */
TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
continue;
}
if (new_channel->target_vcpu == cur_vcpu){
return new_channel;
}
old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
(outgoing_channel->target_vcpu - cur_vcpu) :
(cur_vcpu - outgoing_channel->target_vcpu));
new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
(new_channel->target_vcpu - cur_vcpu) :
(cur_vcpu - new_channel->target_vcpu));
if (old_cpu_distance < new_cpu_distance) {
continue;
}
outgoing_channel = new_channel;
}
return(outgoing_channel);
}
struct hv_vmbus_channel **
vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
{
struct hv_vmbus_channel **ret, *chan;
int i;
ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
M_WAITOK);
mtx_lock(&pri_chan->ch_subchan_lock);
while (pri_chan->ch_subchan_cnt < subchan_cnt)
mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
i = 0;
TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
/* TODO: refcnt chan */
ret[i] = chan;
++i;
if (i == subchan_cnt)
break;
}
KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
pri_chan->ch_subchan_cnt, subchan_cnt));
mtx_unlock(&pri_chan->ch_subchan_lock);
return ret;
}
void
vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
{
free(subchan, M_TEMP);
}
void
vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
{
mtx_lock(&pri_chan->ch_subchan_lock);
while (pri_chan->ch_subchan_cnt > 0)
mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
mtx_unlock(&pri_chan->ch_subchan_lock);
}
void
vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
{
vmbus_chanmsg_proc_t msg_proc;
uint32_t msg_type;
msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
("invalid message type %u", msg_type));
msg_proc = vmbus_chan_msgprocs[msg_type];
if (msg_proc != NULL)
msg_proc(sc, msg);
}

View File

@ -1,518 +0,0 @@
/*-
* Copyright (c) 2009-2012,2016 Microsoft Corp.
* Copyright (c) 2012 NetApp Inc.
* Copyright (c) 2012 Citrix Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <dev/hyperv/include/hyperv_busdma.h>
#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
#include <dev/hyperv/vmbus/vmbus_reg.h>
#include <dev/hyperv/vmbus/vmbus_var.h>
static void vmbus_chan_detach_task(void *, int);
static void vmbus_chan_msgproc_choffer(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_chan_msgproc_chrescind(struct vmbus_softc *,
const struct vmbus_message *);
/*
* Vmbus channel message processing.
*/
static const vmbus_chanmsg_proc_t
vmbus_chan_msgprocs[VMBUS_CHANMSG_TYPE_MAX] = {
VMBUS_CHANMSG_PROC(CHOFFER, vmbus_chan_msgproc_choffer),
VMBUS_CHANMSG_PROC(CHRESCIND, vmbus_chan_msgproc_chrescind),
VMBUS_CHANMSG_PROC_WAKEUP(CHOPEN_RESP),
VMBUS_CHANMSG_PROC_WAKEUP(GPADL_CONNRESP),
VMBUS_CHANMSG_PROC_WAKEUP(GPADL_DISCONNRESP)
};
static struct hv_vmbus_channel *
vmbus_chan_alloc(struct vmbus_softc *sc)
{
struct hv_vmbus_channel *chan;
chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
chan->ch_monprm = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev),
HYPERCALL_PARAM_ALIGN, 0, sizeof(struct hyperv_mon_param),
&chan->ch_monprm_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
if (chan->ch_monprm == NULL) {
device_printf(sc->vmbus_dev, "monprm alloc failed\n");
free(chan, M_DEVBUF);
return NULL;
}
chan->vmbus_sc = sc;
mtx_init(&chan->ch_subchan_lock, "vmbus subchan", NULL, MTX_DEF);
TAILQ_INIT(&chan->ch_subchans);
TASK_INIT(&chan->ch_detach_task, 0, vmbus_chan_detach_task, chan);
return chan;
}
static void
vmbus_chan_free(struct hv_vmbus_channel *chan)
{
/* TODO: assert sub-channel list is empty */
/* TODO: asset no longer on the primary channel's sub-channel list */
/* TODO: asset no longer on the vmbus channel list */
hyperv_dmamem_free(&chan->ch_monprm_dma, chan->ch_monprm);
mtx_destroy(&chan->ch_subchan_lock);
free(chan, M_DEVBUF);
}
static int
vmbus_chan_add(struct hv_vmbus_channel *newchan)
{
struct vmbus_softc *sc = newchan->vmbus_sc;
struct hv_vmbus_channel *prichan;
if (newchan->ch_id == 0) {
/*
* XXX
* Chan0 will neither be processed nor should be offered;
* skip it.
*/
device_printf(sc->vmbus_dev, "got chan0 offer, discard\n");
return EINVAL;
} else if (newchan->ch_id >= VMBUS_CHAN_MAX) {
device_printf(sc->vmbus_dev, "invalid chan%u offer\n",
newchan->ch_id);
return EINVAL;
}
sc->vmbus_chmap[newchan->ch_id] = newchan;
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n",
newchan->ch_id, newchan->ch_subidx);
}
mtx_lock(&sc->vmbus_prichan_lock);
TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) {
/*
* Sub-channel will have the same type GUID and instance
* GUID as its primary channel.
*/
if (memcmp(&prichan->ch_guid_type, &newchan->ch_guid_type,
sizeof(struct hyperv_guid)) == 0 &&
memcmp(&prichan->ch_guid_inst, &newchan->ch_guid_inst,
sizeof(struct hyperv_guid)) == 0)
break;
}
if (VMBUS_CHAN_ISPRIMARY(newchan)) {
if (prichan == NULL) {
/* Install the new primary channel */
TAILQ_INSERT_TAIL(&sc->vmbus_prichans, newchan,
ch_prilink);
mtx_unlock(&sc->vmbus_prichan_lock);
return 0;
} else {
mtx_unlock(&sc->vmbus_prichan_lock);
device_printf(sc->vmbus_dev, "duplicated primary "
"chan%u\n", newchan->ch_id);
return EINVAL;
}
} else { /* Sub-channel */
if (prichan == NULL) {
mtx_unlock(&sc->vmbus_prichan_lock);
device_printf(sc->vmbus_dev, "no primary chan for "
"chan%u\n", newchan->ch_id);
return EINVAL;
}
/*
* Found the primary channel for this sub-channel and
* move on.
*
* XXX refcnt prichan
*/
}
mtx_unlock(&sc->vmbus_prichan_lock);
/*
* This is a sub-channel; link it with the primary channel.
*/
KASSERT(!VMBUS_CHAN_ISPRIMARY(newchan),
("new channel is not sub-channel"));
KASSERT(prichan != NULL, ("no primary channel"));
newchan->ch_prichan = prichan;
newchan->ch_dev = prichan->ch_dev;
mtx_lock(&prichan->ch_subchan_lock);
TAILQ_INSERT_TAIL(&prichan->ch_subchans, newchan, ch_sublink);
/*
* Bump up sub-channel count and notify anyone that is
* interested in this sub-channel, after this sub-channel
* is setup.
*/
prichan->ch_subchan_cnt++;
mtx_unlock(&prichan->ch_subchan_lock);
wakeup(prichan);
return 0;
}
void
vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
{
KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
/* Only cpu0 is supported */
cpu = 0;
}
chan->target_cpu = cpu;
chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
if (bootverbose) {
printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
chan->ch_id,
chan->target_cpu, chan->target_vcpu);
}
}
void
vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
{
static uint32_t vmbus_chan_nextcpu;
int cpu;
cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
vmbus_channel_cpu_set(chan, cpu);
}
static void
vmbus_chan_cpu_default(struct hv_vmbus_channel *chan)
{
/*
* By default, pin the channel to cpu0. Devices having
* special channel-cpu mapping requirement should call
* vmbus_channel_cpu_{set,rr}().
*/
vmbus_channel_cpu_set(chan, 0);
}
static void
vmbus_chan_msgproc_choffer(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const struct vmbus_chanmsg_choffer *offer;
struct hv_vmbus_channel *chan;
int error;
offer = (const struct vmbus_chanmsg_choffer *)msg->msg_data;
chan = vmbus_chan_alloc(sc);
if (chan == NULL) {
device_printf(sc->vmbus_dev, "allocate chan%u failed\n",
offer->chm_chanid);
return;
}
chan->ch_id = offer->chm_chanid;
chan->ch_subidx = offer->chm_subidx;
chan->ch_guid_type = offer->chm_chtype;
chan->ch_guid_inst = offer->chm_chinst;
/* Batch reading is on by default */
chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
chan->ch_monprm->mp_connid = VMBUS_CONNID_EVENT;
if (sc->vmbus_version != VMBUS_VERSION_WS2008)
chan->ch_monprm->mp_connid = offer->chm_connid;
if (offer->chm_flags1 & VMBUS_CHOFFER_FLAG1_HASMNF) {
/*
* Setup MNF stuffs.
*/
chan->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
chan->ch_montrig_idx = offer->chm_montrig / VMBUS_MONTRIG_LEN;
if (chan->ch_montrig_idx >= VMBUS_MONTRIGS_MAX)
panic("invalid monitor trigger %u", offer->chm_montrig);
chan->ch_montrig_mask =
1 << (offer->chm_montrig % VMBUS_MONTRIG_LEN);
}
/* Select default cpu for this channel. */
vmbus_chan_cpu_default(chan);
error = vmbus_chan_add(chan);
if (error) {
device_printf(sc->vmbus_dev, "add chan%u failed: %d\n",
chan->ch_id, error);
vmbus_chan_free(chan);
return;
}
if (VMBUS_CHAN_ISPRIMARY(chan)) {
/*
* Add device for this primary channel.
*
* NOTE:
* Error is ignored here; don't have much to do if error
* really happens.
*/
hv_vmbus_child_device_register(chan);
}
}
/*
* XXX pretty broken; need rework.
*/
static void
vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const struct vmbus_chanmsg_chrescind *note;
struct hv_vmbus_channel *chan;
note = (const struct vmbus_chanmsg_chrescind *)msg->msg_data;
if (note->chm_chanid > VMBUS_CHAN_MAX) {
device_printf(sc->vmbus_dev, "invalid rescinded chan%u\n",
note->chm_chanid);
return;
}
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u rescinded\n",
note->chm_chanid);
}
chan = sc->vmbus_chmap[note->chm_chanid];
if (chan == NULL)
return;
sc->vmbus_chmap[note->chm_chanid] = NULL;
taskqueue_enqueue(taskqueue_thread, &chan->ch_detach_task);
}
static void
vmbus_chan_detach_task(void *xchan, int pending __unused)
{
struct hv_vmbus_channel *chan = xchan;
if (VMBUS_CHAN_ISPRIMARY(chan)) {
/* Only primary channel owns the device */
hv_vmbus_child_device_unregister(chan);
/* NOTE: DO NOT free primary channel for now */
} else {
struct vmbus_softc *sc = chan->vmbus_sc;
struct hv_vmbus_channel *pri_chan = chan->ch_prichan;
struct vmbus_chanmsg_chfree *req;
struct vmbus_msghc *mh;
int error;
mh = vmbus_msghc_get(sc, sizeof(*req));
if (mh == NULL) {
device_printf(sc->vmbus_dev,
"can not get msg hypercall for chfree(chan%u)\n",
chan->ch_id);
goto remove;
}
req = vmbus_msghc_dataptr(mh);
req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
req->chm_chanid = chan->ch_id;
error = vmbus_msghc_exec_noresult(mh);
vmbus_msghc_put(sc, mh);
if (error) {
device_printf(sc->vmbus_dev,
"chfree(chan%u) failed: %d",
chan->ch_id, error);
/* NOTE: Move on! */
} else {
if (bootverbose) {
device_printf(sc->vmbus_dev, "chan%u freed\n",
chan->ch_id);
}
}
remove:
mtx_lock(&pri_chan->ch_subchan_lock);
TAILQ_REMOVE(&pri_chan->ch_subchans, chan, ch_sublink);
KASSERT(pri_chan->ch_subchan_cnt > 0,
("invalid subchan_cnt %d", pri_chan->ch_subchan_cnt));
pri_chan->ch_subchan_cnt--;
mtx_unlock(&pri_chan->ch_subchan_lock);
wakeup(pri_chan);
vmbus_chan_free(chan);
}
}
/*
* Detach all devices and destroy the corresponding primary channels.
*/
void
vmbus_chan_destroy_all(struct vmbus_softc *sc)
{
struct hv_vmbus_channel *chan;
mtx_lock(&sc->vmbus_prichan_lock);
while ((chan = TAILQ_FIRST(&sc->vmbus_prichans)) != NULL) {
KASSERT(VMBUS_CHAN_ISPRIMARY(chan), ("not primary channel"));
TAILQ_REMOVE(&sc->vmbus_prichans, chan, ch_prilink);
mtx_unlock(&sc->vmbus_prichan_lock);
hv_vmbus_child_device_unregister(chan);
vmbus_chan_free(chan);
mtx_lock(&sc->vmbus_prichan_lock);
}
bzero(sc->vmbus_chmap,
sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
mtx_unlock(&sc->vmbus_prichan_lock);
}
/**
* @brief Select the best outgoing channel
*
* The channel whose vcpu binding is closest to the currect vcpu will
* be selected.
* If no multi-channel, always select primary channel
*
* @param primary - primary channel
*/
struct hv_vmbus_channel *
vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
{
hv_vmbus_channel *new_channel = NULL;
hv_vmbus_channel *outgoing_channel = primary;
int old_cpu_distance = 0;
int new_cpu_distance = 0;
int cur_vcpu = 0;
int smp_pro_id = PCPU_GET(cpuid);
if (TAILQ_EMPTY(&primary->ch_subchans)) {
return outgoing_channel;
}
if (smp_pro_id >= MAXCPU) {
return outgoing_channel;
}
cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
/* XXX need lock */
TAILQ_FOREACH(new_channel, &primary->ch_subchans, ch_sublink) {
if ((new_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
continue;
}
if (new_channel->target_vcpu == cur_vcpu){
return new_channel;
}
old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
(outgoing_channel->target_vcpu - cur_vcpu) :
(cur_vcpu - outgoing_channel->target_vcpu));
new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
(new_channel->target_vcpu - cur_vcpu) :
(cur_vcpu - new_channel->target_vcpu));
if (old_cpu_distance < new_cpu_distance) {
continue;
}
outgoing_channel = new_channel;
}
return(outgoing_channel);
}
struct hv_vmbus_channel **
vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
{
struct hv_vmbus_channel **ret, *chan;
int i;
ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
M_WAITOK);
mtx_lock(&pri_chan->ch_subchan_lock);
while (pri_chan->ch_subchan_cnt < subchan_cnt)
mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "subch", 0);
i = 0;
TAILQ_FOREACH(chan, &pri_chan->ch_subchans, ch_sublink) {
/* TODO: refcnt chan */
ret[i] = chan;
++i;
if (i == subchan_cnt)
break;
}
KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
pri_chan->ch_subchan_cnt, subchan_cnt));
mtx_unlock(&pri_chan->ch_subchan_lock);
return ret;
}
void
vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
{
free(subchan, M_TEMP);
}
void
vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
{
mtx_lock(&pri_chan->ch_subchan_lock);
while (pri_chan->ch_subchan_cnt > 0)
mtx_sleep(pri_chan, &pri_chan->ch_subchan_lock, 0, "dsubch", 0);
mtx_unlock(&pri_chan->ch_subchan_lock);
}
void
vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
{
vmbus_chanmsg_proc_t msg_proc;
uint32_t msg_type;
msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
KASSERT(msg_type < VMBUS_CHANMSG_TYPE_MAX,
("invalid message type %u", msg_type));
msg_proc = vmbus_chan_msgprocs[msg_type];
if (msg_proc != NULL)
msg_proc(sc, msg);
}

View File

@ -5,7 +5,6 @@
KMOD= hv_vmbus
SRCS= hv_channel.c \
hv_channel_mgmt.c \
hv_ring_buffer.c \
hyperv.c \
hyperv_busdma.c \