}
-/*
- *------------------------------------------------------------------------------
- *
- * VMCIDoorbellHostContextNotify --
- *
- * Stub. Not called in the guest driver (yet).
- *
- * Result:
- * Always VMCI_ERROR_GENERIC.
- *
- *------------------------------------------------------------------------------
- */
-
-int
-VMCIDoorbellHostContextNotify(VMCIId srcCID,
- VMCIHandle handle)
-{
- return VMCI_ERROR_GENERIC;
-}
-
-
-/*
- *------------------------------------------------------------------------------
- *
- * VMCIDoorbellGetPrivFlags --
- *
- * Stub. Not called in the guest driver (yet).
- *
- * Result:
- * Always VMCI_ERROR_GENERIC.
- *
- *------------------------------------------------------------------------------
- */
-
-int
-VMCIDoorbellGetPrivFlags(VMCIHandle handle,
- VMCIPrivilegeFlags *privFlags)
-{
- return VMCI_ERROR_GENERIC;
-}
-
-
/*
*------------------------------------------------------------------------------
*
/*
*------------------------------------------------------------------------------
*
- * VMCIDatagramDispatchFromHostToSelfOrGuest --
+ * VMCIDatagramDispatchAsHost --
*
- * Dispatch datagram to host or other vm context. This function cannot
- * dispatch to hypervisor context handlers. This should have been handled
- * before we get here by VMCIDatagramDispatch.
+ * Dispatch datagram as a host, to the host or other vm context. This
+ * function cannot dispatch to hypervisor context handlers. This should
+ * have been handled before we get here by VMCIDatagramDispatch.
*
* Result:
* Number of bytes sent on success, appropriate error code otherwise.
*/
static int
-VMCIDatagramDispatchFromHostToSelfOrGuest(VMCIId contextID, // IN:
- VMCIDatagram *dg) // IN:
+VMCIDatagramDispatchAsHost(VMCIId contextID, // IN:
+ VMCIDatagram *dg) // IN:
{
int retval;
size_t dgSize;
char dstDomain[VMCI_DOMAIN_NAME_MAXLEN]; /* Not used on hosted. */
ASSERT(dg);
+ ASSERT(VMCI_HasHostDevice());
dgSize = VMCI_DG_SIZE(dg);
/*
*------------------------------------------------------------------------------
*
- * VMCIDatagramDispatchFromGuestToHost --
+ * VMCIDatagramDispatchAsGuest --
*
- * Dispatch datagram down through the VMX, and potentially to the host.
+ * Dispatch datagram as a guest, down through the VMX and potentially to
+ * the host.
*
* Result:
* Number of bytes sent on success, appropriate error code otherwise.
*/
static int
-VMCIDatagramDispatchFromGuestToHost(VMCIDatagram *dg)
+VMCIDatagramDispatchAsGuest(VMCIDatagram *dg)
{
#if defined(VMKERNEL)
VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
VMCIResource *resource;
extern int VMCI_SendDatagram(VMCIDatagram *);
+ ASSERT(VMCI_HasGuestDevice());
+
resource = VMCIResource_Get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM);
if (NULL == resource) {
return VMCI_ERROR_NO_HANDLE;
return retval;
}
- if (VMCI_ROUTE_HOST_TO_SELF == route ||
- VMCI_ROUTE_HOST_TO_GUEST == route) {
+ if (VMCI_ROUTE_AS_HOST == route) {
if (VMCI_INVALID_ID == contextID) {
contextID = VMCI_HOST_CONTEXT_ID;
}
- return VMCIDatagramDispatchFromHostToSelfOrGuest(contextID, dg);
+ return VMCIDatagramDispatchAsHost(contextID, dg);
}
- if (VMCI_ROUTE_GUEST_TO_HOST == route ||
- VMCI_ROUTE_GUEST_TO_HYPERVISOR == route) {
- return VMCIDatagramDispatchFromGuestToHost(dg);
+ if (VMCI_ROUTE_AS_GUEST == route) {
+ return VMCIDatagramDispatchAsGuest(dg);
}
VMCI_WARNING((LGPFX"Unknown route (%d) for datagram.\n", route));
DatagramEntry *dstEntry;
ASSERT(dg);
+ ASSERT(VMCI_HasGuestDevice());
resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM);
if (NULL == resource) {
int VMCIDatagram_Init(void);
void VMCIDatagram_Exit(void);
-
/* Datagram API for non-public use. */
int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *dg, Bool fromGuest);
int VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg);
--- /dev/null
+/*********************************************************
+ * Copyright (C) 2010 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * vmciDoorbell.c --
+ *
+ * This file implements the VMCI doorbell API on the host.
+ */
+
+#if defined(__linux__) && !defined(VMKERNEL)
+# include "driver-config.h"
+# include "compat_kernel.h"
+# include "compat_module.h"
+#endif // __linux__
+#include "vmci_kernel_if.h"
+#include "vm_assert.h"
+#include "vmci_defs.h"
+#include "vmci_infrastructure.h"
+#include "vmciCommonInt.h"
+#include "vmciDatagram.h"
+#include "vmciDoorbell.h"
+#include "vmciKernelAPI.h"
+#include "vmciResource.h"
+#include "vmciRoute.h"
+#ifdef VMX86_TOOLS
+# include "vmciInt.h"
+# include "vmciUtil.h"
+#elif defined(VMKERNEL)
+# include "vmciVmkInt.h"
+# include "vm_libc.h"
+# include "helper_ext.h"
+# include "vmciDriver.h"
+#else
+# include "vmciDriver.h"
+#endif
+
+#define LGPFX "VMCIDoorbell: "
+
+#if !defined(SOLARIS) && !defined(__APPLE__)
+
+#if defined(VMKERNEL)
+ /* VMK doesn't need BH locks, so use lower ranks. */
+# define VMCIDoorbellInitLock(_l, _n) \
+ VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_HIGHER)
+# define VMCIDoorbellGrabLock(_l, _f) VMCI_GrabLock(_l, _f)
+# define VMCIDoorbellReleaseLock(_l, _f) VMCI_ReleaseLock(_l, _f)
+#else // VMKERNEL
+# define VMCIDoorbellInitLock(_l, _n) \
+ VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_MIDDLE_BH)
+# define VMCIDoorbellGrabLock(_l, _f) VMCI_GrabLock_BH(_l, _f)
+# define VMCIDoorbellReleaseLock(_l, _f) VMCI_ReleaseLock_BH(_l, _f)
+#endif // VMKERNEL
+
+#define VMCI_DOORBELL_INDEX_TABLE_SIZE 64
+#define VMCI_DOORBELL_HASH(_idx) \
+ VMCI_HashId((_idx), VMCI_DOORBELL_INDEX_TABLE_SIZE)
+
+
+/*
+ * DoorbellEntry describes the a doorbell notification handle allocated by the
+ * host.
+ */
+
+typedef struct VMCIDoorbellEntry {
+ VMCIResource resource;
+ uint32 idx;
+ VMCIListItem idxListItem;
+ VMCIPrivilegeFlags privFlags;
+ Bool isDoorbell;
+ Bool runDelayed;
+ VMCICallback notifyCB;
+ void *clientData;
+ VMCIEvent destroyEvent;
+} VMCIDoorbellEntry;
+
+typedef struct VMCIDoorbellIndexTable {
+ VMCILock lock;
+ VMCIList entries[VMCI_DOORBELL_INDEX_TABLE_SIZE];
+} VMCIDoorbellIndexTable;
+
+
+/* The VMCI index table keeps track of currently registered doorbells. */
+static VMCIDoorbellIndexTable vmciDoorbellIT;
+
+
+/*
+ * The maxNotifyIdx is one larger than the currently known bitmap index in
+ * use, and is used to determine how much of the bitmap needs to be scanned.
+ */
+
+static uint32 maxNotifyIdx;
+
+/*
+ * The notifyIdxCount is used for determining whether there are free entries
+ * within the bitmap (if notifyIdxCount + 1 < maxNotifyIdx).
+ */
+
+static uint32 notifyIdxCount;
+
+/*
+ * The lastNotifyIdxReserved is used to track the last index handed out - in
+ * the case where multiple handles share a notification index, we hand out
+ * indexes round robin based on lastNotifyIdxReserved.
+ */
+
+static uint32 lastNotifyIdxReserved;
+
+/* This is a one entry cache used to by the index allocation. */
+static uint32 lastNotifyIdxReleased = PAGE_SIZE;
+
+
+static void VMCIDoorbellFreeCB(void *clientData);
+static int VMCIDoorbellReleaseCB(void *clientData);
+static void VMCIDoorbellDelayedDispatchCB(void *data);
+extern int VMCI_SendDatagram(VMCIDatagram *);
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Init --
+ *
+ * General init code.
+ *
+ * Result:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIDoorbell_Init(void)
+{
+ uint32 bucket;
+
+ for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); ++bucket) {
+ VMCIList_Init(&vmciDoorbellIT.entries[bucket]);
+ }
+
+ VMCIDoorbellInitLock(&vmciDoorbellIT.lock, "VMCIDoorbellIndexTableLock");
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Exit --
+ *
+ * General init code.
+ *
+ * Result:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIDoorbell_Exit(void)
+{
+ VMCI_CleanupLock(&vmciDoorbellIT.lock);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellFreeCB --
+ *
+ * Callback to free doorbell entry structure when resource is no longer used,
+ * ie. the reference count reached 0. The entry is freed in
+ * VMCIDoorbell_Destroy(), which is waiting on the signal that gets fired
+ * here.
+ *
+ * Result:
+ * None.
+ *
+ * Side effects:
+ * Signals VMCI event.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static void
+VMCIDoorbellFreeCB(void *clientData) // IN
+{
+ VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData;
+ ASSERT(entry);
+ VMCI_SignalEvent(&entry->destroyEvent);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellReleaseCB --
+ *
+ * Callback to release the resource reference. It is called by the
+ * VMCI_WaitOnEvent function before it blocks.
+ *
+ * Result:
+ * Always 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDoorbellReleaseCB(void *clientData) // IN: doorbell entry
+{
+ VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)clientData;
+ ASSERT(entry);
+ VMCIResource_Release(&entry->resource);
+ return 0;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellGetPrivFlags --
+ *
+ * Utility function that retrieves the privilege flags associated
+ * with a given doorbell handle. For guest endpoints, the
+ * privileges are determined by the context ID, but for host
+ * endpoints privileges are associated with the complete
+ * handle. Hypervisor endpoints are not yet supported.
+ *
+ * Result:
+ * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIDoorbellGetPrivFlags(VMCIHandle handle, // IN
+ VMCIPrivilegeFlags *privFlags) // OUT
+{
+ if (privFlags == NULL || handle.context == VMCI_INVALID_ID) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (handle.context == VMCI_HOST_CONTEXT_ID) {
+ VMCIDoorbellEntry *entry;
+ VMCIResource *resource;
+
+ resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
+ if (resource == NULL) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+ entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
+ *privFlags = entry->privFlags;
+ VMCIResource_Release(resource);
+ } else if (handle.context == VMCI_HYPERVISOR_CONTEXT_ID) {
+ /* Hypervisor endpoints for notifications are not supported (yet). */
+ return VMCI_ERROR_INVALID_ARGS;
+ } else {
+ *privFlags = VMCIContext_GetPrivFlags(handle.context);
+ }
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIDoorbellIndexTableFind --
+ *
+ * Find doorbell entry by bitmap index.
+ *
+ * Results:
+ * Entry if found, NULL if not.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static VMCIDoorbellEntry *
+VMCIDoorbellIndexTableFind(uint32 idx) // IN
+{
+ uint32 bucket = VMCI_DOORBELL_HASH(idx);
+ VMCIListItem *iter;
+
+ ASSERT(VMCI_HasGuestDevice());
+
+ VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
+ VMCIDoorbellEntry *cur =
+ VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
+
+ ASSERT(cur);
+
+ if (idx == cur->idx) {
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellIndexTableAdd --
+ *
+ * Add the given entry to the index table. This will hold() the entry's
+ * resource so that the entry is not deleted before it is removed from the
+ * table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static void
+VMCIDoorbellIndexTableAdd(VMCIDoorbellEntry *entry) // IN/OUT
+{
+ uint32 bucket;
+ uint32 newNotifyIdx;
+ VMCILockFlags flags;
+
+ ASSERT(entry);
+ ASSERT(VMCI_HasGuestDevice());
+
+ VMCIResource_Hold(&entry->resource);
+
+ VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
+
+ /*
+ * Below we try to allocate an index in the notification bitmap with "not
+ * too much" sharing between resources. If we use less that the full bitmap,
+ * we either add to the end if there are no unused flags within the
+ * currently used area, or we search for unused ones. If we use the full
+ * bitmap, we allocate the index round robin.
+ */
+
+ if (maxNotifyIdx < PAGE_SIZE || notifyIdxCount < PAGE_SIZE) {
+ if (lastNotifyIdxReleased < maxNotifyIdx &&
+ !VMCIDoorbellIndexTableFind(lastNotifyIdxReleased)) {
+ newNotifyIdx = lastNotifyIdxReleased;
+ lastNotifyIdxReleased = PAGE_SIZE;
+ } else {
+ Bool reused = FALSE;
+ newNotifyIdx = lastNotifyIdxReserved;
+ if (notifyIdxCount + 1 < maxNotifyIdx) {
+ do {
+ if (!VMCIDoorbellIndexTableFind(newNotifyIdx)) {
+ reused = TRUE;
+ break;
+ }
+ newNotifyIdx = (newNotifyIdx + 1) % maxNotifyIdx;
+ } while(newNotifyIdx != lastNotifyIdxReleased);
+ }
+ if (!reused) {
+ newNotifyIdx = maxNotifyIdx;
+ maxNotifyIdx++;
+ }
+ }
+ } else {
+ newNotifyIdx = (lastNotifyIdxReserved + 1) % PAGE_SIZE;
+ }
+ lastNotifyIdxReserved = newNotifyIdx;
+ notifyIdxCount++;
+
+ entry->idx = newNotifyIdx;
+ bucket = VMCI_DOORBELL_HASH(entry->idx);
+ VMCIList_Insert(&entry->idxListItem, &vmciDoorbellIT.entries[bucket]);
+
+ VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellIndexTableRemove --
+ *
+ * Remove the given entry from the index table. This will release() the
+ * entry's resource.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static void
+VMCIDoorbellIndexTableRemove(VMCIDoorbellEntry *entry) // IN/OUT
+{
+ uint32 bucket;
+ VMCILockFlags flags;
+
+ ASSERT(entry);
+ ASSERT(VMCI_HasGuestDevice());
+
+ VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
+
+ bucket = VMCI_DOORBELL_HASH(entry->idx);
+ VMCIList_Remove(&entry->idxListItem, &vmciDoorbellIT.entries[bucket]);
+
+ notifyIdxCount--;
+ if (entry->idx == maxNotifyIdx - 1) {
+ /*
+ * If we delete an entry with the maximum known notification index, we
+ * take the opportunity to prune the current max. As there might be other
+ * unused indices immediately below, we lower the maximum until we hit an
+ * index in use.
+ */
+
+ while (maxNotifyIdx > 0 &&
+ !VMCIDoorbellIndexTableFind(maxNotifyIdx - 1)) {
+ maxNotifyIdx--;
+ }
+ }
+ lastNotifyIdxReleased = entry->idx;
+
+ VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
+
+ VMCIResource_Release(&entry->resource);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellLink --
+ *
+ * Creates a link between the given doorbell handle and the given
+ * index in the bitmap in the device backend.
+ *
+ * Results:
+ * VMCI_SUCCESS if success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * Notification state is created in hypervisor.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDoorbellLink(VMCIHandle handle, // IN
+ Bool isDoorbell, // IN
+ uint32 notifyIdx) // IN
+{
+#if defined(VMKERNEL)
+ VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
+#else // VMKERNEL
+ VMCIId resourceID;
+ VMCIDoorbellLinkMsg linkMsg;
+
+ ASSERT(!VMCI_HANDLE_INVALID(handle));
+ ASSERT(VMCI_HasGuestDevice());
+
+ if (isDoorbell) {
+ resourceID = VMCI_DOORBELL_LINK;
+ } else {
+ ASSERT(FALSE);
+ return VMCI_ERROR_UNAVAILABLE;
+ }
+
+ linkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
+ linkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
+ linkMsg.hdr.payloadSize = sizeof linkMsg - VMCI_DG_HEADERSIZE;
+ linkMsg.handle = handle;
+ linkMsg.notifyIdx = notifyIdx;
+
+ return VMCI_SendDatagram((VMCIDatagram *)&linkMsg);
+#endif // VMKERNEL
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellUnlink --
+ *
+ * Unlinks the given doorbell handle from an index in the bitmap in
+ * the device backend.
+ *
+ * Results:
+ * VMCI_SUCCESS if success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * Notification state is destroyed in hypervisor.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDoorbellUnlink(VMCIHandle handle, // IN
+ Bool isDoorbell) // IN
+{
+#if defined(VMKERNEL)
+ VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
+#else // VMKERNEL
+ VMCIId resourceID;
+ VMCIDoorbellUnlinkMsg unlinkMsg;
+
+ ASSERT(!VMCI_HANDLE_INVALID(handle));
+ ASSERT(VMCI_HasGuestDevice());
+
+ if (isDoorbell) {
+ resourceID = VMCI_DOORBELL_UNLINK;
+ } else {
+ ASSERT(FALSE);
+ return VMCI_ERROR_UNAVAILABLE;
+ }
+
+ unlinkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
+ unlinkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
+ unlinkMsg.hdr.payloadSize = sizeof unlinkMsg - VMCI_DG_HEADERSIZE;
+ unlinkMsg.handle = handle;
+
+ return VMCI_SendDatagram((VMCIDatagram *)&unlinkMsg);
+#endif // VMKERNEL
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Create --
+ *
+ * Creates a doorbell with the given callback. If the handle is
+ * VMCI_INVALID_HANDLE, a free handle will be assigned, if
+ * possible. The callback can be run immediately (potentially with
+ * locks held - the default) or delayed (in a kernel thread) by
+ * specifying the flag VMCI_FLAG_DELAYED_CB. If delayed execution
+ * is selected, a given callback may not be run if the kernel is
+ * unable to allocate memory for the delayed execution (highly
+ * unlikely).
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
+int
+VMCIDoorbell_Create(VMCIHandle *handle, // IN/OUT
+ uint32 flags, // IN
+ VMCIPrivilegeFlags privFlags, // IN
+ VMCICallback notifyCB, // IN
+ void *clientData) // IN
+{
+ VMCIDoorbellEntry *entry;
+ VMCIHandle newHandle;
+ int result;
+
+ if (!handle || !notifyCB || flags & ~VMCI_FLAG_DELAYED_CB ||
+ privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED);
+ if (entry == NULL) {
+ VMCI_WARNING((LGPFX"Failed allocating memory for datagram entry.\n"));
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ if (!VMCI_CanScheduleDelayedWork() && (flags & VMCI_FLAG_DELAYED_CB)) {
+ result = VMCI_ERROR_INVALID_ARGS;
+ goto freeMem;
+ }
+
+ if (VMCI_HANDLE_INVALID(*handle)) {
+ VMCIId contextID = VMCI_GetContextID();
+ VMCIId resourceID = VMCIResource_GetID(contextID);
+ if (resourceID == VMCI_INVALID_ID) {
+ result = VMCI_ERROR_NO_HANDLE;
+ goto freeMem;
+ }
+ newHandle = VMCI_MAKE_HANDLE(contextID, resourceID);
+ } else {
+ Bool validContext;
+
+ /*
+ * Validate the handle. We must do both of the checks below because we
+ * can be acting as both a host and a guest at the same time.
+ */
+
+ validContext = FALSE;
+ if (VMCI_HasHostDevice() && VMCI_HOST_CONTEXT_ID == handle->context) {
+ validContext = TRUE;
+ }
+ if (VMCI_HasGuestDevice() && VMCI_GetContextID() == handle->context) {
+ validContext = TRUE;
+ }
+
+ if (!validContext || VMCI_INVALID_ID == handle->resource) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Invalid argument (handle=0x%x:0x%x).\n",
+ handle->context, handle->resource));
+ result = VMCI_ERROR_INVALID_ARGS;
+ goto freeMem;
+ }
+
+ newHandle = *handle;
+ }
+
+ entry->idx = 0;
+ VMCIList_Init(&entry->idxListItem);
+ entry->privFlags = privFlags;
+ entry->isDoorbell = TRUE;
+ entry->runDelayed = (flags & VMCI_FLAG_DELAYED_CB) ? TRUE : FALSE;
+ entry->notifyCB = notifyCB;
+ entry->clientData = clientData;
+ VMCI_CreateEvent(&entry->destroyEvent);
+
+ result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DOORBELL,
+ newHandle, VMCIDoorbellFreeCB, entry);
+ if (result != VMCI_SUCCESS) {
+ VMCI_WARNING((LGPFX"Failed to add new resource (handle=0x%x:0x%x).\n",
+ newHandle.context, newHandle.resource));
+ goto destroy;
+ }
+
+ if (VMCI_HasGuestDevice()) {
+ result = VMCIDoorbellLink(newHandle, entry->isDoorbell, entry->idx);
+ if (VMCI_SUCCESS != result) {
+ goto destroyResource;
+ }
+ VMCIDoorbellIndexTableAdd(entry);
+ }
+
+ if (VMCI_HANDLE_INVALID(*handle)) {
+ *handle = newHandle;
+ }
+
+ return result;
+
+destroyResource:
+ VMCIResource_Remove(newHandle, VMCI_RESOURCE_TYPE_DOORBELL);
+destroy:
+ VMCI_DestroyEvent(&entry->destroyEvent);
+freeMem:
+ VMCI_FreeKernelMem(entry, sizeof *entry);
+ return result;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Destroy --
+ *
+ * Destroys a doorbell previously created with
+ * VMCIDoorbell_Create. This operation may block waiting for a
+ * callback to finish.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * May block.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
+int
+VMCIDoorbell_Destroy(VMCIHandle handle) // IN
+{
+ VMCIDoorbellEntry *entry;
+ VMCIResource *resource;
+
+ if (VMCI_HANDLE_INVALID(handle)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
+ if (resource == NULL) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy doorbell (handle=0x%x:0x%x).\n",
+ handle.context, handle.resource));
+ return VMCI_ERROR_NOT_FOUND;
+ }
+ entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
+
+ if (VMCI_HasGuestDevice()) {
+ int result;
+
+ VMCIDoorbellIndexTableRemove(entry);
+
+ result = VMCIDoorbellUnlink(handle, entry->isDoorbell);
+ if (VMCI_SUCCESS != result) {
+
+ /*
+ * The only reason this should fail would be an inconsistency between
+ * guest and hypervisor state, where the guest believes it has an
+ * active registration whereas the hypervisor doesn't. One case where
+ * this may happen is if a doorbell is unregistered following a
+ * hibernation at a time where the doorbell state hasn't been restored
+ * on the hypervisor side yet. Since the handle has now been removed
+ * in the guest, we just print a warning and return success.
+ */
+
+ VMCI_DEBUG_LOG(4, (LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown by "
+ "hypervisor (error=%d).\n",
+ entry->isDoorbell ? "doorbell" : "queuepair",
+ handle.context, handle.resource, result));
+ }
+ }
+
+ /*
+ * Now remove the resource from the table. It might still be in use
+ * after this, in a callback or still on the delayed work queue.
+ */
+
+ VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DOORBELL);
+
+ /*
+ * We now wait on the destroyEvent and release the reference we got
+ * above.
+ */
+
+ VMCI_WaitOnEvent(&entry->destroyEvent, VMCIDoorbellReleaseCB, entry);
+
+ /*
+ * We know that we are now the only reference to the above entry so
+ * can safely free it.
+ */
+
+ VMCI_DestroyEvent(&entry->destroyEvent);
+ VMCI_FreeKernelMem(entry, sizeof *entry);
+
+ return VMCI_SUCCESS;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellNotifyAsGuest --
+ *
+ * Notify another guest or the host. We send a datagram down to the
+ * host via the hypervisor with the notification info.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * May do a hypercall.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static int
+VMCIDoorbellNotifyAsGuest(VMCIHandle handle, // IN
+ VMCIPrivilegeFlags privFlags) // IN
+{
+#if defined(VMKERNEL)
+ VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n"));
+ return VMCI_ERROR_DST_UNREACHABLE;
+#else // VMKERNEL
+ VMCIDoorbellNotifyMsg notifyMsg;
+
+ ASSERT(VMCI_HasGuestDevice());
+
+ notifyMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
+ VMCI_DOORBELL_NOTIFY);
+ notifyMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
+ notifyMsg.hdr.payloadSize = sizeof notifyMsg - VMCI_DG_HEADERSIZE;
+ notifyMsg.handle = handle;
+
+ return VMCI_SendDatagram((VMCIDatagram *)¬ifyMsg);
+#endif // VMKERNEL
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Notify --
+ *
+ * Generates a notification on the doorbell identified by the
+ * handle. For host side generation of notifications, the caller
+ * can specify what the privilege of the calling side is.
+ *
+ * Results:
+ * VMCI_SUCCESS on success, appropriate error code otherwise.
+ *
+ * Side effects:
+ * May do a hypercall.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
+int
+VMCIDoorbell_Notify(VMCIHandle dst, // IN
+ VMCIPrivilegeFlags privFlags) // IN
+{
+ int retval;
+ VMCIRoute route;
+ VMCIHandle src;
+
+ if (VMCI_HANDLE_INVALID(dst) || (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ src = VMCI_INVALID_HANDLE;
+ retval = VMCI_Route(&src, &dst, FALSE, &route);
+ if (retval < VMCI_SUCCESS) {
+ return retval;
+ }
+
+ if (VMCI_ROUTE_AS_HOST == route) {
+ return VMCIContext_NotifyDoorbell(VMCI_HOST_CONTEXT_ID, dst, privFlags);
+ }
+
+ if (VMCI_ROUTE_AS_GUEST == route) {
+ return VMCIDoorbellNotifyAsGuest(dst, privFlags);
+ }
+
+ VMCI_WARNING((LGPFX"Unknown route (%d) for doorbell.\n", route));
+ return VMCI_ERROR_DST_UNREACHABLE;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellDelayedDispatchCB --
+ *
+ * Calls the specified callback in a delayed context.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+static void
+VMCIDoorbellDelayedDispatchCB(void *data) // IN
+{
+ VMCIDoorbellEntry *entry = (VMCIDoorbellEntry *)data;
+
+ ASSERT(data);
+
+ entry->notifyCB(entry->clientData);
+
+ VMCIResource_Release(&entry->resource);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbellHostContextNotify --
+ *
+ * Dispatches a doorbell notification to the host context.
+ *
+ * Results:
+ * VMCI_SUCCESS on success. Appropriate error code otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+int
+VMCIDoorbellHostContextNotify(VMCIId srcCID, // IN
+ VMCIHandle handle) // IN
+{
+ VMCIDoorbellEntry *entry;
+ VMCIResource *resource;
+ int result;
+
+ ASSERT(VMCI_HasHostDevice());
+
+ resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DOORBELL);
+ if (resource == NULL) {
+ VMCI_DEBUG_LOG(4,
+ (LGPFX"Notifying an invalid doorbell (handle=0x%x:0x%x).\n",
+ handle.context, handle.resource));
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+ entry = RESOURCE_CONTAINER(resource, VMCIDoorbellEntry, resource);
+
+ if (entry->runDelayed) {
+ result = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, entry);
+ if (result < VMCI_SUCCESS) {
+ /*
+ * If we failed to schedule the delayed work, we need to
+ * release the resource immediately. Otherwise, the resource
+ * will be released once the delayed callback has been
+ * completed.
+ */
+
+ VMCI_DEBUG_LOG(10, (LGPFX"Failed to schedule delayed doorbell "
+ "notification (result=%d).\n", result));
+ VMCIResource_Release(resource);
+ }
+ } else {
+ entry->notifyCB(entry->clientData);
+ VMCIResource_Release(resource);
+ result = VMCI_SUCCESS;
+ }
+ return result;
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Hibernate --
+ *
+ * When a guest leaves hibernation, the device driver state is out of sync
+ * with the device state, since the driver state has doorbells registered
+ * that aren't known to the device. This function takes care of
+ * reregistering any doorbells. In case an error occurs during
+ * reregistration (this is highly unlikely since 1) it succeeded the first
+ * time 2) the device driver is the only source of doorbell registrations),
+ * we simply log the error. The doorbell can still be destroyed using
+ * VMCIDoorbell_Destroy.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIDoorbell_Hibernate(Bool enterHibernate)
+{
+ uint32 bucket;
+ VMCIListItem *iter;
+ VMCILockFlags flags;
+
+ if (!VMCI_HasGuestDevice() || enterHibernate) {
+ return;
+ }
+
+ VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
+
+ for (bucket = 0; bucket < ARRAYSIZE(vmciDoorbellIT.entries); bucket++) {
+ VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
+ int result;
+ VMCIDoorbellEntry *cur;
+
+ cur = VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
+ result = VMCIDoorbellLink(cur->resource.handle, cur->isDoorbell,
+ cur->idx);
+ if (result != VMCI_SUCCESS && result != VMCI_ERROR_DUPLICATE_ENTRY) {
+ VMCI_WARNING((LGPFX"Failed to reregister doorbell "
+ "(handle=0x%x:0x%x) of resource %s to index "
+ "(error: %d).\n",
+ cur->resource.handle.context,
+ cur->resource.handle.resource,
+ cur->isDoorbell ? "doorbell" : "queue pair", result));
+ }
+ }
+ }
+
+ VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Sync --
+ *
+ * Use this as a synchronization point when setting globals, for example,
+ * during device shutdown.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIDoorbell_Sync(void)
+{
+ VMCILockFlags flags;
+ VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
+ VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
+ VMCIResource_Sync();
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCI_RegisterNotificationBitmap --
+ *
+ * Register the notification bitmap with the host.
+ *
+ * Results:
+ * TRUE if the bitmap is registered successfully with the device, FALSE
+ * otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+Bool
+VMCI_RegisterNotificationBitmap(PPN bitmapPPN)
+{
+ int result;
+ VMCINotifyBitmapSetMsg bitmapSetMsg;
+
+ /*
+ * Do not ASSERT() on the guest device here. This function can get called
+ * during device initialization, so the ASSERT() will fail even though
+ * the device is (almost) up.
+ */
+
+ bitmapSetMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
+ VMCI_SET_NOTIFY_BITMAP);
+ bitmapSetMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
+ bitmapSetMsg.hdr.payloadSize = sizeof bitmapSetMsg - VMCI_DG_HEADERSIZE;
+ bitmapSetMsg.bitmapPPN = bitmapPPN;
+
+ result = VMCI_SendDatagram((VMCIDatagram *)&bitmapSetMsg);
+ if (result != VMCI_SUCCESS) {
+ VMCI_DEBUG_LOG(4, (LGPFX"Failed to register (PPN=%u) as "
+ "notification bitmap (error=%d).\n",
+ bitmapPPN, result));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * VMCIDoorbellFireEntries --
+ *
+ * Executes or schedules the handlers for a given notify index.
+ *
+ * Result:
+ * Notification hash entry if found. NULL otherwise.
+ *
+ * Side effects:
+ * Whatever the side effects of the handlers are.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static void
+VMCIDoorbellFireEntries(uint32 notifyIdx) // IN
+{
+ uint32 bucket = VMCI_DOORBELL_HASH(notifyIdx);
+ VMCIListItem *iter;
+ VMCILockFlags flags;
+
+ ASSERT(VMCI_HasGuestDevice());
+
+ VMCIDoorbellGrabLock(&vmciDoorbellIT.lock, &flags);
+
+ VMCIList_Scan(iter, &vmciDoorbellIT.entries[bucket]) {
+ VMCIDoorbellEntry *cur =
+ VMCIList_Entry(iter, VMCIDoorbellEntry, idxListItem);
+
+ ASSERT(cur);
+
+ if (cur->idx == notifyIdx) {
+ ASSERT(cur->notifyCB);
+ if (cur->runDelayed) {
+ int err;
+
+ VMCIResource_Hold(&cur->resource);
+ err = VMCI_ScheduleDelayedWork(VMCIDoorbellDelayedDispatchCB, cur);
+ if (err != VMCI_SUCCESS) {
+ VMCIResource_Release(&cur->resource);
+ goto out;
+ }
+ } else {
+ cur->notifyCB(cur->clientData);
+ }
+ }
+ }
+
+out:
+ VMCIDoorbellReleaseLock(&vmciDoorbellIT.lock, flags);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCI_ScanNotificationBitmap --
+ *
+ * Scans the notification bitmap, collects pending notifications,
+ * resets the bitmap and invokes appropriate callbacks.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May schedule tasks, allocate memory and run callbacks.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCI_ScanNotificationBitmap(uint8 *bitmap)
+{
+ uint32 idx;
+
+ ASSERT(bitmap);
+ ASSERT(VMCI_HasGuestDevice());
+
+ for (idx = 0; idx < maxNotifyIdx; idx++) {
+ if (bitmap[idx] & 0x1) {
+ bitmap[idx] &= ~1;
+ VMCIDoorbellFireEntries(idx);
+ }
+ }
+}
+
+
+#else // SOLARIS) || __APPLE__
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIDoorbell_Create/VMCIDoorbell_Destroy/VMCIDoorbell_Notify --
+ *
+ * The doorbell functions have yet to be implemented for Solaris
+ * and Mac OS X guest drivers.
+ *
+ * Results:
+ * VMCI_ERROR_UNAVAILABLE.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
+int
+VMCIDoorbell_Create(VMCIHandle *handle, // IN
+ uint32 flags, // I
+ VMCIPrivilegeFlags privFlags, // IN
+ VMCICallback notifyCB, // IN
+ void *clientData) // IN
+{
+ return VMCI_ERROR_UNAVAILABLE;
+}
+
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
+int
+VMCIDoorbell_Destroy(VMCIHandle handle) // IN
+{
+ return VMCI_ERROR_UNAVAILABLE;
+}
+
+
+VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
+int
+VMCIDoorbell_Notify(VMCIHandle handle, // IN
+ VMCIPrivilegeFlags privFlags) // IN
+{
+ return VMCI_ERROR_UNAVAILABLE;
+}
+
+#endif // SOLARIS) || __APPLE__
#include "vmci_kernel_if.h"
#include "vmci_defs.h"
+void VMCIDoorbell_Init(void);
+void VMCIDoorbell_Exit(void);
+void VMCIDoorbell_Hibernate(Bool enterHibernation);
+void VMCIDoorbell_Sync(void);
+
int VMCIDoorbellHostContextNotify(VMCIId srcCID, VMCIHandle handle);
int VMCIDoorbellGetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags);
-void VMCIDoorbell_Sync(void);
+
+Bool VMCI_RegisterNotificationBitmap(PPN bitmapPPN);
+void VMCI_ScanNotificationBitmap(uint8 *bitmap);
#endif // VMCI_DOORBELL_H
#define LGPFX "VMCIHashTable: "
+#if defined(VMKERNEL)
+ /* VMK doesn't need BH locks, so use lower ranks. */
+# define VMCIHashTableInitLock(_l, _n) \
+ VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_HIGHEST)
+# define VMCIHashTableGrabLock(_l, _f) VMCI_GrabLock(_l, _f)
+# define VMCIHashTableReleaseLock(_l, _f) VMCI_ReleaseLock(_l, _f)
+#else // VMKERNEL
+# define VMCIHashTableInitLock(_l, _n) \
+ VMCI_InitLock(_l, _n, VMCI_LOCK_RANK_HIGH_BH)
+# define VMCIHashTableGrabLock(_l, _f) VMCI_GrabLock_BH(_l, _f)
+# define VMCIHashTableReleaseLock(_l, _f) VMCI_ReleaseLock_BH(_l, _f)
+#endif // VMKERNEL
+
#define VMCI_HASHTABLE_HASH(_h, _sz) \
VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(_h), (_sz))
}
memset(table->entries, 0, sizeof *table->entries * size);
table->size = size;
- VMCI_InitLock(&table->lock,
- "VMCIHashTableLock",
- VMCI_LOCK_RANK_HIGH);
+ VMCIHashTableInitLock(&table->lock, "VMCIHashTableLock");
return table;
}
ASSERT(table);
- VMCI_GrabLock(&table->lock, &flags);
+ VMCIHashTableGrabLock(&table->lock, &flags);
#if 0
#ifdef VMX86_DEBUG
for (i = 0; i < table->size; i++) {
#endif
VMCI_FreeKernelMem(table->entries, sizeof *table->entries * table->size);
table->entries = NULL;
- VMCI_ReleaseLock(&table->lock, flags);
- VMCI_CleanupLock(&table->lock);
+ VMCIHashTableReleaseLock(&table->lock, flags);
+ VMCI_CleanupLock(&table->lock);
VMCI_FreeKernelMem(table, sizeof *table);
}
ASSERT(entry);
ASSERT(table);
- VMCI_GrabLock(&table->lock, &flags);
+ VMCIHashTableGrabLock(&table->lock, &flags);
/* Do not allow addition of a new entry if the device is being shutdown. */
if (VMCI_DeviceShutdown()) {
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return VMCI_ERROR_DEVICE_NOT_FOUND;
}
if (VMCIHashTableEntryExistsLocked(table, entry->handle)) {
VMCI_DEBUG_LOG(4, (LGPFX"Entry (handle=0x%x:0x%x) already exists.\n",
entry->handle.context, entry->handle.resource));
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return VMCI_ERROR_DUPLICATE_ENTRY;
}
entry->refCount++;
entry->next = table->entries[idx];
table->entries[idx] = entry;
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return VMCI_SUCCESS;
}
ASSERT(table);
ASSERT(entry);
- VMCI_GrabLock(&table->lock, &flags);
-
+ VMCIHashTableGrabLock(&table->lock, &flags);
+
/* First unlink the entry. */
result = HashTableUnlinkEntry(table, entry);
if (result != VMCI_SUCCESS) {
result = VMCI_SUCCESS_ENTRY_DEAD;
goto done;
}
-
+
done:
- VMCI_ReleaseLock(&table->lock, flags);
-
+ VMCIHashTableReleaseLock(&table->lock, flags);
+
return result;
}
}
ASSERT(table);
-
- VMCI_GrabLock(&table->lock, &flags);
+
+ VMCIHashTableGrabLock(&table->lock, &flags);
entry = VMCIHashTableGetEntryLocked(table, handle);
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return entry;
}
+/*
+ *------------------------------------------------------------------------------
+ *
+ * VMCIHashTable_HoldEntry --
+ *
+ * Hold the given entry. This will increment the entry's reference count.
+ * This is like a GetEntry() but without having to lookup the entry by
+ * handle.
+ *
+ * Result:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIHashTable_HoldEntry(VMCIHashTable *table, // IN
+ VMCIHashEntry *entry) // IN/OUT
+{
+ VMCILockFlags flags;
+
+ ASSERT(table);
+ ASSERT(entry);
+
+ VMCIHashTableGrabLock(&table->lock, &flags);
+ entry->refCount++;
+ VMCIHashTableReleaseLock(&table->lock, flags);
+}
+
+
/*
*------------------------------------------------------------------------------
*
entry->refCount--;
/* Check if this is last reference and report if so. */
- if (entry->refCount == 0) {
+ if (entry->refCount == 0) {
/*
* Remove entry from hash table if not already removed. This could have
int result;
ASSERT(table);
- VMCI_GrabLock(&table->lock, &flags);
+ VMCIHashTableGrabLock(&table->lock, &flags);
result = VMCIHashTableReleaseEntryLocked(table, entry);
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return result;
}
ASSERT(table);
- VMCI_GrabLock(&table->lock, &flags);
+ VMCIHashTableGrabLock(&table->lock, &flags);
exists = VMCIHashTableEntryExistsLocked(table, handle);
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
return exists;
}
{
VMCILockFlags flags;
ASSERT(table);
- VMCI_GrabLock(&table->lock, &flags);
- VMCI_ReleaseLock(&table->lock, flags);
+ VMCIHashTableGrabLock(&table->lock, &flags);
+ VMCIHashTableReleaseLock(&table->lock, flags);
}
int VMCIHashTable_AddEntry(VMCIHashTable *table, VMCIHashEntry *entry);
int VMCIHashTable_RemoveEntry(VMCIHashTable *table, VMCIHashEntry *entry);
VMCIHashEntry *VMCIHashTable_GetEntry(VMCIHashTable *table, VMCIHandle handle);
+void VMCIHashTable_HoldEntry(VMCIHashTable *table, VMCIHashEntry *entry);
int VMCIHashTable_ReleaseEntry(VMCIHashTable *table, VMCIHashEntry *entry);
Bool VMCIHashTable_EntryExists(VMCIHashTable *table, VMCIHandle handle);
void VMCIHashTable_Sync(VMCIHashTable *table);
+++ /dev/null
-/*********************************************************
- * Copyright (C) 2010 VMware, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 and no later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- *********************************************************/
-
-/*
- * vmciNotifications.c --
- *
- * Implementation of the VMCI notifications registration and
- * delivery, and the related doorbell API for the guest driver.
- */
-
-#ifdef __linux__
-# include "driver-config.h"
-# include "compat_kernel.h"
-# include "compat_module.h"
-#endif // linux
-
-#include "vmci_kernel_if.h"
-#include "vm_basic_types.h"
-#include "vm_assert.h"
-#include "vmciKernelAPI.h"
-#include "vmci_defs.h"
-#include "vmci_call_defs.h"
-#include "vmciInt.h"
-#include "vmciUtil.h"
-
-#define LGPFX "VMCINotifications: "
-
-#if !defined(SOLARIS) && !defined(__APPLE__)
-
-/*
- * The VMCI Notify hash table provides two mappings:
- * 1) one maps a given notification index in the bitmap to the
- * entries, giving the set of handlers registered for that
- * index. This is mainly used for firing handlers for a given
- * bitmap index.
- * 2) the other maps a handle and a resource (doorbell/queuepair)
- * to the entry (used to check for duplicates and delete the
- * entry)
- */
-
-#define HASH_TABLE_SIZE 64
-#define VMCI_NOTIF_HASH(val) VMCI_HashId(val, HASH_TABLE_SIZE)
-
-typedef struct VMCINotifyHashEntry {
- uint32 idx; // Bitmap index
- VMCIHandle handle;
- Bool doorbell;
- Bool runDelayed;
- VMCICallback notifyCB;
- void *callbackData;
- VMCIEvent destroyEvent;
- int refCount;
- VMCIListItem handleListItem;
- VMCIListItem idxListItem;
-} VMCINotifyHashEntry;
-
-typedef struct VMCINotifyHashTable {
- VMCILock lock;
- VMCIList entriesByIdx[HASH_TABLE_SIZE];
- VMCIList entriesByHandle[HASH_TABLE_SIZE];
-} VMCINotifyHashTable;
-
-
-static int VMCINotifyHashAddEntry(VMCINotifyHashEntry *entry);
-static void VMCINotifyHashSetEntryCallback(VMCINotifyHashEntry *entry,
- VMCICallback notifyCB);
-static VMCINotifyHashEntry *VMCINotifyHashRemoveEntry(VMCIHandle handle,
- Bool doorbell);
-static int VMCINotifyReleaseCB(void *clientData);
-static void VMCINotifyHashReleaseEntry(VMCINotifyHashEntry *entry);
-static VMCINotifyHashEntry *VMCINotifyHashFindByIdx(uint32 idx,
- uint32 *hashBucket);
-static VMCINotifyHashEntry *VMCINotifyHashFindByHandle(VMCIHandle handle,
- Bool doorbell,
- uint32 *hashBucket);
-
-static void VMCINotifyDelayedDispatchCB(void *data);
-static void VMCINotifyHashFireEntries(uint32 notifyIdx);
-
-static int LinkNotificationHypercall(VMCIHandle handle, Bool doorbell,
- uint32 notifyIdx);
-static int UnlinkNotificationHypercall(VMCIHandle handle, Bool doorbell);
-
-
-/*
- * The VMCI notify hash table keeps track of currently registered
- * notifications.
- */
-
-static VMCINotifyHashTable vmciNotifyHT;
-
-/*
- * The maxNotifyIdx is one larger than the currently known bitmap
- * index in use, and is used to determine how much of the bitmap needs
- * to be scanned.
- */
-
-static uint32 maxNotifyIdx;
-
-/*
- * the notifyIdxCount is used for determining whether there are free
- * entries within the bitmap (if notifyIdxCount + 1 < maxNotifyIdx).
- */
-
-static uint32 notifyIdxCount;
-
-/*
- * The lastNotifyIdxReserved is used to track the last index handed
- * out - in the case where multiple handles share a notification
- * index, we hand out indexes round robin based on
- * lastNotifyIdxReserved.
- */
-
-static uint32 lastNotifyIdxReserved;
-
-/*
- * lastNotifyIdxReleased is a one entry cache used to by the index
- * allocation.
- */
-
-static uint32 lastNotifyIdxReleased = PAGE_SIZE;
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCINotifications_Init --
- *
- * General init code.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-VMCINotifications_Init(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vmciNotifyHT.entriesByIdx); i++) {
- VMCIList_Init(&vmciNotifyHT.entriesByIdx[i]);
- }
- for (i = 0; i < ARRAY_SIZE(vmciNotifyHT.entriesByHandle); i++) {
- VMCIList_Init(&vmciNotifyHT.entriesByHandle[i]);
- }
-
- VMCI_InitLock(&vmciNotifyHT.lock, "VMCINotifyHashLock",
- VMCI_LOCK_RANK_HIGHER_BH);
- return;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCINotifications_Exit --
- *
- * General exit code.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-VMCINotifications_Exit(void)
-{
- uint32 bucket;
- VMCIListItem *iter, *iter2;
-
- for (bucket = 0; bucket < HASH_TABLE_SIZE; bucket++) {
- VMCIList_ScanSafe(iter, iter2, &vmciNotifyHT.entriesByIdx[bucket]) {
- VMCINotifyHashEntry *cur;
-
- /*
- * We should never get here because all notifications should have been
- * unregistered before we try to unload the driver module.
- * Also, delayed callbacks could still be firing so this cleanup
- * would not be safe.
- * Still it is better to free the memory than not ... so we
- * leave this code in just in case....
- *
- */
- ASSERT(FALSE);
-
- cur = VMCIList_Entry(iter, VMCINotifyHashEntry, idxListItem);
- VMCI_FreeKernelMem(cur, sizeof *cur);
- }
- }
- VMCI_CleanupLock(&vmciNotifyHT.lock);
-
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotifications_Sync --
- *
- * Use this as a synchronization point when setting globals, for example,
- * during device shutdown.
- *
- * Results:
- * TRUE.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-void
-VMCINotifications_Sync(void)
-{
- VMCILockFlags flags;
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * VMCINotifications_Hibernate --
- *
- * When a guest leaves hibernation, the device driver state is out
- * of sync with the device state, since the driver state has
- * doorbells registered that aren't known to the device. This
- * function takes care of reregistering any doorbells. In case an
- * error occurs during reregistration (this is highly unlikely
- * since 1) it succeeded the first time 2) the device driver is the
- * only source of doorbell registrations), we simply log the
- * error. The doorbell can still be destroyed using
- * VMCIDoorbell_Destroy.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-VMCINotifications_Hibernate(Bool enterHibernate) // IN
-{
- VMCILockFlags flags;
- uint32 bucket;
- VMCIListItem *iter;
-
- if (enterHibernate) {
- /*
- * Nothing to do when entering hibernation.
- */
-
- return;
- }
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
-
- for (bucket = 0; bucket < HASH_TABLE_SIZE; bucket++) {
- VMCIList_Scan(iter, &vmciNotifyHT.entriesByIdx[bucket]) {
- VMCINotifyHashEntry *cur;
- int result;
-
- cur = VMCIList_Entry(iter, VMCINotifyHashEntry, idxListItem);
- result = LinkNotificationHypercall(cur->handle, cur->doorbell, cur->idx);
- if (result != VMCI_SUCCESS && result != VMCI_ERROR_DUPLICATE_ENTRY) {
- VMCI_WARNING((LGPFX"Failed to reregister doorbell (handle=0x%x:0x%x) "
- "of resource %s to index (error=%d).\n",
- cur->handle.context, cur->handle.resource,
- cur->doorbell ? "doorbell" : "queue pair", result));
- }
- }
- }
-
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-}
-
-
-/*
- *-------------------------------------------------------------------------
- *
- * VMCINotifyHashAddEntry --
- *
- * Given a notification entry, adds it to the hashtable of
- * notifications.
- *
- * Result:
- * VMCI_SUCCESS on success, appropriate error code otherwise.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static int
-VMCINotifyHashAddEntry(VMCINotifyHashEntry *entry) // IN
-{
- VMCILockFlags flags;
- uint32 bucket;
- uint32 newNotifyIdx;
- int result;
- static VMCIId notifyRID = 0;
-
- ASSERT(entry);
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
-
- /* Do not allow addition of a new handle if the device is being shutdown. */
- if (VMCI_DeviceShutdown()) {
- result = VMCI_ERROR_DEVICE_NOT_FOUND;
- goto out;
- }
-
- if (VMCI_HANDLE_INVALID(entry->handle)) {
- VMCIHandle newHandle;
- VMCIId oldRID = notifyRID;
- Bool found;
-
- do {
- newHandle = VMCI_MAKE_HANDLE(VMCI_GetContextID(), notifyRID);
- notifyRID++;
- found = VMCINotifyHashFindByHandle(newHandle, entry->doorbell, NULL) != NULL;
- } while(found && oldRID != notifyRID);
- if (UNLIKELY(found)) {
- /*
- * We went full circle and still didn't find a free handle.
- */
-
- result = VMCI_ERROR_NO_HANDLE;
- goto out;
- }
- entry->handle = newHandle;
- } else if (VMCI_GetContextID() != entry->handle.context) {
- /*
- * The context id passed down should either be invalid or
- * the context id of the guest.
- */
-
- result = VMCI_ERROR_INVALID_ARGS;
- goto out;
- }
-
- if (VMCINotifyHashFindByHandle(entry->handle, entry->doorbell, &bucket)) {
- result = VMCI_ERROR_ALREADY_EXISTS;
- goto out;
- }
- VMCIList_Insert(&entry->handleListItem,
- &vmciNotifyHT.entriesByHandle[bucket]);
-
- /*
- * Below we try to allocate an index in the notification bitmap
- * with "not too much" sharing between resources. If we use less
- * that the full bitmap, we either add to the end if there are no
- * unused flags withing the currently used area, or we search for
- * unused ones. If we use the full bitmap, we allocate the index
- * round robin.
- */
-
- if (maxNotifyIdx < PAGE_SIZE || notifyIdxCount < PAGE_SIZE) {
- if (lastNotifyIdxReleased < maxNotifyIdx &&
- !VMCINotifyHashFindByIdx(lastNotifyIdxReleased, NULL)) {
- newNotifyIdx = lastNotifyIdxReleased;
- lastNotifyIdxReleased = PAGE_SIZE;
- } else {
- Bool reused = FALSE;
- newNotifyIdx = lastNotifyIdxReserved;
- if (notifyIdxCount + 1 < maxNotifyIdx) {
- do {
- if (!VMCINotifyHashFindByIdx(newNotifyIdx, NULL)) {
- reused = TRUE;
- break;
- }
- newNotifyIdx = (newNotifyIdx + 1) % maxNotifyIdx;
- } while(newNotifyIdx != lastNotifyIdxReleased);
- }
- if (!reused) {
- newNotifyIdx = maxNotifyIdx;
- maxNotifyIdx++;
- }
- }
- } else {
- newNotifyIdx = (lastNotifyIdxReserved + 1) % PAGE_SIZE;
- }
- lastNotifyIdxReserved = newNotifyIdx;
- notifyIdxCount++;
-
- bucket = VMCI_NOTIF_HASH(newNotifyIdx);
- entry->refCount++;
- entry->idx = newNotifyIdx;
- VMCIList_Insert(&entry->idxListItem, &vmciNotifyHT.entriesByIdx[bucket]);
- result = VMCI_SUCCESS;
-out:
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
- return result;
-}
-
-
-/*
- *-------------------------------------------------------------------------
- *
- * VMCINotifyHashSetEntryCallback --
- *
- * Sets the notify callback of the given entry. Once the callback
- * has been set, it may start firing.
- *
- * Result:
- * None.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-VMCINotifyHashSetEntryCallback(VMCINotifyHashEntry *entry, // IN
- VMCICallback notifyCB) // IN
-{
- VMCILockFlags flags;
-
- ASSERT(entry && notifyCB);
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
- entry->notifyCB = notifyCB;
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-}
-
-
-/*
- *-------------------------------------------------------------------------
- *
- * VMCINotifyHashRemoveEntry --
- *
- * Removes the entry identified by the handle of the given
- * resource type from the hash table.
- *
- * Result:
- * Pointer to entry if removed, NULL if not found.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------------
- */
-
-static VMCINotifyHashEntry *
-VMCINotifyHashRemoveEntry(VMCIHandle handle, // IN
- Bool doorbell) // IN
-{
- VMCILockFlags flags;
- VMCINotifyHashEntry *entry;
- uint32 bucket;
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
- entry = VMCINotifyHashFindByHandle(handle, doorbell, &bucket);
- if (entry) {
- ASSERT(entry->refCount > 0);
- VMCIList_Remove(&entry->handleListItem,
- &vmciNotifyHT.entriesByHandle[bucket]);
- bucket = VMCI_NOTIF_HASH(entry->idx);
- VMCIList_Remove(&entry->idxListItem, &vmciNotifyHT.entriesByIdx[bucket]);
- notifyIdxCount--;
- if (entry->idx == maxNotifyIdx - 1) {
- /*
- * If we delete an entry with the maximum known notification
- * index, we take the opportunity to prune the current
- * max. As there might be other unused indices immediately
- * below, we lower the maximum until we hit an index in use.
- */
-
- while (maxNotifyIdx > 0 &&
- !VMCINotifyHashFindByIdx(maxNotifyIdx - 1, NULL)) {
- maxNotifyIdx--;
- }
- }
- lastNotifyIdxReleased = entry->idx;
- }
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-
- return entry;
-}
-
-
-/*
- *------------------------------------------------------------------------------
- *
- * VMCINotifyReleaseCB --
- *
- * Callback to release the notification entry reference. It is
- * called by the VMCI_WaitOnEvent function before it blocks.
- *
- * Result:
- * 0.
- *
- * Side effects:
- * Releases hash entry (see below).
- *
- *------------------------------------------------------------------------------
- */
-
-static int
-VMCINotifyReleaseCB(void *clientData) // IN
-{
- VMCINotifyHashEntry *entry = (VMCINotifyHashEntry *)clientData;
- ASSERT(entry);
- VMCINotifyHashReleaseEntry(entry);
-
- return 0;
-}
-
-
-/*
- *-------------------------------------------------------------------------
- *
- * VMCINotifyHashReleaseEntry --
- *
- * Drops a reference to the current hash entry. If this is the last
- * reference then the entry is freed.
- *
- * Result:
- * None.
- *
- * Side effects:
- * May signal event.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-VMCINotifyHashReleaseEntry(VMCINotifyHashEntry *entry) // IN
-{
- VMCILockFlags flags;
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
- entry->refCount--;
-
- /*
- * Check if this is last reference and signal the destroy event if
- * so.
- */
-
- if (entry->refCount == 0) {
- VMCI_SignalEvent(&entry->destroyEvent);
- }
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotifyHashFindByIdx --
- *
- * Find hash entry by bitmap index. Assumes lock is
- * held. Regardless of whether an entry was found, the bucket that
- * the entry would have been in is returned in hashBucket (if
- * valid).
- *
- * Results:
- * Entry if found, NULL if not.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static VMCINotifyHashEntry *
-VMCINotifyHashFindByIdx(uint32 idx, // IN
- uint32 *hashBucket) // IN/OUT: hash value for idx
-{
- VMCIListItem *iter;
- uint32 bucket = VMCI_NOTIF_HASH(idx);
-
- if (hashBucket) {
- *hashBucket = bucket;
- }
-
- VMCIList_Scan(iter, &vmciNotifyHT.entriesByIdx[bucket]) {
- VMCINotifyHashEntry *cur =
- VMCIList_Entry(iter, VMCINotifyHashEntry, idxListItem);
- if (cur->idx == idx) {
- return cur;
- }
- }
- return NULL;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotifyHashFindByHandle --
- *
- * Find hash entry by handle and resoruce. Assumes lock is
- * held. Regardless of whether an entry was found, the bucket that
- * the entry would have been in is returned in hashBucket (if
- * valid).
- *
- * Results:
- * Entry if found, NULL if not.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static VMCINotifyHashEntry *
-VMCINotifyHashFindByHandle(VMCIHandle handle, // IN
- Bool doorbell, // IN
- uint32 *hashBucket) // IN/OUT: hash value for handle
-{
- VMCIListItem *iter;
- uint32 bucket = VMCI_NOTIF_HASH(handle.resource);
-
- if (hashBucket) {
- *hashBucket = bucket;
- }
-
- VMCIList_Scan(iter, &vmciNotifyHT.entriesByHandle[bucket]) {
- VMCINotifyHashEntry *cur =
- VMCIList_Entry(iter, VMCINotifyHashEntry, handleListItem);
- if (VMCI_HANDLE_EQUAL(cur->handle, handle) && cur->doorbell == doorbell) {
- return cur;
- }
- }
- return NULL;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotifyDelayedDispatchCB --
- *
- * Calls the specified callback in a delayed context.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-static void
-VMCINotifyDelayedDispatchCB(void *data) // IN
-{
- VMCINotifyHashEntry *entry = (VMCINotifyHashEntry *)data;
-
- ASSERT(data);
-
- entry->notifyCB(entry->callbackData);
-
- VMCINotifyHashReleaseEntry(entry);
-}
-
-
-/*
- *-------------------------------------------------------------------------
- *
- * VMCINotifyHashFireEntries --
- *
- * Executes or schedules the handlers for a given notify index.
- *
- * Result:
- * Notification hash entry if found. NULL otherwise.
- *
- * Side effects:
- * Whatever the side effects of the handlers are.
- *
- *-------------------------------------------------------------------------
- */
-
-static void
-VMCINotifyHashFireEntries(uint32 notifyIdx) // IN
-{
- VMCILockFlags flags;
- VMCIListItem *iter;
- int bucket = VMCI_NOTIF_HASH(notifyIdx);
-
- VMCI_GrabLock_BH(&vmciNotifyHT.lock, &flags);
-
- VMCIList_Scan(iter, &vmciNotifyHT.entriesByIdx[bucket]) {
- VMCINotifyHashEntry *cur = VMCIList_Entry(iter, VMCINotifyHashEntry,
- idxListItem);
- if (cur->idx == notifyIdx && cur->notifyCB) {
- if (cur->runDelayed) {
- int err;
-
- cur->refCount++;
- err = VMCI_ScheduleDelayedWork(VMCINotifyDelayedDispatchCB, cur);
- if (err != VMCI_SUCCESS) {
- cur->refCount--;
- goto out;
- }
- } else {
- cur->notifyCB(cur->callbackData);
- }
- }
- }
-out:
- VMCI_ReleaseLock_BH(&vmciNotifyHT.lock, flags);
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * LinkNotificationHypercall --
- *
- * Creates a link between the given doorbell handle and the given
- * index in the bitmap in the device backend.
- *
- * Results:
- * VMCI_SUCCESS if success, appropriate error code otherwise.
- *
- * Side effects:
- * Notification state is created in hypervisor.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-LinkNotificationHypercall(VMCIHandle handle, // IN
- Bool doorbell, // IN
- uint32 notifyIdx) // IN
-{
- VMCIDoorbellLinkMsg linkMsg;
- int result;
- VMCIId resourceID;
-
- ASSERT(!VMCI_HANDLE_INVALID(handle));
-
- if (doorbell) {
- resourceID = VMCI_DOORBELL_LINK;
- } else {
- ASSERT(FALSE);
- return VMCI_ERROR_UNAVAILABLE;
- }
-
- linkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
- linkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- linkMsg.hdr.payloadSize = sizeof linkMsg - VMCI_DG_HEADERSIZE;
- linkMsg.handle = handle;
- linkMsg.notifyIdx = notifyIdx;
-
- result = VMCI_SendDatagram((VMCIDatagram *)&linkMsg);
-
- return result;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * UnlinkNotificationHypercall --
- *
- * Unlinks the given doorbell handle from an index in the bitmap in
- * the device backend.
- *
- * Results:
- * VMCI_SUCCESS if success, appropriate error code otherwise.
- *
- * Side effects:
- * Notification state is destroyed in hypervisor.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-UnlinkNotificationHypercall(VMCIHandle handle, // IN
- Bool doorbell) // IN
-{
- VMCIDoorbellUnlinkMsg unlinkMsg;
- int result;
- VMCIId resourceID;
-
- ASSERT(!VMCI_HANDLE_INVALID(handle));
-
- if (doorbell) {
- resourceID = VMCI_DOORBELL_UNLINK;
- } else {
- ASSERT(FALSE);
- return VMCI_ERROR_UNAVAILABLE;
- }
-
- unlinkMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, resourceID);
- unlinkMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- unlinkMsg.hdr.payloadSize = sizeof unlinkMsg - VMCI_DG_HEADERSIZE;
- unlinkMsg.handle = handle;
-
- result = VMCI_SendDatagram((VMCIDatagram *)&unlinkMsg);
-
- return result;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotificationRegister --
- *
- * Links a resource with an index in the notification bitmap.
- *
- * Results:
- * VMCI_SUCCESS on success, appropriate error code otherwise.
- *
- * Side effects:
- * Notification state is created both in guest and on host.
- *
- *-----------------------------------------------------------------------------
- */
-
-int
-VMCINotificationRegister(VMCIHandle *handle, // IN
- Bool doorbell, // IN
- uint32 flags, // IN
- VMCICallback notifyCB, // IN
- void *callbackData) // IN
-{
- int result;
- VMCINotifyHashEntry *entry;
-
- if (!notifyCB || !handle) {
- return VMCI_ERROR_INVALID_ARGS;
- }
-
- entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED);
- if (entry == NULL) {
- return VMCI_ERROR_NO_MEM;
- }
-
- entry->runDelayed = (flags & VMCI_FLAG_DELAYED_CB) ? TRUE : FALSE;
- if (entry->runDelayed && !VMCI_CanScheduleDelayedWork()) {
- VMCI_FreeKernelMem(entry, sizeof *entry);
- return VMCI_ERROR_INVALID_ARGS;
- }
-
- /*
- * Reserve an index in the notification bitmap.
- */
-
- entry->handle = *handle;
- entry->doorbell = doorbell;
- entry->notifyCB = NULL; // Wait with this until link is established in hypervisor
- entry->callbackData = callbackData;
- entry->refCount = 0;
- VMCIList_InitEntry(&entry->handleListItem);
- VMCIList_InitEntry(&entry->idxListItem);
- result = VMCINotifyHashAddEntry(entry);
- if (result != VMCI_SUCCESS) {
- VMCI_FreeKernelMem(entry, sizeof *entry);
- return result;
- }
-
- VMCI_CreateEvent(&entry->destroyEvent);
-
- result = LinkNotificationHypercall(entry->handle, doorbell, entry->idx);
- if (result != VMCI_SUCCESS) {
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to link (handle=0x%x:0x%x) of resource %s "
- "to index (error=%d).\n",
- entry->handle.context, entry->handle.resource,
- entry->doorbell ? "doorbell" : "queue pair", result));
- VMCINotifyHashRemoveEntry(entry->handle, entry->doorbell);
- VMCI_DestroyEvent(&entry->destroyEvent);
- VMCI_FreeKernelMem(entry, sizeof *entry);
- } else {
- /*
- * When the handle is set, the notification callback may start
- * to fire. Since flags in the notification bitmap can be
- * shared, a given callback may fire immediately.
- */
-
- VMCINotifyHashSetEntryCallback(entry, notifyCB);
- *handle = entry->handle;
- }
-
- return result;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCINotificationUnregister --
- *
- * Unregisters a notification previously created through
- * VMCINotificationRegister. This function may block. The call
- * always succeeds if the notification exists.
- *
- * Results:
- * VMCI_SUCCESS if success, VMCI_ERROR_NOT_FOUND otherwise.
- *
- * Side effects:
- * Notification state is destroyed both in guest and hypervisor.
- *
- *-----------------------------------------------------------------------------
- */
-
-int
-VMCINotificationUnregister(VMCIHandle handle, // IN
- Bool doorbell) // IN
-{
- VMCINotifyHashEntry *entry;
- int result;
-
- entry = VMCINotifyHashRemoveEntry(handle, doorbell);
- if (!entry) {
- return VMCI_ERROR_NOT_FOUND;
- }
-
- VMCI_WaitOnEvent(&entry->destroyEvent, VMCINotifyReleaseCB, entry);
- VMCI_DestroyEvent(&entry->destroyEvent);
- VMCI_FreeKernelMem(entry, sizeof *entry);
-
- result = UnlinkNotificationHypercall(handle, doorbell);
- if (result != VMCI_SUCCESS) {
- /*
- * The only reason this should fail would be an inconsistency
- * between guest and hypervisor state, where the guest believes
- * it has an active registration whereas the hypervisor
- * doesn't. One case where this may happen is if a doorbell is
- * unregistered following a hibernation at a time where the
- * doorbell state hasn't been restored on the hypervisor side
- * yet. Since the handle has now been removed in the guest, we
- * just print a warning and return success.
- */
-
- VMCI_DEBUG_LOG(4, (LGPFX"Unlink of %s (handle=0x%x:0x%x) unknown by "
- "hypervisor (error=%d).\n",
- doorbell ? "doorbell" : "queuepair",
- handle.context, handle.resource, result));
- }
- return VMCI_SUCCESS;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCI_RegisterNotificationBitmap --
- *
- * Verify that the host supports the hypercalls we need. If it does not,
- * try to find fallback hypercalls and use those instead.
- *
- * Results:
- * TRUE if the bitmap is registered successfully with the device, FALSE
- * otherwise.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-Bool
-VMCI_RegisterNotificationBitmap(PPN bitmapPPN) // IN
-{
- VMCINotifyBitmapSetMsg bitmapSetMsg;
- int result;
-
- bitmapSetMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
- VMCI_SET_NOTIFY_BITMAP);
- bitmapSetMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- bitmapSetMsg.hdr.payloadSize = sizeof bitmapSetMsg - VMCI_DG_HEADERSIZE;
- bitmapSetMsg.bitmapPPN = bitmapPPN;
-
- result = VMCI_SendDatagram((VMCIDatagram *)&bitmapSetMsg);
- if (result != VMCI_SUCCESS) {
- VMCI_DEBUG_LOG(4, (LGPFX"Failed to register (PPN=%u) as "
- "notification bitmap (error=%d).\n",
- bitmapPPN, result));
- return FALSE;
- }
- return TRUE;
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCI_ScanNotificationBitmap --
- *
- * Scans the notification bitmap, collects pending notifications,
- * resets the bitmap and invokes appropriate callbacks.
- *
- * Results:
- * None.
- *
- * Side effects:
- * May schedule tasks, allocate memory and run callbacks.
- *
- *-----------------------------------------------------------------------------
- */
-
-void
-VMCI_ScanNotificationBitmap(uint8 *bitmap) // IN
-{
- size_t idx;
-
- for (idx = 0; idx < maxNotifyIdx; idx++) {
- if (bitmap[idx] & 0x1) {
- bitmap[idx] &= ~1;
- VMCINotifyHashFireEntries(idx);
- }
- }
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCIDoorbell_Create --
- *
- * Creates a doorbell with the given callback. If the handle is
- * VMCI_INVALID_HANDLE, a free handle will be assigned, if
- * possible. The callback can be run in interrupt context (the
- * default) or delayed (in a kernel thread) by specifying the
- * flag VMCI_FLAG_DELAYED_CB. If delayed execution is selected, a
- * given callback may not be run if the kernel is unable to
- * allocate memory for the delayed execution (highly unlikely).
- *
- * Results:
- * VMCI_SUCCESS on success, appropriate error code otherwise.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
-int
-VMCIDoorbell_Create(VMCIHandle *handle, // IN
- uint32 flags, // IN
- VMCIPrivilegeFlags privFlags, // IN: Unused in guest
- VMCICallback notifyCB, // IN
- void *clientData) // IN
-{
- if (!handle || !notifyCB || flags & ~VMCI_FLAG_DELAYED_CB) {
- return VMCI_ERROR_INVALID_ARGS;
- }
- if (privFlags & ~VMCI_LEAST_PRIVILEGE_FLAGS) {
- return VMCI_ERROR_NO_ACCESS;
- }
-
- return VMCINotificationRegister(handle, TRUE, flags,
- notifyCB, clientData);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCIDoorbell_Destroy --
- *
- * Destroys a doorbell previously created with
- * VMCIDoorbell_Create. This operation may block waiting for a
- * callback to finish.
- *
- * Results:
- * VMCI_SUCCESS on success, appropriate error code otherwise.
- *
- * Side effects:
- * May block.
- *
- *-----------------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
-int
-VMCIDoorbell_Destroy(VMCIHandle handle) // IN
-{
- if (VMCI_HANDLE_INVALID(handle)) {
- return VMCI_ERROR_INVALID_ARGS;
- }
- return VMCINotificationUnregister(handle, TRUE);
-}
-
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCIDoorbell_Notify --
- *
- * Generates a notification on the doorbell identified by the
- * handle.
- *
- * Results:
- * VMCI_SUCCESS on success, appropriate error code otherwise.
- *
- * Side effects:
- * May do a hypercall.
- *
- *-----------------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
-int
-VMCIDoorbell_Notify(VMCIHandle handle, // IN
- VMCIPrivilegeFlags privFlags) // IN: Unused in guest
-{
- VMCIDoorbellNotifyMsg notifyMsg;
- int result;
-
- if (VMCI_HANDLE_INVALID(handle)) {
- return VMCI_ERROR_INVALID_ARGS;
- }
-
- if (privFlags & ~VMCI_LEAST_PRIVILEGE_FLAGS) {
- return VMCI_ERROR_NO_ACCESS;
- }
-
- notifyMsg.hdr.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, VMCI_DOORBELL_NOTIFY);
- notifyMsg.hdr.src = VMCI_ANON_SRC_HANDLE;
- notifyMsg.hdr.payloadSize = sizeof notifyMsg - VMCI_DG_HEADERSIZE;
- notifyMsg.handle = handle;
-
- result = VMCI_SendDatagram((VMCIDatagram *)¬ifyMsg);
-
- return result;
-}
-
-
-#else // defined(SOLARIS) || defined(__APPLE__)
-
-/*
- *-----------------------------------------------------------------------------
- *
- * VMCIDoorbell_Create/VMCIDoorbell_Destroy/VMCIDoorbell_Notify --
- *
- * The doorbell functions have yet to be implemented for Solaris
- * and Mac OS X guest drivers.
- *
- * Results:
- * VMCI_ERROR_UNAVAILABLE.
- *
- * Side effects:
- * None.
- *
- *-----------------------------------------------------------------------------
- */
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Create)
-int
-VMCIDoorbell_Create(VMCIHandle *handle, // IN
- uint32 flags, // I
- VMCIPrivilegeFlags privFlags, // IN
- VMCICallback notifyCB, // IN
- void *clientData) // IN
-{
- return VMCI_ERROR_UNAVAILABLE;
-}
-
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Destroy)
-int
-VMCIDoorbell_Destroy(VMCIHandle handle) // IN
-{
- return VMCI_ERROR_UNAVAILABLE;
-}
-
-
-VMCI_EXPORT_SYMBOL(VMCIDoorbell_Notify)
-int
-VMCIDoorbell_Notify(VMCIHandle handle, // IN
- VMCIPrivilegeFlags privFlags) // IN
-{
- return VMCI_ERROR_UNAVAILABLE;
-}
-
-#endif
+++ /dev/null
-/*********************************************************
- * Copyright (C) 2010 VMware, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation version 2 and no later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- *********************************************************/
-
-/*
- * vmciNotifications.h --
- *
- * VMCI notifications API for OS device drivers.
- */
-
-#ifndef _VMCI_NOTIFICATIONS_H_
-#define _VMCI_NOTIFICATIONS_H_
-
-#include "vm_basic_types.h"
-#include "vmci_defs.h"
-#include "vmciKernelAPI.h"
-
-void VMCINotifications_Init(void);
-void VMCINotifications_Exit(void);
-void VMCINotifications_Sync(void);
-
-Bool VMCI_RegisterNotificationBitmap(PPN bitmapPPN);
-void VMCI_ScanNotificationBitmap(uint8 *bitmap);
-
-int VMCINotificationRegister(VMCIHandle *handle, Bool doorbell, uint32 flags,
- VMCICallback notifyCB, void *callbackData);
-int VMCINotificationUnregister(VMCIHandle handle, Bool doorbell);
-
-void VMCINotifications_Hibernate(Bool enterHibernation);
-
-#endif /* !_VMCI_NOTIFICATIONS_H_ */
-
/* Public Resource Access Control API. */
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Init --
*
* Initializes the VMCI Resource Access Control API. Creates a hashtable
- * to hold all resources, and registers vectors and callbacks for hypercalls.
+ * to hold all resources, and registers vectors and callbacks for
+ * hypercalls.
*
* Results:
* None.
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
int
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Exit --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
void
/*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_GetID --
*
* None.
*
*
- *-------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
VMCIId
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Add --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
int
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Remove --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
void
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Get --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
VMCIResource *
return NULL;
}
resource = RESOURCE_CONTAINER(entry, VMCIResource, hashEntry);
- if ((resourceType == VMCI_RESOURCE_TYPE_ANY) || (resource->type == resourceType)) {
+ if (resourceType == VMCI_RESOURCE_TYPE_ANY ||
+ resource->type == resourceType) {
return resource;
}
VMCIHashTable_ReleaseEntry(resourceTable, entry);
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
+ *
+ * VMCIResource_Hold --
+ *
+ * Hold the given resource. This will hold the hashtable entry. This
+ * is like doing a Get() but without having to lookup the resource by
+ * handle.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------------------------
+ */
+
+void
+VMCIResource_Hold(VMCIResource *resource)
+{
+ ASSERT(resource);
+ VMCIHashTable_HoldEntry(resourceTable, &resource->hashEntry);
+}
+
+
+/*
+ *------------------------------------------------------------------------------
*
* VMCIResourceDoRemove --
*
* Side effects:
* May deallocate memory and invoke a callback for the removed resource.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
static void INLINE
/*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Release --
*
* Side effects:
* resource's containerFreeCB will get called if last reference.
*
- *-----------------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
int
/*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*
* VMCIResource_Sync --
*
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *------------------------------------------------------------------------------
*/
void
VMCIResourceType resourceType);
VMCIResource *VMCIResource_Get(VMCIHandle resourceHandle,
VMCIResourceType resourceType);
+void VMCIResource_Hold(VMCIResource *resource);
int VMCIResource_Release(VMCIResource *resource);
if (fromGuest && hasHostDevice &&
(VMCI_WELL_KNOWN_CONTEXT_ID == src->context ||
VMCI_WELL_KNOWN_CONTEXT_ID == dst->context)) {
- *route = VMCI_ROUTE_HOST_TO_GUEST;
+ *route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
}
}
/* Send from local client down to the hypervisor. */
- *route = VMCI_ROUTE_GUEST_TO_HYPERVISOR;
+ *route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}
}
/* Send it from local client down to the host. */
- *route = VMCI_ROUTE_GUEST_TO_HOST;
+ *route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}
}
/* Route to local client. */
- *route = VMCI_ROUTE_HOST_TO_SELF;
+ *route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
}
}
/* Pass it up to the guest. */
- *route = VMCI_ROUTE_HOST_TO_GUEST;
+ *route = VMCI_ROUTE_AS_HOST;
return VMCI_SUCCESS;
}
}
* the other guest for us.
*/
- *route = VMCI_ROUTE_GUEST_TO_HOST;
+ *route = VMCI_ROUTE_AS_GUEST;
return VMCI_SUCCESS;
}
{
const char *vmciRouteStrings[] = {
"none",
- "host to self",
- "host to guest",
- "guest to host/hypervisor",
+ "as host",
+ "as guest",
};
- if (route >= VMCI_ROUTE_NONE && route <= VMCI_ROUTE_GUEST_TO_HYPERVISOR) {
+ if (route >= VMCI_ROUTE_NONE && route <= VMCI_ROUTE_AS_GUEST) {
return vmciRouteStrings[route];
}
return "";
typedef enum {
VMCI_ROUTE_NONE,
- VMCI_ROUTE_HOST_TO_SELF,
- VMCI_ROUTE_HOST_TO_GUEST,
- VMCI_ROUTE_GUEST_TO_HOST,
- VMCI_ROUTE_GUEST_TO_HYPERVISOR = VMCI_ROUTE_GUEST_TO_HOST
+ VMCI_ROUTE_AS_HOST,
+ VMCI_ROUTE_AS_GUEST,
} VMCIRoute;
#include "vmci_version.h"
#include "vmciContext.h"
#include "vmciDatagram.h"
+#include "vmciDoorbell.h"
#include "vmciEvent.h"
#include "vmciInt.h"
-#include "vmciNotifications.h"
#include "vmciQueuePairInt.h"
#include "vmciResource.h"
#include "vmciUtil.h"
VMCIDatagram_Init();
VMCIEvent_Init();
VMCIUtil_Init();
- VMCINotifications_Init();
+ VMCIDoorbell_Init();
VMCIQPGuestEndpoints_Init();
/*
components_exit:
VMCIQPGuestEndpoints_Exit();
- VMCINotifications_Exit();
+ VMCIDoorbell_Exit();
VMCIUtil_Exit();
VMCIEvent_Exit();
VMCIContext_Exit();
release_region(dev->ioaddr, dev->ioaddr_size);
dev->enabled = FALSE;
- VMCINotifications_Exit();
+ VMCIDoorbell_Exit();
if (notification_bitmap) {
vfree(notification_bitmap);
}