hv_connection.c revision 294553
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
29250199Sgrehan#include <sys/param.h>
30296028Ssephe#include <sys/malloc.h>
31250199Sgrehan#include <sys/systm.h>
32250199Sgrehan#include <sys/lock.h>
33250199Sgrehan#include <sys/mutex.h>
34250199Sgrehan#include <machine/bus.h>
35250199Sgrehan#include <vm/vm.h>
36250199Sgrehan#include <vm/vm_param.h>
37250199Sgrehan#include <vm/pmap.h>
38250199Sgrehan
39250199Sgrehan#include "hv_vmbus_priv.h"
40250199Sgrehan
41250199Sgrehan/*
42250199Sgrehan * Globals
43250199Sgrehan */
44250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection =
45250199Sgrehan	{ .connect_state = HV_DISCONNECTED,
46250199Sgrehan	  .next_gpadl_handle = 0xE1E10, };
47250199Sgrehan
48250199Sgrehanuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
49282212Swhu
50282212Swhustatic uint32_t
51282212Swhuhv_vmbus_get_next_version(uint32_t current_ver)
52282212Swhu{
53282212Swhu	switch (current_ver) {
54282212Swhu	case (HV_VMBUS_VERSION_WIN7):
55282212Swhu		return(HV_VMBUS_VERSION_WS2008);
56282212Swhu
57282212Swhu	case (HV_VMBUS_VERSION_WIN8):
58282212Swhu		return(HV_VMBUS_VERSION_WIN7);
59282212Swhu
60282212Swhu	case (HV_VMBUS_VERSION_WIN8_1):
61282212Swhu		return(HV_VMBUS_VERSION_WIN8);
62282212Swhu
63282212Swhu	case (HV_VMBUS_VERSION_WS2008):
64282212Swhu	default:
65282212Swhu		return(HV_VMBUS_VERSION_INVALID);
66282212Swhu	}
67282212Swhu}
68282212Swhu
69282212Swhu/**
70250199Sgrehan * Negotiate the highest supported hypervisor version.
71282212Swhu */
72282212Swhustatic int
73282212Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
74282212Swhu	uint32_t version)
75282212Swhu{
76282212Swhu	int					ret = 0;
77282212Swhu	hv_vmbus_channel_initiate_contact	*msg;
78282212Swhu
79282212Swhu	sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
80282212Swhu	msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
81282212Swhu
82282212Swhu	msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
83282212Swhu	msg->vmbus_version_requested = version;
84282212Swhu
85282212Swhu	msg->interrupt_page = hv_get_phys_addr(
86282212Swhu		hv_vmbus_g_connection.interrupt_page);
87282212Swhu
88282212Swhu	msg->monitor_page_1 = hv_get_phys_addr(
89282212Swhu		hv_vmbus_g_connection.monitor_pages);
90295309Ssephe
91282212Swhu	msg->monitor_page_2 =
92295308Ssephe		hv_get_phys_addr(
93295309Ssephe			((uint8_t *) hv_vmbus_g_connection.monitor_pages
94282212Swhu			+ PAGE_SIZE));
95282212Swhu
96282212Swhu	/**
97282212Swhu	 * Add to list before we send the request since we may receive the
98282212Swhu	 * response before returning from this routine
99282212Swhu	 */
100282212Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
101282212Swhu
102282212Swhu	TAILQ_INSERT_TAIL(
103282212Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
104282212Swhu		msg_info,
105282212Swhu		msg_list_entry);
106282212Swhu
107282212Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
108282212Swhu
109282212Swhu	ret = hv_vmbus_post_message(
110282212Swhu		msg,
111282212Swhu		sizeof(hv_vmbus_channel_initiate_contact));
112282212Swhu
113282212Swhu	if (ret != 0) {
114282212Swhu		mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
115282212Swhu		TAILQ_REMOVE(
116282212Swhu			&hv_vmbus_g_connection.channel_msg_anchor,
117282212Swhu			msg_info,
118282212Swhu			msg_list_entry);
119282212Swhu		mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
120282212Swhu		return (ret);
121282212Swhu	}
122282212Swhu
123282212Swhu	/**
124282212Swhu	 * Wait for the connection response
125296028Ssephe	 */
126282212Swhu	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
127282212Swhu
128282212Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
129282212Swhu	TAILQ_REMOVE(
130282212Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
131282212Swhu		msg_info,
132282212Swhu		msg_list_entry);
133282212Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
134282212Swhu
135282212Swhu	/**
136282212Swhu	 * Check if successful
137282212Swhu	 */
138282212Swhu	if (msg_info->response.version_response.version_supported) {
139282212Swhu		hv_vmbus_g_connection.connect_state = HV_CONNECTED;
140282212Swhu	} else {
141282212Swhu		ret = ECONNREFUSED;
142282212Swhu	}
143282212Swhu
144282212Swhu	return (ret);
145282212Swhu}
146282212Swhu
147250199Sgrehan/**
148250199Sgrehan * Send a connect request on the partition service connection
149250199Sgrehan */
150250199Sgrehanint
151250199Sgrehanhv_vmbus_connect(void) {
152282212Swhu	int					ret = 0;
153250199Sgrehan	uint32_t				version;
154250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info = NULL;
155250199Sgrehan
156250199Sgrehan	/**
157250199Sgrehan	 * Make sure we are not connecting or connected
158250199Sgrehan	 */
159250199Sgrehan	if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
160250199Sgrehan		return (-1);
161250199Sgrehan	}
162250199Sgrehan
163250199Sgrehan	/**
164250199Sgrehan	 * Initialize the vmbus connection
165250199Sgrehan	 */
166250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_CONNECTING;
167250199Sgrehan	hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
168250199Sgrehan	sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
169250199Sgrehan
170250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
171250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
172250199Sgrehan		NULL, MTX_SPIN);
173282212Swhu
174250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
175250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
176250199Sgrehan		NULL, MTX_DEF);
177250199Sgrehan
178250199Sgrehan	/**
179295309Ssephe	 * Setup the vmbus event connection for channel interrupt abstraction
180250199Sgrehan	 * stuff
181295309Ssephe	 */
182250199Sgrehan	hv_vmbus_g_connection.interrupt_page = contigmalloc(
183250199Sgrehan					PAGE_SIZE, M_DEVBUF,
184250199Sgrehan					M_NOWAIT | M_ZERO, 0UL,
185250199Sgrehan					BUS_SPACE_MAXADDR,
186250199Sgrehan					PAGE_SIZE, 0);
187250199Sgrehan	KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
188250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Channel"
189250199Sgrehan		" Request Event message!"));
190250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page == NULL) {
191250199Sgrehan	    ret = ENOMEM;
192250199Sgrehan	    goto cleanup;
193250199Sgrehan	}
194295309Ssephe
195295309Ssephe	hv_vmbus_g_connection.recv_interrupt_page =
196250199Sgrehan		hv_vmbus_g_connection.interrupt_page;
197295309Ssephe
198295309Ssephe	hv_vmbus_g_connection.send_interrupt_page =
199250199Sgrehan		((uint8_t *) hv_vmbus_g_connection.interrupt_page +
200295309Ssephe		    (PAGE_SIZE >> 1));
201295309Ssephe
202250199Sgrehan	/**
203250199Sgrehan	 * Set up the monitor notification facility. The 1st page for
204250199Sgrehan	 * parent->child and the 2nd page for child->parent
205250199Sgrehan	 */
206295308Ssephe	hv_vmbus_g_connection.monitor_pages = contigmalloc(
207250199Sgrehan		2 * PAGE_SIZE,
208294553Ssephe		M_DEVBUF,
209294553Ssephe		M_NOWAIT | M_ZERO,
210294553Ssephe		0UL,
211282212Swhu		BUS_SPACE_MAXADDR,
212282212Swhu		PAGE_SIZE,
213250199Sgrehan		0);
214282212Swhu	KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
215250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
216282212Swhu	if (hv_vmbus_g_connection.monitor_pages == NULL) {
217282212Swhu	    ret = ENOMEM;
218282212Swhu	    goto cleanup;
219282212Swhu	}
220282212Swhu
221282212Swhu	msg_info = (hv_vmbus_channel_msg_info*)
222282212Swhu		malloc(sizeof(hv_vmbus_channel_msg_info) +
223282212Swhu			sizeof(hv_vmbus_channel_initiate_contact),
224250199Sgrehan			M_DEVBUF, M_NOWAIT | M_ZERO);
225282212Swhu	KASSERT(msg_info != NULL,
226282212Swhu	    ("Error VMBUS: malloc failed for Initiate Contact message!"));
227250199Sgrehan	if (msg_info == NULL) {
228282212Swhu	    ret = ENOMEM;
229282212Swhu	    goto cleanup;
230250199Sgrehan	}
231282212Swhu
232282212Swhu	hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
233293870Ssephe		HV_CHANNEL_MAX_COUNT,
234282212Swhu		M_DEVBUF, M_WAITOK | M_ZERO);
235250199Sgrehan	/*
236250199Sgrehan	 * Find the highest vmbus version number we can support.
237250199Sgrehan	 */
238250199Sgrehan	version = HV_VMBUS_VERSION_CURRENT;
239250199Sgrehan
240250199Sgrehan	do {
241250199Sgrehan		ret = hv_vmbus_negotiate_version(msg_info, version);
242250199Sgrehan		if (ret == EWOULDBLOCK) {
243250199Sgrehan			/*
244250199Sgrehan			 * We timed out.
245250199Sgrehan			 */
246250199Sgrehan			goto cleanup;
247250199Sgrehan		}
248250199Sgrehan
249250199Sgrehan		if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
250250199Sgrehan			break;
251250199Sgrehan
252295964Ssephe		version = hv_vmbus_get_next_version(version);
253250199Sgrehan	} while (version != HV_VMBUS_VERSION_INVALID);
254250199Sgrehan
255250199Sgrehan	hv_vmbus_protocal_version = version;
256295309Ssephe	if (bootverbose)
257295309Ssephe		printf("VMBUS: Protocol Version: %d.%d\n",
258250199Sgrehan		    version >> 16, version & 0xFFFF);
259250199Sgrehan
260250199Sgrehan	sema_destroy(&msg_info->wait_sema);
261250199Sgrehan	free(msg_info, M_DEVBUF);
262250199Sgrehan
263250199Sgrehan	return (0);
264294553Ssephe
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);
274295308Ssephe	mtx_destroy(&hv_vmbus_g_connection.channel_lock);
275250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
276295308Ssephe
277250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page != NULL) {
278295308Ssephe		contigfree(
279250199Sgrehan			hv_vmbus_g_connection.interrupt_page,
280295964Ssephe			PAGE_SIZE,
281250199Sgrehan			M_DEVBUF);
282250199Sgrehan		hv_vmbus_g_connection.interrupt_page = NULL;
283250199Sgrehan	}
284294553Ssephe
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) {
294294886Ssephe		sema_destroy(&msg_info->wait_sema);
295250199Sgrehan		free(msg_info, M_DEVBUF);
296282212Swhu	}
297250199Sgrehan
298282212Swhu	free(hv_vmbus_g_connection.channels, M_DEVBUF);
299282212Swhu	return (ret);
300250199Sgrehan}
301282212Swhu
302282212Swhu/**
303250199Sgrehan * Send a disconnect request on the partition service connection
304250199Sgrehan */
305282212Swhuint
306282212Swhuhv_vmbus_disconnect(void) {
307250199Sgrehan	int			 ret = 0;
308282212Swhu	hv_vmbus_channel_unload* msg;
309282212Swhu
310282212Swhu	msg = malloc(sizeof(hv_vmbus_channel_unload),
311282212Swhu	    M_DEVBUF, M_NOWAIT | M_ZERO);
312282212Swhu	KASSERT(msg != NULL,
313282212Swhu	    ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
314282212Swhu	if (msg == NULL)
315282212Swhu	    return (ENOMEM);
316282212Swhu
317282212Swhu	msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
318282212Swhu
319282212Swhu	ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
320282212Swhu
321282212Swhu
322282212Swhu	contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
323282212Swhu
324282212Swhu	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
325282212Swhu
326282212Swhu	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
327282212Swhu	sema_destroy(&hv_vmbus_g_connection.control_sema);
328282212Swhu
329250199Sgrehan	free(hv_vmbus_g_connection.channels, M_DEVBUF);
330250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
331250199Sgrehan
332250199Sgrehan	free(msg, M_DEVBUF);
333250199Sgrehan
334250199Sgrehan	return (ret);
335294553Ssephe}
336250199Sgrehan
337250199Sgrehan/**
338250199Sgrehan * Process a channel event notification
339250199Sgrehan */
340250199Sgrehanstatic void
341250199SgrehanVmbusProcessChannelEvent(uint32_t relid)
342250199Sgrehan{
343250199Sgrehan	void* arg;
344250199Sgrehan	uint32_t bytes_to_read;
345250199Sgrehan	hv_vmbus_channel* channel;
346294886Ssephe	boolean_t is_batched_reading;
347294886Ssephe
348294886Ssephe	/**
349294886Ssephe	 * Find the channel based on this relid and invokes
350250199Sgrehan	 * the channel callback to process the event
351294886Ssephe	 */
352294886Ssephe
353296272Sjhb	channel = hv_vmbus_g_connection.channels[relid];
354250199Sgrehan
355250199Sgrehan	if (channel == NULL) {
356250199Sgrehan		return;
357250199Sgrehan	}
358250199Sgrehan	/**
359250199Sgrehan	 * To deal with the race condition where we might
360250199Sgrehan	 * receive a packet while the relevant driver is
361250199Sgrehan	 * being unloaded, dispatch the callback while
362250199Sgrehan	 * holding the channel lock. The unloading driver
363250199Sgrehan	 * will acquire the same channel lock to set the
364250199Sgrehan	 * callback to NULL. This closes the window.
365250199Sgrehan	 */
366250199Sgrehan
367297219Ssephe	/*
368297219Ssephe	 * Disable the lock due to newly added WITNESS check in r277723.
369250199Sgrehan	 * Will seek other way to avoid race condition.
370297219Ssephe	 * -- whu
371297219Ssephe	 */
372297219Ssephe	// mtx_lock(&channel->inbound_lock);
373250199Sgrehan	if (channel->on_channel_callback != NULL) {
374297219Ssephe		arg = channel->channel_callback_context;
375297219Ssephe		is_batched_reading = channel->batched_reading;
376250199Sgrehan		/*
377297219Ssephe		 * Optimize host to guest signaling by ensuring:
378297219Ssephe		 * 1. While reading the channel, we disable interrupts from
379297219Ssephe		 *    host.
380250199Sgrehan		 * 2. Ensure that we process all posted messages from the host
381297219Ssephe		 *    before returning from this callback.
382297219Ssephe		 * 3. Once we return, enable signaling from the host. Once this
383297219Ssephe		 *    state is set we check to see if additional packets are
384297219Ssephe		 *    available to read. In this case we repeat the process.
385297219Ssephe		 */
386297219Ssephe		do {
387297219Ssephe			if (is_batched_reading)
388297219Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
389297219Ssephe
390250199Sgrehan			channel->on_channel_callback(arg);
391250199Sgrehan
392297219Ssephe			if (is_batched_reading)
393297219Ssephe				bytes_to_read =
394250199Sgrehan				    hv_ring_buffer_read_end(&channel->inbound);
395297219Ssephe			else
396250199Sgrehan				bytes_to_read = 0;
397250199Sgrehan		} while (is_batched_reading && (bytes_to_read != 0));
398250199Sgrehan	}
399250199Sgrehan	// mtx_unlock(&channel->inbound_lock);
400250199Sgrehan}
401250199Sgrehan
402282212Swhu/**
403250199Sgrehan * Handler for events
404282212Swhu */
405250199Sgrehanvoid
406250199Sgrehanhv_vmbus_on_events(void *arg)
407250199Sgrehan{
408250199Sgrehan	int bit;
409250199Sgrehan	int cpu;
410250199Sgrehan	int dword;
411282212Swhu	void *page_addr;
412250199Sgrehan	uint32_t* recv_interrupt_page = NULL;
413250199Sgrehan	int rel_id;
414250199Sgrehan	int maxdword;
415	hv_vmbus_synic_event_flags *event;
416	/* int maxdword = PAGE_SIZE >> 3; */
417
418	cpu = (int)(long)arg;
419	KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
420	    "cpu out of range!"));
421
422	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
423	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
424		maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
425		/*
426		 * receive size is 1/2 page and divide that by 4 bytes
427		 */
428		recv_interrupt_page =
429		    hv_vmbus_g_connection.recv_interrupt_page;
430	} else {
431		/*
432		 * On Host with Win8 or above, the event page can be
433		 * checked directly to get the id of the channel
434		 * that has the pending interrupt.
435		 */
436		maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
437		page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
438		event = (hv_vmbus_synic_event_flags *)
439		    page_addr + HV_VMBUS_MESSAGE_SINT;
440		recv_interrupt_page = event->flags32;
441	}
442
443	/*
444	 * Check events
445	 */
446	if (recv_interrupt_page != NULL) {
447	    for (dword = 0; dword < maxdword; dword++) {
448		if (recv_interrupt_page[dword]) {
449		    for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
450			if (synch_test_and_clear_bit(bit,
451			    (uint32_t *) &recv_interrupt_page[dword])) {
452			    rel_id = (dword << 5) + bit;
453			    if (rel_id == 0) {
454				/*
455				 * Special case -
456				 * vmbus channel protocol msg.
457				 */
458				continue;
459			    } else {
460				VmbusProcessChannelEvent(rel_id);
461
462			    }
463			}
464		    }
465		}
466	    }
467	}
468
469	return;
470}
471
472/**
473 * Send a msg on the vmbus's message connection
474 */
475int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
476	int ret = 0;
477	hv_vmbus_connection_id connId;
478	unsigned retries = 0;
479
480	/* NetScaler delays from previous code were consolidated here */
481	static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
482
483	/* for(each entry in delayAmount) try to post message,
484	 *  delay a little bit before retrying
485	 */
486	for (retries = 0;
487	    retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
488	    connId.as_uint32_t = 0;
489	    connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
490	    ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
491	    if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
492		break;
493	    /* TODO: KYS We should use a blocking wait call */
494	    DELAY(delayAmount[retries]);
495	}
496
497	KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
498
499	return (ret);
500}
501
502/**
503 * Send an event notification to the parent
504 */
505int
506hv_vmbus_set_event(hv_vmbus_channel *channel) {
507	int ret = 0;
508	uint32_t child_rel_id = channel->offer_msg.child_rel_id;
509
510	/* Each uint32_t represents 32 channels */
511
512	synch_set_bit(child_rel_id & 31,
513		(((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
514			+ (child_rel_id >> 5))));
515	ret = hv_vmbus_signal_event(channel->signal_event_param);
516
517	return (ret);
518}
519