162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VMware VMCI Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/vmw_vmci_defs.h> 962306a36Sopenharmony_ci#include <linux/vmw_vmci_api.h> 1062306a36Sopenharmony_ci#include <linux/completion.h> 1162306a36Sopenharmony_ci#include <linux/hash.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "vmci_datagram.h" 1962306a36Sopenharmony_ci#include "vmci_doorbell.h" 2062306a36Sopenharmony_ci#include "vmci_resource.h" 2162306a36Sopenharmony_ci#include "vmci_driver.h" 2262306a36Sopenharmony_ci#include "vmci_route.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define VMCI_DOORBELL_INDEX_BITS 6 2662306a36Sopenharmony_ci#define VMCI_DOORBELL_INDEX_TABLE_SIZE (1 << VMCI_DOORBELL_INDEX_BITS) 2762306a36Sopenharmony_ci#define VMCI_DOORBELL_HASH(_idx) hash_32(_idx, VMCI_DOORBELL_INDEX_BITS) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * DoorbellEntry describes the a doorbell notification handle allocated by the 3162306a36Sopenharmony_ci * host. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistruct dbell_entry { 3462306a36Sopenharmony_ci struct vmci_resource resource; 3562306a36Sopenharmony_ci struct hlist_node node; 3662306a36Sopenharmony_ci struct work_struct work; 3762306a36Sopenharmony_ci vmci_callback notify_cb; 3862306a36Sopenharmony_ci void *client_data; 3962306a36Sopenharmony_ci u32 idx; 4062306a36Sopenharmony_ci u32 priv_flags; 4162306a36Sopenharmony_ci bool run_delayed; 4262306a36Sopenharmony_ci atomic_t active; /* Only used by guest personality */ 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* The VMCI index table keeps track of currently registered doorbells. */ 4662306a36Sopenharmony_cistruct dbell_index_table { 4762306a36Sopenharmony_ci spinlock_t lock; /* Index table lock */ 4862306a36Sopenharmony_ci struct hlist_head entries[VMCI_DOORBELL_INDEX_TABLE_SIZE]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic struct dbell_index_table vmci_doorbell_it = { 5262306a36Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(vmci_doorbell_it.lock), 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * The max_notify_idx is one larger than the currently known bitmap index in 5762306a36Sopenharmony_ci * use, and is used to determine how much of the bitmap needs to be scanned. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic u32 max_notify_idx; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * The notify_idx_count is used for determining whether there are free entries 6362306a36Sopenharmony_ci * within the bitmap (if notify_idx_count + 1 < max_notify_idx). 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic u32 notify_idx_count; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * The last_notify_idx_reserved is used to track the last index handed out - in 6962306a36Sopenharmony_ci * the case where multiple handles share a notification index, we hand out 7062306a36Sopenharmony_ci * indexes round robin based on last_notify_idx_reserved. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic u32 last_notify_idx_reserved; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* This is a one entry cache used to by the index allocation. */ 7562306a36Sopenharmony_cistatic u32 last_notify_idx_released = PAGE_SIZE; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Utility function that retrieves the privilege flags associated 8062306a36Sopenharmony_ci * with a given doorbell handle. For guest endpoints, the 8162306a36Sopenharmony_ci * privileges are determined by the context ID, but for host 8262306a36Sopenharmony_ci * endpoints privileges are associated with the complete 8362306a36Sopenharmony_ci * handle. Hypervisor endpoints are not yet supported. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ciint vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci if (priv_flags == NULL || handle.context == VMCI_INVALID_ID) 8862306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (handle.context == VMCI_HOST_CONTEXT_ID) { 9162306a36Sopenharmony_ci struct dbell_entry *entry; 9262306a36Sopenharmony_ci struct vmci_resource *resource; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci resource = vmci_resource_by_handle(handle, 9562306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 9662306a36Sopenharmony_ci if (!resource) 9762306a36Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 10062306a36Sopenharmony_ci *priv_flags = entry->priv_flags; 10162306a36Sopenharmony_ci vmci_resource_put(resource); 10262306a36Sopenharmony_ci } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) { 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Hypervisor endpoints for notifications are not 10562306a36Sopenharmony_ci * supported (yet). 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci *priv_flags = vmci_context_get_priv_flags(handle.context); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return VMCI_SUCCESS; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * Find doorbell entry by bitmap index. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic struct dbell_entry *dbell_index_table_find(u32 idx) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci u32 bucket = VMCI_DOORBELL_HASH(idx); 12162306a36Sopenharmony_ci struct dbell_entry *dbell; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], 12462306a36Sopenharmony_ci node) { 12562306a36Sopenharmony_ci if (idx == dbell->idx) 12662306a36Sopenharmony_ci return dbell; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return NULL; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Add the given entry to the index table. This willi take a reference to the 13462306a36Sopenharmony_ci * entry's resource so that the entry is not deleted before it is removed from 13562306a36Sopenharmony_ci * the * table. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic void dbell_index_table_add(struct dbell_entry *entry) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci u32 bucket; 14062306a36Sopenharmony_ci u32 new_notify_idx; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci vmci_resource_get(&entry->resource); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * Below we try to allocate an index in the notification 14862306a36Sopenharmony_ci * bitmap with "not too much" sharing between resources. If we 14962306a36Sopenharmony_ci * use less that the full bitmap, we either add to the end if 15062306a36Sopenharmony_ci * there are no unused flags within the currently used area, 15162306a36Sopenharmony_ci * or we search for unused ones. If we use the full bitmap, we 15262306a36Sopenharmony_ci * allocate the index round robin. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) { 15562306a36Sopenharmony_ci if (last_notify_idx_released < max_notify_idx && 15662306a36Sopenharmony_ci !dbell_index_table_find(last_notify_idx_released)) { 15762306a36Sopenharmony_ci new_notify_idx = last_notify_idx_released; 15862306a36Sopenharmony_ci last_notify_idx_released = PAGE_SIZE; 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci bool reused = false; 16162306a36Sopenharmony_ci new_notify_idx = last_notify_idx_reserved; 16262306a36Sopenharmony_ci if (notify_idx_count + 1 < max_notify_idx) { 16362306a36Sopenharmony_ci do { 16462306a36Sopenharmony_ci if (!dbell_index_table_find 16562306a36Sopenharmony_ci (new_notify_idx)) { 16662306a36Sopenharmony_ci reused = true; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci new_notify_idx = (new_notify_idx + 1) % 17062306a36Sopenharmony_ci max_notify_idx; 17162306a36Sopenharmony_ci } while (new_notify_idx != 17262306a36Sopenharmony_ci last_notify_idx_released); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci if (!reused) { 17562306a36Sopenharmony_ci new_notify_idx = max_notify_idx; 17662306a36Sopenharmony_ci max_notify_idx++; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci last_notify_idx_reserved = new_notify_idx; 18462306a36Sopenharmony_ci notify_idx_count++; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci entry->idx = new_notify_idx; 18762306a36Sopenharmony_ci bucket = VMCI_DOORBELL_HASH(entry->idx); 18862306a36Sopenharmony_ci hlist_add_head(&entry->node, &vmci_doorbell_it.entries[bucket]); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * Remove the given entry from the index table. This will release() the 19562306a36Sopenharmony_ci * entry's resource. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic void dbell_index_table_remove(struct dbell_entry *entry) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci hlist_del_init(&entry->node); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci notify_idx_count--; 20462306a36Sopenharmony_ci if (entry->idx == max_notify_idx - 1) { 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * If we delete an entry with the maximum known 20762306a36Sopenharmony_ci * notification index, we take the opportunity to 20862306a36Sopenharmony_ci * prune the current max. As there might be other 20962306a36Sopenharmony_ci * unused indices immediately below, we lower the 21062306a36Sopenharmony_ci * maximum until we hit an index in use. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci while (max_notify_idx > 0 && 21362306a36Sopenharmony_ci !dbell_index_table_find(max_notify_idx - 1)) 21462306a36Sopenharmony_ci max_notify_idx--; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci last_notify_idx_released = entry->idx; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci vmci_resource_put(&entry->resource); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * Creates a link between the given doorbell handle and the given 22662306a36Sopenharmony_ci * index in the bitmap in the device backend. A notification state 22762306a36Sopenharmony_ci * is created in hypervisor. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic int dbell_link(struct vmci_handle handle, u32 notify_idx) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct vmci_doorbell_link_msg link_msg; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci link_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 23462306a36Sopenharmony_ci VMCI_DOORBELL_LINK); 23562306a36Sopenharmony_ci link_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 23662306a36Sopenharmony_ci link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE; 23762306a36Sopenharmony_ci link_msg.handle = handle; 23862306a36Sopenharmony_ci link_msg.notify_idx = notify_idx; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return vmci_send_datagram(&link_msg.hdr); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * Unlinks the given doorbell handle from an index in the bitmap in 24562306a36Sopenharmony_ci * the device backend. The notification state is destroyed in hypervisor. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic int dbell_unlink(struct vmci_handle handle) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct vmci_doorbell_unlink_msg unlink_msg; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci unlink_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 25262306a36Sopenharmony_ci VMCI_DOORBELL_UNLINK); 25362306a36Sopenharmony_ci unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 25462306a36Sopenharmony_ci unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE; 25562306a36Sopenharmony_ci unlink_msg.handle = handle; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return vmci_send_datagram(&unlink_msg.hdr); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * Notify another guest or the host. We send a datagram down to the 26262306a36Sopenharmony_ci * host via the hypervisor with the notification info. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_cistatic int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct vmci_doorbell_notify_msg notify_msg; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 26962306a36Sopenharmony_ci VMCI_DOORBELL_NOTIFY); 27062306a36Sopenharmony_ci notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 27162306a36Sopenharmony_ci notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE; 27262306a36Sopenharmony_ci notify_msg.handle = handle; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return vmci_send_datagram(¬ify_msg.hdr); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Calls the specified callback in a delayed context. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_cistatic void dbell_delayed_dispatch(struct work_struct *work) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct dbell_entry *entry = container_of(work, 28362306a36Sopenharmony_ci struct dbell_entry, work); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci entry->notify_cb(entry->client_data); 28662306a36Sopenharmony_ci vmci_resource_put(&entry->resource); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * Dispatches a doorbell notification to the host context. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ciint vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct dbell_entry *entry; 29562306a36Sopenharmony_ci struct vmci_resource *resource; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle)) { 29862306a36Sopenharmony_ci pr_devel("Notifying an invalid doorbell (handle=0x%x:0x%x)\n", 29962306a36Sopenharmony_ci handle.context, handle.resource); 30062306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci resource = vmci_resource_by_handle(handle, 30462306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 30562306a36Sopenharmony_ci if (!resource) { 30662306a36Sopenharmony_ci pr_devel("Notifying an unknown doorbell (handle=0x%x:0x%x)\n", 30762306a36Sopenharmony_ci handle.context, handle.resource); 30862306a36Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 31262306a36Sopenharmony_ci if (entry->run_delayed) { 31362306a36Sopenharmony_ci if (!schedule_work(&entry->work)) 31462306a36Sopenharmony_ci vmci_resource_put(resource); 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci entry->notify_cb(entry->client_data); 31762306a36Sopenharmony_ci vmci_resource_put(resource); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return VMCI_SUCCESS; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/* 32462306a36Sopenharmony_ci * Register the notification bitmap with the host. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cibool vmci_dbell_register_notification_bitmap(u64 bitmap_ppn) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int result; 32962306a36Sopenharmony_ci struct vmci_notify_bm_set_msg bitmap_set_msg = { }; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci bitmap_set_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, 33262306a36Sopenharmony_ci VMCI_SET_NOTIFY_BITMAP); 33362306a36Sopenharmony_ci bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE; 33462306a36Sopenharmony_ci bitmap_set_msg.hdr.payload_size = sizeof(bitmap_set_msg) - 33562306a36Sopenharmony_ci VMCI_DG_HEADERSIZE; 33662306a36Sopenharmony_ci if (vmci_use_ppn64()) 33762306a36Sopenharmony_ci bitmap_set_msg.bitmap_ppn64 = bitmap_ppn; 33862306a36Sopenharmony_ci else 33962306a36Sopenharmony_ci bitmap_set_msg.bitmap_ppn32 = (u32) bitmap_ppn; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci result = vmci_send_datagram(&bitmap_set_msg.hdr); 34262306a36Sopenharmony_ci if (result != VMCI_SUCCESS) { 34362306a36Sopenharmony_ci pr_devel("Failed to register (PPN=%llu) as notification bitmap (error=%d)\n", 34462306a36Sopenharmony_ci bitmap_ppn, result); 34562306a36Sopenharmony_ci return false; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return true; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * Executes or schedules the handlers for a given notify index. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic void dbell_fire_entries(u32 notify_idx) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci u32 bucket = VMCI_DOORBELL_HASH(notify_idx); 35662306a36Sopenharmony_ci struct dbell_entry *dbell; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci spin_lock_bh(&vmci_doorbell_it.lock); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) { 36162306a36Sopenharmony_ci if (dbell->idx == notify_idx && 36262306a36Sopenharmony_ci atomic_read(&dbell->active) == 1) { 36362306a36Sopenharmony_ci if (dbell->run_delayed) { 36462306a36Sopenharmony_ci vmci_resource_get(&dbell->resource); 36562306a36Sopenharmony_ci if (!schedule_work(&dbell->work)) 36662306a36Sopenharmony_ci vmci_resource_put(&dbell->resource); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci dbell->notify_cb(dbell->client_data); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci spin_unlock_bh(&vmci_doorbell_it.lock); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* 37762306a36Sopenharmony_ci * Scans the notification bitmap, collects pending notifications, 37862306a36Sopenharmony_ci * resets the bitmap and invokes appropriate callbacks. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_civoid vmci_dbell_scan_notification_entries(u8 *bitmap) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci u32 idx; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (idx = 0; idx < max_notify_idx; idx++) { 38562306a36Sopenharmony_ci if (bitmap[idx] & 0x1) { 38662306a36Sopenharmony_ci bitmap[idx] &= ~1; 38762306a36Sopenharmony_ci dbell_fire_entries(idx); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * vmci_doorbell_create() - Creates a doorbell 39462306a36Sopenharmony_ci * @handle: A handle used to track the resource. Can be invalid. 39562306a36Sopenharmony_ci * @flags: Flag that determines context of callback. 39662306a36Sopenharmony_ci * @priv_flags: Privileges flags. 39762306a36Sopenharmony_ci * @notify_cb: The callback to be ivoked when the doorbell fires. 39862306a36Sopenharmony_ci * @client_data: A parameter to be passed to the callback. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Creates a doorbell with the given callback. If the handle is 40162306a36Sopenharmony_ci * VMCI_INVALID_HANDLE, a free handle will be assigned, if 40262306a36Sopenharmony_ci * possible. The callback can be run immediately (potentially with 40362306a36Sopenharmony_ci * locks held - the default) or delayed (in a kernel thread) by 40462306a36Sopenharmony_ci * specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution 40562306a36Sopenharmony_ci * is selected, a given callback may not be run if the kernel is 40662306a36Sopenharmony_ci * unable to allocate memory for the delayed execution (highly 40762306a36Sopenharmony_ci * unlikely). 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ciint vmci_doorbell_create(struct vmci_handle *handle, 41062306a36Sopenharmony_ci u32 flags, 41162306a36Sopenharmony_ci u32 priv_flags, 41262306a36Sopenharmony_ci vmci_callback notify_cb, void *client_data) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct dbell_entry *entry; 41562306a36Sopenharmony_ci struct vmci_handle new_handle; 41662306a36Sopenharmony_ci int result; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB || 41962306a36Sopenharmony_ci priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) 42062306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 42362306a36Sopenharmony_ci if (entry == NULL) { 42462306a36Sopenharmony_ci pr_warn("Failed allocating memory for datagram entry\n"); 42562306a36Sopenharmony_ci return VMCI_ERROR_NO_MEM; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (vmci_handle_is_invalid(*handle)) { 42962306a36Sopenharmony_ci u32 context_id = vmci_get_context_id(); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (context_id == VMCI_INVALID_ID) { 43262306a36Sopenharmony_ci pr_warn("Failed to get context ID\n"); 43362306a36Sopenharmony_ci result = VMCI_ERROR_NO_RESOURCES; 43462306a36Sopenharmony_ci goto free_mem; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Let resource code allocate a free ID for us */ 43862306a36Sopenharmony_ci new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID); 43962306a36Sopenharmony_ci } else { 44062306a36Sopenharmony_ci bool valid_context = false; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * Validate the handle. We must do both of the checks below 44462306a36Sopenharmony_ci * because we can be acting as both a host and a guest at the 44562306a36Sopenharmony_ci * same time. We always allow the host context ID, since the 44662306a36Sopenharmony_ci * host functionality is in practice always there with the 44762306a36Sopenharmony_ci * unified driver. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (handle->context == VMCI_HOST_CONTEXT_ID || 45062306a36Sopenharmony_ci (vmci_guest_code_active() && 45162306a36Sopenharmony_ci vmci_get_context_id() == handle->context)) { 45262306a36Sopenharmony_ci valid_context = true; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!valid_context || handle->resource == VMCI_INVALID_ID) { 45662306a36Sopenharmony_ci pr_devel("Invalid argument (handle=0x%x:0x%x)\n", 45762306a36Sopenharmony_ci handle->context, handle->resource); 45862306a36Sopenharmony_ci result = VMCI_ERROR_INVALID_ARGS; 45962306a36Sopenharmony_ci goto free_mem; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci new_handle = *handle; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci entry->idx = 0; 46662306a36Sopenharmony_ci INIT_HLIST_NODE(&entry->node); 46762306a36Sopenharmony_ci entry->priv_flags = priv_flags; 46862306a36Sopenharmony_ci INIT_WORK(&entry->work, dbell_delayed_dispatch); 46962306a36Sopenharmony_ci entry->run_delayed = flags & VMCI_FLAG_DELAYED_CB; 47062306a36Sopenharmony_ci entry->notify_cb = notify_cb; 47162306a36Sopenharmony_ci entry->client_data = client_data; 47262306a36Sopenharmony_ci atomic_set(&entry->active, 0); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci result = vmci_resource_add(&entry->resource, 47562306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL, 47662306a36Sopenharmony_ci new_handle); 47762306a36Sopenharmony_ci if (result != VMCI_SUCCESS) { 47862306a36Sopenharmony_ci pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n", 47962306a36Sopenharmony_ci new_handle.context, new_handle.resource, result); 48062306a36Sopenharmony_ci goto free_mem; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci new_handle = vmci_resource_handle(&entry->resource); 48462306a36Sopenharmony_ci if (vmci_guest_code_active()) { 48562306a36Sopenharmony_ci dbell_index_table_add(entry); 48662306a36Sopenharmony_ci result = dbell_link(new_handle, entry->idx); 48762306a36Sopenharmony_ci if (VMCI_SUCCESS != result) 48862306a36Sopenharmony_ci goto destroy_resource; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci atomic_set(&entry->active, 1); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci *handle = new_handle; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return result; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci destroy_resource: 49862306a36Sopenharmony_ci dbell_index_table_remove(entry); 49962306a36Sopenharmony_ci vmci_resource_remove(&entry->resource); 50062306a36Sopenharmony_ci free_mem: 50162306a36Sopenharmony_ci kfree(entry); 50262306a36Sopenharmony_ci return result; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_create); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * vmci_doorbell_destroy() - Destroy a doorbell. 50862306a36Sopenharmony_ci * @handle: The handle tracking the resource. 50962306a36Sopenharmony_ci * 51062306a36Sopenharmony_ci * Destroys a doorbell previously created with vmcii_doorbell_create. This 51162306a36Sopenharmony_ci * operation may block waiting for a callback to finish. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ciint vmci_doorbell_destroy(struct vmci_handle handle) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct dbell_entry *entry; 51662306a36Sopenharmony_ci struct vmci_resource *resource; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (vmci_handle_is_invalid(handle)) 51962306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci resource = vmci_resource_by_handle(handle, 52262306a36Sopenharmony_ci VMCI_RESOURCE_TYPE_DOORBELL); 52362306a36Sopenharmony_ci if (!resource) { 52462306a36Sopenharmony_ci pr_devel("Failed to destroy doorbell (handle=0x%x:0x%x)\n", 52562306a36Sopenharmony_ci handle.context, handle.resource); 52662306a36Sopenharmony_ci return VMCI_ERROR_NOT_FOUND; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci entry = container_of(resource, struct dbell_entry, resource); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!hlist_unhashed(&entry->node)) { 53262306a36Sopenharmony_ci int result; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci dbell_index_table_remove(entry); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci result = dbell_unlink(handle); 53762306a36Sopenharmony_ci if (VMCI_SUCCESS != result) { 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * The only reason this should fail would be 54162306a36Sopenharmony_ci * an inconsistency between guest and 54262306a36Sopenharmony_ci * hypervisor state, where the guest believes 54362306a36Sopenharmony_ci * it has an active registration whereas the 54462306a36Sopenharmony_ci * hypervisor doesn't. One case where this may 54562306a36Sopenharmony_ci * happen is if a doorbell is unregistered 54662306a36Sopenharmony_ci * following a hibernation at a time where the 54762306a36Sopenharmony_ci * doorbell state hasn't been restored on the 54862306a36Sopenharmony_ci * hypervisor side yet. Since the handle has 54962306a36Sopenharmony_ci * now been removed in the guest, we just 55062306a36Sopenharmony_ci * print a warning and return success. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci pr_devel("Unlink of doorbell (handle=0x%x:0x%x) unknown by hypervisor (error=%d)\n", 55362306a36Sopenharmony_ci handle.context, handle.resource, result); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* 55862306a36Sopenharmony_ci * Now remove the resource from the table. It might still be in use 55962306a36Sopenharmony_ci * after this, in a callback or still on the delayed work queue. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci vmci_resource_put(&entry->resource); 56262306a36Sopenharmony_ci vmci_resource_remove(&entry->resource); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci kfree(entry); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return VMCI_SUCCESS; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_destroy); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * vmci_doorbell_notify() - Ring the doorbell (and hide in the bushes). 57262306a36Sopenharmony_ci * @dst: The handlle identifying the doorbell resource 57362306a36Sopenharmony_ci * @priv_flags: Priviledge flags. 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * Generates a notification on the doorbell identified by the 57662306a36Sopenharmony_ci * handle. For host side generation of notifications, the caller 57762306a36Sopenharmony_ci * can specify what the privilege of the calling side is. 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ciint vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci int retval; 58262306a36Sopenharmony_ci enum vmci_route route; 58362306a36Sopenharmony_ci struct vmci_handle src; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (vmci_handle_is_invalid(dst) || 58662306a36Sopenharmony_ci (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)) 58762306a36Sopenharmony_ci return VMCI_ERROR_INVALID_ARGS; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci src = VMCI_INVALID_HANDLE; 59062306a36Sopenharmony_ci retval = vmci_route(&src, &dst, false, &route); 59162306a36Sopenharmony_ci if (retval < VMCI_SUCCESS) 59262306a36Sopenharmony_ci return retval; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (VMCI_ROUTE_AS_HOST == route) 59562306a36Sopenharmony_ci return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID, 59662306a36Sopenharmony_ci dst, priv_flags); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (VMCI_ROUTE_AS_GUEST == route) 59962306a36Sopenharmony_ci return dbell_notify_as_guest(dst, priv_flags); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci pr_warn("Unknown route (%d) for doorbell\n", route); 60262306a36Sopenharmony_ci return VMCI_ERROR_DST_UNREACHABLE; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_doorbell_notify); 605