1
/*********************************************************
2
* Copyright (C) 2010 VMware, Inc. All rights reserved.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License as published by the
6
* Free Software Foundation version 2 and no later version.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* You should have received a copy of the GNU General Public License along
14
* with this program; if not, write to the Free Software Foundation, Inc.,
15
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
*********************************************************/
22
* This file implements the VMCI doorbell API on the host.
25
#include "vmci_kernel_if.h"
26
#include "vm_assert.h"
27
#include "vmci_defs.h"
28
#include "vmci_infrastructure.h"
29
#include "vmciCommonInt.h"
30
#include "vmciDatagram.h"
31
#include "vmciDoorbell.h"
32
#include "vmciDriver.h"
33
#include "vmciKernelAPI.h"
34
#include "vmciResource.h"
35
#include "vmciRoute.h"
37
# include "vmciVmkInt.h"
39
# include "helper_ext.h"
42
#define LGPFX "VMCIDoorbell: "
44
#if !defined(SOLARIS) && !defined(__APPLE__)
47
/* VMK doesn't need BH locks, so use lower ranks. */
48
# define VMCIDoorbellInitLock(_l, _n) \
49
VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_HIGHER)
50
# define VMCIDoorbellGrabLock(_l, _f) VMCI_GrabLock(_l, _f)
51
# define VMCIDoorbellReleaseLock(_l, _f) VMCI_ReleaseLock(_l, _f)
53
# define VMCIDoorbellInitLock(_l, _n) \
54
VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_MIDDLE_BH)
55
# define VMCIDoorbellGrabLock(_l, _f) VMCI_GrabLock_BH(_l, _f)
56
# define VMCIDoorbellReleaseLock(_l, _f) VMCI_ReleaseLock_BH(_l, _f)
59
#define VMCI_DOORBELL_INDEX_TABLE_SIZE 64
60
#define VMCI_DOORBELL_HASH(_idx) \
61
VMCI_HashId((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE)
65
* DoorbellEntry describes the a doorbell notification handle allocated by the
69
typedef struct VMCIDoorbellEntry {
70
VMCIResource resource;
72
VMCIListItem idxListItem;
73
VMCIPrivilegeFlags privFlags;
76
VMCICallback notifyCB;
78
VMCIEvent destroyEvent;
81
typedef struct VMCIDoorbellIndexTable {
83
VMCIList entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
84
} VMCIDoorbellIndexTable;
87
/* The VMCI index table keeps track of currently registered doorbells. */
88
static VMCIDoorbellIndexTable vmciDoorbellIT;
92
* The maxNotifyIdx is one larger than the currently known bitmap index in
93
* use, and is used to determine how much of the bitmap needs to be scanned.
96
static uint32 maxNotifyIdx;
99
* The notifyIdxCount is used for determining whether there are free entries
100
* within the bitmap (if notifyIdxCount + 1 < maxNotifyIdx).
103
static uint32 notifyIdxCount;
106
* The lastNotifyIdxReserved is used to track the last index handed out - in
107
* the case where multiple handles share a notification index, we hand out
108
* indexes round robin based on lastNotifyIdxReserved.
111
static uint32 lastNotifyIdxReserved;
113
/* This is a one entry cache used to by the index allocation. */
114
static uint32 lastNotifyIdxReleased = PAGE_SIZE;
117
static void VMCIDoorbellFreeCB(void *clientData);
118
static int VMCIDoorbellReleaseCB(void *clientData);
119
static void VMCIDoorbellDelayedDispatchCB(void *data);
123
*------------------------------------------------------------------------------
125
* VMCIDoorbell_Init --
135
*------------------------------------------------------------------------------
139
VMCIDoorbell_Init(void)
143
for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); ++bucket) {
144
VMCIList_Init(&vmciDoorbellIT.entries[bucket]);
147
VMCIDoorbellInitLock(&vmciDoorbellIT.lock, "VMCIDoorbellIndexTableLock");
152
*------------------------------------------------------------------------------
154
* VMCIDoorbell_Exit --
164
*------------------------------------------------------------------------------
168
VMCIDoorbell_Exit(void)
170
VMCI_CleanupLock(&vmciDoorbellIT.lock);
175
*------------------------------------------------------------------------------
177
* VMCIDoorbellFreeCB --
179
* Callback to free doorbell entry structure when resource is no longer used,
180
* ie. the reference count reached 0. The entry is freed in
181
* VMCIDoorbell_Destroy(), which is waiting on the signal that gets fired
188
* Signals VMCI event.
190
*------------------------------------------------------------------------------
194
VMCIDoorbellFreeCB(void *clientData) // IN
196
VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData;
198
VMCI_SignalEvent(&entry->destroyEvent);
203
*------------------------------------------------------------------------------
205
* VMCIDoorbellReleaseCB --
207
* Callback to release the resource reference. It is called by the
208
* VMCI_WaitOnEvent function before it blocks.
216
*------------------------------------------------------------------------------
220
VMCIDoorbellReleaseCB(void *clientData) // IN: doorbell entry
222
VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData;
224
VMCIResource_Release(&entry->resource);
230
*------------------------------------------------------------------------------
232
* VMCIDoorbellGetPrivFlags --
234
* Utility function that retrieves the privilege flags associated
235
* with a given doorbell handle. For guest endpoints, the
236
* privileges are determined by the context ID, but for host
237
* endpoints privileges are associated with the complete
238
* handle. Hypervisor endpoints are not yet supported.
241
* VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
246
*------------------------------------------------------------------------------
250
VMCIDoorbellGetPrivFlags(VMCIHandle handle, // IN
251
VMCIPrivilegeFlags *privFlags) // OUT
253
if (privFlags == NULL || handle.context == VMCI_INVALID_ID) {
254
return VMCI_ERROR_INVALID_ARGS;
257
if (handle.context == VMCI_HOST_CONTEXT_ID) {
258
VMCIDoorbellEntry *entry;
259
VMCIResource *resource;
261
resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
262
if (resource == NULL) {
263
return VMCI_ERROR_INVALID_ARGS;
265
entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
266
*privFlags = entry->privFlags;
267
VMCIResource_Release(resource);
268
} else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
269
/* Hypervisor endpoints for notifications are not supported (yet). */
270
return VMCI_ERROR_INVALID_ARGS;
272
*privFlags = VMCIContext_GetPrivFlags(handle.context);
280
*-----------------------------------------------------------------------------
282
* VMCIDoorbellIndexTableFind --
284
* Find doorbell entry by bitmap index.
287
* Entry if found, NULL if not.
292
*-----------------------------------------------------------------------------
295
static VMCIDoorbellEntry *
296
VMCIDoorbellIndexTableFind(uint32 idx) // IN
298
uint32 bucket = VMCI_DOORBELL_HASH(idx);
301
ASSERT(VMCI_GuestPersonalityActive());
303
VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
304
VMCIDoorbellEntry *cur =
305
VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
309
if (idx == cur->idx) {
319
*------------------------------------------------------------------------------
321
* VMCIDoorbellIndexTableAdd --
323
* Add the given entry to the index table. This will hold() the entry's
324
* resource so that the entry is not deleted before it is removed from the
333
*------------------------------------------------------------------------------
337
VMCIDoorbellIndexTableAdd(VMCIDoorbellEntry *entry) // IN/OUT
344
ASSERT(VMCI_GuestPersonalityActive());
346
VMCIResource_Hold(&entry->resource);
348
VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
351
* Below we try to allocate an index in the notification bitmap with "not
352
* too much" sharing between resources. If we use less that the full bitmap,
353
* we either add to the end if there are no unused flags within the
354
* currently used area, or we search for unused ones. If we use the full
355
* bitmap, we allocate the index round robin.
358
if (maxNotifyIdx < PAGE_SIZE || notifyIdxCount < PAGE_SIZE) {
359
if (lastNotifyIdxReleased < maxNotifyIdx &&
360
!VMCIDoorbellIndexTableFind(lastNotifyIdxReleased)) {
361
newNotifyIdx = lastNotifyIdxReleased;
362
lastNotifyIdxReleased = PAGE_SIZE;
365
newNotifyIdx = lastNotifyIdxReserved;
366
if (notifyIdxCount + 1 < maxNotifyIdx) {
368
if (!VMCIDoorbellIndexTableFind(newNotifyIdx)) {
372
newNotifyIdx = (newNotifyIdx + 1) % maxNotifyIdx;
373
} while(newNotifyIdx != lastNotifyIdxReleased);
376
newNotifyIdx = maxNotifyIdx;
381
newNotifyIdx = (lastNotifyIdxReserved + 1) % PAGE_SIZE;
383
lastNotifyIdxReserved = newNotifyIdx;
386
entry->idx = newNotifyIdx;
387
bucket = VMCI_DOORBELL_HASH(entry->idx);
388
VMCIList_Insert(&entry->idxListItem, &vmciDoorbellIT.entries[bucket]);
390
VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
395
*------------------------------------------------------------------------------
397
* VMCIDoorbellIndexTableRemove --
399
* Remove the given entry from the index table. This will release() the
408
*------------------------------------------------------------------------------
412
VMCIDoorbellIndexTableRemove(VMCIDoorbellEntry *entry) // IN/OUT
418
ASSERT(VMCI_GuestPersonalityActive());
420
VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
422
bucket = VMCI_DOORBELL_HASH(entry->idx);
423
VMCIList_Remove(&entry->idxListItem, &vmciDoorbellIT.entries[bucket]);
426
if (entry->idx == maxNotifyIdx - 1) {
428
* If we delete an entry with the maximum known notification index, we
429
* take the opportunity to prune the current max. As there might be other
430
* unused indices immediately below, we lower the maximum until we hit an
434
while (maxNotifyIdx > 0 &&
435
!VMCIDoorbellIndexTableFind(maxNotifyIdx - 1)) {
439
lastNotifyIdxReleased = entry->idx;
441
VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
443
VMCIResource_Release(&entry->resource);
448
*------------------------------------------------------------------------------
450
* VMCIDoorbellLink --
452
* Creates a link between the given doorbell handle and the given
453
* index in the bitmap in the device backend.
456
* VMCI_SUCCESS if success, appropriate error code otherwise.
459
* Notification state is created in hypervisor.
461
*------------------------------------------------------------------------------
465
VMCIDoorbellLink(VMCIHandle handle, // IN
466
Bool isDoorbell, // IN
467
uint32 notifyIdx) // IN
469
#if defined(VMKERNEL)
470
VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
471
return VMCI_ERROR_DST_UNREACHABLE;
474
VMCIDoorbellLinkMsg linkMsg;
476
ASSERT(!VMCI_HANDLE_INVALID(handle));
477
ASSERT(VMCI_GuestPersonalityActive());
480
resourceID = VMCI_DOORBELL_LINK;
483
return VMCI_ERROR_UNAVAILABLE;
486
linkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
487
linkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
488
linkMsg.hdr.payloadSize = sizeof linkMsg - VMCI_DG_HEADERSIZE;
489
linkMsg.handle = handle;
490
linkMsg.notifyIdx = notifyIdx;
492
return VMCI_SendDatagram((VMCIDatagram *)&linkMsg);
498
*------------------------------------------------------------------------------
500
* VMCIDoorbellUnlink --
502
* Unlinks the given doorbell handle from an index in the bitmap in
503
* the device backend.
506
* VMCI_SUCCESS if success, appropriate error code otherwise.
509
* Notification state is destroyed in hypervisor.
511
*------------------------------------------------------------------------------
515
VMCIDoorbellUnlink(VMCIHandle handle, // IN
516
Bool isDoorbell) // IN
518
#if defined(VMKERNEL)
519
VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
520
return VMCI_ERROR_DST_UNREACHABLE;
523
VMCIDoorbellUnlinkMsg unlinkMsg;
525
ASSERT(!VMCI_HANDLE_INVALID(handle));
526
ASSERT(VMCI_GuestPersonalityActive());
529
resourceID = VMCI_DOORBELL_UNLINK;
532
return VMCI_ERROR_UNAVAILABLE;
535
unlinkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
536
unlinkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
537
unlinkMsg.hdr.payloadSize = sizeof unlinkMsg - VMCI_DG_HEADERSIZE;
538
unlinkMsg.handle = handle;
540
return VMCI_SendDatagram((VMCIDatagram *)&unlinkMsg);
546
*------------------------------------------------------------------------------
548
* VMCIDoorbell_Create --
550
* Creates a doorbell with the given callback. If the handle is
551
* VMCI_INVALID_HANDLE, a free handle will be assigned, if
552
* possible. The callback can be run immediately (potentially with
553
* locks held - the default) or delayed (in a kernel thread) by
554
* specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution
555
* is selected, a given callback may not be run if the kernel is
556
* unable to allocate memory for the delayed execution (highly
560
* VMCI_SUCCESS on success, appropriate error code otherwise.
565
*------------------------------------------------------------------------------
568
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
570
VMCIDoorbell_Create(VMCIHandle *handle, // IN/OUT
572
VMCIPrivilegeFlags privFlags, // IN
573
VMCICallback notifyCB, // IN
574
void *clientData) // IN
576
VMCIDoorbellEntry *entry;
577
VMCIHandle newHandle;
580
if (!handle || !notifyCB || flags & ~VMCI_FLAG_DELAYED_CB ||
581
privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
582
return VMCI_ERROR_INVALID_ARGS;
585
entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED);
587
VMCI_WARNING((LGPFX"Failed allocating memory for datagram entry.\n"));
588
return VMCI_ERROR_NO_MEM;
591
if (!VMCI_CanScheduleDelayedWork() && (flags & VMCI_FLAG_DELAYED_CB)) {
592
result = VMCI_ERROR_INVALID_ARGS;
596
if (VMCI_HANDLE_INVALID(*handle)) {
597
VMCIId contextID = VMCI_GetContextID();
598
VMCIId resourceID = VMCIResource_GetID(contextID);
599
if (resourceID == VMCI_INVALID_ID) {
600
result = VMCI_ERROR_NO_HANDLE;
603
newHandle = VMCI_MAKE_HANDLE(contextID, resourceID);
608
* Validate the handle. We must do both of the checks below
609
* because we can be acting as both a host and a guest at the
610
* same time. We always allow the host context ID, since the
611
* host functionality is in practice always there with the
615
validContext = FALSE;
616
if (VMCI_HOST_CONTEXT_ID == handle->context) {
619
if (VMCI_GuestPersonalityActive() && VMCI_GetContextID() == handle->context) {
623
if (!validContext || VMCI_INVALID_ID == handle->resource) {
624
VMCI_DEBUG_LOG(4, (LGPFX"Invalid argument (handle=0x%x:0x%x).\n",
625
handle->context, handle->resource));
626
result = VMCI_ERROR_INVALID_ARGS;
634
VMCIList_Init(&entry->idxListItem);
635
entry->privFlags = privFlags;
636
entry->isDoorbell = TRUE;
637
entry->runDelayed = (flags & VMCI_FLAG_DELAYED_CB) ? TRUE : FALSE;
638
entry->notifyCB = notifyCB;
639
entry->clientData = clientData;
640
VMCI_CreateEvent(&entry->destroyEvent);
642
result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DOORBELL,
643
newHandle, VMCIDoorbellFreeCB, entry);
644
if (result != VMCI_SUCCESS) {
645
VMCI_WARNING((LGPFX"Failed to add new resource (handle=0x%x:0x%x).\n",
646
newHandle.context, newHandle.resource));
650
if (VMCI_GuestPersonalityActive()) {
651
result = VMCIDoorbellLink(newHandle, entry->isDoorbell, entry->idx);
652
if (VMCI_SUCCESS != result) {
653
goto destroyResource;
655
VMCIDoorbellIndexTableAdd(entry);
658
if (VMCI_HANDLE_INVALID(*handle)) {
665
VMCIResource_Remove(newHandle, VMCI_RESOURCE_TYPE_DOORBELL);
667
VMCI_DestroyEvent(&entry->destroyEvent);
669
VMCI_FreeKernelMem(entry, sizeof *entry);
675
*------------------------------------------------------------------------------
677
* VMCIDoorbell_Destroy --
679
* Destroys a doorbell previously created with
680
* VMCIDoorbell_Create. This operation may block waiting for a
681
* callback to finish.
684
* VMCI_SUCCESS on success, appropriate error code otherwise.
689
*------------------------------------------------------------------------------
692
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
694
VMCIDoorbell_Destroy(VMCIHandle handle) // IN
696
VMCIDoorbellEntry *entry;
697
VMCIResource *resource;
699
if (VMCI_HANDLE_INVALID(handle)) {
700
return VMCI_ERROR_INVALID_ARGS;
703
resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
704
if (resource == NULL) {
705
VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy doorbell (handle=0x%x:0x%x).\n",
706
handle.context, handle.resource));
707
return VMCI_ERROR_NOT_FOUND;
709
entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
711
if (VMCI_GuestPersonalityActive()) {
714
VMCIDoorbellIndexTableRemove(entry);
716
result = VMCIDoorbellUnlink(handle, entry->isDoorbell);
717
if (VMCI_SUCCESS != result) {
720
* The only reason this should fail would be an inconsistency between
721
* guest and hypervisor state, where the guest believes it has an
722
* active registration whereas the hypervisor doesn't. One case where
723
* this may happen is if a doorbell is unregistered following a
724
* hibernation at a time where the doorbell state hasn't been restored
725
* on the hypervisor side yet. Since the handle has now been removed
726
* in the guest, we just print a warning and return success.
729
VMCI_DEBUG_LOG(4, (LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown by "
730
"hypervisor (error=%d).\n",
731
entry->isDoorbell ? "doorbell" : "queuepair",
732
handle.context, handle.resource, result));
737
* Now remove the resource from the table. It might still be in use
738
* after this, in a callback or still on the delayed work queue.
741
VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DOORBELL);
744
* We now wait on the destroyEvent and release the reference we got
748
VMCI_WaitOnEvent(&entry->destroyEvent, VMCIDoorbellReleaseCB, entry);
751
* We know that we are now the only reference to the above entry so
752
* can safely free it.
755
VMCI_DestroyEvent(&entry->destroyEvent);
756
VMCI_FreeKernelMem(entry, sizeof *entry);
763
*------------------------------------------------------------------------------
765
* VMCIDoorbellNotifyAsGuest --
767
* Notify another guest or the host. We send a datagram down to the
768
* host via the hypervisor with the notification info.
771
* VMCI_SUCCESS on success, appropriate error code otherwise.
774
* May do a hypercall.
776
*------------------------------------------------------------------------------
780
VMCIDoorbellNotifyAsGuest(VMCIHandle handle, // IN
781
VMCIPrivilegeFlags privFlags) // IN
783
#if defined(VMKERNEL)
784
VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
785
return VMCI_ERROR_DST_UNREACHABLE;
787
VMCIDoorbellNotifyMsg notifyMsg;
789
ASSERT(VMCI_GuestPersonalityActive());
791
notifyMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
792
VMCI_DOORBELL_NOTIFY);
793
notifyMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
794
notifyMsg.hdr.payloadSize = sizeof notifyMsg - VMCI_DG_HEADERSIZE;
795
notifyMsg.handle = handle;
797
return VMCI_SendDatagram((VMCIDatagram *)¬ifyMsg);
803
*------------------------------------------------------------------------------
805
* VMCIDoorbell_Notify --
807
* Generates a notification on the doorbell identified by the
808
* handle. For host side generation of notifications, the caller
809
* can specify what the privilege of the calling side is.
812
* VMCI_SUCCESS on success, appropriate error code otherwise.
815
* May do a hypercall.
817
*------------------------------------------------------------------------------
820
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
822
VMCIDoorbell_Notify(VMCIHandle dst, // IN
823
VMCIPrivilegeFlags privFlags) // IN
829
if (VMCI_HANDLE_INVALID(dst) || (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS)) {
830
return VMCI_ERROR_INVALID_ARGS;
833
src = VMCI_INVALID_HANDLE;
834
retval = VMCI_Route(&src, &dst, FALSE, &route);
835
if (retval < VMCI_SUCCESS) {
839
if (VMCI_ROUTE_AS_HOST == route) {
840
return VMCIContext_NotifyDoorbell(VMCI_HOST_CONTEXT_ID, dst, privFlags);
843
if (VMCI_ROUTE_AS_GUEST == route) {
844
return VMCIDoorbellNotifyAsGuest(dst, privFlags);
847
VMCI_WARNING((LGPFX"Unknown route (%d) for doorbell.\n", route));
848
return VMCI_ERROR_DST_UNREACHABLE;
853
*------------------------------------------------------------------------------
855
* VMCIDoorbellDelayedDispatchCB --
857
* Calls the specified callback in a delayed context.
865
*------------------------------------------------------------------------------
869
VMCIDoorbellDelayedDispatchCB(void *data) // IN
871
VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)data;
875
entry->notifyCB(entry->clientData);
877
VMCIResource_Release(&entry->resource);
882
*------------------------------------------------------------------------------
884
* VMCIDoorbellHostContextNotify --
886
* Dispatches a doorbell notification to the host context.
889
* VMCI_SUCCESS on success. Appropriate error code otherwise.
894
*------------------------------------------------------------------------------
898
VMCIDoorbellHostContextNotify(VMCIId srcCID, // IN
899
VMCIHandle handle) // IN
901
VMCIDoorbellEntry *entry;
902
VMCIResource *resource;
905
ASSERT(VMCI_HostPersonalityActive());
907
resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
908
if (resource == NULL) {
910
(LGPFX"Notifying an invalid doorbell (handle=0x%x:0x%x).\n",
911
handle.context, handle.resource));
912
return VMCI_ERROR_INVALID_ARGS;
914
entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
916
if (entry->runDelayed) {
917
result = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, entry);
918
if (result < VMCI_SUCCESS) {
920
* If we failed to schedule the delayed work, we need to
921
* release the resource immediately. Otherwise, the resource
922
* will be released once the delayed callback has been
926
VMCI_DEBUG_LOG(10, (LGPFX"Failed to schedule delayed doorbell "
927
"notification (result=%d).\n", result));
928
VMCIResource_Release(resource);
931
entry->notifyCB(entry->clientData);
932
VMCIResource_Release(resource);
933
result = VMCI_SUCCESS;
940
*------------------------------------------------------------------------------
942
* VMCIDoorbell_Hibernate --
944
* When a guest leaves hibernation, the device driver state is out of sync
945
* with the device state, since the driver state has doorbells registered
946
* that aren't known to the device. This function takes care of
947
* reregistering any doorbells. In case an error occurs during
948
* reregistration (this is highly unlikely since 1) it succeeded the first
949
* time 2) the device driver is the only source of doorbell registrations),
950
* we simply log the error. The doorbell can still be destroyed using
951
* VMCIDoorbell_Destroy.
959
*------------------------------------------------------------------------------
963
VMCIDoorbell_Hibernate(Bool enterHibernate)
969
if (!VMCI_GuestPersonalityActive() || enterHibernate) {
973
VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
975
for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); bucket++) {
976
VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
978
VMCIDoorbellEntry *cur;
980
cur = VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
981
result = VMCIDoorbellLink(cur->resource.handle, cur->isDoorbell,
983
if (result != VMCI_SUCCESS && result != VMCI_ERROR_DUPLICATE_ENTRY) {
984
VMCI_WARNING((LGPFX"Failed to reregister doorbell "
985
"(handle=0x%x:0x%x) of resource %s to index "
987
cur->resource.handle.context,
988
cur->resource.handle.resource,
989
cur->isDoorbell ? "doorbell" : "queue pair", result));
994
VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
999
*------------------------------------------------------------------------------
1001
* VMCIDoorbell_Sync --
1003
* Use this as a synchronization point when setting globals, for example,
1004
* during device shutdown.
1012
*------------------------------------------------------------------------------
1016
VMCIDoorbell_Sync(void)
1018
VMCILockFlags flags;
1019
VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
1020
VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
1021
VMCIResource_Sync();
1026
*------------------------------------------------------------------------------
1028
* VMCI_RegisterNotificationBitmap --
1030
* Register the notification bitmap with the host.
1033
* TRUE if the bitmap is registered successfully with the device, FALSE
1039
*------------------------------------------------------------------------------
1043
VMCI_RegisterNotificationBitmap(PPN bitmapPPN)
1046
VMCINotifyBitmapSetMsg bitmapSetMsg;
1049
* Do not ASSERT() on the guest device here. This function can get called
1050
* during device initialization, so the ASSERT() will fail even though
1051
* the device is (almost) up.
1054
bitmapSetMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
1055
VMCI_SET_NOTIFY_BITMAP);
1056
bitmapSetMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
1057
bitmapSetMsg.hdr.payloadSize = sizeof bitmapSetMsg - VMCI_DG_HEADERSIZE;
1058
bitmapSetMsg.bitmapPPN = bitmapPPN;
1060
result = VMCI_SendDatagram((VMCIDatagram *)&bitmapSetMsg);
1061
if (result != VMCI_SUCCESS) {
1062
VMCI_DEBUG_LOG(4, (LGPFX"Failed to register (PPN=%u) as "
1063
"notification bitmap (error=%d).\n",
1064
bitmapPPN, result));
1072
*-------------------------------------------------------------------------
1074
* VMCIDoorbellFireEntries --
1076
* Executes or schedules the handlers for a given notify index.
1079
* Notification hash entry if found. NULL otherwise.
1082
* Whatever the side effects of the handlers are.
1084
*-------------------------------------------------------------------------
1088
VMCIDoorbellFireEntries(uint32 notifyIdx) // IN
1090
uint32 bucket = VMCI_DOORBELL_HASH(notifyIdx);
1092
VMCILockFlags flags;
1094
ASSERT(VMCI_GuestPersonalityActive());
1096
VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
1098
VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
1099
VMCIDoorbellEntry *cur =
1100
VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
1104
if (cur->idx == notifyIdx) {
1105
ASSERT(cur->notifyCB);
1106
if (cur->runDelayed) {
1109
VMCIResource_Hold(&cur->resource);
1110
err = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, cur);
1111
if (err != VMCI_SUCCESS) {
1112
VMCIResource_Release(&cur->resource);
1116
cur->notifyCB(cur->clientData);
1122
VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
1127
*------------------------------------------------------------------------------
1129
* VMCI_ScanNotificationBitmap --
1131
* Scans the notification bitmap, collects pending notifications,
1132
* resets the bitmap and invokes appropriate callbacks.
1138
* May schedule tasks, allocate memory and run callbacks.
1140
*------------------------------------------------------------------------------
1144
VMCI_ScanNotificationBitmap(uint8 *bitmap)
1149
ASSERT(VMCI_GuestPersonalityActive());
1151
for (idx = 0; idx < maxNotifyIdx; idx++) {
1152
if (bitmap[idx] & 0x1) {
1154
VMCIDoorbellFireEntries(idx);
1160
#else // SOLARIS) || __APPLE__
1163
*-----------------------------------------------------------------------------
1165
* VMCIDoorbell_Create/VMCIDoorbell_Destroy/VMCIDoorbell_Notify/
1166
* VMCIDoorbellHostContextNotify/VMCIDoorbellGetPrivFlags/
1167
* VMCIDoorbell_Init/VMCIDoorbell_Exit --
1169
* The doorbell functions have yet to be implemented for Solaris
1170
* and Mac OS X guest drivers.
1173
* VMCI_ERROR_UNAVAILABLE.
1178
*-----------------------------------------------------------------------------
1181
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
1183
VMCIDoorbell_Create(VMCIHandle *handle, // IN
1185
VMCIPrivilegeFlags privFlags, // IN
1186
VMCICallback notifyCB, // IN
1187
void *clientData) // IN
1189
return VMCI_ERROR_UNAVAILABLE;
1193
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
1195
VMCIDoorbell_Destroy(VMCIHandle handle) // IN
1197
return VMCI_ERROR_UNAVAILABLE;
1201
VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
1203
VMCIDoorbell_Notify(VMCIHandle handle, // IN
1204
VMCIPrivilegeFlags privFlags) // IN
1206
return VMCI_ERROR_UNAVAILABLE;
1211
VMCIDoorbellHostContextNotify(VMCIId srcCID, // IN
1212
VMCIHandle handle) // IN
1214
return VMCI_ERROR_UNAVAILABLE;
1219
VMCIDoorbellGetPrivFlags(VMCIHandle handle, // IN
1220
VMCIPrivilegeFlags *privFlags) // OUT
1222
return VMCI_ERROR_UNAVAILABLE;
1227
VMCIDoorbell_Init(void)
1233
VMCIDoorbell_Exit(void)
1237
#endif // SOLARIS) || __APPLE__