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