vmbus_chan.c revision 296028
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
29256276Sdim#include <sys/cdefs.h>
30256276Sdim__FBSDID("$FreeBSD: head/sys/dev/hyperv/vmbus/hv_channel.c 296028 2016-02-25 09:27:50Z sephe $");
31256276Sdim
32250199Sgrehan#include <sys/param.h>
33296028Ssephe#include <sys/kernel.h>
34250199Sgrehan#include <sys/malloc.h>
35250199Sgrehan#include <sys/systm.h>
36250199Sgrehan#include <sys/mbuf.h>
37250199Sgrehan#include <sys/lock.h>
38250199Sgrehan#include <sys/mutex.h>
39250199Sgrehan#include <machine/bus.h>
40250199Sgrehan#include <vm/vm.h>
41250199Sgrehan#include <vm/vm_param.h>
42250199Sgrehan#include <vm/pmap.h>
43250199Sgrehan
44250199Sgrehan#include "hv_vmbus_priv.h"
45250199Sgrehan
46250199Sgrehanstatic int 	vmbus_channel_create_gpadl_header(
47250199Sgrehan			/* must be phys and virt contiguous*/
48250199Sgrehan			void*				contig_buffer,
49250199Sgrehan			/* page-size multiple */
50250199Sgrehan			uint32_t 			size,
51250199Sgrehan			hv_vmbus_channel_msg_info**	msg_info,
52250199Sgrehan			uint32_t*			message_count);
53250199Sgrehan
54250199Sgrehanstatic void 	vmbus_channel_set_event(hv_vmbus_channel* channel);
55294886Ssephestatic void	VmbusProcessChannelEvent(void* channel, int pending);
56250199Sgrehan
57250199Sgrehan/**
58250199Sgrehan *  @brief Trigger an event notification on the specified channel
59250199Sgrehan */
60250199Sgrehanstatic void
61250199Sgrehanvmbus_channel_set_event(hv_vmbus_channel *channel)
62250199Sgrehan{
63250199Sgrehan	hv_vmbus_monitor_page *monitor_page;
64250199Sgrehan
65250199Sgrehan	if (channel->offer_msg.monitor_allocated) {
66250199Sgrehan		/* Each uint32_t represents 32 channels */
67250199Sgrehan		synch_set_bit((channel->offer_msg.child_rel_id & 31),
68250199Sgrehan			((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
69250199Sgrehan				+ ((channel->offer_msg.child_rel_id >> 5))));
70250199Sgrehan
71250199Sgrehan		monitor_page = (hv_vmbus_monitor_page *)
72295309Ssephe			hv_vmbus_g_connection.monitor_page_2;
73250199Sgrehan
74250199Sgrehan		synch_set_bit(channel->monitor_bit,
75250199Sgrehan			(uint32_t *)&monitor_page->
76256276Sdim				trigger_group[channel->monitor_group].u.pending);
77250199Sgrehan	} else {
78282212Swhu		hv_vmbus_set_event(channel);
79250199Sgrehan	}
80250199Sgrehan
81250199Sgrehan}
82250199Sgrehan
83250199Sgrehan/**
84250199Sgrehan * @brief Open the specified channel
85250199Sgrehan */
86250199Sgrehanint
87250199Sgrehanhv_vmbus_channel_open(
88250199Sgrehan	hv_vmbus_channel*		new_channel,
89250199Sgrehan	uint32_t			send_ring_buffer_size,
90250199Sgrehan	uint32_t			recv_ring_buffer_size,
91250199Sgrehan	void*				user_data,
92250199Sgrehan	uint32_t			user_data_len,
93250199Sgrehan	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
94250199Sgrehan	void* 				context)
95250199Sgrehan{
96250199Sgrehan
97250199Sgrehan	int ret = 0;
98250199Sgrehan	void *in, *out;
99250199Sgrehan	hv_vmbus_channel_open_channel*	open_msg;
100250199Sgrehan	hv_vmbus_channel_msg_info* 	open_info;
101250199Sgrehan
102282212Swhu	mtx_lock(&new_channel->sc_lock);
103282212Swhu	if (new_channel->state == HV_CHANNEL_OPEN_STATE) {
104282212Swhu	    new_channel->state = HV_CHANNEL_OPENING_STATE;
105282212Swhu	} else {
106282212Swhu	    mtx_unlock(&new_channel->sc_lock);
107282212Swhu	    if(bootverbose)
108282212Swhu		printf("VMBUS: Trying to open channel <%p> which in "
109282212Swhu		    "%d state.\n", new_channel, new_channel->state);
110282212Swhu	    return (EINVAL);
111282212Swhu	}
112282212Swhu	mtx_unlock(&new_channel->sc_lock);
113282212Swhu
114250199Sgrehan	new_channel->on_channel_callback = pfn_on_channel_callback;
115250199Sgrehan	new_channel->channel_callback_context = context;
116250199Sgrehan
117294886Ssephe	new_channel->rxq = hv_vmbus_g_context.hv_event_queue[new_channel->target_cpu];
118294886Ssephe	TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, new_channel);
119294886Ssephe
120250199Sgrehan	/* Allocate the ring buffer */
121250199Sgrehan	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
122256350Sgrehan	    M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
123250199Sgrehan	KASSERT(out != NULL,
124250199Sgrehan	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
125250199Sgrehan	if (out == NULL)
126256350Sgrehan		return (ENOMEM);
127250199Sgrehan
128250199Sgrehan	in = ((uint8_t *) out + send_ring_buffer_size);
129250199Sgrehan
130250199Sgrehan	new_channel->ring_buffer_pages = out;
131256350Sgrehan	new_channel->ring_buffer_page_count = (send_ring_buffer_size +
132256350Sgrehan	    recv_ring_buffer_size) >> PAGE_SHIFT;
133256350Sgrehan	new_channel->ring_buffer_size = send_ring_buffer_size +
134256350Sgrehan	    recv_ring_buffer_size;
135250199Sgrehan
136250199Sgrehan	hv_vmbus_ring_buffer_init(
137250199Sgrehan		&new_channel->outbound,
138250199Sgrehan		out,
139250199Sgrehan		send_ring_buffer_size);
140250199Sgrehan
141250199Sgrehan	hv_vmbus_ring_buffer_init(
142250199Sgrehan		&new_channel->inbound,
143250199Sgrehan		in,
144250199Sgrehan		recv_ring_buffer_size);
145250199Sgrehan
146250199Sgrehan	/**
147250199Sgrehan	 * Establish the gpadl for the ring buffer
148250199Sgrehan	 */
149250199Sgrehan	new_channel->ring_buffer_gpadl_handle = 0;
150250199Sgrehan
151250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(new_channel,
152250199Sgrehan		new_channel->outbound.ring_buffer,
153250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
154250199Sgrehan		&new_channel->ring_buffer_gpadl_handle);
155250199Sgrehan
156250199Sgrehan	/**
157250199Sgrehan	 * Create and init the channel open message
158250199Sgrehan	 */
159250199Sgrehan	open_info = (hv_vmbus_channel_msg_info*) malloc(
160250199Sgrehan		sizeof(hv_vmbus_channel_msg_info) +
161250199Sgrehan			sizeof(hv_vmbus_channel_open_channel),
162250199Sgrehan		M_DEVBUF,
163250199Sgrehan		M_NOWAIT);
164250199Sgrehan	KASSERT(open_info != NULL,
165250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Open Channel message!"));
166250199Sgrehan
167250199Sgrehan	if (open_info == NULL)
168250199Sgrehan		return (ENOMEM);
169250199Sgrehan
170250199Sgrehan	sema_init(&open_info->wait_sema, 0, "Open Info Sema");
171250199Sgrehan
172250199Sgrehan	open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
173250199Sgrehan	open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
174250199Sgrehan	open_msg->open_id = new_channel->offer_msg.child_rel_id;
175250199Sgrehan	open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
176250199Sgrehan	open_msg->ring_buffer_gpadl_handle =
177250199Sgrehan		new_channel->ring_buffer_gpadl_handle;
178250199Sgrehan	open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
179250199Sgrehan		>> PAGE_SHIFT;
180282212Swhu	open_msg->target_vcpu = new_channel->target_vcpu;
181250199Sgrehan
182250199Sgrehan	if (user_data_len)
183250199Sgrehan		memcpy(open_msg->user_data, user_data, user_data_len);
184250199Sgrehan
185250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
186250199Sgrehan	TAILQ_INSERT_TAIL(
187250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
188250199Sgrehan		open_info,
189250199Sgrehan		msg_list_entry);
190250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
191250199Sgrehan
192250199Sgrehan	ret = hv_vmbus_post_message(
193250199Sgrehan		open_msg, sizeof(hv_vmbus_channel_open_channel));
194250199Sgrehan
195250199Sgrehan	if (ret != 0)
196250199Sgrehan	    goto cleanup;
197250199Sgrehan
198296028Ssephe	ret = sema_timedwait(&open_info->wait_sema, 5 * hz); /* KYS 5 seconds */
199250199Sgrehan
200282212Swhu	if (ret) {
201282212Swhu	    if(bootverbose)
202282212Swhu		printf("VMBUS: channel <%p> open timeout.\n", new_channel);
203250199Sgrehan	    goto cleanup;
204282212Swhu	}
205250199Sgrehan
206250199Sgrehan	if (open_info->response.open_result.status == 0) {
207282212Swhu	    new_channel->state = HV_CHANNEL_OPENED_STATE;
208250199Sgrehan	    if(bootverbose)
209250199Sgrehan		printf("VMBUS: channel <%p> open success.\n", new_channel);
210250199Sgrehan	} else {
211250199Sgrehan	    if(bootverbose)
212250199Sgrehan		printf("Error VMBUS: channel <%p> open failed - %d!\n",
213250199Sgrehan			new_channel, open_info->response.open_result.status);
214250199Sgrehan	}
215250199Sgrehan
216250199Sgrehan	cleanup:
217250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
218250199Sgrehan	TAILQ_REMOVE(
219250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
220250199Sgrehan		open_info,
221250199Sgrehan		msg_list_entry);
222250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
223250199Sgrehan	sema_destroy(&open_info->wait_sema);
224250199Sgrehan	free(open_info, M_DEVBUF);
225250199Sgrehan
226250199Sgrehan	return (ret);
227250199Sgrehan}
228250199Sgrehan
229250199Sgrehan/**
230250199Sgrehan * @brief Create a gpadl for the specified buffer
231250199Sgrehan */
232250199Sgrehanstatic int
233250199Sgrehanvmbus_channel_create_gpadl_header(
234250199Sgrehan	void*				contig_buffer,
235250199Sgrehan	uint32_t			size,	/* page-size multiple */
236250199Sgrehan	hv_vmbus_channel_msg_info**	msg_info,
237250199Sgrehan	uint32_t*			message_count)
238250199Sgrehan{
239250199Sgrehan	int				i;
240250199Sgrehan	int				page_count;
241250199Sgrehan	unsigned long long 		pfn;
242250199Sgrehan	uint32_t			msg_size;
243250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpa_header;
244250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
245250199Sgrehan	hv_vmbus_channel_msg_info*	msg_header;
246250199Sgrehan	hv_vmbus_channel_msg_info*	msg_body;
247250199Sgrehan
248250199Sgrehan	int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
249250199Sgrehan
250250199Sgrehan	page_count = size >> PAGE_SHIFT;
251250199Sgrehan	pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
252250199Sgrehan
253250199Sgrehan	/*do we need a gpadl body msg */
254250199Sgrehan	pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
255250199Sgrehan	    - sizeof(hv_vmbus_channel_gpadl_header)
256250199Sgrehan	    - sizeof(hv_gpa_range);
257250199Sgrehan	pfnCount = pfnSize / sizeof(uint64_t);
258250199Sgrehan
259250199Sgrehan	if (page_count > pfnCount) { /* if(we need a gpadl body)	*/
260250199Sgrehan	    /* fill in the header		*/
261250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info)
262250199Sgrehan		+ sizeof(hv_vmbus_channel_gpadl_header)
263250199Sgrehan		+ sizeof(hv_gpa_range)
264250199Sgrehan		+ pfnCount * sizeof(uint64_t);
265250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
266250199Sgrehan	    KASSERT(
267250199Sgrehan		msg_header != NULL,
268250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
269250199Sgrehan	    if (msg_header == NULL)
270250199Sgrehan		return (ENOMEM);
271250199Sgrehan
272250199Sgrehan	    TAILQ_INIT(&msg_header->sub_msg_list_anchor);
273250199Sgrehan	    msg_header->message_size = msg_size;
274250199Sgrehan
275250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
276250199Sgrehan	    gpa_header->range_count = 1;
277250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range)
278250199Sgrehan		+ page_count * sizeof(uint64_t);
279250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
280250199Sgrehan	    gpa_header->range[0].byte_count = size;
281250199Sgrehan	    for (i = 0; i < pfnCount; i++) {
282250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
283250199Sgrehan	    }
284250199Sgrehan	    *msg_info = msg_header;
285250199Sgrehan	    *message_count = 1;
286250199Sgrehan
287250199Sgrehan	    pfnSum = pfnCount;
288250199Sgrehan	    pfnLeft = page_count - pfnCount;
289250199Sgrehan
290250199Sgrehan	    /*
291250199Sgrehan	     *  figure out how many pfns we can fit
292250199Sgrehan	     */
293250199Sgrehan	    pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
294250199Sgrehan		- sizeof(hv_vmbus_channel_gpadl_body);
295250199Sgrehan	    pfnCount = pfnSize / sizeof(uint64_t);
296250199Sgrehan
297250199Sgrehan	    /*
298250199Sgrehan	     * fill in the body
299250199Sgrehan	     */
300250199Sgrehan	    while (pfnLeft) {
301250199Sgrehan		if (pfnLeft > pfnCount) {
302250199Sgrehan		    pfnCurr = pfnCount;
303250199Sgrehan		} else {
304250199Sgrehan		    pfnCurr = pfnLeft;
305250199Sgrehan		}
306250199Sgrehan
307250199Sgrehan		msg_size = sizeof(hv_vmbus_channel_msg_info) +
308250199Sgrehan		    sizeof(hv_vmbus_channel_gpadl_body) +
309250199Sgrehan		    pfnCurr * sizeof(uint64_t);
310250199Sgrehan		msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
311250199Sgrehan		KASSERT(
312250199Sgrehan		    msg_body != NULL,
313250199Sgrehan		    ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
314250199Sgrehan		if (msg_body == NULL)
315250199Sgrehan		    return (ENOMEM);
316250199Sgrehan
317250199Sgrehan		msg_body->message_size = msg_size;
318250199Sgrehan		(*message_count)++;
319250199Sgrehan		gpadl_body =
320250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) msg_body->msg;
321250199Sgrehan		/*
322250199Sgrehan		 * gpadl_body->gpadl = kbuffer;
323250199Sgrehan		 */
324250199Sgrehan		for (i = 0; i < pfnCurr; i++) {
325250199Sgrehan		    gpadl_body->pfn[i] = pfn + pfnSum + i;
326250199Sgrehan		}
327250199Sgrehan
328250199Sgrehan		TAILQ_INSERT_TAIL(
329250199Sgrehan		    &msg_header->sub_msg_list_anchor,
330250199Sgrehan		    msg_body,
331250199Sgrehan		    msg_list_entry);
332250199Sgrehan		pfnSum += pfnCurr;
333250199Sgrehan		pfnLeft -= pfnCurr;
334250199Sgrehan	    }
335250199Sgrehan	} else { /* else everything fits in a header */
336250199Sgrehan
337250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info) +
338250199Sgrehan		sizeof(hv_vmbus_channel_gpadl_header) +
339250199Sgrehan		sizeof(hv_gpa_range) +
340250199Sgrehan		page_count * sizeof(uint64_t);
341250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
342250199Sgrehan	    KASSERT(
343250199Sgrehan		msg_header != NULL,
344250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
345250199Sgrehan	    if (msg_header == NULL)
346250199Sgrehan		return (ENOMEM);
347250199Sgrehan
348250199Sgrehan	    msg_header->message_size = msg_size;
349250199Sgrehan
350250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
351250199Sgrehan	    gpa_header->range_count = 1;
352250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range) +
353250199Sgrehan		page_count * sizeof(uint64_t);
354250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
355250199Sgrehan	    gpa_header->range[0].byte_count = size;
356250199Sgrehan	    for (i = 0; i < page_count; i++) {
357250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
358250199Sgrehan	    }
359250199Sgrehan
360250199Sgrehan	    *msg_info = msg_header;
361250199Sgrehan	    *message_count = 1;
362250199Sgrehan	}
363250199Sgrehan
364250199Sgrehan	return (0);
365250199Sgrehan}
366250199Sgrehan
367250199Sgrehan/**
368250199Sgrehan * @brief Establish a GPADL for the specified buffer
369250199Sgrehan */
370250199Sgrehanint
371250199Sgrehanhv_vmbus_channel_establish_gpadl(
372250199Sgrehan	hv_vmbus_channel*	channel,
373250199Sgrehan	void*			contig_buffer,
374250199Sgrehan	uint32_t		size, /* page-size multiple */
375250199Sgrehan	uint32_t*		gpadl_handle)
376250199Sgrehan
377250199Sgrehan{
378250199Sgrehan	int ret = 0;
379250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpadl_msg;
380250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
381250199Sgrehan	hv_vmbus_channel_msg_info*	msg_info;
382250199Sgrehan	hv_vmbus_channel_msg_info*	sub_msg_info;
383250199Sgrehan	uint32_t			msg_count;
384250199Sgrehan	hv_vmbus_channel_msg_info*	curr;
385250199Sgrehan	uint32_t			next_gpadl_handle;
386250199Sgrehan
387250199Sgrehan	next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle;
388250199Sgrehan	atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1);
389250199Sgrehan
390250199Sgrehan	ret = vmbus_channel_create_gpadl_header(
391250199Sgrehan		contig_buffer, size, &msg_info, &msg_count);
392250199Sgrehan
393250199Sgrehan	if(ret != 0) { /* if(allocation failed) return immediately */
394250199Sgrehan	    /* reverse atomic_add_int above */
395250199Sgrehan	    atomic_subtract_int((int*)
396250199Sgrehan		    &hv_vmbus_g_connection.next_gpadl_handle, 1);
397250199Sgrehan	    return ret;
398250199Sgrehan	}
399250199Sgrehan
400250199Sgrehan	sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
401250199Sgrehan	gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
402250199Sgrehan	gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
403250199Sgrehan	gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
404250199Sgrehan	gpadl_msg->gpadl = next_gpadl_handle;
405250199Sgrehan
406250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
407250199Sgrehan	TAILQ_INSERT_TAIL(
408250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
409250199Sgrehan		msg_info,
410250199Sgrehan		msg_list_entry);
411250199Sgrehan
412250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
413250199Sgrehan
414250199Sgrehan	ret = hv_vmbus_post_message(
415250199Sgrehan		gpadl_msg,
416250199Sgrehan		msg_info->message_size -
417250199Sgrehan		    (uint32_t) sizeof(hv_vmbus_channel_msg_info));
418250199Sgrehan
419250199Sgrehan	if (ret != 0)
420250199Sgrehan	    goto cleanup;
421250199Sgrehan
422250199Sgrehan	if (msg_count > 1) {
423250199Sgrehan	    TAILQ_FOREACH(curr,
424250199Sgrehan		    &msg_info->sub_msg_list_anchor, msg_list_entry) {
425250199Sgrehan		sub_msg_info = curr;
426250199Sgrehan		gpadl_body =
427250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
428250199Sgrehan
429250199Sgrehan		gpadl_body->header.message_type =
430250199Sgrehan		    HV_CHANNEL_MESSAGE_GPADL_BODY;
431250199Sgrehan		gpadl_body->gpadl = next_gpadl_handle;
432250199Sgrehan
433250199Sgrehan		ret = hv_vmbus_post_message(
434250199Sgrehan			gpadl_body,
435250199Sgrehan			sub_msg_info->message_size
436250199Sgrehan			    - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
437250199Sgrehan		 /* if (the post message failed) give up and clean up */
438250199Sgrehan		if(ret != 0)
439250199Sgrehan		    goto cleanup;
440250199Sgrehan	    }
441250199Sgrehan	}
442250199Sgrehan
443296028Ssephe	ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds*/
444250199Sgrehan	if (ret != 0)
445250199Sgrehan	    goto cleanup;
446250199Sgrehan
447250199Sgrehan	*gpadl_handle = gpadl_msg->gpadl;
448250199Sgrehan
449250199Sgrehancleanup:
450250199Sgrehan
451250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
452250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
453250199Sgrehan		msg_info, msg_list_entry);
454250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
455250199Sgrehan
456250199Sgrehan	sema_destroy(&msg_info->wait_sema);
457250199Sgrehan	free(msg_info, M_DEVBUF);
458250199Sgrehan
459250199Sgrehan	return (ret);
460250199Sgrehan}
461250199Sgrehan
462250199Sgrehan/**
463250199Sgrehan * @brief Teardown the specified GPADL handle
464250199Sgrehan */
465250199Sgrehanint
466250199Sgrehanhv_vmbus_channel_teardown_gpdal(
467250199Sgrehan	hv_vmbus_channel*	channel,
468250199Sgrehan	uint32_t		gpadl_handle)
469250199Sgrehan{
470250199Sgrehan	int					ret = 0;
471250199Sgrehan	hv_vmbus_channel_gpadl_teardown*	msg;
472250199Sgrehan	hv_vmbus_channel_msg_info*		info;
473250199Sgrehan
474250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
475250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
476250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown),
477250199Sgrehan				M_DEVBUF, M_NOWAIT);
478250199Sgrehan	KASSERT(info != NULL,
479250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
480250199Sgrehan	if (info == NULL) {
481250199Sgrehan	    ret = ENOMEM;
482250199Sgrehan	    goto cleanup;
483250199Sgrehan	}
484250199Sgrehan
485250199Sgrehan	sema_init(&info->wait_sema, 0, "Open Info Sema");
486250199Sgrehan
487250199Sgrehan	msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
488250199Sgrehan
489250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
490250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
491250199Sgrehan	msg->gpadl = gpadl_handle;
492250199Sgrehan
493250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
494250199Sgrehan	TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
495250199Sgrehan			info, msg_list_entry);
496250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
497250199Sgrehan
498250199Sgrehan	ret = hv_vmbus_post_message(msg,
499250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown));
500250199Sgrehan	if (ret != 0)
501250199Sgrehan	    goto cleanup;
502250199Sgrehan
503296028Ssephe	ret = sema_timedwait(&info->wait_sema, 5 * hz); /* KYS 5 seconds */
504250199Sgrehan
505250199Sgrehancleanup:
506250199Sgrehan	/*
507250199Sgrehan	 * Received a torndown response
508250199Sgrehan	 */
509250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
510250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
511250199Sgrehan			info, msg_list_entry);
512250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
513250199Sgrehan	sema_destroy(&info->wait_sema);
514250199Sgrehan	free(info, M_DEVBUF);
515250199Sgrehan
516250199Sgrehan	return (ret);
517250199Sgrehan}
518250199Sgrehan
519282212Swhustatic void
520282212Swhuhv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
521250199Sgrehan{
522250199Sgrehan	int ret = 0;
523294886Ssephe	struct taskqueue *rxq = channel->rxq;
524250199Sgrehan	hv_vmbus_channel_close_channel* msg;
525250199Sgrehan	hv_vmbus_channel_msg_info* info;
526250199Sgrehan
527282212Swhu	channel->state = HV_CHANNEL_OPEN_STATE;
528282212Swhu	channel->sc_creation_callback = NULL;
529282212Swhu
530282212Swhu	/*
531294886Ssephe	 * set rxq to NULL to avoid more requests be scheduled
532294886Ssephe	 */
533294886Ssephe	channel->rxq = NULL;
534294886Ssephe	taskqueue_drain(rxq, &channel->channel_task);
535294886Ssephe	/*
536282212Swhu	 * Grab the lock to prevent race condition when a packet received
537282212Swhu	 * and unloading driver is in the process.
538282212Swhu	 */
539250199Sgrehan	mtx_lock(&channel->inbound_lock);
540250199Sgrehan	channel->on_channel_callback = NULL;
541250199Sgrehan	mtx_unlock(&channel->inbound_lock);
542250199Sgrehan
543250199Sgrehan	/**
544250199Sgrehan	 * Send a closing message
545250199Sgrehan	 */
546250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
547250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
548250199Sgrehan			sizeof(hv_vmbus_channel_close_channel),
549250199Sgrehan				M_DEVBUF, M_NOWAIT);
550250199Sgrehan	KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
551250199Sgrehan	if(info == NULL)
552250199Sgrehan	    return;
553250199Sgrehan
554250199Sgrehan	msg = (hv_vmbus_channel_close_channel*) info->msg;
555250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
556250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
557250199Sgrehan
558250199Sgrehan	ret = hv_vmbus_post_message(
559250199Sgrehan		msg, sizeof(hv_vmbus_channel_close_channel));
560250199Sgrehan
561250199Sgrehan	/* Tear down the gpadl for the channel's ring buffer */
562250199Sgrehan	if (channel->ring_buffer_gpadl_handle) {
563250199Sgrehan		hv_vmbus_channel_teardown_gpdal(channel,
564250199Sgrehan			channel->ring_buffer_gpadl_handle);
565250199Sgrehan	}
566250199Sgrehan
567250199Sgrehan	/* TODO: Send a msg to release the childRelId */
568250199Sgrehan
569250199Sgrehan	/* cleanup the ring buffers for this channel */
570250199Sgrehan	hv_ring_buffer_cleanup(&channel->outbound);
571250199Sgrehan	hv_ring_buffer_cleanup(&channel->inbound);
572250199Sgrehan
573256350Sgrehan	contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
574256350Sgrehan	    M_DEVBUF);
575250199Sgrehan
576250199Sgrehan	free(info, M_DEVBUF);
577282212Swhu}
578250199Sgrehan
579282212Swhu/**
580282212Swhu * @brief Close the specified channel
581282212Swhu */
582282212Swhuvoid
583282212Swhuhv_vmbus_channel_close(hv_vmbus_channel *channel)
584282212Swhu{
585282212Swhu	hv_vmbus_channel*	sub_channel;
586282212Swhu
587282212Swhu	if (channel->primary_channel != NULL) {
588282212Swhu		/*
589282212Swhu		 * We only close multi-channels when the primary is
590282212Swhu		 * closed.
591282212Swhu		 */
592282212Swhu		return;
593282212Swhu	}
594282212Swhu
595250199Sgrehan	/*
596282212Swhu	 * Close all multi-channels first.
597250199Sgrehan	 */
598282212Swhu	TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor,
599282212Swhu	    sc_list_entry) {
600282212Swhu		if (sub_channel->state != HV_CHANNEL_OPENED_STATE)
601282212Swhu			continue;
602282212Swhu		hv_vmbus_channel_close_internal(sub_channel);
603250199Sgrehan	}
604282212Swhu	/*
605282212Swhu	 * Then close the primary channel.
606282212Swhu	 */
607282212Swhu	hv_vmbus_channel_close_internal(channel);
608250199Sgrehan}
609250199Sgrehan
610250199Sgrehan/**
611250199Sgrehan * @brief Send the specified buffer on the given channel
612250199Sgrehan */
613250199Sgrehanint
614250199Sgrehanhv_vmbus_channel_send_packet(
615250199Sgrehan	hv_vmbus_channel*	channel,
616250199Sgrehan	void*			buffer,
617250199Sgrehan	uint32_t		buffer_len,
618250199Sgrehan	uint64_t		request_id,
619250199Sgrehan	hv_vmbus_packet_type	type,
620250199Sgrehan	uint32_t		flags)
621250199Sgrehan{
622250199Sgrehan	int			ret = 0;
623250199Sgrehan	hv_vm_packet_descriptor	desc;
624250199Sgrehan	uint32_t		packet_len;
625250199Sgrehan	uint64_t		aligned_data;
626250199Sgrehan	uint32_t		packet_len_aligned;
627282212Swhu	boolean_t		need_sig;
628250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
629250199Sgrehan
630250199Sgrehan	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
631250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
632250199Sgrehan	aligned_data = 0;
633250199Sgrehan
634250199Sgrehan	/* Setup the descriptor */
635250199Sgrehan	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
636250199Sgrehan	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
637250199Sgrehan			    /* in 8-bytes granularity */
638250199Sgrehan	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
639250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
640250199Sgrehan	desc.transaction_id = request_id;
641250199Sgrehan
642250199Sgrehan	buffer_list[0].data = &desc;
643250199Sgrehan	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
644250199Sgrehan
645250199Sgrehan	buffer_list[1].data = buffer;
646250199Sgrehan	buffer_list[1].length = buffer_len;
647250199Sgrehan
648250199Sgrehan	buffer_list[2].data = &aligned_data;
649250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
650250199Sgrehan
651282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
652282212Swhu	    &need_sig);
653250199Sgrehan
654250199Sgrehan	/* TODO: We should determine if this is optional */
655282212Swhu	if (ret == 0 && need_sig) {
656250199Sgrehan		vmbus_channel_set_event(channel);
657250199Sgrehan	}
658250199Sgrehan
659250199Sgrehan	return (ret);
660250199Sgrehan}
661250199Sgrehan
662250199Sgrehan/**
663250199Sgrehan * @brief Send a range of single-page buffer packets using
664250199Sgrehan * a GPADL Direct packet type
665250199Sgrehan */
666250199Sgrehanint
667250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer(
668250199Sgrehan	hv_vmbus_channel*	channel,
669250199Sgrehan	hv_vmbus_page_buffer	page_buffers[],
670250199Sgrehan	uint32_t		page_count,
671250199Sgrehan	void*			buffer,
672250199Sgrehan	uint32_t		buffer_len,
673250199Sgrehan	uint64_t		request_id)
674250199Sgrehan{
675250199Sgrehan
676250199Sgrehan	int					ret = 0;
677282212Swhu	boolean_t				need_sig;
678250199Sgrehan	uint32_t				packet_len;
679294705Ssephe	uint32_t				page_buflen;
680250199Sgrehan	uint32_t				packetLen_aligned;
681294705Ssephe	hv_vmbus_sg_buffer_list			buffer_list[4];
682250199Sgrehan	hv_vmbus_channel_packet_page_buffer	desc;
683250199Sgrehan	uint32_t				descSize;
684250199Sgrehan	uint64_t				alignedData = 0;
685250199Sgrehan
686250199Sgrehan	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
687250199Sgrehan		return (EINVAL);
688250199Sgrehan
689250199Sgrehan	/*
690250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
691250199Sgrehan	 *  is the largest size we support
692250199Sgrehan	 */
693294705Ssephe	descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range);
694294705Ssephe	page_buflen = sizeof(hv_vmbus_page_buffer) * page_count;
695294705Ssephe	packet_len = descSize + page_buflen + buffer_len;
696250199Sgrehan	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
697250199Sgrehan
698250199Sgrehan	/* Setup the descriptor */
699250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
700250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
701294705Ssephe	/* in 8-bytes granularity */
702294705Ssephe	desc.data_offset8 = (descSize + page_buflen) >> 3;
703250199Sgrehan	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
704250199Sgrehan	desc.transaction_id = request_id;
705250199Sgrehan	desc.range_count = page_count;
706250199Sgrehan
707250199Sgrehan	buffer_list[0].data = &desc;
708250199Sgrehan	buffer_list[0].length = descSize;
709250199Sgrehan
710294705Ssephe	buffer_list[1].data = page_buffers;
711294705Ssephe	buffer_list[1].length = page_buflen;
712250199Sgrehan
713294705Ssephe	buffer_list[2].data = buffer;
714294705Ssephe	buffer_list[2].length = buffer_len;
715250199Sgrehan
716294705Ssephe	buffer_list[3].data = &alignedData;
717294705Ssephe	buffer_list[3].length = packetLen_aligned - packet_len;
718294705Ssephe
719294705Ssephe	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 4,
720282212Swhu	    &need_sig);
721250199Sgrehan
722250199Sgrehan	/* TODO: We should determine if this is optional */
723282212Swhu	if (ret == 0 && need_sig) {
724250199Sgrehan		vmbus_channel_set_event(channel);
725250199Sgrehan	}
726250199Sgrehan
727250199Sgrehan	return (ret);
728250199Sgrehan}
729250199Sgrehan
730250199Sgrehan/**
731250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type
732250199Sgrehan */
733250199Sgrehanint
734250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer(
735250199Sgrehan	hv_vmbus_channel*		channel,
736250199Sgrehan	hv_vmbus_multipage_buffer*	multi_page_buffer,
737250199Sgrehan	void*				buffer,
738250199Sgrehan	uint32_t			buffer_len,
739250199Sgrehan	uint64_t			request_id)
740250199Sgrehan{
741250199Sgrehan
742250199Sgrehan	int			ret = 0;
743250199Sgrehan	uint32_t		desc_size;
744282212Swhu	boolean_t		need_sig;
745250199Sgrehan	uint32_t		packet_len;
746250199Sgrehan	uint32_t		packet_len_aligned;
747250199Sgrehan	uint32_t		pfn_count;
748250199Sgrehan	uint64_t		aligned_data = 0;
749250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
750250199Sgrehan	hv_vmbus_channel_packet_multipage_buffer desc;
751250199Sgrehan
752250199Sgrehan	pfn_count =
753250199Sgrehan	    HV_NUM_PAGES_SPANNED(
754250199Sgrehan		    multi_page_buffer->offset,
755250199Sgrehan		    multi_page_buffer->length);
756250199Sgrehan
757250199Sgrehan	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
758250199Sgrehan	    return (EINVAL);
759250199Sgrehan	/*
760250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
761250199Sgrehan	 * is the largest size we support
762250199Sgrehan	 */
763250199Sgrehan	desc_size =
764250199Sgrehan	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
765250199Sgrehan		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
766250199Sgrehan			sizeof(uint64_t));
767250199Sgrehan	packet_len = desc_size + buffer_len;
768250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
769250199Sgrehan
770250199Sgrehan	/*
771250199Sgrehan	 * Setup the descriptor
772250199Sgrehan	 */
773250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
774250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
775250199Sgrehan	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
776250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
777250199Sgrehan	desc.transaction_id = request_id;
778250199Sgrehan	desc.range_count = 1;
779250199Sgrehan
780250199Sgrehan	desc.range.length = multi_page_buffer->length;
781250199Sgrehan	desc.range.offset = multi_page_buffer->offset;
782250199Sgrehan
783250199Sgrehan	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
784250199Sgrehan		pfn_count * sizeof(uint64_t));
785250199Sgrehan
786250199Sgrehan	buffer_list[0].data = &desc;
787250199Sgrehan	buffer_list[0].length = desc_size;
788250199Sgrehan
789250199Sgrehan	buffer_list[1].data = buffer;
790250199Sgrehan	buffer_list[1].length = buffer_len;
791250199Sgrehan
792250199Sgrehan	buffer_list[2].data = &aligned_data;
793250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
794250199Sgrehan
795282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
796282212Swhu	    &need_sig);
797250199Sgrehan
798250199Sgrehan	/* TODO: We should determine if this is optional */
799282212Swhu	if (ret == 0 && need_sig) {
800250199Sgrehan	    vmbus_channel_set_event(channel);
801250199Sgrehan	}
802250199Sgrehan
803250199Sgrehan	return (ret);
804250199Sgrehan}
805250199Sgrehan
806250199Sgrehan/**
807250199Sgrehan * @brief Retrieve the user packet on the specified channel
808250199Sgrehan */
809250199Sgrehanint
810250199Sgrehanhv_vmbus_channel_recv_packet(
811250199Sgrehan	hv_vmbus_channel*	channel,
812250199Sgrehan	void*			Buffer,
813250199Sgrehan	uint32_t		buffer_len,
814250199Sgrehan	uint32_t*		buffer_actual_len,
815250199Sgrehan	uint64_t*		request_id)
816250199Sgrehan{
817250199Sgrehan	int			ret;
818250199Sgrehan	uint32_t		user_len;
819250199Sgrehan	uint32_t		packet_len;
820250199Sgrehan	hv_vm_packet_descriptor	desc;
821250199Sgrehan
822250199Sgrehan	*buffer_actual_len = 0;
823250199Sgrehan	*request_id = 0;
824250199Sgrehan
825250199Sgrehan	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
826250199Sgrehan		sizeof(hv_vm_packet_descriptor));
827250199Sgrehan	if (ret != 0)
828250199Sgrehan		return (0);
829250199Sgrehan
830250199Sgrehan	packet_len = desc.length8 << 3;
831250199Sgrehan	user_len = packet_len - (desc.data_offset8 << 3);
832250199Sgrehan
833250199Sgrehan	*buffer_actual_len = user_len;
834250199Sgrehan
835250199Sgrehan	if (user_len > buffer_len)
836250199Sgrehan		return (EINVAL);
837250199Sgrehan
838250199Sgrehan	*request_id = desc.transaction_id;
839250199Sgrehan
840250199Sgrehan	/* Copy over the packet to the user buffer */
841250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
842250199Sgrehan		(desc.data_offset8 << 3));
843250199Sgrehan
844250199Sgrehan	return (0);
845250199Sgrehan}
846250199Sgrehan
847250199Sgrehan/**
848250199Sgrehan * @brief Retrieve the raw packet on the specified channel
849250199Sgrehan */
850250199Sgrehanint
851250199Sgrehanhv_vmbus_channel_recv_packet_raw(
852250199Sgrehan	hv_vmbus_channel*	channel,
853250199Sgrehan	void*			buffer,
854250199Sgrehan	uint32_t		buffer_len,
855250199Sgrehan	uint32_t*		buffer_actual_len,
856250199Sgrehan	uint64_t*		request_id)
857250199Sgrehan{
858250199Sgrehan	int		ret;
859250199Sgrehan	uint32_t	packetLen;
860250199Sgrehan	uint32_t	userLen;
861250199Sgrehan	hv_vm_packet_descriptor	desc;
862250199Sgrehan
863250199Sgrehan	*buffer_actual_len = 0;
864250199Sgrehan	*request_id = 0;
865250199Sgrehan
866250199Sgrehan	ret = hv_ring_buffer_peek(
867250199Sgrehan		&channel->inbound, &desc,
868250199Sgrehan		sizeof(hv_vm_packet_descriptor));
869250199Sgrehan
870250199Sgrehan	if (ret != 0)
871250199Sgrehan	    return (0);
872250199Sgrehan
873250199Sgrehan	packetLen = desc.length8 << 3;
874250199Sgrehan	userLen = packetLen - (desc.data_offset8 << 3);
875250199Sgrehan
876250199Sgrehan	*buffer_actual_len = packetLen;
877250199Sgrehan
878250199Sgrehan	if (packetLen > buffer_len)
879250199Sgrehan	    return (ENOBUFS);
880250199Sgrehan
881250199Sgrehan	*request_id = desc.transaction_id;
882250199Sgrehan
883250199Sgrehan	/* Copy over the entire packet to the user buffer */
884250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
885250199Sgrehan
886250199Sgrehan	return (0);
887250199Sgrehan}
888294886Ssephe
889294886Ssephe
890294886Ssephe/**
891294886Ssephe * Process a channel event notification
892294886Ssephe */
893294886Ssephestatic void
894294886SsepheVmbusProcessChannelEvent(void* context, int pending)
895294886Ssephe{
896294886Ssephe	void* arg;
897294886Ssephe	uint32_t bytes_to_read;
898294886Ssephe	hv_vmbus_channel* channel = (hv_vmbus_channel*)context;
899294886Ssephe	boolean_t is_batched_reading;
900294886Ssephe
901294886Ssephe	/**
902294886Ssephe	 * Find the channel based on this relid and invokes
903294886Ssephe	 * the channel callback to process the event
904294886Ssephe	 */
905294886Ssephe
906294886Ssephe	if (channel == NULL) {
907294886Ssephe		return;
908294886Ssephe	}
909294886Ssephe	/**
910294886Ssephe	 * To deal with the race condition where we might
911294886Ssephe	 * receive a packet while the relevant driver is
912294886Ssephe	 * being unloaded, dispatch the callback while
913294886Ssephe	 * holding the channel lock. The unloading driver
914294886Ssephe	 * will acquire the same channel lock to set the
915294886Ssephe	 * callback to NULL. This closes the window.
916294886Ssephe	 */
917294886Ssephe
918294886Ssephe	/*
919294886Ssephe	 * Disable the lock due to newly added WITNESS check in r277723.
920294886Ssephe	 * Will seek other way to avoid race condition.
921294886Ssephe	 * -- whu
922294886Ssephe	 */
923294886Ssephe	// mtx_lock(&channel->inbound_lock);
924294886Ssephe	if (channel->on_channel_callback != NULL) {
925294886Ssephe		arg = channel->channel_callback_context;
926294886Ssephe		is_batched_reading = channel->batched_reading;
927294886Ssephe		/*
928294886Ssephe		 * Optimize host to guest signaling by ensuring:
929294886Ssephe		 * 1. While reading the channel, we disable interrupts from
930294886Ssephe		 *    host.
931294886Ssephe		 * 2. Ensure that we process all posted messages from the host
932294886Ssephe		 *    before returning from this callback.
933294886Ssephe		 * 3. Once we return, enable signaling from the host. Once this
934294886Ssephe		 *    state is set we check to see if additional packets are
935294886Ssephe		 *    available to read. In this case we repeat the process.
936294886Ssephe		 */
937294886Ssephe		do {
938294886Ssephe			if (is_batched_reading)
939294886Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
940294886Ssephe
941294886Ssephe			channel->on_channel_callback(arg);
942294886Ssephe
943294886Ssephe			if (is_batched_reading)
944294886Ssephe				bytes_to_read =
945294886Ssephe				    hv_ring_buffer_read_end(&channel->inbound);
946294886Ssephe			else
947294886Ssephe				bytes_to_read = 0;
948294886Ssephe		} while (is_batched_reading && (bytes_to_read != 0));
949294886Ssephe	}
950294886Ssephe	// mtx_unlock(&channel->inbound_lock);
951294886Ssephe}
952