xref: /freebsd/sys/dev/vmware/vmci/vmci_doorbell.c (revision bc7512cc58af2e8bbe5bbf5ca0059b1daa1da897)
1 /*-
2  * Copyright (c) 2018 VMware, Inc.
3  *
4  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5  */
6 
7 /* This file implements the VMCI doorbell API. */
8 
9 #include <sys/cdefs.h>
10 __FBSDID("$FreeBSD$");
11 
12 #include <sys/types.h>
13 
14 #include "vmci_doorbell.h"
15 #include "vmci_driver.h"
16 #include "vmci_kernel_api.h"
17 #include "vmci_kernel_defs.h"
18 #include "vmci_resource.h"
19 #include "vmci_utils.h"
20 
21 #define LGPFX				"vmci_doorbell: "
22 
23 #define VMCI_DOORBELL_INDEX_TABLE_SIZE	64
24 #define VMCI_DOORBELL_HASH(_idx)					\
25 	vmci_hash_id((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE)
26 
27 /* Describes a doorbell notification handle allocated by the host. */
28 struct vmci_doorbell_entry {
29 	struct vmci_resource			resource;
30 	uint32_t				idx;
31 	vmci_list_item(vmci_doorbell_entry)	idx_list_item;
32 	vmci_privilege_flags			priv_flags;
33 	bool					is_doorbell;
34 	bool					run_delayed;
35 	vmci_callback				notify_cb;
36 	void					*client_data;
37 	vmci_event				destroy_event;
38 	volatile int				active;
39 };
40 
41 struct vmci_doorbell_index_table {
42 	vmci_lock			lock;
43 	vmci_list(vmci_doorbell_entry)	entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
44 };
45 
46 /* The VMCI index table keeps track of currently registered doorbells. */
47 static struct vmci_doorbell_index_table vmci_doorbell_it;
48 
49 /*
50  * The max_notify_idx is one larger than the currently known bitmap index in
51  * use, and is used to determine how much of the bitmap needs to be scanned.
52  */
53 static uint32_t	max_notify_idx;
54 
55 /*
56  * The notify_idx_count is used for determining whether there are free entries
57  * within the bitmap (if notify_idx_count + 1 < max_notify_idx).
58  */
59 static uint32_t notify_idx_count;
60 
61 /*
62  * The last_notify_idx_reserved is used to track the last index handed out - in
63  * the case where multiple handles share a notification index, we hand out
64  * indexes round robin based on last_notify_idx_reserved.
65  */
66 static uint32_t last_notify_idx_reserved;
67 
68 /* This is a one entry cache used to by the index allocation. */
69 static uint32_t last_notify_idx_released = PAGE_SIZE;
70 
71 static void	vmci_doorbell_free_cb(void *client_data);
72 static int	vmci_doorbell_release_cb(void *client_data);
73 static void	vmci_doorbell_delayed_dispatch_cb(void *data);
74 
75 /*
76  *------------------------------------------------------------------------------
77  *
78  * vmci_doorbell_init --
79  *
80  *     General init code.
81  *
82  * Result:
83  *     VMCI_SUCCESS on success, lock allocation error otherwise.
84  *
85  * Side effects:
86  *     None.
87  *
88  *------------------------------------------------------------------------------
89  */
90 
91 int
92 vmci_doorbell_init(void)
93 {
94 	uint32_t bucket;
95 
96 	for (bucket = 0; bucket < ARRAYSIZE(vmci_doorbell_it.entries);
97 	    ++bucket)
98 		vmci_list_init(&vmci_doorbell_it.entries[bucket]);
99 
100 	return (vmci_init_lock(&vmci_doorbell_it.lock,
101 	    "VMCI Doorbell index table lock"));
102 }
103 
104 /*
105  *------------------------------------------------------------------------------
106  *
107  * vmci_doorbell_exit --
108  *
109  *     General exit code.
110  *
111  * Result:
112  *     None.
113  *
114  * Side effects:
115  *     None.
116  *
117  *------------------------------------------------------------------------------
118  */
119 
120 void
121 vmci_doorbell_exit(void)
122 {
123 
124 	vmci_cleanup_lock(&vmci_doorbell_it.lock);
125 }
126 
127 /*
128  *------------------------------------------------------------------------------
129  *
130  * vmci_doorbell_free_cb --
131  *
132  *     Callback to free doorbell entry structure when resource is no longer used,
133  *     i.e. the reference count reached 0.  The entry is freed in
134  *     vmci_doorbell_destroy(), which is waiting on the signal that gets fired
135  *     here.
136  *
137  * Result:
138  *     None.
139  *
140  * Side effects:
141  *     Signals VMCI event.
142  *
143  *------------------------------------------------------------------------------
144  */
145 
146 static void
147 vmci_doorbell_free_cb(void *client_data)
148 {
149 	struct vmci_doorbell_entry *entry;
150 
151 	entry = (struct vmci_doorbell_entry *)client_data;
152 	ASSERT(entry);
153 	vmci_signal_event(&entry->destroy_event);
154 }
155 
156 /*
157  *------------------------------------------------------------------------------
158  *
159  * vmci_doorbell_release_cb --
160  *
161  *     Callback to release the resource reference. It is called by the
162  *     vmci_wait_on_event function before it blocks.
163  *
164  * Result:
165  *     Always 0.
166  *
167  * Side effects:
168  *     None.
169  *
170  *------------------------------------------------------------------------------
171  */
172 
173 static int
174 vmci_doorbell_release_cb(void *client_data)
175 {
176 	struct vmci_doorbell_entry *entry;
177 
178 	entry  = (struct vmci_doorbell_entry *)client_data;
179 	ASSERT(entry);
180 	vmci_resource_release(&entry->resource);
181 	return (0);
182 }
183 
184 /*
185  *------------------------------------------------------------------------------
186  *
187  * vmci_doorbell_get_priv_flags --
188  *
189  *     Utility function that retrieves the privilege flags associated with a
190  *     given doorbell handle. For guest endpoints, the privileges are determined
191  *     by the context ID, but for host endpoints privileges are associated with
192  *     the complete handle. Hypervisor endpoints are not yet supported.
193  *
194  * Result:
195  *     VMCI_SUCCESS on success,
196  *     VMCI_ERROR_NOT_FOUND if handle isn't found,
197  *     VMCI_ERROR_INVALID_ARGS if handle is invalid.
198  *
199  * Side effects:
200  *     None.
201  *
202  *------------------------------------------------------------------------------
203  */
204 
205 int
206 vmci_doorbell_get_priv_flags(struct vmci_handle handle,
207     vmci_privilege_flags *priv_flags)
208 {
209 
210 	if (priv_flags == NULL || handle.context == VMCI_INVALID_ID)
211 		return (VMCI_ERROR_INVALID_ARGS);
212 
213 	if (handle.context == VMCI_HOST_CONTEXT_ID) {
214 		struct vmci_doorbell_entry *entry;
215 		struct vmci_resource *resource;
216 
217 		resource = vmci_resource_get(handle,
218 		    VMCI_RESOURCE_TYPE_DOORBELL);
219 		if (resource == NULL)
220 			return (VMCI_ERROR_NOT_FOUND);
221 		entry = RESOURCE_CONTAINER(
222 		    resource, struct vmci_doorbell_entry, resource);
223 		*priv_flags = entry->priv_flags;
224 		vmci_resource_release(resource);
225 	} else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
226 		/* Hypervisor endpoints for notifications are not supported. */
227 		return (VMCI_ERROR_INVALID_ARGS);
228 	} else
229 		*priv_flags = VMCI_NO_PRIVILEGE_FLAGS;
230 
231 	return (VMCI_SUCCESS);
232 }
233 
234 /*
235  *------------------------------------------------------------------------------
236  *
237  * vmci_doorbell_index_table_find --
238  *
239  *     Find doorbell entry by bitmap index.
240  *
241  * Results:
242  *     Entry if found, NULL if not.
243  *
244  * Side effects:
245  *     None.
246  *
247  *------------------------------------------------------------------------------
248  */
249 
250 static struct vmci_doorbell_entry *
251 vmci_doorbell_index_table_find(uint32_t idx)
252 {
253 	struct vmci_doorbell_entry *iter;
254 	uint32_t bucket;
255 
256 	bucket = VMCI_DOORBELL_HASH(idx);
257 
258 	vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) {
259 		if (idx == iter->idx)
260 			return (iter);
261 	}
262 
263 	return (NULL);
264 }
265 
266 /*
267  *------------------------------------------------------------------------------
268  *
269  * vmci_doorbell_index_table_add --
270  *
271  *     Add the given entry to the index table. This will hold() the entry's
272  *     resource so that the entry is not deleted before it is removed from the
273  *     table.
274  *
275  * Results:
276  *     None.
277  *
278  * Side effects:
279  *     None.
280  *
281  *------------------------------------------------------------------------------
282  */
283 
284 static void
285 vmci_doorbell_index_table_add(struct vmci_doorbell_entry *entry)
286 {
287 	uint32_t bucket;
288 	uint32_t new_notify_idx;
289 
290 	ASSERT(entry);
291 
292 	vmci_resource_hold(&entry->resource);
293 
294 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
295 
296 	/*
297 	 * Below we try to allocate an index in the notification bitmap with
298 	 * "not too much" sharing between resources. If we use less that the
299 	 * full bitmap, we either add to the end if there are no unused flags
300 	 * within the currently used area, or we search for unused ones. If we
301 	 * use the full bitmap, we allocate the index round robin.
302 	 */
303 
304 	if (max_notify_idx < PAGE_SIZE || notify_idx_count < PAGE_SIZE) {
305 		if (last_notify_idx_released < max_notify_idx &&
306 		    !vmci_doorbell_index_table_find(last_notify_idx_released)) {
307 			new_notify_idx = last_notify_idx_released;
308 			last_notify_idx_released = PAGE_SIZE;
309 		} else {
310 			bool reused = false;
311 			new_notify_idx = last_notify_idx_reserved;
312 			if (notify_idx_count + 1 < max_notify_idx) {
313 				do {
314 					if (!vmci_doorbell_index_table_find(
315 					    new_notify_idx)) {
316 						reused = true;
317 						break;
318 					}
319 					new_notify_idx = (new_notify_idx + 1) %
320 					    max_notify_idx;
321 				} while (new_notify_idx !=
322 				    last_notify_idx_released);
323 			}
324 			if (!reused) {
325 				new_notify_idx = max_notify_idx;
326 				max_notify_idx++;
327 			}
328 		}
329 	} else {
330 		new_notify_idx = (last_notify_idx_reserved + 1) % PAGE_SIZE;
331 	}
332 	last_notify_idx_reserved = new_notify_idx;
333 	notify_idx_count++;
334 
335 	entry->idx = new_notify_idx;
336 	bucket = VMCI_DOORBELL_HASH(entry->idx);
337 	vmci_list_insert(&vmci_doorbell_it.entries[bucket], entry,
338 	    idx_list_item);
339 
340 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
341 }
342 
343 /*
344  *------------------------------------------------------------------------------
345  *
346  * vmci_doorbell_index_table_remove --
347  *
348  *     Remove the given entry from the index table. This will release() the
349  *     entry's resource.
350  *
351  * Results:
352  *     None.
353  *
354  * Side effects:
355  *     None.
356  *
357  *------------------------------------------------------------------------------
358  */
359 
360 static void
361 vmci_doorbell_index_table_remove(struct vmci_doorbell_entry *entry)
362 {
363 	ASSERT(entry);
364 
365 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
366 
367 	vmci_list_remove(entry, idx_list_item);
368 
369 	notify_idx_count--;
370 	if (entry->idx == max_notify_idx - 1) {
371 		/*
372 		 * If we delete an entry with the maximum known notification
373 		 * index, we take the opportunity to prune the current max. As
374 		 * there might be other unused indices immediately below, we
375 		 * lower the maximum until we hit an index in use
376 		 */
377 
378 		while (max_notify_idx > 0 &&
379 		    !vmci_doorbell_index_table_find(max_notify_idx - 1))
380 			max_notify_idx--;
381 	}
382 	last_notify_idx_released = entry->idx;
383 
384 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
385 
386 	vmci_resource_release(&entry->resource);
387 }
388 
389 /*
390  *------------------------------------------------------------------------------
391  *
392  * vmci_doorbell_link --
393  *
394  *     Creates a link between the given doorbell handle and the given index in
395  *     the bitmap in the device backend.
396  *
397  * Results:
398  *     VMCI_SUCCESS if success, appropriate error code otherwise.
399  *
400  * Side effects:
401  *     Notification state is created in hypervisor.
402  *
403  *------------------------------------------------------------------------------
404  */
405 
406 static int
407 vmci_doorbell_link(struct vmci_handle handle, bool is_doorbell,
408     uint32_t notify_idx)
409 {
410 	struct vmci_doorbell_link_msg link_msg;
411 	vmci_id resource_id;
412 
413 	ASSERT(!VMCI_HANDLE_INVALID(handle));
414 
415 	if (is_doorbell)
416 		resource_id = VMCI_DOORBELL_LINK;
417 	else {
418 		ASSERT(false);
419 		return (VMCI_ERROR_UNAVAILABLE);
420 	}
421 
422 	link_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
423 	    resource_id);
424 	link_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
425 	link_msg.hdr.payload_size = sizeof(link_msg) - VMCI_DG_HEADERSIZE;
426 	link_msg.handle = handle;
427 	link_msg.notify_idx = notify_idx;
428 
429 	return (vmci_send_datagram((struct vmci_datagram *)&link_msg));
430 }
431 
432 /*
433  *------------------------------------------------------------------------------
434  *
435  * vmci_doorbell_unlink --
436  *
437  *     Unlinks the given doorbell handle from an index in the bitmap in the
438  *     device backend.
439  *
440  * Results:
441  *     VMCI_SUCCESS if success, appropriate error code otherwise.
442  *
443  * Side effects:
444  *     Notification state is destroyed in hypervisor.
445  *
446  *------------------------------------------------------------------------------
447  */
448 
449 static int
450 vmci_doorbell_unlink(struct vmci_handle handle, bool is_doorbell)
451 {
452 	struct vmci_doorbell_unlink_msg unlink_msg;
453 	vmci_id resource_id;
454 
455 	ASSERT(!VMCI_HANDLE_INVALID(handle));
456 
457 	if (is_doorbell)
458 		resource_id = VMCI_DOORBELL_UNLINK;
459 	else {
460 		ASSERT(false);
461 		return (VMCI_ERROR_UNAVAILABLE);
462 	}
463 
464 	unlink_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
465 	    resource_id);
466 	unlink_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
467 	unlink_msg.hdr.payload_size = sizeof(unlink_msg) - VMCI_DG_HEADERSIZE;
468 	unlink_msg.handle = handle;
469 
470 	return (vmci_send_datagram((struct vmci_datagram *)&unlink_msg));
471 }
472 
473 /*
474  *------------------------------------------------------------------------------
475  *
476  * vmci_doorbell_create --
477  *
478  *     Creates a doorbell with the given callback. If the handle is
479  *     VMCI_INVALID_HANDLE, a free handle will be assigned, if possible. The
480  *     callback can be run immediately (potentially with locks held - the
481  *     default) or delayed (in a kernel thread) by specifying the flag
482  *     VMCI_FLAG_DELAYED_CB. If delayed execution is selected, a given callback
483  *     may not be run if the kernel is unable to allocate memory for the delayed
484  *     execution (highly unlikely).
485  *
486  * Results:
487  *     VMCI_SUCCESS on success, appropriate error code otherwise.
488  *
489  * Side effects:
490  *     None.
491  *
492  *------------------------------------------------------------------------------
493  */
494 
495 int
496 vmci_doorbell_create(struct vmci_handle *handle, uint32_t flags,
497     vmci_privilege_flags priv_flags, vmci_callback notify_cb, void *client_data)
498 {
499 	struct vmci_doorbell_entry *entry;
500 	struct vmci_handle new_handle;
501 	int result;
502 
503 	if (!handle || !notify_cb || flags & ~VMCI_FLAG_DELAYED_CB ||
504 	    priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)
505 		return (VMCI_ERROR_INVALID_ARGS);
506 
507 	entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL);
508 	if (entry == NULL) {
509 		VMCI_LOG_WARNING(LGPFX"Failed allocating memory for datagram "
510 		    "entry.\n");
511 		return (VMCI_ERROR_NO_MEM);
512 	}
513 
514 	if (!vmci_can_schedule_delayed_work() &&
515 	    (flags & VMCI_FLAG_DELAYED_CB)) {
516 		result = VMCI_ERROR_INVALID_ARGS;
517 		goto free_mem;
518 	}
519 
520 	if (VMCI_HANDLE_INVALID(*handle)) {
521 		vmci_id context_id;
522 
523 		context_id = vmci_get_context_id();
524 		vmci_id resource_id = vmci_resource_get_id(context_id);
525 		if (resource_id == VMCI_INVALID_ID) {
526 			result = VMCI_ERROR_NO_HANDLE;
527 			goto free_mem;
528 		}
529 		new_handle = VMCI_MAKE_HANDLE(context_id, resource_id);
530 	} else {
531 		if (VMCI_INVALID_ID == handle->resource) {
532 			VMCI_LOG_DEBUG(LGPFX"Invalid argument "
533 			    "(handle=0x%x:0x%x).\n", handle->context,
534 			    handle->resource);
535 			result = VMCI_ERROR_INVALID_ARGS;
536 			goto free_mem;
537 		}
538 		new_handle = *handle;
539 	}
540 
541 	entry->idx = 0;
542 	entry->priv_flags = priv_flags;
543 	entry->is_doorbell = true;
544 	entry->run_delayed = (flags & VMCI_FLAG_DELAYED_CB) ? true : false;
545 	entry->notify_cb = notify_cb;
546 	entry->client_data = client_data;
547 	atomic_store_int(&entry->active, 0);
548 	vmci_create_event(&entry->destroy_event);
549 
550 	result = vmci_resource_add(&entry->resource,
551 	    VMCI_RESOURCE_TYPE_DOORBELL, new_handle, vmci_doorbell_free_cb,
552 	    entry);
553 	if (result != VMCI_SUCCESS) {
554 		VMCI_LOG_WARNING(LGPFX"Failed to add new resource "
555 		    "(handle=0x%x:0x%x).\n", new_handle.context,
556 		    new_handle.resource);
557 		if (result == VMCI_ERROR_DUPLICATE_ENTRY)
558 			result = VMCI_ERROR_ALREADY_EXISTS;
559 
560 		goto destroy;
561 	}
562 
563 	vmci_doorbell_index_table_add(entry);
564 	result = vmci_doorbell_link(new_handle, entry->is_doorbell, entry->idx);
565 	if (VMCI_SUCCESS != result)
566 		goto destroy_resource;
567 	atomic_store_int(&entry->active, 1);
568 
569 	if (VMCI_HANDLE_INVALID(*handle))
570 		*handle = new_handle;
571 
572 	return (result);
573 
574 destroy_resource:
575 	vmci_doorbell_index_table_remove(entry);
576 	vmci_resource_remove(new_handle, VMCI_RESOURCE_TYPE_DOORBELL);
577 destroy:
578 	vmci_destroy_event(&entry->destroy_event);
579 free_mem:
580 	vmci_free_kernel_mem(entry, sizeof(*entry));
581 	return (result);
582 }
583 
584 /*
585  *------------------------------------------------------------------------------
586  *
587  * vmci_doorbell_destroy --
588  *
589  *     Destroys a doorbell previously created with vmci_doorbell_create. This
590  *     operation may block waiting for a callback to finish.
591  *
592  * Results:
593  *     VMCI_SUCCESS on success, appropriate error code otherwise.
594  *
595  * Side effects:
596  *     May block.
597  *
598  *------------------------------------------------------------------------------
599  */
600 
601 int
602 vmci_doorbell_destroy(struct vmci_handle handle)
603 {
604 	struct vmci_doorbell_entry *entry;
605 	struct vmci_resource *resource;
606 	int result;
607 
608 	if (VMCI_HANDLE_INVALID(handle))
609 		return (VMCI_ERROR_INVALID_ARGS);
610 
611 	resource = vmci_resource_get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
612 	if (resource == NULL) {
613 		VMCI_LOG_DEBUG(LGPFX"Failed to destroy doorbell "
614 		    "(handle=0x%x:0x%x).\n", handle.context, handle.resource);
615 		return (VMCI_ERROR_NOT_FOUND);
616 	}
617 	entry = RESOURCE_CONTAINER(resource, struct vmci_doorbell_entry,
618 	    resource);
619 
620 	vmci_doorbell_index_table_remove(entry);
621 
622 	result = vmci_doorbell_unlink(handle, entry->is_doorbell);
623 	if (VMCI_SUCCESS != result) {
624 		/*
625 		 * The only reason this should fail would be an inconsistency
626 		 * between guest and hypervisor state, where the guest believes
627 		 * it has an active registration whereas the hypervisor doesn't.
628 		 * One case where this may happen is if a doorbell is
629 		 * unregistered following a hibernation at a time where the
630 		 * doorbell state hasn't been restored on the hypervisor side
631 		 * yet. Since the handle has now been removed in the guest,
632 		 * we just print a warning and return success.
633 		 */
634 
635 		VMCI_LOG_DEBUG(LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown "
636 		    "by hypervisor (error=%d).\n",
637 		    entry->is_doorbell ? "doorbell" : "queuepair",
638 		    handle.context, handle.resource, result);
639 	}
640 
641 	/*
642 	 * Now remove the resource from the table.  It might still be in use
643 	 * after this, in a callback or still on the delayed work queue.
644 	 */
645 
646 	vmci_resource_remove(handle, VMCI_RESOURCE_TYPE_DOORBELL);
647 
648 	/*
649 	 * We now wait on the destroyEvent and release the reference we got
650 	 * above.
651 	 */
652 
653 	vmci_wait_on_event(&entry->destroy_event, vmci_doorbell_release_cb,
654 	    entry);
655 
656 	/*
657 	 * We know that we are now the only reference to the above entry so
658 	 * can safely free it.
659 	 */
660 
661 	vmci_destroy_event(&entry->destroy_event);
662 	vmci_free_kernel_mem(entry, sizeof(*entry));
663 
664 	return (VMCI_SUCCESS);
665 }
666 
667 /*
668  *------------------------------------------------------------------------------
669  *
670  * vmci_doorbell_notify_as_guest --
671  *
672  *     Notify another guest or the host. We send a datagram down to the host
673  *     via the hypervisor with the notification info.
674  *
675  * Results:
676  *     VMCI_SUCCESS on success, appropriate error code otherwise.
677  *
678  * Side effects:
679  *     May do a hypercall.
680  *
681  *------------------------------------------------------------------------------
682  */
683 
684 static int
685 vmci_doorbell_notify_as_guest(struct vmci_handle handle,
686     vmci_privilege_flags priv_flags)
687 {
688 	struct vmci_doorbell_notify_msg notify_msg;
689 
690 	notify_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
691 	    VMCI_DOORBELL_NOTIFY);
692 	notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
693 	notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE;
694 	notify_msg.handle = handle;
695 
696 	return (vmci_send_datagram((struct vmci_datagram *)&notify_msg));
697 }
698 
699 /*
700  *------------------------------------------------------------------------------
701  *
702  * vmci_doorbell_notify --
703  *
704  *     Generates a notification on the doorbell identified by the handle. For
705  *     host side generation of notifications, the caller can specify what the
706  *     privilege of the calling side is.
707  *
708  * Results:
709  *     VMCI_SUCCESS on success, appropriate error code otherwise.
710  *
711  * Side effects:
712  *     May do a hypercall.
713  *
714  *------------------------------------------------------------------------------
715  */
716 
717 int
718 vmci_doorbell_notify(struct vmci_handle dst, vmci_privilege_flags priv_flags)
719 {
720 	if (VMCI_HANDLE_INVALID(dst) ||
721 	    (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS))
722 		return (VMCI_ERROR_INVALID_ARGS);
723 
724 	return (vmci_doorbell_notify_as_guest(dst, priv_flags));
725 }
726 
727 /*
728  *------------------------------------------------------------------------------
729  *
730  * vmci_doorbell_delayed_dispatch_cb --
731  *
732  *     Calls the specified callback in a delayed context.
733  *
734  * Results:
735  *     None.
736  *
737  * Side effects:
738  *     None.
739  *
740  *------------------------------------------------------------------------------
741  */
742 
743 static void
744 vmci_doorbell_delayed_dispatch_cb(void *data)
745 {
746 	struct vmci_doorbell_entry *entry = (struct vmci_doorbell_entry *)data;
747 
748 	ASSERT(data);
749 
750 	entry->notify_cb(entry->client_data);
751 
752 	vmci_resource_release(&entry->resource);
753 }
754 
755 /*
756  *------------------------------------------------------------------------------
757  *
758  * vmci_doorbell_sync --
759  *
760  *     Use this as a synchronization point when setting globals, for example,
761  *     during device shutdown.
762  *
763  * Results:
764  *     None.
765  *
766  * Side effects:
767  *     None.
768  *
769  *------------------------------------------------------------------------------
770  */
771 
772 void
773 vmci_doorbell_sync(void)
774 {
775 
776 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
777 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
778 	vmci_resource_sync();
779 }
780 
781 /*
782  *------------------------------------------------------------------------------
783  *
784  * vmci_register_notification_bitmap --
785  *
786  *     Register the notification bitmap with the host.
787  *
788  * Results:
789  *     true if the bitmap is registered successfully with the device, false
790  *     otherwise.
791  *
792  * Side effects:
793  *     None.
794  *
795  *------------------------------------------------------------------------------
796  */
797 
798 bool
799 vmci_register_notification_bitmap(PPN bitmap_ppn)
800 {
801 	struct vmci_notify_bitmap_set_msg bitmap_set_msg;
802 	int result;
803 
804 	/*
805 	 * Do not ASSERT() on the guest device here. This function can get
806 	 * called during device initialization, so the ASSERT() will fail even
807 	 * though the device is (almost) up.
808 	 */
809 
810 	bitmap_set_msg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
811 	    VMCI_SET_NOTIFY_BITMAP);
812 	bitmap_set_msg.hdr.src = VMCI_ANON_SRC_HANDLE;
813 	bitmap_set_msg.hdr.payload_size =
814 	    sizeof(bitmap_set_msg) - VMCI_DG_HEADERSIZE;
815 	bitmap_set_msg.bitmap_ppn = bitmap_ppn;
816 
817 	result = vmci_send_datagram((struct vmci_datagram *)&bitmap_set_msg);
818 	if (result != VMCI_SUCCESS) {
819 		VMCI_LOG_DEBUG(LGPFX"Failed to register (PPN=%u) as "
820 		    "notification bitmap (error=%d).\n",
821 		    bitmap_ppn, result);
822 		return (false);
823 	}
824 	return (true);
825 }
826 
827 /*
828  *------------------------------------------------------------------------------
829  *
830  * vmci_doorbell_fire_entries --
831  *
832  *     Executes or schedules the handlers for a given notify index.
833  *
834  * Result:
835  *     Notification hash entry if found. NULL otherwise.
836  *
837  * Side effects:
838  *     Whatever the side effects of the handlers are.
839  *
840  *------------------------------------------------------------------------------
841  */
842 
843 static void
844 vmci_doorbell_fire_entries(uint32_t notify_idx)
845 {
846 	struct vmci_doorbell_entry *iter;
847 	uint32_t bucket = VMCI_DOORBELL_HASH(notify_idx);
848 
849 	vmci_grab_lock_bh(&vmci_doorbell_it.lock);
850 
851 	vmci_list_scan(iter, &vmci_doorbell_it.entries[bucket], idx_list_item) {
852 		if (iter->idx == notify_idx &&
853 		    atomic_load_int(&iter->active) == 1) {
854 			ASSERT(iter->notify_cb);
855 			if (iter->run_delayed) {
856 				int err;
857 
858 				vmci_resource_hold(&iter->resource);
859 				err = vmci_schedule_delayed_work(
860 				    vmci_doorbell_delayed_dispatch_cb, iter);
861 				if (err != VMCI_SUCCESS) {
862 					vmci_resource_release(&iter->resource);
863 					goto out;
864 				}
865 			} else
866 				iter->notify_cb(iter->client_data);
867 		}
868 	}
869 
870 out:
871 	vmci_release_lock_bh(&vmci_doorbell_it.lock);
872 }
873 
874 /*
875  *------------------------------------------------------------------------------
876  *
877  * vmci_scan_notification_bitmap --
878  *
879  *     Scans the notification bitmap, collects pending notifications, resets
880  *     the bitmap and invokes appropriate callbacks.
881  *
882  * Results:
883  *     None.
884  *
885  * Side effects:
886  *     May schedule tasks, allocate memory and run callbacks.
887  *
888  *------------------------------------------------------------------------------
889  */
890 
891 void
892 vmci_scan_notification_bitmap(uint8_t *bitmap)
893 {
894 	uint32_t idx;
895 
896 	ASSERT(bitmap);
897 
898 	for (idx = 0; idx < max_notify_idx; idx++) {
899 		if (bitmap[idx] & 0x1) {
900 			bitmap[idx] &= ~1;
901 			vmci_doorbell_fire_entries(idx);
902 		}
903 	}
904 }
905