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