vmbus_chan.c revision 250199
1/*-
2 * Copyright (c) 2009-2012 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/malloc.h>
31#include <sys/systm.h>
32#include <sys/mbuf.h>
33#include <sys/lock.h>
34#include <sys/mutex.h>
35#include <machine/bus.h>
36#include <vm/vm.h>
37#include <vm/vm_param.h>
38#include <vm/pmap.h>
39
40#include "hv_vmbus_priv.h"
41
42static int 	vmbus_channel_create_gpadl_header(
43			/* must be phys and virt contiguous*/
44			void*				contig_buffer,
45			/* page-size multiple */
46			uint32_t 			size,
47			hv_vmbus_channel_msg_info**	msg_info,
48			uint32_t*			message_count);
49
50static void 	vmbus_channel_set_event(hv_vmbus_channel* channel);
51
52/**
53 *  @brief Trigger an event notification on the specified channel
54 */
55static void
56vmbus_channel_set_event(hv_vmbus_channel *channel)
57{
58	hv_vmbus_monitor_page *monitor_page;
59
60	if (channel->offer_msg.monitor_allocated) {
61		/* Each uint32_t represents 32 channels */
62		synch_set_bit((channel->offer_msg.child_rel_id & 31),
63			((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
64				+ ((channel->offer_msg.child_rel_id >> 5))));
65
66		monitor_page = (hv_vmbus_monitor_page *)
67			hv_vmbus_g_connection.monitor_pages;
68
69		monitor_page++; /* Get the child to parent monitor page */
70
71		synch_set_bit(channel->monitor_bit,
72			(uint32_t *)&monitor_page->
73				trigger_group[channel->monitor_group].pending);
74	} else {
75		hv_vmbus_set_event(channel->offer_msg.child_rel_id);
76	}
77
78}
79
80/**
81 * @brief Open the specified channel
82 */
83int
84hv_vmbus_channel_open(
85	hv_vmbus_channel*		new_channel,
86	uint32_t			send_ring_buffer_size,
87	uint32_t			recv_ring_buffer_size,
88	void*				user_data,
89	uint32_t			user_data_len,
90	hv_vmbus_pfn_channel_callback	pfn_on_channel_callback,
91	void* 				context)
92{
93
94	int ret = 0;
95	void *in, *out;
96	hv_vmbus_channel_open_channel*	open_msg;
97	hv_vmbus_channel_msg_info* 	open_info;
98
99	new_channel->on_channel_callback = pfn_on_channel_callback;
100	new_channel->channel_callback_context = context;
101
102	/* Allocate the ring buffer */
103	out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
104		M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
105	KASSERT(out != NULL,
106	    ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
107	if (out == NULL)
108	    return (ENOMEM);
109
110	in = ((uint8_t *) out + send_ring_buffer_size);
111
112	new_channel->ring_buffer_pages = out;
113	new_channel->ring_buffer_page_count = (send_ring_buffer_size
114		+ recv_ring_buffer_size) >> PAGE_SHIFT;
115
116	hv_vmbus_ring_buffer_init(
117		&new_channel->outbound,
118		out,
119		send_ring_buffer_size);
120
121	hv_vmbus_ring_buffer_init(
122		&new_channel->inbound,
123		in,
124		recv_ring_buffer_size);
125
126	/**
127	 * Establish the gpadl for the ring buffer
128	 */
129	new_channel->ring_buffer_gpadl_handle = 0;
130
131	ret = hv_vmbus_channel_establish_gpadl(new_channel,
132		new_channel->outbound.ring_buffer,
133		send_ring_buffer_size + recv_ring_buffer_size,
134		&new_channel->ring_buffer_gpadl_handle);
135
136	/**
137	 * Create and init the channel open message
138	 */
139	open_info = (hv_vmbus_channel_msg_info*) malloc(
140		sizeof(hv_vmbus_channel_msg_info) +
141			sizeof(hv_vmbus_channel_open_channel),
142		M_DEVBUF,
143		M_NOWAIT);
144	KASSERT(open_info != NULL,
145	    ("Error VMBUS: malloc failed to allocate Open Channel message!"));
146
147	if (open_info == NULL)
148		return (ENOMEM);
149
150	sema_init(&open_info->wait_sema, 0, "Open Info Sema");
151
152	open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
153	open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
154	open_msg->open_id = new_channel->offer_msg.child_rel_id;
155	open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
156	open_msg->ring_buffer_gpadl_handle =
157		new_channel->ring_buffer_gpadl_handle;
158	open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
159		>> PAGE_SHIFT;
160	open_msg->server_context_area_gpadl_handle = 0;
161
162	if (user_data_len)
163		memcpy(open_msg->user_data, user_data, user_data_len);
164
165	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
166	TAILQ_INSERT_TAIL(
167		&hv_vmbus_g_connection.channel_msg_anchor,
168		open_info,
169		msg_list_entry);
170	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
171
172	ret = hv_vmbus_post_message(
173		open_msg, sizeof(hv_vmbus_channel_open_channel));
174
175	if (ret != 0)
176	    goto cleanup;
177
178	ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */
179
180	if (ret)
181	    goto cleanup;
182
183	if (open_info->response.open_result.status == 0) {
184	    if(bootverbose)
185		printf("VMBUS: channel <%p> open success.\n", new_channel);
186	} else {
187	    if(bootverbose)
188		printf("Error VMBUS: channel <%p> open failed - %d!\n",
189			new_channel, open_info->response.open_result.status);
190	}
191
192	cleanup:
193	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
194	TAILQ_REMOVE(
195		&hv_vmbus_g_connection.channel_msg_anchor,
196		open_info,
197		msg_list_entry);
198	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
199	sema_destroy(&open_info->wait_sema);
200	free(open_info, M_DEVBUF);
201
202	return (ret);
203}
204
205/**
206 * @brief Create a gpadl for the specified buffer
207 */
208static int
209vmbus_channel_create_gpadl_header(
210	void*				contig_buffer,
211	uint32_t			size,	/* page-size multiple */
212	hv_vmbus_channel_msg_info**	msg_info,
213	uint32_t*			message_count)
214{
215	int				i;
216	int				page_count;
217	unsigned long long 		pfn;
218	uint32_t			msg_size;
219	hv_vmbus_channel_gpadl_header*	gpa_header;
220	hv_vmbus_channel_gpadl_body*	gpadl_body;
221	hv_vmbus_channel_msg_info*	msg_header;
222	hv_vmbus_channel_msg_info*	msg_body;
223
224	int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
225
226	page_count = size >> PAGE_SHIFT;
227	pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
228
229	/*do we need a gpadl body msg */
230	pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
231	    - sizeof(hv_vmbus_channel_gpadl_header)
232	    - sizeof(hv_gpa_range);
233	pfnCount = pfnSize / sizeof(uint64_t);
234
235	if (page_count > pfnCount) { /* if(we need a gpadl body)	*/
236	    /* fill in the header		*/
237	    msg_size = sizeof(hv_vmbus_channel_msg_info)
238		+ sizeof(hv_vmbus_channel_gpadl_header)
239		+ sizeof(hv_gpa_range)
240		+ pfnCount * sizeof(uint64_t);
241	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
242	    KASSERT(
243		msg_header != NULL,
244		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
245	    if (msg_header == NULL)
246		return (ENOMEM);
247
248	    TAILQ_INIT(&msg_header->sub_msg_list_anchor);
249	    msg_header->message_size = msg_size;
250
251	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
252	    gpa_header->range_count = 1;
253	    gpa_header->range_buf_len = sizeof(hv_gpa_range)
254		+ page_count * sizeof(uint64_t);
255	    gpa_header->range[0].byte_offset = 0;
256	    gpa_header->range[0].byte_count = size;
257	    for (i = 0; i < pfnCount; i++) {
258		gpa_header->range[0].pfn_array[i] = pfn + i;
259	    }
260	    *msg_info = msg_header;
261	    *message_count = 1;
262
263	    pfnSum = pfnCount;
264	    pfnLeft = page_count - pfnCount;
265
266	    /*
267	     *  figure out how many pfns we can fit
268	     */
269	    pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
270		- sizeof(hv_vmbus_channel_gpadl_body);
271	    pfnCount = pfnSize / sizeof(uint64_t);
272
273	    /*
274	     * fill in the body
275	     */
276	    while (pfnLeft) {
277		if (pfnLeft > pfnCount) {
278		    pfnCurr = pfnCount;
279		} else {
280		    pfnCurr = pfnLeft;
281		}
282
283		msg_size = sizeof(hv_vmbus_channel_msg_info) +
284		    sizeof(hv_vmbus_channel_gpadl_body) +
285		    pfnCurr * sizeof(uint64_t);
286		msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
287		KASSERT(
288		    msg_body != NULL,
289		    ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
290		if (msg_body == NULL)
291		    return (ENOMEM);
292
293		msg_body->message_size = msg_size;
294		(*message_count)++;
295		gpadl_body =
296		    (hv_vmbus_channel_gpadl_body*) msg_body->msg;
297		/*
298		 * gpadl_body->gpadl = kbuffer;
299		 */
300		for (i = 0; i < pfnCurr; i++) {
301		    gpadl_body->pfn[i] = pfn + pfnSum + i;
302		}
303
304		TAILQ_INSERT_TAIL(
305		    &msg_header->sub_msg_list_anchor,
306		    msg_body,
307		    msg_list_entry);
308		pfnSum += pfnCurr;
309		pfnLeft -= pfnCurr;
310	    }
311	} else { /* else everything fits in a header */
312
313	    msg_size = sizeof(hv_vmbus_channel_msg_info) +
314		sizeof(hv_vmbus_channel_gpadl_header) +
315		sizeof(hv_gpa_range) +
316		page_count * sizeof(uint64_t);
317	    msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
318	    KASSERT(
319		msg_header != NULL,
320		("Error VMBUS: malloc failed to allocate Gpadl Message!"));
321	    if (msg_header == NULL)
322		return (ENOMEM);
323
324	    msg_header->message_size = msg_size;
325
326	    gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
327	    gpa_header->range_count = 1;
328	    gpa_header->range_buf_len = sizeof(hv_gpa_range) +
329		page_count * sizeof(uint64_t);
330	    gpa_header->range[0].byte_offset = 0;
331	    gpa_header->range[0].byte_count = size;
332	    for (i = 0; i < page_count; i++) {
333		gpa_header->range[0].pfn_array[i] = pfn + i;
334	    }
335
336	    *msg_info = msg_header;
337	    *message_count = 1;
338	}
339
340	return (0);
341}
342
343/**
344 * @brief Establish a GPADL for the specified buffer
345 */
346int
347hv_vmbus_channel_establish_gpadl(
348	hv_vmbus_channel*	channel,
349	void*			contig_buffer,
350	uint32_t		size, /* page-size multiple */
351	uint32_t*		gpadl_handle)
352
353{
354	int ret = 0;
355	hv_vmbus_channel_gpadl_header*	gpadl_msg;
356	hv_vmbus_channel_gpadl_body*	gpadl_body;
357	hv_vmbus_channel_msg_info*	msg_info;
358	hv_vmbus_channel_msg_info*	sub_msg_info;
359	uint32_t			msg_count;
360	hv_vmbus_channel_msg_info*	curr;
361	uint32_t			next_gpadl_handle;
362
363	next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle;
364	atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1);
365
366	ret = vmbus_channel_create_gpadl_header(
367		contig_buffer, size, &msg_info, &msg_count);
368
369	if(ret != 0) { /* if(allocation failed) return immediately */
370	    /* reverse atomic_add_int above */
371	    atomic_subtract_int((int*)
372		    &hv_vmbus_g_connection.next_gpadl_handle, 1);
373	    return ret;
374	}
375
376	sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
377	gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
378	gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
379	gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
380	gpadl_msg->gpadl = next_gpadl_handle;
381
382	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
383	TAILQ_INSERT_TAIL(
384		&hv_vmbus_g_connection.channel_msg_anchor,
385		msg_info,
386		msg_list_entry);
387
388	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
389
390	ret = hv_vmbus_post_message(
391		gpadl_msg,
392		msg_info->message_size -
393		    (uint32_t) sizeof(hv_vmbus_channel_msg_info));
394
395	if (ret != 0)
396	    goto cleanup;
397
398	if (msg_count > 1) {
399	    TAILQ_FOREACH(curr,
400		    &msg_info->sub_msg_list_anchor, msg_list_entry) {
401		sub_msg_info = curr;
402		gpadl_body =
403		    (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
404
405		gpadl_body->header.message_type =
406		    HV_CHANNEL_MESSAGE_GPADL_BODY;
407		gpadl_body->gpadl = next_gpadl_handle;
408
409		ret = hv_vmbus_post_message(
410			gpadl_body,
411			sub_msg_info->message_size
412			    - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
413		 /* if (the post message failed) give up and clean up */
414		if(ret != 0)
415		    goto cleanup;
416	    }
417	}
418
419	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds*/
420	if (ret != 0)
421	    goto cleanup;
422
423	*gpadl_handle = gpadl_msg->gpadl;
424
425cleanup:
426
427	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
428	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
429		msg_info, msg_list_entry);
430	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
431
432	sema_destroy(&msg_info->wait_sema);
433	free(msg_info, M_DEVBUF);
434
435	return (ret);
436}
437
438/**
439 * @brief Teardown the specified GPADL handle
440 */
441int
442hv_vmbus_channel_teardown_gpdal(
443	hv_vmbus_channel*	channel,
444	uint32_t		gpadl_handle)
445{
446	int					ret = 0;
447	hv_vmbus_channel_gpadl_teardown*	msg;
448	hv_vmbus_channel_msg_info*		info;
449
450	info = (hv_vmbus_channel_msg_info *)
451		malloc(	sizeof(hv_vmbus_channel_msg_info) +
452			sizeof(hv_vmbus_channel_gpadl_teardown),
453				M_DEVBUF, M_NOWAIT);
454	KASSERT(info != NULL,
455	    ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
456	if (info == NULL) {
457	    ret = ENOMEM;
458	    goto cleanup;
459	}
460
461	sema_init(&info->wait_sema, 0, "Open Info Sema");
462
463	msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
464
465	msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
466	msg->child_rel_id = channel->offer_msg.child_rel_id;
467	msg->gpadl = gpadl_handle;
468
469	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
470	TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
471			info, msg_list_entry);
472	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
473
474	ret = hv_vmbus_post_message(msg,
475			sizeof(hv_vmbus_channel_gpadl_teardown));
476	if (ret != 0)
477	    goto cleanup;
478
479	ret = sema_timedwait(&info->wait_sema, 500); /* KYS 5 seconds */
480
481cleanup:
482	/*
483	 * Received a torndown response
484	 */
485	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
486	TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
487			info, msg_list_entry);
488	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
489	sema_destroy(&info->wait_sema);
490	free(info, M_DEVBUF);
491
492	return (ret);
493}
494
495/**
496 * @brief Close the specified channel
497 */
498void
499hv_vmbus_channel_close(hv_vmbus_channel *channel)
500{
501	int ret = 0;
502	hv_vmbus_channel_close_channel* msg;
503	hv_vmbus_channel_msg_info* info;
504
505	mtx_lock(&channel->inbound_lock);
506	channel->on_channel_callback = NULL;
507	mtx_unlock(&channel->inbound_lock);
508
509	/**
510	 * Send a closing message
511	 */
512	info = (hv_vmbus_channel_msg_info *)
513		malloc(	sizeof(hv_vmbus_channel_msg_info) +
514			sizeof(hv_vmbus_channel_close_channel),
515				M_DEVBUF, M_NOWAIT);
516	KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
517	if(info == NULL)
518	    return;
519
520	msg = (hv_vmbus_channel_close_channel*) info->msg;
521	msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
522	msg->child_rel_id = channel->offer_msg.child_rel_id;
523
524	ret = hv_vmbus_post_message(
525		msg, sizeof(hv_vmbus_channel_close_channel));
526
527	/* Tear down the gpadl for the channel's ring buffer */
528	if (channel->ring_buffer_gpadl_handle) {
529		hv_vmbus_channel_teardown_gpdal(channel,
530			channel->ring_buffer_gpadl_handle);
531	}
532
533	/* TODO: Send a msg to release the childRelId */
534
535	/* cleanup the ring buffers for this channel */
536	hv_ring_buffer_cleanup(&channel->outbound);
537	hv_ring_buffer_cleanup(&channel->inbound);
538
539	contigfree(
540		channel->ring_buffer_pages,
541		channel->ring_buffer_page_count,
542		M_DEVBUF);
543
544	free(info, M_DEVBUF);
545
546	/*
547	 *  If we are closing the channel during an error path in
548	 *  opening the channel, don't free the channel
549	 *  since the caller will free the channel
550	 */
551	if (channel->state == HV_CHANNEL_OPEN_STATE) {
552		mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
553		TAILQ_REMOVE(
554			&hv_vmbus_g_connection.channel_anchor,
555			channel,
556			list_entry);
557		mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
558
559		hv_vmbus_free_vmbus_channel(channel);
560	}
561
562}
563
564/**
565 * @brief Send the specified buffer on the given channel
566 */
567int
568hv_vmbus_channel_send_packet(
569	hv_vmbus_channel*	channel,
570	void*			buffer,
571	uint32_t		buffer_len,
572	uint64_t		request_id,
573	hv_vmbus_packet_type	type,
574	uint32_t		flags)
575{
576	int			ret = 0;
577	hv_vm_packet_descriptor	desc;
578	uint32_t		packet_len;
579	uint64_t		aligned_data;
580	uint32_t		packet_len_aligned;
581	hv_vmbus_sg_buffer_list	buffer_list[3];
582
583	packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
584	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
585	aligned_data = 0;
586
587	/* Setup the descriptor */
588	desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
589	desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
590			    /* in 8-bytes granularity */
591	desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
592	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
593	desc.transaction_id = request_id;
594
595	buffer_list[0].data = &desc;
596	buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
597
598	buffer_list[1].data = buffer;
599	buffer_list[1].length = buffer_len;
600
601	buffer_list[2].data = &aligned_data;
602	buffer_list[2].length = packet_len_aligned - packet_len;
603
604	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
605
606	/* TODO: We should determine if this is optional */
607	if (ret == 0
608		&& !hv_vmbus_get_ring_buffer_interrupt_mask(
609			&channel->outbound)) {
610		vmbus_channel_set_event(channel);
611	}
612
613	return (ret);
614}
615
616/**
617 * @brief Send a range of single-page buffer packets using
618 * a GPADL Direct packet type
619 */
620int
621hv_vmbus_channel_send_packet_pagebuffer(
622	hv_vmbus_channel*	channel,
623	hv_vmbus_page_buffer	page_buffers[],
624	uint32_t		page_count,
625	void*			buffer,
626	uint32_t		buffer_len,
627	uint64_t		request_id)
628{
629
630	int					ret = 0;
631	int					i = 0;
632	uint32_t				packet_len;
633	uint32_t				packetLen_aligned;
634	hv_vmbus_sg_buffer_list			buffer_list[3];
635	hv_vmbus_channel_packet_page_buffer	desc;
636	uint32_t				descSize;
637	uint64_t				alignedData = 0;
638
639	if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
640		return (EINVAL);
641
642	/*
643	 * Adjust the size down since hv_vmbus_channel_packet_page_buffer
644	 *  is the largest size we support
645	 */
646	descSize = sizeof(hv_vmbus_channel_packet_page_buffer) -
647			((HV_MAX_PAGE_BUFFER_COUNT - page_count) *
648			sizeof(hv_vmbus_page_buffer));
649	packet_len = descSize + buffer_len;
650	packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
651
652	/* Setup the descriptor */
653	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
654	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
655	desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */
656	desc.length8 = (uint16_t) (packetLen_aligned >> 3);
657	desc.transaction_id = request_id;
658	desc.range_count = page_count;
659
660	for (i = 0; i < page_count; i++) {
661		desc.range[i].length = page_buffers[i].length;
662		desc.range[i].offset = page_buffers[i].offset;
663		desc.range[i].pfn = page_buffers[i].pfn;
664	}
665
666	buffer_list[0].data = &desc;
667	buffer_list[0].length = descSize;
668
669	buffer_list[1].data = buffer;
670	buffer_list[1].length = buffer_len;
671
672	buffer_list[2].data = &alignedData;
673	buffer_list[2].length = packetLen_aligned - packet_len;
674
675	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
676
677	/* TODO: We should determine if this is optional */
678	if (ret == 0 &&
679		!hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
680		vmbus_channel_set_event(channel);
681	}
682
683	return (ret);
684}
685
686/**
687 * @brief Send a multi-page buffer packet using a GPADL Direct packet type
688 */
689int
690hv_vmbus_channel_send_packet_multipagebuffer(
691	hv_vmbus_channel*		channel,
692	hv_vmbus_multipage_buffer*	multi_page_buffer,
693	void*				buffer,
694	uint32_t			buffer_len,
695	uint64_t			request_id)
696{
697
698	int			ret = 0;
699	uint32_t		desc_size;
700	uint32_t		packet_len;
701	uint32_t		packet_len_aligned;
702	uint32_t		pfn_count;
703	uint64_t		aligned_data = 0;
704	hv_vmbus_sg_buffer_list	buffer_list[3];
705	hv_vmbus_channel_packet_multipage_buffer desc;
706
707	pfn_count =
708	    HV_NUM_PAGES_SPANNED(
709		    multi_page_buffer->offset,
710		    multi_page_buffer->length);
711
712	if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
713	    return (EINVAL);
714	/*
715	 * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
716	 * is the largest size we support
717	 */
718	desc_size =
719	    sizeof(hv_vmbus_channel_packet_multipage_buffer) -
720		    ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
721			sizeof(uint64_t));
722	packet_len = desc_size + buffer_len;
723	packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
724
725	/*
726	 * Setup the descriptor
727	 */
728	desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
729	desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
730	desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
731	desc.length8 = (uint16_t) (packet_len_aligned >> 3);
732	desc.transaction_id = request_id;
733	desc.range_count = 1;
734
735	desc.range.length = multi_page_buffer->length;
736	desc.range.offset = multi_page_buffer->offset;
737
738	memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
739		pfn_count * sizeof(uint64_t));
740
741	buffer_list[0].data = &desc;
742	buffer_list[0].length = desc_size;
743
744	buffer_list[1].data = buffer;
745	buffer_list[1].length = buffer_len;
746
747	buffer_list[2].data = &aligned_data;
748	buffer_list[2].length = packet_len_aligned - packet_len;
749
750	ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3);
751
752	/* TODO: We should determine if this is optional */
753	if (ret == 0 &&
754	    !hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) {
755	    vmbus_channel_set_event(channel);
756	}
757
758	return (ret);
759}
760
761/**
762 * @brief Retrieve the user packet on the specified channel
763 */
764int
765hv_vmbus_channel_recv_packet(
766	hv_vmbus_channel*	channel,
767	void*			Buffer,
768	uint32_t		buffer_len,
769	uint32_t*		buffer_actual_len,
770	uint64_t*		request_id)
771{
772	int			ret;
773	uint32_t		user_len;
774	uint32_t		packet_len;
775	hv_vm_packet_descriptor	desc;
776
777	*buffer_actual_len = 0;
778	*request_id = 0;
779
780	ret = hv_ring_buffer_peek(&channel->inbound, &desc,
781		sizeof(hv_vm_packet_descriptor));
782	if (ret != 0)
783		return (0);
784
785	packet_len = desc.length8 << 3;
786	user_len = packet_len - (desc.data_offset8 << 3);
787
788	*buffer_actual_len = user_len;
789
790	if (user_len > buffer_len)
791		return (EINVAL);
792
793	*request_id = desc.transaction_id;
794
795	/* Copy over the packet to the user buffer */
796	ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
797		(desc.data_offset8 << 3));
798
799	return (0);
800}
801
802/**
803 * @brief Retrieve the raw packet on the specified channel
804 */
805int
806hv_vmbus_channel_recv_packet_raw(
807	hv_vmbus_channel*	channel,
808	void*			buffer,
809	uint32_t		buffer_len,
810	uint32_t*		buffer_actual_len,
811	uint64_t*		request_id)
812{
813	int		ret;
814	uint32_t	packetLen;
815	uint32_t	userLen;
816	hv_vm_packet_descriptor	desc;
817
818	*buffer_actual_len = 0;
819	*request_id = 0;
820
821	ret = hv_ring_buffer_peek(
822		&channel->inbound, &desc,
823		sizeof(hv_vm_packet_descriptor));
824
825	if (ret != 0)
826	    return (0);
827
828	packetLen = desc.length8 << 3;
829	userLen = packetLen - (desc.data_offset8 << 3);
830
831	*buffer_actual_len = packetLen;
832
833	if (packetLen > buffer_len)
834	    return (ENOBUFS);
835
836	*request_id = desc.transaction_id;
837
838	/* Copy over the entire packet to the user buffer */
839	ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
840
841	return (0);
842}
843