2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/kernel.h>
36 #include <sys/mutex.h>
38 #include <dev/hyperv/include/hyperv_busdma.h>
39 #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
40 #include <dev/hyperv/vmbus/vmbus_reg.h>
41 #include <dev/hyperv/vmbus/vmbus_var.h>
43 typedef void (*vmbus_chanmsg_proc_t)
44 (struct vmbus_softc *, const struct vmbus_message *);
46 static struct hv_vmbus_channel *hv_vmbus_allocate_channel(struct vmbus_softc *);
47 static void vmbus_channel_on_offer_internal(struct vmbus_softc *,
48 const hv_vmbus_channel_offer_channel *offer);
49 static void vmbus_chan_detach_task(void *, int);
51 static void vmbus_channel_on_offer(struct vmbus_softc *,
52 const struct vmbus_message *);
53 static void vmbus_channel_on_offer_rescind(struct vmbus_softc *,
54 const struct vmbus_message *);
55 static void vmbus_channel_on_offers_delivered(struct vmbus_softc *,
56 const struct vmbus_message *);
59 * Channel message dispatch table
61 static const vmbus_chanmsg_proc_t
62 vmbus_chanmsg_process[HV_CHANNEL_MESSAGE_COUNT] = {
63 [HV_CHANNEL_MESSAGE_OFFER_CHANNEL] =
64 vmbus_channel_on_offer,
65 [HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER] =
66 vmbus_channel_on_offer_rescind,
67 [HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED] =
68 vmbus_channel_on_offers_delivered,
69 [HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT] =
71 [HV_CHANNEL_MESSAGE_GPADL_CREATED] =
73 [HV_CHANNEL_MESSAGE_GPADL_TORNDOWN] =
75 [HV_CHANNEL_MESSAGE_VERSION_RESPONSE] =
80 * @brief Allocate and initialize a vmbus channel object
82 static struct hv_vmbus_channel *
83 hv_vmbus_allocate_channel(struct vmbus_softc *sc)
85 struct hv_vmbus_channel *channel;
87 channel = malloc(sizeof(*channel), M_DEVBUF, M_WAITOK | M_ZERO);
88 channel->vmbus_sc = sc;
90 mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
91 TAILQ_INIT(&channel->sc_list_anchor);
92 TASK_INIT(&channel->ch_detach_task, 0, vmbus_chan_detach_task, channel);
98 * @brief Release the resources used by the vmbus channel object
101 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
103 mtx_destroy(&channel->sc_lock);
104 free(channel, M_DEVBUF);
108 * @brief Process the offer by creating a channel/device
109 * associated with this offer
112 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
114 struct vmbus_softc *sc = new_channel->vmbus_sc;
115 hv_vmbus_channel* channel;
118 relid = new_channel->ch_id;
120 * Make sure this is a new offer
122 mtx_lock(&sc->vmbus_chlist_lock);
125 * XXX channel0 will not be processed; skip it.
127 printf("VMBUS: got channel0 offer\n");
129 sc->vmbus_chmap[relid] = new_channel;
132 TAILQ_FOREACH(channel, &sc->vmbus_chlist, ch_link) {
133 if (memcmp(&channel->ch_guid_type, &new_channel->ch_guid_type,
134 sizeof(hv_guid)) == 0 &&
135 memcmp(&channel->ch_guid_inst, &new_channel->ch_guid_inst,
136 sizeof(hv_guid)) == 0)
140 if (channel == NULL) {
141 /* Install the new primary channel */
142 TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel, ch_link);
144 mtx_unlock(&sc->vmbus_chlist_lock);
150 if (channel != NULL) {
151 snprintf(logstr, sizeof(logstr), ", primary chan%u",
154 device_printf(sc->vmbus_dev, "chan%u subchanid%u offer%s\n",
156 new_channel->ch_subidx, logstr);
159 if (channel != NULL) {
161 * Check if this is a sub channel.
163 if (new_channel->ch_subidx != 0) {
165 * It is a sub channel offer, process it.
167 new_channel->primary_channel = channel;
168 new_channel->ch_dev = channel->ch_dev;
169 mtx_lock(&channel->sc_lock);
170 TAILQ_INSERT_TAIL(&channel->sc_list_anchor,
171 new_channel, sc_list_entry);
172 mtx_unlock(&channel->sc_lock);
175 * Insert the new channel to the end of the global
179 * The new sub-channel MUST be inserted AFTER it's
180 * primary channel, so that the primary channel will
181 * be found in the above loop for its baby siblings.
183 mtx_lock(&sc->vmbus_chlist_lock);
184 TAILQ_INSERT_TAIL(&sc->vmbus_chlist, new_channel,
186 mtx_unlock(&sc->vmbus_chlist_lock);
188 new_channel->state = HV_CHANNEL_OPEN_STATE;
191 * Bump up sub-channel count and notify anyone that is
192 * interested in this sub-channel, after this sub-channel
195 mtx_lock(&channel->sc_lock);
196 channel->subchan_cnt++;
197 mtx_unlock(&channel->sc_lock);
203 printf("VMBUS: duplicated primary channel%u\n",
205 hv_vmbus_free_vmbus_channel(new_channel);
209 new_channel->state = HV_CHANNEL_OPEN_STATE;
212 * Add the new device to the bus. This will kick off device-driver
213 * binding which eventually invokes the device driver's AddDevice()
217 * Error is ignored here; don't have much to do if error really
220 hv_vmbus_child_device_register(new_channel);
224 vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
226 KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
228 if (chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WS2008 ||
229 chan->vmbus_sc->vmbus_version == VMBUS_VERSION_WIN7) {
230 /* Only cpu0 is supported */
234 chan->target_cpu = cpu;
235 chan->target_vcpu = VMBUS_PCPU_GET(chan->vmbus_sc, vcpuid, cpu);
238 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
240 chan->target_cpu, chan->target_vcpu);
245 vmbus_channel_cpu_rr(struct hv_vmbus_channel *chan)
247 static uint32_t vmbus_chan_nextcpu;
250 cpu = atomic_fetchadd_int(&vmbus_chan_nextcpu, 1) % mp_ncpus;
251 vmbus_channel_cpu_set(chan, cpu);
255 vmbus_channel_select_defcpu(struct hv_vmbus_channel *chan)
258 * By default, pin the channel to cpu0. Devices having
259 * special channel-cpu mapping requirement should call
260 * vmbus_channel_cpu_{set,rr}().
262 vmbus_channel_cpu_set(chan, 0);
266 * @brief Handler for channel offers from Hyper-V/Azure
268 * Handler for channel offers from vmbus in parent partition.
271 vmbus_channel_on_offer(struct vmbus_softc *sc, const struct vmbus_message *msg)
273 const hv_vmbus_channel_offer_channel *offer;
275 /* New channel is offered by vmbus */
276 vmbus_scan_newchan(sc);
278 offer = (const hv_vmbus_channel_offer_channel *)msg->msg_data;
279 vmbus_channel_on_offer_internal(sc, offer);
283 vmbus_channel_on_offer_internal(struct vmbus_softc *sc,
284 const hv_vmbus_channel_offer_channel *offer)
286 hv_vmbus_channel* new_channel;
289 * Allocate the channel object and save this offer
291 new_channel = hv_vmbus_allocate_channel(sc);
292 new_channel->ch_id = offer->child_rel_id;
293 new_channel->ch_subidx = offer->offer.sub_channel_index;
294 new_channel->ch_guid_type = offer->offer.interface_type;
295 new_channel->ch_guid_inst = offer->offer.interface_instance;
297 /* Batch reading is on by default */
298 new_channel->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
299 if (offer->monitor_allocated)
300 new_channel->ch_flags |= VMBUS_CHAN_FLAG_HASMNF;
302 new_channel->ch_sigevt = hyperv_dmamem_alloc(
303 bus_get_dma_tag(sc->vmbus_dev),
304 HYPERCALL_SIGEVTIN_ALIGN, 0, sizeof(struct hypercall_sigevt_in),
305 &new_channel->ch_sigevt_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
306 if (new_channel->ch_sigevt == NULL) {
307 device_printf(sc->vmbus_dev, "sigevt alloc failed\n");
309 mtx_destroy(&new_channel->sc_lock);
310 free(new_channel, M_DEVBUF);
313 new_channel->ch_sigevt->hc_connid = VMBUS_CONNID_EVENT;
314 if (sc->vmbus_version != VMBUS_VERSION_WS2008)
315 new_channel->ch_sigevt->hc_connid = offer->connection_id;
317 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
318 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
320 /* Select default cpu for this channel. */
321 vmbus_channel_select_defcpu(new_channel);
323 vmbus_channel_process_offer(new_channel);
327 * @brief Rescind offer handler.
329 * We queue a work item to process this offer
332 * XXX pretty broken; need rework.
335 vmbus_channel_on_offer_rescind(struct vmbus_softc *sc,
336 const struct vmbus_message *msg)
338 const hv_vmbus_channel_rescind_offer *rescind;
339 hv_vmbus_channel* channel;
341 rescind = (const hv_vmbus_channel_rescind_offer *)msg->msg_data;
343 device_printf(sc->vmbus_dev, "chan%u rescind\n",
344 rescind->child_rel_id);
347 channel = sc->vmbus_chmap[rescind->child_rel_id];
350 sc->vmbus_chmap[rescind->child_rel_id] = NULL;
352 taskqueue_enqueue(taskqueue_thread, &channel->ch_detach_task);
356 vmbus_chan_detach_task(void *xchan, int pending __unused)
358 struct hv_vmbus_channel *chan = xchan;
360 if (HV_VMBUS_CHAN_ISPRIMARY(chan)) {
361 /* Only primary channel owns the device */
362 hv_vmbus_child_device_unregister(chan);
363 /* NOTE: DO NOT free primary channel for now */
365 struct vmbus_softc *sc = chan->vmbus_sc;
366 struct hv_vmbus_channel *pri_chan = chan->primary_channel;
367 struct vmbus_chanmsg_chfree *req;
368 struct vmbus_msghc *mh;
371 mh = vmbus_msghc_get(sc, sizeof(*req));
373 device_printf(sc->vmbus_dev,
374 "can not get msg hypercall for chfree(chan%u)\n",
379 req = vmbus_msghc_dataptr(mh);
380 req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHFREE;
381 req->chm_chanid = chan->ch_id;
383 error = vmbus_msghc_exec_noresult(mh);
384 vmbus_msghc_put(sc, mh);
387 device_printf(sc->vmbus_dev,
388 "chfree(chan%u) failed: %d",
393 device_printf(sc->vmbus_dev, "chan%u freed\n",
398 mtx_lock(&sc->vmbus_chlist_lock);
399 TAILQ_REMOVE(&sc->vmbus_chlist, chan, ch_link);
400 mtx_unlock(&sc->vmbus_chlist_lock);
402 mtx_lock(&pri_chan->sc_lock);
403 TAILQ_REMOVE(&pri_chan->sc_list_anchor, chan, sc_list_entry);
404 KASSERT(pri_chan->subchan_cnt > 0,
405 ("invalid subchan_cnt %d", pri_chan->subchan_cnt));
406 pri_chan->subchan_cnt--;
407 mtx_unlock(&pri_chan->sc_lock);
410 hv_vmbus_free_vmbus_channel(chan);
416 * @brief Invoked when all offers have been delivered.
419 vmbus_channel_on_offers_delivered(struct vmbus_softc *sc,
420 const struct vmbus_message *msg __unused)
423 /* No more new channels for the channel request. */
428 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
431 hv_vmbus_release_unattached_channels(struct vmbus_softc *sc)
433 hv_vmbus_channel *channel;
435 mtx_lock(&sc->vmbus_chlist_lock);
437 while (!TAILQ_EMPTY(&sc->vmbus_chlist)) {
438 channel = TAILQ_FIRST(&sc->vmbus_chlist);
439 TAILQ_REMOVE(&sc->vmbus_chlist, channel, ch_link);
441 if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
442 /* Only primary channel owns the device */
443 hv_vmbus_child_device_unregister(channel);
445 hv_vmbus_free_vmbus_channel(channel);
447 bzero(sc->vmbus_chmap,
448 sizeof(struct hv_vmbus_channel *) * VMBUS_CHAN_MAX);
450 mtx_unlock(&sc->vmbus_chlist_lock);
454 * @brief Select the best outgoing channel
456 * The channel whose vcpu binding is closest to the currect vcpu will
458 * If no multi-channel, always select primary channel
460 * @param primary - primary channel
462 struct hv_vmbus_channel *
463 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
465 hv_vmbus_channel *new_channel = NULL;
466 hv_vmbus_channel *outgoing_channel = primary;
467 int old_cpu_distance = 0;
468 int new_cpu_distance = 0;
470 int smp_pro_id = PCPU_GET(cpuid);
472 if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
473 return outgoing_channel;
476 if (smp_pro_id >= MAXCPU) {
477 return outgoing_channel;
480 cur_vcpu = VMBUS_PCPU_GET(primary->vmbus_sc, vcpuid, smp_pro_id);
482 TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
483 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
487 if (new_channel->target_vcpu == cur_vcpu){
491 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
492 (outgoing_channel->target_vcpu - cur_vcpu) :
493 (cur_vcpu - outgoing_channel->target_vcpu));
495 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
496 (new_channel->target_vcpu - cur_vcpu) :
497 (cur_vcpu - new_channel->target_vcpu));
499 if (old_cpu_distance < new_cpu_distance) {
503 outgoing_channel = new_channel;
506 return(outgoing_channel);
509 struct hv_vmbus_channel **
510 vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
512 struct hv_vmbus_channel **ret, *chan;
515 ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
518 mtx_lock(&pri_chan->sc_lock);
520 while (pri_chan->subchan_cnt < subchan_cnt)
521 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "subch", 0);
524 TAILQ_FOREACH(chan, &pri_chan->sc_list_anchor, sc_list_entry) {
525 /* TODO: refcnt chan */
529 if (i == subchan_cnt)
532 KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
533 pri_chan->subchan_cnt, subchan_cnt));
535 mtx_unlock(&pri_chan->sc_lock);
541 vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
544 free(subchan, M_TEMP);
548 vmbus_drain_subchan(struct hv_vmbus_channel *pri_chan)
550 mtx_lock(&pri_chan->sc_lock);
551 while (pri_chan->subchan_cnt > 0)
552 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "dsubch", 0);
553 mtx_unlock(&pri_chan->sc_lock);
557 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
559 vmbus_chanmsg_proc_t msg_proc;
562 msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type;
563 if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
564 device_printf(sc->vmbus_dev, "unknown message type 0x%x\n",
569 msg_proc = vmbus_chanmsg_process[msg_type];
570 if (msg_proc != NULL)