svn commit: r302864 - in head/sys: conf dev/hyperv/vmbus modules/hyperv/vmbus

Sepherosa Ziehau sephe at FreeBSD.org
Fri Jul 15 04:42:10 UTC 2016


Author: sephe
Date: Fri Jul 15 04:42:08 2016
New Revision: 302864
URL: https://svnweb.freebsd.org/changeset/base/302864

Log:
  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

Deleted:
  head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c
Modified:
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/dev/hyperv/vmbus/hv_channel.c
  head/sys/modules/hyperv/vmbus/Makefile

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Fri Jul 15 02:29:10 2016	(r302863)
+++ head/sys/conf/files.amd64	Fri Jul 15 04:42:08 2016	(r302864)
@@ -271,7 +271,6 @@ dev/hyperv/utilities/hv_shutdown.c			opt
 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

Modified: head/sys/conf/files.i386
==============================================================================
--- head/sys/conf/files.i386	Fri Jul 15 02:29:10 2016	(r302863)
+++ head/sys/conf/files.i386	Fri Jul 15 04:42:08 2016	(r302864)
@@ -247,7 +247,6 @@ dev/hyperv/utilities/hv_shutdown.c			opt
 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

Modified: head/sys/dev/hyperv/vmbus/hv_channel.c
==============================================================================
--- head/sys/dev/hyperv/vmbus/hv_channel.c	Fri Jul 15 02:29:10 2016	(r302863)
+++ head/sys/dev/hyperv/vmbus/hv_channel.c	Fri Jul 15 04:42:08 2016	(r302864)
@@ -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 vmbu
 		}
 	}
 }
+
+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);
+}

Modified: head/sys/modules/hyperv/vmbus/Makefile
==============================================================================
--- head/sys/modules/hyperv/vmbus/Makefile	Fri Jul 15 02:29:10 2016	(r302863)
+++ head/sys/modules/hyperv/vmbus/Makefile	Fri Jul 15 04:42:08 2016	(r302864)
@@ -5,7 +5,6 @@
 
 KMOD=	hv_vmbus
 SRCS=	hv_channel.c \
-	hv_channel_mgmt.c \
 	hv_ring_buffer.c \
 	hyperv.c \
 	hyperv_busdma.c \


More information about the svn-src-head mailing list