18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * VMware VMCI Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 98c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_api.h> 108c2ecf20Sopenharmony_ci#include <linux/highmem.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 168c2ecf20Sopenharmony_ci#include <linux/pci.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/uio.h> 208c2ecf20Sopenharmony_ci#include <linux/wait.h> 218c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 228c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "vmci_handle_array.h" 258c2ecf20Sopenharmony_ci#include "vmci_queue_pair.h" 268c2ecf20Sopenharmony_ci#include "vmci_datagram.h" 278c2ecf20Sopenharmony_ci#include "vmci_resource.h" 288c2ecf20Sopenharmony_ci#include "vmci_context.h" 298c2ecf20Sopenharmony_ci#include "vmci_driver.h" 308c2ecf20Sopenharmony_ci#include "vmci_event.h" 318c2ecf20Sopenharmony_ci#include "vmci_route.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * In the following, we will distinguish between two kinds of VMX processes - 358c2ecf20Sopenharmony_ci * the ones with versions lower than VMCI_VERSION_NOVMVM that use specialized 368c2ecf20Sopenharmony_ci * VMCI page files in the VMX and supporting VM to VM communication and the 378c2ecf20Sopenharmony_ci * newer ones that use the guest memory directly. We will in the following 388c2ecf20Sopenharmony_ci * refer to the older VMX versions as old-style VMX'en, and the newer ones as 398c2ecf20Sopenharmony_ci * new-style VMX'en. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * The state transition datagram is as follows (the VMCIQPB_ prefix has been 428c2ecf20Sopenharmony_ci * removed for readability) - see below for more details on the transtions: 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * -------------- NEW ------------- 458c2ecf20Sopenharmony_ci * | | 468c2ecf20Sopenharmony_ci * \_/ \_/ 478c2ecf20Sopenharmony_ci * CREATED_NO_MEM <-----------------> CREATED_MEM 488c2ecf20Sopenharmony_ci * | | | 498c2ecf20Sopenharmony_ci * | o-----------------------o | 508c2ecf20Sopenharmony_ci * | | | 518c2ecf20Sopenharmony_ci * \_/ \_/ \_/ 528c2ecf20Sopenharmony_ci * ATTACHED_NO_MEM <----------------> ATTACHED_MEM 538c2ecf20Sopenharmony_ci * | | | 548c2ecf20Sopenharmony_ci * | o----------------------o | 558c2ecf20Sopenharmony_ci * | | | 568c2ecf20Sopenharmony_ci * \_/ \_/ \_/ 578c2ecf20Sopenharmony_ci * SHUTDOWN_NO_MEM <----------------> SHUTDOWN_MEM 588c2ecf20Sopenharmony_ci * | | 598c2ecf20Sopenharmony_ci * | | 608c2ecf20Sopenharmony_ci * -------------> gone <------------- 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * In more detail. When a VMCI queue pair is first created, it will be in the 638c2ecf20Sopenharmony_ci * VMCIQPB_NEW state. It will then move into one of the following states: 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * - VMCIQPB_CREATED_NO_MEM: this state indicates that either: 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * - the created was performed by a host endpoint, in which case there is 688c2ecf20Sopenharmony_ci * no backing memory yet. 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * - the create was initiated by an old-style VMX, that uses 718c2ecf20Sopenharmony_ci * vmci_qp_broker_set_page_store to specify the UVAs of the queue pair at 728c2ecf20Sopenharmony_ci * a later point in time. This state can be distinguished from the one 738c2ecf20Sopenharmony_ci * above by the context ID of the creator. A host side is not allowed to 748c2ecf20Sopenharmony_ci * attach until the page store has been set. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * - VMCIQPB_CREATED_MEM: this state is the result when the queue pair 778c2ecf20Sopenharmony_ci * is created by a VMX using the queue pair device backend that 788c2ecf20Sopenharmony_ci * sets the UVAs of the queue pair immediately and stores the 798c2ecf20Sopenharmony_ci * information for later attachers. At this point, it is ready for 808c2ecf20Sopenharmony_ci * the host side to attach to it. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Once the queue pair is in one of the created states (with the exception of 838c2ecf20Sopenharmony_ci * the case mentioned for older VMX'en above), it is possible to attach to the 848c2ecf20Sopenharmony_ci * queue pair. Again we have two new states possible: 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * - VMCIQPB_ATTACHED_MEM: this state can be reached through the following 878c2ecf20Sopenharmony_ci * paths: 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * - from VMCIQPB_CREATED_NO_MEM when a new-style VMX allocates a queue 908c2ecf20Sopenharmony_ci * pair, and attaches to a queue pair previously created by the host side. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * - from VMCIQPB_CREATED_MEM when the host side attaches to a queue pair 938c2ecf20Sopenharmony_ci * already created by a guest. 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * - from VMCIQPB_ATTACHED_NO_MEM, when an old-style VMX calls 968c2ecf20Sopenharmony_ci * vmci_qp_broker_set_page_store (see below). 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * - VMCIQPB_ATTACHED_NO_MEM: If the queue pair already was in the 998c2ecf20Sopenharmony_ci * VMCIQPB_CREATED_NO_MEM due to a host side create, an old-style VMX will 1008c2ecf20Sopenharmony_ci * bring the queue pair into this state. Once vmci_qp_broker_set_page_store 1018c2ecf20Sopenharmony_ci * is called to register the user memory, the VMCIQPB_ATTACH_MEM state 1028c2ecf20Sopenharmony_ci * will be entered. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * From the attached queue pair, the queue pair can enter the shutdown states 1058c2ecf20Sopenharmony_ci * when either side of the queue pair detaches. If the guest side detaches 1068c2ecf20Sopenharmony_ci * first, the queue pair will enter the VMCIQPB_SHUTDOWN_NO_MEM state, where 1078c2ecf20Sopenharmony_ci * the content of the queue pair will no longer be available. If the host 1088c2ecf20Sopenharmony_ci * side detaches first, the queue pair will either enter the 1098c2ecf20Sopenharmony_ci * VMCIQPB_SHUTDOWN_MEM, if the guest memory is currently mapped, or 1108c2ecf20Sopenharmony_ci * VMCIQPB_SHUTDOWN_NO_MEM, if the guest memory is not mapped 1118c2ecf20Sopenharmony_ci * (e.g., the host detaches while a guest is stunned). 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * New-style VMX'en will also unmap guest memory, if the guest is 1148c2ecf20Sopenharmony_ci * quiesced, e.g., during a snapshot operation. In that case, the guest 1158c2ecf20Sopenharmony_ci * memory will no longer be available, and the queue pair will transition from 1168c2ecf20Sopenharmony_ci * *_MEM state to a *_NO_MEM state. The VMX may later map the memory once more, 1178c2ecf20Sopenharmony_ci * in which case the queue pair will transition from the *_NO_MEM state at that 1188c2ecf20Sopenharmony_ci * point back to the *_MEM state. Note that the *_NO_MEM state may have changed, 1198c2ecf20Sopenharmony_ci * since the peer may have either attached or detached in the meantime. The 1208c2ecf20Sopenharmony_ci * values are laid out such that ++ on a state will move from a *_NO_MEM to a 1218c2ecf20Sopenharmony_ci * *_MEM state, and vice versa. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* The Kernel specific component of the struct vmci_queue structure. */ 1258c2ecf20Sopenharmony_cistruct vmci_queue_kern_if { 1268c2ecf20Sopenharmony_ci struct mutex __mutex; /* Protects the queue. */ 1278c2ecf20Sopenharmony_ci struct mutex *mutex; /* Shared by producer and consumer queues. */ 1288c2ecf20Sopenharmony_ci size_t num_pages; /* Number of pages incl. header. */ 1298c2ecf20Sopenharmony_ci bool host; /* Host or guest? */ 1308c2ecf20Sopenharmony_ci union { 1318c2ecf20Sopenharmony_ci struct { 1328c2ecf20Sopenharmony_ci dma_addr_t *pas; 1338c2ecf20Sopenharmony_ci void **vas; 1348c2ecf20Sopenharmony_ci } g; /* Used by the guest. */ 1358c2ecf20Sopenharmony_ci struct { 1368c2ecf20Sopenharmony_ci struct page **page; 1378c2ecf20Sopenharmony_ci struct page **header_page; 1388c2ecf20Sopenharmony_ci } h; /* Used by the host. */ 1398c2ecf20Sopenharmony_ci } u; 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * This structure is opaque to the clients. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_cistruct vmci_qp { 1468c2ecf20Sopenharmony_ci struct vmci_handle handle; 1478c2ecf20Sopenharmony_ci struct vmci_queue *produce_q; 1488c2ecf20Sopenharmony_ci struct vmci_queue *consume_q; 1498c2ecf20Sopenharmony_ci u64 produce_q_size; 1508c2ecf20Sopenharmony_ci u64 consume_q_size; 1518c2ecf20Sopenharmony_ci u32 peer; 1528c2ecf20Sopenharmony_ci u32 flags; 1538c2ecf20Sopenharmony_ci u32 priv_flags; 1548c2ecf20Sopenharmony_ci bool guest_endpoint; 1558c2ecf20Sopenharmony_ci unsigned int blocked; 1568c2ecf20Sopenharmony_ci unsigned int generation; 1578c2ecf20Sopenharmony_ci wait_queue_head_t event; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cienum qp_broker_state { 1618c2ecf20Sopenharmony_ci VMCIQPB_NEW, 1628c2ecf20Sopenharmony_ci VMCIQPB_CREATED_NO_MEM, 1638c2ecf20Sopenharmony_ci VMCIQPB_CREATED_MEM, 1648c2ecf20Sopenharmony_ci VMCIQPB_ATTACHED_NO_MEM, 1658c2ecf20Sopenharmony_ci VMCIQPB_ATTACHED_MEM, 1668c2ecf20Sopenharmony_ci VMCIQPB_SHUTDOWN_NO_MEM, 1678c2ecf20Sopenharmony_ci VMCIQPB_SHUTDOWN_MEM, 1688c2ecf20Sopenharmony_ci VMCIQPB_GONE 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define QPBROKERSTATE_HAS_MEM(_qpb) (_qpb->state == VMCIQPB_CREATED_MEM || \ 1728c2ecf20Sopenharmony_ci _qpb->state == VMCIQPB_ATTACHED_MEM || \ 1738c2ecf20Sopenharmony_ci _qpb->state == VMCIQPB_SHUTDOWN_MEM) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * In the queue pair broker, we always use the guest point of view for 1778c2ecf20Sopenharmony_ci * the produce and consume queue values and references, e.g., the 1788c2ecf20Sopenharmony_ci * produce queue size stored is the guests produce queue size. The 1798c2ecf20Sopenharmony_ci * host endpoint will need to swap these around. The only exception is 1808c2ecf20Sopenharmony_ci * the local queue pairs on the host, in which case the host endpoint 1818c2ecf20Sopenharmony_ci * that creates the queue pair will have the right orientation, and 1828c2ecf20Sopenharmony_ci * the attaching host endpoint will need to swap. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistruct qp_entry { 1858c2ecf20Sopenharmony_ci struct list_head list_item; 1868c2ecf20Sopenharmony_ci struct vmci_handle handle; 1878c2ecf20Sopenharmony_ci u32 peer; 1888c2ecf20Sopenharmony_ci u32 flags; 1898c2ecf20Sopenharmony_ci u64 produce_size; 1908c2ecf20Sopenharmony_ci u64 consume_size; 1918c2ecf20Sopenharmony_ci u32 ref_count; 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistruct qp_broker_entry { 1958c2ecf20Sopenharmony_ci struct vmci_resource resource; 1968c2ecf20Sopenharmony_ci struct qp_entry qp; 1978c2ecf20Sopenharmony_ci u32 create_id; 1988c2ecf20Sopenharmony_ci u32 attach_id; 1998c2ecf20Sopenharmony_ci enum qp_broker_state state; 2008c2ecf20Sopenharmony_ci bool require_trusted_attach; 2018c2ecf20Sopenharmony_ci bool created_by_trusted; 2028c2ecf20Sopenharmony_ci bool vmci_page_files; /* Created by VMX using VMCI page files */ 2038c2ecf20Sopenharmony_ci struct vmci_queue *produce_q; 2048c2ecf20Sopenharmony_ci struct vmci_queue *consume_q; 2058c2ecf20Sopenharmony_ci struct vmci_queue_header saved_produce_q; 2068c2ecf20Sopenharmony_ci struct vmci_queue_header saved_consume_q; 2078c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb; 2088c2ecf20Sopenharmony_ci void *client_data; 2098c2ecf20Sopenharmony_ci void *local_mem; /* Kernel memory for local queue pair */ 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistruct qp_guest_endpoint { 2138c2ecf20Sopenharmony_ci struct vmci_resource resource; 2148c2ecf20Sopenharmony_ci struct qp_entry qp; 2158c2ecf20Sopenharmony_ci u64 num_ppns; 2168c2ecf20Sopenharmony_ci void *produce_q; 2178c2ecf20Sopenharmony_ci void *consume_q; 2188c2ecf20Sopenharmony_ci struct ppn_set ppn_set; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct qp_list { 2228c2ecf20Sopenharmony_ci struct list_head head; 2238c2ecf20Sopenharmony_ci struct mutex mutex; /* Protect queue list. */ 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic struct qp_list qp_broker_list = { 2278c2ecf20Sopenharmony_ci .head = LIST_HEAD_INIT(qp_broker_list.head), 2288c2ecf20Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(qp_broker_list.mutex), 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct qp_list qp_guest_endpoints = { 2328c2ecf20Sopenharmony_ci .head = LIST_HEAD_INIT(qp_guest_endpoints.head), 2338c2ecf20Sopenharmony_ci .mutex = __MUTEX_INITIALIZER(qp_guest_endpoints.mutex), 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci#define INVALID_VMCI_GUEST_MEM_ID 0 2378c2ecf20Sopenharmony_ci#define QPE_NUM_PAGES(_QPE) ((u32) \ 2388c2ecf20Sopenharmony_ci (DIV_ROUND_UP(_QPE.produce_size, PAGE_SIZE) + \ 2398c2ecf20Sopenharmony_ci DIV_ROUND_UP(_QPE.consume_size, PAGE_SIZE) + 2)) 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Frees kernel VA space for a given queue and its queue header, and 2448c2ecf20Sopenharmony_ci * frees physical data pages. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic void qp_free_queue(void *q, u64 size) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct vmci_queue *queue = q; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (queue) { 2518c2ecf20Sopenharmony_ci u64 i; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Given size does not include header, so add in a page here. */ 2548c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(size, PAGE_SIZE) + 1; i++) { 2558c2ecf20Sopenharmony_ci dma_free_coherent(&vmci_pdev->dev, PAGE_SIZE, 2568c2ecf20Sopenharmony_ci queue->kernel_if->u.g.vas[i], 2578c2ecf20Sopenharmony_ci queue->kernel_if->u.g.pas[i]); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci vfree(queue); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* 2658c2ecf20Sopenharmony_ci * Allocates kernel queue pages of specified size with IOMMU mappings, 2668c2ecf20Sopenharmony_ci * plus space for the queue structure/kernel interface and the queue 2678c2ecf20Sopenharmony_ci * header. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic void *qp_alloc_queue(u64 size, u32 flags) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci u64 i; 2728c2ecf20Sopenharmony_ci struct vmci_queue *queue; 2738c2ecf20Sopenharmony_ci size_t pas_size; 2748c2ecf20Sopenharmony_ci size_t vas_size; 2758c2ecf20Sopenharmony_ci size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if); 2768c2ecf20Sopenharmony_ci u64 num_pages; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (size > SIZE_MAX - PAGE_SIZE) 2798c2ecf20Sopenharmony_ci return NULL; 2808c2ecf20Sopenharmony_ci num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; 2818c2ecf20Sopenharmony_ci if (num_pages > 2828c2ecf20Sopenharmony_ci (SIZE_MAX - queue_size) / 2838c2ecf20Sopenharmony_ci (sizeof(*queue->kernel_if->u.g.pas) + 2848c2ecf20Sopenharmony_ci sizeof(*queue->kernel_if->u.g.vas))) 2858c2ecf20Sopenharmony_ci return NULL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas); 2888c2ecf20Sopenharmony_ci vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas); 2898c2ecf20Sopenharmony_ci queue_size += pas_size + vas_size; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci queue = vmalloc(queue_size); 2928c2ecf20Sopenharmony_ci if (!queue) 2938c2ecf20Sopenharmony_ci return NULL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci queue->q_header = NULL; 2968c2ecf20Sopenharmony_ci queue->saved_header = NULL; 2978c2ecf20Sopenharmony_ci queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); 2988c2ecf20Sopenharmony_ci queue->kernel_if->mutex = NULL; 2998c2ecf20Sopenharmony_ci queue->kernel_if->num_pages = num_pages; 3008c2ecf20Sopenharmony_ci queue->kernel_if->u.g.pas = (dma_addr_t *)(queue->kernel_if + 1); 3018c2ecf20Sopenharmony_ci queue->kernel_if->u.g.vas = 3028c2ecf20Sopenharmony_ci (void **)((u8 *)queue->kernel_if->u.g.pas + pas_size); 3038c2ecf20Sopenharmony_ci queue->kernel_if->host = false; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci for (i = 0; i < num_pages; i++) { 3068c2ecf20Sopenharmony_ci queue->kernel_if->u.g.vas[i] = 3078c2ecf20Sopenharmony_ci dma_alloc_coherent(&vmci_pdev->dev, PAGE_SIZE, 3088c2ecf20Sopenharmony_ci &queue->kernel_if->u.g.pas[i], 3098c2ecf20Sopenharmony_ci GFP_KERNEL); 3108c2ecf20Sopenharmony_ci if (!queue->kernel_if->u.g.vas[i]) { 3118c2ecf20Sopenharmony_ci /* Size excl. the header. */ 3128c2ecf20Sopenharmony_ci qp_free_queue(queue, i * PAGE_SIZE); 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Queue header is the first page. */ 3188c2ecf20Sopenharmony_ci queue->q_header = queue->kernel_if->u.g.vas[0]; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return queue; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * Copies from a given buffer or iovector to a VMCI Queue. Uses 3258c2ecf20Sopenharmony_ci * kmap()/kunmap() to dynamically map/unmap required portions of the queue 3268c2ecf20Sopenharmony_ci * by traversing the offset -> page translation structure for the queue. 3278c2ecf20Sopenharmony_ci * Assumes that offset + size does not wrap around in the queue. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_cistatic int qp_memcpy_to_queue_iter(struct vmci_queue *queue, 3308c2ecf20Sopenharmony_ci u64 queue_offset, 3318c2ecf20Sopenharmony_ci struct iov_iter *from, 3328c2ecf20Sopenharmony_ci size_t size) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct vmci_queue_kern_if *kernel_if = queue->kernel_if; 3358c2ecf20Sopenharmony_ci size_t bytes_copied = 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci while (bytes_copied < size) { 3388c2ecf20Sopenharmony_ci const u64 page_index = 3398c2ecf20Sopenharmony_ci (queue_offset + bytes_copied) / PAGE_SIZE; 3408c2ecf20Sopenharmony_ci const size_t page_offset = 3418c2ecf20Sopenharmony_ci (queue_offset + bytes_copied) & (PAGE_SIZE - 1); 3428c2ecf20Sopenharmony_ci void *va; 3438c2ecf20Sopenharmony_ci size_t to_copy; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (kernel_if->host) 3468c2ecf20Sopenharmony_ci va = kmap(kernel_if->u.h.page[page_index]); 3478c2ecf20Sopenharmony_ci else 3488c2ecf20Sopenharmony_ci va = kernel_if->u.g.vas[page_index + 1]; 3498c2ecf20Sopenharmony_ci /* Skip header. */ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (size - bytes_copied > PAGE_SIZE - page_offset) 3528c2ecf20Sopenharmony_ci /* Enough payload to fill up from this page. */ 3538c2ecf20Sopenharmony_ci to_copy = PAGE_SIZE - page_offset; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci to_copy = size - bytes_copied; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!copy_from_iter_full((u8 *)va + page_offset, to_copy, 3588c2ecf20Sopenharmony_ci from)) { 3598c2ecf20Sopenharmony_ci if (kernel_if->host) 3608c2ecf20Sopenharmony_ci kunmap(kernel_if->u.h.page[page_index]); 3618c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci bytes_copied += to_copy; 3648c2ecf20Sopenharmony_ci if (kernel_if->host) 3658c2ecf20Sopenharmony_ci kunmap(kernel_if->u.h.page[page_index]); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/* 3728c2ecf20Sopenharmony_ci * Copies to a given buffer or iovector from a VMCI Queue. Uses 3738c2ecf20Sopenharmony_ci * kmap()/kunmap() to dynamically map/unmap required portions of the queue 3748c2ecf20Sopenharmony_ci * by traversing the offset -> page translation structure for the queue. 3758c2ecf20Sopenharmony_ci * Assumes that offset + size does not wrap around in the queue. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_cistatic int qp_memcpy_from_queue_iter(struct iov_iter *to, 3788c2ecf20Sopenharmony_ci const struct vmci_queue *queue, 3798c2ecf20Sopenharmony_ci u64 queue_offset, size_t size) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct vmci_queue_kern_if *kernel_if = queue->kernel_if; 3828c2ecf20Sopenharmony_ci size_t bytes_copied = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci while (bytes_copied < size) { 3858c2ecf20Sopenharmony_ci const u64 page_index = 3868c2ecf20Sopenharmony_ci (queue_offset + bytes_copied) / PAGE_SIZE; 3878c2ecf20Sopenharmony_ci const size_t page_offset = 3888c2ecf20Sopenharmony_ci (queue_offset + bytes_copied) & (PAGE_SIZE - 1); 3898c2ecf20Sopenharmony_ci void *va; 3908c2ecf20Sopenharmony_ci size_t to_copy; 3918c2ecf20Sopenharmony_ci int err; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (kernel_if->host) 3948c2ecf20Sopenharmony_ci va = kmap(kernel_if->u.h.page[page_index]); 3958c2ecf20Sopenharmony_ci else 3968c2ecf20Sopenharmony_ci va = kernel_if->u.g.vas[page_index + 1]; 3978c2ecf20Sopenharmony_ci /* Skip header. */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (size - bytes_copied > PAGE_SIZE - page_offset) 4008c2ecf20Sopenharmony_ci /* Enough payload to fill up this page. */ 4018c2ecf20Sopenharmony_ci to_copy = PAGE_SIZE - page_offset; 4028c2ecf20Sopenharmony_ci else 4038c2ecf20Sopenharmony_ci to_copy = size - bytes_copied; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci err = copy_to_iter((u8 *)va + page_offset, to_copy, to); 4068c2ecf20Sopenharmony_ci if (err != to_copy) { 4078c2ecf20Sopenharmony_ci if (kernel_if->host) 4088c2ecf20Sopenharmony_ci kunmap(kernel_if->u.h.page[page_index]); 4098c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci bytes_copied += to_copy; 4128c2ecf20Sopenharmony_ci if (kernel_if->host) 4138c2ecf20Sopenharmony_ci kunmap(kernel_if->u.h.page[page_index]); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* 4208c2ecf20Sopenharmony_ci * Allocates two list of PPNs --- one for the pages in the produce queue, 4218c2ecf20Sopenharmony_ci * and the other for the pages in the consume queue. Intializes the list 4228c2ecf20Sopenharmony_ci * of PPNs with the page frame numbers of the KVA for the two queues (and 4238c2ecf20Sopenharmony_ci * the queue headers). 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_cistatic int qp_alloc_ppn_set(void *prod_q, 4268c2ecf20Sopenharmony_ci u64 num_produce_pages, 4278c2ecf20Sopenharmony_ci void *cons_q, 4288c2ecf20Sopenharmony_ci u64 num_consume_pages, struct ppn_set *ppn_set) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci u64 *produce_ppns; 4318c2ecf20Sopenharmony_ci u64 *consume_ppns; 4328c2ecf20Sopenharmony_ci struct vmci_queue *produce_q = prod_q; 4338c2ecf20Sopenharmony_ci struct vmci_queue *consume_q = cons_q; 4348c2ecf20Sopenharmony_ci u64 i; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!produce_q || !num_produce_pages || !consume_q || 4378c2ecf20Sopenharmony_ci !num_consume_pages || !ppn_set) 4388c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (ppn_set->initialized) 4418c2ecf20Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci produce_ppns = 4448c2ecf20Sopenharmony_ci kmalloc_array(num_produce_pages, sizeof(*produce_ppns), 4458c2ecf20Sopenharmony_ci GFP_KERNEL); 4468c2ecf20Sopenharmony_ci if (!produce_ppns) 4478c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci consume_ppns = 4508c2ecf20Sopenharmony_ci kmalloc_array(num_consume_pages, sizeof(*consume_ppns), 4518c2ecf20Sopenharmony_ci GFP_KERNEL); 4528c2ecf20Sopenharmony_ci if (!consume_ppns) { 4538c2ecf20Sopenharmony_ci kfree(produce_ppns); 4548c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < num_produce_pages; i++) 4588c2ecf20Sopenharmony_ci produce_ppns[i] = 4598c2ecf20Sopenharmony_ci produce_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for (i = 0; i < num_consume_pages; i++) 4628c2ecf20Sopenharmony_ci consume_ppns[i] = 4638c2ecf20Sopenharmony_ci consume_q->kernel_if->u.g.pas[i] >> PAGE_SHIFT; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ppn_set->num_produce_pages = num_produce_pages; 4668c2ecf20Sopenharmony_ci ppn_set->num_consume_pages = num_consume_pages; 4678c2ecf20Sopenharmony_ci ppn_set->produce_ppns = produce_ppns; 4688c2ecf20Sopenharmony_ci ppn_set->consume_ppns = consume_ppns; 4698c2ecf20Sopenharmony_ci ppn_set->initialized = true; 4708c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* 4748c2ecf20Sopenharmony_ci * Frees the two list of PPNs for a queue pair. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_cistatic void qp_free_ppn_set(struct ppn_set *ppn_set) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci if (ppn_set->initialized) { 4798c2ecf20Sopenharmony_ci /* Do not call these functions on NULL inputs. */ 4808c2ecf20Sopenharmony_ci kfree(ppn_set->produce_ppns); 4818c2ecf20Sopenharmony_ci kfree(ppn_set->consume_ppns); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci memset(ppn_set, 0, sizeof(*ppn_set)); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* 4878c2ecf20Sopenharmony_ci * Populates the list of PPNs in the hypercall structure with the PPNS 4888c2ecf20Sopenharmony_ci * of the produce queue and the consume queue. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_cistatic int qp_populate_ppn_set(u8 *call_buf, const struct ppn_set *ppn_set) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci if (vmci_use_ppn64()) { 4938c2ecf20Sopenharmony_ci memcpy(call_buf, ppn_set->produce_ppns, 4948c2ecf20Sopenharmony_ci ppn_set->num_produce_pages * 4958c2ecf20Sopenharmony_ci sizeof(*ppn_set->produce_ppns)); 4968c2ecf20Sopenharmony_ci memcpy(call_buf + 4978c2ecf20Sopenharmony_ci ppn_set->num_produce_pages * 4988c2ecf20Sopenharmony_ci sizeof(*ppn_set->produce_ppns), 4998c2ecf20Sopenharmony_ci ppn_set->consume_ppns, 5008c2ecf20Sopenharmony_ci ppn_set->num_consume_pages * 5018c2ecf20Sopenharmony_ci sizeof(*ppn_set->consume_ppns)); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci int i; 5048c2ecf20Sopenharmony_ci u32 *ppns = (u32 *) call_buf; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci for (i = 0; i < ppn_set->num_produce_pages; i++) 5078c2ecf20Sopenharmony_ci ppns[i] = (u32) ppn_set->produce_ppns[i]; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ppns = &ppns[ppn_set->num_produce_pages]; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci for (i = 0; i < ppn_set->num_consume_pages; i++) 5128c2ecf20Sopenharmony_ci ppns[i] = (u32) ppn_set->consume_ppns[i]; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/* 5198c2ecf20Sopenharmony_ci * Allocates kernel VA space of specified size plus space for the queue 5208c2ecf20Sopenharmony_ci * and kernel interface. This is different from the guest queue allocator, 5218c2ecf20Sopenharmony_ci * because we do not allocate our own queue header/data pages here but 5228c2ecf20Sopenharmony_ci * share those of the guest. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic struct vmci_queue *qp_host_alloc_queue(u64 size) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct vmci_queue *queue; 5278c2ecf20Sopenharmony_ci size_t queue_page_size; 5288c2ecf20Sopenharmony_ci u64 num_pages; 5298c2ecf20Sopenharmony_ci const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if)); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (size > SIZE_MAX - PAGE_SIZE) 5328c2ecf20Sopenharmony_ci return NULL; 5338c2ecf20Sopenharmony_ci num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; 5348c2ecf20Sopenharmony_ci if (num_pages > (SIZE_MAX - queue_size) / 5358c2ecf20Sopenharmony_ci sizeof(*queue->kernel_if->u.h.page)) 5368c2ecf20Sopenharmony_ci return NULL; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci queue_page_size = num_pages * sizeof(*queue->kernel_if->u.h.page); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (queue_size + queue_page_size > KMALLOC_MAX_SIZE) 5418c2ecf20Sopenharmony_ci return NULL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL); 5448c2ecf20Sopenharmony_ci if (queue) { 5458c2ecf20Sopenharmony_ci queue->q_header = NULL; 5468c2ecf20Sopenharmony_ci queue->saved_header = NULL; 5478c2ecf20Sopenharmony_ci queue->kernel_if = (struct vmci_queue_kern_if *)(queue + 1); 5488c2ecf20Sopenharmony_ci queue->kernel_if->host = true; 5498c2ecf20Sopenharmony_ci queue->kernel_if->mutex = NULL; 5508c2ecf20Sopenharmony_ci queue->kernel_if->num_pages = num_pages; 5518c2ecf20Sopenharmony_ci queue->kernel_if->u.h.header_page = 5528c2ecf20Sopenharmony_ci (struct page **)((u8 *)queue + queue_size); 5538c2ecf20Sopenharmony_ci queue->kernel_if->u.h.page = 5548c2ecf20Sopenharmony_ci &queue->kernel_if->u.h.header_page[1]; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci return queue; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* 5618c2ecf20Sopenharmony_ci * Frees kernel memory for a given queue (header plus translation 5628c2ecf20Sopenharmony_ci * structure). 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_cistatic void qp_host_free_queue(struct vmci_queue *queue, u64 queue_size) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci kfree(queue); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * Initialize the mutex for the pair of queues. This mutex is used to 5718c2ecf20Sopenharmony_ci * protect the q_header and the buffer from changing out from under any 5728c2ecf20Sopenharmony_ci * users of either queue. Of course, it's only any good if the mutexes 5738c2ecf20Sopenharmony_ci * are actually acquired. Queue structure must lie on non-paged memory 5748c2ecf20Sopenharmony_ci * or we cannot guarantee access to the mutex. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_cistatic void qp_init_queue_mutex(struct vmci_queue *produce_q, 5778c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci /* 5808c2ecf20Sopenharmony_ci * Only the host queue has shared state - the guest queues do not 5818c2ecf20Sopenharmony_ci * need to synchronize access using a queue mutex. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (produce_q->kernel_if->host) { 5858c2ecf20Sopenharmony_ci produce_q->kernel_if->mutex = &produce_q->kernel_if->__mutex; 5868c2ecf20Sopenharmony_ci consume_q->kernel_if->mutex = &produce_q->kernel_if->__mutex; 5878c2ecf20Sopenharmony_ci mutex_init(produce_q->kernel_if->mutex); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci/* 5928c2ecf20Sopenharmony_ci * Cleans up the mutex for the pair of queues. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_cistatic void qp_cleanup_queue_mutex(struct vmci_queue *produce_q, 5958c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci if (produce_q->kernel_if->host) { 5988c2ecf20Sopenharmony_ci produce_q->kernel_if->mutex = NULL; 5998c2ecf20Sopenharmony_ci consume_q->kernel_if->mutex = NULL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* 6048c2ecf20Sopenharmony_ci * Acquire the mutex for the queue. Note that the produce_q and 6058c2ecf20Sopenharmony_ci * the consume_q share a mutex. So, only one of the two need to 6068c2ecf20Sopenharmony_ci * be passed in to this routine. Either will work just fine. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_cistatic void qp_acquire_queue_mutex(struct vmci_queue *queue) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci if (queue->kernel_if->host) 6118c2ecf20Sopenharmony_ci mutex_lock(queue->kernel_if->mutex); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/* 6158c2ecf20Sopenharmony_ci * Release the mutex for the queue. Note that the produce_q and 6168c2ecf20Sopenharmony_ci * the consume_q share a mutex. So, only one of the two need to 6178c2ecf20Sopenharmony_ci * be passed in to this routine. Either will work just fine. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_cistatic void qp_release_queue_mutex(struct vmci_queue *queue) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci if (queue->kernel_if->host) 6228c2ecf20Sopenharmony_ci mutex_unlock(queue->kernel_if->mutex); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/* 6268c2ecf20Sopenharmony_ci * Helper function to release pages in the PageStoreAttachInfo 6278c2ecf20Sopenharmony_ci * previously obtained using get_user_pages. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_cistatic void qp_release_pages(struct page **pages, 6308c2ecf20Sopenharmony_ci u64 num_pages, bool dirty) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci int i; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci for (i = 0; i < num_pages; i++) { 6358c2ecf20Sopenharmony_ci if (dirty) 6368c2ecf20Sopenharmony_ci set_page_dirty_lock(pages[i]); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci put_page(pages[i]); 6398c2ecf20Sopenharmony_ci pages[i] = NULL; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* 6448c2ecf20Sopenharmony_ci * Lock the user pages referenced by the {produce,consume}Buffer 6458c2ecf20Sopenharmony_ci * struct into memory and populate the {produce,consume}Pages 6468c2ecf20Sopenharmony_ci * arrays in the attach structure with them. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_cistatic int qp_host_get_user_memory(u64 produce_uva, 6498c2ecf20Sopenharmony_ci u64 consume_uva, 6508c2ecf20Sopenharmony_ci struct vmci_queue *produce_q, 6518c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci int retval; 6548c2ecf20Sopenharmony_ci int err = VMCI_SUCCESS; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci retval = get_user_pages_fast((uintptr_t) produce_uva, 6578c2ecf20Sopenharmony_ci produce_q->kernel_if->num_pages, 6588c2ecf20Sopenharmony_ci FOLL_WRITE, 6598c2ecf20Sopenharmony_ci produce_q->kernel_if->u.h.header_page); 6608c2ecf20Sopenharmony_ci if (retval < (int)produce_q->kernel_if->num_pages) { 6618c2ecf20Sopenharmony_ci pr_debug("get_user_pages_fast(produce) failed (retval=%d)", 6628c2ecf20Sopenharmony_ci retval); 6638c2ecf20Sopenharmony_ci if (retval > 0) 6648c2ecf20Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 6658c2ecf20Sopenharmony_ci retval, false); 6668c2ecf20Sopenharmony_ci err = VMCI_ERROR_NO_MEM; 6678c2ecf20Sopenharmony_ci goto out; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci retval = get_user_pages_fast((uintptr_t) consume_uva, 6718c2ecf20Sopenharmony_ci consume_q->kernel_if->num_pages, 6728c2ecf20Sopenharmony_ci FOLL_WRITE, 6738c2ecf20Sopenharmony_ci consume_q->kernel_if->u.h.header_page); 6748c2ecf20Sopenharmony_ci if (retval < (int)consume_q->kernel_if->num_pages) { 6758c2ecf20Sopenharmony_ci pr_debug("get_user_pages_fast(consume) failed (retval=%d)", 6768c2ecf20Sopenharmony_ci retval); 6778c2ecf20Sopenharmony_ci if (retval > 0) 6788c2ecf20Sopenharmony_ci qp_release_pages(consume_q->kernel_if->u.h.header_page, 6798c2ecf20Sopenharmony_ci retval, false); 6808c2ecf20Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 6818c2ecf20Sopenharmony_ci produce_q->kernel_if->num_pages, false); 6828c2ecf20Sopenharmony_ci err = VMCI_ERROR_NO_MEM; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci out: 6868c2ecf20Sopenharmony_ci return err; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* 6908c2ecf20Sopenharmony_ci * Registers the specification of the user pages used for backing a queue 6918c2ecf20Sopenharmony_ci * pair. Enough information to map in pages is stored in the OS specific 6928c2ecf20Sopenharmony_ci * part of the struct vmci_queue structure. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cistatic int qp_host_register_user_memory(struct vmci_qp_page_store *page_store, 6958c2ecf20Sopenharmony_ci struct vmci_queue *produce_q, 6968c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci u64 produce_uva; 6998c2ecf20Sopenharmony_ci u64 consume_uva; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* 7028c2ecf20Sopenharmony_ci * The new style and the old style mapping only differs in 7038c2ecf20Sopenharmony_ci * that we either get a single or two UVAs, so we split the 7048c2ecf20Sopenharmony_ci * single UVA range at the appropriate spot. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci produce_uva = page_store->pages; 7078c2ecf20Sopenharmony_ci consume_uva = page_store->pages + 7088c2ecf20Sopenharmony_ci produce_q->kernel_if->num_pages * PAGE_SIZE; 7098c2ecf20Sopenharmony_ci return qp_host_get_user_memory(produce_uva, consume_uva, produce_q, 7108c2ecf20Sopenharmony_ci consume_q); 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci/* 7148c2ecf20Sopenharmony_ci * Releases and removes the references to user pages stored in the attach 7158c2ecf20Sopenharmony_ci * struct. Pages are released from the page cache and may become 7168c2ecf20Sopenharmony_ci * swappable again. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_cistatic void qp_host_unregister_user_memory(struct vmci_queue *produce_q, 7198c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci qp_release_pages(produce_q->kernel_if->u.h.header_page, 7228c2ecf20Sopenharmony_ci produce_q->kernel_if->num_pages, true); 7238c2ecf20Sopenharmony_ci memset(produce_q->kernel_if->u.h.header_page, 0, 7248c2ecf20Sopenharmony_ci sizeof(*produce_q->kernel_if->u.h.header_page) * 7258c2ecf20Sopenharmony_ci produce_q->kernel_if->num_pages); 7268c2ecf20Sopenharmony_ci qp_release_pages(consume_q->kernel_if->u.h.header_page, 7278c2ecf20Sopenharmony_ci consume_q->kernel_if->num_pages, true); 7288c2ecf20Sopenharmony_ci memset(consume_q->kernel_if->u.h.header_page, 0, 7298c2ecf20Sopenharmony_ci sizeof(*consume_q->kernel_if->u.h.header_page) * 7308c2ecf20Sopenharmony_ci consume_q->kernel_if->num_pages); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* 7348c2ecf20Sopenharmony_ci * Once qp_host_register_user_memory has been performed on a 7358c2ecf20Sopenharmony_ci * queue, the queue pair headers can be mapped into the 7368c2ecf20Sopenharmony_ci * kernel. Once mapped, they must be unmapped with 7378c2ecf20Sopenharmony_ci * qp_host_unmap_queues prior to calling 7388c2ecf20Sopenharmony_ci * qp_host_unregister_user_memory. 7398c2ecf20Sopenharmony_ci * Pages are pinned. 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_cistatic int qp_host_map_queues(struct vmci_queue *produce_q, 7428c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci int result; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (!produce_q->q_header || !consume_q->q_header) { 7478c2ecf20Sopenharmony_ci struct page *headers[2]; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (produce_q->q_header != consume_q->q_header) 7508c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (produce_q->kernel_if->u.h.header_page == NULL || 7538c2ecf20Sopenharmony_ci *produce_q->kernel_if->u.h.header_page == NULL) 7548c2ecf20Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci headers[0] = *produce_q->kernel_if->u.h.header_page; 7578c2ecf20Sopenharmony_ci headers[1] = *consume_q->kernel_if->u.h.header_page; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci produce_q->q_header = vmap(headers, 2, VM_MAP, PAGE_KERNEL); 7608c2ecf20Sopenharmony_ci if (produce_q->q_header != NULL) { 7618c2ecf20Sopenharmony_ci consume_q->q_header = 7628c2ecf20Sopenharmony_ci (struct vmci_queue_header *)((u8 *) 7638c2ecf20Sopenharmony_ci produce_q->q_header + 7648c2ecf20Sopenharmony_ci PAGE_SIZE); 7658c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 7668c2ecf20Sopenharmony_ci } else { 7678c2ecf20Sopenharmony_ci pr_warn("vmap failed\n"); 7688c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci return result; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* 7788c2ecf20Sopenharmony_ci * Unmaps previously mapped queue pair headers from the kernel. 7798c2ecf20Sopenharmony_ci * Pages are unpinned. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic int qp_host_unmap_queues(u32 gid, 7828c2ecf20Sopenharmony_ci struct vmci_queue *produce_q, 7838c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci if (produce_q->q_header) { 7868c2ecf20Sopenharmony_ci if (produce_q->q_header < consume_q->q_header) 7878c2ecf20Sopenharmony_ci vunmap(produce_q->q_header); 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci vunmap(consume_q->q_header); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci produce_q->q_header = NULL; 7928c2ecf20Sopenharmony_ci consume_q->q_header = NULL; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci/* 7998c2ecf20Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. Assumes 8008c2ecf20Sopenharmony_ci * that the list is locked. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_cistatic struct qp_entry *qp_list_find(struct qp_list *qp_list, 8038c2ecf20Sopenharmony_ci struct vmci_handle handle) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct qp_entry *entry; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 8088c2ecf20Sopenharmony_ci return NULL; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci list_for_each_entry(entry, &qp_list->head, list_item) { 8118c2ecf20Sopenharmony_ci if (vmci_handle_is_equal(entry->handle, handle)) 8128c2ecf20Sopenharmony_ci return entry; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return NULL; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci/* 8198c2ecf20Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_cistatic struct qp_guest_endpoint * 8228c2ecf20Sopenharmony_ciqp_guest_handle_to_entry(struct vmci_handle handle) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct qp_guest_endpoint *entry; 8258c2ecf20Sopenharmony_ci struct qp_entry *qp = qp_list_find(&qp_guest_endpoints, handle); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci entry = qp ? container_of( 8288c2ecf20Sopenharmony_ci qp, struct qp_guest_endpoint, qp) : NULL; 8298c2ecf20Sopenharmony_ci return entry; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci/* 8338c2ecf20Sopenharmony_ci * Finds the entry in the list corresponding to a given handle. 8348c2ecf20Sopenharmony_ci */ 8358c2ecf20Sopenharmony_cistatic struct qp_broker_entry * 8368c2ecf20Sopenharmony_ciqp_broker_handle_to_entry(struct vmci_handle handle) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 8398c2ecf20Sopenharmony_ci struct qp_entry *qp = qp_list_find(&qp_broker_list, handle); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci entry = qp ? container_of( 8428c2ecf20Sopenharmony_ci qp, struct qp_broker_entry, qp) : NULL; 8438c2ecf20Sopenharmony_ci return entry; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* 8478c2ecf20Sopenharmony_ci * Dispatches a queue pair event message directly into the local event 8488c2ecf20Sopenharmony_ci * queue. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_cistatic int qp_notify_peer_local(bool attach, struct vmci_handle handle) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci u32 context_id = vmci_get_context_id(); 8538c2ecf20Sopenharmony_ci struct vmci_event_qp ev; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 8568c2ecf20Sopenharmony_ci ev.msg.hdr.dst = vmci_make_handle(context_id, VMCI_EVENT_HANDLER); 8578c2ecf20Sopenharmony_ci ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 8588c2ecf20Sopenharmony_ci VMCI_CONTEXT_RESOURCE_ID); 8598c2ecf20Sopenharmony_ci ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); 8608c2ecf20Sopenharmony_ci ev.msg.event_data.event = 8618c2ecf20Sopenharmony_ci attach ? VMCI_EVENT_QP_PEER_ATTACH : VMCI_EVENT_QP_PEER_DETACH; 8628c2ecf20Sopenharmony_ci ev.payload.peer_id = context_id; 8638c2ecf20Sopenharmony_ci ev.payload.handle = handle; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return vmci_event_dispatch(&ev.msg.hdr); 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci/* 8698c2ecf20Sopenharmony_ci * Allocates and initializes a qp_guest_endpoint structure. 8708c2ecf20Sopenharmony_ci * Allocates a queue_pair rid (and handle) iff the given entry has 8718c2ecf20Sopenharmony_ci * an invalid handle. 0 through VMCI_RESERVED_RESOURCE_ID_MAX 8728c2ecf20Sopenharmony_ci * are reserved handles. Assumes that the QP list mutex is held 8738c2ecf20Sopenharmony_ci * by the caller. 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_cistatic struct qp_guest_endpoint * 8768c2ecf20Sopenharmony_ciqp_guest_endpoint_create(struct vmci_handle handle, 8778c2ecf20Sopenharmony_ci u32 peer, 8788c2ecf20Sopenharmony_ci u32 flags, 8798c2ecf20Sopenharmony_ci u64 produce_size, 8808c2ecf20Sopenharmony_ci u64 consume_size, 8818c2ecf20Sopenharmony_ci void *produce_q, 8828c2ecf20Sopenharmony_ci void *consume_q) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci int result; 8858c2ecf20Sopenharmony_ci struct qp_guest_endpoint *entry; 8868c2ecf20Sopenharmony_ci /* One page each for the queue headers. */ 8878c2ecf20Sopenharmony_ci const u64 num_ppns = DIV_ROUND_UP(produce_size, PAGE_SIZE) + 8888c2ecf20Sopenharmony_ci DIV_ROUND_UP(consume_size, PAGE_SIZE) + 2; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle)) { 8918c2ecf20Sopenharmony_ci u32 context_id = vmci_get_context_id(); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci handle = vmci_make_handle(context_id, VMCI_INVALID_ID); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 8978c2ecf20Sopenharmony_ci if (entry) { 8988c2ecf20Sopenharmony_ci entry->qp.peer = peer; 8998c2ecf20Sopenharmony_ci entry->qp.flags = flags; 9008c2ecf20Sopenharmony_ci entry->qp.produce_size = produce_size; 9018c2ecf20Sopenharmony_ci entry->qp.consume_size = consume_size; 9028c2ecf20Sopenharmony_ci entry->qp.ref_count = 0; 9038c2ecf20Sopenharmony_ci entry->num_ppns = num_ppns; 9048c2ecf20Sopenharmony_ci entry->produce_q = produce_q; 9058c2ecf20Sopenharmony_ci entry->consume_q = consume_q; 9068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&entry->qp.list_item); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* Add resource obj */ 9098c2ecf20Sopenharmony_ci result = vmci_resource_add(&entry->resource, 9108c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_QPAIR_GUEST, 9118c2ecf20Sopenharmony_ci handle); 9128c2ecf20Sopenharmony_ci entry->qp.handle = vmci_resource_handle(&entry->resource); 9138c2ecf20Sopenharmony_ci if ((result != VMCI_SUCCESS) || 9148c2ecf20Sopenharmony_ci qp_list_find(&qp_guest_endpoints, entry->qp.handle)) { 9158c2ecf20Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d", 9168c2ecf20Sopenharmony_ci handle.context, handle.resource, result); 9178c2ecf20Sopenharmony_ci kfree(entry); 9188c2ecf20Sopenharmony_ci entry = NULL; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci return entry; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci/* 9258c2ecf20Sopenharmony_ci * Frees a qp_guest_endpoint structure. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_cistatic void qp_guest_endpoint_destroy(struct qp_guest_endpoint *entry) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci qp_free_ppn_set(&entry->ppn_set); 9308c2ecf20Sopenharmony_ci qp_cleanup_queue_mutex(entry->produce_q, entry->consume_q); 9318c2ecf20Sopenharmony_ci qp_free_queue(entry->produce_q, entry->qp.produce_size); 9328c2ecf20Sopenharmony_ci qp_free_queue(entry->consume_q, entry->qp.consume_size); 9338c2ecf20Sopenharmony_ci /* Unlink from resource hash table and free callback */ 9348c2ecf20Sopenharmony_ci vmci_resource_remove(&entry->resource); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci kfree(entry); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * Helper to make a queue_pairAlloc hypercall when the driver is 9418c2ecf20Sopenharmony_ci * supporting a guest device. 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_cistatic int qp_alloc_hypercall(const struct qp_guest_endpoint *entry) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct vmci_qp_alloc_msg *alloc_msg; 9468c2ecf20Sopenharmony_ci size_t msg_size; 9478c2ecf20Sopenharmony_ci size_t ppn_size; 9488c2ecf20Sopenharmony_ci int result; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (!entry || entry->num_ppns <= 2) 9518c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci ppn_size = vmci_use_ppn64() ? sizeof(u64) : sizeof(u32); 9548c2ecf20Sopenharmony_ci msg_size = sizeof(*alloc_msg) + 9558c2ecf20Sopenharmony_ci (size_t) entry->num_ppns * ppn_size; 9568c2ecf20Sopenharmony_ci alloc_msg = kmalloc(msg_size, GFP_KERNEL); 9578c2ecf20Sopenharmony_ci if (!alloc_msg) 9588c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci alloc_msg->hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 9618c2ecf20Sopenharmony_ci VMCI_QUEUEPAIR_ALLOC); 9628c2ecf20Sopenharmony_ci alloc_msg->hdr.src = VMCI_ANON_SRC_HANDLE; 9638c2ecf20Sopenharmony_ci alloc_msg->hdr.payload_size = msg_size - VMCI_DG_HEADERSIZE; 9648c2ecf20Sopenharmony_ci alloc_msg->handle = entry->qp.handle; 9658c2ecf20Sopenharmony_ci alloc_msg->peer = entry->qp.peer; 9668c2ecf20Sopenharmony_ci alloc_msg->flags = entry->qp.flags; 9678c2ecf20Sopenharmony_ci alloc_msg->produce_size = entry->qp.produce_size; 9688c2ecf20Sopenharmony_ci alloc_msg->consume_size = entry->qp.consume_size; 9698c2ecf20Sopenharmony_ci alloc_msg->num_ppns = entry->num_ppns; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci result = qp_populate_ppn_set((u8 *)alloc_msg + sizeof(*alloc_msg), 9728c2ecf20Sopenharmony_ci &entry->ppn_set); 9738c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 9748c2ecf20Sopenharmony_ci result = vmci_send_datagram(&alloc_msg->hdr); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci kfree(alloc_msg); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return result; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* 9828c2ecf20Sopenharmony_ci * Helper to make a queue_pairDetach hypercall when the driver is 9838c2ecf20Sopenharmony_ci * supporting a guest device. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_cistatic int qp_detatch_hypercall(struct vmci_handle handle) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct vmci_qp_detach_msg detach_msg; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci detach_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 9908c2ecf20Sopenharmony_ci VMCI_QUEUEPAIR_DETACH); 9918c2ecf20Sopenharmony_ci detach_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 9928c2ecf20Sopenharmony_ci detach_msg.hdr.payload_size = sizeof(handle); 9938c2ecf20Sopenharmony_ci detach_msg.handle = handle; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci return vmci_send_datagram(&detach_msg.hdr); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci/* 9998c2ecf20Sopenharmony_ci * Adds the given entry to the list. Assumes that the list is locked. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_cistatic void qp_list_add_entry(struct qp_list *qp_list, struct qp_entry *entry) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci if (entry) 10048c2ecf20Sopenharmony_ci list_add(&entry->list_item, &qp_list->head); 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci/* 10088c2ecf20Sopenharmony_ci * Removes the given entry from the list. Assumes that the list is locked. 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_cistatic void qp_list_remove_entry(struct qp_list *qp_list, 10118c2ecf20Sopenharmony_ci struct qp_entry *entry) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci if (entry) 10148c2ecf20Sopenharmony_ci list_del(&entry->list_item); 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci/* 10188c2ecf20Sopenharmony_ci * Helper for VMCI queue_pair detach interface. Frees the physical 10198c2ecf20Sopenharmony_ci * pages for the queue pair. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_cistatic int qp_detatch_guest_work(struct vmci_handle handle) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci int result; 10248c2ecf20Sopenharmony_ci struct qp_guest_endpoint *entry; 10258c2ecf20Sopenharmony_ci u32 ref_count = ~0; /* To avoid compiler warning below */ 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci entry = qp_guest_handle_to_entry(handle); 10308c2ecf20Sopenharmony_ci if (!entry) { 10318c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 10328c2ecf20Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (entry->qp.flags & VMCI_QPFLAG_LOCAL) { 10368c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (entry->qp.ref_count > 1) { 10398c2ecf20Sopenharmony_ci result = qp_notify_peer_local(false, handle); 10408c2ecf20Sopenharmony_ci /* 10418c2ecf20Sopenharmony_ci * We can fail to notify a local queuepair 10428c2ecf20Sopenharmony_ci * because we can't allocate. We still want 10438c2ecf20Sopenharmony_ci * to release the entry if that happens, so 10448c2ecf20Sopenharmony_ci * don't bail out yet. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci } else { 10488c2ecf20Sopenharmony_ci result = qp_detatch_hypercall(handle); 10498c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) { 10508c2ecf20Sopenharmony_ci /* 10518c2ecf20Sopenharmony_ci * We failed to notify a non-local queuepair. 10528c2ecf20Sopenharmony_ci * That other queuepair might still be 10538c2ecf20Sopenharmony_ci * accessing the shared memory, so don't 10548c2ecf20Sopenharmony_ci * release the entry yet. It will get cleaned 10558c2ecf20Sopenharmony_ci * up by VMCIqueue_pair_Exit() if necessary 10568c2ecf20Sopenharmony_ci * (assuming we are going away, otherwise why 10578c2ecf20Sopenharmony_ci * did this fail?). 10588c2ecf20Sopenharmony_ci */ 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 10618c2ecf20Sopenharmony_ci return result; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* 10668c2ecf20Sopenharmony_ci * If we get here then we either failed to notify a local queuepair, or 10678c2ecf20Sopenharmony_ci * we succeeded in all cases. Release the entry if required. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci entry->qp.ref_count--; 10718c2ecf20Sopenharmony_ci if (entry->qp.ref_count == 0) 10728c2ecf20Sopenharmony_ci qp_list_remove_entry(&qp_guest_endpoints, &entry->qp); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* If we didn't remove the entry, this could change once we unlock. */ 10758c2ecf20Sopenharmony_ci if (entry) 10768c2ecf20Sopenharmony_ci ref_count = entry->qp.ref_count; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (ref_count == 0) 10818c2ecf20Sopenharmony_ci qp_guest_endpoint_destroy(entry); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return result; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci/* 10878c2ecf20Sopenharmony_ci * This functions handles the actual allocation of a VMCI queue 10888c2ecf20Sopenharmony_ci * pair guest endpoint. Allocates physical pages for the queue 10898c2ecf20Sopenharmony_ci * pair. It makes OS dependent calls through generic wrappers. 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_cistatic int qp_alloc_guest_work(struct vmci_handle *handle, 10928c2ecf20Sopenharmony_ci struct vmci_queue **produce_q, 10938c2ecf20Sopenharmony_ci u64 produce_size, 10948c2ecf20Sopenharmony_ci struct vmci_queue **consume_q, 10958c2ecf20Sopenharmony_ci u64 consume_size, 10968c2ecf20Sopenharmony_ci u32 peer, 10978c2ecf20Sopenharmony_ci u32 flags, 10988c2ecf20Sopenharmony_ci u32 priv_flags) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci const u64 num_produce_pages = 11018c2ecf20Sopenharmony_ci DIV_ROUND_UP(produce_size, PAGE_SIZE) + 1; 11028c2ecf20Sopenharmony_ci const u64 num_consume_pages = 11038c2ecf20Sopenharmony_ci DIV_ROUND_UP(consume_size, PAGE_SIZE) + 1; 11048c2ecf20Sopenharmony_ci void *my_produce_q = NULL; 11058c2ecf20Sopenharmony_ci void *my_consume_q = NULL; 11068c2ecf20Sopenharmony_ci int result; 11078c2ecf20Sopenharmony_ci struct qp_guest_endpoint *queue_pair_entry = NULL; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (priv_flags != VMCI_NO_PRIVILEGE_FLAGS) 11108c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci queue_pair_entry = qp_guest_handle_to_entry(*handle); 11158c2ecf20Sopenharmony_ci if (queue_pair_entry) { 11168c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) { 11178c2ecf20Sopenharmony_ci /* Local attach case. */ 11188c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.ref_count > 1) { 11198c2ecf20Sopenharmony_ci pr_devel("Error attempting to attach more than once\n"); 11208c2ecf20Sopenharmony_ci result = VMCI_ERROR_UNAVAILABLE; 11218c2ecf20Sopenharmony_ci goto error_keep_entry; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.produce_size != consume_size || 11258c2ecf20Sopenharmony_ci queue_pair_entry->qp.consume_size != 11268c2ecf20Sopenharmony_ci produce_size || 11278c2ecf20Sopenharmony_ci queue_pair_entry->qp.flags != 11288c2ecf20Sopenharmony_ci (flags & ~VMCI_QPFLAG_ATTACH_ONLY)) { 11298c2ecf20Sopenharmony_ci pr_devel("Error mismatched queue pair in local attach\n"); 11308c2ecf20Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_MISMATCH; 11318c2ecf20Sopenharmony_ci goto error_keep_entry; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* 11358c2ecf20Sopenharmony_ci * Do a local attach. We swap the consume and 11368c2ecf20Sopenharmony_ci * produce queues for the attacher and deliver 11378c2ecf20Sopenharmony_ci * an attach event. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci result = qp_notify_peer_local(true, *handle); 11408c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 11418c2ecf20Sopenharmony_ci goto error_keep_entry; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci my_produce_q = queue_pair_entry->consume_q; 11448c2ecf20Sopenharmony_ci my_consume_q = queue_pair_entry->produce_q; 11458c2ecf20Sopenharmony_ci goto out; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci result = VMCI_ERROR_ALREADY_EXISTS; 11498c2ecf20Sopenharmony_ci goto error_keep_entry; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci my_produce_q = qp_alloc_queue(produce_size, flags); 11538c2ecf20Sopenharmony_ci if (!my_produce_q) { 11548c2ecf20Sopenharmony_ci pr_warn("Error allocating pages for produce queue\n"); 11558c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 11568c2ecf20Sopenharmony_ci goto error; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci my_consume_q = qp_alloc_queue(consume_size, flags); 11608c2ecf20Sopenharmony_ci if (!my_consume_q) { 11618c2ecf20Sopenharmony_ci pr_warn("Error allocating pages for consume queue\n"); 11628c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 11638c2ecf20Sopenharmony_ci goto error; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci queue_pair_entry = qp_guest_endpoint_create(*handle, peer, flags, 11678c2ecf20Sopenharmony_ci produce_size, consume_size, 11688c2ecf20Sopenharmony_ci my_produce_q, my_consume_q); 11698c2ecf20Sopenharmony_ci if (!queue_pair_entry) { 11708c2ecf20Sopenharmony_ci pr_warn("Error allocating memory in %s\n", __func__); 11718c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 11728c2ecf20Sopenharmony_ci goto error; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci result = qp_alloc_ppn_set(my_produce_q, num_produce_pages, my_consume_q, 11768c2ecf20Sopenharmony_ci num_consume_pages, 11778c2ecf20Sopenharmony_ci &queue_pair_entry->ppn_set); 11788c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) { 11798c2ecf20Sopenharmony_ci pr_warn("qp_alloc_ppn_set failed\n"); 11808c2ecf20Sopenharmony_ci goto error; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* 11848c2ecf20Sopenharmony_ci * It's only necessary to notify the host if this queue pair will be 11858c2ecf20Sopenharmony_ci * attached to from another context. 11868c2ecf20Sopenharmony_ci */ 11878c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) { 11888c2ecf20Sopenharmony_ci /* Local create case. */ 11898c2ecf20Sopenharmony_ci u32 context_id = vmci_get_context_id(); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * Enforce similar checks on local queue pairs as we 11938c2ecf20Sopenharmony_ci * do for regular ones. The handle's context must 11948c2ecf20Sopenharmony_ci * match the creator or attacher context id (here they 11958c2ecf20Sopenharmony_ci * are both the current context id) and the 11968c2ecf20Sopenharmony_ci * attach-only flag cannot exist during create. We 11978c2ecf20Sopenharmony_ci * also ensure specified peer is this context or an 11988c2ecf20Sopenharmony_ci * invalid one. 11998c2ecf20Sopenharmony_ci */ 12008c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.handle.context != context_id || 12018c2ecf20Sopenharmony_ci (queue_pair_entry->qp.peer != VMCI_INVALID_ID && 12028c2ecf20Sopenharmony_ci queue_pair_entry->qp.peer != context_id)) { 12038c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_ACCESS; 12048c2ecf20Sopenharmony_ci goto error; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (queue_pair_entry->qp.flags & VMCI_QPFLAG_ATTACH_ONLY) { 12088c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 12098c2ecf20Sopenharmony_ci goto error; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci } else { 12128c2ecf20Sopenharmony_ci result = qp_alloc_hypercall(queue_pair_entry); 12138c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) { 12148c2ecf20Sopenharmony_ci pr_warn("qp_alloc_hypercall result = %d\n", result); 12158c2ecf20Sopenharmony_ci goto error; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci qp_init_queue_mutex((struct vmci_queue *)my_produce_q, 12208c2ecf20Sopenharmony_ci (struct vmci_queue *)my_consume_q); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci qp_list_add_entry(&qp_guest_endpoints, &queue_pair_entry->qp); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci out: 12258c2ecf20Sopenharmony_ci queue_pair_entry->qp.ref_count++; 12268c2ecf20Sopenharmony_ci *handle = queue_pair_entry->qp.handle; 12278c2ecf20Sopenharmony_ci *produce_q = (struct vmci_queue *)my_produce_q; 12288c2ecf20Sopenharmony_ci *consume_q = (struct vmci_queue *)my_consume_q; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* 12318c2ecf20Sopenharmony_ci * We should initialize the queue pair header pages on a local 12328c2ecf20Sopenharmony_ci * queue pair create. For non-local queue pairs, the 12338c2ecf20Sopenharmony_ci * hypervisor initializes the header pages in the create step. 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_ci if ((queue_pair_entry->qp.flags & VMCI_QPFLAG_LOCAL) && 12368c2ecf20Sopenharmony_ci queue_pair_entry->qp.ref_count == 1) { 12378c2ecf20Sopenharmony_ci vmci_q_header_init((*produce_q)->q_header, *handle); 12388c2ecf20Sopenharmony_ci vmci_q_header_init((*consume_q)->q_header, *handle); 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci error: 12468c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 12478c2ecf20Sopenharmony_ci if (queue_pair_entry) { 12488c2ecf20Sopenharmony_ci /* The queues will be freed inside the destroy routine. */ 12498c2ecf20Sopenharmony_ci qp_guest_endpoint_destroy(queue_pair_entry); 12508c2ecf20Sopenharmony_ci } else { 12518c2ecf20Sopenharmony_ci qp_free_queue(my_produce_q, produce_size); 12528c2ecf20Sopenharmony_ci qp_free_queue(my_consume_q, consume_size); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci return result; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci error_keep_entry: 12578c2ecf20Sopenharmony_ci /* This path should only be used when an existing entry was found. */ 12588c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 12598c2ecf20Sopenharmony_ci return result; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci/* 12638c2ecf20Sopenharmony_ci * The first endpoint issuing a queue pair allocation will create the state 12648c2ecf20Sopenharmony_ci * of the queue pair in the queue pair broker. 12658c2ecf20Sopenharmony_ci * 12668c2ecf20Sopenharmony_ci * If the creator is a guest, it will associate a VMX virtual address range 12678c2ecf20Sopenharmony_ci * with the queue pair as specified by the page_store. For compatibility with 12688c2ecf20Sopenharmony_ci * older VMX'en, that would use a separate step to set the VMX virtual 12698c2ecf20Sopenharmony_ci * address range, the virtual address range can be registered later using 12708c2ecf20Sopenharmony_ci * vmci_qp_broker_set_page_store. In that case, a page_store of NULL should be 12718c2ecf20Sopenharmony_ci * used. 12728c2ecf20Sopenharmony_ci * 12738c2ecf20Sopenharmony_ci * If the creator is the host, a page_store of NULL should be used as well, 12748c2ecf20Sopenharmony_ci * since the host is not able to supply a page store for the queue pair. 12758c2ecf20Sopenharmony_ci * 12768c2ecf20Sopenharmony_ci * For older VMX and host callers, the queue pair will be created in the 12778c2ecf20Sopenharmony_ci * VMCIQPB_CREATED_NO_MEM state, and for current VMX callers, it will be 12788c2ecf20Sopenharmony_ci * created in VMCOQPB_CREATED_MEM state. 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_cistatic int qp_broker_create(struct vmci_handle handle, 12818c2ecf20Sopenharmony_ci u32 peer, 12828c2ecf20Sopenharmony_ci u32 flags, 12838c2ecf20Sopenharmony_ci u32 priv_flags, 12848c2ecf20Sopenharmony_ci u64 produce_size, 12858c2ecf20Sopenharmony_ci u64 consume_size, 12868c2ecf20Sopenharmony_ci struct vmci_qp_page_store *page_store, 12878c2ecf20Sopenharmony_ci struct vmci_ctx *context, 12888c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb, 12898c2ecf20Sopenharmony_ci void *client_data, struct qp_broker_entry **ent) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci struct qp_broker_entry *entry = NULL; 12928c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 12938c2ecf20Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 12948c2ecf20Sopenharmony_ci int result; 12958c2ecf20Sopenharmony_ci u64 guest_produce_size; 12968c2ecf20Sopenharmony_ci u64 guest_consume_size; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* Do not create if the caller asked not to. */ 12998c2ecf20Sopenharmony_ci if (flags & VMCI_QPFLAG_ATTACH_ONLY) 13008c2ecf20Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci /* 13038c2ecf20Sopenharmony_ci * Creator's context ID should match handle's context ID or the creator 13048c2ecf20Sopenharmony_ci * must allow the context in handle's context ID as the "peer". 13058c2ecf20Sopenharmony_ci */ 13068c2ecf20Sopenharmony_ci if (handle.context != context_id && handle.context != peer) 13078c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (VMCI_CONTEXT_IS_VM(context_id) && VMCI_CONTEXT_IS_VM(peer)) 13108c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci /* 13138c2ecf20Sopenharmony_ci * Creator's context ID for local queue pairs should match the 13148c2ecf20Sopenharmony_ci * peer, if a peer is specified. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_ci if (is_local && peer != VMCI_INVALID_ID && context_id != peer) 13178c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 13208c2ecf20Sopenharmony_ci if (!entry) 13218c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (vmci_ctx_get_id(context) == VMCI_HOST_CONTEXT_ID && !is_local) { 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * The queue pair broker entry stores values from the guest 13268c2ecf20Sopenharmony_ci * point of view, so a creating host side endpoint should swap 13278c2ecf20Sopenharmony_ci * produce and consume values -- unless it is a local queue 13288c2ecf20Sopenharmony_ci * pair, in which case no swapping is necessary, since the local 13298c2ecf20Sopenharmony_ci * attacher will swap queues. 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci guest_produce_size = consume_size; 13338c2ecf20Sopenharmony_ci guest_consume_size = produce_size; 13348c2ecf20Sopenharmony_ci } else { 13358c2ecf20Sopenharmony_ci guest_produce_size = produce_size; 13368c2ecf20Sopenharmony_ci guest_consume_size = consume_size; 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci entry->qp.handle = handle; 13408c2ecf20Sopenharmony_ci entry->qp.peer = peer; 13418c2ecf20Sopenharmony_ci entry->qp.flags = flags; 13428c2ecf20Sopenharmony_ci entry->qp.produce_size = guest_produce_size; 13438c2ecf20Sopenharmony_ci entry->qp.consume_size = guest_consume_size; 13448c2ecf20Sopenharmony_ci entry->qp.ref_count = 1; 13458c2ecf20Sopenharmony_ci entry->create_id = context_id; 13468c2ecf20Sopenharmony_ci entry->attach_id = VMCI_INVALID_ID; 13478c2ecf20Sopenharmony_ci entry->state = VMCIQPB_NEW; 13488c2ecf20Sopenharmony_ci entry->require_trusted_attach = 13498c2ecf20Sopenharmony_ci !!(context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED); 13508c2ecf20Sopenharmony_ci entry->created_by_trusted = 13518c2ecf20Sopenharmony_ci !!(priv_flags & VMCI_PRIVILEGE_FLAG_TRUSTED); 13528c2ecf20Sopenharmony_ci entry->vmci_page_files = false; 13538c2ecf20Sopenharmony_ci entry->wakeup_cb = wakeup_cb; 13548c2ecf20Sopenharmony_ci entry->client_data = client_data; 13558c2ecf20Sopenharmony_ci entry->produce_q = qp_host_alloc_queue(guest_produce_size); 13568c2ecf20Sopenharmony_ci if (entry->produce_q == NULL) { 13578c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 13588c2ecf20Sopenharmony_ci goto error; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci entry->consume_q = qp_host_alloc_queue(guest_consume_size); 13618c2ecf20Sopenharmony_ci if (entry->consume_q == NULL) { 13628c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 13638c2ecf20Sopenharmony_ci goto error; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci qp_init_queue_mutex(entry->produce_q, entry->consume_q); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&entry->qp.list_item); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci if (is_local) { 13718c2ecf20Sopenharmony_ci u8 *tmp; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci entry->local_mem = kcalloc(QPE_NUM_PAGES(entry->qp), 13748c2ecf20Sopenharmony_ci PAGE_SIZE, GFP_KERNEL); 13758c2ecf20Sopenharmony_ci if (entry->local_mem == NULL) { 13768c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_MEM; 13778c2ecf20Sopenharmony_ci goto error; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 13808c2ecf20Sopenharmony_ci entry->produce_q->q_header = entry->local_mem; 13818c2ecf20Sopenharmony_ci tmp = (u8 *)entry->local_mem + PAGE_SIZE * 13828c2ecf20Sopenharmony_ci (DIV_ROUND_UP(entry->qp.produce_size, PAGE_SIZE) + 1); 13838c2ecf20Sopenharmony_ci entry->consume_q->q_header = (struct vmci_queue_header *)tmp; 13848c2ecf20Sopenharmony_ci } else if (page_store) { 13858c2ecf20Sopenharmony_ci /* 13868c2ecf20Sopenharmony_ci * The VMX already initialized the queue pair headers, so no 13878c2ecf20Sopenharmony_ci * need for the kernel side to do that. 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci result = qp_host_register_user_memory(page_store, 13908c2ecf20Sopenharmony_ci entry->produce_q, 13918c2ecf20Sopenharmony_ci entry->consume_q); 13928c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 13938c2ecf20Sopenharmony_ci goto error; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 13968c2ecf20Sopenharmony_ci } else { 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * A create without a page_store may be either a host 13998c2ecf20Sopenharmony_ci * side create (in which case we are waiting for the 14008c2ecf20Sopenharmony_ci * guest side to supply the memory) or an old style 14018c2ecf20Sopenharmony_ci * queue pair create (in which case we will expect a 14028c2ecf20Sopenharmony_ci * set page store call as the next step). 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_ci entry->state = VMCIQPB_CREATED_NO_MEM; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci qp_list_add_entry(&qp_broker_list, &entry->qp); 14088c2ecf20Sopenharmony_ci if (ent != NULL) 14098c2ecf20Sopenharmony_ci *ent = entry; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* Add to resource obj */ 14128c2ecf20Sopenharmony_ci result = vmci_resource_add(&entry->resource, 14138c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_QPAIR_HOST, 14148c2ecf20Sopenharmony_ci handle); 14158c2ecf20Sopenharmony_ci if (result != VMCI_SUCCESS) { 14168c2ecf20Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d", 14178c2ecf20Sopenharmony_ci handle.context, handle.resource, result); 14188c2ecf20Sopenharmony_ci goto error; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci entry->qp.handle = vmci_resource_handle(&entry->resource); 14228c2ecf20Sopenharmony_ci if (is_local) { 14238c2ecf20Sopenharmony_ci vmci_q_header_init(entry->produce_q->q_header, 14248c2ecf20Sopenharmony_ci entry->qp.handle); 14258c2ecf20Sopenharmony_ci vmci_q_header_init(entry->consume_q->q_header, 14268c2ecf20Sopenharmony_ci entry->qp.handle); 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci vmci_ctx_qp_create(context, entry->qp.handle); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci error: 14348c2ecf20Sopenharmony_ci if (entry != NULL) { 14358c2ecf20Sopenharmony_ci qp_host_free_queue(entry->produce_q, guest_produce_size); 14368c2ecf20Sopenharmony_ci qp_host_free_queue(entry->consume_q, guest_consume_size); 14378c2ecf20Sopenharmony_ci kfree(entry); 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci return result; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci/* 14448c2ecf20Sopenharmony_ci * Enqueues an event datagram to notify the peer VM attached to 14458c2ecf20Sopenharmony_ci * the given queue pair handle about attach/detach event by the 14468c2ecf20Sopenharmony_ci * given VM. Returns Payload size of datagram enqueued on 14478c2ecf20Sopenharmony_ci * success, error code otherwise. 14488c2ecf20Sopenharmony_ci */ 14498c2ecf20Sopenharmony_cistatic int qp_notify_peer(bool attach, 14508c2ecf20Sopenharmony_ci struct vmci_handle handle, 14518c2ecf20Sopenharmony_ci u32 my_id, 14528c2ecf20Sopenharmony_ci u32 peer_id) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci int rv; 14558c2ecf20Sopenharmony_ci struct vmci_event_qp ev; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || my_id == VMCI_INVALID_ID || 14588c2ecf20Sopenharmony_ci peer_id == VMCI_INVALID_ID) 14598c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci /* 14628c2ecf20Sopenharmony_ci * In vmci_ctx_enqueue_datagram() we enforce the upper limit on 14638c2ecf20Sopenharmony_ci * number of pending events from the hypervisor to a given VM 14648c2ecf20Sopenharmony_ci * otherwise a rogue VM could do an arbitrary number of attach 14658c2ecf20Sopenharmony_ci * and detach operations causing memory pressure in the host 14668c2ecf20Sopenharmony_ci * kernel. 14678c2ecf20Sopenharmony_ci */ 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 14708c2ecf20Sopenharmony_ci ev.msg.hdr.dst = vmci_make_handle(peer_id, VMCI_EVENT_HANDLER); 14718c2ecf20Sopenharmony_ci ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 14728c2ecf20Sopenharmony_ci VMCI_CONTEXT_RESOURCE_ID); 14738c2ecf20Sopenharmony_ci ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); 14748c2ecf20Sopenharmony_ci ev.msg.event_data.event = attach ? 14758c2ecf20Sopenharmony_ci VMCI_EVENT_QP_PEER_ATTACH : VMCI_EVENT_QP_PEER_DETACH; 14768c2ecf20Sopenharmony_ci ev.payload.handle = handle; 14778c2ecf20Sopenharmony_ci ev.payload.peer_id = my_id; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci rv = vmci_datagram_dispatch(VMCI_HYPERVISOR_CONTEXT_ID, 14808c2ecf20Sopenharmony_ci &ev.msg.hdr, false); 14818c2ecf20Sopenharmony_ci if (rv < VMCI_SUCCESS) 14828c2ecf20Sopenharmony_ci pr_warn("Failed to enqueue queue_pair %s event datagram for context (ID=0x%x)\n", 14838c2ecf20Sopenharmony_ci attach ? "ATTACH" : "DETACH", peer_id); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci return rv; 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci/* 14898c2ecf20Sopenharmony_ci * The second endpoint issuing a queue pair allocation will attach to 14908c2ecf20Sopenharmony_ci * the queue pair registered with the queue pair broker. 14918c2ecf20Sopenharmony_ci * 14928c2ecf20Sopenharmony_ci * If the attacher is a guest, it will associate a VMX virtual address 14938c2ecf20Sopenharmony_ci * range with the queue pair as specified by the page_store. At this 14948c2ecf20Sopenharmony_ci * point, the already attach host endpoint may start using the queue 14958c2ecf20Sopenharmony_ci * pair, and an attach event is sent to it. For compatibility with 14968c2ecf20Sopenharmony_ci * older VMX'en, that used a separate step to set the VMX virtual 14978c2ecf20Sopenharmony_ci * address range, the virtual address range can be registered later 14988c2ecf20Sopenharmony_ci * using vmci_qp_broker_set_page_store. In that case, a page_store of 14998c2ecf20Sopenharmony_ci * NULL should be used, and the attach event will be generated once 15008c2ecf20Sopenharmony_ci * the actual page store has been set. 15018c2ecf20Sopenharmony_ci * 15028c2ecf20Sopenharmony_ci * If the attacher is the host, a page_store of NULL should be used as 15038c2ecf20Sopenharmony_ci * well, since the page store information is already set by the guest. 15048c2ecf20Sopenharmony_ci * 15058c2ecf20Sopenharmony_ci * For new VMX and host callers, the queue pair will be moved to the 15068c2ecf20Sopenharmony_ci * VMCIQPB_ATTACHED_MEM state, and for older VMX callers, it will be 15078c2ecf20Sopenharmony_ci * moved to the VMCOQPB_ATTACHED_NO_MEM state. 15088c2ecf20Sopenharmony_ci */ 15098c2ecf20Sopenharmony_cistatic int qp_broker_attach(struct qp_broker_entry *entry, 15108c2ecf20Sopenharmony_ci u32 peer, 15118c2ecf20Sopenharmony_ci u32 flags, 15128c2ecf20Sopenharmony_ci u32 priv_flags, 15138c2ecf20Sopenharmony_ci u64 produce_size, 15148c2ecf20Sopenharmony_ci u64 consume_size, 15158c2ecf20Sopenharmony_ci struct vmci_qp_page_store *page_store, 15168c2ecf20Sopenharmony_ci struct vmci_ctx *context, 15178c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb, 15188c2ecf20Sopenharmony_ci void *client_data, 15198c2ecf20Sopenharmony_ci struct qp_broker_entry **ent) 15208c2ecf20Sopenharmony_ci{ 15218c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 15228c2ecf20Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 15238c2ecf20Sopenharmony_ci int result; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM && 15268c2ecf20Sopenharmony_ci entry->state != VMCIQPB_CREATED_MEM) 15278c2ecf20Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if (is_local) { 15308c2ecf20Sopenharmony_ci if (!(entry->qp.flags & VMCI_QPFLAG_LOCAL) || 15318c2ecf20Sopenharmony_ci context_id != entry->create_id) { 15328c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci } else if (context_id == entry->create_id || 15358c2ecf20Sopenharmony_ci context_id == entry->attach_id) { 15368c2ecf20Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (VMCI_CONTEXT_IS_VM(context_id) && 15408c2ecf20Sopenharmony_ci VMCI_CONTEXT_IS_VM(entry->create_id)) 15418c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci /* 15448c2ecf20Sopenharmony_ci * If we are attaching from a restricted context then the queuepair 15458c2ecf20Sopenharmony_ci * must have been created by a trusted endpoint. 15468c2ecf20Sopenharmony_ci */ 15478c2ecf20Sopenharmony_ci if ((context->priv_flags & VMCI_PRIVILEGE_FLAG_RESTRICTED) && 15488c2ecf20Sopenharmony_ci !entry->created_by_trusted) 15498c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* 15528c2ecf20Sopenharmony_ci * If we are attaching to a queuepair that was created by a restricted 15538c2ecf20Sopenharmony_ci * context then we must be trusted. 15548c2ecf20Sopenharmony_ci */ 15558c2ecf20Sopenharmony_ci if (entry->require_trusted_attach && 15568c2ecf20Sopenharmony_ci (!(priv_flags & VMCI_PRIVILEGE_FLAG_TRUSTED))) 15578c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* 15608c2ecf20Sopenharmony_ci * If the creator specifies VMCI_INVALID_ID in "peer" field, access 15618c2ecf20Sopenharmony_ci * control check is not performed. 15628c2ecf20Sopenharmony_ci */ 15638c2ecf20Sopenharmony_ci if (entry->qp.peer != VMCI_INVALID_ID && entry->qp.peer != context_id) 15648c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_ACCESS; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (entry->create_id == VMCI_HOST_CONTEXT_ID) { 15678c2ecf20Sopenharmony_ci /* 15688c2ecf20Sopenharmony_ci * Do not attach if the caller doesn't support Host Queue Pairs 15698c2ecf20Sopenharmony_ci * and a host created this queue pair. 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (!vmci_ctx_supports_host_qp(context)) 15738c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_RESOURCE; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci } else if (context_id == VMCI_HOST_CONTEXT_ID) { 15768c2ecf20Sopenharmony_ci struct vmci_ctx *create_context; 15778c2ecf20Sopenharmony_ci bool supports_host_qp; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* 15808c2ecf20Sopenharmony_ci * Do not attach a host to a user created queue pair if that 15818c2ecf20Sopenharmony_ci * user doesn't support host queue pair end points. 15828c2ecf20Sopenharmony_ci */ 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci create_context = vmci_ctx_get(entry->create_id); 15858c2ecf20Sopenharmony_ci supports_host_qp = vmci_ctx_supports_host_qp(create_context); 15868c2ecf20Sopenharmony_ci vmci_ctx_put(create_context); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (!supports_host_qp) 15898c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_RESOURCE; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if ((entry->qp.flags & ~VMCI_QP_ASYMM) != (flags & ~VMCI_QP_ASYMM_PEER)) 15938c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 15968c2ecf20Sopenharmony_ci /* 15978c2ecf20Sopenharmony_ci * The queue pair broker entry stores values from the guest 15988c2ecf20Sopenharmony_ci * point of view, so an attaching guest should match the values 15998c2ecf20Sopenharmony_ci * stored in the entry. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if (entry->qp.produce_size != produce_size || 16038c2ecf20Sopenharmony_ci entry->qp.consume_size != consume_size) { 16048c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci } else if (entry->qp.produce_size != consume_size || 16078c2ecf20Sopenharmony_ci entry->qp.consume_size != produce_size) { 16088c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_MISMATCH; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 16128c2ecf20Sopenharmony_ci /* 16138c2ecf20Sopenharmony_ci * If a guest attached to a queue pair, it will supply 16148c2ecf20Sopenharmony_ci * the backing memory. If this is a pre NOVMVM vmx, 16158c2ecf20Sopenharmony_ci * the backing memory will be supplied by calling 16168c2ecf20Sopenharmony_ci * vmci_qp_broker_set_page_store() following the 16178c2ecf20Sopenharmony_ci * return of the vmci_qp_broker_alloc() call. If it is 16188c2ecf20Sopenharmony_ci * a vmx of version NOVMVM or later, the page store 16198c2ecf20Sopenharmony_ci * must be supplied as part of the 16208c2ecf20Sopenharmony_ci * vmci_qp_broker_alloc call. Under all circumstances 16218c2ecf20Sopenharmony_ci * must the initially created queue pair not have any 16228c2ecf20Sopenharmony_ci * memory associated with it already. 16238c2ecf20Sopenharmony_ci */ 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM) 16268c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci if (page_store != NULL) { 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * Patch up host state to point to guest 16318c2ecf20Sopenharmony_ci * supplied memory. The VMX already 16328c2ecf20Sopenharmony_ci * initialized the queue pair headers, so no 16338c2ecf20Sopenharmony_ci * need for the kernel side to do that. 16348c2ecf20Sopenharmony_ci */ 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci result = qp_host_register_user_memory(page_store, 16378c2ecf20Sopenharmony_ci entry->produce_q, 16388c2ecf20Sopenharmony_ci entry->consume_q); 16398c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 16408c2ecf20Sopenharmony_ci return result; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 16438c2ecf20Sopenharmony_ci } else { 16448c2ecf20Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_NO_MEM; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci } else if (entry->state == VMCIQPB_CREATED_NO_MEM) { 16478c2ecf20Sopenharmony_ci /* 16488c2ecf20Sopenharmony_ci * The host side is attempting to attach to a queue 16498c2ecf20Sopenharmony_ci * pair that doesn't have any memory associated with 16508c2ecf20Sopenharmony_ci * it. This must be a pre NOVMVM vmx that hasn't set 16518c2ecf20Sopenharmony_ci * the page store information yet, or a quiesced VM. 16528c2ecf20Sopenharmony_ci */ 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci return VMCI_ERROR_UNAVAILABLE; 16558c2ecf20Sopenharmony_ci } else { 16568c2ecf20Sopenharmony_ci /* The host side has successfully attached to a queue pair. */ 16578c2ecf20Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci if (entry->state == VMCIQPB_ATTACHED_MEM) { 16618c2ecf20Sopenharmony_ci result = 16628c2ecf20Sopenharmony_ci qp_notify_peer(true, entry->qp.handle, context_id, 16638c2ecf20Sopenharmony_ci entry->create_id); 16648c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 16658c2ecf20Sopenharmony_ci pr_warn("Failed to notify peer (ID=0x%x) of attach to queue pair (handle=0x%x:0x%x)\n", 16668c2ecf20Sopenharmony_ci entry->create_id, entry->qp.handle.context, 16678c2ecf20Sopenharmony_ci entry->qp.handle.resource); 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci entry->attach_id = context_id; 16718c2ecf20Sopenharmony_ci entry->qp.ref_count++; 16728c2ecf20Sopenharmony_ci if (wakeup_cb) { 16738c2ecf20Sopenharmony_ci entry->wakeup_cb = wakeup_cb; 16748c2ecf20Sopenharmony_ci entry->client_data = client_data; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* 16788c2ecf20Sopenharmony_ci * When attaching to local queue pairs, the context already has 16798c2ecf20Sopenharmony_ci * an entry tracking the queue pair, so don't add another one. 16808c2ecf20Sopenharmony_ci */ 16818c2ecf20Sopenharmony_ci if (!is_local) 16828c2ecf20Sopenharmony_ci vmci_ctx_qp_create(context, entry->qp.handle); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci if (ent != NULL) 16858c2ecf20Sopenharmony_ci *ent = entry; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 16888c2ecf20Sopenharmony_ci} 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci/* 16918c2ecf20Sopenharmony_ci * queue_pair_Alloc for use when setting up queue pair endpoints 16928c2ecf20Sopenharmony_ci * on the host. 16938c2ecf20Sopenharmony_ci */ 16948c2ecf20Sopenharmony_cistatic int qp_broker_alloc(struct vmci_handle handle, 16958c2ecf20Sopenharmony_ci u32 peer, 16968c2ecf20Sopenharmony_ci u32 flags, 16978c2ecf20Sopenharmony_ci u32 priv_flags, 16988c2ecf20Sopenharmony_ci u64 produce_size, 16998c2ecf20Sopenharmony_ci u64 consume_size, 17008c2ecf20Sopenharmony_ci struct vmci_qp_page_store *page_store, 17018c2ecf20Sopenharmony_ci struct vmci_ctx *context, 17028c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb, 17038c2ecf20Sopenharmony_ci void *client_data, 17048c2ecf20Sopenharmony_ci struct qp_broker_entry **ent, 17058c2ecf20Sopenharmony_ci bool *swap) 17068c2ecf20Sopenharmony_ci{ 17078c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 17088c2ecf20Sopenharmony_ci bool create; 17098c2ecf20Sopenharmony_ci struct qp_broker_entry *entry = NULL; 17108c2ecf20Sopenharmony_ci bool is_local = flags & VMCI_QPFLAG_LOCAL; 17118c2ecf20Sopenharmony_ci int result; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || 17148c2ecf20Sopenharmony_ci (flags & ~VMCI_QP_ALL_FLAGS) || is_local || 17158c2ecf20Sopenharmony_ci !(produce_size || consume_size) || 17168c2ecf20Sopenharmony_ci !context || context_id == VMCI_INVALID_ID || 17178c2ecf20Sopenharmony_ci handle.context == VMCI_INVALID_ID) { 17188c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if (page_store && !VMCI_QP_PAGESTORE_IS_WELLFORMED(page_store)) 17228c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci /* 17258c2ecf20Sopenharmony_ci * In the initial argument check, we ensure that non-vmkernel hosts 17268c2ecf20Sopenharmony_ci * are not allowed to create local queue pairs. 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (!is_local && vmci_ctx_qp_exists(context, handle)) { 17328c2ecf20Sopenharmony_ci pr_devel("Context (ID=0x%x) already attached to queue pair (handle=0x%x:0x%x)\n", 17338c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 17348c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 17358c2ecf20Sopenharmony_ci return VMCI_ERROR_ALREADY_EXISTS; 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci if (handle.resource != VMCI_INVALID_ID) 17398c2ecf20Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (!entry) { 17428c2ecf20Sopenharmony_ci create = true; 17438c2ecf20Sopenharmony_ci result = 17448c2ecf20Sopenharmony_ci qp_broker_create(handle, peer, flags, priv_flags, 17458c2ecf20Sopenharmony_ci produce_size, consume_size, page_store, 17468c2ecf20Sopenharmony_ci context, wakeup_cb, client_data, ent); 17478c2ecf20Sopenharmony_ci } else { 17488c2ecf20Sopenharmony_ci create = false; 17498c2ecf20Sopenharmony_ci result = 17508c2ecf20Sopenharmony_ci qp_broker_attach(entry, peer, flags, priv_flags, 17518c2ecf20Sopenharmony_ci produce_size, consume_size, page_store, 17528c2ecf20Sopenharmony_ci context, wakeup_cb, client_data, ent); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (swap) 17588c2ecf20Sopenharmony_ci *swap = (context_id == VMCI_HOST_CONTEXT_ID) && 17598c2ecf20Sopenharmony_ci !(create && is_local); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci return result; 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci/* 17658c2ecf20Sopenharmony_ci * This function implements the kernel API for allocating a queue 17668c2ecf20Sopenharmony_ci * pair. 17678c2ecf20Sopenharmony_ci */ 17688c2ecf20Sopenharmony_cistatic int qp_alloc_host_work(struct vmci_handle *handle, 17698c2ecf20Sopenharmony_ci struct vmci_queue **produce_q, 17708c2ecf20Sopenharmony_ci u64 produce_size, 17718c2ecf20Sopenharmony_ci struct vmci_queue **consume_q, 17728c2ecf20Sopenharmony_ci u64 consume_size, 17738c2ecf20Sopenharmony_ci u32 peer, 17748c2ecf20Sopenharmony_ci u32 flags, 17758c2ecf20Sopenharmony_ci u32 priv_flags, 17768c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb, 17778c2ecf20Sopenharmony_ci void *client_data) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci struct vmci_handle new_handle; 17808c2ecf20Sopenharmony_ci struct vmci_ctx *context; 17818c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 17828c2ecf20Sopenharmony_ci int result; 17838c2ecf20Sopenharmony_ci bool swap; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(*handle)) { 17868c2ecf20Sopenharmony_ci new_handle = vmci_make_handle( 17878c2ecf20Sopenharmony_ci VMCI_HOST_CONTEXT_ID, VMCI_INVALID_ID); 17888c2ecf20Sopenharmony_ci } else 17898c2ecf20Sopenharmony_ci new_handle = *handle; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci context = vmci_ctx_get(VMCI_HOST_CONTEXT_ID); 17928c2ecf20Sopenharmony_ci entry = NULL; 17938c2ecf20Sopenharmony_ci result = 17948c2ecf20Sopenharmony_ci qp_broker_alloc(new_handle, peer, flags, priv_flags, 17958c2ecf20Sopenharmony_ci produce_size, consume_size, NULL, context, 17968c2ecf20Sopenharmony_ci wakeup_cb, client_data, &entry, &swap); 17978c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) { 17988c2ecf20Sopenharmony_ci if (swap) { 17998c2ecf20Sopenharmony_ci /* 18008c2ecf20Sopenharmony_ci * If this is a local queue pair, the attacher 18018c2ecf20Sopenharmony_ci * will swap around produce and consume 18028c2ecf20Sopenharmony_ci * queues. 18038c2ecf20Sopenharmony_ci */ 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci *produce_q = entry->consume_q; 18068c2ecf20Sopenharmony_ci *consume_q = entry->produce_q; 18078c2ecf20Sopenharmony_ci } else { 18088c2ecf20Sopenharmony_ci *produce_q = entry->produce_q; 18098c2ecf20Sopenharmony_ci *consume_q = entry->consume_q; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci *handle = vmci_resource_handle(&entry->resource); 18138c2ecf20Sopenharmony_ci } else { 18148c2ecf20Sopenharmony_ci *handle = VMCI_INVALID_HANDLE; 18158c2ecf20Sopenharmony_ci pr_devel("queue pair broker failed to alloc (result=%d)\n", 18168c2ecf20Sopenharmony_ci result); 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci vmci_ctx_put(context); 18198c2ecf20Sopenharmony_ci return result; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci/* 18238c2ecf20Sopenharmony_ci * Allocates a VMCI queue_pair. Only checks validity of input 18248c2ecf20Sopenharmony_ci * arguments. The real work is done in the host or guest 18258c2ecf20Sopenharmony_ci * specific function. 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ciint vmci_qp_alloc(struct vmci_handle *handle, 18288c2ecf20Sopenharmony_ci struct vmci_queue **produce_q, 18298c2ecf20Sopenharmony_ci u64 produce_size, 18308c2ecf20Sopenharmony_ci struct vmci_queue **consume_q, 18318c2ecf20Sopenharmony_ci u64 consume_size, 18328c2ecf20Sopenharmony_ci u32 peer, 18338c2ecf20Sopenharmony_ci u32 flags, 18348c2ecf20Sopenharmony_ci u32 priv_flags, 18358c2ecf20Sopenharmony_ci bool guest_endpoint, 18368c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb, 18378c2ecf20Sopenharmony_ci void *client_data) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci if (!handle || !produce_q || !consume_q || 18408c2ecf20Sopenharmony_ci (!produce_size && !consume_size) || (flags & ~VMCI_QP_ALL_FLAGS)) 18418c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (guest_endpoint) { 18448c2ecf20Sopenharmony_ci return qp_alloc_guest_work(handle, produce_q, 18458c2ecf20Sopenharmony_ci produce_size, consume_q, 18468c2ecf20Sopenharmony_ci consume_size, peer, 18478c2ecf20Sopenharmony_ci flags, priv_flags); 18488c2ecf20Sopenharmony_ci } else { 18498c2ecf20Sopenharmony_ci return qp_alloc_host_work(handle, produce_q, 18508c2ecf20Sopenharmony_ci produce_size, consume_q, 18518c2ecf20Sopenharmony_ci consume_size, peer, flags, 18528c2ecf20Sopenharmony_ci priv_flags, wakeup_cb, client_data); 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci/* 18578c2ecf20Sopenharmony_ci * This function implements the host kernel API for detaching from 18588c2ecf20Sopenharmony_ci * a queue pair. 18598c2ecf20Sopenharmony_ci */ 18608c2ecf20Sopenharmony_cistatic int qp_detatch_host_work(struct vmci_handle handle) 18618c2ecf20Sopenharmony_ci{ 18628c2ecf20Sopenharmony_ci int result; 18638c2ecf20Sopenharmony_ci struct vmci_ctx *context; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci context = vmci_ctx_get(VMCI_HOST_CONTEXT_ID); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci result = vmci_qp_broker_detach(handle, context); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci vmci_ctx_put(context); 18708c2ecf20Sopenharmony_ci return result; 18718c2ecf20Sopenharmony_ci} 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci/* 18748c2ecf20Sopenharmony_ci * Detaches from a VMCI queue_pair. Only checks validity of input argument. 18758c2ecf20Sopenharmony_ci * Real work is done in the host or guest specific function. 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_cistatic int qp_detatch(struct vmci_handle handle, bool guest_endpoint) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 18808c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (guest_endpoint) 18838c2ecf20Sopenharmony_ci return qp_detatch_guest_work(handle); 18848c2ecf20Sopenharmony_ci else 18858c2ecf20Sopenharmony_ci return qp_detatch_host_work(handle); 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci/* 18898c2ecf20Sopenharmony_ci * Returns the entry from the head of the list. Assumes that the list is 18908c2ecf20Sopenharmony_ci * locked. 18918c2ecf20Sopenharmony_ci */ 18928c2ecf20Sopenharmony_cistatic struct qp_entry *qp_list_get_head(struct qp_list *qp_list) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci if (!list_empty(&qp_list->head)) { 18958c2ecf20Sopenharmony_ci struct qp_entry *entry = 18968c2ecf20Sopenharmony_ci list_first_entry(&qp_list->head, struct qp_entry, 18978c2ecf20Sopenharmony_ci list_item); 18988c2ecf20Sopenharmony_ci return entry; 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci return NULL; 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_civoid vmci_qp_broker_exit(void) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci struct qp_entry *entry; 19078c2ecf20Sopenharmony_ci struct qp_broker_entry *be; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci while ((entry = qp_list_get_head(&qp_broker_list))) { 19128c2ecf20Sopenharmony_ci be = (struct qp_broker_entry *)entry; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci qp_list_remove_entry(&qp_broker_list, entry); 19158c2ecf20Sopenharmony_ci kfree(be); 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 19198c2ecf20Sopenharmony_ci} 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci/* 19228c2ecf20Sopenharmony_ci * Requests that a queue pair be allocated with the VMCI queue 19238c2ecf20Sopenharmony_ci * pair broker. Allocates a queue pair entry if one does not 19248c2ecf20Sopenharmony_ci * exist. Attaches to one if it exists, and retrieves the page 19258c2ecf20Sopenharmony_ci * files backing that queue_pair. Assumes that the queue pair 19268c2ecf20Sopenharmony_ci * broker lock is held. 19278c2ecf20Sopenharmony_ci */ 19288c2ecf20Sopenharmony_ciint vmci_qp_broker_alloc(struct vmci_handle handle, 19298c2ecf20Sopenharmony_ci u32 peer, 19308c2ecf20Sopenharmony_ci u32 flags, 19318c2ecf20Sopenharmony_ci u32 priv_flags, 19328c2ecf20Sopenharmony_ci u64 produce_size, 19338c2ecf20Sopenharmony_ci u64 consume_size, 19348c2ecf20Sopenharmony_ci struct vmci_qp_page_store *page_store, 19358c2ecf20Sopenharmony_ci struct vmci_ctx *context) 19368c2ecf20Sopenharmony_ci{ 19378c2ecf20Sopenharmony_ci return qp_broker_alloc(handle, peer, flags, priv_flags, 19388c2ecf20Sopenharmony_ci produce_size, consume_size, 19398c2ecf20Sopenharmony_ci page_store, context, NULL, NULL, NULL, NULL); 19408c2ecf20Sopenharmony_ci} 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci/* 19438c2ecf20Sopenharmony_ci * VMX'en with versions lower than VMCI_VERSION_NOVMVM use a separate 19448c2ecf20Sopenharmony_ci * step to add the UVAs of the VMX mapping of the queue pair. This function 19458c2ecf20Sopenharmony_ci * provides backwards compatibility with such VMX'en, and takes care of 19468c2ecf20Sopenharmony_ci * registering the page store for a queue pair previously allocated by the 19478c2ecf20Sopenharmony_ci * VMX during create or attach. This function will move the queue pair state 19488c2ecf20Sopenharmony_ci * to either from VMCIQBP_CREATED_NO_MEM to VMCIQBP_CREATED_MEM or 19498c2ecf20Sopenharmony_ci * VMCIQBP_ATTACHED_NO_MEM to VMCIQBP_ATTACHED_MEM. If moving to the 19508c2ecf20Sopenharmony_ci * attached state with memory, the queue pair is ready to be used by the 19518c2ecf20Sopenharmony_ci * host peer, and an attached event will be generated. 19528c2ecf20Sopenharmony_ci * 19538c2ecf20Sopenharmony_ci * Assumes that the queue pair broker lock is held. 19548c2ecf20Sopenharmony_ci * 19558c2ecf20Sopenharmony_ci * This function is only used by the hosted platform, since there is no 19568c2ecf20Sopenharmony_ci * issue with backwards compatibility for vmkernel. 19578c2ecf20Sopenharmony_ci */ 19588c2ecf20Sopenharmony_ciint vmci_qp_broker_set_page_store(struct vmci_handle handle, 19598c2ecf20Sopenharmony_ci u64 produce_uva, 19608c2ecf20Sopenharmony_ci u64 consume_uva, 19618c2ecf20Sopenharmony_ci struct vmci_ctx *context) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 19648c2ecf20Sopenharmony_ci int result; 19658c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 19688c2ecf20Sopenharmony_ci context_id == VMCI_INVALID_ID) 19698c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* 19728c2ecf20Sopenharmony_ci * We only support guest to host queue pairs, so the VMX must 19738c2ecf20Sopenharmony_ci * supply UVAs for the mapped page files. 19748c2ecf20Sopenharmony_ci */ 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci if (produce_uva == 0 || consume_uva == 0) 19778c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 19828c2ecf20Sopenharmony_ci pr_warn("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 19838c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 19848c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 19858c2ecf20Sopenharmony_ci goto out; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 19898c2ecf20Sopenharmony_ci if (!entry) { 19908c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 19918c2ecf20Sopenharmony_ci goto out; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci /* 19958c2ecf20Sopenharmony_ci * If I'm the owner then I can set the page store. 19968c2ecf20Sopenharmony_ci * 19978c2ecf20Sopenharmony_ci * Or, if a host created the queue_pair and I'm the attached peer 19988c2ecf20Sopenharmony_ci * then I can set the page store. 19998c2ecf20Sopenharmony_ci */ 20008c2ecf20Sopenharmony_ci if (entry->create_id != context_id && 20018c2ecf20Sopenharmony_ci (entry->create_id != VMCI_HOST_CONTEXT_ID || 20028c2ecf20Sopenharmony_ci entry->attach_id != context_id)) { 20038c2ecf20Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTOWNER; 20048c2ecf20Sopenharmony_ci goto out; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (entry->state != VMCIQPB_CREATED_NO_MEM && 20088c2ecf20Sopenharmony_ci entry->state != VMCIQPB_ATTACHED_NO_MEM) { 20098c2ecf20Sopenharmony_ci result = VMCI_ERROR_UNAVAILABLE; 20108c2ecf20Sopenharmony_ci goto out; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci result = qp_host_get_user_memory(produce_uva, consume_uva, 20148c2ecf20Sopenharmony_ci entry->produce_q, entry->consume_q); 20158c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 20168c2ecf20Sopenharmony_ci goto out; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci result = qp_host_map_queues(entry->produce_q, entry->consume_q); 20198c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) { 20208c2ecf20Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 20218c2ecf20Sopenharmony_ci entry->consume_q); 20228c2ecf20Sopenharmony_ci goto out; 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci if (entry->state == VMCIQPB_CREATED_NO_MEM) 20268c2ecf20Sopenharmony_ci entry->state = VMCIQPB_CREATED_MEM; 20278c2ecf20Sopenharmony_ci else 20288c2ecf20Sopenharmony_ci entry->state = VMCIQPB_ATTACHED_MEM; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci entry->vmci_page_files = true; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (entry->state == VMCIQPB_ATTACHED_MEM) { 20338c2ecf20Sopenharmony_ci result = 20348c2ecf20Sopenharmony_ci qp_notify_peer(true, handle, context_id, entry->create_id); 20358c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) { 20368c2ecf20Sopenharmony_ci pr_warn("Failed to notify peer (ID=0x%x) of attach to queue pair (handle=0x%x:0x%x)\n", 20378c2ecf20Sopenharmony_ci entry->create_id, entry->qp.handle.context, 20388c2ecf20Sopenharmony_ci entry->qp.handle.resource); 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 20438c2ecf20Sopenharmony_ci out: 20448c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 20458c2ecf20Sopenharmony_ci return result; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci/* 20498c2ecf20Sopenharmony_ci * Resets saved queue headers for the given QP broker 20508c2ecf20Sopenharmony_ci * entry. Should be used when guest memory becomes available 20518c2ecf20Sopenharmony_ci * again, or the guest detaches. 20528c2ecf20Sopenharmony_ci */ 20538c2ecf20Sopenharmony_cistatic void qp_reset_saved_headers(struct qp_broker_entry *entry) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci entry->produce_q->saved_header = NULL; 20568c2ecf20Sopenharmony_ci entry->consume_q->saved_header = NULL; 20578c2ecf20Sopenharmony_ci} 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci/* 20608c2ecf20Sopenharmony_ci * The main entry point for detaching from a queue pair registered with the 20618c2ecf20Sopenharmony_ci * queue pair broker. If more than one endpoint is attached to the queue 20628c2ecf20Sopenharmony_ci * pair, the first endpoint will mainly decrement a reference count and 20638c2ecf20Sopenharmony_ci * generate a notification to its peer. The last endpoint will clean up 20648c2ecf20Sopenharmony_ci * the queue pair state registered with the broker. 20658c2ecf20Sopenharmony_ci * 20668c2ecf20Sopenharmony_ci * When a guest endpoint detaches, it will unmap and unregister the guest 20678c2ecf20Sopenharmony_ci * memory backing the queue pair. If the host is still attached, it will 20688c2ecf20Sopenharmony_ci * no longer be able to access the queue pair content. 20698c2ecf20Sopenharmony_ci * 20708c2ecf20Sopenharmony_ci * If the queue pair is already in a state where there is no memory 20718c2ecf20Sopenharmony_ci * registered for the queue pair (any *_NO_MEM state), it will transition to 20728c2ecf20Sopenharmony_ci * the VMCIQPB_SHUTDOWN_NO_MEM state. This will also happen, if a guest 20738c2ecf20Sopenharmony_ci * endpoint is the first of two endpoints to detach. If the host endpoint is 20748c2ecf20Sopenharmony_ci * the first out of two to detach, the queue pair will move to the 20758c2ecf20Sopenharmony_ci * VMCIQPB_SHUTDOWN_MEM state. 20768c2ecf20Sopenharmony_ci */ 20778c2ecf20Sopenharmony_ciint vmci_qp_broker_detach(struct vmci_handle handle, struct vmci_ctx *context) 20788c2ecf20Sopenharmony_ci{ 20798c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 20808c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 20818c2ecf20Sopenharmony_ci u32 peer_id; 20828c2ecf20Sopenharmony_ci bool is_local = false; 20838c2ecf20Sopenharmony_ci int result; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 20868c2ecf20Sopenharmony_ci context_id == VMCI_INVALID_ID) { 20878c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 20888c2ecf20Sopenharmony_ci } 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 20938c2ecf20Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 20948c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 20958c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 20968c2ecf20Sopenharmony_ci goto out; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 21008c2ecf20Sopenharmony_ci if (!entry) { 21018c2ecf20Sopenharmony_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", 21028c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 21038c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 21048c2ecf20Sopenharmony_ci goto out; 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 21088c2ecf20Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 21098c2ecf20Sopenharmony_ci goto out; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci if (context_id == entry->create_id) { 21138c2ecf20Sopenharmony_ci peer_id = entry->attach_id; 21148c2ecf20Sopenharmony_ci entry->create_id = VMCI_INVALID_ID; 21158c2ecf20Sopenharmony_ci } else { 21168c2ecf20Sopenharmony_ci peer_id = entry->create_id; 21178c2ecf20Sopenharmony_ci entry->attach_id = VMCI_INVALID_ID; 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci entry->qp.ref_count--; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci is_local = entry->qp.flags & VMCI_QPFLAG_LOCAL; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID) { 21248c2ecf20Sopenharmony_ci bool headers_mapped; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci /* 21278c2ecf20Sopenharmony_ci * Pre NOVMVM vmx'en may detach from a queue pair 21288c2ecf20Sopenharmony_ci * before setting the page store, and in that case 21298c2ecf20Sopenharmony_ci * there is no user memory to detach from. Also, more 21308c2ecf20Sopenharmony_ci * recent VMX'en may detach from a queue pair in the 21318c2ecf20Sopenharmony_ci * quiesced state. 21328c2ecf20Sopenharmony_ci */ 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 21358c2ecf20Sopenharmony_ci headers_mapped = entry->produce_q->q_header || 21368c2ecf20Sopenharmony_ci entry->consume_q->q_header; 21378c2ecf20Sopenharmony_ci if (QPBROKERSTATE_HAS_MEM(entry)) { 21388c2ecf20Sopenharmony_ci result = 21398c2ecf20Sopenharmony_ci qp_host_unmap_queues(INVALID_VMCI_GUEST_MEM_ID, 21408c2ecf20Sopenharmony_ci entry->produce_q, 21418c2ecf20Sopenharmony_ci entry->consume_q); 21428c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 21438c2ecf20Sopenharmony_ci pr_warn("Failed to unmap queue headers for queue pair (handle=0x%x:0x%x,result=%d)\n", 21448c2ecf20Sopenharmony_ci handle.context, handle.resource, 21458c2ecf20Sopenharmony_ci result); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 21488c2ecf20Sopenharmony_ci entry->consume_q); 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci if (!headers_mapped) 21538c2ecf20Sopenharmony_ci qp_reset_saved_headers(entry); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (!headers_mapped && entry->wakeup_cb) 21588c2ecf20Sopenharmony_ci entry->wakeup_cb(entry->client_data); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci } else { 21618c2ecf20Sopenharmony_ci if (entry->wakeup_cb) { 21628c2ecf20Sopenharmony_ci entry->wakeup_cb = NULL; 21638c2ecf20Sopenharmony_ci entry->client_data = NULL; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci if (entry->qp.ref_count == 0) { 21688c2ecf20Sopenharmony_ci qp_list_remove_entry(&qp_broker_list, &entry->qp); 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci if (is_local) 21718c2ecf20Sopenharmony_ci kfree(entry->local_mem); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci qp_cleanup_queue_mutex(entry->produce_q, entry->consume_q); 21748c2ecf20Sopenharmony_ci qp_host_free_queue(entry->produce_q, entry->qp.produce_size); 21758c2ecf20Sopenharmony_ci qp_host_free_queue(entry->consume_q, entry->qp.consume_size); 21768c2ecf20Sopenharmony_ci /* Unlink from resource hash table and free callback */ 21778c2ecf20Sopenharmony_ci vmci_resource_remove(&entry->resource); 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci kfree(entry); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci vmci_ctx_qp_destroy(context, handle); 21828c2ecf20Sopenharmony_ci } else { 21838c2ecf20Sopenharmony_ci qp_notify_peer(false, handle, context_id, peer_id); 21848c2ecf20Sopenharmony_ci if (context_id == VMCI_HOST_CONTEXT_ID && 21858c2ecf20Sopenharmony_ci QPBROKERSTATE_HAS_MEM(entry)) { 21868c2ecf20Sopenharmony_ci entry->state = VMCIQPB_SHUTDOWN_MEM; 21878c2ecf20Sopenharmony_ci } else { 21888c2ecf20Sopenharmony_ci entry->state = VMCIQPB_SHUTDOWN_NO_MEM; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (!is_local) 21928c2ecf20Sopenharmony_ci vmci_ctx_qp_destroy(context, handle); 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 21968c2ecf20Sopenharmony_ci out: 21978c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 21988c2ecf20Sopenharmony_ci return result; 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci/* 22028c2ecf20Sopenharmony_ci * Establishes the necessary mappings for a queue pair given a 22038c2ecf20Sopenharmony_ci * reference to the queue pair guest memory. This is usually 22048c2ecf20Sopenharmony_ci * called when a guest is unquiesced and the VMX is allowed to 22058c2ecf20Sopenharmony_ci * map guest memory once again. 22068c2ecf20Sopenharmony_ci */ 22078c2ecf20Sopenharmony_ciint vmci_qp_broker_map(struct vmci_handle handle, 22088c2ecf20Sopenharmony_ci struct vmci_ctx *context, 22098c2ecf20Sopenharmony_ci u64 guest_mem) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 22128c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 22138c2ecf20Sopenharmony_ci int result; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 22168c2ecf20Sopenharmony_ci context_id == VMCI_INVALID_ID) 22178c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 22228c2ecf20Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 22238c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 22248c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 22258c2ecf20Sopenharmony_ci goto out; 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 22298c2ecf20Sopenharmony_ci if (!entry) { 22308c2ecf20Sopenharmony_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", 22318c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 22328c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 22338c2ecf20Sopenharmony_ci goto out; 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 22378c2ecf20Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 22388c2ecf20Sopenharmony_ci goto out; 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID && 22448c2ecf20Sopenharmony_ci !QPBROKERSTATE_HAS_MEM(entry)) { 22458c2ecf20Sopenharmony_ci struct vmci_qp_page_store page_store; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci page_store.pages = guest_mem; 22488c2ecf20Sopenharmony_ci page_store.len = QPE_NUM_PAGES(entry->qp); 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 22518c2ecf20Sopenharmony_ci qp_reset_saved_headers(entry); 22528c2ecf20Sopenharmony_ci result = 22538c2ecf20Sopenharmony_ci qp_host_register_user_memory(&page_store, 22548c2ecf20Sopenharmony_ci entry->produce_q, 22558c2ecf20Sopenharmony_ci entry->consume_q); 22568c2ecf20Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 22578c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) { 22588c2ecf20Sopenharmony_ci /* Move state from *_NO_MEM to *_MEM */ 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci entry->state++; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci if (entry->wakeup_cb) 22638c2ecf20Sopenharmony_ci entry->wakeup_cb(entry->client_data); 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci out: 22688c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 22698c2ecf20Sopenharmony_ci return result; 22708c2ecf20Sopenharmony_ci} 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci/* 22738c2ecf20Sopenharmony_ci * Saves a snapshot of the queue headers for the given QP broker 22748c2ecf20Sopenharmony_ci * entry. Should be used when guest memory is unmapped. 22758c2ecf20Sopenharmony_ci * Results: 22768c2ecf20Sopenharmony_ci * VMCI_SUCCESS on success, appropriate error code if guest memory 22778c2ecf20Sopenharmony_ci * can't be accessed.. 22788c2ecf20Sopenharmony_ci */ 22798c2ecf20Sopenharmony_cistatic int qp_save_headers(struct qp_broker_entry *entry) 22808c2ecf20Sopenharmony_ci{ 22818c2ecf20Sopenharmony_ci int result; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci if (entry->produce_q->saved_header != NULL && 22848c2ecf20Sopenharmony_ci entry->consume_q->saved_header != NULL) { 22858c2ecf20Sopenharmony_ci /* 22868c2ecf20Sopenharmony_ci * If the headers have already been saved, we don't need to do 22878c2ecf20Sopenharmony_ci * it again, and we don't want to map in the headers 22888c2ecf20Sopenharmony_ci * unnecessarily. 22898c2ecf20Sopenharmony_ci */ 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 22928c2ecf20Sopenharmony_ci } 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci if (NULL == entry->produce_q->q_header || 22958c2ecf20Sopenharmony_ci NULL == entry->consume_q->q_header) { 22968c2ecf20Sopenharmony_ci result = qp_host_map_queues(entry->produce_q, entry->consume_q); 22978c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 22988c2ecf20Sopenharmony_ci return result; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci memcpy(&entry->saved_produce_q, entry->produce_q->q_header, 23028c2ecf20Sopenharmony_ci sizeof(entry->saved_produce_q)); 23038c2ecf20Sopenharmony_ci entry->produce_q->saved_header = &entry->saved_produce_q; 23048c2ecf20Sopenharmony_ci memcpy(&entry->saved_consume_q, entry->consume_q->q_header, 23058c2ecf20Sopenharmony_ci sizeof(entry->saved_consume_q)); 23068c2ecf20Sopenharmony_ci entry->consume_q->saved_header = &entry->saved_consume_q; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 23098c2ecf20Sopenharmony_ci} 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci/* 23128c2ecf20Sopenharmony_ci * Removes all references to the guest memory of a given queue pair, and 23138c2ecf20Sopenharmony_ci * will move the queue pair from state *_MEM to *_NO_MEM. It is usually 23148c2ecf20Sopenharmony_ci * called when a VM is being quiesced where access to guest memory should 23158c2ecf20Sopenharmony_ci * avoided. 23168c2ecf20Sopenharmony_ci */ 23178c2ecf20Sopenharmony_ciint vmci_qp_broker_unmap(struct vmci_handle handle, 23188c2ecf20Sopenharmony_ci struct vmci_ctx *context, 23198c2ecf20Sopenharmony_ci u32 gid) 23208c2ecf20Sopenharmony_ci{ 23218c2ecf20Sopenharmony_ci struct qp_broker_entry *entry; 23228c2ecf20Sopenharmony_ci const u32 context_id = vmci_ctx_get_id(context); 23238c2ecf20Sopenharmony_ci int result; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle) || !context || 23268c2ecf20Sopenharmony_ci context_id == VMCI_INVALID_ID) 23278c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci mutex_lock(&qp_broker_list.mutex); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (!vmci_ctx_qp_exists(context, handle)) { 23328c2ecf20Sopenharmony_ci pr_devel("Context (ID=0x%x) not attached to queue pair (handle=0x%x:0x%x)\n", 23338c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 23348c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 23358c2ecf20Sopenharmony_ci goto out; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci entry = qp_broker_handle_to_entry(handle); 23398c2ecf20Sopenharmony_ci if (!entry) { 23408c2ecf20Sopenharmony_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", 23418c2ecf20Sopenharmony_ci context_id, handle.context, handle.resource); 23428c2ecf20Sopenharmony_ci result = VMCI_ERROR_NOT_FOUND; 23438c2ecf20Sopenharmony_ci goto out; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (context_id != entry->create_id && context_id != entry->attach_id) { 23478c2ecf20Sopenharmony_ci result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 23488c2ecf20Sopenharmony_ci goto out; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci if (context_id != VMCI_HOST_CONTEXT_ID && 23528c2ecf20Sopenharmony_ci QPBROKERSTATE_HAS_MEM(entry)) { 23538c2ecf20Sopenharmony_ci qp_acquire_queue_mutex(entry->produce_q); 23548c2ecf20Sopenharmony_ci result = qp_save_headers(entry); 23558c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 23568c2ecf20Sopenharmony_ci pr_warn("Failed to save queue headers for queue pair (handle=0x%x:0x%x,result=%d)\n", 23578c2ecf20Sopenharmony_ci handle.context, handle.resource, result); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci qp_host_unmap_queues(gid, entry->produce_q, entry->consume_q); 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci /* 23628c2ecf20Sopenharmony_ci * On hosted, when we unmap queue pairs, the VMX will also 23638c2ecf20Sopenharmony_ci * unmap the guest memory, so we invalidate the previously 23648c2ecf20Sopenharmony_ci * registered memory. If the queue pair is mapped again at a 23658c2ecf20Sopenharmony_ci * later point in time, we will need to reregister the user 23668c2ecf20Sopenharmony_ci * memory with a possibly new user VA. 23678c2ecf20Sopenharmony_ci */ 23688c2ecf20Sopenharmony_ci qp_host_unregister_user_memory(entry->produce_q, 23698c2ecf20Sopenharmony_ci entry->consume_q); 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci /* 23728c2ecf20Sopenharmony_ci * Move state from *_MEM to *_NO_MEM. 23738c2ecf20Sopenharmony_ci */ 23748c2ecf20Sopenharmony_ci entry->state--; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci qp_release_queue_mutex(entry->produce_q); 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci out: 23828c2ecf20Sopenharmony_ci mutex_unlock(&qp_broker_list.mutex); 23838c2ecf20Sopenharmony_ci return result; 23848c2ecf20Sopenharmony_ci} 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci/* 23878c2ecf20Sopenharmony_ci * Destroys all guest queue pair endpoints. If active guest queue 23888c2ecf20Sopenharmony_ci * pairs still exist, hypercalls to attempt detach from these 23898c2ecf20Sopenharmony_ci * queue pairs will be made. Any failure to detach is silently 23908c2ecf20Sopenharmony_ci * ignored. 23918c2ecf20Sopenharmony_ci */ 23928c2ecf20Sopenharmony_civoid vmci_qp_guest_endpoints_exit(void) 23938c2ecf20Sopenharmony_ci{ 23948c2ecf20Sopenharmony_ci struct qp_entry *entry; 23958c2ecf20Sopenharmony_ci struct qp_guest_endpoint *ep; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci mutex_lock(&qp_guest_endpoints.mutex); 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci while ((entry = qp_list_get_head(&qp_guest_endpoints))) { 24008c2ecf20Sopenharmony_ci ep = (struct qp_guest_endpoint *)entry; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci /* Don't make a hypercall for local queue_pairs. */ 24038c2ecf20Sopenharmony_ci if (!(entry->flags & VMCI_QPFLAG_LOCAL)) 24048c2ecf20Sopenharmony_ci qp_detatch_hypercall(entry->handle); 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci /* We cannot fail the exit, so let's reset ref_count. */ 24078c2ecf20Sopenharmony_ci entry->ref_count = 0; 24088c2ecf20Sopenharmony_ci qp_list_remove_entry(&qp_guest_endpoints, entry); 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci qp_guest_endpoint_destroy(ep); 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci mutex_unlock(&qp_guest_endpoints.mutex); 24148c2ecf20Sopenharmony_ci} 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci/* 24178c2ecf20Sopenharmony_ci * Helper routine that will lock the queue pair before subsequent 24188c2ecf20Sopenharmony_ci * operations. 24198c2ecf20Sopenharmony_ci * Note: Non-blocking on the host side is currently only implemented in ESX. 24208c2ecf20Sopenharmony_ci * Since non-blocking isn't yet implemented on the host personality we 24218c2ecf20Sopenharmony_ci * have no reason to acquire a spin lock. So to avoid the use of an 24228c2ecf20Sopenharmony_ci * unnecessary lock only acquire the mutex if we can block. 24238c2ecf20Sopenharmony_ci */ 24248c2ecf20Sopenharmony_cistatic void qp_lock(const struct vmci_qp *qpair) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci qp_acquire_queue_mutex(qpair->produce_q); 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci/* 24308c2ecf20Sopenharmony_ci * Helper routine that unlocks the queue pair after calling 24318c2ecf20Sopenharmony_ci * qp_lock. 24328c2ecf20Sopenharmony_ci */ 24338c2ecf20Sopenharmony_cistatic void qp_unlock(const struct vmci_qp *qpair) 24348c2ecf20Sopenharmony_ci{ 24358c2ecf20Sopenharmony_ci qp_release_queue_mutex(qpair->produce_q); 24368c2ecf20Sopenharmony_ci} 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci/* 24398c2ecf20Sopenharmony_ci * The queue headers may not be mapped at all times. If a queue is 24408c2ecf20Sopenharmony_ci * currently not mapped, it will be attempted to do so. 24418c2ecf20Sopenharmony_ci */ 24428c2ecf20Sopenharmony_cistatic int qp_map_queue_headers(struct vmci_queue *produce_q, 24438c2ecf20Sopenharmony_ci struct vmci_queue *consume_q) 24448c2ecf20Sopenharmony_ci{ 24458c2ecf20Sopenharmony_ci int result; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci if (NULL == produce_q->q_header || NULL == consume_q->q_header) { 24488c2ecf20Sopenharmony_ci result = qp_host_map_queues(produce_q, consume_q); 24498c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 24508c2ecf20Sopenharmony_ci return (produce_q->saved_header && 24518c2ecf20Sopenharmony_ci consume_q->saved_header) ? 24528c2ecf20Sopenharmony_ci VMCI_ERROR_QUEUEPAIR_NOT_READY : 24538c2ecf20Sopenharmony_ci VMCI_ERROR_QUEUEPAIR_NOTATTACHED; 24548c2ecf20Sopenharmony_ci } 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci/* 24608c2ecf20Sopenharmony_ci * Helper routine that will retrieve the produce and consume 24618c2ecf20Sopenharmony_ci * headers of a given queue pair. If the guest memory of the 24628c2ecf20Sopenharmony_ci * queue pair is currently not available, the saved queue headers 24638c2ecf20Sopenharmony_ci * will be returned, if these are available. 24648c2ecf20Sopenharmony_ci */ 24658c2ecf20Sopenharmony_cistatic int qp_get_queue_headers(const struct vmci_qp *qpair, 24668c2ecf20Sopenharmony_ci struct vmci_queue_header **produce_q_header, 24678c2ecf20Sopenharmony_ci struct vmci_queue_header **consume_q_header) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci int result; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci result = qp_map_queue_headers(qpair->produce_q, qpair->consume_q); 24728c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) { 24738c2ecf20Sopenharmony_ci *produce_q_header = qpair->produce_q->q_header; 24748c2ecf20Sopenharmony_ci *consume_q_header = qpair->consume_q->q_header; 24758c2ecf20Sopenharmony_ci } else if (qpair->produce_q->saved_header && 24768c2ecf20Sopenharmony_ci qpair->consume_q->saved_header) { 24778c2ecf20Sopenharmony_ci *produce_q_header = qpair->produce_q->saved_header; 24788c2ecf20Sopenharmony_ci *consume_q_header = qpair->consume_q->saved_header; 24798c2ecf20Sopenharmony_ci result = VMCI_SUCCESS; 24808c2ecf20Sopenharmony_ci } 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci return result; 24838c2ecf20Sopenharmony_ci} 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci/* 24868c2ecf20Sopenharmony_ci * Callback from VMCI queue pair broker indicating that a queue 24878c2ecf20Sopenharmony_ci * pair that was previously not ready, now either is ready or 24888c2ecf20Sopenharmony_ci * gone forever. 24898c2ecf20Sopenharmony_ci */ 24908c2ecf20Sopenharmony_cistatic int qp_wakeup_cb(void *client_data) 24918c2ecf20Sopenharmony_ci{ 24928c2ecf20Sopenharmony_ci struct vmci_qp *qpair = (struct vmci_qp *)client_data; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci qp_lock(qpair); 24958c2ecf20Sopenharmony_ci while (qpair->blocked > 0) { 24968c2ecf20Sopenharmony_ci qpair->blocked--; 24978c2ecf20Sopenharmony_ci qpair->generation++; 24988c2ecf20Sopenharmony_ci wake_up(&qpair->event); 24998c2ecf20Sopenharmony_ci } 25008c2ecf20Sopenharmony_ci qp_unlock(qpair); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 25038c2ecf20Sopenharmony_ci} 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci/* 25068c2ecf20Sopenharmony_ci * Makes the calling thread wait for the queue pair to become 25078c2ecf20Sopenharmony_ci * ready for host side access. Returns true when thread is 25088c2ecf20Sopenharmony_ci * woken up after queue pair state change, false otherwise. 25098c2ecf20Sopenharmony_ci */ 25108c2ecf20Sopenharmony_cistatic bool qp_wait_for_ready_queue(struct vmci_qp *qpair) 25118c2ecf20Sopenharmony_ci{ 25128c2ecf20Sopenharmony_ci unsigned int generation; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci qpair->blocked++; 25158c2ecf20Sopenharmony_ci generation = qpair->generation; 25168c2ecf20Sopenharmony_ci qp_unlock(qpair); 25178c2ecf20Sopenharmony_ci wait_event(qpair->event, generation != qpair->generation); 25188c2ecf20Sopenharmony_ci qp_lock(qpair); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci return true; 25218c2ecf20Sopenharmony_ci} 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci/* 25248c2ecf20Sopenharmony_ci * Enqueues a given buffer to the produce queue using the provided 25258c2ecf20Sopenharmony_ci * function. As many bytes as possible (space available in the queue) 25268c2ecf20Sopenharmony_ci * are enqueued. Assumes the queue->mutex has been acquired. Returns 25278c2ecf20Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue 25288c2ecf20Sopenharmony_ci * data, VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the 25298c2ecf20Sopenharmony_ci * queue (as defined by the queue size), VMCI_ERROR_INVALID_ARGS, if 25308c2ecf20Sopenharmony_ci * an error occured when accessing the buffer, 25318c2ecf20Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't 25328c2ecf20Sopenharmony_ci * available. Otherwise, the number of bytes written to the queue is 25338c2ecf20Sopenharmony_ci * returned. Updates the tail pointer of the produce queue. 25348c2ecf20Sopenharmony_ci */ 25358c2ecf20Sopenharmony_cistatic ssize_t qp_enqueue_locked(struct vmci_queue *produce_q, 25368c2ecf20Sopenharmony_ci struct vmci_queue *consume_q, 25378c2ecf20Sopenharmony_ci const u64 produce_q_size, 25388c2ecf20Sopenharmony_ci struct iov_iter *from) 25398c2ecf20Sopenharmony_ci{ 25408c2ecf20Sopenharmony_ci s64 free_space; 25418c2ecf20Sopenharmony_ci u64 tail; 25428c2ecf20Sopenharmony_ci size_t buf_size = iov_iter_count(from); 25438c2ecf20Sopenharmony_ci size_t written; 25448c2ecf20Sopenharmony_ci ssize_t result; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci result = qp_map_queue_headers(produce_q, consume_q); 25478c2ecf20Sopenharmony_ci if (unlikely(result != VMCI_SUCCESS)) 25488c2ecf20Sopenharmony_ci return result; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci free_space = vmci_q_header_free_space(produce_q->q_header, 25518c2ecf20Sopenharmony_ci consume_q->q_header, 25528c2ecf20Sopenharmony_ci produce_q_size); 25538c2ecf20Sopenharmony_ci if (free_space == 0) 25548c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_NOSPACE; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci if (free_space < VMCI_SUCCESS) 25578c2ecf20Sopenharmony_ci return (ssize_t) free_space; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci written = (size_t) (free_space > buf_size ? buf_size : free_space); 25608c2ecf20Sopenharmony_ci tail = vmci_q_header_producer_tail(produce_q->q_header); 25618c2ecf20Sopenharmony_ci if (likely(tail + written < produce_q_size)) { 25628c2ecf20Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, tail, from, written); 25638c2ecf20Sopenharmony_ci } else { 25648c2ecf20Sopenharmony_ci /* Tail pointer wraps around. */ 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci const size_t tmp = (size_t) (produce_q_size - tail); 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, tail, from, tmp); 25698c2ecf20Sopenharmony_ci if (result >= VMCI_SUCCESS) 25708c2ecf20Sopenharmony_ci result = qp_memcpy_to_queue_iter(produce_q, 0, from, 25718c2ecf20Sopenharmony_ci written - tmp); 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 25758c2ecf20Sopenharmony_ci return result; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci vmci_q_header_add_producer_tail(produce_q->q_header, written, 25788c2ecf20Sopenharmony_ci produce_q_size); 25798c2ecf20Sopenharmony_ci return written; 25808c2ecf20Sopenharmony_ci} 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci/* 25838c2ecf20Sopenharmony_ci * Dequeues data (if available) from the given consume queue. Writes data 25848c2ecf20Sopenharmony_ci * to the user provided buffer using the provided function. 25858c2ecf20Sopenharmony_ci * Assumes the queue->mutex has been acquired. 25868c2ecf20Sopenharmony_ci * Results: 25878c2ecf20Sopenharmony_ci * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue. 25888c2ecf20Sopenharmony_ci * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue 25898c2ecf20Sopenharmony_ci * (as defined by the queue size). 25908c2ecf20Sopenharmony_ci * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer. 25918c2ecf20Sopenharmony_ci * Otherwise the number of bytes dequeued is returned. 25928c2ecf20Sopenharmony_ci * Side effects: 25938c2ecf20Sopenharmony_ci * Updates the head pointer of the consume queue. 25948c2ecf20Sopenharmony_ci */ 25958c2ecf20Sopenharmony_cistatic ssize_t qp_dequeue_locked(struct vmci_queue *produce_q, 25968c2ecf20Sopenharmony_ci struct vmci_queue *consume_q, 25978c2ecf20Sopenharmony_ci const u64 consume_q_size, 25988c2ecf20Sopenharmony_ci struct iov_iter *to, 25998c2ecf20Sopenharmony_ci bool update_consumer) 26008c2ecf20Sopenharmony_ci{ 26018c2ecf20Sopenharmony_ci size_t buf_size = iov_iter_count(to); 26028c2ecf20Sopenharmony_ci s64 buf_ready; 26038c2ecf20Sopenharmony_ci u64 head; 26048c2ecf20Sopenharmony_ci size_t read; 26058c2ecf20Sopenharmony_ci ssize_t result; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci result = qp_map_queue_headers(produce_q, consume_q); 26088c2ecf20Sopenharmony_ci if (unlikely(result != VMCI_SUCCESS)) 26098c2ecf20Sopenharmony_ci return result; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci buf_ready = vmci_q_header_buf_ready(consume_q->q_header, 26128c2ecf20Sopenharmony_ci produce_q->q_header, 26138c2ecf20Sopenharmony_ci consume_q_size); 26148c2ecf20Sopenharmony_ci if (buf_ready == 0) 26158c2ecf20Sopenharmony_ci return VMCI_ERROR_QUEUEPAIR_NODATA; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci if (buf_ready < VMCI_SUCCESS) 26188c2ecf20Sopenharmony_ci return (ssize_t) buf_ready; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci read = (size_t) (buf_ready > buf_size ? buf_size : buf_ready); 26218c2ecf20Sopenharmony_ci head = vmci_q_header_consumer_head(produce_q->q_header); 26228c2ecf20Sopenharmony_ci if (likely(head + read < consume_q_size)) { 26238c2ecf20Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, head, read); 26248c2ecf20Sopenharmony_ci } else { 26258c2ecf20Sopenharmony_ci /* Head pointer wraps around. */ 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci const size_t tmp = (size_t) (consume_q_size - head); 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, head, tmp); 26308c2ecf20Sopenharmony_ci if (result >= VMCI_SUCCESS) 26318c2ecf20Sopenharmony_ci result = qp_memcpy_from_queue_iter(to, consume_q, 0, 26328c2ecf20Sopenharmony_ci read - tmp); 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci } 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci if (result < VMCI_SUCCESS) 26378c2ecf20Sopenharmony_ci return result; 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci if (update_consumer) 26408c2ecf20Sopenharmony_ci vmci_q_header_add_consumer_head(produce_q->q_header, 26418c2ecf20Sopenharmony_ci read, consume_q_size); 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci return read; 26448c2ecf20Sopenharmony_ci} 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci/* 26478c2ecf20Sopenharmony_ci * vmci_qpair_alloc() - Allocates a queue pair. 26488c2ecf20Sopenharmony_ci * @qpair: Pointer for the new vmci_qp struct. 26498c2ecf20Sopenharmony_ci * @handle: Handle to track the resource. 26508c2ecf20Sopenharmony_ci * @produce_qsize: Desired size of the producer queue. 26518c2ecf20Sopenharmony_ci * @consume_qsize: Desired size of the consumer queue. 26528c2ecf20Sopenharmony_ci * @peer: ContextID of the peer. 26538c2ecf20Sopenharmony_ci * @flags: VMCI flags. 26548c2ecf20Sopenharmony_ci * @priv_flags: VMCI priviledge flags. 26558c2ecf20Sopenharmony_ci * 26568c2ecf20Sopenharmony_ci * This is the client interface for allocating the memory for a 26578c2ecf20Sopenharmony_ci * vmci_qp structure and then attaching to the underlying 26588c2ecf20Sopenharmony_ci * queue. If an error occurs allocating the memory for the 26598c2ecf20Sopenharmony_ci * vmci_qp structure no attempt is made to attach. If an 26608c2ecf20Sopenharmony_ci * error occurs attaching, then the structure is freed. 26618c2ecf20Sopenharmony_ci */ 26628c2ecf20Sopenharmony_ciint vmci_qpair_alloc(struct vmci_qp **qpair, 26638c2ecf20Sopenharmony_ci struct vmci_handle *handle, 26648c2ecf20Sopenharmony_ci u64 produce_qsize, 26658c2ecf20Sopenharmony_ci u64 consume_qsize, 26668c2ecf20Sopenharmony_ci u32 peer, 26678c2ecf20Sopenharmony_ci u32 flags, 26688c2ecf20Sopenharmony_ci u32 priv_flags) 26698c2ecf20Sopenharmony_ci{ 26708c2ecf20Sopenharmony_ci struct vmci_qp *my_qpair; 26718c2ecf20Sopenharmony_ci int retval; 26728c2ecf20Sopenharmony_ci struct vmci_handle src = VMCI_INVALID_HANDLE; 26738c2ecf20Sopenharmony_ci struct vmci_handle dst = vmci_make_handle(peer, VMCI_INVALID_ID); 26748c2ecf20Sopenharmony_ci enum vmci_route route; 26758c2ecf20Sopenharmony_ci vmci_event_release_cb wakeup_cb; 26768c2ecf20Sopenharmony_ci void *client_data; 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci /* 26798c2ecf20Sopenharmony_ci * Restrict the size of a queuepair. The device already 26808c2ecf20Sopenharmony_ci * enforces a limit on the total amount of memory that can be 26818c2ecf20Sopenharmony_ci * allocated to queuepairs for a guest. However, we try to 26828c2ecf20Sopenharmony_ci * allocate this memory before we make the queuepair 26838c2ecf20Sopenharmony_ci * allocation hypercall. On Linux, we allocate each page 26848c2ecf20Sopenharmony_ci * separately, which means rather than fail, the guest will 26858c2ecf20Sopenharmony_ci * thrash while it tries to allocate, and will become 26868c2ecf20Sopenharmony_ci * increasingly unresponsive to the point where it appears to 26878c2ecf20Sopenharmony_ci * be hung. So we place a limit on the size of an individual 26888c2ecf20Sopenharmony_ci * queuepair here, and leave the device to enforce the 26898c2ecf20Sopenharmony_ci * restriction on total queuepair memory. (Note that this 26908c2ecf20Sopenharmony_ci * doesn't prevent all cases; a user with only this much 26918c2ecf20Sopenharmony_ci * physical memory could still get into trouble.) The error 26928c2ecf20Sopenharmony_ci * used by the device is NO_RESOURCES, so use that here too. 26938c2ecf20Sopenharmony_ci */ 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci if (produce_qsize + consume_qsize < max(produce_qsize, consume_qsize) || 26968c2ecf20Sopenharmony_ci produce_qsize + consume_qsize > VMCI_MAX_GUEST_QP_MEMORY) 26978c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_RESOURCES; 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci retval = vmci_route(&src, &dst, false, &route); 27008c2ecf20Sopenharmony_ci if (retval < VMCI_SUCCESS) 27018c2ecf20Sopenharmony_ci route = vmci_guest_code_active() ? 27028c2ecf20Sopenharmony_ci VMCI_ROUTE_AS_GUEST : VMCI_ROUTE_AS_HOST; 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci if (flags & (VMCI_QPFLAG_NONBLOCK | VMCI_QPFLAG_PINNED)) { 27058c2ecf20Sopenharmony_ci pr_devel("NONBLOCK OR PINNED set"); 27068c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 27078c2ecf20Sopenharmony_ci } 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci my_qpair = kzalloc(sizeof(*my_qpair), GFP_KERNEL); 27108c2ecf20Sopenharmony_ci if (!my_qpair) 27118c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_ci my_qpair->produce_q_size = produce_qsize; 27148c2ecf20Sopenharmony_ci my_qpair->consume_q_size = consume_qsize; 27158c2ecf20Sopenharmony_ci my_qpair->peer = peer; 27168c2ecf20Sopenharmony_ci my_qpair->flags = flags; 27178c2ecf20Sopenharmony_ci my_qpair->priv_flags = priv_flags; 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci wakeup_cb = NULL; 27208c2ecf20Sopenharmony_ci client_data = NULL; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (VMCI_ROUTE_AS_HOST == route) { 27238c2ecf20Sopenharmony_ci my_qpair->guest_endpoint = false; 27248c2ecf20Sopenharmony_ci if (!(flags & VMCI_QPFLAG_LOCAL)) { 27258c2ecf20Sopenharmony_ci my_qpair->blocked = 0; 27268c2ecf20Sopenharmony_ci my_qpair->generation = 0; 27278c2ecf20Sopenharmony_ci init_waitqueue_head(&my_qpair->event); 27288c2ecf20Sopenharmony_ci wakeup_cb = qp_wakeup_cb; 27298c2ecf20Sopenharmony_ci client_data = (void *)my_qpair; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci } else { 27328c2ecf20Sopenharmony_ci my_qpair->guest_endpoint = true; 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci retval = vmci_qp_alloc(handle, 27368c2ecf20Sopenharmony_ci &my_qpair->produce_q, 27378c2ecf20Sopenharmony_ci my_qpair->produce_q_size, 27388c2ecf20Sopenharmony_ci &my_qpair->consume_q, 27398c2ecf20Sopenharmony_ci my_qpair->consume_q_size, 27408c2ecf20Sopenharmony_ci my_qpair->peer, 27418c2ecf20Sopenharmony_ci my_qpair->flags, 27428c2ecf20Sopenharmony_ci my_qpair->priv_flags, 27438c2ecf20Sopenharmony_ci my_qpair->guest_endpoint, 27448c2ecf20Sopenharmony_ci wakeup_cb, client_data); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci if (retval < VMCI_SUCCESS) { 27478c2ecf20Sopenharmony_ci kfree(my_qpair); 27488c2ecf20Sopenharmony_ci return retval; 27498c2ecf20Sopenharmony_ci } 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci *qpair = my_qpair; 27528c2ecf20Sopenharmony_ci my_qpair->handle = *handle; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci return retval; 27558c2ecf20Sopenharmony_ci} 27568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_alloc); 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci/* 27598c2ecf20Sopenharmony_ci * vmci_qpair_detach() - Detatches the client from a queue pair. 27608c2ecf20Sopenharmony_ci * @qpair: Reference of a pointer to the qpair struct. 27618c2ecf20Sopenharmony_ci * 27628c2ecf20Sopenharmony_ci * This is the client interface for detaching from a VMCIQPair. 27638c2ecf20Sopenharmony_ci * Note that this routine will free the memory allocated for the 27648c2ecf20Sopenharmony_ci * vmci_qp structure too. 27658c2ecf20Sopenharmony_ci */ 27668c2ecf20Sopenharmony_ciint vmci_qpair_detach(struct vmci_qp **qpair) 27678c2ecf20Sopenharmony_ci{ 27688c2ecf20Sopenharmony_ci int result; 27698c2ecf20Sopenharmony_ci struct vmci_qp *old_qpair; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci if (!qpair || !(*qpair)) 27728c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci old_qpair = *qpair; 27758c2ecf20Sopenharmony_ci result = qp_detatch(old_qpair->handle, old_qpair->guest_endpoint); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* 27788c2ecf20Sopenharmony_ci * The guest can fail to detach for a number of reasons, and 27798c2ecf20Sopenharmony_ci * if it does so, it will cleanup the entry (if there is one). 27808c2ecf20Sopenharmony_ci * The host can fail too, but it won't cleanup the entry 27818c2ecf20Sopenharmony_ci * immediately, it will do that later when the context is 27828c2ecf20Sopenharmony_ci * freed. Either way, we need to release the qpair struct 27838c2ecf20Sopenharmony_ci * here; there isn't much the caller can do, and we don't want 27848c2ecf20Sopenharmony_ci * to leak. 27858c2ecf20Sopenharmony_ci */ 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci memset(old_qpair, 0, sizeof(*old_qpair)); 27888c2ecf20Sopenharmony_ci old_qpair->handle = VMCI_INVALID_HANDLE; 27898c2ecf20Sopenharmony_ci old_qpair->peer = VMCI_INVALID_ID; 27908c2ecf20Sopenharmony_ci kfree(old_qpair); 27918c2ecf20Sopenharmony_ci *qpair = NULL; 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci return result; 27948c2ecf20Sopenharmony_ci} 27958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_detach); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci/* 27988c2ecf20Sopenharmony_ci * vmci_qpair_get_produce_indexes() - Retrieves the indexes of the producer. 27998c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 28008c2ecf20Sopenharmony_ci * @producer_tail: Reference used for storing producer tail index. 28018c2ecf20Sopenharmony_ci * @consumer_head: Reference used for storing the consumer head index. 28028c2ecf20Sopenharmony_ci * 28038c2ecf20Sopenharmony_ci * This is the client interface for getting the current indexes of the 28048c2ecf20Sopenharmony_ci * QPair from the point of the view of the caller as the producer. 28058c2ecf20Sopenharmony_ci */ 28068c2ecf20Sopenharmony_ciint vmci_qpair_get_produce_indexes(const struct vmci_qp *qpair, 28078c2ecf20Sopenharmony_ci u64 *producer_tail, 28088c2ecf20Sopenharmony_ci u64 *consumer_head) 28098c2ecf20Sopenharmony_ci{ 28108c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 28118c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 28128c2ecf20Sopenharmony_ci int result; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci if (!qpair) 28158c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci qp_lock(qpair); 28188c2ecf20Sopenharmony_ci result = 28198c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 28208c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 28218c2ecf20Sopenharmony_ci vmci_q_header_get_pointers(produce_q_header, consume_q_header, 28228c2ecf20Sopenharmony_ci producer_tail, consumer_head); 28238c2ecf20Sopenharmony_ci qp_unlock(qpair); 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS && 28268c2ecf20Sopenharmony_ci ((producer_tail && *producer_tail >= qpair->produce_q_size) || 28278c2ecf20Sopenharmony_ci (consumer_head && *consumer_head >= qpair->produce_q_size))) 28288c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_SIZE; 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci return result; 28318c2ecf20Sopenharmony_ci} 28328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_get_produce_indexes); 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci/* 28358c2ecf20Sopenharmony_ci * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the consumer. 28368c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 28378c2ecf20Sopenharmony_ci * @consumer_tail: Reference used for storing consumer tail index. 28388c2ecf20Sopenharmony_ci * @producer_head: Reference used for storing the producer head index. 28398c2ecf20Sopenharmony_ci * 28408c2ecf20Sopenharmony_ci * This is the client interface for getting the current indexes of the 28418c2ecf20Sopenharmony_ci * QPair from the point of the view of the caller as the consumer. 28428c2ecf20Sopenharmony_ci */ 28438c2ecf20Sopenharmony_ciint vmci_qpair_get_consume_indexes(const struct vmci_qp *qpair, 28448c2ecf20Sopenharmony_ci u64 *consumer_tail, 28458c2ecf20Sopenharmony_ci u64 *producer_head) 28468c2ecf20Sopenharmony_ci{ 28478c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 28488c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 28498c2ecf20Sopenharmony_ci int result; 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci if (!qpair) 28528c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci qp_lock(qpair); 28558c2ecf20Sopenharmony_ci result = 28568c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 28578c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 28588c2ecf20Sopenharmony_ci vmci_q_header_get_pointers(consume_q_header, produce_q_header, 28598c2ecf20Sopenharmony_ci consumer_tail, producer_head); 28608c2ecf20Sopenharmony_ci qp_unlock(qpair); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS && 28638c2ecf20Sopenharmony_ci ((consumer_tail && *consumer_tail >= qpair->consume_q_size) || 28648c2ecf20Sopenharmony_ci (producer_head && *producer_head >= qpair->consume_q_size))) 28658c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_SIZE; 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci return result; 28688c2ecf20Sopenharmony_ci} 28698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_get_consume_indexes); 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci/* 28728c2ecf20Sopenharmony_ci * vmci_qpair_produce_free_space() - Retrieves free space in producer queue. 28738c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 28748c2ecf20Sopenharmony_ci * 28758c2ecf20Sopenharmony_ci * This is the client interface for getting the amount of free 28768c2ecf20Sopenharmony_ci * space in the QPair from the point of the view of the caller as 28778c2ecf20Sopenharmony_ci * the producer which is the common case. Returns < 0 if err, else 28788c2ecf20Sopenharmony_ci * available bytes into which data can be enqueued if > 0. 28798c2ecf20Sopenharmony_ci */ 28808c2ecf20Sopenharmony_cis64 vmci_qpair_produce_free_space(const struct vmci_qp *qpair) 28818c2ecf20Sopenharmony_ci{ 28828c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 28838c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 28848c2ecf20Sopenharmony_ci s64 result; 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci if (!qpair) 28878c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci qp_lock(qpair); 28908c2ecf20Sopenharmony_ci result = 28918c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 28928c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 28938c2ecf20Sopenharmony_ci result = vmci_q_header_free_space(produce_q_header, 28948c2ecf20Sopenharmony_ci consume_q_header, 28958c2ecf20Sopenharmony_ci qpair->produce_q_size); 28968c2ecf20Sopenharmony_ci else 28978c2ecf20Sopenharmony_ci result = 0; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci qp_unlock(qpair); 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci return result; 29028c2ecf20Sopenharmony_ci} 29038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_produce_free_space); 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci/* 29068c2ecf20Sopenharmony_ci * vmci_qpair_consume_free_space() - Retrieves free space in consumer queue. 29078c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 29088c2ecf20Sopenharmony_ci * 29098c2ecf20Sopenharmony_ci * This is the client interface for getting the amount of free 29108c2ecf20Sopenharmony_ci * space in the QPair from the point of the view of the caller as 29118c2ecf20Sopenharmony_ci * the consumer which is not the common case. Returns < 0 if err, else 29128c2ecf20Sopenharmony_ci * available bytes into which data can be enqueued if > 0. 29138c2ecf20Sopenharmony_ci */ 29148c2ecf20Sopenharmony_cis64 vmci_qpair_consume_free_space(const struct vmci_qp *qpair) 29158c2ecf20Sopenharmony_ci{ 29168c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 29178c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 29188c2ecf20Sopenharmony_ci s64 result; 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci if (!qpair) 29218c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_ci qp_lock(qpair); 29248c2ecf20Sopenharmony_ci result = 29258c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 29268c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 29278c2ecf20Sopenharmony_ci result = vmci_q_header_free_space(consume_q_header, 29288c2ecf20Sopenharmony_ci produce_q_header, 29298c2ecf20Sopenharmony_ci qpair->consume_q_size); 29308c2ecf20Sopenharmony_ci else 29318c2ecf20Sopenharmony_ci result = 0; 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci qp_unlock(qpair); 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci return result; 29368c2ecf20Sopenharmony_ci} 29378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_consume_free_space); 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci/* 29408c2ecf20Sopenharmony_ci * vmci_qpair_produce_buf_ready() - Gets bytes ready to read from 29418c2ecf20Sopenharmony_ci * producer queue. 29428c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 29438c2ecf20Sopenharmony_ci * 29448c2ecf20Sopenharmony_ci * This is the client interface for getting the amount of 29458c2ecf20Sopenharmony_ci * enqueued data in the QPair from the point of the view of the 29468c2ecf20Sopenharmony_ci * caller as the producer which is not the common case. Returns < 0 if err, 29478c2ecf20Sopenharmony_ci * else available bytes that may be read. 29488c2ecf20Sopenharmony_ci */ 29498c2ecf20Sopenharmony_cis64 vmci_qpair_produce_buf_ready(const struct vmci_qp *qpair) 29508c2ecf20Sopenharmony_ci{ 29518c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 29528c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 29538c2ecf20Sopenharmony_ci s64 result; 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci if (!qpair) 29568c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci qp_lock(qpair); 29598c2ecf20Sopenharmony_ci result = 29608c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 29618c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 29628c2ecf20Sopenharmony_ci result = vmci_q_header_buf_ready(produce_q_header, 29638c2ecf20Sopenharmony_ci consume_q_header, 29648c2ecf20Sopenharmony_ci qpair->produce_q_size); 29658c2ecf20Sopenharmony_ci else 29668c2ecf20Sopenharmony_ci result = 0; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci qp_unlock(qpair); 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci return result; 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_produce_buf_ready); 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci/* 29758c2ecf20Sopenharmony_ci * vmci_qpair_consume_buf_ready() - Gets bytes ready to read from 29768c2ecf20Sopenharmony_ci * consumer queue. 29778c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 29788c2ecf20Sopenharmony_ci * 29798c2ecf20Sopenharmony_ci * This is the client interface for getting the amount of 29808c2ecf20Sopenharmony_ci * enqueued data in the QPair from the point of the view of the 29818c2ecf20Sopenharmony_ci * caller as the consumer which is the normal case. Returns < 0 if err, 29828c2ecf20Sopenharmony_ci * else available bytes that may be read. 29838c2ecf20Sopenharmony_ci */ 29848c2ecf20Sopenharmony_cis64 vmci_qpair_consume_buf_ready(const struct vmci_qp *qpair) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci struct vmci_queue_header *produce_q_header; 29878c2ecf20Sopenharmony_ci struct vmci_queue_header *consume_q_header; 29888c2ecf20Sopenharmony_ci s64 result; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci if (!qpair) 29918c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ci qp_lock(qpair); 29948c2ecf20Sopenharmony_ci result = 29958c2ecf20Sopenharmony_ci qp_get_queue_headers(qpair, &produce_q_header, &consume_q_header); 29968c2ecf20Sopenharmony_ci if (result == VMCI_SUCCESS) 29978c2ecf20Sopenharmony_ci result = vmci_q_header_buf_ready(consume_q_header, 29988c2ecf20Sopenharmony_ci produce_q_header, 29998c2ecf20Sopenharmony_ci qpair->consume_q_size); 30008c2ecf20Sopenharmony_ci else 30018c2ecf20Sopenharmony_ci result = 0; 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci qp_unlock(qpair); 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci return result; 30068c2ecf20Sopenharmony_ci} 30078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_consume_buf_ready); 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci/* 30108c2ecf20Sopenharmony_ci * vmci_qpair_enqueue() - Throw data on the queue. 30118c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 30128c2ecf20Sopenharmony_ci * @buf: Pointer to buffer containing data 30138c2ecf20Sopenharmony_ci * @buf_size: Length of buffer. 30148c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused). 30158c2ecf20Sopenharmony_ci * 30168c2ecf20Sopenharmony_ci * This is the client interface for enqueueing data into the queue. 30178c2ecf20Sopenharmony_ci * Returns number of bytes enqueued or < 0 on error. 30188c2ecf20Sopenharmony_ci */ 30198c2ecf20Sopenharmony_cissize_t vmci_qpair_enqueue(struct vmci_qp *qpair, 30208c2ecf20Sopenharmony_ci const void *buf, 30218c2ecf20Sopenharmony_ci size_t buf_size, 30228c2ecf20Sopenharmony_ci int buf_type) 30238c2ecf20Sopenharmony_ci{ 30248c2ecf20Sopenharmony_ci ssize_t result; 30258c2ecf20Sopenharmony_ci struct iov_iter from; 30268c2ecf20Sopenharmony_ci struct kvec v = {.iov_base = (void *)buf, .iov_len = buf_size}; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci if (!qpair || !buf) 30298c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci iov_iter_kvec(&from, WRITE, &v, 1, buf_size); 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci qp_lock(qpair); 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci do { 30368c2ecf20Sopenharmony_ci result = qp_enqueue_locked(qpair->produce_q, 30378c2ecf20Sopenharmony_ci qpair->consume_q, 30388c2ecf20Sopenharmony_ci qpair->produce_q_size, 30398c2ecf20Sopenharmony_ci &from); 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 30428c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 30438c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci qp_unlock(qpair); 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci return result; 30508c2ecf20Sopenharmony_ci} 30518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_enqueue); 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci/* 30548c2ecf20Sopenharmony_ci * vmci_qpair_dequeue() - Get data from the queue. 30558c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 30568c2ecf20Sopenharmony_ci * @buf: Pointer to buffer for the data 30578c2ecf20Sopenharmony_ci * @buf_size: Length of buffer. 30588c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused). 30598c2ecf20Sopenharmony_ci * 30608c2ecf20Sopenharmony_ci * This is the client interface for dequeueing data from the queue. 30618c2ecf20Sopenharmony_ci * Returns number of bytes dequeued or < 0 on error. 30628c2ecf20Sopenharmony_ci */ 30638c2ecf20Sopenharmony_cissize_t vmci_qpair_dequeue(struct vmci_qp *qpair, 30648c2ecf20Sopenharmony_ci void *buf, 30658c2ecf20Sopenharmony_ci size_t buf_size, 30668c2ecf20Sopenharmony_ci int buf_type) 30678c2ecf20Sopenharmony_ci{ 30688c2ecf20Sopenharmony_ci ssize_t result; 30698c2ecf20Sopenharmony_ci struct iov_iter to; 30708c2ecf20Sopenharmony_ci struct kvec v = {.iov_base = buf, .iov_len = buf_size}; 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci if (!qpair || !buf) 30738c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci iov_iter_kvec(&to, READ, &v, 1, buf_size); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci qp_lock(qpair); 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci do { 30808c2ecf20Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 30818c2ecf20Sopenharmony_ci qpair->consume_q, 30828c2ecf20Sopenharmony_ci qpair->consume_q_size, 30838c2ecf20Sopenharmony_ci &to, true); 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 30868c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 30878c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci qp_unlock(qpair); 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_ci return result; 30948c2ecf20Sopenharmony_ci} 30958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_dequeue); 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci/* 30988c2ecf20Sopenharmony_ci * vmci_qpair_peek() - Peek at the data in the queue. 30998c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 31008c2ecf20Sopenharmony_ci * @buf: Pointer to buffer for the data 31018c2ecf20Sopenharmony_ci * @buf_size: Length of buffer. 31028c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused on Linux). 31038c2ecf20Sopenharmony_ci * 31048c2ecf20Sopenharmony_ci * This is the client interface for peeking into a queue. (I.e., 31058c2ecf20Sopenharmony_ci * copy data from the queue without updating the head pointer.) 31068c2ecf20Sopenharmony_ci * Returns number of bytes dequeued or < 0 on error. 31078c2ecf20Sopenharmony_ci */ 31088c2ecf20Sopenharmony_cissize_t vmci_qpair_peek(struct vmci_qp *qpair, 31098c2ecf20Sopenharmony_ci void *buf, 31108c2ecf20Sopenharmony_ci size_t buf_size, 31118c2ecf20Sopenharmony_ci int buf_type) 31128c2ecf20Sopenharmony_ci{ 31138c2ecf20Sopenharmony_ci struct iov_iter to; 31148c2ecf20Sopenharmony_ci struct kvec v = {.iov_base = buf, .iov_len = buf_size}; 31158c2ecf20Sopenharmony_ci ssize_t result; 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_ci if (!qpair || !buf) 31188c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci iov_iter_kvec(&to, READ, &v, 1, buf_size); 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_ci qp_lock(qpair); 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci do { 31258c2ecf20Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 31268c2ecf20Sopenharmony_ci qpair->consume_q, 31278c2ecf20Sopenharmony_ci qpair->consume_q_size, 31288c2ecf20Sopenharmony_ci &to, false); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 31318c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 31328c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci qp_unlock(qpair); 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci return result; 31398c2ecf20Sopenharmony_ci} 31408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_peek); 31418c2ecf20Sopenharmony_ci 31428c2ecf20Sopenharmony_ci/* 31438c2ecf20Sopenharmony_ci * vmci_qpair_enquev() - Throw data on the queue using iov. 31448c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 31458c2ecf20Sopenharmony_ci * @iov: Pointer to buffer containing data 31468c2ecf20Sopenharmony_ci * @iov_size: Length of buffer. 31478c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused). 31488c2ecf20Sopenharmony_ci * 31498c2ecf20Sopenharmony_ci * This is the client interface for enqueueing data into the queue. 31508c2ecf20Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 31518c2ecf20Sopenharmony_ci * of bytes enqueued or < 0 on error. 31528c2ecf20Sopenharmony_ci */ 31538c2ecf20Sopenharmony_cissize_t vmci_qpair_enquev(struct vmci_qp *qpair, 31548c2ecf20Sopenharmony_ci struct msghdr *msg, 31558c2ecf20Sopenharmony_ci size_t iov_size, 31568c2ecf20Sopenharmony_ci int buf_type) 31578c2ecf20Sopenharmony_ci{ 31588c2ecf20Sopenharmony_ci ssize_t result; 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci if (!qpair) 31618c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci qp_lock(qpair); 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci do { 31668c2ecf20Sopenharmony_ci result = qp_enqueue_locked(qpair->produce_q, 31678c2ecf20Sopenharmony_ci qpair->consume_q, 31688c2ecf20Sopenharmony_ci qpair->produce_q_size, 31698c2ecf20Sopenharmony_ci &msg->msg_iter); 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 31728c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 31738c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci qp_unlock(qpair); 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci return result; 31808c2ecf20Sopenharmony_ci} 31818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_enquev); 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci/* 31848c2ecf20Sopenharmony_ci * vmci_qpair_dequev() - Get data from the queue using iov. 31858c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 31868c2ecf20Sopenharmony_ci * @iov: Pointer to buffer for the data 31878c2ecf20Sopenharmony_ci * @iov_size: Length of buffer. 31888c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused). 31898c2ecf20Sopenharmony_ci * 31908c2ecf20Sopenharmony_ci * This is the client interface for dequeueing data from the queue. 31918c2ecf20Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 31928c2ecf20Sopenharmony_ci * of bytes dequeued or < 0 on error. 31938c2ecf20Sopenharmony_ci */ 31948c2ecf20Sopenharmony_cissize_t vmci_qpair_dequev(struct vmci_qp *qpair, 31958c2ecf20Sopenharmony_ci struct msghdr *msg, 31968c2ecf20Sopenharmony_ci size_t iov_size, 31978c2ecf20Sopenharmony_ci int buf_type) 31988c2ecf20Sopenharmony_ci{ 31998c2ecf20Sopenharmony_ci ssize_t result; 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci if (!qpair) 32028c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci qp_lock(qpair); 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci do { 32078c2ecf20Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 32088c2ecf20Sopenharmony_ci qpair->consume_q, 32098c2ecf20Sopenharmony_ci qpair->consume_q_size, 32108c2ecf20Sopenharmony_ci &msg->msg_iter, true); 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 32138c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 32148c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci qp_unlock(qpair); 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_ci return result; 32218c2ecf20Sopenharmony_ci} 32228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_dequev); 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci/* 32258c2ecf20Sopenharmony_ci * vmci_qpair_peekv() - Peek at the data in the queue using iov. 32268c2ecf20Sopenharmony_ci * @qpair: Pointer to the queue pair struct. 32278c2ecf20Sopenharmony_ci * @iov: Pointer to buffer for the data 32288c2ecf20Sopenharmony_ci * @iov_size: Length of buffer. 32298c2ecf20Sopenharmony_ci * @buf_type: Buffer type (Unused on Linux). 32308c2ecf20Sopenharmony_ci * 32318c2ecf20Sopenharmony_ci * This is the client interface for peeking into a queue. (I.e., 32328c2ecf20Sopenharmony_ci * copy data from the queue without updating the head pointer.) 32338c2ecf20Sopenharmony_ci * This function uses IO vectors to handle the work. Returns number 32348c2ecf20Sopenharmony_ci * of bytes peeked or < 0 on error. 32358c2ecf20Sopenharmony_ci */ 32368c2ecf20Sopenharmony_cissize_t vmci_qpair_peekv(struct vmci_qp *qpair, 32378c2ecf20Sopenharmony_ci struct msghdr *msg, 32388c2ecf20Sopenharmony_ci size_t iov_size, 32398c2ecf20Sopenharmony_ci int buf_type) 32408c2ecf20Sopenharmony_ci{ 32418c2ecf20Sopenharmony_ci ssize_t result; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if (!qpair) 32448c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci qp_lock(qpair); 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci do { 32498c2ecf20Sopenharmony_ci result = qp_dequeue_locked(qpair->produce_q, 32508c2ecf20Sopenharmony_ci qpair->consume_q, 32518c2ecf20Sopenharmony_ci qpair->consume_q_size, 32528c2ecf20Sopenharmony_ci &msg->msg_iter, false); 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_ci if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && 32558c2ecf20Sopenharmony_ci !qp_wait_for_ready_queue(qpair)) 32568c2ecf20Sopenharmony_ci result = VMCI_ERROR_WOULD_BLOCK; 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci qp_unlock(qpair); 32618c2ecf20Sopenharmony_ci return result; 32628c2ecf20Sopenharmony_ci} 32638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_qpair_peekv); 3264