162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 762306a36Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/wait.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/hyperv.h> 1862306a36Sopenharmony_ci#include <linux/uio.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/set_memory.h> 2162306a36Sopenharmony_ci#include <asm/page.h> 2262306a36Sopenharmony_ci#include <asm/mshyperv.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "hyperv_vmbus.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * hv_gpadl_size - Return the real size of a gpadl, the size that Hyper-V uses 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * For BUFFER gpadl, Hyper-V uses the exact same size as the guest does. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * For RING gpadl, in each ring, the guest uses one PAGE_SIZE as the header 3262306a36Sopenharmony_ci * (because of the alignment requirement), however, the hypervisor only 3362306a36Sopenharmony_ci * uses the first HV_HYP_PAGE_SIZE as the header, therefore leaving a 3462306a36Sopenharmony_ci * (PAGE_SIZE - HV_HYP_PAGE_SIZE) gap. And since there are two rings in a 3562306a36Sopenharmony_ci * ringbuffer, the total size for a RING gpadl that Hyper-V uses is the 3662306a36Sopenharmony_ci * total size that the guest uses minus twice of the gap size. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic inline u32 hv_gpadl_size(enum hv_gpadl_type type, u32 size) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci switch (type) { 4162306a36Sopenharmony_ci case HV_GPADL_BUFFER: 4262306a36Sopenharmony_ci return size; 4362306a36Sopenharmony_ci case HV_GPADL_RING: 4462306a36Sopenharmony_ci /* The size of a ringbuffer must be page-aligned */ 4562306a36Sopenharmony_ci BUG_ON(size % PAGE_SIZE); 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * Two things to notice here: 4862306a36Sopenharmony_ci * 1) We're processing two ring buffers as a unit 4962306a36Sopenharmony_ci * 2) We're skipping any space larger than HV_HYP_PAGE_SIZE in 5062306a36Sopenharmony_ci * the first guest-size page of each of the two ring buffers. 5162306a36Sopenharmony_ci * So we effectively subtract out two guest-size pages, and add 5262306a36Sopenharmony_ci * back two Hyper-V size pages. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci return size - 2 * (PAGE_SIZE - HV_HYP_PAGE_SIZE); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci BUG(); 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * hv_ring_gpadl_send_hvpgoffset - Calculate the send offset (in unit of 6262306a36Sopenharmony_ci * HV_HYP_PAGE) in a ring gpadl based on the 6362306a36Sopenharmony_ci * offset in the guest 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * @offset: the offset (in bytes) where the send ringbuffer starts in the 6662306a36Sopenharmony_ci * virtual address space of the guest 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic inline u32 hv_ring_gpadl_send_hvpgoffset(u32 offset) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * For RING gpadl, in each ring, the guest uses one PAGE_SIZE as the 7362306a36Sopenharmony_ci * header (because of the alignment requirement), however, the 7462306a36Sopenharmony_ci * hypervisor only uses the first HV_HYP_PAGE_SIZE as the header, 7562306a36Sopenharmony_ci * therefore leaving a (PAGE_SIZE - HV_HYP_PAGE_SIZE) gap. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * And to calculate the effective send offset in gpadl, we need to 7862306a36Sopenharmony_ci * substract this gap. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci return (offset - (PAGE_SIZE - HV_HYP_PAGE_SIZE)) >> HV_HYP_PAGE_SHIFT; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * hv_gpadl_hvpfn - Return the Hyper-V page PFN of the @i th Hyper-V page in 8562306a36Sopenharmony_ci * the gpadl 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * @type: the type of the gpadl 8862306a36Sopenharmony_ci * @kbuffer: the pointer to the gpadl in the guest 8962306a36Sopenharmony_ci * @size: the total size (in bytes) of the gpadl 9062306a36Sopenharmony_ci * @send_offset: the offset (in bytes) where the send ringbuffer starts in the 9162306a36Sopenharmony_ci * virtual address space of the guest 9262306a36Sopenharmony_ci * @i: the index 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic inline u64 hv_gpadl_hvpfn(enum hv_gpadl_type type, void *kbuffer, 9562306a36Sopenharmony_ci u32 size, u32 send_offset, int i) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int send_idx = hv_ring_gpadl_send_hvpgoffset(send_offset); 9862306a36Sopenharmony_ci unsigned long delta = 0UL; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci switch (type) { 10162306a36Sopenharmony_ci case HV_GPADL_BUFFER: 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case HV_GPADL_RING: 10462306a36Sopenharmony_ci if (i == 0) 10562306a36Sopenharmony_ci delta = 0; 10662306a36Sopenharmony_ci else if (i <= send_idx) 10762306a36Sopenharmony_ci delta = PAGE_SIZE - HV_HYP_PAGE_SIZE; 10862306a36Sopenharmony_ci else 10962306a36Sopenharmony_ci delta = 2 * (PAGE_SIZE - HV_HYP_PAGE_SIZE); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci default: 11262306a36Sopenharmony_ci BUG(); 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return virt_to_hvpfn(kbuffer + delta + (HV_HYP_PAGE_SIZE * i)); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * vmbus_setevent- Trigger an event notification on the specified 12162306a36Sopenharmony_ci * channel. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_civoid vmbus_setevent(struct vmbus_channel *channel) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct hv_monitor_page *monitorpage; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci trace_vmbus_setevent(channel); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* 13062306a36Sopenharmony_ci * For channels marked as in "low latency" mode 13162306a36Sopenharmony_ci * bypass the monitor page mechanism. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (channel->offermsg.monitor_allocated && !channel->low_latency) { 13462306a36Sopenharmony_ci vmbus_send_interrupt(channel->offermsg.child_relid); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Get the child to parent monitor page */ 13762306a36Sopenharmony_ci monitorpage = vmbus_connection.monitor_pages[1]; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci sync_set_bit(channel->monitor_bit, 14062306a36Sopenharmony_ci (unsigned long *)&monitorpage->trigger_group 14162306a36Sopenharmony_ci [channel->monitor_grp].pending); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci } else { 14462306a36Sopenharmony_ci vmbus_set_event(channel); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_setevent); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* vmbus_free_ring - drop mapping of ring buffer */ 15062306a36Sopenharmony_civoid vmbus_free_ring(struct vmbus_channel *channel) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci hv_ringbuffer_cleanup(&channel->outbound); 15362306a36Sopenharmony_ci hv_ringbuffer_cleanup(&channel->inbound); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (channel->ringbuffer_page) { 15662306a36Sopenharmony_ci __free_pages(channel->ringbuffer_page, 15762306a36Sopenharmony_ci get_order(channel->ringbuffer_pagecount 15862306a36Sopenharmony_ci << PAGE_SHIFT)); 15962306a36Sopenharmony_ci channel->ringbuffer_page = NULL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_free_ring); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* vmbus_alloc_ring - allocate and map pages for ring buffer */ 16562306a36Sopenharmony_ciint vmbus_alloc_ring(struct vmbus_channel *newchannel, 16662306a36Sopenharmony_ci u32 send_size, u32 recv_size) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct page *page; 16962306a36Sopenharmony_ci int order; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (send_size % PAGE_SIZE || recv_size % PAGE_SIZE) 17262306a36Sopenharmony_ci return -EINVAL; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Allocate the ring buffer */ 17562306a36Sopenharmony_ci order = get_order(send_size + recv_size); 17662306a36Sopenharmony_ci page = alloc_pages_node(cpu_to_node(newchannel->target_cpu), 17762306a36Sopenharmony_ci GFP_KERNEL|__GFP_ZERO, order); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!page) 18062306a36Sopenharmony_ci page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!page) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci newchannel->ringbuffer_page = page; 18662306a36Sopenharmony_ci newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT; 18762306a36Sopenharmony_ci newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_alloc_ring); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* Used for Hyper-V Socket: a guest client's connect() to the host */ 19462306a36Sopenharmony_ciint vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id, 19562306a36Sopenharmony_ci const guid_t *shv_host_servie_id) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct vmbus_channel_tl_connect_request conn_msg; 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci memset(&conn_msg, 0, sizeof(conn_msg)); 20162306a36Sopenharmony_ci conn_msg.header.msgtype = CHANNELMSG_TL_CONNECT_REQUEST; 20262306a36Sopenharmony_ci conn_msg.guest_endpoint_id = *shv_guest_servie_id; 20362306a36Sopenharmony_ci conn_msg.host_service_id = *shv_host_servie_id; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci trace_vmbus_send_tl_connect_request(&conn_msg, ret); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int send_modifychannel_without_ack(struct vmbus_channel *channel, u32 target_vp) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct vmbus_channel_modifychannel msg; 21662306a36Sopenharmony_ci int ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 21962306a36Sopenharmony_ci msg.header.msgtype = CHANNELMSG_MODIFYCHANNEL; 22062306a36Sopenharmony_ci msg.child_relid = channel->offermsg.child_relid; 22162306a36Sopenharmony_ci msg.target_vp = target_vp; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = vmbus_post_msg(&msg, sizeof(msg), true); 22462306a36Sopenharmony_ci trace_vmbus_send_modifychannel(&msg, ret); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int send_modifychannel_with_ack(struct vmbus_channel *channel, u32 target_vp) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct vmbus_channel_modifychannel *msg; 23262306a36Sopenharmony_ci struct vmbus_channel_msginfo *info; 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci info = kzalloc(sizeof(struct vmbus_channel_msginfo) + 23762306a36Sopenharmony_ci sizeof(struct vmbus_channel_modifychannel), 23862306a36Sopenharmony_ci GFP_KERNEL); 23962306a36Sopenharmony_ci if (!info) 24062306a36Sopenharmony_ci return -ENOMEM; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci init_completion(&info->waitevent); 24362306a36Sopenharmony_ci info->waiting_channel = channel; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci msg = (struct vmbus_channel_modifychannel *)info->msg; 24662306a36Sopenharmony_ci msg->header.msgtype = CHANNELMSG_MODIFYCHANNEL; 24762306a36Sopenharmony_ci msg->child_relid = channel->offermsg.child_relid; 24862306a36Sopenharmony_ci msg->target_vp = target_vp; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 25162306a36Sopenharmony_ci list_add_tail(&info->msglistentry, &vmbus_connection.chn_msg_list); 25262306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = vmbus_post_msg(msg, sizeof(*msg), true); 25562306a36Sopenharmony_ci trace_vmbus_send_modifychannel(msg, ret); 25662306a36Sopenharmony_ci if (ret != 0) { 25762306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 25862306a36Sopenharmony_ci list_del(&info->msglistentry); 25962306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 26062306a36Sopenharmony_ci goto free_info; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * Release channel_mutex; otherwise, vmbus_onoffer_rescind() could block on 26562306a36Sopenharmony_ci * the mutex and be unable to signal the completion. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * See the caller target_cpu_store() for information about the usage of the 26862306a36Sopenharmony_ci * mutex. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 27162306a36Sopenharmony_ci wait_for_completion(&info->waitevent); 27262306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 27562306a36Sopenharmony_ci list_del(&info->msglistentry); 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (info->response.modify_response.status) 27962306a36Sopenharmony_ci ret = -EAGAIN; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cifree_info: 28262306a36Sopenharmony_ci kfree(info); 28362306a36Sopenharmony_ci return ret; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * Set/change the vCPU (@target_vp) the channel (@child_relid) will interrupt. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * CHANNELMSG_MODIFYCHANNEL messages are aynchronous. When VMbus version 5.3 29062306a36Sopenharmony_ci * or later is negotiated, Hyper-V always sends an ACK in response to such a 29162306a36Sopenharmony_ci * message. For VMbus version 5.2 and earlier, it never sends an ACK. With- 29262306a36Sopenharmony_ci * out an ACK, we can not know when the host will stop interrupting the "old" 29362306a36Sopenharmony_ci * vCPU and start interrupting the "new" vCPU for the given channel. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * The CHANNELMSG_MODIFYCHANNEL message type is supported since VMBus version 29662306a36Sopenharmony_ci * VERSION_WIN10_V4_1. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ciint vmbus_send_modifychannel(struct vmbus_channel *channel, u32 target_vp) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (vmbus_proto_version >= VERSION_WIN10_V5_3) 30162306a36Sopenharmony_ci return send_modifychannel_with_ack(channel, target_vp); 30262306a36Sopenharmony_ci return send_modifychannel_without_ack(channel, target_vp); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_send_modifychannel); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * create_gpadl_header - Creates a gpadl for the specified buffer 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic int create_gpadl_header(enum hv_gpadl_type type, void *kbuffer, 31062306a36Sopenharmony_ci u32 size, u32 send_offset, 31162306a36Sopenharmony_ci struct vmbus_channel_msginfo **msginfo) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci int i; 31462306a36Sopenharmony_ci int pagecount; 31562306a36Sopenharmony_ci struct vmbus_channel_gpadl_header *gpadl_header; 31662306a36Sopenharmony_ci struct vmbus_channel_gpadl_body *gpadl_body; 31762306a36Sopenharmony_ci struct vmbus_channel_msginfo *msgheader; 31862306a36Sopenharmony_ci struct vmbus_channel_msginfo *msgbody = NULL; 31962306a36Sopenharmony_ci u32 msgsize; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci pagecount = hv_gpadl_size(type, size) >> HV_HYP_PAGE_SHIFT; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* do we need a gpadl body msg */ 32662306a36Sopenharmony_ci pfnsize = MAX_SIZE_CHANNEL_MESSAGE - 32762306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_header) - 32862306a36Sopenharmony_ci sizeof(struct gpa_range); 32962306a36Sopenharmony_ci pfncount = pfnsize / sizeof(u64); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (pagecount > pfncount) { 33262306a36Sopenharmony_ci /* we need a gpadl body */ 33362306a36Sopenharmony_ci /* fill in the header */ 33462306a36Sopenharmony_ci msgsize = sizeof(struct vmbus_channel_msginfo) + 33562306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_header) + 33662306a36Sopenharmony_ci sizeof(struct gpa_range) + pfncount * sizeof(u64); 33762306a36Sopenharmony_ci msgheader = kzalloc(msgsize, GFP_KERNEL); 33862306a36Sopenharmony_ci if (!msgheader) 33962306a36Sopenharmony_ci goto nomem; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci INIT_LIST_HEAD(&msgheader->submsglist); 34262306a36Sopenharmony_ci msgheader->msgsize = msgsize; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci gpadl_header = (struct vmbus_channel_gpadl_header *) 34562306a36Sopenharmony_ci msgheader->msg; 34662306a36Sopenharmony_ci gpadl_header->rangecount = 1; 34762306a36Sopenharmony_ci gpadl_header->range_buflen = sizeof(struct gpa_range) + 34862306a36Sopenharmony_ci pagecount * sizeof(u64); 34962306a36Sopenharmony_ci gpadl_header->range[0].byte_offset = 0; 35062306a36Sopenharmony_ci gpadl_header->range[0].byte_count = hv_gpadl_size(type, size); 35162306a36Sopenharmony_ci for (i = 0; i < pfncount; i++) 35262306a36Sopenharmony_ci gpadl_header->range[0].pfn_array[i] = hv_gpadl_hvpfn( 35362306a36Sopenharmony_ci type, kbuffer, size, send_offset, i); 35462306a36Sopenharmony_ci *msginfo = msgheader; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pfnsum = pfncount; 35762306a36Sopenharmony_ci pfnleft = pagecount - pfncount; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* how many pfns can we fit */ 36062306a36Sopenharmony_ci pfnsize = MAX_SIZE_CHANNEL_MESSAGE - 36162306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_body); 36262306a36Sopenharmony_ci pfncount = pfnsize / sizeof(u64); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* fill in the body */ 36562306a36Sopenharmony_ci while (pfnleft) { 36662306a36Sopenharmony_ci if (pfnleft > pfncount) 36762306a36Sopenharmony_ci pfncurr = pfncount; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci pfncurr = pfnleft; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci msgsize = sizeof(struct vmbus_channel_msginfo) + 37262306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_body) + 37362306a36Sopenharmony_ci pfncurr * sizeof(u64); 37462306a36Sopenharmony_ci msgbody = kzalloc(msgsize, GFP_KERNEL); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!msgbody) { 37762306a36Sopenharmony_ci struct vmbus_channel_msginfo *pos = NULL; 37862306a36Sopenharmony_ci struct vmbus_channel_msginfo *tmp = NULL; 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * Free up all the allocated messages. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci list_for_each_entry_safe(pos, tmp, 38362306a36Sopenharmony_ci &msgheader->submsglist, 38462306a36Sopenharmony_ci msglistentry) { 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci list_del(&pos->msglistentry); 38762306a36Sopenharmony_ci kfree(pos); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci goto nomem; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci msgbody->msgsize = msgsize; 39462306a36Sopenharmony_ci gpadl_body = 39562306a36Sopenharmony_ci (struct vmbus_channel_gpadl_body *)msgbody->msg; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Gpadl is u32 and we are using a pointer which could 39962306a36Sopenharmony_ci * be 64-bit 40062306a36Sopenharmony_ci * This is governed by the guest/host protocol and 40162306a36Sopenharmony_ci * so the hypervisor guarantees that this is ok. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci for (i = 0; i < pfncurr; i++) 40462306a36Sopenharmony_ci gpadl_body->pfn[i] = hv_gpadl_hvpfn(type, 40562306a36Sopenharmony_ci kbuffer, size, send_offset, pfnsum + i); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* add to msg header */ 40862306a36Sopenharmony_ci list_add_tail(&msgbody->msglistentry, 40962306a36Sopenharmony_ci &msgheader->submsglist); 41062306a36Sopenharmony_ci pfnsum += pfncurr; 41162306a36Sopenharmony_ci pfnleft -= pfncurr; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci /* everything fits in a header */ 41562306a36Sopenharmony_ci msgsize = sizeof(struct vmbus_channel_msginfo) + 41662306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_header) + 41762306a36Sopenharmony_ci sizeof(struct gpa_range) + pagecount * sizeof(u64); 41862306a36Sopenharmony_ci msgheader = kzalloc(msgsize, GFP_KERNEL); 41962306a36Sopenharmony_ci if (msgheader == NULL) 42062306a36Sopenharmony_ci goto nomem; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci INIT_LIST_HEAD(&msgheader->submsglist); 42362306a36Sopenharmony_ci msgheader->msgsize = msgsize; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci gpadl_header = (struct vmbus_channel_gpadl_header *) 42662306a36Sopenharmony_ci msgheader->msg; 42762306a36Sopenharmony_ci gpadl_header->rangecount = 1; 42862306a36Sopenharmony_ci gpadl_header->range_buflen = sizeof(struct gpa_range) + 42962306a36Sopenharmony_ci pagecount * sizeof(u64); 43062306a36Sopenharmony_ci gpadl_header->range[0].byte_offset = 0; 43162306a36Sopenharmony_ci gpadl_header->range[0].byte_count = hv_gpadl_size(type, size); 43262306a36Sopenharmony_ci for (i = 0; i < pagecount; i++) 43362306a36Sopenharmony_ci gpadl_header->range[0].pfn_array[i] = hv_gpadl_hvpfn( 43462306a36Sopenharmony_ci type, kbuffer, size, send_offset, i); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci *msginfo = msgheader; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_cinomem: 44162306a36Sopenharmony_ci kfree(msgheader); 44262306a36Sopenharmony_ci kfree(msgbody); 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * __vmbus_establish_gpadl - Establish a GPADL for a buffer or ringbuffer 44862306a36Sopenharmony_ci * 44962306a36Sopenharmony_ci * @channel: a channel 45062306a36Sopenharmony_ci * @type: the type of the corresponding GPADL, only meaningful for the guest. 45162306a36Sopenharmony_ci * @kbuffer: from kmalloc or vmalloc 45262306a36Sopenharmony_ci * @size: page-size multiple 45362306a36Sopenharmony_ci * @send_offset: the offset (in bytes) where the send ring buffer starts, 45462306a36Sopenharmony_ci * should be 0 for BUFFER type gpadl 45562306a36Sopenharmony_ci * @gpadl_handle: some funky thing 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic int __vmbus_establish_gpadl(struct vmbus_channel *channel, 45862306a36Sopenharmony_ci enum hv_gpadl_type type, void *kbuffer, 45962306a36Sopenharmony_ci u32 size, u32 send_offset, 46062306a36Sopenharmony_ci struct vmbus_gpadl *gpadl) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct vmbus_channel_gpadl_header *gpadlmsg; 46362306a36Sopenharmony_ci struct vmbus_channel_gpadl_body *gpadl_body; 46462306a36Sopenharmony_ci struct vmbus_channel_msginfo *msginfo = NULL; 46562306a36Sopenharmony_ci struct vmbus_channel_msginfo *submsginfo, *tmp; 46662306a36Sopenharmony_ci struct list_head *curr; 46762306a36Sopenharmony_ci u32 next_gpadl_handle; 46862306a36Sopenharmony_ci unsigned long flags; 46962306a36Sopenharmony_ci int ret = 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci next_gpadl_handle = 47262306a36Sopenharmony_ci (atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = create_gpadl_header(type, kbuffer, size, send_offset, &msginfo); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = set_memory_decrypted((unsigned long)kbuffer, 47962306a36Sopenharmony_ci PFN_UP(size)); 48062306a36Sopenharmony_ci if (ret) { 48162306a36Sopenharmony_ci dev_warn(&channel->device_obj->device, 48262306a36Sopenharmony_ci "Failed to set host visibility for new GPADL %d.\n", 48362306a36Sopenharmony_ci ret); 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci init_completion(&msginfo->waitevent); 48862306a36Sopenharmony_ci msginfo->waiting_channel = channel; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; 49162306a36Sopenharmony_ci gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; 49262306a36Sopenharmony_ci gpadlmsg->child_relid = channel->offermsg.child_relid; 49362306a36Sopenharmony_ci gpadlmsg->gpadl = next_gpadl_handle; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 49762306a36Sopenharmony_ci list_add_tail(&msginfo->msglistentry, 49862306a36Sopenharmony_ci &vmbus_connection.chn_msg_list); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (channel->rescind) { 50362306a36Sopenharmony_ci ret = -ENODEV; 50462306a36Sopenharmony_ci goto cleanup; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - 50862306a36Sopenharmony_ci sizeof(*msginfo), true); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci trace_vmbus_establish_gpadl_header(gpadlmsg, ret); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (ret != 0) 51362306a36Sopenharmony_ci goto cleanup; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci list_for_each(curr, &msginfo->submsglist) { 51662306a36Sopenharmony_ci submsginfo = (struct vmbus_channel_msginfo *)curr; 51762306a36Sopenharmony_ci gpadl_body = 51862306a36Sopenharmony_ci (struct vmbus_channel_gpadl_body *)submsginfo->msg; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci gpadl_body->header.msgtype = 52162306a36Sopenharmony_ci CHANNELMSG_GPADL_BODY; 52262306a36Sopenharmony_ci gpadl_body->gpadl = next_gpadl_handle; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ret = vmbus_post_msg(gpadl_body, 52562306a36Sopenharmony_ci submsginfo->msgsize - sizeof(*submsginfo), 52662306a36Sopenharmony_ci true); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci trace_vmbus_establish_gpadl_body(gpadl_body, ret); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (ret != 0) 53162306a36Sopenharmony_ci goto cleanup; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci wait_for_completion(&msginfo->waitevent); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (msginfo->response.gpadl_created.creation_status != 0) { 53762306a36Sopenharmony_ci pr_err("Failed to establish GPADL: err = 0x%x\n", 53862306a36Sopenharmony_ci msginfo->response.gpadl_created.creation_status); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ret = -EDQUOT; 54162306a36Sopenharmony_ci goto cleanup; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (channel->rescind) { 54562306a36Sopenharmony_ci ret = -ENODEV; 54662306a36Sopenharmony_ci goto cleanup; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* At this point, we received the gpadl created msg */ 55062306a36Sopenharmony_ci gpadl->gpadl_handle = gpadlmsg->gpadl; 55162306a36Sopenharmony_ci gpadl->buffer = kbuffer; 55262306a36Sopenharmony_ci gpadl->size = size; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cicleanup: 55662306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 55762306a36Sopenharmony_ci list_del(&msginfo->msglistentry); 55862306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 55962306a36Sopenharmony_ci list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist, 56062306a36Sopenharmony_ci msglistentry) { 56162306a36Sopenharmony_ci kfree(submsginfo); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci kfree(msginfo); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (ret) 56762306a36Sopenharmony_ci set_memory_encrypted((unsigned long)kbuffer, 56862306a36Sopenharmony_ci PFN_UP(size)); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* 57462306a36Sopenharmony_ci * vmbus_establish_gpadl - Establish a GPADL for the specified buffer 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * @channel: a channel 57762306a36Sopenharmony_ci * @kbuffer: from kmalloc or vmalloc 57862306a36Sopenharmony_ci * @size: page-size multiple 57962306a36Sopenharmony_ci * @gpadl_handle: some funky thing 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ciint vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, 58262306a36Sopenharmony_ci u32 size, struct vmbus_gpadl *gpadl) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci return __vmbus_establish_gpadl(channel, HV_GPADL_BUFFER, kbuffer, size, 58562306a36Sopenharmony_ci 0U, gpadl); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_establish_gpadl); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/** 59062306a36Sopenharmony_ci * request_arr_init - Allocates memory for the requestor array. Each slot 59162306a36Sopenharmony_ci * keeps track of the next available slot in the array. Initially, each 59262306a36Sopenharmony_ci * slot points to the next one (as in a Linked List). The last slot 59362306a36Sopenharmony_ci * does not point to anything, so its value is U64_MAX by default. 59462306a36Sopenharmony_ci * @size The size of the array 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_cistatic u64 *request_arr_init(u32 size) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci int i; 59962306a36Sopenharmony_ci u64 *req_arr; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci req_arr = kcalloc(size, sizeof(u64), GFP_KERNEL); 60262306a36Sopenharmony_ci if (!req_arr) 60362306a36Sopenharmony_ci return NULL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci for (i = 0; i < size - 1; i++) 60662306a36Sopenharmony_ci req_arr[i] = i + 1; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Last slot (no more available slots) */ 60962306a36Sopenharmony_ci req_arr[i] = U64_MAX; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return req_arr; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* 61562306a36Sopenharmony_ci * vmbus_alloc_requestor - Initializes @rqstor's fields. 61662306a36Sopenharmony_ci * Index 0 is the first free slot 61762306a36Sopenharmony_ci * @size: Size of the requestor array 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_cistatic int vmbus_alloc_requestor(struct vmbus_requestor *rqstor, u32 size) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u64 *rqst_arr; 62262306a36Sopenharmony_ci unsigned long *bitmap; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci rqst_arr = request_arr_init(size); 62562306a36Sopenharmony_ci if (!rqst_arr) 62662306a36Sopenharmony_ci return -ENOMEM; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci bitmap = bitmap_zalloc(size, GFP_KERNEL); 62962306a36Sopenharmony_ci if (!bitmap) { 63062306a36Sopenharmony_ci kfree(rqst_arr); 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci rqstor->req_arr = rqst_arr; 63562306a36Sopenharmony_ci rqstor->req_bitmap = bitmap; 63662306a36Sopenharmony_ci rqstor->size = size; 63762306a36Sopenharmony_ci rqstor->next_request_id = 0; 63862306a36Sopenharmony_ci spin_lock_init(&rqstor->req_lock); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * vmbus_free_requestor - Frees memory allocated for @rqstor 64562306a36Sopenharmony_ci * @rqstor: Pointer to the requestor struct 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistatic void vmbus_free_requestor(struct vmbus_requestor *rqstor) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci kfree(rqstor->req_arr); 65062306a36Sopenharmony_ci bitmap_free(rqstor->req_bitmap); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int __vmbus_open(struct vmbus_channel *newchannel, 65462306a36Sopenharmony_ci void *userdata, u32 userdatalen, 65562306a36Sopenharmony_ci void (*onchannelcallback)(void *context), void *context) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct vmbus_channel_open_channel *open_msg; 65862306a36Sopenharmony_ci struct vmbus_channel_msginfo *open_info = NULL; 65962306a36Sopenharmony_ci struct page *page = newchannel->ringbuffer_page; 66062306a36Sopenharmony_ci u32 send_pages, recv_pages; 66162306a36Sopenharmony_ci unsigned long flags; 66262306a36Sopenharmony_ci int err; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (userdatalen > MAX_USER_DEFINED_BYTES) 66562306a36Sopenharmony_ci return -EINVAL; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci send_pages = newchannel->ringbuffer_send_offset; 66862306a36Sopenharmony_ci recv_pages = newchannel->ringbuffer_pagecount - send_pages; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (newchannel->state != CHANNEL_OPEN_STATE) 67162306a36Sopenharmony_ci return -EINVAL; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* Create and init requestor */ 67462306a36Sopenharmony_ci if (newchannel->rqstor_size) { 67562306a36Sopenharmony_ci if (vmbus_alloc_requestor(&newchannel->requestor, newchannel->rqstor_size)) 67662306a36Sopenharmony_ci return -ENOMEM; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci newchannel->state = CHANNEL_OPENING_STATE; 68062306a36Sopenharmony_ci newchannel->onchannel_callback = onchannelcallback; 68162306a36Sopenharmony_ci newchannel->channel_callback_context = context; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!newchannel->max_pkt_size) 68462306a36Sopenharmony_ci newchannel->max_pkt_size = VMBUS_DEFAULT_MAX_PKT_SIZE; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Establish the gpadl for the ring buffer */ 68762306a36Sopenharmony_ci newchannel->ringbuffer_gpadlhandle.gpadl_handle = 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING, 69062306a36Sopenharmony_ci page_address(newchannel->ringbuffer_page), 69162306a36Sopenharmony_ci (send_pages + recv_pages) << PAGE_SHIFT, 69262306a36Sopenharmony_ci newchannel->ringbuffer_send_offset << PAGE_SHIFT, 69362306a36Sopenharmony_ci &newchannel->ringbuffer_gpadlhandle); 69462306a36Sopenharmony_ci if (err) 69562306a36Sopenharmony_ci goto error_clean_ring; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci err = hv_ringbuffer_init(&newchannel->outbound, 69862306a36Sopenharmony_ci page, send_pages, 0); 69962306a36Sopenharmony_ci if (err) 70062306a36Sopenharmony_ci goto error_free_gpadl; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci err = hv_ringbuffer_init(&newchannel->inbound, &page[send_pages], 70362306a36Sopenharmony_ci recv_pages, newchannel->max_pkt_size); 70462306a36Sopenharmony_ci if (err) 70562306a36Sopenharmony_ci goto error_free_gpadl; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Create and init the channel open message */ 70862306a36Sopenharmony_ci open_info = kzalloc(sizeof(*open_info) + 70962306a36Sopenharmony_ci sizeof(struct vmbus_channel_open_channel), 71062306a36Sopenharmony_ci GFP_KERNEL); 71162306a36Sopenharmony_ci if (!open_info) { 71262306a36Sopenharmony_ci err = -ENOMEM; 71362306a36Sopenharmony_ci goto error_free_gpadl; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci init_completion(&open_info->waitevent); 71762306a36Sopenharmony_ci open_info->waiting_channel = newchannel; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci open_msg = (struct vmbus_channel_open_channel *)open_info->msg; 72062306a36Sopenharmony_ci open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; 72162306a36Sopenharmony_ci open_msg->openid = newchannel->offermsg.child_relid; 72262306a36Sopenharmony_ci open_msg->child_relid = newchannel->offermsg.child_relid; 72362306a36Sopenharmony_ci open_msg->ringbuffer_gpadlhandle 72462306a36Sopenharmony_ci = newchannel->ringbuffer_gpadlhandle.gpadl_handle; 72562306a36Sopenharmony_ci /* 72662306a36Sopenharmony_ci * The unit of ->downstream_ringbuffer_pageoffset is HV_HYP_PAGE and 72762306a36Sopenharmony_ci * the unit of ->ringbuffer_send_offset (i.e. send_pages) is PAGE, so 72862306a36Sopenharmony_ci * here we calculate it into HV_HYP_PAGE. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_ci open_msg->downstream_ringbuffer_pageoffset = 73162306a36Sopenharmony_ci hv_ring_gpadl_send_hvpgoffset(send_pages << PAGE_SHIFT); 73262306a36Sopenharmony_ci open_msg->target_vp = hv_cpu_number_to_vp_number(newchannel->target_cpu); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (userdatalen) 73562306a36Sopenharmony_ci memcpy(open_msg->userdata, userdata, userdatalen); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 73862306a36Sopenharmony_ci list_add_tail(&open_info->msglistentry, 73962306a36Sopenharmony_ci &vmbus_connection.chn_msg_list); 74062306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (newchannel->rescind) { 74362306a36Sopenharmony_ci err = -ENODEV; 74462306a36Sopenharmony_ci goto error_clean_msglist; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci err = vmbus_post_msg(open_msg, 74862306a36Sopenharmony_ci sizeof(struct vmbus_channel_open_channel), true); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci trace_vmbus_open(open_msg, err); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (err != 0) 75362306a36Sopenharmony_ci goto error_clean_msglist; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci wait_for_completion(&open_info->waitevent); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 75862306a36Sopenharmony_ci list_del(&open_info->msglistentry); 75962306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (newchannel->rescind) { 76262306a36Sopenharmony_ci err = -ENODEV; 76362306a36Sopenharmony_ci goto error_free_info; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (open_info->response.open_result.status) { 76762306a36Sopenharmony_ci err = -EAGAIN; 76862306a36Sopenharmony_ci goto error_free_info; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci newchannel->state = CHANNEL_OPENED_STATE; 77262306a36Sopenharmony_ci kfree(open_info); 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cierror_clean_msglist: 77662306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 77762306a36Sopenharmony_ci list_del(&open_info->msglistentry); 77862306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 77962306a36Sopenharmony_cierror_free_info: 78062306a36Sopenharmony_ci kfree(open_info); 78162306a36Sopenharmony_cierror_free_gpadl: 78262306a36Sopenharmony_ci vmbus_teardown_gpadl(newchannel, &newchannel->ringbuffer_gpadlhandle); 78362306a36Sopenharmony_cierror_clean_ring: 78462306a36Sopenharmony_ci hv_ringbuffer_cleanup(&newchannel->outbound); 78562306a36Sopenharmony_ci hv_ringbuffer_cleanup(&newchannel->inbound); 78662306a36Sopenharmony_ci vmbus_free_requestor(&newchannel->requestor); 78762306a36Sopenharmony_ci newchannel->state = CHANNEL_OPEN_STATE; 78862306a36Sopenharmony_ci return err; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* 79262306a36Sopenharmony_ci * vmbus_connect_ring - Open the channel but reuse ring buffer 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ciint vmbus_connect_ring(struct vmbus_channel *newchannel, 79562306a36Sopenharmony_ci void (*onchannelcallback)(void *context), void *context) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci return __vmbus_open(newchannel, NULL, 0, onchannelcallback, context); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_connect_ring); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* 80262306a36Sopenharmony_ci * vmbus_open - Open the specified channel. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ciint vmbus_open(struct vmbus_channel *newchannel, 80562306a36Sopenharmony_ci u32 send_ringbuffer_size, u32 recv_ringbuffer_size, 80662306a36Sopenharmony_ci void *userdata, u32 userdatalen, 80762306a36Sopenharmony_ci void (*onchannelcallback)(void *context), void *context) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci int err; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci err = vmbus_alloc_ring(newchannel, send_ringbuffer_size, 81262306a36Sopenharmony_ci recv_ringbuffer_size); 81362306a36Sopenharmony_ci if (err) 81462306a36Sopenharmony_ci return err; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci err = __vmbus_open(newchannel, userdata, userdatalen, 81762306a36Sopenharmony_ci onchannelcallback, context); 81862306a36Sopenharmony_ci if (err) 81962306a36Sopenharmony_ci vmbus_free_ring(newchannel); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return err; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_open); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* 82662306a36Sopenharmony_ci * vmbus_teardown_gpadl -Teardown the specified GPADL handle 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_ciint vmbus_teardown_gpadl(struct vmbus_channel *channel, struct vmbus_gpadl *gpadl) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct vmbus_channel_gpadl_teardown *msg; 83162306a36Sopenharmony_ci struct vmbus_channel_msginfo *info; 83262306a36Sopenharmony_ci unsigned long flags; 83362306a36Sopenharmony_ci int ret; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci info = kzalloc(sizeof(*info) + 83662306a36Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); 83762306a36Sopenharmony_ci if (!info) 83862306a36Sopenharmony_ci return -ENOMEM; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci init_completion(&info->waitevent); 84162306a36Sopenharmony_ci info->waiting_channel = channel; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci msg = (struct vmbus_channel_gpadl_teardown *)info->msg; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; 84662306a36Sopenharmony_ci msg->child_relid = channel->offermsg.child_relid; 84762306a36Sopenharmony_ci msg->gpadl = gpadl->gpadl_handle; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 85062306a36Sopenharmony_ci list_add_tail(&info->msglistentry, 85162306a36Sopenharmony_ci &vmbus_connection.chn_msg_list); 85262306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (channel->rescind) 85562306a36Sopenharmony_ci goto post_msg_err; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown), 85862306a36Sopenharmony_ci true); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci trace_vmbus_teardown_gpadl(msg, ret); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (ret) 86362306a36Sopenharmony_ci goto post_msg_err; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci wait_for_completion(&info->waitevent); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci gpadl->gpadl_handle = 0; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cipost_msg_err: 87062306a36Sopenharmony_ci /* 87162306a36Sopenharmony_ci * If the channel has been rescinded; 87262306a36Sopenharmony_ci * we will be awakened by the rescind 87362306a36Sopenharmony_ci * handler; set the error code to zero so we don't leak memory. 87462306a36Sopenharmony_ci */ 87562306a36Sopenharmony_ci if (channel->rescind) 87662306a36Sopenharmony_ci ret = 0; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 87962306a36Sopenharmony_ci list_del(&info->msglistentry); 88062306a36Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci kfree(info); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci ret = set_memory_encrypted((unsigned long)gpadl->buffer, 88562306a36Sopenharmony_ci PFN_UP(gpadl->size)); 88662306a36Sopenharmony_ci if (ret) 88762306a36Sopenharmony_ci pr_warn("Fail to set mem host visibility in GPADL teardown %d.\n", ret); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return ret; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_civoid vmbus_reset_channel_cb(struct vmbus_channel *channel) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci unsigned long flags; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* 89862306a36Sopenharmony_ci * vmbus_on_event(), running in the per-channel tasklet, can race 89962306a36Sopenharmony_ci * with vmbus_close_internal() in the case of SMP guest, e.g., when 90062306a36Sopenharmony_ci * the former is accessing channel->inbound.ring_buffer, the latter 90162306a36Sopenharmony_ci * could be freeing the ring_buffer pages, so here we must stop it 90262306a36Sopenharmony_ci * first. 90362306a36Sopenharmony_ci * 90462306a36Sopenharmony_ci * vmbus_chan_sched() might call the netvsc driver callback function 90562306a36Sopenharmony_ci * that ends up scheduling NAPI work that accesses the ring buffer. 90662306a36Sopenharmony_ci * At this point, we have to ensure that any such work is completed 90762306a36Sopenharmony_ci * and that the channel ring buffer is no longer being accessed, cf. 90862306a36Sopenharmony_ci * the calls to napi_disable() in netvsc_device_remove(). 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_ci tasklet_disable(&channel->callback_event); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* See the inline comments in vmbus_chan_sched(). */ 91362306a36Sopenharmony_ci spin_lock_irqsave(&channel->sched_lock, flags); 91462306a36Sopenharmony_ci channel->onchannel_callback = NULL; 91562306a36Sopenharmony_ci spin_unlock_irqrestore(&channel->sched_lock, flags); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci channel->sc_creation_callback = NULL; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* Re-enable tasklet for use on re-open */ 92062306a36Sopenharmony_ci tasklet_enable(&channel->callback_event); 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic int vmbus_close_internal(struct vmbus_channel *channel) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct vmbus_channel_close_channel *msg; 92662306a36Sopenharmony_ci int ret; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci vmbus_reset_channel_cb(channel); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * In case a device driver's probe() fails (e.g., 93262306a36Sopenharmony_ci * util_probe() -> vmbus_open() returns -ENOMEM) and the device is 93362306a36Sopenharmony_ci * rescinded later (e.g., we dynamically disable an Integrated Service 93462306a36Sopenharmony_ci * in Hyper-V Manager), the driver's remove() invokes vmbus_close(): 93562306a36Sopenharmony_ci * here we should skip most of the below cleanup work. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ci if (channel->state != CHANNEL_OPENED_STATE) 93862306a36Sopenharmony_ci return -EINVAL; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci channel->state = CHANNEL_OPEN_STATE; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Send a closing message */ 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci msg = &channel->close_msg.msg; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; 94762306a36Sopenharmony_ci msg->child_relid = channel->offermsg.child_relid; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel), 95062306a36Sopenharmony_ci true); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci trace_vmbus_close_internal(msg, ret); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (ret) { 95562306a36Sopenharmony_ci pr_err("Close failed: close post msg return is %d\n", ret); 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * If we failed to post the close msg, 95862306a36Sopenharmony_ci * it is perhaps better to leak memory. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Tear down the gpadl for the channel's ring buffer */ 96362306a36Sopenharmony_ci else if (channel->ringbuffer_gpadlhandle.gpadl_handle) { 96462306a36Sopenharmony_ci ret = vmbus_teardown_gpadl(channel, &channel->ringbuffer_gpadlhandle); 96562306a36Sopenharmony_ci if (ret) { 96662306a36Sopenharmony_ci pr_err("Close failed: teardown gpadl return %d\n", ret); 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * If we failed to teardown gpadl, 96962306a36Sopenharmony_ci * it is perhaps better to leak memory. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (!ret) 97562306a36Sopenharmony_ci vmbus_free_requestor(&channel->requestor); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return ret; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/* disconnect ring - close all channels */ 98162306a36Sopenharmony_ciint vmbus_disconnect_ring(struct vmbus_channel *channel) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct vmbus_channel *cur_channel, *tmp; 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (channel->primary_channel != NULL) 98762306a36Sopenharmony_ci return -EINVAL; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci list_for_each_entry_safe(cur_channel, tmp, &channel->sc_list, sc_list) { 99062306a36Sopenharmony_ci if (cur_channel->rescind) 99162306a36Sopenharmony_ci wait_for_completion(&cur_channel->rescind_event); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 99462306a36Sopenharmony_ci if (vmbus_close_internal(cur_channel) == 0) { 99562306a36Sopenharmony_ci vmbus_free_ring(cur_channel); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (cur_channel->rescind) 99862306a36Sopenharmony_ci hv_process_channel_removal(cur_channel); 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* 100462306a36Sopenharmony_ci * Now close the primary. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 100762306a36Sopenharmony_ci ret = vmbus_close_internal(channel); 100862306a36Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return ret; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_disconnect_ring); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci/* 101562306a36Sopenharmony_ci * vmbus_close - Close the specified channel 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_civoid vmbus_close(struct vmbus_channel *channel) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci if (vmbus_disconnect_ring(channel) == 0) 102062306a36Sopenharmony_ci vmbus_free_ring(channel); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_close); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci/** 102562306a36Sopenharmony_ci * vmbus_sendpacket_getid() - Send the specified buffer on the given channel 102662306a36Sopenharmony_ci * @channel: Pointer to vmbus_channel structure 102762306a36Sopenharmony_ci * @buffer: Pointer to the buffer you want to send the data from. 102862306a36Sopenharmony_ci * @bufferlen: Maximum size of what the buffer holds. 102962306a36Sopenharmony_ci * @requestid: Identifier of the request 103062306a36Sopenharmony_ci * @trans_id: Identifier of the transaction associated to this request, if 103162306a36Sopenharmony_ci * the send is successful; undefined, otherwise. 103262306a36Sopenharmony_ci * @type: Type of packet that is being sent e.g. negotiate, time 103362306a36Sopenharmony_ci * packet etc. 103462306a36Sopenharmony_ci * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * Sends data in @buffer directly to Hyper-V via the vmbus. 103762306a36Sopenharmony_ci * This will send the data unparsed to Hyper-V. 103862306a36Sopenharmony_ci * 103962306a36Sopenharmony_ci * Mainly used by Hyper-V drivers. 104062306a36Sopenharmony_ci */ 104162306a36Sopenharmony_ciint vmbus_sendpacket_getid(struct vmbus_channel *channel, void *buffer, 104262306a36Sopenharmony_ci u32 bufferlen, u64 requestid, u64 *trans_id, 104362306a36Sopenharmony_ci enum vmbus_packet_type type, u32 flags) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci struct vmpacket_descriptor desc; 104662306a36Sopenharmony_ci u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; 104762306a36Sopenharmony_ci u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); 104862306a36Sopenharmony_ci struct kvec bufferlist[3]; 104962306a36Sopenharmony_ci u64 aligned_data = 0; 105062306a36Sopenharmony_ci int num_vecs = ((bufferlen != 0) ? 3 : 1); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* Setup the descriptor */ 105462306a36Sopenharmony_ci desc.type = type; /* VmbusPacketTypeDataInBand; */ 105562306a36Sopenharmony_ci desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ 105662306a36Sopenharmony_ci /* in 8-bytes granularity */ 105762306a36Sopenharmony_ci desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; 105862306a36Sopenharmony_ci desc.len8 = (u16)(packetlen_aligned >> 3); 105962306a36Sopenharmony_ci desc.trans_id = VMBUS_RQST_ERROR; /* will be updated in hv_ringbuffer_write() */ 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci bufferlist[0].iov_base = &desc; 106262306a36Sopenharmony_ci bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor); 106362306a36Sopenharmony_ci bufferlist[1].iov_base = buffer; 106462306a36Sopenharmony_ci bufferlist[1].iov_len = bufferlen; 106562306a36Sopenharmony_ci bufferlist[2].iov_base = &aligned_data; 106662306a36Sopenharmony_ci bufferlist[2].iov_len = (packetlen_aligned - packetlen); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci return hv_ringbuffer_write(channel, bufferlist, num_vecs, requestid, trans_id); 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ciEXPORT_SYMBOL(vmbus_sendpacket_getid); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci/** 107362306a36Sopenharmony_ci * vmbus_sendpacket() - Send the specified buffer on the given channel 107462306a36Sopenharmony_ci * @channel: Pointer to vmbus_channel structure 107562306a36Sopenharmony_ci * @buffer: Pointer to the buffer you want to send the data from. 107662306a36Sopenharmony_ci * @bufferlen: Maximum size of what the buffer holds. 107762306a36Sopenharmony_ci * @requestid: Identifier of the request 107862306a36Sopenharmony_ci * @type: Type of packet that is being sent e.g. negotiate, time 107962306a36Sopenharmony_ci * packet etc. 108062306a36Sopenharmony_ci * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED 108162306a36Sopenharmony_ci * 108262306a36Sopenharmony_ci * Sends data in @buffer directly to Hyper-V via the vmbus. 108362306a36Sopenharmony_ci * This will send the data unparsed to Hyper-V. 108462306a36Sopenharmony_ci * 108562306a36Sopenharmony_ci * Mainly used by Hyper-V drivers. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ciint vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, 108862306a36Sopenharmony_ci u32 bufferlen, u64 requestid, 108962306a36Sopenharmony_ci enum vmbus_packet_type type, u32 flags) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci return vmbus_sendpacket_getid(channel, buffer, bufferlen, 109262306a36Sopenharmony_ci requestid, NULL, type, flags); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ciEXPORT_SYMBOL(vmbus_sendpacket); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci/* 109762306a36Sopenharmony_ci * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer 109862306a36Sopenharmony_ci * packets using a GPADL Direct packet type. This interface allows you 109962306a36Sopenharmony_ci * to control notifying the host. This will be useful for sending 110062306a36Sopenharmony_ci * batched data. Also the sender can control the send flags 110162306a36Sopenharmony_ci * explicitly. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ciint vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, 110462306a36Sopenharmony_ci struct hv_page_buffer pagebuffers[], 110562306a36Sopenharmony_ci u32 pagecount, void *buffer, u32 bufferlen, 110662306a36Sopenharmony_ci u64 requestid) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci int i; 110962306a36Sopenharmony_ci struct vmbus_channel_packet_page_buffer desc; 111062306a36Sopenharmony_ci u32 descsize; 111162306a36Sopenharmony_ci u32 packetlen; 111262306a36Sopenharmony_ci u32 packetlen_aligned; 111362306a36Sopenharmony_ci struct kvec bufferlist[3]; 111462306a36Sopenharmony_ci u64 aligned_data = 0; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if (pagecount > MAX_PAGE_BUFFER_COUNT) 111762306a36Sopenharmony_ci return -EINVAL; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* 112062306a36Sopenharmony_ci * Adjust the size down since vmbus_channel_packet_page_buffer is the 112162306a36Sopenharmony_ci * largest size we support 112262306a36Sopenharmony_ci */ 112362306a36Sopenharmony_ci descsize = sizeof(struct vmbus_channel_packet_page_buffer) - 112462306a36Sopenharmony_ci ((MAX_PAGE_BUFFER_COUNT - pagecount) * 112562306a36Sopenharmony_ci sizeof(struct hv_page_buffer)); 112662306a36Sopenharmony_ci packetlen = descsize + bufferlen; 112762306a36Sopenharmony_ci packetlen_aligned = ALIGN(packetlen, sizeof(u64)); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Setup the descriptor */ 113062306a36Sopenharmony_ci desc.type = VM_PKT_DATA_USING_GPA_DIRECT; 113162306a36Sopenharmony_ci desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; 113262306a36Sopenharmony_ci desc.dataoffset8 = descsize >> 3; /* in 8-bytes granularity */ 113362306a36Sopenharmony_ci desc.length8 = (u16)(packetlen_aligned >> 3); 113462306a36Sopenharmony_ci desc.transactionid = VMBUS_RQST_ERROR; /* will be updated in hv_ringbuffer_write() */ 113562306a36Sopenharmony_ci desc.reserved = 0; 113662306a36Sopenharmony_ci desc.rangecount = pagecount; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci for (i = 0; i < pagecount; i++) { 113962306a36Sopenharmony_ci desc.range[i].len = pagebuffers[i].len; 114062306a36Sopenharmony_ci desc.range[i].offset = pagebuffers[i].offset; 114162306a36Sopenharmony_ci desc.range[i].pfn = pagebuffers[i].pfn; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci bufferlist[0].iov_base = &desc; 114562306a36Sopenharmony_ci bufferlist[0].iov_len = descsize; 114662306a36Sopenharmony_ci bufferlist[1].iov_base = buffer; 114762306a36Sopenharmony_ci bufferlist[1].iov_len = bufferlen; 114862306a36Sopenharmony_ci bufferlist[2].iov_base = &aligned_data; 114962306a36Sopenharmony_ci bufferlist[2].iov_len = (packetlen_aligned - packetlen); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci return hv_ringbuffer_write(channel, bufferlist, 3, requestid, NULL); 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/* 115662306a36Sopenharmony_ci * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet 115762306a36Sopenharmony_ci * using a GPADL Direct packet type. 115862306a36Sopenharmony_ci * The buffer includes the vmbus descriptor. 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ciint vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, 116162306a36Sopenharmony_ci struct vmbus_packet_mpb_array *desc, 116262306a36Sopenharmony_ci u32 desc_size, 116362306a36Sopenharmony_ci void *buffer, u32 bufferlen, u64 requestid) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci u32 packetlen; 116662306a36Sopenharmony_ci u32 packetlen_aligned; 116762306a36Sopenharmony_ci struct kvec bufferlist[3]; 116862306a36Sopenharmony_ci u64 aligned_data = 0; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci packetlen = desc_size + bufferlen; 117162306a36Sopenharmony_ci packetlen_aligned = ALIGN(packetlen, sizeof(u64)); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* Setup the descriptor */ 117462306a36Sopenharmony_ci desc->type = VM_PKT_DATA_USING_GPA_DIRECT; 117562306a36Sopenharmony_ci desc->flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; 117662306a36Sopenharmony_ci desc->dataoffset8 = desc_size >> 3; /* in 8-bytes granularity */ 117762306a36Sopenharmony_ci desc->length8 = (u16)(packetlen_aligned >> 3); 117862306a36Sopenharmony_ci desc->transactionid = VMBUS_RQST_ERROR; /* will be updated in hv_ringbuffer_write() */ 117962306a36Sopenharmony_ci desc->reserved = 0; 118062306a36Sopenharmony_ci desc->rangecount = 1; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci bufferlist[0].iov_base = desc; 118362306a36Sopenharmony_ci bufferlist[0].iov_len = desc_size; 118462306a36Sopenharmony_ci bufferlist[1].iov_base = buffer; 118562306a36Sopenharmony_ci bufferlist[1].iov_len = bufferlen; 118662306a36Sopenharmony_ci bufferlist[2].iov_base = &aligned_data; 118762306a36Sopenharmony_ci bufferlist[2].iov_len = (packetlen_aligned - packetlen); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return hv_ringbuffer_write(channel, bufferlist, 3, requestid, NULL); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci/** 119462306a36Sopenharmony_ci * __vmbus_recvpacket() - Retrieve the user packet on the specified channel 119562306a36Sopenharmony_ci * @channel: Pointer to vmbus_channel structure 119662306a36Sopenharmony_ci * @buffer: Pointer to the buffer you want to receive the data into. 119762306a36Sopenharmony_ci * @bufferlen: Maximum size of what the buffer can hold. 119862306a36Sopenharmony_ci * @buffer_actual_len: The actual size of the data after it was received. 119962306a36Sopenharmony_ci * @requestid: Identifier of the request 120062306a36Sopenharmony_ci * @raw: true means keep the vmpacket_descriptor header in the received data. 120162306a36Sopenharmony_ci * 120262306a36Sopenharmony_ci * Receives directly from the hyper-v vmbus and puts the data it received 120362306a36Sopenharmony_ci * into Buffer. This will receive the data unparsed from hyper-v. 120462306a36Sopenharmony_ci * 120562306a36Sopenharmony_ci * Mainly used by Hyper-V drivers. 120662306a36Sopenharmony_ci */ 120762306a36Sopenharmony_cistatic inline int 120862306a36Sopenharmony_ci__vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, 120962306a36Sopenharmony_ci u32 bufferlen, u32 *buffer_actual_len, u64 *requestid, 121062306a36Sopenharmony_ci bool raw) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci return hv_ringbuffer_read(channel, buffer, bufferlen, 121362306a36Sopenharmony_ci buffer_actual_len, requestid, raw); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ciint vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, 121862306a36Sopenharmony_ci u32 bufferlen, u32 *buffer_actual_len, 121962306a36Sopenharmony_ci u64 *requestid) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci return __vmbus_recvpacket(channel, buffer, bufferlen, 122262306a36Sopenharmony_ci buffer_actual_len, requestid, false); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ciEXPORT_SYMBOL(vmbus_recvpacket); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci/* 122762306a36Sopenharmony_ci * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ciint vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, 123062306a36Sopenharmony_ci u32 bufferlen, u32 *buffer_actual_len, 123162306a36Sopenharmony_ci u64 *requestid) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci return __vmbus_recvpacket(channel, buffer, bufferlen, 123462306a36Sopenharmony_ci buffer_actual_len, requestid, true); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci/* 123962306a36Sopenharmony_ci * vmbus_next_request_id - Returns a new request id. It is also 124062306a36Sopenharmony_ci * the index at which the guest memory address is stored. 124162306a36Sopenharmony_ci * Uses a spin lock to avoid race conditions. 124262306a36Sopenharmony_ci * @channel: Pointer to the VMbus channel struct 124362306a36Sopenharmony_ci * @rqst_add: Guest memory address to be stored in the array 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_ciu64 vmbus_next_request_id(struct vmbus_channel *channel, u64 rqst_addr) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct vmbus_requestor *rqstor = &channel->requestor; 124862306a36Sopenharmony_ci unsigned long flags; 124962306a36Sopenharmony_ci u64 current_id; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* Check rqstor has been initialized */ 125262306a36Sopenharmony_ci if (!channel->rqstor_size) 125362306a36Sopenharmony_ci return VMBUS_NO_RQSTOR; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci lock_requestor(channel, flags); 125662306a36Sopenharmony_ci current_id = rqstor->next_request_id; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* Requestor array is full */ 125962306a36Sopenharmony_ci if (current_id >= rqstor->size) { 126062306a36Sopenharmony_ci unlock_requestor(channel, flags); 126162306a36Sopenharmony_ci return VMBUS_RQST_ERROR; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci rqstor->next_request_id = rqstor->req_arr[current_id]; 126562306a36Sopenharmony_ci rqstor->req_arr[current_id] = rqst_addr; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* The already held spin lock provides atomicity */ 126862306a36Sopenharmony_ci bitmap_set(rqstor->req_bitmap, current_id, 1); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci unlock_requestor(channel, flags); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* 127362306a36Sopenharmony_ci * Cannot return an ID of 0, which is reserved for an unsolicited 127462306a36Sopenharmony_ci * message from Hyper-V; Hyper-V does not acknowledge (respond to) 127562306a36Sopenharmony_ci * VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED requests with ID of 127662306a36Sopenharmony_ci * 0 sent by the guest. 127762306a36Sopenharmony_ci */ 127862306a36Sopenharmony_ci return current_id + 1; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_next_request_id); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci/* As in vmbus_request_addr_match() but without the requestor lock */ 128362306a36Sopenharmony_ciu64 __vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id, 128462306a36Sopenharmony_ci u64 rqst_addr) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct vmbus_requestor *rqstor = &channel->requestor; 128762306a36Sopenharmony_ci u64 req_addr; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* Check rqstor has been initialized */ 129062306a36Sopenharmony_ci if (!channel->rqstor_size) 129162306a36Sopenharmony_ci return VMBUS_NO_RQSTOR; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* Hyper-V can send an unsolicited message with ID of 0 */ 129462306a36Sopenharmony_ci if (!trans_id) 129562306a36Sopenharmony_ci return VMBUS_RQST_ERROR; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci /* Data corresponding to trans_id is stored at trans_id - 1 */ 129862306a36Sopenharmony_ci trans_id--; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Invalid trans_id */ 130162306a36Sopenharmony_ci if (trans_id >= rqstor->size || !test_bit(trans_id, rqstor->req_bitmap)) 130262306a36Sopenharmony_ci return VMBUS_RQST_ERROR; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci req_addr = rqstor->req_arr[trans_id]; 130562306a36Sopenharmony_ci if (rqst_addr == VMBUS_RQST_ADDR_ANY || req_addr == rqst_addr) { 130662306a36Sopenharmony_ci rqstor->req_arr[trans_id] = rqstor->next_request_id; 130762306a36Sopenharmony_ci rqstor->next_request_id = trans_id; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci /* The already held spin lock provides atomicity */ 131062306a36Sopenharmony_ci bitmap_clear(rqstor->req_bitmap, trans_id, 1); 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci return req_addr; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__vmbus_request_addr_match); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci/* 131862306a36Sopenharmony_ci * vmbus_request_addr_match - Clears/removes @trans_id from the @channel's 131962306a36Sopenharmony_ci * requestor, provided the memory address stored at @trans_id equals @rqst_addr 132062306a36Sopenharmony_ci * (or provided @rqst_addr matches the sentinel value VMBUS_RQST_ADDR_ANY). 132162306a36Sopenharmony_ci * 132262306a36Sopenharmony_ci * Returns the memory address stored at @trans_id, or VMBUS_RQST_ERROR if 132362306a36Sopenharmony_ci * @trans_id is not contained in the requestor. 132462306a36Sopenharmony_ci * 132562306a36Sopenharmony_ci * Acquires and releases the requestor spin lock. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ciu64 vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id, 132862306a36Sopenharmony_ci u64 rqst_addr) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci unsigned long flags; 133162306a36Sopenharmony_ci u64 req_addr; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci lock_requestor(channel, flags); 133462306a36Sopenharmony_ci req_addr = __vmbus_request_addr_match(channel, trans_id, rqst_addr); 133562306a36Sopenharmony_ci unlock_requestor(channel, flags); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci return req_addr; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_request_addr_match); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci/* 134262306a36Sopenharmony_ci * vmbus_request_addr - Returns the memory address stored at @trans_id 134362306a36Sopenharmony_ci * in @rqstor. Uses a spin lock to avoid race conditions. 134462306a36Sopenharmony_ci * @channel: Pointer to the VMbus channel struct 134562306a36Sopenharmony_ci * @trans_id: Request id sent back from Hyper-V. Becomes the requestor's 134662306a36Sopenharmony_ci * next request id. 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ciu64 vmbus_request_addr(struct vmbus_channel *channel, u64 trans_id) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci return vmbus_request_addr_match(channel, trans_id, VMBUS_RQST_ADDR_ANY); 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_request_addr); 1353