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