1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29283280Swhu#include <sys/cdefs.h>
30283280Swhu__FBSDID("$FreeBSD: releng/10.2/sys/dev/hyperv/vmbus/hv_connection.c 283280 2015-05-22 09:03:55Z whu $");
31283280Swhu
32250199Sgrehan#include <sys/param.h>
33250199Sgrehan#include <sys/malloc.h>
34250199Sgrehan#include <sys/systm.h>
35250199Sgrehan#include <sys/lock.h>
36250199Sgrehan#include <sys/mutex.h>
37250199Sgrehan#include <machine/bus.h>
38250199Sgrehan#include <vm/vm.h>
39250199Sgrehan#include <vm/vm_param.h>
40250199Sgrehan#include <vm/pmap.h>
41250199Sgrehan
42250199Sgrehan#include "hv_vmbus_priv.h"
43250199Sgrehan
44250199Sgrehan/*
45250199Sgrehan * Globals
46250199Sgrehan */
47250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection =
48250199Sgrehan	{ .connect_state = HV_DISCONNECTED,
49250199Sgrehan	  .next_gpadl_handle = 0xE1E10, };
50250199Sgrehan
51283280Swhuuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
52283280Swhu
53283280Swhustatic uint32_t
54283280Swhuhv_vmbus_get_next_version(uint32_t current_ver)
55283280Swhu{
56283280Swhu	switch (current_ver) {
57283280Swhu	case (HV_VMBUS_VERSION_WIN7):
58283280Swhu		return(HV_VMBUS_VERSION_WS2008);
59283280Swhu
60283280Swhu	case (HV_VMBUS_VERSION_WIN8):
61283280Swhu		return(HV_VMBUS_VERSION_WIN7);
62283280Swhu
63283280Swhu	case (HV_VMBUS_VERSION_WIN8_1):
64283280Swhu		return(HV_VMBUS_VERSION_WIN8);
65283280Swhu
66283280Swhu	case (HV_VMBUS_VERSION_WS2008):
67283280Swhu	default:
68283280Swhu		return(HV_VMBUS_VERSION_INVALID);
69283280Swhu	}
70283280Swhu}
71283280Swhu
72250199Sgrehan/**
73283280Swhu * Negotiate the highest supported hypervisor version.
74283280Swhu */
75283280Swhustatic int
76283280Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
77283280Swhu	uint32_t version)
78283280Swhu{
79283280Swhu	int					ret = 0;
80283280Swhu	hv_vmbus_channel_initiate_contact	*msg;
81283280Swhu
82283280Swhu	sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
83283280Swhu	msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
84283280Swhu
85283280Swhu	msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
86283280Swhu	msg->vmbus_version_requested = version;
87283280Swhu
88283280Swhu	msg->interrupt_page = hv_get_phys_addr(
89283280Swhu		hv_vmbus_g_connection.interrupt_page);
90283280Swhu
91283280Swhu	msg->monitor_page_1 = hv_get_phys_addr(
92283280Swhu		hv_vmbus_g_connection.monitor_pages);
93283280Swhu
94283280Swhu	msg->monitor_page_2 =
95283280Swhu		hv_get_phys_addr(
96283280Swhu			((uint8_t *) hv_vmbus_g_connection.monitor_pages
97283280Swhu			+ PAGE_SIZE));
98283280Swhu
99283280Swhu	/**
100283280Swhu	 * Add to list before we send the request since we may receive the
101283280Swhu	 * response before returning from this routine
102283280Swhu	 */
103283280Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
104283280Swhu
105283280Swhu	TAILQ_INSERT_TAIL(
106283280Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
107283280Swhu		msg_info,
108283280Swhu		msg_list_entry);
109283280Swhu
110283280Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
111283280Swhu
112283280Swhu	ret = hv_vmbus_post_message(
113283280Swhu		msg,
114283280Swhu		sizeof(hv_vmbus_channel_initiate_contact));
115283280Swhu
116283280Swhu	if (ret != 0) {
117283280Swhu		mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
118283280Swhu		TAILQ_REMOVE(
119283280Swhu			&hv_vmbus_g_connection.channel_msg_anchor,
120283280Swhu			msg_info,
121283280Swhu			msg_list_entry);
122283280Swhu		mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
123283280Swhu		return (ret);
124283280Swhu	}
125283280Swhu
126283280Swhu	/**
127283280Swhu	 * Wait for the connection response
128283280Swhu	 */
129283280Swhu	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
130283280Swhu
131283280Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
132283280Swhu	TAILQ_REMOVE(
133283280Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
134283280Swhu		msg_info,
135283280Swhu		msg_list_entry);
136283280Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
137283280Swhu
138283280Swhu	/**
139283280Swhu	 * Check if successful
140283280Swhu	 */
141283280Swhu	if (msg_info->response.version_response.version_supported) {
142283280Swhu		hv_vmbus_g_connection.connect_state = HV_CONNECTED;
143283280Swhu	} else {
144283280Swhu		ret = ECONNREFUSED;
145283280Swhu	}
146283280Swhu
147283280Swhu	return (ret);
148283280Swhu}
149283280Swhu
150283280Swhu/**
151250199Sgrehan * Send a connect request on the partition service connection
152250199Sgrehan */
153250199Sgrehanint
154250199Sgrehanhv_vmbus_connect(void) {
155250199Sgrehan	int					ret = 0;
156283280Swhu	uint32_t				version;
157250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info = NULL;
158250199Sgrehan
159250199Sgrehan	/**
160250199Sgrehan	 * Make sure we are not connecting or connected
161250199Sgrehan	 */
162250199Sgrehan	if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
163250199Sgrehan		return (-1);
164250199Sgrehan	}
165250199Sgrehan
166250199Sgrehan	/**
167250199Sgrehan	 * Initialize the vmbus connection
168250199Sgrehan	 */
169250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_CONNECTING;
170250199Sgrehan	hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
171250199Sgrehan	sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
172250199Sgrehan
173250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
174250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
175250199Sgrehan		NULL, MTX_SPIN);
176250199Sgrehan
177250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
178250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
179283280Swhu		NULL, MTX_DEF);
180250199Sgrehan
181250199Sgrehan	/**
182250199Sgrehan	 * Setup the vmbus event connection for channel interrupt abstraction
183250199Sgrehan	 * stuff
184250199Sgrehan	 */
185250199Sgrehan	hv_vmbus_g_connection.interrupt_page = contigmalloc(
186250199Sgrehan					PAGE_SIZE, M_DEVBUF,
187250199Sgrehan					M_NOWAIT | M_ZERO, 0UL,
188250199Sgrehan					BUS_SPACE_MAXADDR,
189250199Sgrehan					PAGE_SIZE, 0);
190250199Sgrehan	KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
191250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Channel"
192250199Sgrehan		" Request Event message!"));
193250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page == NULL) {
194250199Sgrehan	    ret = ENOMEM;
195250199Sgrehan	    goto cleanup;
196250199Sgrehan	}
197250199Sgrehan
198250199Sgrehan	hv_vmbus_g_connection.recv_interrupt_page =
199250199Sgrehan		hv_vmbus_g_connection.interrupt_page;
200250199Sgrehan
201250199Sgrehan	hv_vmbus_g_connection.send_interrupt_page =
202250199Sgrehan		((uint8_t *) hv_vmbus_g_connection.interrupt_page +
203250199Sgrehan		    (PAGE_SIZE >> 1));
204250199Sgrehan
205250199Sgrehan	/**
206250199Sgrehan	 * Set up the monitor notification facility. The 1st page for
207250199Sgrehan	 * parent->child and the 2nd page for child->parent
208250199Sgrehan	 */
209250199Sgrehan	hv_vmbus_g_connection.monitor_pages = contigmalloc(
210250199Sgrehan		2 * PAGE_SIZE,
211250199Sgrehan		M_DEVBUF,
212250199Sgrehan		M_NOWAIT | M_ZERO,
213250199Sgrehan		0UL,
214250199Sgrehan		BUS_SPACE_MAXADDR,
215250199Sgrehan		PAGE_SIZE,
216250199Sgrehan		0);
217250199Sgrehan	KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
218250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
219250199Sgrehan	if (hv_vmbus_g_connection.monitor_pages == NULL) {
220250199Sgrehan	    ret = ENOMEM;
221250199Sgrehan	    goto cleanup;
222250199Sgrehan	}
223250199Sgrehan
224250199Sgrehan	msg_info = (hv_vmbus_channel_msg_info*)
225250199Sgrehan		malloc(sizeof(hv_vmbus_channel_msg_info) +
226250199Sgrehan			sizeof(hv_vmbus_channel_initiate_contact),
227250199Sgrehan			M_DEVBUF, M_NOWAIT | M_ZERO);
228250199Sgrehan	KASSERT(msg_info != NULL,
229250199Sgrehan	    ("Error VMBUS: malloc failed for Initiate Contact message!"));
230250199Sgrehan	if (msg_info == NULL) {
231250199Sgrehan	    ret = ENOMEM;
232250199Sgrehan	    goto cleanup;
233250199Sgrehan	}
234250199Sgrehan
235283280Swhu	/*
236283280Swhu	 * Find the highest vmbus version number we can support.
237250199Sgrehan	 */
238283280Swhu	version = HV_VMBUS_VERSION_CURRENT;
239250199Sgrehan
240283280Swhu	do {
241283280Swhu		ret = hv_vmbus_negotiate_version(msg_info, version);
242283280Swhu		if (ret == EWOULDBLOCK) {
243283280Swhu			/*
244283280Swhu			 * We timed out.
245283280Swhu			 */
246283280Swhu			goto cleanup;
247283280Swhu		}
248250199Sgrehan
249283280Swhu		if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
250283280Swhu			break;
251250199Sgrehan
252283280Swhu		version = hv_vmbus_get_next_version(version);
253283280Swhu	} while (version != HV_VMBUS_VERSION_INVALID);
254250199Sgrehan
255283280Swhu	hv_vmbus_protocal_version = version;
256283280Swhu	if (bootverbose)
257283280Swhu		printf("VMBUS: Portocal Version: %d.%d\n",
258283280Swhu		    version >> 16, version & 0xFFFF);
259250199Sgrehan
260250199Sgrehan	sema_destroy(&msg_info->wait_sema);
261250199Sgrehan	free(msg_info, M_DEVBUF);
262250199Sgrehan
263250199Sgrehan	return (0);
264250199Sgrehan
265250199Sgrehan	/*
266250199Sgrehan	 * Cleanup after failure!
267250199Sgrehan	 */
268250199Sgrehan	cleanup:
269250199Sgrehan
270250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
271250199Sgrehan
272250199Sgrehan	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
273250199Sgrehan	sema_destroy(&hv_vmbus_g_connection.control_sema);
274250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_lock);
275250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
276250199Sgrehan
277250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page != NULL) {
278250199Sgrehan		contigfree(
279250199Sgrehan			hv_vmbus_g_connection.interrupt_page,
280250199Sgrehan			PAGE_SIZE,
281250199Sgrehan			M_DEVBUF);
282250199Sgrehan		hv_vmbus_g_connection.interrupt_page = NULL;
283250199Sgrehan	}
284250199Sgrehan
285250199Sgrehan	if (hv_vmbus_g_connection.monitor_pages != NULL) {
286250199Sgrehan		contigfree(
287250199Sgrehan			hv_vmbus_g_connection.monitor_pages,
288250199Sgrehan			2 * PAGE_SIZE,
289250199Sgrehan			M_DEVBUF);
290250199Sgrehan		hv_vmbus_g_connection.monitor_pages = NULL;
291250199Sgrehan	}
292250199Sgrehan
293250199Sgrehan	if (msg_info) {
294250199Sgrehan		sema_destroy(&msg_info->wait_sema);
295250199Sgrehan		free(msg_info, M_DEVBUF);
296250199Sgrehan	}
297250199Sgrehan
298250199Sgrehan	return (ret);
299250199Sgrehan}
300250199Sgrehan
301250199Sgrehan/**
302250199Sgrehan * Send a disconnect request on the partition service connection
303250199Sgrehan */
304250199Sgrehanint
305250199Sgrehanhv_vmbus_disconnect(void) {
306250199Sgrehan	int			 ret = 0;
307250199Sgrehan	hv_vmbus_channel_unload* msg;
308250199Sgrehan
309250199Sgrehan	msg = malloc(sizeof(hv_vmbus_channel_unload),
310250199Sgrehan	    M_DEVBUF, M_NOWAIT | M_ZERO);
311250199Sgrehan	KASSERT(msg != NULL,
312250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
313250199Sgrehan	if (msg == NULL)
314250199Sgrehan	    return (ENOMEM);
315250199Sgrehan
316250199Sgrehan	msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
317250199Sgrehan
318250199Sgrehan	ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
319250199Sgrehan
320250199Sgrehan
321250199Sgrehan	contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
322250199Sgrehan
323250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
324250199Sgrehan
325250199Sgrehan	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
326250199Sgrehan	sema_destroy(&hv_vmbus_g_connection.control_sema);
327250199Sgrehan
328250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
329250199Sgrehan
330250199Sgrehan	free(msg, M_DEVBUF);
331250199Sgrehan
332250199Sgrehan	return (ret);
333250199Sgrehan}
334250199Sgrehan
335250199Sgrehan/**
336250199Sgrehan * Get the channel object given its child relative id (ie channel id)
337250199Sgrehan */
338250199Sgrehanhv_vmbus_channel*
339250199Sgrehanhv_vmbus_get_channel_from_rel_id(uint32_t rel_id) {
340250199Sgrehan
341250199Sgrehan	hv_vmbus_channel* channel;
342250199Sgrehan	hv_vmbus_channel* foundChannel = NULL;
343250199Sgrehan
344250199Sgrehan	/*
345250199Sgrehan	 * TODO:
346250199Sgrehan	 * Consider optimization where relids are stored in a fixed size array
347250199Sgrehan	 *  and channels are accessed without the need to take this lock or search
348250199Sgrehan	 *  the list.
349250199Sgrehan	 */
350283280Swhu	mtx_lock(&hv_vmbus_g_connection.channel_lock);
351250199Sgrehan	TAILQ_FOREACH(channel,
352250199Sgrehan		&hv_vmbus_g_connection.channel_anchor, list_entry) {
353250199Sgrehan
354250199Sgrehan	    if (channel->offer_msg.child_rel_id == rel_id) {
355250199Sgrehan		foundChannel = channel;
356250199Sgrehan		break;
357250199Sgrehan	    }
358250199Sgrehan	}
359283280Swhu	mtx_unlock(&hv_vmbus_g_connection.channel_lock);
360250199Sgrehan
361250199Sgrehan	return (foundChannel);
362250199Sgrehan}
363250199Sgrehan
364250199Sgrehan/**
365250199Sgrehan * Process a channel event notification
366250199Sgrehan */
367250199Sgrehanstatic void
368250199SgrehanVmbusProcessChannelEvent(uint32_t relid)
369250199Sgrehan{
370283280Swhu	void* arg;
371283280Swhu	uint32_t bytes_to_read;
372250199Sgrehan	hv_vmbus_channel* channel;
373283280Swhu	boolean_t is_batched_reading;
374250199Sgrehan
375250199Sgrehan	/**
376250199Sgrehan	 * Find the channel based on this relid and invokes
377250199Sgrehan	 * the channel callback to process the event
378250199Sgrehan	 */
379250199Sgrehan
380250199Sgrehan	channel = hv_vmbus_get_channel_from_rel_id(relid);
381250199Sgrehan
382250199Sgrehan	if (channel == NULL) {
383250199Sgrehan		return;
384250199Sgrehan	}
385250199Sgrehan	/**
386250199Sgrehan	 * To deal with the race condition where we might
387250199Sgrehan	 * receive a packet while the relevant driver is
388250199Sgrehan	 * being unloaded, dispatch the callback while
389250199Sgrehan	 * holding the channel lock. The unloading driver
390250199Sgrehan	 * will acquire the same channel lock to set the
391250199Sgrehan	 * callback to NULL. This closes the window.
392250199Sgrehan	 */
393250199Sgrehan
394283280Swhu	/*
395283280Swhu	 * Disable the lock due to newly added WITNESS check in r277723.
396283280Swhu	 * Will seek other way to avoid race condition.
397283280Swhu	 * -- whu
398283280Swhu	 */
399283280Swhu	// mtx_lock(&channel->inbound_lock);
400250199Sgrehan	if (channel->on_channel_callback != NULL) {
401283280Swhu		arg = channel->channel_callback_context;
402283280Swhu		is_batched_reading = channel->batched_reading;
403283280Swhu		/*
404283280Swhu		 * Optimize host to guest signaling by ensuring:
405283280Swhu		 * 1. While reading the channel, we disable interrupts from
406283280Swhu		 *    host.
407283280Swhu		 * 2. Ensure that we process all posted messages from the host
408283280Swhu		 *    before returning from this callback.
409283280Swhu		 * 3. Once we return, enable signaling from the host. Once this
410283280Swhu		 *    state is set we check to see if additional packets are
411283280Swhu		 *    available to read. In this case we repeat the process.
412283280Swhu		 */
413283280Swhu		do {
414283280Swhu			if (is_batched_reading)
415283280Swhu				hv_ring_buffer_read_begin(&channel->inbound);
416283280Swhu
417283280Swhu			channel->on_channel_callback(arg);
418283280Swhu
419283280Swhu			if (is_batched_reading)
420283280Swhu				bytes_to_read =
421283280Swhu				    hv_ring_buffer_read_end(&channel->inbound);
422283280Swhu			else
423283280Swhu				bytes_to_read = 0;
424283280Swhu		} while (is_batched_reading && (bytes_to_read != 0));
425250199Sgrehan	}
426283280Swhu	// mtx_unlock(&channel->inbound_lock);
427250199Sgrehan}
428250199Sgrehan
429283280Swhu#ifdef HV_DEBUG_INTR
430283280Swhuextern uint32_t hv_intr_count;
431283280Swhuextern uint32_t hv_vmbus_swintr_event_cpu[MAXCPU];
432283280Swhuextern uint32_t hv_vmbus_intr_cpu[MAXCPU];
433283280Swhu#endif
434283280Swhu
435250199Sgrehan/**
436250199Sgrehan * Handler for events
437250199Sgrehan */
438250199Sgrehanvoid
439250199Sgrehanhv_vmbus_on_events(void *arg)
440250199Sgrehan{
441283280Swhu	int bit;
442283280Swhu	int cpu;
443250199Sgrehan	int dword;
444283280Swhu	void *page_addr;
445283280Swhu	uint32_t* recv_interrupt_page = NULL;
446250199Sgrehan	int rel_id;
447283280Swhu	int maxdword;
448283280Swhu	hv_vmbus_synic_event_flags *event;
449250199Sgrehan	/* int maxdword = PAGE_SIZE >> 3; */
450250199Sgrehan
451283280Swhu	cpu = (int)(long)arg;
452283280Swhu	KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
453283280Swhu	    "cpu out of range!"));
454250199Sgrehan
455283280Swhu#ifdef HV_DEBUG_INTR
456283280Swhu	int i;
457283280Swhu	hv_vmbus_swintr_event_cpu[cpu]++;
458283280Swhu	if (hv_intr_count % 10000 == 0) {
459283280Swhu                printf("VMBUS: Total interrupt %d\n", hv_intr_count);
460283280Swhu                for (i = 0; i < mp_ncpus; i++)
461283280Swhu                        printf("VMBUS: hw cpu[%d]: %d, event sw intr cpu[%d]: %d\n",
462283280Swhu			    i, hv_vmbus_intr_cpu[i], i, hv_vmbus_swintr_event_cpu[i]);
463283280Swhu        }
464283280Swhu#endif
465250199Sgrehan
466283280Swhu	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
467283280Swhu	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
468283280Swhu		maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
469283280Swhu		/*
470283280Swhu		 * receive size is 1/2 page and divide that by 4 bytes
471283280Swhu		 */
472283280Swhu		recv_interrupt_page =
473283280Swhu		    hv_vmbus_g_connection.recv_interrupt_page;
474283280Swhu	} else {
475283280Swhu		/*
476283280Swhu		 * On Host with Win8 or above, the event page can be
477283280Swhu		 * checked directly to get the id of the channel
478283280Swhu		 * that has the pending interrupt.
479283280Swhu		 */
480283280Swhu		maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
481283280Swhu		page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
482283280Swhu		event = (hv_vmbus_synic_event_flags *)
483283280Swhu		    page_addr + HV_VMBUS_MESSAGE_SINT;
484283280Swhu		recv_interrupt_page = event->flags32;
485283280Swhu	}
486283280Swhu
487250199Sgrehan	/*
488250199Sgrehan	 * Check events
489250199Sgrehan	 */
490250199Sgrehan	if (recv_interrupt_page != NULL) {
491250199Sgrehan	    for (dword = 0; dword < maxdword; dword++) {
492250199Sgrehan		if (recv_interrupt_page[dword]) {
493250199Sgrehan		    for (bit = 0; bit < 32; bit++) {
494250199Sgrehan			if (synch_test_and_clear_bit(bit,
495250199Sgrehan			    (uint32_t *) &recv_interrupt_page[dword])) {
496250199Sgrehan			    rel_id = (dword << 5) + bit;
497250199Sgrehan			    if (rel_id == 0) {
498250199Sgrehan				/*
499250199Sgrehan				 * Special case -
500250199Sgrehan				 * vmbus channel protocol msg.
501250199Sgrehan				 */
502250199Sgrehan				continue;
503250199Sgrehan			    } else {
504250199Sgrehan				VmbusProcessChannelEvent(rel_id);
505250199Sgrehan
506250199Sgrehan			    }
507250199Sgrehan			}
508250199Sgrehan		    }
509250199Sgrehan		}
510250199Sgrehan	    }
511250199Sgrehan	}
512250199Sgrehan
513250199Sgrehan	return;
514250199Sgrehan}
515250199Sgrehan
516250199Sgrehan/**
517250199Sgrehan * Send a msg on the vmbus's message connection
518250199Sgrehan */
519250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) {
520250199Sgrehan	int ret = 0;
521250199Sgrehan	hv_vmbus_connection_id connId;
522250199Sgrehan	unsigned retries = 0;
523250199Sgrehan
524250199Sgrehan	/* NetScaler delays from previous code were consolidated here */
525250199Sgrehan	static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
526250199Sgrehan
527250199Sgrehan	/* for(each entry in delayAmount) try to post message,
528250199Sgrehan	 *  delay a little bit before retrying
529250199Sgrehan	 */
530250199Sgrehan	for (retries = 0;
531250199Sgrehan	    retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
532250199Sgrehan	    connId.as_uint32_t = 0;
533250199Sgrehan	    connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
534250199Sgrehan	    ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
535250199Sgrehan	    if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
536250199Sgrehan		break;
537250199Sgrehan	    /* TODO: KYS We should use a blocking wait call */
538250199Sgrehan	    DELAY(delayAmount[retries]);
539250199Sgrehan	}
540250199Sgrehan
541250199Sgrehan	KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
542250199Sgrehan
543250199Sgrehan	return (ret);
544250199Sgrehan}
545250199Sgrehan
546250199Sgrehan/**
547250199Sgrehan * Send an event notification to the parent
548250199Sgrehan */
549250199Sgrehanint
550283280Swhuhv_vmbus_set_event(hv_vmbus_channel *channel) {
551250199Sgrehan	int ret = 0;
552283280Swhu	uint32_t child_rel_id = channel->offer_msg.child_rel_id;
553250199Sgrehan
554250199Sgrehan	/* Each uint32_t represents 32 channels */
555250199Sgrehan
556250199Sgrehan	synch_set_bit(child_rel_id & 31,
557250199Sgrehan		(((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
558250199Sgrehan			+ (child_rel_id >> 5))));
559283280Swhu	ret = hv_vmbus_signal_event(channel->signal_event_param);
560250199Sgrehan
561250199Sgrehan	return (ret);
562250199Sgrehan}
563