diff --git a/src/adapter.c b/src/adapter.c index a62444d..b995a73 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -137,6 +137,9 @@ tapAdapterContextAllocate( // NBL pool for making TAP receive indications. NdisZeroMemory(&nblPoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS)); + // Initialize event used to determine when all receive NBLs have been returned. + NdisInitializeEvent(&adapter->ReceiveNblInFlightCountZeroEvent); + // Add initial reference. Normally removed in AdapterHalt. adapter->RefCount = 1; @@ -954,6 +957,63 @@ Return Value: DEBUGP (("[TAP] <-- AdapterHalt\n")); } +VOID +tapWaitForReceiveNblInFlightCountZeroEvent( + __in PTAP_ADAPTER_CONTEXT Adapter +) +{ + LONG nblCount; + + // + // Wait until higher-level protocol has returned all NBLs + // to the driver. + // + + // Add one NBL "bias" to insure allow event to be reset safely. + nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0); + NdisResetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + + // + // Now remove the bias and wait for the ReceiveNblInFlightCountZeroEvent + // if the count returned is not zero. + // + nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0); + + if (nblCount) + { + LARGE_INTEGER startTime, currentTime; + + NdisGetSystemUpTimeEx(&startTime); + + for (;;) + { + BOOLEAN waitResult = NdisWaitEvent( + &Adapter->ReceiveNblInFlightCountZeroEvent, + TAP_WAIT_POLL_LOOP_TIMEOUT + ); + + NdisGetSystemUpTimeEx(¤tTime); + + if (waitResult) + { + break; + } + + DEBUGP(("[%s] Waiting for %d in-flight receive NBLs to be returned.\n", + MINIPORT_INSTANCE_ID(Adapter), + Adapter->ReceiveNblInFlightCount + )); + } + + DEBUGP(("[%s] Waited %d ms for all in-flight NBLs to be returned.\n", + MINIPORT_INSTANCE_ID(Adapter), + (currentTime.LowPart - startTime.LowPart) + )); + } +} + NDIS_STATUS AdapterPause( __in NDIS_HANDLE MiniportAdapterContext, @@ -1016,6 +1076,21 @@ Return Value: adapter->Locked.AdapterState = MiniportPausingState; tapAdapterReleaseLock(adapter,FALSE); + // + // Stop the flow of network data through the receive path + // ------------------------------------------------------ + // In the Pausing and Paused state tapAdapterSendAndReceiveReady + // will prevent new calls to NdisMIndicateReceiveNetBufferLists + // to indicate additional receive NBLs to the host. + // + // However, there may be some in-flight NBLs owned by the driver + // that have been indicated to the host but have not yet been + // returned. + // + // Wait here for all in-flight receive indications to be returned. + // + tapWaitForReceiveNblInFlightCountZeroEvent(adapter); + // // Stop the flow of network data through the send path // --------------------------------------------------- diff --git a/src/adapter.h b/src/adapter.h index c62249f..2a7b2bf 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -184,6 +184,10 @@ typedef struct _TAP_ADAPTER_CONTEXT // NBL pool for making TAP receive indications. NDIS_HANDLE ReceiveNblPool; + volatile LONG ReceiveNblInFlightCount; +#define TAP_WAIT_POLL_LOOP_TIMEOUT 3000 // 3 seconds + NDIS_EVENT ReceiveNblInFlightCountZeroEvent; + // Info for point-to-point mode BOOLEAN m_tun; IPADDR m_localIP; diff --git a/src/rxpath.c b/src/rxpath.c index 3204d26..747a775 100644 --- a/src/rxpath.c +++ b/src/rxpath.c @@ -39,7 +39,8 @@ VOID tapFreeReceiveNetBufferList( __in PTAP_ADAPTER_CONTEXT Adapter, - __in PNET_BUFFER_LIST NetBufferList // Only one NB here... + __in PNET_BUFFER_LIST NetBufferList, // Only one NB here... + __in BOOLEAN DecrementInflight ) { ULONG frameType, netBufferCount, byteCount; @@ -130,6 +131,17 @@ tapFreeReceiveNetBufferList( NdisFreeMdl(mdl); } + if (DecrementInflight) + { + // Decrement in-flight receive NBL count. + LONG nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount >= 0); + if (0 == nblCount) + { + NdisSetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); + } + } + // Free the NBL NdisFreeNetBufferList(NetBufferList); } @@ -224,7 +236,7 @@ IndicateReceivePacket( if(netBufferList != NULL) { - ULONG receiveFlags = NDIS_RECEIVE_FLAGS_RESOURCES; + ULONG receiveFlags = 0; NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL @@ -240,6 +252,10 @@ IndicateReceivePacket( netBufferList->MiniportReserved[0] = NULL; netBufferList->MiniportReserved[1] = NULL; + // Increment in-flight receive NBL count. + LONG nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); + ASSERT(nblCount > 0); + netBufferList->SourceHandle = Adapter->MiniportAdapterHandle; // @@ -256,8 +272,6 @@ IndicateReceivePacket( receiveFlags ); - tapFreeReceiveNetBufferList(Adapter->MiniportAdapterHandle, netBufferList); - return; } else @@ -292,11 +306,33 @@ AdapterReturnNetBufferLists( __in NDIS_HANDLE MiniportAdapterContext, __in PNET_BUFFER_LIST NetBufferLists, __in ULONG ReturnFlags - ) +) { - PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; + PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT)MiniportAdapterContext; + PNET_BUFFER_LIST currentNbl; - DEBUGP(("[%s] Unexpected AdapterReturnNetBufferLists() call\n", MINIPORT_INSTANCE_ID(adapter))); + UNREFERENCED_PARAMETER(ReturnFlags); + + // + // Process each NBL individually + // + currentNbl = NetBufferLists; + while (currentNbl) + { + PNET_BUFFER_LIST nextNbl; + + nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl); + NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL; + + // Complete write IRP and free NBL and associated resources. + tapFreeReceiveNetBufferList( + adapter, + currentNbl, + TRUE); + + // Move to next NBL + currentNbl = nextNbl; + } } static PVOID @@ -527,7 +563,7 @@ TapSharedSendPacket( NDIS_RECEIVE_FLAGS_RESOURCES // ReceiveFlags ); - tapFreeReceiveNetBufferList(Adapter->MiniportAdapterHandle, netBufferList); + tapFreeReceiveNetBufferList(Adapter->MiniportAdapterHandle, netBufferList, FALSE); return STATUS_SUCCESS; }