From: VMware, Inc <> Date: Mon, 26 Sep 2011 18:41:03 +0000 (-0700) Subject: VMCI queue pair quiesce support X-Git-Tag: 2011.09.23-491607~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=89fe2a179ca8486216bbc142bc7a2388b35a6828;p=thirdparty%2Fopen-vm-tools.git VMCI queue pair quiesce support This change allows the host side of a queue pair to deal with a VM being temporarily stunned in two ways; it may either block in the queue pair enqueue or dequeue operation until the VM becomes available or it can use a non-blocking behaviour, where the operation will return a would block error message. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/lib/include/vmci_defs.h b/open-vm-tools/lib/include/vmci_defs.h index 0c96ea34a..5a9c966b0 100644 --- a/open-vm-tools/lib/include/vmci_defs.h +++ b/open-vm-tools/lib/include/vmci_defs.h @@ -296,6 +296,8 @@ static const VMCIHandle VMCI_INVALID_HANDLE = {VMCI_INVALID_ID, #define VMCI_ERROR_BUSMEM_INVALIDATION (-37) #define VMCI_ERROR_MODULE_NOT_LOADED (-38) #define VMCI_ERROR_DEVICE_NOT_FOUND (-39) +#define VMCI_ERROR_QUEUEPAIR_NOT_READY (-40) +#define VMCI_ERROR_WOULD_BLOCK (-41) /* VMCI clients should return error code withing this range */ #define VMCI_ERROR_CLIENT_MIN (-500) diff --git a/open-vm-tools/modules/linux/shared/vmci_call_defs.h b/open-vm-tools/modules/linux/shared/vmci_call_defs.h index e85807c59..b3d70a08b 100644 --- a/open-vm-tools/modules/linux/shared/vmci_call_defs.h +++ b/open-vm-tools/modules/linux/shared/vmci_call_defs.h @@ -279,8 +279,10 @@ VMCIEventMsgPayload(VMCIEventMsg *eMsg) // IN: /* Flags for VMCI QueuePair API. */ #define VMCI_QPFLAG_ATTACH_ONLY 0x1 /* Fail alloc if QP not created by peer. */ #define VMCI_QPFLAG_LOCAL 0x2 /* Only allow attaches from local context. */ +#define VMCI_QPFLAG_NONBLOCK 0x4 /* Host won't block when guest is quiesced. */ /* Update the following (bitwise OR flags) while adding new flags. */ -#define VMCI_QP_ALL_FLAGS (VMCI_QPFLAG_ATTACH_ONLY | VMCI_QPFLAG_LOCAL) +#define VMCI_QP_ALL_FLAGS (VMCI_QPFLAG_ATTACH_ONLY | VMCI_QPFLAG_LOCAL | \ + VMCI_QPFLAG_NONBLOCK) /* * Structs used for QueuePair alloc and detach messages. We align fields of diff --git a/open-vm-tools/modules/linux/shared/vmci_defs.h b/open-vm-tools/modules/linux/shared/vmci_defs.h index b07d7e85a..68b2273a5 100644 --- a/open-vm-tools/modules/linux/shared/vmci_defs.h +++ b/open-vm-tools/modules/linux/shared/vmci_defs.h @@ -296,6 +296,8 @@ static const VMCIHandle VMCI_INVALID_HANDLE = {VMCI_INVALID_ID, #define VMCI_ERROR_BUSMEM_INVALIDATION (-37) #define VMCI_ERROR_MODULE_NOT_LOADED (-38) #define VMCI_ERROR_DEVICE_NOT_FOUND (-39) +#define VMCI_ERROR_QUEUEPAIR_NOT_READY (-40) +#define VMCI_ERROR_WOULD_BLOCK (-41) /* VMCI clients should return error code withing this range */ #define VMCI_ERROR_CLIENT_MIN (-500) diff --git a/open-vm-tools/modules/linux/vmci/common/vmciContext.c b/open-vm-tools/modules/linux/vmci/common/vmciContext.c index 5a7a7d62c..c89e462bf 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciContext.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciContext.c @@ -2435,6 +2435,51 @@ VMCIContext_SupportsHostQP(VMCIContext *context) // IN: Context structure } +/* + *---------------------------------------------------------------------- + * + * VMCIContext_RegisterGuestMem -- + * + * Tells the context that guest memory is available for access. + * + * Results: + * None. + * + * Side effects: + * Notifies host side endpoints of queue pairs that the queue pairs + * can be accessed. + * + *---------------------------------------------------------------------- + */ + +void +VMCIContext_RegisterGuestMem(VMCIContext *context) // IN: Context structure +{ +#ifdef VMKERNEL + uint32 numQueuePairs; + uint32 cur; + + VMCIQPBroker_Lock(); + numQueuePairs = VMCIHandleArray_GetSize(context->queuePairArray); + for (cur = 0; cur < numQueuePairs; cur++) { + VMCIHandle handle; + handle = VMCIHandleArray_GetEntry(context->queuePairArray, cur); + if (!VMCI_HANDLE_EQUAL(handle, VMCI_INVALID_HANDLE)) { + int res; + + res = VMCIQPBroker_Map(handle, context, NULL); + if (res < VMCI_SUCCESS) { + VMCI_WARNING(("Failed to map guest memory for queue pair " + "(handle=0x%x:0x%x, res=%d).\n", + handle.context, handle.resource, res)); + } + } + } + VMCIQPBroker_Unlock(); +#endif +} + + /* *---------------------------------------------------------------------- * diff --git a/open-vm-tools/modules/linux/vmci/common/vmciContext.h b/open-vm-tools/modules/linux/vmci/common/vmciContext.h index 60bcdf610..6cc781092 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciContext.h +++ b/open-vm-tools/modules/linux/vmci/common/vmciContext.h @@ -91,6 +91,7 @@ int VMCIContext_GetCheckpointState(VMCIId contextID, uint32 cptType, uint32 *numCIDs, char **cptBufPtr); int VMCIContext_SetCheckpointState(VMCIId contextID, uint32 cptType, uint32 numCIDs, char *cptBuf); +void VMCIContext_RegisterGuestMem(VMCIContext *context); void VMCIContext_ReleaseGuestMem(VMCIContext *context, VMCIGuestMemID gid); #ifndef VMX86_SERVER diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQPair.c b/open-vm-tools/modules/linux/vmci/common/vmciQPair.c index 4c7ebedc7..b48d85e58 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciQPair.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciQPair.c @@ -69,6 +69,7 @@ #include "vmciQueuePair.h" #include "vmciRoute.h" + /* * VMCIQPair * @@ -85,12 +86,110 @@ struct VMCIQPair { uint32 flags; VMCIPrivilegeFlags privFlags; Bool guestEndpoint; + uint32 blocked; + VMCIEvent event; }; static int VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, VMCIQueue *consumeQ); +static int VMCIQPairGetQueueHeaders(const VMCIQPair *qpair, + VMCIQueueHeader **produceQHeader, + VMCIQueueHeader **consumeQHeader); +static int VMCIQPairWakeupCB(void *clientData); +static int VMCIQPairReleaseMutexCB(void *clientData); +static Bool VMCIQPairWaitForReadyQueue(VMCIQPair *qpair); + + +/* + *----------------------------------------------------------------------------- + * + * VMCIQPair_Lock -- + * + * Helper routine that will lock the QPair before subsequent operations. + * + * Results: + * None. + * + * Side effects: + * May block. + * + *----------------------------------------------------------------------------- + */ + +static INLINE void +VMCIQPairLock(const VMCIQPair *qpair) // IN +{ +#if !defined VMX86_VMX + VMCI_AcquireQueueMutex(qpair->produceQ); +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * VMCIQPair_Unlock -- + * + * Helper routine that will unlock the QPair after various operations. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static INLINE void +VMCIQPairUnlock(const VMCIQPair *qpair) // IN +{ +#if !defined VMX86_VMX + VMCI_ReleaseQueueMutex(qpair->produceQ); +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * 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 + VMCIQueueHeader **produceQHeader, // OUT + VMCIQueueHeader **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; +} -#define VMCI_QPAIR_NO_QUEUE(_qp) (VMCIQPairMapQueueHeaders(_qp->produceQ, \ - _qp->consumeQ) != VMCI_SUCCESS) /* *----------------------------------------------------------------------------- @@ -104,7 +203,7 @@ static int VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, VMCIQueue *consumeQ); * VMCI_SUCCESS if queues were validated, appropriate error code otherwise. * * Side effects: - * Windows blocking call. + * May attempt to map in guest memory. * *----------------------------------------------------------------------------- */ @@ -118,7 +217,11 @@ VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, // IN if (NULL == produceQ->qHeader || NULL == consumeQ->qHeader) { result = VMCIHost_MapQueueHeaders(produceQ, consumeQ); if (result < VMCI_SUCCESS) { - return result; + if (produceQ->savedHeader && consumeQ->savedHeader) { + return VMCI_ERROR_QUEUEPAIR_NOT_READY; + } else { + return VMCI_ERROR_QUEUEPAIR_NOTATTACHED; + } } } @@ -126,6 +229,103 @@ VMCIQPairMapQueueHeaders(VMCIQueue *produceQ, // IN } +/* + *----------------------------------------------------------------------------- + * + * 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); + + VMCIQPairLock(qpair); + while (qpair->blocked > 0) { + qpair->blocked--; + VMCI_SignalEvent(&qpair->event); + } + VMCIQPairUnlock(qpair); + + 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); + VMCIQPairUnlock(qpair); + 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); + return FALSE; + } + if (qpair->flags & VMCI_QPFLAG_NONBLOCK) { + return FALSE; + } + qpair->blocked++; + VMCI_WaitOnEvent(&qpair->event, VMCIQPairReleaseMutexCB, qpair); + VMCIQPairLock(qpair); + return TRUE; +} + + /* *----------------------------------------------------------------------------- * @@ -162,6 +362,8 @@ VMCIQPair_Alloc(VMCIQPair **qpair, // OUT VMCIHandle src = VMCI_INVALID_HANDLE; VMCIHandle dst = VMCI_MAKE_HANDLE(peer, VMCI_INVALID_ID); VMCIRoute route; + VMCIEventReleaseCB wakeupCB; + void *clientData; /* * Restrict the size of a queuepair. The device already enforces a limit @@ -195,7 +397,6 @@ VMCIQPair_Alloc(VMCIQPair **qpair, // OUT myQPair->peer = peer; myQPair->flags = flags; myQPair->privFlags = privFlags; - retval = VMCI_Route(&src, &dst, FALSE, &route); if (retval < VMCI_SUCCESS) { if (VMCI_GuestPersonalityActive()) { @@ -205,8 +406,15 @@ VMCIQPair_Alloc(VMCIQPair **qpair, // OUT } } + wakeupCB = clientData = NULL; if (VMCI_ROUTE_AS_HOST == route) { myQPair->guestEndpoint = FALSE; + if (!(flags & VMCI_QPFLAG_LOCAL)) { + myQPair->blocked = 0; + VMCI_CreateEvent(&myQPair->event); + wakeupCB = VMCIQPairWakeupCB; + clientData = (void *)myQPair; + } } else { myQPair->guestEndpoint = TRUE; } @@ -219,9 +427,14 @@ VMCIQPair_Alloc(VMCIQPair **qpair, // OUT myQPair->peer, myQPair->flags, myQPair->privFlags, - myQPair->guestEndpoint); + myQPair->guestEndpoint, + wakeupCB, + clientData); if (retval < VMCI_SUCCESS) { + if (VMCI_ROUTE_AS_HOST == route && !(flags & VMCI_QPFLAG_LOCAL)) { + VMCI_DestroyEvent(&myQPair->event); + } VMCI_FreeKernelMem(myQPair, sizeof *myQPair); return retval; } @@ -273,6 +486,9 @@ VMCIQPair_Detach(VMCIQPair **qpair) // IN/OUT * there isn't much the caller can do, and we don't want to leak. */ + if (!(oldQPair->guestEndpoint || (oldQPair->flags & VMCI_QPFLAG_LOCAL))) { + VMCI_DestroyEvent(&oldQPair->event); + } memset(oldQPair, 0, sizeof *oldQPair); oldQPair->handle = VMCI_INVALID_HANDLE; oldQPair->peer = VMCI_INVALID_ID; @@ -283,68 +499,6 @@ VMCIQPair_Detach(VMCIQPair **qpair) // IN/OUT } -/* - * "Windows blocking call." - * - * Note that on the Windows platform, kernel module clients may - * block when calling into any these rouintes. The reason is - * that a mutex has to be acquired in order to view/modify the - * VMCIQueue structure fields: pointers, handle, and buffer data. - * However, other platforms don't require the acquisition of a - * mutex and thus don't block. - */ - - -/* - *----------------------------------------------------------------------------- - * - * VMCIQPair_Lock -- - * - * Helper routine that will lock the QPair before subsequent operations. - * - * Results: - * None. - * - * Side effects: - * Windows blocking call. - * - *----------------------------------------------------------------------------- - */ - -static INLINE void -VMCIQPairLock(const VMCIQPair *qpair) // IN -{ -#if !defined VMX86_VMX - VMCI_AcquireQueueMutex(qpair->produceQ); -#endif -} - - -/* - *----------------------------------------------------------------------------- - * - * VMCIQPair_Unlock -- - * - * Helper routine that will unlock the QPair after various operations. - * - * Results: - * None. - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -static INLINE void -VMCIQPairUnlock(const VMCIQPair *qpair) // IN -{ -#if !defined VMX86_VMX - VMCI_ReleaseQueueMutex(qpair->produceQ); -#endif -} - - /* *----------------------------------------------------------------------------- * @@ -369,6 +523,8 @@ VMCIQPair_GetProduceIndexes(const VMCIQPair *qpair, // IN uint64 *producerTail, // OUT uint64 *consumerHead) // OUT { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int result; if (!qpair) { @@ -376,17 +532,11 @@ VMCIQPair_GetProduceIndexes(const VMCIQPair *qpair, // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; - } else { - VMCIQueueHeader_GetPointers(qpair->produceQ->qHeader, - qpair->consumeQ->qHeader, - producerTail, - consumerHead); - result = VMCI_SUCCESS; + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + VMCIQueueHeader_GetPointers(produceQHeader, consumeQHeader, + producerTail, consumerHead); } - VMCIQPairUnlock(qpair); if (result == VMCI_SUCCESS && @@ -423,6 +573,8 @@ VMCIQPair_GetConsumeIndexes(const VMCIQPair *qpair, // IN uint64 *consumerTail, // OUT uint64 *producerHead) // OUT { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int result; if (!qpair) { @@ -430,17 +582,11 @@ VMCIQPair_GetConsumeIndexes(const VMCIQPair *qpair, // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = VMCI_ERROR_QUEUEPAIR_NOTATTACHED; - } else { - VMCIQueueHeader_GetPointers(qpair->consumeQ->qHeader, - qpair->produceQ->qHeader, - consumerTail, - producerHead); - result = VMCI_SUCCESS; + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + VMCIQueueHeader_GetPointers(consumeQHeader, produceQHeader, + consumerTail, producerHead); } - VMCIQPairUnlock(qpair); if (result == VMCI_SUCCESS && @@ -477,6 +623,8 @@ VMCI_EXPORT_SYMBOL(VMCIQPair_ProduceFreeSpace) int64 VMCIQPair_ProduceFreeSpace(const VMCIQPair *qpair) // IN { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int64 result; if (!qpair) { @@ -484,15 +632,13 @@ VMCIQPair_ProduceFreeSpace(const VMCIQPair *qpair) // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = 0; - } else { - result = VMCIQueueHeader_FreeSpace(qpair->produceQ->qHeader, - qpair->consumeQ->qHeader, + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + result = VMCIQueueHeader_FreeSpace(produceQHeader, consumeQHeader, qpair->produceQSize); + } else { + result = 0; } - VMCIQPairUnlock(qpair); return result; @@ -524,6 +670,8 @@ VMCI_EXPORT_SYMBOL(VMCIQPair_ConsumeFreeSpace) int64 VMCIQPair_ConsumeFreeSpace(const VMCIQPair *qpair) // IN { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int64 result; if (!qpair) { @@ -531,15 +679,13 @@ VMCIQPair_ConsumeFreeSpace(const VMCIQPair *qpair) // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = 0; - } else { - result = VMCIQueueHeader_FreeSpace(qpair->consumeQ->qHeader, - qpair->produceQ->qHeader, + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + result = VMCIQueueHeader_FreeSpace(consumeQHeader, produceQHeader, qpair->consumeQSize); + } else { + result = 0; } - VMCIQPairUnlock(qpair); return result; @@ -571,6 +717,8 @@ VMCI_EXPORT_SYMBOL(VMCIQPair_ProduceBufReady) int64 VMCIQPair_ProduceBufReady(const VMCIQPair *qpair) // IN { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int64 result; if (!qpair) { @@ -578,15 +726,13 @@ VMCIQPair_ProduceBufReady(const VMCIQPair *qpair) // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = 0; - } else { - result = VMCIQueueHeader_BufReady(qpair->produceQ->qHeader, - qpair->consumeQ->qHeader, + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + result = VMCIQueueHeader_BufReady(produceQHeader, consumeQHeader, qpair->produceQSize); + } else { + result = 0; } - VMCIQPairUnlock(qpair); return result; @@ -617,6 +763,8 @@ VMCI_EXPORT_SYMBOL(VMCIQPair_ConsumeBufReady) int64 VMCIQPair_ConsumeBufReady(const VMCIQPair *qpair) // IN { + VMCIQueueHeader *produceQHeader; + VMCIQueueHeader *consumeQHeader; int64 result; if (!qpair) { @@ -624,15 +772,13 @@ VMCIQPair_ConsumeBufReady(const VMCIQPair *qpair) // IN } VMCIQPairLock(qpair); - - if (UNLIKELY(VMCI_QPAIR_NO_QUEUE(qpair))) { - result = 0; - } else { - result = VMCIQueueHeader_BufReady(qpair->consumeQ->qHeader, - qpair->produceQ->qHeader, + result = VMCIQPairGetQueueHeaders(qpair, &produceQHeader, &consumeQHeader); + if (result == VMCI_SUCCESS) { + result = VMCIQueueHeader_BufReady(consumeQHeader, produceQHeader, qpair->consumeQSize); + } else { + result = 0; } - VMCIQPairUnlock(qpair); return result; @@ -684,8 +830,9 @@ EnqueueLocked(VMCIQueue *produceQ, // IN return (ssize_t) bufSize; } - if (UNLIKELY(VMCIQPairMapQueueHeaders(produceQ, consumeQ) != VMCI_SUCCESS)) { - return VMCI_ERROR_QUEUEPAIR_NOTATTACHED; + result = VMCIQPairMapQueueHeaders(produceQ, consumeQ); + if (UNLIKELY(result != VMCI_SUCCESS)) { + return result; } #endif @@ -763,8 +910,9 @@ DequeueLocked(VMCIQueue *produceQ, // IN ssize_t result; #if !defined VMX86_VMX - if (UNLIKELY(VMCIQPairMapQueueHeaders(produceQ, consumeQ) != VMCI_SUCCESS)) { - return VMCI_ERROR_QUEUEPAIR_NODATA; + result = VMCIQPairMapQueueHeaders(produceQ, consumeQ); + if (UNLIKELY(result != VMCI_SUCCESS)) { + return result; } #endif @@ -839,13 +987,20 @@ VMCIQPair_Enqueue(VMCIQPair *qpair, // IN VMCIQPairLock(qpair); - result = EnqueueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->produceQSize, - buf, bufSize, bufType, - qpair->flags & VMCI_QPFLAG_LOCAL? - VMCIMemcpyToQueueLocal: - VMCIMemcpyToQueue); + 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); VMCIQPairUnlock(qpair); @@ -885,14 +1040,21 @@ VMCIQPair_Dequeue(VMCIQPair *qpair, // IN VMCIQPairLock(qpair); - result = DequeueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->consumeQSize, - buf, bufSize, bufType, - qpair->flags & VMCI_QPFLAG_LOCAL? - VMCIMemcpyFromQueueLocal: - VMCIMemcpyFromQueue, - TRUE); + 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); VMCIQPairUnlock(qpair); @@ -933,14 +1095,21 @@ VMCIQPair_Peek(VMCIQPair *qpair, // IN VMCIQPairLock(qpair); - result = DequeueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->consumeQSize, - buf, bufSize, bufType, - qpair->flags & VMCI_QPFLAG_LOCAL? - VMCIMemcpyFromQueueLocal: - VMCIMemcpyFromQueue, - FALSE); + 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); VMCIQPairUnlock(qpair); @@ -984,11 +1153,18 @@ VMCIQPair_EnqueueV(VMCIQPair *qpair, // IN VMCIQPairLock(qpair); - result = EnqueueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->produceQSize, - iov, iovSize, bufType, - VMCIMemcpyToQueueV); + 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); VMCIQPairUnlock(qpair); @@ -1028,12 +1204,19 @@ VMCIQPair_DequeueV(VMCIQPair *qpair, // IN return VMCI_ERROR_INVALID_ARGS; } - result = DequeueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->consumeQSize, - iov, iovSize, bufType, - VMCIMemcpyFromQueueV, - TRUE); + 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); VMCIQPairUnlock(qpair); @@ -1074,12 +1257,19 @@ VMCIQPair_PeekV(VMCIQPair *qpair, // IN VMCIQPairLock(qpair); - result = DequeueLocked(qpair->produceQ, - qpair->consumeQ, - qpair->consumeQSize, - iov, iovSize, bufType, - VMCIMemcpyFromQueueV, - FALSE); + 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); VMCIQPairUnlock(qpair); diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c index cb988fab7..80f9760d1 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.c @@ -158,6 +158,10 @@ typedef struct QPBrokerEntry { Bool vmciPageFiles; // Created by VMX using VMCI page files VMCIQueue *produceQ; VMCIQueue *consumeQ; + VMCIQueueHeader savedProduceQ; + VMCIQueueHeader savedConsumeQ; + VMCIEventReleaseCB wakeupCB; + void *clientData; void *localMem; // Kernel memory for local queue pair } QPBrokerEntry; @@ -206,6 +210,8 @@ static int VMCIQPBrokerAllocInt(VMCIHandle handle, VMCIId peer, uint64 consumeSize, QueuePairPageStore *pageStore, VMCIContext *context, + VMCIEventReleaseCB wakeupCB, + void *clientData, QPBrokerEntry **ent, Bool *swap); static int VMCIQPBrokerAttach(QPBrokerEntry *entry, @@ -216,6 +222,8 @@ static int VMCIQPBrokerAttach(QPBrokerEntry *entry, uint64 consumeSize, QueuePairPageStore *pageStore, VMCIContext *context, + VMCIEventReleaseCB wakeupCB, + void *clientData, QPBrokerEntry **ent); static int VMCIQPBrokerCreate(VMCIHandle handle, VMCIId peer, @@ -225,14 +233,21 @@ static int VMCIQPBrokerCreate(VMCIHandle handle, uint64 consumeSize, QueuePairPageStore *pageStore, VMCIContext *context, + VMCIEventReleaseCB wakeupCB, + void *clientData, QPBrokerEntry **ent); static int VMCIQueuePairAllocHostWork(VMCIHandle *handle, VMCIQueue **produceQ, uint64 produceSize, VMCIQueue **consumeQ, uint64 consumeSize, VMCIId peer, uint32 flags, - VMCIPrivilegeFlags privFlags); + VMCIPrivilegeFlags privFlags, + VMCIEventReleaseCB wakeupCB, + void *clientData); static int VMCIQueuePairDetachHostWork(VMCIHandle handle); +static int QueuePairSaveHeaders(QPBrokerEntry *entry); +static void QueuePairResetSavedHeaders(QPBrokerEntry *entry); + #if !defined(VMKERNEL) static int QueuePairNotifyPeerLocal(Bool attach, VMCIHandle handle); @@ -286,7 +301,9 @@ VMCIQueuePair_Alloc(VMCIHandle *handle, // IN/OUT VMCIId peer, // IN uint32 flags, // IN VMCIPrivilegeFlags privFlags, // IN - Bool guestEndpoint) // IN + Bool guestEndpoint, // IN + VMCIEventReleaseCB wakeupCB, // IN + void *clientData) // IN { if (!handle || !produceQ || !consumeQ || (!produceSize && !consumeSize) || (flags & ~VMCI_QP_ALL_FLAGS)) { @@ -302,7 +319,8 @@ VMCIQueuePair_Alloc(VMCIHandle *handle, // IN/OUT #endif } else { return VMCIQueuePairAllocHostWork(handle, produceQ, produceSize, consumeQ, - consumeSize, peer, flags, privFlags); + consumeSize, peer, flags, privFlags, + wakeupCB, clientData); } } @@ -699,7 +717,8 @@ VMCIQPBroker_Alloc(VMCIHandle handle, // IN { return VMCIQPBrokerAllocInt(handle, peer, flags, privFlags, produceSize, consumeSize, - pageStore, context, NULL, NULL); + pageStore, context, NULL, NULL, + NULL, NULL); } @@ -801,7 +820,9 @@ VMCIQueuePairAllocHostWork(VMCIHandle *handle, // IN/OUT uint64 consumeSize, // IN VMCIId peer, // IN uint32 flags, // IN - VMCIPrivilegeFlags privFlags) // IN + VMCIPrivilegeFlags privFlags, // IN + VMCIEventReleaseCB wakeupCB, // IN + void *clientData) // IN { VMCIContext *context; QPBrokerEntry *entry; @@ -822,7 +843,8 @@ VMCIQueuePairAllocHostWork(VMCIHandle *handle, // IN/OUT entry = NULL; VMCIQPBroker_Lock(); result = VMCIQPBrokerAllocInt(*handle, peer, flags, privFlags, produceSize, - consumeSize, NULL, context, &entry, &swap); + consumeSize, NULL, context, wakeupCB, clientData, + &entry, &swap); if (result == VMCI_SUCCESS) { if (swap) { /* @@ -908,6 +930,8 @@ VMCIQPBrokerAllocInt(VMCIHandle handle, // IN uint64 consumeSize, // IN QueuePairPageStore *pageStore, // IN/OUT VMCIContext *context, // IN: Caller + VMCIEventReleaseCB wakeupCB, // IN + void *clientData, // IN QPBrokerEntry **ent, // OUT Bool *swap) // OUT: swap queues? { @@ -949,11 +973,13 @@ VMCIQPBrokerAllocInt(VMCIHandle handle, // IN if (!entry) { create = TRUE; result = VMCIQPBrokerCreate(handle, peer, flags, privFlags, produceSize, - consumeSize, pageStore, context, ent); + consumeSize, pageStore, context, wakeupCB, + clientData, ent); } else { create = FALSE; result = VMCIQPBrokerAttach(entry, peer, flags, privFlags, produceSize, - consumeSize, pageStore, context, ent); + consumeSize, pageStore, context, wakeupCB, + clientData, ent); } if (swap) { @@ -1004,6 +1030,8 @@ VMCIQPBrokerCreate(VMCIHandle handle, // IN uint64 consumeSize, // IN QueuePairPageStore *pageStore, // IN VMCIContext *context, // IN: Caller + VMCIEventReleaseCB wakeupCB, // IN + void *clientData, // IN QPBrokerEntry **ent) // OUT { QPBrokerEntry *entry = NULL; @@ -1057,12 +1085,13 @@ VMCIQPBrokerCreate(VMCIHandle handle, // IN entry->createdByTrusted = (privFlags & VMCI_PRIVILEGE_FLAG_TRUSTED) ? TRUE : FALSE; entry->vmciPageFiles = FALSE; + entry->wakeupCB = wakeupCB; + entry->clientData = clientData; entry->produceQ = VMCIHost_AllocQueue(produceSize); if (entry->produceQ == NULL) { result = VMCI_ERROR_NO_MEM; goto error; } - entry->consumeQ = VMCIHost_AllocQueue(consumeSize); if (entry->consumeQ == NULL) { result = VMCI_ERROR_NO_MEM; @@ -1181,6 +1210,8 @@ VMCIQPBrokerAttach(QPBrokerEntry *entry, // IN uint64 consumeSize, // IN QueuePairPageStore *pageStore, // IN/OUT VMCIContext *context, // IN: Caller + VMCIEventReleaseCB wakeupCB, // IN + void *clientData, // IN QPBrokerEntry **ent) // OUT { const VMCIId contextId = VMCIContext_GetId(context); @@ -1335,6 +1366,11 @@ VMCIQPBrokerAttach(QPBrokerEntry *entry, // IN entry->attachId = contextId; entry->qp.refCount++; + if (wakeupCB) { + ASSERT(!entry->wakeupCB); + entry->wakeupCB = wakeupCB; + entry->clientData = clientData; + } /* * When attaching to local queue pairs, the context already has @@ -1581,6 +1617,18 @@ VMCIQPBroker_Detach(VMCIHandle handle, // IN VMCIHost_UnregisterUserMemory(entry->produceQ, entry->consumeQ); } VMCI_ReleaseQueueMutex(entry->produceQ); + } else { + VMCI_AcquireQueueMutex(entry->produceQ); + QueuePairResetSavedHeaders(entry); + VMCI_ReleaseQueueMutex(entry->produceQ); + if (entry->wakeupCB) { + entry->wakeupCB(entry->clientData); + } + } + } else { + if (entry->wakeupCB) { + entry->wakeupCB = NULL; + entry->clientData = NULL; } } @@ -1642,11 +1690,6 @@ VMCIQPBroker_Map(VMCIHandle handle, // IN Bool isLocal = FALSE; int result; - if (vmkernel) { - ASSERT(FALSE); - return VMCI_ERROR_UNAVAILABLE; - } - if (VMCI_HANDLE_INVALID(handle) || !context || contextId == VMCI_INVALID_ID) { return VMCI_ERROR_INVALID_ARGS; } @@ -1672,7 +1715,20 @@ VMCIQPBroker_Map(VMCIHandle handle, // IN isLocal = entry->qp.flags & VMCI_QPFLAG_LOCAL; - if (contextId != VMCI_HOST_CONTEXT_ID) { + if (vmkernel) { + /* + * On vmkernel, the readiness of the queue pair can be signalled + * immediately since the guest memory is already registered. + */ + + VMCI_AcquireQueueMutex(entry->produceQ); + QueuePairResetSavedHeaders(entry); + VMCI_ReleaseQueueMutex(entry->produceQ); + if (entry->wakeupCB) { + entry->wakeupCB(entry->clientData); + } + result = VMCI_SUCCESS; + } else if (contextId != VMCI_HOST_CONTEXT_ID) { QueuePairPageStore pageStore; ASSERT(entry->state == VMCIQPB_CREATED_NO_MEM || @@ -1684,8 +1740,9 @@ VMCIQPBroker_Map(VMCIHandle handle, // IN pageStore.len = QPE_NUM_PAGES(entry->qp); VMCI_AcquireQueueMutex(entry->produceQ); - + QueuePairResetSavedHeaders(entry); result = VMCIHost_RegisterUserMemory(&pageStore, entry->produceQ, entry->consumeQ); + VMCI_ReleaseQueueMutex(entry->produceQ); if (result == VMCI_SUCCESS) { /* * Move state from *_NO_MEM to *_MEM. @@ -1696,8 +1753,11 @@ VMCIQPBroker_Map(VMCIHandle handle, // IN ASSERT(entry->state == VMCIQPB_CREATED_MEM || entry->state == VMCIQPB_SHUTDOWN_MEM || entry->state == VMCIQPB_ATTACHED_MEM); + + if (entry->wakeupCB) { + entry->wakeupCB(entry->clientData); + } } - VMCI_ReleaseQueueMutex(entry->produceQ); } else { result = VMCI_SUCCESS; } @@ -1705,6 +1765,71 @@ VMCIQPBroker_Map(VMCIHandle handle, // IN return result; } + +/* + *----------------------------------------------------------------------------- + * + * QueuePairSaveHeaders -- + * + * Saves a snapshot of the queue headers for the given QP broker + * entry. Should be used when guest memory is unmapped. + * + * Results: + * VMCI_SUCCESS on success, appropriate error code if guest memory + * can't be accessed.. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static int +QueuePairSaveHeaders(QPBrokerEntry *entry) // IN +{ + int result; + + if (NULL == entry->produceQ->qHeader || NULL == entry->consumeQ->qHeader) { + result = VMCIHost_MapQueueHeaders(entry->produceQ, entry->consumeQ); + if (result < VMCI_SUCCESS) { + return result; + } + } + memcpy(&entry->savedProduceQ, entry->produceQ->qHeader, sizeof entry->savedProduceQ); + entry->produceQ->savedHeader = &entry->savedProduceQ; + memcpy(&entry->savedConsumeQ, entry->consumeQ->qHeader, sizeof entry->savedConsumeQ); + entry->consumeQ->savedHeader = &entry->savedConsumeQ; + + return VMCI_SUCCESS; +} + + +/* + *----------------------------------------------------------------------------- + * + * QueuePairResetSavedHeaders -- + * + * Resets saved queue headers for the given QP broker + * entry. Should be used when guest memory becomes available + * again, or the guest detaches. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static void +QueuePairResetSavedHeaders(QPBrokerEntry *entry) // IN +{ + entry->produceQ->savedHeader = NULL; + entry->consumeQ->savedHeader = NULL; +} + + /* *----------------------------------------------------------------------------- * @@ -1759,13 +1884,20 @@ VMCIQPBroker_Unmap(VMCIHandle handle, // IN isLocal = entry->qp.flags & VMCI_QPFLAG_LOCAL; if (contextId != VMCI_HOST_CONTEXT_ID) { + int result; + ASSERT(entry->state != VMCIQPB_CREATED_NO_MEM && entry->state != VMCIQPB_SHUTDOWN_NO_MEM && entry->state != VMCIQPB_ATTACHED_NO_MEM); ASSERT(!isLocal); VMCI_AcquireQueueMutex(entry->produceQ); - + result = QueuePairSaveHeaders(entry); + if (result < VMCI_SUCCESS) { + VMCI_WARNING((LGPFX"Failed to save queue headers for queue pair " + "(handle=0x%x:0x%x,result=%d).\n", handle.context, + handle.resource, result)); + } VMCIHost_UnmapQueueHeaders(gid, entry->produceQ, entry->consumeQ); diff --git a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h index 8b473572a..524f4e189 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h +++ b/open-vm-tools/modules/linux/vmci/common/vmciQueuePair.h @@ -95,7 +95,8 @@ void VMCIQPGuestEndpoints_Convert(Bool toLocal, Bool deviceReset); int VMCIQueuePair_Alloc(VMCIHandle *handle, VMCIQueue **produceQ, uint64 produceSize, VMCIQueue **consumeQ, uint64 consumeSize, VMCIId peer, uint32 flags, - VMCIPrivilegeFlags privFlags, Bool guestEndpoint); + VMCIPrivilegeFlags privFlags, Bool guestEndpoint, + VMCIEventReleaseCB wakeupCB, void *clientData); int VMCIQueuePair_Detach(VMCIHandle handle, Bool guestEndpoint); int VMCIQPBroker_Map(VMCIHandle handle, VMCIContext *context, VMCIQPGuestMem guestMem); int VMCIQPBroker_Unmap(VMCIHandle handle, VMCIContext *context, VMCIGuestMemID gid); diff --git a/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c b/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c index 3344a4002..ab883ec31 100644 --- a/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c +++ b/open-vm-tools/modules/linux/vmci/linux/vmciKernelIf.c @@ -914,6 +914,7 @@ VMCI_AllocQueue(uint64 size) // IN: size of queue (not including header) queue = (VMCIQueue *)((uint8 *)qHeader + PAGE_SIZE); queue->qHeader = qHeader; + queue->savedHeader = NULL; queue->kernelIf = (VMCIQueueKernelIf *)((uint8 *)queue + sizeof *queue); queue->kernelIf->headerPage = NULL; // Unused in guest. queue->kernelIf->page = (struct page **)((uint8 *)queue->kernelIf + @@ -1513,6 +1514,7 @@ VMCIHost_AllocQueue(uint64 size) // IN: queue = VMCI_AllocKernelMem(queueSize + queuePageSize, VMCI_MEMORY_NORMAL); if (queue) { queue->qHeader = NULL; + queue->savedHeader = NULL; queue->kernelIf = (VMCIQueueKernelIf *)((uint8 *)queue + sizeof *queue); queue->kernelIf->host = TRUE; queue->kernelIf->mutex = NULL; diff --git a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h index e1939f29d..bda4ae566 100644 --- a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h +++ b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h @@ -25,8 +25,8 @@ #ifndef _VMCI_VERSION_H_ #define _VMCI_VERSION_H_ -#define VMCI_DRIVER_VERSION 9.3.4.0 -#define VMCI_DRIVER_VERSION_COMMAS 9,3,4,0 -#define VMCI_DRIVER_VERSION_STRING "9.3.4.0" +#define VMCI_DRIVER_VERSION 9.3.5.0 +#define VMCI_DRIVER_VERSION_COMMAS 9,3,5,0 +#define VMCI_DRIVER_VERSION_STRING "9.3.5.0" #endif /* _VMCI_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h b/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h index 81a5ffd9f..78a57d32f 100644 --- a/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h +++ b/open-vm-tools/modules/linux/vmci/shared/vmciQueue.h @@ -64,6 +64,7 @@ typedef struct VMCIQueueKernelIf VMCIQueueKernelIf; typedef struct VMCIQueue { VMCIQueueHeader *qHeader; + VMCIQueueHeader *savedHeader; VMCIQueueKernelIf *kernelIf; } VMCIQueue;