From: "Andrew Stiegmann (stieg)" <astiegmann@vmware.com>
To: linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org
Cc: pv-drivers@vmware.com, vm-crosstalk@vmware.com,
"Andrew Stiegmann (stieg)" <astiegmann@vmware.com>,
cschamp@vmware.com, gregkh@linuxfoundation.org
Subject: [vmw_vmci 01/11] Apply VMCI context code
Date: Thu, 26 Jul 2012 16:39:30 -0700 [thread overview]
Message-ID: <1343345980-32397-2-git-send-email-astiegmann@vmware.com> (raw)
In-Reply-To: <1343345980-32397-1-git-send-email-astiegmann@vmware.com>
Context code maintains state for vmci and allows the driver
to communicate with multiple VMs.
Signed-off-by: Andrew Stiegmann (stieg) <astiegmann@vmware.com>
---
drivers/misc/vmw_vmci/vmci_context.c | 1269 ++++++++++++++++++++++++++++++++++
drivers/misc/vmw_vmci/vmci_context.h | 161 +++++
2 files changed, 1430 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/vmw_vmci/vmci_context.c
create mode 100644 drivers/misc/vmw_vmci/vmci_context.h
diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c
new file mode 100644
index 0000000..46faf10
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmci_context.c
@@ -0,0 +1,1269 @@
+/*
+ * VMware VMCI Driver
+ *
+ * Copyright (C) 2012 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
+ */
+
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/vmw_vmci_api.h>
+#include <linux/vmw_vmci_defs.h>
+
+#include "vmci_common_int.h"
+#include "vmci_context.h"
+#include "vmci_datagram.h"
+#include "vmci_doorbell.h"
+#include "vmci_driver.h"
+#include "vmci_event.h"
+#include "vmci_queue_pair.h"
+
+/*
+ * List of current VMCI contexts.
+ */
+static struct {
+ struct list_head head;
+ spinlock_t lock;
+ spinlock_t firingLock;
+} ctx_list;
+
+
+static void ctx_signal_notify(struct vmci_ctx *context)
+{
+ if (context->notify)
+ *context->notify = true;
+}
+
+static void ctx_clear_notify(struct vmci_ctx *context)
+{
+ if (context->notify)
+ *context->notify = false;
+}
+
+/*
+ * If nothing requires the attention of the guest, clears both
+ * notify flag and call.
+ */
+static void ctx_clear_notify_call(struct vmci_ctx *context)
+{
+ if (context->pendingDatagrams == 0 &&
+ vmci_handle_arr_get_size(context->pendingDoorbellArray) == 0)
+ ctx_clear_notify(context);
+}
+
+/*
+ * Check if a context with the specified context ID exists.
+ * Assumes the ctx_list.lock is held.
+ */
+static bool ctx_exists_locked(uint32_t cid)
+{
+ struct vmci_ctx *context;
+
+ list_for_each_entry(context, &ctx_list.head, listItem) {
+ if (context->cid == cid)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Sets the context's notify flag iff datagrams are pending for this
+ * context. Called from vmci_setup_notify().
+ */
+void vmci_ctx_check_signal_notify(struct vmci_ctx *context)
+{
+ ASSERT(context);
+
+ spin_lock(&ctx_list.lock);
+ if (context->pendingDatagrams)
+ ctx_signal_notify(context);
+ spin_unlock(&ctx_list.lock);
+}
+
+int __init vmci_ctx_init(void)
+{
+ INIT_LIST_HEAD(&ctx_list.head);
+
+ spin_lock_init(&ctx_list.lock);
+ spin_lock_init(&ctx_list.firingLock);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Allocates and initializes a VMCI context.
+ */
+int vmci_ctx_init_ctx(uint32_t cid,
+ uint32_t privFlags,
+ uintptr_t eventHnd,
+ int userVersion,
+ uid_t *user, struct vmci_ctx **outContext)
+{
+ struct vmci_ctx *context;
+ int result;
+
+ if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
+ pr_devel("Invalid flag (flags=0x%x) for VMCI context.",
+ privFlags);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (userVersion == 0)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = kzalloc(sizeof *context, GFP_KERNEL);
+ if (context == NULL) {
+ pr_warn("Failed to allocate memory for VMCI context.");
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ INIT_LIST_HEAD(&context->listItem);
+ INIT_LIST_HEAD(&context->datagramQueue);
+
+ context->userVersion = userVersion;
+
+ context->queuePairArray = vmci_handle_arr_create(0);
+ if (!context->queuePairArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->doorbellArray = vmci_handle_arr_create(0);
+ if (!context->doorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->pendingDoorbellArray = vmci_handle_arr_create(0);
+ if (!context->pendingDoorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->notifierArray = vmci_handle_arr_create(0);
+ if (context->notifierArray == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ spin_lock_init(&context->lock);
+
+ atomic_set(&context->refCount, 1);
+
+ /* Inititialize host-specific VMCI context. */
+ init_waitqueue_head(&context->hostContext.waitQueue);
+
+ 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.
+ */
+ spin_lock(&ctx_list.lock);
+ ASSERT(cid != VMCI_INVALID_ID);
+ while (ctx_exists_locked(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(!ctx_exists_locked(cid));
+ context->cid = cid;
+ context->validUser = user != NULL;
+ if (context->validUser)
+ context->user = *user;
+ list_add(&context->listItem, &ctx_list.head);
+ spin_unlock(&ctx_list.lock);
+
+ context->notify = NULL;
+ context->notifyPage = NULL;
+
+ *outContext = context;
+ return VMCI_SUCCESS;
+
+error:
+ if (context->notifierArray)
+ vmci_handle_arr_destroy(context->notifierArray);
+ if (context->queuePairArray)
+ vmci_handle_arr_destroy(context->queuePairArray);
+ if (context->doorbellArray)
+ vmci_handle_arr_destroy(context->doorbellArray);
+ if (context->pendingDoorbellArray)
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ kfree(context);
+ return result;
+}
+
+/*
+ * Dequeue VMCI context.
+ */
+void vmci_ctx_release_ctx(struct vmci_ctx *context)
+{
+ spin_lock(&ctx_list.lock);
+ list_del(&context->listItem);
+ spin_unlock(&ctx_list.lock);
+
+ vmci_ctx_release(context);
+}
+
+/*
+ * Fire notification for all contexts interested in given cid.
+ */
+static int ctx_fire_notification(uint32_t contextID,
+ uint32_t privFlags)
+{
+ uint32_t i, arraySize;
+ struct vmci_ctx *subCtx;
+ struct vmci_handle_arr *subscriberArray;
+ struct vmci_handle contextHandle =
+ vmci_make_handle(contextID, VMCI_EVENT_HANDLER);
+
+ /*
+ * We create an array to hold the subscribers we find when
+ * scanning through all contexts.
+ */
+ subscriberArray = vmci_handle_arr_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.
+ */
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&ctx_list.lock);
+ list_for_each_entry(subCtx, &ctx_list.head, listItem) {
+ /*
+ * We only deliver notifications of the removal of
+ * contexts, if the two contexts are allowed to
+ * interact.
+ */
+ if (vmci_handle_arr_has_entry
+ (subCtx->notifierArray, contextHandle)
+ && !vmci_deny_interaction(privFlags, subCtx->privFlags)) {
+ vmci_handle_arr_append_entry(&subscriberArray,
+ vmci_make_handle
+ (subCtx->cid,
+ VMCI_EVENT_HANDLER));
+ }
+ }
+ spin_unlock(&ctx_list.lock);
+ spin_unlock(&ctx_list.firingLock);
+
+ /* Fire event to all subscribers. */
+ arraySize = vmci_handle_arr_get_size(subscriberArray);
+ for (i = 0; i < arraySize; i++) {
+ int result;
+ struct vmci_event_msg *eMsg;
+ struct vmci_event_payld_ctx *evPayload;
+ char buf[sizeof *eMsg + sizeof *evPayload];
+
+ eMsg = (struct vmci_event_msg *)buf;
+
+ /* Clear out any garbage. */
+ memset(eMsg, 0, sizeof *eMsg + sizeof *evPayload);
+ eMsg->hdr.dst = vmci_handle_arr_get_entry(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 = vmci_event_data_payload(&eMsg->eventData);
+ evPayload->contextID = contextID;
+
+ result = vmci_dg_dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
+ (struct vmci_dg *)
+ eMsg, false);
+ if (result < VMCI_SUCCESS) {
+ pr_devel("Failed to enqueue event datagram " \
+ "(type=%d) for context (ID=0x%x).",
+ eMsg->eventData.event, eMsg->hdr.dst.context);
+ /* We continue to enqueue on next subscriber. */
+ }
+ }
+ vmci_handle_arr_destroy(subscriberArray);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * 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.
+ */
+int vmci_ctx_pending_dgs(uint32_t cid,
+ uint32_t *pending)
+{
+ struct vmci_ctx *context;
+
+ context = vmci_ctx_get(cid);
+ if (context == NULL)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ spin_lock(&context->lock);
+ if (pending)
+ *pending = context->pendingDatagrams;
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Queues a VMCI datagram for the appropriate target VM context.
+ */
+int vmci_ctx_enqueue_dg(uint32_t cid,
+ struct vmci_dg *dg)
+{
+ struct vmci_dg_queue_entry *dqEntry;
+ struct vmci_ctx *context;
+ struct vmci_handle 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 = vmci_ctx_get(cid);
+ if (context == NULL) {
+ pr_devel("Invalid context (ID=0x%x).", cid);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /* Allocate guest call entry and add it to the target VM's queue. */
+ dqEntry = kmalloc(sizeof *dqEntry, GFP_KERNEL);
+ if (dqEntry == NULL) {
+ pr_warn("Failed to allocate memory for datagram.");
+ vmci_ctx_release(context);
+ return VMCI_ERROR_NO_MEM;
+ }
+ dqEntry->dg = dg;
+ dqEntry->dgSize = vmciDgSize;
+ dgSrc = dg->src;
+ INIT_LIST_HEAD(&dqEntry->listItem);
+
+ spin_lock(&context->lock);
+
+ /*
+ * 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)) {
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+ kfree(dqEntry);
+ pr_devel("Context (ID=0x%x) receive queue is full.",
+ cid);
+ return VMCI_ERROR_NO_RESOURCES;
+ }
+
+ list_add(&dqEntry->listItem, &context->datagramQueue);
+ context->pendingDatagrams++;
+ context->datagramQueueSize += vmciDgSize;
+ ctx_signal_notify(context);
+ wake_up(&context->hostContext.waitQueue);
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return vmciDgSize;
+}
+
+/*
+ * Verifies whether a context with the specified context ID exists.
+ */
+bool vmci_ctx_exists(uint32_t cid)
+{
+ bool rv;
+
+ spin_lock(&ctx_list.lock);
+ rv = ctx_exists_locked(cid);
+ spin_unlock(&ctx_list.lock);
+ return rv;
+}
+
+/*
+ * Retrieves VMCI context corresponding to the given cid.
+ */
+struct vmci_ctx *vmci_ctx_get(uint32_t cid)
+{
+ struct vmci_ctx *context = NULL;
+
+ if (cid == VMCI_INVALID_ID)
+ return NULL;
+
+ spin_lock(&ctx_list.lock);
+ list_for_each_entry(context, &ctx_list.head, 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;
+ }
+ }
+ spin_unlock(&ctx_list.lock);
+
+ return (context && context->cid == cid) ? context : NULL;
+}
+
+/*
+ * 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.
+ */
+static void ctx_free_ctx(struct vmci_ctx *context)
+{
+ struct list_head *curr;
+ struct list_head *next;
+ struct vmci_dg_queue_entry *dqEntry;
+ struct vmci_handle tempHandle;
+
+ /*
+ * Fire event to all contexts interested in knowing this
+ * context is dying.
+ */
+ ctx_fire_notification(context->cid, context->privFlags);
+
+ /*
+ * 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 = vmci_handle_arr_get_entry(context->queuePairArray, 0);
+ while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) {
+ if (vmci_qp_broker_detach(tempHandle, context) < VMCI_SUCCESS) {
+ /*
+ * When vmci_qp_broker_detach() succeeds it
+ * removes the handle from the array. If
+ * detach fails, we must remove the handle
+ * ourselves.
+ */
+ vmci_handle_arr_remove_entry(context->queuePairArray,
+ tempHandle);
+ }
+ tempHandle =
+ vmci_handle_arr_get_entry(context->queuePairArray, 0);
+ }
+
+ /*
+ * It is fine to destroy this without locking the callQueue, as
+ * this is the only thread having a reference to the context.
+ */ list_for_each_safe(curr, next, &context->datagramQueue) {
+ dqEntry =
+ list_entry(curr, struct vmci_dg_queue_entry, listItem);
+ list_del(curr);
+ ASSERT(dqEntry && dqEntry->dg);
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ kfree(dqEntry->dg);
+ kfree(dqEntry);
+ }
+
+ vmci_handle_arr_destroy(context->notifierArray);
+ vmci_handle_arr_destroy(context->queuePairArray);
+ vmci_handle_arr_destroy(context->doorbellArray);
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ vmci_ctx_unset_notify(context);
+ kfree(context);
+}
+
+/*
+ * 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.
+ */
+void vmci_ctx_release(struct vmci_ctx *context)
+{
+ ASSERT(context);
+ if (atomic_dec_and_test(&context->refCount))
+ ctx_free_ctx(context);
+}
+
+/*
+ * 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.
+ */int vmci_ctx_dequeue_dg(struct vmci_ctx *context,
+ size_t *maxSize,
+ struct vmci_dg **dg)
+{
+ struct vmci_dg_queue_entry *dqEntry;
+ struct list_head *listItem;
+ int rv;
+
+ ASSERT(context && dg);
+
+ /* Dequeue the next datagram entry. */
+ spin_lock(&context->lock);
+ if (context->pendingDatagrams == 0) {
+ ctx_clear_notify_call(context);
+ spin_unlock(&context->lock);
+ pr_devel("No datagrams pending.");
+ return VMCI_ERROR_NO_MORE_DATAGRAMS;
+ }
+
+ listItem = context->datagramQueue.next;
+ ASSERT(!list_empty(&context->datagramQueue));
+
+ dqEntry = list_entry(listItem, struct vmci_dg_queue_entry, listItem);
+ ASSERT(dqEntry->dg);
+
+ /* Check size of caller's buffer. */
+ if (*maxSize < dqEntry->dgSize) {
+ *maxSize = dqEntry->dgSize;
+ spin_unlock(&context->lock);
+ pr_devel("Caller's buffer should be at least " \
+ "(size=%u bytes).", (uint32_t) *maxSize);
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ list_del(listItem);
+ context->pendingDatagrams--;
+ context->datagramQueueSize -= dqEntry->dgSize;
+ if (context->pendingDatagrams == 0) {
+ ctx_clear_notify_call(context);
+ rv = VMCI_SUCCESS;
+ } else {
+ /*
+ * Return the size of the next datagram.
+ */
+ struct vmci_dg_queue_entry *nextEntry;
+
+ listItem = context->datagramQueue.next;
+ ASSERT(!list_empty(&context->datagramQueue));
+ nextEntry = list_entry(listItem, struct vmci_dg_queue_entry,
+ 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;
+ }
+ spin_unlock(&context->lock);
+
+ /* Caller must free datagram. */
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ *dg = dqEntry->dg;
+ dqEntry->dg = NULL;
+ kfree(dqEntry);
+
+ return rv;
+}
+
+/*
+ * Reverts actions set up by vmci_setup_notify(). Unmaps and unlocks the
+ * page mapped/locked by vmci_setup_notify().
+ */
+void vmci_ctx_unset_notify(struct vmci_ctx *context)
+{
+ struct page *notifyPage = context->notifyPage;
+
+ if (!notifyPage)
+ return;
+
+ context->notify = NULL;
+ context->notifyPage = NULL;
+ kunmap(notifyPage);
+ put_page(notifyPage);
+
+}
+
+uint32_t vmci_ctx_get_id(struct vmci_ctx *context)
+{
+ if (!context)
+ return VMCI_INVALID_ID;
+
+ ASSERT(context->cid != VMCI_INVALID_ID);
+ return context->cid;
+}
+
+/*
+ * Add remoteCID to list of contexts current contexts wants
+ * notifications from/about.
+ */
+int vmci_ctx_add_notification(uint32_t contextID,
+ uint32_t remoteCID)
+{
+ int result = VMCI_ERROR_ALREADY_EXISTS;
+ struct vmci_handle notifierHandle;
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ if (VMCI_CONTEXT_IS_VM(contextID) && VMCI_CONTEXT_IS_VM(remoteCID)) {
+ pr_devel("Context removed notifications for other VMs not " \
+ "supported (src=0x%x, remote=0x%x).",
+ contextID, remoteCID);
+ result = VMCI_ERROR_DST_UNREACHABLE;
+ goto out;
+ }
+
+ if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+
+ notifierHandle = vmci_make_handle(remoteCID, VMCI_EVENT_HANDLER);
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&context->lock);
+ if (!vmci_handle_arr_has_entry(context->notifierArray,
+ notifierHandle)) {
+ vmci_handle_arr_append_entry(&context->notifierArray,
+ notifierHandle);
+ result = VMCI_SUCCESS;
+ }
+ spin_unlock(&context->lock);
+ spin_unlock(&ctx_list.firingLock);
+
+ out:
+ vmci_ctx_release(context);
+ return result;
+}
+
+/*
+ * Remove remoteCID from current context's list of contexts it is
+ * interested in getting notifications from/about.
+ */
+int vmci_ctx_remove_notification(uint32_t contextID,
+ uint32_t remoteCID)
+{
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+ struct vmci_handle tmpHandle;
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&context->lock);
+ tmpHandle = vmci_make_handle(remoteCID, VMCI_EVENT_HANDLER);
+ tmpHandle = vmci_handle_arr_remove_entry(context->notifierArray,
+ tmpHandle);
+ spin_unlock(&context->lock);
+ spin_unlock(&ctx_list.firingLock);
+ vmci_ctx_release(context);
+
+ if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE))
+ return VMCI_ERROR_NOT_FOUND;
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Get current context's checkpoint state of given type.
+ */
+int vmci_ctx_get_chkpt_state(uint32_t contextID,
+ uint32_t cptType,
+ uint32_t *bufSize,
+ char **cptBufPtr)
+{
+ int i, result;
+ uint32_t arraySize, cptDataSize;
+ struct vmci_handle_arr *array;
+ struct vmci_ctx *context;
+ char *cptBuf;
+ bool getContextID;
+
+ ASSERT(bufSize && cptBufPtr);
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ if (cptType == VMCI_NOTIFICATION_CPT_STATE) {
+ ASSERT(context->notifierArray);
+ array = context->notifierArray;
+ getContextID = true;
+ } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) {
+ /*
+ * For compatibility with VMX'en with VM to VM communication, we
+ * always return zero wellknown handles.
+ */
+
+ *bufSize = 0;
+ *cptBufPtr = NULL;
+ result = VMCI_SUCCESS;
+ goto release;
+ } else if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ASSERT(context->doorbellArray);
+ array = context->doorbellArray;
+ getContextID = false;
+ } else {
+ pr_devel("Invalid cpt state (type=%d).", cptType);
+ result = VMCI_ERROR_INVALID_ARGS;
+ goto release;
+ }
+
+ arraySize = vmci_handle_arr_get_size(array);
+ if (arraySize > 0) {
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ cptDataSize =
+ arraySize * sizeof(struct dbell_cpt_state);
+ } else {
+ cptDataSize = arraySize * sizeof(uint32_t);
+ }
+
+ if (*bufSize < cptDataSize) {
+ *bufSize = cptDataSize;
+ result = VMCI_ERROR_MORE_DATA;
+ goto release;
+ }
+
+ cptBuf = kmalloc(cptDataSize, GFP_ATOMIC);
+
+ if (cptBuf == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto release;
+ }
+
+ for (i = 0; i < arraySize; i++) {
+ struct vmci_handle tmpHandle =
+ vmci_handle_arr_get_entry(array, i);
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ((struct dbell_cpt_state *)cptBuf)[i].handle =
+ tmpHandle;
+ } else {
+ ((uint32_t *) cptBuf)[i] =
+ getContextID ? tmpHandle.context :
+ tmpHandle.resource;
+ }
+ }
+ *bufSize = cptDataSize;
+ *cptBufPtr = cptBuf;
+ } else {
+ *bufSize = 0;
+ *cptBufPtr = NULL;
+ }
+ result = VMCI_SUCCESS;
+
+release:
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Set current context's checkpoint state of given type.
+ */
+int vmci_ctx_set_chkpt_state(uint32_t contextID,
+ uint32_t cptType,
+ uint32_t bufSize,
+ char *cptBuf)
+{
+ uint32_t i;
+ uint32_t currentID;
+ int result = VMCI_SUCCESS;
+ uint32_t numIDs = bufSize / sizeof(uint32_t);
+ ASSERT(cptBuf);
+
+ if (cptType == VMCI_WELLKNOWN_CPT_STATE && numIDs > 0) {
+ /*
+ * We would end up here if VMX with VM to VM communication
+ * attempts to restore a checkpoint with wellknown handles.
+ */
+ pr_warn("Attempt to restore checkpoint with obsolete " \
+ "wellknown handles.");
+ return VMCI_ERROR_OBSOLETE;
+ }
+
+ if (cptType != VMCI_NOTIFICATION_CPT_STATE) {
+ pr_devel("Invalid cpt state (type=%d).", cptType);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ for (i = 0; i < numIDs && result == VMCI_SUCCESS; i++) {
+ currentID = ((uint32_t *) cptBuf)[i];
+ result = vmci_ctx_add_notification(contextID, currentID);
+ if (result != VMCI_SUCCESS)
+ break;
+ }
+ if (result != VMCI_SUCCESS)
+ pr_devel("Failed to set cpt state (type=%d) " \
+ "(error=%d).", cptType, result);
+
+ return result;
+}
+
+/*
+ * 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
+ * vmci_ctx_rcv_notifications_release.
+ */
+int vmci_ctx_rcv_notifications_get(uint32_t contextID,
+ struct vmci_handle_arr **dbHandleArray,
+ struct vmci_handle_arr **qpHandleArray)
+{
+ struct vmci_ctx *context;
+ int result = VMCI_SUCCESS;
+
+ ASSERT(dbHandleArray && qpHandleArray);
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+
+ *dbHandleArray = context->pendingDoorbellArray;
+ context->pendingDoorbellArray = vmci_handle_arr_create(0);
+ if (!context->pendingDoorbellArray) {
+ context->pendingDoorbellArray = *dbHandleArray;
+ *dbHandleArray = NULL;
+ result = VMCI_ERROR_NO_MEM;
+ }
+ *qpHandleArray = NULL;
+
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Releases handle arrays with pending notifications previously
+ * retrieved using vmci_ctx_rcv_notifications_get. If the
+ * notifications were not successfully handed over to the guest,
+ * success must be false.
+ */
+void vmci_ctx_rcv_notifications_release(uint32_t contextID,
+ struct vmci_handle_arr *dbHandleArray,
+ struct vmci_handle_arr *qpHandleArray,
+ bool success)
+{
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+
+ if (!context) {
+ /*
+ * 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);
+ }
+
+ spin_lock(&context->lock);
+ if (!success) {
+ struct vmci_handle 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 = vmci_handle_arr_remove_tail(
+ context->pendingDoorbellArray);
+ while (!VMCI_HANDLE_INVALID(handle)) {
+ ASSERT(vmci_handle_arr_has_entry
+ (context->doorbellArray, handle));
+ if (!vmci_handle_arr_has_entry
+ (dbHandleArray, handle)) {
+ vmci_handle_arr_append_entry
+ (&dbHandleArray, handle);
+ }
+ handle = vmci_handle_arr_remove_tail(
+ context->pendingDoorbellArray);
+ }
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ context->pendingDoorbellArray = dbHandleArray;
+ dbHandleArray = NULL;
+ } else {
+ ctx_clear_notify_call(context);
+ }
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ if (dbHandleArray)
+ vmci_handle_arr_destroy(dbHandleArray);
+
+ if (qpHandleArray)
+ vmci_handle_arr_destroy(qpHandleArray);
+}
+
+/*
+ * Registers that a new doorbell handle has been allocated by the
+ * context. Only doorbell handles registered can be notified.
+ */
+int vmci_ctx_dbell_create(uint32_t contextID,
+ struct vmci_handle handle)
+{
+ struct vmci_ctx *context;
+ int result;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ if (!vmci_handle_arr_has_entry(context->doorbellArray, handle)) {
+ vmci_handle_arr_append_entry(&context->doorbellArray, handle);
+ result = VMCI_SUCCESS;
+ } else {
+ result = VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Unregisters a doorbell handle that was previously registered
+ * with vmci_ctx_dbell_create.
+ */
+int vmci_ctx_dbell_destroy(uint32_t contextID,
+ struct vmci_handle handle)
+{
+ struct vmci_ctx *context;
+ struct vmci_handle removedHandle;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ removedHandle =
+ vmci_handle_arr_remove_entry(context->doorbellArray, handle);
+ vmci_handle_arr_remove_entry(context->pendingDoorbellArray, handle);
+ spin_unlock(&context->lock);
+
+ vmci_ctx_release(context);
+
+ return VMCI_HANDLE_INVALID(removedHandle) ?
+ VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
+}
+
+/*
+ * Unregisters all doorbell handles that were previously
+ * registered with vmci_ctx_dbell_create.
+ */
+int vmci_ctx_dbell_destroy_all(uint32_t contextID)
+{
+ struct vmci_ctx *context;
+ struct vmci_handle handle;
+
+ if (contextID == VMCI_INVALID_ID)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ do {
+ struct vmci_handle_arr *arr = context->doorbellArray;
+ handle = vmci_handle_arr_remove_tail(arr);
+ } while (!VMCI_HANDLE_INVALID(handle));
+ do {
+ struct vmci_handle_arr *arr = context->pendingDoorbellArray;
+ handle = vmci_handle_arr_remove_tail(arr);
+ } while (!VMCI_HANDLE_INVALID(handle));
+ spin_unlock(&context->lock);
+
+ vmci_ctx_release(context);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * 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.
+ */
+int vmci_ctx_notify_dbell(uint32_t srcCID,
+ struct vmci_handle handle,
+ uint32_t srcPrivFlags)
+{
+ struct vmci_ctx *dstContext;
+ int result;
+
+ if (VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ /* Get the target VM's VMCI context. */
+ dstContext = vmci_ctx_get(handle.context);
+ if (dstContext == NULL) {
+ pr_devel("Invalid context (ID=0x%x).", handle.context);
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ if (srcCID != handle.context) {
+ uint32_t dstPrivFlags;
+
+ if (VMCI_CONTEXT_IS_VM(srcCID)
+ && VMCI_CONTEXT_IS_VM(handle.context)) {
+ pr_devel("Doorbell notification from VM to VM not " \
+ "supported (src=0x%x, dst=0x%x).", srcCID,
+ handle.context);
+ result = VMCI_ERROR_DST_UNREACHABLE;
+ goto out;
+ }
+
+ result = vmci_dbell_get_priv_flags(handle, &dstPrivFlags);
+ if (result < VMCI_SUCCESS) {
+ pr_warn("Failed to get privilege flags for " \
+ "destination (handle=0x%x:0x%x).",
+ handle.context, handle.resource);
+ goto out;
+ }
+
+ if (srcCID != VMCI_HOST_CONTEXT_ID ||
+ srcPrivFlags == VMCI_NO_PRIVILEGE_FLAGS) {
+ srcPrivFlags = VMCIContext_GetPrivFlags(srcCID);
+ }
+
+ if (vmci_deny_interaction(srcPrivFlags, dstPrivFlags)) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+ }
+
+ if (handle.context == VMCI_HOST_CONTEXT_ID) {
+ result = vmci_dbell_host_context_notify(srcCID, handle);
+ } else {
+ spin_lock(&dstContext->lock);
+
+ if (!vmci_handle_arr_has_entry
+ (dstContext->doorbellArray, handle)) {
+ result = VMCI_ERROR_NOT_FOUND;
+ } else {
+ if (!vmci_handle_arr_has_entry
+ (dstContext->pendingDoorbellArray, handle)) {
+ vmci_handle_arr_append_entry
+ (&dstContext->pendingDoorbellArray,
+ handle);
+
+ ctx_signal_notify(dstContext);
+ wake_up(&dstContext->hostContext.waitQueue);
+
+ }
+ result = VMCI_SUCCESS;
+ }
+ spin_unlock(&dstContext->lock);
+ }
+
+out:
+ vmci_ctx_release(dstContext);
+
+ return result;
+}
+
+static int ctx_compare_user(uid_t *user1, uid_t *user2)
+{
+ if (!user1 || !user2)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ return (*user1 == *user2) ? VMCI_SUCCESS : VMCI_ERROR_GENERIC;
+}
+
+bool vmci_ctx_supports_host_qp(struct vmci_ctx *context)
+{
+ return context && context->userVersion >= VMCI_VERSION_HOSTQP;
+}
+
+/*
+ * Registers that a new queue pair handle has been allocated by
+ * the context.
+ */
+int vmci_ctx_qp_create(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ int result;
+
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ if (!vmci_handle_arr_has_entry(context->queuePairArray, handle)) {
+ vmci_handle_arr_append_entry(&context->queuePairArray, handle);
+ result = VMCI_SUCCESS;
+ } else {
+ result = VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+
+ return result;
+}
+
+/*
+ * Unregisters a queue pair handle that was previously registered
+ * with vmci_ctx_qp_create.
+ */
+int vmci_ctx_qp_destroy(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ struct vmci_handle hndl;
+
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ hndl = vmci_handle_arr_remove_entry(context->queuePairArray, handle);
+
+ return VMCI_HANDLE_INVALID(hndl) ?
+ VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
+}
+
+/*
+ * Determines whether a given queue pair handle is registered
+ * with the given context.
+ */
+bool vmci_ctx_qp_exists(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return false;
+
+ return vmci_handle_arr_has_entry(context->queuePairArray, handle);
+}
+
+/**
+ * VMCIContext_GetPrivFlags() - Retrieve privilege flags.
+ * @contextID: The context ID of the VMCI context.
+ *
+ * Retrieves privilege flags of the given VMCI context ID.
+ */
+uint32_t VMCIContext_GetPrivFlags(uint32_t contextID)
+{
+ if (vmci_host_code_active()) {
+ uint32_t flags;
+ struct vmci_ctx *context;
+
+ context = vmci_ctx_get(contextID);
+ if (!context)
+ return VMCI_LEAST_PRIVILEGE_FLAGS;
+
+ flags = context->privFlags;
+ vmci_ctx_release(context);
+ return flags;
+ }
+ return VMCI_NO_PRIVILEGE_FLAGS;
+}
+EXPORT_SYMBOL(VMCIContext_GetPrivFlags);
+
+/**
+ * VMCI_ContextID2HostVmID() - Map CID to HostID
+ * @contextID: Context ID of VMCI context.
+ * @hostVmID: Host VM ID data
+ * @hostVmIDLen: Length of Host VM ID Data.
+ *
+ * Maps a context ID to the host specific (process/world) ID
+ * of the VM/VMX. This function is not used on Linux systems
+ * and should be ignored.
+ */
+int VMCI_ContextID2HostVmID(uint32_t contextID,
+ void *hostVmID,
+ size_t hostVmIDLen)
+{
+ return VMCI_ERROR_UNAVAILABLE;
+}
+EXPORT_SYMBOL(VMCI_ContextID2HostVmID);
+
+/**
+ * VMCI_IsContextOwner() - Determimnes if user is the context owner
+ * @contextID: The context ID of the VMCI context.
+ * @hostUser: The user as a void pointer.
+ *
+ * Determines whether a given host OS specific representation of
+ * user is the owner of the VM/VMX.
+ */
+int VMCI_IsContextOwner(uint32_t contextID,
+ void *hostUser)
+{
+ if (vmci_host_code_active()) {
+ struct vmci_ctx *context;
+ uid_t *user = hostUser;
+ int retval;
+
+ if (!hostUser)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (!context)
+ return VMCI_ERROR_NOT_FOUND;
+
+ if (context->validUser)
+ retval = ctx_compare_user(user, &context->user);
+ else
+ retval = VMCI_ERROR_UNAVAILABLE;
+
+ vmci_ctx_release(context);
+ return retval;
+ }
+ return VMCI_ERROR_UNAVAILABLE;
+}
+EXPORT_SYMBOL(VMCI_IsContextOwner);
diff --git a/drivers/misc/vmw_vmci/vmci_context.h b/drivers/misc/vmw_vmci/vmci_context.h
new file mode 100644
index 0000000..0b80a5d
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmci_context.h
@@ -0,0 +1,161 @@
+/*
+ * VMware VMCI driver (vmciContext.h)
+ *
+ * Copyright (C) 2012 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
+ */
+
+#ifndef _VMCI_CONTEXT_H_
+#define _VMCI_CONTEXT_H_
+
+#include <linux/vmw_vmci_defs.h>
+
+#include "vmci_datagram.h"
+#include "vmci_common_int.h"
+#include "vmci_handle_array.h"
+
+/* Used to determine what checkpoint state to get and set. */
+enum {
+ VMCI_NOTIFICATION_CPT_STATE = 1,
+ VMCI_WELLKNOWN_CPT_STATE = 2,
+ VMCI_DG_OUT_STATE = 3,
+ VMCI_DG_IN_STATE = 4,
+ VMCI_DG_IN_SIZE_STATE = 5,
+ VMCI_DOORBELL_CPT_STATE = 6,
+};
+
+/* Host specific struct used for signalling */
+struct vmci_host {
+ wait_queue_head_t waitQueue;
+};
+
+struct vmci_ctx {
+ struct list_head listItem; /* For global VMCI list. */
+ uint32_t cid;
+ atomic_t refCount;
+ struct list_head datagramQueue; /* Head of per VM queue. */
+ uint32_t pendingDatagrams;
+ size_t datagramQueueSize; /* Size of datagram queue in bytes. */
+
+ /*
+ * Version of the code that created
+ * this context; e.g., VMX.
+ */
+ int userVersion;
+ spinlock_t lock; /* Locks callQueue and handleArrays. */
+
+ /*
+ * 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.
+ */
+ struct vmci_handle_arr *queuePairArray;
+
+ /* Doorbells created by context. */
+ struct vmci_handle_arr *doorbellArray;
+
+ /* Doorbells pending for context. */
+ struct vmci_handle_arr *pendingDoorbellArray;
+
+ /* Contexts current context is subscribing to. */
+ struct vmci_handle_arr *notifierArray;
+ struct vmci_host hostContext;
+ uint32_t privFlags;
+ uid_t user;
+ bool validUser;
+ bool *notify; /* Notify flag pointer - hosted only. */
+ struct page *notifyPage; /* Page backing the notify UVA. */
+};
+
+/* VMCINotifyAddRemoveInfo: Used to add/remove remote context notifications. */
+struct vmci_ctx_info {
+ uint32_t remoteCID;
+ int result;
+};
+
+/* VMCICptBufInfo: Used to set/get current context's checkpoint state. */
+struct vmci_ctx_chkpt_buf_info {
+ uint64_t cptBuf;
+ uint32_t cptType;
+ uint32_t bufSize;
+ int32_t result;
+ uint32_t _pad;
+};
+
+/*
+ * VMCINotificationReceiveInfo: Used to recieve pending notifications
+ * for doorbells and queue pairs.
+ */
+struct vmci_ctx_notify_recv_info {
+ uint64_t dbHandleBufUVA;
+ uint64_t dbHandleBufSize;
+ uint64_t qpHandleBufUVA;
+ uint64_t qpHandleBufSize;
+ int32_t result;
+ uint32_t _pad;
+};
+
+int vmci_ctx_init(void);
+int vmci_ctx_init_ctx(uint32_t cid, uint32_t flags,
+ uintptr_t eventHnd, int version,
+ uid_t *user, struct vmci_ctx **context);
+
+bool vmci_ctx_supports_host_qp(struct vmci_ctx *context);
+void vmci_ctx_release_ctx(struct vmci_ctx *context);
+int vmci_ctx_enqueue_dg(uint32_t cid, struct vmci_dg *dg);
+int vmci_ctx_dequeue_dg(struct vmci_ctx *context,
+ size_t *maxSize, struct vmci_dg **dg);
+int vmci_ctx_pending_dgs(uint32_t cid, uint32_t *pending);
+struct vmci_ctx *vmci_ctx_get(uint32_t cid);
+void vmci_ctx_release(struct vmci_ctx *context);
+bool vmci_ctx_exists(uint32_t cid);
+
+uint32_t vmci_ctx_get_id(struct vmci_ctx *context);
+int vmci_ctx_add_notification(uint32_t contextID, uint32_t remoteCID);
+int vmci_ctx_remove_notification(uint32_t contextID, uint32_t remoteCID);
+int vmci_ctx_get_chkpt_state(uint32_t contextID, uint32_t cptType,
+ uint32_t *numCIDs, char **cptBufPtr);
+int vmci_ctx_set_chkpt_state(uint32_t contextID, uint32_t cptType,
+ uint32_t numCIDs, char *cptBuf);
+
+int vmci_ctx_qp_create(struct vmci_ctx *context,
+ struct vmci_handle handle);
+int vmci_ctx_qp_destroy(struct vmci_ctx *context,
+ struct vmci_handle handle);
+bool vmci_ctx_qp_exists(struct vmci_ctx *context,
+ struct vmci_handle handle);
+
+void vmci_ctx_check_signal_notify(struct vmci_ctx *context);
+void vmci_ctx_unset_notify(struct vmci_ctx *context);
+
+int vmci_ctx_dbell_create(uint32_t contextID, struct vmci_handle handle);
+int vmci_ctx_dbell_destroy(uint32_t contextID, struct vmci_handle handle);
+int vmci_ctx_dbell_destroy_all(uint32_t contextID);
+int vmci_ctx_notify_dbell(uint32_t cid, struct vmci_handle handle,
+ uint32_t srcPrivFlags);
+
+int vmci_ctx_rcv_notifications_get(uint32_t contextID, struct vmci_handle_arr
+ **dbHandleArray, struct vmci_handle_arr
+ **qpHandleArray);
+void
+vmci_ctx_rcv_notifications_release(uint32_t contextID, struct vmci_handle_arr
+ *dbHandleArray, struct vmci_handle_arr
+ *qpHandleArray, bool success);
+#endif /* _VMCI_CONTEXT_H_ */
--
1.7.0.4
WARNING: multiple messages have this Message-ID (diff)
From: "Andrew Stiegmann (stieg)" <astiegmann@vmware.com>
To: linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org
Cc: pv-drivers@vmware.com, vm-crosstalk@vmware.com,
cschamp@vmware.com, gregkh@linuxfoundation.org,
"Andrew Stiegmann (stieg)" <astiegmann@vmware.com>
Subject: [vmw_vmci 01/11] Apply VMCI context code
Date: Thu, 26 Jul 2012 16:39:30 -0700 [thread overview]
Message-ID: <1343345980-32397-2-git-send-email-astiegmann@vmware.com> (raw)
In-Reply-To: <1343345980-32397-1-git-send-email-astiegmann@vmware.com>
Context code maintains state for vmci and allows the driver
to communicate with multiple VMs.
Signed-off-by: Andrew Stiegmann (stieg) <astiegmann@vmware.com>
---
drivers/misc/vmw_vmci/vmci_context.c | 1269 ++++++++++++++++++++++++++++++++++
drivers/misc/vmw_vmci/vmci_context.h | 161 +++++
2 files changed, 1430 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/vmw_vmci/vmci_context.c
create mode 100644 drivers/misc/vmw_vmci/vmci_context.h
diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c
new file mode 100644
index 0000000..46faf10
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmci_context.c
@@ -0,0 +1,1269 @@
+/*
+ * VMware VMCI Driver
+ *
+ * Copyright (C) 2012 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
+ */
+
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/vmw_vmci_api.h>
+#include <linux/vmw_vmci_defs.h>
+
+#include "vmci_common_int.h"
+#include "vmci_context.h"
+#include "vmci_datagram.h"
+#include "vmci_doorbell.h"
+#include "vmci_driver.h"
+#include "vmci_event.h"
+#include "vmci_queue_pair.h"
+
+/*
+ * List of current VMCI contexts.
+ */
+static struct {
+ struct list_head head;
+ spinlock_t lock;
+ spinlock_t firingLock;
+} ctx_list;
+
+
+static void ctx_signal_notify(struct vmci_ctx *context)
+{
+ if (context->notify)
+ *context->notify = true;
+}
+
+static void ctx_clear_notify(struct vmci_ctx *context)
+{
+ if (context->notify)
+ *context->notify = false;
+}
+
+/*
+ * If nothing requires the attention of the guest, clears both
+ * notify flag and call.
+ */
+static void ctx_clear_notify_call(struct vmci_ctx *context)
+{
+ if (context->pendingDatagrams == 0 &&
+ vmci_handle_arr_get_size(context->pendingDoorbellArray) == 0)
+ ctx_clear_notify(context);
+}
+
+/*
+ * Check if a context with the specified context ID exists.
+ * Assumes the ctx_list.lock is held.
+ */
+static bool ctx_exists_locked(uint32_t cid)
+{
+ struct vmci_ctx *context;
+
+ list_for_each_entry(context, &ctx_list.head, listItem) {
+ if (context->cid == cid)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Sets the context's notify flag iff datagrams are pending for this
+ * context. Called from vmci_setup_notify().
+ */
+void vmci_ctx_check_signal_notify(struct vmci_ctx *context)
+{
+ ASSERT(context);
+
+ spin_lock(&ctx_list.lock);
+ if (context->pendingDatagrams)
+ ctx_signal_notify(context);
+ spin_unlock(&ctx_list.lock);
+}
+
+int __init vmci_ctx_init(void)
+{
+ INIT_LIST_HEAD(&ctx_list.head);
+
+ spin_lock_init(&ctx_list.lock);
+ spin_lock_init(&ctx_list.firingLock);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Allocates and initializes a VMCI context.
+ */
+int vmci_ctx_init_ctx(uint32_t cid,
+ uint32_t privFlags,
+ uintptr_t eventHnd,
+ int userVersion,
+ uid_t *user, struct vmci_ctx **outContext)
+{
+ struct vmci_ctx *context;
+ int result;
+
+ if (privFlags & ~VMCI_PRIVILEGE_ALL_FLAGS) {
+ pr_devel("Invalid flag (flags=0x%x) for VMCI context.",
+ privFlags);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ if (userVersion == 0)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = kzalloc(sizeof *context, GFP_KERNEL);
+ if (context == NULL) {
+ pr_warn("Failed to allocate memory for VMCI context.");
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ INIT_LIST_HEAD(&context->listItem);
+ INIT_LIST_HEAD(&context->datagramQueue);
+
+ context->userVersion = userVersion;
+
+ context->queuePairArray = vmci_handle_arr_create(0);
+ if (!context->queuePairArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->doorbellArray = vmci_handle_arr_create(0);
+ if (!context->doorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->pendingDoorbellArray = vmci_handle_arr_create(0);
+ if (!context->pendingDoorbellArray) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ context->notifierArray = vmci_handle_arr_create(0);
+ if (context->notifierArray == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto error;
+ }
+
+ spin_lock_init(&context->lock);
+
+ atomic_set(&context->refCount, 1);
+
+ /* Inititialize host-specific VMCI context. */
+ init_waitqueue_head(&context->hostContext.waitQueue);
+
+ 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.
+ */
+ spin_lock(&ctx_list.lock);
+ ASSERT(cid != VMCI_INVALID_ID);
+ while (ctx_exists_locked(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(!ctx_exists_locked(cid));
+ context->cid = cid;
+ context->validUser = user != NULL;
+ if (context->validUser)
+ context->user = *user;
+ list_add(&context->listItem, &ctx_list.head);
+ spin_unlock(&ctx_list.lock);
+
+ context->notify = NULL;
+ context->notifyPage = NULL;
+
+ *outContext = context;
+ return VMCI_SUCCESS;
+
+error:
+ if (context->notifierArray)
+ vmci_handle_arr_destroy(context->notifierArray);
+ if (context->queuePairArray)
+ vmci_handle_arr_destroy(context->queuePairArray);
+ if (context->doorbellArray)
+ vmci_handle_arr_destroy(context->doorbellArray);
+ if (context->pendingDoorbellArray)
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ kfree(context);
+ return result;
+}
+
+/*
+ * Dequeue VMCI context.
+ */
+void vmci_ctx_release_ctx(struct vmci_ctx *context)
+{
+ spin_lock(&ctx_list.lock);
+ list_del(&context->listItem);
+ spin_unlock(&ctx_list.lock);
+
+ vmci_ctx_release(context);
+}
+
+/*
+ * Fire notification for all contexts interested in given cid.
+ */
+static int ctx_fire_notification(uint32_t contextID,
+ uint32_t privFlags)
+{
+ uint32_t i, arraySize;
+ struct vmci_ctx *subCtx;
+ struct vmci_handle_arr *subscriberArray;
+ struct vmci_handle contextHandle =
+ vmci_make_handle(contextID, VMCI_EVENT_HANDLER);
+
+ /*
+ * We create an array to hold the subscribers we find when
+ * scanning through all contexts.
+ */
+ subscriberArray = vmci_handle_arr_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.
+ */
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&ctx_list.lock);
+ list_for_each_entry(subCtx, &ctx_list.head, listItem) {
+ /*
+ * We only deliver notifications of the removal of
+ * contexts, if the two contexts are allowed to
+ * interact.
+ */
+ if (vmci_handle_arr_has_entry
+ (subCtx->notifierArray, contextHandle)
+ && !vmci_deny_interaction(privFlags, subCtx->privFlags)) {
+ vmci_handle_arr_append_entry(&subscriberArray,
+ vmci_make_handle
+ (subCtx->cid,
+ VMCI_EVENT_HANDLER));
+ }
+ }
+ spin_unlock(&ctx_list.lock);
+ spin_unlock(&ctx_list.firingLock);
+
+ /* Fire event to all subscribers. */
+ arraySize = vmci_handle_arr_get_size(subscriberArray);
+ for (i = 0; i < arraySize; i++) {
+ int result;
+ struct vmci_event_msg *eMsg;
+ struct vmci_event_payld_ctx *evPayload;
+ char buf[sizeof *eMsg + sizeof *evPayload];
+
+ eMsg = (struct vmci_event_msg *)buf;
+
+ /* Clear out any garbage. */
+ memset(eMsg, 0, sizeof *eMsg + sizeof *evPayload);
+ eMsg->hdr.dst = vmci_handle_arr_get_entry(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 = vmci_event_data_payload(&eMsg->eventData);
+ evPayload->contextID = contextID;
+
+ result = vmci_dg_dispatch(VMCI_HYPERVISOR_CONTEXT_ID,
+ (struct vmci_dg *)
+ eMsg, false);
+ if (result < VMCI_SUCCESS) {
+ pr_devel("Failed to enqueue event datagram " \
+ "(type=%d) for context (ID=0x%x).",
+ eMsg->eventData.event, eMsg->hdr.dst.context);
+ /* We continue to enqueue on next subscriber. */
+ }
+ }
+ vmci_handle_arr_destroy(subscriberArray);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * 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.
+ */
+int vmci_ctx_pending_dgs(uint32_t cid,
+ uint32_t *pending)
+{
+ struct vmci_ctx *context;
+
+ context = vmci_ctx_get(cid);
+ if (context == NULL)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ spin_lock(&context->lock);
+ if (pending)
+ *pending = context->pendingDatagrams;
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Queues a VMCI datagram for the appropriate target VM context.
+ */
+int vmci_ctx_enqueue_dg(uint32_t cid,
+ struct vmci_dg *dg)
+{
+ struct vmci_dg_queue_entry *dqEntry;
+ struct vmci_ctx *context;
+ struct vmci_handle 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 = vmci_ctx_get(cid);
+ if (context == NULL) {
+ pr_devel("Invalid context (ID=0x%x).", cid);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ /* Allocate guest call entry and add it to the target VM's queue. */
+ dqEntry = kmalloc(sizeof *dqEntry, GFP_KERNEL);
+ if (dqEntry == NULL) {
+ pr_warn("Failed to allocate memory for datagram.");
+ vmci_ctx_release(context);
+ return VMCI_ERROR_NO_MEM;
+ }
+ dqEntry->dg = dg;
+ dqEntry->dgSize = vmciDgSize;
+ dgSrc = dg->src;
+ INIT_LIST_HEAD(&dqEntry->listItem);
+
+ spin_lock(&context->lock);
+
+ /*
+ * 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)) {
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+ kfree(dqEntry);
+ pr_devel("Context (ID=0x%x) receive queue is full.",
+ cid);
+ return VMCI_ERROR_NO_RESOURCES;
+ }
+
+ list_add(&dqEntry->listItem, &context->datagramQueue);
+ context->pendingDatagrams++;
+ context->datagramQueueSize += vmciDgSize;
+ ctx_signal_notify(context);
+ wake_up(&context->hostContext.waitQueue);
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return vmciDgSize;
+}
+
+/*
+ * Verifies whether a context with the specified context ID exists.
+ */
+bool vmci_ctx_exists(uint32_t cid)
+{
+ bool rv;
+
+ spin_lock(&ctx_list.lock);
+ rv = ctx_exists_locked(cid);
+ spin_unlock(&ctx_list.lock);
+ return rv;
+}
+
+/*
+ * Retrieves VMCI context corresponding to the given cid.
+ */
+struct vmci_ctx *vmci_ctx_get(uint32_t cid)
+{
+ struct vmci_ctx *context = NULL;
+
+ if (cid == VMCI_INVALID_ID)
+ return NULL;
+
+ spin_lock(&ctx_list.lock);
+ list_for_each_entry(context, &ctx_list.head, 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;
+ }
+ }
+ spin_unlock(&ctx_list.lock);
+
+ return (context && context->cid == cid) ? context : NULL;
+}
+
+/*
+ * 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.
+ */
+static void ctx_free_ctx(struct vmci_ctx *context)
+{
+ struct list_head *curr;
+ struct list_head *next;
+ struct vmci_dg_queue_entry *dqEntry;
+ struct vmci_handle tempHandle;
+
+ /*
+ * Fire event to all contexts interested in knowing this
+ * context is dying.
+ */
+ ctx_fire_notification(context->cid, context->privFlags);
+
+ /*
+ * 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 = vmci_handle_arr_get_entry(context->queuePairArray, 0);
+ while (!VMCI_HANDLE_EQUAL(tempHandle, VMCI_INVALID_HANDLE)) {
+ if (vmci_qp_broker_detach(tempHandle, context) < VMCI_SUCCESS) {
+ /*
+ * When vmci_qp_broker_detach() succeeds it
+ * removes the handle from the array. If
+ * detach fails, we must remove the handle
+ * ourselves.
+ */
+ vmci_handle_arr_remove_entry(context->queuePairArray,
+ tempHandle);
+ }
+ tempHandle =
+ vmci_handle_arr_get_entry(context->queuePairArray, 0);
+ }
+
+ /*
+ * It is fine to destroy this without locking the callQueue, as
+ * this is the only thread having a reference to the context.
+ */ list_for_each_safe(curr, next, &context->datagramQueue) {
+ dqEntry =
+ list_entry(curr, struct vmci_dg_queue_entry, listItem);
+ list_del(curr);
+ ASSERT(dqEntry && dqEntry->dg);
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ kfree(dqEntry->dg);
+ kfree(dqEntry);
+ }
+
+ vmci_handle_arr_destroy(context->notifierArray);
+ vmci_handle_arr_destroy(context->queuePairArray);
+ vmci_handle_arr_destroy(context->doorbellArray);
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ vmci_ctx_unset_notify(context);
+ kfree(context);
+}
+
+/*
+ * 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.
+ */
+void vmci_ctx_release(struct vmci_ctx *context)
+{
+ ASSERT(context);
+ if (atomic_dec_and_test(&context->refCount))
+ ctx_free_ctx(context);
+}
+
+/*
+ * 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.
+ */int vmci_ctx_dequeue_dg(struct vmci_ctx *context,
+ size_t *maxSize,
+ struct vmci_dg **dg)
+{
+ struct vmci_dg_queue_entry *dqEntry;
+ struct list_head *listItem;
+ int rv;
+
+ ASSERT(context && dg);
+
+ /* Dequeue the next datagram entry. */
+ spin_lock(&context->lock);
+ if (context->pendingDatagrams == 0) {
+ ctx_clear_notify_call(context);
+ spin_unlock(&context->lock);
+ pr_devel("No datagrams pending.");
+ return VMCI_ERROR_NO_MORE_DATAGRAMS;
+ }
+
+ listItem = context->datagramQueue.next;
+ ASSERT(!list_empty(&context->datagramQueue));
+
+ dqEntry = list_entry(listItem, struct vmci_dg_queue_entry, listItem);
+ ASSERT(dqEntry->dg);
+
+ /* Check size of caller's buffer. */
+ if (*maxSize < dqEntry->dgSize) {
+ *maxSize = dqEntry->dgSize;
+ spin_unlock(&context->lock);
+ pr_devel("Caller's buffer should be at least " \
+ "(size=%u bytes).", (uint32_t) *maxSize);
+ return VMCI_ERROR_NO_MEM;
+ }
+
+ list_del(listItem);
+ context->pendingDatagrams--;
+ context->datagramQueueSize -= dqEntry->dgSize;
+ if (context->pendingDatagrams == 0) {
+ ctx_clear_notify_call(context);
+ rv = VMCI_SUCCESS;
+ } else {
+ /*
+ * Return the size of the next datagram.
+ */
+ struct vmci_dg_queue_entry *nextEntry;
+
+ listItem = context->datagramQueue.next;
+ ASSERT(!list_empty(&context->datagramQueue));
+ nextEntry = list_entry(listItem, struct vmci_dg_queue_entry,
+ 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;
+ }
+ spin_unlock(&context->lock);
+
+ /* Caller must free datagram. */
+ ASSERT(dqEntry->dgSize == VMCI_DG_SIZE(dqEntry->dg));
+ *dg = dqEntry->dg;
+ dqEntry->dg = NULL;
+ kfree(dqEntry);
+
+ return rv;
+}
+
+/*
+ * Reverts actions set up by vmci_setup_notify(). Unmaps and unlocks the
+ * page mapped/locked by vmci_setup_notify().
+ */
+void vmci_ctx_unset_notify(struct vmci_ctx *context)
+{
+ struct page *notifyPage = context->notifyPage;
+
+ if (!notifyPage)
+ return;
+
+ context->notify = NULL;
+ context->notifyPage = NULL;
+ kunmap(notifyPage);
+ put_page(notifyPage);
+
+}
+
+uint32_t vmci_ctx_get_id(struct vmci_ctx *context)
+{
+ if (!context)
+ return VMCI_INVALID_ID;
+
+ ASSERT(context->cid != VMCI_INVALID_ID);
+ return context->cid;
+}
+
+/*
+ * Add remoteCID to list of contexts current contexts wants
+ * notifications from/about.
+ */
+int vmci_ctx_add_notification(uint32_t contextID,
+ uint32_t remoteCID)
+{
+ int result = VMCI_ERROR_ALREADY_EXISTS;
+ struct vmci_handle notifierHandle;
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ if (VMCI_CONTEXT_IS_VM(contextID) && VMCI_CONTEXT_IS_VM(remoteCID)) {
+ pr_devel("Context removed notifications for other VMs not " \
+ "supported (src=0x%x, remote=0x%x).",
+ contextID, remoteCID);
+ result = VMCI_ERROR_DST_UNREACHABLE;
+ goto out;
+ }
+
+ if (context->privFlags & VMCI_PRIVILEGE_FLAG_RESTRICTED) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+
+ notifierHandle = vmci_make_handle(remoteCID, VMCI_EVENT_HANDLER);
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&context->lock);
+ if (!vmci_handle_arr_has_entry(context->notifierArray,
+ notifierHandle)) {
+ vmci_handle_arr_append_entry(&context->notifierArray,
+ notifierHandle);
+ result = VMCI_SUCCESS;
+ }
+ spin_unlock(&context->lock);
+ spin_unlock(&ctx_list.firingLock);
+
+ out:
+ vmci_ctx_release(context);
+ return result;
+}
+
+/*
+ * Remove remoteCID from current context's list of contexts it is
+ * interested in getting notifications from/about.
+ */
+int vmci_ctx_remove_notification(uint32_t contextID,
+ uint32_t remoteCID)
+{
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+ struct vmci_handle tmpHandle;
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&ctx_list.firingLock);
+ spin_lock(&context->lock);
+ tmpHandle = vmci_make_handle(remoteCID, VMCI_EVENT_HANDLER);
+ tmpHandle = vmci_handle_arr_remove_entry(context->notifierArray,
+ tmpHandle);
+ spin_unlock(&context->lock);
+ spin_unlock(&ctx_list.firingLock);
+ vmci_ctx_release(context);
+
+ if (VMCI_HANDLE_EQUAL(tmpHandle, VMCI_INVALID_HANDLE))
+ return VMCI_ERROR_NOT_FOUND;
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * Get current context's checkpoint state of given type.
+ */
+int vmci_ctx_get_chkpt_state(uint32_t contextID,
+ uint32_t cptType,
+ uint32_t *bufSize,
+ char **cptBufPtr)
+{
+ int i, result;
+ uint32_t arraySize, cptDataSize;
+ struct vmci_handle_arr *array;
+ struct vmci_ctx *context;
+ char *cptBuf;
+ bool getContextID;
+
+ ASSERT(bufSize && cptBufPtr);
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ if (cptType == VMCI_NOTIFICATION_CPT_STATE) {
+ ASSERT(context->notifierArray);
+ array = context->notifierArray;
+ getContextID = true;
+ } else if (cptType == VMCI_WELLKNOWN_CPT_STATE) {
+ /*
+ * For compatibility with VMX'en with VM to VM communication, we
+ * always return zero wellknown handles.
+ */
+
+ *bufSize = 0;
+ *cptBufPtr = NULL;
+ result = VMCI_SUCCESS;
+ goto release;
+ } else if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ASSERT(context->doorbellArray);
+ array = context->doorbellArray;
+ getContextID = false;
+ } else {
+ pr_devel("Invalid cpt state (type=%d).", cptType);
+ result = VMCI_ERROR_INVALID_ARGS;
+ goto release;
+ }
+
+ arraySize = vmci_handle_arr_get_size(array);
+ if (arraySize > 0) {
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ cptDataSize =
+ arraySize * sizeof(struct dbell_cpt_state);
+ } else {
+ cptDataSize = arraySize * sizeof(uint32_t);
+ }
+
+ if (*bufSize < cptDataSize) {
+ *bufSize = cptDataSize;
+ result = VMCI_ERROR_MORE_DATA;
+ goto release;
+ }
+
+ cptBuf = kmalloc(cptDataSize, GFP_ATOMIC);
+
+ if (cptBuf == NULL) {
+ result = VMCI_ERROR_NO_MEM;
+ goto release;
+ }
+
+ for (i = 0; i < arraySize; i++) {
+ struct vmci_handle tmpHandle =
+ vmci_handle_arr_get_entry(array, i);
+ if (cptType == VMCI_DOORBELL_CPT_STATE) {
+ ((struct dbell_cpt_state *)cptBuf)[i].handle =
+ tmpHandle;
+ } else {
+ ((uint32_t *) cptBuf)[i] =
+ getContextID ? tmpHandle.context :
+ tmpHandle.resource;
+ }
+ }
+ *bufSize = cptDataSize;
+ *cptBufPtr = cptBuf;
+ } else {
+ *bufSize = 0;
+ *cptBufPtr = NULL;
+ }
+ result = VMCI_SUCCESS;
+
+release:
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Set current context's checkpoint state of given type.
+ */
+int vmci_ctx_set_chkpt_state(uint32_t contextID,
+ uint32_t cptType,
+ uint32_t bufSize,
+ char *cptBuf)
+{
+ uint32_t i;
+ uint32_t currentID;
+ int result = VMCI_SUCCESS;
+ uint32_t numIDs = bufSize / sizeof(uint32_t);
+ ASSERT(cptBuf);
+
+ if (cptType == VMCI_WELLKNOWN_CPT_STATE && numIDs > 0) {
+ /*
+ * We would end up here if VMX with VM to VM communication
+ * attempts to restore a checkpoint with wellknown handles.
+ */
+ pr_warn("Attempt to restore checkpoint with obsolete " \
+ "wellknown handles.");
+ return VMCI_ERROR_OBSOLETE;
+ }
+
+ if (cptType != VMCI_NOTIFICATION_CPT_STATE) {
+ pr_devel("Invalid cpt state (type=%d).", cptType);
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ for (i = 0; i < numIDs && result == VMCI_SUCCESS; i++) {
+ currentID = ((uint32_t *) cptBuf)[i];
+ result = vmci_ctx_add_notification(contextID, currentID);
+ if (result != VMCI_SUCCESS)
+ break;
+ }
+ if (result != VMCI_SUCCESS)
+ pr_devel("Failed to set cpt state (type=%d) " \
+ "(error=%d).", cptType, result);
+
+ return result;
+}
+
+/*
+ * 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
+ * vmci_ctx_rcv_notifications_release.
+ */
+int vmci_ctx_rcv_notifications_get(uint32_t contextID,
+ struct vmci_handle_arr **dbHandleArray,
+ struct vmci_handle_arr **qpHandleArray)
+{
+ struct vmci_ctx *context;
+ int result = VMCI_SUCCESS;
+
+ ASSERT(dbHandleArray && qpHandleArray);
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+
+ *dbHandleArray = context->pendingDoorbellArray;
+ context->pendingDoorbellArray = vmci_handle_arr_create(0);
+ if (!context->pendingDoorbellArray) {
+ context->pendingDoorbellArray = *dbHandleArray;
+ *dbHandleArray = NULL;
+ result = VMCI_ERROR_NO_MEM;
+ }
+ *qpHandleArray = NULL;
+
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Releases handle arrays with pending notifications previously
+ * retrieved using vmci_ctx_rcv_notifications_get. If the
+ * notifications were not successfully handed over to the guest,
+ * success must be false.
+ */
+void vmci_ctx_rcv_notifications_release(uint32_t contextID,
+ struct vmci_handle_arr *dbHandleArray,
+ struct vmci_handle_arr *qpHandleArray,
+ bool success)
+{
+ struct vmci_ctx *context = vmci_ctx_get(contextID);
+
+ if (!context) {
+ /*
+ * 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);
+ }
+
+ spin_lock(&context->lock);
+ if (!success) {
+ struct vmci_handle 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 = vmci_handle_arr_remove_tail(
+ context->pendingDoorbellArray);
+ while (!VMCI_HANDLE_INVALID(handle)) {
+ ASSERT(vmci_handle_arr_has_entry
+ (context->doorbellArray, handle));
+ if (!vmci_handle_arr_has_entry
+ (dbHandleArray, handle)) {
+ vmci_handle_arr_append_entry
+ (&dbHandleArray, handle);
+ }
+ handle = vmci_handle_arr_remove_tail(
+ context->pendingDoorbellArray);
+ }
+ vmci_handle_arr_destroy(context->pendingDoorbellArray);
+ context->pendingDoorbellArray = dbHandleArray;
+ dbHandleArray = NULL;
+ } else {
+ ctx_clear_notify_call(context);
+ }
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ if (dbHandleArray)
+ vmci_handle_arr_destroy(dbHandleArray);
+
+ if (qpHandleArray)
+ vmci_handle_arr_destroy(qpHandleArray);
+}
+
+/*
+ * Registers that a new doorbell handle has been allocated by the
+ * context. Only doorbell handles registered can be notified.
+ */
+int vmci_ctx_dbell_create(uint32_t contextID,
+ struct vmci_handle handle)
+{
+ struct vmci_ctx *context;
+ int result;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ if (!vmci_handle_arr_has_entry(context->doorbellArray, handle)) {
+ vmci_handle_arr_append_entry(&context->doorbellArray, handle);
+ result = VMCI_SUCCESS;
+ } else {
+ result = VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+
+ spin_unlock(&context->lock);
+ vmci_ctx_release(context);
+
+ return result;
+}
+
+/*
+ * Unregisters a doorbell handle that was previously registered
+ * with vmci_ctx_dbell_create.
+ */
+int vmci_ctx_dbell_destroy(uint32_t contextID,
+ struct vmci_handle handle)
+{
+ struct vmci_ctx *context;
+ struct vmci_handle removedHandle;
+
+ if (contextID == VMCI_INVALID_ID || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ removedHandle =
+ vmci_handle_arr_remove_entry(context->doorbellArray, handle);
+ vmci_handle_arr_remove_entry(context->pendingDoorbellArray, handle);
+ spin_unlock(&context->lock);
+
+ vmci_ctx_release(context);
+
+ return VMCI_HANDLE_INVALID(removedHandle) ?
+ VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
+}
+
+/*
+ * Unregisters all doorbell handles that were previously
+ * registered with vmci_ctx_dbell_create.
+ */
+int vmci_ctx_dbell_destroy_all(uint32_t contextID)
+{
+ struct vmci_ctx *context;
+ struct vmci_handle handle;
+
+ if (contextID == VMCI_INVALID_ID)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (context == NULL)
+ return VMCI_ERROR_NOT_FOUND;
+
+ spin_lock(&context->lock);
+ do {
+ struct vmci_handle_arr *arr = context->doorbellArray;
+ handle = vmci_handle_arr_remove_tail(arr);
+ } while (!VMCI_HANDLE_INVALID(handle));
+ do {
+ struct vmci_handle_arr *arr = context->pendingDoorbellArray;
+ handle = vmci_handle_arr_remove_tail(arr);
+ } while (!VMCI_HANDLE_INVALID(handle));
+ spin_unlock(&context->lock);
+
+ vmci_ctx_release(context);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ * 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.
+ */
+int vmci_ctx_notify_dbell(uint32_t srcCID,
+ struct vmci_handle handle,
+ uint32_t srcPrivFlags)
+{
+ struct vmci_ctx *dstContext;
+ int result;
+
+ if (VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ /* Get the target VM's VMCI context. */
+ dstContext = vmci_ctx_get(handle.context);
+ if (dstContext == NULL) {
+ pr_devel("Invalid context (ID=0x%x).", handle.context);
+ return VMCI_ERROR_NOT_FOUND;
+ }
+
+ if (srcCID != handle.context) {
+ uint32_t dstPrivFlags;
+
+ if (VMCI_CONTEXT_IS_VM(srcCID)
+ && VMCI_CONTEXT_IS_VM(handle.context)) {
+ pr_devel("Doorbell notification from VM to VM not " \
+ "supported (src=0x%x, dst=0x%x).", srcCID,
+ handle.context);
+ result = VMCI_ERROR_DST_UNREACHABLE;
+ goto out;
+ }
+
+ result = vmci_dbell_get_priv_flags(handle, &dstPrivFlags);
+ if (result < VMCI_SUCCESS) {
+ pr_warn("Failed to get privilege flags for " \
+ "destination (handle=0x%x:0x%x).",
+ handle.context, handle.resource);
+ goto out;
+ }
+
+ if (srcCID != VMCI_HOST_CONTEXT_ID ||
+ srcPrivFlags == VMCI_NO_PRIVILEGE_FLAGS) {
+ srcPrivFlags = VMCIContext_GetPrivFlags(srcCID);
+ }
+
+ if (vmci_deny_interaction(srcPrivFlags, dstPrivFlags)) {
+ result = VMCI_ERROR_NO_ACCESS;
+ goto out;
+ }
+ }
+
+ if (handle.context == VMCI_HOST_CONTEXT_ID) {
+ result = vmci_dbell_host_context_notify(srcCID, handle);
+ } else {
+ spin_lock(&dstContext->lock);
+
+ if (!vmci_handle_arr_has_entry
+ (dstContext->doorbellArray, handle)) {
+ result = VMCI_ERROR_NOT_FOUND;
+ } else {
+ if (!vmci_handle_arr_has_entry
+ (dstContext->pendingDoorbellArray, handle)) {
+ vmci_handle_arr_append_entry
+ (&dstContext->pendingDoorbellArray,
+ handle);
+
+ ctx_signal_notify(dstContext);
+ wake_up(&dstContext->hostContext.waitQueue);
+
+ }
+ result = VMCI_SUCCESS;
+ }
+ spin_unlock(&dstContext->lock);
+ }
+
+out:
+ vmci_ctx_release(dstContext);
+
+ return result;
+}
+
+static int ctx_compare_user(uid_t *user1, uid_t *user2)
+{
+ if (!user1 || !user2)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ return (*user1 == *user2) ? VMCI_SUCCESS : VMCI_ERROR_GENERIC;
+}
+
+bool vmci_ctx_supports_host_qp(struct vmci_ctx *context)
+{
+ return context && context->userVersion >= VMCI_VERSION_HOSTQP;
+}
+
+/*
+ * Registers that a new queue pair handle has been allocated by
+ * the context.
+ */
+int vmci_ctx_qp_create(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ int result;
+
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ if (!vmci_handle_arr_has_entry(context->queuePairArray, handle)) {
+ vmci_handle_arr_append_entry(&context->queuePairArray, handle);
+ result = VMCI_SUCCESS;
+ } else {
+ result = VMCI_ERROR_DUPLICATE_ENTRY;
+ }
+
+ return result;
+}
+
+/*
+ * Unregisters a queue pair handle that was previously registered
+ * with vmci_ctx_qp_create.
+ */
+int vmci_ctx_qp_destroy(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ struct vmci_handle hndl;
+
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return VMCI_ERROR_INVALID_ARGS;
+
+ hndl = vmci_handle_arr_remove_entry(context->queuePairArray, handle);
+
+ return VMCI_HANDLE_INVALID(hndl) ?
+ VMCI_ERROR_NOT_FOUND : VMCI_SUCCESS;
+}
+
+/*
+ * Determines whether a given queue pair handle is registered
+ * with the given context.
+ */
+bool vmci_ctx_qp_exists(struct vmci_ctx *context,
+ struct vmci_handle handle)
+{
+ if (context == NULL || VMCI_HANDLE_INVALID(handle))
+ return false;
+
+ return vmci_handle_arr_has_entry(context->queuePairArray, handle);
+}
+
+/**
+ * VMCIContext_GetPrivFlags() - Retrieve privilege flags.
+ * @contextID: The context ID of the VMCI context.
+ *
+ * Retrieves privilege flags of the given VMCI context ID.
+ */
+uint32_t VMCIContext_GetPrivFlags(uint32_t contextID)
+{
+ if (vmci_host_code_active()) {
+ uint32_t flags;
+ struct vmci_ctx *context;
+
+ context = vmci_ctx_get(contextID);
+ if (!context)
+ return VMCI_LEAST_PRIVILEGE_FLAGS;
+
+ flags = context->privFlags;
+ vmci_ctx_release(context);
+ return flags;
+ }
+ return VMCI_NO_PRIVILEGE_FLAGS;
+}
+EXPORT_SYMBOL(VMCIContext_GetPrivFlags);
+
+/**
+ * VMCI_ContextID2HostVmID() - Map CID to HostID
+ * @contextID: Context ID of VMCI context.
+ * @hostVmID: Host VM ID data
+ * @hostVmIDLen: Length of Host VM ID Data.
+ *
+ * Maps a context ID to the host specific (process/world) ID
+ * of the VM/VMX. This function is not used on Linux systems
+ * and should be ignored.
+ */
+int VMCI_ContextID2HostVmID(uint32_t contextID,
+ void *hostVmID,
+ size_t hostVmIDLen)
+{
+ return VMCI_ERROR_UNAVAILABLE;
+}
+EXPORT_SYMBOL(VMCI_ContextID2HostVmID);
+
+/**
+ * VMCI_IsContextOwner() - Determimnes if user is the context owner
+ * @contextID: The context ID of the VMCI context.
+ * @hostUser: The user as a void pointer.
+ *
+ * Determines whether a given host OS specific representation of
+ * user is the owner of the VM/VMX.
+ */
+int VMCI_IsContextOwner(uint32_t contextID,
+ void *hostUser)
+{
+ if (vmci_host_code_active()) {
+ struct vmci_ctx *context;
+ uid_t *user = hostUser;
+ int retval;
+
+ if (!hostUser)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ context = vmci_ctx_get(contextID);
+ if (!context)
+ return VMCI_ERROR_NOT_FOUND;
+
+ if (context->validUser)
+ retval = ctx_compare_user(user, &context->user);
+ else
+ retval = VMCI_ERROR_UNAVAILABLE;
+
+ vmci_ctx_release(context);
+ return retval;
+ }
+ return VMCI_ERROR_UNAVAILABLE;
+}
+EXPORT_SYMBOL(VMCI_IsContextOwner);
diff --git a/drivers/misc/vmw_vmci/vmci_context.h b/drivers/misc/vmw_vmci/vmci_context.h
new file mode 100644
index 0000000..0b80a5d
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmci_context.h
@@ -0,0 +1,161 @@
+/*
+ * VMware VMCI driver (vmciContext.h)
+ *
+ * Copyright (C) 2012 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
+ */
+
+#ifndef _VMCI_CONTEXT_H_
+#define _VMCI_CONTEXT_H_
+
+#include <linux/vmw_vmci_defs.h>
+
+#include "vmci_datagram.h"
+#include "vmci_common_int.h"
+#include "vmci_handle_array.h"
+
+/* Used to determine what checkpoint state to get and set. */
+enum {
+ VMCI_NOTIFICATION_CPT_STATE = 1,
+ VMCI_WELLKNOWN_CPT_STATE = 2,
+ VMCI_DG_OUT_STATE = 3,
+ VMCI_DG_IN_STATE = 4,
+ VMCI_DG_IN_SIZE_STATE = 5,
+ VMCI_DOORBELL_CPT_STATE = 6,
+};
+
+/* Host specific struct used for signalling */
+struct vmci_host {
+ wait_queue_head_t waitQueue;
+};
+
+struct vmci_ctx {
+ struct list_head listItem; /* For global VMCI list. */
+ uint32_t cid;
+ atomic_t refCount;
+ struct list_head datagramQueue; /* Head of per VM queue. */
+ uint32_t pendingDatagrams;
+ size_t datagramQueueSize; /* Size of datagram queue in bytes. */
+
+ /*
+ * Version of the code that created
+ * this context; e.g., VMX.
+ */
+ int userVersion;
+ spinlock_t lock; /* Locks callQueue and handleArrays. */
+
+ /*
+ * 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.
+ */
+ struct vmci_handle_arr *queuePairArray;
+
+ /* Doorbells created by context. */
+ struct vmci_handle_arr *doorbellArray;
+
+ /* Doorbells pending for context. */
+ struct vmci_handle_arr *pendingDoorbellArray;
+
+ /* Contexts current context is subscribing to. */
+ struct vmci_handle_arr *notifierArray;
+ struct vmci_host hostContext;
+ uint32_t privFlags;
+ uid_t user;
+ bool validUser;
+ bool *notify; /* Notify flag pointer - hosted only. */
+ struct page *notifyPage; /* Page backing the notify UVA. */
+};
+
+/* VMCINotifyAddRemoveInfo: Used to add/remove remote context notifications. */
+struct vmci_ctx_info {
+ uint32_t remoteCID;
+ int result;
+};
+
+/* VMCICptBufInfo: Used to set/get current context's checkpoint state. */
+struct vmci_ctx_chkpt_buf_info {
+ uint64_t cptBuf;
+ uint32_t cptType;
+ uint32_t bufSize;
+ int32_t result;
+ uint32_t _pad;
+};
+
+/*
+ * VMCINotificationReceiveInfo: Used to recieve pending notifications
+ * for doorbells and queue pairs.
+ */
+struct vmci_ctx_notify_recv_info {
+ uint64_t dbHandleBufUVA;
+ uint64_t dbHandleBufSize;
+ uint64_t qpHandleBufUVA;
+ uint64_t qpHandleBufSize;
+ int32_t result;
+ uint32_t _pad;
+};
+
+int vmci_ctx_init(void);
+int vmci_ctx_init_ctx(uint32_t cid, uint32_t flags,
+ uintptr_t eventHnd, int version,
+ uid_t *user, struct vmci_ctx **context);
+
+bool vmci_ctx_supports_host_qp(struct vmci_ctx *context);
+void vmci_ctx_release_ctx(struct vmci_ctx *context);
+int vmci_ctx_enqueue_dg(uint32_t cid, struct vmci_dg *dg);
+int vmci_ctx_dequeue_dg(struct vmci_ctx *context,
+ size_t *maxSize, struct vmci_dg **dg);
+int vmci_ctx_pending_dgs(uint32_t cid, uint32_t *pending);
+struct vmci_ctx *vmci_ctx_get(uint32_t cid);
+void vmci_ctx_release(struct vmci_ctx *context);
+bool vmci_ctx_exists(uint32_t cid);
+
+uint32_t vmci_ctx_get_id(struct vmci_ctx *context);
+int vmci_ctx_add_notification(uint32_t contextID, uint32_t remoteCID);
+int vmci_ctx_remove_notification(uint32_t contextID, uint32_t remoteCID);
+int vmci_ctx_get_chkpt_state(uint32_t contextID, uint32_t cptType,
+ uint32_t *numCIDs, char **cptBufPtr);
+int vmci_ctx_set_chkpt_state(uint32_t contextID, uint32_t cptType,
+ uint32_t numCIDs, char *cptBuf);
+
+int vmci_ctx_qp_create(struct vmci_ctx *context,
+ struct vmci_handle handle);
+int vmci_ctx_qp_destroy(struct vmci_ctx *context,
+ struct vmci_handle handle);
+bool vmci_ctx_qp_exists(struct vmci_ctx *context,
+ struct vmci_handle handle);
+
+void vmci_ctx_check_signal_notify(struct vmci_ctx *context);
+void vmci_ctx_unset_notify(struct vmci_ctx *context);
+
+int vmci_ctx_dbell_create(uint32_t contextID, struct vmci_handle handle);
+int vmci_ctx_dbell_destroy(uint32_t contextID, struct vmci_handle handle);
+int vmci_ctx_dbell_destroy_all(uint32_t contextID);
+int vmci_ctx_notify_dbell(uint32_t cid, struct vmci_handle handle,
+ uint32_t srcPrivFlags);
+
+int vmci_ctx_rcv_notifications_get(uint32_t contextID, struct vmci_handle_arr
+ **dbHandleArray, struct vmci_handle_arr
+ **qpHandleArray);
+void
+vmci_ctx_rcv_notifications_release(uint32_t contextID, struct vmci_handle_arr
+ *dbHandleArray, struct vmci_handle_arr
+ *qpHandleArray, bool success);
+#endif /* _VMCI_CONTEXT_H_ */
--
1.7.0.4
next prev parent reply other threads:[~2012-07-26 23:39 UTC|newest]
Thread overview: 72+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-26 23:39 [vmw_vmci 00/11] VMCI for Linux Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg) [this message]
2012-07-26 23:39 ` [vmw_vmci 01/11] Apply VMCI context code Andrew Stiegmann (stieg)
2012-07-26 23:48 ` Greg KH
2012-07-26 23:48 ` Greg KH
2012-07-27 0:01 ` Andrew Stiegmann
2012-07-27 0:01 ` Andrew Stiegmann
2012-07-26 23:39 ` [vmw_vmci 02/11] Apply VMCI datagram code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 03/11] Apply VMCI doorbell code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 04/11] Apply VMCI driver code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 05/11] Apply VMCI event code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 06/11] Apply dynamic array code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 07/11] Apply VMCI hash table Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:49 ` Greg KH
2012-07-26 23:49 ` Greg KH
2012-07-27 0:01 ` Andrew Stiegmann
2012-07-27 0:01 ` Andrew Stiegmann
2012-07-26 23:39 ` [vmw_vmci 08/11] Apply VMCI queue pairs Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 09/11] Apply VMCI resource code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 10/11] Apply vmci routing code Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:39 ` [vmw_vmci 11/11] Apply the header code to make VMCI build Andrew Stiegmann (stieg)
2012-07-26 23:39 ` Andrew Stiegmann (stieg)
2012-07-26 23:56 ` Greg KH
2012-07-26 23:56 ` Greg KH
2012-07-27 9:53 ` Alan Cox
2012-07-27 9:53 ` Alan Cox
2012-07-27 18:04 ` [Pv-drivers] " Dmitry Torokhov
2012-07-27 18:04 ` Dmitry Torokhov
2012-07-27 10:34 ` Sam Ravnborg
2012-07-27 10:34 ` Sam Ravnborg
2012-07-27 17:20 ` Andrew Stiegmann
2012-07-27 17:20 ` Andrew Stiegmann
2012-07-27 18:16 ` Greg KH
2012-07-27 18:16 ` Greg KH
2012-07-27 18:39 ` Andrew Stiegmann
2012-07-27 18:39 ` Andrew Stiegmann
2012-07-27 18:52 ` Greg KH
2012-07-27 18:52 ` Greg KH
2012-07-27 20:29 ` [Pv-drivers] " Dmitry Torokhov
2012-07-27 20:29 ` Dmitry Torokhov
2012-07-28 19:55 ` Greg KH
2012-07-28 19:55 ` Greg KH
2012-07-28 21:10 ` Dmitry Torokhov
2012-07-28 21:10 ` Dmitry Torokhov
2012-07-27 19:53 ` Sam Ravnborg
2012-07-27 19:53 ` Sam Ravnborg
2012-07-27 20:07 ` Andrew Stiegmann
2012-07-27 20:07 ` Andrew Stiegmann
2012-08-02 19:50 ` Jan Engelhardt
2012-08-02 19:50 ` Jan Engelhardt
2012-08-02 20:22 ` Sam Ravnborg
2012-08-02 20:22 ` Sam Ravnborg
2012-08-15 20:45 ` Jan Engelhardt
2012-08-15 20:45 ` Jan Engelhardt
2012-07-26 23:47 ` [vmw_vmci 00/11] VMCI for Linux Greg KH
2012-07-26 23:47 ` Greg KH
2012-07-27 1:06 ` Josh Boyer
2012-07-27 1:06 ` Josh Boyer
2012-07-27 1:46 ` Greg KH
2012-07-27 1:46 ` Greg KH
2012-07-31 12:48 ` Josh Boyer
2012-07-31 12:48 ` Josh Boyer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1343345980-32397-2-git-send-email-astiegmann@vmware.com \
--to=astiegmann@vmware.com \
--cc=cschamp@vmware.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pv-drivers@vmware.com \
--cc=virtualization@lists.linux-foundation.org \
--cc=vm-crosstalk@vmware.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.