From: VMware, Inc <> Date: Thu, 24 Feb 2011 23:01:12 +0000 (-0800) Subject: Unify the VMCI datagram code. X-Git-Tag: 2011.02.23-368700~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=864bf2f5d47bce54fdba3fedbbe5ca0ffe5262e7;p=thirdparty%2Fopen-vm-tools.git Unify the VMCI datagram code. We now use the host datagram code, with resources and contexts, for both drivers, with only a few tweaks. We have a new component that performs routing, and using the decision from that, we dispatch to host or guest as appropriate. There are obviously still some TODOs. We don't yet create a context for the guest itself when acting as a guest, and this is necessary to get the privs flags right; for now, we steal the original guest code for that. And getting the context ID is still determined by which driver we build. Most of this change is in vmciDatagram.c and vmciRoute.c; the rest was necessary to get the files to compile for both drivers, on all platforms. Bleh. Also cleaned up a bunch of whitespace/tabs. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/modules/linux/shared/vmci_kernel_if.h b/open-vm-tools/modules/linux/shared/vmci_kernel_if.h index a03864b1c..51bc36423 100644 --- a/open-vm-tools/modules/linux/shared/vmci_kernel_if.h +++ b/open-vm-tools/modules/linux/shared/vmci_kernel_if.h @@ -261,13 +261,9 @@ void VMCI_FreeKernelMem(void *ptr, size_t size); int VMCI_CopyToUser(VA64 dst, const void *src, size_t len, int mode); #else int VMCI_CopyToUser(VA64 dst, const void *src, size_t len); -/* - * Don't need the following for guests, hence no Solaris code for this - * function. - */ +#endif Bool VMCIWellKnownID_AllowMap(VMCIId wellKnownID, VMCIPrivilegeFlags privFlags); -#endif int VMCIHost_CompareUser(VMCIHostUser *user1, VMCIHostUser *user2); @@ -365,6 +361,8 @@ void VMCI_FreeQueueBuffer(void *queue, uint64 size); void VMCI_DeviceShutdownBegin(void); void VMCI_DeviceShutdownEnd(void); Bool VMCI_DeviceShutdown(void); +Bool VMCI_HasGuestDevice(void); +Bool VMCI_HasHostDevice(void); #else // _WIN32 # define VMCI_InitQueueMutex(_pq, _cq) # define VMCI_AcquireQueueMutex(_q) @@ -374,8 +372,16 @@ Bool VMCI_DeviceShutdown(void); # define VMCI_RevertToNonLocalQueue(_q, _nlq, _s) # define VMCI_FreeQueueBuffer(_q, _s) # define VMCI_DeviceShutdown() FALSE +#if defined(VMX86_TOOLS) +# define VMCI_HasGuestDevice() TRUE +# define VMCI_HasHostDevice() FALSE +#else // VMX86_TOOLS +# define VMCI_HasGuestDevice() FALSE +# define VMCI_HasHostDevice() TRUE +#endif // VMX86_TOOLS #endif // !_WIN32 + #if defined(VMKERNEL) typedef List_Links VMCIListItem; typedef List_Links VMCIList; diff --git a/open-vm-tools/modules/linux/vmci/stubs.c b/open-vm-tools/modules/linux/vmci/stubs.c new file mode 100644 index 000000000..fec98ab2f --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/stubs.c @@ -0,0 +1,154 @@ +/********************************************************* + * Copyright (C) 2011 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 + * + *********************************************************/ + +/* + * stubs.c + * + * Stubs for host functions still missing from the guest driver. + * + */ + +#ifdef __linux__ +# include "driver-config.h" +#endif + +#include "vmci_kernel_if.h" +#include "vmci_defs.h" +#include "vmciContext.h" +#include "vmciDoorbell.h" + + +/* + *------------------------------------------------------------------------------ + * + * VMCIQPBroker_Lock -- + * + * Stub. Not called in the guest driver (yet). + * + * Result: + * Always VMCI_ERROR_GENERIC. + * + *------------------------------------------------------------------------------ + */ + +void +VMCIQPBroker_Lock(void) +{ +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIQPBroker_Unlock -- + * + * Stub. Not called in the guest driver (yet). + * + * Result: + * Always VMCI_ERROR_GENERIC. + * + *------------------------------------------------------------------------------ + */ + +void +VMCIQPBroker_Unlock(void) +{ +} + + +/* + *------------------------------------------------------------------------------ + * + * QueuePair_Detach -- + * + * Stub. Not called in the guest driver (yet). + * + * Result: + * Always VMCI_ERROR_GENERIC. + * + *------------------------------------------------------------------------------ + */ + +int +VMCIQPBroker_Detach(VMCIHandle handle, + VMCIContext *context, + Bool detach) +{ + return VMCI_ERROR_GENERIC; +} + + +/* + *------------------------------------------------------------------------------ + * + * 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; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIUnsetNotify -- + * + * Stub. Not called in the guest driver (yet). + * + * Result: + * Always VMCI_ERROR_GENERIC. + * + *------------------------------------------------------------------------------ + */ + +void +VMCIUnsetNotify(VMCIContext *context) +{ +} diff --git a/open-vm-tools/modules/linux/vmci/vmciCommonInt.h b/open-vm-tools/modules/linux/vmci/vmciCommonInt.h new file mode 100644 index 000000000..b8cd0c821 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciCommonInt.h @@ -0,0 +1,165 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciCommonInt.h -- + * + * Struct definitions for VMCI internal common code. + */ + +#ifndef _VMCI_COMMONINT_H_ +#define _VMCI_COMMONINT_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vm_atomic.h" +#include "vmci_defs.h" +#include "vmci_call_defs.h" +#include "vmci_infrastructure.h" +#include "vmci_handle_array.h" +#include "vmci_kernel_if.h" + + +/* + * The DatagramQueueEntry is a queue header for the in-kernel VMCI + * datagram queues. It is allocated in non-paged memory, as the + * content is accessed while holding a spinlock. The pending datagram + * itself may be allocated from paged memory. We shadow the size of + * the datagram in the non-paged queue entry as this size is used + * while holding the same spinlock as above. + */ + +typedef struct DatagramQueueEntry { + VMCIListItem listItem; /* For queuing. */ + size_t dgSize; /* Size of datagram. */ + VMCIDatagram *dg; /* Pending datagram. */ +} DatagramQueueEntry; + + +struct VMCIContext { + VMCIListItem listItem; /* For global VMCI list. */ + VMCIId cid; + Atomic_uint32 refCount; + VMCIList datagramQueue; /* Head of per VM queue. */ + uint32 pendingDatagrams; + size_t datagramQueueSize;/* Size of datagram queue in bytes. */ + int userVersion; /* + * Version of the code that created + * this context; e.g., VMX. + */ + VMCILock lock; /* Locks callQueue and handleArrays. */ + VMCIHandleArray *wellKnownArray; /* WellKnown mappings owned by context. */ + VMCIHandleArray *queuePairArray; /* + * QueuePairs attached to. The array of + * handles for queue pairs is accessed + * from the code for QP API, and there + * it is protected by the QP lock. It + * is also accessed from the context + * clean up path, which does not + * require a lock. VMCILock is not + * used to protect the QP array field. + */ + VMCIHandleArray *doorbellArray; /* Doorbells created by context. */ + VMCIHandleArray *pendingDoorbellArray; /* Doorbells pending for context. */ + VMCIHandleArray *notifierArray; /* Contexts current context is subscribing to. */ + VMCIHost hostContext; + VMCIPrivilegeFlags privFlags; + VMCIHostUser user; + Bool validUser; +#ifdef VMKERNEL + char domainName[VMCI_DOMAIN_NAME_MAXLEN]; + Bool isQuiesced; /* Whether current VM is quiesced */ + VMCIId migrateCid; /* The migrate cid if it is migrating */ +#endif +#ifndef VMX86_SERVER + Bool *notify; /* Notify flag pointer - hosted only. */ +# ifdef __linux__ + struct page *notifyPage; /* Page backing the notify UVA. */ +# endif +#endif +}; + + +/* + *------------------------------------------------------------------------------ + * + * VMCIDenyInteraction -- + * + * Utilility function that checks whether two entities are allowed + * to interact. If one of them is restricted, the other one must + * be trusted. On ESX, the vmci domain must match for unrestricted + * domains. + * + * Result: + * TRUE if the two entities are not allowed to interact. FALSE otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static INLINE Bool +VMCIDenyInteraction(VMCIPrivilegeFlags partOne, // IN + VMCIPrivilegeFlags partTwo, // IN + const char *srcDomain, // IN: Unused on hosted + const char *dstDomain) // IN: Unused on hosted +{ +#ifndef VMKERNEL + return (((partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) && + !(partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)) || + ((partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED) && + !(partOne & VMCI_PRIVILEGE_FLAG_TRUSTED))); +#else + /* + * If source or destination is trusted (hypervisor), we allow the + * communication. + */ + if ((partOne & VMCI_PRIVILEGE_FLAG_TRUSTED) || + (partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)) { + return FALSE; + } + /* + * If source or destination is restricted, we deny the communication. + */ + if ((partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) || + (partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED)) { + return TRUE; + } + /* + * We are here, means that neither of source or destination are trusted, and + * both are unrestricted. + */ + ASSERT(!(partOne & VMCI_PRIVILEGE_FLAG_TRUSTED) && + !(partTwo & VMCI_PRIVILEGE_FLAG_TRUSTED)); + ASSERT(!(partOne & VMCI_PRIVILEGE_FLAG_RESTRICTED) && + !(partTwo & VMCI_PRIVILEGE_FLAG_RESTRICTED)); + /* + * We now compare the source and destination domain names, and allow + * communication iff they match. + */ + return strcmp(srcDomain, dstDomain) ? TRUE : /* Deny. */ + FALSE; /* Allow. */ +#endif +} + +#endif /* _VMCI_COMMONINT_H_ */ diff --git a/open-vm-tools/modules/linux/vmci/vmciContext.c b/open-vm-tools/modules/linux/vmci/vmciContext.c new file mode 100644 index 000000000..d978c546d --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciContext.c @@ -0,0 +1,2390 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciContext.c -- + * + * Platform independent routines for VMCI calls. + */ + +#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 "vmciContext.h" +#include "vmciDatagram.h" +#include "vmciDoorbell.h" +#include "vmciEvent.h" +#include "vmciKernelAPI.h" +#include "vmciQueuePair.h" +#include "vmciResource.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 "VMCIContext: " + +static void VMCIContextFreeContext(VMCIContext *context); +static Bool VMCIContextExists(VMCIId cid); +static int VMCIContextFireNotification(VMCIId contextID, + VMCIPrivilegeFlags privFlags, + const char *domain); + +/* + * List of current VMCI contexts. + */ + +static struct { + VMCIList head; + VMCILock lock; + VMCILock firingLock; +} contextList; + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextSignalNotify -- + * + * Sets the notify flag to TRUE. Assumes that the context lock is + * held. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static INLINE void +VMCIContextSignalNotify(VMCIContext *context) // IN: +{ +#ifndef VMX86_SERVER + if (context->notify) { + *context->notify = TRUE; + } +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextClearNotify -- + * + * Sets the notify flag to FALSE. Assumes that the context lock is + * held. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static INLINE void +VMCIContextClearNotify(VMCIContext *context) // IN: +{ +#ifndef VMX86_SERVER + if (context->notify) { + *context->notify = FALSE; + } +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextClearNotifyAndCall -- + * + * If nothing requires the attention of the guest, clears both + * notify flag and call. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static INLINE void +VMCIContextClearNotifyAndCall(VMCIContext *context) // IN: +{ + if (context->pendingDatagrams == 0 && + VMCIHandleArray_GetSize(context->pendingDoorbellArray) == 0) { + VMCIHost_ClearCall(&context->hostContext); + VMCIContextClearNotify(context); + } +} + + +#ifndef VMX86_SERVER +/* + *---------------------------------------------------------------------- + * + * VMCIContext_CheckAndSignalNotify -- + * + * Sets the context's notify flag iff datagrams are pending for this + * context. Called from VMCISetupNotify(). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_CheckAndSignalNotify(VMCIContext *context) // IN: +{ + VMCILockFlags flags; + + ASSERT(context); + VMCI_GrabLock(&contextList.lock, &flags); + if (context->pendingDatagrams) { + VMCIContextSignalNotify(context); + } + VMCI_ReleaseLock(&contextList.lock, flags); +} +#endif + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextGetDomainName -- + * + * Internal function for retrieving a context domain name, if + * supported by the platform. The returned pointer can only be + * assumed valid while a reference count is held on the given + * context. + * + * Results: + * Pointer to name if appropriate. NULL otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static INLINE char * +VMCIContextGetDomainName(VMCIContext *context) // IN +{ +#ifdef VMKERNEL + return context->domainName; +#else + return NULL; +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_Init -- + * + * Initializes the VMCI context module. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_Init(void) +{ + VMCIList_Init(&contextList.head); + VMCI_InitLock(&contextList.lock, "VMCIContextListLock", + VMCI_LOCK_RANK_HIGHER); + VMCI_InitLock(&contextList.firingLock, "VMCIContextFiringLock", + VMCI_LOCK_RANK_MIDDLE_LOW); + + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_Exit -- + * + * Cleans up the contexts module. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_Exit(void) +{ + VMCI_CleanupLock(&contextList.firingLock); + VMCI_CleanupLock(&contextList.lock); +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_InitContext -- + * + * Allocates and initializes a VMCI context. + * + * Results: + * Returns 0 on success, appropriate error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_InitContext(VMCIId cid, // IN + VMCIPrivilegeFlags privFlags, // IN + uintptr_t eventHnd, // IN + int userVersion, // IN: User's vers no. + VMCIHostUser *user, // IN + VMCIContext **outContext) // OUT +{ + VMCILockFlags flags; + VMCIContext *context; + int result; + + if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) { + VMCI_WARNING((LGPFX"Invalid flag for VMCI context.\n")); + return VMCI_ERROR_INVALID_ARGS; + } + + if (userVersion == 0) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCI_AllocKernelMem(sizeof *context, VMCI_MEMORY_NONPAGED); + if (context == NULL) { + VMCI_WARNING((LGPFX"Failed to allocate memory for VMCI context.\n")); + return VMCI_ERROR_NO_MEM; + } + memset(context, 0, sizeof *context); + + VMCIList_InitEntry(&context->listItem); + VMCIList_Init(&context->datagramQueue); + + context->userVersion = userVersion; + + context->wellKnownArray = VMCIHandleArray_Create(0); + if (context->wellKnownArray == NULL) { + result = VMCI_ERROR_NO_MEM; + goto error; + } + + context->queuePairArray = VMCIHandleArray_Create(0); + if (!context->queuePairArray) { + result = VMCI_ERROR_NO_MEM; + goto error; + } + + context->doorbellArray = VMCIHandleArray_Create(0); + if (!context->doorbellArray) { + result = VMCI_ERROR_NO_MEM; + goto error; + } + + context->pendingDoorbellArray = VMCIHandleArray_Create(0); + if (!context->pendingDoorbellArray) { + result = VMCI_ERROR_NO_MEM; + goto error; + } + + context->notifierArray = VMCIHandleArray_Create(0); + if (context->notifierArray == NULL) { + result = VMCI_ERROR_NO_MEM; + goto error; + } + + VMCI_InitLock(&context->lock, + "VMCIContextLock", + VMCI_LOCK_RANK_HIGHER); + Atomic_Write(&context->refCount, 1); + + /* Inititialize host-specific VMCI context. */ + VMCIHost_InitContext(&context->hostContext, eventHnd); + + context->privFlags = privFlags; + + /* + * If we collide with an existing context we generate a new and use it + * instead. The VMX will determine if regeneration is okay. Since there + * isn't 4B - 16 VMs running on a given host, the below loop will terminate. + */ + VMCI_GrabLock(&contextList.lock, &flags); + ASSERT(cid != VMCI_INVALID_ID); + while (VMCIContextExists(cid)) { + + /* + * If the cid is below our limit and we collide we are creating duplicate + * contexts internally so we want to assert fail in that case. + */ + ASSERT(cid >= VMCI_RESERVED_CID_LIMIT); + + /* We reserve the lowest 16 ids for fixed contexts. */ + cid = MAX(cid, VMCI_RESERVED_CID_LIMIT-1) + 1; + if (cid == VMCI_INVALID_ID) { + cid = VMCI_RESERVED_CID_LIMIT; + } + } + ASSERT(!VMCIContextExists(cid)); + context->cid = cid; + context->validUser = user != NULL; + if (context->validUser) { + context->user = *user; + } + VMCIList_Insert(&context->listItem, &contextList.head); + VMCI_ReleaseLock(&contextList.lock, flags); + +#ifdef VMKERNEL + /* + * Set default domain name. + */ + VMCIContext_SetDomainName(context, ""); + VMCIContext_SetFSRState(context, FALSE, VMCI_INVALID_ID, eventHnd, FALSE); +#endif + +#ifndef VMX86_SERVER + context->notify = NULL; +# ifdef __linux__ + context->notifyPage = NULL; +# endif +#endif + + *outContext = context; + return VMCI_SUCCESS; + +error: + if (context->notifierArray) { + VMCIHandleArray_Destroy(context->notifierArray); + } + if (context->wellKnownArray) { + VMCIHandleArray_Destroy(context->wellKnownArray); + } + if (context->queuePairArray) { + VMCIHandleArray_Destroy(context->queuePairArray); + } + if (context->doorbellArray) { + VMCIHandleArray_Destroy(context->doorbellArray); + } + if (context->pendingDoorbellArray) { + VMCIHandleArray_Destroy(context->pendingDoorbellArray); + } + VMCI_FreeKernelMem(context, sizeof *context); + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_ReleaseContext -- + * + * Cleans up a VMCI context. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_ReleaseContext(VMCIContext *context) // IN +{ + VMCILockFlags flags; + + /* Dequeue VMCI context. */ + + VMCI_GrabLock(&contextList.lock, &flags); + VMCIList_Remove(&context->listItem, &contextList.head); + VMCI_ReleaseLock(&contextList.lock, flags); + + VMCIContext_Release(context); +} + + +/* + *----------------------------------------------------------------------------- + * + * VMCIContextFreeContext -- + * + * Deallocates all parts of a context datastructure. This + * functions doesn't lock the context, because it assumes that + * the caller is holding the last reference to context. As paged + * memory may be freed as part of the call, the function must be + * called without holding any spinlocks as this is not allowed on + * Windows. + * + * Results: + * None. + * + * Side effects: + * Paged memory is freed. + * + *----------------------------------------------------------------------------- + */ + +static void +VMCIContextFreeContext(VMCIContext *context) // IN +{ + VMCIListItem *curr; + VMCIListItem *next; + DatagramQueueEntry *dqEntry; + VMCIHandle tempHandle; + + /* Fire event to all contexts interested in knowing this context is dying. */ + VMCIContextFireNotification(context->cid, context->privFlags, + VMCIContextGetDomainName(context)); + + /* + * Cleanup all wellknown mappings owned by context. Ideally these would + * be removed already but we maintain this list to make sure no resources + * are leaked. It is updated by the VMCIDatagramAdd/RemoveWellKnownMap. + */ + ASSERT(context->wellKnownArray); + tempHandle = VMCIHandleArray_RemoveTail(context->wellKnownArray); + while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) { + VMCIDatagramRemoveWellKnownMap(tempHandle.resource, context->cid); + tempHandle = VMCIHandleArray_RemoveTail(context->wellKnownArray); + } + +#ifndef VMKERNEL + /* + * Cleanup all queue pair resources attached to context. If the VM dies + * without cleaning up, this code will make sure that no resources are + * leaked. + */ + + tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0); + while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) { + VMCIQPBroker_Lock(); + if (VMCIQPBroker_Detach(tempHandle, context, TRUE) < VMCI_SUCCESS) { + /* + * When VMCIQPBroker_Detach() succeeds it removes the handle from the + * array. If detach fails, we must remove the handle ourselves. + */ + VMCIHandleArray_RemoveEntry(context->queuePairArray, tempHandle); + } + VMCIQPBroker_Unlock(); + tempHandle = VMCIHandleArray_GetEntry(context->queuePairArray, 0); + } +#else + /* + * On ESX, all entries in the queuePairArray have been cleaned up + * either by the regular VMCI device destroy path or by the world + * cleanup destroy path. We assert that no resources are leaked. + */ + + ASSERT(VMCI_HANDLE_EQUAL(VMCIHandleArray_GetEntry(context->queuePairArray, 0), + VMCI_INVALID_HANDLE)); +#endif /* !VMKERNEL */ + + /* + * It is fine to destroy this without locking the callQueue, as + * this is the only thread having a reference to the context. + */ + + VMCIList_ScanSafe(curr, next, &context->datagramQueue) { + dqEntry = VMCIList_Entry(curr, DatagramQueueEntry, listItem); + VMCIList_Remove(curr, &context->datagramQueue); + ASSERT(dqEntry && dqEntry->dg); + ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg)); + VMCI_FreeKernelMem(dqEntry->dg, dqEntry->dgSize); + VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); + } + + VMCIHandleArray_Destroy(context->notifierArray); + VMCIHandleArray_Destroy(context->wellKnownArray); + VMCIHandleArray_Destroy(context->queuePairArray); + VMCIHandleArray_Destroy(context->doorbellArray); + VMCIHandleArray_Destroy(context->pendingDoorbellArray); + VMCI_CleanupLock(&context->lock); + VMCIHost_ReleaseContext(&context->hostContext); +#ifndef VMX86_SERVER +# ifdef __linux__ + /* TODO Windows and Mac OS. */ + VMCIUnsetNotify(context); +# endif +#endif + VMCI_FreeKernelMem(context, sizeof *context); +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_PendingDatagrams -- + * + * Returns the current number of pending datagrams. The call may + * also serve as a synchronization point for the datagram queue, + * as no enqueue operations can occur concurrently. + * + * Results: + * Length of datagram queue for the given context. + * + * Side effects: + * Locks datagram queue. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_PendingDatagrams(VMCIId cid, // IN + uint32 *pending) // OUT +{ + VMCIContext *context; + VMCILockFlags flags; + + context = VMCIContext_Get(cid); + if (context == NULL) { + return VMCI_ERROR_INVALID_ARGS; + } + + VMCI_GrabLock(&context->lock, &flags); + if (pending) { + *pending = context->pendingDatagrams; + } + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + return VMCI_SUCCESS; +} + + +/* + * We allow at least 1024 more event datagrams from the hypervisor past the + * normally allowed datagrams pending for a given context. We define this + * limit on event datagrams from the hypervisor to guard against DoS attack + * from a malicious VM which could repeatedly attach to and detach from a queue + * pair, causing events to be queued at the destination VM. However, the rate + * at which such events can be generated is small since it requires a VM exit + * and handling of queue pair attach/detach call at the hypervisor. Event + * datagrams may be queued up at the destination VM if it has interrupts + * disabled or if it is not draining events for some other reason. 1024 + * datagrams is a grossly conservative estimate of the time for which + * interrupts may be disabled in the destination VM, but at the same time does + * not exacerbate the memory pressure problem on the host by much (size of each + * event datagram is small). + */ + +#define VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE \ + (VMCI_MAX_DATAGRAM_QUEUE_SIZE + \ + 1024 * (sizeof(VMCIDatagram) + sizeof(VMCIEventData_Max))) + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_EnqueueDatagram -- + * + * Queues a VMCI datagram for the appropriate target VM + * context. + * + * Results: + * Size of enqueued data on success, appropriate error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_EnqueueDatagram(VMCIId cid, // IN: Target VM + VMCIDatagram *dg) // IN: +{ + DatagramQueueEntry *dqEntry; + VMCIContext *context; + VMCILockFlags flags; + VMCIHandle dgSrc; + size_t vmciDgSize; + + ASSERT(dg); + vmciDgSize = VMCI_DG_SIZE(dg); + ASSERT(vmciDgSize <= VMCI_MAX_DG_SIZE); + + /* Get the target VM's VMCI context. */ + context = VMCIContext_Get(cid); + if (context == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Invalid cid.\n")); + return VMCI_ERROR_INVALID_ARGS; + } + + /* Allocate guest call entry and add it to the target VM's queue. */ + dqEntry = VMCI_AllocKernelMem(sizeof *dqEntry, VMCI_MEMORY_NONPAGED); + if (dqEntry == NULL) { + VMCI_WARNING((LGPFX"Failed to allocate memory for datagram.\n")); + VMCIContext_Release(context); + return VMCI_ERROR_NO_MEM; + } + dqEntry->dg = dg; + dqEntry->dgSize = vmciDgSize; + dgSrc = dg->src; + VMCIList_InitEntry(&dqEntry->listItem); + + VMCI_GrabLock(&context->lock, &flags); + /* + * We put a higher limit on datagrams from the hypervisor. If the pending + * datagram is not from hypervisor, then we check if enqueueing it would + * exceed the VMCI_MAX_DATAGRAM_QUEUE_SIZE limit on the destination. If the + * pending datagram is from hypervisor, we allow it to be queued at the + * destination side provided we don't reach the + * VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE limit. + */ + if (context->datagramQueueSize + vmciDgSize >= + VMCI_MAX_DATAGRAM_QUEUE_SIZE && + (!VMCI_HANDLE_EQUAL(dgSrc, + VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, + VMCI_CONTEXT_RESOURCE_ID)) || + context->datagramQueueSize + vmciDgSize >= + VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE)) { + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); + VMCI_DEBUG_LOG(10, (LGPFX"Context 0x%x receive queue is full.\n", cid)); + return VMCI_ERROR_NO_RESOURCES; + } + + VMCIList_Insert(&dqEntry->listItem, &context->datagramQueue); + context->pendingDatagrams++; + context->datagramQueueSize += vmciDgSize; + VMCIContextSignalNotify(context); + VMCIHost_SignalCall(&context->hostContext); + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + return vmciDgSize; +} + +#undef VMCI_MAX_DATAGRAM_AND_EVENT_QUEUE_SIZE + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextExists -- + * + * Internal helper to check if a context with the specified context + * ID exists. Assumes the contextList.lock is held. + * + * Results: + * TRUE if a context exists with the given cid. + * FALSE otherwise + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static Bool +VMCIContextExists(VMCIId cid) // IN +{ + VMCIContext *context; + VMCIListItem *next; + Bool rv = FALSE; + + VMCIList_Scan(next, &contextList.head) { + context = VMCIList_Entry(next, VMCIContext, listItem); + if (context->cid == cid) { + rv = TRUE; + break; + } + } + return rv; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_Exists -- + * + * Verifies whether a context with the specified context ID exists. + * + * Results: + * TRUE if a context exists with the given cid. + * FALSE otherwise + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +VMCIContext_Exists(VMCIId cid) // IN +{ + VMCILockFlags flags; + Bool rv; + + VMCI_GrabLock(&contextList.lock, &flags); + rv = VMCIContextExists(cid); + VMCI_ReleaseLock(&contextList.lock, flags); + return rv; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_Get -- + * + * Retrieves VMCI context corresponding to the given cid. + * + * Results: + * VMCI context on success, NULL otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCIContext * +VMCIContext_Get(VMCIId cid) // IN +{ + VMCIContext *context = NULL; + VMCIListItem *next; + VMCILockFlags flags; + + if (cid == VMCI_INVALID_ID) { + return NULL; + } + + VMCI_GrabLock(&contextList.lock, &flags); + if (VMCIList_Empty(&contextList.head)) { + goto out; + } + + VMCIList_Scan(next, &contextList.head) { + context = VMCIList_Entry(next, VMCIContext, listItem); + if (context->cid == cid) { + /* + * At this point, we are sure that the reference count is + * larger already than zero. When starting the destruction of + * a context, we always remove it from the context list + * before decreasing the reference count. As we found the + * context here, it hasn't been destroyed yet. This means + * that we are not about to increase the reference count of + * something that is in the process of being destroyed. + */ + + Atomic_Inc(&context->refCount); + break; + } + } + +out: + VMCI_ReleaseLock(&contextList.lock, flags); + return (context && context->cid == cid) ? context : NULL; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_Release -- + * + * Releases the VMCI context. If this is the last reference to + * the context it will be deallocated. A context is created with + * a reference count of one, and on destroy, it is removed from + * the context list before its reference count is + * decremented. Thus, if we reach zero, we are sure that nobody + * else are about to increment it (they need the entry in the + * context list for that). This function musn't be called with a + * lock held. + * + * Results: + * None. + * + * Side effects: + * Paged memory may be deallocated. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_Release(VMCIContext *context) // IN +{ + uint32 refCount; + ASSERT(context); + refCount = Atomic_FetchAndDec(&context->refCount); + if (refCount == 1) { + VMCIContextFreeContext(context); + } +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_DequeueDatagram -- + * + * Dequeues the next datagram and returns it to caller. + * The caller passes in a pointer to the max size datagram + * it can handle and the datagram is only unqueued if the + * size is less than maxSize. If larger maxSize is set to + * the size of the datagram to give the caller a chance to + * set up a larger buffer for the guestcall. + * + * Results: + * On success: 0 if no more pending datagrams, otherwise the size of + * the next pending datagram. + * On failure: appropriate error code. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_DequeueDatagram(VMCIContext *context, // IN + size_t *maxSize, // IN/OUT: max size of datagram caller can handle. + VMCIDatagram **dg) // OUT: +{ + DatagramQueueEntry *dqEntry; + VMCIListItem *listItem; + VMCILockFlags flags; + int rv; + + ASSERT(context && dg); + + /* Dequeue the next datagram entry. */ + VMCI_GrabLock(&context->lock, &flags); + if (context->pendingDatagrams == 0) { + VMCIContextClearNotifyAndCall(context); + VMCI_ReleaseLock(&context->lock, flags); + VMCI_DEBUG_LOG(4, (LGPFX"No datagrams pending.\n")); + return VMCI_ERROR_NO_MORE_DATAGRAMS; + } + + listItem = VMCIList_First(&context->datagramQueue); + ASSERT (listItem != NULL); + + dqEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem); + ASSERT(dqEntry->dg); + + /* Check size of caller's buffer. */ + if (*maxSize < dqEntry->dgSize) { + *maxSize = dqEntry->dgSize; + VMCI_ReleaseLock(&context->lock, flags); + VMCI_DEBUG_LOG(4, (LGPFX"Caller's buffer is too small. It must be at " + "least %"FMTSZ"d bytes.\n", *maxSize)); + return VMCI_ERROR_NO_MEM; + } + + VMCIList_Remove(listItem, &context->datagramQueue); + context->pendingDatagrams--; + context->datagramQueueSize -= dqEntry->dgSize; + if (context->pendingDatagrams == 0) { + VMCIContextClearNotifyAndCall(context); + rv = VMCI_SUCCESS; + } else { + /* + * Return the size of the next datagram. + */ + DatagramQueueEntry *nextEntry; + + listItem = VMCIList_First(&context->datagramQueue); + ASSERT(listItem); + nextEntry = VMCIList_Entry(listItem, DatagramQueueEntry, listItem); + ASSERT(nextEntry && nextEntry->dg); + /* + * The following size_t -> int truncation is fine as the maximum size of + * a (routable) datagram is 68KB. + */ + rv = (int)nextEntry->dgSize; + } + VMCI_ReleaseLock(&context->lock, flags); + + /* Caller must free datagram. */ + ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg)); + *dg = dqEntry->dg; + dqEntry->dg = NULL; + VMCI_FreeKernelMem(dqEntry, sizeof *dqEntry); + + return rv; +} + + +#ifdef VMKERNEL +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SetFSRState -- + * + * Set the states related to FSR (quiesced state, migrateCid, + * active event handle). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_SetFSRState(VMCIContext *context, // IN + Bool isQuiesced, // IN + VMCIId migrateCid, // IN + uintptr_t eventHnd, // IN + Bool isLocked) // IN +{ + VMCILockFlags flags; + if (!context) { + return; + } + if (!isLocked) { + VMCI_GrabLock(&context->lock, &flags); + } + context->isQuiesced = isQuiesced; + context->migrateCid = migrateCid; + VMCIHost_SetActiveHnd(&context->hostContext, eventHnd); + if (!isLocked) { + VMCI_ReleaseLock(&context->lock, flags); + } +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_FindAndUpdateSrcFSR -- + * + * Find the source context for fast-suspend-resume. If found, the + * source context's FSR state is changed to reflect the new active + * event handle. + * + * Results: + * If found, source context for fast-suspend-resume, NULL otherwise. + * + * Side effects: + * The source context reference count increased and the caller is + * supposed to release the context once it is done using it. + * + *---------------------------------------------------------------------- + */ + +VMCIContext * +VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid, // IN + uintptr_t eventHnd, // IN + uintptr_t *srcEventHnd) // IN/OUT +{ + VMCIContext *contextSrc = VMCIContext_Get(migrateCid); + + if (contextSrc) { + VMCILockFlags flags; + + VMCI_GrabLock(&contextSrc->lock, &flags); + if (contextSrc->isQuiesced && contextSrc->migrateCid == migrateCid) { + if (srcEventHnd) { + *srcEventHnd = VMCIHost_GetActiveHnd(&contextSrc->hostContext); + ASSERT(*srcEventHnd != VMCI_INVALID_ID); + } + VMCIContext_SetFSRState(contextSrc, FALSE, VMCI_INVALID_ID, + eventHnd, TRUE); + VMCI_ReleaseLock(&contextSrc->lock, flags); + return contextSrc; + } + VMCI_ReleaseLock(&contextSrc->lock, flags); + VMCIContext_Release(contextSrc); + } + return NULL; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_IsActiveHnd -- + * + * Whether the curent event handle is the active handle. + * + * Results: + * TRUE if the event handle is active, FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +Bool +VMCIContext_IsActiveHnd(VMCIContext *context, // IN + uintptr_t eventHnd) // IN +{ + VMCILockFlags flags; + Bool isActive; + + ASSERT(context); + VMCI_GrabLock(&context->lock, &flags); + isActive = VMCIHost_IsActiveHnd(&context->hostContext, eventHnd); + VMCI_ReleaseLock(&context->lock, flags); + return isActive; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SetInactiveHnd -- + * + * Set the handle to be the inactive one. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_SetInactiveHnd(VMCIContext *context, // IN + uintptr_t eventHnd) // IN +{ + VMCILockFlags flags; + + ASSERT(context); + VMCI_GrabLock(&context->lock, &flags); + VMCIHost_SetInactiveHnd(&context->hostContext, eventHnd); + VMCI_ReleaseLock(&context->lock, flags); +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_RemoveHnd -- + * + * Remove the event handle from host context. + * + * Results: + * Whether the handle exists and removed, also number of handles + * before removal and number of handles after removal. + * + * Side effects: + * If this is active handle, the inactive handle becomes active. + * + *---------------------------------------------------------------------- + */ + +Bool +VMCIContext_RemoveHnd(VMCIContext *context, // IN + uintptr_t eventHnd, // IN + uint32 *numOld, // OUT + uint32 *numNew) // OUT +{ + VMCILockFlags flags; + uint32 numHandleOld, numHandleNew; + Bool ret; + + ASSERT(context); + VMCI_GrabLock(&context->lock, &flags); + numHandleOld = VMCIHost_NumHnds(&context->hostContext); + ret = VMCIHost_RemoveHnd(&context->hostContext, eventHnd); + numHandleNew = VMCIHost_NumHnds(&context->hostContext); + /* + * This is needed to prevent FSR to share this + * context while this context is being destroyed. + */ + if (ret && numHandleOld == 1 && numHandleNew == 1) { + context->migrateCid = VMCI_INVALID_ID; + } + VMCI_ReleaseLock(&context->lock, flags); + + if (numOld) { + *numOld = numHandleOld; + } + if (numNew) { + *numNew = numHandleNew; + } + return ret; +} + + +/* + *----------------------------------------------------------------------------- + * + * VMCIContext_ClearDatagrams -- + * + * Clear pending datagrams. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +VMCIContext_ClearDatagrams(VMCIContext *context) // IN +{ + int retval; + VMCIDatagram *dg = NULL; + size_t size = VMCI_MAX_DG_SIZE; + uint32 pending; + + /* Drop all datagrams that are currently pending for given context. */ + if (context == NULL) { + return; + } + retval = VMCIContext_PendingDatagrams(context->cid, &pending); + if (retval != VMCI_SUCCESS) { + /* + * This shouldn't happen as we already verified that the context + * exists. + */ + + ASSERT(FALSE); + return; + } + + /* + * We drain the queue for any datagrams pending at the beginning of + * the loop. As datagrams may arrive at any point in time, we + * cannot guarantee that the queue is empty after this point. Only + * removing a fixed number of pending datagrams prevents us from + * looping forever. + */ + + while (pending > 0 && + VMCIContext_DequeueDatagram(context, &size, &dg) >= 0) { + ASSERT(dg); + VMCI_FreeKernelMem(dg, VMCI_DG_SIZE(dg)); + --pending; + } +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SetId -- + * + * Set the cid of given VMCI context. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_SetId(VMCIContext *context, // IN + VMCIId cid) // IN: +{ + VMCILockFlags flags; + + if (!context) { + return; + } + VMCI_GrabLock(&context->lock, &flags); + context->cid = cid; + VMCI_ReleaseLock(&context->lock, flags); +} +#endif + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_GetId -- + * + * Retrieves cid of given VMCI context. + * + * Results: + * VMCIId of context on success, VMCI_INVALID_ID otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCIId +VMCIContext_GetId(VMCIContext *context) // IN: +{ + if (!context) { + return VMCI_INVALID_ID; + } + ASSERT(context->cid != VMCI_INVALID_ID); + return context->cid; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_GetPrivFlags -- + * + * Retrieves the privilege flags of the given VMCI context ID. + * + * Results: + * Context's privilege flags. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCI_EXPORT_SYMBOL(VMCIContext_GetPrivFlags) +VMCIPrivilegeFlags +VMCIContext_GetPrivFlags(VMCIId contextID) // IN +{ +#if defined(VMX86_TOOLS) + return VMCI_NO_PRIVILEGE_FLAGS; +#else // VMX86_TOOLS + VMCIPrivilegeFlags flags; + VMCIContext *context; + + context = VMCIContext_Get(contextID); + if (!context) { + return VMCI_LEAST_PRIVILEGE_FLAGS; + } + flags = context->privFlags; + VMCIContext_Release(context); + return flags; +#endif // VMX86_TOOLS +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_AddWellKnown -- + * + * Wrapper to call VMCIHandleArray_AppendEntry(). + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * As in VMCIHandleArray_AppendEntry(). + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_AddWellKnown(VMCIId contextID, // IN: + VMCIId wellKnownID) // IN: +{ + VMCILockFlags flags; + VMCIHandle wkHandle; + VMCIContext *context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, wellKnownID); + VMCI_GrabLock(&context->lock, &flags); + VMCIHandleArray_AppendEntry(&context->wellKnownArray, wkHandle); + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_RemoveWellKnown -- + * + * Wrapper to call VMCIHandleArray_RemoveEntry(). + * + * Results: + * VMCI_SUCCESS if removed, error code otherwise. + * + * Side effects: + * As in VMCIHandleArray_RemoveEntry(). + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_RemoveWellKnown(VMCIId contextID, // IN: + VMCIId wellKnownID) // IN: +{ + VMCILockFlags flags; + VMCIHandle wkHandle, tmpHandle; + VMCIContext *context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, wellKnownID); + VMCI_GrabLock(&context->lock, &flags); + tmpHandle = VMCIHandleArray_RemoveEntry(context->wellKnownArray, wkHandle); + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE)) { + return VMCI_ERROR_NOT_FOUND; + } + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_AddNotification -- + * + * Add remoteCID to list of contexts current contexts wants + * notifications from/about. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * As in VMCIHandleArray_AppendEntry(). + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_AddNotification(VMCIId contextID, // IN: + VMCIId remoteCID) // IN: +{ + int result = VMCI_ERROR_ALREADY_EXISTS; + VMCILockFlags flags; + VMCILockFlags firingFlags; + VMCIHandle notifierHandle; + VMCIContext *context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + + if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) { + result = VMCI_ERROR_NO_ACCESS; + goto out; + } + + notifierHandle = VMCI_MAKE_HANDLE(remoteCID, VMCI_EVENT_HANDLER); + VMCI_GrabLock(&contextList.firingLock, &firingFlags); + VMCI_GrabLock(&context->lock, &flags); + if (!VMCIHandleArray_HasEntry(context->notifierArray, notifierHandle)) { + VMCIHandleArray_AppendEntry(&context->notifierArray, notifierHandle); + result = VMCI_SUCCESS; + } + VMCI_ReleaseLock(&context->lock, flags); + VMCI_ReleaseLock(&contextList.firingLock, firingFlags); +out: + VMCIContext_Release(context); + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_RemoveNotification -- + * + * Remove remoteCID from current context's list of contexts it is + * interested in getting notifications from/about. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_RemoveNotification(VMCIId contextID, // IN: + VMCIId remoteCID) // IN: +{ + VMCILockFlags flags; + VMCILockFlags firingFlags; + VMCIContext *context = VMCIContext_Get(contextID); + VMCIHandle tmpHandle; + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + VMCI_GrabLock(&contextList.firingLock, &firingFlags); + VMCI_GrabLock(&context->lock, &flags); + tmpHandle = + VMCIHandleArray_RemoveEntry(context->notifierArray, + VMCI_MAKE_HANDLE(remoteCID, + VMCI_EVENT_HANDLER)); + VMCI_ReleaseLock(&context->lock, flags); + VMCI_ReleaseLock(&contextList.firingLock, firingFlags); + VMCIContext_Release(context); + + if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE)) { + return VMCI_ERROR_NOT_FOUND; + } + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContextFireNotification -- + * + * Fire notification for all contexts interested in given cid. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +VMCIContextFireNotification(VMCIId contextID, // IN + VMCIPrivilegeFlags privFlags, // IN + const char *domain) // IN +{ + uint32 i, arraySize; + VMCIListItem *next; + VMCILockFlags flags; + VMCILockFlags firingFlags; + VMCIHandleArray *subscriberArray; + VMCIHandle contextHandle = VMCI_MAKE_HANDLE(contextID, VMCI_EVENT_HANDLER); + + /* + * We create an array to hold the subscribers we find when scanning through + * all contexts. + */ + subscriberArray = VMCIHandleArray_Create(0); + if (subscriberArray == NULL) { + return VMCI_ERROR_NO_MEM; + } + + /* + * Scan all contexts to find who is interested in being notified about + * given contextID. We have a special firingLock that we use to synchronize + * across all notification operations. This avoids us having to take the + * context lock for each HasEntry call and it solves a lock ranking issue. + */ + VMCI_GrabLock(&contextList.firingLock, &firingFlags); + VMCI_GrabLock(&contextList.lock, &flags); + VMCIList_Scan(next, &contextList.head) { + VMCIContext *subCtx = VMCIList_Entry(next, VMCIContext, listItem); + + /* + * We only deliver notifications of the removal of contexts, if + * the two contexts are allowed to interact. + */ + + if (VMCIHandleArray_HasEntry(subCtx->notifierArray, contextHandle) && + !VMCIDenyInteraction(privFlags, subCtx->privFlags, domain, + VMCIContextGetDomainName(subCtx))) { + VMCIHandleArray_AppendEntry(&subscriberArray, + VMCI_MAKE_HANDLE(subCtx->cid, + VMCI_EVENT_HANDLER)); + } + } + VMCI_ReleaseLock(&contextList.lock, flags); + VMCI_ReleaseLock(&contextList.firingLock, firingFlags); + + /* Fire event to all subscribers. */ + arraySize = VMCIHandleArray_GetSize(subscriberArray); + for (i = 0; i < arraySize; i++) { + int result; + VMCIEventMsg *eMsg; + VMCIEventPayload_Context *evPayload; + char buf[sizeof *eMsg + sizeof *evPayload]; + + eMsg = (VMCIEventMsg *)buf; + + /* Clear out any garbage. */ + memset(eMsg, 0, sizeof *eMsg + sizeof *evPayload); + eMsg->hdr.dst = VMCIHandleArray_GetEntry(subscriberArray, i); + eMsg->hdr.src = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID, + VMCI_CONTEXT_RESOURCE_ID); + eMsg->hdr.payloadSize = sizeof *eMsg + sizeof *evPayload - + sizeof eMsg->hdr; + eMsg->eventData.event = VMCI_EVENT_CTX_REMOVED; + evPayload = VMCIEventMsgPayload(eMsg); + evPayload->contextID = contextID; + + result = VMCIDatagram_Dispatch(VMCI_HYPERVISOR_CONTEXT_ID, + (VMCIDatagram *)eMsg, FALSE); + if (result < VMCI_SUCCESS) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to enqueue event datagram %d for " + "context %d.\n", + eMsg->eventData.event, eMsg->hdr.dst.context)); + /* We continue to enqueue on next subscriber. */ + } + } + VMCIHandleArray_Destroy(subscriberArray); + + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_GetCheckpointState -- + * + * Get current context's checkpoint state of given type. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_GetCheckpointState(VMCIId contextID, // IN: + uint32 cptType, // IN: + uint32 *bufSize, // IN/OUT: + char **cptBufPtr) // OUT: +{ + int i, result; + VMCILockFlags flags; + uint32 arraySize, cptDataSize; + VMCIHandleArray *array; + VMCIContext *context; + char *cptBuf; + Bool getContextID; + + ASSERT(bufSize && cptBufPtr); + + context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + + VMCI_GrabLock(&context->lock, &flags); + if (cptType == VMCI_NOTIFICATION_CPT_STATE) { + ASSERT(context->notifierArray); + array = context->notifierArray; + getContextID = TRUE; + } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) { + ASSERT(context->wellKnownArray); + array = context->wellKnownArray; + getContextID = FALSE; + } else if (cptType == VMCI_DOORBELL_CPT_STATE) { + ASSERT(context->doorbellArray); + array = context->doorbellArray; + getContextID = FALSE; + } else { + VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state type %d.\n", cptType)); + result = VMCI_ERROR_INVALID_ARGS; + goto release; + } + + arraySize = VMCIHandleArray_GetSize(array); + if (arraySize > 0) { + if (cptType == VMCI_DOORBELL_CPT_STATE) { + cptDataSize = arraySize * sizeof(VMCIDoorbellCptState); + } else { + cptDataSize = arraySize * sizeof(VMCIId); + } + if (*bufSize < cptDataSize) { + *bufSize = cptDataSize; + result = VMCI_ERROR_MORE_DATA; + goto release; + } + + cptBuf = VMCI_AllocKernelMem(cptDataSize, + VMCI_MEMORY_NONPAGED | VMCI_MEMORY_ATOMIC); + if (cptBuf == NULL) { + result = VMCI_ERROR_NO_MEM; + goto release; + } + + for (i = 0; i < arraySize; i++) { + VMCIHandle tmpHandle = VMCIHandleArray_GetEntry(array, i); + if (cptType == VMCI_DOORBELL_CPT_STATE) { + ((VMCIDoorbellCptState *)cptBuf)[i].handle = tmpHandle; + } else { + ((VMCIId *)cptBuf)[i] = + getContextID ? tmpHandle.context : tmpHandle.resource; + } + } + *bufSize = cptDataSize; + *cptBufPtr = cptBuf; + } else { + *bufSize = 0; + *cptBufPtr = NULL; + } + result = VMCI_SUCCESS; + + release: + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SetCheckpointState -- + * + * Set current context's checkpoint state of given type. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_SetCheckpointState(VMCIId contextID, // IN: + uint32 cptType, // IN: + uint32 bufSize, // IN: + char *cptBuf) // IN: +{ + uint32 i; + VMCIId currentID; + int result = VMCI_SUCCESS; + uint32 numIDs = bufSize / sizeof(VMCIId); + ASSERT(cptBuf); + + if (cptType != VMCI_NOTIFICATION_CPT_STATE && + cptType != VMCI_WELLKNOWN_CPT_STATE) { + VMCI_DEBUG_LOG(4, (LGPFX"Invalid cpt state type %d.\n", cptType)); + return VMCI_ERROR_INVALID_ARGS; + } + + for (i = 0; i < numIDs && result == VMCI_SUCCESS; i++) { + currentID = ((VMCIId *)cptBuf)[i]; + if (cptType == VMCI_NOTIFICATION_CPT_STATE) { + result = VMCIContext_AddNotification(contextID, currentID); + } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) { + result = VMCIDatagramRequestWellKnownMap(currentID, contextID, + VMCIContext_GetPrivFlags(contextID)); + } + } + if (result != VMCI_SUCCESS) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to set cpt state type %d, error %d.\n", + cptType, result)); + } + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_ReceiveNotificationsGet -- + * + * Retrieves the specified context's pending notifications in the + * form of a handle array. The handle arrays returned are the + * actual data - not a copy and should not be modified by the + * caller. They must be released using + * VMCIContext_ReceiveNotificationsRelease. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_ReceiveNotificationsGet(VMCIId contextID, // IN + VMCIHandleArray **dbHandleArray, // OUT + VMCIHandleArray **qpHandleArray) // OUT +{ + VMCIContext *context; + VMCILockFlags flags; + int result = VMCI_SUCCESS; + + ASSERT(dbHandleArray && qpHandleArray); + + context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + VMCI_GrabLock(&context->lock, &flags); + + *dbHandleArray = context->pendingDoorbellArray; + context->pendingDoorbellArray = VMCIHandleArray_Create(0); + if (!context->pendingDoorbellArray) { + context->pendingDoorbellArray = *dbHandleArray; + *dbHandleArray = NULL; + result = VMCI_ERROR_NO_MEM; + } + *qpHandleArray = NULL; + + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_ReceiveNotificationsRelease -- + * + * Releases handle arrays with pending notifications previously + * retrieved using VMCIContext_ReceiveNotificationsGet. If the + * notifications were not successfully handed over to the guest, + * success must be false. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_ReceiveNotificationsRelease(VMCIId contextID, // IN + VMCIHandleArray *dbHandleArray, // IN + VMCIHandleArray *qpHandleArray, // IN + Bool success) // IN +{ + VMCIContext *context = VMCIContext_Get(contextID); + + if (context) { + VMCILockFlags flags; + + VMCI_GrabLock(&context->lock, &flags); + if (!success) { + VMCIHandle handle; + + /* + * New notifications may have been added while we were not + * holding the context lock, so we transfer any new pending + * doorbell notifications to the old array, and reinstate the + * old array. + */ + + handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); + while (!VMCI_HANDLE_INVALID(handle)) { + ASSERT(VMCIHandleArray_HasEntry(context->doorbellArray, handle)); + if (!VMCIHandleArray_HasEntry(dbHandleArray, handle)) { + VMCIHandleArray_AppendEntry(&dbHandleArray, handle); + } + handle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); + } + VMCIHandleArray_Destroy(context->pendingDoorbellArray); + context->pendingDoorbellArray = dbHandleArray; + dbHandleArray = NULL; + } else { + VMCIContextClearNotifyAndCall(context); + } + VMCI_ReleaseLock(&context->lock, flags); + VMCIContext_Release(context); + } else { + /* + * The OS driver part is holding on to the context for the + * duration of the receive notification ioctl, so it should + * still be here. + */ + + ASSERT(FALSE); + } + + if (dbHandleArray) { + VMCIHandleArray_Destroy(dbHandleArray); + } + if (qpHandleArray) { + VMCIHandleArray_Destroy(qpHandleArray); + } +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_DoorbellCreate -- + * + * Registers that a new doorbell handle has been allocated by the + * context. Only doorbell handles registered can be notified. + * + * Results: + * VMCI_SUCCESS on success, appropriate error code otherewise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_DoorbellCreate(VMCIId contextID, // IN + VMCIHandle handle) // IN +{ + VMCIContext *context; + VMCILockFlags flags; + int result; + + if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + + VMCI_GrabLock(&context->lock, &flags); + if (!VMCIHandleArray_HasEntry(context->doorbellArray, handle)) { + VMCIHandleArray_AppendEntry(&context->doorbellArray, handle); + result = VMCI_SUCCESS; + } else { + result = VMCI_ERROR_DUPLICATE_ENTRY; + } + VMCI_ReleaseLock(&context->lock, flags); + + VMCIContext_Release(context); + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_DoorbellDestroy -- + * + * Unregisters a doorbell handle that was previously registered + * with VMCIContext_DoorbellCreate. + * + * Results: + * VMCI_SUCCESS on success, appropriate error code otherewise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_DoorbellDestroy(VMCIId contextID, // IN + VMCIHandle handle) // IN +{ + VMCIContext *context; + VMCILockFlags flags; + VMCIHandle removedHandle; + + if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle)) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + + VMCI_GrabLock(&context->lock, &flags); + removedHandle = VMCIHandleArray_RemoveEntry(context->doorbellArray, handle); + VMCIHandleArray_RemoveEntry(context->pendingDoorbellArray, handle); + VMCI_ReleaseLock(&context->lock, flags); + + VMCIContext_Release(context); + + if (VMCI_HANDLE_INVALID(removedHandle)) { + return VMCI_ERROR_NOT_FOUND; + } else { + return VMCI_SUCCESS; + } +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_DoorbellDestroyAll -- + * + * Unregisters all doorbell handles that were previously + * registered with VMCIContext_DoorbellCreate. + * + * Results: + * VMCI_SUCCESS on success, appropriate error code otherewise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_DoorbellDestroyAll(VMCIId contextID) // IN +{ + VMCIContext *context; + VMCILockFlags flags; + VMCIHandle removedHandle; + + if (contextID == VMCI_INVALID_ID) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCIContext_Get(contextID); + if (context == NULL) { + return VMCI_ERROR_NOT_FOUND; + } + + VMCI_GrabLock(&context->lock, &flags); + do { + removedHandle = VMCIHandleArray_RemoveTail(context->doorbellArray); + } while(!VMCI_HANDLE_INVALID(removedHandle)); + do { + removedHandle = VMCIHandleArray_RemoveTail(context->pendingDoorbellArray); + } while(!VMCI_HANDLE_INVALID(removedHandle)); + VMCI_ReleaseLock(&context->lock, flags); + + VMCIContext_Release(context); + + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_NotifyDoorbell -- + * + * Registers a notification of a doorbell handle initiated by the + * specified source context. The notification of doorbells are + * subject to the same isolation rules as datagram delivery. To + * allow host side senders of notifications a finer granularity + * of sender rights than those assigned to the sending context + * itself, the host context is required to specify a different + * set of privilege flags that will override the privileges of + * the source context. + * + * Results: + * VMCI_SUCCESS on success, appropriate error code otherewise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_NotifyDoorbell(VMCIId srcCID, // IN + VMCIHandle handle, // IN + VMCIPrivilegeFlags srcPrivFlags) // IN +{ + VMCIContext *dstContext; + VMCILockFlags flags; + int result; + + if (VMCI_HANDLE_INVALID(handle)) { + return VMCI_ERROR_INVALID_ARGS; + } + + /* Get the target VM's VMCI context. */ + dstContext = VMCIContext_Get(handle.context); + if (dstContext == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Invalid cid.\n")); + return VMCI_ERROR_INVALID_ARGS; + } + + if (srcCID != handle.context) { + VMCIPrivilegeFlags dstPrivFlags; +#if !defined(VMKERNEL) + char *srcDomain = NULL; +#else + char srcDomain[VMCI_DOMAIN_NAME_MAXLEN]; + + result = VMCIContext_GetDomainName(srcCID, srcDomain, sizeof srcDomain); + if (result < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to get domain name for source context %u.\n", + srcCID)); + goto out; + } +#endif + result = VMCIDoorbellGetPrivFlags(handle, &dstPrivFlags); + if (result < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to get privilege flags for destination " + "handle 0x%x:0x%x.\n", handle.context, handle.resource)); + goto out; + } + + if (srcCID != VMCI_HOST_CONTEXT_ID || + srcPrivFlags == VMCI_NO_PRIVILEGE_FLAGS) { + srcPrivFlags = VMCIContext_GetPrivFlags(srcCID); + } + + if (VMCIDenyInteraction(srcPrivFlags, dstPrivFlags, srcDomain, + VMCIContextGetDomainName(dstContext))) { + result = VMCI_ERROR_NO_ACCESS; + goto out; + } + } + + if (handle.context == VMCI_HOST_CONTEXT_ID) { + result = VMCIDoorbellHostContextNotify(srcCID, handle); + } else { + VMCI_GrabLock(&dstContext->lock, &flags); + + if (!VMCIHandleArray_HasEntry(dstContext->doorbellArray, handle)) { + result = VMCI_ERROR_NOT_FOUND; + } else { + if (!VMCIHandleArray_HasEntry(dstContext->pendingDoorbellArray, handle)) { + VMCIHandleArray_AppendEntry(&dstContext->pendingDoorbellArray, handle); + + VMCIContextSignalNotify(dstContext); +#if defined(VMKERNEL) + VMCIHost_SignalBitmap(&dstContext->hostContext); +#else + VMCIHost_SignalCall(&dstContext->hostContext); +#endif + } + result = VMCI_SUCCESS; + } + VMCI_ReleaseLock(&dstContext->lock, flags); + } + +out: + VMCIContext_Release(dstContext); + + return result; +} + + +#ifdef VMKERNEL + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SignalPendingDoorbells -- + * + * Signals the guest if any doorbell notifications are + * pending. This is used after the VMCI device is unquiesced to + * ensure that no pending notifications go unnoticed, since + * signals may not be fully processed while the device is + * quiesced. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_SignalPendingDoorbells(VMCIId contextID) +{ + VMCIContext *context; + VMCILockFlags flags; + Bool pending; + + context = VMCIContext_Get(contextID); + if (!context) { + ASSERT(FALSE); + return; + } + + VMCI_GrabLock(&context->lock, &flags); + pending = VMCIHandleArray_GetSize(context->pendingDoorbellArray) > 0; + VMCI_ReleaseLock(&context->lock, flags); + + if (pending) { + VMCIHost_SignalBitmap(&context->hostContext); + } + + VMCIContext_Release(context); +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SetDomainName -- + * + * Sets the domain name of the given context. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_SetDomainName(VMCIContext *context, // IN; + const char *domainName) // IN: +{ + size_t domainNameLen; + + if (!context || !domainName) { + return VMCI_ERROR_INVALID_ARGS; + } + + domainNameLen = strlen(domainName); + if (domainNameLen >= sizeof context->domainName) { + return VMCI_ERROR_NO_MEM; + } + + memcpy(context->domainName, domainName, domainNameLen + 1); + + return VMCI_SUCCESS; +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_GetDomainName -- + * + * Returns the domain name of the given context. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +VMCIContext_GetDomainName(VMCIId contextID, // IN: + char *domainName, // OUT: + size_t domainNameBufSize) // IN: +{ + VMCIContext *context; + int rv = VMCI_SUCCESS; + size_t domainNameLen; + + if (contextID == VMCI_INVALID_ID || !domainName || !domainNameBufSize) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCIContext_Get(contextID); + if (!context) { + return VMCI_ERROR_NOT_FOUND; + } + + domainNameLen = strlen(context->domainName); + if (domainNameLen >= domainNameBufSize) { + rv = VMCI_ERROR_NO_MEM; + goto out; + } + + memcpy(domainName, context->domainName, domainNameLen + 1); + +out: + VMCIContext_Release(context); + return rv; +} + + +#endif // defined(VMKERNEL) + + +/* + *---------------------------------------------------------------------- + * + * VMCI_ContextID2HostVmID -- + * + * Maps a context ID to the host specific (process/world) ID + * of the VM/VMX. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCI_EXPORT_SYMBOL(VMCI_ContextID2HostVmID) +int +VMCI_ContextID2HostVmID(VMCIId contextID, // IN + void *hostVmID, // OUT + size_t hostVmIDLen) // IN +{ +#if defined(VMKERNEL) + VMCIContext *context; + VMCIHostVmID vmID; + int result; + + context = VMCIContext_Get(contextID); + if (!context) { + return VMCI_ERROR_NOT_FOUND; + } + + result = VMCIHost_ContextToHostVmID(&context->hostContext, &vmID); + if (result == VMCI_SUCCESS) { + if (sizeof vmID == hostVmIDLen) { + memcpy(hostVmID, &vmID, hostVmIDLen); + } else { + result = VMCI_ERROR_INVALID_ARGS; + } + } + + VMCIContext_Release(context); + + return result; +#else // !defined(VMKERNEL) + return VMCI_ERROR_UNAVAILABLE; +#endif +} + + +/* + *---------------------------------------------------------------------- + * + * VMCI_IsContextOwner -- + * + * Determines whether a given host OS specific representation of + * user is the owner of the VM/VMX. + * + * Results: + * VMCI_SUCCESS if the hostUser is owner, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +VMCI_EXPORT_SYMBOL(VMCI_IsContextOwner) +int +VMCI_IsContextOwner(VMCIId contextID, // IN + void *hostUser) // IN +{ +#if defined(VMX86_TOOLS) + return VMCI_ERROR_UNAVAILABLE; +#else // VMX86_TOOLS + VMCIContext *context; + VMCIHostUser *user = (VMCIHostUser *)hostUser; + int retval; + + if (vmkernel) { + return VMCI_ERROR_UNAVAILABLE; + } + + if (!hostUser) { + return VMCI_ERROR_INVALID_ARGS; + } + + context = VMCIContext_Get(contextID); + if (!context) { + return VMCI_ERROR_NOT_FOUND; + } + + if (context->validUser) { + retval = VMCIHost_CompareUser(user, &context->user); + } else { + retval = VMCI_ERROR_UNAVAILABLE; + } + VMCIContext_Release(context); + + return retval; +#endif // VMX86_TOOLS +} + + +/* + *---------------------------------------------------------------------- + * + * VMCIContext_SupportsHostQP -- + * + * Can host QPs be connected to this user process. The answer is + * FALSE unless a sufficient version number has previously been set + * by this caller. + * + * Results: + * VMCI_SUCCESS on success, error code otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +VMCIContext_SupportsHostQP(VMCIContext *context) // IN: Context structure +{ +#ifdef VMKERNEL + return TRUE; +#else + if (!context || context->userVersion < VMCI_VERSION_HOSTQP) { + return FALSE; + } + return TRUE; +#endif +} diff --git a/open-vm-tools/modules/linux/vmci/vmciContext.h b/open-vm-tools/modules/linux/vmci/vmciContext.h new file mode 100644 index 000000000..ef4da7269 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciContext.h @@ -0,0 +1,121 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciContext.h -- + * + * VMCI state to enable sending calls between VMs. + */ + +#ifndef _VMCI_CONTEXT_H_ +#define _VMCI_CONTEXT_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_defs.h" +#include "vmci_call_defs.h" +#include "vmci_handle_array.h" +#include "vmci_infrastructure.h" + +#define MAX_QUEUED_GUESTCALLS_PER_VM 100 + +typedef struct VMCIContext VMCIContext; + +int VMCIContext_Init(void); +void VMCIContext_Exit(void); +int VMCIContext_InitContext(VMCIId cid, VMCIPrivilegeFlags flags, + uintptr_t eventHnd, int version, + VMCIHostUser *user, VMCIContext **context); +#ifdef VMKERNEL +int VMCIContext_SetDomainName(VMCIContext *context, const char *domainName); +int VMCIContext_GetDomainName(VMCIId contextID, char *domainName, + size_t domainNameBufSize); +void VMCIContext_SetFSRState(VMCIContext *context, + Bool isQuiesced, + VMCIId migrateCid, + uintptr_t eventHnd, + Bool isLocked); +VMCIContext *VMCIContext_FindAndUpdateSrcFSR(VMCIId migrateCid, + uintptr_t eventHnd, + uintptr_t *srcEventHnd); +Bool VMCIContext_IsActiveHnd(VMCIContext *context, + uintptr_t eventHnd); +void VMCIContext_SetInactiveHnd(VMCIContext *context, + uintptr_t eventHnd); +Bool VMCIContext_RemoveHnd(VMCIContext *context, + uintptr_t eventHnd, + uint32 *numOld, + uint32 *numNew); +void VMCIContext_ClearDatagrams(VMCIContext *context); +void VMCIContext_SetId(VMCIContext *context, VMCIId cid); +#endif +Bool VMCIContext_SupportsHostQP(VMCIContext *context); +void VMCIContext_ReleaseContext(VMCIContext *context); +int VMCIContext_EnqueueDatagram(VMCIId cid, VMCIDatagram *dg); +int VMCIContext_DequeueDatagram(VMCIContext *context, size_t *maxSize, + VMCIDatagram **dg); +int VMCIContext_PendingDatagrams(VMCIId cid, uint32 *pending); +VMCIContext *VMCIContext_Get(VMCIId cid); +void VMCIContext_Release(VMCIContext *context); +Bool VMCIContext_Exists(VMCIId cid); + +VMCIId VMCIContext_GetId(VMCIContext *context); +int VMCIContext_AddGroupEntry(VMCIContext *context, + VMCIHandle entryHandle); +VMCIHandle VMCIContext_RemoveGroupEntry(VMCIContext *context, + VMCIHandle entryHandle); +int VMCIContext_AddWellKnown(VMCIId contextID, VMCIId wellKnownID); +int VMCIContext_RemoveWellKnown(VMCIId contextID, VMCIId wellKnownID); +int VMCIContext_AddNotification(VMCIId contextID, VMCIId remoteCID); +int VMCIContext_RemoveNotification(VMCIId contextID, VMCIId remoteCID); +int VMCIContext_GetCheckpointState(VMCIId contextID, uint32 cptType, + uint32 *numCIDs, char **cptBufPtr); +int VMCIContext_SetCheckpointState(VMCIId contextID, uint32 cptType, + uint32 numCIDs, char *cptBuf); +#ifndef VMX86_SERVER +void VMCIContext_CheckAndSignalNotify(VMCIContext *context); +# ifdef __linux__ +/* TODO Windows and Mac OS. */ +void VMCIUnsetNotify(VMCIContext *context); +# endif +#endif + +int VMCIContext_DoorbellCreate(VMCIId contextID, VMCIHandle handle); +int VMCIContext_DoorbellDestroy(VMCIId contextID, VMCIHandle handle); +int VMCIContext_DoorbellDestroyAll(VMCIId contextID); +int VMCIContext_NotifyDoorbell(VMCIId cid, VMCIHandle handle, + VMCIPrivilegeFlags srcPrivFlags); + +int VMCIContext_ReceiveNotificationsGet(VMCIId contextID, + VMCIHandleArray **dbHandleArray, + VMCIHandleArray **qpHandleArray); +void VMCIContext_ReceiveNotificationsRelease(VMCIId contextID, + VMCIHandleArray *dbHandleArray, + VMCIHandleArray *qpHandleArray, + Bool success); +#if defined(VMKERNEL) +void VMCIContext_SignalPendingDoorbells(VMCIId contextID); + +int VMCIContextID2HostVmID(VMCIId contextID, void *hostVmID, size_t hostVmIDLen); +#endif + +#endif // _VMCI_CONTEXT_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciDatagram.c b/open-vm-tools/modules/linux/vmci/vmciDatagram.c index cbaacec10..2278c4d21 100644 --- a/open-vm-tools/modules/linux/vmci/vmciDatagram.c +++ b/open-vm-tools/modules/linux/vmci/vmciDatagram.c @@ -19,86 +19,117 @@ /* * vmciDatagram.c -- * - * Simple Datagram API for the guest driver. + * This file implements the VMCI Simple Datagram API on the host. */ -#ifdef __linux__ +#if defined(__linux__) && !defined(VMKERNEL) # include "driver-config.h" -# include # include "compat_kernel.h" -# include "compat_pci.h" -#elif defined(_WIN32) -# include -#elif defined(SOLARIS) -# include -# include -#elif defined(__APPLE__) -#else -# error "Platform not support by VMCI datagram API." -#endif // linux - -#define LGPFX "VMCIDatagram: " - +# include "compat_module.h" +#endif // __linux__ #include "vmci_kernel_if.h" -#include "vm_basic_types.h" #include "vm_assert.h" #include "vmci_defs.h" #include "vmci_infrastructure.h" +#include "vmciCommonInt.h" +#include "vmciContext.h" #include "vmciDatagram.h" -#include "vmciInt.h" +#include "vmciEvent.h" +#include "vmciHashtable.h" #include "vmciKernelAPI.h" -#include "vmciUtil.h" - -typedef struct DatagramHashEntry { - struct DatagramHashEntry *next; - int refCount; - - VMCIHandle handle; - uint32 flags; - Bool runDelayed; - VMCIDatagramRecvCB recvCB; - void *clientData; - VMCIEvent destroyEvent; -} DatagramHashEntry; +#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 HASH_TABLE_SIZE 64 +#define LGPFX "VMCIDatagram: " -#define VMCI_HASHRESOURCE(handle, size) \ - VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(handle), (size)) /* - * Hash table containing all the datagram handles for this VM. It is - * synchronized using a single lock but we should consider making it more - * fine grained, e.g. a per bucket lock or per set of buckets' lock. + * DatagramEntry describes the datagram entity. It is used for datagram + * entities created only on the host. */ - -typedef struct DatagramHashTable { - VMCILock lock; - DatagramHashEntry *entries[HASH_TABLE_SIZE]; -} DatagramHashTable; +typedef struct DatagramEntry { + VMCIResource resource; + uint32 flags; + Bool runDelayed; + VMCIDatagramRecvCB recvCB; + void *clientData; + VMCIEvent destroyEvent; + VMCIPrivilegeFlags privFlags; +} DatagramEntry; + +/* Mapping between wellknown resource and context. */ +typedef struct DatagramWKMapping { + VMCIHashEntry entry; + VMCIId contextID; +} DatagramWKMapping; typedef struct VMCIDelayedDatagramInfo { - DatagramHashEntry *entry; + Bool inDGHostQueue; + DatagramEntry *entry; VMCIDatagram msg; } VMCIDelayedDatagramInfo; +/* Wellknown mapping hashtable. */ +static VMCIHashTable *wellKnownTable = NULL; + +static Atomic_uint32 delayedDGHostQueueSize; + +static int VMCIDatagramGetPrivFlagsInt(VMCIId contextID, VMCIHandle handle, + VMCIPrivilegeFlags *privFlags); +static void DatagramFreeCB(void *resource); static int DatagramReleaseCB(void *clientData); -static int DatagramHashAddEntry(DatagramHashEntry *entry, VMCIId contextID); -static int DatagramHashRemoveEntry(VMCIHandle handle); -static DatagramHashEntry *DatagramHashGetEntry(VMCIHandle handle); -static DatagramHashEntry *DatagramHashGetEntryAnyCid(VMCIHandle handle); -static void DatagramHashReleaseEntry(DatagramHashEntry *entry); -static Bool DatagramHandleUniqueLockedAnyCid(VMCIHandle handle); -DatagramHashTable hashTable; +static DatagramWKMapping *DatagramGetWellKnownMap(VMCIId wellKnownID); +static void DatagramReleaseWellKnownMap(DatagramWKMapping *wkMap); + + +/*------------------------------ Helper functions ----------------------------*/ + +/* + *------------------------------------------------------------------------------ + * + * DatagramFreeCB -- + * Callback to free datagram structure when resource is no longer used, + * ie. the reference count reached 0. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +static void +DatagramFreeCB(void *clientData) +{ + DatagramEntry *entry = (DatagramEntry *)clientData; + ASSERT(entry); + VMCI_SignalEvent(&entry->destroyEvent); + + /* + * The entry is freed in VMCIDatagram_DestroyHnd, who is waiting for the + * above signal. + */ +} + /* *------------------------------------------------------------------------------ * * DatagramReleaseCB -- * - * Callback to release the datagram entry reference. It is called by the + * Callback to release the resource reference. It is called by the * VMCI_WaitOnEvent function before it blocks. * * Result: @@ -110,490 +141,838 @@ DatagramHashTable hashTable; static int DatagramReleaseCB(void *clientData) { - DatagramHashEntry *entry = (DatagramHashEntry *)clientData; + DatagramEntry *entry = (DatagramEntry *)clientData; ASSERT(entry); - DatagramHashReleaseEntry(entry); - + VMCIResource_Release(&entry->resource); return 0; } /* - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * DatagramHashAddEntry -- - * Given a datagram handle entry, adds it to the hashtable of datagram - * entries. Allocates a resource id iff the handle of the given entry - * is an invalid one. 0 through VMCI_RESERVED_RESOURCE_ID_MAX are - * reserved resource ids. + * DatagramCreateHnd -- * - * Result: - * VMCI_SUCCESS if added, error if not. + * Internal function to create a datagram entry given a handle. * - *------------------------------------------------------------------------- + * Results: + * VMCI_SUCCESS if created, negative errno value otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ */ static int -DatagramHashAddEntry(DatagramHashEntry *entry, // IN: - VMCIId contextID) // IN: +DatagramCreateHnd(VMCIId resourceID, // IN: + uint32 flags, // IN: + VMCIPrivilegeFlags privFlags, // IN: + VMCIDatagramRecvCB recvCB, // IN: + void *clientData, // IN: + VMCIHandle *outHandle) // OUT: + { - int idx; - VMCILockFlags flags; - static VMCIId datagramRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; + int result; + VMCIId contextID; + VMCIHandle handle; + DatagramEntry *entry; - ASSERT(entry); + ASSERT(recvCB != NULL); + ASSERT(outHandle != NULL); + ASSERT(!(privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS)); - VMCI_GrabLock_BH(&hashTable.lock, &flags); + if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) { + return VMCI_ERROR_INVALID_ARGS; + } else { + if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) { + contextID = VMCI_INVALID_ID; + } else { + contextID = VMCI_GetContextID(); + if (contextID == VMCI_INVALID_ID) { + return VMCI_ERROR_NO_RESOURCES; + } + } - /* Do not allow addition of a new handle if the device is being shutdown. */ - if (VMCI_DeviceShutdown()) { - VMCI_ReleaseLock_BH(&hashTable.lock, flags); - return VMCI_ERROR_DEVICE_NOT_FOUND; - } + if (resourceID == VMCI_INVALID_ID) { + resourceID = VMCIResource_GetID(contextID); + if (resourceID == VMCI_INVALID_ID) { + return VMCI_ERROR_NO_HANDLE; + } + } - if (!VMCI_HANDLE_INVALID(entry->handle) && - !DatagramHandleUniqueLockedAnyCid(entry->handle)) { - VMCI_ReleaseLock_BH(&hashTable.lock, flags); - return VMCI_ERROR_DUPLICATE_ENTRY; - } else if (VMCI_HANDLE_INVALID(entry->handle)) { - VMCIId oldRID = datagramRID; - VMCIHandle handle; - Bool foundRID = FALSE; + handle = VMCI_MAKE_HANDLE(contextID, resourceID); + } - /* - * Generate a unique datagram rid. Keep on trying until we wrap around - * in the RID space. - */ - ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX); - do { - handle = VMCI_MAKE_HANDLE(contextID, datagramRID); - foundRID = DatagramHandleUniqueLockedAnyCid(handle); - datagramRID++; - if (UNLIKELY(!datagramRID)) { - /* - * Skip the reserved rids. - */ - datagramRID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; - } - } while (!foundRID && datagramRID != oldRID); + 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 (LIKELY(foundRID)) { - entry->handle = handle; - } else { - /* - * We wrapped around --- no rids were free. - */ - ASSERT(datagramRID == oldRID); - VMCI_ReleaseLock_BH(&hashTable.lock, flags); - return VMCI_ERROR_NO_HANDLE; + if (!VMCI_CanScheduleDelayedWork()) { + if (flags & VMCI_FLAG_DG_DELAYED_CB) { + VMCI_FreeKernelMem(entry, sizeof *entry); + return VMCI_ERROR_INVALID_ARGS; } + entry->runDelayed = FALSE; + } else { + entry->runDelayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? TRUE : FALSE; } - ASSERT(!VMCI_HANDLE_INVALID(entry->handle)); - idx = VMCI_HASHRESOURCE(entry->handle, HASH_TABLE_SIZE); + entry->flags = flags; + entry->recvCB = recvCB; + entry->clientData = clientData; + VMCI_CreateEvent(&entry->destroyEvent); + entry->privFlags = privFlags; - /* New entry is added to top/front of hash bucket. */ - entry->refCount++; - entry->next = hashTable.entries[idx]; - hashTable.entries[idx] = entry; - VMCI_ReleaseLock_BH(&hashTable.lock, flags); + /* Make datagram resource live. */ + result = VMCIResource_Add(&entry->resource, VMCI_RESOURCE_TYPE_DATAGRAM, + handle, DatagramFreeCB, entry); + if (result != VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to add new resource %d:%d.\n", + handle.context, handle.resource)); + VMCI_DestroyEvent(&entry->destroyEvent); + VMCI_FreeKernelMem(entry, sizeof *entry); + return result; + } + *outHandle = handle; return VMCI_SUCCESS; } +/*------------------------------ Init functions ----------------------------*/ + /* - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ + * + * VMCIDatagram_Init -- * - * DatagramHashRemoveEntry -- + * Initialize Datagram API, ie. register the API functions with their + * corresponding vectors. * * Result: - * VMCI_SUCCESS if removed, VMCI_ERROR_NO_HANDLE if not found. + * None. + * + * Side effects: + * None. * - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -static int -DatagramHashRemoveEntry(VMCIHandle handle) +int +VMCIDatagram_Init(void) { - int result = VMCI_ERROR_NOT_FOUND; - VMCILockFlags flags; - DatagramHashEntry *prev, *cur; - int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE); - - prev = NULL; - VMCI_GrabLock_BH(&hashTable.lock, &flags); - cur = hashTable.entries[idx]; - while (TRUE) { - if (cur == NULL) { - break; - } - if (VMCI_HANDLE_EQUAL(cur->handle, handle)) { - /* Remove entry and break. */ - if (prev) { - prev->next = cur->next; - } else { - hashTable.entries[idx] = cur->next; - } - - cur->refCount--; - - /* - * We know that DestroyHnd still has a reference so refCount must be - * at least 1. - */ - ASSERT(cur->refCount > 0); - result = VMCI_SUCCESS; - break; - } - prev = cur; - cur = cur->next; + /* Create hash table for wellknown mappings. */ + wellKnownTable = VMCIHashTable_Create(32); + if (wellKnownTable == NULL) { + return VMCI_ERROR_NO_RESOURCES; } - VMCI_ReleaseLock_BH(&hashTable.lock, flags); - return result; + Atomic_Write(&delayedDGHostQueueSize, 0); + return VMCI_SUCCESS; } /* - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * DatagramHashGetEntry -- + * VMCIDatagram_Exit -- * - * Gets the given datagram hashtable entry based on handle lookup. + * Cleanup Datagram API. * * Result: - * Datagram hash entry if found. NULL otherwise. + * None. * - *------------------------------------------------------------------------- + * Side effects: + * None. + * + *------------------------------------------------------------------------------ */ -static DatagramHashEntry * -DatagramHashGetEntry(VMCIHandle handle) // IN +void +VMCIDatagram_Exit(void) { - VMCILockFlags flags; - DatagramHashEntry *cur; - int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE); + if (wellKnownTable != NULL) { + VMCIHashTable_Destroy(wellKnownTable); + wellKnownTable = NULL; + } +} - VMCI_GrabLock_BH(&hashTable.lock, &flags); - for (cur = hashTable.entries[idx]; cur != NULL; cur = cur->next) { - if (VMCI_HANDLE_EQUAL(cur->handle, handle)) { - cur->refCount++; - break; - } +/*------------------------------ Public API functions ------------------------*/ + +/* + *------------------------------------------------------------------------------ + * + * VMCIDatagram_CreateHnd -- + * + * Creates a host context datagram endpoint and returns a handle to it. + * + * Results: + * VMCI_SUCCESS if created, negative errno value otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHnd) +int +VMCIDatagram_CreateHnd(VMCIId resourceID, // IN: Optional, generated + // if VMCI_INVALID_ID + uint32 flags, // IN: + VMCIDatagramRecvCB recvCB, // IN: + void *clientData, // IN: + VMCIHandle *outHandle) // OUT: newly created handle +{ + if (outHandle == NULL) { + return VMCI_ERROR_INVALID_ARGS; } - VMCI_ReleaseLock_BH(&hashTable.lock, flags); + if (recvCB == NULL) { + VMCI_DEBUG_LOG(4, + (LGPFX"Client callback needed when creating datagram.\n")); + return VMCI_ERROR_INVALID_ARGS; + } - return cur; + return DatagramCreateHnd(resourceID, flags, VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, + recvCB, clientData, outHandle); } /* - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ + * + * VMCIDatagram_CreateHndPriv -- * - * DatagramHashGetEntryAnyCid -- + * Creates a host context datagram endpoint and returns a handle to it. * - * Gets the given datagram hashtable entry based on handle lookup. - * Will match "any" or specific cid. + * Results: + * VMCI_SUCCESS if created, negative errno value otherwise. * - * Result: - * Datagram hash entry if found. NULL otherwise. + * Side effects: + * None. * - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -static DatagramHashEntry * -DatagramHashGetEntryAnyCid(VMCIHandle handle) // IN +VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHndPriv) +int +VMCIDatagram_CreateHndPriv(VMCIId resourceID, // IN: Optional, generated + // if VMCI_INVALID_ID + uint32 flags, // IN: + VMCIPrivilegeFlags privFlags,// IN: + VMCIDatagramRecvCB recvCB, // IN: + void *clientData, // IN: + VMCIHandle *outHandle) // OUT: newly created handle { - VMCILockFlags flags; - DatagramHashEntry *cur; - int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE); - - VMCI_GrabLock_BH(&hashTable.lock, &flags); - - for (cur = hashTable.entries[idx]; cur != NULL; cur = cur->next) { - if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) == - VMCI_HANDLE_TO_RESOURCE_ID(handle)) { - if (VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) == VMCI_INVALID_ID || - VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) == - VMCI_HANDLE_TO_CONTEXT_ID(handle)) { - cur->refCount++; - break; - } - } + if (outHandle == NULL) { + return VMCI_ERROR_INVALID_ARGS; + } + + if (recvCB == NULL) { + VMCI_DEBUG_LOG(4, + (LGPFX"Client callback needed when creating datagram.\n")); + return VMCI_ERROR_INVALID_ARGS; } - VMCI_ReleaseLock_BH(&hashTable.lock, flags); + if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) { + return VMCI_ERROR_INVALID_ARGS; + } - return cur; + return DatagramCreateHnd(resourceID, flags, privFlags, recvCB, clientData, + outHandle); } /* - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * DatagramHashReleaseEntry -- + * VMCIDatagram_DestroyHnd -- * - * Drops a reference to the current hash entry. If this is the last - * reference then the entry is freed. + * Destroys a handle. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +VMCI_EXPORT_SYMBOL(VMCIDatagram_DestroyHnd) +int +VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN +{ + DatagramEntry *entry; + VMCIResource *resource = VMCIResource_Get(handle, + VMCI_RESOURCE_TYPE_DATAGRAM); + if (resource == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to destroy handle 0x%x:0x%x.\n", + handle.context, handle.resource)); + return VMCI_ERROR_NOT_FOUND; + } + entry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); + + VMCIResource_Remove(handle, VMCI_RESOURCE_TYPE_DATAGRAM); + + /* + * We now wait on the destroyEvent and release the reference we got + * above. + */ + VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry); + + if ((entry->flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) { + VMCIDatagramRemoveWellKnownMap(handle.resource, VMCI_HOST_CONTEXT_ID); + } + + /* + * 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; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIDatagramGetPrivFlagsInt -- + * + * Internal utilility function with the same purpose as + * VMCIDatagram_GetPrivFlags that also takes a contextID. * * Result: + * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. + * + * Side effects: * None. * - *------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -static void -DatagramHashReleaseEntry(DatagramHashEntry *entry) // IN +static int +VMCIDatagramGetPrivFlagsInt(VMCIId contextID, // IN + VMCIHandle handle, // IN + VMCIPrivilegeFlags *privFlags) // OUT { - VMCILockFlags flags; + ASSERT(privFlags); + ASSERT(contextID != VMCI_INVALID_ID); - VMCI_GrabLock_BH(&hashTable.lock, &flags); - entry->refCount--; + if (contextID == VMCI_HOST_CONTEXT_ID) { + DatagramEntry *srcEntry; + VMCIResource *resource; - /* Check if this is last reference and signal the destroy event if so. */ - if (entry->refCount == 0) { - VMCI_SignalEvent(&entry->destroyEvent); + resource = VMCIResource_Get(handle, VMCI_RESOURCE_TYPE_DATAGRAM); + if (resource == NULL) { + return VMCI_ERROR_INVALID_ARGS; + } + srcEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); + *privFlags = srcEntry->privFlags; + VMCIResource_Release(resource); + } else if (contextID == VMCI_HYPERVISOR_CONTEXT_ID) { + *privFlags = VMCI_MAX_PRIVILEGE_FLAGS; + } else { + *privFlags = VMCIContext_GetPrivFlags(contextID); } - VMCI_ReleaseLock_BH(&hashTable.lock, flags); + + return VMCI_SUCCESS; } /* *------------------------------------------------------------------------------ * - * DatagramHandleUniqueLockedAnyCid -- + * VMCIDatagram_GetPrivFlags -- * - * Checks whether the resource id (with any context id) is already in the - * hash table. - * Assumes that the caller has the hash table lock. + * Utilility function that retrieves the privilege flags + * associated with a given datagram handle. For hypervisor and + * guest endpoints, the privileges are determined by the context + * ID, but for host endpoints privileges are associated with the + * complete handle. * * Result: - * TRUE if the handle is unique. FALSE otherwise. + * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. + * + * Side effects: + * None. * *------------------------------------------------------------------------------ */ -static Bool -DatagramHandleUniqueLockedAnyCid(VMCIHandle handle) // IN +int +VMCIDatagram_GetPrivFlags(VMCIHandle handle, // IN + VMCIPrivilegeFlags *privFlags) // OUT { - Bool unique = TRUE; - DatagramHashEntry *entry; - int idx = VMCI_HASHRESOURCE(handle, HASH_TABLE_SIZE); - - entry = hashTable.entries[idx]; - while (entry) { - if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) == - VMCI_HANDLE_TO_RESOURCE_ID(handle)) { - unique = FALSE; - break; - } - entry = entry->next; + if (privFlags == NULL || handle.context == VMCI_INVALID_ID) { + return VMCI_ERROR_INVALID_ARGS; } - return unique; + return VMCIDatagramGetPrivFlagsInt(handle.context, handle, privFlags); } /* *----------------------------------------------------------------------------- * - * VMCIDatagram_CreateHnd -- + * VMCIDatagramDelayedDispatchCB -- * - * Creates a datagram endpoint and returns a handle to it. + * Calls the specified callback in a delayed context. * * Results: - * Returns handle if success, negative errno value otherwise. + * None. * * Side effects: - * Datagram endpoint is created both in guest and on host. + * None. * *----------------------------------------------------------------------------- */ -VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHnd) -int -VMCIDatagram_CreateHnd(VMCIId resourceID, // IN - uint32 flags, // IN - VMCIDatagramRecvCB recvCB, // IN - void *clientData, // IN - VMCIHandle *outHandle) // OUT +static void +VMCIDatagramDelayedDispatchCB(void *data) // IN { - int result; - DatagramHashEntry *entry; - VMCIHandle handle; - VMCIId contextID; + Bool inDGHostQueue; + VMCIDelayedDatagramInfo *dgInfo = (VMCIDelayedDatagramInfo *)data; - if (!recvCB || !outHandle) { - return VMCI_ERROR_INVALID_ARGS; + ASSERT(data); + + dgInfo->entry->recvCB(dgInfo->entry->clientData, &dgInfo->msg); + + VMCIResource_Release(&dgInfo->entry->resource); + + inDGHostQueue = dgInfo->inDGHostQueue; + VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dgInfo->msg.payloadSize); + + if (inDGHostQueue) { + Atomic_Dec(&delayedDGHostQueueSize); } +} - if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) { - contextID = VMCI_INVALID_ID; - } else { - contextID = VMCI_GetContextID(); - /* Validate contextID. */ - if (contextID == VMCI_INVALID_ID) { - return VMCI_ERROR_NO_RESOURCES; - } + +/* + *------------------------------------------------------------------------------ + * + * VMCIDatagramDispatchFromHostToSelfOrGuest -- + * + * 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. + * + * Result: + * Number of bytes sent on success, appropriate error code otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +VMCIDatagramDispatchFromHostToSelfOrGuest(VMCIId contextID, // IN: + VMCIDatagram *dg) // IN: +{ + int retval; + size_t dgSize; + VMCIId dstContext; + VMCIPrivilegeFlags srcPrivFlags; + char srcDomain[VMCI_DOMAIN_NAME_MAXLEN]; /* Not used on hosted. */ + char dstDomain[VMCI_DOMAIN_NAME_MAXLEN]; /* Not used on hosted. */ + + ASSERT(dg); + + dgSize = VMCI_DG_SIZE(dg); + + if (contextID == VMCI_HOST_CONTEXT_ID && + dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID) { + return VMCI_ERROR_DST_UNREACHABLE; } - if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) { - VMCIDatagramWellKnownMapMsg wkMsg; - if (resourceID == VMCI_INVALID_ID) { - return VMCI_ERROR_INVALID_ARGS; - } - wkMsg.hdr.dst.context = VMCI_HYPERVISOR_CONTEXT_ID; - wkMsg.hdr.dst.resource = VMCI_DATAGRAM_REQUEST_MAP; - wkMsg.hdr.src = VMCI_ANON_SRC_HANDLE; - wkMsg.hdr.payloadSize = sizeof wkMsg - VMCI_DG_HEADERSIZE; - wkMsg.wellKnownID = resourceID; - result = VMCI_SendDatagram((VMCIDatagram *)&wkMsg); - if (result < VMCI_SUCCESS) { - VMCI_DEBUG_LOG(4, (LGPFX"Failed to reserve wellknown id %d, " - "error %d.\n", resourceID, result)); - return result; - } + ASSERT(dg->dst.context != VMCI_HYPERVISOR_CONTEXT_ID); - handle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, resourceID); - } else { - if (resourceID == VMCI_INVALID_ID) { - handle = VMCI_INVALID_HANDLE; + VMCI_DEBUG_LOG(10, (LGPFX"Sending from handle 0x%x:0x%x to handle 0x%x:0x%x, " + "datagram size %u.\n", + dg->src.context, dg->src.resource, + dg->dst.context, dg->dst.resource, (uint32)dgSize)); + + /* + * Check that source handle matches sending context. + */ + if (dg->src.context != contextID) { + if (dg->src.context == VMCI_WELL_KNOWN_CONTEXT_ID) { + /* Determine mapping. */ + DatagramWKMapping *wkMap = DatagramGetWellKnownMap(dg->src.resource); + if (wkMap == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Sending from invalid well-known resource " + "id 0x%x:0x%x.\n", + dg->src.context, dg->src.resource)); + return VMCI_ERROR_INVALID_RESOURCE; + } + if (wkMap->contextID != contextID) { + VMCI_DEBUG_LOG(4, (LGPFX"Sender context 0x%x is not owner of " + "well-known src datagram entry with handle " + "0x%x:0x%x.\n", + contextID, dg->src.context, dg->src.resource)); + DatagramReleaseWellKnownMap(wkMap); + return VMCI_ERROR_NO_ACCESS; + } + DatagramReleaseWellKnownMap(wkMap); } else { - handle = VMCI_MAKE_HANDLE(contextID, resourceID); + VMCI_DEBUG_LOG(4, (LGPFX"Sender context 0x%x is not owner of src " + "datagram entry with handle 0x%x:0x%x.\n", + contextID, dg->src.context, dg->src.resource)); + return VMCI_ERROR_NO_ACCESS; } } - /* Update local datastructure. */ - entry = VMCI_AllocKernelMem(sizeof *entry, VMCI_MEMORY_NONPAGED); - if (entry == NULL) { - return VMCI_ERROR_NO_MEM; + if (dg->dst.context == VMCI_WELL_KNOWN_CONTEXT_ID) { + /* Determine mapping. */ + DatagramWKMapping *wkMap = DatagramGetWellKnownMap(dg->dst.resource); + if (wkMap == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid wellknown destination " + "0x%x:0x%x.\n", + dg->dst.context, dg->dst.resource)); + return VMCI_ERROR_DST_UNREACHABLE; + } + dstContext = wkMap->contextID; + DatagramReleaseWellKnownMap(wkMap); + } else { + dstContext = dg->dst.context; } - if (!VMCI_CanScheduleDelayedWork()) { - if (flags & VMCI_FLAG_DG_DELAYED_CB) { - VMCI_FreeKernelMem(entry, sizeof *entry); - return VMCI_ERROR_INVALID_ARGS; + /* + * Get hold of privileges of sending endpoint. + */ + + retval = VMCIDatagramGetPrivFlagsInt(contextID, dg->src, &srcPrivFlags); + if (retval != VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Couldn't get privileges for handle 0x%x:0x%x.\n", + dg->src.context, dg->src.resource)); + return retval; + } + +#ifdef VMKERNEL + /* + * In the vmkernel, all communicating contexts except the + * hypervisor context must belong to the same domain. If the + * hypervisor is the source, the domain doesn't matter. + */ + + if (contextID != VMCI_HYPERVISOR_CONTEXT_ID) { + retval = VMCIContext_GetDomainName(contextID, srcDomain, + sizeof srcDomain); + if (retval < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to get domain name for context %u.\n", + contextID)); + return retval; + } + } +#endif + + /* Determine if we should route to host or guest destination. */ + if (dstContext == VMCI_HOST_CONTEXT_ID) { + /* Route to host datagram entry. */ + DatagramEntry *dstEntry; + VMCIResource *resource; + + if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && + dg->dst.resource == VMCI_EVENT_HANDLER) { + return VMCIEvent_Dispatch(dg); + } + + resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); + if (resource == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid destination handle " + "0x%x:0x%x.\n", dg->dst.context, dg->dst.resource)); + return VMCI_ERROR_INVALID_RESOURCE; + } + dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); +#ifdef VMKERNEL + retval = VMCIContext_GetDomainName(VMCI_HOST_CONTEXT_ID, dstDomain, + sizeof dstDomain); + if (retval < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to get domain name for context %u.\n", + VMCI_HOST_CONTEXT_ID)); + VMCIResource_Release(resource); + return retval; + } +#endif + if (VMCIDenyInteraction(srcPrivFlags, dstEntry->privFlags, srcDomain, + dstDomain)) { + VMCIResource_Release(resource); + return VMCI_ERROR_NO_ACCESS; + } + ASSERT(dstEntry->recvCB); + + /* + * If a VMCI datagram destined for the host is also sent by the + * host, we always run it delayed. This ensures that no locks + * are held when the datagram callback runs. + */ + + if (dstEntry->runDelayed || + (dg->src.context == VMCI_HOST_CONTEXT_ID && + VMCI_CanScheduleDelayedWork())) { + VMCIDelayedDatagramInfo *dgInfo; + + if (Atomic_FetchAndAdd(&delayedDGHostQueueSize, 1) == + VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) { + Atomic_Dec(&delayedDGHostQueueSize); + VMCIResource_Release(resource); + return VMCI_ERROR_NO_MEM; + } + + dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize, + (VMCI_MEMORY_ATOMIC | + VMCI_MEMORY_NONPAGED)); + if (NULL == dgInfo) { + Atomic_Dec(&delayedDGHostQueueSize); + VMCIResource_Release(resource); + return VMCI_ERROR_NO_MEM; + } + + dgInfo->inDGHostQueue = TRUE; + dgInfo->entry = dstEntry; + memcpy(&dgInfo->msg, dg, dgSize); + + retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); + if (retval < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram, " + "result %d.\n", retval)); + VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize); + VMCIResource_Release(resource); + Atomic_Dec(&delayedDGHostQueueSize); + return retval; + } + } else { + retval = dstEntry->recvCB(dstEntry->clientData, dg); + VMCIResource_Release(resource); + if (retval < VMCI_SUCCESS) { + return retval; + } } - entry->runDelayed = FALSE; } else { - entry->runDelayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? TRUE : FALSE; + /* Route to destination VM context. */ + VMCIDatagram *newDG; + +#ifdef VMKERNEL + retval = VMCIContext_GetDomainName(dstContext, dstDomain, + sizeof dstDomain); + if (retval < VMCI_SUCCESS) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to get domain name for context %u.\n", + dstContext)); + return retval; + } +#endif + if (contextID != dstContext && + VMCIDenyInteraction(srcPrivFlags, VMCIContext_GetPrivFlags(dstContext), + srcDomain, dstDomain)) { + return VMCI_ERROR_NO_ACCESS; + } + + /* We make a copy to enqueue. */ + newDG = VMCI_AllocKernelMem(dgSize, VMCI_MEMORY_NORMAL); + if (newDG == NULL) { + return VMCI_ERROR_NO_MEM; + } + memcpy(newDG, dg, dgSize); + retval = VMCIContext_EnqueueDatagram(dstContext, newDG); + if (retval < VMCI_SUCCESS) { + VMCI_FreeKernelMem(newDG, dgSize); + return retval; + } } + /* The datagram is freed when the context reads it. */ + VMCI_DEBUG_LOG(10, (LGPFX"Sent datagram of size %u.\n", (uint32)dgSize)); - entry->handle = handle; - entry->flags = flags; - entry->recvCB = recvCB; - entry->clientData = clientData; - entry->refCount = 0; - VMCI_CreateEvent(&entry->destroyEvent); + /* + * We currently truncate the size to signed 32 bits. This doesn't + * matter for this handler as it only support 4Kb messages. + */ + return (int)dgSize; +} - result = DatagramHashAddEntry(entry, contextID); - if (result != VMCI_SUCCESS) { - VMCI_DEBUG_LOG(4, (LGPFX"Failed to add new entry, err 0x%x.\n", result)); - VMCI_DestroyEvent(&entry->destroyEvent); - VMCI_FreeKernelMem(entry, sizeof *entry); - return result; + +/* + *------------------------------------------------------------------------------ + * + * VMCIDatagramDispatchFromGuestToHost -- + * + * Dispatch datagram down through the VMX, and potentially to the host. + * + * Result: + * Number of bytes sent on success, appropriate error code otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +VMCIDatagramDispatchFromGuestToHost(VMCIDatagram *dg) +{ +#if defined(VMKERNEL) + VMCI_WARNING((LGPFX"Cannot send down to host from VMKERNEL.\n")); + return VMCI_ERROR_DST_UNREACHABLE; +#else // VMKERNEL + int retval; + VMCIResource *resource; + extern int VMCI_SendDatagram(VMCIDatagram *); + + resource = VMCIResource_Get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM); + if (NULL == resource) { + return VMCI_ERROR_NO_HANDLE; } - ASSERT(!VMCI_HANDLE_INVALID(entry->handle)); - *outHandle = entry->handle; - return VMCI_SUCCESS; + retval = VMCI_SendDatagram(dg); + VMCIResource_Release(resource); + return retval; +#endif // VMKERNEL } + /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * VMCIDatagram_CreateHndPriv -- + * VMCIDatagram_Dispatch -- * - * API provided for compatibility with the host vmci API. This function - * doesn't ever succeed since you can't ask for elevated privileges from - * the guest. Use VMCIDatagram_CreateHnd instead. + * Dispatch datagram. This will determine the routing for the datagram + * and dispatch it accordingly. * - * Results: - * Returns a negative error. + * Result: + * Number of bytes sent on success, appropriate error code otherwise. * - * Side effects: - * None. + * Side effects: + * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -VMCI_EXPORT_SYMBOL(VMCIDatagram_CreateHndPriv) int -VMCIDatagram_CreateHndPriv(VMCIId resourceID, // IN: - uint32 flags, // IN: - VMCIPrivilegeFlags privFlags, // IN: - VMCIDatagramRecvCB recvCB, // IN: - void *clientData, // IN: - VMCIHandle *outHandle) // OUT: +VMCIDatagram_Dispatch(VMCIId contextID, + VMCIDatagram *dg, + Bool fromGuest) { - return VMCI_ERROR_NO_ACCESS; + int retval; + VMCIRoute route; + + ASSERT(dg); + ASSERT_ON_COMPILE(sizeof(VMCIDatagram) == 24); + + if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { + VMCI_DEBUG_LOG(4, (LGPFX"Payload size %"FMT64"u too big to send.\n", + dg->payloadSize)); + return VMCI_ERROR_INVALID_ARGS; + } + + retval = VMCI_Route(&dg->src, &dg->dst, fromGuest, &route); + if (retval < VMCI_SUCCESS) { + return retval; + } + + if (VMCI_ROUTE_HOST_TO_SELF == route || + VMCI_ROUTE_HOST_TO_GUEST == route) { + if (VMCI_INVALID_ID == contextID) { + contextID = VMCI_HOST_CONTEXT_ID; + } + return VMCIDatagramDispatchFromHostToSelfOrGuest(contextID, dg); + } + + if (VMCI_ROUTE_GUEST_TO_HOST == route || + VMCI_ROUTE_GUEST_TO_HYPERVISOR == route) { + return VMCIDatagramDispatchFromGuestToHost(dg); + } + + VMCI_WARNING((LGPFX"Unknown route for datagram.\n")); + return VMCI_ERROR_DST_UNREACHABLE; } /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * VMCIDatagram_DestroyHnd -- + * VMCIDatagram_InvokeGuestHandler -- * - * Destroys a handle. + * Invoke the handler for the given datagram. This is intended to be + * called only when acting as a guest and receiving a datagram from the + * virtual device. * - * Results: - * VMCI_SUCCESS or error code. + * Result: + * VMCI_SUCCESS on success, other error values on failure. * - * Side effects: - * Host and guest state is cleaned up. + * Side effects: + * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -VMCI_EXPORT_SYMBOL(VMCIDatagram_DestroyHnd) int -VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN +VMCIDatagram_InvokeGuestHandler(VMCIDatagram *dg) // IN { - DatagramHashEntry *entry = DatagramHashGetEntry(handle); - if (entry == NULL) { - return VMCI_ERROR_NOT_FOUND; +#if defined(VMKERNEL) + VMCI_WARNING((LGPFX"Cannot dispatch within guest in VMKERNEL.\n")); + return VMCI_ERROR_DST_UNREACHABLE; +#else // VMKERNEL + int retval; + VMCIResource *resource; + DatagramEntry *dstEntry; + + ASSERT(dg); + + resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); + if (NULL == resource) { + VMCI_DEBUG_LOG(4, (LGPFX"destination handle 0x%x:0x%x doesn't exist.\n", + dg->dst.context, dg->dst.resource)); + return VMCI_ERROR_NO_HANDLE; } - DatagramHashRemoveEntry(entry->handle); - - /* - * We wait for destroyEvent to be signalled. The resource is released - * as part of the wait. - */ - VMCI_WaitOnEvent(&entry->destroyEvent, DatagramReleaseCB, entry); + dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); + if (dstEntry->runDelayed) { + VMCIDelayedDatagramInfo *dgInfo; + dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize, + (VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED)); + if (NULL == dgInfo) { + VMCIResource_Release(resource); + retval = VMCI_ERROR_NO_MEM; + goto exit; + } - if ((entry->flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) { - int result; - VMCIDatagramWellKnownMapMsg wkMsg; - - wkMsg.hdr.dst.context = VMCI_HYPERVISOR_CONTEXT_ID; - wkMsg.hdr.dst.resource = VMCI_DATAGRAM_REMOVE_MAP; - wkMsg.hdr.src = VMCI_ANON_SRC_HANDLE; - wkMsg.hdr.payloadSize = sizeof wkMsg - VMCI_DG_HEADERSIZE; - wkMsg.wellKnownID = entry->handle.resource; - result = VMCI_SendDatagram((VMCIDatagram *)&wkMsg); - if (result < VMCI_SUCCESS) { - VMCI_WARNING((LGPFX"Failed to remove well-known mapping for " - "resource %d.\n", entry->handle.resource)); + dgInfo->inDGHostQueue = FALSE; + dgInfo->entry = dstEntry; + memcpy(&dgInfo->msg, dg, VMCI_DG_SIZE(dg)); + + retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); + if (retval < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram, " + "result %d.\n", retval)); + VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize); + VMCIResource_Release(resource); + dgInfo = NULL; + goto exit; } + } else { + dstEntry->recvCB(dstEntry->clientData, dg); + VMCIResource_Release(resource); + retval = VMCI_SUCCESS; } - /* We know we are now holding the last reference so we can free the entry. */ - VMCI_DestroyEvent(&entry->destroyEvent); - VMCI_FreeKernelMem(entry, sizeof *entry); - - return VMCI_SUCCESS; +exit: + return retval; +#endif // VMKERNEL } /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * * VMCIDatagram_Send -- * @@ -605,56 +984,58 @@ VMCIDatagram_DestroyHnd(VMCIHandle handle) // IN * Side effects: * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ VMCI_EXPORT_SYMBOL(VMCIDatagram_Send) int VMCIDatagram_Send(VMCIDatagram *msg) // IN { - uint32 retval; - DatagramHashEntry *entry; - VMCIId contextId; - if (msg == NULL) { - VMCI_DEBUG_LOG(4, (LGPFX"Invalid datagram.\n")); return VMCI_ERROR_INVALID_ARGS; } - if (VMCI_DG_SIZE(msg) > VMCI_MAX_DG_SIZE) { - VMCI_DEBUG_LOG(4, (LGPFX"Payload size %"FMT64"u too big to send.\n", - msg->payloadSize)); - return VMCI_ERROR_INVALID_ARGS; - } + return VMCIDatagram_Dispatch(VMCI_INVALID_ID, msg, FALSE); +} - /* Check srcHandle exists otherwise fail. */ - entry = DatagramHashGetEntry(msg->src); - if (entry == NULL) { - VMCI_DEBUG_LOG(4, (LGPFX"Couldn't find handle 0x%x:0x%x.\n", - msg->src.context, msg->src.resource)); - return VMCI_ERROR_INVALID_ARGS; - } - contextId = VMCI_HANDLE_TO_CONTEXT_ID(msg->src); +/* + *------------------------------------------------------------------------------ + * + * DatagramGetWellKnownMap -- + * + * Gets a mapping between handle and wellknown resource. + * + * Results: + * DatagramWKMapping * if found, NULL if not. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ - if (contextId == VMCI_INVALID_ID) { - msg->src = VMCI_MAKE_HANDLE(VMCI_GetContextID(), - VMCI_HANDLE_TO_RESOURCE_ID(msg->src)); +static DatagramWKMapping * +DatagramGetWellKnownMap(VMCIId wellKnownID) // IN: +{ + VMCIHashEntry *entry; + DatagramWKMapping *wkMap = NULL; + VMCIHandle wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, + wellKnownID); + entry = VMCIHashTable_GetEntry(wellKnownTable, wkHandle); + if (entry != NULL) { + wkMap = RESOURCE_CONTAINER(entry, DatagramWKMapping, entry); } - - retval = VMCI_SendDatagram(msg); - DatagramHashReleaseEntry(entry); - - return retval; + return wkMap; } /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * VMCIDatagramDelayedDispatchCB -- + * DatagramReleaseWellKnownMap -- * - * Calls the specified callback in a delayed context. + * Releases a wellknown mapping. * * Results: * None. @@ -662,133 +1043,124 @@ VMCIDatagram_Send(VMCIDatagram *msg) // IN * Side effects: * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ static void -VMCIDatagramDelayedDispatchCB(void *data) // IN +DatagramReleaseWellKnownMap(DatagramWKMapping *wkMap) // IN: { - VMCIDelayedDatagramInfo *dgInfo = (VMCIDelayedDatagramInfo *)data; - - ASSERT(data); - - dgInfo->entry->recvCB(dgInfo->entry->clientData, &dgInfo->msg); - - DatagramHashReleaseEntry(dgInfo->entry); - VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dgInfo->msg.payloadSize); + if (VMCIHashTable_ReleaseEntry(wellKnownTable, &wkMap->entry) == + VMCI_SUCCESS_ENTRY_DEAD) { + VMCI_FreeKernelMem(wkMap, sizeof *wkMap); + } } /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * VMCIDatagram_Dispatch -- + * VMCIDatagramRequestWellKnownMap -- * - * Forwards the datagram to the corresponding entry's callback. This will - * defer the datagram if requested by the client, so that the callback - * is invoked in a delayed context. + * Creates a mapping between handle and wellknown resource. If resource + * is already used we fail the request. * * Results: - * VMCI_SUCCESS on success, error code if not. + * VMCI_SUCCESS if created, negative errno value otherwise. * * Side effects: * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ int -VMCIDatagram_Dispatch(VMCIId contextID, // IN: unused - VMCIDatagram *msg) // IN +VMCIDatagramRequestWellKnownMap(VMCIId wellKnownID, // IN: + VMCIId contextID, // IN: + VMCIPrivilegeFlags privFlags) // IN: { - int32 err = VMCI_SUCCESS; - DatagramHashEntry *entry; - - ASSERT(msg); + int result; + DatagramWKMapping *wkMap; + VMCIHandle wkHandle = VMCI_MAKE_HANDLE(VMCI_WELL_KNOWN_CONTEXT_ID, + wellKnownID); - entry = DatagramHashGetEntryAnyCid(msg->dst); - if (entry == NULL) { - VMCI_DEBUG_LOG(4, (LGPFX"destination handle 0x%x:0x%x doesn't exist.\n", - msg->dst.context, msg->dst.resource)); - return VMCI_ERROR_NO_HANDLE; + if (privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED || + !VMCIWellKnownID_AllowMap(wellKnownID, privFlags)) { + return VMCI_ERROR_NO_ACCESS; } - if (!entry->recvCB) { - VMCI_WARNING((LGPFX"no handle callback for handle 0x%x:0x%x payload of " - "size %"FMT64"d.\n", msg->dst.context, msg->dst.resource, - msg->payloadSize)); - goto out; + wkMap = VMCI_AllocKernelMem(sizeof *wkMap, VMCI_MEMORY_NONPAGED); + if (wkMap == NULL) { + return VMCI_ERROR_NO_MEM; } - if (entry->runDelayed) { - VMCIDelayedDatagramInfo *dgInfo; - - dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)msg->payloadSize, - VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED); - if (NULL == dgInfo) { - err = VMCI_ERROR_NO_MEM; - goto out; - } - - dgInfo->entry = entry; - memcpy(&dgInfo->msg, msg, VMCI_DG_SIZE(msg)); - - err = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); - if (VMCI_SUCCESS == err) { - return err; - } + VMCIHashTable_InitEntry(&wkMap->entry, wkHandle); + wkMap->contextID = contextID; - VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)msg->payloadSize); - } else { - entry->recvCB(entry->clientData, msg); + /* Fails if wkHandle (wellKnownID) already exists. */ + result = VMCIHashTable_AddEntry(wellKnownTable, &wkMap->entry); + if (result != VMCI_SUCCESS) { + VMCI_FreeKernelMem(wkMap, sizeof *wkMap); + return result; } - -out: - DatagramHashReleaseEntry(entry); - return err; + result = VMCIContext_AddWellKnown(contextID, wellKnownID); + if (UNLIKELY(result < VMCI_SUCCESS)) { + VMCIHashTable_RemoveEntry(wellKnownTable, &wkMap->entry); + VMCI_FreeKernelMem(wkMap, sizeof *wkMap); + } + return result; } /* - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ * - * VMCIDatagram_Init -- + * VMCIDatagramRemoveWellKnownMap -- * - * Register guest call handlers. + * Removes a mapping between handle and wellknown resource. Checks if + * mapping belongs to calling context. * * Results: - * None + * VMCI_SUCCESS if removed, negative errno value otherwise. * * Side effects: * None. * - *----------------------------------------------------------------------------- + *------------------------------------------------------------------------------ */ -void -VMCIDatagram_Init(void) +int +VMCIDatagramRemoveWellKnownMap(VMCIId wellKnownID, // IN: + VMCIId contextID) // IN: { - int i; + int result = VMCI_ERROR_NO_ACCESS; + DatagramWKMapping *wkMap = DatagramGetWellKnownMap(wellKnownID); + if (wkMap == NULL) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to remove well-known mapping between " + "resource %d and context %d.\n", wellKnownID, + contextID)); + return VMCI_ERROR_NOT_FOUND; + } - VMCI_InitLock(&hashTable.lock, - "VMCIDatagramHashtable", - VMCI_LOCK_RANK_MIDDLE_BH); - for (i = 0; i < HASH_TABLE_SIZE; i++) { - hashTable.entries[i] = NULL; + if (contextID == wkMap->contextID) { + VMCIHashTable_RemoveEntry(wellKnownTable, &wkMap->entry); + VMCIContext_RemoveWellKnown(contextID, wellKnownID); + result = VMCI_SUCCESS; } + DatagramReleaseWellKnownMap(wkMap); + return result; } /* *----------------------------------------------------------------------------- * - * VMCIDatagram_CheckHostCapabilities -- + * VMCIDatagram_Sync -- * - * Verify that the host supports the resources we need. - * None are required for datagrams since they are implicitly supported. + * Use this as a synchronization point when setting globals, for example, + * during device shutdown. * * Results: - * TRUE. + * None. * * Side effects: * None. @@ -796,23 +1168,23 @@ VMCIDatagram_Init(void) *----------------------------------------------------------------------------- */ -Bool -VMCIDatagram_CheckHostCapabilities(void) +void +VMCIDatagram_Sync(void) { - return TRUE; + VMCIResource_Sync(); } /* *----------------------------------------------------------------------------- * - * VMCIDatagram_Sync -- + * VMCIDatagram_CheckHostCapabilities -- * - * Use this as a synchronization point when setting globals, for example, - * during device shutdown. + * Verify that the host supports the resources we need. + * None are required for datagrams since they are implicitly supported. * * Results: - * None. + * TRUE. * * Side effects: * None. @@ -820,10 +1192,8 @@ VMCIDatagram_CheckHostCapabilities(void) *----------------------------------------------------------------------------- */ -void -VMCIDatagram_Sync(void) +Bool +VMCIDatagram_CheckHostCapabilities(void) { - VMCILockFlags flags; - VMCI_GrabLock_BH(&hashTable.lock, &flags); - VMCI_ReleaseLock_BH(&hashTable.lock, flags); + return TRUE; } diff --git a/open-vm-tools/modules/linux/vmci/vmciDatagram.h b/open-vm-tools/modules/linux/vmci/vmciDatagram.h index 7661823c5..2bae0d8ca 100644 --- a/open-vm-tools/modules/linux/vmci/vmciDatagram.h +++ b/open-vm-tools/modules/linux/vmci/vmciDatagram.h @@ -19,24 +19,45 @@ /* * vmciDatagram.h -- * - * Simple Datagram API for the Linux guest driver. + * Internal functions in the VMCI Simple Datagram API. */ -#ifndef __VMCI_DATAGRAM_H__ -#define __VMCI_DATAGRAM_H__ +#ifndef _VMCI_DATAGRAM_H_ +#define _VMCI_DATAGRAM_H_ #define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL #include "includeCheck.h" -#include "vmci_defs.h" +#include "vmciContext.h" #include "vmci_call_defs.h" -#include "vmci_kernel_if.h" -#include "vmci_infrastructure.h" +#ifndef VMX86_SERVER #include "vmci_iocontrols.h" +#endif // !VMX86_SERVER -void VMCIDatagram_Init(void); +#define VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE 256 + +/* Init functions. */ +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); +int VMCIDatagram_GetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags); + +/* Misc. */ void VMCIDatagram_Sync(void); Bool VMCIDatagram_CheckHostCapabilities(void); -int VMCIDatagram_Dispatch(VMCIId contextID, VMCIDatagram *msg); -#endif //__VMCI_DATAGRAM_H__ +/* Non public datagram API. */ +int VMCIDatagramRequestWellKnownMap(VMCIId wellKnownID, VMCIId contextID, + VMCIPrivilegeFlags privFlags); +int VMCIDatagramRemoveWellKnownMap(VMCIId wellKnownID, VMCIId contextID); + +#endif // _VMCI_DATAGRAM_H_ + + diff --git a/open-vm-tools/modules/linux/vmci/vmciDoorbell.h b/open-vm-tools/modules/linux/vmci/vmciDoorbell.h new file mode 100644 index 000000000..c9c45d5e8 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciDoorbell.h @@ -0,0 +1,40 @@ +/********************************************************* + * 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.h -- + * + * Internal functions in the VMCI Doorbell API. + */ + +#ifndef VMCI_DOORBELL_H +#define VMCI_DOORBELL_H + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_kernel_if.h" +#include "vmci_defs.h" + +int VMCIDoorbellHostContextNotify(VMCIId srcCID, VMCIHandle handle); +int VMCIDoorbellGetPrivFlags(VMCIHandle handle, VMCIPrivilegeFlags *privFlags); +void VMCIDoorbell_Sync(void); + +#endif // VMCI_DOORBELL_H diff --git a/open-vm-tools/modules/linux/vmci/vmciDriver.h b/open-vm-tools/modules/linux/vmci/vmciDriver.h new file mode 100644 index 000000000..731258d5a --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciDriver.h @@ -0,0 +1,77 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciDriver.h -- + * + * VMCI host driver interface. + */ + +#ifndef _VMCI_DRIVER_H_ +#define _VMCI_DRIVER_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_defs.h" +#include "vmci_infrastructure.h" +#include "vmciContext.h" + +/* + * A few macros to encapsulate logging in common code. The macros + * result in LOG/LOGThrottled on vmkernel and Log on hosted. + */ + +#ifdef _WIN32 +# include "vmciLog.h" +#else // _WIN32 +#ifdef VMKERNEL +# define LOGLEVEL_MODULE_LEN 0 +# define LOGLEVEL_MODULE VMCIVMK +# include "log.h" +# ifdef VMX86_LOG +# define _VMCILOG(_args...) LOG(i, _args) +# define VMCI_DEBUG_LOG(_level, _args) \ + do { \ + int i = _level; \ + _VMCILOG _args ; \ + } while(FALSE) +# else +# define VMCI_DEBUG_LOG(_level, _args) +# endif +#else +# define VMCI_DEBUG_LEVEL 4 +# define VMCI_DEBUG_LOG(_level, _args) \ + do { \ + if (_level < VMCI_DEBUG_LEVEL) { \ + Log _args ; \ + } \ + } while(FALSE) +#endif +#define VMCI_LOG(_args) Log _args +#define VMCI_WARNING(_args) Warning _args +#endif // _WIN32 + +int VMCI_Init(void); +void VMCI_Cleanup(void); +VMCIId VMCI_GetContextID(void); + +#endif // _VMCI_DRIVER_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciHashtable.c b/open-vm-tools/modules/linux/vmci/vmciHashtable.c new file mode 100644 index 000000000..826d76d90 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciHashtable.c @@ -0,0 +1,581 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciHashtable.c -- + * + * Implementation of the VMCI Hashtable. + * TODO: Look into what is takes to use lib/misc/hashTable.c instead of + * our own implementation. + */ + +#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 "vmciHashtable.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 "VMCIHashTable: " + +#define VMCI_HASHTABLE_HASH(_h, _sz) \ + VMCI_HashId(VMCI_HANDLE_TO_RESOURCE_ID(_h), (_sz)) + + +static int HashTableUnlinkEntry(VMCIHashTable *table, VMCIHashEntry *entry); +static Bool VMCIHashTableEntryExistsLocked(VMCIHashTable *table, + VMCIHandle handle); + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_Create -- + * XXX Factor out the hashtable code to be shared amongst host and guest. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +VMCIHashTable * +VMCIHashTable_Create(int size) +{ + VMCIHashTable *table = VMCI_AllocKernelMem(sizeof *table, + VMCI_MEMORY_NONPAGED); + if (table == NULL) { + return NULL; + } + + table->entries = VMCI_AllocKernelMem(sizeof *table->entries * size, + VMCI_MEMORY_NONPAGED); + if (table->entries == NULL) { + VMCI_FreeKernelMem(table, sizeof *table); + return NULL; + } + memset(table->entries, 0, sizeof *table->entries * size); + table->size = size; + VMCI_InitLock(&table->lock, + "VMCIHashTableLock", + VMCI_LOCK_RANK_HIGH); + + return table; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_Destroy -- + * This function should be called at module exit time. + * We rely on the module ref count to insure that no one is accessing any + * hash table entries at this point in time. Hence we should be able to just + * remove all entries from the hash table. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +void +VMCIHashTable_Destroy(VMCIHashTable *table) +{ + VMCILockFlags flags; +#if 0 + DEBUG_ONLY(int i;) + DEBUG_ONLY(int leakingEntries = 0;) +#endif + + ASSERT(table); + + VMCI_GrabLock(&table->lock, &flags); +#if 0 +#ifdef VMX86_DEBUG + for (i = 0; i < table->size; i++) { + VMCIHashEntry *head = table->entries[i]; + while (head) { + leakingEntries++; + head = head->next; + } + } + if (leakingEntries) { + VMCI_WARNING((LGPFX"Leaking %d hash table entries for table %p.\n", + leakingEntries, table)); + } +#endif // VMX86_DEBUG +#endif + VMCI_FreeKernelMem(table->entries, sizeof *table->entries * table->size); + table->entries = NULL; + VMCI_ReleaseLock(&table->lock, flags); + VMCI_CleanupLock(&table->lock); + VMCI_FreeKernelMem(table, sizeof *table); +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_InitEntry -- + * Initializes a hash entry; + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ +void +VMCIHashTable_InitEntry(VMCIHashEntry *entry, // IN + VMCIHandle handle) // IN +{ + ASSERT(entry); + entry->handle = handle; + entry->refCount = 0; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_AddEntry -- + * XXX Factor out the hashtable code to be shared amongst host and guest. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +int +VMCIHashTable_AddEntry(VMCIHashTable *table, // IN + VMCIHashEntry *entry) // IN +{ + int idx; + VMCILockFlags flags; + + ASSERT(entry); + ASSERT(table); + + VMCI_GrabLock(&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); + return VMCI_ERROR_DEVICE_NOT_FOUND; + } + + if (VMCIHashTableEntryExistsLocked(table, entry->handle)) { + VMCI_DEBUG_LOG(4, (LGPFX"Entry's handle 0x%x:0x%x already exists.\n", + entry->handle.context, entry->handle.resource)); + VMCI_ReleaseLock(&table->lock, flags); + return VMCI_ERROR_DUPLICATE_ENTRY; + } + + idx = VMCI_HASHTABLE_HASH(entry->handle, table->size); + ASSERT(idx < table->size); + + /* New entry is added to top/front of hash bucket. */ + entry->refCount++; + entry->next = table->entries[idx]; + table->entries[idx] = entry; + VMCI_ReleaseLock(&table->lock, flags); + + return VMCI_SUCCESS; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_RemoveEntry -- + * XXX Factor out the hashtable code to shared amongst API and perhaps + * host and guest. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +int +VMCIHashTable_RemoveEntry(VMCIHashTable *table, // IN + VMCIHashEntry *entry) // IN +{ + int result; + VMCILockFlags flags; + + ASSERT(table); + ASSERT(entry); + + VMCI_GrabLock(&table->lock, &flags); + + /* First unlink the entry. */ + result = HashTableUnlinkEntry(table, entry); + if (result != VMCI_SUCCESS) { + /* We failed to find the entry. */ + goto done; + } + + /* Decrement refcount and check if this is last reference. */ + entry->refCount--; + if (entry->refCount == 0) { + result = VMCI_SUCCESS_ENTRY_DEAD; + goto done; + } + + done: + VMCI_ReleaseLock(&table->lock, flags); + + return result; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTableGetEntryLocked -- + * + * Looks up an entry in the hash table, that is already locked. + * + * Result: + * If the element is found, a pointer to the element is returned. + * Otherwise NULL is returned. + * + * Side effects: + * The reference count of the returned element is increased. + * + *------------------------------------------------------------------------------ + */ + +static INLINE VMCIHashEntry * +VMCIHashTableGetEntryLocked(VMCIHashTable *table, // IN + VMCIHandle handle) // IN +{ + VMCIHashEntry *cur = NULL; + int idx; + + ASSERT(!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)); + ASSERT(table); + + idx = VMCI_HASHTABLE_HASH(handle, table->size); + + cur = table->entries[idx]; + while (TRUE) { + if (cur == NULL) { + break; + } + + if (VMCI_HANDLE_TO_RESOURCE_ID(cur->handle) == + VMCI_HANDLE_TO_RESOURCE_ID(handle)) { + if ((VMCI_HANDLE_TO_CONTEXT_ID(cur->handle) == + VMCI_HANDLE_TO_CONTEXT_ID(handle)) || + (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(cur->handle))) { + cur->refCount++; + break; + } + } + cur = cur->next; + } + + return cur; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_GetEntry -- + * XXX Factor out the hashtable code to shared amongst API and perhaps + * host and guest. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +VMCIHashEntry * +VMCIHashTable_GetEntry(VMCIHashTable *table, // IN + VMCIHandle handle) // IN +{ + VMCIHashEntry *entry; + VMCILockFlags flags; + + if (VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) { + return NULL; + } + + ASSERT(table); + + VMCI_GrabLock(&table->lock, &flags); + entry = VMCIHashTableGetEntryLocked(table, handle); + VMCI_ReleaseLock(&table->lock, flags); + + return entry; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTableReleaseEntryLocked -- + * + * Releases an element previously obtained with + * VMCIHashTableGetEntryLocked. + * + * Result: + * If the entry is removed from the hash table, VMCI_SUCCESS_ENTRY_DEAD + * is returned. Otherwise, VMCI_SUCCESS is returned. + * + * Side effects: + * The reference count of the entry is decreased and the entry is removed + * from the hash table on 0. + * + *------------------------------------------------------------------------------ + */ + +static INLINE int +VMCIHashTableReleaseEntryLocked(VMCIHashTable *table, // IN + VMCIHashEntry *entry) // IN +{ + int result = VMCI_SUCCESS; + + ASSERT(table); + ASSERT(entry); + + entry->refCount--; + /* Check if this is last reference and report if so. */ + if (entry->refCount == 0) { + + /* + * Remove entry from hash table if not already removed. This could have + * happened already because VMCIHashTable_RemoveEntry was called to unlink + * it. We ignore if it is not found. Datagram handles will often have + * RemoveEntry called, whereas SharedMemory regions rely on ReleaseEntry + * to unlink the entry, since the creator does not call RemoveEntry when + * it detaches. + */ + + HashTableUnlinkEntry(table, entry); + result = VMCI_SUCCESS_ENTRY_DEAD; + } + + return result; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_ReleaseEntry -- + * XXX Factor out the hashtable code to shared amongst API and perhaps + * host and guest. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +int +VMCIHashTable_ReleaseEntry(VMCIHashTable *table, // IN + VMCIHashEntry *entry) // IN +{ + VMCILockFlags flags; + int result; + + ASSERT(table); + VMCI_GrabLock(&table->lock, &flags); + result = VMCIHashTableReleaseEntryLocked(table, entry); + VMCI_ReleaseLock(&table->lock, flags); + + return result; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTable_EntryExists -- + * XXX Factor out the hashtable code to shared amongst API and perhaps + * host and guest. + * + * Result: + * TRUE if handle already in hashtable. FALSE otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +Bool +VMCIHashTable_EntryExists(VMCIHashTable *table, // IN + VMCIHandle handle) // IN +{ + Bool exists; + VMCILockFlags flags; + + ASSERT(table); + + VMCI_GrabLock(&table->lock, &flags); + exists = VMCIHashTableEntryExistsLocked(table, handle); + VMCI_ReleaseLock(&table->lock, flags); + + return exists; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCIHashTableEntryExistsLocked -- + * + * Unlocked version of VMCIHashTable_EntryExists. + * + * Result: + * TRUE if handle already in hashtable. FALSE otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static Bool +VMCIHashTableEntryExistsLocked(VMCIHashTable *table, // IN + VMCIHandle handle) // IN + +{ + VMCIHashEntry *entry; + int idx; + + ASSERT(table); + + idx = VMCI_HASHTABLE_HASH(handle, table->size); + + entry = table->entries[idx]; + while (entry) { + if (VMCI_HANDLE_TO_RESOURCE_ID(entry->handle) == + VMCI_HANDLE_TO_RESOURCE_ID(handle)) { + if ((VMCI_HANDLE_TO_CONTEXT_ID(entry->handle) == + VMCI_HANDLE_TO_CONTEXT_ID(handle)) || + (VMCI_INVALID_ID == VMCI_HANDLE_TO_CONTEXT_ID(handle))) { + return TRUE; + } + } + entry = entry->next; + } + + return FALSE; +} + + +/* + *------------------------------------------------------------------------------ + * + * HashTableUnlinkEntry -- + * XXX Factor out the hashtable code to shared amongst API and perhaps + * host and guest. + * Assumes caller holds table lock. + * + * Result: + * None. + * + *------------------------------------------------------------------------------ + */ + +static int +HashTableUnlinkEntry(VMCIHashTable *table, // IN + VMCIHashEntry *entry) // IN +{ + int result; + VMCIHashEntry *prev, *cur; + int idx; + + idx = VMCI_HASHTABLE_HASH(entry->handle, table->size); + + prev = NULL; + cur = table->entries[idx]; + while (TRUE) { + if (cur == NULL) { + result = VMCI_ERROR_NOT_FOUND; + break; + } + if (VMCI_HANDLE_EQUAL(cur->handle, entry->handle)) { + ASSERT(cur == entry); + + /* Remove entry and break. */ + if (prev) { + prev->next = cur->next; + } else { + table->entries[idx] = cur->next; + } + cur->next = NULL; + result = VMCI_SUCCESS; + break; + } + prev = cur; + cur = cur->next; + } + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * VMCIHashTable_Sync -- + * + * Use this as a synchronization point when setting globals, for example, + * during device shutdown. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +VMCIHashTable_Sync(VMCIHashTable *table) +{ + VMCILockFlags flags; + ASSERT(table); + VMCI_GrabLock(&table->lock, &flags); + VMCI_ReleaseLock(&table->lock, flags); +} diff --git a/open-vm-tools/modules/linux/vmci/vmciHashtable.h b/open-vm-tools/modules/linux/vmci/vmciHashtable.h new file mode 100644 index 000000000..c1e062fdf --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciHashtable.h @@ -0,0 +1,59 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciHashtable.h -- + * + * Hash table for use in the APIs. + */ + +#ifndef _VMCI_HASHTABLE_H_ +#define _VMCI_HASHTABLE_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_kernel_if.h" +#include "vmci_defs.h" + +typedef struct VMCIHashEntry { + VMCIHandle handle; + int refCount; + struct VMCIHashEntry *next; +} VMCIHashEntry; + +typedef struct VMCIHashTable { + VMCIHashEntry **entries; + int size; // Number of buckets in above array. + VMCILock lock; +} VMCIHashTable; + +VMCIHashTable *VMCIHashTable_Create(int size); +void VMCIHashTable_Destroy(VMCIHashTable *table); +void VMCIHashTable_InitEntry(VMCIHashEntry *entry, VMCIHandle handle); +int VMCIHashTable_AddEntry(VMCIHashTable *table, VMCIHashEntry *entry); +int VMCIHashTable_RemoveEntry(VMCIHashTable *table, VMCIHashEntry *entry); +VMCIHashEntry *VMCIHashTable_GetEntry(VMCIHashTable *table, VMCIHandle handle); +int VMCIHashTable_ReleaseEntry(VMCIHashTable *table, VMCIHashEntry *entry); +Bool VMCIHashTable_EntryExists(VMCIHashTable *table, VMCIHandle handle); +void VMCIHashTable_Sync(VMCIHashTable *table); + +#endif // _VMCI_HASHTABLE_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciQueuePair.h b/open-vm-tools/modules/linux/vmci/vmciQueuePair.h new file mode 100644 index 000000000..609d0fc21 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciQueuePair.h @@ -0,0 +1,168 @@ +/********************************************************* + * Copyright (C) 2007 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 + * + *********************************************************/ + +/* + * vmciQueuePair.h -- + * + * VMCI QueuePair API implementation in the host driver. + */ + +#ifndef _VMCI_QUEUE_PAIR_H_ +#define _VMCI_QUEUE_PAIR_H_ + +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_defs.h" +#include "vmciContext.h" +#include "vmci_iocontrols.h" +#include "vmciQueue.h" +#ifdef VMKERNEL +#include "vm_atomic.h" +#include "util_copy_dist.h" +#include "shm.h" + +/* + * On vmkernel, the queue pairs are either backed by shared memory or + * kernel memory allocated on the VMCI heap. Shared memory is used for + * guest to guest and guest to host queue pairs, whereas the heap + * allocated queue pairs are used for host local queue pairs. + */ + +typedef struct QueuePairPageStore { + Bool shared; // Indicates whether the pages are stored in shared memory + union { + Shm_ID shmID; + void *ptr; + } store; +} QueuePairPageStore; + +#else + +typedef struct QueuePairPageStore { + Bool user; // Whether the page file strings are userspace pointers + VA64 producePageFile; // Name of the file + VA64 consumePageFile; // Name of the file + uint64 producePageFileSize; // Size of the string + uint64 consumePageFileSize; // Size of the string + VA64 producePageUVA; // User space VA of the mapped file in VMX + VA64 consumePageUVA; // User space VA of the mapped file in VMX +} QueuePairPageStore; + +#endif // !VMKERNEL + + +/* + *------------------------------------------------------------------------------ + * + * VMCI_QP_PAGESTORE_IS_WELLFORMED -- + * + * Utility function that checks whether the fields of the page + * store contain valid values. + * + * Result: + * TRUE if the page store is wellformed. FALSE otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +static INLINE Bool +VMCI_QP_PAGESTORE_IS_WELLFORMED(QueuePairPageStore *pageStore) // IN +{ +#ifdef VMKERNEL + return (pageStore->shared && pageStore->store.shmID != SHM_INVALID_ID) || + (!pageStore->shared && pageStore->store.ptr != NULL); +#else + return pageStore->producePageFile && pageStore->consumePageFile && + pageStore->producePageFileSize && pageStore->consumePageFileSize; +#endif // !VMKERNEL +} + + +int VMCIQPBroker_Init(void); +void VMCIQPBroker_Exit(void); +void VMCIQPBroker_Lock(void); +void VMCIQPBroker_Unlock(void); +int VMCIQPBroker_Alloc(VMCIHandle handle, VMCIId peer, uint32 flags, + VMCIPrivilegeFlags privFlags, + uint64 produceSize, uint64 consumeSize, + QueuePairPageStore *pageStore, + VMCIContext *context); +int VMCIQPBroker_SetPageStore(VMCIHandle handle, + QueuePairPageStore *pageStore, + VMCIContext *context); +int VMCIQPBroker_Detach(VMCIHandle handle, VMCIContext *context, Bool detach); + +#if (defined(__linux__) || defined(_WIN32) || defined(__APPLE__)) && !defined(VMKERNEL) +struct VMCIQueue; + +typedef struct PageStoreAttachInfo { + char producePageFile[VMCI_PATH_MAX]; + char consumePageFile[VMCI_PATH_MAX]; + uint64 numProducePages; + uint64 numConsumePages; + + /* User VAs in the VMX task */ + VA64 produceBuffer; + VA64 consumeBuffer; + + /* + * Platform-specific references to the physical pages backing the + * queue. These include a page for the header. + * + * PR361589 tracks this, too. + */ + +#if defined(__linux__) +# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) + struct kiobuf *produceIoBuf; + struct kiobuf *consumeIoBuf; +# else + struct page **producePages; + struct page **consumePages; +# endif +#elif defined(_WIN32) + void *kmallocPtr; + size_t kmallocSize; + PMDL produceMDL; + PMDL consumeMDL; +#elif defined(__APPLE__) + /* + * All the Mac OS X fields are members of the VMCIQueue + */ +#endif +} PageStoreAttachInfo; + +#endif // (__linux__ || _WIN32 || __APPLE__) && !VMKERNEL + + +int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ, + uint64 produceSize, VMCIQueue **consumeQ, + uint64 consumeSize, VMCIId peer, uint32 flags, + VMCIPrivilegeFlags privFlags); +int VMCIQueuePair_Detach(VMCIHandle handle); + + +#endif /* !_VMCI_QUEUE_PAIR_H_ */ + diff --git a/open-vm-tools/modules/linux/vmci/vmciResource.c b/open-vm-tools/modules/linux/vmci/vmciResource.c new file mode 100644 index 000000000..257aa9662 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciResource.c @@ -0,0 +1,376 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciResource.c -- + * + * Implementation of the VMCI Resource Access Control API. + */ + +#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 "vmciHashtable.h" +#include "vmciResource.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 "VMCIResource: " + +/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */ +static uint32 resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; +static VMCILock resourceIdLock; + +static void VMCIResourceDoRemove(VMCIResource *resource); + +static VMCIHashTable *resourceTable = NULL; + + +/* 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. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------------- + */ + +int +VMCIResource_Init(void) +{ + resourceTable = VMCIHashTable_Create(128); + if (resourceTable == NULL) { + VMCI_WARNING((LGPFX"Failed creating a resource hash table for VMCI.\n")); + return VMCI_ERROR_NO_MEM; + } + + VMCI_InitLock(&resourceIdLock, "VMCIRIDLock", + VMCI_LOCK_RANK_HIGHEST); + + return VMCI_SUCCESS; +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResource_Exit -- + * + * Cleans up resources. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------------- + */ + +void +VMCIResource_Exit(void) +{ + /* Cleanup resources.*/ + VMCI_CleanupLock(&resourceIdLock); + + if (resourceTable) { + VMCIHashTable_Destroy(resourceTable); + } +} + + +/* + *------------------------------------------------------------------------- + * + * VMCIResource_GetID -- + * + * Return resource ID. The first VMCI_RESERVED_RESOURCE_ID_MAX are + * reserved so we start from its value + 1. + * + * Result: + * VMCI resource id on success, VMCI_INVALID_ID on failure. + * + * Side effects: + * None. + * + * + *------------------------------------------------------------------------- + */ + +VMCIId +VMCIResource_GetID(VMCIId contextID) +{ + VMCIId oldRID = resourceID; + VMCIId currentRID; + Bool foundRID = FALSE; + + /* + * Generate a unique resource ID. Keep on trying until we wrap around + * in the RID space. + */ + ASSERT(oldRID > VMCI_RESERVED_RESOURCE_ID_MAX); + + do { + VMCILockFlags flags; + VMCIHandle handle; + + VMCI_GrabLock(&resourceIdLock, &flags); + currentRID = resourceID; + handle = VMCI_MAKE_HANDLE(contextID, currentRID); + resourceID++; + if (UNLIKELY(resourceID == VMCI_INVALID_ID)) { + /* + * Skip the reserved rids. + */ + + resourceID = VMCI_RESERVED_RESOURCE_ID_MAX + 1; + } + VMCI_ReleaseLock(&resourceIdLock, flags); + foundRID = !VMCIHashTable_EntryExists(resourceTable, handle); + } while (!foundRID && resourceID != oldRID); + + if (UNLIKELY(!foundRID)) { + return VMCI_INVALID_ID; + } else { + return currentRID; + } +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResource_Add -- + * + * Results: + * VMCI_SUCCESS if successful, error code if not. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------------- + */ + +int +VMCIResource_Add(VMCIResource *resource, // IN + VMCIResourceType resourceType, // IN + VMCIHandle resourceHandle, // IN + VMCIResourceFreeCB containerFreeCB, // IN + void *containerObject) // IN +{ + int result; + + ASSERT(resource); + + if (VMCI_HANDLE_EQUAL(resourceHandle, VMCI_INVALID_HANDLE)) { + VMCI_DEBUG_LOG(4, (LGPFX"Invalid arguments resource 0x%x:0x%x.\n", + resourceHandle.context, resourceHandle.resource)); + return VMCI_ERROR_INVALID_ARGS; + } + + VMCIHashTable_InitEntry(&resource->hashEntry, resourceHandle); + resource->type = resourceType; + resource->containerFreeCB = containerFreeCB; + resource->containerObject = containerObject; + resource->handle = resourceHandle; + + /* Add resource to hashtable. */ + result = VMCIHashTable_AddEntry(resourceTable, &resource->hashEntry); + if (result != VMCI_SUCCESS) { + VMCI_DEBUG_LOG(4, (LGPFX"Failed to add entry to hash table.\n")); + return result; + } + + return result; +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResource_Remove -- + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------------- + */ + +void +VMCIResource_Remove(VMCIHandle resourceHandle, // IN: + VMCIResourceType resourceType) // IN: +{ + VMCIResource *resource = VMCIResource_Get(resourceHandle, resourceType); + if (resource == NULL) { + return; + } + + /* Remove resource from hashtable. */ + VMCIHashTable_RemoveEntry(resourceTable, &resource->hashEntry); + + VMCIResource_Release(resource); + /* resource could be freed by now. */ +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResource_Get -- + * + * Results: + * Resource is successful. Otherwise NULL. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------------- + */ + +VMCIResource * +VMCIResource_Get(VMCIHandle resourceHandle, // IN + VMCIResourceType resourceType) // IN +{ + VMCIResource *resource; + VMCIHashEntry *entry = VMCIHashTable_GetEntry(resourceTable, resourceHandle); + if (entry == NULL) { + return NULL; + } + resource = RESOURCE_CONTAINER(entry, VMCIResource, hashEntry); + if ((resourceType == VMCI_RESOURCE_TYPE_ANY) || (resource->type == resourceType)) { + return resource; + } + VMCIHashTable_ReleaseEntry(resourceTable, entry); + return NULL; +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResourceDoRemove -- + * + * Deallocates data structures associated with the given resource + * and invoke any call back registered for the resource. + * + * Results: + * None. + * + * Side effects: + * May deallocate memory and invoke a callback for the removed resource. + * + *----------------------------------------------------------------------------------- + */ + +static void INLINE +VMCIResourceDoRemove(VMCIResource *resource) +{ + ASSERT(resource); + + if (resource->containerFreeCB) { + resource->containerFreeCB(resource->containerObject); + /* Resource has been freed don't dereference it. */ + } +} + + +/* + *----------------------------------------------------------------------------------- + * + * VMCIResource_Release -- + * + * Results: + * None. + * + * Side effects: + * resource's containerFreeCB will get called if last reference. + * + *----------------------------------------------------------------------------------- + */ + +int +VMCIResource_Release(VMCIResource *resource) +{ + int result; + + ASSERT(resource); + + result = VMCIHashTable_ReleaseEntry(resourceTable, &resource->hashEntry); + if (result == VMCI_SUCCESS_ENTRY_DEAD) { + VMCIResourceDoRemove(resource); + } + + /* + * We propagate the information back to caller in case it wants to know + * whether entry was freed. + */ + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * VMCIResource_Sync -- + * + * Use this as a synchronization point when setting globals, for example, + * during device shutdown. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +VMCIResource_Sync(void) +{ + VMCIHashTable_Sync(resourceTable); +} diff --git a/open-vm-tools/modules/linux/vmci/vmciResource.h b/open-vm-tools/modules/linux/vmci/vmciResource.h new file mode 100644 index 000000000..d23f6c677 --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciResource.h @@ -0,0 +1,78 @@ +/********************************************************* + * Copyright (C) 2006 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 + * + *********************************************************/ + +/* + * vmciResource.h -- + * + * VMCI Resource Access Control API. + */ + +#ifndef _VMCI_RESOURCE_H_ +#define _VMCI_RESOURCE_H_ + +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_defs.h" +#include "vmci_kernel_if.h" +#include "vmciHashtable.h" +#include "vmciContext.h" + +#define RESOURCE_CONTAINER(ptr, type, member) \ + ((type *)((char *)(ptr) - offsetof(type, member))) + +typedef void(*VMCIResourceFreeCB)(void *resource); + +typedef enum { + VMCI_RESOURCE_TYPE_ANY, + VMCI_RESOURCE_TYPE_API, + VMCI_RESOURCE_TYPE_GROUP, + VMCI_RESOURCE_TYPE_DATAGRAM, + VMCI_RESOURCE_TYPE_DOORBELL, +} VMCIResourceType; + +typedef struct VMCIResource { + VMCIHashEntry hashEntry; + VMCIResourceType type; + VMCIResourceFreeCB containerFreeCB; // Callback to free container + // object when refCount is 0. + void *containerObject; // Container object reference. + VMCIHandle handle; +} VMCIResource; + + +int VMCIResource_Init(void); +void VMCIResource_Exit(void); +void VMCIResource_Sync(void); + +VMCIId VMCIResource_GetID(VMCIId contextID); + +int VMCIResource_Add(VMCIResource *resource, VMCIResourceType resourceType, + VMCIHandle resourceHandle, + VMCIResourceFreeCB containerFreeCB, void *containerObject); +void VMCIResource_Remove(VMCIHandle resourceHandle, + VMCIResourceType resourceType); +VMCIResource *VMCIResource_Get(VMCIHandle resourceHandle, + VMCIResourceType resourceType); +int VMCIResource_Release(VMCIResource *resource); + + +#endif // _VMCI_RESOURCE_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciRoute.c b/open-vm-tools/modules/linux/vmci/vmciRoute.c new file mode 100644 index 000000000..0392c54cb --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciRoute.c @@ -0,0 +1,270 @@ +/********************************************************* + * Copyright (C) 2011 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 + * + *********************************************************/ + +/* + * vmciRoute.c -- + * + * Implementation of VMCI routing rules. + */ + +#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 "vmciContext.h" +#include "vmciKernelAPI.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 "VMCIRoute: " + + +/* + *------------------------------------------------------------------------------ + * + * VMCI_Route -- + * + * Make a routing decision for the given source and destination handles. + * This will try to determine the route using the handles and the available + * devices. + * + * Result: + * A VMCIRoute value. + * + * Side effects: + * Sets the source context if it is invalid. + * + *------------------------------------------------------------------------------ + */ + +int +VMCI_Route(VMCIHandle *src, // IN/OUT + const VMCIHandle *dst, // IN + Bool fromGuest, // IN + VMCIRoute *route) // OUT +{ + Bool hasHostDevice; + Bool hasGuestDevice; + + ASSERT(src); + ASSERT(dst); + ASSERT(route); + + *route = VMCI_ROUTE_NONE; + + /* + * "fromGuest" is only ever set to TRUE by IOCTL_VMCI_DATAGRAM_SEND (or by + * the vmkernel equivalent), which comes from the VMX, so we know it is + * coming from a guest. + */ + + /* + * To avoid inconsistencies, test these once. We will test them again + * when we do the actual send to ensure that we do not touch a non-existent + * device. + */ + + hasHostDevice = VMCI_HasHostDevice(); + hasGuestDevice = VMCI_HasGuestDevice(); + + /* Must have a valid destination context. */ + if (VMCI_INVALID_ID == dst->context) { + return VMCI_ERROR_INVALID_ARGS; + } + + /* + * If we are acting as a host and the datagram is from or for a well-known + * context (which also means it must have been passed down from a guest), + * then we can assume it is intended for a guest on this host. + */ + + if (fromGuest && hasHostDevice && + (VMCI_WELL_KNOWN_CONTEXT_ID == src->context || + VMCI_WELL_KNOWN_CONTEXT_ID == dst->context)) { + *route = VMCI_ROUTE_HOST_TO_GUEST; + return VMCI_SUCCESS; + } + + /* Anywhere to hypervisor. */ + if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) { + /* + * If this message already came from a guest then we cannot send it + * to the hypervisor. It must come from a local client. + */ + + if (fromGuest) { + return VMCI_ERROR_DST_UNREACHABLE; + } + + /* We must be acting as a guest in order to send to the hypervisor. */ + if (!hasGuestDevice) { + return VMCI_ERROR_DEVICE_NOT_FOUND; + } + + /* And we cannot send if the source is the host context. */ + if (VMCI_HOST_CONTEXT_ID == src->context) { + return VMCI_ERROR_INVALID_ARGS; + } + + /* Send from local client down to the hypervisor. */ + *route = VMCI_ROUTE_GUEST_TO_HYPERVISOR; + return VMCI_SUCCESS; + } + + /* Anywhere to local client on host. */ + if (VMCI_HOST_CONTEXT_ID == dst->context) { + /* + * If it is not from a guest but we are acting as a guest, then we need + * to send it down to the host. Note that if we are also acting as a + * host then this will prevent us from sending from local client to + * local client, but we accept that restriction as a way to remove + * any ambiguity from the host context. + */ + + if (!fromGuest && hasGuestDevice) { + /* If no source context then use the current. */ + if (VMCI_INVALID_ID == src->context) { + src->context = VMCI_GetContextID(); + } + + /* Send it from local client down to the host. */ + *route = VMCI_ROUTE_GUEST_TO_HOST; + return VMCI_SUCCESS; + } + + /* + * Otherwise we already received it from a guest and it is destined + * for a local client on this host, or it is from another local client + * on this host. We must be acting as a host to service it. + */ + + if (!hasHostDevice) { + return VMCI_ERROR_DEVICE_NOT_FOUND; + } + + if (VMCI_INVALID_ID == src->context) { + /* + * If it came from a guest then it must have a valid context. + * Otherwise we can use the host context. + */ + + if (fromGuest) { + return VMCI_ERROR_INVALID_ARGS; + } + src->context = VMCI_HOST_CONTEXT_ID; + } + + /* Route to local client. */ + *route = VMCI_ROUTE_HOST_TO_SELF; + return VMCI_SUCCESS; + } + + /* If we are acting as a host then this might be destined for a guest. */ + if (hasHostDevice) { + /* It will have a context if it is meant for a guest. */ + VMCIContext *context = VMCIContext_Get(dst->context); + if (NULL != context) { + if (VMCI_INVALID_ID == src->context) { + /* + * If it came from a guest then it must have a valid context. + * Otherwise we can use the host context. + */ + + if (fromGuest) { + return VMCI_ERROR_INVALID_ARGS; + } + src->context = VMCI_HOST_CONTEXT_ID; + } + + /* Pass it up to the guest. */ + *route = VMCI_ROUTE_HOST_TO_GUEST; + return VMCI_SUCCESS; + } + } + + /* + * We must be a guest trying to send to another guest, which means we + * need to send it down to the host. + */ + + if (!hasGuestDevice) { + return VMCI_ERROR_DEVICE_NOT_FOUND; + } + + /* If no source context then use the current context. */ + if (VMCI_INVALID_ID == src->context) { + src->context = VMCI_GetContextID(); + } + + /* + * Send it from local client down to the host, which will route it to + * the other guest for us. + */ + + *route = VMCI_ROUTE_GUEST_TO_HOST; + return VMCI_SUCCESS; +} + + +/* + *------------------------------------------------------------------------------ + * + * VMCI_RouteString -- + * + * Get a string for the given route. + * + * Result: + * A string representing the route, if the route is valid, otherwise an + * empty string. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------------ + */ + +const char * +VMCI_RouteString(VMCIRoute route) // IN +{ + const char *vmciRouteStrings[] = { + "none", + "host to self", + "host to guest", + "guest to host/hypervisor", + }; + if (route >= VMCI_ROUTE_NONE && route <= VMCI_ROUTE_GUEST_TO_HYPERVISOR) { + return vmciRouteStrings[route]; + } + return ""; +} diff --git a/open-vm-tools/modules/linux/vmci/vmciRoute.h b/open-vm-tools/modules/linux/vmci/vmciRoute.h new file mode 100644 index 000000000..b75f78a2c --- /dev/null +++ b/open-vm-tools/modules/linux/vmci/vmciRoute.h @@ -0,0 +1,51 @@ +/********************************************************* + * Copyright (C) 2011 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 + * + *********************************************************/ + +/* + * vmciRoute.h -- + * + * VMCI Routing. + */ + +#ifndef _VMCI_ROUTE_H_ +#define _VMCI_ROUTE_H_ + +#define INCLUDE_ALLOW_VMMON +#define INCLUDE_ALLOW_VMCORE +#define INCLUDE_ALLOW_MODULE +#define INCLUDE_ALLOW_VMKERNEL +#include "includeCheck.h" + +#include "vmci_defs.h" + + +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 +} VMCIRoute; + + +int VMCI_Route(VMCIHandle *src, const VMCIHandle *dst, Bool fromGuest, + VMCIRoute *route); +const char *VMCI_RouteString(VMCIRoute route); + + +#endif // _VMCI_ROUTE_H_ diff --git a/open-vm-tools/modules/linux/vmci/vmciUtil.c b/open-vm-tools/modules/linux/vmci/vmciUtil.c index d1135c6be..3b2e75c1d 100644 --- a/open-vm-tools/modules/linux/vmci/vmciUtil.c +++ b/open-vm-tools/modules/linux/vmci/vmciUtil.c @@ -25,31 +25,15 @@ #ifdef __linux__ # include "driver-config.h" - # define EXPORT_SYMTAB - # include # include "compat_kernel.h" # include "compat_slab.h" # include "compat_interrupt.h" -#elif defined(_WIN32) -# ifndef WINNT_DDK -# error This file only works with the NT ddk -# endif // WINNT_DDK -# include -#elif defined(SOLARIS) -# include -# include -# include -#elif defined(__APPLE__) -# include -#else -#error "platform not supported." -#endif //linux +#endif -#define LGPFX "VMCIUtil: " - -#include "vmware.h" +#include "vmci_kernel_if.h" +#include "vm_assert.h" #include "vm_atomic.h" #include "vmci_defs.h" #include "vmci_kernel_if.h" @@ -60,6 +44,8 @@ #include "vmciEvent.h" #include "vmciKernelAPI.h" +#define LGPFX "VMCIUtil: " + static void VMCIUtilCidUpdate(VMCIId subID, VMCI_EventData *eventData, void *clientData); @@ -532,7 +518,7 @@ VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN dg->dst.resource == VMCI_EVENT_HANDLER) { result = VMCIEvent_Dispatch(dg); } else { - result = VMCIDatagram_Dispatch(dg->src.context, dg); + result = VMCIDatagram_InvokeGuestHandler(dg); } if (result < VMCI_SUCCESS) { VMCI_DEBUG_LOG(4, (LGPFX"Datagram with resource %d failed with " @@ -576,79 +562,3 @@ VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN } } } - - -/* - *---------------------------------------------------------------------- - * - * VMCIContext_GetPrivFlags -- - * - * Provided for compatibility with the host VMCI API. - * - * Results: - * Always returns VMCI_NO_PRIVILEGE_FLAGS. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -VMCI_EXPORT_SYMBOL(VMCIContext_GetPrivFlags) -VMCIPrivilegeFlags -VMCIContext_GetPrivFlags(VMCIId contextID) // IN -{ - return VMCI_NO_PRIVILEGE_FLAGS; -} - - -/* - *---------------------------------------------------------------------- - * - * VMCI_ContextID2HostVmID -- - * - * Provided for compatibility with the host VMCI API. - * - * Results: - * Returns VMCI_ERROR_UNAVAILABLE. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -VMCI_EXPORT_SYMBOL(VMCI_ContextID2HostVmID) -int -VMCI_ContextID2HostVmID(VMCIId contextID, // IN - void *hostVmID, // OUT - size_t hostVmIDLen) // IN -{ - return VMCI_ERROR_UNAVAILABLE; -} - - -/* - *---------------------------------------------------------------------- - * - * VMCI_IsContextOwner -- - * - * Provided for compatibility with the host VMCI API. - * - * Results: - * Returns VMCI_ERROR_UNAVAILABLE. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -VMCI_EXPORT_SYMBOL(VMCI_IsContextOwner) -int -VMCI_IsContextOwner(VMCIId contextID, // IN - void *hostUser) // IN -{ - return VMCI_ERROR_UNAVAILABLE; -} - diff --git a/open-vm-tools/modules/linux/vmci/vmci_drv.c b/open-vm-tools/modules/linux/vmci/vmci_drv.c index cb6ffdcd7..681d73670 100644 --- a/open-vm-tools/modules/linux/vmci/vmci_drv.c +++ b/open-vm-tools/modules/linux/vmci/vmci_drv.c @@ -45,11 +45,13 @@ #include "vmci_infrastructure.h" #include "vmci_iocontrols.h" #include "vmci_version.h" +#include "vmciContext.h" #include "vmciDatagram.h" #include "vmciEvent.h" #include "vmciInt.h" #include "vmciNotifications.h" #include "vmciQueuePairInt.h" +#include "vmciResource.h" #include "vmciUtil.h" #define LGPFX "VMCI: " @@ -412,6 +414,8 @@ vmci_probe_device(struct pci_dev *pdev, // IN: vmci PCI device * components, and it may be invoked once request_irq() has * registered the handler (as the irq line may be shared). */ + VMCIResource_Init(); + VMCIContext_Init(); VMCIDatagram_Init(); VMCIEvent_Init(); VMCIUtil_Init(); @@ -481,6 +485,8 @@ vmci_probe_device(struct pci_dev *pdev, // IN: vmci PCI device VMCINotifications_Exit(); VMCIUtil_Exit(); VMCIEvent_Exit(); + VMCIContext_Exit(); + VMCIResource_Exit(); if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSIX) { pci_disable_msix(pdev); } else if (vmci_dev.intr_type == VMCI_INTR_TYPE_MSI) { @@ -561,6 +567,9 @@ vmci_remove_device(struct pci_dev* pdev) vfree(notification_bitmap); } + VMCIContext_Exit(); + VMCIResource_Exit(); + printk(KERN_INFO "Unregistered vmci device.\n"); compat_mutex_unlock(&dev->lock); diff --git a/open-vm-tools/modules/linux/vmci/vmci_version.h b/open-vm-tools/modules/linux/vmci/vmci_version.h index 91d6cb443..8ca2ed98c 100644 --- a/open-vm-tools/modules/linux/vmci/vmci_version.h +++ b/open-vm-tools/modules/linux/vmci/vmci_version.h @@ -25,8 +25,8 @@ #ifndef _VMCI_VERSION_H_ #define _VMCI_VERSION_H_ -#define VMCI_DRIVER_VERSION 9.1.6.0 -#define VMCI_DRIVER_VERSION_COMMAS 9,1,6,0 -#define VMCI_DRIVER_VERSION_STRING "9.1.6.0" +#define VMCI_DRIVER_VERSION 9.1.7.0 +#define VMCI_DRIVER_VERSION_COMMAS 9,1,7,0 +#define VMCI_DRIVER_VERSION_STRING "9.1.7.0" #endif /* _VMCI_VERSION_H_ */