xref: /freebsd/sys/dev/vmware/vmci/vmci_doorbell.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
163a93856SMark Peek /*-
2*3eeb7511SMark Peek  * Copyright (c) 2018 VMware, Inc.
363a93856SMark Peek  *
48c302b2eSMark Peek  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
563a93856SMark Peek  */
663a93856SMark Peek 
763a93856SMark Peek /* This file implements the VMCI doorbell API. */
863a93856SMark Peek 
963a93856SMark Peek #include <sys/types.h>
1063a93856SMark Peek 
1163a93856SMark Peek #include "vmci_doorbell.h"
1263a93856SMark Peek #include "vmci_driver.h"
1363a93856SMark Peek #include "vmci_kernel_api.h"
1463a93856SMark Peek #include "vmci_kernel_defs.h"
1563a93856SMark Peek #include "vmci_resource.h"
1663a93856SMark Peek #include "vmci_utils.h"
1763a93856SMark Peek 
1863a93856SMark Peek #define LGPFX				"vmci_doorbell: "
1963a93856SMark Peek 
2063a93856SMark Peek #define VMCI_DOORBELL_INDEX_TABLE_SIZE	64
2163a93856SMark Peek #define VMCI_DOORBELL_HASH(_idx)					\
2263a93856SMark Peek 	vmci_hash_id((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE)
2363a93856SMark Peek 
2463a93856SMark Peek /* Describes a doorbell notification handle allocated by the host. */
2563a93856SMark Peek struct vmci_doorbell_entry {
2663a93856SMark Peek 	struct vmci_resource			resource;
2763a93856SMark Peek 	uint32_t				idx;
2863a93856SMark Peek 	vmci_list_item(vmci_doorbell_entry)	idx_list_item;
2963a93856SMark Peek 	vmci_privilege_flags			priv_flags;
3063a93856SMark Peek 	bool					is_doorbell;
3163a93856SMark Peek 	bool					run_delayed;
3263a93856SMark Peek 	vmci_callback				notify_cb;
3363a93856SMark Peek 	void					*client_data;
3463a93856SMark Peek 	vmci_event				destroy_event;
3563a93856SMark Peek 	volatile int				active;
3663a93856SMark Peek };
3763a93856SMark Peek 
3863a93856SMark Peek struct vmci_doorbell_index_table {
3963a93856SMark Peek 	vmci_lock			lock;
4063a93856SMark Peek 	vmci_list(vmci_doorbell_entry)	entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
4163a93856SMark Peek };
4263a93856SMark Peek 
4363a93856SMark Peek /* The VMCI index table keeps track of currently registered doorbells. */
4463a93856SMark Peek static struct vmci_doorbell_index_table vmci_doorbell_it;
4563a93856SMark Peek 
4663a93856SMark Peek /*
4763a93856SMark Peek  * The max_notify_idx is one larger than the currently known bitmap index in
4863a93856SMark Peek  * use, and is used to determine how much of the bitmap needs to be scanned.
4963a93856SMark Peek  */
5063a93856SMark Peek static uint32_t	max_notify_idx;
5163a93856SMark Peek 
5263a93856SMark Peek /*
5363a93856SMark Peek  * The notify_idx_count is used for determining whether there are free entries
5463a93856SMark Peek  * within the bitmap (if notify_idx_count + 1 < max_notify_idx).
5563a93856SMark Peek  */
5663a93856SMark Peek static uint32_t notify_idx_count;
5763a93856SMark Peek 
5863a93856SMark Peek /*
5963a93856SMark Peek  * The last_notify_idx_reserved is used to track the last index handed out - in
6063a93856SMark Peek  * the case where multiple handles share a notification index, we hand out
6163a93856SMark Peek  * indexes round robin based on last_notify_idx_reserved.
6263a93856SMark Peek  */
6363a93856SMark Peek static uint32_t last_notify_idx_reserved;
6463a93856SMark Peek 
6563a93856SMark Peek /* This is a one entry cache used to by the index allocation. */
6663a93856SMark Peek static uint32_t last_notify_idx_released = PAGE_SIZE;
6763a93856SMark Peek 
6863a93856SMark Peek static void	vmci_doorbell_free_cb(void *client_data);
6963a93856SMark Peek static int	vmci_doorbell_release_cb(void *client_data);
7063a93856SMark Peek static void	vmci_doorbell_delayed_dispatch_cb(void *data);
7163a93856SMark Peek 
7263a93856SMark Peek /*
7363a93856SMark Peek  *------------------------------------------------------------------------------
7463a93856SMark Peek  *
7563a93856SMark Peek  * vmci_doorbell_init --
7663a93856SMark Peek  *
7763a93856SMark Peek  *     General init code.
7863a93856SMark Peek  *
7963a93856SMark Peek  * Result:
8063a93856SMark Peek  *     VMCI_SUCCESS on success, lock allocation error otherwise.
8163a93856SMark Peek  *
8263a93856SMark Peek  * Side effects:
8363a93856SMark Peek  *     None.
8463a93856SMark Peek  *
8563a93856SMark Peek  *------------------------------------------------------------------------------
8663a93856SMark Peek  */
8763a93856SMark Peek 
8863a93856SMark Peek int
vmci_doorbell_init(void)8963a93856SMark Peek vmci_doorbell_init(void)
9063a93856SMark Peek {
9163a93856SMark Peek 	uint32_t bucket;
9263a93856SMark Peek 
9363a93856SMark Peek 	for (bucket = 0; bucket < ARRAYSIZE(vmci_doorbell_it.entries);
9463a93856SMark Peek 	    ++bucket)
9563a93856SMark Peek 		vmci_list_init(&vmci_doorbell_it.entries[bucket]);
9663a93856SMark Peek 
9763a93856SMark Peek 	return (vmci_init_lock(&vmci_doorbell_it.lock,
9863a93856SMark Peek 	    "VMCI Doorbell index table lock"));
9963a93856SMark Peek }
10063a93856SMark Peek 
10163a93856SMark Peek /*
10263a93856SMark Peek  *------------------------------------------------------------------------------
10363a93856SMark Peek  *
10463a93856SMark Peek  * vmci_doorbell_exit --
10563a93856SMark Peek  *
10663a93856SMark Peek  *     General exit code.
10763a93856SMark Peek  *
10863a93856SMark Peek  * Result:
10963a93856SMark Peek  *     None.
11063a93856SMark Peek  *
11163a93856SMark Peek  * Side effects:
11263a93856SMark Peek  *     None.
11363a93856SMark Peek  *
11463a93856SMark Peek  *------------------------------------------------------------------------------
11563a93856SMark Peek  */
11663a93856SMark Peek 
11763a93856SMark Peek void
vmci_doorbell_exit(void)11863a93856SMark Peek vmci_doorbell_exit(void)
11963a93856SMark Peek {
12063a93856SMark Peek 
12163a93856SMark Peek 	vmci_cleanup_lock(&vmci_doorbell_it.lock);
12263a93856SMark Peek }
12363a93856SMark Peek 
12463a93856SMark Peek /*
12563a93856SMark Peek  *------------------------------------------------------------------------------
12663a93856SMark Peek  *
12763a93856SMark Peek  * vmci_doorbell_free_cb --
12863a93856SMark Peek  *
12963a93856SMark Peek  *     Callback to free doorbell entry structure when resource is no longer used,
13063a93856SMark Peek  *     i.e. the reference count reached 0.  The entry is freed in
13163a93856SMark Peek  *     vmci_doorbell_destroy(), which is waiting on the signal that gets fired
13263a93856SMark Peek  *     here.
13363a93856SMark Peek  *
13463a93856SMark Peek  * Result:
13563a93856SMark Peek  *     None.
13663a93856SMark Peek  *
13763a93856SMark Peek  * Side effects:
13863a93856SMark Peek  *     Signals VMCI event.
13963a93856SMark Peek  *
14063a93856SMark Peek  *------------------------------------------------------------------------------
14163a93856SMark Peek  */
14263a93856SMark Peek 
14363a93856SMark Peek static void
vmci_doorbell_free_cb(void * client_data)14463a93856SMark Peek vmci_doorbell_free_cb(void *client_data)
14563a93856SMark Peek {
14663a93856SMark Peek 	struct vmci_doorbell_entry *entry;
14763a93856SMark Peek 
14863a93856SMark Peek 	entry = (struct vmci_doorbell_entry *)client_data;
14963a93856SMark Peek 	ASSERT(entry);
15063a93856SMark Peek 	vmci_signal_event(&entry->destroy_event);
15163a93856SMark Peek }
15263a93856SMark Peek 
15363a93856SMark Peek /*
15463a93856SMark Peek  *------------------------------------------------------------------------------
15563a93856SMark Peek  *
15663a93856SMark Peek  * vmci_doorbell_release_cb --
15763a93856SMark Peek  *
15863a93856SMark Peek  *     Callback to release the resource reference. It is called by the
15963a93856SMark Peek  *     vmci_wait_on_event function before it blocks.
16063a93856SMark Peek  *
16163a93856SMark Peek  * Result:
16263a93856SMark Peek  *     Always 0.
16363a93856SMark Peek  *
16463a93856SMark Peek  * Side effects:
16563a93856SMark Peek  *     None.
16663a93856SMark Peek  *
16763a93856SMark Peek  *------------------------------------------------------------------------------
16863a93856SMark Peek  */
16963a93856SMark Peek 
17063a93856SMark Peek static int
vmci_doorbell_release_cb(void * client_data)17163a93856SMark Peek vmci_doorbell_release_cb(void *client_data)
17263a93856SMark Peek {
17363a93856SMark Peek 	struct vmci_doorbell_entry *entry;
17463a93856SMark Peek 
17563a93856SMark Peek 	entry  = (struct vmci_doorbell_entry *)client_data;
17663a93856SMark Peek 	ASSERT(entry);
17763a93856SMark Peek 	vmci_resource_release(&entry->resource);
17863a93856SMark Peek 	return (0);
17963a93856SMark Peek }
18063a93856SMark Peek 
18163a93856SMark Peek /*
18263a93856SMark Peek  *------------------------------------------------------------------------------
18363a93856SMark Peek  *
18463a93856SMark Peek  * vmci_doorbell_get_priv_flags --
18563a93856SMark Peek  *
18663a93856SMark Peek  *     Utility function that retrieves the privilege flags associated with a
18763a93856SMark Peek  *     given doorbell handle. For guest endpoints, the privileges are determined
18863a93856SMark Peek  *     by the context ID, but for host endpoints privileges are associated with
18963a93856SMark Peek  *     the complete handle. Hypervisor endpoints are not yet supported.
19063a93856SMark Peek  *
19163a93856SMark Peek  * Result:
19263a93856SMark Peek  *     VMCI_SUCCESS on success,
19363a93856SMark Peek  *     VMCI_ERROR_NOT_FOUND if handle isn't found,
19463a93856SMark Peek  *     VMCI_ERROR_INVALID_ARGS if handle is invalid.
19563a93856SMark Peek  *
19663a93856SMark Peek  * Side effects:
19763a93856SMark Peek  *     None.
19863a93856SMark Peek  *
19963a93856SMark Peek  *------------------------------------------------------------------------------
20063a93856SMark Peek  */
20163a93856SMark Peek 
20263a93856SMark Peek int
vmci_doorbell_get_priv_flags(struct vmci_handle handle,vmci_privilege_flags * priv_flags)20363a93856SMark Peek vmci_doorbell_get_priv_flags(struct vmci_handle handle,
20463a93856SMark Peek     vmci_privilege_flags *priv_flags)
20563a93856SMark Peek {
20663a93856SMark Peek 
20763a93856SMark Peek 	if (priv_flags == NULL || handle.context == VMCI_INVALID_ID)
20863a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
20963a93856SMark Peek 
21063a93856SMark Peek 	if (handle.context == VMCI_HOST_CONTEXT_ID) {
21163a93856SMark Peek 		struct vmci_doorbell_entry *entry;
21263a93856SMark Peek 		struct vmci_resource *resource;
21363a93856SMark Peek 
21463a93856SMark Peek 		resource = vmci_resource_get(handle,
21563a93856SMark Peek 		    VMCI_RESOURCE_TYPE_DOORBELL);
21663a93856SMark Peek 		if (resource == NULL)
21763a93856SMark Peek 			return (VMCI_ERROR_NOT_FOUND);
21863a93856SMark Peek 		entry = RESOURCE_CONTAINER(
21963a93856SMark Peek 		    resource, struct vmci_doorbell_entry, resource);
22063a93856SMark Peek 		*priv_flags = entry->priv_flags;
22163a93856SMark Peek 		vmci_resource_release(resource);
22263a93856SMark Peek 	} else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
22363a93856SMark Peek 		/* Hypervisor endpoints for notifications are not supported. */
22463a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
22563a93856SMark Peek 	} else
22663a93856SMark Peek 		*priv_flags = VMCI_NO_PRIVILEGE_FLAGS;
22763a93856SMark Peek 
22863a93856SMark Peek 	return (VMCI_SUCCESS);
22963a93856SMark Peek }
23063a93856SMark Peek 
23163a93856SMark Peek /*
23263a93856SMark Peek  *------------------------------------------------------------------------------
23363a93856SMark Peek  *
23463a93856SMark Peek  * vmci_doorbell_index_table_find --
23563a93856SMark Peek  *
23663a93856SMark Peek  *     Find doorbell entry by bitmap index.
23763a93856SMark Peek  *
23863a93856SMark Peek  * Results:
23963a93856SMark Peek  *     Entry if found, NULL if not.
24063a93856SMark Peek  *
24163a93856SMark Peek  * Side effects:
24263a93856SMark Peek  *     None.
24363a93856SMark Peek  *
24463a93856SMark Peek  *------------------------------------------------------------------------------
24563a93856SMark Peek  */
24663a93856SMark Peek 
24763a93856SMark Peek static struct vmci_doorbell_entry *
vmci_doorbell_index_table_find(uint32_t idx)24863a93856SMark Peek vmci_doorbell_index_table_find(uint32_t idx)
24963a93856SMark Peek {
25063a93856SMark Peek 	struct vmci_doorbell_entry *iter;
25163a93856SMark Peek 	uint32_t bucket;
25263a93856SMark Peek 
25363a93856SMark Peek 	bucket = VMCI_DOORBELL_HASH(idx);
25463a93856SMark Peek 
25563a93856SMark Peek 	vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) {
25663a93856SMark Peek 		if (idx == iter->idx)
25763a93856SMark Peek 			return (iter);
25863a93856SMark Peek 	}
25963a93856SMark Peek 
26063a93856SMark Peek 	return (NULL);
26163a93856SMark Peek }
26263a93856SMark Peek 
26363a93856SMark Peek /*
26463a93856SMark Peek  *------------------------------------------------------------------------------
26563a93856SMark Peek  *
26663a93856SMark Peek  * vmci_doorbell_index_table_add --
26763a93856SMark Peek  *
26863a93856SMark Peek  *     Add the given entry to the index table. This will hold() the entry's
26963a93856SMark Peek  *     resource so that the entry is not deleted before it is removed from the
27063a93856SMark Peek  *     table.
27163a93856SMark Peek  *
27263a93856SMark Peek  * Results:
27363a93856SMark Peek  *     None.
27463a93856SMark Peek  *
27563a93856SMark Peek  * Side effects:
27663a93856SMark Peek  *     None.
27763a93856SMark Peek  *
27863a93856SMark Peek  *------------------------------------------------------------------------------
27963a93856SMark Peek  */
28063a93856SMark Peek 
28163a93856SMark Peek static void
vmci_doorbell_index_table_add(struct vmci_doorbell_entry * entry)28263a93856SMark Peek vmci_doorbell_index_table_add(struct vmci_doorbell_entry *entry)
28363a93856SMark Peek {
28463a93856SMark Peek 	uint32_t bucket;
28563a93856SMark Peek 	uint32_t new_notify_idx;
28663a93856SMark Peek 
28763a93856SMark Peek 	ASSERT(entry);
28863a93856SMark Peek 
28963a93856SMark Peek 	vmci_resource_hold(&entry->resource);
29063a93856SMark Peek 
29163a93856SMark Peek 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
29263a93856SMark Peek 
29363a93856SMark Peek 	/*
29463a93856SMark Peek 	 * Below we try to allocate an index in the notification bitmap with
29563a93856SMark Peek 	 * "not too much" sharing between resources. If we use less that the
29663a93856SMark Peek 	 * full bitmap, we either add to the end if there are no unused flags
29763a93856SMark Peek 	 * within the currently used area, or we search for unused ones. If we
29863a93856SMark Peek 	 * use the full bitmap, we allocate the index round robin.
29963a93856SMark Peek 	 */
30063a93856SMark Peek 
30163a93856SMark Peek 	if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) {
30263a93856SMark Peek 		if (last_notify_idx_released < max_notify_idx &&
30363a93856SMark Peek 		    !vmci_doorbell_index_table_find(last_notify_idx_released)) {
30463a93856SMark Peek 			new_notify_idx = last_notify_idx_released;
30563a93856SMark Peek 			last_notify_idx_released = PAGE_SIZE;
30663a93856SMark Peek 		} else {
30763a93856SMark Peek 			bool reused = false;
30863a93856SMark Peek 			new_notify_idx = last_notify_idx_reserved;
30963a93856SMark Peek 			if (notify_idx_count + 1 < max_notify_idx) {
31063a93856SMark Peek 				do {
31163a93856SMark Peek 					if (!vmci_doorbell_index_table_find(
31263a93856SMark Peek 					    new_notify_idx)) {
31363a93856SMark Peek 						reused = true;
31463a93856SMark Peek 						break;
31563a93856SMark Peek 					}
31663a93856SMark Peek 					new_notify_idx = (new_notify_idx + 1) %
31763a93856SMark Peek 					    max_notify_idx;
31863a93856SMark Peek 				} while (new_notify_idx !=
31963a93856SMark Peek 				    last_notify_idx_released);
32063a93856SMark Peek 			}
32163a93856SMark Peek 			if (!reused) {
32263a93856SMark Peek 				new_notify_idx = max_notify_idx;
32363a93856SMark Peek 				max_notify_idx++;
32463a93856SMark Peek 			}
32563a93856SMark Peek 		}
32663a93856SMark Peek 	} else {
32763a93856SMark Peek 		new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE;
32863a93856SMark Peek 	}
32963a93856SMark Peek 	last_notify_idx_reserved = new_notify_idx;
33063a93856SMark Peek 	notify_idx_count++;
33163a93856SMark Peek 
33263a93856SMark Peek 	entry->idx = new_notify_idx;
33363a93856SMark Peek 	bucket = VMCI_DOORBELL_HASH(entry->idx);
33463a93856SMark Peek 	vmci_list_insert(&vmci_doorbell_it.entries[bucket], entry,
33563a93856SMark Peek 	    idx_list_item);
33663a93856SMark Peek 
33763a93856SMark Peek 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
33863a93856SMark Peek }
33963a93856SMark Peek 
34063a93856SMark Peek /*
34163a93856SMark Peek  *------------------------------------------------------------------------------
34263a93856SMark Peek  *
34363a93856SMark Peek  * vmci_doorbell_index_table_remove --
34463a93856SMark Peek  *
34563a93856SMark Peek  *     Remove the given entry from the index table. This will release() the
34663a93856SMark Peek  *     entry's resource.
34763a93856SMark Peek  *
34863a93856SMark Peek  * Results:
34963a93856SMark Peek  *     None.
35063a93856SMark Peek  *
35163a93856SMark Peek  * Side effects:
35263a93856SMark Peek  *     None.
35363a93856SMark Peek  *
35463a93856SMark Peek  *------------------------------------------------------------------------------
35563a93856SMark Peek  */
35663a93856SMark Peek 
35763a93856SMark Peek static void
vmci_doorbell_index_table_remove(struct vmci_doorbell_entry * entry)35863a93856SMark Peek vmci_doorbell_index_table_remove(struct vmci_doorbell_entry *entry)
35963a93856SMark Peek {
36063a93856SMark Peek 	ASSERT(entry);
36163a93856SMark Peek 
36263a93856SMark Peek 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
36363a93856SMark Peek 
36463a93856SMark Peek 	vmci_list_remove(entry, idx_list_item);
36563a93856SMark Peek 
36663a93856SMark Peek 	notify_idx_count--;
36763a93856SMark Peek 	if (entry->idx == max_notify_idx - 1) {
36863a93856SMark Peek 		/*
36963a93856SMark Peek 		 * If we delete an entry with the maximum known notification
37063a93856SMark Peek 		 * index, we take the opportunity to prune the current max. As
37163a93856SMark Peek 		 * there might be other unused indices immediately below, we
37263a93856SMark Peek 		 * lower the maximum until we hit an index in use
37363a93856SMark Peek 		 */
37463a93856SMark Peek 
37563a93856SMark Peek 		while (max_notify_idx > 0 &&
37663a93856SMark Peek 		    !vmci_doorbell_index_table_find(max_notify_idx - 1))
37763a93856SMark Peek 			max_notify_idx--;
37863a93856SMark Peek 	}
37963a93856SMark Peek 	last_notify_idx_released = entry->idx;
38063a93856SMark Peek 
38163a93856SMark Peek 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
38263a93856SMark Peek 
38363a93856SMark Peek 	vmci_resource_release(&entry->resource);
38463a93856SMark Peek }
38563a93856SMark Peek 
38663a93856SMark Peek /*
38763a93856SMark Peek  *------------------------------------------------------------------------------
38863a93856SMark Peek  *
38963a93856SMark Peek  * vmci_doorbell_link --
39063a93856SMark Peek  *
39163a93856SMark Peek  *     Creates a link between the given doorbell handle and the given index in
39263a93856SMark Peek  *     the bitmap in the device backend.
39363a93856SMark Peek  *
39463a93856SMark Peek  * Results:
39563a93856SMark Peek  *     VMCI_SUCCESS if success, appropriate error code otherwise.
39663a93856SMark Peek  *
39763a93856SMark Peek  * Side effects:
39863a93856SMark Peek  *     Notification state is created in hypervisor.
39963a93856SMark Peek  *
40063a93856SMark Peek  *------------------------------------------------------------------------------
40163a93856SMark Peek  */
40263a93856SMark Peek 
40363a93856SMark Peek static int
vmci_doorbell_link(struct vmci_handle handle,bool is_doorbell,uint32_t notify_idx)40463a93856SMark Peek vmci_doorbell_link(struct vmci_handle handle, bool is_doorbell,
40563a93856SMark Peek     uint32_t notify_idx)
40663a93856SMark Peek {
40763a93856SMark Peek 	struct vmci_doorbell_link_msg link_msg;
40863a93856SMark Peek 	vmci_id resource_id;
40963a93856SMark Peek 
41063a93856SMark Peek 	ASSERT(!VMCI_HANDLE_INVALID(handle));
41163a93856SMark Peek 
41263a93856SMark Peek 	if (is_doorbell)
41363a93856SMark Peek 		resource_id = VMCI_DOORBELL_LINK;
41463a93856SMark Peek 	else {
41563a93856SMark Peek 		ASSERT(false);
41663a93856SMark Peek 		return (VMCI_ERROR_UNAVAILABLE);
41763a93856SMark Peek 	}
41863a93856SMark Peek 
41963a93856SMark Peek 	link_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
42063a93856SMark Peek 	    resource_id);
42163a93856SMark Peek 	link_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
42263a93856SMark Peek 	link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE;
42363a93856SMark Peek 	link_msg.handle = handle;
42463a93856SMark Peek 	link_msg.notify_idx = notify_idx;
42563a93856SMark Peek 
42663a93856SMark Peek 	return (vmci_send_datagram((struct vmci_datagram *)&link_msg));
42763a93856SMark Peek }
42863a93856SMark Peek 
42963a93856SMark Peek /*
43063a93856SMark Peek  *------------------------------------------------------------------------------
43163a93856SMark Peek  *
43263a93856SMark Peek  * vmci_doorbell_unlink --
43363a93856SMark Peek  *
43463a93856SMark Peek  *     Unlinks the given doorbell handle from an index in the bitmap in the
43563a93856SMark Peek  *     device backend.
43663a93856SMark Peek  *
43763a93856SMark Peek  * Results:
43863a93856SMark Peek  *     VMCI_SUCCESS if success, appropriate error code otherwise.
43963a93856SMark Peek  *
44063a93856SMark Peek  * Side effects:
44163a93856SMark Peek  *     Notification state is destroyed in hypervisor.
44263a93856SMark Peek  *
44363a93856SMark Peek  *------------------------------------------------------------------------------
44463a93856SMark Peek  */
44563a93856SMark Peek 
44663a93856SMark Peek static int
vmci_doorbell_unlink(struct vmci_handle handle,bool is_doorbell)44763a93856SMark Peek vmci_doorbell_unlink(struct vmci_handle handle, bool is_doorbell)
44863a93856SMark Peek {
44963a93856SMark Peek 	struct vmci_doorbell_unlink_msg unlink_msg;
45063a93856SMark Peek 	vmci_id resource_id;
45163a93856SMark Peek 
45263a93856SMark Peek 	ASSERT(!VMCI_HANDLE_INVALID(handle));
45363a93856SMark Peek 
45463a93856SMark Peek 	if (is_doorbell)
45563a93856SMark Peek 		resource_id = VMCI_DOORBELL_UNLINK;
45663a93856SMark Peek 	else {
45763a93856SMark Peek 		ASSERT(false);
45863a93856SMark Peek 		return (VMCI_ERROR_UNAVAILABLE);
45963a93856SMark Peek 	}
46063a93856SMark Peek 
46163a93856SMark Peek 	unlink_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
46263a93856SMark Peek 	    resource_id);
46363a93856SMark Peek 	unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
46463a93856SMark Peek 	unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE;
46563a93856SMark Peek 	unlink_msg.handle = handle;
46663a93856SMark Peek 
46763a93856SMark Peek 	return (vmci_send_datagram((struct vmci_datagram *)&unlink_msg));
46863a93856SMark Peek }
46963a93856SMark Peek 
47063a93856SMark Peek /*
47163a93856SMark Peek  *------------------------------------------------------------------------------
47263a93856SMark Peek  *
47363a93856SMark Peek  * vmci_doorbell_create --
47463a93856SMark Peek  *
47563a93856SMark Peek  *     Creates a doorbell with the given callback. If the handle is
47663a93856SMark Peek  *     VMCI_INVALID_HANDLE, a free handle will be assigned, if possible. The
47763a93856SMark Peek  *     callback can be run immediately (potentially with locks held - the
47863a93856SMark Peek  *     default) or delayed (in a kernel thread) by specifying the flag
47963a93856SMark Peek  *     VMCI_FLAG_DELAYED_CB. If delayed execution is selected, a given callback
48063a93856SMark Peek  *     may not be run if the kernel is unable to allocate memory for the delayed
48163a93856SMark Peek  *     execution (highly unlikely).
48263a93856SMark Peek  *
48363a93856SMark Peek  * Results:
48463a93856SMark Peek  *     VMCI_SUCCESS on success, appropriate error code otherwise.
48563a93856SMark Peek  *
48663a93856SMark Peek  * Side effects:
48763a93856SMark Peek  *     None.
48863a93856SMark Peek  *
48963a93856SMark Peek  *------------------------------------------------------------------------------
49063a93856SMark Peek  */
49163a93856SMark Peek 
49263a93856SMark Peek int
vmci_doorbell_create(struct vmci_handle * handle,uint32_t flags,vmci_privilege_flags priv_flags,vmci_callback notify_cb,void * client_data)49363a93856SMark Peek vmci_doorbell_create(struct vmci_handle *handle, uint32_t flags,
49463a93856SMark Peek     vmci_privilege_flags priv_flags, vmci_callback notify_cb, void *client_data)
49563a93856SMark Peek {
49663a93856SMark Peek 	struct vmci_doorbell_entry *entry;
49763a93856SMark Peek 	struct vmci_handle new_handle;
49863a93856SMark Peek 	int result;
49963a93856SMark Peek 
50063a93856SMark Peek 	if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB ||
50163a93856SMark Peek 	    priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
50263a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
50363a93856SMark Peek 
50463a93856SMark Peek 	entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL);
50563a93856SMark Peek 	if (entry == NULL) {
50663a93856SMark Peek 		VMCI_LOG_WARNING(LGPFX"Failed allocating memory for datagram "
50763a93856SMark Peek 		    "entry.\n");
50863a93856SMark Peek 		return (VMCI_ERROR_NO_MEM);
50963a93856SMark Peek 	}
51063a93856SMark Peek 
51163a93856SMark Peek 	if (!vmci_can_schedule_delayed_work() &&
51263a93856SMark Peek 	    (flags & VMCI_FLAG_DELAYED_CB)) {
51363a93856SMark Peek 		result = VMCI_ERROR_INVALID_ARGS;
51463a93856SMark Peek 		goto free_mem;
51563a93856SMark Peek 	}
51663a93856SMark Peek 
51763a93856SMark Peek 	if (VMCI_HANDLE_INVALID(*handle)) {
51863a93856SMark Peek 		vmci_id context_id;
51963a93856SMark Peek 
52063a93856SMark Peek 		context_id = vmci_get_context_id();
52163a93856SMark Peek 		vmci_id resource_id = vmci_resource_get_id(context_id);
52263a93856SMark Peek 		if (resource_id == VMCI_INVALID_ID) {
52363a93856SMark Peek 			result = VMCI_ERROR_NO_HANDLE;
52463a93856SMark Peek 			goto free_mem;
52563a93856SMark Peek 		}
52663a93856SMark Peek 		new_handle = VMCI_MAKE_HANDLE(context_id, resource_id);
52763a93856SMark Peek 	} else {
52863a93856SMark Peek 		if (VMCI_INVALID_ID == handle->resource) {
52963a93856SMark Peek 			VMCI_LOG_DEBUG(LGPFX"Invalid argument "
53063a93856SMark Peek 			    "(handle=0x%x:0x%x).\n", handle->context,
53163a93856SMark Peek 			    handle->resource);
53263a93856SMark Peek 			result = VMCI_ERROR_INVALID_ARGS;
53363a93856SMark Peek 			goto free_mem;
53463a93856SMark Peek 		}
53563a93856SMark Peek 		new_handle = *handle;
53663a93856SMark Peek 	}
53763a93856SMark Peek 
53863a93856SMark Peek 	entry->idx = 0;
53963a93856SMark Peek 	entry->priv_flags = priv_flags;
54063a93856SMark Peek 	entry->is_doorbell = true;
54163a93856SMark Peek 	entry->run_delayed = (flags & VMCI_FLAG_DELAYED_CB) ? true : false;
54263a93856SMark Peek 	entry->notify_cb = notify_cb;
54363a93856SMark Peek 	entry->client_data = client_data;
54463a93856SMark Peek 	atomic_store_int(&entry->active, 0);
54563a93856SMark Peek 	vmci_create_event(&entry->destroy_event);
54663a93856SMark Peek 
54763a93856SMark Peek 	result = vmci_resource_add(&entry->resource,
54863a93856SMark Peek 	    VMCI_RESOURCE_TYPE_DOORBELL, new_handle, vmci_doorbell_free_cb,
54963a93856SMark Peek 	    entry);
55063a93856SMark Peek 	if (result != VMCI_SUCCESS) {
55163a93856SMark Peek 		VMCI_LOG_WARNING(LGPFX"Failed to add new resource "
55263a93856SMark Peek 		    "(handle=0x%x:0x%x).\n", new_handle.context,
55363a93856SMark Peek 		    new_handle.resource);
55463a93856SMark Peek 		if (result == VMCI_ERROR_DUPLICATE_ENTRY)
55563a93856SMark Peek 			result = VMCI_ERROR_ALREADY_EXISTS;
55663a93856SMark Peek 
55763a93856SMark Peek 		goto destroy;
55863a93856SMark Peek 	}
55963a93856SMark Peek 
56063a93856SMark Peek 	vmci_doorbell_index_table_add(entry);
56163a93856SMark Peek 	result = vmci_doorbell_link(new_handle, entry->is_doorbell, entry->idx);
56263a93856SMark Peek 	if (VMCI_SUCCESS != result)
56363a93856SMark Peek 		goto destroy_resource;
56463a93856SMark Peek 	atomic_store_int(&entry->active, 1);
56563a93856SMark Peek 
56663a93856SMark Peek 	if (VMCI_HANDLE_INVALID(*handle))
56763a93856SMark Peek 		*handle = new_handle;
56863a93856SMark Peek 
56963a93856SMark Peek 	return (result);
57063a93856SMark Peek 
57163a93856SMark Peek destroy_resource:
57263a93856SMark Peek 	vmci_doorbell_index_table_remove(entry);
57363a93856SMark Peek 	vmci_resource_remove(new_handle, VMCI_RESOURCE_TYPE_DOORBELL);
57463a93856SMark Peek destroy:
57563a93856SMark Peek 	vmci_destroy_event(&entry->destroy_event);
57663a93856SMark Peek free_mem:
57763a93856SMark Peek 	vmci_free_kernel_mem(entry, sizeof(*entry));
57863a93856SMark Peek 	return (result);
57963a93856SMark Peek }
58063a93856SMark Peek 
58163a93856SMark Peek /*
58263a93856SMark Peek  *------------------------------------------------------------------------------
58363a93856SMark Peek  *
58463a93856SMark Peek  * vmci_doorbell_destroy --
58563a93856SMark Peek  *
58663a93856SMark Peek  *     Destroys a doorbell previously created with vmci_doorbell_create. This
58763a93856SMark Peek  *     operation may block waiting for a callback to finish.
58863a93856SMark Peek  *
58963a93856SMark Peek  * Results:
59063a93856SMark Peek  *     VMCI_SUCCESS on success, appropriate error code otherwise.
59163a93856SMark Peek  *
59263a93856SMark Peek  * Side effects:
59363a93856SMark Peek  *     May block.
59463a93856SMark Peek  *
59563a93856SMark Peek  *------------------------------------------------------------------------------
59663a93856SMark Peek  */
59763a93856SMark Peek 
59863a93856SMark Peek int
vmci_doorbell_destroy(struct vmci_handle handle)59963a93856SMark Peek vmci_doorbell_destroy(struct vmci_handle handle)
60063a93856SMark Peek {
60163a93856SMark Peek 	struct vmci_doorbell_entry *entry;
60263a93856SMark Peek 	struct vmci_resource *resource;
60363a93856SMark Peek 	int result;
60463a93856SMark Peek 
60563a93856SMark Peek 	if (VMCI_HANDLE_INVALID(handle))
60663a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
60763a93856SMark Peek 
60863a93856SMark Peek 	resource = vmci_resource_get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
60963a93856SMark Peek 	if (resource == NULL) {
61063a93856SMark Peek 		VMCI_LOG_DEBUG(LGPFX"Failed to destroy doorbell "
61163a93856SMark Peek 		    "(handle=0x%x:0x%x).\n", handle.context, handle.resource);
61263a93856SMark Peek 		return (VMCI_ERROR_NOT_FOUND);
61363a93856SMark Peek 	}
61463a93856SMark Peek 	entry = RESOURCE_CONTAINER(resource, struct vmci_doorbell_entry,
61563a93856SMark Peek 	    resource);
61663a93856SMark Peek 
61763a93856SMark Peek 	vmci_doorbell_index_table_remove(entry);
61863a93856SMark Peek 
61963a93856SMark Peek 	result = vmci_doorbell_unlink(handle, entry->is_doorbell);
62063a93856SMark Peek 	if (VMCI_SUCCESS != result) {
62163a93856SMark Peek 		/*
62263a93856SMark Peek 		 * The only reason this should fail would be an inconsistency
62363a93856SMark Peek 		 * between guest and hypervisor state, where the guest believes
62463a93856SMark Peek 		 * it has an active registration whereas the hypervisor doesn't.
62563a93856SMark Peek 		 * One case where this may happen is if a doorbell is
62663a93856SMark Peek 		 * unregistered following a hibernation at a time where the
62763a93856SMark Peek 		 * doorbell state hasn't been restored on the hypervisor side
62863a93856SMark Peek 		 * yet. Since the handle has now been removed in the guest,
62963a93856SMark Peek 		 * we just print a warning and return success.
63063a93856SMark Peek 		 */
63163a93856SMark Peek 
63263a93856SMark Peek 		VMCI_LOG_DEBUG(LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown "
63363a93856SMark Peek 		    "by hypervisor (error=%d).\n",
63463a93856SMark Peek 		    entry->is_doorbell ? "doorbell" : "queuepair",
63563a93856SMark Peek 		    handle.context, handle.resource, result);
63663a93856SMark Peek 	}
63763a93856SMark Peek 
63863a93856SMark Peek 	/*
63963a93856SMark Peek 	 * Now remove the resource from the table.  It might still be in use
64063a93856SMark Peek 	 * after this, in a callback or still on the delayed work queue.
64163a93856SMark Peek 	 */
64263a93856SMark Peek 
64363a93856SMark Peek 	vmci_resource_remove(handle, VMCI_RESOURCE_TYPE_DOORBELL);
64463a93856SMark Peek 
64563a93856SMark Peek 	/*
64663a93856SMark Peek 	 * We now wait on the destroyEvent and release the reference we got
64763a93856SMark Peek 	 * above.
64863a93856SMark Peek 	 */
64963a93856SMark Peek 
65063a93856SMark Peek 	vmci_wait_on_event(&entry->destroy_event, vmci_doorbell_release_cb,
65163a93856SMark Peek 	    entry);
65263a93856SMark Peek 
65363a93856SMark Peek 	/*
65463a93856SMark Peek 	 * We know that we are now the only reference to the above entry so
65563a93856SMark Peek 	 * can safely free it.
65663a93856SMark Peek 	 */
65763a93856SMark Peek 
65863a93856SMark Peek 	vmci_destroy_event(&entry->destroy_event);
65963a93856SMark Peek 	vmci_free_kernel_mem(entry, sizeof(*entry));
66063a93856SMark Peek 
66163a93856SMark Peek 	return (VMCI_SUCCESS);
66263a93856SMark Peek }
66363a93856SMark Peek 
66463a93856SMark Peek /*
66563a93856SMark Peek  *------------------------------------------------------------------------------
66663a93856SMark Peek  *
66763a93856SMark Peek  * vmci_doorbell_notify_as_guest --
66863a93856SMark Peek  *
66963a93856SMark Peek  *     Notify another guest or the host. We send a datagram down to the host
67063a93856SMark Peek  *     via the hypervisor with the notification info.
67163a93856SMark Peek  *
67263a93856SMark Peek  * Results:
67363a93856SMark Peek  *     VMCI_SUCCESS on success, appropriate error code otherwise.
67463a93856SMark Peek  *
67563a93856SMark Peek  * Side effects:
67663a93856SMark Peek  *     May do a hypercall.
67763a93856SMark Peek  *
67863a93856SMark Peek  *------------------------------------------------------------------------------
67963a93856SMark Peek  */
68063a93856SMark Peek 
68163a93856SMark Peek static int
vmci_doorbell_notify_as_guest(struct vmci_handle handle,vmci_privilege_flags priv_flags)68263a93856SMark Peek vmci_doorbell_notify_as_guest(struct vmci_handle handle,
68363a93856SMark Peek     vmci_privilege_flags priv_flags)
68463a93856SMark Peek {
68563a93856SMark Peek 	struct vmci_doorbell_notify_msg notify_msg;
68663a93856SMark Peek 
68763a93856SMark Peek 	notify_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
68863a93856SMark Peek 	    VMCI_DOORBELL_NOTIFY);
68963a93856SMark Peek 	notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
69063a93856SMark Peek 	notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE;
69163a93856SMark Peek 	notify_msg.handle = handle;
69263a93856SMark Peek 
69363a93856SMark Peek 	return (vmci_send_datagram((struct vmci_datagram *)&notify_msg));
69463a93856SMark Peek }
69563a93856SMark Peek 
69663a93856SMark Peek /*
69763a93856SMark Peek  *------------------------------------------------------------------------------
69863a93856SMark Peek  *
69963a93856SMark Peek  * vmci_doorbell_notify --
70063a93856SMark Peek  *
70163a93856SMark Peek  *     Generates a notification on the doorbell identified by the handle. For
70263a93856SMark Peek  *     host side generation of notifications, the caller can specify what the
70363a93856SMark Peek  *     privilege of the calling side is.
70463a93856SMark Peek  *
70563a93856SMark Peek  * Results:
70663a93856SMark Peek  *     VMCI_SUCCESS on success, appropriate error code otherwise.
70763a93856SMark Peek  *
70863a93856SMark Peek  * Side effects:
70963a93856SMark Peek  *     May do a hypercall.
71063a93856SMark Peek  *
71163a93856SMark Peek  *------------------------------------------------------------------------------
71263a93856SMark Peek  */
71363a93856SMark Peek 
71463a93856SMark Peek int
vmci_doorbell_notify(struct vmci_handle dst,vmci_privilege_flags priv_flags)71563a93856SMark Peek vmci_doorbell_notify(struct vmci_handle dst, vmci_privilege_flags priv_flags)
71663a93856SMark Peek {
71763a93856SMark Peek 	if (VMCI_HANDLE_INVALID(dst) ||
71863a93856SMark Peek 	    (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS))
71963a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
72063a93856SMark Peek 
72163a93856SMark Peek 	return (vmci_doorbell_notify_as_guest(dst, priv_flags));
72263a93856SMark Peek }
72363a93856SMark Peek 
72463a93856SMark Peek /*
72563a93856SMark Peek  *------------------------------------------------------------------------------
72663a93856SMark Peek  *
72763a93856SMark Peek  * vmci_doorbell_delayed_dispatch_cb --
72863a93856SMark Peek  *
72963a93856SMark Peek  *     Calls the specified callback in a delayed context.
73063a93856SMark Peek  *
73163a93856SMark Peek  * Results:
73263a93856SMark Peek  *     None.
73363a93856SMark Peek  *
73463a93856SMark Peek  * Side effects:
73563a93856SMark Peek  *     None.
73663a93856SMark Peek  *
73763a93856SMark Peek  *------------------------------------------------------------------------------
73863a93856SMark Peek  */
73963a93856SMark Peek 
74063a93856SMark Peek static void
vmci_doorbell_delayed_dispatch_cb(void * data)74163a93856SMark Peek vmci_doorbell_delayed_dispatch_cb(void *data)
74263a93856SMark Peek {
74363a93856SMark Peek 	struct vmci_doorbell_entry *entry = (struct vmci_doorbell_entry *)data;
74463a93856SMark Peek 
74563a93856SMark Peek 	ASSERT(data);
74663a93856SMark Peek 
74763a93856SMark Peek 	entry->notify_cb(entry->client_data);
74863a93856SMark Peek 
74963a93856SMark Peek 	vmci_resource_release(&entry->resource);
75063a93856SMark Peek }
75163a93856SMark Peek 
75263a93856SMark Peek /*
75363a93856SMark Peek  *------------------------------------------------------------------------------
75463a93856SMark Peek  *
75563a93856SMark Peek  * vmci_doorbell_sync --
75663a93856SMark Peek  *
75763a93856SMark Peek  *     Use this as a synchronization point when setting globals, for example,
75863a93856SMark Peek  *     during device shutdown.
75963a93856SMark Peek  *
76063a93856SMark Peek  * Results:
76163a93856SMark Peek  *     None.
76263a93856SMark Peek  *
76363a93856SMark Peek  * Side effects:
76463a93856SMark Peek  *     None.
76563a93856SMark Peek  *
76663a93856SMark Peek  *------------------------------------------------------------------------------
76763a93856SMark Peek  */
76863a93856SMark Peek 
76963a93856SMark Peek void
vmci_doorbell_sync(void)77063a93856SMark Peek vmci_doorbell_sync(void)
77163a93856SMark Peek {
77263a93856SMark Peek 
77363a93856SMark Peek 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
77463a93856SMark Peek 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
77563a93856SMark Peek 	vmci_resource_sync();
77663a93856SMark Peek }
77763a93856SMark Peek 
77863a93856SMark Peek /*
77963a93856SMark Peek  *------------------------------------------------------------------------------
78063a93856SMark Peek  *
78163a93856SMark Peek  * vmci_register_notification_bitmap --
78263a93856SMark Peek  *
78363a93856SMark Peek  *     Register the notification bitmap with the host.
78463a93856SMark Peek  *
78563a93856SMark Peek  * Results:
78663a93856SMark Peek  *     true if the bitmap is registered successfully with the device, false
78763a93856SMark Peek  *     otherwise.
78863a93856SMark Peek  *
78963a93856SMark Peek  * Side effects:
79063a93856SMark Peek  *     None.
79163a93856SMark Peek  *
79263a93856SMark Peek  *------------------------------------------------------------------------------
79363a93856SMark Peek  */
79463a93856SMark Peek 
79563a93856SMark Peek bool
vmci_register_notification_bitmap(PPN bitmap_ppn)79663a93856SMark Peek vmci_register_notification_bitmap(PPN bitmap_ppn)
79763a93856SMark Peek {
79863a93856SMark Peek 	struct vmci_notify_bitmap_set_msg bitmap_set_msg;
79963a93856SMark Peek 	int result;
80063a93856SMark Peek 
80163a93856SMark Peek 	/*
80263a93856SMark Peek 	 * Do not ASSERT() on the guest device here. This function can get
80363a93856SMark Peek 	 * called during device initialization, so the ASSERT() will fail even
80463a93856SMark Peek 	 * though the device is (almost) up.
80563a93856SMark Peek 	 */
80663a93856SMark Peek 
80763a93856SMark Peek 	bitmap_set_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
80863a93856SMark Peek 	    VMCI_SET_NOTIFY_BITMAP);
80963a93856SMark Peek 	bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
81063a93856SMark Peek 	bitmap_set_msg.hdr.payload_size =
81163a93856SMark Peek 	    sizeof(bitmap_set_msg) - VMCI_DG_HEADERSIZE;
81263a93856SMark Peek 	bitmap_set_msg.bitmap_ppn = bitmap_ppn;
81363a93856SMark Peek 
81463a93856SMark Peek 	result = vmci_send_datagram((struct vmci_datagram *)&bitmap_set_msg);
81563a93856SMark Peek 	if (result != VMCI_SUCCESS) {
81663a93856SMark Peek 		VMCI_LOG_DEBUG(LGPFX"Failed to register (PPN=%u) as "
81763a93856SMark Peek 		    "notification bitmap (error=%d).\n",
81863a93856SMark Peek 		    bitmap_ppn, result);
81963a93856SMark Peek 		return (false);
82063a93856SMark Peek 	}
82163a93856SMark Peek 	return (true);
82263a93856SMark Peek }
82363a93856SMark Peek 
82463a93856SMark Peek /*
82563a93856SMark Peek  *------------------------------------------------------------------------------
82663a93856SMark Peek  *
82763a93856SMark Peek  * vmci_doorbell_fire_entries --
82863a93856SMark Peek  *
82963a93856SMark Peek  *     Executes or schedules the handlers for a given notify index.
83063a93856SMark Peek  *
83163a93856SMark Peek  * Result:
83263a93856SMark Peek  *     Notification hash entry if found. NULL otherwise.
83363a93856SMark Peek  *
83463a93856SMark Peek  * Side effects:
83563a93856SMark Peek  *     Whatever the side effects of the handlers are.
83663a93856SMark Peek  *
83763a93856SMark Peek  *------------------------------------------------------------------------------
83863a93856SMark Peek  */
83963a93856SMark Peek 
84063a93856SMark Peek static void
vmci_doorbell_fire_entries(uint32_t notify_idx)84163a93856SMark Peek vmci_doorbell_fire_entries(uint32_t notify_idx)
84263a93856SMark Peek {
84363a93856SMark Peek 	struct vmci_doorbell_entry *iter;
84463a93856SMark Peek 	uint32_t bucket = VMCI_DOORBELL_HASH(notify_idx);
84563a93856SMark Peek 
84663a93856SMark Peek 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
84763a93856SMark Peek 
84863a93856SMark Peek 	vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) {
84963a93856SMark Peek 		if (iter->idx == notify_idx &&
85063a93856SMark Peek 		    atomic_load_int(&iter->active) == 1) {
85163a93856SMark Peek 			ASSERT(iter->notify_cb);
85263a93856SMark Peek 			if (iter->run_delayed) {
85363a93856SMark Peek 				int err;
85463a93856SMark Peek 
85563a93856SMark Peek 				vmci_resource_hold(&iter->resource);
85663a93856SMark Peek 				err = vmci_schedule_delayed_work(
85763a93856SMark Peek 				    vmci_doorbell_delayed_dispatch_cb, iter);
85863a93856SMark Peek 				if (err != VMCI_SUCCESS) {
85963a93856SMark Peek 					vmci_resource_release(&iter->resource);
86063a93856SMark Peek 					goto out;
86163a93856SMark Peek 				}
86263a93856SMark Peek 			} else
86363a93856SMark Peek 				iter->notify_cb(iter->client_data);
86463a93856SMark Peek 		}
86563a93856SMark Peek 	}
86663a93856SMark Peek 
86763a93856SMark Peek out:
86863a93856SMark Peek 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
86963a93856SMark Peek }
87063a93856SMark Peek 
87163a93856SMark Peek /*
87263a93856SMark Peek  *------------------------------------------------------------------------------
87363a93856SMark Peek  *
87463a93856SMark Peek  * vmci_scan_notification_bitmap --
87563a93856SMark Peek  *
87663a93856SMark Peek  *     Scans the notification bitmap, collects pending notifications, resets
87763a93856SMark Peek  *     the bitmap and invokes appropriate callbacks.
87863a93856SMark Peek  *
87963a93856SMark Peek  * Results:
88063a93856SMark Peek  *     None.
88163a93856SMark Peek  *
88263a93856SMark Peek  * Side effects:
88363a93856SMark Peek  *     May schedule tasks, allocate memory and run callbacks.
88463a93856SMark Peek  *
88563a93856SMark Peek  *------------------------------------------------------------------------------
88663a93856SMark Peek  */
88763a93856SMark Peek 
88863a93856SMark Peek void
vmci_scan_notification_bitmap(uint8_t * bitmap)88963a93856SMark Peek vmci_scan_notification_bitmap(uint8_t *bitmap)
89063a93856SMark Peek {
89163a93856SMark Peek 	uint32_t idx;
89263a93856SMark Peek 
89363a93856SMark Peek 	ASSERT(bitmap);
89463a93856SMark Peek 
89563a93856SMark Peek 	for (idx = 0; idx < max_notify_idx; idx++) {
89663a93856SMark Peek 		if (bitmap[idx] & 0x1) {
89763a93856SMark Peek 			bitmap[idx] &= ~1;
89863a93856SMark Peek 			vmci_doorbell_fire_entries(idx);
89963a93856SMark Peek 		}
90063a93856SMark Peek 	}
90163a93856SMark Peek }
902