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/param.h>
30 #include <sys/kernel.h>
33 #include <sys/mutex.h>
35 #include <dev/hyperv/vmbus/hv_vmbus_priv.h>
36 #include <dev/hyperv/vmbus/vmbus_reg.h>
37 #include <dev/hyperv/vmbus/vmbus_var.h>
43 typedef void (*vmbus_msg_handler)(const hv_vmbus_channel_msg_header *msg);
45 typedef struct hv_vmbus_channel_msg_table_entry {
46 hv_vmbus_channel_msg_type messageType;
47 vmbus_msg_handler messageHandler;
48 } hv_vmbus_channel_msg_table_entry;
50 static void vmbus_channel_on_offer_internal(void *context);
51 static void vmbus_channel_on_offer_rescind_internal(void *context);
53 static void vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr);
54 static void vmbus_channel_on_open_result(
55 const hv_vmbus_channel_msg_header *hdr);
56 static void vmbus_channel_on_offer_rescind(
57 const hv_vmbus_channel_msg_header *hdr);
58 static void vmbus_channel_on_gpadl_created(
59 const hv_vmbus_channel_msg_header *hdr);
60 static void vmbus_channel_on_gpadl_torndown(
61 const hv_vmbus_channel_msg_header *hdr);
62 static void vmbus_channel_on_offers_delivered(
63 const hv_vmbus_channel_msg_header *hdr);
64 static void vmbus_channel_on_version_response(
65 const hv_vmbus_channel_msg_header *hdr);
68 * Channel message dispatch table
70 static const hv_vmbus_channel_msg_table_entry
71 g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
72 { HV_CHANNEL_MESSAGE_INVALID,
74 { HV_CHANNEL_MESSAGE_OFFER_CHANNEL,
75 vmbus_channel_on_offer },
76 { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
77 vmbus_channel_on_offer_rescind },
78 { HV_CHANNEL_MESSAGE_REQUEST_OFFERS,
80 { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
81 vmbus_channel_on_offers_delivered },
82 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL,
84 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
85 vmbus_channel_on_open_result },
86 { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL,
88 { HV_CHANNEL_MESSAGEL_GPADL_HEADER,
90 { HV_CHANNEL_MESSAGE_GPADL_BODY,
92 { HV_CHANNEL_MESSAGE_GPADL_CREATED,
93 vmbus_channel_on_gpadl_created },
94 { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN,
96 { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
97 vmbus_channel_on_gpadl_torndown },
98 { HV_CHANNEL_MESSAGE_REL_ID_RELEASED,
100 { HV_CHANNEL_MESSAGE_INITIATED_CONTACT,
102 { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
103 vmbus_channel_on_version_response },
104 { HV_CHANNEL_MESSAGE_UNLOAD,
108 typedef struct hv_work_item {
110 void (*callback)(void *);
114 static struct mtx vmbus_chwait_lock;
115 MTX_SYSINIT(vmbus_chwait_lk, &vmbus_chwait_lock, "vmbus primarych wait lock",
117 static uint32_t vmbus_chancnt;
118 static uint32_t vmbus_devcnt;
120 #define VMBUS_CHANCNT_DONE 0x80000000
123 * Implementation of the work abstraction.
126 work_item_callback(void *work, int pending)
128 struct hv_work_item *w = (struct hv_work_item *)work;
130 w->callback(w->context);
136 * @brief Create work item
140 void (*callback)(void *), void *context)
142 struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
144 KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
148 w->callback = callback;
149 w->context = context;
151 TASK_INIT(&w->work, 0, work_item_callback, w);
153 return (taskqueue_enqueue(taskqueue_thread, &w->work));
158 * @brief Allocate and initialize a vmbus channel object
161 hv_vmbus_allocate_channel(void)
163 hv_vmbus_channel* channel;
165 channel = (hv_vmbus_channel*) malloc(
166 sizeof(hv_vmbus_channel),
170 mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
171 TAILQ_INIT(&channel->sc_list_anchor);
177 * @brief Release the resources used by the vmbus channel object
180 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
182 mtx_destroy(&channel->sc_lock);
183 free(channel, M_DEVBUF);
187 * @brief Process the offer by creating a channel/device
188 * associated with this offer
191 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
193 hv_vmbus_channel* channel;
197 relid = new_channel->offer_msg.child_rel_id;
199 * Make sure this is a new offer
201 mtx_lock(&hv_vmbus_g_connection.channel_lock);
204 * XXX channel0 will not be processed; skip it.
206 printf("VMBUS: got channel0 offer\n");
208 hv_vmbus_g_connection.channels[relid] = new_channel;
211 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
213 if (memcmp(&channel->offer_msg.offer.interface_type,
214 &new_channel->offer_msg.offer.interface_type,
215 sizeof(hv_guid)) == 0 &&
216 memcmp(&channel->offer_msg.offer.interface_instance,
217 &new_channel->offer_msg.offer.interface_instance,
218 sizeof(hv_guid)) == 0)
222 if (channel == NULL) {
223 /* Install the new primary channel */
224 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
225 new_channel, list_entry);
227 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
229 if (channel != NULL) {
231 * Check if this is a sub channel.
233 if (new_channel->offer_msg.offer.sub_channel_index != 0) {
235 * It is a sub channel offer, process it.
237 new_channel->primary_channel = channel;
238 new_channel->device = channel->device;
239 mtx_lock(&channel->sc_lock);
240 TAILQ_INSERT_TAIL(&channel->sc_list_anchor,
241 new_channel, sc_list_entry);
242 mtx_unlock(&channel->sc_lock);
245 printf("VMBUS get multi-channel offer, "
247 new_channel->offer_msg.child_rel_id,
248 new_channel->offer_msg.offer.sub_channel_index);
251 /* Insert new channel into channel_anchor. */
252 mtx_lock(&hv_vmbus_g_connection.channel_lock);
253 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
254 new_channel, list_entry);
255 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
258 printf("VMBUS: new multi-channel offer <%p>, "
259 "its primary channel is <%p>.\n",
260 new_channel, new_channel->primary_channel);
262 new_channel->state = HV_CHANNEL_OPEN_STATE;
265 * Bump up sub-channel count and notify anyone that is
266 * interested in this sub-channel, after this sub-channel
269 mtx_lock(&channel->sc_lock);
270 channel->subchan_cnt++;
271 mtx_unlock(&channel->sc_lock);
277 printf("VMBUS: duplicated primary channel%u\n",
278 new_channel->offer_msg.child_rel_id);
279 hv_vmbus_free_vmbus_channel(new_channel);
283 new_channel->state = HV_CHANNEL_OPEN_STATE;
286 * Start the process of binding this offer to the driver
287 * (We need to set the device field before calling
288 * hv_vmbus_child_device_add())
290 new_channel->device = hv_vmbus_child_device_create(
291 new_channel->offer_msg.offer.interface_type,
292 new_channel->offer_msg.offer.interface_instance, new_channel);
295 * Add the new device to the bus. This will kick off device-driver
296 * binding which eventually invokes the device driver's AddDevice()
299 ret = hv_vmbus_child_device_register(new_channel->device);
301 mtx_lock(&hv_vmbus_g_connection.channel_lock);
302 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
303 new_channel, list_entry);
304 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
305 hv_vmbus_free_vmbus_channel(new_channel);
308 mtx_lock(&vmbus_chwait_lock);
310 mtx_unlock(&vmbus_chwait_lock);
311 wakeup(&vmbus_devcnt);
315 vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
317 KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
319 if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
320 hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) {
321 /* Only cpu0 is supported */
325 chan->target_cpu = cpu;
326 chan->target_vcpu = VMBUS_PCPU_GET(vmbus_get_softc(), vcpuid, cpu);
329 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
330 chan->offer_msg.child_rel_id,
331 chan->target_cpu, chan->target_vcpu);
336 * Array of device guids that are performance critical. We try to distribute
337 * the interrupt load for these devices across all online cpus.
339 static const hv_guid high_perf_devices[] = {
353 * We use this static number to distribute the channel interrupt load.
355 static uint32_t next_vcpu;
358 * Starting with Win8, we can statically distribute the incoming
359 * channel interrupt load by binding a channel to VCPU. We
360 * implement here a simple round robin scheme for distributing
361 * the interrupt load.
362 * We will bind channels that are not performance critical to cpu 0 and
363 * performance critical channels (IDE, SCSI and Network) will be uniformly
364 * distributed across all available CPUs.
367 vmbus_channel_select_defcpu(struct hv_vmbus_channel *channel)
369 uint32_t current_cpu;
371 boolean_t is_perf_channel = FALSE;
372 const hv_guid *guid = &channel->offer_msg.offer.interface_type;
374 for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
375 if (memcmp(guid->data, high_perf_devices[i].data,
376 sizeof(hv_guid)) == 0) {
377 is_perf_channel = TRUE;
382 if (!is_perf_channel) {
384 vmbus_channel_cpu_set(channel, 0);
387 /* mp_ncpus should have the number cpus currently online */
388 current_cpu = (++next_vcpu % mp_ncpus);
389 vmbus_channel_cpu_set(channel, current_cpu);
393 * @brief Handler for channel offers from Hyper-V/Azure
395 * Handler for channel offers from vmbus in parent partition. We ignore
396 * all offers except network and storage offers. For each network and storage
397 * offers, we create a channel object and queue a work item to the channel
398 * object to process the offer synchronously
401 vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr)
403 const hv_vmbus_channel_offer_channel *offer;
404 hv_vmbus_channel_offer_channel *copied;
406 offer = (const hv_vmbus_channel_offer_channel *)hdr;
409 copied = malloc(sizeof(*copied), M_DEVBUF, M_NOWAIT);
410 if (copied == NULL) {
411 printf("fail to allocate memory\n");
415 memcpy(copied, hdr, sizeof(*copied));
416 hv_queue_work_item(vmbus_channel_on_offer_internal, copied);
418 mtx_lock(&vmbus_chwait_lock);
419 if ((vmbus_chancnt & VMBUS_CHANCNT_DONE) == 0)
421 mtx_unlock(&vmbus_chwait_lock);
425 vmbus_channel_on_offer_internal(void* context)
427 hv_vmbus_channel* new_channel;
429 hv_vmbus_channel_offer_channel* offer = (hv_vmbus_channel_offer_channel*)context;
430 /* Allocate the channel object and save this offer */
431 new_channel = hv_vmbus_allocate_channel();
434 * By default we setup state to enable batched
435 * reading. A specific service can choose to
436 * disable this prior to opening the channel.
438 new_channel->batched_reading = TRUE;
440 new_channel->signal_event_param =
441 (hv_vmbus_input_signal_event *)
442 (HV_ALIGN_UP((unsigned long)
443 &new_channel->signal_event_buffer,
444 HV_HYPERCALL_PARAM_ALIGN));
446 new_channel->signal_event_param->connection_id.as_uint32_t = 0;
447 new_channel->signal_event_param->connection_id.u.id =
448 HV_VMBUS_EVENT_CONNECTION_ID;
449 new_channel->signal_event_param->flag_number = 0;
450 new_channel->signal_event_param->rsvd_z = 0;
452 if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
453 new_channel->is_dedicated_interrupt =
454 (offer->is_dedicated_interrupt != 0);
455 new_channel->signal_event_param->connection_id.u.id =
456 offer->connection_id;
459 memcpy(&new_channel->offer_msg, offer,
460 sizeof(hv_vmbus_channel_offer_channel));
461 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
462 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
464 /* Select default cpu for this channel. */
465 vmbus_channel_select_defcpu(new_channel);
467 vmbus_channel_process_offer(new_channel);
469 free(offer, M_DEVBUF);
473 * @brief Rescind offer handler.
475 * We queue a work item to process this offer
479 vmbus_channel_on_offer_rescind(const hv_vmbus_channel_msg_header *hdr)
481 const hv_vmbus_channel_rescind_offer *rescind;
482 hv_vmbus_channel* channel;
484 rescind = (const hv_vmbus_channel_rescind_offer *)hdr;
486 channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
490 hv_queue_work_item(vmbus_channel_on_offer_rescind_internal, channel);
491 hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
495 vmbus_channel_on_offer_rescind_internal(void *context)
497 hv_vmbus_channel* channel;
499 channel = (hv_vmbus_channel*)context;
500 if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
501 /* Only primary channel owns the hv_device */
502 hv_vmbus_child_device_unregister(channel->device);
508 * @brief Invoked when all offers have been delivered.
511 vmbus_channel_on_offers_delivered(
512 const hv_vmbus_channel_msg_header *hdr __unused)
515 mtx_lock(&vmbus_chwait_lock);
516 vmbus_chancnt |= VMBUS_CHANCNT_DONE;
517 mtx_unlock(&vmbus_chwait_lock);
518 wakeup(&vmbus_chancnt);
522 * @brief Open result handler.
524 * This is invoked when we received a response
525 * to our channel open request. Find the matching request, copy the
526 * response and signal the requesting thread.
529 vmbus_channel_on_open_result(const hv_vmbus_channel_msg_header *hdr)
531 const hv_vmbus_channel_open_result *result;
532 hv_vmbus_channel_msg_info* msg_info;
533 hv_vmbus_channel_msg_header* requestHeader;
534 hv_vmbus_channel_open_channel* openMsg;
536 result = (const hv_vmbus_channel_open_result *)hdr;
539 * Find the open msg, copy the result and signal/unblock the wait event
541 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
543 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
545 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
547 if (requestHeader->message_type ==
548 HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
549 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
550 if (openMsg->child_rel_id == result->child_rel_id
551 && openMsg->open_id == result->open_id) {
552 memcpy(&msg_info->response.open_result, result,
553 sizeof(hv_vmbus_channel_open_result));
554 sema_post(&msg_info->wait_sema);
559 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
564 * @brief GPADL created handler.
566 * This is invoked when we received a response
567 * to our gpadl create request. Find the matching request, copy the
568 * response and signal the requesting thread.
571 vmbus_channel_on_gpadl_created(const hv_vmbus_channel_msg_header *hdr)
573 const hv_vmbus_channel_gpadl_created *gpadl_created;
574 hv_vmbus_channel_msg_info* msg_info;
575 hv_vmbus_channel_msg_header* request_header;
576 hv_vmbus_channel_gpadl_header* gpadl_header;
578 gpadl_created = (const hv_vmbus_channel_gpadl_created *)hdr;
580 /* Find the establish msg, copy the result and signal/unblock
583 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
584 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
586 request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
587 if (request_header->message_type ==
588 HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
590 (hv_vmbus_channel_gpadl_header*) request_header;
592 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
593 && (gpadl_created->gpadl == gpadl_header->gpadl)) {
594 memcpy(&msg_info->response.gpadl_created,
596 sizeof(hv_vmbus_channel_gpadl_created));
597 sema_post(&msg_info->wait_sema);
602 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
606 * @brief GPADL torndown handler.
608 * This is invoked when we received a respons
609 * to our gpadl teardown request. Find the matching request, copy the
610 * response and signal the requesting thread
613 vmbus_channel_on_gpadl_torndown(const hv_vmbus_channel_msg_header *hdr)
615 const hv_vmbus_channel_gpadl_torndown *gpadl_torndown;
616 hv_vmbus_channel_msg_info* msg_info;
617 hv_vmbus_channel_msg_header* requestHeader;
618 hv_vmbus_channel_gpadl_teardown* gpadlTeardown;
620 gpadl_torndown = (const hv_vmbus_channel_gpadl_torndown *)hdr;
623 * Find the open msg, copy the result and signal/unblock the
627 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
629 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
631 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
633 if (requestHeader->message_type
634 == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
636 (hv_vmbus_channel_gpadl_teardown*) requestHeader;
638 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
639 memcpy(&msg_info->response.gpadl_torndown,
641 sizeof(hv_vmbus_channel_gpadl_torndown));
642 sema_post(&msg_info->wait_sema);
647 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
651 * @brief Version response handler.
653 * This is invoked when we received a response
654 * to our initiate contact request. Find the matching request, copy th
655 * response and signal the requesting thread.
658 vmbus_channel_on_version_response(const hv_vmbus_channel_msg_header *hdr)
660 hv_vmbus_channel_msg_info* msg_info;
661 hv_vmbus_channel_msg_header* requestHeader;
662 hv_vmbus_channel_initiate_contact* initiate;
663 const hv_vmbus_channel_version_response *versionResponse;
665 versionResponse = (const hv_vmbus_channel_version_response *)hdr;
667 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
668 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
670 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
671 if (requestHeader->message_type
672 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
674 (hv_vmbus_channel_initiate_contact*) requestHeader;
675 memcpy(&msg_info->response.version_response,
677 sizeof(hv_vmbus_channel_version_response));
678 sema_post(&msg_info->wait_sema);
681 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
686 * @brief Send a request to get all our pending offers.
689 hv_vmbus_request_channel_offers(void)
692 hv_vmbus_channel_msg_header* msg;
693 hv_vmbus_channel_msg_info* msg_info;
695 msg_info = (hv_vmbus_channel_msg_info *)
696 malloc(sizeof(hv_vmbus_channel_msg_info)
697 + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
699 if (msg_info == NULL) {
701 printf("Error VMBUS: malloc failed for Request Offers\n");
705 msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
706 msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
708 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
710 free(msg_info, M_DEVBUF);
716 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
719 hv_vmbus_release_unattached_channels(void)
721 hv_vmbus_channel *channel;
723 mtx_lock(&hv_vmbus_g_connection.channel_lock);
725 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
726 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
727 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
728 channel, list_entry);
730 if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
731 /* Only primary channel owns the hv_device */
732 hv_vmbus_child_device_unregister(channel->device);
734 hv_vmbus_free_vmbus_channel(channel);
736 bzero(hv_vmbus_g_connection.channels,
737 sizeof(hv_vmbus_channel*) * VMBUS_CHAN_MAX);
738 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
742 * @brief Select the best outgoing channel
744 * The channel whose vcpu binding is closest to the currect vcpu will
746 * If no multi-channel, always select primary channel
748 * @param primary - primary channel
750 struct hv_vmbus_channel *
751 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
753 hv_vmbus_channel *new_channel = NULL;
754 hv_vmbus_channel *outgoing_channel = primary;
755 int old_cpu_distance = 0;
756 int new_cpu_distance = 0;
758 int smp_pro_id = PCPU_GET(cpuid);
760 if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
761 return outgoing_channel;
764 if (smp_pro_id >= MAXCPU) {
765 return outgoing_channel;
768 cur_vcpu = VMBUS_PCPU_GET(vmbus_get_softc(), vcpuid, smp_pro_id);
770 TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
771 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
775 if (new_channel->target_vcpu == cur_vcpu){
779 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
780 (outgoing_channel->target_vcpu - cur_vcpu) :
781 (cur_vcpu - outgoing_channel->target_vcpu));
783 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
784 (new_channel->target_vcpu - cur_vcpu) :
785 (cur_vcpu - new_channel->target_vcpu));
787 if (old_cpu_distance < new_cpu_distance) {
791 outgoing_channel = new_channel;
794 return(outgoing_channel);
802 mtx_lock(&vmbus_chwait_lock);
803 while ((vmbus_chancnt & VMBUS_CHANCNT_DONE) == 0)
804 mtx_sleep(&vmbus_chancnt, &vmbus_chwait_lock, 0, "waitch", 0);
805 chancnt = vmbus_chancnt & ~VMBUS_CHANCNT_DONE;
807 while (vmbus_devcnt != chancnt)
808 mtx_sleep(&vmbus_devcnt, &vmbus_chwait_lock, 0, "waitdev", 0);
809 mtx_unlock(&vmbus_chwait_lock);
812 struct hv_vmbus_channel **
813 vmbus_get_subchan(struct hv_vmbus_channel *pri_chan, int subchan_cnt)
815 struct hv_vmbus_channel **ret, *chan;
818 ret = malloc(subchan_cnt * sizeof(struct hv_vmbus_channel *), M_TEMP,
821 mtx_lock(&pri_chan->sc_lock);
823 while (pri_chan->subchan_cnt < subchan_cnt)
824 mtx_sleep(pri_chan, &pri_chan->sc_lock, 0, "subch", 0);
827 TAILQ_FOREACH(chan, &pri_chan->sc_list_anchor, sc_list_entry) {
828 /* TODO: refcnt chan */
832 if (i == subchan_cnt)
835 KASSERT(i == subchan_cnt, ("invalid subchan count %d, should be %d",
836 pri_chan->subchan_cnt, subchan_cnt));
838 mtx_unlock(&pri_chan->sc_lock);
844 vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
847 free(subchan, M_TEMP);
851 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
853 const hv_vmbus_channel_msg_table_entry *entry;
854 const hv_vmbus_channel_msg_header *hdr;
855 hv_vmbus_channel_msg_type msg_type;
857 hdr = (const hv_vmbus_channel_msg_header *)msg->msg_data;
858 msg_type = hdr->message_type;
860 if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) {
861 device_printf(sc->vmbus_dev, "unknown message type 0x%x\n",
866 entry = &g_channel_message_table[msg_type];
867 if (entry->messageHandler)
868 entry->messageHandler(hdr);