Use CSQ instead of StartIO
This also introduces a proper queueing and refcounting system, which should increase performance and allow for IRP cancelation. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
ce9936089c
commit
0dac9b2bc7
@ -1,173 +0,0 @@
|
||||
typedef struct _PACKET_QUEUE {
|
||||
KSPIN_LOCK Lock;
|
||||
NET_BUFFER_LIST *FirstNbl, *LastNbl;
|
||||
volatile NET_BUFFER_LIST *FirstToFree;
|
||||
NET_BUFFER *NextNb;
|
||||
UINT NumNbl;
|
||||
} PACKET_QUEUE, *PPACKET_QUEUE;
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
void AddToFreeList(PACKET_QUEUE *Queue, NET_BUFFER_LIST *Nbl)
|
||||
{
|
||||
if (!Queue->FirstToFree)
|
||||
Queue->FirstToFree = Nbl;
|
||||
else {
|
||||
NET_BUFFER_LIST_NEXT_NBL(Nbl) = Queue->FirstToFree;
|
||||
Queue->FirstToFree = Nbl;
|
||||
}
|
||||
--Queue->NumNbl;
|
||||
}
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
void FreeFreeList(PACKET_QUEUE *Queue)
|
||||
{
|
||||
NET_BUFFER_LIST *Next;
|
||||
|
||||
while (Queue->FirstToFree) {
|
||||
Next = NET_BUFFER_LIST_NEXT_NBL(Queue->FirstToFree);
|
||||
FreeNbl(Queue->FirstToFree);
|
||||
Queue->FirstToFree = Next;
|
||||
}
|
||||
}
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
NET_BUFFER *RemoveFromQueue(PACKET_QUEUE *Queue, NET_BUFFER_LIST **DetachedNbl)
|
||||
{
|
||||
NET_BUFFER_LIST *Top;
|
||||
NET_BUFFER *Ret;
|
||||
|
||||
Top = Queue->FirstNbl;
|
||||
if (!Top)
|
||||
return NULL;
|
||||
if (!Queue->NextNb)
|
||||
Queue->NextNb = NET_BUFFER_LIST_FIRST_NB(Top);
|
||||
Ret = Queue->NextNb;
|
||||
Queue->NextNb = NET_BUFFER_NEXT_NB(Ret);
|
||||
if (!Queue->NextNb) {
|
||||
Queue->FirstNbl = NET_BUFFER_LIST_NEXT_NBL(Top);
|
||||
if (!Queue->FirstNbl)
|
||||
Queue->LastNbl = NULL;
|
||||
NET_BUFFER_LIST_NEXT_NBL(Top) = NULL;
|
||||
AddToFreeList(Queue, Top);
|
||||
if (DetachedNbl)
|
||||
*DetachedNbl = Top;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
void AppendToQueue(PACKET_QUEUE *Queue, NET_BUFFER_LIST *Nbl)
|
||||
{
|
||||
NET_BUFFER_LIST *Next;
|
||||
|
||||
for (; Nbl; Nbl = Next) {
|
||||
Next = NET_BUFFER_LIST_NEXT_NBL(Nbl);
|
||||
if (!NET_BUFFER_LIST_FIRST_NB(Nbl->FirstNb)) {
|
||||
FreeNbl(Nbl);
|
||||
continue;
|
||||
}
|
||||
if (!Queue->FirstNbl)
|
||||
Queue->FirstNbl = Queue->LastNbl = Nbl;
|
||||
else {
|
||||
NET_BUFFER_LIST_NEXT_NBL(Queue->LastNbl) = Nbl;
|
||||
Queue->LastNbl = Nbl;
|
||||
}
|
||||
++Queue->NumNbl;
|
||||
}
|
||||
}
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
// Note: Must be called immediately after RemoveFromQueue without dropping Queue->Lock.
|
||||
void PrependToQueue(PACKET_QUEUE *Queue, NET_BUFFER *Nb, NET_BUFFER_LIST *DetachedNbl)
|
||||
{
|
||||
Queue->NextNb = Nb;
|
||||
|
||||
if (!DetachedNbl)
|
||||
return;
|
||||
|
||||
if (DetachedNbl == Queue->FirstToFree)
|
||||
Queue->FirstToFree = NET_BUFFER_LIST_NEXT_NBL(DetachedNbl);
|
||||
|
||||
if (!Queue->FirstNbl)
|
||||
Queue->FirstNbl = Queue->LastNbl = DetachedNbl;
|
||||
else {
|
||||
NET_BUFFER_LIST_NEXT_NBL(DetachedNbl) = Queue->FirstNbl;
|
||||
Queue->FirstNbl = DetachedNbl;
|
||||
}
|
||||
++Queue->NumNbl;
|
||||
}
|
||||
|
||||
// Annotation: requires Queue->Lock
|
||||
void TrimQueueToNItems(PACKET_QUEUE *Queue, UINT MaxNbls)
|
||||
{
|
||||
NET_BUFFER_LIST *Next;
|
||||
|
||||
while (Queue->NumNbl > MaxNbls) {
|
||||
if (!Queue->FirstNbl)
|
||||
return;
|
||||
Next = NET_BUFFER_LIST_NEXT_NBL(Queue->FirstNbl);
|
||||
AddToFreeList(Queue->FirstNbl);
|
||||
Queue->NextNb = NULL;
|
||||
Queue->FirstNbl = Next;
|
||||
if (!Queue->FirstNbl)
|
||||
Queue->LastNbl = NULL;
|
||||
}
|
||||
FreeFreeList(Queue);
|
||||
}
|
||||
|
||||
void TunDispatchRead(IRP *Irp)
|
||||
{
|
||||
IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);
|
||||
ProcessQueuedPackets();
|
||||
}
|
||||
|
||||
void ProcessQueuedPackets(void)
|
||||
{
|
||||
IRP *Irp;
|
||||
NET_BUFFER *Nb;
|
||||
KLOCK_QUEUE_HANDLE LockHandle;
|
||||
|
||||
for (;;) {
|
||||
if (!Irp) {
|
||||
NET_BUFFER_LIST *DetachedNbl = NULL;
|
||||
|
||||
KeAcquireInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
Nb = RemoveFromQueue(Queue, &DetachedNbl);
|
||||
if (!Nb) {
|
||||
KeReleaseInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
return;
|
||||
}
|
||||
Irp = IoCsqRemoveNextIrp(IoCsq, NULL);
|
||||
if (!Irp) {
|
||||
PrependToQueue(Queue, Nb, DetachedNbl);
|
||||
KeReleaseInStackQueuedSpinLock(Queue, &LockHandle);
|
||||
return;
|
||||
}
|
||||
KeReleaseInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
} else {
|
||||
KeAcquireInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
Nb = RemoveFromQueue(Queue, NULL);
|
||||
KeReleaseInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
}
|
||||
if (!Nb || WriteIntoIrp(Irp, Nb) == IRP_HAD_NO_ROOM_FOR_IT) {
|
||||
TunCompleteRequest(Irp, 0, STATUS_SUCCESS);
|
||||
Irp = NULL;
|
||||
}
|
||||
if (Queue->FirstToFree) {
|
||||
KeAcquireInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
FreeFreeList(Queue);
|
||||
KeReleaseInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TunSendNetBufferLists(NET_BUFFER_LIST *Nbl)
|
||||
{
|
||||
KLOCK_QUEUE_HANDLE LockHandle;
|
||||
|
||||
KeAcquireInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
AppendToQueue(Queue, Nbl);
|
||||
TrimQueueToNItems(Queue, 1000);
|
||||
KeReleaseInStackQueuedSpinLock(Queue->Lock, &LockHandle);
|
||||
ProcessQueuedPackets();
|
||||
}
|
640
wintun.c
640
wintun.c
@ -32,7 +32,7 @@
|
||||
#define TUN_EXCH_ALIGNMENT 16 // Memory alignment in exchange buffers
|
||||
#define TUN_EXCH_MAX_IP_PACKET_SIZE (TUN_EXCH_MAX_PACKET_SIZE - sizeof(TUN_PACKET)) // Maximum IP packet size (headers + payload)
|
||||
#define TUN_EXCH_MAX_BUFFER_SIZE (TUN_EXCH_MAX_PACKETS * TUN_EXCH_MAX_PACKET_SIZE) // Maximum size of read/write exchange buffer
|
||||
#define TUN_QUEUE_MAX_NBLS 4096
|
||||
#define TUN_QUEUE_MAX_NBLS 1000
|
||||
|
||||
typedef struct _TUN_PACKET {
|
||||
ULONG Size; // Size of packet data (TUN_EXCH_MAX_IP_PACKET_SIZE max)
|
||||
@ -41,16 +41,6 @@ typedef struct _TUN_PACKET {
|
||||
UCHAR Data[]; // Packet data
|
||||
} TUN_PACKET;
|
||||
|
||||
typedef struct _TUN_EVENT {
|
||||
KEVENT *Event;
|
||||
HANDLE Handle;
|
||||
} TUN_EVENT;
|
||||
|
||||
typedef struct _TUN_NBL_POOL {
|
||||
NDIS_HANDLE Handle;
|
||||
NET_BUFFER_LIST *List;
|
||||
} TUN_NBL_POOL;
|
||||
|
||||
typedef enum _TUN_STATE {
|
||||
TUN_STATE_HALTED = 0, // The Halted state is the initial state of all adapters. When an adapter is in the Halted state, NDIS can call the driver's MiniportInitializeEx function to initialize the adapter.
|
||||
TUN_STATE_SHUTDOWN, // In the Shutdown state, a system shutdown and restart must occur before the system can use the adapter again
|
||||
@ -75,17 +65,22 @@ typedef struct _TUN_CTX {
|
||||
NDIS_HANDLE Handle;
|
||||
DEVICE_OBJECT *Object;
|
||||
volatile LONG64 RefCount;
|
||||
IRP volatile *ActiveIrp;
|
||||
|
||||
struct {
|
||||
KSPIN_LOCK Lock;
|
||||
IO_CSQ Csq;
|
||||
LIST_ENTRY List;
|
||||
} ReadQueue;
|
||||
} Device;
|
||||
|
||||
struct {
|
||||
NDIS_SPIN_LOCK Lock;
|
||||
NET_BUFFER_LIST *Head, *Tail;
|
||||
NET_BUFFER *Buffer;
|
||||
UINT Count;
|
||||
KSPIN_LOCK Lock;
|
||||
NET_BUFFER_LIST *FirstNbl, *LastNbl;
|
||||
NET_BUFFER *NextNb;
|
||||
LONG NumNbl;
|
||||
} PacketQueue;
|
||||
|
||||
NDIS_HANDLE NBLPool;
|
||||
NDIS_HANDLE NBLPool;
|
||||
} TUN_CTX;
|
||||
|
||||
static NDIS_HANDLE NdisMiniportDriverHandle = NULL;
|
||||
@ -102,21 +97,17 @@ static NDIS_HANDLE NdisMiniportDriverHandle = NULL;
|
||||
#error "Unable to determine endianess"
|
||||
#endif
|
||||
|
||||
#define TUN_CSQ_INSERT_HEAD ((PVOID)TRUE)
|
||||
#define TUN_CSQ_INSERT_TAIL ((PVOID)FALSE)
|
||||
|
||||
#define InterlockedGet(val) (InterlockedAdd((val), 0))
|
||||
#define InterlockedGet64(val) (InterlockedAdd64((val), 0))
|
||||
#define InterlockedGetPointer(val) (InterlockedCompareExchangePointer((val), NULL, NULL))
|
||||
#define InterlockedSubtract(val, n) (InterlockedAdd((val), -(LONG)(n)))
|
||||
#define InterlockedSubtract64(val, n) (InterlockedAdd64((val), -(LONG64)(n)))
|
||||
#define TunPacketAlign(size) (((UINT)(size) + (UINT)(TUN_EXCH_ALIGNMENT - 1)) & ~(UINT)(TUN_EXCH_ALIGNMENT - 1))
|
||||
#define TunInitUnicodeString(str, buf) { (str)->Length = 0; (str)->MaximumLength = sizeof(buf); (str)->Buffer = buf; }
|
||||
|
||||
_IRQL_requires_same_
|
||||
static void TunAppendNBL(_Inout_ NET_BUFFER_LIST **head, _Inout_ NET_BUFFER_LIST **tail, __drv_aliasesMem _In_ NET_BUFFER_LIST *nbl)
|
||||
{
|
||||
*(*tail ? &NET_BUFFER_LIST_NEXT_NBL(*tail) : head) = nbl;
|
||||
*tail = nbl;
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
_IRQL_requires_same_
|
||||
static void TunIndicateStatus(_In_ TUN_CTX *ctx)
|
||||
@ -172,7 +163,7 @@ static NTSTATUS TunCheckForPause(_Inout_ TUN_CTX *ctx, _In_ LONG64 increment)
|
||||
return
|
||||
InterlockedGet((LONG *)&ctx->State) != TUN_STATE_RUNNING ? STATUS_NDIS_PAUSED :
|
||||
ctx->PowerState >= NdisDeviceStateD1 ? STATUS_NDIS_LOW_POWER_STATE :
|
||||
STATUS_SUCCESS;
|
||||
STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@ -180,17 +171,84 @@ static void TunCompletePause(_Inout_ TUN_CTX *ctx, _In_ LONG64 decrement)
|
||||
{
|
||||
ASSERT(decrement <= InterlockedGet64(&ctx->ActiveTransactionCount));
|
||||
if (!InterlockedSubtract64(&ctx->ActiveTransactionCount, decrement) &&
|
||||
InterlockedCompareExchange((LONG *)&ctx->State, TUN_STATE_PAUSED, TUN_STATE_PAUSING) == TUN_STATE_PAUSING)
|
||||
InterlockedCompareExchange((LONG *)&ctx->State, TUN_STATE_PAUSED, TUN_STATE_PAUSING) == TUN_STATE_PAUSING)
|
||||
NdisMPauseComplete(ctx->MiniportAdapterHandle);
|
||||
}
|
||||
|
||||
_IRQL_requires_same_
|
||||
static ULONG TunSetNBLStatus(_Inout_opt_ NET_BUFFER_LIST *nbl, _In_ NDIS_STATUS status)
|
||||
static IO_CSQ_INSERT_IRP_EX TunCsqInsertIrpEx;
|
||||
_Use_decl_annotations_
|
||||
static NTSTATUS TunCsqInsertIrpEx(IO_CSQ *Csq, IRP *Irp, PVOID InsertContext)
|
||||
{
|
||||
ULONG nbl_count = 0;
|
||||
for (; nbl; nbl = NET_BUFFER_LIST_NEXT_NBL(nbl), nbl_count++)
|
||||
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
||||
(InsertContext == TUN_CSQ_INSERT_HEAD ? InsertHeadList : InsertTailList)(&ctx->Device.ReadQueue.List, &Irp->Tail.Overlay.ListEntry);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static IO_CSQ_REMOVE_IRP TunCsqRemoveIrp;
|
||||
_Use_decl_annotations_
|
||||
static VOID TunCsqRemoveIrp(IO_CSQ *Csq, IRP *Irp)
|
||||
{
|
||||
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
||||
}
|
||||
|
||||
static IO_CSQ_PEEK_NEXT_IRP TunCsqPeekNextIrp;
|
||||
_Use_decl_annotations_
|
||||
static IRP *TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext)
|
||||
{
|
||||
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
||||
|
||||
/* If the IRP is non-NULL, we will start peeking from that IRP onwards, else
|
||||
* we will start from the listhead. This is done under the assumption that
|
||||
* new IRPs are always inserted at the tail. */
|
||||
for (LIST_ENTRY
|
||||
*listHead = &ctx->Device.ReadQueue.List,
|
||||
*nextEntry = Irp ? Irp->Tail.Overlay.ListEntry.Flink : listHead->Flink;
|
||||
nextEntry != listHead;
|
||||
nextEntry = nextEntry->Flink)
|
||||
{
|
||||
IRP *nextIrp = CONTAINING_RECORD(nextEntry, IRP, Tail.Overlay.ListEntry);
|
||||
if (!PeekContext)
|
||||
return nextIrp;
|
||||
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(nextIrp);
|
||||
if (stack->FileObject == (FILE_OBJECT *)PeekContext)
|
||||
return nextIrp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_IRQL_raises_(DISPATCH_LEVEL)
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
_Acquires_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
||||
static VOID TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql)
|
||||
{
|
||||
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
||||
KeAcquireSpinLock(&ctx->Device.ReadQueue.Lock, Irql);
|
||||
}
|
||||
|
||||
_IRQL_requires_(DISPATCH_LEVEL)
|
||||
_Releases_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
|
||||
static VOID TunCsqReleaseLock(_In_ IO_CSQ *Csq, _In_ _IRQL_restores_ KIRQL Irql)
|
||||
{
|
||||
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
||||
KeReleaseSpinLock(&ctx->Device.ReadQueue.Lock, Irql);
|
||||
}
|
||||
|
||||
static IO_CSQ_COMPLETE_CANCELED_IRP TunCsqCompleteCanceledIrp;
|
||||
_Use_decl_annotations_
|
||||
static VOID TunCsqCompleteCanceledIrp(IO_CSQ *Csq, IRP *Irp)
|
||||
{
|
||||
TUN_CTX *ctx = CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq);
|
||||
TunCompleteRequest(Irp, 0, STATUS_CANCELLED);
|
||||
TunCompletePause(ctx, 1);
|
||||
}
|
||||
|
||||
_IRQL_requires_same_
|
||||
static void TunSetNBLStatus(_Inout_opt_ NET_BUFFER_LIST *nbl, _In_ NDIS_STATUS status)
|
||||
{
|
||||
for (; nbl; nbl = NET_BUFFER_LIST_NEXT_NBL(nbl))
|
||||
NET_BUFFER_LIST_STATUS(nbl) = status;
|
||||
return nbl_count;
|
||||
}
|
||||
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
@ -219,126 +277,214 @@ static NTSTATUS TunGetIRPBuffer(_Inout_ IRP *Irp, _Out_ UCHAR **buffer, _Out_ UL
|
||||
/* Get buffer size and address. */
|
||||
if (!Irp->MdlAddress)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
ULONG sizeMdl;
|
||||
NdisQueryMdl(Irp->MdlAddress, buffer, &sizeMdl, priority);
|
||||
ULONG size_mdl;
|
||||
NdisQueryMdl(Irp->MdlAddress, buffer, &size_mdl, priority);
|
||||
if (!buffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
if (sizeMdl < *size)
|
||||
*size = sizeMdl;
|
||||
if (size_mdl < *size)
|
||||
*size = size_mdl;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_Requires_lock_not_held_(ctx->PacketQueue.Lock.SpinLock)
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
static void TunProcessPacketQueue(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
||||
static NTSTATUS TunWriteIntoIrp(_Inout_ IRP *Irp, _In_ NET_BUFFER *nb)
|
||||
{
|
||||
/* Prepare IRP for read. */
|
||||
CCHAR PriorityBoost = IO_NO_INCREMENT;
|
||||
ULONG SendCompleteFlags = 0;
|
||||
ULONG nbl_count = 0;
|
||||
NET_BUFFER_LIST *nbl_head = NULL, *nbl_tail = NULL;
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
UCHAR *buffer = NULL;
|
||||
ULONG size = 0;
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = TunGetIRPBuffer(Irp, &buffer, &size);
|
||||
if (Irp->IoStatus.Status != STATUS_SUCCESS)
|
||||
goto cleanup_complete_req;
|
||||
status = TunGetIRPBuffer(Irp, &buffer, &size);
|
||||
if (status != STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
UCHAR *b = buffer, *b_end = buffer + size;
|
||||
LONG64 stat_size = 0, stat_p_ok = 0, stat_p_err = 0;
|
||||
UCHAR *b = buffer + Irp->IoStatus.Information, *b_end = buffer + size;
|
||||
ULONG p_size = NET_BUFFER_DATA_LENGTH(nb);
|
||||
if (p_size > TUN_EXCH_MAX_IP_PACKET_SIZE)
|
||||
return NDIS_STATUS_INVALID_LENGTH;
|
||||
|
||||
/* Transfer packets from NBL queue to IRP. */
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
if (ctx->PacketQueue.Lock.OldIrql >= DISPATCH_LEVEL)
|
||||
SendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL;
|
||||
while (ctx->PacketQueue.Head) {
|
||||
for (; ctx->PacketQueue.Buffer; ctx->PacketQueue.Buffer = NET_BUFFER_NEXT_NB(ctx->PacketQueue.Buffer)) {
|
||||
ULONG p_size = NET_BUFFER_DATA_LENGTH(ctx->PacketQueue.Buffer);
|
||||
if (p_size > TUN_EXCH_MAX_IP_PACKET_SIZE) {
|
||||
NET_BUFFER_LIST_STATUS(ctx->PacketQueue.Head) = NDIS_STATUS_INVALID_LENGTH;
|
||||
goto error;
|
||||
}
|
||||
UCHAR *b_next = b + TunPacketAlign(sizeof(TUN_PACKET) + p_size);
|
||||
if (b_next > b_end)
|
||||
return STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
UCHAR *b_next = b + TunPacketAlign(sizeof(TUN_PACKET) + p_size);
|
||||
if (b_next > b_end)
|
||||
goto cleanup_NdisReleaseSpinLock;
|
||||
TUN_PACKET *p = (TUN_PACKET *)b;
|
||||
p->Size = p_size;
|
||||
void *ptr = NdisGetDataBuffer(nb, p_size, p->Data, 1, 0);
|
||||
if (!ptr)
|
||||
return NDIS_STATUS_RESOURCES;
|
||||
if (ptr != p->Data)
|
||||
NdisMoveMemory(p->Data, ptr, p_size);
|
||||
|
||||
TUN_PACKET *p = (TUN_PACKET *)b;
|
||||
p->Size = p_size;
|
||||
void *ptr = NdisGetDataBuffer(ctx->PacketQueue.Buffer, p_size, p->Data, 1, 0);
|
||||
if (!ptr) {
|
||||
NET_BUFFER_LIST_STATUS(ctx->PacketQueue.Head) = NDIS_STATUS_RESOURCES;
|
||||
goto error;
|
||||
}
|
||||
if (ptr != p->Data)
|
||||
NdisMoveMemory(p->Data, ptr, p_size);
|
||||
Irp->IoStatus.Information = b_next - buffer;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
stat_size += p_size;
|
||||
stat_p_ok++;
|
||||
b = b_next;
|
||||
#define NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl))
|
||||
|
||||
_IRQL_requires_same_
|
||||
static void TunNBLRefInit(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl)
|
||||
{
|
||||
InterlockedAdd64(&ctx->ActiveTransactionCount, 1);
|
||||
InterlockedAdd(&ctx->PacketQueue.NumNbl, 1);
|
||||
InterlockedExchange64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1);
|
||||
}
|
||||
|
||||
_IRQL_requires_same_
|
||||
static void TunNBLRefInc(_Inout_ NET_BUFFER_LIST *nbl)
|
||||
{
|
||||
ASSERT(InterlockedGet64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl)));
|
||||
InterlockedAdd64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1);
|
||||
}
|
||||
|
||||
_IRQL_requires_same_
|
||||
static BOOLEAN TunNBLRefDec(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl)
|
||||
{
|
||||
ASSERT(InterlockedGet64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl)));
|
||||
if (!InterlockedSubtract64(NET_BUFFER_LIST_MINIPORT_RESERVED_REFCOUNT(nbl), 1)) {
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, 0);
|
||||
InterlockedSubtract(&ctx->PacketQueue.NumNbl, 1);
|
||||
TunCompletePause(ctx, 1);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_IRQL_requires_same_
|
||||
static void TunAppendNBL(_Inout_ NET_BUFFER_LIST **head, _Inout_ NET_BUFFER_LIST **tail, __drv_aliasesMem _In_ NET_BUFFER_LIST *nbl)
|
||||
{
|
||||
*(*tail ? &NET_BUFFER_LIST_NEXT_NBL(*tail) : head) = nbl;
|
||||
*tail = nbl;
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||
}
|
||||
|
||||
_Requires_lock_not_held_(ctx->PacketQueue.Lock)
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
static void TunQueueAppend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER_LIST *nbl, _In_ UINT max_nbls)
|
||||
{
|
||||
for (NET_BUFFER_LIST *nbl_next; nbl; nbl = nbl_next) {
|
||||
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
|
||||
if (!NET_BUFFER_LIST_FIRST_NB(nbl)) {
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, 0);
|
||||
continue;
|
||||
|
||||
error:
|
||||
stat_p_err++;
|
||||
}
|
||||
|
||||
/* NBL depleted: Relocate it to the "completed" queue. */
|
||||
NET_BUFFER_LIST *nbl_next = NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.Head);
|
||||
TunAppendNBL(&nbl_head, &nbl_tail, ctx->PacketQueue.Head);
|
||||
nbl_count++;
|
||||
ctx->PacketQueue.Head = nbl_next;
|
||||
if (ctx->PacketQueue.Head)
|
||||
ctx->PacketQueue.Buffer = NET_BUFFER_LIST_FIRST_NB(ctx->PacketQueue.Head);
|
||||
else
|
||||
ctx->PacketQueue.Tail = NULL;
|
||||
}
|
||||
cleanup_NdisReleaseSpinLock:
|
||||
ctx->PacketQueue.Count -= nbl_count;
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
KLOCK_QUEUE_HANDLE lqh;
|
||||
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh);
|
||||
TunNBLRefInit(ctx, nbl);
|
||||
TunAppendNBL(&ctx->PacketQueue.FirstNbl, &ctx->PacketQueue.LastNbl, nbl);
|
||||
|
||||
Irp->IoStatus.Information = b - buffer;
|
||||
PriorityBoost = IO_NETWORK_INCREMENT;
|
||||
while ((UINT)InterlockedGet(&ctx->PacketQueue.NumNbl) > max_nbls && ctx->PacketQueue.FirstNbl) {
|
||||
NET_BUFFER_LIST *nbl_second = NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.FirstNbl);
|
||||
|
||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCOutOctets, stat_size);
|
||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCOutUcastOctets, stat_size);
|
||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifHCOutUcastPkts, stat_p_ok);
|
||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifOutErrors, stat_p_err);
|
||||
NET_BUFFER_LIST_STATUS(ctx->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED;
|
||||
TunNBLRefDec(ctx, ctx->PacketQueue.FirstNbl);
|
||||
|
||||
cleanup_complete_req:
|
||||
IoCompleteRequest(Irp, PriorityBoost);
|
||||
IoStartNextPacket(ctx->Device.Object, FALSE);
|
||||
ctx->PacketQueue.NextNb = NULL;
|
||||
ctx->PacketQueue.FirstNbl = nbl_second;
|
||||
if (!ctx->PacketQueue.FirstNbl)
|
||||
ctx->PacketQueue.LastNbl = NULL;
|
||||
}
|
||||
|
||||
if (nbl_head) {
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl_head, SendCompleteFlags);
|
||||
TunCompletePause(ctx, nbl_count);
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
}
|
||||
}
|
||||
|
||||
static DRIVER_STARTIO TunStartIo;
|
||||
_Use_decl_annotations_
|
||||
VOID TunStartIo(_Inout_ DEVICE_OBJECT *DeviceObject, _Inout_ IRP *Irp)
|
||||
_Requires_lock_held_(ctx->PacketQueue.Lock)
|
||||
_IRQL_requires_(DISPATCH_LEVEL)
|
||||
static NET_BUFFER *TunQueueRemove(_Inout_ TUN_CTX *ctx, _Out_ NET_BUFFER_LIST **nbl)
|
||||
{
|
||||
TUN_CTX *ctx = TunGetContext(DeviceObject);
|
||||
if (!ctx) {
|
||||
TunCompleteRequest(Irp, 0, STATUS_CANCELLED);
|
||||
IoStartNextPacket(DeviceObject, FALSE);
|
||||
return;
|
||||
}
|
||||
NET_BUFFER_LIST *nbl_top;
|
||||
NET_BUFFER *ret;
|
||||
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
if (!ctx->PacketQueue.Head) {
|
||||
/* Set active IRP before releasing the spin lock. If this thread would be interrupted after ctx->PacketQueue.Lock
|
||||
* release and before ctx->Device.ActiveIrp is set, and TunSendNetBufferLists() is called meanwhile, the later
|
||||
* will not notice pending IRP and not call TunProcessPacketQueue() causing read to stall until next
|
||||
* TunSendNetBufferLists() call. */
|
||||
InterlockedExchangePointer((PVOID volatile *)&ctx->Device.ActiveIrp, Irp);
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
return;
|
||||
}
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
nbl_top = ctx->PacketQueue.FirstNbl;
|
||||
*nbl = nbl_top;
|
||||
if (!nbl_top)
|
||||
return NULL;
|
||||
if (!ctx->PacketQueue.NextNb)
|
||||
ctx->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(nbl_top);
|
||||
ret = ctx->PacketQueue.NextNb;
|
||||
ctx->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(ret);
|
||||
if (!ctx->PacketQueue.NextNb) {
|
||||
ctx->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(nbl_top);
|
||||
if (!ctx->PacketQueue.FirstNbl)
|
||||
ctx->PacketQueue.LastNbl = NULL;
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl_top) = NULL;
|
||||
} else
|
||||
TunNBLRefInc(nbl_top);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TunProcessPacketQueue(ctx, Irp);
|
||||
/* Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock. */
|
||||
_Requires_lock_held_(ctx->PacketQueue.Lock)
|
||||
_IRQL_requires_(DISPATCH_LEVEL)
|
||||
static void TunQueuePrepend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER *nb, _In_ NET_BUFFER_LIST *nbl_detached)
|
||||
{
|
||||
ctx->PacketQueue.NextNb = nb;
|
||||
|
||||
if (!nbl_detached)
|
||||
return;
|
||||
|
||||
if (!ctx->PacketQueue.FirstNbl)
|
||||
ctx->PacketQueue.FirstNbl = ctx->PacketQueue.LastNbl = nbl_detached;
|
||||
else {
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl_detached) = ctx->PacketQueue.FirstNbl;
|
||||
ctx->PacketQueue.FirstNbl = nbl_detached;
|
||||
}
|
||||
}
|
||||
|
||||
_Requires_lock_not_held_(ctx->PacketQueue.Lock)
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
static void TunQueueProcess(_Inout_ TUN_CTX *ctx)
|
||||
{
|
||||
IRP *Irp = NULL;
|
||||
NET_BUFFER *nb;
|
||||
KLOCK_QUEUE_HANDLE lqh;
|
||||
|
||||
for (;;) {
|
||||
NET_BUFFER_LIST *nbl;
|
||||
NTSTATUS status;
|
||||
|
||||
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh);
|
||||
if (!Irp) {
|
||||
nb = TunQueueRemove(ctx, &nbl);
|
||||
if (!nb) {
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
return;
|
||||
}
|
||||
Irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, NULL);
|
||||
if (!Irp) {
|
||||
if (!nbl|| !TunNBLRefDec(ctx, nbl))
|
||||
TunQueuePrepend(ctx, nb, nbl);
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
nb = TunQueueRemove(ctx, &nbl);
|
||||
}
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
|
||||
if (!nb || (status = TunWriteIntoIrp(Irp, nb)) == STATUS_BUFFER_TOO_SMALL) { /* Irp complete */
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
|
||||
TunCompletePause(ctx, 1);
|
||||
Irp = NULL;
|
||||
} else if (status == NDIS_STATUS_INVALID_LENGTH || status == NDIS_STATUS_RESOURCES) { /* nb-related errors */
|
||||
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh);
|
||||
if (nbl)
|
||||
NET_BUFFER_LIST_STATUS(nbl) = status;
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_HEAD);
|
||||
Irp = NULL;
|
||||
} else if (!NT_SUCCESS(status)) { /* Irp-related errors */
|
||||
Irp->IoStatus.Status = status;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
TunCompletePause(ctx, 1);
|
||||
Irp = NULL;
|
||||
}
|
||||
if (nbl)
|
||||
TunNBLRefDec(ctx, nbl);
|
||||
}
|
||||
}
|
||||
|
||||
static DRIVER_DISPATCH TunDispatchCreate;
|
||||
@ -353,7 +499,7 @@ static NTSTATUS TunDispatchCreate(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
goto cleanup_complete_req;
|
||||
}
|
||||
|
||||
if ((status = TunCheckForPause(ctx, 1i64)) != STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1)))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
ASSERT(InterlockedGet64(&ctx->Device.RefCount) < MAXLONG64);
|
||||
@ -361,7 +507,7 @@ static NTSTATUS TunDispatchCreate(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
TunIndicateStatus(ctx);
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1i64);
|
||||
TunCompletePause(ctx, 1);
|
||||
cleanup_complete_req:
|
||||
TunCompleteRequest(Irp, 0, status);
|
||||
return status;
|
||||
@ -379,7 +525,7 @@ static NTSTATUS TunDispatchClose(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
goto cleanup_complete_req;
|
||||
}
|
||||
|
||||
if ((status = TunCheckForPause(ctx, 1i64)) != STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1)))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
ASSERT(InterlockedGet64(&ctx->Device.RefCount) > 0);
|
||||
@ -387,7 +533,7 @@ static NTSTATUS TunDispatchClose(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
TunIndicateStatus(ctx);
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1i64);
|
||||
TunCompletePause(ctx, 1);
|
||||
cleanup_complete_req:
|
||||
TunCompleteRequest(Irp, 0, status);
|
||||
return status;
|
||||
@ -405,16 +551,24 @@ static NTSTATUS TunDispatchRead(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
goto cleanup_complete_req;
|
||||
}
|
||||
|
||||
if ((status = TunCheckForPause(ctx, 1i64)) != STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1)))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
IoMarkIrpPending(Irp);
|
||||
IoStartPacket(DeviceObject, Irp, NULL, NULL);
|
||||
TunCompletePause(ctx, 1i64);
|
||||
Irp->IoStatus.Information = 0;
|
||||
InterlockedIncrement64(&ctx->ActiveTransactionCount);
|
||||
status = IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
TunCompletePause(ctx, 1);
|
||||
goto cleanup_TunCompletePause;
|
||||
}
|
||||
|
||||
TunQueueProcess(ctx);
|
||||
|
||||
TunCompletePause(ctx, 1);
|
||||
return STATUS_PENDING;
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1i64);
|
||||
TunCompletePause(ctx, 1);
|
||||
cleanup_complete_req:
|
||||
TunCompleteRequest(Irp, 0, status);
|
||||
return status;
|
||||
@ -433,13 +587,13 @@ static NTSTATUS TunDispatchWrite(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
goto cleanup_complete_req;
|
||||
}
|
||||
|
||||
if ((status = TunCheckForPause(ctx, 1i64)) != STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1)))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
UCHAR *buffer = NULL;
|
||||
ULONG size = 0;
|
||||
status = TunGetIRPBuffer(Irp, &buffer, &size);
|
||||
if (status != STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(status))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
const UCHAR *b = buffer, *b_end = buffer + size;
|
||||
@ -527,7 +681,7 @@ static NTSTATUS TunDispatchWrite(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||
|
||||
MDL *mdl = NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(nbl));
|
||||
if (NET_BUFFER_LIST_STATUS(nbl) == NDIS_STATUS_SUCCESS) {
|
||||
if (NT_SUCCESS(NET_BUFFER_LIST_STATUS(nbl))) {
|
||||
ULONG p_size = MmGetMdlByteCount(mdl);
|
||||
stat_size += p_size;
|
||||
stat_p_ok++;
|
||||
@ -545,12 +699,42 @@ static NTSTATUS TunDispatchWrite(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
information = b - buffer;
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1i64);
|
||||
TunCompletePause(ctx, 1);
|
||||
cleanup_complete_req:
|
||||
TunCompleteRequest(Irp, information, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static DRIVER_DISPATCH TunDispatchCleanup;
|
||||
_Use_decl_annotations_
|
||||
static NTSTATUS TunDispatchCleanup(DEVICE_OBJECT *DeviceObject, IRP *Irp)
|
||||
{
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
TUN_CTX *ctx = TunGetContext(DeviceObject);
|
||||
if (!ctx) {
|
||||
status = STATUS_INVALID_HANDLE;
|
||||
goto cleanup_complete_req;
|
||||
}
|
||||
|
||||
LONG64 count = 0;
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1)))
|
||||
goto cleanup_TunCompletePause;
|
||||
|
||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
|
||||
IRP *pending_irp;
|
||||
while ((pending_irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, stack->FileObject)) != NULL) {
|
||||
count++;
|
||||
TunCompleteRequest(pending_irp, 0, STATUS_CANCELLED);
|
||||
}
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1LL + count);
|
||||
cleanup_complete_req:
|
||||
TunCompleteRequest(Irp, 0, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static MINIPORT_SET_OPTIONS TunSetOptions;
|
||||
_Use_decl_annotations_
|
||||
static NDIS_STATUS TunSetOptions(NDIS_HANDLE NdisDriverHandle, NDIS_HANDLE DriverContext)
|
||||
@ -567,35 +751,38 @@ static NDIS_STATUS TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_P
|
||||
if (InterlockedCompareExchange((LONG *)&ctx->State, TUN_STATE_PAUSING, TUN_STATE_RUNNING) != TUN_STATE_RUNNING)
|
||||
return NDIS_STATUS_FAILURE;
|
||||
|
||||
/* Reset adapter context in device object, as Windows keep calling dispatch handlers even after NdisDeregisterDeviceEx(). */
|
||||
/* Reset adapter context in device object, as Windows keeps calling dispatch handlers even after NdisDeregisterDeviceEx(). */
|
||||
TUN_CTX **control_device_extension = (TUN_CTX **)NdisGetDeviceReservedExtension(ctx->Device.Object);
|
||||
if (control_device_extension)
|
||||
InterlockedExchangePointer(control_device_extension, NULL);
|
||||
|
||||
ULONG nbl_count = 0;
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
if (ctx->PacketQueue.Head) {
|
||||
NET_BUFFER_LIST *nbl = ctx->PacketQueue.Head;
|
||||
ctx->PacketQueue.Head = NULL;
|
||||
ctx->PacketQueue.Buffer = NULL;
|
||||
ctx->PacketQueue.Tail = NULL;
|
||||
ctx->PacketQueue.Count = 0;
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
LONG64 count = 1;
|
||||
InterlockedAdd64(&ctx->ActiveTransactionCount, 1);
|
||||
|
||||
nbl_count += TunSetNBLStatus(nbl, NDIS_STATUS_PAUSED);
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, 0);
|
||||
} else
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
KLOCK_QUEUE_HANDLE lqh;
|
||||
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh);
|
||||
TunSetNBLStatus(ctx->PacketQueue.FirstNbl, STATUS_NDIS_PAUSED);
|
||||
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) {
|
||||
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
|
||||
TunNBLRefDec(ctx, nbl);
|
||||
}
|
||||
ctx->PacketQueue.FirstNbl = NULL;
|
||||
ctx->PacketQueue.LastNbl = NULL;
|
||||
ctx->PacketQueue.NextNb = NULL;
|
||||
InterlockedExchange(&ctx->PacketQueue.NumNbl, 0);
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
|
||||
/* Cancel pending IRP to unblock waiting clients. */
|
||||
IRP *Irp = InterlockedExchangePointer((PVOID volatile *)&ctx->Device.ActiveIrp, NULL);
|
||||
if (Irp)
|
||||
TunCompleteRequest(Irp, 0, STATUS_CANCELLED);
|
||||
/* Cancel pending IRPs to unblock waiting clients. */
|
||||
IRP *pending_irp;
|
||||
while ((pending_irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, NULL)) != NULL) {
|
||||
count++;
|
||||
TunCompleteRequest(pending_irp, 0, STATUS_CANCELLED);
|
||||
}
|
||||
|
||||
InterlockedExchange64(&ctx->Device.RefCount, 0);
|
||||
InterlockedExchange64(&ctx->Device.RefCount, 0); //TODO: Is this correct? Aren't handles still open potentially? Do we have to do something with the close handler?
|
||||
TunIndicateStatus(ctx);
|
||||
|
||||
if (InterlockedSubtract64(&ctx->ActiveTransactionCount, nbl_count))
|
||||
if (InterlockedSubtract64(&ctx->ActiveTransactionCount, count))
|
||||
return NDIS_STATUS_PENDING;
|
||||
|
||||
InterlockedExchange((LONG *)&ctx->State, TUN_STATE_PAUSED);
|
||||
@ -633,35 +820,24 @@ _Use_decl_annotations_
|
||||
static void TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
|
||||
{
|
||||
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
||||
ULONG nbl_drop_count = 0;
|
||||
NET_BUFFER_LIST *nbl_keep_head = NULL, *nbl_keep_tail = NULL, *nbl_drop_head = NULL, *nbl_drop_tail = NULL;
|
||||
KLOCK_QUEUE_HANDLE lqh;
|
||||
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
ULONG SendCompleteFlags = ctx->PacketQueue.Lock.OldIrql >= DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0;
|
||||
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh);
|
||||
|
||||
/* Split NBL queue into two queues: one with NBLs to keep, one with NBLs to drop. */
|
||||
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.Head, *nbl_next; nbl; nbl = nbl_next) {
|
||||
NET_BUFFER_LIST *Last = NULL, **LastLink = &ctx->PacketQueue.FirstNbl;
|
||||
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) {
|
||||
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
|
||||
if (NDIS_GET_NET_BUFFER_LIST_CANCEL_ID(nbl) == CancelId) {
|
||||
NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SEND_ABORTED;
|
||||
TunAppendNBL(&nbl_drop_head, &nbl_drop_tail, nbl);
|
||||
nbl_drop_count++;
|
||||
} else
|
||||
TunAppendNBL(&nbl_keep_head, &nbl_keep_tail, nbl);
|
||||
*LastLink = nbl_next;
|
||||
TunNBLRefDec(ctx, nbl);
|
||||
} else {
|
||||
Last = nbl;
|
||||
LastLink = &NET_BUFFER_LIST_NEXT_NBL(nbl);
|
||||
}
|
||||
}
|
||||
ctx->PacketQueue.LastNbl = Last;
|
||||
|
||||
if (ctx->PacketQueue.Head != nbl_keep_head) {
|
||||
ctx->PacketQueue.Buffer = nbl_keep_head ? NET_BUFFER_LIST_FIRST_NB(nbl_keep_head) : NULL;
|
||||
ctx->PacketQueue.Head = nbl_keep_head;
|
||||
}
|
||||
|
||||
ctx->PacketQueue.Count -= nbl_drop_count;
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
|
||||
if (nbl_drop_head) {
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl_drop_head, SendCompleteFlags);
|
||||
TunCompletePause(ctx, nbl_drop_count);
|
||||
}
|
||||
KeReleaseInStackQueuedSpinLock(&lqh);
|
||||
}
|
||||
|
||||
static MINIPORT_DEVICE_PNP_EVENT_NOTIFY TunDevicePnPEventNotify;
|
||||
@ -705,7 +881,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
|
||||
TUN_CTX *ctx;
|
||||
#pragma warning(suppress: 6014) /* Leaking memory 'ctx'. Note: 'ctx' is aliased in attr.MiniportAdapterContext; or freed on failure. */
|
||||
if (NdisAllocateMemoryWithTag(&ctx, sizeof(TUN_CTX), TUN_MEMORY_TAG) != NDIS_STATUS_SUCCESS)
|
||||
if (!NT_SUCCESS(NdisAllocateMemoryWithTag(&ctx, sizeof(TUN_CTX), TUN_MEMORY_TAG)))
|
||||
return NDIS_STATUS_FAILURE;
|
||||
|
||||
NdisZeroMemory(ctx, sizeof(*ctx));
|
||||
@ -746,7 +922,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
.InterfaceType = NdisInterfaceInternal,
|
||||
.MiniportAdapterContext = ctx
|
||||
};
|
||||
if (NdisMSetMiniportAttributes(MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&attr) != NDIS_STATUS_SUCCESS) {
|
||||
if (!NT_SUCCESS(NdisMSetMiniportAttributes(MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&attr))) {
|
||||
status = NDIS_STATUS_FAILURE;
|
||||
goto cleanup_ctx;
|
||||
}
|
||||
@ -821,12 +997,22 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
.SupportedOidListLength = sizeof(suported_oids),
|
||||
.PowerManagementCapabilitiesEx = &pmcap
|
||||
};
|
||||
if (NdisMSetMiniportAttributes(MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&gen) != NDIS_STATUS_SUCCESS) {
|
||||
if (!NT_SUCCESS(NdisMSetMiniportAttributes(MiniportAdapterHandle, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&gen))) {
|
||||
status = NDIS_STATUS_FAILURE;
|
||||
goto cleanup_ctx;
|
||||
}
|
||||
|
||||
NdisAllocateSpinLock(&ctx->PacketQueue.Lock);
|
||||
KeInitializeSpinLock(&ctx->PacketQueue.Lock);
|
||||
|
||||
KeInitializeSpinLock(&ctx->Device.ReadQueue.Lock);
|
||||
InitializeListHead(&ctx->Device.ReadQueue.List);
|
||||
IoCsqInitializeEx(&ctx->Device.ReadQueue.Csq,
|
||||
TunCsqInsertIrpEx,
|
||||
TunCsqRemoveIrp,
|
||||
TunCsqPeekNextIrp,
|
||||
TunCsqAcquireLock,
|
||||
TunCsqReleaseLock,
|
||||
TunCsqCompleteCanceledIrp);
|
||||
|
||||
/* A miniport driver can call NdisMIndicateStatusEx after setting its
|
||||
* registration attributes even if the driver is still in the context
|
||||
@ -848,7 +1034,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
ctx->NBLPool = NdisAllocateNetBufferListPool(MiniportAdapterHandle, &nbl_pool_param);
|
||||
if (!ctx->NBLPool) {
|
||||
status = NDIS_STATUS_FAILURE;
|
||||
goto cleanup_NdisFreeSpinLock;
|
||||
goto cleanup_ctx;
|
||||
}
|
||||
|
||||
WCHAR device_name[(sizeof(L"\\Device\\" TUN_DEVICE_NAME) + 10/*MAXULONG as string*/) / sizeof(WCHAR)];
|
||||
@ -862,11 +1048,25 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
RtlUnicodeStringPrintf(&unicode_symbolic_name, L"\\DosDevices\\" TUN_DEVICE_NAME, (ULONG)MiniportInitParameters->NetLuid.Info.NetLuidIndex);
|
||||
|
||||
static PDRIVER_DISPATCH dispatch_table[IRP_MJ_MAXIMUM_FUNCTION + 1] = {
|
||||
TunDispatchCreate, /* IRP_MJ_CREATE */
|
||||
NULL, /* IRP_MJ_CREATE_NAMED_PIPE */
|
||||
TunDispatchClose, /* IRP_MJ_CLOSE */
|
||||
TunDispatchRead, /* IRP_MJ_READ */
|
||||
TunDispatchWrite, /* IRP_MJ_WRITE */
|
||||
TunDispatchCreate, /* IRP_MJ_CREATE */
|
||||
NULL, /* IRP_MJ_CREATE_NAMED_PIPE */
|
||||
TunDispatchClose, /* IRP_MJ_CLOSE */
|
||||
TunDispatchRead, /* IRP_MJ_READ */
|
||||
TunDispatchWrite, /* IRP_MJ_WRITE */
|
||||
NULL, /* IRP_MJ_QUERY_INFORMATION */
|
||||
NULL, /* IRP_MJ_SET_INFORMATION */
|
||||
NULL, /* IRP_MJ_QUERY_EA */
|
||||
NULL, /* IRP_MJ_SET_EA */
|
||||
NULL, /* IRP_MJ_FLUSH_BUFFERS */
|
||||
NULL, /* IRP_MJ_QUERY_VOLUME_INFORMATION */
|
||||
NULL, /* IRP_MJ_SET_VOLUME_INFORMATION */
|
||||
NULL, /* IRP_MJ_DIRECTORY_CONTROL */
|
||||
NULL, /* IRP_MJ_FILE_SYSTEM_CONTROL */
|
||||
NULL, /* IRP_MJ_DEVICE_CONTROL */
|
||||
NULL, /* IRP_MJ_INTERNAL_DEVICE_CONTROL */
|
||||
NULL, /* IRP_MJ_SHUTDOWN */
|
||||
NULL, /* IRP_MJ_LOCK_CONTROL */
|
||||
TunDispatchCleanup, /* IRP_MJ_CLEANUP */
|
||||
};
|
||||
NDIS_DEVICE_OBJECT_ATTRIBUTES t = {
|
||||
.Header = {
|
||||
@ -880,7 +1080,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
.ExtensionSize = sizeof(TUN_CTX *),
|
||||
.DefaultSDDLString = &SDDL_DEVOBJ_SYS_ALL /* Kernel, and SYSTEM: full control. Others: none */
|
||||
};
|
||||
if (NdisRegisterDeviceEx(NdisMiniportDriverHandle, &t, &ctx->Device.Object, &ctx->Device.Handle) != NDIS_STATUS_SUCCESS) {
|
||||
if (!NT_SUCCESS(NdisRegisterDeviceEx(NdisMiniportDriverHandle, &t, &ctx->Device.Object, &ctx->Device.Handle))) {
|
||||
status = NDIS_STATUS_FAILURE;
|
||||
goto cleanup_NdisFreeNetBufferListPool;
|
||||
}
|
||||
@ -888,15 +1088,11 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL
|
||||
ctx->Device.Object->Flags &= ~DO_BUFFERED_IO;
|
||||
ctx->Device.Object->Flags |= DO_DIRECT_IO;
|
||||
|
||||
IoSetStartIoAttributes(ctx->Device.Object, TRUE, TRUE);
|
||||
|
||||
ctx->State = TUN_STATE_PAUSED;
|
||||
return NDIS_STATUS_SUCCESS;
|
||||
|
||||
cleanup_NdisFreeNetBufferListPool:
|
||||
NdisFreeNetBufferListPool(ctx->NBLPool);
|
||||
cleanup_NdisFreeSpinLock:
|
||||
NdisFreeSpinLock(&ctx->PacketQueue.Lock);
|
||||
cleanup_ctx:
|
||||
NdisFreeMemory(ctx, 0, 0);
|
||||
return status;
|
||||
@ -918,13 +1114,11 @@ static void TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltA
|
||||
return;
|
||||
|
||||
ASSERT(!InterlockedGet64(&ctx->ActiveTransactionCount));
|
||||
ASSERT(!InterlockedGetPointer((PVOID volatile *)&ctx->Device.ActiveIrp));
|
||||
ASSERT(!InterlockedGet64(&ctx->Device.RefCount));
|
||||
|
||||
/* Release resources. */
|
||||
NdisDeregisterDeviceEx(ctx->Device.Handle);
|
||||
NdisFreeNetBufferListPool(ctx->NBLPool);
|
||||
NdisFreeSpinLock(&ctx->PacketQueue.Lock);
|
||||
NdisFreeMemory(ctx, 0, 0);
|
||||
}
|
||||
|
||||
@ -1089,48 +1283,19 @@ _Use_decl_annotations_
|
||||
static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER_LIST *NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags)
|
||||
{
|
||||
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
|
||||
ULONG nbl_count = 0;
|
||||
|
||||
NDIS_STATUS status;
|
||||
if ((status = TunCheckForPause(ctx, 1i64)) != STATUS_SUCCESS) {
|
||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx, 1))) {
|
||||
TunSetNBLStatus(NetBufferLists, status);
|
||||
goto cleanup_NdisMSendNetBufferListsComplete;
|
||||
}
|
||||
|
||||
/* Append NBL(s) to the queue. */
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
if (ctx->PacketQueue.Tail)
|
||||
NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.Tail) = NetBufferLists;
|
||||
else {
|
||||
ctx->PacketQueue.Head = NetBufferLists;
|
||||
ctx->PacketQueue.Buffer = NET_BUFFER_LIST_FIRST_NB(NetBufferLists);
|
||||
}
|
||||
for (; NetBufferLists; NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists), nbl_count++)
|
||||
ctx->PacketQueue.Tail = NetBufferLists;
|
||||
ctx->PacketQueue.Count += nbl_count;
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
InterlockedAdd64(&ctx->ActiveTransactionCount, nbl_count);
|
||||
|
||||
IRP *Irp = InterlockedExchangePointer((PVOID volatile *)&ctx->Device.ActiveIrp, NULL);
|
||||
if (Irp)
|
||||
TunProcessPacketQueue(ctx, Irp);
|
||||
|
||||
/* Prevent accumulation of NBLs by keeping only TUN_QUEUE_MAX_NBLS most recent ones. */
|
||||
NdisAcquireSpinLock(&ctx->PacketQueue.Lock);
|
||||
for (nbl_count = 0; ctx->PacketQueue.Count > TUN_QUEUE_MAX_NBLS; ctx->PacketQueue.Count--, nbl_count++) {
|
||||
_Analysis_assume_(ctx->PacketQueue.Head); /* ctx->PacketQueue.Count > 0 => ctx->PacketQueue.Head != NULL. */
|
||||
NET_BUFFER_LIST *nbl = ctx->PacketQueue.Head;
|
||||
ctx->PacketQueue.Head = NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.Head);
|
||||
NET_BUFFER_LIST_NEXT_NBL(nbl) = NetBufferLists;
|
||||
NetBufferLists = nbl;
|
||||
}
|
||||
NdisReleaseSpinLock(&ctx->PacketQueue.Lock);
|
||||
|
||||
cleanup_NdisMSendNetBufferListsComplete:
|
||||
if (NetBufferLists)
|
||||
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, NetBufferLists, SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0);
|
||||
goto cleanup_TunCompletePause;
|
||||
}
|
||||
|
||||
TunCompletePause(ctx, 1i64 + nbl_count);
|
||||
TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS);
|
||||
TunQueueProcess(ctx);
|
||||
|
||||
cleanup_TunCompletePause:
|
||||
TunCompletePause(ctx, 1);
|
||||
}
|
||||
|
||||
DRIVER_INITIALIZE DriverEntry;
|
||||
@ -1166,10 +1331,5 @@ NTSTATUS DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
|
||||
.DirectOidRequestHandler = TunDirectOidRequest,
|
||||
.CancelDirectOidRequestHandler = TunCancelDirectOidRequest
|
||||
};
|
||||
NTSTATUS status = NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle);
|
||||
if (status != STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
DriverObject->DriverStartIo = TunStartIo;
|
||||
return STATUS_SUCCESS;
|
||||
return NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user