]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
MFC 298039,298042
[FreeBSD/stable/10.git] / sys / dev / hyperv / vmbus / hv_channel_mgmt.c
1 /*-
2  * Copyright (c) 2009-2012 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
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.
16  *
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.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/mbuf.h>
34
35 #include "hv_vmbus_priv.h"
36
37 /*
38  * Internal functions
39  */
40
41 static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr);
42 static void vmbus_channel_on_offer_internal(void* context);
43 static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr);
44 static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr);
45 static void vmbus_channel_on_offer_rescind_internal(void* context);
46 static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr);
47 static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr);
48 static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr);
49 static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr);
50
51 /**
52  * Channel message dispatch table
53  */
54 hv_vmbus_channel_msg_table_entry
55     g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
56         { HV_CHANNEL_MESSAGE_INVALID,
57                 NULL },
58         { HV_CHANNEL_MESSAGE_OFFER_CHANNEL,
59                 vmbus_channel_on_offer },
60         { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
61                 vmbus_channel_on_offer_rescind },
62         { HV_CHANNEL_MESSAGE_REQUEST_OFFERS,
63                 NULL },
64         { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
65                 vmbus_channel_on_offers_delivered },
66         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL,
67                 NULL },
68         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
69                 vmbus_channel_on_open_result },
70         { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL,
71                 NULL },
72         { HV_CHANNEL_MESSAGEL_GPADL_HEADER,
73                 NULL },
74         { HV_CHANNEL_MESSAGE_GPADL_BODY,
75                 NULL },
76         { HV_CHANNEL_MESSAGE_GPADL_CREATED,
77                 vmbus_channel_on_gpadl_created },
78         { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN,
79                 NULL },
80         { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
81                 vmbus_channel_on_gpadl_torndown },
82         { HV_CHANNEL_MESSAGE_REL_ID_RELEASED,
83                 NULL },
84         { HV_CHANNEL_MESSAGE_INITIATED_CONTACT,
85                 NULL },
86         { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
87                 vmbus_channel_on_version_response },
88         { HV_CHANNEL_MESSAGE_UNLOAD,
89                 NULL }
90 };
91
92 typedef struct hv_work_item {
93         struct task     work;
94         void            (*callback)(void *);
95         void*           context;
96 } hv_work_item;
97
98 /**
99  * Implementation of the work abstraction.
100  */
101 static void
102 work_item_callback(void *work, int pending)
103 {
104         struct hv_work_item *w = (struct hv_work_item *)work;
105
106         w->callback(w->context);
107
108         free(w, M_DEVBUF);
109 }
110
111 /**
112  * @brief Create work item
113  */
114 static int
115 hv_queue_work_item(
116         void (*callback)(void *), void *context)
117 {
118         struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
119                                         M_DEVBUF, M_NOWAIT);
120         KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
121         if (w == NULL)
122             return (ENOMEM);
123
124         w->callback = callback;
125         w->context = context;
126
127         TASK_INIT(&w->work, 0, work_item_callback, w);
128
129         return (taskqueue_enqueue(taskqueue_thread, &w->work));
130 }
131
132
133 /**
134  * @brief Allocate and initialize a vmbus channel object
135  */
136 hv_vmbus_channel*
137 hv_vmbus_allocate_channel(void)
138 {
139         hv_vmbus_channel* channel;
140
141         channel = (hv_vmbus_channel*) malloc(
142                                         sizeof(hv_vmbus_channel),
143                                         M_DEVBUF,
144                                         M_WAITOK | M_ZERO);
145
146         mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
147         TAILQ_INIT(&channel->sc_list_anchor);
148
149         return (channel);
150 }
151
152 /**
153  * @brief Release the resources used by the vmbus channel object
154  */
155 void
156 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
157 {
158         mtx_destroy(&channel->sc_lock);
159         free(channel, M_DEVBUF);
160 }
161
162 /**
163  * @brief Process the offer by creating a channel/device
164  * associated with this offer
165  */
166 static void
167 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
168 {
169         boolean_t               f_new;
170         hv_vmbus_channel*       channel;
171         int                     ret;
172         uint32_t                relid;
173
174         f_new = TRUE;
175         channel = NULL;
176         relid = new_channel->offer_msg.child_rel_id;
177         /*
178          * Make sure this is a new offer
179          */
180         mtx_lock(&hv_vmbus_g_connection.channel_lock);
181         hv_vmbus_g_connection.channels[relid] = new_channel;
182
183         TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
184             list_entry)
185         {
186                 if (memcmp(&channel->offer_msg.offer.interface_type,
187                     &new_channel->offer_msg.offer.interface_type,
188                     sizeof(hv_guid)) == 0 &&
189                     memcmp(&channel->offer_msg.offer.interface_instance,
190                     &new_channel->offer_msg.offer.interface_instance,
191                     sizeof(hv_guid)) == 0) {
192                         f_new = FALSE;
193                         break;
194                 }
195         }
196
197         if (f_new) {
198                 /* Insert at tail */
199                 TAILQ_INSERT_TAIL(
200                     &hv_vmbus_g_connection.channel_anchor,
201                     new_channel,
202                     list_entry);
203         }
204         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
205
206         /*XXX add new channel to percpu_list */
207
208         if (!f_new) {
209                 /*
210                  * Check if this is a sub channel.
211                  */
212                 if (new_channel->offer_msg.offer.sub_channel_index != 0) {
213                         /*
214                          * It is a sub channel offer, process it.
215                          */
216                         new_channel->primary_channel = channel;
217                         new_channel->device = channel->device;
218                         mtx_lock(&channel->sc_lock);
219                         TAILQ_INSERT_TAIL(
220                             &channel->sc_list_anchor,
221                             new_channel,
222                             sc_list_entry);
223                         mtx_unlock(&channel->sc_lock);
224
225                         if (bootverbose) {
226                                 printf("VMBUS get multi-channel offer, "
227                                     "rel=%u, sub=%u\n",
228                                     new_channel->offer_msg.child_rel_id,
229                                     new_channel->offer_msg.offer.sub_channel_index);    
230                         }
231
232                         /* Insert new channel into channel_anchor. */
233                         mtx_lock(&hv_vmbus_g_connection.channel_lock);
234                         TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
235                             new_channel, list_entry);                           
236                         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
237
238                         if(bootverbose)
239                                 printf("VMBUS: new multi-channel offer <%p>, "
240                                     "its primary channel is <%p>.\n",
241                                     new_channel, new_channel->primary_channel);
242
243                         /*XXX add it to percpu_list */
244
245                         new_channel->state = HV_CHANNEL_OPEN_STATE;
246                         if (channel->sc_creation_callback != NULL) {
247                                 channel->sc_creation_callback(new_channel);
248                         }
249                         return;
250                 }
251
252             hv_vmbus_free_vmbus_channel(new_channel);
253             return;
254         }
255
256         new_channel->state = HV_CHANNEL_OPEN_STATE;
257
258         /*
259          * Start the process of binding this offer to the driver
260          * (We need to set the device field before calling
261          * hv_vmbus_child_device_add())
262          */
263         new_channel->device = hv_vmbus_child_device_create(
264             new_channel->offer_msg.offer.interface_type,
265             new_channel->offer_msg.offer.interface_instance, new_channel);
266
267         /*
268          * Add the new device to the bus. This will kick off device-driver
269          * binding which eventually invokes the device driver's AddDevice()
270          * method.
271          */
272         ret = hv_vmbus_child_device_register(new_channel->device);
273         if (ret != 0) {
274                 mtx_lock(&hv_vmbus_g_connection.channel_lock);
275                 TAILQ_REMOVE(
276                     &hv_vmbus_g_connection.channel_anchor,
277                     new_channel,
278                     list_entry);
279                 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
280                 hv_vmbus_free_vmbus_channel(new_channel);
281         }
282 }
283
284 void
285 vmbus_channel_cpu_set(struct hv_vmbus_channel *chan, int cpu)
286 {
287         KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu %d", cpu));
288
289         if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
290             hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) {
291                 /* Only cpu0 is supported */
292                 cpu = 0;
293         }
294
295         chan->target_cpu = cpu;
296         chan->target_vcpu = hv_vmbus_g_context.hv_vcpu_index[cpu];
297
298         if (bootverbose) {
299                 printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n",
300                     chan->offer_msg.child_rel_id,
301                     chan->target_cpu, chan->target_vcpu);
302         }
303 }
304
305 /**
306  * Array of device guids that are performance critical. We try to distribute
307  * the interrupt load for these devices across all online cpus. 
308  */
309 static const hv_guid high_perf_devices[] = {
310         {HV_NIC_GUID, },
311         {HV_IDE_GUID, },
312         {HV_SCSI_GUID, },
313 };
314
315 enum {
316         PERF_CHN_NIC = 0,
317         PERF_CHN_IDE,
318         PERF_CHN_SCSI,
319         MAX_PERF_CHN,
320 };
321
322 /*
323  * We use this static number to distribute the channel interrupt load.
324  */
325 static uint32_t next_vcpu;
326
327 /**
328  * Starting with Win8, we can statically distribute the incoming
329  * channel interrupt load by binding a channel to VCPU. We
330  * implement here a simple round robin scheme for distributing
331  * the interrupt load.
332  * We will bind channels that are not performance critical to cpu 0 and
333  * performance critical channels (IDE, SCSI and Network) will be uniformly
334  * distributed across all available CPUs.
335  */
336 static void
337 vmbus_channel_select_defcpu(struct hv_vmbus_channel *channel)
338 {
339         uint32_t current_cpu;
340         int i;
341         boolean_t is_perf_channel = FALSE;
342         const hv_guid *guid = &channel->offer_msg.offer.interface_type;
343
344         for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
345                 if (memcmp(guid->data, high_perf_devices[i].data,
346                     sizeof(hv_guid)) == 0) {
347                         is_perf_channel = TRUE;
348                         break;
349                 }
350         }
351
352         if (!is_perf_channel) {
353                 /* Stick to cpu0 */
354                 vmbus_channel_cpu_set(channel, 0);
355                 return;
356         }
357         /* mp_ncpus should have the number cpus currently online */
358         current_cpu = (++next_vcpu % mp_ncpus);
359         vmbus_channel_cpu_set(channel, current_cpu);
360 }
361
362 /**
363  * @brief Handler for channel offers from Hyper-V/Azure
364  *
365  * Handler for channel offers from vmbus in parent partition. We ignore
366  * all offers except network and storage offers. For each network and storage
367  * offers, we create a channel object and queue a work item to the channel
368  * object to process the offer synchronously
369  */
370 static void
371 vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
372 {
373         hv_vmbus_channel_offer_channel* offer;
374         hv_vmbus_channel_offer_channel* copied;
375
376         offer = (hv_vmbus_channel_offer_channel*) hdr;
377
378         hv_guid *guidType;
379         hv_guid *guidInstance;
380
381         guidType = &offer->offer.interface_type;
382         guidInstance = &offer->offer.interface_instance;
383
384         // copy offer data
385         copied = malloc(sizeof(*copied), M_DEVBUF, M_NOWAIT);
386         if (copied == NULL) {
387                 printf("fail to allocate memory\n");
388                 return;
389         }
390
391         memcpy(copied, hdr, sizeof(*copied));
392         hv_queue_work_item(vmbus_channel_on_offer_internal, copied);
393 }
394
395 static void
396 vmbus_channel_on_offer_internal(void* context)
397 {
398         hv_vmbus_channel* new_channel;
399
400         hv_vmbus_channel_offer_channel* offer = (hv_vmbus_channel_offer_channel*)context;
401         /* Allocate the channel object and save this offer */
402         new_channel = hv_vmbus_allocate_channel();
403
404         /*
405          * By default we setup state to enable batched
406          * reading. A specific service can choose to
407          * disable this prior to opening the channel.
408          */
409         new_channel->batched_reading = TRUE;
410
411         new_channel->signal_event_param =
412             (hv_vmbus_input_signal_event *)
413             (HV_ALIGN_UP((unsigned long)
414                 &new_channel->signal_event_buffer,
415                 HV_HYPERCALL_PARAM_ALIGN));
416
417         new_channel->signal_event_param->connection_id.as_uint32_t = 0; 
418         new_channel->signal_event_param->connection_id.u.id =
419             HV_VMBUS_EVENT_CONNECTION_ID;
420         new_channel->signal_event_param->flag_number = 0;
421         new_channel->signal_event_param->rsvd_z = 0;
422
423         if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
424                 new_channel->is_dedicated_interrupt =
425                     (offer->is_dedicated_interrupt != 0);
426                 new_channel->signal_event_param->connection_id.u.id =
427                     offer->connection_id;
428         }
429
430         memcpy(&new_channel->offer_msg, offer,
431             sizeof(hv_vmbus_channel_offer_channel));
432         new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
433         new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
434
435         /* Select default cpu for this channel. */
436         vmbus_channel_select_defcpu(new_channel);
437
438         vmbus_channel_process_offer(new_channel);
439
440         free(offer, M_DEVBUF);
441 }
442
443 /**
444  * @brief Rescind offer handler.
445  *
446  * We queue a work item to process this offer
447  * synchronously
448  */
449 static void
450 vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
451 {
452         hv_vmbus_channel_rescind_offer* rescind;
453         hv_vmbus_channel*               channel;
454
455         rescind = (hv_vmbus_channel_rescind_offer*) hdr;
456
457         channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
458         if (channel == NULL)
459             return;
460
461         hv_queue_work_item(vmbus_channel_on_offer_rescind_internal, channel);
462         hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
463 }
464
465 static void
466 vmbus_channel_on_offer_rescind_internal(void *context)
467 {
468         hv_vmbus_channel*               channel;
469
470         channel = (hv_vmbus_channel*)context;
471         if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
472                 /* Only primary channel owns the hv_device */
473                 hv_vmbus_child_device_unregister(channel->device);
474         }
475 }
476
477 /**
478  *
479  * @brief Invoked when all offers have been delivered.
480  */
481 static void
482 vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
483 {
484 }
485
486 /**
487  * @brief Open result handler.
488  *
489  * This is invoked when we received a response
490  * to our channel open request. Find the matching request, copy the
491  * response and signal the requesting thread.
492  */
493 static void
494 vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
495 {
496         hv_vmbus_channel_open_result*   result;
497         hv_vmbus_channel_msg_info*      msg_info;
498         hv_vmbus_channel_msg_header*    requestHeader;
499         hv_vmbus_channel_open_channel*  openMsg;
500
501         result = (hv_vmbus_channel_open_result*) hdr;
502
503         /*
504          * Find the open msg, copy the result and signal/unblock the wait event
505          */
506         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
507
508         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
509             msg_list_entry) {
510             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
511
512             if (requestHeader->message_type ==
513                     HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
514                 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
515                 if (openMsg->child_rel_id == result->child_rel_id
516                     && openMsg->open_id == result->open_id) {
517                     memcpy(&msg_info->response.open_result, result,
518                         sizeof(hv_vmbus_channel_open_result));
519                     sema_post(&msg_info->wait_sema);
520                     break;
521                 }
522             }
523         }
524         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
525
526 }
527
528 /**
529  * @brief GPADL created handler.
530  *
531  * This is invoked when we received a response
532  * to our gpadl create request. Find the matching request, copy the
533  * response and signal the requesting thread.
534  */
535 static void
536 vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
537 {
538         hv_vmbus_channel_gpadl_created*         gpadl_created;
539         hv_vmbus_channel_msg_info*              msg_info;
540         hv_vmbus_channel_msg_header*            request_header;
541         hv_vmbus_channel_gpadl_header*          gpadl_header;
542
543         gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
544
545         /* Find the establish msg, copy the result and signal/unblock
546          * the wait event
547          */
548         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
549         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
550                 msg_list_entry) {
551             request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
552             if (request_header->message_type ==
553                     HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
554                 gpadl_header =
555                     (hv_vmbus_channel_gpadl_header*) request_header;
556
557                 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
558                     && (gpadl_created->gpadl == gpadl_header->gpadl)) {
559                     memcpy(&msg_info->response.gpadl_created,
560                         gpadl_created,
561                         sizeof(hv_vmbus_channel_gpadl_created));
562                     sema_post(&msg_info->wait_sema);
563                     break;
564                 }
565             }
566         }
567         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
568 }
569
570 /**
571  * @brief GPADL torndown handler.
572  *
573  * This is invoked when we received a respons
574  * to our gpadl teardown request. Find the matching request, copy the
575  * response and signal the requesting thread
576  */
577 static void
578 vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
579 {
580         hv_vmbus_channel_gpadl_torndown*        gpadl_torndown;
581         hv_vmbus_channel_msg_info*              msg_info;
582         hv_vmbus_channel_msg_header*            requestHeader;
583         hv_vmbus_channel_gpadl_teardown*        gpadlTeardown;
584
585         gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
586
587         /*
588          * Find the open msg, copy the result and signal/unblock the
589          * wait event.
590          */
591
592         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
593
594         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
595                 msg_list_entry) {
596             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
597
598             if (requestHeader->message_type
599                     == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
600                 gpadlTeardown =
601                     (hv_vmbus_channel_gpadl_teardown*) requestHeader;
602
603                 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
604                     memcpy(&msg_info->response.gpadl_torndown,
605                         gpadl_torndown,
606                         sizeof(hv_vmbus_channel_gpadl_torndown));
607                     sema_post(&msg_info->wait_sema);
608                     break;
609                 }
610             }
611         }
612     mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
613 }
614
615 /**
616  * @brief Version response handler.
617  *
618  * This is invoked when we received a response
619  * to our initiate contact request. Find the matching request, copy th
620  * response and signal the requesting thread.
621  */
622 static void
623 vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
624 {
625         hv_vmbus_channel_msg_info*              msg_info;
626         hv_vmbus_channel_msg_header*            requestHeader;
627         hv_vmbus_channel_initiate_contact*      initiate;
628         hv_vmbus_channel_version_response*      versionResponse;
629
630         versionResponse = (hv_vmbus_channel_version_response*)hdr;
631
632         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
633         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
634             msg_list_entry) {
635             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
636             if (requestHeader->message_type
637                 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
638                 initiate =
639                     (hv_vmbus_channel_initiate_contact*) requestHeader;
640                 memcpy(&msg_info->response.version_response,
641                     versionResponse,
642                     sizeof(hv_vmbus_channel_version_response));
643                 sema_post(&msg_info->wait_sema);
644             }
645         }
646     mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
647
648 }
649
650 /**
651  *  @brief Send a request to get all our pending offers.
652  */
653 int
654 hv_vmbus_request_channel_offers(void)
655 {
656         int                             ret;
657         hv_vmbus_channel_msg_header*    msg;
658         hv_vmbus_channel_msg_info*      msg_info;
659
660         msg_info = (hv_vmbus_channel_msg_info *)
661             malloc(sizeof(hv_vmbus_channel_msg_info)
662                     + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
663
664         if (msg_info == NULL) {
665             if(bootverbose)
666                 printf("Error VMBUS: malloc failed for Request Offers\n");
667             return (ENOMEM);
668         }
669
670         msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
671         msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
672
673         ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
674
675         free(msg_info, M_DEVBUF);
676
677         return (ret);
678 }
679
680 /**
681  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
682  */
683 void
684 hv_vmbus_release_unattached_channels(void) 
685 {
686         hv_vmbus_channel *channel;
687
688         mtx_lock(&hv_vmbus_g_connection.channel_lock);
689
690         while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
691             channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
692             TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
693                             channel, list_entry);
694
695             if (HV_VMBUS_CHAN_ISPRIMARY(channel)) {
696                 /* Only primary channel owns the hv_device */
697                 hv_vmbus_child_device_unregister(channel->device);
698             }
699             hv_vmbus_free_vmbus_channel(channel);
700         }
701         bzero(hv_vmbus_g_connection.channels, 
702                 sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT);
703         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
704 }
705
706 /**
707  * @brief Select the best outgoing channel
708  * 
709  * The channel whose vcpu binding is closest to the currect vcpu will
710  * be selected.
711  * If no multi-channel, always select primary channel
712  * 
713  * @param primary - primary channel
714  */
715 struct hv_vmbus_channel *
716 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
717 {
718         hv_vmbus_channel *new_channel = NULL;
719         hv_vmbus_channel *outgoing_channel = primary;
720         int old_cpu_distance = 0;
721         int new_cpu_distance = 0;
722         int cur_vcpu = 0;
723         int smp_pro_id = PCPU_GET(cpuid);
724
725         if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
726                 return outgoing_channel;
727         }
728
729         if (smp_pro_id >= MAXCPU) {
730                 return outgoing_channel;
731         }
732
733         cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id];
734         
735         TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
736                 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
737                         continue;
738                 }
739
740                 if (new_channel->target_vcpu == cur_vcpu){
741                         return new_channel;
742                 }
743
744                 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
745                     (outgoing_channel->target_vcpu - cur_vcpu) :
746                     (cur_vcpu - outgoing_channel->target_vcpu));
747
748                 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
749                     (new_channel->target_vcpu - cur_vcpu) :
750                     (cur_vcpu - new_channel->target_vcpu));
751
752                 if (old_cpu_distance < new_cpu_distance) {
753                         continue;
754                 }
755
756                 outgoing_channel = new_channel;
757         }
758
759         return(outgoing_channel);
760 }