Example #1
0
__inline
VOID
HvlSetCtxSInProgress(_Inout_ PHVL pHvl)
{
    ASSERT(HvlIsLocked(pHvl));
    pHvl->ulStatusFlags |= HVL_CTXS_IN_PROGRESS;
}
Example #2
0
__inline
VOID
HvlCtxSDone(_In_ PHVL pHvl)
{
    ASSERT(HvlIsLocked(pHvl));
    pHvl->ulStatusFlags &= ~HVL_CTXS_IN_PROGRESS;
}
Example #3
0
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;
}
Example #4
0
__inline
BOOLEAN
HvlIsExAccessGranted(PHVL pHvl)
{
    ASSERT(HvlIsLocked(pHvl));

    return ((NULL != pHvl->pExAccessVNic) ? TRUE : FALSE);
}
Example #5
0
__inline
BOOLEAN
HvlTimedCtxSBlocked(_In_ PHVL pHvl)
{
    ASSERT(HvlIsLocked(pHvl));
    
    return ((pHvl->ulStatusFlags & HVL_TIMED_CTXS_BLOCKED) ? TRUE : FALSE);
}
Example #6
0
extern
__inline
BOOLEAN
HvlIsCtxSInProgress(_In_ PHVL pHvl)
{
    ASSERT(HvlIsLocked(pHvl));
    return ((pHvl->ulStatusFlags & HVL_CTXS_IN_PROGRESS) ? TRUE : FALSE);
}
Example #7
0
/*
    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;
}
Example #8
0
__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));
}
Example #9
0
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;
    }
}
Example #10
0
__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));
}
Example #11
0
extern
__inline
PVNIC    
HvlGetExAccessVNic(PHVL pHvl)
{
    PVNIC pVNic = NULL;
    ASSERT(HvlIsLocked(pHvl));

    pVNic = pHvl->pExAccessVNic;

    return pVNic;
}
Example #12
0
/*
    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;
}
Example #13
0
// 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);
    }
}
Example #14
0
/*
    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--;
}
Example #15
0
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;
}
Example #16
0
// 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;
}
Example #17
0
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);
}
Example #18
0
/*
    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);
    }
    
}
Example #19
0
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;
}
Example #20
0
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;
}