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/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/hash.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "vmci_datagram.h" 198c2ecf20Sopenharmony_ci#include "vmci_doorbell.h" 208c2ecf20Sopenharmony_ci#include "vmci_resource.h" 218c2ecf20Sopenharmony_ci#include "vmci_driver.h" 228c2ecf20Sopenharmony_ci#include "vmci_route.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define VMCI_DOORBELL_INDEX_BITS 6 268c2ecf20Sopenharmony_ci#define VMCI_DOORBELL_INDEX_TABLE_SIZE (1 << VMCI_DOORBELL_INDEX_BITS) 278c2ecf20Sopenharmony_ci#define VMCI_DOORBELL_HASH(_idx) hash_32(_idx, VMCI_DOORBELL_INDEX_BITS) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * DoorbellEntry describes the a doorbell notification handle allocated by the 318c2ecf20Sopenharmony_ci * host. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistruct dbell_entry { 348c2ecf20Sopenharmony_ci struct vmci_resource resource; 358c2ecf20Sopenharmony_ci struct hlist_node node; 368c2ecf20Sopenharmony_ci struct work_struct work; 378c2ecf20Sopenharmony_ci vmci_callback notify_cb; 388c2ecf20Sopenharmony_ci void *client_data; 398c2ecf20Sopenharmony_ci u32 idx; 408c2ecf20Sopenharmony_ci u32 priv_flags; 418c2ecf20Sopenharmony_ci bool run_delayed; 428c2ecf20Sopenharmony_ci atomic_t active; /* Only used by guest personality */ 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* The VMCI index table keeps track of currently registered doorbells. */ 468c2ecf20Sopenharmony_cistruct dbell_index_table { 478c2ecf20Sopenharmony_ci spinlock_t lock; /* Index table lock */ 488c2ecf20Sopenharmony_ci struct hlist_head entries[VMCI_DOORBELL_INDEX_TABLE_SIZE]; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct dbell_index_table vmci_doorbell_it = { 528c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(vmci_doorbell_it.lock), 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * The max_notify_idx is one larger than the currently known bitmap index in 578c2ecf20Sopenharmony_ci * use, and is used to determine how much of the bitmap needs to be scanned. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic u32 max_notify_idx; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * The notify_idx_count is used for determining whether there are free entries 638c2ecf20Sopenharmony_ci * within the bitmap (if notify_idx_count + 1 < max_notify_idx). 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic u32 notify_idx_count; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * The last_notify_idx_reserved is used to track the last index handed out - in 698c2ecf20Sopenharmony_ci * the case where multiple handles share a notification index, we hand out 708c2ecf20Sopenharmony_ci * indexes round robin based on last_notify_idx_reserved. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic u32 last_notify_idx_reserved; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* This is a one entry cache used to by the index allocation. */ 758c2ecf20Sopenharmony_cistatic u32 last_notify_idx_released = PAGE_SIZE; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * Utility function that retrieves the privilege flags associated 808c2ecf20Sopenharmony_ci * with a given doorbell handle. For guest endpoints, the 818c2ecf20Sopenharmony_ci * privileges are determined by the context ID, but for host 828c2ecf20Sopenharmony_ci * endpoints privileges are associated with the complete 838c2ecf20Sopenharmony_ci * handle. Hypervisor endpoints are not yet supported. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ciint vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci if (priv_flags == NULL || handle.context == VMCI_INVALID_ID) 888c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (handle.context == VMCI_HOST_CONTEXT_ID) { 918c2ecf20Sopenharmony_ci struct dbell_entry *entry; 928c2ecf20Sopenharmony_ci struct vmci_resource *resource; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci resource = vmci_resource_by_handle(handle, 958c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 968c2ecf20Sopenharmony_ci if (!resource) 978c2ecf20Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 1008c2ecf20Sopenharmony_ci *priv_flags = entry->priv_flags; 1018c2ecf20Sopenharmony_ci vmci_resource_put(resource); 1028c2ecf20Sopenharmony_ci } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) { 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Hypervisor endpoints for notifications are not 1058c2ecf20Sopenharmony_ci * supported (yet). 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci *priv_flags = vmci_context_get_priv_flags(handle.context); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * Find doorbell entry by bitmap index. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_cistatic struct dbell_entry *dbell_index_table_find(u32 idx) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci u32 bucket = VMCI_DOORBELL_HASH(idx); 1218c2ecf20Sopenharmony_ci struct dbell_entry *dbell; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], 1248c2ecf20Sopenharmony_ci node) { 1258c2ecf20Sopenharmony_ci if (idx == dbell->idx) 1268c2ecf20Sopenharmony_ci return dbell; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return NULL; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* 1338c2ecf20Sopenharmony_ci * Add the given entry to the index table. This willi take a reference to the 1348c2ecf20Sopenharmony_ci * entry's resource so that the entry is not deleted before it is removed from 1358c2ecf20Sopenharmony_ci * the * table. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic void dbell_index_table_add(struct dbell_entry *entry) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u32 bucket; 1408c2ecf20Sopenharmony_ci u32 new_notify_idx; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci vmci_resource_get(&entry->resource); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Below we try to allocate an index in the notification 1488c2ecf20Sopenharmony_ci * bitmap with "not too much" sharing between resources. If we 1498c2ecf20Sopenharmony_ci * use less that the full bitmap, we either add to the end if 1508c2ecf20Sopenharmony_ci * there are no unused flags within the currently used area, 1518c2ecf20Sopenharmony_ci * or we search for unused ones. If we use the full bitmap, we 1528c2ecf20Sopenharmony_ci * allocate the index round robin. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) { 1558c2ecf20Sopenharmony_ci if (last_notify_idx_released < max_notify_idx && 1568c2ecf20Sopenharmony_ci !dbell_index_table_find(last_notify_idx_released)) { 1578c2ecf20Sopenharmony_ci new_notify_idx = last_notify_idx_released; 1588c2ecf20Sopenharmony_ci last_notify_idx_released = PAGE_SIZE; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci bool reused = false; 1618c2ecf20Sopenharmony_ci new_notify_idx = last_notify_idx_reserved; 1628c2ecf20Sopenharmony_ci if (notify_idx_count + 1 < max_notify_idx) { 1638c2ecf20Sopenharmony_ci do { 1648c2ecf20Sopenharmony_ci if (!dbell_index_table_find 1658c2ecf20Sopenharmony_ci (new_notify_idx)) { 1668c2ecf20Sopenharmony_ci reused = true; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci new_notify_idx = (new_notify_idx + 1) % 1708c2ecf20Sopenharmony_ci max_notify_idx; 1718c2ecf20Sopenharmony_ci } while (new_notify_idx != 1728c2ecf20Sopenharmony_ci last_notify_idx_released); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci if (!reused) { 1758c2ecf20Sopenharmony_ci new_notify_idx = max_notify_idx; 1768c2ecf20Sopenharmony_ci max_notify_idx++; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci last_notify_idx_reserved = new_notify_idx; 1848c2ecf20Sopenharmony_ci notify_idx_count++; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci entry->idx = new_notify_idx; 1878c2ecf20Sopenharmony_ci bucket = VMCI_DOORBELL_HASH(entry->idx); 1888c2ecf20Sopenharmony_ci hlist_add_head(&entry->node, &vmci_doorbell_it.entries[bucket]); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * Remove the given entry from the index table. This will release() the 1958c2ecf20Sopenharmony_ci * entry's resource. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic void dbell_index_table_remove(struct dbell_entry *entry) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci hlist_del_init(&entry->node); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci notify_idx_count--; 2048c2ecf20Sopenharmony_ci if (entry->idx == max_notify_idx - 1) { 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * If we delete an entry with the maximum known 2078c2ecf20Sopenharmony_ci * notification index, we take the opportunity to 2088c2ecf20Sopenharmony_ci * prune the current max. As there might be other 2098c2ecf20Sopenharmony_ci * unused indices immediately below, we lower the 2108c2ecf20Sopenharmony_ci * maximum until we hit an index in use. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci while (max_notify_idx > 0 && 2138c2ecf20Sopenharmony_ci !dbell_index_table_find(max_notify_idx - 1)) 2148c2ecf20Sopenharmony_ci max_notify_idx--; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci last_notify_idx_released = entry->idx; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci vmci_resource_put(&entry->resource); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* 2258c2ecf20Sopenharmony_ci * Creates a link between the given doorbell handle and the given 2268c2ecf20Sopenharmony_ci * index in the bitmap in the device backend. A notification state 2278c2ecf20Sopenharmony_ci * is created in hypervisor. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic int dbell_link(struct vmci_handle handle, u32 notify_idx) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct vmci_doorbell_link_msg link_msg; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci link_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 2348c2ecf20Sopenharmony_ci VMCI_DOORBELL_LINK); 2358c2ecf20Sopenharmony_ci link_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 2368c2ecf20Sopenharmony_ci link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE; 2378c2ecf20Sopenharmony_ci link_msg.handle = handle; 2388c2ecf20Sopenharmony_ci link_msg.notify_idx = notify_idx; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return vmci_send_datagram(&link_msg.hdr); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * Unlinks the given doorbell handle from an index in the bitmap in 2458c2ecf20Sopenharmony_ci * the device backend. The notification state is destroyed in hypervisor. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic int dbell_unlink(struct vmci_handle handle) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct vmci_doorbell_unlink_msg unlink_msg; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci unlink_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 2528c2ecf20Sopenharmony_ci VMCI_DOORBELL_UNLINK); 2538c2ecf20Sopenharmony_ci unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 2548c2ecf20Sopenharmony_ci unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE; 2558c2ecf20Sopenharmony_ci unlink_msg.handle = handle; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return vmci_send_datagram(&unlink_msg.hdr); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* 2618c2ecf20Sopenharmony_ci * Notify another guest or the host. We send a datagram down to the 2628c2ecf20Sopenharmony_ci * host via the hypervisor with the notification info. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct vmci_doorbell_notify_msg notify_msg; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 2698c2ecf20Sopenharmony_ci VMCI_DOORBELL_NOTIFY); 2708c2ecf20Sopenharmony_ci notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 2718c2ecf20Sopenharmony_ci notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE; 2728c2ecf20Sopenharmony_ci notify_msg.handle = handle; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return vmci_send_datagram(¬ify_msg.hdr); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Calls the specified callback in a delayed context. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistatic void dbell_delayed_dispatch(struct work_struct *work) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct dbell_entry *entry = container_of(work, 2838c2ecf20Sopenharmony_ci struct dbell_entry, work); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci entry->notify_cb(entry->client_data); 2868c2ecf20Sopenharmony_ci vmci_resource_put(&entry->resource); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * Dispatches a doorbell notification to the host context. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ciint vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct dbell_entry *entry; 2958c2ecf20Sopenharmony_ci struct vmci_resource *resource; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle)) { 2988c2ecf20Sopenharmony_ci pr_devel("Notifying an invalid doorbell (handle=0x%x:0x%x)\n", 2998c2ecf20Sopenharmony_ci handle.context, handle.resource); 3008c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci resource = vmci_resource_by_handle(handle, 3048c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 3058c2ecf20Sopenharmony_ci if (!resource) { 3068c2ecf20Sopenharmony_ci pr_devel("Notifying an unknown doorbell (handle=0x%x:0x%x)\n", 3078c2ecf20Sopenharmony_ci handle.context, handle.resource); 3088c2ecf20Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 3128c2ecf20Sopenharmony_ci if (entry->run_delayed) { 3138c2ecf20Sopenharmony_ci if (!schedule_work(&entry->work)) 3148c2ecf20Sopenharmony_ci vmci_resource_put(resource); 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci entry->notify_cb(entry->client_data); 3178c2ecf20Sopenharmony_ci vmci_resource_put(resource); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * Register the notification bitmap with the host. 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_cibool vmci_dbell_register_notification_bitmap(u64 bitmap_ppn) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int result; 3298c2ecf20Sopenharmony_ci struct vmci_notify_bm_set_msg bitmap_set_msg = { }; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci bitmap_set_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 3328c2ecf20Sopenharmony_ci VMCI_SET_NOTIFY_BITMAP); 3338c2ecf20Sopenharmony_ci bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 3348c2ecf20Sopenharmony_ci bitmap_set_msg.hdr.payload_size = sizeof(bitmap_set_msg) - 3358c2ecf20Sopenharmony_ci VMCI_DG_HEADERSIZE; 3368c2ecf20Sopenharmony_ci if (vmci_use_ppn64()) 3378c2ecf20Sopenharmony_ci bitmap_set_msg.bitmap_ppn64 = bitmap_ppn; 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci bitmap_set_msg.bitmap_ppn32 = (u32) bitmap_ppn; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci result = vmci_send_datagram(&bitmap_set_msg.hdr); 3428c2ecf20Sopenharmony_ci if (result != VMCI_SUCCESS) { 3438c2ecf20Sopenharmony_ci pr_devel("Failed to register (PPN=%llu) as notification bitmap (error=%d)\n", 3448c2ecf20Sopenharmony_ci bitmap_ppn, result); 3458c2ecf20Sopenharmony_ci return false; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci return true; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* 3518c2ecf20Sopenharmony_ci * Executes or schedules the handlers for a given notify index. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic void dbell_fire_entries(u32 notify_idx) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci u32 bucket = VMCI_DOORBELL_HASH(notify_idx); 3568c2ecf20Sopenharmony_ci struct dbell_entry *dbell; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) { 3618c2ecf20Sopenharmony_ci if (dbell->idx == notify_idx && 3628c2ecf20Sopenharmony_ci atomic_read(&dbell->active) == 1) { 3638c2ecf20Sopenharmony_ci if (dbell->run_delayed) { 3648c2ecf20Sopenharmony_ci vmci_resource_get(&dbell->resource); 3658c2ecf20Sopenharmony_ci if (!schedule_work(&dbell->work)) 3668c2ecf20Sopenharmony_ci vmci_resource_put(&dbell->resource); 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci dbell->notify_cb(dbell->client_data); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * Scans the notification bitmap, collects pending notifications, 3788c2ecf20Sopenharmony_ci * resets the bitmap and invokes appropriate callbacks. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_civoid vmci_dbell_scan_notification_entries(u8 *bitmap) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci u32 idx; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (idx = 0; idx < max_notify_idx; idx++) { 3858c2ecf20Sopenharmony_ci if (bitmap[idx] & 0x1) { 3868c2ecf20Sopenharmony_ci bitmap[idx] &= ~1; 3878c2ecf20Sopenharmony_ci dbell_fire_entries(idx); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* 3938c2ecf20Sopenharmony_ci * vmci_doorbell_create() - Creates a doorbell 3948c2ecf20Sopenharmony_ci * @handle: A handle used to track the resource. Can be invalid. 3958c2ecf20Sopenharmony_ci * @flags: Flag that determines context of callback. 3968c2ecf20Sopenharmony_ci * @priv_flags: Privileges flags. 3978c2ecf20Sopenharmony_ci * @notify_cb: The callback to be ivoked when the doorbell fires. 3988c2ecf20Sopenharmony_ci * @client_data: A parameter to be passed to the callback. 3998c2ecf20Sopenharmony_ci * 4008c2ecf20Sopenharmony_ci * Creates a doorbell with the given callback. If the handle is 4018c2ecf20Sopenharmony_ci * VMCI_INVALID_HANDLE, a free handle will be assigned, if 4028c2ecf20Sopenharmony_ci * possible. The callback can be run immediately (potentially with 4038c2ecf20Sopenharmony_ci * locks held - the default) or delayed (in a kernel thread) by 4048c2ecf20Sopenharmony_ci * specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution 4058c2ecf20Sopenharmony_ci * is selected, a given callback may not be run if the kernel is 4068c2ecf20Sopenharmony_ci * unable to allocate memory for the delayed execution (highly 4078c2ecf20Sopenharmony_ci * unlikely). 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ciint vmci_doorbell_create(struct vmci_handle *handle, 4108c2ecf20Sopenharmony_ci u32 flags, 4118c2ecf20Sopenharmony_ci u32 priv_flags, 4128c2ecf20Sopenharmony_ci vmci_callback notify_cb, void *client_data) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct dbell_entry *entry; 4158c2ecf20Sopenharmony_ci struct vmci_handle new_handle; 4168c2ecf20Sopenharmony_ci int result; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB || 4198c2ecf20Sopenharmony_ci priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) 4208c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (entry == NULL) { 4248c2ecf20Sopenharmony_ci pr_warn("Failed allocating memory for datagram entry\n"); 4258c2ecf20Sopenharmony_ci return VMCI_ERROR_NO_MEM; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(*handle)) { 4298c2ecf20Sopenharmony_ci u32 context_id = vmci_get_context_id(); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (context_id == VMCI_INVALID_ID) { 4328c2ecf20Sopenharmony_ci pr_warn("Failed to get context ID\n"); 4338c2ecf20Sopenharmony_ci result = VMCI_ERROR_NO_RESOURCES; 4348c2ecf20Sopenharmony_ci goto free_mem; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Let resource code allocate a free ID for us */ 4388c2ecf20Sopenharmony_ci new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID); 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci bool valid_context = false; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * Validate the handle. We must do both of the checks below 4448c2ecf20Sopenharmony_ci * because we can be acting as both a host and a guest at the 4458c2ecf20Sopenharmony_ci * same time. We always allow the host context ID, since the 4468c2ecf20Sopenharmony_ci * host functionality is in practice always there with the 4478c2ecf20Sopenharmony_ci * unified driver. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci if (handle->context == VMCI_HOST_CONTEXT_ID || 4508c2ecf20Sopenharmony_ci (vmci_guest_code_active() && 4518c2ecf20Sopenharmony_ci vmci_get_context_id() == handle->context)) { 4528c2ecf20Sopenharmony_ci valid_context = true; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!valid_context || handle->resource == VMCI_INVALID_ID) { 4568c2ecf20Sopenharmony_ci pr_devel("Invalid argument (handle=0x%x:0x%x)\n", 4578c2ecf20Sopenharmony_ci handle->context, handle->resource); 4588c2ecf20Sopenharmony_ci result = VMCI_ERROR_INVALID_ARGS; 4598c2ecf20Sopenharmony_ci goto free_mem; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci new_handle = *handle; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci entry->idx = 0; 4668c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&entry->node); 4678c2ecf20Sopenharmony_ci entry->priv_flags = priv_flags; 4688c2ecf20Sopenharmony_ci INIT_WORK(&entry->work, dbell_delayed_dispatch); 4698c2ecf20Sopenharmony_ci entry->run_delayed = flags & VMCI_FLAG_DELAYED_CB; 4708c2ecf20Sopenharmony_ci entry->notify_cb = notify_cb; 4718c2ecf20Sopenharmony_ci entry->client_data = client_data; 4728c2ecf20Sopenharmony_ci atomic_set(&entry->active, 0); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci result = vmci_resource_add(&entry->resource, 4758c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL, 4768c2ecf20Sopenharmony_ci new_handle); 4778c2ecf20Sopenharmony_ci if (result != VMCI_SUCCESS) { 4788c2ecf20Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n", 4798c2ecf20Sopenharmony_ci new_handle.context, new_handle.resource, result); 4808c2ecf20Sopenharmony_ci goto free_mem; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci new_handle = vmci_resource_handle(&entry->resource); 4848c2ecf20Sopenharmony_ci if (vmci_guest_code_active()) { 4858c2ecf20Sopenharmony_ci dbell_index_table_add(entry); 4868c2ecf20Sopenharmony_ci result = dbell_link(new_handle, entry->idx); 4878c2ecf20Sopenharmony_ci if (VMCI_SUCCESS != result) 4888c2ecf20Sopenharmony_ci goto destroy_resource; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci atomic_set(&entry->active, 1); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci *handle = new_handle; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return result; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci destroy_resource: 4988c2ecf20Sopenharmony_ci dbell_index_table_remove(entry); 4998c2ecf20Sopenharmony_ci vmci_resource_remove(&entry->resource); 5008c2ecf20Sopenharmony_ci free_mem: 5018c2ecf20Sopenharmony_ci kfree(entry); 5028c2ecf20Sopenharmony_ci return result; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_create); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * vmci_doorbell_destroy() - Destroy a doorbell. 5088c2ecf20Sopenharmony_ci * @handle: The handle tracking the resource. 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * Destroys a doorbell previously created with vmcii_doorbell_create. This 5118c2ecf20Sopenharmony_ci * operation may block waiting for a callback to finish. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ciint vmci_doorbell_destroy(struct vmci_handle handle) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct dbell_entry *entry; 5168c2ecf20Sopenharmony_ci struct vmci_resource *resource; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 5198c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci resource = vmci_resource_by_handle(handle, 5228c2ecf20Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 5238c2ecf20Sopenharmony_ci if (!resource) { 5248c2ecf20Sopenharmony_ci pr_devel("Failed to destroy doorbell (handle=0x%x:0x%x)\n", 5258c2ecf20Sopenharmony_ci handle.context, handle.resource); 5268c2ecf20Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!hlist_unhashed(&entry->node)) { 5328c2ecf20Sopenharmony_ci int result; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci dbell_index_table_remove(entry); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci result = dbell_unlink(handle); 5378c2ecf20Sopenharmony_ci if (VMCI_SUCCESS != result) { 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* 5408c2ecf20Sopenharmony_ci * The only reason this should fail would be 5418c2ecf20Sopenharmony_ci * an inconsistency between guest and 5428c2ecf20Sopenharmony_ci * hypervisor state, where the guest believes 5438c2ecf20Sopenharmony_ci * it has an active registration whereas the 5448c2ecf20Sopenharmony_ci * hypervisor doesn't. One case where this may 5458c2ecf20Sopenharmony_ci * happen is if a doorbell is unregistered 5468c2ecf20Sopenharmony_ci * following a hibernation at a time where the 5478c2ecf20Sopenharmony_ci * doorbell state hasn't been restored on the 5488c2ecf20Sopenharmony_ci * hypervisor side yet. Since the handle has 5498c2ecf20Sopenharmony_ci * now been removed in the guest, we just 5508c2ecf20Sopenharmony_ci * print a warning and return success. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci pr_devel("Unlink of doorbell (handle=0x%x:0x%x) unknown by hypervisor (error=%d)\n", 5538c2ecf20Sopenharmony_ci handle.context, handle.resource, result); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * Now remove the resource from the table. It might still be in use 5598c2ecf20Sopenharmony_ci * after this, in a callback or still on the delayed work queue. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci vmci_resource_put(&entry->resource); 5628c2ecf20Sopenharmony_ci vmci_resource_remove(&entry->resource); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci kfree(entry); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return VMCI_SUCCESS; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_destroy); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/* 5718c2ecf20Sopenharmony_ci * vmci_doorbell_notify() - Ring the doorbell (and hide in the bushes). 5728c2ecf20Sopenharmony_ci * @dst: The handlle identifying the doorbell resource 5738c2ecf20Sopenharmony_ci * @priv_flags: Priviledge flags. 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * Generates a notification on the doorbell identified by the 5768c2ecf20Sopenharmony_ci * handle. For host side generation of notifications, the caller 5778c2ecf20Sopenharmony_ci * can specify what the privilege of the calling side is. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ciint vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int retval; 5828c2ecf20Sopenharmony_ci enum vmci_route route; 5838c2ecf20Sopenharmony_ci struct vmci_handle src; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (vmci_handle_is_invalid(dst) || 5868c2ecf20Sopenharmony_ci (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)) 5878c2ecf20Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci src = VMCI_INVALID_HANDLE; 5908c2ecf20Sopenharmony_ci retval = vmci_route(&src, &dst, false, &route); 5918c2ecf20Sopenharmony_ci if (retval < VMCI_SUCCESS) 5928c2ecf20Sopenharmony_ci return retval; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (VMCI_ROUTE_AS_HOST == route) 5958c2ecf20Sopenharmony_ci return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID, 5968c2ecf20Sopenharmony_ci dst, priv_flags); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (VMCI_ROUTE_AS_GUEST == route) 5998c2ecf20Sopenharmony_ci return dbell_notify_as_guest(dst, priv_flags); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci pr_warn("Unknown route (%d) for doorbell\n", route); 6028c2ecf20Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_notify); 605