162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VMware VMCI Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 962306a36Sopenharmony_ci#include <linux/vmw_vmci_api.h> 1062306a36Sopenharmony_ci#include <linux/highmem.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/pagemap.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/uio.h> 2062306a36Sopenharmony_ci#include <linux/wait.h> 2162306a36Sopenharmony_ci#include <linux/vmalloc.h> 2262306a36Sopenharmony_ci#include <linux/skbuff.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "vmci_handle_array.h" 2562306a36Sopenharmony_ci#include "vmci_queue_pair.h" 2662306a36Sopenharmony_ci#include "vmci_datagram.h" 2762306a36Sopenharmony_ci#include "vmci_resource.h" 2862306a36Sopenharmony_ci#include "vmci_context.h" 2962306a36Sopenharmony_ci#include "vmci_driver.h" 3062306a36Sopenharmony_ci#include "vmci_event.h" 3162306a36Sopenharmony_ci#include "vmci_route.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * In the following, we will distinguish between two kinds of VMX processes - 3562306a36Sopenharmony_ci * the ones with versions lower than VMCI_VERSION_NOVMVM that use specialized 3662306a36Sopenharmony_ci * VMCI page files in the VMX and supporting VM to VM communication and the 3762306a36Sopenharmony_ci * newer ones that use the guest memory directly. We will in the following 3862306a36Sopenharmony_ci * refer to the older VMX versions as old-style VMX'en, and the newer ones as 3962306a36Sopenharmony_ci * new-style VMX'en. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * The state transition datagram is as follows (the VMCIQPB_ prefix has been 4262306a36Sopenharmony_ci * removed for readability) - see below for more details on the transtions: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * -------------- NEW ------------- 4562306a36Sopenharmony_ci * | | 4662306a36Sopenharmony_ci * \_/ \_/ 4762306a36Sopenharmony_ci * CREATED_NO_MEM <-----------------> CREATED_MEM 4862306a36Sopenharmony_ci * | | | 4962306a36Sopenharmony_ci * | o-----------------------o | 5062306a36Sopenharmony_ci * | | | 5162306a36Sopenharmony_ci * \_/ \_/ \_/ 5262306a36Sopenharmony_ci * ATTACHED_NO_MEM <----------------> ATTACHED_MEM 5362306a36Sopenharmony_ci * | | | 5462306a36Sopenharmony_ci * | o----------------------o | 5562306a36Sopenharmony_ci * | | | 5662306a36Sopenharmony_ci * \_/ \_/ \_/ 5762306a36Sopenharmony_ci * SHUTDOWN_NO_MEM <----------------> SHUTDOWN_MEM 5862306a36Sopenharmony_ci * | | 5962306a36Sopenharmony_ci * | | 6062306a36Sopenharmony_ci * -------------> gone <------------- 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * In more detail. When a VMCI queue pair is first created, it will be in the 6362306a36Sopenharmony_ci * VMCIQPB_NEW state. It will then move into one of the following states: 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * - VMCIQPB_CREATED_NO_MEM: this state indicates that either: 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * - the created was performed by a host endpoint, in which case there is 6862306a36Sopenharmony_ci * no backing memory yet. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * - the create was initiated by an old-style VMX, that uses 7162306a36Sopenharmony_ci * vmci_qp_broker_set_page_store to specify the UVAs of the queue pair at 7262306a36Sopenharmony_ci * a later point in time. This state can be distinguished from the one 7362306a36Sopenharmony_ci * above by the context ID of the creator. A host side is not allowed to 7462306a36Sopenharmony_ci * attach until the page store has been set. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * - VMCIQPB_CREATED_MEM: this state is the result when the queue pair 7762306a36Sopenharmony_ci * is created by a VMX using the queue pair device backend that 7862306a36Sopenharmony_ci * sets the UVAs of the queue pair immediately and stores the 7962306a36Sopenharmony_ci * information for later attachers. At this point, it is ready for 8062306a36Sopenharmony_ci * the host side to attach to it. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Once the queue pair is in one of the created states (with the exception of 8362306a36Sopenharmony_ci * the case mentioned for older VMX'en above), it is possible to attach to the 8462306a36Sopenharmony_ci * queue pair. Again we have two new states possible: 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * - VMCIQPB_ATTACHED_MEM: this state can be reached through the following 8762306a36Sopenharmony_ci * paths: 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * - from VMCIQPB_CREATED_NO_MEM when a new-style VMX allocates a queue 9062306a36Sopenharmony_ci * pair, and attaches to a queue pair previously created by the host side. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * - from VMCIQPB_CREATED_MEM when the host side attaches to a queue pair 9362306a36Sopenharmony_ci * already created by a guest. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * - from VMCIQPB_ATTACHED_NO_MEM, when an old-style VMX calls 9662306a36Sopenharmony_ci * vmci_qp_broker_set_page_store (see below). 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * - VMCIQPB_ATTACHED_NO_MEM: If the queue pair already was in the 9962306a36Sopenharmony_ci * VMCIQPB_CREATED_NO_MEM due to a host side create, an old-style VMX will 10062306a36Sopenharmony_ci * bring the queue pair into this state. Once vmci_qp_broker_set_page_store 10162306a36Sopenharmony_ci * is called to register the user memory, the VMCIQPB_ATTACH_MEM state 10262306a36Sopenharmony_ci * will be entered. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * From the attached queue pair, the queue pair can enter the shutdown states 10562306a36Sopenharmony_ci * when either side of the queue pair detaches. If the guest side detaches 10662306a36Sopenharmony_ci * first, the queue pair will enter the VMCIQPB_SHUTDOWN_NO_MEM state, where 10762306a36Sopenharmony_ci * the content of the queue pair will no longer be available. If the host 10862306a36Sopenharmony_ci * side detaches first, the queue pair will either enter the 10962306a36Sopenharmony_ci * VMCIQPB_SHUTDOWN_MEM, if the guest memory is currently mapped, or 11062306a36Sopenharmony_ci * VMCIQPB_SHUTDOWN_NO_MEM, if the guest memory is not mapped 11162306a36Sopenharmony_ci * (e.g., the host detaches while a guest is stunned). 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * New-style VMX'en will also unmap guest memory, if the guest is 11462306a36Sopenharmony_ci * quiesced, e.g., during a snapshot operation. In that case, the guest 11562306a36Sopenharmony_ci * memory will no longer be available, and the queue pair will transition from 11662306a36Sopenharmony_ci * *_MEM state to a *_NO_MEM state. The VMX may later map the memory once more, 11762306a36Sopenharmony_ci * in which case the queue pair will transition from the *_NO_MEM state at that 11862306a36Sopenharmony_ci * point back to the *_MEM state. Note that the *_NO_MEM state may have changed, 11962306a36Sopenharmony_ci * since the peer may have either attached or detached in the meantime. The 12062306a36Sopenharmony_ci * values are laid out such that ++ on a state will move from a *_NO_MEM to a 12162306a36Sopenharmony_ci * *_MEM state, and vice versa. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* The Kernel specific component of the struct vmci_queue structure. */ 12562306a36Sopenharmony_cistruct vmci_queue_kern_if { 12662306a36Sopenharmony_ci struct mutex __mutex; /* Protects the queue. */ 12762306a36Sopenharmony_ci struct mutex *mutex; /* Shared by producer and consumer queues. */ 12862306a36Sopenharmony_ci size_t num_pages; /* Number of pages incl. header. */ 12962306a36Sopenharmony_ci bool host; /* Host or guest? */ 13062306a36Sopenharmony_ci union { 13162306a36Sopenharmony_ci struct { 13262306a36Sopenharmony_ci dma_addr_t *pas; 13362306a36Sopenharmony_ci void **vas; 13462306a36Sopenharmony_ci } g; /* Used by the guest. */ 13562306a36Sopenharmony_ci struct { 13662306a36Sopenharmony_ci struct page **page; 13762306a36Sopenharmony_ci struct page **header_page; 13862306a36Sopenharmony_ci } h; /* Used by the host. */ 13962306a36Sopenharmony_ci } u; 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * This structure is opaque to the clients. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistruct vmci_qp { 14662306a36Sopenharmony_ci struct vmci_handle handle; 14762306a36Sopenharmony_ci struct vmci_queue *produce_q; 14862306a36Sopenharmony_ci struct vmci_queue *consume_q; 14962306a36Sopenharmony_ci u64 produce_q_size; 15062306a36Sopenharmony_ci u64 consume_q_size; 15162306a36Sopenharmony_ci u32 peer; 15262306a36Sopenharmony_ci u32 flags; 15362306a36Sopenharmony_ci u32 priv_flags; 15462306a36Sopenharmony_ci bool guest_endpoint; 15562306a36Sopenharmony_ci unsigned int blocked; 15662306a36Sopenharmony_ci unsigned int generation; 15762306a36Sopenharmony_ci wait_queue_head_t event; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cienum qp_broker_state { 16162306a36Sopenharmony_ci VMCIQPB_NEW, 16262306a36Sopenharmony_ci VMCIQPB_CREATED_NO_MEM, 16362306a36Sopenharmony_ci VMCIQPB_CREATED_MEM, 16462306a36Sopenharmony_ci VMCIQPB_ATTACHED_NO_MEM, 16562306a36Sopenharmony_ci VMCIQPB_ATTACHED_MEM, 16662306a36Sopenharmony_ci VMCIQPB_SHUTDOWN_NO_MEM, 16762306a36Sopenharmony_ci VMCIQPB_SHUTDOWN_MEM, 16862306a36Sopenharmony_ci VMCIQPB_GONE 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#define QPBROKERSTATE_HAS_MEM(_qpb) (_qpb->state == VMCIQPB_CREATED_MEM || \ 17262306a36Sopenharmony_ci _qpb->state == VMCIQPB_ATTACHED_MEM || \ 17362306a36Sopenharmony_ci _qpb->state == VMCIQPB_SHUTDOWN_MEM) 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * In the queue pair broker, we always use the guest point of view for 17762306a36Sopenharmony_ci * the produce and consume queue values and references, e.g., the 17862306a36Sopenharmony_ci * produce queue size stored is the guests produce queue size. The 17962306a36Sopenharmony_ci * host endpoint will need to swap these around. The only exception is 18062306a36Sopenharmony_ci * the local queue pairs on the host, in which case the host endpoint 18162306a36Sopenharmony_ci * that creates the queue pair will have the right orientation, and 18262306a36Sopenharmony_ci * the attaching host endpoint will need to swap. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistruct qp_entry { 18562306a36Sopenharmony_ci struct list_head list_item; 18662306a36Sopenharmony_ci struct vmci_handle handle; 18762306a36Sopenharmony_ci u32 peer; 18862306a36Sopenharmony_ci u32 flags; 18962306a36Sopenharmony_ci u64 produce_size; 19062306a36Sopenharmony_ci u64 consume_size; 19162306a36Sopenharmony_ci u32 ref_count; 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistruct qp_broker_entry { 19562306a36Sopenharmony_ci struct vmci_resource resource; 19662306a36Sopenharmony_ci struct qp_entry qp; 19762306a36Sopenharmony_ci u32 create_id; 19862306a36Sopenharmony_ci u32 attach_id; 19962306a36Sopenharmony_ci enum qp_broker_state state; 20062306a36Sopenharmony_ci bool require_trusted_attach; 20162306a36Sopenharmony_ci bool created_by_trusted; 20262306a36Sopenharmony_ci bool vmci_page_files; /* Created by VMX using VMCI page files */ 20362306a36Sopenharmony_ci struct vmci_queue *produce_q; 20462306a36Sopenharmony_ci struct vmci_queue *consume_q; 20562306a36Sopenharmony_ci struct vmci_queue_header saved_produce_q; 20662306a36Sopenharmony_ci struct vmci_queue_header saved_consume_q; 20762306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb; 20862306a36Sopenharmony_ci void *client_data; 20962306a36Sopenharmony_ci void *local_mem; /* Kernel memory for local queue pair */ 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistruct qp_guest_endpoint { 21362306a36Sopenharmony_ci struct vmci_resource resource; 21462306a36Sopenharmony_ci struct qp_entry qp; 21562306a36Sopenharmony_ci u64 num_ppns; 21662306a36Sopenharmony_ci void *produce_q; 21762306a36Sopenharmony_ci void *consume_q; 21862306a36Sopenharmony_ci struct ppn_set ppn_set; 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistruct qp_list { 22262306a36Sopenharmony_ci struct list_head head; 22362306a36Sopenharmony_ci struct mutex mutex; /* Protect queue list. */ 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct qp_list qp_broker_list = { 22762306a36Sopenharmony_ci .head = LIST_HEAD_INIT(qp_broker_list.head), 22862306a36Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(qp_broker_list.mutex), 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic struct qp_list qp_guest_endpoints = { 23262306a36Sopenharmony_ci .head = LIST_HEAD_INIT(qp_guest_endpoints.head), 23362306a36Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(qp_guest_endpoints.mutex), 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define INVALID_VMCI_GUEST_MEM_ID 0 23762306a36Sopenharmony_ci#define QPE_NUM_PAGES(_QPE) ((u32) \ 23862306a36Sopenharmony_ci (DIV_ROUND_UP(_QPE.produce_size, PAGE_SIZE) + \ 23962306a36Sopenharmony_ci DIV_ROUND_UP(_QPE.consume_size, PAGE_SIZE) + 2)) 24062306a36Sopenharmony_ci#define QP_SIZES_ARE_VALID(_prod_qsize, _cons_qsize) \ 24162306a36Sopenharmony_ci ((_prod_qsize) + (_cons_qsize) >= max(_prod_qsize, _cons_qsize) && \ 24262306a36Sopenharmony_ci (_prod_qsize) + (_cons_qsize) <= VMCI_MAX_GUEST_QP_MEMORY) 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * Frees kernel VA space for a given queue and its queue header, and 24662306a36Sopenharmony_ci * frees physical data pages. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic void qp_free_queue(void *q, u64 size) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct vmci_queue *queue = q; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (queue) { 25362306a36Sopenharmony_ci u64 i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Given size does not include header, so add in a page here. */ 25662306a36Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(size, PAGE_SIZE) + 1; i++) { 25762306a36Sopenharmony_ci dma_free_coherent(&vmci_pdev->dev, PAGE_SIZE, 25862306a36Sopenharmony_ci queue->kernel_if->u.g.vas[i], 25962306a36Sopenharmony_ci queue->kernel_if->u.g.pas[i]); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci vfree(queue); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Allocates kernel queue pages of specified size with IOMMU mappings, 26862306a36Sopenharmony_ci * plus space for the queue structure/kernel interface and the queue 26962306a36Sopenharmony_ci * header. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic void *qp_alloc_queue(u64 size, u32 flags) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci u64 i; 27462306a36Sopenharmony_ci struct vmci_queue *queue; 27562306a36Sopenharmony_ci size_t pas_size; 27662306a36Sopenharmony_ci size_t vas_size; 27762306a36Sopenharmony_ci size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if); 27862306a36Sopenharmony_ci u64 num_pages; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (size > SIZE_MAX - PAGE_SIZE) 28162306a36Sopenharmony_ci return NULL; 28262306a36Sopenharmony_ci num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; 28362306a36Sopenharmony_ci if (num_pages > 28462306a36Sopenharmony_ci (SIZE_MAX - queue_size) / 28562306a36Sopenharmony_ci (sizeof(*queue->kernel_if->u.g.pas) + 28662306a36Sopenharmony_ci sizeof(*queue->kernel_if->u.g.vas))) 28762306a36Sopenharmony_ci return NULL; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas); 29062306a36Sopenharmony_ci vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas); 29162306a36Sopenharmony_ci queue_size += pas_size + vas_size; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci queue = vmalloc(queue_size); 29462306a36Sopenharmony_ci if (!queue) 29562306a36Sopenharmony_ci return NULL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci queue->q_header = NULL; 29862306a36Sopenharmony_ci queue->saved_header = NULL; 29962306a36Sopenharmony_ci queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); 30062306a36Sopenharmony_ci queue->kernel_if->mutex = NULL; 30162306a36Sopenharmony_ci queue->kernel_if->num_pages = num_pages; 30262306a36Sopenharmony_ci queue->kernel_if->u.g.pas = (dma_addr_t *)(queue->kernel_if + 1); 30362306a36Sopenharmony_ci queue->kernel_if->u.g.vas = 30462306a36Sopenharmony_ci (void **)((u8 *)queue->kernel_if->u.g.pas + pas_size); 30562306a36Sopenharmony_ci queue->kernel_if->host = false; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) { 30862306a36Sopenharmony_ci queue->kernel_if->u.g.vas[i] = 30962306a36Sopenharmony_ci dma_alloc_coherent(&vmci_pdev->dev, PAGE_SIZE, 31062306a36Sopenharmony_ci &queue->kernel_if->u.g.pas[i], 31162306a36Sopenharmony_ci GFP_KERNEL); 31262306a36Sopenharmony_ci if (!queue->kernel_if->u.g.vas[i]) { 31362306a36Sopenharmony_ci /* Size excl. the header. */ 31462306a36Sopenharmony_ci qp_free_queue(queue, i * PAGE_SIZE); 31562306a36Sopenharmony_ci return NULL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Queue header is the first page. */ 32062306a36Sopenharmony_ci queue->q_header = queue->kernel_if->u.g.vas[0]; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return queue; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Copies from a given buffer or iovector to a VMCI Queue. Uses 32762306a36Sopenharmony_ci * kmap_local_page() to dynamically map required portions of the queue 32862306a36Sopenharmony_ci * by traversing the offset -> page translation structure for the queue. 32962306a36Sopenharmony_ci * Assumes that offset + size does not wrap around in the queue. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic int qp_memcpy_to_queue_iter(struct vmci_queue *queue, 33262306a36Sopenharmony_ci u64 queue_offset, 33362306a36Sopenharmony_ci struct iov_iter *from, 33462306a36Sopenharmony_ci size_t size) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct vmci_queue_kern_if *kernel_if = queue->kernel_if; 33762306a36Sopenharmony_ci size_t bytes_copied = 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci while (bytes_copied < size) { 34062306a36Sopenharmony_ci const u64 page_index = 34162306a36Sopenharmony_ci (queue_offset + bytes_copied) / PAGE_SIZE; 34262306a36Sopenharmony_ci const size_t page_offset = 34362306a36Sopenharmony_ci (queue_offset + bytes_copied) & (PAGE_SIZE - 1); 34462306a36Sopenharmony_ci void *va; 34562306a36Sopenharmony_ci size_t to_copy; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (kernel_if->host) 34862306a36Sopenharmony_ci va = kmap_local_page(kernel_if->u.h.page[page_index]); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci va = kernel_if->u.g.vas[page_index + 1]; 35162306a36Sopenharmony_ci /* Skip header. */ 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (size - bytes_copied > PAGE_SIZE - page_offset) 35462306a36Sopenharmony_ci /* Enough payload to fill up from this page. */ 35562306a36Sopenharmony_ci to_copy = PAGE_SIZE - page_offset; 35662306a36Sopenharmony_ci else 35762306a36Sopenharmony_ci to_copy = size - bytes_copied; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!copy_from_iter_full((u8 *)va + page_offset, to_copy, 36062306a36Sopenharmony_ci from)) { 36162306a36Sopenharmony_ci if (kernel_if->host) 36262306a36Sopenharmony_ci kunmap_local(va); 36362306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci bytes_copied += to_copy; 36662306a36Sopenharmony_ci if (kernel_if->host) 36762306a36Sopenharmony_ci kunmap_local(va); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return VMCI_SUCCESS; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* 37462306a36Sopenharmony_ci * Copies to a given buffer or iovector from a VMCI Queue. Uses 37562306a36Sopenharmony_ci * kmap_local_page() to dynamically map required portions of the queue 37662306a36Sopenharmony_ci * by traversing the offset -> page translation structure for the queue. 37762306a36Sopenharmony_ci * Assumes that offset + size does not wrap around in the queue. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic int qp_memcpy_from_queue_iter(struct iov_iter *to, 38062306a36Sopenharmony_ci const struct vmci_queue *queue, 38162306a36Sopenharmony_ci u64 queue_offset, size_t size) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct vmci_queue_kern_if *kernel_if = queue->kernel_if; 38462306a36Sopenharmony_ci size_t bytes_copied = 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci while (bytes_copied < size) { 38762306a36Sopenharmony_ci const u64 page_index = 38862306a36Sopenharmony_ci (queue_offset + bytes_copied) / PAGE_SIZE; 38962306a36Sopenharmony_ci const size_t page_offset = 39062306a36Sopenharmony_ci (queue_offset + bytes_copied) & (PAGE_SIZE - 1); 39162306a36Sopenharmony_ci void *va; 39262306a36Sopenharmony_ci size_t to_copy; 39362306a36Sopenharmony_ci int err; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (kernel_if->host) 39662306a36Sopenharmony_ci va = kmap_local_page(kernel_if->u.h.page[page_index]); 39762306a36Sopenharmony_ci else 39862306a36Sopenharmony_ci va = kernel_if->u.g.vas[page_index + 1]; 39962306a36Sopenharmony_ci /* Skip header. */ 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (size - bytes_copied > PAGE_SIZE - page_offset) 40262306a36Sopenharmony_ci /* Enough payload to fill up this page. */ 40362306a36Sopenharmony_ci to_copy = PAGE_SIZE - page_offset; 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci to_copy = size - bytes_copied; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci err = copy_to_iter((u8 *)va + page_offset, to_copy, to); 40862306a36Sopenharmony_ci if (err != to_copy) { 40962306a36Sopenharmony_ci if (kernel_if->host) 41062306a36Sopenharmony_ci kunmap_local(va); 41162306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci bytes_copied += to_copy; 41462306a36Sopenharmony_ci if (kernel_if->host) 41562306a36Sopenharmony_ci kunmap_local(va); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return VMCI_SUCCESS; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * Allocates two list of PPNs --- one for the pages in the produce queue, 42362306a36Sopenharmony_ci * and the other for the pages in the consume queue. Intializes the list 42462306a36Sopenharmony_ci * of PPNs with the page frame numbers of the KVA for the two queues (and 42562306a36Sopenharmony_ci * the queue headers). 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic int qp_alloc_ppn_set(void *prod_q, 42862306a36Sopenharmony_ci u64 num_produce_pages, 42962306a36Sopenharmony_ci void *cons_q, 43062306a36Sopenharmony_ci u64 num_consume_pages, struct ppn_set *ppn_set) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci u64 *produce_ppns; 43362306a36Sopenharmony_ci u64 *consume_ppns; 43462306a36Sopenharmony_ci struct vmci_queue *produce_q = prod_q; 43562306a36Sopenharmony_ci struct vmci_queue *consume_q = cons_q; 43662306a36Sopenharmony_ci u64 i; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!produce_q || !num_produce_pages || !consume_q || 43962306a36Sopenharmony_ci !num_consume_pages || !ppn_set) 44062306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (ppn_set->initialized) 44362306a36Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci produce_ppns = 44662306a36Sopenharmony_ci kmalloc_array(num_produce_pages, sizeof(*produce_ppns), 44762306a36Sopenharmony_ci GFP_KERNEL); 44862306a36Sopenharmony_ci if (!produce_ppns) 44962306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci consume_ppns = 45262306a36Sopenharmony_ci kmalloc_array(num_consume_pages, sizeof(*consume_ppns), 45362306a36Sopenharmony_ci GFP_KERNEL); 45462306a36Sopenharmony_ci if (!consume_ppns) { 45562306a36Sopenharmony_ci kfree(produce_ppns); 45662306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci for (i = 0; i < num_produce_pages; i++) 46062306a36Sopenharmony_ci produce_ppns[i] = 46162306a36Sopenharmony_ci produce_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci for (i = 0; i < num_consume_pages; i++) 46462306a36Sopenharmony_ci consume_ppns[i] = 46562306a36Sopenharmony_ci consume_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ppn_set->num_produce_pages = num_produce_pages; 46862306a36Sopenharmony_ci ppn_set->num_consume_pages = num_consume_pages; 46962306a36Sopenharmony_ci ppn_set->produce_ppns = produce_ppns; 47062306a36Sopenharmony_ci ppn_set->consume_ppns = consume_ppns; 47162306a36Sopenharmony_ci ppn_set->initialized = true; 47262306a36Sopenharmony_ci return VMCI_SUCCESS; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/* 47662306a36Sopenharmony_ci * Frees the two list of PPNs for a queue pair. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_cistatic void qp_free_ppn_set(struct ppn_set *ppn_set) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci if (ppn_set->initialized) { 48162306a36Sopenharmony_ci /* Do not call these functions on NULL inputs. */ 48262306a36Sopenharmony_ci kfree(ppn_set->produce_ppns); 48362306a36Sopenharmony_ci kfree(ppn_set->consume_ppns); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci memset(ppn_set, 0, sizeof(*ppn_set)); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * Populates the list of PPNs in the hypercall structure with the PPNS 49062306a36Sopenharmony_ci * of the produce queue and the consume queue. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_cistatic int qp_populate_ppn_set(u8 *call_buf, const struct ppn_set *ppn_set) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci if (vmci_use_ppn64()) { 49562306a36Sopenharmony_ci memcpy(call_buf, ppn_set->produce_ppns, 49662306a36Sopenharmony_ci ppn_set->num_produce_pages * 49762306a36Sopenharmony_ci sizeof(*ppn_set->produce_ppns)); 49862306a36Sopenharmony_ci memcpy(call_buf + 49962306a36Sopenharmony_ci ppn_set->num_produce_pages * 50062306a36Sopenharmony_ci sizeof(*ppn_set->produce_ppns), 50162306a36Sopenharmony_ci ppn_set->consume_ppns, 50262306a36Sopenharmony_ci ppn_set->num_consume_pages * 50362306a36Sopenharmony_ci sizeof(*ppn_set->consume_ppns)); 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci int i; 50662306a36Sopenharmony_ci u32 *ppns = (u32 *) call_buf; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (i = 0; i < ppn_set->num_produce_pages; i++) 50962306a36Sopenharmony_ci ppns[i] = (u32) ppn_set->produce_ppns[i]; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ppns = &ppns[ppn_set->num_produce_pages]; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (i = 0; i < ppn_set->num_consume_pages; i++) 51462306a36Sopenharmony_ci ppns[i] = (u32) ppn_set->consume_ppns[i]; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return VMCI_SUCCESS; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* 52162306a36Sopenharmony_ci * Allocates kernel VA space of specified size plus space for the queue 52262306a36Sopenharmony_ci * and kernel interface. This is different from the guest queue allocator, 52362306a36Sopenharmony_ci * because we do not allocate our own queue header/data pages here but 52462306a36Sopenharmony_ci * share those of the guest. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_cistatic struct vmci_queue *qp_host_alloc_queue(u64 size) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct vmci_queue *queue; 52962306a36Sopenharmony_ci size_t queue_page_size; 53062306a36Sopenharmony_ci u64 num_pages; 53162306a36Sopenharmony_ci const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if)); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (size > min_t(size_t, VMCI_MAX_GUEST_QP_MEMORY, SIZE_MAX - PAGE_SIZE)) 53462306a36Sopenharmony_ci return NULL; 53562306a36Sopenharmony_ci num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; 53662306a36Sopenharmony_ci if (num_pages > (SIZE_MAX - queue_size) / 53762306a36Sopenharmony_ci sizeof(*queue->kernel_if->u.h.page)) 53862306a36Sopenharmony_ci return NULL; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci queue_page_size = num_pages * sizeof(*queue->kernel_if->u.h.page); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (queue_size + queue_page_size > KMALLOC_MAX_SIZE) 54362306a36Sopenharmony_ci return NULL; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL); 54662306a36Sopenharmony_ci if (queue) { 54762306a36Sopenharmony_ci queue->q_header = NULL; 54862306a36Sopenharmony_ci queue->saved_header = NULL; 54962306a36Sopenharmony_ci queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); 55062306a36Sopenharmony_ci queue->kernel_if->host = true; 55162306a36Sopenharmony_ci queue->kernel_if->mutex = NULL; 55262306a36Sopenharmony_ci queue->kernel_if->num_pages = num_pages; 55362306a36Sopenharmony_ci queue->kernel_if->u.h.header_page = 55462306a36Sopenharmony_ci (struct page **)((u8 *)queue + queue_size); 55562306a36Sopenharmony_ci queue->kernel_if->u.h.page = 55662306a36Sopenharmony_ci &queue->kernel_if->u.h.header_page[1]; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return queue; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/* 56362306a36Sopenharmony_ci * Frees kernel memory for a given queue (header plus translation 56462306a36Sopenharmony_ci * structure). 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic void qp_host_free_queue(struct vmci_queue *queue, u64 queue_size) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci kfree(queue); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * Initialize the mutex for the pair of queues. This mutex is used to 57362306a36Sopenharmony_ci * protect the q_header and the buffer from changing out from under any 57462306a36Sopenharmony_ci * users of either queue. Of course, it's only any good if the mutexes 57562306a36Sopenharmony_ci * are actually acquired. Queue structure must lie on non-paged memory 57662306a36Sopenharmony_ci * or we cannot guarantee access to the mutex. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_cistatic void qp_init_queue_mutex(struct vmci_queue *produce_q, 57962306a36Sopenharmony_ci struct vmci_queue *consume_q) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * Only the host queue has shared state - the guest queues do not 58362306a36Sopenharmony_ci * need to synchronize access using a queue mutex. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (produce_q->kernel_if->host) { 58762306a36Sopenharmony_ci produce_q->kernel_if->mutex = &produce_q->kernel_if->__mutex; 58862306a36Sopenharmony_ci consume_q->kernel_if->mutex = &produce_q->kernel_if->__mutex; 58962306a36Sopenharmony_ci mutex_init(produce_q->kernel_if->mutex); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * Cleans up the mutex for the pair of queues. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_cistatic void qp_cleanup_queue_mutex(struct vmci_queue *produce_q, 59762306a36Sopenharmony_ci struct vmci_queue *consume_q) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci if (produce_q->kernel_if->host) { 60062306a36Sopenharmony_ci produce_q->kernel_if->mutex = NULL; 60162306a36Sopenharmony_ci consume_q->kernel_if->mutex = NULL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * Acquire the mutex for the queue. Note that the produce_q and 60762306a36Sopenharmony_ci * the consume_q share a mutex. So, only one of the two need to 60862306a36Sopenharmony_ci * be passed in to this routine. Either will work just fine. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_cistatic void qp_acquire_queue_mutex(struct vmci_queue *queue) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci if (queue->kernel_if->host) 61362306a36Sopenharmony_ci mutex_lock(queue->kernel_if->mutex); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* 61762306a36Sopenharmony_ci * Release the mutex for the queue. Note that the produce_q and 61862306a36Sopenharmony_ci * the consume_q share a mutex. So, only one of the two need to 61962306a36Sopenharmony_ci * be passed in to this routine. Either will work just fine. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic void qp_release_queue_mutex(struct vmci_queue *queue) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci if (queue->kernel_if->host) 62462306a36Sopenharmony_ci mutex_unlock(queue->kernel_if->mutex); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/* 62862306a36Sopenharmony_ci * Helper function to release pages in the PageStoreAttachInfo 62962306a36Sopenharmony_ci * previously obtained using get_user_pages. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_cistatic void qp_release_pages(struct page **pages, 63262306a36Sopenharmony_ci u64 num_pages, bool dirty) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci int i; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (i = 0; i < num_pages; i++) { 63762306a36Sopenharmony_ci if (dirty) 63862306a36Sopenharmony_ci set_page_dirty_lock(pages[i]); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci put_page(pages[i]); 64162306a36Sopenharmony_ci pages[i] = NULL; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* 64662306a36Sopenharmony_ci * Lock the user pages referenced by the {produce,consume}Buffer 64762306a36Sopenharmony_ci * struct into memory and populate the {produce,consume}Pages 64862306a36Sopenharmony_ci * arrays in the attach structure with them. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_cistatic int qp_host_get_user_memory(u64 produce_uva, 65162306a36Sopenharmony_ci u64 consume_uva, 65262306a36Sopenharmony_ci struct vmci_queue *produce_q, 65362306a36Sopenharmony_ci struct vmci_queue *consume_q) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci int retval; 65662306a36Sopenharmony_ci int err = VMCI_SUCCESS; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci retval = get_user_pages_fast((uintptr_t) produce_uva, 65962306a36Sopenharmony_ci produce_q->kernel_if->num_pages, 66062306a36Sopenharmony_ci FOLL_WRITE, 66162306a36Sopenharmony_ci produce_q->kernel_if->u.h.header_page); 66262306a36Sopenharmony_ci if (retval < (int)produce_q->kernel_if->num_pages) { 66362306a36Sopenharmony_ci pr_debug("get_user_pages_fast(produce) failed (retval=%d)", 66462306a36Sopenharmony_ci retval); 66562306a36Sopenharmony_ci if (retval > 0) 66662306a36Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 66762306a36Sopenharmony_ci retval, false); 66862306a36Sopenharmony_ci err = VMCI_ERROR_NO_MEM; 66962306a36Sopenharmony_ci goto out; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci retval = get_user_pages_fast((uintptr_t) consume_uva, 67362306a36Sopenharmony_ci consume_q->kernel_if->num_pages, 67462306a36Sopenharmony_ci FOLL_WRITE, 67562306a36Sopenharmony_ci consume_q->kernel_if->u.h.header_page); 67662306a36Sopenharmony_ci if (retval < (int)consume_q->kernel_if->num_pages) { 67762306a36Sopenharmony_ci pr_debug("get_user_pages_fast(consume) failed (retval=%d)", 67862306a36Sopenharmony_ci retval); 67962306a36Sopenharmony_ci if (retval > 0) 68062306a36Sopenharmony_ci qp_release_pages(consume_q->kernel_if->u.h.header_page, 68162306a36Sopenharmony_ci retval, false); 68262306a36Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 68362306a36Sopenharmony_ci produce_q->kernel_if->num_pages, false); 68462306a36Sopenharmony_ci err = VMCI_ERROR_NO_MEM; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci out: 68862306a36Sopenharmony_ci return err; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/* 69262306a36Sopenharmony_ci * Registers the specification of the user pages used for backing a queue 69362306a36Sopenharmony_ci * pair. Enough information to map in pages is stored in the OS specific 69462306a36Sopenharmony_ci * part of the struct vmci_queue structure. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_cistatic int qp_host_register_user_memory(struct vmci_qp_page_store *page_store, 69762306a36Sopenharmony_ci struct vmci_queue *produce_q, 69862306a36Sopenharmony_ci struct vmci_queue *consume_q) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci u64 produce_uva; 70162306a36Sopenharmony_ci u64 consume_uva; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* 70462306a36Sopenharmony_ci * The new style and the old style mapping only differs in 70562306a36Sopenharmony_ci * that we either get a single or two UVAs, so we split the 70662306a36Sopenharmony_ci * single UVA range at the appropriate spot. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ci produce_uva = page_store->pages; 70962306a36Sopenharmony_ci consume_uva = page_store->pages + 71062306a36Sopenharmony_ci produce_q->kernel_if->num_pages * PAGE_SIZE; 71162306a36Sopenharmony_ci return qp_host_get_user_memory(produce_uva, consume_uva, produce_q, 71262306a36Sopenharmony_ci consume_q); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * Releases and removes the references to user pages stored in the attach 71762306a36Sopenharmony_ci * struct. Pages are released from the page cache and may become 71862306a36Sopenharmony_ci * swappable again. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_cistatic void qp_host_unregister_user_memory(struct vmci_queue *produce_q, 72162306a36Sopenharmony_ci struct vmci_queue *consume_q) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 72462306a36Sopenharmony_ci produce_q->kernel_if->num_pages, true); 72562306a36Sopenharmony_ci memset(produce_q->kernel_if->u.h.header_page, 0, 72662306a36Sopenharmony_ci sizeof(*produce_q->kernel_if->u.h.header_page) * 72762306a36Sopenharmony_ci produce_q->kernel_if->num_pages); 72862306a36Sopenharmony_ci qp_release_pages(consume_q->kernel_if->u.h.header_page, 72962306a36Sopenharmony_ci consume_q->kernel_if->num_pages, true); 73062306a36Sopenharmony_ci memset(consume_q->kernel_if->u.h.header_page, 0, 73162306a36Sopenharmony_ci sizeof(*consume_q->kernel_if->u.h.header_page) * 73262306a36Sopenharmony_ci consume_q->kernel_if->num_pages); 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/* 73662306a36Sopenharmony_ci * Once qp_host_register_user_memory has been performed on a 73762306a36Sopenharmony_ci * queue, the queue pair headers can be mapped into the 73862306a36Sopenharmony_ci * kernel. Once mapped, they must be unmapped with 73962306a36Sopenharmony_ci * qp_host_unmap_queues prior to calling 74062306a36Sopenharmony_ci * qp_host_unregister_user_memory. 74162306a36Sopenharmony_ci * Pages are pinned. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_cistatic int qp_host_map_queues(struct vmci_queue *produce_q, 74462306a36Sopenharmony_ci struct vmci_queue *consume_q) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci int result; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (!produce_q->q_header || !consume_q->q_header) { 74962306a36Sopenharmony_ci struct page *headers[2]; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (produce_q->q_header != consume_q->q_header) 75262306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (produce_q->kernel_if->u.h.header_page == NULL || 75562306a36Sopenharmony_ci *produce_q->kernel_if->u.h.header_page == NULL) 75662306a36Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci headers[0] = *produce_q->kernel_if->u.h.header_page; 75962306a36Sopenharmony_ci headers[1] = *consume_q->kernel_if->u.h.header_page; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci produce_q->q_header = vmap(headers, 2, VM_MAP, PAGE_KERNEL); 76262306a36Sopenharmony_ci if (produce_q->q_header != NULL) { 76362306a36Sopenharmony_ci consume_q->q_header = 76462306a36Sopenharmony_ci (struct vmci_queue_header *)((u8 *) 76562306a36Sopenharmony_ci produce_q->q_header + 76662306a36Sopenharmony_ci PAGE_SIZE); 76762306a36Sopenharmony_ci result = VMCI_SUCCESS; 76862306a36Sopenharmony_ci } else { 76962306a36Sopenharmony_ci pr_warn("vmap failed\n"); 77062306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci } else { 77362306a36Sopenharmony_ci result = VMCI_SUCCESS; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci return result; 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/* 78062306a36Sopenharmony_ci * Unmaps previously mapped queue pair headers from the kernel. 78162306a36Sopenharmony_ci * Pages are unpinned. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_cistatic int qp_host_unmap_queues(u32 gid, 78462306a36Sopenharmony_ci struct vmci_queue *produce_q, 78562306a36Sopenharmony_ci struct vmci_queue *consume_q) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci if (produce_q->q_header) { 78862306a36Sopenharmony_ci if (produce_q->q_header < consume_q->q_header) 78962306a36Sopenharmony_ci vunmap(produce_q->q_header); 79062306a36Sopenharmony_ci else 79162306a36Sopenharmony_ci vunmap(consume_q->q_header); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci produce_q->q_header = NULL; 79462306a36Sopenharmony_ci consume_q->q_header = NULL; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return VMCI_SUCCESS; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* 80162306a36Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. Assumes 80262306a36Sopenharmony_ci * that the list is locked. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_cistatic struct qp_entry *qp_list_find(struct qp_list *qp_list, 80562306a36Sopenharmony_ci struct vmci_handle handle) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct qp_entry *entry; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 81062306a36Sopenharmony_ci return NULL; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci list_for_each_entry(entry, &qp_list->head, list_item) { 81362306a36Sopenharmony_ci if (vmci_handle_is_equal(entry->handle, handle)) 81462306a36Sopenharmony_ci return entry; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return NULL; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/* 82162306a36Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_cistatic struct qp_guest_endpoint * 82462306a36Sopenharmony_ciqp_guest_handle_to_entry(struct vmci_handle handle) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct qp_guest_endpoint *entry; 82762306a36Sopenharmony_ci struct qp_entry *qp = qp_list_find(&qp_guest_endpoints, handle); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci entry = qp ? container_of( 83062306a36Sopenharmony_ci qp, struct qp_guest_endpoint, qp) : NULL; 83162306a36Sopenharmony_ci return entry; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci/* 83562306a36Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_cistatic struct qp_broker_entry * 83862306a36Sopenharmony_ciqp_broker_handle_to_entry(struct vmci_handle handle) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct qp_broker_entry *entry; 84162306a36Sopenharmony_ci struct qp_entry *qp = qp_list_find(&qp_broker_list, handle); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci entry = qp ? container_of( 84462306a36Sopenharmony_ci qp, struct qp_broker_entry, qp) : NULL; 84562306a36Sopenharmony_ci return entry; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* 84962306a36Sopenharmony_ci * Dispatches a queue pair event message directly into the local event 85062306a36Sopenharmony_ci * queue. 85162306a36Sopenharmony_ci */ 85262306a36Sopenharmony_cistatic int qp_notify_peer_local(bool attach, struct vmci_handle handle) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci u32 context_id = vmci_get_context_id(); 85562306a36Sopenharmony_ci struct vmci_event_qp ev; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 85862306a36Sopenharmony_ci ev.msg.hdr.dst = vmci_make_handle(context_id, VMCI_EVENT_HANDLER); 85962306a36Sopenharmony_ci ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 86062306a36Sopenharmony_ci VMCI_CONTEXT_RESOURCE_ID); 86162306a36Sopenharmony_ci ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); 86262306a36Sopenharmony_ci ev.msg.event_data.event = 86362306a36Sopenharmony_ci attach ? VMCI_EVENT_QP_PEER_ATTACH : VMCI_EVENT_QP_PEER_DETACH; 86462306a36Sopenharmony_ci ev.payload.peer_id = context_id; 86562306a36Sopenharmony_ci ev.payload.handle = handle; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return vmci_event_dispatch(&ev.msg.hdr); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/* 87162306a36Sopenharmony_ci * Allocates and initializes a qp_guest_endpoint structure. 87262306a36Sopenharmony_ci * Allocates a queue_pair rid (and handle) iff the given entry has 87362306a36Sopenharmony_ci * an invalid handle. 0 through VMCI_RESERVED_RESOURCE_ID_MAX 87462306a36Sopenharmony_ci * are reserved handles. Assumes that the QP list mutex is held 87562306a36Sopenharmony_ci * by the caller. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_cistatic struct qp_guest_endpoint * 87862306a36Sopenharmony_ciqp_guest_endpoint_create(struct vmci_handle handle, 87962306a36Sopenharmony_ci u32 peer, 88062306a36Sopenharmony_ci u32 flags, 88162306a36Sopenharmony_ci u64 produce_size, 88262306a36Sopenharmony_ci u64 consume_size, 88362306a36Sopenharmony_ci void *produce_q, 88462306a36Sopenharmony_ci void *consume_q) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int result; 88762306a36Sopenharmony_ci struct qp_guest_endpoint *entry; 88862306a36Sopenharmony_ci /* One page each for the queue headers. */ 88962306a36Sopenharmony_ci const u64 num_ppns = DIV_ROUND_UP(produce_size, PAGE_SIZE) + 89062306a36Sopenharmony_ci DIV_ROUND_UP(consume_size, PAGE_SIZE) + 2; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle)) { 89362306a36Sopenharmony_ci u32 context_id = vmci_get_context_id(); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci handle = vmci_make_handle(context_id, VMCI_INVALID_ID); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 89962306a36Sopenharmony_ci if (entry) { 90062306a36Sopenharmony_ci entry->qp.peer = peer; 90162306a36Sopenharmony_ci entry->qp.flags = flags; 90262306a36Sopenharmony_ci entry->qp.produce_size = produce_size; 90362306a36Sopenharmony_ci entry->qp.consume_size = consume_size; 90462306a36Sopenharmony_ci entry->qp.ref_count = 0; 90562306a36Sopenharmony_ci entry->num_ppns = num_ppns; 90662306a36Sopenharmony_ci entry->produce_q = produce_q; 90762306a36Sopenharmony_ci entry->consume_q = consume_q; 90862306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->qp.list_item); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* Add resource obj */ 91162306a36Sopenharmony_ci result = vmci_resource_add(&entry->resource, 91262306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_QPAIR_GUEST, 91362306a36Sopenharmony_ci handle); 91462306a36Sopenharmony_ci entry->qp.handle = vmci_resource_handle(&entry->resource); 91562306a36Sopenharmony_ci if ((result != VMCI_SUCCESS) || 91662306a36Sopenharmony_ci qp_list_find(&qp_guest_endpoints, entry->qp.handle)) { 91762306a36Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d", 91862306a36Sopenharmony_ci handle.context, handle.resource, result); 91962306a36Sopenharmony_ci kfree(entry); 92062306a36Sopenharmony_ci entry = NULL; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci return entry; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * Frees a qp_guest_endpoint structure. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_cistatic void qp_guest_endpoint_destroy(struct qp_guest_endpoint *entry) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci qp_free_ppn_set(&entry->ppn_set); 93262306a36Sopenharmony_ci qp_cleanup_queue_mutex(entry->produce_q, entry->consume_q); 93362306a36Sopenharmony_ci qp_free_queue(entry->produce_q, entry->qp.produce_size); 93462306a36Sopenharmony_ci qp_free_queue(entry->consume_q, entry->qp.consume_size); 93562306a36Sopenharmony_ci /* Unlink from resource hash table and free callback */ 93662306a36Sopenharmony_ci vmci_resource_remove(&entry->resource); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci kfree(entry); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/* 94262306a36Sopenharmony_ci * Helper to make a queue_pairAlloc hypercall when the driver is 94362306a36Sopenharmony_ci * supporting a guest device. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_cistatic int qp_alloc_hypercall(const struct qp_guest_endpoint *entry) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct vmci_qp_alloc_msg *alloc_msg; 94862306a36Sopenharmony_ci size_t msg_size; 94962306a36Sopenharmony_ci size_t ppn_size; 95062306a36Sopenharmony_ci int result; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (!entry || entry->num_ppns <= 2) 95362306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ppn_size = vmci_use_ppn64() ? sizeof(u64) : sizeof(u32); 95662306a36Sopenharmony_ci msg_size = sizeof(*alloc_msg) + 95762306a36Sopenharmony_ci (size_t) entry->num_ppns * ppn_size; 95862306a36Sopenharmony_ci alloc_msg = kmalloc(msg_size, GFP_KERNEL); 95962306a36Sopenharmony_ci if (!alloc_msg) 96062306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci alloc_msg->hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 96362306a36Sopenharmony_ci VMCI_QUEUEPAIR_ALLOC); 96462306a36Sopenharmony_ci alloc_msg->hdr.src = VMCI_ANON_SRC_HANDLE; 96562306a36Sopenharmony_ci alloc_msg->hdr.payload_size = msg_size - VMCI_DG_HEADERSIZE; 96662306a36Sopenharmony_ci alloc_msg->handle = entry->qp.handle; 96762306a36Sopenharmony_ci alloc_msg->peer = entry->qp.peer; 96862306a36Sopenharmony_ci alloc_msg->flags = entry->qp.flags; 96962306a36Sopenharmony_ci alloc_msg->produce_size = entry->qp.produce_size; 97062306a36Sopenharmony_ci alloc_msg->consume_size = entry->qp.consume_size; 97162306a36Sopenharmony_ci alloc_msg->num_ppns = entry->num_ppns; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci result = qp_populate_ppn_set((u8 *)alloc_msg + sizeof(*alloc_msg), 97462306a36Sopenharmony_ci &entry->ppn_set); 97562306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 97662306a36Sopenharmony_ci result = vmci_send_datagram(&alloc_msg->hdr); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci kfree(alloc_msg); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return result; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* 98462306a36Sopenharmony_ci * Helper to make a queue_pairDetach hypercall when the driver is 98562306a36Sopenharmony_ci * supporting a guest device. 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_cistatic int qp_detatch_hypercall(struct vmci_handle handle) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct vmci_qp_detach_msg detach_msg; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci detach_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 99262306a36Sopenharmony_ci VMCI_QUEUEPAIR_DETACH); 99362306a36Sopenharmony_ci detach_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 99462306a36Sopenharmony_ci detach_msg.hdr.payload_size = sizeof(handle); 99562306a36Sopenharmony_ci detach_msg.handle = handle; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return vmci_send_datagram(&detach_msg.hdr); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/* 100162306a36Sopenharmony_ci * Adds the given entry to the list. Assumes that the list is locked. 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_cistatic void qp_list_add_entry(struct qp_list *qp_list, struct qp_entry *entry) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci if (entry) 100662306a36Sopenharmony_ci list_add(&entry->list_item, &qp_list->head); 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci/* 101062306a36Sopenharmony_ci * Removes the given entry from the list. Assumes that the list is locked. 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_cistatic void qp_list_remove_entry(struct qp_list *qp_list, 101362306a36Sopenharmony_ci struct qp_entry *entry) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci if (entry) 101662306a36Sopenharmony_ci list_del(&entry->list_item); 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci/* 102062306a36Sopenharmony_ci * Helper for VMCI queue_pair detach interface. Frees the physical 102162306a36Sopenharmony_ci * pages for the queue pair. 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_cistatic int qp_detatch_guest_work(struct vmci_handle handle) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci int result; 102662306a36Sopenharmony_ci struct qp_guest_endpoint *entry; 102762306a36Sopenharmony_ci u32 ref_count = ~0; /* To avoid compiler warning below */ 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci entry = qp_guest_handle_to_entry(handle); 103262306a36Sopenharmony_ci if (!entry) { 103362306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 103462306a36Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (entry->qp.flags & VMCI_QPFLAG_LOCAL) { 103862306a36Sopenharmony_ci result = VMCI_SUCCESS; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (entry->qp.ref_count > 1) { 104162306a36Sopenharmony_ci result = qp_notify_peer_local(false, handle); 104262306a36Sopenharmony_ci /* 104362306a36Sopenharmony_ci * We can fail to notify a local queuepair 104462306a36Sopenharmony_ci * because we can't allocate. We still want 104562306a36Sopenharmony_ci * to release the entry if that happens, so 104662306a36Sopenharmony_ci * don't bail out yet. 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci } else { 105062306a36Sopenharmony_ci result = qp_detatch_hypercall(handle); 105162306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 105262306a36Sopenharmony_ci /* 105362306a36Sopenharmony_ci * We failed to notify a non-local queuepair. 105462306a36Sopenharmony_ci * That other queuepair might still be 105562306a36Sopenharmony_ci * accessing the shared memory, so don't 105662306a36Sopenharmony_ci * release the entry yet. It will get cleaned 105762306a36Sopenharmony_ci * up by VMCIqueue_pair_Exit() if necessary 105862306a36Sopenharmony_ci * (assuming we are going away, otherwise why 105962306a36Sopenharmony_ci * did this fail?). 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 106362306a36Sopenharmony_ci return result; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* 106862306a36Sopenharmony_ci * If we get here then we either failed to notify a local queuepair, or 106962306a36Sopenharmony_ci * we succeeded in all cases. Release the entry if required. 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci entry->qp.ref_count--; 107362306a36Sopenharmony_ci if (entry->qp.ref_count == 0) 107462306a36Sopenharmony_ci qp_list_remove_entry(&qp_guest_endpoints, &entry->qp); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* If we didn't remove the entry, this could change once we unlock. */ 107762306a36Sopenharmony_ci if (entry) 107862306a36Sopenharmony_ci ref_count = entry->qp.ref_count; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (ref_count == 0) 108362306a36Sopenharmony_ci qp_guest_endpoint_destroy(entry); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return result; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci/* 108962306a36Sopenharmony_ci * This functions handles the actual allocation of a VMCI queue 109062306a36Sopenharmony_ci * pair guest endpoint. Allocates physical pages for the queue 109162306a36Sopenharmony_ci * pair. It makes OS dependent calls through generic wrappers. 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_cistatic int qp_alloc_guest_work(struct vmci_handle *handle, 109462306a36Sopenharmony_ci struct vmci_queue **produce_q, 109562306a36Sopenharmony_ci u64 produce_size, 109662306a36Sopenharmony_ci struct vmci_queue **consume_q, 109762306a36Sopenharmony_ci u64 consume_size, 109862306a36Sopenharmony_ci u32 peer, 109962306a36Sopenharmony_ci u32 flags, 110062306a36Sopenharmony_ci u32 priv_flags) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci const u64 num_produce_pages = 110362306a36Sopenharmony_ci DIV_ROUND_UP(produce_size, PAGE_SIZE) + 1; 110462306a36Sopenharmony_ci const u64 num_consume_pages = 110562306a36Sopenharmony_ci DIV_ROUND_UP(consume_size, PAGE_SIZE) + 1; 110662306a36Sopenharmony_ci void *my_produce_q = NULL; 110762306a36Sopenharmony_ci void *my_consume_q = NULL; 110862306a36Sopenharmony_ci int result; 110962306a36Sopenharmony_ci struct qp_guest_endpoint *queue_pair_entry = NULL; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (priv_flags != VMCI_NO_PRIVILEGE_FLAGS) 111262306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci queue_pair_entry = qp_guest_handle_to_entry(*handle); 111762306a36Sopenharmony_ci if (queue_pair_entry) { 111862306a36Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) { 111962306a36Sopenharmony_ci /* Local attach case. */ 112062306a36Sopenharmony_ci if (queue_pair_entry->qp.ref_count > 1) { 112162306a36Sopenharmony_ci pr_devel("Error attempting to attach more than once\n"); 112262306a36Sopenharmony_ci result = VMCI_ERROR_UNAVAILABLE; 112362306a36Sopenharmony_ci goto error_keep_entry; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (queue_pair_entry->qp.produce_size != consume_size || 112762306a36Sopenharmony_ci queue_pair_entry->qp.consume_size != 112862306a36Sopenharmony_ci produce_size || 112962306a36Sopenharmony_ci queue_pair_entry->qp.flags != 113062306a36Sopenharmony_ci (flags & ~VMCI_QPFLAG_ATTACH_ONLY)) { 113162306a36Sopenharmony_ci pr_devel("Error mismatched queue pair in local attach\n"); 113262306a36Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_MISMATCH; 113362306a36Sopenharmony_ci goto error_keep_entry; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* 113762306a36Sopenharmony_ci * Do a local attach. We swap the consume and 113862306a36Sopenharmony_ci * produce queues for the attacher and deliver 113962306a36Sopenharmony_ci * an attach event. 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci result = qp_notify_peer_local(true, *handle); 114262306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 114362306a36Sopenharmony_ci goto error_keep_entry; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci my_produce_q = queue_pair_entry->consume_q; 114662306a36Sopenharmony_ci my_consume_q = queue_pair_entry->produce_q; 114762306a36Sopenharmony_ci goto out; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci result = VMCI_ERROR_ALREADY_EXISTS; 115162306a36Sopenharmony_ci goto error_keep_entry; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci my_produce_q = qp_alloc_queue(produce_size, flags); 115562306a36Sopenharmony_ci if (!my_produce_q) { 115662306a36Sopenharmony_ci pr_warn("Error allocating pages for produce queue\n"); 115762306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 115862306a36Sopenharmony_ci goto error; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci my_consume_q = qp_alloc_queue(consume_size, flags); 116262306a36Sopenharmony_ci if (!my_consume_q) { 116362306a36Sopenharmony_ci pr_warn("Error allocating pages for consume queue\n"); 116462306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 116562306a36Sopenharmony_ci goto error; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci queue_pair_entry = qp_guest_endpoint_create(*handle, peer, flags, 116962306a36Sopenharmony_ci produce_size, consume_size, 117062306a36Sopenharmony_ci my_produce_q, my_consume_q); 117162306a36Sopenharmony_ci if (!queue_pair_entry) { 117262306a36Sopenharmony_ci pr_warn("Error allocating memory in %s\n", __func__); 117362306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 117462306a36Sopenharmony_ci goto error; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci result = qp_alloc_ppn_set(my_produce_q, num_produce_pages, my_consume_q, 117862306a36Sopenharmony_ci num_consume_pages, 117962306a36Sopenharmony_ci &queue_pair_entry->ppn_set); 118062306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 118162306a36Sopenharmony_ci pr_warn("qp_alloc_ppn_set failed\n"); 118262306a36Sopenharmony_ci goto error; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* 118662306a36Sopenharmony_ci * It's only necessary to notify the host if this queue pair will be 118762306a36Sopenharmony_ci * attached to from another context. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) { 119062306a36Sopenharmony_ci /* Local create case. */ 119162306a36Sopenharmony_ci u32 context_id = vmci_get_context_id(); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* 119462306a36Sopenharmony_ci * Enforce similar checks on local queue pairs as we 119562306a36Sopenharmony_ci * do for regular ones. The handle's context must 119662306a36Sopenharmony_ci * match the creator or attacher context id (here they 119762306a36Sopenharmony_ci * are both the current context id) and the 119862306a36Sopenharmony_ci * attach-only flag cannot exist during create. We 119962306a36Sopenharmony_ci * also ensure specified peer is this context or an 120062306a36Sopenharmony_ci * invalid one. 120162306a36Sopenharmony_ci */ 120262306a36Sopenharmony_ci if (queue_pair_entry->qp.handle.context != context_id || 120362306a36Sopenharmony_ci (queue_pair_entry->qp.peer != VMCI_INVALID_ID && 120462306a36Sopenharmony_ci queue_pair_entry->qp.peer != context_id)) { 120562306a36Sopenharmony_ci result = VMCI_ERROR_NO_ACCESS; 120662306a36Sopenharmony_ci goto error; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_ATTACH_ONLY) { 121062306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 121162306a36Sopenharmony_ci goto error; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci } else { 121462306a36Sopenharmony_ci result = qp_alloc_hypercall(queue_pair_entry); 121562306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 121662306a36Sopenharmony_ci pr_devel("qp_alloc_hypercall result = %d\n", result); 121762306a36Sopenharmony_ci goto error; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci qp_init_queue_mutex((struct vmci_queue *)my_produce_q, 122262306a36Sopenharmony_ci (struct vmci_queue *)my_consume_q); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci qp_list_add_entry(&qp_guest_endpoints, &queue_pair_entry->qp); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci out: 122762306a36Sopenharmony_ci queue_pair_entry->qp.ref_count++; 122862306a36Sopenharmony_ci *handle = queue_pair_entry->qp.handle; 122962306a36Sopenharmony_ci *produce_q = (struct vmci_queue *)my_produce_q; 123062306a36Sopenharmony_ci *consume_q = (struct vmci_queue *)my_consume_q; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* 123362306a36Sopenharmony_ci * We should initialize the queue pair header pages on a local 123462306a36Sopenharmony_ci * queue pair create. For non-local queue pairs, the 123562306a36Sopenharmony_ci * hypervisor initializes the header pages in the create step. 123662306a36Sopenharmony_ci */ 123762306a36Sopenharmony_ci if ((queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) && 123862306a36Sopenharmony_ci queue_pair_entry->qp.ref_count == 1) { 123962306a36Sopenharmony_ci vmci_q_header_init((*produce_q)->q_header, *handle); 124062306a36Sopenharmony_ci vmci_q_header_init((*consume_q)->q_header, *handle); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return VMCI_SUCCESS; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci error: 124862306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 124962306a36Sopenharmony_ci if (queue_pair_entry) { 125062306a36Sopenharmony_ci /* The queues will be freed inside the destroy routine. */ 125162306a36Sopenharmony_ci qp_guest_endpoint_destroy(queue_pair_entry); 125262306a36Sopenharmony_ci } else { 125362306a36Sopenharmony_ci qp_free_queue(my_produce_q, produce_size); 125462306a36Sopenharmony_ci qp_free_queue(my_consume_q, consume_size); 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci return result; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci error_keep_entry: 125962306a36Sopenharmony_ci /* This path should only be used when an existing entry was found. */ 126062306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 126162306a36Sopenharmony_ci return result; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci/* 126562306a36Sopenharmony_ci * The first endpoint issuing a queue pair allocation will create the state 126662306a36Sopenharmony_ci * of the queue pair in the queue pair broker. 126762306a36Sopenharmony_ci * 126862306a36Sopenharmony_ci * If the creator is a guest, it will associate a VMX virtual address range 126962306a36Sopenharmony_ci * with the queue pair as specified by the page_store. For compatibility with 127062306a36Sopenharmony_ci * older VMX'en, that would use a separate step to set the VMX virtual 127162306a36Sopenharmony_ci * address range, the virtual address range can be registered later using 127262306a36Sopenharmony_ci * vmci_qp_broker_set_page_store. In that case, a page_store of NULL should be 127362306a36Sopenharmony_ci * used. 127462306a36Sopenharmony_ci * 127562306a36Sopenharmony_ci * If the creator is the host, a page_store of NULL should be used as well, 127662306a36Sopenharmony_ci * since the host is not able to supply a page store for the queue pair. 127762306a36Sopenharmony_ci * 127862306a36Sopenharmony_ci * For older VMX and host callers, the queue pair will be created in the 127962306a36Sopenharmony_ci * VMCIQPB_CREATED_NO_MEM state, and for current VMX callers, it will be 128062306a36Sopenharmony_ci * created in VMCOQPB_CREATED_MEM state. 128162306a36Sopenharmony_ci */ 128262306a36Sopenharmony_cistatic int qp_broker_create(struct vmci_handle handle, 128362306a36Sopenharmony_ci u32 peer, 128462306a36Sopenharmony_ci u32 flags, 128562306a36Sopenharmony_ci u32 priv_flags, 128662306a36Sopenharmony_ci u64 produce_size, 128762306a36Sopenharmony_ci u64 consume_size, 128862306a36Sopenharmony_ci struct vmci_qp_page_store *page_store, 128962306a36Sopenharmony_ci struct vmci_ctx *context, 129062306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb, 129162306a36Sopenharmony_ci void *client_data, struct qp_broker_entry **ent) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct qp_broker_entry *entry = NULL; 129462306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 129562306a36Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 129662306a36Sopenharmony_ci int result; 129762306a36Sopenharmony_ci u64 guest_produce_size; 129862306a36Sopenharmony_ci u64 guest_consume_size; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Do not create if the caller asked not to. */ 130162306a36Sopenharmony_ci if (flags & VMCI_QPFLAG_ATTACH_ONLY) 130262306a36Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci /* 130562306a36Sopenharmony_ci * Creator's context ID should match handle's context ID or the creator 130662306a36Sopenharmony_ci * must allow the context in handle's context ID as the "peer". 130762306a36Sopenharmony_ci */ 130862306a36Sopenharmony_ci if (handle.context != context_id && handle.context != peer) 130962306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(peer)) 131262306a36Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* 131562306a36Sopenharmony_ci * Creator's context ID for local queue pairs should match the 131662306a36Sopenharmony_ci * peer, if a peer is specified. 131762306a36Sopenharmony_ci */ 131862306a36Sopenharmony_ci if (is_local && peer != VMCI_INVALID_ID && context_id != peer) 131962306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 132262306a36Sopenharmony_ci if (!entry) 132362306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (vmci_ctx_get_id(context) == VMCI_HOST_CONTEXT_ID && !is_local) { 132662306a36Sopenharmony_ci /* 132762306a36Sopenharmony_ci * The queue pair broker entry stores values from the guest 132862306a36Sopenharmony_ci * point of view, so a creating host side endpoint should swap 132962306a36Sopenharmony_ci * produce and consume values -- unless it is a local queue 133062306a36Sopenharmony_ci * pair, in which case no swapping is necessary, since the local 133162306a36Sopenharmony_ci * attacher will swap queues. 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci guest_produce_size = consume_size; 133562306a36Sopenharmony_ci guest_consume_size = produce_size; 133662306a36Sopenharmony_ci } else { 133762306a36Sopenharmony_ci guest_produce_size = produce_size; 133862306a36Sopenharmony_ci guest_consume_size = consume_size; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci entry->qp.handle = handle; 134262306a36Sopenharmony_ci entry->qp.peer = peer; 134362306a36Sopenharmony_ci entry->qp.flags = flags; 134462306a36Sopenharmony_ci entry->qp.produce_size = guest_produce_size; 134562306a36Sopenharmony_ci entry->qp.consume_size = guest_consume_size; 134662306a36Sopenharmony_ci entry->qp.ref_count = 1; 134762306a36Sopenharmony_ci entry->create_id = context_id; 134862306a36Sopenharmony_ci entry->attach_id = VMCI_INVALID_ID; 134962306a36Sopenharmony_ci entry->state = VMCIQPB_NEW; 135062306a36Sopenharmony_ci entry->require_trusted_attach = 135162306a36Sopenharmony_ci !!(context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED); 135262306a36Sopenharmony_ci entry->created_by_trusted = 135362306a36Sopenharmony_ci !!(priv_flags & VMCI_PRIVILEGE_FLAG_TRUSTED); 135462306a36Sopenharmony_ci entry->vmci_page_files = false; 135562306a36Sopenharmony_ci entry->wakeup_cb = wakeup_cb; 135662306a36Sopenharmony_ci entry->client_data = client_data; 135762306a36Sopenharmony_ci entry->produce_q = qp_host_alloc_queue(guest_produce_size); 135862306a36Sopenharmony_ci if (entry->produce_q == NULL) { 135962306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 136062306a36Sopenharmony_ci goto error; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci entry->consume_q = qp_host_alloc_queue(guest_consume_size); 136362306a36Sopenharmony_ci if (entry->consume_q == NULL) { 136462306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 136562306a36Sopenharmony_ci goto error; 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci qp_init_queue_mutex(entry->produce_q, entry->consume_q); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->qp.list_item); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (is_local) { 137362306a36Sopenharmony_ci u8 *tmp; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci entry->local_mem = kcalloc(QPE_NUM_PAGES(entry->qp), 137662306a36Sopenharmony_ci PAGE_SIZE, GFP_KERNEL); 137762306a36Sopenharmony_ci if (entry->local_mem == NULL) { 137862306a36Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 137962306a36Sopenharmony_ci goto error; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 138262306a36Sopenharmony_ci entry->produce_q->q_header = entry->local_mem; 138362306a36Sopenharmony_ci tmp = (u8 *)entry->local_mem + PAGE_SIZE * 138462306a36Sopenharmony_ci (DIV_ROUND_UP(entry->qp.produce_size, PAGE_SIZE) + 1); 138562306a36Sopenharmony_ci entry->consume_q->q_header = (struct vmci_queue_header *)tmp; 138662306a36Sopenharmony_ci } else if (page_store) { 138762306a36Sopenharmony_ci /* 138862306a36Sopenharmony_ci * The VMX already initialized the queue pair headers, so no 138962306a36Sopenharmony_ci * need for the kernel side to do that. 139062306a36Sopenharmony_ci */ 139162306a36Sopenharmony_ci result = qp_host_register_user_memory(page_store, 139262306a36Sopenharmony_ci entry->produce_q, 139362306a36Sopenharmony_ci entry->consume_q); 139462306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 139562306a36Sopenharmony_ci goto error; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 139862306a36Sopenharmony_ci } else { 139962306a36Sopenharmony_ci /* 140062306a36Sopenharmony_ci * A create without a page_store may be either a host 140162306a36Sopenharmony_ci * side create (in which case we are waiting for the 140262306a36Sopenharmony_ci * guest side to supply the memory) or an old style 140362306a36Sopenharmony_ci * queue pair create (in which case we will expect a 140462306a36Sopenharmony_ci * set page store call as the next step). 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_ci entry->state = VMCIQPB_CREATED_NO_MEM; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci qp_list_add_entry(&qp_broker_list, &entry->qp); 141062306a36Sopenharmony_ci if (ent != NULL) 141162306a36Sopenharmony_ci *ent = entry; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* Add to resource obj */ 141462306a36Sopenharmony_ci result = vmci_resource_add(&entry->resource, 141562306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_QPAIR_HOST, 141662306a36Sopenharmony_ci handle); 141762306a36Sopenharmony_ci if (result != VMCI_SUCCESS) { 141862306a36Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d", 141962306a36Sopenharmony_ci handle.context, handle.resource, result); 142062306a36Sopenharmony_ci goto error; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci entry->qp.handle = vmci_resource_handle(&entry->resource); 142462306a36Sopenharmony_ci if (is_local) { 142562306a36Sopenharmony_ci vmci_q_header_init(entry->produce_q->q_header, 142662306a36Sopenharmony_ci entry->qp.handle); 142762306a36Sopenharmony_ci vmci_q_header_init(entry->consume_q->q_header, 142862306a36Sopenharmony_ci entry->qp.handle); 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci vmci_ctx_qp_create(context, entry->qp.handle); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci return VMCI_SUCCESS; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci error: 143662306a36Sopenharmony_ci if (entry != NULL) { 143762306a36Sopenharmony_ci qp_host_free_queue(entry->produce_q, guest_produce_size); 143862306a36Sopenharmony_ci qp_host_free_queue(entry->consume_q, guest_consume_size); 143962306a36Sopenharmony_ci kfree(entry); 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci return result; 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci/* 144662306a36Sopenharmony_ci * Enqueues an event datagram to notify the peer VM attached to 144762306a36Sopenharmony_ci * the given queue pair handle about attach/detach event by the 144862306a36Sopenharmony_ci * given VM. Returns Payload size of datagram enqueued on 144962306a36Sopenharmony_ci * success, error code otherwise. 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_cistatic int qp_notify_peer(bool attach, 145262306a36Sopenharmony_ci struct vmci_handle handle, 145362306a36Sopenharmony_ci u32 my_id, 145462306a36Sopenharmony_ci u32 peer_id) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci int rv; 145762306a36Sopenharmony_ci struct vmci_event_qp ev; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || my_id == VMCI_INVALID_ID || 146062306a36Sopenharmony_ci peer_id == VMCI_INVALID_ID) 146162306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* 146462306a36Sopenharmony_ci * In vmci_ctx_enqueue_datagram() we enforce the upper limit on 146562306a36Sopenharmony_ci * number of pending events from the hypervisor to a given VM 146662306a36Sopenharmony_ci * otherwise a rogue VM could do an arbitrary number of attach 146762306a36Sopenharmony_ci * and detach operations causing memory pressure in the host 146862306a36Sopenharmony_ci * kernel. 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 147262306a36Sopenharmony_ci ev.msg.hdr.dst = vmci_make_handle(peer_id, VMCI_EVENT_HANDLER); 147362306a36Sopenharmony_ci ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 147462306a36Sopenharmony_ci VMCI_CONTEXT_RESOURCE_ID); 147562306a36Sopenharmony_ci ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); 147662306a36Sopenharmony_ci ev.msg.event_data.event = attach ? 147762306a36Sopenharmony_ci VMCI_EVENT_QP_PEER_ATTACH : VMCI_EVENT_QP_PEER_DETACH; 147862306a36Sopenharmony_ci ev.payload.handle = handle; 147962306a36Sopenharmony_ci ev.payload.peer_id = my_id; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci rv = vmci_datagram_dispatch(VMCI_HYPERVISOR_CONTEXT_ID, 148262306a36Sopenharmony_ci &ev.msg.hdr, false); 148362306a36Sopenharmony_ci if (rv < VMCI_SUCCESS) 148462306a36Sopenharmony_ci pr_warn("Failed to enqueue queue_pair %s event datagram for context (ID=0x%x)\n", 148562306a36Sopenharmony_ci attach ? "ATTACH" : "DETACH", peer_id); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return rv; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci/* 149162306a36Sopenharmony_ci * The second endpoint issuing a queue pair allocation will attach to 149262306a36Sopenharmony_ci * the queue pair registered with the queue pair broker. 149362306a36Sopenharmony_ci * 149462306a36Sopenharmony_ci * If the attacher is a guest, it will associate a VMX virtual address 149562306a36Sopenharmony_ci * range with the queue pair as specified by the page_store. At this 149662306a36Sopenharmony_ci * point, the already attach host endpoint may start using the queue 149762306a36Sopenharmony_ci * pair, and an attach event is sent to it. For compatibility with 149862306a36Sopenharmony_ci * older VMX'en, that used a separate step to set the VMX virtual 149962306a36Sopenharmony_ci * address range, the virtual address range can be registered later 150062306a36Sopenharmony_ci * using vmci_qp_broker_set_page_store. In that case, a page_store of 150162306a36Sopenharmony_ci * NULL should be used, and the attach event will be generated once 150262306a36Sopenharmony_ci * the actual page store has been set. 150362306a36Sopenharmony_ci * 150462306a36Sopenharmony_ci * If the attacher is the host, a page_store of NULL should be used as 150562306a36Sopenharmony_ci * well, since the page store information is already set by the guest. 150662306a36Sopenharmony_ci * 150762306a36Sopenharmony_ci * For new VMX and host callers, the queue pair will be moved to the 150862306a36Sopenharmony_ci * VMCIQPB_ATTACHED_MEM state, and for older VMX callers, it will be 150962306a36Sopenharmony_ci * moved to the VMCOQPB_ATTACHED_NO_MEM state. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_cistatic int qp_broker_attach(struct qp_broker_entry *entry, 151262306a36Sopenharmony_ci u32 peer, 151362306a36Sopenharmony_ci u32 flags, 151462306a36Sopenharmony_ci u32 priv_flags, 151562306a36Sopenharmony_ci u64 produce_size, 151662306a36Sopenharmony_ci u64 consume_size, 151762306a36Sopenharmony_ci struct vmci_qp_page_store *page_store, 151862306a36Sopenharmony_ci struct vmci_ctx *context, 151962306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb, 152062306a36Sopenharmony_ci void *client_data, 152162306a36Sopenharmony_ci struct qp_broker_entry **ent) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 152462306a36Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 152562306a36Sopenharmony_ci int result; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM && 152862306a36Sopenharmony_ci entry->state != VMCIQPB_CREATED_MEM) 152962306a36Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (is_local) { 153262306a36Sopenharmony_ci if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL) || 153362306a36Sopenharmony_ci context_id != entry->create_id) { 153462306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci } else if (context_id == entry->create_id || 153762306a36Sopenharmony_ci context_id == entry->attach_id) { 153862306a36Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci if (VMCI_CONTEXT_IS_VM(context_id) && 154262306a36Sopenharmony_ci VMCI_CONTEXT_IS_VM(entry->create_id)) 154362306a36Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* 154662306a36Sopenharmony_ci * If we are attaching from a restricted context then the queuepair 154762306a36Sopenharmony_ci * must have been created by a trusted endpoint. 154862306a36Sopenharmony_ci */ 154962306a36Sopenharmony_ci if ((context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED) && 155062306a36Sopenharmony_ci !entry->created_by_trusted) 155162306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci /* 155462306a36Sopenharmony_ci * If we are attaching to a queuepair that was created by a restricted 155562306a36Sopenharmony_ci * context then we must be trusted. 155662306a36Sopenharmony_ci */ 155762306a36Sopenharmony_ci if (entry->require_trusted_attach && 155862306a36Sopenharmony_ci (!(priv_flags & VMCI_PRIVILEGE_FLAG_TRUSTED))) 155962306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* 156262306a36Sopenharmony_ci * If the creator specifies VMCI_INVALID_ID in "peer" field, access 156362306a36Sopenharmony_ci * control check is not performed. 156462306a36Sopenharmony_ci */ 156562306a36Sopenharmony_ci if (entry->qp.peer != VMCI_INVALID_ID && entry->qp.peer != context_id) 156662306a36Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (entry->create_id == VMCI_HOST_CONTEXT_ID) { 156962306a36Sopenharmony_ci /* 157062306a36Sopenharmony_ci * Do not attach if the caller doesn't support Host Queue Pairs 157162306a36Sopenharmony_ci * and a host created this queue pair. 157262306a36Sopenharmony_ci */ 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci if (!vmci_ctx_supports_host_qp(context)) 157562306a36Sopenharmony_ci return VMCI_ERROR_INVALID_RESOURCE; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci } else if (context_id == VMCI_HOST_CONTEXT_ID) { 157862306a36Sopenharmony_ci struct vmci_ctx *create_context; 157962306a36Sopenharmony_ci bool supports_host_qp; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci /* 158262306a36Sopenharmony_ci * Do not attach a host to a user created queue pair if that 158362306a36Sopenharmony_ci * user doesn't support host queue pair end points. 158462306a36Sopenharmony_ci */ 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci create_context = vmci_ctx_get(entry->create_id); 158762306a36Sopenharmony_ci supports_host_qp = vmci_ctx_supports_host_qp(create_context); 158862306a36Sopenharmony_ci vmci_ctx_put(create_context); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (!supports_host_qp) 159162306a36Sopenharmony_ci return VMCI_ERROR_INVALID_RESOURCE; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if ((entry->qp.flags & ~VMCI_QP_ASYMM) != (flags & ~VMCI_QP_ASYMM_PEER)) 159562306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * The queue pair broker entry stores values from the guest 160062306a36Sopenharmony_ci * point of view, so an attaching guest should match the values 160162306a36Sopenharmony_ci * stored in the entry. 160262306a36Sopenharmony_ci */ 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (entry->qp.produce_size != produce_size || 160562306a36Sopenharmony_ci entry->qp.consume_size != consume_size) { 160662306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci } else if (entry->qp.produce_size != consume_size || 160962306a36Sopenharmony_ci entry->qp.consume_size != produce_size) { 161062306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 161462306a36Sopenharmony_ci /* 161562306a36Sopenharmony_ci * If a guest attached to a queue pair, it will supply 161662306a36Sopenharmony_ci * the backing memory. If this is a pre NOVMVM vmx, 161762306a36Sopenharmony_ci * the backing memory will be supplied by calling 161862306a36Sopenharmony_ci * vmci_qp_broker_set_page_store() following the 161962306a36Sopenharmony_ci * return of the vmci_qp_broker_alloc() call. If it is 162062306a36Sopenharmony_ci * a vmx of version NOVMVM or later, the page store 162162306a36Sopenharmony_ci * must be supplied as part of the 162262306a36Sopenharmony_ci * vmci_qp_broker_alloc call. Under all circumstances 162362306a36Sopenharmony_ci * must the initially created queue pair not have any 162462306a36Sopenharmony_ci * memory associated with it already. 162562306a36Sopenharmony_ci */ 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM) 162862306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci if (page_store != NULL) { 163162306a36Sopenharmony_ci /* 163262306a36Sopenharmony_ci * Patch up host state to point to guest 163362306a36Sopenharmony_ci * supplied memory. The VMX already 163462306a36Sopenharmony_ci * initialized the queue pair headers, so no 163562306a36Sopenharmony_ci * need for the kernel side to do that. 163662306a36Sopenharmony_ci */ 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci result = qp_host_register_user_memory(page_store, 163962306a36Sopenharmony_ci entry->produce_q, 164062306a36Sopenharmony_ci entry->consume_q); 164162306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 164262306a36Sopenharmony_ci return result; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 164562306a36Sopenharmony_ci } else { 164662306a36Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_NO_MEM; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci } else if (entry->state == VMCIQPB_CREATED_NO_MEM) { 164962306a36Sopenharmony_ci /* 165062306a36Sopenharmony_ci * The host side is attempting to attach to a queue 165162306a36Sopenharmony_ci * pair that doesn't have any memory associated with 165262306a36Sopenharmony_ci * it. This must be a pre NOVMVM vmx that hasn't set 165362306a36Sopenharmony_ci * the page store information yet, or a quiesced VM. 165462306a36Sopenharmony_ci */ 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 165762306a36Sopenharmony_ci } else { 165862306a36Sopenharmony_ci /* The host side has successfully attached to a queue pair. */ 165962306a36Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci if (entry->state == VMCIQPB_ATTACHED_MEM) { 166362306a36Sopenharmony_ci result = 166462306a36Sopenharmony_ci qp_notify_peer(true, entry->qp.handle, context_id, 166562306a36Sopenharmony_ci entry->create_id); 166662306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 166762306a36Sopenharmony_ci pr_warn("Failed to notify peer (ID=0x%x) of attach to queue pair (handle=0x%x:0x%x)\n", 166862306a36Sopenharmony_ci entry->create_id, entry->qp.handle.context, 166962306a36Sopenharmony_ci entry->qp.handle.resource); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci entry->attach_id = context_id; 167362306a36Sopenharmony_ci entry->qp.ref_count++; 167462306a36Sopenharmony_ci if (wakeup_cb) { 167562306a36Sopenharmony_ci entry->wakeup_cb = wakeup_cb; 167662306a36Sopenharmony_ci entry->client_data = client_data; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci /* 168062306a36Sopenharmony_ci * When attaching to local queue pairs, the context already has 168162306a36Sopenharmony_ci * an entry tracking the queue pair, so don't add another one. 168262306a36Sopenharmony_ci */ 168362306a36Sopenharmony_ci if (!is_local) 168462306a36Sopenharmony_ci vmci_ctx_qp_create(context, entry->qp.handle); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (ent != NULL) 168762306a36Sopenharmony_ci *ent = entry; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci return VMCI_SUCCESS; 169062306a36Sopenharmony_ci} 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci/* 169362306a36Sopenharmony_ci * queue_pair_Alloc for use when setting up queue pair endpoints 169462306a36Sopenharmony_ci * on the host. 169562306a36Sopenharmony_ci */ 169662306a36Sopenharmony_cistatic int qp_broker_alloc(struct vmci_handle handle, 169762306a36Sopenharmony_ci u32 peer, 169862306a36Sopenharmony_ci u32 flags, 169962306a36Sopenharmony_ci u32 priv_flags, 170062306a36Sopenharmony_ci u64 produce_size, 170162306a36Sopenharmony_ci u64 consume_size, 170262306a36Sopenharmony_ci struct vmci_qp_page_store *page_store, 170362306a36Sopenharmony_ci struct vmci_ctx *context, 170462306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb, 170562306a36Sopenharmony_ci void *client_data, 170662306a36Sopenharmony_ci struct qp_broker_entry **ent, 170762306a36Sopenharmony_ci bool *swap) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 171062306a36Sopenharmony_ci bool create; 171162306a36Sopenharmony_ci struct qp_broker_entry *entry = NULL; 171262306a36Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 171362306a36Sopenharmony_ci int result; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || 171662306a36Sopenharmony_ci (flags & ~VMCI_QP_ALL_FLAGS) || is_local || 171762306a36Sopenharmony_ci !(produce_size || consume_size) || 171862306a36Sopenharmony_ci !context || context_id == VMCI_INVALID_ID || 171962306a36Sopenharmony_ci handle.context == VMCI_INVALID_ID) { 172062306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 172162306a36Sopenharmony_ci } 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (page_store && !VMCI_QP_PAGESTORE_IS_WELLFORMED(page_store)) 172462306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* 172762306a36Sopenharmony_ci * In the initial argument check, we ensure that non-vmkernel hosts 172862306a36Sopenharmony_ci * are not allowed to create local queue pairs. 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci if (!is_local && vmci_ctx_qp_exists(context, handle)) { 173462306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) already attached to queue pair (handle=0x%x:0x%x)\n", 173562306a36Sopenharmony_ci context_id, handle.context, handle.resource); 173662306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 173762306a36Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 173862306a36Sopenharmony_ci } 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (handle.resource != VMCI_INVALID_ID) 174162306a36Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (!entry) { 174462306a36Sopenharmony_ci create = true; 174562306a36Sopenharmony_ci result = 174662306a36Sopenharmony_ci qp_broker_create(handle, peer, flags, priv_flags, 174762306a36Sopenharmony_ci produce_size, consume_size, page_store, 174862306a36Sopenharmony_ci context, wakeup_cb, client_data, ent); 174962306a36Sopenharmony_ci } else { 175062306a36Sopenharmony_ci create = false; 175162306a36Sopenharmony_ci result = 175262306a36Sopenharmony_ci qp_broker_attach(entry, peer, flags, priv_flags, 175362306a36Sopenharmony_ci produce_size, consume_size, page_store, 175462306a36Sopenharmony_ci context, wakeup_cb, client_data, ent); 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (swap) 176062306a36Sopenharmony_ci *swap = (context_id == VMCI_HOST_CONTEXT_ID) && 176162306a36Sopenharmony_ci !(create && is_local); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci return result; 176462306a36Sopenharmony_ci} 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci/* 176762306a36Sopenharmony_ci * This function implements the kernel API for allocating a queue 176862306a36Sopenharmony_ci * pair. 176962306a36Sopenharmony_ci */ 177062306a36Sopenharmony_cistatic int qp_alloc_host_work(struct vmci_handle *handle, 177162306a36Sopenharmony_ci struct vmci_queue **produce_q, 177262306a36Sopenharmony_ci u64 produce_size, 177362306a36Sopenharmony_ci struct vmci_queue **consume_q, 177462306a36Sopenharmony_ci u64 consume_size, 177562306a36Sopenharmony_ci u32 peer, 177662306a36Sopenharmony_ci u32 flags, 177762306a36Sopenharmony_ci u32 priv_flags, 177862306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb, 177962306a36Sopenharmony_ci void *client_data) 178062306a36Sopenharmony_ci{ 178162306a36Sopenharmony_ci struct vmci_handle new_handle; 178262306a36Sopenharmony_ci struct vmci_ctx *context; 178362306a36Sopenharmony_ci struct qp_broker_entry *entry; 178462306a36Sopenharmony_ci int result; 178562306a36Sopenharmony_ci bool swap; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci if (vmci_handle_is_invalid(*handle)) { 178862306a36Sopenharmony_ci new_handle = vmci_make_handle( 178962306a36Sopenharmony_ci VMCI_HOST_CONTEXT_ID, VMCI_INVALID_ID); 179062306a36Sopenharmony_ci } else 179162306a36Sopenharmony_ci new_handle = *handle; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci context = vmci_ctx_get(VMCI_HOST_CONTEXT_ID); 179462306a36Sopenharmony_ci entry = NULL; 179562306a36Sopenharmony_ci result = 179662306a36Sopenharmony_ci qp_broker_alloc(new_handle, peer, flags, priv_flags, 179762306a36Sopenharmony_ci produce_size, consume_size, NULL, context, 179862306a36Sopenharmony_ci wakeup_cb, client_data, &entry, &swap); 179962306a36Sopenharmony_ci if (result == VMCI_SUCCESS) { 180062306a36Sopenharmony_ci if (swap) { 180162306a36Sopenharmony_ci /* 180262306a36Sopenharmony_ci * If this is a local queue pair, the attacher 180362306a36Sopenharmony_ci * will swap around produce and consume 180462306a36Sopenharmony_ci * queues. 180562306a36Sopenharmony_ci */ 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci *produce_q = entry->consume_q; 180862306a36Sopenharmony_ci *consume_q = entry->produce_q; 180962306a36Sopenharmony_ci } else { 181062306a36Sopenharmony_ci *produce_q = entry->produce_q; 181162306a36Sopenharmony_ci *consume_q = entry->consume_q; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci *handle = vmci_resource_handle(&entry->resource); 181562306a36Sopenharmony_ci } else { 181662306a36Sopenharmony_ci *handle = VMCI_INVALID_HANDLE; 181762306a36Sopenharmony_ci pr_devel("queue pair broker failed to alloc (result=%d)\n", 181862306a36Sopenharmony_ci result); 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci vmci_ctx_put(context); 182162306a36Sopenharmony_ci return result; 182262306a36Sopenharmony_ci} 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci/* 182562306a36Sopenharmony_ci * Allocates a VMCI queue_pair. Only checks validity of input 182662306a36Sopenharmony_ci * arguments. The real work is done in the host or guest 182762306a36Sopenharmony_ci * specific function. 182862306a36Sopenharmony_ci */ 182962306a36Sopenharmony_ciint vmci_qp_alloc(struct vmci_handle *handle, 183062306a36Sopenharmony_ci struct vmci_queue **produce_q, 183162306a36Sopenharmony_ci u64 produce_size, 183262306a36Sopenharmony_ci struct vmci_queue **consume_q, 183362306a36Sopenharmony_ci u64 consume_size, 183462306a36Sopenharmony_ci u32 peer, 183562306a36Sopenharmony_ci u32 flags, 183662306a36Sopenharmony_ci u32 priv_flags, 183762306a36Sopenharmony_ci bool guest_endpoint, 183862306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb, 183962306a36Sopenharmony_ci void *client_data) 184062306a36Sopenharmony_ci{ 184162306a36Sopenharmony_ci if (!handle || !produce_q || !consume_q || 184262306a36Sopenharmony_ci (!produce_size && !consume_size) || (flags & ~VMCI_QP_ALL_FLAGS)) 184362306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci if (guest_endpoint) { 184662306a36Sopenharmony_ci return qp_alloc_guest_work(handle, produce_q, 184762306a36Sopenharmony_ci produce_size, consume_q, 184862306a36Sopenharmony_ci consume_size, peer, 184962306a36Sopenharmony_ci flags, priv_flags); 185062306a36Sopenharmony_ci } else { 185162306a36Sopenharmony_ci return qp_alloc_host_work(handle, produce_q, 185262306a36Sopenharmony_ci produce_size, consume_q, 185362306a36Sopenharmony_ci consume_size, peer, flags, 185462306a36Sopenharmony_ci priv_flags, wakeup_cb, client_data); 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci} 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci/* 185962306a36Sopenharmony_ci * This function implements the host kernel API for detaching from 186062306a36Sopenharmony_ci * a queue pair. 186162306a36Sopenharmony_ci */ 186262306a36Sopenharmony_cistatic int qp_detatch_host_work(struct vmci_handle handle) 186362306a36Sopenharmony_ci{ 186462306a36Sopenharmony_ci int result; 186562306a36Sopenharmony_ci struct vmci_ctx *context; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci context = vmci_ctx_get(VMCI_HOST_CONTEXT_ID); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci result = vmci_qp_broker_detach(handle, context); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci vmci_ctx_put(context); 187262306a36Sopenharmony_ci return result; 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci/* 187662306a36Sopenharmony_ci * Detaches from a VMCI queue_pair. Only checks validity of input argument. 187762306a36Sopenharmony_ci * Real work is done in the host or guest specific function. 187862306a36Sopenharmony_ci */ 187962306a36Sopenharmony_cistatic int qp_detatch(struct vmci_handle handle, bool guest_endpoint) 188062306a36Sopenharmony_ci{ 188162306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 188262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (guest_endpoint) 188562306a36Sopenharmony_ci return qp_detatch_guest_work(handle); 188662306a36Sopenharmony_ci else 188762306a36Sopenharmony_ci return qp_detatch_host_work(handle); 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci/* 189162306a36Sopenharmony_ci * Returns the entry from the head of the list. Assumes that the list is 189262306a36Sopenharmony_ci * locked. 189362306a36Sopenharmony_ci */ 189462306a36Sopenharmony_cistatic struct qp_entry *qp_list_get_head(struct qp_list *qp_list) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci if (!list_empty(&qp_list->head)) { 189762306a36Sopenharmony_ci struct qp_entry *entry = 189862306a36Sopenharmony_ci list_first_entry(&qp_list->head, struct qp_entry, 189962306a36Sopenharmony_ci list_item); 190062306a36Sopenharmony_ci return entry; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return NULL; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_civoid vmci_qp_broker_exit(void) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct qp_entry *entry; 190962306a36Sopenharmony_ci struct qp_broker_entry *be; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci while ((entry = qp_list_get_head(&qp_broker_list))) { 191462306a36Sopenharmony_ci be = (struct qp_broker_entry *)entry; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci qp_list_remove_entry(&qp_broker_list, entry); 191762306a36Sopenharmony_ci kfree(be); 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci/* 192462306a36Sopenharmony_ci * Requests that a queue pair be allocated with the VMCI queue 192562306a36Sopenharmony_ci * pair broker. Allocates a queue pair entry if one does not 192662306a36Sopenharmony_ci * exist. Attaches to one if it exists, and retrieves the page 192762306a36Sopenharmony_ci * files backing that queue_pair. Assumes that the queue pair 192862306a36Sopenharmony_ci * broker lock is held. 192962306a36Sopenharmony_ci */ 193062306a36Sopenharmony_ciint vmci_qp_broker_alloc(struct vmci_handle handle, 193162306a36Sopenharmony_ci u32 peer, 193262306a36Sopenharmony_ci u32 flags, 193362306a36Sopenharmony_ci u32 priv_flags, 193462306a36Sopenharmony_ci u64 produce_size, 193562306a36Sopenharmony_ci u64 consume_size, 193662306a36Sopenharmony_ci struct vmci_qp_page_store *page_store, 193762306a36Sopenharmony_ci struct vmci_ctx *context) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci if (!QP_SIZES_ARE_VALID(produce_size, consume_size)) 194062306a36Sopenharmony_ci return VMCI_ERROR_NO_RESOURCES; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci return qp_broker_alloc(handle, peer, flags, priv_flags, 194362306a36Sopenharmony_ci produce_size, consume_size, 194462306a36Sopenharmony_ci page_store, context, NULL, NULL, NULL, NULL); 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci/* 194862306a36Sopenharmony_ci * VMX'en with versions lower than VMCI_VERSION_NOVMVM use a separate 194962306a36Sopenharmony_ci * step to add the UVAs of the VMX mapping of the queue pair. This function 195062306a36Sopenharmony_ci * provides backwards compatibility with such VMX'en, and takes care of 195162306a36Sopenharmony_ci * registering the page store for a queue pair previously allocated by the 195262306a36Sopenharmony_ci * VMX during create or attach. This function will move the queue pair state 195362306a36Sopenharmony_ci * to either from VMCIQBP_CREATED_NO_MEM to VMCIQBP_CREATED_MEM or 195462306a36Sopenharmony_ci * VMCIQBP_ATTACHED_NO_MEM to VMCIQBP_ATTACHED_MEM. If moving to the 195562306a36Sopenharmony_ci * attached state with memory, the queue pair is ready to be used by the 195662306a36Sopenharmony_ci * host peer, and an attached event will be generated. 195762306a36Sopenharmony_ci * 195862306a36Sopenharmony_ci * Assumes that the queue pair broker lock is held. 195962306a36Sopenharmony_ci * 196062306a36Sopenharmony_ci * This function is only used by the hosted platform, since there is no 196162306a36Sopenharmony_ci * issue with backwards compatibility for vmkernel. 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_ciint vmci_qp_broker_set_page_store(struct vmci_handle handle, 196462306a36Sopenharmony_ci u64 produce_uva, 196562306a36Sopenharmony_ci u64 consume_uva, 196662306a36Sopenharmony_ci struct vmci_ctx *context) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci struct qp_broker_entry *entry; 196962306a36Sopenharmony_ci int result; 197062306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 197362306a36Sopenharmony_ci context_id == VMCI_INVALID_ID) 197462306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* 197762306a36Sopenharmony_ci * We only support guest to host queue pairs, so the VMX must 197862306a36Sopenharmony_ci * supply UVAs for the mapped page files. 197962306a36Sopenharmony_ci */ 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci if (produce_uva == 0 || consume_uva == 0) 198262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 198762306a36Sopenharmony_ci pr_warn("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 198862306a36Sopenharmony_ci context_id, handle.context, handle.resource); 198962306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 199062306a36Sopenharmony_ci goto out; 199162306a36Sopenharmony_ci } 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 199462306a36Sopenharmony_ci if (!entry) { 199562306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 199662306a36Sopenharmony_ci goto out; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* 200062306a36Sopenharmony_ci * If I'm the owner then I can set the page store. 200162306a36Sopenharmony_ci * 200262306a36Sopenharmony_ci * Or, if a host created the queue_pair and I'm the attached peer 200362306a36Sopenharmony_ci * then I can set the page store. 200462306a36Sopenharmony_ci */ 200562306a36Sopenharmony_ci if (entry->create_id != context_id && 200662306a36Sopenharmony_ci (entry->create_id != VMCI_HOST_CONTEXT_ID || 200762306a36Sopenharmony_ci entry->attach_id != context_id)) { 200862306a36Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTOWNER; 200962306a36Sopenharmony_ci goto out; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM && 201362306a36Sopenharmony_ci entry->state != VMCIQPB_ATTACHED_NO_MEM) { 201462306a36Sopenharmony_ci result = VMCI_ERROR_UNAVAILABLE; 201562306a36Sopenharmony_ci goto out; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci result = qp_host_get_user_memory(produce_uva, consume_uva, 201962306a36Sopenharmony_ci entry->produce_q, entry->consume_q); 202062306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 202162306a36Sopenharmony_ci goto out; 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci result = qp_host_map_queues(entry->produce_q, entry->consume_q); 202462306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 202562306a36Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 202662306a36Sopenharmony_ci entry->consume_q); 202762306a36Sopenharmony_ci goto out; 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (entry->state == VMCIQPB_CREATED_NO_MEM) 203162306a36Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 203262306a36Sopenharmony_ci else 203362306a36Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci entry->vmci_page_files = true; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (entry->state == VMCIQPB_ATTACHED_MEM) { 203862306a36Sopenharmony_ci result = 203962306a36Sopenharmony_ci qp_notify_peer(true, handle, context_id, entry->create_id); 204062306a36Sopenharmony_ci if (result < VMCI_SUCCESS) { 204162306a36Sopenharmony_ci pr_warn("Failed to notify peer (ID=0x%x) of attach to queue pair (handle=0x%x:0x%x)\n", 204262306a36Sopenharmony_ci entry->create_id, entry->qp.handle.context, 204362306a36Sopenharmony_ci entry->qp.handle.resource); 204462306a36Sopenharmony_ci } 204562306a36Sopenharmony_ci } 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci result = VMCI_SUCCESS; 204862306a36Sopenharmony_ci out: 204962306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 205062306a36Sopenharmony_ci return result; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci/* 205462306a36Sopenharmony_ci * Resets saved queue headers for the given QP broker 205562306a36Sopenharmony_ci * entry. Should be used when guest memory becomes available 205662306a36Sopenharmony_ci * again, or the guest detaches. 205762306a36Sopenharmony_ci */ 205862306a36Sopenharmony_cistatic void qp_reset_saved_headers(struct qp_broker_entry *entry) 205962306a36Sopenharmony_ci{ 206062306a36Sopenharmony_ci entry->produce_q->saved_header = NULL; 206162306a36Sopenharmony_ci entry->consume_q->saved_header = NULL; 206262306a36Sopenharmony_ci} 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci/* 206562306a36Sopenharmony_ci * The main entry point for detaching from a queue pair registered with the 206662306a36Sopenharmony_ci * queue pair broker. If more than one endpoint is attached to the queue 206762306a36Sopenharmony_ci * pair, the first endpoint will mainly decrement a reference count and 206862306a36Sopenharmony_ci * generate a notification to its peer. The last endpoint will clean up 206962306a36Sopenharmony_ci * the queue pair state registered with the broker. 207062306a36Sopenharmony_ci * 207162306a36Sopenharmony_ci * When a guest endpoint detaches, it will unmap and unregister the guest 207262306a36Sopenharmony_ci * memory backing the queue pair. If the host is still attached, it will 207362306a36Sopenharmony_ci * no longer be able to access the queue pair content. 207462306a36Sopenharmony_ci * 207562306a36Sopenharmony_ci * If the queue pair is already in a state where there is no memory 207662306a36Sopenharmony_ci * registered for the queue pair (any *_NO_MEM state), it will transition to 207762306a36Sopenharmony_ci * the VMCIQPB_SHUTDOWN_NO_MEM state. This will also happen, if a guest 207862306a36Sopenharmony_ci * endpoint is the first of two endpoints to detach. If the host endpoint is 207962306a36Sopenharmony_ci * the first out of two to detach, the queue pair will move to the 208062306a36Sopenharmony_ci * VMCIQPB_SHUTDOWN_MEM state. 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ciint vmci_qp_broker_detach(struct vmci_handle handle, struct vmci_ctx *context) 208362306a36Sopenharmony_ci{ 208462306a36Sopenharmony_ci struct qp_broker_entry *entry; 208562306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 208662306a36Sopenharmony_ci u32 peer_id; 208762306a36Sopenharmony_ci bool is_local = false; 208862306a36Sopenharmony_ci int result; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 209162306a36Sopenharmony_ci context_id == VMCI_INVALID_ID) { 209262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 209362306a36Sopenharmony_ci } 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 209862306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 209962306a36Sopenharmony_ci context_id, handle.context, handle.resource); 210062306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 210162306a36Sopenharmony_ci goto out; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 210562306a36Sopenharmony_ci if (!entry) { 210662306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) reports being attached to queue pair(handle=0x%x:0x%x) that isn't present in broker\n", 210762306a36Sopenharmony_ci context_id, handle.context, handle.resource); 210862306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 210962306a36Sopenharmony_ci goto out; 211062306a36Sopenharmony_ci } 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 211362306a36Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 211462306a36Sopenharmony_ci goto out; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci if (context_id == entry->create_id) { 211862306a36Sopenharmony_ci peer_id = entry->attach_id; 211962306a36Sopenharmony_ci entry->create_id = VMCI_INVALID_ID; 212062306a36Sopenharmony_ci } else { 212162306a36Sopenharmony_ci peer_id = entry->create_id; 212262306a36Sopenharmony_ci entry->attach_id = VMCI_INVALID_ID; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci entry->qp.ref_count--; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci is_local = entry->qp.flags & VMCI_QPFLAG_LOCAL; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 212962306a36Sopenharmony_ci bool headers_mapped; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci /* 213262306a36Sopenharmony_ci * Pre NOVMVM vmx'en may detach from a queue pair 213362306a36Sopenharmony_ci * before setting the page store, and in that case 213462306a36Sopenharmony_ci * there is no user memory to detach from. Also, more 213562306a36Sopenharmony_ci * recent VMX'en may detach from a queue pair in the 213662306a36Sopenharmony_ci * quiesced state. 213762306a36Sopenharmony_ci */ 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 214062306a36Sopenharmony_ci headers_mapped = entry->produce_q->q_header || 214162306a36Sopenharmony_ci entry->consume_q->q_header; 214262306a36Sopenharmony_ci if (QPBROKERSTATE_HAS_MEM(entry)) { 214362306a36Sopenharmony_ci result = 214462306a36Sopenharmony_ci qp_host_unmap_queues(INVALID_VMCI_GUEST_MEM_ID, 214562306a36Sopenharmony_ci entry->produce_q, 214662306a36Sopenharmony_ci entry->consume_q); 214762306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 214862306a36Sopenharmony_ci pr_warn("Failed to unmap queue headers for queue pair (handle=0x%x:0x%x,result=%d)\n", 214962306a36Sopenharmony_ci handle.context, handle.resource, 215062306a36Sopenharmony_ci result); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 215362306a36Sopenharmony_ci entry->consume_q); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci if (!headers_mapped) 215862306a36Sopenharmony_ci qp_reset_saved_headers(entry); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (!headers_mapped && entry->wakeup_cb) 216362306a36Sopenharmony_ci entry->wakeup_cb(entry->client_data); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci } else { 216662306a36Sopenharmony_ci if (entry->wakeup_cb) { 216762306a36Sopenharmony_ci entry->wakeup_cb = NULL; 216862306a36Sopenharmony_ci entry->client_data = NULL; 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci if (entry->qp.ref_count == 0) { 217362306a36Sopenharmony_ci qp_list_remove_entry(&qp_broker_list, &entry->qp); 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci if (is_local) 217662306a36Sopenharmony_ci kfree(entry->local_mem); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci qp_cleanup_queue_mutex(entry->produce_q, entry->consume_q); 217962306a36Sopenharmony_ci qp_host_free_queue(entry->produce_q, entry->qp.produce_size); 218062306a36Sopenharmony_ci qp_host_free_queue(entry->consume_q, entry->qp.consume_size); 218162306a36Sopenharmony_ci /* Unlink from resource hash table and free callback */ 218262306a36Sopenharmony_ci vmci_resource_remove(&entry->resource); 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci kfree(entry); 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci vmci_ctx_qp_destroy(context, handle); 218762306a36Sopenharmony_ci } else { 218862306a36Sopenharmony_ci qp_notify_peer(false, handle, context_id, peer_id); 218962306a36Sopenharmony_ci if (context_id == VMCI_HOST_CONTEXT_ID && 219062306a36Sopenharmony_ci QPBROKERSTATE_HAS_MEM(entry)) { 219162306a36Sopenharmony_ci entry->state = VMCIQPB_SHUTDOWN_MEM; 219262306a36Sopenharmony_ci } else { 219362306a36Sopenharmony_ci entry->state = VMCIQPB_SHUTDOWN_NO_MEM; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (!is_local) 219762306a36Sopenharmony_ci vmci_ctx_qp_destroy(context, handle); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci result = VMCI_SUCCESS; 220162306a36Sopenharmony_ci out: 220262306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 220362306a36Sopenharmony_ci return result; 220462306a36Sopenharmony_ci} 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci/* 220762306a36Sopenharmony_ci * Establishes the necessary mappings for a queue pair given a 220862306a36Sopenharmony_ci * reference to the queue pair guest memory. This is usually 220962306a36Sopenharmony_ci * called when a guest is unquiesced and the VMX is allowed to 221062306a36Sopenharmony_ci * map guest memory once again. 221162306a36Sopenharmony_ci */ 221262306a36Sopenharmony_ciint vmci_qp_broker_map(struct vmci_handle handle, 221362306a36Sopenharmony_ci struct vmci_ctx *context, 221462306a36Sopenharmony_ci u64 guest_mem) 221562306a36Sopenharmony_ci{ 221662306a36Sopenharmony_ci struct qp_broker_entry *entry; 221762306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 221862306a36Sopenharmony_ci int result; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 222162306a36Sopenharmony_ci context_id == VMCI_INVALID_ID) 222262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 222762306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 222862306a36Sopenharmony_ci context_id, handle.context, handle.resource); 222962306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 223062306a36Sopenharmony_ci goto out; 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 223462306a36Sopenharmony_ci if (!entry) { 223562306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) reports being attached to queue pair (handle=0x%x:0x%x) that isn't present in broker\n", 223662306a36Sopenharmony_ci context_id, handle.context, handle.resource); 223762306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 223862306a36Sopenharmony_ci goto out; 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 224262306a36Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 224362306a36Sopenharmony_ci goto out; 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci result = VMCI_SUCCESS; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID && 224962306a36Sopenharmony_ci !QPBROKERSTATE_HAS_MEM(entry)) { 225062306a36Sopenharmony_ci struct vmci_qp_page_store page_store; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci page_store.pages = guest_mem; 225362306a36Sopenharmony_ci page_store.len = QPE_NUM_PAGES(entry->qp); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 225662306a36Sopenharmony_ci qp_reset_saved_headers(entry); 225762306a36Sopenharmony_ci result = 225862306a36Sopenharmony_ci qp_host_register_user_memory(&page_store, 225962306a36Sopenharmony_ci entry->produce_q, 226062306a36Sopenharmony_ci entry->consume_q); 226162306a36Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 226262306a36Sopenharmony_ci if (result == VMCI_SUCCESS) { 226362306a36Sopenharmony_ci /* Move state from *_NO_MEM to *_MEM */ 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci entry->state++; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci if (entry->wakeup_cb) 226862306a36Sopenharmony_ci entry->wakeup_cb(entry->client_data); 226962306a36Sopenharmony_ci } 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci out: 227362306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 227462306a36Sopenharmony_ci return result; 227562306a36Sopenharmony_ci} 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci/* 227862306a36Sopenharmony_ci * Saves a snapshot of the queue headers for the given QP broker 227962306a36Sopenharmony_ci * entry. Should be used when guest memory is unmapped. 228062306a36Sopenharmony_ci * Results: 228162306a36Sopenharmony_ci * VMCI_SUCCESS on success, appropriate error code if guest memory 228262306a36Sopenharmony_ci * can't be accessed.. 228362306a36Sopenharmony_ci */ 228462306a36Sopenharmony_cistatic int qp_save_headers(struct qp_broker_entry *entry) 228562306a36Sopenharmony_ci{ 228662306a36Sopenharmony_ci int result; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci if (entry->produce_q->saved_header != NULL && 228962306a36Sopenharmony_ci entry->consume_q->saved_header != NULL) { 229062306a36Sopenharmony_ci /* 229162306a36Sopenharmony_ci * If the headers have already been saved, we don't need to do 229262306a36Sopenharmony_ci * it again, and we don't want to map in the headers 229362306a36Sopenharmony_ci * unnecessarily. 229462306a36Sopenharmony_ci */ 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci return VMCI_SUCCESS; 229762306a36Sopenharmony_ci } 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci if (NULL == entry->produce_q->q_header || 230062306a36Sopenharmony_ci NULL == entry->consume_q->q_header) { 230162306a36Sopenharmony_ci result = qp_host_map_queues(entry->produce_q, entry->consume_q); 230262306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 230362306a36Sopenharmony_ci return result; 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci memcpy(&entry->saved_produce_q, entry->produce_q->q_header, 230762306a36Sopenharmony_ci sizeof(entry->saved_produce_q)); 230862306a36Sopenharmony_ci entry->produce_q->saved_header = &entry->saved_produce_q; 230962306a36Sopenharmony_ci memcpy(&entry->saved_consume_q, entry->consume_q->q_header, 231062306a36Sopenharmony_ci sizeof(entry->saved_consume_q)); 231162306a36Sopenharmony_ci entry->consume_q->saved_header = &entry->saved_consume_q; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci return VMCI_SUCCESS; 231462306a36Sopenharmony_ci} 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci/* 231762306a36Sopenharmony_ci * Removes all references to the guest memory of a given queue pair, and 231862306a36Sopenharmony_ci * will move the queue pair from state *_MEM to *_NO_MEM. It is usually 231962306a36Sopenharmony_ci * called when a VM is being quiesced where access to guest memory should 232062306a36Sopenharmony_ci * avoided. 232162306a36Sopenharmony_ci */ 232262306a36Sopenharmony_ciint vmci_qp_broker_unmap(struct vmci_handle handle, 232362306a36Sopenharmony_ci struct vmci_ctx *context, 232462306a36Sopenharmony_ci u32 gid) 232562306a36Sopenharmony_ci{ 232662306a36Sopenharmony_ci struct qp_broker_entry *entry; 232762306a36Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 232862306a36Sopenharmony_ci int result; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 233162306a36Sopenharmony_ci context_id == VMCI_INVALID_ID) 233262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 233762306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 233862306a36Sopenharmony_ci context_id, handle.context, handle.resource); 233962306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 234062306a36Sopenharmony_ci goto out; 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 234462306a36Sopenharmony_ci if (!entry) { 234562306a36Sopenharmony_ci pr_devel("Context (ID=0x%x) reports being attached to queue pair (handle=0x%x:0x%x) that isn't present in broker\n", 234662306a36Sopenharmony_ci context_id, handle.context, handle.resource); 234762306a36Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 234862306a36Sopenharmony_ci goto out; 234962306a36Sopenharmony_ci } 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 235262306a36Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 235362306a36Sopenharmony_ci goto out; 235462306a36Sopenharmony_ci } 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID && 235762306a36Sopenharmony_ci QPBROKERSTATE_HAS_MEM(entry)) { 235862306a36Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 235962306a36Sopenharmony_ci result = qp_save_headers(entry); 236062306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 236162306a36Sopenharmony_ci pr_warn("Failed to save queue headers for queue pair (handle=0x%x:0x%x,result=%d)\n", 236262306a36Sopenharmony_ci handle.context, handle.resource, result); 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci qp_host_unmap_queues(gid, entry->produce_q, entry->consume_q); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* 236762306a36Sopenharmony_ci * On hosted, when we unmap queue pairs, the VMX will also 236862306a36Sopenharmony_ci * unmap the guest memory, so we invalidate the previously 236962306a36Sopenharmony_ci * registered memory. If the queue pair is mapped again at a 237062306a36Sopenharmony_ci * later point in time, we will need to reregister the user 237162306a36Sopenharmony_ci * memory with a possibly new user VA. 237262306a36Sopenharmony_ci */ 237362306a36Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 237462306a36Sopenharmony_ci entry->consume_q); 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci /* 237762306a36Sopenharmony_ci * Move state from *_MEM to *_NO_MEM. 237862306a36Sopenharmony_ci */ 237962306a36Sopenharmony_ci entry->state--; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci result = VMCI_SUCCESS; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci out: 238762306a36Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 238862306a36Sopenharmony_ci return result; 238962306a36Sopenharmony_ci} 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci/* 239262306a36Sopenharmony_ci * Destroys all guest queue pair endpoints. If active guest queue 239362306a36Sopenharmony_ci * pairs still exist, hypercalls to attempt detach from these 239462306a36Sopenharmony_ci * queue pairs will be made. Any failure to detach is silently 239562306a36Sopenharmony_ci * ignored. 239662306a36Sopenharmony_ci */ 239762306a36Sopenharmony_civoid vmci_qp_guest_endpoints_exit(void) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct qp_entry *entry; 240062306a36Sopenharmony_ci struct qp_guest_endpoint *ep; 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci while ((entry = qp_list_get_head(&qp_guest_endpoints))) { 240562306a36Sopenharmony_ci ep = (struct qp_guest_endpoint *)entry; 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci /* Don't make a hypercall for local queue_pairs. */ 240862306a36Sopenharmony_ci if (!(entry->flags & VMCI_QPFLAG_LOCAL)) 240962306a36Sopenharmony_ci qp_detatch_hypercall(entry->handle); 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci /* We cannot fail the exit, so let's reset ref_count. */ 241262306a36Sopenharmony_ci entry->ref_count = 0; 241362306a36Sopenharmony_ci qp_list_remove_entry(&qp_guest_endpoints, entry); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci qp_guest_endpoint_destroy(ep); 241662306a36Sopenharmony_ci } 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 241962306a36Sopenharmony_ci} 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci/* 242262306a36Sopenharmony_ci * Helper routine that will lock the queue pair before subsequent 242362306a36Sopenharmony_ci * operations. 242462306a36Sopenharmony_ci * Note: Non-blocking on the host side is currently only implemented in ESX. 242562306a36Sopenharmony_ci * Since non-blocking isn't yet implemented on the host personality we 242662306a36Sopenharmony_ci * have no reason to acquire a spin lock. So to avoid the use of an 242762306a36Sopenharmony_ci * unnecessary lock only acquire the mutex if we can block. 242862306a36Sopenharmony_ci */ 242962306a36Sopenharmony_cistatic void qp_lock(const struct vmci_qp *qpair) 243062306a36Sopenharmony_ci{ 243162306a36Sopenharmony_ci qp_acquire_queue_mutex(qpair->produce_q); 243262306a36Sopenharmony_ci} 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci/* 243562306a36Sopenharmony_ci * Helper routine that unlocks the queue pair after calling 243662306a36Sopenharmony_ci * qp_lock. 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_cistatic void qp_unlock(const struct vmci_qp *qpair) 243962306a36Sopenharmony_ci{ 244062306a36Sopenharmony_ci qp_release_queue_mutex(qpair->produce_q); 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci/* 244462306a36Sopenharmony_ci * The queue headers may not be mapped at all times. If a queue is 244562306a36Sopenharmony_ci * currently not mapped, it will be attempted to do so. 244662306a36Sopenharmony_ci */ 244762306a36Sopenharmony_cistatic int qp_map_queue_headers(struct vmci_queue *produce_q, 244862306a36Sopenharmony_ci struct vmci_queue *consume_q) 244962306a36Sopenharmony_ci{ 245062306a36Sopenharmony_ci int result; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci if (NULL == produce_q->q_header || NULL == consume_q->q_header) { 245362306a36Sopenharmony_ci result = qp_host_map_queues(produce_q, consume_q); 245462306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 245562306a36Sopenharmony_ci return (produce_q->saved_header && 245662306a36Sopenharmony_ci consume_q->saved_header) ? 245762306a36Sopenharmony_ci VMCI_ERROR_QUEUEPAIR_NOT_READY : 245862306a36Sopenharmony_ci VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci return VMCI_SUCCESS; 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci/* 246562306a36Sopenharmony_ci * Helper routine that will retrieve the produce and consume 246662306a36Sopenharmony_ci * headers of a given queue pair. If the guest memory of the 246762306a36Sopenharmony_ci * queue pair is currently not available, the saved queue headers 246862306a36Sopenharmony_ci * will be returned, if these are available. 246962306a36Sopenharmony_ci */ 247062306a36Sopenharmony_cistatic int qp_get_queue_headers(const struct vmci_qp *qpair, 247162306a36Sopenharmony_ci struct vmci_queue_header **produce_q_header, 247262306a36Sopenharmony_ci struct vmci_queue_header **consume_q_header) 247362306a36Sopenharmony_ci{ 247462306a36Sopenharmony_ci int result; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci result = qp_map_queue_headers(qpair->produce_q, qpair->consume_q); 247762306a36Sopenharmony_ci if (result == VMCI_SUCCESS) { 247862306a36Sopenharmony_ci *produce_q_header = qpair->produce_q->q_header; 247962306a36Sopenharmony_ci *consume_q_header = qpair->consume_q->q_header; 248062306a36Sopenharmony_ci } else if (qpair->produce_q->saved_header && 248162306a36Sopenharmony_ci qpair->consume_q->saved_header) { 248262306a36Sopenharmony_ci *produce_q_header = qpair->produce_q->saved_header; 248362306a36Sopenharmony_ci *consume_q_header = qpair->consume_q->saved_header; 248462306a36Sopenharmony_ci result = VMCI_SUCCESS; 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci return result; 248862306a36Sopenharmony_ci} 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci/* 249162306a36Sopenharmony_ci * Callback from VMCI queue pair broker indicating that a queue 249262306a36Sopenharmony_ci * pair that was previously not ready, now either is ready or 249362306a36Sopenharmony_ci * gone forever. 249462306a36Sopenharmony_ci */ 249562306a36Sopenharmony_cistatic int qp_wakeup_cb(void *client_data) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci struct vmci_qp *qpair = (struct vmci_qp *)client_data; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci qp_lock(qpair); 250062306a36Sopenharmony_ci while (qpair->blocked > 0) { 250162306a36Sopenharmony_ci qpair->blocked--; 250262306a36Sopenharmony_ci qpair->generation++; 250362306a36Sopenharmony_ci wake_up(&qpair->event); 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci qp_unlock(qpair); 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci return VMCI_SUCCESS; 250862306a36Sopenharmony_ci} 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci/* 251162306a36Sopenharmony_ci * Makes the calling thread wait for the queue pair to become 251262306a36Sopenharmony_ci * ready for host side access. Returns true when thread is 251362306a36Sopenharmony_ci * woken up after queue pair state change, false otherwise. 251462306a36Sopenharmony_ci */ 251562306a36Sopenharmony_cistatic bool qp_wait_for_ready_queue(struct vmci_qp *qpair) 251662306a36Sopenharmony_ci{ 251762306a36Sopenharmony_ci unsigned int generation; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci qpair->blocked++; 252062306a36Sopenharmony_ci generation = qpair->generation; 252162306a36Sopenharmony_ci qp_unlock(qpair); 252262306a36Sopenharmony_ci wait_event(qpair->event, generation != qpair->generation); 252362306a36Sopenharmony_ci qp_lock(qpair); 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci return true; 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci/* 252962306a36Sopenharmony_ci * Enqueues a given buffer to the produce queue using the provided 253062306a36Sopenharmony_ci * function. As many bytes as possible (space available in the queue) 253162306a36Sopenharmony_ci * are enqueued. Assumes the queue->mutex has been acquired. Returns 253262306a36Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue 253362306a36Sopenharmony_ci * data, VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the 253462306a36Sopenharmony_ci * queue (as defined by the queue size), VMCI_ERROR_INVALID_ARGS, if 253562306a36Sopenharmony_ci * an error occured when accessing the buffer, 253662306a36Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't 253762306a36Sopenharmony_ci * available. Otherwise, the number of bytes written to the queue is 253862306a36Sopenharmony_ci * returned. Updates the tail pointer of the produce queue. 253962306a36Sopenharmony_ci */ 254062306a36Sopenharmony_cistatic ssize_t qp_enqueue_locked(struct vmci_queue *produce_q, 254162306a36Sopenharmony_ci struct vmci_queue *consume_q, 254262306a36Sopenharmony_ci const u64 produce_q_size, 254362306a36Sopenharmony_ci struct iov_iter *from) 254462306a36Sopenharmony_ci{ 254562306a36Sopenharmony_ci s64 free_space; 254662306a36Sopenharmony_ci u64 tail; 254762306a36Sopenharmony_ci size_t buf_size = iov_iter_count(from); 254862306a36Sopenharmony_ci size_t written; 254962306a36Sopenharmony_ci ssize_t result; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci result = qp_map_queue_headers(produce_q, consume_q); 255262306a36Sopenharmony_ci if (unlikely(result != VMCI_SUCCESS)) 255362306a36Sopenharmony_ci return result; 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci free_space = vmci_q_header_free_space(produce_q->q_header, 255662306a36Sopenharmony_ci consume_q->q_header, 255762306a36Sopenharmony_ci produce_q_size); 255862306a36Sopenharmony_ci if (free_space == 0) 255962306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_NOSPACE; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci if (free_space < VMCI_SUCCESS) 256262306a36Sopenharmony_ci return (ssize_t) free_space; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci written = (size_t) (free_space > buf_size ? buf_size : free_space); 256562306a36Sopenharmony_ci tail = vmci_q_header_producer_tail(produce_q->q_header); 256662306a36Sopenharmony_ci if (likely(tail + written < produce_q_size)) { 256762306a36Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, tail, from, written); 256862306a36Sopenharmony_ci } else { 256962306a36Sopenharmony_ci /* Tail pointer wraps around. */ 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci const size_t tmp = (size_t) (produce_q_size - tail); 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, tail, from, tmp); 257462306a36Sopenharmony_ci if (result >= VMCI_SUCCESS) 257562306a36Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, 0, from, 257662306a36Sopenharmony_ci written - tmp); 257762306a36Sopenharmony_ci } 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 258062306a36Sopenharmony_ci return result; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci /* 258362306a36Sopenharmony_ci * This virt_wmb() ensures that data written to the queue 258462306a36Sopenharmony_ci * is observable before the new producer_tail is. 258562306a36Sopenharmony_ci */ 258662306a36Sopenharmony_ci virt_wmb(); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci vmci_q_header_add_producer_tail(produce_q->q_header, written, 258962306a36Sopenharmony_ci produce_q_size); 259062306a36Sopenharmony_ci return written; 259162306a36Sopenharmony_ci} 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci/* 259462306a36Sopenharmony_ci * Dequeues data (if available) from the given consume queue. Writes data 259562306a36Sopenharmony_ci * to the user provided buffer using the provided function. 259662306a36Sopenharmony_ci * Assumes the queue->mutex has been acquired. 259762306a36Sopenharmony_ci * Results: 259862306a36Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. 259962306a36Sopenharmony_ci * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue 260062306a36Sopenharmony_ci * (as defined by the queue size). 260162306a36Sopenharmony_ci * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. 260262306a36Sopenharmony_ci * Otherwise the number of bytes dequeued is returned. 260362306a36Sopenharmony_ci * Side effects: 260462306a36Sopenharmony_ci * Updates the head pointer of the consume queue. 260562306a36Sopenharmony_ci */ 260662306a36Sopenharmony_cistatic ssize_t qp_dequeue_locked(struct vmci_queue *produce_q, 260762306a36Sopenharmony_ci struct vmci_queue *consume_q, 260862306a36Sopenharmony_ci const u64 consume_q_size, 260962306a36Sopenharmony_ci struct iov_iter *to, 261062306a36Sopenharmony_ci bool update_consumer) 261162306a36Sopenharmony_ci{ 261262306a36Sopenharmony_ci size_t buf_size = iov_iter_count(to); 261362306a36Sopenharmony_ci s64 buf_ready; 261462306a36Sopenharmony_ci u64 head; 261562306a36Sopenharmony_ci size_t read; 261662306a36Sopenharmony_ci ssize_t result; 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci result = qp_map_queue_headers(produce_q, consume_q); 261962306a36Sopenharmony_ci if (unlikely(result != VMCI_SUCCESS)) 262062306a36Sopenharmony_ci return result; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci buf_ready = vmci_q_header_buf_ready(consume_q->q_header, 262362306a36Sopenharmony_ci produce_q->q_header, 262462306a36Sopenharmony_ci consume_q_size); 262562306a36Sopenharmony_ci if (buf_ready == 0) 262662306a36Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_NODATA; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci if (buf_ready < VMCI_SUCCESS) 262962306a36Sopenharmony_ci return (ssize_t) buf_ready; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* 263262306a36Sopenharmony_ci * This virt_rmb() ensures that data from the queue will be read 263362306a36Sopenharmony_ci * after we have determined how much is ready to be consumed. 263462306a36Sopenharmony_ci */ 263562306a36Sopenharmony_ci virt_rmb(); 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci read = (size_t) (buf_ready > buf_size ? buf_size : buf_ready); 263862306a36Sopenharmony_ci head = vmci_q_header_consumer_head(produce_q->q_header); 263962306a36Sopenharmony_ci if (likely(head + read < consume_q_size)) { 264062306a36Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, head, read); 264162306a36Sopenharmony_ci } else { 264262306a36Sopenharmony_ci /* Head pointer wraps around. */ 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci const size_t tmp = (size_t) (consume_q_size - head); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, head, tmp); 264762306a36Sopenharmony_ci if (result >= VMCI_SUCCESS) 264862306a36Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, 0, 264962306a36Sopenharmony_ci read - tmp); 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (result < VMCI_SUCCESS) 265462306a36Sopenharmony_ci return result; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci if (update_consumer) 265762306a36Sopenharmony_ci vmci_q_header_add_consumer_head(produce_q->q_header, 265862306a36Sopenharmony_ci read, consume_q_size); 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci return read; 266162306a36Sopenharmony_ci} 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci/* 266462306a36Sopenharmony_ci * vmci_qpair_alloc() - Allocates a queue pair. 266562306a36Sopenharmony_ci * @qpair: Pointer for the new vmci_qp struct. 266662306a36Sopenharmony_ci * @handle: Handle to track the resource. 266762306a36Sopenharmony_ci * @produce_qsize: Desired size of the producer queue. 266862306a36Sopenharmony_ci * @consume_qsize: Desired size of the consumer queue. 266962306a36Sopenharmony_ci * @peer: ContextID of the peer. 267062306a36Sopenharmony_ci * @flags: VMCI flags. 267162306a36Sopenharmony_ci * @priv_flags: VMCI priviledge flags. 267262306a36Sopenharmony_ci * 267362306a36Sopenharmony_ci * This is the client interface for allocating the memory for a 267462306a36Sopenharmony_ci * vmci_qp structure and then attaching to the underlying 267562306a36Sopenharmony_ci * queue. If an error occurs allocating the memory for the 267662306a36Sopenharmony_ci * vmci_qp structure no attempt is made to attach. If an 267762306a36Sopenharmony_ci * error occurs attaching, then the structure is freed. 267862306a36Sopenharmony_ci */ 267962306a36Sopenharmony_ciint vmci_qpair_alloc(struct vmci_qp **qpair, 268062306a36Sopenharmony_ci struct vmci_handle *handle, 268162306a36Sopenharmony_ci u64 produce_qsize, 268262306a36Sopenharmony_ci u64 consume_qsize, 268362306a36Sopenharmony_ci u32 peer, 268462306a36Sopenharmony_ci u32 flags, 268562306a36Sopenharmony_ci u32 priv_flags) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci struct vmci_qp *my_qpair; 268862306a36Sopenharmony_ci int retval; 268962306a36Sopenharmony_ci struct vmci_handle src = VMCI_INVALID_HANDLE; 269062306a36Sopenharmony_ci struct vmci_handle dst = vmci_make_handle(peer, VMCI_INVALID_ID); 269162306a36Sopenharmony_ci enum vmci_route route; 269262306a36Sopenharmony_ci vmci_event_release_cb wakeup_cb; 269362306a36Sopenharmony_ci void *client_data; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci /* 269662306a36Sopenharmony_ci * Restrict the size of a queuepair. The device already 269762306a36Sopenharmony_ci * enforces a limit on the total amount of memory that can be 269862306a36Sopenharmony_ci * allocated to queuepairs for a guest. However, we try to 269962306a36Sopenharmony_ci * allocate this memory before we make the queuepair 270062306a36Sopenharmony_ci * allocation hypercall. On Linux, we allocate each page 270162306a36Sopenharmony_ci * separately, which means rather than fail, the guest will 270262306a36Sopenharmony_ci * thrash while it tries to allocate, and will become 270362306a36Sopenharmony_ci * increasingly unresponsive to the point where it appears to 270462306a36Sopenharmony_ci * be hung. So we place a limit on the size of an individual 270562306a36Sopenharmony_ci * queuepair here, and leave the device to enforce the 270662306a36Sopenharmony_ci * restriction on total queuepair memory. (Note that this 270762306a36Sopenharmony_ci * doesn't prevent all cases; a user with only this much 270862306a36Sopenharmony_ci * physical memory could still get into trouble.) The error 270962306a36Sopenharmony_ci * used by the device is NO_RESOURCES, so use that here too. 271062306a36Sopenharmony_ci */ 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci if (!QP_SIZES_ARE_VALID(produce_qsize, consume_qsize)) 271362306a36Sopenharmony_ci return VMCI_ERROR_NO_RESOURCES; 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci retval = vmci_route(&src, &dst, false, &route); 271662306a36Sopenharmony_ci if (retval < VMCI_SUCCESS) 271762306a36Sopenharmony_ci route = vmci_guest_code_active() ? 271862306a36Sopenharmony_ci VMCI_ROUTE_AS_GUEST : VMCI_ROUTE_AS_HOST; 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci if (flags & (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED)) { 272162306a36Sopenharmony_ci pr_devel("NONBLOCK OR PINNED set"); 272262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 272362306a36Sopenharmony_ci } 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci my_qpair = kzalloc(sizeof(*my_qpair), GFP_KERNEL); 272662306a36Sopenharmony_ci if (!my_qpair) 272762306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci my_qpair->produce_q_size = produce_qsize; 273062306a36Sopenharmony_ci my_qpair->consume_q_size = consume_qsize; 273162306a36Sopenharmony_ci my_qpair->peer = peer; 273262306a36Sopenharmony_ci my_qpair->flags = flags; 273362306a36Sopenharmony_ci my_qpair->priv_flags = priv_flags; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci wakeup_cb = NULL; 273662306a36Sopenharmony_ci client_data = NULL; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci if (VMCI_ROUTE_AS_HOST == route) { 273962306a36Sopenharmony_ci my_qpair->guest_endpoint = false; 274062306a36Sopenharmony_ci if (!(flags & VMCI_QPFLAG_LOCAL)) { 274162306a36Sopenharmony_ci my_qpair->blocked = 0; 274262306a36Sopenharmony_ci my_qpair->generation = 0; 274362306a36Sopenharmony_ci init_waitqueue_head(&my_qpair->event); 274462306a36Sopenharmony_ci wakeup_cb = qp_wakeup_cb; 274562306a36Sopenharmony_ci client_data = (void *)my_qpair; 274662306a36Sopenharmony_ci } 274762306a36Sopenharmony_ci } else { 274862306a36Sopenharmony_ci my_qpair->guest_endpoint = true; 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci retval = vmci_qp_alloc(handle, 275262306a36Sopenharmony_ci &my_qpair->produce_q, 275362306a36Sopenharmony_ci my_qpair->produce_q_size, 275462306a36Sopenharmony_ci &my_qpair->consume_q, 275562306a36Sopenharmony_ci my_qpair->consume_q_size, 275662306a36Sopenharmony_ci my_qpair->peer, 275762306a36Sopenharmony_ci my_qpair->flags, 275862306a36Sopenharmony_ci my_qpair->priv_flags, 275962306a36Sopenharmony_ci my_qpair->guest_endpoint, 276062306a36Sopenharmony_ci wakeup_cb, client_data); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci if (retval < VMCI_SUCCESS) { 276362306a36Sopenharmony_ci kfree(my_qpair); 276462306a36Sopenharmony_ci return retval; 276562306a36Sopenharmony_ci } 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci *qpair = my_qpair; 276862306a36Sopenharmony_ci my_qpair->handle = *handle; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci return retval; 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_alloc); 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci/* 277562306a36Sopenharmony_ci * vmci_qpair_detach() - Detatches the client from a queue pair. 277662306a36Sopenharmony_ci * @qpair: Reference of a pointer to the qpair struct. 277762306a36Sopenharmony_ci * 277862306a36Sopenharmony_ci * This is the client interface for detaching from a VMCIQPair. 277962306a36Sopenharmony_ci * Note that this routine will free the memory allocated for the 278062306a36Sopenharmony_ci * vmci_qp structure too. 278162306a36Sopenharmony_ci */ 278262306a36Sopenharmony_ciint vmci_qpair_detach(struct vmci_qp **qpair) 278362306a36Sopenharmony_ci{ 278462306a36Sopenharmony_ci int result; 278562306a36Sopenharmony_ci struct vmci_qp *old_qpair; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci if (!qpair || !(*qpair)) 278862306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci old_qpair = *qpair; 279162306a36Sopenharmony_ci result = qp_detatch(old_qpair->handle, old_qpair->guest_endpoint); 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_ci /* 279462306a36Sopenharmony_ci * The guest can fail to detach for a number of reasons, and 279562306a36Sopenharmony_ci * if it does so, it will cleanup the entry (if there is one). 279662306a36Sopenharmony_ci * The host can fail too, but it won't cleanup the entry 279762306a36Sopenharmony_ci * immediately, it will do that later when the context is 279862306a36Sopenharmony_ci * freed. Either way, we need to release the qpair struct 279962306a36Sopenharmony_ci * here; there isn't much the caller can do, and we don't want 280062306a36Sopenharmony_ci * to leak. 280162306a36Sopenharmony_ci */ 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci memset(old_qpair, 0, sizeof(*old_qpair)); 280462306a36Sopenharmony_ci old_qpair->handle = VMCI_INVALID_HANDLE; 280562306a36Sopenharmony_ci old_qpair->peer = VMCI_INVALID_ID; 280662306a36Sopenharmony_ci kfree(old_qpair); 280762306a36Sopenharmony_ci *qpair = NULL; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci return result; 281062306a36Sopenharmony_ci} 281162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_detach); 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci/* 281462306a36Sopenharmony_ci * vmci_qpair_get_produce_indexes() - Retrieves the indexes of the producer. 281562306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 281662306a36Sopenharmony_ci * @producer_tail: Reference used for storing producer tail index. 281762306a36Sopenharmony_ci * @consumer_head: Reference used for storing the consumer head index. 281862306a36Sopenharmony_ci * 281962306a36Sopenharmony_ci * This is the client interface for getting the current indexes of the 282062306a36Sopenharmony_ci * QPair from the point of the view of the caller as the producer. 282162306a36Sopenharmony_ci */ 282262306a36Sopenharmony_ciint vmci_qpair_get_produce_indexes(const struct vmci_qp *qpair, 282362306a36Sopenharmony_ci u64 *producer_tail, 282462306a36Sopenharmony_ci u64 *consumer_head) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 282762306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 282862306a36Sopenharmony_ci int result; 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci if (!qpair) 283162306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci qp_lock(qpair); 283462306a36Sopenharmony_ci result = 283562306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 283662306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 283762306a36Sopenharmony_ci vmci_q_header_get_pointers(produce_q_header, consume_q_header, 283862306a36Sopenharmony_ci producer_tail, consumer_head); 283962306a36Sopenharmony_ci qp_unlock(qpair); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci if (result == VMCI_SUCCESS && 284262306a36Sopenharmony_ci ((producer_tail && *producer_tail >= qpair->produce_q_size) || 284362306a36Sopenharmony_ci (consumer_head && *consumer_head >= qpair->produce_q_size))) 284462306a36Sopenharmony_ci return VMCI_ERROR_INVALID_SIZE; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci return result; 284762306a36Sopenharmony_ci} 284862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_get_produce_indexes); 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci/* 285162306a36Sopenharmony_ci * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the consumer. 285262306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 285362306a36Sopenharmony_ci * @consumer_tail: Reference used for storing consumer tail index. 285462306a36Sopenharmony_ci * @producer_head: Reference used for storing the producer head index. 285562306a36Sopenharmony_ci * 285662306a36Sopenharmony_ci * This is the client interface for getting the current indexes of the 285762306a36Sopenharmony_ci * QPair from the point of the view of the caller as the consumer. 285862306a36Sopenharmony_ci */ 285962306a36Sopenharmony_ciint vmci_qpair_get_consume_indexes(const struct vmci_qp *qpair, 286062306a36Sopenharmony_ci u64 *consumer_tail, 286162306a36Sopenharmony_ci u64 *producer_head) 286262306a36Sopenharmony_ci{ 286362306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 286462306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 286562306a36Sopenharmony_ci int result; 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci if (!qpair) 286862306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci qp_lock(qpair); 287162306a36Sopenharmony_ci result = 287262306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 287362306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 287462306a36Sopenharmony_ci vmci_q_header_get_pointers(consume_q_header, produce_q_header, 287562306a36Sopenharmony_ci consumer_tail, producer_head); 287662306a36Sopenharmony_ci qp_unlock(qpair); 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci if (result == VMCI_SUCCESS && 287962306a36Sopenharmony_ci ((consumer_tail && *consumer_tail >= qpair->consume_q_size) || 288062306a36Sopenharmony_ci (producer_head && *producer_head >= qpair->consume_q_size))) 288162306a36Sopenharmony_ci return VMCI_ERROR_INVALID_SIZE; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci return result; 288462306a36Sopenharmony_ci} 288562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_get_consume_indexes); 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci/* 288862306a36Sopenharmony_ci * vmci_qpair_produce_free_space() - Retrieves free space in producer queue. 288962306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 289062306a36Sopenharmony_ci * 289162306a36Sopenharmony_ci * This is the client interface for getting the amount of free 289262306a36Sopenharmony_ci * space in the QPair from the point of the view of the caller as 289362306a36Sopenharmony_ci * the producer which is the common case. Returns < 0 if err, else 289462306a36Sopenharmony_ci * available bytes into which data can be enqueued if > 0. 289562306a36Sopenharmony_ci */ 289662306a36Sopenharmony_cis64 vmci_qpair_produce_free_space(const struct vmci_qp *qpair) 289762306a36Sopenharmony_ci{ 289862306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 289962306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 290062306a36Sopenharmony_ci s64 result; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci if (!qpair) 290362306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci qp_lock(qpair); 290662306a36Sopenharmony_ci result = 290762306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 290862306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 290962306a36Sopenharmony_ci result = vmci_q_header_free_space(produce_q_header, 291062306a36Sopenharmony_ci consume_q_header, 291162306a36Sopenharmony_ci qpair->produce_q_size); 291262306a36Sopenharmony_ci else 291362306a36Sopenharmony_ci result = 0; 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci qp_unlock(qpair); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci return result; 291862306a36Sopenharmony_ci} 291962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_produce_free_space); 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci/* 292262306a36Sopenharmony_ci * vmci_qpair_consume_free_space() - Retrieves free space in consumer queue. 292362306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 292462306a36Sopenharmony_ci * 292562306a36Sopenharmony_ci * This is the client interface for getting the amount of free 292662306a36Sopenharmony_ci * space in the QPair from the point of the view of the caller as 292762306a36Sopenharmony_ci * the consumer which is not the common case. Returns < 0 if err, else 292862306a36Sopenharmony_ci * available bytes into which data can be enqueued if > 0. 292962306a36Sopenharmony_ci */ 293062306a36Sopenharmony_cis64 vmci_qpair_consume_free_space(const struct vmci_qp *qpair) 293162306a36Sopenharmony_ci{ 293262306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 293362306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 293462306a36Sopenharmony_ci s64 result; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci if (!qpair) 293762306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci qp_lock(qpair); 294062306a36Sopenharmony_ci result = 294162306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 294262306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 294362306a36Sopenharmony_ci result = vmci_q_header_free_space(consume_q_header, 294462306a36Sopenharmony_ci produce_q_header, 294562306a36Sopenharmony_ci qpair->consume_q_size); 294662306a36Sopenharmony_ci else 294762306a36Sopenharmony_ci result = 0; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci qp_unlock(qpair); 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci return result; 295262306a36Sopenharmony_ci} 295362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_consume_free_space); 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci/* 295662306a36Sopenharmony_ci * vmci_qpair_produce_buf_ready() - Gets bytes ready to read from 295762306a36Sopenharmony_ci * producer queue. 295862306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 295962306a36Sopenharmony_ci * 296062306a36Sopenharmony_ci * This is the client interface for getting the amount of 296162306a36Sopenharmony_ci * enqueued data in the QPair from the point of the view of the 296262306a36Sopenharmony_ci * caller as the producer which is not the common case. Returns < 0 if err, 296362306a36Sopenharmony_ci * else available bytes that may be read. 296462306a36Sopenharmony_ci */ 296562306a36Sopenharmony_cis64 vmci_qpair_produce_buf_ready(const struct vmci_qp *qpair) 296662306a36Sopenharmony_ci{ 296762306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 296862306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 296962306a36Sopenharmony_ci s64 result; 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci if (!qpair) 297262306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci qp_lock(qpair); 297562306a36Sopenharmony_ci result = 297662306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 297762306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 297862306a36Sopenharmony_ci result = vmci_q_header_buf_ready(produce_q_header, 297962306a36Sopenharmony_ci consume_q_header, 298062306a36Sopenharmony_ci qpair->produce_q_size); 298162306a36Sopenharmony_ci else 298262306a36Sopenharmony_ci result = 0; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci qp_unlock(qpair); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci return result; 298762306a36Sopenharmony_ci} 298862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_produce_buf_ready); 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci/* 299162306a36Sopenharmony_ci * vmci_qpair_consume_buf_ready() - Gets bytes ready to read from 299262306a36Sopenharmony_ci * consumer queue. 299362306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 299462306a36Sopenharmony_ci * 299562306a36Sopenharmony_ci * This is the client interface for getting the amount of 299662306a36Sopenharmony_ci * enqueued data in the QPair from the point of the view of the 299762306a36Sopenharmony_ci * caller as the consumer which is the normal case. Returns < 0 if err, 299862306a36Sopenharmony_ci * else available bytes that may be read. 299962306a36Sopenharmony_ci */ 300062306a36Sopenharmony_cis64 vmci_qpair_consume_buf_ready(const struct vmci_qp *qpair) 300162306a36Sopenharmony_ci{ 300262306a36Sopenharmony_ci struct vmci_queue_header *produce_q_header; 300362306a36Sopenharmony_ci struct vmci_queue_header *consume_q_header; 300462306a36Sopenharmony_ci s64 result; 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci if (!qpair) 300762306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci qp_lock(qpair); 301062306a36Sopenharmony_ci result = 301162306a36Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 301262306a36Sopenharmony_ci if (result == VMCI_SUCCESS) 301362306a36Sopenharmony_ci result = vmci_q_header_buf_ready(consume_q_header, 301462306a36Sopenharmony_ci produce_q_header, 301562306a36Sopenharmony_ci qpair->consume_q_size); 301662306a36Sopenharmony_ci else 301762306a36Sopenharmony_ci result = 0; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci qp_unlock(qpair); 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci return result; 302262306a36Sopenharmony_ci} 302362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_consume_buf_ready); 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci/* 302662306a36Sopenharmony_ci * vmci_qpair_enqueue() - Throw data on the queue. 302762306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 302862306a36Sopenharmony_ci * @buf: Pointer to buffer containing data 302962306a36Sopenharmony_ci * @buf_size: Length of buffer. 303062306a36Sopenharmony_ci * @buf_type: Buffer type (Unused). 303162306a36Sopenharmony_ci * 303262306a36Sopenharmony_ci * This is the client interface for enqueueing data into the queue. 303362306a36Sopenharmony_ci * Returns number of bytes enqueued or < 0 on error. 303462306a36Sopenharmony_ci */ 303562306a36Sopenharmony_cissize_t vmci_qpair_enqueue(struct vmci_qp *qpair, 303662306a36Sopenharmony_ci const void *buf, 303762306a36Sopenharmony_ci size_t buf_size, 303862306a36Sopenharmony_ci int buf_type) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci ssize_t result; 304162306a36Sopenharmony_ci struct iov_iter from; 304262306a36Sopenharmony_ci struct kvec v = {.iov_base = (void *)buf, .iov_len = buf_size}; 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci if (!qpair || !buf) 304562306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci iov_iter_kvec(&from, ITER_SOURCE, &v, 1, buf_size); 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci qp_lock(qpair); 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci do { 305262306a36Sopenharmony_ci result = qp_enqueue_locked(qpair->produce_q, 305362306a36Sopenharmony_ci qpair->consume_q, 305462306a36Sopenharmony_ci qpair->produce_q_size, 305562306a36Sopenharmony_ci &from); 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 305862306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 305962306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci qp_unlock(qpair); 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci return result; 306662306a36Sopenharmony_ci} 306762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_enqueue); 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci/* 307062306a36Sopenharmony_ci * vmci_qpair_dequeue() - Get data from the queue. 307162306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 307262306a36Sopenharmony_ci * @buf: Pointer to buffer for the data 307362306a36Sopenharmony_ci * @buf_size: Length of buffer. 307462306a36Sopenharmony_ci * @buf_type: Buffer type (Unused). 307562306a36Sopenharmony_ci * 307662306a36Sopenharmony_ci * This is the client interface for dequeueing data from the queue. 307762306a36Sopenharmony_ci * Returns number of bytes dequeued or < 0 on error. 307862306a36Sopenharmony_ci */ 307962306a36Sopenharmony_cissize_t vmci_qpair_dequeue(struct vmci_qp *qpair, 308062306a36Sopenharmony_ci void *buf, 308162306a36Sopenharmony_ci size_t buf_size, 308262306a36Sopenharmony_ci int buf_type) 308362306a36Sopenharmony_ci{ 308462306a36Sopenharmony_ci ssize_t result; 308562306a36Sopenharmony_ci struct iov_iter to; 308662306a36Sopenharmony_ci struct kvec v = {.iov_base = buf, .iov_len = buf_size}; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci if (!qpair || !buf) 308962306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci qp_lock(qpair); 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci do { 309662306a36Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 309762306a36Sopenharmony_ci qpair->consume_q, 309862306a36Sopenharmony_ci qpair->consume_q_size, 309962306a36Sopenharmony_ci &to, true); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 310262306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 310362306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci qp_unlock(qpair); 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci return result; 311062306a36Sopenharmony_ci} 311162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_dequeue); 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci/* 311462306a36Sopenharmony_ci * vmci_qpair_peek() - Peek at the data in the queue. 311562306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 311662306a36Sopenharmony_ci * @buf: Pointer to buffer for the data 311762306a36Sopenharmony_ci * @buf_size: Length of buffer. 311862306a36Sopenharmony_ci * @buf_type: Buffer type (Unused on Linux). 311962306a36Sopenharmony_ci * 312062306a36Sopenharmony_ci * This is the client interface for peeking into a queue. (I.e., 312162306a36Sopenharmony_ci * copy data from the queue without updating the head pointer.) 312262306a36Sopenharmony_ci * Returns number of bytes dequeued or < 0 on error. 312362306a36Sopenharmony_ci */ 312462306a36Sopenharmony_cissize_t vmci_qpair_peek(struct vmci_qp *qpair, 312562306a36Sopenharmony_ci void *buf, 312662306a36Sopenharmony_ci size_t buf_size, 312762306a36Sopenharmony_ci int buf_type) 312862306a36Sopenharmony_ci{ 312962306a36Sopenharmony_ci struct iov_iter to; 313062306a36Sopenharmony_ci struct kvec v = {.iov_base = buf, .iov_len = buf_size}; 313162306a36Sopenharmony_ci ssize_t result; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci if (!qpair || !buf) 313462306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci qp_lock(qpair); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci do { 314162306a36Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 314262306a36Sopenharmony_ci qpair->consume_q, 314362306a36Sopenharmony_ci qpair->consume_q_size, 314462306a36Sopenharmony_ci &to, false); 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 314762306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 314862306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci qp_unlock(qpair); 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci return result; 315562306a36Sopenharmony_ci} 315662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_peek); 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci/* 315962306a36Sopenharmony_ci * vmci_qpair_enquev() - Throw data on the queue using iov. 316062306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 316162306a36Sopenharmony_ci * @iov: Pointer to buffer containing data 316262306a36Sopenharmony_ci * @iov_size: Length of buffer. 316362306a36Sopenharmony_ci * @buf_type: Buffer type (Unused). 316462306a36Sopenharmony_ci * 316562306a36Sopenharmony_ci * This is the client interface for enqueueing data into the queue. 316662306a36Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 316762306a36Sopenharmony_ci * of bytes enqueued or < 0 on error. 316862306a36Sopenharmony_ci */ 316962306a36Sopenharmony_cissize_t vmci_qpair_enquev(struct vmci_qp *qpair, 317062306a36Sopenharmony_ci struct msghdr *msg, 317162306a36Sopenharmony_ci size_t iov_size, 317262306a36Sopenharmony_ci int buf_type) 317362306a36Sopenharmony_ci{ 317462306a36Sopenharmony_ci ssize_t result; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci if (!qpair) 317762306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci qp_lock(qpair); 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci do { 318262306a36Sopenharmony_ci result = qp_enqueue_locked(qpair->produce_q, 318362306a36Sopenharmony_ci qpair->consume_q, 318462306a36Sopenharmony_ci qpair->produce_q_size, 318562306a36Sopenharmony_ci &msg->msg_iter); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 318862306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 318962306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci qp_unlock(qpair); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci return result; 319662306a36Sopenharmony_ci} 319762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_enquev); 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci/* 320062306a36Sopenharmony_ci * vmci_qpair_dequev() - Get data from the queue using iov. 320162306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 320262306a36Sopenharmony_ci * @iov: Pointer to buffer for the data 320362306a36Sopenharmony_ci * @iov_size: Length of buffer. 320462306a36Sopenharmony_ci * @buf_type: Buffer type (Unused). 320562306a36Sopenharmony_ci * 320662306a36Sopenharmony_ci * This is the client interface for dequeueing data from the queue. 320762306a36Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 320862306a36Sopenharmony_ci * of bytes dequeued or < 0 on error. 320962306a36Sopenharmony_ci */ 321062306a36Sopenharmony_cissize_t vmci_qpair_dequev(struct vmci_qp *qpair, 321162306a36Sopenharmony_ci struct msghdr *msg, 321262306a36Sopenharmony_ci size_t iov_size, 321362306a36Sopenharmony_ci int buf_type) 321462306a36Sopenharmony_ci{ 321562306a36Sopenharmony_ci ssize_t result; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci if (!qpair) 321862306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci qp_lock(qpair); 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci do { 322362306a36Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 322462306a36Sopenharmony_ci qpair->consume_q, 322562306a36Sopenharmony_ci qpair->consume_q_size, 322662306a36Sopenharmony_ci &msg->msg_iter, true); 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 322962306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 323062306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci qp_unlock(qpair); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci return result; 323762306a36Sopenharmony_ci} 323862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_dequev); 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci/* 324162306a36Sopenharmony_ci * vmci_qpair_peekv() - Peek at the data in the queue using iov. 324262306a36Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 324362306a36Sopenharmony_ci * @iov: Pointer to buffer for the data 324462306a36Sopenharmony_ci * @iov_size: Length of buffer. 324562306a36Sopenharmony_ci * @buf_type: Buffer type (Unused on Linux). 324662306a36Sopenharmony_ci * 324762306a36Sopenharmony_ci * This is the client interface for peeking into a queue. (I.e., 324862306a36Sopenharmony_ci * copy data from the queue without updating the head pointer.) 324962306a36Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 325062306a36Sopenharmony_ci * of bytes peeked or < 0 on error. 325162306a36Sopenharmony_ci */ 325262306a36Sopenharmony_cissize_t vmci_qpair_peekv(struct vmci_qp *qpair, 325362306a36Sopenharmony_ci struct msghdr *msg, 325462306a36Sopenharmony_ci size_t iov_size, 325562306a36Sopenharmony_ci int buf_type) 325662306a36Sopenharmony_ci{ 325762306a36Sopenharmony_ci ssize_t result; 325862306a36Sopenharmony_ci 325962306a36Sopenharmony_ci if (!qpair) 326062306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci qp_lock(qpair); 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci do { 326562306a36Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 326662306a36Sopenharmony_ci qpair->consume_q, 326762306a36Sopenharmony_ci qpair->consume_q_size, 326862306a36Sopenharmony_ci &msg->msg_iter, false); 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 327162306a36Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 327262306a36Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci qp_unlock(qpair); 327762306a36Sopenharmony_ci return result; 327862306a36Sopenharmony_ci} 327962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_peekv); 3280