vmbus_chan.c revision 256350
1151937Sjkim/*-
2151937Sjkim * Copyright (c) 2009-2012 Microsoft Corp.
3151937Sjkim * Copyright (c) 2012 NetApp Inc.
4151937Sjkim * Copyright (c) 2012 Citrix Inc.
5151937Sjkim * All rights reserved.
6151937Sjkim *
7217365Sjkim * Redistribution and use in source and binary forms, with or without
8306536Sjkim * modification, are permitted provided that the following conditions
9151937Sjkim * are met:
10151937Sjkim * 1. Redistributions of source code must retain the above copyright
11217365Sjkim *    notice unmodified, this list of conditions, and the following
12217365Sjkim *    disclaimer.
13217365Sjkim * 2. Redistributions in binary form must reproduce the above copyright
14217365Sjkim *    notice, this list of conditions and the following disclaimer in the
15217365Sjkim *    documentation and/or other materials provided with the distribution.
16217365Sjkim *
17217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18217365Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19217365Sjkim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20217365Sjkim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21217365Sjkim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22217365Sjkim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23217365Sjkim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24217365Sjkim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25151937Sjkim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26217365Sjkim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27217365Sjkim */
28217365Sjkim
29151937Sjkim#include <sys/cdefs.h>
30217365Sjkim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_channel.c 256350 2013-10-11 21:30:27Z grehan $");
31217365Sjkim
32217365Sjkim#include <sys/param.h>
33217365Sjkim#include <sys/malloc.h>
34217365Sjkim#include <sys/systm.h>
35217365Sjkim#include <sys/mbuf.h>
36217365Sjkim#include <sys/lock.h>
37217365Sjkim#include <sys/mutex.h>
38217365Sjkim#include <machine/bus.h>
39217365Sjkim#include <vm/vm.h>
40217365Sjkim#include <vm/vm_param.h>
41217365Sjkim#include <vm/pmap.h>
42217365Sjkim
43151937Sjkim#include "hv_vmbus_priv.h"
44193341Sjkim
45193341Sjkimstatic int 	vmbus_channel_create_gpadl_header(
46151937Sjkim			/* must be phys and virt contiguous*/
47151937Sjkim			void*				contig_buffer,
48151937Sjkim			/* page-size multiple */
49151937Sjkim			uint32_t 			size,
50151937Sjkim			hv_vmbus_channel_msg_info**	msg_info,
51151937Sjkim			uint32_t*			message_count);
52151937Sjkim
53151937Sjkimstatic void 	vmbus_channel_set_event(hv_vmbus_channel* channel);
54151937Sjkim
55151937Sjkim/**
56151937Sjkim *  @brief Trigger an event notification on the specified channel
57151937Sjkim */
58151937Sjkimstatic void
59151937Sjkimvmbus_channel_set_event(hv_vmbus_channel *channel)
60151937Sjkim{
61151937Sjkim	hv_vmbus_monitor_page *monitor_page;
62151937Sjkim
63151937Sjkim	if (channel->offer_msg.monitor_allocated) {
64151937Sjkim		/* Each uint32_t represents 32 channels */
65151937Sjkim		synch_set_bit((channel->offer_msg.child_rel_id & 31),
66151937Sjkim			((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
67151937Sjkim				+ ((channel->offer_msg.child_rel_id >> 5))));
68151937Sjkim
69151937Sjkim		monitor_page = (hv_vmbus_monitor_page *)
70151937Sjkim			hv_vmbus_g_connection.monitor_pages;
71151937Sjkim
72151937Sjkim		monitor_page++; /* Get the child to parent monitor page */
73151937Sjkim
74151937Sjkim		synch_set_bit(channel->monitor_bit,
75151937Sjkim			(uint32_t *)&monitor_page->
76151937Sjkim				trigger_group[channel->monitor_group].u.pending);
77151937Sjkim	} else {
78151937Sjkim		hv_vmbus_set_event(channel->offer_msg.child_rel_id);
79151937Sjkim	}
80151937Sjkim
81151937Sjkim}
82151937Sjkim
83151937Sjkim/**
84151937Sjkim * @brief Open the specified channel
85151937Sjkim */
86151937Sjkimint
87151937Sjkimhv_vmbus_channel_open(
88151937Sjkim	hv_vmbus_channel*		new_channel,
89151937Sjkim	uint32_t			send_ring_buffer_size,
90151937Sjkim	uint32_t			recv_ring_buffer_size,
91151937Sjkim	void*				user_data,
92151937Sjkim	uint32_t			user_data_len,
93151937Sjkim	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
94151937Sjkim	void* 				context)
95306536Sjkim{
96306536Sjkim
97151937Sjkim	int ret = 0;
98306536Sjkim	void *in, *out;
99151937Sjkim	hv_vmbus_channel_open_channel*	open_msg;
100151937Sjkim	hv_vmbus_channel_msg_info* 	open_info;
101151937Sjkim
102151937Sjkim	new_channel->on_channel_callback = pfn_on_channel_callback;
103151937Sjkim	new_channel->channel_callback_context = context;
104151937Sjkim
105151937Sjkim	/* Allocate the ring buffer */
106151937Sjkim	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
107151937Sjkim	    M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
108151937Sjkim	KASSERT(out != NULL,
109151937Sjkim	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
110151937Sjkim	if (out == NULL)
111151937Sjkim		return (ENOMEM);
112151937Sjkim
113151937Sjkim	in = ((uint8_t *) out + send_ring_buffer_size);
114151937Sjkim
115151937Sjkim	new_channel->ring_buffer_pages = out;
116151937Sjkim	new_channel->ring_buffer_page_count = (send_ring_buffer_size +
117151937Sjkim	    recv_ring_buffer_size) >> PAGE_SHIFT;
118151937Sjkim	new_channel->ring_buffer_size = send_ring_buffer_size +
119151937Sjkim	    recv_ring_buffer_size;
120151937Sjkim
121246040Sjkim	hv_vmbus_ring_buffer_init(
122193267Sjkim		&new_channel->outbound,
123151937Sjkim		out,
124151937Sjkim		send_ring_buffer_size);
125151937Sjkim
126151937Sjkim	hv_vmbus_ring_buffer_init(
127151937Sjkim		&new_channel->inbound,
128151937Sjkim		in,
129151937Sjkim		recv_ring_buffer_size);
130151937Sjkim
131151937Sjkim	/**
132151937Sjkim	 * Establish the gpadl for the ring buffer
133193267Sjkim	 */
134193267Sjkim	new_channel->ring_buffer_gpadl_handle = 0;
135193267Sjkim
136193267Sjkim	ret = hv_vmbus_channel_establish_gpadl(new_channel,
137193267Sjkim		new_channel->outbound.ring_buffer,
138193267Sjkim		send_ring_buffer_size + recv_ring_buffer_size,
139151937Sjkim		&new_channel->ring_buffer_gpadl_handle);
140151937Sjkim
141151937Sjkim	/**
142151937Sjkim	 * Create and init the channel open message
143151937Sjkim	 */
144151937Sjkim	open_info = (hv_vmbus_channel_msg_info*) malloc(
145246040Sjkim		sizeof(hv_vmbus_channel_msg_info) +
146167802Sjkim			sizeof(hv_vmbus_channel_open_channel),
147151937Sjkim		M_DEVBUF,
148151937Sjkim		M_NOWAIT);
149151937Sjkim	KASSERT(open_info != NULL,
150151937Sjkim	    ("Error VMBUS: malloc failed to allocate Open Channel message!"));
151151937Sjkim
152193267Sjkim	if (open_info == NULL)
153151937Sjkim		return (ENOMEM);
154151937Sjkim
155151937Sjkim	sema_init(&open_info->wait_sema, 0, "Open Info Sema");
156151937Sjkim
157151937Sjkim	open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
158151937Sjkim	open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
159151937Sjkim	open_msg->open_id = new_channel->offer_msg.child_rel_id;
160151937Sjkim	open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
161151937Sjkim	open_msg->ring_buffer_gpadl_handle =
162151937Sjkim		new_channel->ring_buffer_gpadl_handle;
163151937Sjkim	open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
164151937Sjkim		>> PAGE_SHIFT;
165151937Sjkim	open_msg->server_context_area_gpadl_handle = 0;
166151937Sjkim
167151937Sjkim	if (user_data_len)
168151937Sjkim		memcpy(open_msg->user_data, user_data, user_data_len);
169151937Sjkim
170151937Sjkim	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
171151937Sjkim	TAILQ_INSERT_TAIL(
172151937Sjkim		&hv_vmbus_g_connection.channel_msg_anchor,
173151937Sjkim		open_info,
174151937Sjkim		msg_list_entry);
175151937Sjkim	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
176151937Sjkim
177151937Sjkim	ret = hv_vmbus_post_message(
178151937Sjkim		open_msg, sizeof(hv_vmbus_channel_open_channel));
179151937Sjkim
180151937Sjkim	if (ret != 0)
181151937Sjkim	    goto cleanup;
182151937Sjkim
183151937Sjkim	ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */
184151937Sjkim
185151937Sjkim	if (ret)
186151937Sjkim	    goto cleanup;
187151937Sjkim
188151937Sjkim	if (open_info->response.open_result.status == 0) {
189151937Sjkim	    if(bootverbose)
190151937Sjkim		printf("VMBUS: channel <%p> open success.\n", new_channel);
191151937Sjkim	} else {
192151937Sjkim	    if(bootverbose)
193151937Sjkim		printf("Error VMBUS: channel <%p> open failed - %d!\n",
194151937Sjkim			new_channel, open_info->response.open_result.status);
195151937Sjkim	}
196151937Sjkim
197151937Sjkim	cleanup:
198151937Sjkim	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
199151937Sjkim	TAILQ_REMOVE(
200151937Sjkim		&hv_vmbus_g_connection.channel_msg_anchor,
201151937Sjkim		open_info,
202151937Sjkim		msg_list_entry);
203151937Sjkim	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
204241973Sjkim	sema_destroy(&open_info->wait_sema);
205151937Sjkim	free(open_info, M_DEVBUF);
206151937Sjkim
207151937Sjkim	return (ret);
208151937Sjkim}
209151937Sjkim
210151937Sjkim/**
211151937Sjkim * @brief Create a gpadl for the specified buffer
212151937Sjkim */
213151937Sjkimstatic int
214151937Sjkimvmbus_channel_create_gpadl_header(
215151937Sjkim	void*				contig_buffer,
216151937Sjkim	uint32_t			size,	/* page-size multiple */
217151937Sjkim	hv_vmbus_channel_msg_info**	msg_info,
218151937Sjkim	uint32_t*			message_count)
219151937Sjkim{
220151937Sjkim	int				i;
221151937Sjkim	int				page_count;
222151937Sjkim	unsigned long long 		pfn;
223151937Sjkim	uint32_t			msg_size;
224151937Sjkim	hv_vmbus_channel_gpadl_header*	gpa_header;
225151937Sjkim	hv_vmbus_channel_gpadl_body*	gpadl_body;
226151937Sjkim	hv_vmbus_channel_msg_info*	msg_header;
227151937Sjkim	hv_vmbus_channel_msg_info*	msg_body;
228151937Sjkim
229167802Sjkim	int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
230151937Sjkim
231151937Sjkim	page_count = size >> PAGE_SHIFT;
232151937Sjkim	pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
233151937Sjkim
234151937Sjkim	/*do we need a gpadl body msg */
235151937Sjkim	pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
236151937Sjkim	    - sizeof(hv_vmbus_channel_gpadl_header)
237151937Sjkim	    - sizeof(hv_gpa_range);
238151937Sjkim	pfnCount = pfnSize / sizeof(uint64_t);
239151937Sjkim
240151937Sjkim	if (page_count > pfnCount) { /* if(we need a gpadl body)	*/
241151937Sjkim	    /* fill in the header		*/
242151937Sjkim	    msg_size = sizeof(hv_vmbus_channel_msg_info)
243151937Sjkim		+ sizeof(hv_vmbus_channel_gpadl_header)
244151937Sjkim		+ sizeof(hv_gpa_range)
245306536Sjkim		+ pfnCount * sizeof(uint64_t);
246151937Sjkim	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
247151937Sjkim	    KASSERT(
248151937Sjkim		msg_header != NULL,
249151937Sjkim		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
250246040Sjkim	    if (msg_header == NULL)
251151937Sjkim		return (ENOMEM);
252151937Sjkim
253151937Sjkim	    TAILQ_INIT(&msg_header->sub_msg_list_anchor);
254151937Sjkim	    msg_header->message_size = msg_size;
255151937Sjkim
256151937Sjkim	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
257151937Sjkim	    gpa_header->range_count = 1;
258151937Sjkim	    gpa_header->range_buf_len = sizeof(hv_gpa_range)
259151937Sjkim		+ page_count * sizeof(uint64_t);
260151937Sjkim	    gpa_header->range[0].byte_offset = 0;
261151937Sjkim	    gpa_header->range[0].byte_count = size;
262151937Sjkim	    for (i = 0; i < pfnCount; i++) {
263151937Sjkim		gpa_header->range[0].pfn_array[i] = pfn + i;
264151937Sjkim	    }
265151937Sjkim	    *msg_info = msg_header;
266151937Sjkim	    *message_count = 1;
267241973Sjkim
268151937Sjkim	    pfnSum = pfnCount;
269241973Sjkim	    pfnLeft = page_count - pfnCount;
270151937Sjkim
271151937Sjkim	    /*
272151937Sjkim	     *  figure out how many pfns we can fit
273151937Sjkim	     */
274151937Sjkim	    pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
275151937Sjkim		- sizeof(hv_vmbus_channel_gpadl_body);
276151937Sjkim	    pfnCount = pfnSize / sizeof(uint64_t);
277151937Sjkim
278151937Sjkim	    /*
279151937Sjkim	     * fill in the body
280151937Sjkim	     */
281151937Sjkim	    while (pfnLeft) {
282306536Sjkim		if (pfnLeft > pfnCount) {
283151937Sjkim		    pfnCurr = pfnCount;
284151937Sjkim		} else {
285151937Sjkim		    pfnCurr = pfnLeft;
286151937Sjkim		}
287281075Sdim
288151937Sjkim		msg_size = sizeof(hv_vmbus_channel_msg_info) +
289151937Sjkim		    sizeof(hv_vmbus_channel_gpadl_body) +
290151937Sjkim		    pfnCurr * sizeof(uint64_t);
291151937Sjkim		msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
292151937Sjkim		KASSERT(
293281075Sdim		    msg_body != NULL,
294151937Sjkim		    ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
295151937Sjkim		if (msg_body == NULL)
296151937Sjkim		    return (ENOMEM);
297151937Sjkim
298151937Sjkim		msg_body->message_size = msg_size;
299151937Sjkim		(*message_count)++;
300151937Sjkim		gpadl_body =
301151937Sjkim		    (hv_vmbus_channel_gpadl_body*) msg_body->msg;
302151937Sjkim		/*
303151937Sjkim		 * gpadl_body->gpadl = kbuffer;
304151937Sjkim		 */
305246040Sjkim		for (i = 0; i < pfnCurr; i++) {
306151937Sjkim		    gpadl_body->pfn[i] = pfn + pfnSum + i;
307151937Sjkim		}
308151937Sjkim
309151937Sjkim		TAILQ_INSERT_TAIL(
310167802Sjkim		    &msg_header->sub_msg_list_anchor,
311167802Sjkim		    msg_body,
312151937Sjkim		    msg_list_entry);
313151937Sjkim		pfnSum += pfnCurr;
314151937Sjkim		pfnLeft -= pfnCurr;
315151937Sjkim	    }
316281075Sdim	} else { /* else everything fits in a header */
317151937Sjkim
318151937Sjkim	    msg_size = sizeof(hv_vmbus_channel_msg_info) +
319151937Sjkim		sizeof(hv_vmbus_channel_gpadl_header) +
320151937Sjkim		sizeof(hv_gpa_range) +
321306536Sjkim		page_count * sizeof(uint64_t);
322151937Sjkim	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
323151937Sjkim	    KASSERT(
324151937Sjkim		msg_header != NULL,
325151937Sjkim		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
326151937Sjkim	    if (msg_header == NULL)
327151937Sjkim		return (ENOMEM);
328151937Sjkim
329167802Sjkim	    msg_header->message_size = msg_size;
330167802Sjkim
331167802Sjkim	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
332167802Sjkim	    gpa_header->range_count = 1;
333167802Sjkim	    gpa_header->range_buf_len = sizeof(hv_gpa_range) +
334167802Sjkim		page_count * sizeof(uint64_t);
335151937Sjkim	    gpa_header->range[0].byte_offset = 0;
336167802Sjkim	    gpa_header->range[0].byte_count = size;
337167802Sjkim	    for (i = 0; i < page_count; i++) {
338151937Sjkim		gpa_header->range[0].pfn_array[i] = pfn + i;
339151937Sjkim	    }
340151937Sjkim
341281075Sdim	    *msg_info = msg_header;
342151937Sjkim	    *message_count = 1;
343151937Sjkim	}
344167802Sjkim
345151937Sjkim	return (0);
346151937Sjkim}
347281075Sdim
348151937Sjkim/**
349151937Sjkim * @brief Establish a GPADL for the specified buffer
350151937Sjkim */
351281075Sdimint
352151937Sjkimhv_vmbus_channel_establish_gpadl(
353151937Sjkim	hv_vmbus_channel*	channel,
354	void*			contig_buffer,
355	uint32_t		size, /* page-size multiple */
356	uint32_t*		gpadl_handle)
357
358{
359	int ret = 0;
360	hv_vmbus_channel_gpadl_header*	gpadl_msg;
361	hv_vmbus_channel_gpadl_body*	gpadl_body;
362	hv_vmbus_channel_msg_info*	msg_info;
363	hv_vmbus_channel_msg_info*	sub_msg_info;
364	uint32_t			msg_count;
365	hv_vmbus_channel_msg_info*	curr;
366	uint32_t			next_gpadl_handle;
367
368	next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle;
369	atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1);
370
371	ret = vmbus_channel_create_gpadl_header(
372		contig_buffer, size, &msg_info, &msg_count);
373
374	if(ret != 0) { /* if(allocation failed) return immediately */
375	    /* reverse atomic_add_int above */
376	    atomic_subtract_int((int*)
377		    &hv_vmbus_g_connection.next_gpadl_handle, 1);
378	    return ret;
379	}
380
381	sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
382	gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
383	gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
384	gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
385	gpadl_msg->gpadl = next_gpadl_handle;
386
387	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
388	TAILQ_INSERT_TAIL(
389		&hv_vmbus_g_connection.channel_msg_anchor,
390		msg_info,
391		msg_list_entry);
392
393	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
394
395	ret = hv_vmbus_post_message(
396		gpadl_msg,
397		msg_info->message_size -
398		    (uint32_t) sizeof(hv_vmbus_channel_msg_info));
399
400	if (ret != 0)
401	    goto cleanup;
402
403	if (msg_count > 1) {
404	    TAILQ_FOREACH(curr,
405		    &msg_info->sub_msg_list_anchor, msg_list_entry) {
406		sub_msg_info = curr;
407		gpadl_body =
408		    (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
409
410		gpadl_body->header.message_type =
411		    HV_CHANNEL_MESSAGE_GPADL_BODY;
412		gpadl_body->gpadl = next_gpadl_handle;
413
414		ret = hv_vmbus_post_message(
415			gpadl_body,
416			sub_msg_info->message_size
417			    - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
418		 /* if (the post message failed) give up and clean up */
419		if(ret != 0)
420		    goto cleanup;
421	    }
422	}
423
424	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds*/
425	if (ret != 0)
426	    goto cleanup;
427
428	*gpadl_handle = gpadl_msg->gpadl;
429
430cleanup:
431
432	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
433	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
434		msg_info, msg_list_entry);
435	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
436
437	sema_destroy(&msg_info->wait_sema);
438	free(msg_info, M_DEVBUF);
439
440	return (ret);
441}
442
443/**
444 * @brief Teardown the specified GPADL handle
445 */
446int
447hv_vmbus_channel_teardown_gpdal(
448	hv_vmbus_channel*	channel,
449	uint32_t		gpadl_handle)
450{
451	int					ret = 0;
452	hv_vmbus_channel_gpadl_teardown*	msg;
453	hv_vmbus_channel_msg_info*		info;
454
455	info = (hv_vmbus_channel_msg_info *)
456		malloc(	sizeof(hv_vmbus_channel_msg_info) +
457			sizeof(hv_vmbus_channel_gpadl_teardown),
458				M_DEVBUF, M_NOWAIT);
459	KASSERT(info != NULL,
460	    ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
461	if (info == NULL) {
462	    ret = ENOMEM;
463	    goto cleanup;
464	}
465
466	sema_init(&info->wait_sema, 0, "Open Info Sema");
467
468	msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
469
470	msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
471	msg->child_rel_id = channel->offer_msg.child_rel_id;
472	msg->gpadl = gpadl_handle;
473
474	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
475	TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
476			info, msg_list_entry);
477	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
478
479	ret = hv_vmbus_post_message(msg,
480			sizeof(hv_vmbus_channel_gpadl_teardown));
481	if (ret != 0)
482	    goto cleanup;
483
484	ret = sema_timedwait(&info->wait_sema, 500); /* KYS 5 seconds */
485
486cleanup:
487	/*
488	 * Received a torndown response
489	 */
490	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
491	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
492			info, msg_list_entry);
493	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
494	sema_destroy(&info->wait_sema);
495	free(info, M_DEVBUF);
496
497	return (ret);
498}
499
500/**
501 * @brief Close the specified channel
502 */
503void
504hv_vmbus_channel_close(hv_vmbus_channel *channel)
505{
506	int ret = 0;
507	hv_vmbus_channel_close_channel* msg;
508	hv_vmbus_channel_msg_info* info;
509
510	mtx_lock(&channel->inbound_lock);
511	channel->on_channel_callback = NULL;
512	mtx_unlock(&channel->inbound_lock);
513
514	/**
515	 * Send a closing message
516	 */
517	info = (hv_vmbus_channel_msg_info *)
518		malloc(	sizeof(hv_vmbus_channel_msg_info) +
519			sizeof(hv_vmbus_channel_close_channel),
520				M_DEVBUF, M_NOWAIT);
521	KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
522	if(info == NULL)
523	    return;
524
525	msg = (hv_vmbus_channel_close_channel*) info->msg;
526	msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
527	msg->child_rel_id = channel->offer_msg.child_rel_id;
528
529	ret = hv_vmbus_post_message(
530		msg, sizeof(hv_vmbus_channel_close_channel));
531
532	/* Tear down the gpadl for the channel's ring buffer */
533	if (channel->ring_buffer_gpadl_handle) {
534		hv_vmbus_channel_teardown_gpdal(channel,
535			channel->ring_buffer_gpadl_handle);
536	}
537
538	/* TODO: Send a msg to release the childRelId */
539
540	/* cleanup the ring buffers for this channel */
541	hv_ring_buffer_cleanup(&channel->outbound);
542	hv_ring_buffer_cleanup(&channel->inbound);
543
544	contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
545	    M_DEVBUF);
546
547	free(info, M_DEVBUF);
548
549	/*
550	 *  If we are closing the channel during an error path in
551	 *  opening the channel, don't free the channel
552	 *  since the caller will free the channel
553	 */
554	if (channel->state == HV_CHANNEL_OPEN_STATE) {
555		mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
556		TAILQ_REMOVE(
557			&hv_vmbus_g_connection.channel_anchor,
558			channel,
559			list_entry);
560		mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
561
562		hv_vmbus_free_vmbus_channel(channel);
563	}
564
565}
566
567/**
568 * @brief Send the specified buffer on the given channel
569 */
570int
571hv_vmbus_channel_send_packet(
572	hv_vmbus_channel*	channel,
573	void*			buffer,
574	uint32_t		buffer_len,
575	uint64_t		request_id,
576	hv_vmbus_packet_type	type,
577	uint32_t		flags)
578{
579	int			ret = 0;
580	hv_vm_packet_descriptor	desc;
581	uint32_t		packet_len;
582	uint64_t		aligned_data;
583	uint32_t		packet_len_aligned;
584	hv_vmbus_sg_buffer_list	buffer_list[3];
585
586	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
587	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
588	aligned_data = 0;
589
590	/* Setup the descriptor */
591	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
592	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
593			    /* in 8-bytes granularity */
594	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
595	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
596	desc.transaction_id = request_id;
597
598	buffer_list[0].data = &desc;
599	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
600
601	buffer_list[1].data = buffer;
602	buffer_list[1].length = buffer_len;
603
604	buffer_list[2].data = &aligned_data;
605	buffer_list[2].length = packet_len_aligned - packet_len;
606
607	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
608
609	/* TODO: We should determine if this is optional */
610	if (ret == 0
611		&& !hv_vmbus_get_ring_buffer_interrupt_mask(
612			&channel->outbound)) {
613		vmbus_channel_set_event(channel);
614	}
615
616	return (ret);
617}
618
619/**
620 * @brief Send a range of single-page buffer packets using
621 * a GPADL Direct packet type
622 */
623int
624hv_vmbus_channel_send_packet_pagebuffer(
625	hv_vmbus_channel*	channel,
626	hv_vmbus_page_buffer	page_buffers[],
627	uint32_t		page_count,
628	void*			buffer,
629	uint32_t		buffer_len,
630	uint64_t		request_id)
631{
632
633	int					ret = 0;
634	int					i = 0;
635	uint32_t				packet_len;
636	uint32_t				packetLen_aligned;
637	hv_vmbus_sg_buffer_list			buffer_list[3];
638	hv_vmbus_channel_packet_page_buffer	desc;
639	uint32_t				descSize;
640	uint64_t				alignedData = 0;
641
642	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
643		return (EINVAL);
644
645	/*
646	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
647	 *  is the largest size we support
648	 */
649	descSize = sizeof(hv_vmbus_channel_packet_page_buffer) -
650			((HV_MAX_PAGE_BUFFER_COUNT - page_count) *
651			sizeof(hv_vmbus_page_buffer));
652	packet_len = descSize + buffer_len;
653	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
654
655	/* Setup the descriptor */
656	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
657	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
658	desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */
659	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
660	desc.transaction_id = request_id;
661	desc.range_count = page_count;
662
663	for (i = 0; i < page_count; i++) {
664		desc.range[i].length = page_buffers[i].length;
665		desc.range[i].offset = page_buffers[i].offset;
666		desc.range[i].pfn = page_buffers[i].pfn;
667	}
668
669	buffer_list[0].data = &desc;
670	buffer_list[0].length = descSize;
671
672	buffer_list[1].data = buffer;
673	buffer_list[1].length = buffer_len;
674
675	buffer_list[2].data = &alignedData;
676	buffer_list[2].length = packetLen_aligned - packet_len;
677
678	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
679
680	/* TODO: We should determine if this is optional */
681	if (ret == 0 &&
682		!hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
683		vmbus_channel_set_event(channel);
684	}
685
686	return (ret);
687}
688
689/**
690 * @brief Send a multi-page buffer packet using a GPADL Direct packet type
691 */
692int
693hv_vmbus_channel_send_packet_multipagebuffer(
694	hv_vmbus_channel*		channel,
695	hv_vmbus_multipage_buffer*	multi_page_buffer,
696	void*				buffer,
697	uint32_t			buffer_len,
698	uint64_t			request_id)
699{
700
701	int			ret = 0;
702	uint32_t		desc_size;
703	uint32_t		packet_len;
704	uint32_t		packet_len_aligned;
705	uint32_t		pfn_count;
706	uint64_t		aligned_data = 0;
707	hv_vmbus_sg_buffer_list	buffer_list[3];
708	hv_vmbus_channel_packet_multipage_buffer desc;
709
710	pfn_count =
711	    HV_NUM_PAGES_SPANNED(
712		    multi_page_buffer->offset,
713		    multi_page_buffer->length);
714
715	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
716	    return (EINVAL);
717	/*
718	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
719	 * is the largest size we support
720	 */
721	desc_size =
722	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
723		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
724			sizeof(uint64_t));
725	packet_len = desc_size + buffer_len;
726	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
727
728	/*
729	 * Setup the descriptor
730	 */
731	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
732	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
733	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
734	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
735	desc.transaction_id = request_id;
736	desc.range_count = 1;
737
738	desc.range.length = multi_page_buffer->length;
739	desc.range.offset = multi_page_buffer->offset;
740
741	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
742		pfn_count * sizeof(uint64_t));
743
744	buffer_list[0].data = &desc;
745	buffer_list[0].length = desc_size;
746
747	buffer_list[1].data = buffer;
748	buffer_list[1].length = buffer_len;
749
750	buffer_list[2].data = &aligned_data;
751	buffer_list[2].length = packet_len_aligned - packet_len;
752
753	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
754
755	/* TODO: We should determine if this is optional */
756	if (ret == 0 &&
757	    !hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
758	    vmbus_channel_set_event(channel);
759	}
760
761	return (ret);
762}
763
764/**
765 * @brief Retrieve the user packet on the specified channel
766 */
767int
768hv_vmbus_channel_recv_packet(
769	hv_vmbus_channel*	channel,
770	void*			Buffer,
771	uint32_t		buffer_len,
772	uint32_t*		buffer_actual_len,
773	uint64_t*		request_id)
774{
775	int			ret;
776	uint32_t		user_len;
777	uint32_t		packet_len;
778	hv_vm_packet_descriptor	desc;
779
780	*buffer_actual_len = 0;
781	*request_id = 0;
782
783	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
784		sizeof(hv_vm_packet_descriptor));
785	if (ret != 0)
786		return (0);
787
788	packet_len = desc.length8 << 3;
789	user_len = packet_len - (desc.data_offset8 << 3);
790
791	*buffer_actual_len = user_len;
792
793	if (user_len > buffer_len)
794		return (EINVAL);
795
796	*request_id = desc.transaction_id;
797
798	/* Copy over the packet to the user buffer */
799	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
800		(desc.data_offset8 << 3));
801
802	return (0);
803}
804
805/**
806 * @brief Retrieve the raw packet on the specified channel
807 */
808int
809hv_vmbus_channel_recv_packet_raw(
810	hv_vmbus_channel*	channel,
811	void*			buffer,
812	uint32_t		buffer_len,
813	uint32_t*		buffer_actual_len,
814	uint64_t*		request_id)
815{
816	int		ret;
817	uint32_t	packetLen;
818	uint32_t	userLen;
819	hv_vm_packet_descriptor	desc;
820
821	*buffer_actual_len = 0;
822	*request_id = 0;
823
824	ret = hv_ring_buffer_peek(
825		&channel->inbound, &desc,
826		sizeof(hv_vm_packet_descriptor));
827
828	if (ret != 0)
829	    return (0);
830
831	packetLen = desc.length8 << 3;
832	userLen = packetLen - (desc.data_offset8 << 3);
833
834	*buffer_actual_len = packetLen;
835
836	if (packetLen > buffer_len)
837	    return (ENOBUFS);
838
839	*request_id = desc.transaction_id;
840
841	/* Copy over the entire packet to the user buffer */
842	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
843
844	return (0);
845}
846