]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
MFC 295307,295308,295309,295606
[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->inbound_lock, "channel inbound", NULL, MTX_DEF);
147         mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
148
149         TAILQ_INIT(&channel->sc_list_anchor);
150
151         return (channel);
152 }
153
154 /**
155  * @brief Release the resources used by the vmbus channel object
156  */
157 void
158 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
159 {
160         mtx_destroy(&channel->sc_lock);
161         mtx_destroy(&channel->inbound_lock);
162
163         free(channel, M_DEVBUF);
164 }
165
166 /**
167  * @brief Process the offer by creating a channel/device
168  * associated with this offer
169  */
170 static void
171 vmbus_channel_process_offer(hv_vmbus_channel *new_channel)
172 {
173         boolean_t               f_new;
174         hv_vmbus_channel*       channel;
175         int                     ret;
176         uint32_t                relid;
177
178         f_new = TRUE;
179         channel = NULL;
180         relid = new_channel->offer_msg.child_rel_id;
181         /*
182          * Make sure this is a new offer
183          */
184         mtx_lock(&hv_vmbus_g_connection.channel_lock);
185         hv_vmbus_g_connection.channels[relid] = new_channel;
186
187         TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
188             list_entry)
189         {
190                 if (memcmp(&channel->offer_msg.offer.interface_type,
191                     &new_channel->offer_msg.offer.interface_type,
192                     sizeof(hv_guid)) == 0 &&
193                     memcmp(&channel->offer_msg.offer.interface_instance,
194                     &new_channel->offer_msg.offer.interface_instance,
195                     sizeof(hv_guid)) == 0) {
196                         f_new = FALSE;
197                         break;
198                 }
199         }
200
201         if (f_new) {
202                 /* Insert at tail */
203                 TAILQ_INSERT_TAIL(
204                     &hv_vmbus_g_connection.channel_anchor,
205                     new_channel,
206                     list_entry);
207         }
208         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
209
210         /*XXX add new channel to percpu_list */
211
212         if (!f_new) {
213                 /*
214                  * Check if this is a sub channel.
215                  */
216                 if (new_channel->offer_msg.offer.sub_channel_index != 0) {
217                         /*
218                          * It is a sub channel offer, process it.
219                          */
220                         new_channel->primary_channel = channel;
221                         mtx_lock(&channel->sc_lock);
222                         TAILQ_INSERT_TAIL(
223                             &channel->sc_list_anchor,
224                             new_channel,
225                             sc_list_entry);
226                         mtx_unlock(&channel->sc_lock);
227
228                         /* Insert new channel into channel_anchor. */
229                         printf("VMBUS get multi-channel offer, rel=%u,sub=%u\n",
230                             new_channel->offer_msg.child_rel_id,
231                             new_channel->offer_msg.offer.sub_channel_index);    
232                         mtx_lock(&hv_vmbus_g_connection.channel_lock);
233                         TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
234                             new_channel, list_entry);                           
235                         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
236
237                         if(bootverbose)
238                                 printf("VMBUS: new multi-channel offer <%p>, "
239                                     "its primary channel is <%p>.\n",
240                                     new_channel, new_channel->primary_channel);
241
242                         /*XXX add it to percpu_list */
243
244                         new_channel->state = HV_CHANNEL_OPEN_STATE;
245                         if (channel->sc_creation_callback != NULL) {
246                                 channel->sc_creation_callback(new_channel);
247                         }
248                         return;
249                 }
250
251             hv_vmbus_free_vmbus_channel(new_channel);
252             return;
253         }
254
255         new_channel->state = HV_CHANNEL_OPEN_STATE;
256
257         /*
258          * Start the process of binding this offer to the driver
259          * (We need to set the device field before calling
260          * hv_vmbus_child_device_add())
261          */
262         new_channel->device = hv_vmbus_child_device_create(
263             new_channel->offer_msg.offer.interface_type,
264             new_channel->offer_msg.offer.interface_instance, new_channel);
265
266         /*
267          * Add the new device to the bus. This will kick off device-driver
268          * binding which eventually invokes the device driver's AddDevice()
269          * method.
270          */
271         ret = hv_vmbus_child_device_register(new_channel->device);
272         if (ret != 0) {
273                 mtx_lock(&hv_vmbus_g_connection.channel_lock);
274                 TAILQ_REMOVE(
275                     &hv_vmbus_g_connection.channel_anchor,
276                     new_channel,
277                     list_entry);
278                 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
279                 hv_vmbus_free_vmbus_channel(new_channel);
280         }
281 }
282
283 /**
284  * Array of device guids that are performance critical. We try to distribute
285  * the interrupt load for these devices across all online cpus. 
286  */
287 static const hv_guid high_perf_devices[] = {
288         {HV_NIC_GUID, },
289         {HV_IDE_GUID, },
290         {HV_SCSI_GUID, },
291 };
292
293 enum {
294         PERF_CHN_NIC = 0,
295         PERF_CHN_IDE,
296         PERF_CHN_SCSI,
297         MAX_PERF_CHN,
298 };
299
300 /*
301  * We use this static number to distribute the channel interrupt load.
302  */
303 static uint32_t next_vcpu;
304
305 /**
306  * Starting with Win8, we can statically distribute the incoming
307  * channel interrupt load by binding a channel to VCPU. We
308  * implement here a simple round robin scheme for distributing
309  * the interrupt load.
310  * We will bind channels that are not performance critical to cpu 0 and
311  * performance critical channels (IDE, SCSI and Network) will be uniformly
312  * distributed across all available CPUs.
313  */
314 static void
315 vmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid)
316 {
317         uint32_t current_cpu;
318         int i;
319         boolean_t is_perf_channel = FALSE;
320
321         for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
322                 if (memcmp(guid->data, high_perf_devices[i].data,
323                     sizeof(hv_guid)) == 0) {
324                         is_perf_channel = TRUE;
325                         break;
326                 }
327         }
328
329         if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
330             (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) ||
331             (!is_perf_channel)) {
332                 /* Host's view of guest cpu */
333                 channel->target_vcpu = 0;
334                 /* Guest's own view of cpu */
335                 channel->target_cpu = 0;
336                 return;
337         }
338         /* mp_ncpus should have the number cpus currently online */
339         current_cpu = (++next_vcpu % mp_ncpus);
340         channel->target_cpu = current_cpu;
341         channel->target_vcpu =
342             hv_vmbus_g_context.hv_vcpu_index[current_cpu];
343         if (bootverbose)
344                 printf("VMBUS: Total online cpus %d, assign perf channel %d "
345                     "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu,
346                     current_cpu);
347 }
348
349 /**
350  * @brief Handler for channel offers from Hyper-V/Azure
351  *
352  * Handler for channel offers from vmbus in parent partition. We ignore
353  * all offers except network and storage offers. For each network and storage
354  * offers, we create a channel object and queue a work item to the channel
355  * object to process the offer synchronously
356  */
357 static void
358 vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
359 {
360         hv_vmbus_channel_offer_channel* offer;
361         hv_vmbus_channel_offer_channel* copied;
362
363         offer = (hv_vmbus_channel_offer_channel*) hdr;
364
365         hv_guid *guidType;
366         hv_guid *guidInstance;
367
368         guidType = &offer->offer.interface_type;
369         guidInstance = &offer->offer.interface_instance;
370
371         // copy offer data
372         copied = malloc(sizeof(*copied), M_DEVBUF, M_NOWAIT);
373         if (copied == NULL) {
374                 printf("fail to allocate memory\n");
375                 return;
376         }
377
378         memcpy(copied, hdr, sizeof(*copied));
379         hv_queue_work_item(vmbus_channel_on_offer_internal, copied);
380 }
381
382 static void
383 vmbus_channel_on_offer_internal(void* context)
384 {
385         hv_vmbus_channel* new_channel;
386
387         hv_vmbus_channel_offer_channel* offer = (hv_vmbus_channel_offer_channel*)context;
388         /* Allocate the channel object and save this offer */
389         new_channel = hv_vmbus_allocate_channel();
390
391         /*
392          * By default we setup state to enable batched
393          * reading. A specific service can choose to
394          * disable this prior to opening the channel.
395          */
396         new_channel->batched_reading = TRUE;
397
398         new_channel->signal_event_param =
399             (hv_vmbus_input_signal_event *)
400             (HV_ALIGN_UP((unsigned long)
401                 &new_channel->signal_event_buffer,
402                 HV_HYPERCALL_PARAM_ALIGN));
403
404         new_channel->signal_event_param->connection_id.as_uint32_t = 0; 
405         new_channel->signal_event_param->connection_id.u.id =
406             HV_VMBUS_EVENT_CONNECTION_ID;
407         new_channel->signal_event_param->flag_number = 0;
408         new_channel->signal_event_param->rsvd_z = 0;
409
410         if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
411                 new_channel->is_dedicated_interrupt =
412                     (offer->is_dedicated_interrupt != 0);
413                 new_channel->signal_event_param->connection_id.u.id =
414                     offer->connection_id;
415         }
416
417         /*
418          * Bind the channel to a chosen cpu.
419          */
420         vmbus_channel_select_cpu(new_channel,
421             &offer->offer.interface_type);
422
423         memcpy(&new_channel->offer_msg, offer,
424             sizeof(hv_vmbus_channel_offer_channel));
425         new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
426         new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
427
428         vmbus_channel_process_offer(new_channel);
429
430         free(offer, M_DEVBUF);
431 }
432
433 /**
434  * @brief Rescind offer handler.
435  *
436  * We queue a work item to process this offer
437  * synchronously
438  */
439 static void
440 vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
441 {
442         hv_vmbus_channel_rescind_offer* rescind;
443         hv_vmbus_channel*               channel;
444
445         rescind = (hv_vmbus_channel_rescind_offer*) hdr;
446
447         channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
448         if (channel == NULL)
449             return;
450
451         hv_queue_work_item(vmbus_channel_on_offer_rescind_internal, channel);
452         hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
453 }
454
455 static void
456 vmbus_channel_on_offer_rescind_internal(void *context)
457 {
458         hv_vmbus_channel*               channel;
459
460         channel = (hv_vmbus_channel*)context;
461         hv_vmbus_child_device_unregister(channel->device);
462 }
463
464 /**
465  *
466  * @brief Invoked when all offers have been delivered.
467  */
468 static void
469 vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
470 {
471 }
472
473 /**
474  * @brief Open result handler.
475  *
476  * This is invoked when we received a response
477  * to our channel open request. Find the matching request, copy the
478  * response and signal the requesting thread.
479  */
480 static void
481 vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
482 {
483         hv_vmbus_channel_open_result*   result;
484         hv_vmbus_channel_msg_info*      msg_info;
485         hv_vmbus_channel_msg_header*    requestHeader;
486         hv_vmbus_channel_open_channel*  openMsg;
487
488         result = (hv_vmbus_channel_open_result*) hdr;
489
490         /*
491          * Find the open msg, copy the result and signal/unblock the wait event
492          */
493         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
494
495         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
496             msg_list_entry) {
497             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
498
499             if (requestHeader->message_type ==
500                     HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
501                 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
502                 if (openMsg->child_rel_id == result->child_rel_id
503                     && openMsg->open_id == result->open_id) {
504                     memcpy(&msg_info->response.open_result, result,
505                         sizeof(hv_vmbus_channel_open_result));
506                     sema_post(&msg_info->wait_sema);
507                     break;
508                 }
509             }
510         }
511         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
512
513 }
514
515 /**
516  * @brief GPADL created handler.
517  *
518  * This is invoked when we received a response
519  * to our gpadl create request. Find the matching request, copy the
520  * response and signal the requesting thread.
521  */
522 static void
523 vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
524 {
525         hv_vmbus_channel_gpadl_created*         gpadl_created;
526         hv_vmbus_channel_msg_info*              msg_info;
527         hv_vmbus_channel_msg_header*            request_header;
528         hv_vmbus_channel_gpadl_header*          gpadl_header;
529
530         gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
531
532         /* Find the establish msg, copy the result and signal/unblock
533          * the wait event
534          */
535         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
536         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
537                 msg_list_entry) {
538             request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
539             if (request_header->message_type ==
540                     HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
541                 gpadl_header =
542                     (hv_vmbus_channel_gpadl_header*) request_header;
543
544                 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
545                     && (gpadl_created->gpadl == gpadl_header->gpadl)) {
546                     memcpy(&msg_info->response.gpadl_created,
547                         gpadl_created,
548                         sizeof(hv_vmbus_channel_gpadl_created));
549                     sema_post(&msg_info->wait_sema);
550                     break;
551                 }
552             }
553         }
554         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
555 }
556
557 /**
558  * @brief GPADL torndown handler.
559  *
560  * This is invoked when we received a respons
561  * to our gpadl teardown request. Find the matching request, copy the
562  * response and signal the requesting thread
563  */
564 static void
565 vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
566 {
567         hv_vmbus_channel_gpadl_torndown*        gpadl_torndown;
568         hv_vmbus_channel_msg_info*              msg_info;
569         hv_vmbus_channel_msg_header*            requestHeader;
570         hv_vmbus_channel_gpadl_teardown*        gpadlTeardown;
571
572         gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
573
574         /*
575          * Find the open msg, copy the result and signal/unblock the
576          * wait event.
577          */
578
579         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
580
581         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
582                 msg_list_entry) {
583             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
584
585             if (requestHeader->message_type
586                     == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
587                 gpadlTeardown =
588                     (hv_vmbus_channel_gpadl_teardown*) requestHeader;
589
590                 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
591                     memcpy(&msg_info->response.gpadl_torndown,
592                         gpadl_torndown,
593                         sizeof(hv_vmbus_channel_gpadl_torndown));
594                     sema_post(&msg_info->wait_sema);
595                     break;
596                 }
597             }
598         }
599     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
600 }
601
602 /**
603  * @brief Version response handler.
604  *
605  * This is invoked when we received a response
606  * to our initiate contact request. Find the matching request, copy th
607  * response and signal the requesting thread.
608  */
609 static void
610 vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
611 {
612         hv_vmbus_channel_msg_info*              msg_info;
613         hv_vmbus_channel_msg_header*            requestHeader;
614         hv_vmbus_channel_initiate_contact*      initiate;
615         hv_vmbus_channel_version_response*      versionResponse;
616
617         versionResponse = (hv_vmbus_channel_version_response*)hdr;
618
619         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
620         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
621             msg_list_entry) {
622             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
623             if (requestHeader->message_type
624                 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
625                 initiate =
626                     (hv_vmbus_channel_initiate_contact*) requestHeader;
627                 memcpy(&msg_info->response.version_response,
628                     versionResponse,
629                     sizeof(hv_vmbus_channel_version_response));
630                 sema_post(&msg_info->wait_sema);
631             }
632         }
633     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
634
635 }
636
637 /**
638  *  @brief Send a request to get all our pending offers.
639  */
640 int
641 hv_vmbus_request_channel_offers(void)
642 {
643         int                             ret;
644         hv_vmbus_channel_msg_header*    msg;
645         hv_vmbus_channel_msg_info*      msg_info;
646
647         msg_info = (hv_vmbus_channel_msg_info *)
648             malloc(sizeof(hv_vmbus_channel_msg_info)
649                     + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
650
651         if (msg_info == NULL) {
652             if(bootverbose)
653                 printf("Error VMBUS: malloc failed for Request Offers\n");
654             return (ENOMEM);
655         }
656
657         msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
658         msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
659
660         ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
661
662         free(msg_info, M_DEVBUF);
663
664         return (ret);
665 }
666
667 /**
668  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
669  */
670 void
671 hv_vmbus_release_unattached_channels(void) 
672 {
673         hv_vmbus_channel *channel;
674
675         mtx_lock(&hv_vmbus_g_connection.channel_lock);
676
677         while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
678             channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
679             TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
680                             channel, list_entry);
681
682             hv_vmbus_child_device_unregister(channel->device);
683             hv_vmbus_free_vmbus_channel(channel);
684         }
685         bzero(hv_vmbus_g_connection.channels, 
686                 sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT);
687         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
688 }
689
690 /**
691  * @brief Select the best outgoing channel
692  * 
693  * The channel whose vcpu binding is closest to the currect vcpu will
694  * be selected.
695  * If no multi-channel, always select primary channel
696  * 
697  * @param primary - primary channel
698  */
699 struct hv_vmbus_channel *
700 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
701 {
702         hv_vmbus_channel *new_channel = NULL;
703         hv_vmbus_channel *outgoing_channel = primary;
704         int old_cpu_distance = 0;
705         int new_cpu_distance = 0;
706         int cur_vcpu = 0;
707         int smp_pro_id = PCPU_GET(cpuid);
708
709         if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
710                 return outgoing_channel;
711         }
712
713         if (smp_pro_id >= MAXCPU) {
714                 return outgoing_channel;
715         }
716
717         cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id];
718         
719         TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
720                 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
721                         continue;
722                 }
723
724                 if (new_channel->target_vcpu == cur_vcpu){
725                         return new_channel;
726                 }
727
728                 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
729                     (outgoing_channel->target_vcpu - cur_vcpu) :
730                     (cur_vcpu - outgoing_channel->target_vcpu));
731
732                 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
733                     (new_channel->target_vcpu - cur_vcpu) :
734                     (cur_vcpu - new_channel->target_vcpu));
735
736                 if (old_cpu_distance < new_cpu_distance) {
737                         continue;
738                 }
739
740                 outgoing_channel = new_channel;
741         }
742
743         return(outgoing_channel);
744 }