Cleanup NBL reference counting
The Empty event state is now set according to Ctx->Device.Receive.ActiveNbls.Head != NULL. But, we still have to clear the Empty event inside the TransitionLock to prevent race with TunPause(). Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
408665270f
commit
cd6fe285b4
64
wintun.c
64
wintun.c
@ -150,7 +150,6 @@ typedef struct _TUN_CTX
|
|||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
NET_BUFFER_LIST *Head, *Tail;
|
NET_BUFFER_LIST *Head, *Tail;
|
||||||
volatile LONG64 Count;
|
|
||||||
KEVENT Empty;
|
KEVENT Empty;
|
||||||
} ActiveNbls;
|
} ActiveNbls;
|
||||||
} Receive;
|
} Receive;
|
||||||
@ -351,11 +350,6 @@ TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
|
|||||||
* MINIPORT_RETURN_NET_BUFFER_LISTS calls. Therefore, we use our own ->Next pointer for book-keeping. */
|
* MINIPORT_RETURN_NET_BUFFER_LISTS calls. Therefore, we use our own ->Next pointer for book-keeping. */
|
||||||
#define NET_BUFFER_LIST_NEXT_NBL_EX(Nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(Nbl)[1])
|
#define NET_BUFFER_LIST_NEXT_NBL_EX(Nbl) (NET_BUFFER_LIST_MINIPORT_RESERVED(Nbl)[1])
|
||||||
|
|
||||||
/* Wintun-specific MINIPORT_RETURN_NET_BUFFER_LISTS return flag to indicate the NBL was not really sent to NDIS and
|
|
||||||
* the receiver thread is calling the MINIPORT_RETURN_NET_BUFFER_LISTS handler manualy to perform regular NBL's
|
|
||||||
* post-processing. Must not overlap any of the standard NDIS_RETURN_FLAGS_* values. */
|
|
||||||
#define TUN_RETURN_FLAGS_DISCARD 0x00010000
|
|
||||||
|
|
||||||
static MINIPORT_RETURN_NET_BUFFER_LISTS TunReturnNetBufferLists;
|
static MINIPORT_RETURN_NET_BUFFER_LISTS TunReturnNetBufferLists;
|
||||||
_Use_decl_annotations_
|
_Use_decl_annotations_
|
||||||
static VOID
|
static VOID
|
||||||
@ -363,25 +357,19 @@ TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST Net
|
|||||||
{
|
{
|
||||||
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
TUN_CTX *Ctx = (TUN_CTX *)MiniportAdapterContext;
|
||||||
TUN_RING *Ring = Ctx->Device.Receive.Ring;
|
TUN_RING *Ring = Ctx->Device.Receive.Ring;
|
||||||
BOOLEAN WasNdisIndicated = !(ReturnFlags & TUN_RETURN_FLAGS_DISCARD);
|
|
||||||
|
|
||||||
LONG64 ReceivedPacketsCount = 0, ReceivedPacketsSize = 0, ErrorPacketsCount = 0, DiscardedPacketsCount = 0;
|
LONG64 ReceivedPacketsCount = 0, ReceivedPacketsSize = 0, ErrorPacketsCount = 0;
|
||||||
for (NET_BUFFER_LIST *Nbl = NetBufferLists, *NextNbl; Nbl; Nbl = NextNbl)
|
for (NET_BUFFER_LIST *Nbl = NetBufferLists, *NextNbl; Nbl; Nbl = NextNbl)
|
||||||
{
|
{
|
||||||
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
||||||
|
|
||||||
if (WasNdisIndicated)
|
if (NT_SUCCESS(NET_BUFFER_LIST_STATUS(Nbl)))
|
||||||
{
|
{
|
||||||
if (NT_SUCCESS(NET_BUFFER_LIST_STATUS(Nbl)))
|
ReceivedPacketsCount++;
|
||||||
{
|
ReceivedPacketsSize += NET_BUFFER_LIST_FIRST_NB(Nbl)->DataLength;
|
||||||
ReceivedPacketsCount++;
|
|
||||||
ReceivedPacketsSize += NET_BUFFER_LIST_FIRST_NB(Nbl)->DataLength;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ErrorPacketsCount++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
DiscardedPacketsCount++;
|
ErrorPacketsCount++;
|
||||||
|
|
||||||
TunNblMarkCompleted(Nbl);
|
TunNblMarkCompleted(Nbl);
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -395,20 +383,18 @@ TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST Net
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ctx->Device.Receive.ActiveNbls.Head = NET_BUFFER_LIST_NEXT_NBL_EX(CompletedNbl);
|
Ctx->Device.Receive.ActiveNbls.Head = NET_BUFFER_LIST_NEXT_NBL_EX(CompletedNbl);
|
||||||
|
if (!Ctx->Device.Receive.ActiveNbls.Head)
|
||||||
|
KeSetEvent(&Ctx->Device.Receive.ActiveNbls.Empty, IO_NO_INCREMENT, FALSE);
|
||||||
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
||||||
InterlockedSetU(&Ring->Head, TunNblGetOffset(CompletedNbl));
|
InterlockedSetU(&Ring->Head, TunNblGetOffset(CompletedNbl));
|
||||||
NdisFreeNetBufferList(CompletedNbl);
|
NdisFreeNetBufferList(CompletedNbl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WasNdisIndicated && InterlockedDecrement64(&Ctx->Device.Receive.ActiveNbls.Count) <= 0)
|
|
||||||
KeSetEvent(&Ctx->Device.Receive.ActiveNbls.Empty, IO_NO_INCREMENT, FALSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInOctets, ReceivedPacketsSize);
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInOctets, ReceivedPacketsSize);
|
||||||
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastOctets, ReceivedPacketsSize);
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastOctets, ReceivedPacketsSize);
|
||||||
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastPkts, ReceivedPacketsCount);
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifHCInUcastPkts, ReceivedPacketsCount);
|
||||||
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInErrors, ErrorPacketsCount);
|
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInErrors, ErrorPacketsCount);
|
||||||
InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInDiscards, DiscardedPacketsCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||||||
@ -501,31 +487,28 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
|
|||||||
NET_BUFFER_LIST *Nbl = NdisAllocateNetBufferAndNetBufferList(
|
NET_BUFFER_LIST *Nbl = NdisAllocateNetBufferAndNetBufferList(
|
||||||
Ctx->NblPool, 0, 0, Ctx->Device.Receive.Mdl, (ULONG)(Packet->Data - (UCHAR *)Ring), PacketSize);
|
Ctx->NblPool, 0, 0, Ctx->Device.Receive.Mdl, (ULONG)(Packet->Data - (UCHAR *)Ring), PacketSize);
|
||||||
if (!Nbl)
|
if (!Nbl)
|
||||||
{
|
goto skipNbl;
|
||||||
InterlockedIncrement64((LONG64 *)&Ctx->Statistics.ifInDiscards);
|
|
||||||
KeWaitForSingleObject(&Ctx->Device.Receive.ActiveNbls.Empty, Executive, KernelMode, FALSE, NULL);
|
|
||||||
InterlockedSetU(&Ring->Head, RingHead);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Nbl->SourceHandle = Ctx->MiniportAdapterHandle;
|
Nbl->SourceHandle = Ctx->MiniportAdapterHandle;
|
||||||
NdisSetNblFlag(Nbl, NblFlags);
|
NdisSetNblFlag(Nbl, NblFlags);
|
||||||
NET_BUFFER_LIST_INFO(Nbl, NetBufferListFrameType) = (PVOID)NblProto;
|
NET_BUFFER_LIST_INFO(Nbl, NetBufferListFrameType) = (PVOID)NblProto;
|
||||||
NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SUCCESS;
|
NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SUCCESS;
|
||||||
TunNblSetOffsetAndMarkActive(Nbl, RingHead);
|
TunNblSetOffsetAndMarkActive(Nbl, RingHead);
|
||||||
KLOCK_QUEUE_HANDLE LockHandle;
|
|
||||||
KeAcquireInStackQueuedSpinLock(&Ctx->Device.Receive.Lock, &LockHandle);
|
|
||||||
*(Ctx->Device.Receive.ActiveNbls.Head ? &NET_BUFFER_LIST_NEXT_NBL_EX(Ctx->Device.Receive.ActiveNbls.Tail)
|
|
||||||
: &Ctx->Device.Receive.ActiveNbls.Head) = Nbl;
|
|
||||||
Ctx->Device.Receive.ActiveNbls.Tail = Nbl;
|
|
||||||
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
||||||
|
|
||||||
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
KIRQL Irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
|
||||||
if (!InterlockedGet(&Ctx->Running))
|
if (!InterlockedGet(&Ctx->Running))
|
||||||
goto skipNbl;
|
goto cleanupNbl;
|
||||||
|
|
||||||
if (InterlockedIncrement64(&Ctx->Device.Receive.ActiveNbls.Count) == 1)
|
KLOCK_QUEUE_HANDLE LockHandle;
|
||||||
|
KeAcquireInStackQueuedSpinLock(&Ctx->Device.Receive.Lock, &LockHandle);
|
||||||
|
if (Ctx->Device.Receive.ActiveNbls.Head)
|
||||||
|
NET_BUFFER_LIST_NEXT_NBL_EX(Ctx->Device.Receive.ActiveNbls.Tail) = Nbl;
|
||||||
|
else
|
||||||
|
{
|
||||||
KeClearEvent(&Ctx->Device.Receive.ActiveNbls.Empty);
|
KeClearEvent(&Ctx->Device.Receive.ActiveNbls.Empty);
|
||||||
|
Ctx->Device.Receive.ActiveNbls.Head = Nbl;
|
||||||
|
}
|
||||||
|
Ctx->Device.Receive.ActiveNbls.Tail = Nbl;
|
||||||
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
||||||
|
|
||||||
NdisMIndicateReceiveNetBufferLists(
|
NdisMIndicateReceiveNetBufferLists(
|
||||||
Ctx->MiniportAdapterHandle,
|
Ctx->MiniportAdapterHandle,
|
||||||
@ -537,10 +520,13 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
|
|||||||
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
skipNbl:
|
cleanupNbl:
|
||||||
NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
|
|
||||||
TunReturnNetBufferLists(Ctx, Nbl, TUN_RETURN_FLAGS_DISCARD);
|
|
||||||
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
ExReleaseSpinLockShared(&Ctx->TransitionLock, Irql);
|
||||||
|
NdisFreeNetBufferList(Nbl);
|
||||||
|
skipNbl:
|
||||||
|
InterlockedIncrement64((LONG64 *)&Ctx->Statistics.ifInDiscards);
|
||||||
|
KeWaitForSingleObject(&Ctx->Device.Receive.ActiveNbls.Empty, Executive, KernelMode, FALSE, NULL);
|
||||||
|
InterlockedSetU(&Ring->Head, RingHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for all NBLs to return: 1. To prevent race between proceeding and invalidating ring head. 2. To have
|
/* Wait for all NBLs to return: 1. To prevent race between proceeding and invalidating ring head. 2. To have
|
||||||
|
Loading…
Reference in New Issue
Block a user