/* * The max_notify_idx is one larger than the currently known bitmap index in * use, and is used to determine how much of the bitmap needs to be scanned.
*/ static u32 max_notify_idx;
/* * The notify_idx_count is used for determining whether there are free entries * within the bitmap (if notify_idx_count + 1 < max_notify_idx).
*/ static u32 notify_idx_count;
/* * The last_notify_idx_reserved is used to track the last index handed out - in * the case where multiple handles share a notification index, we hand out * indexes round robin based on last_notify_idx_reserved.
*/ static u32 last_notify_idx_reserved;
/* This is a one entry cache used to by the index allocation. */ static u32 last_notify_idx_released = PAGE_SIZE;
/* * Utility function that retrieves the privilege flags associated * with a given doorbell handle. For guest endpoints, the * privileges are determined by the context ID, but for host * endpoints privileges are associated with the complete * handle. Hypervisor endpoints are not yet supported.
*/ int vmci_dbell_get_priv_flags(struct vmci_handle handle, u32 *priv_flags)
{ if (priv_flags == NULL || handle.context == VMCI_INVALID_ID) return VMCI_ERROR_INVALID_ARGS;
/* * Add the given entry to the index table. This willi take a reference to the * entry's resource so that the entry is not deleted before it is removed from * the * table.
*/ staticvoid dbell_index_table_add(struct dbell_entry *entry)
{
u32 bucket;
u32 new_notify_idx;
vmci_resource_get(&entry->resource);
spin_lock_bh(&vmci_doorbell_it.lock);
/* * Below we try to allocate an index in the notification * bitmap with "not too much" sharing between resources. If we * use less that the full bitmap, we either add to the end if * there are no unused flags within the currently used area, * or we search for unused ones. If we use the full bitmap, we * allocate the index round robin.
*/ if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) { if (last_notify_idx_released < max_notify_idx &&
!dbell_index_table_find(last_notify_idx_released)) {
new_notify_idx = last_notify_idx_released;
last_notify_idx_released = PAGE_SIZE;
} else { bool reused = false;
new_notify_idx = last_notify_idx_reserved; if (notify_idx_count + 1 < max_notify_idx) { do { if (!dbell_index_table_find
(new_notify_idx)) {
reused = true; break;
}
new_notify_idx = (new_notify_idx + 1) %
max_notify_idx;
} while (new_notify_idx !=
last_notify_idx_released);
} if (!reused) {
new_notify_idx = max_notify_idx;
max_notify_idx++;
}
}
} else {
new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE;
}
/* * Remove the given entry from the index table. This will release() the * entry's resource.
*/ staticvoid dbell_index_table_remove(struct dbell_entry *entry)
{
spin_lock_bh(&vmci_doorbell_it.lock);
hlist_del_init(&entry->node);
notify_idx_count--; if (entry->idx == max_notify_idx - 1) { /* * If we delete an entry with the maximum known * notification index, we take the opportunity to * prune the current max. As there might be other * unused indices immediately below, we lower the * maximum until we hit an index in use.
*/ while (max_notify_idx > 0 &&
!dbell_index_table_find(max_notify_idx - 1))
max_notify_idx--;
}
last_notify_idx_released = entry->idx;
spin_unlock_bh(&vmci_doorbell_it.lock);
vmci_resource_put(&entry->resource);
}
/* * Creates a link between the given doorbell handle and the given * index in the bitmap in the device backend. A notification state * is created in hypervisor.
*/ staticint dbell_link(struct vmci_handle handle, u32 notify_idx)
{ struct vmci_doorbell_link_msg link_msg;
/* * Unlinks the given doorbell handle from an index in the bitmap in * the device backend. The notification state is destroyed in hypervisor.
*/ staticint dbell_unlink(struct vmci_handle handle)
{ struct vmci_doorbell_unlink_msg unlink_msg;
result = vmci_send_datagram(&bitmap_set_msg.hdr); if (result != VMCI_SUCCESS) {
pr_devel("Failed to register (PPN=%llu) as notification bitmap (error=%d)\n",
bitmap_ppn, result); returnfalse;
} returntrue;
}
/* * Executes or schedules the handlers for a given notify index.
*/ staticvoid dbell_fire_entries(u32 notify_idx)
{
u32 bucket = VMCI_DOORBELL_HASH(notify_idx); struct dbell_entry *dbell;
spin_lock_bh(&vmci_doorbell_it.lock);
hlist_for_each_entry(dbell, &vmci_doorbell_it.entries[bucket], node) { if (dbell->idx == notify_idx &&
atomic_read(&dbell->active) == 1) { if (dbell->run_delayed) {
vmci_resource_get(&dbell->resource); if (!schedule_work(&dbell->work))
vmci_resource_put(&dbell->resource);
} else {
dbell->notify_cb(dbell->client_data);
}
}
}
spin_unlock_bh(&vmci_doorbell_it.lock);
}
/* * Scans the notification bitmap, collects pending notifications, * resets the bitmap and invokes appropriate callbacks.
*/ void vmci_dbell_scan_notification_entries(u8 *bitmap)
{
u32 idx;
/* * vmci_doorbell_create() - Creates a doorbell * @handle: A handle used to track the resource. Can be invalid. * @flags: Flag that determines context of callback. * @priv_flags: Privileges flags. * @notify_cb: The callback to be ivoked when the doorbell fires. * @client_data: A parameter to be passed to the callback. * * Creates a doorbell with the given callback. If the handle is * VMCI_INVALID_HANDLE, a free handle will be assigned, if * possible. The callback can be run immediately (potentially with * locks held - the default) or delayed (in a kernel thread) by * specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution * is selected, a given callback may not be run if the kernel is * unable to allocate memory for the delayed execution (highly * unlikely).
*/ int vmci_doorbell_create(struct vmci_handle *handle,
u32 flags,
u32 priv_flags,
vmci_callback notify_cb, void *client_data)
{ struct dbell_entry *entry; struct vmci_handle new_handle; int result;
entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (entry == NULL) {
pr_warn("Failed allocating memory for datagram entry\n"); return VMCI_ERROR_NO_MEM;
}
if (vmci_handle_is_invalid(*handle)) {
u32 context_id = vmci_get_context_id();
if (context_id == VMCI_INVALID_ID) {
pr_warn("Failed to get context ID\n");
result = VMCI_ERROR_NO_RESOURCES; goto free_mem;
}
/* Let resource code allocate a free ID for us */
new_handle = vmci_make_handle(context_id, VMCI_INVALID_ID);
} else { bool valid_context = false;
/* * Validate the handle. We must do both of the checks below * because we can be acting as both a host and a guest at the * same time. We always allow the host context ID, since the * host functionality is in practice always there with the * unified driver.
*/ if (handle->context == VMCI_HOST_CONTEXT_ID ||
(vmci_guest_code_active() &&
vmci_get_context_id() == handle->context)) {
valid_context = true;
}
if (!valid_context || handle->resource == VMCI_INVALID_ID) {
pr_devel("Invalid argument (handle=0x%x:0x%x)\n",
handle->context, handle->resource);
result = VMCI_ERROR_INVALID_ARGS; goto free_mem;
}
/* * vmci_doorbell_destroy() - Destroy a doorbell. * @handle: The handle tracking the resource. * * Destroys a doorbell previously created with vmcii_doorbell_create. This * operation may block waiting for a callback to finish.
*/ int vmci_doorbell_destroy(struct vmci_handle handle)
{ struct dbell_entry *entry; struct vmci_resource *resource;
if (vmci_handle_is_invalid(handle)) return VMCI_ERROR_INVALID_ARGS;
resource = vmci_resource_by_handle(handle,
VMCI_RESOURCE_TYPE_DOORBELL); if (!resource) {
pr_devel("Failed to destroy doorbell (handle=0x%x:0x%x)\n",
handle.context, handle.resource); return VMCI_ERROR_NOT_FOUND;
}
result = dbell_unlink(handle); if (VMCI_SUCCESS != result) {
/* * The only reason this should fail would be * an inconsistency between guest and * hypervisor state, where the guest believes * it has an active registration whereas the * hypervisor doesn't. One case where this may * happen is if a doorbell is unregistered * following a hibernation at a time where the * doorbell state hasn't been restored on the * hypervisor side yet. Since the handle has * now been removed in the guest, we just * print a warning and return success.
*/
pr_devel("Unlink of doorbell (handle=0x%x:0x%x) unknown by hypervisor (error=%d)\n",
handle.context, handle.resource, result);
}
}
/* * Now remove the resource from the table. It might still be in use * after this, in a callback or still on the delayed work queue.
*/
vmci_resource_put(&entry->resource);
vmci_resource_remove(&entry->resource);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.