[PATCH 10/14] Add accessor methods for Queue Pairs in VMCI

From: Andrew Stiegmann (stieg)
Date: Tue Feb 14 2012 - 20:17:10 EST


---
drivers/misc/vmw_vmci/vmciQPair.c | 1164 +++++++++++++++++++++++++++++++++++++
drivers/misc/vmw_vmci/vmciQueue.h | 108 ++++
2 files changed, 1272 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/vmw_vmci/vmciQPair.c
create mode 100644 drivers/misc/vmw_vmci/vmciQueue.h

diff --git a/drivers/misc/vmw_vmci/vmciQPair.c b/drivers/misc/vmw_vmci/vmciQPair.c
new file mode 100644
index 0000000..3cb5dbd
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmciQPair.c
@@ -0,0 +1,1164 @@
+/*
+ *
+ * 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
+ *
+ *
+ * This file implements Queue accessor methods.
+ *
+ * VMCIQPair is a new interface that hides the queue pair internals.
+ * Rather than access each queue in a pair directly, operations are now
+ * performed on the queue as a whole. This is simpler and less
+ * error-prone, and allows for future queue pair features to be added
+ * under the hood with no change to the client code.
+ *
+ * This also helps in a particular case on Windows hosts, where the memory
+ * allocated by the client (e.g., VMX) will disappear when the client does
+ * (e.g., abnormal termination). The kernel can't lock user memory into
+ * its address space indefinitely. By guarding access to the queue
+ * contents we can correctly handle the case where the client disappears.
+ *
+ * On code style:
+ *
+ * + This entire file started its life as a cut-and-paste of the
+ * static inline functions in bora/public/vmci_queue_pair.h.
+ * From there, new copies of the routines were made named
+ * without the prefix VMCI, without the underscore (the one
+ * that followed struct vmci_queue_). The no-underscore versions of
+ * the routines require that the mutexes are held.
+ *
+ * + The code -always- uses the xyzLocked() version of any given
+ * routine even when the wrapped function is a one-liner. The
+ * reason for this decision was to ensure that we didn't have
+ * copies of logic lying around that needed to be maintained.
+ *
+ * + Note that we still pass around 'const struct vmci_queue *'s.
+ *
+ * + Note that mutex is a field within struct vmci_queue. We skirt the
+ * issue of passing around a const struct vmci_queue, even though the
+ * mutex field (__mutex, specifically) will get modified by not
+ * ever referring to the mutex -itself- except during
+ * initialization. Beyond that, the code only passes the
+ * pointer to the mutex, which is also a member of struct vmci_queue,
+ * obviously, and which doesn't change after initialization.
+ * This eliminates having to redefine all the functions that
+ * are currently taking const struct vmci_queue's so that these
+ * functions are compatible with those definitions.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+
+#include "vmci_defs.h"
+#include "vmci_kernel_if.h"
+#include "vmci_handle_array.h"
+#include "vmciKernelAPI.h"
+#include "vmciQueuePair.h"
+#include "vmciRoute.h"
+
+/*
+ * VMCIQPair
+ *
+ * This structure is opaque to the clients.
+ */
+
+struct VMCIQPair {
+ struct vmci_handle handle;
+ struct vmci_queue *produceQ;
+ struct vmci_queue *consumeQ;
+ uint64_t produceQSize;
+ uint64_t consumeQSize;
+ uint32_t peer;
+ uint32_t flags;
+ uint32_t privFlags;
+ bool guestEndpoint;
+ uint32_t blocked;
+ wait_queue_head_t event;
+};
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPairMapQueueHeaders --
+ *
+ * The queue headers may not be mapped at all times. If a queue is
+ * currently not mapped, it will be attempted to do so.
+ *
+ * Results:
+ * VMCI_SUCCESS if queues were validated, appropriate error code otherwise.
+ *
+ * Side effects:
+ * May attempt to map in guest memory.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int VMCIQPairMapQueueHeaders(struct vmci_queue *produceQ, // IN
+ struct vmci_queue *consumeQ) // IN
+{
+ int result;
+
+ if (NULL == produceQ->qHeader || NULL == consumeQ->qHeader) {
+ result = VMCIHost_MapQueueHeaders(produceQ, consumeQ);
+ if (result < VMCI_SUCCESS) {
+ if (produceQ->savedHeader && consumeQ->savedHeader) {
+ return VMCI_ERROR_QUEUEPAIR_NOT_READY;
+ } else {
+ return VMCI_ERROR_QUEUEPAIR_NOTATTACHED;
+ }
+ }
+ }
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPairGetQueueHeaders --
+ *
+ * Helper routine that will retrieve the produce and consume
+ * headers of a given queue pair. If the guest memory of the
+ * queue pair is currently not available, the saved queue headers
+ * will be returned, if these are available.
+ *
+ * Results:
+ * VMCI_SUCCESS if either current or saved queue headers are found.
+ * Appropriate error code otherwise.
+ *
+ * Side effects:
+ * May block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int VMCIQPairGetQueueHeaders(const VMCIQPair * qpair, // IN
+ struct vmci_queue_header **produceQHeader, // OUT
+ struct vmci_queue_header **consumeQHeader) // OUT
+{
+ int result;
+
+ result = VMCIQPairMapQueueHeaders(qpair->produceQ, qpair->consumeQ);
+ if (result == VMCI_SUCCESS) {
+ *produceQHeader = qpair->produceQ->qHeader;
+ *consumeQHeader = qpair->consumeQ->qHeader;
+ } else if (qpair->produceQ->savedHeader && qpair->consumeQ->savedHeader) {
+ ASSERT(!qpair->guestEndpoint);
+ *produceQHeader = qpair->produceQ->savedHeader;
+ *consumeQHeader = qpair->consumeQ->savedHeader;
+ result = VMCI_SUCCESS;
+ }
+
+ return result;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPairWakeupCB --
+ *
+ * Callback from VMCI queue pair broker indicating that a queue
+ * pair that was previously not ready, now either is ready or
+ * gone forever.
+ *
+ * Results:
+ * VMCI_SUCCESS always.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int VMCIQPairWakeupCB(void *clientData)
+{
+ VMCIQPair *qpair = (VMCIQPair *) clientData;
+ ASSERT(qpair);
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ while (qpair->blocked > 0) {
+ qpair->blocked--;
+ wake_up(&qpair->event);
+ }
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return VMCI_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPairReleaseMutexCB --
+ *
+ * Callback from VMCI_WaitOnEvent releasing the queue pair mutex
+ * protecting the queue pair header state.
+ *
+ * Results:
+ * 0 always.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int VMCIQPairReleaseMutexCB(void *clientData)
+{
+ VMCIQPair *qpair = (VMCIQPair *) clientData;
+ ASSERT(qpair);
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+ return 0;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPairWaitForReadyQueue --
+ *
+ * Makes the calling thread wait for the queue pair to become
+ * ready for host side access.
+ *
+ * Results:
+ * true when thread is woken up after queue pair state change.
+ * false otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static bool VMCIQPairWaitForReadyQueue(VMCIQPair * qpair)
+{
+ if (unlikely(qpair->guestEndpoint))
+ ASSERT(false);
+
+ if (qpair->flags & VMCI_QPFLAG_NONBLOCK)
+ return false;
+
+ qpair->blocked++;
+ VMCI_WaitOnEvent(&qpair->event, VMCIQPairReleaseMutexCB, qpair);
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ return true;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_Alloc --
+ *
+ * This is the client interface for allocating the memory for a
+ * VMCIQPair structure and then attaching to the underlying
+ * queue. If an error occurs allocating the memory for the
+ * VMCIQPair structure, no attempt is made to attach. If an
+ * error occurs attaching, then there's the VMCIQPair structure
+ * is freed.
+ *
+ * Results:
+ * An err, if < 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int VMCIQPair_Alloc(VMCIQPair ** qpair, // OUT
+ struct vmci_handle *handle, // OUT
+ uint64_t produceQSize, // IN
+ uint64_t consumeQSize, // IN
+ uint32_t peer, // IN
+ uint32_t flags, // IN
+ uint32_t privFlags) // IN
+{
+ VMCIQPair *myQPair;
+ int retval;
+ struct vmci_handle src = VMCI_INVALID_HANDLE;
+ struct vmci_handle dst = VMCI_MAKE_HANDLE(peer, VMCI_INVALID_ID);
+ enum vmci_route route;
+ VMCIEventReleaseCB wakeupCB;
+ void *clientData;
+
+ /*
+ * Restrict the size of a queuepair. The device already enforces a limit
+ * on the total amount of memory that can be allocated to queuepairs for a
+ * guest. However, we try to allocate this memory before we make the
+ * queuepair allocation hypercall. On Windows and Mac OS, we request a
+ * single, continguous block, and it will fail if the OS cannot satisfy the
+ * request. On Linux, we allocate each page separately, which means rather
+ * than fail, the guest will thrash while it tries to allocate, and will
+ * become increasingly unresponsive to the point where it appears to be hung.
+ * So we place a limit on the size of an individual queuepair here, and
+ * leave the device to enforce the restriction on total queuepair memory.
+ * (Note that this doesn't prevent all cases; a user with only this much
+ * physical memory could still get into trouble.) The error used by the
+ * device is NO_RESOURCES, so use that here too.
+ */
+
+ if (produceQSize + consumeQSize < max(produceQSize, consumeQSize)
+ || produceQSize + consumeQSize > VMCI_MAX_GUEST_QP_MEMORY)
+ return VMCI_ERROR_NO_RESOURCES;
+
+ myQPair = kmalloc(sizeof *myQPair, GFP_KERNEL);
+ if (!myQPair)
+ return VMCI_ERROR_NO_MEM;
+
+ memset(myQPair, 0, sizeof *myQPair);
+ myQPair->produceQSize = produceQSize;
+ myQPair->consumeQSize = consumeQSize;
+ myQPair->peer = peer;
+ myQPair->flags = flags;
+ myQPair->privFlags = privFlags;
+ retval = VMCI_Route(&src, &dst, false, &route);
+ if (retval < VMCI_SUCCESS) {
+ if (VMCI_GuestPersonalityActive()) {
+ route = VMCI_ROUTE_AS_GUEST;
+ } else {
+ route = VMCI_ROUTE_AS_HOST;
+ }
+ }
+
+ wakeupCB = clientData = NULL;
+ if (VMCI_ROUTE_AS_HOST == route) {
+ myQPair->guestEndpoint = false;
+ if (!(flags & VMCI_QPFLAG_LOCAL)) {
+ myQPair->blocked = 0;
+ init_waitqueue_head(&myQPair->event);
+ wakeupCB = VMCIQPairWakeupCB;
+ clientData = (void *)myQPair;
+ }
+ } else {
+ myQPair->guestEndpoint = true;
+ }
+
+ retval = VMCIQueuePair_Alloc(handle,
+ &myQPair->produceQ,
+ myQPair->produceQSize,
+ &myQPair->consumeQ,
+ myQPair->consumeQSize,
+ myQPair->peer,
+ myQPair->flags,
+ myQPair->privFlags,
+ myQPair->guestEndpoint,
+ wakeupCB, clientData);
+
+ if (retval < VMCI_SUCCESS) {
+ kfree(myQPair);
+ return retval;
+ }
+
+ *qpair = myQPair;
+ myQPair->handle = *handle;
+
+ return retval;
+}
+
+EXPORT_SYMBOL(VMCIQPair_Alloc);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_Detach --
+ *
+ * This is the client interface for detaching from a VMCIQPair.
+ * Note that this routine will free the memory allocated for the
+ * VMCIQPair structure, too.
+ *
+ * Results:
+ * An error, if < 0.
+ *
+ * Side effects:
+ * Will clear the caller's pointer to the VMCIQPair structure.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int VMCIQPair_Detach(VMCIQPair ** qpair) // IN/OUT
+{
+ int result;
+ VMCIQPair *oldQPair;
+
+ if (!qpair || !(*qpair)) {
+ return VMCI_ERROR_INVALID_ARGS;
+ }
+
+ oldQPair = *qpair;
+ result =
+ VMCIQueuePair_Detach(oldQPair->handle, oldQPair->guestEndpoint);
+
+ /*
+ * The guest can fail to detach for a number of reasons, and if it does so,
+ * it will cleanup the entry (if there is one). The host can fail too, but
+ * it won't cleanup the entry immediately, it will do that later when the
+ * context is freed. Either way, we need to release the qpair struct here;
+ * there isn't much the caller can do, and we don't want to leak.
+ */
+
+ memset(oldQPair, 0, sizeof *oldQPair);
+ oldQPair->handle = VMCI_INVALID_HANDLE;
+ oldQPair->peer = VMCI_INVALID_ID;
+ kfree(oldQPair);
+ *qpair = NULL;
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_Detach);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_GetProduceIndexes --
+ *
+ * This is the client interface for getting the current indexes of the
+ * QPair from the point of the view of the caller as the producer.
+ *
+ * Results:
+ * err, if < 0
+ * Success otherwise.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int VMCIQPair_GetProduceIndexes(const VMCIQPair * qpair, // IN
+ uint64_t * producerTail, // OUT
+ uint64_t * consumerHead) // OUT
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS)
+ VMCIQueueHeader_GetPointers(produceQHeader, consumeQHeader,
+ producerTail, consumerHead);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ if (result == VMCI_SUCCESS &&
+ ((producerTail && *producerTail >= qpair->produceQSize) ||
+ (consumerHead && *consumerHead >= qpair->produceQSize)))
+ return VMCI_ERROR_INVALID_SIZE;
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_GetProduceIndexes);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_GetConsumeIndexes --
+ *
+ * This is the client interface for getting the current indexes of the
+ * QPair from the point of the view of the caller as the consumer.
+ *
+ * Results:
+ * err, if < 0
+ * Success otherwise.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int VMCIQPair_GetConsumeIndexes(const VMCIQPair * qpair, // IN
+ uint64_t * consumerTail, // OUT
+ uint64_t * producerHead) // OUT
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS)
+ VMCIQueueHeader_GetPointers(consumeQHeader, produceQHeader,
+ consumerTail, producerHead);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ if (result == VMCI_SUCCESS &&
+ ((consumerTail && *consumerTail >= qpair->consumeQSize) ||
+ (producerHead && *producerHead >= qpair->consumeQSize)))
+ return VMCI_ERROR_INVALID_SIZE;
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_GetConsumeIndexes);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_ProduceFreeSpace --
+ *
+ * This is the client interface for getting the amount of free
+ * space in the QPair from the point of the view of the caller as
+ * the producer which is the common case.
+ *
+ * Results:
+ * Err, if < 0.
+ * Full queue if = 0.
+ * Number of available bytes into which data can be enqueued if > 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int64_t VMCIQPair_ProduceFreeSpace(const VMCIQPair * qpair) // IN
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int64_t result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS) {
+ result =
+ VMCIQueueHeader_FreeSpace(produceQHeader,
+ consumeQHeader,
+ qpair->produceQSize);
+ } else {
+ result = 0;
+ }
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_ProduceFreeSpace);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_ConsumeFreeSpace --
+ *
+ * This is the client interface for getting the amount of free
+ * space in the QPair from the point of the view of the caller as
+ * the consumer which is not the common case (see
+ * VMCIQPair_ProduceFreeSpace(), above).
+ *
+ * Results:
+ * Err, if < 0.
+ * Full queue if = 0.
+ * Number of available bytes into which data can be enqueued if > 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int64_t VMCIQPair_ConsumeFreeSpace(const VMCIQPair * qpair) // IN
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int64_t result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS) {
+ result =
+ VMCIQueueHeader_FreeSpace(consumeQHeader,
+ produceQHeader,
+ qpair->consumeQSize);
+ } else {
+ result = 0;
+ }
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_ConsumeFreeSpace);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_ProduceBufReady --
+ *
+ * This is the client interface for getting the amount of
+ * enqueued data in the QPair from the point of the view of the
+ * caller as the producer which is not the common case (see
+ * VMCIQPair_ConsumeBufReady(), above).
+ *
+ * Results:
+ * Err, if < 0.
+ * Empty queue if = 0.
+ * Number of bytes ready to be dequeued if > 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int64_t VMCIQPair_ProduceBufReady(const VMCIQPair * qpair) // IN
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int64_t result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS) {
+ result =
+ VMCIQueueHeader_BufReady(produceQHeader,
+ consumeQHeader,
+ qpair->produceQSize);
+ } else {
+ result = 0;
+ }
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_ProduceBufReady);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_ConsumeBufReady --
+ *
+ * This is the client interface for getting the amount of
+ * enqueued data in the QPair from the point of the view of the
+ * caller as the consumer which is the normal case.
+ *
+ * Results:
+ * Err, if < 0.
+ * Empty queue if = 0.
+ * Number of bytes ready to be dequeued if > 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int64_t VMCIQPair_ConsumeBufReady(const VMCIQPair * qpair) // IN
+{
+ struct vmci_queue_header *produceQHeader;
+ struct vmci_queue_header *consumeQHeader;
+ int64_t result;
+
+ if (!qpair)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+ result =
+ VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader);
+ if (result == VMCI_SUCCESS) {
+ result =
+ VMCIQueueHeader_BufReady(consumeQHeader,
+ produceQHeader,
+ qpair->consumeQSize);
+ } else {
+ result = 0;
+ }
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_ConsumeBufReady);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * EnqueueLocked --
+ *
+ * Enqueues a given buffer to the produce queue using the provided
+ * function. As many bytes as possible (space available in the queue)
+ * are enqueued.
+ *
+ * Assumes the queue->mutex has been acquired.
+ *
+ * Results:
+ * VMCI_ERROR_QUEUEPAIR_NOSPACE if no space was available to enqueue data.
+ * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
+ * (as defined by the queue size).
+ * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer.
+ * VMCI_ERROR_QUEUEPAIR_NOTATTACHED, if the queue pair pages aren't
+ * available.
+ * Otherwise, the number of bytes written to the queue is returned.
+ *
+ * Side effects:
+ * Updates the tail pointer of the produce queue.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline ssize_t EnqueueLocked(struct vmci_queue *produceQ, // IN
+ struct vmci_queue *consumeQ, // IN
+ const uint64_t produceQSize, // IN
+ const void *buf, // IN
+ size_t bufSize, // IN
+ int bufType, // IN
+ VMCIMemcpyToQueueFunc memcpyToQueue) // IN
+{
+ int64_t freeSpace;
+ uint64_t tail;
+ size_t written;
+ ssize_t result;
+
+ result = VMCIQPairMapQueueHeaders(produceQ, consumeQ);
+ if (unlikely(result != VMCI_SUCCESS))
+ return result;
+
+ freeSpace = VMCIQueueHeader_FreeSpace(produceQ->qHeader,
+ consumeQ->qHeader, produceQSize);
+ if (freeSpace == 0)
+ return VMCI_ERROR_QUEUEPAIR_NOSPACE;
+
+ if (freeSpace < VMCI_SUCCESS)
+ return (ssize_t) freeSpace;
+
+ written = (size_t) (freeSpace > bufSize ? bufSize : freeSpace);
+ tail = VMCIQueueHeader_ProducerTail(produceQ->qHeader);
+ if (likely(tail + written < produceQSize)) {
+ result =
+ memcpyToQueue(produceQ, tail, buf, 0, written, bufType);
+ } else {
+ /* Tail pointer wraps around. */
+
+ const size_t tmp = (size_t) (produceQSize - tail);
+
+ result = memcpyToQueue(produceQ, tail, buf, 0, tmp, bufType);
+ if (result >= VMCI_SUCCESS) {
+ result =
+ memcpyToQueue(produceQ, 0, buf, tmp,
+ written - tmp, bufType);
+ }
+ }
+
+ if (result < VMCI_SUCCESS) {
+ return result;
+ }
+
+ VMCIQueueHeader_AddProducerTail(produceQ->qHeader, written,
+ produceQSize);
+ return written;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DequeueLocked --
+ *
+ * Dequeues data (if available) from the given consume queue. Writes data
+ * to the user provided buffer using the provided function.
+ *
+ * Assumes the queue->mutex has been acquired.
+ *
+ * Results:
+ * VMCI_ERROR_QUEUEPAIR_NODATA if no data was available to dequeue.
+ * VMCI_ERROR_INVALID_SIZE, if any queue pointer is outside the queue
+ * (as defined by the queue size).
+ * VMCI_ERROR_INVALID_ARGS, if an error occured when accessing the buffer.
+ * Otherwise the number of bytes dequeued is returned.
+ *
+ * Side effects:
+ * Updates the head pointer of the consume queue.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static inline ssize_t DequeueLocked(struct vmci_queue *produceQ, // IN
+ struct vmci_queue *consumeQ, // IN
+ const uint64_t consumeQSize, // IN
+ void *buf, // IN
+ size_t bufSize, // IN
+ int bufType, // IN
+ VMCIMemcpyFromQueueFunc memcpyFromQueue, // IN
+ bool updateConsumer) // IN
+{
+ int64_t bufReady;
+ uint64_t head;
+ size_t read;
+ ssize_t result;
+
+ result = VMCIQPairMapQueueHeaders(produceQ, consumeQ);
+ if (unlikely(result != VMCI_SUCCESS))
+ return result;
+
+ bufReady = VMCIQueueHeader_BufReady(consumeQ->qHeader,
+ produceQ->qHeader, consumeQSize);
+ if (bufReady == 0)
+ return VMCI_ERROR_QUEUEPAIR_NODATA;
+
+ if (bufReady < VMCI_SUCCESS)
+ return (ssize_t) bufReady;
+
+ read = (size_t) (bufReady > bufSize ? bufSize : bufReady);
+ head = VMCIQueueHeader_ConsumerHead(produceQ->qHeader);
+ if (likely(head + read < consumeQSize)) {
+ result = memcpyFromQueue(buf, 0, consumeQ, head, read, bufType);
+ } else {
+ /* Head pointer wraps around. */
+
+ const size_t tmp = (size_t) (consumeQSize - head);
+
+ result = memcpyFromQueue(buf, 0, consumeQ, head, tmp, bufType);
+ if (result >= VMCI_SUCCESS) {
+ result =
+ memcpyFromQueue(buf, tmp, consumeQ, 0,
+ read - tmp, bufType);
+ }
+ }
+
+ if (result < VMCI_SUCCESS)
+ return result;
+
+ if (updateConsumer) {
+ VMCIQueueHeader_AddConsumerHead(produceQ->qHeader,
+ read, consumeQSize);
+ }
+
+ return read;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_Enqueue --
+ *
+ * This is the client interface for enqueueing data into the queue.
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes enqueued if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_Enqueue(VMCIQPair * qpair, // IN
+ const void *buf, // IN
+ size_t bufSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ if (!qpair || !buf)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ do {
+ result = EnqueueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->produceQSize,
+ buf, bufSize, bufType,
+ qpair->flags & VMCI_QPFLAG_LOCAL ?
+ VMCIMemcpyToQueueLocal :
+ VMCIMemcpyToQueue);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_Enqueue);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_Dequeue --
+ *
+ * This is the client interface for dequeueing data from the queue.
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes dequeued if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_Dequeue(VMCIQPair * qpair, // IN
+ void *buf, // IN
+ size_t bufSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ if (!qpair || !buf)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ do {
+ result = DequeueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->consumeQSize,
+ buf, bufSize, bufType,
+ qpair->flags & VMCI_QPFLAG_LOCAL ?
+ VMCIMemcpyFromQueueLocal :
+ VMCIMemcpyFromQueue, true);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_Dequeue);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_Peek --
+ *
+ * This is the client interface for peeking into a queue. (I.e.,
+ * copy data from the queue without updating the head pointer.)
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes peeked, if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_Peek(VMCIQPair * qpair, // IN
+ void *buf, // IN
+ size_t bufSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ if (!qpair || !buf)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ do {
+ result = DequeueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->consumeQSize,
+ buf, bufSize, bufType,
+ qpair->flags & VMCI_QPFLAG_LOCAL ?
+ VMCIMemcpyFromQueueLocal :
+ VMCIMemcpyFromQueue, false);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_Peek);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_EnqueueV --
+ *
+ * This is the client interface for enqueueing data into the queue.
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes enqueued if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_EnqueueV(VMCIQPair * qpair, // IN
+ void *iov, // IN
+ size_t iovSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ if (!qpair || !iov)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ do {
+ result = EnqueueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->produceQSize,
+ iov, iovSize, bufType,
+ VMCIMemcpyToQueueV);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_EnqueueV);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_DequeueV --
+ *
+ * This is the client interface for dequeueing data from the queue.
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes dequeued if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_DequeueV(VMCIQPair * qpair, // IN
+ void *iov, // IN
+ size_t iovSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ if (!qpair || !iov)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ do {
+ result = DequeueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->consumeQSize,
+ iov, iovSize, bufType,
+ VMCIMemcpyFromQueueV, true);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_DequeueV);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIQPair_PeekV --
+ *
+ * This is the client interface for peeking into a queue. (I.e.,
+ * copy data from the queue without updating the head pointer.)
+ *
+ * Results:
+ * Err, if < 0.
+ * Number of bytes peeked, if >= 0.
+ *
+ * Side effects:
+ * Windows blocking call.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+ssize_t VMCIQPair_PeekV(VMCIQPair * qpair, // IN
+ void *iov, // IN
+ size_t iovSize, // IN
+ int bufType) // IN
+{
+ ssize_t result;
+
+ if (!qpair || !iov)
+ return VMCI_ERROR_INVALID_ARGS;
+
+ VMCI_AcquireQueueMutex(qpair->produceQ);
+
+ do {
+ result = DequeueLocked(qpair->produceQ,
+ qpair->consumeQ,
+ qpair->consumeQSize,
+ iov, iovSize, bufType,
+ VMCIMemcpyFromQueueV, false);
+ if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY) {
+ if (!VMCIQPairWaitForReadyQueue(qpair)) {
+ result = VMCI_ERROR_WOULD_BLOCK;
+ }
+ }
+ } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY);
+
+ VMCI_ReleaseQueueMutex(qpair->produceQ);
+
+ return result;
+}
+
+EXPORT_SYMBOL(VMCIQPair_PeekV);
diff --git a/drivers/misc/vmw_vmci/vmciQueue.h b/drivers/misc/vmw_vmci/vmciQueue.h
new file mode 100644
index 0000000..1d7c17c
--- /dev/null
+++ b/drivers/misc/vmw_vmci/vmciQueue.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * 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
+ */
+
+#ifndef _VMCI_QUEUE_H_
+#define _VMCI_QUEUE_H_
+
+/*
+ * struct vmci_queue
+ *
+ * This data type contains the information about a queue.
+ *
+ * There are two queues (hence, queue pairs) per transaction model between a
+ * pair of end points, A & B. One queue is used by end point A to transmit
+ * commands and responses to B. The other queue is used by B to transmit
+ * commands and responses.
+ *
+ * struct vmci_queue_kern_if is a per-OS defined Queue structure. It contains either a
+ * direct pointer to the linear address of the buffer contents or a pointer to
+ * structures which help the OS locate those data pages. See vmciKernelIf.c
+ * for each platform for its definition.
+ */
+
+struct vmci_queue {
+ struct vmci_queue_header *qHeader;
+ struct vmci_queue_header *savedHeader;
+ struct vmci_queue_kern_if *kernelIf;
+};
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}QueueFunc() prototypes. Functions of these
+ * types are passed around to enqueue and dequeue routines. Note that
+ * often the functions passed are simply wrappers around memcpy
+ * itself.
+ *
+ * Note: In order for the memcpy typedefs to be compatible with the VMKernel,
+ * there's an unused last parameter for the hosted side. In
+ * ESX, that parameter holds a buffer type.
+ *
+ *-----------------------------------------------------------------------------
+ */
+typedef int VMCIMemcpyToQueueFunc(struct vmci_queue *queue,
+ uint64_t queueOffset, const void *src,
+ size_t srcOffset, size_t size, int bufType);
+typedef int VMCIMemcpyFromQueueFunc(void *dest, size_t destOffset,
+ const struct vmci_queue *queue,
+ uint64_t queueOffset, size_t size,
+ int bufType);
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * VMCIMemcpy{To,From}Queue[v]() prototypes
+ *
+ * Note that these routines are NOT SAFE to call on a host end-point
+ * until the guest end of the queue pair has attached -AND-
+ * SetPageStore(). The VMX crosstalk device will issue the
+ * SetPageStore() on behalf of the guest when the guest creates a
+ * QueuePair or attaches to one created by the host. So, if the guest
+ * notifies the host that it's attached then the queue is safe to use.
+ * Also, if the host registers notification of the connection of the
+ * guest, then it will only receive that notification when the guest
+ * has issued the SetPageStore() call and not before (when the guest
+ * had attached).
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int VMCIMemcpyToQueue(struct vmci_queue *queue, uint64_t queueOffset,
+ const void *src, size_t srcOffset, size_t size,
+ int bufType);
+int VMCIMemcpyFromQueue(void *dest, size_t destOffset,
+ const struct vmci_queue *queue,
+ uint64_t queueOffset, size_t size, int bufType);
+
+int VMCIMemcpyToQueueLocal(struct vmci_queue *queue, uint64_t queueOffset,
+ const void *src, size_t srcOffset, size_t size,
+ int bufType);
+int VMCIMemcpyFromQueueLocal(void *dest, size_t destOffset,
+ const struct vmci_queue *queue,
+ uint64_t queueOffset, size_t size, int bufType);
+
+int VMCIMemcpyToQueueV(struct vmci_queue *queue, uint64_t queueOffset,
+ const void *src, size_t srcOffset, size_t size,
+ int bufType);
+int VMCIMemcpyFromQueueV(void *dest, size_t destOffset,
+ const struct vmci_queue *queue,
+ uint64_t queueOffset, size_t size, int bufType);
+
+#endif /* !_VMCI_QUEUE_H_ */
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/