NDIS_STATUS HvlQueuePendingOperation( _In_ PHVL pHvl, _In_ HVL_PENDING_OP_TYPE OpType, _In_ PVOID pvOpData, _In_ BOOLEAN fPnpOperation ) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PHVL_PENDING_OP pPendingOp = NULL; do { if (fPnpOperation) { ASSERT(pHvl->PnpPendingOp.Link.Blink == NULL && pHvl->PnpPendingOp.Link.Flink == NULL); pPendingOp = &pHvl->PnpPendingOp; } else { 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; } pPendingOp->Type = OpType; pPendingOp->pvOpData= pvOpData; } InsertTailList(&pHvl->PendingOpQueue, &pPendingOp->Link); } while (FALSE); if (NDIS_STATUS_SUCCESS != ndisStatus) { if (pPendingOp) { if (fPnpOperation) { HvlInitPreAllocatedOp(pHvl); } else { HvlDeletePendingOperation(pHvl, pPendingOp); } } } return ndisStatus; }
NDIS_STATUS Hvl11Initialize( _In_ PHVL pHvl, _In_ PHW Hw ) { ULONG i = 0; MpTrace(COMP_HVL, DBG_NORMAL, ("Hvl11Initialize called\n")); pHvl->Hw = Hw; pHvl->ulNumThreadsPending = 0; pHvl->pActiveContext = NULL; pHvl->pHelperPortCtx = NULL; pHvl->pExAccessVNic = NULL; pHvl->pExAccessDelegatedVNic = NULL; pHvl->ulStatusFlags = 0; pHvl->ulNumPortCtxs = 0; pHvl->fNotificationsWorkItemRunning = 0; HvlInitPreAllocatedOp(pHvl); // initialize all the pre-allocated contexts for (i = 0; i < HVL_NUM_CONTEXTS; i++) { HvlinitContext(&(pHvl->HvlContexts[i]), FALSE); } KeInitializeEvent(&pHvl->CtxSEvent, SynchronizationEvent , FALSE); // auto-reset event KeInitializeEvent(&pHvl->TerminatingEvent, NotificationEvent, FALSE); // manual reset event KeInitializeEvent(&pHvl->ExAccessEvent, SynchronizationEvent, FALSE); // auto-reset event HvlClearCachedNotification(pHvl); if (pHvl->fVirtualizationEnabled) { NdisQueueIoWorkItem( pHvl->CtxSWorkItemHandle, HvlCtxSWorkItem, pHvl ); // increment the variable that keeps track of pending threads pHvl->ulNumThreadsPending++; // run the context switch logic for the first time KeSetEvent(&pHvl->CtxSEvent, 0, FALSE); } return NDIS_STATUS_SUCCESS; }
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; }
NDIS_STATUS Hvl11ReleaseExAccess(PHVL pHvl, PVNIC pVNic) { NDIS_STATUS ndisStatus = NDIS_STATUS_SUCCESS; PVNIC pExVNic = NULL; PHVL_EX_ACCESS_REQ pExReq = NULL; MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) releasing exclusive access\n", VNIC_PORT_NO)); HvlLock(pHvl); pExReq = HvlDequeueNextReqForVNic(pHvl, pVNic); if (pExReq && 0 == pExReq->ulRefCount) { /* This is the last release by this VNIC. Actually take away exclusive access if this is the current owner of exclusive access */ pExVNic = HvlGetExAccessVNic(pHvl); if (pExVNic == pVNic) { MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) currently owns exclusive access. Giving it up and removing it from the queue\n", VNIC_PORT_NO)); HvlReleaseExAccess(pHvl, pVNic); // set the event so that some other VNIC waiting for access can now get it KeSetEvent(&pHvl->ExAccessEvent, 0, FALSE); } else { /* The VNIC is releasing exclusive access even when it doesn't own it. This can happen - take for example the following 1. There is a helper port and an extensible station port 2. A Pause is happening and the helper port has exclusive access. 3. The station asks its VNIC to perform a dot11 reset 4. The station's VNIC requests exclusive access. Since the helper port has it, its request is queued 5. The helper port activates the station as part of its Pause processing 6. The station VNIC's CtxSToVNic is called and the VNIC learns it has exclusive access 7. The station VNIC completes its reset operation and asks Hvl to release exclusive access 8. The calls comes into the HVL and we would land up in this else clause. The correct thing to do here is to simply remove the station VNIC's request from the queue which is what we have done here */ MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) doesn't own exclusive access currently. Just removing it from the queue\n", VNIC_PORT_NO)); if (NULL == pExVNic) { //no one has exclusive access right now. set the event so that some other //VNIC waiting for access can now get it KeSetEvent(&pHvl->ExAccessEvent, 0, FALSE); } } // delete the request if (HvlIsPreAllocatedRequest(pHvl, pExReq)) { // it is a pre-allocated request - simply reset it HvlInitPreAllocatedOp(pHvl); } else { HvlDeleteExAccessRequest(pHvl, pExReq); } } else { ASSERT(!HvlIsHelperVNic(pHvl, pVNic)); /* This VNIC has more requests for exclusive access pending */ MpTrace(COMP_HVL, DBG_NORMAL, ("VNic(%d) has %d more exclusive access requests. Decremented the refcount. Not removing it from the queue\n", VNIC_PORT_NO, pExReq ? pExReq->ulRefCount : -1)); ndisStatus = NDIS_STATUS_PENDING; } HvlUnlock(pHvl); return ndisStatus; }