vmbus_chan.c revision 298693
1250199Sgrehan/*-
2298446Ssephe * Copyright (c) 2009-2012,2016 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 298693 2016-04-27 05:45:14Z 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>
39296181Ssephe#include <sys/sysctl.h>
40250199Sgrehan#include <machine/bus.h>
41250199Sgrehan#include <vm/vm.h>
42250199Sgrehan#include <vm/vm_param.h>
43250199Sgrehan#include <vm/pmap.h>
44250199Sgrehan
45250199Sgrehan#include "hv_vmbus_priv.h"
46250199Sgrehan
47250199Sgrehanstatic int 	vmbus_channel_create_gpadl_header(
48250199Sgrehan			/* must be phys and virt contiguous*/
49250199Sgrehan			void*				contig_buffer,
50250199Sgrehan			/* page-size multiple */
51250199Sgrehan			uint32_t 			size,
52250199Sgrehan			hv_vmbus_channel_msg_info**	msg_info,
53250199Sgrehan			uint32_t*			message_count);
54250199Sgrehan
55250199Sgrehanstatic void 	vmbus_channel_set_event(hv_vmbus_channel* channel);
56294886Ssephestatic void	VmbusProcessChannelEvent(void* channel, int pending);
57250199Sgrehan
58250199Sgrehan/**
59250199Sgrehan *  @brief Trigger an event notification on the specified channel
60250199Sgrehan */
61250199Sgrehanstatic void
62250199Sgrehanvmbus_channel_set_event(hv_vmbus_channel *channel)
63250199Sgrehan{
64250199Sgrehan	hv_vmbus_monitor_page *monitor_page;
65250199Sgrehan
66250199Sgrehan	if (channel->offer_msg.monitor_allocated) {
67250199Sgrehan		/* Each uint32_t represents 32 channels */
68250199Sgrehan		synch_set_bit((channel->offer_msg.child_rel_id & 31),
69250199Sgrehan			((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
70250199Sgrehan				+ ((channel->offer_msg.child_rel_id >> 5))));
71250199Sgrehan
72250199Sgrehan		monitor_page = (hv_vmbus_monitor_page *)
73295309Ssephe			hv_vmbus_g_connection.monitor_page_2;
74250199Sgrehan
75250199Sgrehan		synch_set_bit(channel->monitor_bit,
76250199Sgrehan			(uint32_t *)&monitor_page->
77256276Sdim				trigger_group[channel->monitor_group].u.pending);
78250199Sgrehan	} else {
79282212Swhu		hv_vmbus_set_event(channel);
80250199Sgrehan	}
81250199Sgrehan
82250199Sgrehan}
83250199Sgrehan
84296289Ssephestatic int
85296289Ssephevmbus_channel_sysctl_monalloc(SYSCTL_HANDLER_ARGS)
86296289Ssephe{
87296289Ssephe	struct hv_vmbus_channel *chan = arg1;
88296289Ssephe	int alloc = 0;
89296289Ssephe
90296289Ssephe	if (chan->offer_msg.monitor_allocated)
91296289Ssephe		alloc = 1;
92296289Ssephe	return sysctl_handle_int(oidp, &alloc, 0, req);
93296289Ssephe}
94296289Ssephe
95296181Ssephestatic void
96296290Ssephevmbus_channel_sysctl_create(hv_vmbus_channel* channel)
97296181Ssephe{
98296181Ssephe	device_t dev;
99296181Ssephe	struct sysctl_oid *devch_sysctl;
100296181Ssephe	struct sysctl_oid *devch_id_sysctl, *devch_sub_sysctl;
101296181Ssephe	struct sysctl_oid *devch_id_in_sysctl, *devch_id_out_sysctl;
102296181Ssephe	struct sysctl_ctx_list *ctx;
103296181Ssephe	uint32_t ch_id;
104296181Ssephe	uint16_t sub_ch_id;
105296181Ssephe	char name[16];
106296181Ssephe
107296181Ssephe	hv_vmbus_channel* primary_ch = channel->primary_channel;
108296181Ssephe
109296181Ssephe	if (primary_ch == NULL) {
110296181Ssephe		dev = channel->device->device;
111296181Ssephe		ch_id = channel->offer_msg.child_rel_id;
112296181Ssephe	} else {
113296181Ssephe		dev = primary_ch->device->device;
114296181Ssephe		ch_id = primary_ch->offer_msg.child_rel_id;
115296181Ssephe		sub_ch_id = channel->offer_msg.offer.sub_channel_index;
116296181Ssephe	}
117296181Ssephe	ctx = device_get_sysctl_ctx(dev);
118296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel tree */
119296181Ssephe	devch_sysctl = SYSCTL_ADD_NODE(ctx,
120296181Ssephe		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
121298693Ssephe		    OID_AUTO, "channel", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
122296181Ssephe	/* This creates dev.DEVNAME.DEVUNIT.channel.CHANID tree */
123296181Ssephe	snprintf(name, sizeof(name), "%d", ch_id);
124296181Ssephe	devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
125296181Ssephe	    	    SYSCTL_CHILDREN(devch_sysctl),
126298693Ssephe	    	    OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
127296181Ssephe
128296181Ssephe	if (primary_ch != NULL) {
129296181Ssephe		devch_sub_sysctl = SYSCTL_ADD_NODE(ctx,
130296181Ssephe			SYSCTL_CHILDREN(devch_id_sysctl),
131298693Ssephe			OID_AUTO, "sub", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
132296181Ssephe		snprintf(name, sizeof(name), "%d", sub_ch_id);
133296181Ssephe		devch_id_sysctl = SYSCTL_ADD_NODE(ctx,
134296181Ssephe			SYSCTL_CHILDREN(devch_sub_sysctl),
135298693Ssephe			OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
136296188Ssephe
137296188Ssephe		SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl),
138296188Ssephe		    OID_AUTO, "chanid", CTLFLAG_RD,
139296188Ssephe		    &channel->offer_msg.child_rel_id, 0, "channel id");
140296181Ssephe	}
141296188Ssephe	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
142296188Ssephe	    "cpu", CTLFLAG_RD, &channel->target_cpu, 0, "owner CPU id");
143296289Ssephe	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(devch_id_sysctl), OID_AUTO,
144298693Ssephe	    "monitor_allocated", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
145298693Ssephe	    channel, 0, vmbus_channel_sysctl_monalloc, "I",
146296289Ssephe	    "is monitor allocated to this channel");
147296188Ssephe
148296181Ssephe	devch_id_in_sysctl = SYSCTL_ADD_NODE(ctx,
149296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
150296181Ssephe                    OID_AUTO,
151296181Ssephe		    "in",
152298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
153296181Ssephe	devch_id_out_sysctl = SYSCTL_ADD_NODE(ctx,
154296181Ssephe                    SYSCTL_CHILDREN(devch_id_sysctl),
155296181Ssephe                    OID_AUTO,
156296181Ssephe		    "out",
157298693Ssephe		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
158296181Ssephe	hv_ring_buffer_stat(ctx,
159296181Ssephe		SYSCTL_CHILDREN(devch_id_in_sysctl),
160296181Ssephe		&(channel->inbound),
161296181Ssephe		"inbound ring buffer stats");
162296181Ssephe	hv_ring_buffer_stat(ctx,
163296181Ssephe		SYSCTL_CHILDREN(devch_id_out_sysctl),
164296181Ssephe		&(channel->outbound),
165296181Ssephe		"outbound ring buffer stats");
166296181Ssephe}
167296290Ssephe
168250199Sgrehan/**
169250199Sgrehan * @brief Open the specified channel
170250199Sgrehan */
171250199Sgrehanint
172250199Sgrehanhv_vmbus_channel_open(
173250199Sgrehan	hv_vmbus_channel*		new_channel,
174250199Sgrehan	uint32_t			send_ring_buffer_size,
175250199Sgrehan	uint32_t			recv_ring_buffer_size,
176250199Sgrehan	void*				user_data,
177250199Sgrehan	uint32_t			user_data_len,
178250199Sgrehan	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
179250199Sgrehan	void* 				context)
180250199Sgrehan{
181250199Sgrehan
182250199Sgrehan	int ret = 0;
183250199Sgrehan	void *in, *out;
184250199Sgrehan	hv_vmbus_channel_open_channel*	open_msg;
185250199Sgrehan	hv_vmbus_channel_msg_info* 	open_info;
186250199Sgrehan
187282212Swhu	mtx_lock(&new_channel->sc_lock);
188282212Swhu	if (new_channel->state == HV_CHANNEL_OPEN_STATE) {
189282212Swhu	    new_channel->state = HV_CHANNEL_OPENING_STATE;
190282212Swhu	} else {
191282212Swhu	    mtx_unlock(&new_channel->sc_lock);
192282212Swhu	    if(bootverbose)
193282212Swhu		printf("VMBUS: Trying to open channel <%p> which in "
194282212Swhu		    "%d state.\n", new_channel, new_channel->state);
195282212Swhu	    return (EINVAL);
196282212Swhu	}
197282212Swhu	mtx_unlock(&new_channel->sc_lock);
198282212Swhu
199250199Sgrehan	new_channel->on_channel_callback = pfn_on_channel_callback;
200250199Sgrehan	new_channel->channel_callback_context = context;
201250199Sgrehan
202294886Ssephe	new_channel->rxq = hv_vmbus_g_context.hv_event_queue[new_channel->target_cpu];
203294886Ssephe	TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, new_channel);
204294886Ssephe
205250199Sgrehan	/* Allocate the ring buffer */
206250199Sgrehan	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
207256350Sgrehan	    M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
208250199Sgrehan	KASSERT(out != NULL,
209250199Sgrehan	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
210250199Sgrehan	if (out == NULL)
211256350Sgrehan		return (ENOMEM);
212250199Sgrehan
213250199Sgrehan	in = ((uint8_t *) out + send_ring_buffer_size);
214250199Sgrehan
215250199Sgrehan	new_channel->ring_buffer_pages = out;
216256350Sgrehan	new_channel->ring_buffer_page_count = (send_ring_buffer_size +
217256350Sgrehan	    recv_ring_buffer_size) >> PAGE_SHIFT;
218256350Sgrehan	new_channel->ring_buffer_size = send_ring_buffer_size +
219256350Sgrehan	    recv_ring_buffer_size;
220250199Sgrehan
221250199Sgrehan	hv_vmbus_ring_buffer_init(
222250199Sgrehan		&new_channel->outbound,
223250199Sgrehan		out,
224250199Sgrehan		send_ring_buffer_size);
225250199Sgrehan
226250199Sgrehan	hv_vmbus_ring_buffer_init(
227250199Sgrehan		&new_channel->inbound,
228250199Sgrehan		in,
229250199Sgrehan		recv_ring_buffer_size);
230250199Sgrehan
231296290Ssephe	/* Create sysctl tree for this channel */
232296290Ssephe	vmbus_channel_sysctl_create(new_channel);
233296181Ssephe
234250199Sgrehan	/**
235250199Sgrehan	 * Establish the gpadl for the ring buffer
236250199Sgrehan	 */
237250199Sgrehan	new_channel->ring_buffer_gpadl_handle = 0;
238250199Sgrehan
239250199Sgrehan	ret = hv_vmbus_channel_establish_gpadl(new_channel,
240250199Sgrehan		new_channel->outbound.ring_buffer,
241250199Sgrehan		send_ring_buffer_size + recv_ring_buffer_size,
242250199Sgrehan		&new_channel->ring_buffer_gpadl_handle);
243250199Sgrehan
244250199Sgrehan	/**
245250199Sgrehan	 * Create and init the channel open message
246250199Sgrehan	 */
247250199Sgrehan	open_info = (hv_vmbus_channel_msg_info*) malloc(
248250199Sgrehan		sizeof(hv_vmbus_channel_msg_info) +
249250199Sgrehan			sizeof(hv_vmbus_channel_open_channel),
250250199Sgrehan		M_DEVBUF,
251250199Sgrehan		M_NOWAIT);
252250199Sgrehan	KASSERT(open_info != NULL,
253250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Open Channel message!"));
254250199Sgrehan
255250199Sgrehan	if (open_info == NULL)
256250199Sgrehan		return (ENOMEM);
257250199Sgrehan
258250199Sgrehan	sema_init(&open_info->wait_sema, 0, "Open Info Sema");
259250199Sgrehan
260250199Sgrehan	open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
261250199Sgrehan	open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
262250199Sgrehan	open_msg->open_id = new_channel->offer_msg.child_rel_id;
263250199Sgrehan	open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
264250199Sgrehan	open_msg->ring_buffer_gpadl_handle =
265250199Sgrehan		new_channel->ring_buffer_gpadl_handle;
266250199Sgrehan	open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
267250199Sgrehan		>> PAGE_SHIFT;
268282212Swhu	open_msg->target_vcpu = new_channel->target_vcpu;
269250199Sgrehan
270250199Sgrehan	if (user_data_len)
271250199Sgrehan		memcpy(open_msg->user_data, user_data, user_data_len);
272250199Sgrehan
273297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
274250199Sgrehan	TAILQ_INSERT_TAIL(
275250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
276250199Sgrehan		open_info,
277250199Sgrehan		msg_list_entry);
278297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
279250199Sgrehan
280250199Sgrehan	ret = hv_vmbus_post_message(
281250199Sgrehan		open_msg, sizeof(hv_vmbus_channel_open_channel));
282250199Sgrehan
283250199Sgrehan	if (ret != 0)
284250199Sgrehan	    goto cleanup;
285250199Sgrehan
286296028Ssephe	ret = sema_timedwait(&open_info->wait_sema, 5 * hz); /* KYS 5 seconds */
287250199Sgrehan
288282212Swhu	if (ret) {
289282212Swhu	    if(bootverbose)
290282212Swhu		printf("VMBUS: channel <%p> open timeout.\n", new_channel);
291250199Sgrehan	    goto cleanup;
292282212Swhu	}
293250199Sgrehan
294250199Sgrehan	if (open_info->response.open_result.status == 0) {
295282212Swhu	    new_channel->state = HV_CHANNEL_OPENED_STATE;
296250199Sgrehan	    if(bootverbose)
297250199Sgrehan		printf("VMBUS: channel <%p> open success.\n", new_channel);
298250199Sgrehan	} else {
299250199Sgrehan	    if(bootverbose)
300250199Sgrehan		printf("Error VMBUS: channel <%p> open failed - %d!\n",
301250199Sgrehan			new_channel, open_info->response.open_result.status);
302250199Sgrehan	}
303250199Sgrehan
304250199Sgrehan	cleanup:
305297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
306250199Sgrehan	TAILQ_REMOVE(
307250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
308250199Sgrehan		open_info,
309250199Sgrehan		msg_list_entry);
310297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
311250199Sgrehan	sema_destroy(&open_info->wait_sema);
312250199Sgrehan	free(open_info, M_DEVBUF);
313250199Sgrehan
314250199Sgrehan	return (ret);
315250199Sgrehan}
316250199Sgrehan
317250199Sgrehan/**
318250199Sgrehan * @brief Create a gpadl for the specified buffer
319250199Sgrehan */
320250199Sgrehanstatic int
321250199Sgrehanvmbus_channel_create_gpadl_header(
322250199Sgrehan	void*				contig_buffer,
323250199Sgrehan	uint32_t			size,	/* page-size multiple */
324250199Sgrehan	hv_vmbus_channel_msg_info**	msg_info,
325250199Sgrehan	uint32_t*			message_count)
326250199Sgrehan{
327250199Sgrehan	int				i;
328250199Sgrehan	int				page_count;
329250199Sgrehan	unsigned long long 		pfn;
330250199Sgrehan	uint32_t			msg_size;
331250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpa_header;
332250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
333250199Sgrehan	hv_vmbus_channel_msg_info*	msg_header;
334250199Sgrehan	hv_vmbus_channel_msg_info*	msg_body;
335250199Sgrehan
336250199Sgrehan	int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
337250199Sgrehan
338250199Sgrehan	page_count = size >> PAGE_SHIFT;
339250199Sgrehan	pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
340250199Sgrehan
341250199Sgrehan	/*do we need a gpadl body msg */
342250199Sgrehan	pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
343250199Sgrehan	    - sizeof(hv_vmbus_channel_gpadl_header)
344250199Sgrehan	    - sizeof(hv_gpa_range);
345250199Sgrehan	pfnCount = pfnSize / sizeof(uint64_t);
346250199Sgrehan
347250199Sgrehan	if (page_count > pfnCount) { /* if(we need a gpadl body)	*/
348250199Sgrehan	    /* fill in the header		*/
349250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info)
350250199Sgrehan		+ sizeof(hv_vmbus_channel_gpadl_header)
351250199Sgrehan		+ sizeof(hv_gpa_range)
352250199Sgrehan		+ pfnCount * sizeof(uint64_t);
353250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
354250199Sgrehan	    KASSERT(
355250199Sgrehan		msg_header != NULL,
356250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
357250199Sgrehan	    if (msg_header == NULL)
358250199Sgrehan		return (ENOMEM);
359250199Sgrehan
360250199Sgrehan	    TAILQ_INIT(&msg_header->sub_msg_list_anchor);
361250199Sgrehan	    msg_header->message_size = msg_size;
362250199Sgrehan
363250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
364250199Sgrehan	    gpa_header->range_count = 1;
365250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range)
366250199Sgrehan		+ page_count * sizeof(uint64_t);
367250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
368250199Sgrehan	    gpa_header->range[0].byte_count = size;
369250199Sgrehan	    for (i = 0; i < pfnCount; i++) {
370250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
371250199Sgrehan	    }
372250199Sgrehan	    *msg_info = msg_header;
373250199Sgrehan	    *message_count = 1;
374250199Sgrehan
375250199Sgrehan	    pfnSum = pfnCount;
376250199Sgrehan	    pfnLeft = page_count - pfnCount;
377250199Sgrehan
378250199Sgrehan	    /*
379250199Sgrehan	     *  figure out how many pfns we can fit
380250199Sgrehan	     */
381250199Sgrehan	    pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
382250199Sgrehan		- sizeof(hv_vmbus_channel_gpadl_body);
383250199Sgrehan	    pfnCount = pfnSize / sizeof(uint64_t);
384250199Sgrehan
385250199Sgrehan	    /*
386250199Sgrehan	     * fill in the body
387250199Sgrehan	     */
388250199Sgrehan	    while (pfnLeft) {
389250199Sgrehan		if (pfnLeft > pfnCount) {
390250199Sgrehan		    pfnCurr = pfnCount;
391250199Sgrehan		} else {
392250199Sgrehan		    pfnCurr = pfnLeft;
393250199Sgrehan		}
394250199Sgrehan
395250199Sgrehan		msg_size = sizeof(hv_vmbus_channel_msg_info) +
396250199Sgrehan		    sizeof(hv_vmbus_channel_gpadl_body) +
397250199Sgrehan		    pfnCurr * sizeof(uint64_t);
398250199Sgrehan		msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
399250199Sgrehan		KASSERT(
400250199Sgrehan		    msg_body != NULL,
401250199Sgrehan		    ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
402250199Sgrehan		if (msg_body == NULL)
403250199Sgrehan		    return (ENOMEM);
404250199Sgrehan
405250199Sgrehan		msg_body->message_size = msg_size;
406250199Sgrehan		(*message_count)++;
407250199Sgrehan		gpadl_body =
408250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) msg_body->msg;
409250199Sgrehan		/*
410250199Sgrehan		 * gpadl_body->gpadl = kbuffer;
411250199Sgrehan		 */
412250199Sgrehan		for (i = 0; i < pfnCurr; i++) {
413250199Sgrehan		    gpadl_body->pfn[i] = pfn + pfnSum + i;
414250199Sgrehan		}
415250199Sgrehan
416250199Sgrehan		TAILQ_INSERT_TAIL(
417250199Sgrehan		    &msg_header->sub_msg_list_anchor,
418250199Sgrehan		    msg_body,
419250199Sgrehan		    msg_list_entry);
420250199Sgrehan		pfnSum += pfnCurr;
421250199Sgrehan		pfnLeft -= pfnCurr;
422250199Sgrehan	    }
423250199Sgrehan	} else { /* else everything fits in a header */
424250199Sgrehan
425250199Sgrehan	    msg_size = sizeof(hv_vmbus_channel_msg_info) +
426250199Sgrehan		sizeof(hv_vmbus_channel_gpadl_header) +
427250199Sgrehan		sizeof(hv_gpa_range) +
428250199Sgrehan		page_count * sizeof(uint64_t);
429250199Sgrehan	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
430250199Sgrehan	    KASSERT(
431250199Sgrehan		msg_header != NULL,
432250199Sgrehan		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
433250199Sgrehan	    if (msg_header == NULL)
434250199Sgrehan		return (ENOMEM);
435250199Sgrehan
436250199Sgrehan	    msg_header->message_size = msg_size;
437250199Sgrehan
438250199Sgrehan	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
439250199Sgrehan	    gpa_header->range_count = 1;
440250199Sgrehan	    gpa_header->range_buf_len = sizeof(hv_gpa_range) +
441250199Sgrehan		page_count * sizeof(uint64_t);
442250199Sgrehan	    gpa_header->range[0].byte_offset = 0;
443250199Sgrehan	    gpa_header->range[0].byte_count = size;
444250199Sgrehan	    for (i = 0; i < page_count; i++) {
445250199Sgrehan		gpa_header->range[0].pfn_array[i] = pfn + i;
446250199Sgrehan	    }
447250199Sgrehan
448250199Sgrehan	    *msg_info = msg_header;
449250199Sgrehan	    *message_count = 1;
450250199Sgrehan	}
451250199Sgrehan
452250199Sgrehan	return (0);
453250199Sgrehan}
454250199Sgrehan
455250199Sgrehan/**
456250199Sgrehan * @brief Establish a GPADL for the specified buffer
457250199Sgrehan */
458250199Sgrehanint
459250199Sgrehanhv_vmbus_channel_establish_gpadl(
460250199Sgrehan	hv_vmbus_channel*	channel,
461250199Sgrehan	void*			contig_buffer,
462250199Sgrehan	uint32_t		size, /* page-size multiple */
463250199Sgrehan	uint32_t*		gpadl_handle)
464250199Sgrehan
465250199Sgrehan{
466250199Sgrehan	int ret = 0;
467250199Sgrehan	hv_vmbus_channel_gpadl_header*	gpadl_msg;
468250199Sgrehan	hv_vmbus_channel_gpadl_body*	gpadl_body;
469250199Sgrehan	hv_vmbus_channel_msg_info*	msg_info;
470250199Sgrehan	hv_vmbus_channel_msg_info*	sub_msg_info;
471250199Sgrehan	uint32_t			msg_count;
472250199Sgrehan	hv_vmbus_channel_msg_info*	curr;
473250199Sgrehan	uint32_t			next_gpadl_handle;
474250199Sgrehan
475296076Ssephe	next_gpadl_handle = atomic_fetchadd_int(
476296076Ssephe	    &hv_vmbus_g_connection.next_gpadl_handle, 1);
477250199Sgrehan
478250199Sgrehan	ret = vmbus_channel_create_gpadl_header(
479250199Sgrehan		contig_buffer, size, &msg_info, &msg_count);
480250199Sgrehan
481296076Ssephe	if(ret != 0) {
482296076Ssephe		/*
483296076Ssephe		 * XXX
484296076Ssephe		 * We can _not_ even revert the above incremental,
485296076Ssephe		 * if multiple GPADL establishments are running
486296076Ssephe		 * parallelly, decrement the global next_gpadl_handle
487296076Ssephe		 * is calling for _big_ trouble.  A better solution
488296076Ssephe		 * is to have a 0-based GPADL id bitmap ...
489296076Ssephe		 */
490296076Ssephe		return ret;
491250199Sgrehan	}
492250199Sgrehan
493250199Sgrehan	sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
494250199Sgrehan	gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
495250199Sgrehan	gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
496250199Sgrehan	gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
497250199Sgrehan	gpadl_msg->gpadl = next_gpadl_handle;
498250199Sgrehan
499297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
500250199Sgrehan	TAILQ_INSERT_TAIL(
501250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
502250199Sgrehan		msg_info,
503250199Sgrehan		msg_list_entry);
504250199Sgrehan
505297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
506250199Sgrehan
507250199Sgrehan	ret = hv_vmbus_post_message(
508250199Sgrehan		gpadl_msg,
509250199Sgrehan		msg_info->message_size -
510250199Sgrehan		    (uint32_t) sizeof(hv_vmbus_channel_msg_info));
511250199Sgrehan
512250199Sgrehan	if (ret != 0)
513250199Sgrehan	    goto cleanup;
514250199Sgrehan
515250199Sgrehan	if (msg_count > 1) {
516250199Sgrehan	    TAILQ_FOREACH(curr,
517250199Sgrehan		    &msg_info->sub_msg_list_anchor, msg_list_entry) {
518250199Sgrehan		sub_msg_info = curr;
519250199Sgrehan		gpadl_body =
520250199Sgrehan		    (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
521250199Sgrehan
522250199Sgrehan		gpadl_body->header.message_type =
523250199Sgrehan		    HV_CHANNEL_MESSAGE_GPADL_BODY;
524250199Sgrehan		gpadl_body->gpadl = next_gpadl_handle;
525250199Sgrehan
526250199Sgrehan		ret = hv_vmbus_post_message(
527250199Sgrehan			gpadl_body,
528250199Sgrehan			sub_msg_info->message_size
529250199Sgrehan			    - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
530250199Sgrehan		 /* if (the post message failed) give up and clean up */
531250199Sgrehan		if(ret != 0)
532250199Sgrehan		    goto cleanup;
533250199Sgrehan	    }
534250199Sgrehan	}
535250199Sgrehan
536296028Ssephe	ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds*/
537250199Sgrehan	if (ret != 0)
538250199Sgrehan	    goto cleanup;
539250199Sgrehan
540250199Sgrehan	*gpadl_handle = gpadl_msg->gpadl;
541250199Sgrehan
542250199Sgrehancleanup:
543250199Sgrehan
544297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
545250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
546250199Sgrehan		msg_info, msg_list_entry);
547297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
548250199Sgrehan
549250199Sgrehan	sema_destroy(&msg_info->wait_sema);
550250199Sgrehan	free(msg_info, M_DEVBUF);
551250199Sgrehan
552250199Sgrehan	return (ret);
553250199Sgrehan}
554250199Sgrehan
555250199Sgrehan/**
556250199Sgrehan * @brief Teardown the specified GPADL handle
557250199Sgrehan */
558250199Sgrehanint
559250199Sgrehanhv_vmbus_channel_teardown_gpdal(
560250199Sgrehan	hv_vmbus_channel*	channel,
561250199Sgrehan	uint32_t		gpadl_handle)
562250199Sgrehan{
563250199Sgrehan	int					ret = 0;
564250199Sgrehan	hv_vmbus_channel_gpadl_teardown*	msg;
565250199Sgrehan	hv_vmbus_channel_msg_info*		info;
566250199Sgrehan
567250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
568250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
569250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown),
570250199Sgrehan				M_DEVBUF, M_NOWAIT);
571250199Sgrehan	KASSERT(info != NULL,
572250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
573250199Sgrehan	if (info == NULL) {
574250199Sgrehan	    ret = ENOMEM;
575250199Sgrehan	    goto cleanup;
576250199Sgrehan	}
577250199Sgrehan
578250199Sgrehan	sema_init(&info->wait_sema, 0, "Open Info Sema");
579250199Sgrehan
580250199Sgrehan	msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
581250199Sgrehan
582250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
583250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
584250199Sgrehan	msg->gpadl = gpadl_handle;
585250199Sgrehan
586297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
587250199Sgrehan	TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
588250199Sgrehan			info, msg_list_entry);
589297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
590250199Sgrehan
591250199Sgrehan	ret = hv_vmbus_post_message(msg,
592250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_teardown));
593250199Sgrehan	if (ret != 0)
594250199Sgrehan	    goto cleanup;
595250199Sgrehan
596296028Ssephe	ret = sema_timedwait(&info->wait_sema, 5 * hz); /* KYS 5 seconds */
597250199Sgrehan
598250199Sgrehancleanup:
599250199Sgrehan	/*
600250199Sgrehan	 * Received a torndown response
601250199Sgrehan	 */
602297635Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
603250199Sgrehan	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
604250199Sgrehan			info, msg_list_entry);
605297635Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
606250199Sgrehan	sema_destroy(&info->wait_sema);
607250199Sgrehan	free(info, M_DEVBUF);
608250199Sgrehan
609250199Sgrehan	return (ret);
610250199Sgrehan}
611250199Sgrehan
612282212Swhustatic void
613282212Swhuhv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
614250199Sgrehan{
615250199Sgrehan	int ret = 0;
616294886Ssephe	struct taskqueue *rxq = channel->rxq;
617250199Sgrehan	hv_vmbus_channel_close_channel* msg;
618250199Sgrehan	hv_vmbus_channel_msg_info* info;
619250199Sgrehan
620282212Swhu	channel->state = HV_CHANNEL_OPEN_STATE;
621282212Swhu
622282212Swhu	/*
623294886Ssephe	 * set rxq to NULL to avoid more requests be scheduled
624294886Ssephe	 */
625294886Ssephe	channel->rxq = NULL;
626294886Ssephe	taskqueue_drain(rxq, &channel->channel_task);
627250199Sgrehan	channel->on_channel_callback = NULL;
628250199Sgrehan
629250199Sgrehan	/**
630250199Sgrehan	 * Send a closing message
631250199Sgrehan	 */
632250199Sgrehan	info = (hv_vmbus_channel_msg_info *)
633250199Sgrehan		malloc(	sizeof(hv_vmbus_channel_msg_info) +
634250199Sgrehan			sizeof(hv_vmbus_channel_close_channel),
635250199Sgrehan				M_DEVBUF, M_NOWAIT);
636250199Sgrehan	KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
637250199Sgrehan	if(info == NULL)
638250199Sgrehan	    return;
639250199Sgrehan
640250199Sgrehan	msg = (hv_vmbus_channel_close_channel*) info->msg;
641250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
642250199Sgrehan	msg->child_rel_id = channel->offer_msg.child_rel_id;
643250199Sgrehan
644250199Sgrehan	ret = hv_vmbus_post_message(
645250199Sgrehan		msg, sizeof(hv_vmbus_channel_close_channel));
646250199Sgrehan
647250199Sgrehan	/* Tear down the gpadl for the channel's ring buffer */
648250199Sgrehan	if (channel->ring_buffer_gpadl_handle) {
649250199Sgrehan		hv_vmbus_channel_teardown_gpdal(channel,
650250199Sgrehan			channel->ring_buffer_gpadl_handle);
651250199Sgrehan	}
652250199Sgrehan
653250199Sgrehan	/* TODO: Send a msg to release the childRelId */
654250199Sgrehan
655250199Sgrehan	/* cleanup the ring buffers for this channel */
656250199Sgrehan	hv_ring_buffer_cleanup(&channel->outbound);
657250199Sgrehan	hv_ring_buffer_cleanup(&channel->inbound);
658250199Sgrehan
659256350Sgrehan	contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
660256350Sgrehan	    M_DEVBUF);
661250199Sgrehan
662250199Sgrehan	free(info, M_DEVBUF);
663282212Swhu}
664250199Sgrehan
665282212Swhu/**
666282212Swhu * @brief Close the specified channel
667282212Swhu */
668282212Swhuvoid
669282212Swhuhv_vmbus_channel_close(hv_vmbus_channel *channel)
670282212Swhu{
671282212Swhu	hv_vmbus_channel*	sub_channel;
672282212Swhu
673282212Swhu	if (channel->primary_channel != NULL) {
674282212Swhu		/*
675282212Swhu		 * We only close multi-channels when the primary is
676282212Swhu		 * closed.
677282212Swhu		 */
678282212Swhu		return;
679282212Swhu	}
680282212Swhu
681250199Sgrehan	/*
682282212Swhu	 * Close all multi-channels first.
683250199Sgrehan	 */
684282212Swhu	TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor,
685282212Swhu	    sc_list_entry) {
686282212Swhu		if (sub_channel->state != HV_CHANNEL_OPENED_STATE)
687282212Swhu			continue;
688282212Swhu		hv_vmbus_channel_close_internal(sub_channel);
689250199Sgrehan	}
690282212Swhu	/*
691282212Swhu	 * Then close the primary channel.
692282212Swhu	 */
693282212Swhu	hv_vmbus_channel_close_internal(channel);
694250199Sgrehan}
695250199Sgrehan
696250199Sgrehan/**
697250199Sgrehan * @brief Send the specified buffer on the given channel
698250199Sgrehan */
699250199Sgrehanint
700250199Sgrehanhv_vmbus_channel_send_packet(
701250199Sgrehan	hv_vmbus_channel*	channel,
702250199Sgrehan	void*			buffer,
703250199Sgrehan	uint32_t		buffer_len,
704250199Sgrehan	uint64_t		request_id,
705250199Sgrehan	hv_vmbus_packet_type	type,
706250199Sgrehan	uint32_t		flags)
707250199Sgrehan{
708250199Sgrehan	int			ret = 0;
709250199Sgrehan	hv_vm_packet_descriptor	desc;
710250199Sgrehan	uint32_t		packet_len;
711250199Sgrehan	uint64_t		aligned_data;
712250199Sgrehan	uint32_t		packet_len_aligned;
713282212Swhu	boolean_t		need_sig;
714250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
715250199Sgrehan
716250199Sgrehan	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
717250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
718250199Sgrehan	aligned_data = 0;
719250199Sgrehan
720250199Sgrehan	/* Setup the descriptor */
721250199Sgrehan	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
722250199Sgrehan	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
723250199Sgrehan			    /* in 8-bytes granularity */
724250199Sgrehan	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
725250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
726250199Sgrehan	desc.transaction_id = request_id;
727250199Sgrehan
728250199Sgrehan	buffer_list[0].data = &desc;
729250199Sgrehan	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
730250199Sgrehan
731250199Sgrehan	buffer_list[1].data = buffer;
732250199Sgrehan	buffer_list[1].length = buffer_len;
733250199Sgrehan
734250199Sgrehan	buffer_list[2].data = &aligned_data;
735250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
736250199Sgrehan
737282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
738282212Swhu	    &need_sig);
739250199Sgrehan
740250199Sgrehan	/* TODO: We should determine if this is optional */
741282212Swhu	if (ret == 0 && need_sig) {
742250199Sgrehan		vmbus_channel_set_event(channel);
743250199Sgrehan	}
744250199Sgrehan
745250199Sgrehan	return (ret);
746250199Sgrehan}
747250199Sgrehan
748250199Sgrehan/**
749250199Sgrehan * @brief Send a range of single-page buffer packets using
750250199Sgrehan * a GPADL Direct packet type
751250199Sgrehan */
752250199Sgrehanint
753250199Sgrehanhv_vmbus_channel_send_packet_pagebuffer(
754250199Sgrehan	hv_vmbus_channel*	channel,
755250199Sgrehan	hv_vmbus_page_buffer	page_buffers[],
756250199Sgrehan	uint32_t		page_count,
757250199Sgrehan	void*			buffer,
758250199Sgrehan	uint32_t		buffer_len,
759250199Sgrehan	uint64_t		request_id)
760250199Sgrehan{
761250199Sgrehan
762250199Sgrehan	int					ret = 0;
763282212Swhu	boolean_t				need_sig;
764250199Sgrehan	uint32_t				packet_len;
765294705Ssephe	uint32_t				page_buflen;
766250199Sgrehan	uint32_t				packetLen_aligned;
767294705Ssephe	hv_vmbus_sg_buffer_list			buffer_list[4];
768250199Sgrehan	hv_vmbus_channel_packet_page_buffer	desc;
769250199Sgrehan	uint32_t				descSize;
770250199Sgrehan	uint64_t				alignedData = 0;
771250199Sgrehan
772250199Sgrehan	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
773250199Sgrehan		return (EINVAL);
774250199Sgrehan
775250199Sgrehan	/*
776250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
777250199Sgrehan	 *  is the largest size we support
778250199Sgrehan	 */
779294705Ssephe	descSize = __offsetof(hv_vmbus_channel_packet_page_buffer, range);
780294705Ssephe	page_buflen = sizeof(hv_vmbus_page_buffer) * page_count;
781294705Ssephe	packet_len = descSize + page_buflen + buffer_len;
782250199Sgrehan	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
783250199Sgrehan
784250199Sgrehan	/* Setup the descriptor */
785250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
786250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
787294705Ssephe	/* in 8-bytes granularity */
788294705Ssephe	desc.data_offset8 = (descSize + page_buflen) >> 3;
789250199Sgrehan	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
790250199Sgrehan	desc.transaction_id = request_id;
791250199Sgrehan	desc.range_count = page_count;
792250199Sgrehan
793250199Sgrehan	buffer_list[0].data = &desc;
794250199Sgrehan	buffer_list[0].length = descSize;
795250199Sgrehan
796294705Ssephe	buffer_list[1].data = page_buffers;
797294705Ssephe	buffer_list[1].length = page_buflen;
798250199Sgrehan
799294705Ssephe	buffer_list[2].data = buffer;
800294705Ssephe	buffer_list[2].length = buffer_len;
801250199Sgrehan
802294705Ssephe	buffer_list[3].data = &alignedData;
803294705Ssephe	buffer_list[3].length = packetLen_aligned - packet_len;
804294705Ssephe
805294705Ssephe	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 4,
806282212Swhu	    &need_sig);
807250199Sgrehan
808250199Sgrehan	/* TODO: We should determine if this is optional */
809282212Swhu	if (ret == 0 && need_sig) {
810250199Sgrehan		vmbus_channel_set_event(channel);
811250199Sgrehan	}
812250199Sgrehan
813250199Sgrehan	return (ret);
814250199Sgrehan}
815250199Sgrehan
816250199Sgrehan/**
817250199Sgrehan * @brief Send a multi-page buffer packet using a GPADL Direct packet type
818250199Sgrehan */
819250199Sgrehanint
820250199Sgrehanhv_vmbus_channel_send_packet_multipagebuffer(
821250199Sgrehan	hv_vmbus_channel*		channel,
822250199Sgrehan	hv_vmbus_multipage_buffer*	multi_page_buffer,
823250199Sgrehan	void*				buffer,
824250199Sgrehan	uint32_t			buffer_len,
825250199Sgrehan	uint64_t			request_id)
826250199Sgrehan{
827250199Sgrehan
828250199Sgrehan	int			ret = 0;
829250199Sgrehan	uint32_t		desc_size;
830282212Swhu	boolean_t		need_sig;
831250199Sgrehan	uint32_t		packet_len;
832250199Sgrehan	uint32_t		packet_len_aligned;
833250199Sgrehan	uint32_t		pfn_count;
834250199Sgrehan	uint64_t		aligned_data = 0;
835250199Sgrehan	hv_vmbus_sg_buffer_list	buffer_list[3];
836250199Sgrehan	hv_vmbus_channel_packet_multipage_buffer desc;
837250199Sgrehan
838250199Sgrehan	pfn_count =
839250199Sgrehan	    HV_NUM_PAGES_SPANNED(
840250199Sgrehan		    multi_page_buffer->offset,
841250199Sgrehan		    multi_page_buffer->length);
842250199Sgrehan
843250199Sgrehan	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
844250199Sgrehan	    return (EINVAL);
845250199Sgrehan	/*
846250199Sgrehan	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
847250199Sgrehan	 * is the largest size we support
848250199Sgrehan	 */
849250199Sgrehan	desc_size =
850250199Sgrehan	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
851250199Sgrehan		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
852250199Sgrehan			sizeof(uint64_t));
853250199Sgrehan	packet_len = desc_size + buffer_len;
854250199Sgrehan	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
855250199Sgrehan
856250199Sgrehan	/*
857250199Sgrehan	 * Setup the descriptor
858250199Sgrehan	 */
859250199Sgrehan	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
860250199Sgrehan	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
861250199Sgrehan	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
862250199Sgrehan	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
863250199Sgrehan	desc.transaction_id = request_id;
864250199Sgrehan	desc.range_count = 1;
865250199Sgrehan
866250199Sgrehan	desc.range.length = multi_page_buffer->length;
867250199Sgrehan	desc.range.offset = multi_page_buffer->offset;
868250199Sgrehan
869250199Sgrehan	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
870250199Sgrehan		pfn_count * sizeof(uint64_t));
871250199Sgrehan
872250199Sgrehan	buffer_list[0].data = &desc;
873250199Sgrehan	buffer_list[0].length = desc_size;
874250199Sgrehan
875250199Sgrehan	buffer_list[1].data = buffer;
876250199Sgrehan	buffer_list[1].length = buffer_len;
877250199Sgrehan
878250199Sgrehan	buffer_list[2].data = &aligned_data;
879250199Sgrehan	buffer_list[2].length = packet_len_aligned - packet_len;
880250199Sgrehan
881282212Swhu	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
882282212Swhu	    &need_sig);
883250199Sgrehan
884250199Sgrehan	/* TODO: We should determine if this is optional */
885282212Swhu	if (ret == 0 && need_sig) {
886250199Sgrehan	    vmbus_channel_set_event(channel);
887250199Sgrehan	}
888250199Sgrehan
889250199Sgrehan	return (ret);
890250199Sgrehan}
891250199Sgrehan
892250199Sgrehan/**
893250199Sgrehan * @brief Retrieve the user packet on the specified channel
894250199Sgrehan */
895250199Sgrehanint
896250199Sgrehanhv_vmbus_channel_recv_packet(
897250199Sgrehan	hv_vmbus_channel*	channel,
898250199Sgrehan	void*			Buffer,
899250199Sgrehan	uint32_t		buffer_len,
900250199Sgrehan	uint32_t*		buffer_actual_len,
901250199Sgrehan	uint64_t*		request_id)
902250199Sgrehan{
903250199Sgrehan	int			ret;
904250199Sgrehan	uint32_t		user_len;
905250199Sgrehan	uint32_t		packet_len;
906250199Sgrehan	hv_vm_packet_descriptor	desc;
907250199Sgrehan
908250199Sgrehan	*buffer_actual_len = 0;
909250199Sgrehan	*request_id = 0;
910250199Sgrehan
911250199Sgrehan	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
912250199Sgrehan		sizeof(hv_vm_packet_descriptor));
913250199Sgrehan	if (ret != 0)
914250199Sgrehan		return (0);
915250199Sgrehan
916250199Sgrehan	packet_len = desc.length8 << 3;
917250199Sgrehan	user_len = packet_len - (desc.data_offset8 << 3);
918250199Sgrehan
919250199Sgrehan	*buffer_actual_len = user_len;
920250199Sgrehan
921250199Sgrehan	if (user_len > buffer_len)
922250199Sgrehan		return (EINVAL);
923250199Sgrehan
924250199Sgrehan	*request_id = desc.transaction_id;
925250199Sgrehan
926250199Sgrehan	/* Copy over the packet to the user buffer */
927250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
928250199Sgrehan		(desc.data_offset8 << 3));
929250199Sgrehan
930250199Sgrehan	return (0);
931250199Sgrehan}
932250199Sgrehan
933250199Sgrehan/**
934250199Sgrehan * @brief Retrieve the raw packet on the specified channel
935250199Sgrehan */
936250199Sgrehanint
937250199Sgrehanhv_vmbus_channel_recv_packet_raw(
938250199Sgrehan	hv_vmbus_channel*	channel,
939250199Sgrehan	void*			buffer,
940250199Sgrehan	uint32_t		buffer_len,
941250199Sgrehan	uint32_t*		buffer_actual_len,
942250199Sgrehan	uint64_t*		request_id)
943250199Sgrehan{
944250199Sgrehan	int		ret;
945250199Sgrehan	uint32_t	packetLen;
946250199Sgrehan	hv_vm_packet_descriptor	desc;
947250199Sgrehan
948250199Sgrehan	*buffer_actual_len = 0;
949250199Sgrehan	*request_id = 0;
950250199Sgrehan
951250199Sgrehan	ret = hv_ring_buffer_peek(
952250199Sgrehan		&channel->inbound, &desc,
953250199Sgrehan		sizeof(hv_vm_packet_descriptor));
954250199Sgrehan
955250199Sgrehan	if (ret != 0)
956250199Sgrehan	    return (0);
957250199Sgrehan
958250199Sgrehan	packetLen = desc.length8 << 3;
959250199Sgrehan	*buffer_actual_len = packetLen;
960250199Sgrehan
961250199Sgrehan	if (packetLen > buffer_len)
962250199Sgrehan	    return (ENOBUFS);
963250199Sgrehan
964250199Sgrehan	*request_id = desc.transaction_id;
965250199Sgrehan
966250199Sgrehan	/* Copy over the entire packet to the user buffer */
967250199Sgrehan	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
968250199Sgrehan
969250199Sgrehan	return (0);
970250199Sgrehan}
971294886Ssephe
972294886Ssephe
973294886Ssephe/**
974294886Ssephe * Process a channel event notification
975294886Ssephe */
976294886Ssephestatic void
977294886SsepheVmbusProcessChannelEvent(void* context, int pending)
978294886Ssephe{
979294886Ssephe	void* arg;
980294886Ssephe	uint32_t bytes_to_read;
981294886Ssephe	hv_vmbus_channel* channel = (hv_vmbus_channel*)context;
982294886Ssephe	boolean_t is_batched_reading;
983294886Ssephe
984294886Ssephe	/**
985294886Ssephe	 * Find the channel based on this relid and invokes
986294886Ssephe	 * the channel callback to process the event
987294886Ssephe	 */
988294886Ssephe
989294886Ssephe	if (channel == NULL) {
990294886Ssephe		return;
991294886Ssephe	}
992294886Ssephe	/**
993294886Ssephe	 * To deal with the race condition where we might
994294886Ssephe	 * receive a packet while the relevant driver is
995294886Ssephe	 * being unloaded, dispatch the callback while
996294886Ssephe	 * holding the channel lock. The unloading driver
997294886Ssephe	 * will acquire the same channel lock to set the
998294886Ssephe	 * callback to NULL. This closes the window.
999294886Ssephe	 */
1000294886Ssephe
1001294886Ssephe	if (channel->on_channel_callback != NULL) {
1002294886Ssephe		arg = channel->channel_callback_context;
1003294886Ssephe		is_batched_reading = channel->batched_reading;
1004294886Ssephe		/*
1005294886Ssephe		 * Optimize host to guest signaling by ensuring:
1006294886Ssephe		 * 1. While reading the channel, we disable interrupts from
1007294886Ssephe		 *    host.
1008294886Ssephe		 * 2. Ensure that we process all posted messages from the host
1009294886Ssephe		 *    before returning from this callback.
1010294886Ssephe		 * 3. Once we return, enable signaling from the host. Once this
1011294886Ssephe		 *    state is set we check to see if additional packets are
1012294886Ssephe		 *    available to read. In this case we repeat the process.
1013294886Ssephe		 */
1014294886Ssephe		do {
1015294886Ssephe			if (is_batched_reading)
1016294886Ssephe				hv_ring_buffer_read_begin(&channel->inbound);
1017294886Ssephe
1018294886Ssephe			channel->on_channel_callback(arg);
1019294886Ssephe
1020294886Ssephe			if (is_batched_reading)
1021294886Ssephe				bytes_to_read =
1022294886Ssephe				    hv_ring_buffer_read_end(&channel->inbound);
1023294886Ssephe			else
1024294886Ssephe				bytes_to_read = 0;
1025294886Ssephe		} while (is_batched_reading && (bytes_to_read != 0));
1026294886Ssephe	}
1027294886Ssephe}
1028