xref: /kernel/linux/linux-5.10/drivers/hv/channel.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *   Haiyang Zhang <haiyangz@microsoft.com>
78c2ecf20Sopenharmony_ci *   Hank Janssen  <hjanssen@microsoft.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/wait.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/hyperv.h>
188c2ecf20Sopenharmony_ci#include <linux/uio.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <asm/page.h>
218c2ecf20Sopenharmony_ci#include <asm/mshyperv.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * hv_gpadl_size - Return the real size of a gpadl, the size that Hyper-V uses
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * For BUFFER gpadl, Hyper-V uses the exact same size as the guest does.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * For RING gpadl, in each ring, the guest uses one PAGE_SIZE as the header
318c2ecf20Sopenharmony_ci * (because of the alignment requirement), however, the hypervisor only
328c2ecf20Sopenharmony_ci * uses the first HV_HYP_PAGE_SIZE as the header, therefore leaving a
338c2ecf20Sopenharmony_ci * (PAGE_SIZE - HV_HYP_PAGE_SIZE) gap. And since there are two rings in a
348c2ecf20Sopenharmony_ci * ringbuffer, the total size for a RING gpadl that Hyper-V uses is the
358c2ecf20Sopenharmony_ci * total size that the guest uses minus twice of the gap size.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistatic inline u32 hv_gpadl_size(enum hv_gpadl_type type, u32 size)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	switch (type) {
408c2ecf20Sopenharmony_ci	case HV_GPADL_BUFFER:
418c2ecf20Sopenharmony_ci		return size;
428c2ecf20Sopenharmony_ci	case HV_GPADL_RING:
438c2ecf20Sopenharmony_ci		/* The size of a ringbuffer must be page-aligned */
448c2ecf20Sopenharmony_ci		BUG_ON(size % PAGE_SIZE);
458c2ecf20Sopenharmony_ci		/*
468c2ecf20Sopenharmony_ci		 * Two things to notice here:
478c2ecf20Sopenharmony_ci		 * 1) We're processing two ring buffers as a unit
488c2ecf20Sopenharmony_ci		 * 2) We're skipping any space larger than HV_HYP_PAGE_SIZE in
498c2ecf20Sopenharmony_ci		 * the first guest-size page of each of the two ring buffers.
508c2ecf20Sopenharmony_ci		 * So we effectively subtract out two guest-size pages, and add
518c2ecf20Sopenharmony_ci		 * back two Hyper-V size pages.
528c2ecf20Sopenharmony_ci		 */
538c2ecf20Sopenharmony_ci		return size - 2 * (PAGE_SIZE - HV_HYP_PAGE_SIZE);
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci	BUG();
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * hv_ring_gpadl_send_hvpgoffset - Calculate the send offset (in unit of
618c2ecf20Sopenharmony_ci *                                 HV_HYP_PAGE) in a ring gpadl based on the
628c2ecf20Sopenharmony_ci *                                 offset in the guest
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * @offset: the offset (in bytes) where the send ringbuffer starts in the
658c2ecf20Sopenharmony_ci *               virtual address space of the guest
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic inline u32 hv_ring_gpadl_send_hvpgoffset(u32 offset)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/*
718c2ecf20Sopenharmony_ci	 * For RING gpadl, in each ring, the guest uses one PAGE_SIZE as the
728c2ecf20Sopenharmony_ci	 * header (because of the alignment requirement), however, the
738c2ecf20Sopenharmony_ci	 * hypervisor only uses the first HV_HYP_PAGE_SIZE as the header,
748c2ecf20Sopenharmony_ci	 * therefore leaving a (PAGE_SIZE - HV_HYP_PAGE_SIZE) gap.
758c2ecf20Sopenharmony_ci	 *
768c2ecf20Sopenharmony_ci	 * And to calculate the effective send offset in gpadl, we need to
778c2ecf20Sopenharmony_ci	 * substract this gap.
788c2ecf20Sopenharmony_ci	 */
798c2ecf20Sopenharmony_ci	return (offset - (PAGE_SIZE - HV_HYP_PAGE_SIZE)) >> HV_HYP_PAGE_SHIFT;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci * hv_gpadl_hvpfn - Return the Hyper-V page PFN of the @i th Hyper-V page in
848c2ecf20Sopenharmony_ci *                  the gpadl
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * @type: the type of the gpadl
878c2ecf20Sopenharmony_ci * @kbuffer: the pointer to the gpadl in the guest
888c2ecf20Sopenharmony_ci * @size: the total size (in bytes) of the gpadl
898c2ecf20Sopenharmony_ci * @send_offset: the offset (in bytes) where the send ringbuffer starts in the
908c2ecf20Sopenharmony_ci *               virtual address space of the guest
918c2ecf20Sopenharmony_ci * @i: the index
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_cistatic inline u64 hv_gpadl_hvpfn(enum hv_gpadl_type type, void *kbuffer,
948c2ecf20Sopenharmony_ci				 u32 size, u32 send_offset, int i)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int send_idx = hv_ring_gpadl_send_hvpgoffset(send_offset);
978c2ecf20Sopenharmony_ci	unsigned long delta = 0UL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	switch (type) {
1008c2ecf20Sopenharmony_ci	case HV_GPADL_BUFFER:
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	case HV_GPADL_RING:
1038c2ecf20Sopenharmony_ci		if (i == 0)
1048c2ecf20Sopenharmony_ci			delta = 0;
1058c2ecf20Sopenharmony_ci		else if (i <= send_idx)
1068c2ecf20Sopenharmony_ci			delta = PAGE_SIZE - HV_HYP_PAGE_SIZE;
1078c2ecf20Sopenharmony_ci		else
1088c2ecf20Sopenharmony_ci			delta = 2 * (PAGE_SIZE - HV_HYP_PAGE_SIZE);
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci	default:
1118c2ecf20Sopenharmony_ci		BUG();
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return virt_to_hvpfn(kbuffer + delta + (HV_HYP_PAGE_SIZE * i));
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * vmbus_setevent- Trigger an event notification on the specified
1208c2ecf20Sopenharmony_ci * channel.
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_civoid vmbus_setevent(struct vmbus_channel *channel)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct hv_monitor_page *monitorpage;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	trace_vmbus_setevent(channel);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * For channels marked as in "low latency" mode
1308c2ecf20Sopenharmony_ci	 * bypass the monitor page mechanism.
1318c2ecf20Sopenharmony_ci	 */
1328c2ecf20Sopenharmony_ci	if (channel->offermsg.monitor_allocated && !channel->low_latency) {
1338c2ecf20Sopenharmony_ci		vmbus_send_interrupt(channel->offermsg.child_relid);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		/* Get the child to parent monitor page */
1368c2ecf20Sopenharmony_ci		monitorpage = vmbus_connection.monitor_pages[1];
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		sync_set_bit(channel->monitor_bit,
1398c2ecf20Sopenharmony_ci			(unsigned long *)&monitorpage->trigger_group
1408c2ecf20Sopenharmony_ci					[channel->monitor_grp].pending);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	} else {
1438c2ecf20Sopenharmony_ci		vmbus_set_event(channel);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_setevent);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* vmbus_free_ring - drop mapping of ring buffer */
1498c2ecf20Sopenharmony_civoid vmbus_free_ring(struct vmbus_channel *channel)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	hv_ringbuffer_cleanup(&channel->outbound);
1528c2ecf20Sopenharmony_ci	hv_ringbuffer_cleanup(&channel->inbound);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (channel->ringbuffer_page) {
1558c2ecf20Sopenharmony_ci		__free_pages(channel->ringbuffer_page,
1568c2ecf20Sopenharmony_ci			     get_order(channel->ringbuffer_pagecount
1578c2ecf20Sopenharmony_ci				       << PAGE_SHIFT));
1588c2ecf20Sopenharmony_ci		channel->ringbuffer_page = NULL;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_free_ring);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* vmbus_alloc_ring - allocate and map pages for ring buffer */
1648c2ecf20Sopenharmony_ciint vmbus_alloc_ring(struct vmbus_channel *newchannel,
1658c2ecf20Sopenharmony_ci		     u32 send_size, u32 recv_size)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct page *page;
1688c2ecf20Sopenharmony_ci	int order;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (send_size % PAGE_SIZE || recv_size % PAGE_SIZE)
1718c2ecf20Sopenharmony_ci		return -EINVAL;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* Allocate the ring buffer */
1748c2ecf20Sopenharmony_ci	order = get_order(send_size + recv_size);
1758c2ecf20Sopenharmony_ci	page = alloc_pages_node(cpu_to_node(newchannel->target_cpu),
1768c2ecf20Sopenharmony_ci				GFP_KERNEL|__GFP_ZERO, order);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!page)
1798c2ecf20Sopenharmony_ci		page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (!page)
1828c2ecf20Sopenharmony_ci		return -ENOMEM;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	newchannel->ringbuffer_page = page;
1858c2ecf20Sopenharmony_ci	newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT;
1868c2ecf20Sopenharmony_ci	newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_alloc_ring);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* Used for Hyper-V Socket: a guest client's connect() to the host */
1938c2ecf20Sopenharmony_ciint vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id,
1948c2ecf20Sopenharmony_ci				  const guid_t *shv_host_servie_id)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct vmbus_channel_tl_connect_request conn_msg;
1978c2ecf20Sopenharmony_ci	int ret;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	memset(&conn_msg, 0, sizeof(conn_msg));
2008c2ecf20Sopenharmony_ci	conn_msg.header.msgtype = CHANNELMSG_TL_CONNECT_REQUEST;
2018c2ecf20Sopenharmony_ci	conn_msg.guest_endpoint_id = *shv_guest_servie_id;
2028c2ecf20Sopenharmony_ci	conn_msg.host_service_id = *shv_host_servie_id;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	trace_vmbus_send_tl_connect_request(&conn_msg, ret);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return ret;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * Set/change the vCPU (@target_vp) the channel (@child_relid) will interrupt.
2148c2ecf20Sopenharmony_ci *
2158c2ecf20Sopenharmony_ci * CHANNELMSG_MODIFYCHANNEL messages are aynchronous.  Also, Hyper-V does not
2168c2ecf20Sopenharmony_ci * ACK such messages.  IOW we can't know when the host will stop interrupting
2178c2ecf20Sopenharmony_ci * the "old" vCPU and start interrupting the "new" vCPU for the given channel.
2188c2ecf20Sopenharmony_ci *
2198c2ecf20Sopenharmony_ci * The CHANNELMSG_MODIFYCHANNEL message type is supported since VMBus version
2208c2ecf20Sopenharmony_ci * VERSION_WIN10_V4_1.
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ciint vmbus_send_modifychannel(u32 child_relid, u32 target_vp)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct vmbus_channel_modifychannel conn_msg;
2258c2ecf20Sopenharmony_ci	int ret;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	memset(&conn_msg, 0, sizeof(conn_msg));
2288c2ecf20Sopenharmony_ci	conn_msg.header.msgtype = CHANNELMSG_MODIFYCHANNEL;
2298c2ecf20Sopenharmony_ci	conn_msg.child_relid = child_relid;
2308c2ecf20Sopenharmony_ci	conn_msg.target_vp = target_vp;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	trace_vmbus_send_modifychannel(&conn_msg, ret);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_send_modifychannel);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * create_gpadl_header - Creates a gpadl for the specified buffer
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_cistatic int create_gpadl_header(enum hv_gpadl_type type, void *kbuffer,
2448c2ecf20Sopenharmony_ci			       u32 size, u32 send_offset,
2458c2ecf20Sopenharmony_ci			       struct vmbus_channel_msginfo **msginfo)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	int i;
2488c2ecf20Sopenharmony_ci	int pagecount;
2498c2ecf20Sopenharmony_ci	struct vmbus_channel_gpadl_header *gpadl_header;
2508c2ecf20Sopenharmony_ci	struct vmbus_channel_gpadl_body *gpadl_body;
2518c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *msgheader;
2528c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *msgbody = NULL;
2538c2ecf20Sopenharmony_ci	u32 msgsize;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	int pfnsum, pfncount, pfnleft, pfncurr, pfnsize;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	pagecount = hv_gpadl_size(type, size) >> HV_HYP_PAGE_SHIFT;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* do we need a gpadl body msg */
2608c2ecf20Sopenharmony_ci	pfnsize = MAX_SIZE_CHANNEL_MESSAGE -
2618c2ecf20Sopenharmony_ci		  sizeof(struct vmbus_channel_gpadl_header) -
2628c2ecf20Sopenharmony_ci		  sizeof(struct gpa_range);
2638c2ecf20Sopenharmony_ci	pfncount = pfnsize / sizeof(u64);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (pagecount > pfncount) {
2668c2ecf20Sopenharmony_ci		/* we need a gpadl body */
2678c2ecf20Sopenharmony_ci		/* fill in the header */
2688c2ecf20Sopenharmony_ci		msgsize = sizeof(struct vmbus_channel_msginfo) +
2698c2ecf20Sopenharmony_ci			  sizeof(struct vmbus_channel_gpadl_header) +
2708c2ecf20Sopenharmony_ci			  sizeof(struct gpa_range) + pfncount * sizeof(u64);
2718c2ecf20Sopenharmony_ci		msgheader =  kzalloc(msgsize, GFP_KERNEL);
2728c2ecf20Sopenharmony_ci		if (!msgheader)
2738c2ecf20Sopenharmony_ci			goto nomem;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&msgheader->submsglist);
2768c2ecf20Sopenharmony_ci		msgheader->msgsize = msgsize;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		gpadl_header = (struct vmbus_channel_gpadl_header *)
2798c2ecf20Sopenharmony_ci			msgheader->msg;
2808c2ecf20Sopenharmony_ci		gpadl_header->rangecount = 1;
2818c2ecf20Sopenharmony_ci		gpadl_header->range_buflen = sizeof(struct gpa_range) +
2828c2ecf20Sopenharmony_ci					 pagecount * sizeof(u64);
2838c2ecf20Sopenharmony_ci		gpadl_header->range[0].byte_offset = 0;
2848c2ecf20Sopenharmony_ci		gpadl_header->range[0].byte_count = hv_gpadl_size(type, size);
2858c2ecf20Sopenharmony_ci		for (i = 0; i < pfncount; i++)
2868c2ecf20Sopenharmony_ci			gpadl_header->range[0].pfn_array[i] = hv_gpadl_hvpfn(
2878c2ecf20Sopenharmony_ci				type, kbuffer, size, send_offset, i);
2888c2ecf20Sopenharmony_ci		*msginfo = msgheader;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		pfnsum = pfncount;
2918c2ecf20Sopenharmony_ci		pfnleft = pagecount - pfncount;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		/* how many pfns can we fit */
2948c2ecf20Sopenharmony_ci		pfnsize = MAX_SIZE_CHANNEL_MESSAGE -
2958c2ecf20Sopenharmony_ci			  sizeof(struct vmbus_channel_gpadl_body);
2968c2ecf20Sopenharmony_ci		pfncount = pfnsize / sizeof(u64);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		/* fill in the body */
2998c2ecf20Sopenharmony_ci		while (pfnleft) {
3008c2ecf20Sopenharmony_ci			if (pfnleft > pfncount)
3018c2ecf20Sopenharmony_ci				pfncurr = pfncount;
3028c2ecf20Sopenharmony_ci			else
3038c2ecf20Sopenharmony_ci				pfncurr = pfnleft;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci			msgsize = sizeof(struct vmbus_channel_msginfo) +
3068c2ecf20Sopenharmony_ci				  sizeof(struct vmbus_channel_gpadl_body) +
3078c2ecf20Sopenharmony_ci				  pfncurr * sizeof(u64);
3088c2ecf20Sopenharmony_ci			msgbody = kzalloc(msgsize, GFP_KERNEL);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci			if (!msgbody) {
3118c2ecf20Sopenharmony_ci				struct vmbus_channel_msginfo *pos = NULL;
3128c2ecf20Sopenharmony_ci				struct vmbus_channel_msginfo *tmp = NULL;
3138c2ecf20Sopenharmony_ci				/*
3148c2ecf20Sopenharmony_ci				 * Free up all the allocated messages.
3158c2ecf20Sopenharmony_ci				 */
3168c2ecf20Sopenharmony_ci				list_for_each_entry_safe(pos, tmp,
3178c2ecf20Sopenharmony_ci					&msgheader->submsglist,
3188c2ecf20Sopenharmony_ci					msglistentry) {
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci					list_del(&pos->msglistentry);
3218c2ecf20Sopenharmony_ci					kfree(pos);
3228c2ecf20Sopenharmony_ci				}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci				goto nomem;
3258c2ecf20Sopenharmony_ci			}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci			msgbody->msgsize = msgsize;
3288c2ecf20Sopenharmony_ci			gpadl_body =
3298c2ecf20Sopenharmony_ci				(struct vmbus_channel_gpadl_body *)msgbody->msg;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci			/*
3328c2ecf20Sopenharmony_ci			 * Gpadl is u32 and we are using a pointer which could
3338c2ecf20Sopenharmony_ci			 * be 64-bit
3348c2ecf20Sopenharmony_ci			 * This is governed by the guest/host protocol and
3358c2ecf20Sopenharmony_ci			 * so the hypervisor guarantees that this is ok.
3368c2ecf20Sopenharmony_ci			 */
3378c2ecf20Sopenharmony_ci			for (i = 0; i < pfncurr; i++)
3388c2ecf20Sopenharmony_ci				gpadl_body->pfn[i] = hv_gpadl_hvpfn(type,
3398c2ecf20Sopenharmony_ci					kbuffer, size, send_offset, pfnsum + i);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci			/* add to msg header */
3428c2ecf20Sopenharmony_ci			list_add_tail(&msgbody->msglistentry,
3438c2ecf20Sopenharmony_ci				      &msgheader->submsglist);
3448c2ecf20Sopenharmony_ci			pfnsum += pfncurr;
3458c2ecf20Sopenharmony_ci			pfnleft -= pfncurr;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci	} else {
3488c2ecf20Sopenharmony_ci		/* everything fits in a header */
3498c2ecf20Sopenharmony_ci		msgsize = sizeof(struct vmbus_channel_msginfo) +
3508c2ecf20Sopenharmony_ci			  sizeof(struct vmbus_channel_gpadl_header) +
3518c2ecf20Sopenharmony_ci			  sizeof(struct gpa_range) + pagecount * sizeof(u64);
3528c2ecf20Sopenharmony_ci		msgheader = kzalloc(msgsize, GFP_KERNEL);
3538c2ecf20Sopenharmony_ci		if (msgheader == NULL)
3548c2ecf20Sopenharmony_ci			goto nomem;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&msgheader->submsglist);
3578c2ecf20Sopenharmony_ci		msgheader->msgsize = msgsize;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		gpadl_header = (struct vmbus_channel_gpadl_header *)
3608c2ecf20Sopenharmony_ci			msgheader->msg;
3618c2ecf20Sopenharmony_ci		gpadl_header->rangecount = 1;
3628c2ecf20Sopenharmony_ci		gpadl_header->range_buflen = sizeof(struct gpa_range) +
3638c2ecf20Sopenharmony_ci					 pagecount * sizeof(u64);
3648c2ecf20Sopenharmony_ci		gpadl_header->range[0].byte_offset = 0;
3658c2ecf20Sopenharmony_ci		gpadl_header->range[0].byte_count = hv_gpadl_size(type, size);
3668c2ecf20Sopenharmony_ci		for (i = 0; i < pagecount; i++)
3678c2ecf20Sopenharmony_ci			gpadl_header->range[0].pfn_array[i] = hv_gpadl_hvpfn(
3688c2ecf20Sopenharmony_ci				type, kbuffer, size, send_offset, i);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		*msginfo = msgheader;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_cinomem:
3758c2ecf20Sopenharmony_ci	kfree(msgheader);
3768c2ecf20Sopenharmony_ci	kfree(msgbody);
3778c2ecf20Sopenharmony_ci	return -ENOMEM;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci/*
3818c2ecf20Sopenharmony_ci * __vmbus_establish_gpadl - Establish a GPADL for a buffer or ringbuffer
3828c2ecf20Sopenharmony_ci *
3838c2ecf20Sopenharmony_ci * @channel: a channel
3848c2ecf20Sopenharmony_ci * @type: the type of the corresponding GPADL, only meaningful for the guest.
3858c2ecf20Sopenharmony_ci * @kbuffer: from kmalloc or vmalloc
3868c2ecf20Sopenharmony_ci * @size: page-size multiple
3878c2ecf20Sopenharmony_ci * @send_offset: the offset (in bytes) where the send ring buffer starts,
3888c2ecf20Sopenharmony_ci * 		 should be 0 for BUFFER type gpadl
3898c2ecf20Sopenharmony_ci * @gpadl_handle: some funky thing
3908c2ecf20Sopenharmony_ci */
3918c2ecf20Sopenharmony_cistatic int __vmbus_establish_gpadl(struct vmbus_channel *channel,
3928c2ecf20Sopenharmony_ci				   enum hv_gpadl_type type, void *kbuffer,
3938c2ecf20Sopenharmony_ci				   u32 size, u32 send_offset,
3948c2ecf20Sopenharmony_ci				   u32 *gpadl_handle)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct vmbus_channel_gpadl_header *gpadlmsg;
3978c2ecf20Sopenharmony_ci	struct vmbus_channel_gpadl_body *gpadl_body;
3988c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo = NULL;
3998c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *submsginfo, *tmp;
4008c2ecf20Sopenharmony_ci	struct list_head *curr;
4018c2ecf20Sopenharmony_ci	u32 next_gpadl_handle;
4028c2ecf20Sopenharmony_ci	unsigned long flags;
4038c2ecf20Sopenharmony_ci	int ret = 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	next_gpadl_handle =
4068c2ecf20Sopenharmony_ci		(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	ret = create_gpadl_header(type, kbuffer, size, send_offset, &msginfo);
4098c2ecf20Sopenharmony_ci	if (ret)
4108c2ecf20Sopenharmony_ci		return ret;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	init_completion(&msginfo->waitevent);
4138c2ecf20Sopenharmony_ci	msginfo->waiting_channel = channel;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg;
4168c2ecf20Sopenharmony_ci	gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER;
4178c2ecf20Sopenharmony_ci	gpadlmsg->child_relid = channel->offermsg.child_relid;
4188c2ecf20Sopenharmony_ci	gpadlmsg->gpadl = next_gpadl_handle;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
4228c2ecf20Sopenharmony_ci	list_add_tail(&msginfo->msglistentry,
4238c2ecf20Sopenharmony_ci		      &vmbus_connection.chn_msg_list);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (channel->rescind) {
4288c2ecf20Sopenharmony_ci		ret = -ENODEV;
4298c2ecf20Sopenharmony_ci		goto cleanup;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
4338c2ecf20Sopenharmony_ci			     sizeof(*msginfo), true);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	trace_vmbus_establish_gpadl_header(gpadlmsg, ret);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (ret != 0)
4388c2ecf20Sopenharmony_ci		goto cleanup;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	list_for_each(curr, &msginfo->submsglist) {
4418c2ecf20Sopenharmony_ci		submsginfo = (struct vmbus_channel_msginfo *)curr;
4428c2ecf20Sopenharmony_ci		gpadl_body =
4438c2ecf20Sopenharmony_ci			(struct vmbus_channel_gpadl_body *)submsginfo->msg;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		gpadl_body->header.msgtype =
4468c2ecf20Sopenharmony_ci			CHANNELMSG_GPADL_BODY;
4478c2ecf20Sopenharmony_ci		gpadl_body->gpadl = next_gpadl_handle;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		ret = vmbus_post_msg(gpadl_body,
4508c2ecf20Sopenharmony_ci				     submsginfo->msgsize - sizeof(*submsginfo),
4518c2ecf20Sopenharmony_ci				     true);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		trace_vmbus_establish_gpadl_body(gpadl_body, ret);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci		if (ret != 0)
4568c2ecf20Sopenharmony_ci			goto cleanup;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci	wait_for_completion(&msginfo->waitevent);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (msginfo->response.gpadl_created.creation_status != 0) {
4628c2ecf20Sopenharmony_ci		pr_err("Failed to establish GPADL: err = 0x%x\n",
4638c2ecf20Sopenharmony_ci		       msginfo->response.gpadl_created.creation_status);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		ret = -EDQUOT;
4668c2ecf20Sopenharmony_ci		goto cleanup;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (channel->rescind) {
4708c2ecf20Sopenharmony_ci		ret = -ENODEV;
4718c2ecf20Sopenharmony_ci		goto cleanup;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* At this point, we received the gpadl created msg */
4758c2ecf20Sopenharmony_ci	*gpadl_handle = gpadlmsg->gpadl;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cicleanup:
4788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
4798c2ecf20Sopenharmony_ci	list_del(&msginfo->msglistentry);
4808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
4818c2ecf20Sopenharmony_ci	list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
4828c2ecf20Sopenharmony_ci				 msglistentry) {
4838c2ecf20Sopenharmony_ci		kfree(submsginfo);
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	kfree(msginfo);
4878c2ecf20Sopenharmony_ci	return ret;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/*
4918c2ecf20Sopenharmony_ci * vmbus_establish_gpadl - Establish a GPADL for the specified buffer
4928c2ecf20Sopenharmony_ci *
4938c2ecf20Sopenharmony_ci * @channel: a channel
4948c2ecf20Sopenharmony_ci * @kbuffer: from kmalloc or vmalloc
4958c2ecf20Sopenharmony_ci * @size: page-size multiple
4968c2ecf20Sopenharmony_ci * @gpadl_handle: some funky thing
4978c2ecf20Sopenharmony_ci */
4988c2ecf20Sopenharmony_ciint vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
4998c2ecf20Sopenharmony_ci			  u32 size, u32 *gpadl_handle)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER, kbuffer, size,
5028c2ecf20Sopenharmony_ci				       0U, gpadl_handle);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_establish_gpadl);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int __vmbus_open(struct vmbus_channel *newchannel,
5078c2ecf20Sopenharmony_ci		       void *userdata, u32 userdatalen,
5088c2ecf20Sopenharmony_ci		       void (*onchannelcallback)(void *context), void *context)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct vmbus_channel_open_channel *open_msg;
5118c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *open_info = NULL;
5128c2ecf20Sopenharmony_ci	struct page *page = newchannel->ringbuffer_page;
5138c2ecf20Sopenharmony_ci	u32 send_pages, recv_pages;
5148c2ecf20Sopenharmony_ci	unsigned long flags;
5158c2ecf20Sopenharmony_ci	int err;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (userdatalen > MAX_USER_DEFINED_BYTES)
5188c2ecf20Sopenharmony_ci		return -EINVAL;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	send_pages = newchannel->ringbuffer_send_offset;
5218c2ecf20Sopenharmony_ci	recv_pages = newchannel->ringbuffer_pagecount - send_pages;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (newchannel->state != CHANNEL_OPEN_STATE)
5248c2ecf20Sopenharmony_ci		return -EINVAL;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	newchannel->state = CHANNEL_OPENING_STATE;
5278c2ecf20Sopenharmony_ci	newchannel->onchannel_callback = onchannelcallback;
5288c2ecf20Sopenharmony_ci	newchannel->channel_callback_context = context;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	err = hv_ringbuffer_init(&newchannel->outbound, page, send_pages);
5318c2ecf20Sopenharmony_ci	if (err)
5328c2ecf20Sopenharmony_ci		goto error_clean_ring;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	err = hv_ringbuffer_init(&newchannel->inbound,
5358c2ecf20Sopenharmony_ci				 &page[send_pages], recv_pages);
5368c2ecf20Sopenharmony_ci	if (err)
5378c2ecf20Sopenharmony_ci		goto error_clean_ring;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	/* Establish the gpadl for the ring buffer */
5408c2ecf20Sopenharmony_ci	newchannel->ringbuffer_gpadlhandle = 0;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING,
5438c2ecf20Sopenharmony_ci				      page_address(newchannel->ringbuffer_page),
5448c2ecf20Sopenharmony_ci				      (send_pages + recv_pages) << PAGE_SHIFT,
5458c2ecf20Sopenharmony_ci				      newchannel->ringbuffer_send_offset << PAGE_SHIFT,
5468c2ecf20Sopenharmony_ci				      &newchannel->ringbuffer_gpadlhandle);
5478c2ecf20Sopenharmony_ci	if (err)
5488c2ecf20Sopenharmony_ci		goto error_clean_ring;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* Create and init the channel open message */
5518c2ecf20Sopenharmony_ci	open_info = kmalloc(sizeof(*open_info) +
5528c2ecf20Sopenharmony_ci			   sizeof(struct vmbus_channel_open_channel),
5538c2ecf20Sopenharmony_ci			   GFP_KERNEL);
5548c2ecf20Sopenharmony_ci	if (!open_info) {
5558c2ecf20Sopenharmony_ci		err = -ENOMEM;
5568c2ecf20Sopenharmony_ci		goto error_free_gpadl;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	init_completion(&open_info->waitevent);
5608c2ecf20Sopenharmony_ci	open_info->waiting_channel = newchannel;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	open_msg = (struct vmbus_channel_open_channel *)open_info->msg;
5638c2ecf20Sopenharmony_ci	open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL;
5648c2ecf20Sopenharmony_ci	open_msg->openid = newchannel->offermsg.child_relid;
5658c2ecf20Sopenharmony_ci	open_msg->child_relid = newchannel->offermsg.child_relid;
5668c2ecf20Sopenharmony_ci	open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle;
5678c2ecf20Sopenharmony_ci	/*
5688c2ecf20Sopenharmony_ci	 * The unit of ->downstream_ringbuffer_pageoffset is HV_HYP_PAGE and
5698c2ecf20Sopenharmony_ci	 * the unit of ->ringbuffer_send_offset (i.e. send_pages) is PAGE, so
5708c2ecf20Sopenharmony_ci	 * here we calculate it into HV_HYP_PAGE.
5718c2ecf20Sopenharmony_ci	 */
5728c2ecf20Sopenharmony_ci	open_msg->downstream_ringbuffer_pageoffset =
5738c2ecf20Sopenharmony_ci		hv_ring_gpadl_send_hvpgoffset(send_pages << PAGE_SHIFT);
5748c2ecf20Sopenharmony_ci	open_msg->target_vp = hv_cpu_number_to_vp_number(newchannel->target_cpu);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (userdatalen)
5778c2ecf20Sopenharmony_ci		memcpy(open_msg->userdata, userdata, userdatalen);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
5808c2ecf20Sopenharmony_ci	list_add_tail(&open_info->msglistentry,
5818c2ecf20Sopenharmony_ci		      &vmbus_connection.chn_msg_list);
5828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (newchannel->rescind) {
5858c2ecf20Sopenharmony_ci		err = -ENODEV;
5868c2ecf20Sopenharmony_ci		goto error_clean_msglist;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	err = vmbus_post_msg(open_msg,
5908c2ecf20Sopenharmony_ci			     sizeof(struct vmbus_channel_open_channel), true);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	trace_vmbus_open(open_msg, err);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (err != 0)
5958c2ecf20Sopenharmony_ci		goto error_clean_msglist;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	wait_for_completion(&open_info->waitevent);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
6008c2ecf20Sopenharmony_ci	list_del(&open_info->msglistentry);
6018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	if (newchannel->rescind) {
6048c2ecf20Sopenharmony_ci		err = -ENODEV;
6058c2ecf20Sopenharmony_ci		goto error_free_info;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	if (open_info->response.open_result.status) {
6098c2ecf20Sopenharmony_ci		err = -EAGAIN;
6108c2ecf20Sopenharmony_ci		goto error_free_info;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	newchannel->state = CHANNEL_OPENED_STATE;
6148c2ecf20Sopenharmony_ci	kfree(open_info);
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cierror_clean_msglist:
6188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
6198c2ecf20Sopenharmony_ci	list_del(&open_info->msglistentry);
6208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
6218c2ecf20Sopenharmony_cierror_free_info:
6228c2ecf20Sopenharmony_ci	kfree(open_info);
6238c2ecf20Sopenharmony_cierror_free_gpadl:
6248c2ecf20Sopenharmony_ci	vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
6258c2ecf20Sopenharmony_ci	newchannel->ringbuffer_gpadlhandle = 0;
6268c2ecf20Sopenharmony_cierror_clean_ring:
6278c2ecf20Sopenharmony_ci	hv_ringbuffer_cleanup(&newchannel->outbound);
6288c2ecf20Sopenharmony_ci	hv_ringbuffer_cleanup(&newchannel->inbound);
6298c2ecf20Sopenharmony_ci	newchannel->state = CHANNEL_OPEN_STATE;
6308c2ecf20Sopenharmony_ci	return err;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/*
6348c2ecf20Sopenharmony_ci * vmbus_connect_ring - Open the channel but reuse ring buffer
6358c2ecf20Sopenharmony_ci */
6368c2ecf20Sopenharmony_ciint vmbus_connect_ring(struct vmbus_channel *newchannel,
6378c2ecf20Sopenharmony_ci		       void (*onchannelcallback)(void *context), void *context)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	return  __vmbus_open(newchannel, NULL, 0, onchannelcallback, context);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_connect_ring);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/*
6448c2ecf20Sopenharmony_ci * vmbus_open - Open the specified channel.
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_ciint vmbus_open(struct vmbus_channel *newchannel,
6478c2ecf20Sopenharmony_ci	       u32 send_ringbuffer_size, u32 recv_ringbuffer_size,
6488c2ecf20Sopenharmony_ci	       void *userdata, u32 userdatalen,
6498c2ecf20Sopenharmony_ci	       void (*onchannelcallback)(void *context), void *context)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	int err;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	err = vmbus_alloc_ring(newchannel, send_ringbuffer_size,
6548c2ecf20Sopenharmony_ci			       recv_ringbuffer_size);
6558c2ecf20Sopenharmony_ci	if (err)
6568c2ecf20Sopenharmony_ci		return err;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	err = __vmbus_open(newchannel, userdata, userdatalen,
6598c2ecf20Sopenharmony_ci			   onchannelcallback, context);
6608c2ecf20Sopenharmony_ci	if (err)
6618c2ecf20Sopenharmony_ci		vmbus_free_ring(newchannel);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return err;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_open);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci/*
6688c2ecf20Sopenharmony_ci * vmbus_teardown_gpadl -Teardown the specified GPADL handle
6698c2ecf20Sopenharmony_ci */
6708c2ecf20Sopenharmony_ciint vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct vmbus_channel_gpadl_teardown *msg;
6738c2ecf20Sopenharmony_ci	struct vmbus_channel_msginfo *info;
6748c2ecf20Sopenharmony_ci	unsigned long flags;
6758c2ecf20Sopenharmony_ci	int ret;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	info = kmalloc(sizeof(*info) +
6788c2ecf20Sopenharmony_ci		       sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL);
6798c2ecf20Sopenharmony_ci	if (!info)
6808c2ecf20Sopenharmony_ci		return -ENOMEM;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	init_completion(&info->waitevent);
6838c2ecf20Sopenharmony_ci	info->waiting_channel = channel;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	msg = (struct vmbus_channel_gpadl_teardown *)info->msg;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN;
6888c2ecf20Sopenharmony_ci	msg->child_relid = channel->offermsg.child_relid;
6898c2ecf20Sopenharmony_ci	msg->gpadl = gpadl_handle;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
6928c2ecf20Sopenharmony_ci	list_add_tail(&info->msglistentry,
6938c2ecf20Sopenharmony_ci		      &vmbus_connection.chn_msg_list);
6948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (channel->rescind)
6978c2ecf20Sopenharmony_ci		goto post_msg_err;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown),
7008c2ecf20Sopenharmony_ci			     true);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	trace_vmbus_teardown_gpadl(msg, ret);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (ret)
7058c2ecf20Sopenharmony_ci		goto post_msg_err;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	wait_for_completion(&info->waitevent);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cipost_msg_err:
7108c2ecf20Sopenharmony_ci	/*
7118c2ecf20Sopenharmony_ci	 * If the channel has been rescinded;
7128c2ecf20Sopenharmony_ci	 * we will be awakened by the rescind
7138c2ecf20Sopenharmony_ci	 * handler; set the error code to zero so we don't leak memory.
7148c2ecf20Sopenharmony_ci	 */
7158c2ecf20Sopenharmony_ci	if (channel->rescind)
7168c2ecf20Sopenharmony_ci		ret = 0;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
7198c2ecf20Sopenharmony_ci	list_del(&info->msglistentry);
7208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	kfree(info);
7238c2ecf20Sopenharmony_ci	return ret;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_civoid vmbus_reset_channel_cb(struct vmbus_channel *channel)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	unsigned long flags;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/*
7328c2ecf20Sopenharmony_ci	 * vmbus_on_event(), running in the per-channel tasklet, can race
7338c2ecf20Sopenharmony_ci	 * with vmbus_close_internal() in the case of SMP guest, e.g., when
7348c2ecf20Sopenharmony_ci	 * the former is accessing channel->inbound.ring_buffer, the latter
7358c2ecf20Sopenharmony_ci	 * could be freeing the ring_buffer pages, so here we must stop it
7368c2ecf20Sopenharmony_ci	 * first.
7378c2ecf20Sopenharmony_ci	 *
7388c2ecf20Sopenharmony_ci	 * vmbus_chan_sched() might call the netvsc driver callback function
7398c2ecf20Sopenharmony_ci	 * that ends up scheduling NAPI work that accesses the ring buffer.
7408c2ecf20Sopenharmony_ci	 * At this point, we have to ensure that any such work is completed
7418c2ecf20Sopenharmony_ci	 * and that the channel ring buffer is no longer being accessed, cf.
7428c2ecf20Sopenharmony_ci	 * the calls to napi_disable() in netvsc_device_remove().
7438c2ecf20Sopenharmony_ci	 */
7448c2ecf20Sopenharmony_ci	tasklet_disable(&channel->callback_event);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	/* See the inline comments in vmbus_chan_sched(). */
7478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&channel->sched_lock, flags);
7488c2ecf20Sopenharmony_ci	channel->onchannel_callback = NULL;
7498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&channel->sched_lock, flags);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	channel->sc_creation_callback = NULL;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* Re-enable tasklet for use on re-open */
7548c2ecf20Sopenharmony_ci	tasklet_enable(&channel->callback_event);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int vmbus_close_internal(struct vmbus_channel *channel)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct vmbus_channel_close_channel *msg;
7608c2ecf20Sopenharmony_ci	int ret;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	vmbus_reset_channel_cb(channel);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/*
7658c2ecf20Sopenharmony_ci	 * In case a device driver's probe() fails (e.g.,
7668c2ecf20Sopenharmony_ci	 * util_probe() -> vmbus_open() returns -ENOMEM) and the device is
7678c2ecf20Sopenharmony_ci	 * rescinded later (e.g., we dynamically disable an Integrated Service
7688c2ecf20Sopenharmony_ci	 * in Hyper-V Manager), the driver's remove() invokes vmbus_close():
7698c2ecf20Sopenharmony_ci	 * here we should skip most of the below cleanup work.
7708c2ecf20Sopenharmony_ci	 */
7718c2ecf20Sopenharmony_ci	if (channel->state != CHANNEL_OPENED_STATE)
7728c2ecf20Sopenharmony_ci		return -EINVAL;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	channel->state = CHANNEL_OPEN_STATE;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Send a closing message */
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	msg = &channel->close_msg.msg;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
7818c2ecf20Sopenharmony_ci	msg->child_relid = channel->offermsg.child_relid;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel),
7848c2ecf20Sopenharmony_ci			     true);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	trace_vmbus_close_internal(msg, ret);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (ret) {
7898c2ecf20Sopenharmony_ci		pr_err("Close failed: close post msg return is %d\n", ret);
7908c2ecf20Sopenharmony_ci		/*
7918c2ecf20Sopenharmony_ci		 * If we failed to post the close msg,
7928c2ecf20Sopenharmony_ci		 * it is perhaps better to leak memory.
7938c2ecf20Sopenharmony_ci		 */
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	/* Tear down the gpadl for the channel's ring buffer */
7978c2ecf20Sopenharmony_ci	else if (channel->ringbuffer_gpadlhandle) {
7988c2ecf20Sopenharmony_ci		ret = vmbus_teardown_gpadl(channel,
7998c2ecf20Sopenharmony_ci					   channel->ringbuffer_gpadlhandle);
8008c2ecf20Sopenharmony_ci		if (ret) {
8018c2ecf20Sopenharmony_ci			pr_err("Close failed: teardown gpadl return %d\n", ret);
8028c2ecf20Sopenharmony_ci			/*
8038c2ecf20Sopenharmony_ci			 * If we failed to teardown gpadl,
8048c2ecf20Sopenharmony_ci			 * it is perhaps better to leak memory.
8058c2ecf20Sopenharmony_ci			 */
8068c2ecf20Sopenharmony_ci		}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		channel->ringbuffer_gpadlhandle = 0;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	return ret;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/* disconnect ring - close all channels */
8158c2ecf20Sopenharmony_ciint vmbus_disconnect_ring(struct vmbus_channel *channel)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct vmbus_channel *cur_channel, *tmp;
8188c2ecf20Sopenharmony_ci	int ret;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (channel->primary_channel != NULL)
8218c2ecf20Sopenharmony_ci		return -EINVAL;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cur_channel, tmp, &channel->sc_list, sc_list) {
8248c2ecf20Sopenharmony_ci		if (cur_channel->rescind)
8258c2ecf20Sopenharmony_ci			wait_for_completion(&cur_channel->rescind_event);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		mutex_lock(&vmbus_connection.channel_mutex);
8288c2ecf20Sopenharmony_ci		if (vmbus_close_internal(cur_channel) == 0) {
8298c2ecf20Sopenharmony_ci			vmbus_free_ring(cur_channel);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			if (cur_channel->rescind)
8328c2ecf20Sopenharmony_ci				hv_process_channel_removal(cur_channel);
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci		mutex_unlock(&vmbus_connection.channel_mutex);
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	/*
8388c2ecf20Sopenharmony_ci	 * Now close the primary.
8398c2ecf20Sopenharmony_ci	 */
8408c2ecf20Sopenharmony_ci	mutex_lock(&vmbus_connection.channel_mutex);
8418c2ecf20Sopenharmony_ci	ret = vmbus_close_internal(channel);
8428c2ecf20Sopenharmony_ci	mutex_unlock(&vmbus_connection.channel_mutex);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return ret;
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_disconnect_ring);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/*
8498c2ecf20Sopenharmony_ci * vmbus_close - Close the specified channel
8508c2ecf20Sopenharmony_ci */
8518c2ecf20Sopenharmony_civoid vmbus_close(struct vmbus_channel *channel)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	if (vmbus_disconnect_ring(channel) == 0)
8548c2ecf20Sopenharmony_ci		vmbus_free_ring(channel);
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_close);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci/**
8598c2ecf20Sopenharmony_ci * vmbus_sendpacket() - Send the specified buffer on the given channel
8608c2ecf20Sopenharmony_ci * @channel: Pointer to vmbus_channel structure
8618c2ecf20Sopenharmony_ci * @buffer: Pointer to the buffer you want to send the data from.
8628c2ecf20Sopenharmony_ci * @bufferlen: Maximum size of what the buffer holds.
8638c2ecf20Sopenharmony_ci * @requestid: Identifier of the request
8648c2ecf20Sopenharmony_ci * @type: Type of packet that is being sent e.g. negotiate, time
8658c2ecf20Sopenharmony_ci *	  packet etc.
8668c2ecf20Sopenharmony_ci * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED
8678c2ecf20Sopenharmony_ci *
8688c2ecf20Sopenharmony_ci * Sends data in @buffer directly to Hyper-V via the vmbus.
8698c2ecf20Sopenharmony_ci * This will send the data unparsed to Hyper-V.
8708c2ecf20Sopenharmony_ci *
8718c2ecf20Sopenharmony_ci * Mainly used by Hyper-V drivers.
8728c2ecf20Sopenharmony_ci */
8738c2ecf20Sopenharmony_ciint vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
8748c2ecf20Sopenharmony_ci			   u32 bufferlen, u64 requestid,
8758c2ecf20Sopenharmony_ci			   enum vmbus_packet_type type, u32 flags)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct vmpacket_descriptor desc;
8788c2ecf20Sopenharmony_ci	u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
8798c2ecf20Sopenharmony_ci	u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
8808c2ecf20Sopenharmony_ci	struct kvec bufferlist[3];
8818c2ecf20Sopenharmony_ci	u64 aligned_data = 0;
8828c2ecf20Sopenharmony_ci	int num_vecs = ((bufferlen != 0) ? 3 : 1);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	/* Setup the descriptor */
8868c2ecf20Sopenharmony_ci	desc.type = type; /* VmbusPacketTypeDataInBand; */
8878c2ecf20Sopenharmony_ci	desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */
8888c2ecf20Sopenharmony_ci	/* in 8-bytes granularity */
8898c2ecf20Sopenharmony_ci	desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3;
8908c2ecf20Sopenharmony_ci	desc.len8 = (u16)(packetlen_aligned >> 3);
8918c2ecf20Sopenharmony_ci	desc.trans_id = requestid;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	bufferlist[0].iov_base = &desc;
8948c2ecf20Sopenharmony_ci	bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor);
8958c2ecf20Sopenharmony_ci	bufferlist[1].iov_base = buffer;
8968c2ecf20Sopenharmony_ci	bufferlist[1].iov_len = bufferlen;
8978c2ecf20Sopenharmony_ci	bufferlist[2].iov_base = &aligned_data;
8988c2ecf20Sopenharmony_ci	bufferlist[2].iov_len = (packetlen_aligned - packetlen);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	return hv_ringbuffer_write(channel, bufferlist, num_vecs);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vmbus_sendpacket);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci/*
9058c2ecf20Sopenharmony_ci * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
9068c2ecf20Sopenharmony_ci * packets using a GPADL Direct packet type. This interface allows you
9078c2ecf20Sopenharmony_ci * to control notifying the host. This will be useful for sending
9088c2ecf20Sopenharmony_ci * batched data. Also the sender can control the send flags
9098c2ecf20Sopenharmony_ci * explicitly.
9108c2ecf20Sopenharmony_ci */
9118c2ecf20Sopenharmony_ciint vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
9128c2ecf20Sopenharmony_ci				struct hv_page_buffer pagebuffers[],
9138c2ecf20Sopenharmony_ci				u32 pagecount, void *buffer, u32 bufferlen,
9148c2ecf20Sopenharmony_ci				u64 requestid)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	int i;
9178c2ecf20Sopenharmony_ci	struct vmbus_channel_packet_page_buffer desc;
9188c2ecf20Sopenharmony_ci	u32 descsize;
9198c2ecf20Sopenharmony_ci	u32 packetlen;
9208c2ecf20Sopenharmony_ci	u32 packetlen_aligned;
9218c2ecf20Sopenharmony_ci	struct kvec bufferlist[3];
9228c2ecf20Sopenharmony_ci	u64 aligned_data = 0;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (pagecount > MAX_PAGE_BUFFER_COUNT)
9258c2ecf20Sopenharmony_ci		return -EINVAL;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	/*
9288c2ecf20Sopenharmony_ci	 * Adjust the size down since vmbus_channel_packet_page_buffer is the
9298c2ecf20Sopenharmony_ci	 * largest size we support
9308c2ecf20Sopenharmony_ci	 */
9318c2ecf20Sopenharmony_ci	descsize = sizeof(struct vmbus_channel_packet_page_buffer) -
9328c2ecf20Sopenharmony_ci			  ((MAX_PAGE_BUFFER_COUNT - pagecount) *
9338c2ecf20Sopenharmony_ci			  sizeof(struct hv_page_buffer));
9348c2ecf20Sopenharmony_ci	packetlen = descsize + bufferlen;
9358c2ecf20Sopenharmony_ci	packetlen_aligned = ALIGN(packetlen, sizeof(u64));
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	/* Setup the descriptor */
9388c2ecf20Sopenharmony_ci	desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
9398c2ecf20Sopenharmony_ci	desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
9408c2ecf20Sopenharmony_ci	desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */
9418c2ecf20Sopenharmony_ci	desc.length8 = (u16)(packetlen_aligned >> 3);
9428c2ecf20Sopenharmony_ci	desc.transactionid = requestid;
9438c2ecf20Sopenharmony_ci	desc.reserved = 0;
9448c2ecf20Sopenharmony_ci	desc.rangecount = pagecount;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	for (i = 0; i < pagecount; i++) {
9478c2ecf20Sopenharmony_ci		desc.range[i].len = pagebuffers[i].len;
9488c2ecf20Sopenharmony_ci		desc.range[i].offset = pagebuffers[i].offset;
9498c2ecf20Sopenharmony_ci		desc.range[i].pfn	 = pagebuffers[i].pfn;
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	bufferlist[0].iov_base = &desc;
9538c2ecf20Sopenharmony_ci	bufferlist[0].iov_len = descsize;
9548c2ecf20Sopenharmony_ci	bufferlist[1].iov_base = buffer;
9558c2ecf20Sopenharmony_ci	bufferlist[1].iov_len = bufferlen;
9568c2ecf20Sopenharmony_ci	bufferlist[2].iov_base = &aligned_data;
9578c2ecf20Sopenharmony_ci	bufferlist[2].iov_len = (packetlen_aligned - packetlen);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	return hv_ringbuffer_write(channel, bufferlist, 3);
9608c2ecf20Sopenharmony_ci}
9618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci/*
9648c2ecf20Sopenharmony_ci * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet
9658c2ecf20Sopenharmony_ci * using a GPADL Direct packet type.
9668c2ecf20Sopenharmony_ci * The buffer includes the vmbus descriptor.
9678c2ecf20Sopenharmony_ci */
9688c2ecf20Sopenharmony_ciint vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
9698c2ecf20Sopenharmony_ci			      struct vmbus_packet_mpb_array *desc,
9708c2ecf20Sopenharmony_ci			      u32 desc_size,
9718c2ecf20Sopenharmony_ci			      void *buffer, u32 bufferlen, u64 requestid)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	u32 packetlen;
9748c2ecf20Sopenharmony_ci	u32 packetlen_aligned;
9758c2ecf20Sopenharmony_ci	struct kvec bufferlist[3];
9768c2ecf20Sopenharmony_ci	u64 aligned_data = 0;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	packetlen = desc_size + bufferlen;
9798c2ecf20Sopenharmony_ci	packetlen_aligned = ALIGN(packetlen, sizeof(u64));
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Setup the descriptor */
9828c2ecf20Sopenharmony_ci	desc->type = VM_PKT_DATA_USING_GPA_DIRECT;
9838c2ecf20Sopenharmony_ci	desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
9848c2ecf20Sopenharmony_ci	desc->dataoffset8 = desc_size >> 3; /* in 8-bytes granularity */
9858c2ecf20Sopenharmony_ci	desc->length8 = (u16)(packetlen_aligned >> 3);
9868c2ecf20Sopenharmony_ci	desc->transactionid = requestid;
9878c2ecf20Sopenharmony_ci	desc->reserved = 0;
9888c2ecf20Sopenharmony_ci	desc->rangecount = 1;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	bufferlist[0].iov_base = desc;
9918c2ecf20Sopenharmony_ci	bufferlist[0].iov_len = desc_size;
9928c2ecf20Sopenharmony_ci	bufferlist[1].iov_base = buffer;
9938c2ecf20Sopenharmony_ci	bufferlist[1].iov_len = bufferlen;
9948c2ecf20Sopenharmony_ci	bufferlist[2].iov_base = &aligned_data;
9958c2ecf20Sopenharmony_ci	bufferlist[2].iov_len = (packetlen_aligned - packetlen);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return hv_ringbuffer_write(channel, bufferlist, 3);
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci/**
10028c2ecf20Sopenharmony_ci * __vmbus_recvpacket() - Retrieve the user packet on the specified channel
10038c2ecf20Sopenharmony_ci * @channel: Pointer to vmbus_channel structure
10048c2ecf20Sopenharmony_ci * @buffer: Pointer to the buffer you want to receive the data into.
10058c2ecf20Sopenharmony_ci * @bufferlen: Maximum size of what the buffer can hold.
10068c2ecf20Sopenharmony_ci * @buffer_actual_len: The actual size of the data after it was received.
10078c2ecf20Sopenharmony_ci * @requestid: Identifier of the request
10088c2ecf20Sopenharmony_ci * @raw: true means keep the vmpacket_descriptor header in the received data.
10098c2ecf20Sopenharmony_ci *
10108c2ecf20Sopenharmony_ci * Receives directly from the hyper-v vmbus and puts the data it received
10118c2ecf20Sopenharmony_ci * into Buffer. This will receive the data unparsed from hyper-v.
10128c2ecf20Sopenharmony_ci *
10138c2ecf20Sopenharmony_ci * Mainly used by Hyper-V drivers.
10148c2ecf20Sopenharmony_ci */
10158c2ecf20Sopenharmony_cistatic inline int
10168c2ecf20Sopenharmony_ci__vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
10178c2ecf20Sopenharmony_ci		   u32 bufferlen, u32 *buffer_actual_len, u64 *requestid,
10188c2ecf20Sopenharmony_ci		   bool raw)
10198c2ecf20Sopenharmony_ci{
10208c2ecf20Sopenharmony_ci	return hv_ringbuffer_read(channel, buffer, bufferlen,
10218c2ecf20Sopenharmony_ci				  buffer_actual_len, requestid, raw);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ciint vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
10268c2ecf20Sopenharmony_ci		     u32 bufferlen, u32 *buffer_actual_len,
10278c2ecf20Sopenharmony_ci		     u64 *requestid)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	return __vmbus_recvpacket(channel, buffer, bufferlen,
10308c2ecf20Sopenharmony_ci				  buffer_actual_len, requestid, false);
10318c2ecf20Sopenharmony_ci}
10328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vmbus_recvpacket);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci/*
10358c2ecf20Sopenharmony_ci * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel
10368c2ecf20Sopenharmony_ci */
10378c2ecf20Sopenharmony_ciint vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
10388c2ecf20Sopenharmony_ci			      u32 bufferlen, u32 *buffer_actual_len,
10398c2ecf20Sopenharmony_ci			      u64 *requestid)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	return __vmbus_recvpacket(channel, buffer, bufferlen,
10428c2ecf20Sopenharmony_ci				  buffer_actual_len, requestid, true);
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_recvpacket_raw);
1045