2 * Copyright (c) 2009-2012 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>
32 #include "hv_vmbus_priv.h"
38 static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr);
39 static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr);
40 static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr);
41 static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr);
42 static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr);
43 static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr);
44 static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr);
47 * Channel message dispatch table
49 hv_vmbus_channel_msg_table_entry
50 g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
51 { HV_CHANNEL_MESSAGE_INVALID,
53 { HV_CHANNEL_MESSAGE_OFFER_CHANNEL,
54 0, vmbus_channel_on_offer },
55 { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
56 0, vmbus_channel_on_offer_rescind },
57 { HV_CHANNEL_MESSAGE_REQUEST_OFFERS,
59 { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
60 1, vmbus_channel_on_offers_delivered },
61 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL,
63 { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
64 1, vmbus_channel_on_open_result },
65 { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL,
67 { HV_CHANNEL_MESSAGEL_GPADL_HEADER,
69 { HV_CHANNEL_MESSAGE_GPADL_BODY,
71 { HV_CHANNEL_MESSAGE_GPADL_CREATED,
72 1, vmbus_channel_on_gpadl_created },
73 { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN,
75 { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
76 1, vmbus_channel_on_gpadl_torndown },
77 { HV_CHANNEL_MESSAGE_REL_ID_RELEASED,
79 { HV_CHANNEL_MESSAGE_INITIATED_CONTACT,
81 { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
82 1, vmbus_channel_on_version_response },
83 { HV_CHANNEL_MESSAGE_UNLOAD,
89 * Implementation of the work abstraction.
92 work_item_callback(void *work, int pending)
94 struct hv_work_item *w = (struct hv_work_item *)work;
97 * Serialize work execution.
99 if (w->wq->work_sema != NULL) {
100 sema_wait(w->wq->work_sema);
103 w->callback(w->context);
105 if (w->wq->work_sema != NULL) {
106 sema_post(w->wq->work_sema);
112 struct hv_work_queue*
113 hv_work_queue_create(char* name)
115 static unsigned int qid = 0;
118 struct hv_work_queue* wq;
120 wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO);
121 KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n"));
126 * We use work abstraction to handle messages
127 * coming from the host and these are typically offers.
128 * Some FreeBsd drivers appear to have a concurrency issue
129 * where probe/attach needs to be serialized. We ensure that
130 * by having only one thread process work elements in a
131 * specific queue by serializing work execution.
134 if (strcmp(name, "vmbusQ") == 0) {
136 } else { /* control */
139 * Initialize semaphore for this queue by pointing
140 * to the globale semaphore used for synchronizing all
143 wq->work_sema = &hv_vmbus_g_connection.control_sema;
146 sprintf(qname, "hv_%s_%u", name, qid);
149 * Fixme: FreeBSD 8.2 has a different prototype for
150 * taskqueue_create(), and for certain other taskqueue functions.
151 * We need to research the implications of these changes.
152 * Fixme: Not sure when the changes were introduced.
154 wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue,
156 #if __FreeBSD_version < 800000
161 if (wq->queue == NULL) {
166 if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) {
167 taskqueue_free(wq->queue);
178 hv_work_queue_close(struct hv_work_queue *wq)
181 * KYS: Need to drain the taskqueue
182 * before we close the hv_work_queue.
184 /*KYS: taskqueue_drain(wq->tq, ); */
185 taskqueue_free(wq->queue);
190 * @brief Create work item
194 struct hv_work_queue *wq,
195 void (*callback)(void *), void *context)
197 struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
198 M_DEVBUF, M_NOWAIT | M_ZERO);
199 KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
203 w->callback = callback;
204 w->context = context;
207 TASK_INIT(&w->work, 0, work_item_callback, w);
209 return (taskqueue_enqueue(wq->queue, &w->work));
214 * @brief Allocate and initialize a vmbus channel object
217 hv_vmbus_allocate_channel(void)
219 hv_vmbus_channel* channel;
221 channel = (hv_vmbus_channel*) malloc(
222 sizeof(hv_vmbus_channel),
225 KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!"));
229 mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF);
230 mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
232 TAILQ_INIT(&channel->sc_list_anchor);
238 * @brief Release the vmbus channel object itself
241 ReleaseVmbusChannel(void *context)
243 hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
244 free(channel, M_DEVBUF);
248 * @brief Release the resources used by the vmbus channel object
251 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
253 mtx_destroy(&channel->sc_lock);
254 mtx_destroy(&channel->inbound_lock);
256 * We have to release the channel's workqueue/thread in
257 * the vmbus's workqueue/thread context
258 * ie we can't destroy ourselves
260 hv_queue_work_item(hv_vmbus_g_connection.work_queue,
261 ReleaseVmbusChannel, (void *) channel);
265 * @brief Process the offer by creating a channel/device
266 * associated with this offer
269 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
272 hv_vmbus_channel* channel;
278 relid = new_channel->offer_msg.child_rel_id;
280 * Make sure this is a new offer
282 mtx_lock(&hv_vmbus_g_connection.channel_lock);
283 hv_vmbus_g_connection.channels[relid] = new_channel;
285 TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
288 if (memcmp(&channel->offer_msg.offer.interface_type,
289 &new_channel->offer_msg.offer.interface_type,
290 sizeof(hv_guid)) == 0 &&
291 memcmp(&channel->offer_msg.offer.interface_instance,
292 &new_channel->offer_msg.offer.interface_instance,
293 sizeof(hv_guid)) == 0) {
302 &hv_vmbus_g_connection.channel_anchor,
306 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
308 /*XXX add new channel to percpu_list */
312 * Check if this is a sub channel.
314 if (new_channel->offer_msg.offer.sub_channel_index != 0) {
316 * It is a sub channel offer, process it.
318 new_channel->primary_channel = channel;
319 mtx_lock(&channel->sc_lock);
321 &channel->sc_list_anchor,
324 mtx_unlock(&channel->sc_lock);
326 /* Insert new channel into channel_anchor. */
327 printf("VMBUS get multi-channel offer, rel=%u,sub=%u\n",
328 new_channel->offer_msg.child_rel_id,
329 new_channel->offer_msg.offer.sub_channel_index);
330 mtx_lock(&hv_vmbus_g_connection.channel_lock);
331 TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
332 new_channel, list_entry);
333 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
336 printf("VMBUS: new multi-channel offer <%p>, "
337 "its primary channel is <%p>.\n",
338 new_channel, new_channel->primary_channel);
340 /*XXX add it to percpu_list */
342 new_channel->state = HV_CHANNEL_OPEN_STATE;
343 if (channel->sc_creation_callback != NULL) {
344 channel->sc_creation_callback(new_channel);
349 hv_vmbus_free_vmbus_channel(new_channel);
353 new_channel->state = HV_CHANNEL_OPEN_STATE;
356 * Start the process of binding this offer to the driver
357 * (We need to set the device field before calling
358 * hv_vmbus_child_device_add())
360 new_channel->device = hv_vmbus_child_device_create(
361 new_channel->offer_msg.offer.interface_type,
362 new_channel->offer_msg.offer.interface_instance, new_channel);
365 * Add the new device to the bus. This will kick off device-driver
366 * binding which eventually invokes the device driver's AddDevice()
369 ret = hv_vmbus_child_device_register(new_channel->device);
371 mtx_lock(&hv_vmbus_g_connection.channel_lock);
373 &hv_vmbus_g_connection.channel_anchor,
376 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
377 hv_vmbus_free_vmbus_channel(new_channel);
382 * Array of device guids that are performance critical. We try to distribute
383 * the interrupt load for these devices across all online cpus.
385 static const hv_guid high_perf_devices[] = {
399 * We use this static number to distribute the channel interrupt load.
401 static uint32_t next_vcpu;
404 * Starting with Win8, we can statically distribute the incoming
405 * channel interrupt load by binding a channel to VCPU. We
406 * implement here a simple round robin scheme for distributing
407 * the interrupt load.
408 * We will bind channels that are not performance critical to cpu 0 and
409 * performance critical channels (IDE, SCSI and Network) will be uniformly
410 * distributed across all available CPUs.
413 vmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid)
415 uint32_t current_cpu;
417 boolean_t is_perf_channel = FALSE;
419 for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
420 if (memcmp(guid->data, high_perf_devices[i].data,
421 sizeof(hv_guid)) == 0) {
422 is_perf_channel = TRUE;
427 if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
428 (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) ||
429 (!is_perf_channel)) {
430 /* Host's view of guest cpu */
431 channel->target_vcpu = 0;
432 /* Guest's own view of cpu */
433 channel->target_cpu = 0;
436 /* mp_ncpus should have the number cpus currently online */
437 current_cpu = (++next_vcpu % mp_ncpus);
438 channel->target_cpu = current_cpu;
439 channel->target_vcpu =
440 hv_vmbus_g_context.hv_vcpu_index[current_cpu];
442 printf("VMBUS: Total online cpus %d, assign perf channel %d "
443 "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu,
448 * @brief Handler for channel offers from Hyper-V/Azure
450 * Handler for channel offers from vmbus in parent partition. We ignore
451 * all offers except network and storage offers. For each network and storage
452 * offers, we create a channel object and queue a work item to the channel
453 * object to process the offer synchronously
456 vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
458 hv_vmbus_channel_offer_channel* offer;
459 hv_vmbus_channel* new_channel;
461 offer = (hv_vmbus_channel_offer_channel*) hdr;
464 hv_guid *guidInstance;
466 guidType = &offer->offer.interface_type;
467 guidInstance = &offer->offer.interface_instance;
469 /* Allocate the channel object and save this offer */
470 new_channel = hv_vmbus_allocate_channel();
471 if (new_channel == NULL)
475 * By default we setup state to enable batched
476 * reading. A specific service can choose to
477 * disable this prior to opening the channel.
479 new_channel->batched_reading = TRUE;
481 new_channel->signal_event_param =
482 (hv_vmbus_input_signal_event *)
483 (HV_ALIGN_UP((unsigned long)
484 &new_channel->signal_event_buffer,
485 HV_HYPERCALL_PARAM_ALIGN));
487 new_channel->signal_event_param->connection_id.as_uint32_t = 0;
488 new_channel->signal_event_param->connection_id.u.id =
489 HV_VMBUS_EVENT_CONNECTION_ID;
490 new_channel->signal_event_param->flag_number = 0;
491 new_channel->signal_event_param->rsvd_z = 0;
493 if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
494 new_channel->is_dedicated_interrupt =
495 (offer->is_dedicated_interrupt != 0);
496 new_channel->signal_event_param->connection_id.u.id =
497 offer->connection_id;
501 * Bind the channel to a chosen cpu.
503 vmbus_channel_select_cpu(new_channel,
504 &offer->offer.interface_type);
506 memcpy(&new_channel->offer_msg, offer,
507 sizeof(hv_vmbus_channel_offer_channel));
508 new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
509 new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
511 vmbus_channel_process_offer(new_channel);
515 * @brief Rescind offer handler.
517 * We queue a work item to process this offer
521 vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
523 hv_vmbus_channel_rescind_offer* rescind;
524 hv_vmbus_channel* channel;
526 rescind = (hv_vmbus_channel_rescind_offer*) hdr;
528 channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
532 hv_vmbus_child_device_unregister(channel->device);
533 mtx_lock(&hv_vmbus_g_connection.channel_lock);
534 hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
535 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
540 * @brief Invoked when all offers have been delivered.
543 vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
548 * @brief Open result handler.
550 * This is invoked when we received a response
551 * to our channel open request. Find the matching request, copy the
552 * response and signal the requesting thread.
555 vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
557 hv_vmbus_channel_open_result* result;
558 hv_vmbus_channel_msg_info* msg_info;
559 hv_vmbus_channel_msg_header* requestHeader;
560 hv_vmbus_channel_open_channel* openMsg;
562 result = (hv_vmbus_channel_open_result*) hdr;
565 * Find the open msg, copy the result and signal/unblock the wait event
567 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
569 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
571 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
573 if (requestHeader->message_type ==
574 HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
575 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
576 if (openMsg->child_rel_id == result->child_rel_id
577 && openMsg->open_id == result->open_id) {
578 memcpy(&msg_info->response.open_result, result,
579 sizeof(hv_vmbus_channel_open_result));
580 sema_post(&msg_info->wait_sema);
585 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
590 * @brief GPADL created handler.
592 * This is invoked when we received a response
593 * to our gpadl create request. Find the matching request, copy the
594 * response and signal the requesting thread.
597 vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
599 hv_vmbus_channel_gpadl_created* gpadl_created;
600 hv_vmbus_channel_msg_info* msg_info;
601 hv_vmbus_channel_msg_header* request_header;
602 hv_vmbus_channel_gpadl_header* gpadl_header;
604 gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
606 /* Find the establish msg, copy the result and signal/unblock
609 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
610 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
612 request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
613 if (request_header->message_type ==
614 HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
616 (hv_vmbus_channel_gpadl_header*) request_header;
618 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
619 && (gpadl_created->gpadl == gpadl_header->gpadl)) {
620 memcpy(&msg_info->response.gpadl_created,
622 sizeof(hv_vmbus_channel_gpadl_created));
623 sema_post(&msg_info->wait_sema);
628 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
632 * @brief GPADL torndown handler.
634 * This is invoked when we received a respons
635 * to our gpadl teardown request. Find the matching request, copy the
636 * response and signal the requesting thread
639 vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
641 hv_vmbus_channel_gpadl_torndown* gpadl_torndown;
642 hv_vmbus_channel_msg_info* msg_info;
643 hv_vmbus_channel_msg_header* requestHeader;
644 hv_vmbus_channel_gpadl_teardown* gpadlTeardown;
646 gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
649 * Find the open msg, copy the result and signal/unblock the
653 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
655 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
657 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
659 if (requestHeader->message_type
660 == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
662 (hv_vmbus_channel_gpadl_teardown*) requestHeader;
664 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
665 memcpy(&msg_info->response.gpadl_torndown,
667 sizeof(hv_vmbus_channel_gpadl_torndown));
668 sema_post(&msg_info->wait_sema);
673 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
677 * @brief Version response handler.
679 * This is invoked when we received a response
680 * to our initiate contact request. Find the matching request, copy th
681 * response and signal the requesting thread.
684 vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
686 hv_vmbus_channel_msg_info* msg_info;
687 hv_vmbus_channel_msg_header* requestHeader;
688 hv_vmbus_channel_initiate_contact* initiate;
689 hv_vmbus_channel_version_response* versionResponse;
691 versionResponse = (hv_vmbus_channel_version_response*)hdr;
693 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
694 TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
696 requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
697 if (requestHeader->message_type
698 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
700 (hv_vmbus_channel_initiate_contact*) requestHeader;
701 memcpy(&msg_info->response.version_response,
703 sizeof(hv_vmbus_channel_version_response));
704 sema_post(&msg_info->wait_sema);
707 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
712 * @brief Handler for channel protocol messages.
714 * This is invoked in the vmbus worker thread context.
717 hv_vmbus_on_channel_message(void *context)
719 hv_vmbus_message* msg;
720 hv_vmbus_channel_msg_header* hdr;
723 msg = (hv_vmbus_message*) context;
724 hdr = (hv_vmbus_channel_msg_header*) msg->u.payload;
725 size = msg->header.payload_size;
727 if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) {
732 if (g_channel_message_table[hdr->message_type].messageHandler) {
733 g_channel_message_table[hdr->message_type].messageHandler(hdr);
736 /* Free the msg that was allocated in VmbusOnMsgDPC() */
741 * @brief Send a request to get all our pending offers.
744 hv_vmbus_request_channel_offers(void)
747 hv_vmbus_channel_msg_header* msg;
748 hv_vmbus_channel_msg_info* msg_info;
750 msg_info = (hv_vmbus_channel_msg_info *)
751 malloc(sizeof(hv_vmbus_channel_msg_info)
752 + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
754 if (msg_info == NULL) {
756 printf("Error VMBUS: malloc failed for Request Offers\n");
760 msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
761 msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
763 ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
766 free(msg_info, M_DEVBUF);
772 * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
775 hv_vmbus_release_unattached_channels(void)
777 hv_vmbus_channel *channel;
779 mtx_lock(&hv_vmbus_g_connection.channel_lock);
781 while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
782 channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
783 TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
784 channel, list_entry);
786 hv_vmbus_child_device_unregister(channel->device);
787 hv_vmbus_free_vmbus_channel(channel);
789 bzero(hv_vmbus_g_connection.channels,
790 sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT);
791 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
795 * @brief Select the best outgoing channel
797 * The channel whose vcpu binding is closest to the currect vcpu will
799 * If no multi-channel, always select primary channel
801 * @param primary - primary channel
803 struct hv_vmbus_channel *
804 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
806 hv_vmbus_channel *new_channel = NULL;
807 hv_vmbus_channel *outgoing_channel = primary;
808 int old_cpu_distance = 0;
809 int new_cpu_distance = 0;
811 int smp_pro_id = PCPU_GET(cpuid);
813 if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
814 return outgoing_channel;
817 if (smp_pro_id >= MAXCPU) {
818 return outgoing_channel;
821 cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id];
823 TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
824 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
828 if (new_channel->target_vcpu == cur_vcpu){
832 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
833 (outgoing_channel->target_vcpu - cur_vcpu) :
834 (cur_vcpu - outgoing_channel->target_vcpu));
836 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
837 (new_channel->target_vcpu - cur_vcpu) :
838 (cur_vcpu - new_channel->target_vcpu));
840 if (old_cpu_distance < new_cpu_distance) {
844 outgoing_channel = new_channel;
847 return(outgoing_channel);