lkml.org 
[lkml]   [2012]   [May]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[vmw_vmci RFC 01/11] Apply VMCI context code
    Date
    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 | 1743 ++++++++++++++++++++++++++++++++++
    drivers/misc/vmw_vmci/vmci_context.h | 150 +++
    2 files changed, 1893 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..16b10b9
    --- /dev/null
    +++ b/drivers/misc/vmw_vmci/vmci_context.c
    @@ -0,0 +1,1743 @@
    +/*
    + * 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;
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * ctx_signal_notify --
    + *
    + * Sets the notify flag to true. Assumes that the context lock is
    + * held.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +static void ctx_signal_notify(struct vmci_ctx *context)
    +{
    + if (context->notify)
    + *context->notify = true;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * ctx_clear_notify --
    + *
    + * Sets the notify flag to false. Assumes that the context lock is
    + * held.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +static void ctx_clear_notify(struct vmci_ctx *context)
    +{
    + if (context->notify)
    + *context->notify = false;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * ctx_clear_notify_call --
    + *
    + * If nothing requires the attention of the guest, clears both
    + * notify flag and call.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +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);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_check_signal_notify --
    + *
    + * Sets the context's notify flag iff datagrams are pending for this
    + * context. Called from vmci_setup_notify().
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +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);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_init --
    + *
    + * Initializes the VMCI context module.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * ctx_exists_locked --
    + *
    + * Internal helper to check if a context with the specified context
    + * ID exists. Assumes the ctx_list.lock is held.
    + *
    + * Results:
    + * true if a context exists with the given cid.
    + * false otherwise
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +static bool ctx_exists_locked(uint32_t cid) // IN
    +{
    + struct vmci_ctx *context;
    +
    + list_for_each_entry(context, &ctx_list.head, listItem) {
    + if (context->cid == cid)
    + return true;
    + }
    +
    + return false;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_init_ctx --
    + *
    + * Allocates and initializes a VMCI context.
    + *
    + * Results:
    + * Returns 0 on success, appropriate error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_release_ctx --
    + *
    + * Cleans up a VMCI context.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +void vmci_ctx_release_ctx(struct vmci_ctx *context) // IN
    +{
    + /* Dequeue VMCI context. */
    +
    + spin_lock(&ctx_list.lock);
    + list_del(&context->listItem);
    + spin_unlock(&ctx_list.lock);
    +
    + vmci_ctx_release(context);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * ctx_fire_notification --
    + *
    + * Fire notification for all contexts interested in given cid.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +static int ctx_fire_notification(uint32_t contextID, // IN
    + uint32_t privFlags) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *-----------------------------------------------------------------------------
    + *
    + * ctx_free_ctx --
    + *
    + * 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.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * Paged memory is freed.
    + *
    + *-----------------------------------------------------------------------------
    + */
    +
    +static void ctx_free_ctx(struct vmci_ctx *context) // IN
    +{
    + 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);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_pending_dgs --
    + *
    + * 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 vmci_ctx_pending_dgs(uint32_t cid, // IN
    + uint32_t * pending) // OUT
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_enqueue_dg --
    + *
    + * 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 vmci_ctx_enqueue_dg(uint32_t cid, // IN: Target VM
    + struct vmci_dg *dg) // IN:
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_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 vmci_ctx_exists(uint32_t cid) // IN
    +{
    + bool rv;
    +
    + spin_lock(&ctx_list.lock);
    + rv = ctx_exists_locked(cid);
    + spin_unlock(&ctx_list.lock);
    + return rv;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_get --
    + *
    + * Retrieves VMCI context corresponding to the given cid.
    + *
    + * Results:
    + * VMCI context on success, NULL otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +struct vmci_ctx *vmci_ctx_get(uint32_t cid) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_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 vmci_ctx_release(struct vmci_ctx *context) // IN
    +{
    + ASSERT(context);
    + if (atomic_dec_and_test(&context->refCount))
    + ctx_free_ctx(context);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_dequeue_dg --
    + *
    + * 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 vmci_ctx_dequeue_dg(struct vmci_ctx *context, // IN
    + size_t * maxSize, // IN/OUT: max size of
    + // datagram caller can handle.
    + struct vmci_dg **dg) // OUT:
    +{
    + 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;
    +}
    +
    +/*
    + *-----------------------------------------------------------------------------
    + *
    + * vmci_ctx_unset_notify --
    + *
    + * Reverts actions set up by vmci_setup_notify(). Unmaps and unlocks the
    + * page mapped/locked by vmci_setup_notify().
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *-----------------------------------------------------------------------------
    + */
    +
    +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);
    +
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_get_id --
    + *
    + * Retrieves context id of given VMCI context.
    + *
    + * Results:
    + * uint32_t of context on success, VMCI_INVALID_ID otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +uint32_t vmci_ctx_get_id(struct vmci_ctx * 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.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +uint32_t VMCIContext_GetPrivFlags(uint32_t contextID) // IN
    +{
    + 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_ctx_add_notification --
    + *
    + * Add remoteCID to list of contexts current contexts wants
    + * notifications from/about.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, error code otherwise.
    + *
    + * Side effects:
    + * As in vmci_handle_arr_append_entry().
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_add_notification(uint32_t contextID, // IN:
    + uint32_t remoteCID) // IN:
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_remove_notification --
    + *
    + * 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 vmci_ctx_remove_notification(uint32_t contextID, // IN:
    + uint32_t remoteCID) // IN:
    +{
    + 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_handle_arr_remove_entry(context->notifierArray,
    + vmci_make_handle(remoteCID,
    + VMCI_EVENT_HANDLER));
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_get_chkpt_state --
    + *
    + * Get current context's checkpoint state of given type.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_get_chkpt_state(uint32_t contextID, // IN:
    + uint32_t cptType, // IN:
    + uint32_t * bufSize, // IN/OUT:
    + char **cptBufPtr) // OUT:
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_set_chkpt_state --
    + *
    + * Set current context's checkpoint state of given type.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_set_chkpt_state(uint32_t contextID, // IN:
    + uint32_t cptType, // IN:
    + uint32_t bufSize, // IN:
    + char *cptBuf) // IN:
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_rcv_notifications_get --
    + *
    + * 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.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_rcv_notifications_get(uint32_t contextID, // IN
    + struct vmci_handle_arr **dbHandleArray, // OUT
    + struct vmci_handle_arr **qpHandleArray) // OUT
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_rcv_notifications_release --
    + *
    + * 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.
    + *
    + * Results:
    + * None.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +void vmci_ctx_rcv_notifications_release(uint32_t contextID, // IN
    + struct vmci_handle_arr *dbHandleArray, // IN
    + struct vmci_handle_arr *qpHandleArray, // IN
    + bool success) // IN
    +{
    + struct vmci_ctx *context = vmci_ctx_get(contextID);
    +
    + if (context) {
    + 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);
    + } 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)
    + vmci_handle_arr_destroy(dbHandleArray);
    +
    + if (qpHandleArray)
    + vmci_handle_arr_destroy(qpHandleArray);
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_dbell_create --
    + *
    + * 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 vmci_ctx_dbell_create(uint32_t contextID, // IN
    + struct vmci_handle handle) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_dbell_destroy --
    + *
    + * Unregisters a doorbell handle that was previously registered
    + * with vmci_ctx_dbell_create.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, appropriate error code otherewise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_dbell_destroy(uint32_t contextID, // IN
    + struct vmci_handle handle) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_dbell_destroy_all --
    + *
    + * Unregisters all doorbell handles that were previously
    + * registered with vmci_ctx_dbell_create.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, appropriate error code otherewise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_dbell_destroy_all(uint32_t contextID) // IN
    +{
    + struct vmci_ctx *context;
    + struct vmci_handle removedHandle;
    +
    + 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 {
    + removedHandle =
    + vmci_handle_arr_remove_tail(context->doorbellArray);
    + } while (!VMCI_HANDLE_INVALID(removedHandle));
    + do {
    + removedHandle =
    + vmci_handle_arr_remove_tail(context->pendingDoorbellArray);
    + } while (!VMCI_HANDLE_INVALID(removedHandle));
    + spin_unlock(&context->lock);
    +
    + vmci_ctx_release(context);
    +
    + return VMCI_SUCCESS;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_notify_dbell --
    + *
    + * 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 vmci_ctx_notify_dbell(uint32_t srcCID, // IN
    + struct vmci_handle handle, // IN
    + uint32_t srcPrivFlags) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * 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.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int VMCI_ContextID2HostVmID(uint32_t contextID, // IN
    + void *hostVmID, // OUT
    + size_t hostVmIDLen) // IN
    +{
    + return VMCI_ERROR_UNAVAILABLE;
    +}
    +
    +EXPORT_SYMBOL(VMCI_ContextID2HostVmID);
    +
    +/*
    + *-----------------------------------------------------------------------------
    + *
    + * ctx_compare_user --
    + *
    + * Determines whether the two users are the same.
    + *
    + * Results:
    + * VMCI_SUCCESS if equal, error code otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *-----------------------------------------------------------------------------
    + */
    +
    +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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * 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.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int VMCI_IsContextOwner(uint32_t contextID, // IN
    + void *hostUser) // IN
    +{
    + if (vmci_host_code_active()) {
    + struct vmci_ctx *context;
    + uid_t *user = (uid_t *) 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);
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_supports_host_qp --
    + *
    + * 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:
    + * true if context supports host queue pairs, false otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +bool vmci_ctx_supports_host_qp(struct vmci_ctx * context) // IN: Context structure
    +{
    + return context && context->userVersion >= VMCI_VERSION_HOSTQP;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_qp_create --
    + *
    + * Registers that a new queue pair handle has been allocated by
    + * the context.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, appropriate error code otherewise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_qp_create(struct vmci_ctx *context, // IN: Context structure
    + struct vmci_handle handle) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_qp_destroy --
    + *
    + * Unregisters a queue pair handle that was previously registered
    + * with vmci_ctx_qp_create.
    + *
    + * Results:
    + * VMCI_SUCCESS on success, appropriate error code otherewise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +int vmci_ctx_qp_destroy(struct vmci_ctx *context, // IN: Context structure
    + struct vmci_handle handle) // IN
    +{
    + 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;
    +}
    +
    +/*
    + *----------------------------------------------------------------------
    + *
    + * vmci_ctx_qp_exists --
    + *
    + * Determines whether a given queue pair handle is registered
    + * with the given context.
    + *
    + * Results:
    + * true, if queue pair is registered with context. false, otherwise.
    + *
    + * Side effects:
    + * None.
    + *
    + *----------------------------------------------------------------------
    + */
    +
    +bool vmci_ctx_qp_exists(struct vmci_ctx * context, // IN: Context structure
    + struct vmci_handle handle) // IN
    +{
    + if (context == NULL || VMCI_HANDLE_INVALID(handle))
    + return VMCI_ERROR_INVALID_ARGS;
    +
    + return vmci_handle_arr_has_entry(context->queuePairArray, handle);
    +}
    diff --git a/drivers/misc/vmw_vmci/vmci_context.h b/drivers/misc/vmw_vmci/vmci_context.h
    new file mode 100644
    index 0000000..2a576f9
    --- /dev/null
    +++ b/drivers/misc/vmw_vmci/vmci_context.h
    @@ -0,0 +1,150 @@
    +/*
    + * 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. */
    + int userVersion; /*
    + * Version of the code that created
    + * this context; e.g., VMX.
    + */
    + spinlock_t lock; /* Locks callQueue and handleArrays. */
    + struct vmci_handle_arr *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.
    + */
    + struct vmci_handle_arr *doorbellArray; /* Doorbells created by context. */
    + struct vmci_handle_arr *pendingDoorbellArray; /* Doorbells pending for context. */
    + struct vmci_handle_arr *notifierArray; /* Contexts current context is subscribing to. */
    + 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


    \
     
     \ /
      Last update: 2012-05-15 22:02    [W:0.135 / U:0.476 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site