vmbus_chan.c revision 250199
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>
30250199Sgrehan#include <sys/malloc.h>
31250199Sgrehan#include <sys/systm.h>
32250199Sgrehan#include <sys/mbuf.h>
33250199Sgrehan#include <sys/lock.h>
34250199Sgrehan#include <sys/mutex.h>
35250199Sgrehan#include <machine/bus.h>
36250199Sgrehan#include <vm/vm.h>
37250199Sgrehan#include <vm/vm_param.h>
38250199Sgrehan#include <vm/pmap.h>
39250199Sgrehan
40250199Sgrehan#include "hv_vmbus_priv.h"
41250199Sgrehan
42250199Sgrehanstatic int 	vmbus_channel_create_gpadl_header(
43250199Sgrehan			/* must be phys and virt contiguous*/
44250199Sgrehan			void*				contig_buffer,
45250199Sgrehan			/* page-size multiple */
46250199Sgrehan			uint32_t 			size,
47250199Sgrehan			hv_vmbus_channel_msg_info**	msg_info,
48250199Sgrehan			uint32_t*			message_count);
49250199Sgrehan
50250199Sgrehanstatic void 	vmbus_channel_set_event(hv_vmbus_channel* channel);
51250199Sgrehan
52250199Sgrehan/**
53250199Sgrehan *  @brief Trigger an event notification on the specified channel
54250199Sgrehan */
55250199Sgrehanstatic void
56250199Sgrehanvmbus_channel_set_event(hv_vmbus_channel *channel)
57250199Sgrehan{
58250199Sgrehan	hv_vmbus_monitor_page *monitor_page;
59250199Sgrehan
60250199Sgrehan	if (channel->offer_msg.monitor_allocated) {
61250199Sgrehan		/* Each uint32_t represents 32 channels */
62250199Sgrehan		synch_set_bit((channel->offer_msg.child_rel_id & 31),
63250199Sgrehan			((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
64250199Sgrehan				+ ((channel->offer_msg.child_rel_id >> 5))));
65250199Sgrehan
66250199Sgrehan		monitor_page = (hv_vmbus_monitor_page *)
67250199Sgrehan			hv_vmbus_g_connection.monitor_pages;
68250199Sgrehan
69250199Sgrehan		monitor_page++; /* Get the child to parent monitor page */
70250199Sgrehan
71250199Sgrehan		synch_set_bit(channel->monitor_bit,
72250199Sgrehan			(uint32_t *)&monitor_page->
73250199Sgrehan				trigger_group[channel->monitor_group].pending);
74250199Sgrehan	} else {
75250199Sgrehan		hv_vmbus_set_event(channel->offer_msg.child_rel_id);
76250199Sgrehan	}
77250199Sgrehan
78250199Sgrehan}
79250199Sgrehan
80250199Sgrehan/**
81250199Sgrehan * @brief Open the specified channel
82250199Sgrehan */
83250199Sgrehanint
84250199Sgrehanhv_vmbus_channel_open(
85250199Sgrehan	hv_vmbus_channel*		new_channel,
86250199Sgrehan	uint32_t			send_ring_buffer_size,
87250199Sgrehan	uint32_t			recv_ring_buffer_size,
88250199Sgrehan	void*				user_data,
89250199Sgrehan	uint32_t			user_data_len,
90250199Sgrehan	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
91250199Sgrehan	void* 				context)
92250199Sgrehan{
93250199Sgrehan
94250199Sgrehan	int ret = 0;
95250199Sgrehan	void *in, *out;
96250199Sgrehan	hv_vmbus_channel_open_channel*	open_msg;
97250199Sgrehan	hv_vmbus_channel_msg_info* 	open_info;
98250199Sgrehan
99250199Sgrehan	new_channel->on_channel_callback = pfn_on_channel_callback;
100250199Sgrehan	new_channel->channel_callback_context = context;
101250199Sgrehan
102250199Sgrehan	/* Allocate the ring buffer */
103250199Sgrehan	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
104250199Sgrehan		M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
105250199Sgrehan	KASSERT(out != NULL,
106250199Sgrehan	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
107250199Sgrehan	if (out == NULL)
108250199Sgrehan	    return (ENOMEM);
109250199Sgrehan
110250199Sgrehan	in = ((uint8_t *) out + send_ring_buffer_size);
111250199Sgrehan
112250199Sgrehan	new_channel->ring_buffer_pages = out;
113250199Sgrehan	new_channel->ring_buffer_page_count = (send_ring_buffer_size
114250199Sgrehan		+ recv_ring_buffer_size) >> PAGE_SHIFT;
115250199Sgrehan
116250199Sgrehan	hv_vmbus_ring_buffer_init(
117250199Sgrehan		&new_channel->outbound,
118250199Sgrehan		out,
119250199Sgrehan		send_ring_buffer_size);
120250199Sgrehan
121250199Sgrehan	hv_vmbus_ring_buffer_init(
122250199Sgrehan		&new_channel->inbound,
123250199Sgrehan		in,
124250199Sgrehan		recv_ring_buffer_size);
125250199Sgrehan
126250199Sgrehan	/**
127250199Sgrehan	 * Establish the gpadl for the ring buffer
128250199Sgrehan	 */
129250199Sgrehan	new_channel->ring_buffer_gpadl_handle = 0;
130250199Sgrehan
131250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(new_channel,
132250199Sgrehan		new_channel->outbound.ring_buffer,
133250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
134250199Sgrehan		&new_channel->ring_buffer_gpadl_handle);
135250199Sgrehan
136250199Sgrehan	/**
137250199Sgrehan	 * Create and init the channel open message
138250199Sgrehan	 */
139250199Sgrehan	open_info = (hv_vmbus_channel_msg_info*) malloc(
140250199Sgrehan		sizeof(hv_vmbus_channel_msg_info) +
141250199Sgrehan			sizeof(hv_vmbus_channel_open_channel),
142250199Sgrehan		M_DEVBUF,
143250199Sgrehan		M_NOWAIT);
144250199Sgrehan	KASSERT(open_info != NULL,
145250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Open Channel message!"));
146250199Sgrehan
147250199Sgrehan	if (open_info == NULL)
148250199Sgrehan		return (ENOMEM);
149250199Sgrehan
150250199Sgrehan	sema_init(&open_info->wait_sema, 0, "Open Info Sema");
151250199Sgrehan
152250199Sgrehan	open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
153250199Sgrehan	open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
154250199Sgrehan	open_msg->open_id = new_channel->offer_msg.child_rel_id;
155250199Sgrehan	open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
156250199Sgrehan	open_msg->ring_buffer_gpadl_handle =
157250199Sgrehan		new_channel->ring_buffer_gpadl_handle;
158250199Sgrehan	open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
159250199Sgrehan		>> PAGE_SHIFT;
160250199Sgrehan	open_msg->server_context_area_gpadl_handle = 0;
161250199Sgrehan
162250199Sgrehan	if (user_data_len)
163250199Sgrehan		memcpy(open_msg->user_data, user_data, user_data_len);
164250199Sgrehan
165250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
166250199Sgrehan	TAILQ_INSERT_TAIL(
167250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
168250199Sgrehan		open_info,
169250199Sgrehan		msg_list_entry);
170250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
171250199Sgrehan
172250199Sgrehan	ret = hv_vmbus_post_message(
173250199Sgrehan		open_msg, sizeof(hv_vmbus_channel_open_channel));
174250199Sgrehan
175250199Sgrehan	if (ret != 0)
176250199Sgrehan	    goto cleanup;
177250199Sgrehan
178250199Sgrehan	ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */
179250199Sgrehan
180250199Sgrehan	if (ret)
181250199Sgrehan	    goto cleanup;
182250199Sgrehan
183250199Sgrehan	if (open_info->response.open_result.status == 0) {
184250199Sgrehan	    if(bootverbose)
185250199Sgrehan		printf("VMBUS: channel <%p> open success.\n", new_channel);
186250199Sgrehan	} else {
187250199Sgrehan	    if(bootverbose)
188250199Sgrehan		printf("Error VMBUS: channel <%p> open failed - %d!\n",
189250199Sgrehan			new_channel, open_info->response.open_result.status);
190250199Sgrehan	}
191250199Sgrehan
192250199Sgrehan	cleanup:
193250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
194250199Sgrehan	TAILQ_REMOVE(
195250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
196250199Sgrehan		open_info,
197250199Sgrehan		msg_list_entry);
198250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
199250199Sgrehan	sema_destroy(&open_info->wait_sema);
200250199Sgrehan	free(open_info, M_DEVBUF);
201250199Sgrehan
202250199Sgrehan	return (ret);
203250199Sgrehan}
204250199Sgrehan
205250199Sgrehan/**
206250199Sgrehan * @brief Create a gpadl for the specified buffer
207250199Sgrehan */
208250199Sgrehanstatic int
209250199Sgrehanvmbus_channel_create_gpadl_header(
210250199Sgrehan	void*				contig_buffer,
211250199Sgrehan	uint32_t			size,	/* page-size multiple */
212250199Sgrehan	hv_vmbus_channel_msg_info**	msg_info,
213250199Sgrehan	uint32_t*			message_count)
214250199Sgrehan{
215250199Sgrehan	int				i;
216250199Sgrehan	int				page_count;
217250199Sgrehan	unsigned long long 		pfn;
218250199Sgrehan	uint32_t			msg_size;
219250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpa_header;
220250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
221250199Sgrehan	hv_vmbus_channel_msg_info*	msg_header;
222250199Sgrehan	hv_vmbus_channel_msg_info*	msg_body;
223250199Sgrehan
224250199Sgrehan	int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
225250199Sgrehan
226250199Sgrehan	page_count = size >> PAGE_SHIFT;
227250199Sgrehan	pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
228250199Sgrehan
229250199Sgrehan	/*do we need a gpadl body msg */
230250199Sgrehan	pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
231250199Sgrehan	    - sizeof(hv_vmbus_channel_gpadl_header)
232250199Sgrehan	    - sizeof(hv_gpa_range);
233250199Sgrehan	pfnCount = pfnSize / sizeof(uint64_t);
234250199Sgrehan
235250199Sgrehan	if (page_count > pfnCount) { /* if(we need a gpadl body)	*/
236250199Sgrehan	    /* fill in the header		*/
237250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info)
238250199Sgrehan		+ sizeof(hv_vmbus_channel_gpadl_header)
239250199Sgrehan		+ sizeof(hv_gpa_range)
240250199Sgrehan		+ pfnCount * sizeof(uint64_t);
241250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
242250199Sgrehan	    KASSERT(
243250199Sgrehan		msg_header != NULL,
244250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
245250199Sgrehan	    if (msg_header == NULL)
246250199Sgrehan		return (ENOMEM);
247250199Sgrehan
248250199Sgrehan	    TAILQ_INIT(&msg_header->sub_msg_list_anchor);
249250199Sgrehan	    msg_header->message_size = msg_size;
250250199Sgrehan
251250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
252250199Sgrehan	    gpa_header->range_count = 1;
253250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range)
254250199Sgrehan		+ page_count * sizeof(uint64_t);
255250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
256250199Sgrehan	    gpa_header->range[0].byte_count = size;
257250199Sgrehan	    for (i = 0; i < pfnCount; i++) {
258250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
259250199Sgrehan	    }
260250199Sgrehan	    *msg_info = msg_header;
261250199Sgrehan	    *message_count = 1;
262250199Sgrehan
263250199Sgrehan	    pfnSum = pfnCount;
264250199Sgrehan	    pfnLeft = page_count - pfnCount;
265250199Sgrehan
266250199Sgrehan	    /*
267250199Sgrehan	     *  figure out how many pfns we can fit
268250199Sgrehan	     */
269250199Sgrehan	    pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
270250199Sgrehan		- sizeof(hv_vmbus_channel_gpadl_body);
271250199Sgrehan	    pfnCount = pfnSize / sizeof(uint64_t);
272250199Sgrehan
273250199Sgrehan	    /*
274250199Sgrehan	     * fill in the body
275250199Sgrehan	     */
276250199Sgrehan	    while (pfnLeft) {
277250199Sgrehan		if (pfnLeft > pfnCount) {
278250199Sgrehan		    pfnCurr = pfnCount;
279250199Sgrehan		} else {
280250199Sgrehan		    pfnCurr = pfnLeft;
281250199Sgrehan		}
282250199Sgrehan
283250199Sgrehan		msg_size = sizeof(hv_vmbus_channel_msg_info) +
284250199Sgrehan		    sizeof(hv_vmbus_channel_gpadl_body) +
285250199Sgrehan		    pfnCurr * sizeof(uint64_t);
286250199Sgrehan		msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
287250199Sgrehan		KASSERT(
288250199Sgrehan		    msg_body != NULL,
289250199Sgrehan		    ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
290250199Sgrehan		if (msg_body == NULL)
291250199Sgrehan		    return (ENOMEM);
292250199Sgrehan
293250199Sgrehan		msg_body->message_size = msg_size;
294250199Sgrehan		(*message_count)++;
295250199Sgrehan		gpadl_body =
296250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) msg_body->msg;
297250199Sgrehan		/*
298250199Sgrehan		 * gpadl_body->gpadl = kbuffer;
299250199Sgrehan		 */
300250199Sgrehan		for (i = 0; i < pfnCurr; i++) {
301250199Sgrehan		    gpadl_body->pfn[i] = pfn + pfnSum + i;
302250199Sgrehan		}
303250199Sgrehan
304250199Sgrehan		TAILQ_INSERT_TAIL(
305250199Sgrehan		    &msg_header->sub_msg_list_anchor,
306250199Sgrehan		    msg_body,
307250199Sgrehan		    msg_list_entry);
308250199Sgrehan		pfnSum += pfnCurr;
309250199Sgrehan		pfnLeft -= pfnCurr;
310250199Sgrehan	    }
311250199Sgrehan	} else { /* else everything fits in a header */
312250199Sgrehan
313250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info) +
314250199Sgrehan		sizeof(hv_vmbus_channel_gpadl_header) +
315250199Sgrehan		sizeof(hv_gpa_range) +
316250199Sgrehan		page_count * sizeof(uint64_t);
317250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
318250199Sgrehan	    KASSERT(
319250199Sgrehan		msg_header != NULL,
320250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
321250199Sgrehan	    if (msg_header == NULL)
322250199Sgrehan		return (ENOMEM);
323250199Sgrehan
324250199Sgrehan	    msg_header->message_size = msg_size;
325250199Sgrehan
326250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
327250199Sgrehan	    gpa_header->range_count = 1;
328250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range) +
329250199Sgrehan		page_count * sizeof(uint64_t);
330250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
331250199Sgrehan	    gpa_header->range[0].byte_count = size;
332250199Sgrehan	    for (i = 0; i < page_count; i++) {
333250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
334250199Sgrehan	    }
335250199Sgrehan
336250199Sgrehan	    *msg_info = msg_header;
337250199Sgrehan	    *message_count = 1;
338250199Sgrehan	}
339250199Sgrehan
340250199Sgrehan	return (0);
341250199Sgrehan}
342250199Sgrehan
343250199Sgrehan/**
344250199Sgrehan * @brief Establish a GPADL for the specified buffer
345250199Sgrehan */
346250199Sgrehanint
347250199Sgrehanhv_vmbus_channel_establish_gpadl(
348250199Sgrehan	hv_vmbus_channel*	channel,
349250199Sgrehan	void*			contig_buffer,
350250199Sgrehan	uint32_t		size, /* page-size multiple */
351250199Sgrehan	uint32_t*		gpadl_handle)
352250199Sgrehan
353250199Sgrehan{
354250199Sgrehan	int ret = 0;
355250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpadl_msg;
356250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
357250199Sgrehan	hv_vmbus_channel_msg_info*	msg_info;
358250199Sgrehan	hv_vmbus_channel_msg_info*	sub_msg_info;
359250199Sgrehan	uint32_t			msg_count;
360250199Sgrehan	hv_vmbus_channel_msg_info*	curr;
361250199Sgrehan	uint32_t			next_gpadl_handle;
362250199Sgrehan
363250199Sgrehan	next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle;
364250199Sgrehan	atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1);
365250199Sgrehan
366250199Sgrehan	ret = vmbus_channel_create_gpadl_header(
367250199Sgrehan		contig_buffer, size, &msg_info, &msg_count);
368250199Sgrehan
369250199Sgrehan	if(ret != 0) { /* if(allocation failed) return immediately */
370250199Sgrehan	    /* reverse atomic_add_int above */
371250199Sgrehan	    atomic_subtract_int((int*)
372250199Sgrehan		    &hv_vmbus_g_connection.next_gpadl_handle, 1);
373250199Sgrehan	    return ret;
374250199Sgrehan	}
375250199Sgrehan
376250199Sgrehan	sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
377250199Sgrehan	gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
378250199Sgrehan	gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
379250199Sgrehan	gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
380250199Sgrehan	gpadl_msg->gpadl = next_gpadl_handle;
381250199Sgrehan
382250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
383250199Sgrehan	TAILQ_INSERT_TAIL(
384250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
385250199Sgrehan		msg_info,
386250199Sgrehan		msg_list_entry);
387250199Sgrehan
388250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
389250199Sgrehan
390250199Sgrehan	ret = hv_vmbus_post_message(
391250199Sgrehan		gpadl_msg,
392250199Sgrehan		msg_info->message_size -
393250199Sgrehan		    (uint32_t) sizeof(hv_vmbus_channel_msg_info));
394250199Sgrehan
395250199Sgrehan	if (ret != 0)
396250199Sgrehan	    goto cleanup;
397250199Sgrehan
398250199Sgrehan	if (msg_count > 1) {
399250199Sgrehan	    TAILQ_FOREACH(curr,
400250199Sgrehan		    &msg_info->sub_msg_list_anchor, msg_list_entry) {
401250199Sgrehan		sub_msg_info = curr;
402250199Sgrehan		gpadl_body =
403250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
404250199Sgrehan
405250199Sgrehan		gpadl_body->header.message_type =
406250199Sgrehan		    HV_CHANNEL_MESSAGE_GPADL_BODY;
407250199Sgrehan		gpadl_body->gpadl = next_gpadl_handle;
408250199Sgrehan
409250199Sgrehan		ret = hv_vmbus_post_message(
410250199Sgrehan			gpadl_body,
411250199Sgrehan			sub_msg_info->message_size
412250199Sgrehan			    - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
413250199Sgrehan		 /* if (the post message failed) give up and clean up */
414250199Sgrehan		if(ret != 0)
415250199Sgrehan		    goto cleanup;
416250199Sgrehan	    }
417250199Sgrehan	}
418250199Sgrehan
419250199Sgrehan	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds*/
420250199Sgrehan	if (ret != 0)
421250199Sgrehan	    goto cleanup;
422250199Sgrehan
423250199Sgrehan	*gpadl_handle = gpadl_msg->gpadl;
424250199Sgrehan
425250199Sgrehancleanup:
426250199Sgrehan
427250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
428250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
429250199Sgrehan		msg_info, msg_list_entry);
430250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
431250199Sgrehan
432250199Sgrehan	sema_destroy(&msg_info->wait_sema);
433250199Sgrehan	free(msg_info, M_DEVBUF);
434250199Sgrehan
435250199Sgrehan	return (ret);
436250199Sgrehan}
437250199Sgrehan
438250199Sgrehan/**
439250199Sgrehan * @brief Teardown the specified GPADL handle
440250199Sgrehan */
441250199Sgrehanint
442250199Sgrehanhv_vmbus_channel_teardown_gpdal(
443250199Sgrehan	hv_vmbus_channel*	channel,
444250199Sgrehan	uint32_t		gpadl_handle)
445250199Sgrehan{
446250199Sgrehan	int					ret = 0;
447250199Sgrehan	hv_vmbus_channel_gpadl_teardown*	msg;
448250199Sgrehan	hv_vmbus_channel_msg_info*		info;
449250199Sgrehan
450250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
451250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
452250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown),
453250199Sgrehan				M_DEVBUF, M_NOWAIT);
454250199Sgrehan	KASSERT(info != NULL,
455250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
456250199Sgrehan	if (info == NULL) {
457250199Sgrehan	    ret = ENOMEM;
458250199Sgrehan	    goto cleanup;
459250199Sgrehan	}
460250199Sgrehan
461250199Sgrehan	sema_init(&info->wait_sema, 0, "Open Info Sema");
462250199Sgrehan
463250199Sgrehan	msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
464250199Sgrehan
465250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
466250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
467250199Sgrehan	msg->gpadl = gpadl_handle;
468250199Sgrehan
469250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
470250199Sgrehan	TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
471250199Sgrehan			info, msg_list_entry);
472250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
473250199Sgrehan
474250199Sgrehan	ret = hv_vmbus_post_message(msg,
475250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown));
476250199Sgrehan	if (ret != 0)
477250199Sgrehan	    goto cleanup;
478250199Sgrehan
479250199Sgrehan	ret = sema_timedwait(&info->wait_sema, 500); /* KYS 5 seconds */
480250199Sgrehan
481250199Sgrehancleanup:
482250199Sgrehan	/*
483250199Sgrehan	 * Received a torndown response
484250199Sgrehan	 */
485250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
486250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
487250199Sgrehan			info, msg_list_entry);
488250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
489250199Sgrehan	sema_destroy(&info->wait_sema);
490250199Sgrehan	free(info, M_DEVBUF);
491250199Sgrehan
492250199Sgrehan	return (ret);
493250199Sgrehan}
494250199Sgrehan
495250199Sgrehan/**
496250199Sgrehan * @brief Close the specified channel
497250199Sgrehan */
498250199Sgrehanvoid
499250199Sgrehanhv_vmbus_channel_close(hv_vmbus_channel *channel)
500250199Sgrehan{
501250199Sgrehan	int ret = 0;
502250199Sgrehan	hv_vmbus_channel_close_channel* msg;
503250199Sgrehan	hv_vmbus_channel_msg_info* info;
504250199Sgrehan
505250199Sgrehan	mtx_lock(&channel->inbound_lock);
506250199Sgrehan	channel->on_channel_callback = NULL;
507250199Sgrehan	mtx_unlock(&channel->inbound_lock);
508250199Sgrehan
509250199Sgrehan	/**
510250199Sgrehan	 * Send a closing message
511250199Sgrehan	 */
512250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
513250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
514250199Sgrehan			sizeof(hv_vmbus_channel_close_channel),
515250199Sgrehan				M_DEVBUF, M_NOWAIT);
516250199Sgrehan	KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
517250199Sgrehan	if(info == NULL)
518250199Sgrehan	    return;
519250199Sgrehan
520250199Sgrehan	msg = (hv_vmbus_channel_close_channel*) info->msg;
521250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
522250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
523250199Sgrehan
524250199Sgrehan	ret = hv_vmbus_post_message(
525250199Sgrehan		msg, sizeof(hv_vmbus_channel_close_channel));
526250199Sgrehan
527250199Sgrehan	/* Tear down the gpadl for the channel's ring buffer */
528250199Sgrehan	if (channel->ring_buffer_gpadl_handle) {
529250199Sgrehan		hv_vmbus_channel_teardown_gpdal(channel,
530250199Sgrehan			channel->ring_buffer_gpadl_handle);
531250199Sgrehan	}
532250199Sgrehan
533250199Sgrehan	/* TODO: Send a msg to release the childRelId */
534250199Sgrehan
535250199Sgrehan	/* cleanup the ring buffers for this channel */
536250199Sgrehan	hv_ring_buffer_cleanup(&channel->outbound);
537250199Sgrehan	hv_ring_buffer_cleanup(&channel->inbound);
538250199Sgrehan
539250199Sgrehan	contigfree(
540250199Sgrehan		channel->ring_buffer_pages,
541250199Sgrehan		channel->ring_buffer_page_count,
542250199Sgrehan		M_DEVBUF);
543250199Sgrehan
544250199Sgrehan	free(info, M_DEVBUF);
545250199Sgrehan
546250199Sgrehan	/*
547250199Sgrehan	 *  If we are closing the channel during an error path in
548250199Sgrehan	 *  opening the channel, don't free the channel
549250199Sgrehan	 *  since the caller will free the channel
550250199Sgrehan	 */
551250199Sgrehan	if (channel->state == HV_CHANNEL_OPEN_STATE) {
552250199Sgrehan		mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
553250199Sgrehan		TAILQ_REMOVE(
554250199Sgrehan			&hv_vmbus_g_connection.channel_anchor,
555250199Sgrehan			channel,
556250199Sgrehan			list_entry);
557250199Sgrehan		mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
558250199Sgrehan
559250199Sgrehan		hv_vmbus_free_vmbus_channel(channel);
560250199Sgrehan	}
561250199Sgrehan
562250199Sgrehan}
563250199Sgrehan
564250199Sgrehan/**
565250199Sgrehan * @brief Send the specified buffer on the given channel
566250199Sgrehan */
567250199Sgrehanint
568250199Sgrehanhv_vmbus_channel_send_packet(
569250199Sgrehan	hv_vmbus_channel*	channel,
570250199Sgrehan	void*			buffer,
571250199Sgrehan	uint32_t		buffer_len,
572250199Sgrehan	uint64_t		request_id,
573250199Sgrehan	hv_vmbus_packet_type	type,
574250199Sgrehan	uint32_t		flags)
575250199Sgrehan{
576250199Sgrehan	int			ret = 0;
577250199Sgrehan	hv_vm_packet_descriptor	desc;
578250199Sgrehan	uint32_t		packet_len;
579250199Sgrehan	uint64_t		aligned_data;
580250199Sgrehan	uint32_t		packet_len_aligned;
581250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
582250199Sgrehan
583250199Sgrehan	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
584250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
585250199Sgrehan	aligned_data = 0;
586250199Sgrehan
587250199Sgrehan	/* Setup the descriptor */
588250199Sgrehan	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
589250199Sgrehan	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
590250199Sgrehan			    /* in 8-bytes granularity */
591250199Sgrehan	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
592250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
593250199Sgrehan	desc.transaction_id = request_id;
594250199Sgrehan
595250199Sgrehan	buffer_list[0].data = &desc;
596250199Sgrehan	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
597250199Sgrehan
598250199Sgrehan	buffer_list[1].data = buffer;
599250199Sgrehan	buffer_list[1].length = buffer_len;
600250199Sgrehan
601250199Sgrehan	buffer_list[2].data = &aligned_data;
602250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
603250199Sgrehan
604250199Sgrehan	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
605250199Sgrehan
606250199Sgrehan	/* TODO: We should determine if this is optional */
607250199Sgrehan	if (ret == 0
608250199Sgrehan		&& !hv_vmbus_get_ring_buffer_interrupt_mask(
609250199Sgrehan			&channel->outbound)) {
610250199Sgrehan		vmbus_channel_set_event(channel);
611250199Sgrehan	}
612250199Sgrehan
613250199Sgrehan	return (ret);
614250199Sgrehan}
615250199Sgrehan
616250199Sgrehan/**
617250199Sgrehan * @brief Send a range of single-page buffer packets using
618250199Sgrehan * a GPADL Direct packet type
619250199Sgrehan */
620250199Sgrehanint
621250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer(
622250199Sgrehan	hv_vmbus_channel*	channel,
623250199Sgrehan	hv_vmbus_page_buffer	page_buffers[],
624250199Sgrehan	uint32_t		page_count,
625250199Sgrehan	void*			buffer,
626250199Sgrehan	uint32_t		buffer_len,
627250199Sgrehan	uint64_t		request_id)
628250199Sgrehan{
629250199Sgrehan
630250199Sgrehan	int					ret = 0;
631250199Sgrehan	int					i = 0;
632250199Sgrehan	uint32_t				packet_len;
633250199Sgrehan	uint32_t				packetLen_aligned;
634250199Sgrehan	hv_vmbus_sg_buffer_list			buffer_list[3];
635250199Sgrehan	hv_vmbus_channel_packet_page_buffer	desc;
636250199Sgrehan	uint32_t				descSize;
637250199Sgrehan	uint64_t				alignedData = 0;
638250199Sgrehan
639250199Sgrehan	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
640250199Sgrehan		return (EINVAL);
641250199Sgrehan
642250199Sgrehan	/*
643250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
644250199Sgrehan	 *  is the largest size we support
645250199Sgrehan	 */
646250199Sgrehan	descSize = sizeof(hv_vmbus_channel_packet_page_buffer) -
647250199Sgrehan			((HV_MAX_PAGE_BUFFER_COUNT - page_count) *
648250199Sgrehan			sizeof(hv_vmbus_page_buffer));
649250199Sgrehan	packet_len = descSize + buffer_len;
650250199Sgrehan	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
651250199Sgrehan
652250199Sgrehan	/* Setup the descriptor */
653250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
654250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
655250199Sgrehan	desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */
656250199Sgrehan	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
657250199Sgrehan	desc.transaction_id = request_id;
658250199Sgrehan	desc.range_count = page_count;
659250199Sgrehan
660250199Sgrehan	for (i = 0; i < page_count; i++) {
661250199Sgrehan		desc.range[i].length = page_buffers[i].length;
662250199Sgrehan		desc.range[i].offset = page_buffers[i].offset;
663250199Sgrehan		desc.range[i].pfn = page_buffers[i].pfn;
664250199Sgrehan	}
665250199Sgrehan
666250199Sgrehan	buffer_list[0].data = &desc;
667250199Sgrehan	buffer_list[0].length = descSize;
668250199Sgrehan
669250199Sgrehan	buffer_list[1].data = buffer;
670250199Sgrehan	buffer_list[1].length = buffer_len;
671250199Sgrehan
672250199Sgrehan	buffer_list[2].data = &alignedData;
673250199Sgrehan	buffer_list[2].length = packetLen_aligned - packet_len;
674250199Sgrehan
675250199Sgrehan	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
676250199Sgrehan
677250199Sgrehan	/* TODO: We should determine if this is optional */
678250199Sgrehan	if (ret == 0 &&
679250199Sgrehan		!hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
680250199Sgrehan		vmbus_channel_set_event(channel);
681250199Sgrehan	}
682250199Sgrehan
683250199Sgrehan	return (ret);
684250199Sgrehan}
685250199Sgrehan
686250199Sgrehan/**
687250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type
688250199Sgrehan */
689250199Sgrehanint
690250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer(
691250199Sgrehan	hv_vmbus_channel*		channel,
692250199Sgrehan	hv_vmbus_multipage_buffer*	multi_page_buffer,
693250199Sgrehan	void*				buffer,
694250199Sgrehan	uint32_t			buffer_len,
695250199Sgrehan	uint64_t			request_id)
696250199Sgrehan{
697250199Sgrehan
698250199Sgrehan	int			ret = 0;
699250199Sgrehan	uint32_t		desc_size;
700250199Sgrehan	uint32_t		packet_len;
701250199Sgrehan	uint32_t		packet_len_aligned;
702250199Sgrehan	uint32_t		pfn_count;
703250199Sgrehan	uint64_t		aligned_data = 0;
704250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
705250199Sgrehan	hv_vmbus_channel_packet_multipage_buffer desc;
706250199Sgrehan
707250199Sgrehan	pfn_count =
708250199Sgrehan	    HV_NUM_PAGES_SPANNED(
709250199Sgrehan		    multi_page_buffer->offset,
710250199Sgrehan		    multi_page_buffer->length);
711250199Sgrehan
712250199Sgrehan	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
713250199Sgrehan	    return (EINVAL);
714250199Sgrehan	/*
715250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
716250199Sgrehan	 * is the largest size we support
717250199Sgrehan	 */
718250199Sgrehan	desc_size =
719250199Sgrehan	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
720250199Sgrehan		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
721250199Sgrehan			sizeof(uint64_t));
722250199Sgrehan	packet_len = desc_size + buffer_len;
723250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
724250199Sgrehan
725250199Sgrehan	/*
726250199Sgrehan	 * Setup the descriptor
727250199Sgrehan	 */
728250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
729250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
730250199Sgrehan	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
731250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
732250199Sgrehan	desc.transaction_id = request_id;
733250199Sgrehan	desc.range_count = 1;
734250199Sgrehan
735250199Sgrehan	desc.range.length = multi_page_buffer->length;
736250199Sgrehan	desc.range.offset = multi_page_buffer->offset;
737250199Sgrehan
738250199Sgrehan	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
739250199Sgrehan		pfn_count * sizeof(uint64_t));
740250199Sgrehan
741250199Sgrehan	buffer_list[0].data = &desc;
742250199Sgrehan	buffer_list[0].length = desc_size;
743250199Sgrehan
744250199Sgrehan	buffer_list[1].data = buffer;
745250199Sgrehan	buffer_list[1].length = buffer_len;
746250199Sgrehan
747250199Sgrehan	buffer_list[2].data = &aligned_data;
748250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
749250199Sgrehan
750250199Sgrehan	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
751250199Sgrehan
752250199Sgrehan	/* TODO: We should determine if this is optional */
753250199Sgrehan	if (ret == 0 &&
754250199Sgrehan	    !hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
755250199Sgrehan	    vmbus_channel_set_event(channel);
756250199Sgrehan	}
757250199Sgrehan
758250199Sgrehan	return (ret);
759250199Sgrehan}
760250199Sgrehan
761250199Sgrehan/**
762250199Sgrehan * @brief Retrieve the user packet on the specified channel
763250199Sgrehan */
764250199Sgrehanint
765250199Sgrehanhv_vmbus_channel_recv_packet(
766250199Sgrehan	hv_vmbus_channel*	channel,
767250199Sgrehan	void*			Buffer,
768250199Sgrehan	uint32_t		buffer_len,
769250199Sgrehan	uint32_t*		buffer_actual_len,
770250199Sgrehan	uint64_t*		request_id)
771250199Sgrehan{
772250199Sgrehan	int			ret;
773250199Sgrehan	uint32_t		user_len;
774250199Sgrehan	uint32_t		packet_len;
775250199Sgrehan	hv_vm_packet_descriptor	desc;
776250199Sgrehan
777250199Sgrehan	*buffer_actual_len = 0;
778250199Sgrehan	*request_id = 0;
779250199Sgrehan
780250199Sgrehan	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
781250199Sgrehan		sizeof(hv_vm_packet_descriptor));
782250199Sgrehan	if (ret != 0)
783250199Sgrehan		return (0);
784250199Sgrehan
785250199Sgrehan	packet_len = desc.length8 << 3;
786250199Sgrehan	user_len = packet_len - (desc.data_offset8 << 3);
787250199Sgrehan
788250199Sgrehan	*buffer_actual_len = user_len;
789250199Sgrehan
790250199Sgrehan	if (user_len > buffer_len)
791250199Sgrehan		return (EINVAL);
792250199Sgrehan
793250199Sgrehan	*request_id = desc.transaction_id;
794250199Sgrehan
795250199Sgrehan	/* Copy over the packet to the user buffer */
796250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
797250199Sgrehan		(desc.data_offset8 << 3));
798250199Sgrehan
799250199Sgrehan	return (0);
800250199Sgrehan}
801250199Sgrehan
802250199Sgrehan/**
803250199Sgrehan * @brief Retrieve the raw packet on the specified channel
804250199Sgrehan */
805250199Sgrehanint
806250199Sgrehanhv_vmbus_channel_recv_packet_raw(
807250199Sgrehan	hv_vmbus_channel*	channel,
808250199Sgrehan	void*			buffer,
809250199Sgrehan	uint32_t		buffer_len,
810250199Sgrehan	uint32_t*		buffer_actual_len,
811250199Sgrehan	uint64_t*		request_id)
812250199Sgrehan{
813250199Sgrehan	int		ret;
814250199Sgrehan	uint32_t	packetLen;
815250199Sgrehan	uint32_t	userLen;
816250199Sgrehan	hv_vm_packet_descriptor	desc;
817250199Sgrehan
818250199Sgrehan	*buffer_actual_len = 0;
819250199Sgrehan	*request_id = 0;
820250199Sgrehan
821250199Sgrehan	ret = hv_ring_buffer_peek(
822250199Sgrehan		&channel->inbound, &desc,
823250199Sgrehan		sizeof(hv_vm_packet_descriptor));
824250199Sgrehan
825250199Sgrehan	if (ret != 0)
826250199Sgrehan	    return (0);
827250199Sgrehan
828250199Sgrehan	packetLen = desc.length8 << 3;
829250199Sgrehan	userLen = packetLen - (desc.data_offset8 << 3);
830250199Sgrehan
831250199Sgrehan	*buffer_actual_len = packetLen;
832250199Sgrehan
833250199Sgrehan	if (packetLen > buffer_len)
834250199Sgrehan	    return (ENOBUFS);
835250199Sgrehan
836250199Sgrehan	*request_id = desc.transaction_id;
837250199Sgrehan
838250199Sgrehan	/* Copy over the entire packet to the user buffer */
839250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
840250199Sgrehan
841250199Sgrehan	return (0);
842250199Sgrehan}
843