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