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