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
43typedef void (*vmbus_msg_handler)(const hv_vmbus_channel_msg_header *msg);
44
45typedef 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
50static void	vmbus_channel_on_offer_internal(void *context);
51static void	vmbus_channel_on_offer_rescind_internal(void *context);
52
53static void	vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr);
54static void	vmbus_channel_on_open_result(
55		    const hv_vmbus_channel_msg_header *hdr);
56static void	vmbus_channel_on_offer_rescind(
57		    const hv_vmbus_channel_msg_header *hdr);
58static void	vmbus_channel_on_gpadl_created(
59		    const hv_vmbus_channel_msg_header *hdr);
60static void	vmbus_channel_on_gpadl_torndown(
61		    const hv_vmbus_channel_msg_header *hdr);
62static void	vmbus_channel_on_offers_delivered(
63		    const hv_vmbus_channel_msg_header *hdr);
64static void	vmbus_channel_on_version_response(
65		    const hv_vmbus_channel_msg_header *hdr);
66
67/**
68 * Channel message dispatch table
69 */
70static 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
108typedef struct hv_work_item {
109	struct task	work;
110	void		(*callback)(void *);
111	void*		context;
112} hv_work_item;
113
114static struct mtx	vmbus_chwait_lock;
115MTX_SYSINIT(vmbus_chwait_lk, &vmbus_chwait_lock, "vmbus primarych wait lock",
116    MTX_DEF);
117static uint32_t		vmbus_chancnt;
118static uint32_t		vmbus_devcnt;
119
120#define VMBUS_CHANCNT_DONE	0x80000000
121
122/**
123 * Implementation of the work abstraction.
124 */
125static void
126work_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 */
138static int
139hv_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 */
160hv_vmbus_channel*
161hv_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 */
179void
180hv_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 */
190static void
191vmbus_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
314void
315vmbus_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 */
339static const hv_guid high_perf_devices[] = {
340	{HV_NIC_GUID, },
341	{HV_IDE_GUID, },
342	{HV_SCSI_GUID, },
343};
344
345enum {
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 */
355static 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 */
366static void
367vmbus_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 */
400static void
401vmbus_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
424static void
425vmbus_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 */
478static void
479vmbus_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
494static void
495vmbus_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 */
510static void
511vmbus_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 */
528static void
529vmbus_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 */
570static void
571vmbus_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 */
612static void
613vmbus_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 */
657static void
658vmbus_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 */
688int
689hv_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 */
718void
719hv_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 */
750struct hv_vmbus_channel *
751vmbus_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
797void
798vmbus_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
812struct hv_vmbus_channel **
813vmbus_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
843void
844vmbus_rel_subchan(struct hv_vmbus_channel **subchan, int subchan_cnt __unused)
845{
846
847	free(subchan, M_TEMP);
848}
849
850void
851vmbus_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}
870