__inline VOID HvlSetCtxSInProgress(_Inout_ PHVL pHvl) { ASSERT(HvlIsLocked(pHvl)); pHvl->ulStatusFlags |= HVL_CTXS_IN_PROGRESS; }
__inline VOID HvlCtxSDone(_In_ PHVL pHvl) { ASSERT(HvlIsLocked(pHvl)); pHvl->ulStatusFlags &= ~HVL_CTXS_IN_PROGRESS; }
PHVL_EX_ACCESS_REQ HvlGetNextReq(PHVL pHvl, BOOLEAN fDequeue) { PHVL_EX_ACCESS_REQ pExReq = NULL; LIST_ENTRY *pEntryOp = NULL; PHVL_PENDING_OP pOp = NULL; ASSERT(HvlIsLocked(pHvl)); if (!HvlIsPendingOpQueueEmpty(pHvl)) { pEntryOp = pHvl->PendingOpQueue.Flink; pOp = CONTAINING_RECORD(pEntryOp, HVL_PENDING_OP, Link); pExReq = (PHVL_EX_ACCESS_REQ)pOp->pvOpData; if (fDequeue) { RemoveEntryList (&pOp->Link); pOp->pvOpData = NULL; // already aliased to pExReq HvlDeletePendingOperation(pHvl, pOp); } } return pExReq; }
__inline BOOLEAN HvlIsExAccessGranted(PHVL pHvl) { ASSERT(HvlIsLocked(pHvl)); return ((NULL != pHvl->pExAccessVNic) ? TRUE : FALSE); }
__inline BOOLEAN HvlTimedCtxSBlocked(_In_ PHVL pHvl) { ASSERT(HvlIsLocked(pHvl)); return ((pHvl->ulStatusFlags & HVL_TIMED_CTXS_BLOCKED) ? TRUE : FALSE); }
extern __inline BOOLEAN HvlIsCtxSInProgress(_In_ PHVL pHvl) { ASSERT(HvlIsLocked(pHvl)); return ((pHvl->ulStatusFlags & HVL_CTXS_IN_PROGRESS) ? TRUE : FALSE); }
/* This function is called with the Hvl locked This function removes the passed in context from the HVL and removes any references to it e.g. as the active context or as the helper port context */ VOID HvlRemoveCtxReferences( PHVL pHvl, PHVL_CONTEXT pCtxToRemove ) { LIST_ENTRY *pEntryCtx = NULL; PHVL_CONTEXT pCtx = NULL; BOOLEAN fFound = FALSE; ASSERT(HvlIsLocked(pHvl)); ASSERT(pCtxToRemove); do { /* Is it the currently active context */ if (pCtxToRemove == pHvl->pActiveContext) { pHvl->pActiveContext = NULL; break; } /* Is it the helper port context */ if (pCtxToRemove == pHvl->pHelperPortCtx) { pHvl->pHelperPortCtx = NULL; break; } /* Traverse the inactive context list to check if pCtxToRemove is present there */ pEntryCtx = pHvl->InactiveContextList.Flink; while (!fFound && (pEntryCtx != &pHvl->InactiveContextList)) { pCtx = CONTAINING_RECORD(pEntryCtx, HVL_CONTEXT, Link); if (pCtxToRemove == pCtx) { fFound = TRUE; RemoveEntryList (&pCtx->Link); InitializeListHead(&pCtx->Link); } else { pEntryCtx = pEntryCtx->Flink; } } } while (FALSE); return; }
__inline VOID HvlGrantExAccess(PHVL pHvl, PVNIC pVNic) { ASSERT(HvlIsLocked(pHvl)); pHvl->pExAccessVNic = pVNic; MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) was granted exclusive access\n", VNIC_PORT_NO)); }
PHVL_EX_ACCESS_REQ HvlGetNextReqForVNic(PHVL pHvl, PVNIC pVNic, BOOLEAN fDequeue) { PHVL_EX_ACCESS_REQ pExReq = NULL; LIST_ENTRY *pEntryOp = NULL; PHVL_PENDING_OP pOp = NULL; BOOLEAN fFound = FALSE; ASSERT(HvlIsLocked(pHvl)); pEntryOp = pHvl->PendingOpQueue.Flink; while (pEntryOp != &pHvl->PendingOpQueue) { pOp = CONTAINING_RECORD(pEntryOp, HVL_PENDING_OP, Link); pExReq = (PHVL_EX_ACCESS_REQ)pOp->pvOpData; if (pExReq->pVNic == pVNic) { fFound = TRUE; if (fDequeue) { pExReq->ulRefCount--; if (0 == pExReq->ulRefCount) { RemoveEntryList (&pOp->Link); pOp->pvOpData = NULL; // already aliased to pExReq HvlDeletePendingOperation(pHvl, pOp); } else { // nothing to be done since we have already decremented ref count } } break; } pEntryOp = pEntryOp->Flink; } if (fFound) { return pExReq; } else { return NULL; } }
__inline VOID HvlReleaseExAccess(PHVL pHvl, PVNIC pVNic) { ASSERT(HvlIsLocked(pHvl)); ASSERT(pVNic == HvlGetExAccessVNic(pHvl)); pHvl->pExAccessVNic = NULL; MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) released exclusive access\n", VNIC_PORT_NO)); }
extern __inline PVNIC HvlGetExAccessVNic(PHVL pHvl) { PVNIC pVNic = NULL; ASSERT(HvlIsLocked(pHvl)); pVNic = pHvl->pExAccessVNic; return pVNic; }
/* This function assigns the VNIC to an unused HVL context */ VOID HvlAssignVNicToContext( _In_ PHVL pHvl, _In_ PVNIC pVNic, _Out_ PHVL_CONTEXT *ppCtx ) { PHVL_CONTEXT pCtx = NULL; BOOLEAN fFoundCtx = FALSE; ULONG ulCtxIndex = 0; ASSERT(ppCtx); ASSERT(HvlIsLocked(pHvl)); do { *ppCtx = NULL; for (ulCtxIndex = 0; ulCtxIndex < HVL_NUM_CONTEXTS; ulCtxIndex++) { pCtx = &(pHvl->HvlContexts[ulCtxIndex]); if (pCtx->fCtxInUse == FALSE) { fFoundCtx = TRUE; break; } } ASSERT(fFoundCtx && ulCtxIndex < HVL_NUM_CONTEXTS); // setup this context HvlinitContext(pCtx, TRUE); // Add the VNIC to the VNIC list in the context HvlAddVNicToCtx(pVNic, pCtx); // set the context signature from the VNIC HvlCtxUpdateSignature(pCtx); pHvl->ulNumPortCtxs++; MpTrace(COMP_HVL, DBG_NORMAL, ("Associated VNIC (%d) to context (%p)", VNIC_PORT_NO, pCtx)); *ppCtx = pCtx; } while (FALSE); return; }
// This function is called with the HVL locked VOID HvlUpdateActiveCtx(PHVL pHvl, PHVL_CONTEXT pCurrCtx, PHVL_CONTEXT pNextCtx) { ASSERT(HvlIsLocked(pHvl)); // remove the next context from the inactive context list RemoveEntryList (&pNextCtx->Link); InitializeListHead(&pNextCtx->Link); // change the active context pHvl->pActiveContext = pNextCtx; if (pCurrCtx && pCurrCtx != pHvl->pHelperPortCtx) { // add the currently active context to the inactive context list InsertTailList (&pHvl->InactiveContextList, &pCurrCtx->Link); } }
/* Removes all the references to this context */ VOID HvlReturnContext( _In_ PHVL pHvl, _In_ PHVL_CONTEXT pCtx ) { ASSERT(HvlIsLocked(pHvl)); ASSERT(pCtx->fCtxInUse); ASSERT(IsListEmpty(&pCtx->VNicList) && pCtx->ulNumVNics == 0); // remove all the references to this context HvlRemoveCtxReferences(pHvl, pCtx); // set the context as not being used HvlinitContext(pCtx, FALSE); pHvl->ulNumPortCtxs--; }
NDIS_STATUS HvlQueueNotification( PHVL pHvl, PVOID pvNotif ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHVL_NOTIFICATION pHvlNotif = NULL; PNOTIFICATION_DATA_HEADER pHdr = (PNOTIFICATION_DATA_HEADER)pvNotif; PVOID pVNicNotif = NULL; ASSERT(HvlIsLocked(pHvl)); do { ndisStatus = ALLOC_MEM(pHvl->MiniportAdapterHandle, sizeof(HVL_NOTIFICATION), &pHvlNotif); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for a HVL notification\n")); break; } ndisStatus = ALLOC_MEM(pHvl->MiniportAdapterHandle, pHdr->Size, &pVNicNotif); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for notification\n")); break; } NdisMoveMemory(pVNicNotif , pvNotif, pHdr->Size); pHvlNotif->pvNotif = pVNicNotif; InsertTailList(&pHvl->NotificationsQueue, &pHvlNotif->Link); } while (FALSE); if (NDIS_FAILURE(ndisStatus)) { HvlFreeNotification(pHvlNotif); } return ndisStatus; }
// This function is called with the HVL locked PHVL_CONTEXT HvlFindNextCtx(PHVL pHvl) { PHVL_CONTEXT pNextCtx = NULL; LIST_ENTRY *pEntryCtx = NULL, *pEntryVNic = NULL; PVNIC pNextVNic = NULL; ASSERT(HvlIsLocked(pHvl)); if (IsListEmpty(&pHvl->InactiveContextList)) { // This is the only context we have return pHvl->pActiveContext; } else { pEntryCtx = pHvl->InactiveContextList.Flink; while (pEntryCtx != &pHvl->InactiveContextList) { pNextCtx = CONTAINING_RECORD(pEntryCtx, HVL_CONTEXT, Link); pEntryVNic = pNextCtx->VNicList.Flink; while (pEntryVNic != &pNextCtx->VNicList) { pNextVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink); if (VNic11IsOkToCtxS(pNextVNic)) { return pNextCtx; } pEntryVNic = pEntryVNic->Flink; } pEntryCtx = pEntryCtx->Flink; } } return NULL; }
VOID HvlNotifyAllVNics( PHVL pHvl, PVOID pvNotif ) { LIST_ENTRY *pEntryVNic = NULL; PVNIC pVNic = NULL; PNOTIFICATION_DATA_HEADER pHdr = NULL; ASSERT(HvlIsLocked(pHvl)); pHdr = (PNOTIFICATION_DATA_HEADER)pvNotif; pEntryVNic = pHvl->VNiclist.Flink; while (pEntryVNic != &pHvl->VNiclist) { pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, VNicLink); // do not notify if this VNIC is the source of the notification if (pVNic != pHdr->pSourceVNic) { _Analysis_assume_lock_held_(pHvl->Lock.SpinLock); HvlUnlock(pHvl); VNic11Notify(pVNic, pvNotif); HvlLock(pHvl); } pEntryVNic = pEntryVNic->Flink; } // Also send the notification to the helper port pEntryVNic = pHvl->pHelperPortCtx->VNicList.Flink; pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink); _Analysis_assume_lock_held_(pHvl->Lock.SpinLock); HvlUnlock(pHvl); VNic11Notify(pVNic, pvNotif); HvlLock(pHvl); }
/* Our merge algorithm is pretty straightforward. 1. If the active context is not null and not the helper context, init the list of known contexts with the active context 2. For all contexts in the inactive context list a. get the signature of the context b. For each context constructed so far if the signature of the two context are compatible merge the two the context else create a new context and add it to the list of constructed contexts */ VOID HvlPerformCtxMerge( PHVL pHvl, BOOLEAN *pfMerged ) { LIST_ENTRY *pEntryCtx = NULL; PHVL_CONTEXT pCtx = NULL; VNIC_SIGNATURE Ctx1Sig = {0}, Ctx2Sig = {0}; BOOLEAN fMerge = FALSE; PHVL_CONTEXT HvlCtxArray[HVL_NUM_CONTEXTS] = {0}; ULONG ulNumCtxs = 0, ulCtxIndex = 0, ulNumCtxsBeforeMerge = 0; ASSERT(HvlIsLocked(pHvl)); *pfMerged = FALSE; if (pHvl->ulNumPortCtxs <= 2) { return; } ulNumCtxsBeforeMerge = pHvl->ulNumPortCtxs; /* 1. If the active context is not null and not the helper context, init the list of known contexts with the active context */ if (pHvl->pActiveContext && pHvl->pHelperPortCtx != pHvl->pActiveContext) { HvlCtxArray[ulNumCtxs++] = pHvl->pActiveContext; } /* 2. For all contexts in the inactive context list */ pEntryCtx = pHvl->InactiveContextList.Flink; while (pEntryCtx != &pHvl->InactiveContextList) { pCtx = CONTAINING_RECORD(pEntryCtx, HVL_CONTEXT, Link); // remove this context from the linked list RemoveEntryList (&pCtx->Link); InitializeListHead(&pCtx->Link); /* a. get the signature of the context */ Ctx1Sig = HvlCtxGetSignature(pCtx); /* b. For each context constructed so far */ fMerge = FALSE; for (ulCtxIndex = 0; ulCtxIndex < ulNumCtxs; ulCtxIndex++) { Ctx2Sig = HvlCtxGetSignature(HvlCtxArray[ulCtxIndex]); if (VNic11AreCompatibleSignatures(&Ctx1Sig, &Ctx2Sig)) { fMerge = TRUE; break; } } /* if the signature of the two context are compatible merge the two the context else create a new context and add it to the list of constructed contexts */ if (fMerge) { HvlMergeCtxs(pHvl, pCtx, HvlCtxArray[ulCtxIndex]); } else { HvlCtxArray[ulNumCtxs++] = pCtx; } // move to the next context in the inactive context list pEntryCtx = pHvl->InactiveContextList.Flink; } /* We have walked the complete linked list of contexts */ ASSERT(IsListEmpty(&pHvl->InactiveContextList)); /* Now build up the Inactive context list again from the context array we have constructed */ ulCtxIndex = 0; if (pHvl->pActiveContext == HvlCtxArray[0]) { /* The first context is already referred by the active context. Skip it */ ulCtxIndex++; } for ( ; ulCtxIndex < ulNumCtxs; ulCtxIndex++) { InsertTailList(&pHvl->InactiveContextList, &HvlCtxArray[ulCtxIndex]->Link); } ulNumCtxs = ulNumCtxs + 1; // +1 for the helper port context if (ulNumCtxsBeforeMerge != ulNumCtxs) { ASSERT(ulNumCtxs < ulNumCtxsBeforeMerge); ASSERT(pHvl->ulNumPortCtxs == ulNumCtxs); *pfMerged = TRUE; MpTrace(COMP_HVL, DBG_NORMAL, ("Merged contexts. Previous # contexts = %d, new # contexts = %d", ulNumCtxsBeforeMerge, ulNumCtxs)); } else { ASSERT(!fMerge); } }
NDIS_STATUS HvlQueueExAccessRequest( _In_ PHVL pHvl, _In_ PVNIC pVNic, _In_ BOOLEAN fPnpOperation ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHVL_EX_ACCESS_REQ pExReq = NULL, pPnpExReq = NULL; PHVL_PENDING_OP pPendingOp = NULL; ASSERT(HvlIsLocked(pHvl)); do { // All requests by the helper port are considered different if (!HvlIsHelperVNic(pHvl, pVNic)) { /* Does an exclusive access request for this VNIC already exist? */ pExReq = HvlGetNextReqForVNic(pHvl, pVNic, FALSE); } if (NULL != pExReq) { /* There is already an exclusive access request existing for this VNIC. */ /* If the request is not a PnP request simply ref-up */ if (!HvlIsPreAllocatedRequest(pHvl, pExReq)) { pExReq->ulRefCount++; MpTrace(COMP_HVL, DBG_NORMAL, ("VNIC (%d): Incremented refcount for an exclusive access requet. New ref = %d \n", VNIC_PORT_NO, pExReq->ulRefCount)); } else { /* a request already exists. However it is the PnP request. Make sure we do not use it for non-Pnp operation. If we did, we might get another PnP operation after this PnP operation is done but we won't be able to use the pre-allocated request. Instead try to allocate a new request now */ // pnp operations must be serialized ASSERT(!fPnpOperation); pPnpExReq = pExReq; pExReq = NULL; // allocate exclusive access request ndisStatus = ALLOC_MEM(pHvl->MiniportAdapterHandle, sizeof(HVL_EX_ACCESS_REQ), &pExReq); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for a new exclusive access request\n")); break; } // allocate pending operation ndisStatus = ALLOC_MEM(pHvl->MiniportAdapterHandle, sizeof(HVL_PENDING_OP), &pPendingOp); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for a new pending operation")); break; } pExReq->pVNic = pVNic; pExReq->ulRefCount = pPnpExReq->ulRefCount + 1; pPendingOp->Type = HVL_PENDING_OP_EX_ACCESS; pPendingOp->pvOpData= pExReq; // now insert this new request in the same place as the old request pPendingOp->Link.Flink = pHvl->PnpPendingOp.Link.Flink; pPendingOp->Link.Blink = pHvl->PnpPendingOp.Link.Blink; pHvl->PnpPendingOp.Link.Flink->Blink = &pPendingOp->Link; pHvl->PnpPendingOp.Link.Blink->Flink = &pPendingOp->Link; // re-init the pre-allocated request since we are no longer using it HvlInitPreAllocatedOp(pHvl); } } else { /* This is the only exclusive access request for the HVL. Create a new request */ if (fPnpOperation) { ASSERT(pHvl->PnpPendingOp.Link.Blink == NULL && pHvl->PnpPendingOp.Link.Flink == NULL); pExReq = pHvl->pPnpOpExReq; } else { ndisStatus = ALLOC_MEM(pHvl->MiniportAdapterHandle, sizeof(HVL_EX_ACCESS_REQ), &pExReq); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("Failed to allocate memory for a new exclusive access request\n")); break; } } pExReq->pVNic = pVNic; pExReq->ulRefCount = 1; ndisStatus = HvlQueuePendingOperation(pHvl, HVL_PENDING_OP_EX_ACCESS, pExReq, fPnpOperation); if (NDIS_STATUS_SUCCESS != ndisStatus) { MpTrace(COMP_HVL, DBG_SERIOUS, ("HvlQueuePendingOperation failed 0x%x\n", ndisStatus)); break; } MpTrace(COMP_HVL, DBG_NORMAL, ("Queued an exclusive access requet for VNIC (%d)\n", VNIC_PORT_NO)); } } while (FALSE); if (NDIS_FAILURE(ndisStatus)) { if (pExReq && !fPnpOperation) { HvlDeleteExAccessRequest(pHvl, pExReq); } if (pPendingOp) { FREE_MEM(pPendingOp); } } return ndisStatus; }
VOID HvlCtxSProcessing( PHVL pHvl, PHVL_CONTEXT pNextActiveContext, BOOLEAN fMerged, ULONG ulFlags ) { //NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHVL_CONTEXT pCurrCtx = NULL, pNextCtx = NULL; BOOLEAN fSameCtx = FALSE; LIST_ENTRY *pEntryVNic = NULL; PVNIC pVNic = NULL; PVNIC pHelperVNic = NULL; LIST_ENTRY *pHelperEntry = NULL; ASSERT(HvlIsLocked(pHvl)); // we can have only one instance of this routine running ASSERT(!HvlIsCtxSInProgress(pHvl)); HvlSetCtxSInProgress(pHvl); do { // get the current active context pCurrCtx = pHvl->pActiveContext; if (NULL == pNextActiveContext) { // Use our logic to find the next active context pNextCtx = HvlFindNextCtx(pHvl); } else { // the caller specified a particular context to switch to pNextCtx = pNextActiveContext; } if (pCurrCtx && pNextCtx == pCurrCtx) { if (!fMerged) { /* No context switch needs to happen. */ MpTrace(COMP_HVL, DBG_NORMAL, ("The next context (%p) is the same as the current one. Not doing a context switch\n", pNextCtx)); #if DBG pEntryVNic = pNextCtx->VNicList.Flink; while (pEntryVNic != &pNextCtx->VNicList) { pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink); ASSERT(pVNic->fActive); pEntryVNic = pEntryVNic->Flink; } #endif break; } else { MpTrace(COMP_HVL, DBG_NORMAL, ("The next context (%p) is the same as the current one. But we have just done a merge. Continuing with the context switch\n", pNextCtx)); /* remembe that our current and next contexts are the same. We need to do this so that we do not update the current context below */ fSameCtx = TRUE; } } if (NULL == pNextCtx) { /* No context switch needs to happen. */ MpTrace(COMP_HVL, DBG_NORMAL, ("No contexts exist. Not doing a context switch\n")); break; } /* Now update the Hvl's state about which VNIC is loosing exclusive access. It is important to update this before VNic11CtxSFromVNic call has been made. This ensures that the VNIC does not gain access while it is being moved away from active context */ if (VNIC_FLAG_HVL_ACTIVATED & ulFlags) { // this is an activation by the helper port ASSERT(pNextActiveContext); ASSERT(pNextActiveContext->ulNumVNics == 1); if (NULL == pNextActiveContext) { return; } pEntryVNic = pNextActiveContext->VNicList.Flink; pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink); pHelperEntry = pHvl->pHelperPortCtx->VNicList.Flink; pHelperVNic = CONTAINING_RECORD(pHelperEntry, VNIC, CtxLink); // Helper port is delegating exclusive access to someone else. Take away the delegated access if (pHvl->pExAccessDelegatedVNic && pVNic != pHvl->pExAccessDelegatedVNic ) { pHvl->pExAccessDelegatedVNic = NULL; } } // inform the active VNICs that they are going to be become inactive if (NULL != pCurrCtx) { HvlNotifyAllVNicsInContext(pHvl, pCurrCtx, VNic11CtxSFromVNic, 0); } /* Tell the hardware layer that we are now going to context switch. Note that this has to happen only after the VNICs have been told they are becoming inactive. The reason for this is that receives should continue to happen while the VNICs perform the tasks they need to become inactive (e.g. send PS packet to the AP) Also note that we need to give up our lock before we call into the hardware. This is because the Hw might need to wait for some operations to complete before it can return this call. Since the context switch processing runs in a single thread, we can safely leave the lock and re-acquire it */ HvlUnlock(pHvl); Hw11CtxSStart(pHvl->Hw); HvlLock(pHvl); // ask the VNICs in the new context to program the hardware HvlNotifyAllVNicsInContext(pHvl, pNextCtx, VNic11ProgramHw, 0); /* program the hardware for the new context - this programs any settings that need to be merged across the VNICs */ HvlProgramHw(pHvl, pNextCtx); /* Now update the Hvl's state about which VNIC is active or has exclusive access. It is important to update this after the VNic11ProgramHw and HvlProgramHw calls have been made. This ensures that the VNIC does not gain access prematurely */ if (VNIC_FLAG_HVL_ACTIVATED & ulFlags) { // this is an activation by the helper port // pHelperEntry and pHelperVNic must have been already calculated above ASSERT(pHelperEntry && pHelperVNic); if (pVNic != pHelperVNic) { // Some VNIC is being delegated exclusive access pHvl->pExAccessDelegatedVNic = pVNic; } } else if (VNIC_FLAG_GRANTED_EX_ACCESS & ulFlags) { // a VNIC is being given exclusive access. Update our state to reflect this ASSERT(pNextActiveContext); ASSERT(pNextActiveContext->ulNumVNics == 1); if (NULL == pNextActiveContext) { return; } pEntryVNic = pNextActiveContext->VNicList.Flink; pVNic = CONTAINING_RECORD(pEntryVNic, VNIC, CtxLink); HvlGrantExAccess(pHvl, pVNic); } if (!fSameCtx) { HvlUpdateActiveCtx(pHvl, pCurrCtx, pNextCtx); MpTrace(COMP_HVL, DBG_NORMAL, ("Current context = %p, next context = %p, fSameCtx = %s\n", pCurrCtx, pNextCtx, fSameCtx?"TRUE":"FALSE")); } // tell the hardware that we are ready with the new context HvlUnlock(pHvl); Hw11CtxSComplete(pHvl->Hw); HvlLock(pHvl); // inform the VNICs that they are now active HvlNotifyAllVNicsInContext(pHvl, pNextCtx, VNic11CtxSToVNic, ulFlags); } while (FALSE); HvlCtxSDone(pHvl); return; }