You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7688 lines
247 KiB
7688 lines
247 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tpackets.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for fast kernel-level file transmission
|
|
over a socket handle.
|
|
|
|
Author:
|
|
|
|
Vadim Eydelman (VadimE) January 1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "afdp.h"
|
|
|
|
|
|
#if DBG
|
|
PIRP Irp;
|
|
C_ASSERT (sizeof (AFD_TPACKETS_IRP_CTX)<=sizeof (Irp->Tail.Overlay.DriverContext));
|
|
#endif
|
|
|
|
#if DBG
|
|
ULONG
|
|
__inline
|
|
AFD_SET_TP_FLAGS (
|
|
PIRP TpIrp,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
PAFD_TPACKETS_IRP_CTX ctx = AFD_GET_TPIC(TpIrp);
|
|
ASSERT ((ctx->StateFlags & Flags)==0);
|
|
return InterlockedExchangeAdd ((PLONG)&(ctx)->StateFlags, Flags);
|
|
}
|
|
|
|
ULONG
|
|
__inline
|
|
AFD_CLEAR_TP_FLAGS (
|
|
PIRP TpIrp,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
PAFD_TPACKETS_IRP_CTX ctx = AFD_GET_TPIC(TpIrp);
|
|
ASSERT ((ctx->StateFlags & Flags)==Flags);
|
|
return InterlockedExchangeAdd ((PLONG)&(ctx)->StateFlags, 0-Flags);
|
|
}
|
|
|
|
#else
|
|
|
|
#define AFD_SET_TP_FLAGS(_i,_f) \
|
|
InterlockedExchangeAdd ((PLONG)&AFD_GET_TPIC(_i)->StateFlags, _f)
|
|
|
|
#define AFD_CLEAR_TP_FLAGS(_i,_f) \
|
|
InterlockedExchangeAdd ((PLONG)&AFD_GET_TPIC(_i)->StateFlags, 0-(_f))
|
|
#endif
|
|
|
|
//
|
|
// Reference/dereference macros for transmit info structure.
|
|
// We keep transmit IRP pending and all the elements of
|
|
// the structure till last reference to it is gone.
|
|
// Note, that reference can be added only if structure
|
|
// already has non-0 reference count.
|
|
//
|
|
#if REFERENCE_DEBUG
|
|
VOID
|
|
AfdReferenceTPackets (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
);
|
|
|
|
LONG
|
|
AfdDereferenceTPackets (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
);
|
|
|
|
VOID
|
|
AfdUpdateTPacketsTrack (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
);
|
|
|
|
#define REFERENCE_TPACKETS(_i) { \
|
|
static LONG _arl; \
|
|
AfdReferenceTPackets(_i,AFD_GET_ARL(__FILE__"(%d)+"),__LINE__); \
|
|
}
|
|
|
|
#define DEREFERENCE_TPACKETS(_i) { \
|
|
static LONG _arl; \
|
|
if (AfdDereferenceTPackets(_i,AFD_GET_ARL(__FILE__"(%d)-"),__LINE__)==0) {\
|
|
AfdCompleteTPackets(_i); \
|
|
};\
|
|
}
|
|
|
|
#define DEREFERENCE_TPACKETS_R(_i,_r) { \
|
|
static LONG _arl; \
|
|
_r = AfdDereferenceTPackets(_i,AFD_GET_ARL(__FILE__"(%d)-"),__LINE__);\
|
|
}
|
|
|
|
#define UPDATE_TPACKETS(_i) { \
|
|
static LONG _arl; \
|
|
AfdUpdateTPacketsTrack((_i),AFD_GET_ARL(__FILE__"(%d)="),__LINE__);\
|
|
}
|
|
|
|
#define UPDATE_TPACKETS2(_i,_s,_p) { \
|
|
static LONG _arl; \
|
|
AfdUpdateTPacketsTrack((_i),AFD_GET_ARL(_s"="),_p); \
|
|
}
|
|
#else // REFERENCE_DEBUG
|
|
|
|
#define REFERENCE_TPACKETS(_i) \
|
|
InterlockedIncrement ((PLONG)&AFD_GET_TPIC(_i)->ReferenceCount)
|
|
|
|
#define DEREFERENCE_TPACKETS(_i) \
|
|
if (InterlockedDecrement((PLONG)&AFD_GET_TPIC(_i)->ReferenceCount)==0) {\
|
|
AfdCompleteTPackets(_i); \
|
|
}
|
|
|
|
#define DEREFERENCE_TPACKETS_R(_i,_r) { \
|
|
_r = InterlockedDecrement((PLONG)&AFD_GET_TPIC(_i)->ReferenceCount);\
|
|
}
|
|
|
|
#define UPDATE_TPACKETS(_i)
|
|
|
|
#define UPDATE_TPACKETS2(_i,_s,_p)
|
|
|
|
#endif // REFERENCE_DEBUG
|
|
|
|
#if DBG
|
|
//
|
|
// Doesn't seem like we have a file system that does not
|
|
// support cache. So this is here for debugging purposes.
|
|
//
|
|
ULONG AfdUseCache=TRUE;
|
|
#define AFD_USE_CACHE(file) \
|
|
(AfdUseCache&&(((file)->Flags&FO_CACHE_SUPPORTED)!=0))
|
|
|
|
#else // DBG
|
|
|
|
#define AFD_USE_CACHE(file) (((file)->Flags & FO_CACHE_SUPPORTED)!=0)
|
|
|
|
#endif // DBG
|
|
|
|
|
|
VOID
|
|
AfdTPacketsWorker (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdPerformTpDisconnect (
|
|
PIRP TpIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdBuildPacketChain (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER *Pd
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdCleanupPacketChain (
|
|
PIRP TpIrp,
|
|
BOOLEAN BelowDispatch
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTPacketsSend (
|
|
PIRP TpIrp,
|
|
USHORT SendIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTPDetachedSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
USHORT
|
|
AfdTPacketsFindSendIrp (
|
|
PIRP TpIrp
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTPacketsMdlRead (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER Pd
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsMdlRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdMdlReadComplete (
|
|
PFILE_OBJECT FileObject,
|
|
PMDL FileMdl,
|
|
PLARGE_INTEGER FileOffset
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartMdlReadComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdLRMdlReadComplete (
|
|
PAFD_BUFFER_HEADER Pd
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdLRProcessFileMdlList (
|
|
PAFD_LR_LIST_ITEM Item
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdTPacketsBufferRead (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER Pd
|
|
);
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsBufferRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdTPacketsContinueAfterRead (
|
|
PIRP TpIrp
|
|
);
|
|
|
|
VOID
|
|
AfdCompleteTPackets (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdAbortTPackets (
|
|
PIRP TpIrp,
|
|
NTSTATUS Status
|
|
);
|
|
|
|
VOID
|
|
AfdCancelTPackets (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
BOOLEAN
|
|
AfdTPacketsEnableSendAndDisconnect (
|
|
PIRP TpIrp
|
|
);
|
|
#endif // TDI_SERVICE_SEND_AND_DISCONNECT
|
|
|
|
BOOLEAN
|
|
AfdQueueTransmit (
|
|
PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
AfdStartNextQueuedTransmit(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdEnqueueTPacketsIrp (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP TpIrp
|
|
);
|
|
|
|
VOID
|
|
AfdStartTPacketsWorker (
|
|
PWORKER_THREAD_ROUTINE WorkerRoutine,
|
|
PIRP TpIrp
|
|
);
|
|
|
|
VOID
|
|
AfdTPacketsApcKernelRoutine (
|
|
IN struct _KAPC *Apc,
|
|
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN OUT PVOID *NormalContext,
|
|
IN OUT PVOID *SystemArgument1,
|
|
IN OUT PVOID *SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
AfdTPacketsApcRundownRoutine (
|
|
IN struct _KAPC *Apc
|
|
);
|
|
|
|
VOID
|
|
AfdStartNextTPacketsIrp (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP TpIrp
|
|
);
|
|
|
|
VOID
|
|
AfdSendQueuedTPSend (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP SendIrp
|
|
);
|
|
|
|
BOOLEAN
|
|
AfdGetTPacketsReference (
|
|
PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
AfdReturnTpInfo (
|
|
PAFD_TPACKETS_INFO_INTERNAL TpInfo
|
|
);
|
|
|
|
PAFD_TPACKETS_INFO_INTERNAL
|
|
AfdInitializeTpInfo (
|
|
PVOID MemoryBlock,
|
|
ULONG ElementCount,
|
|
CCHAR StackCount
|
|
);
|
|
|
|
#ifdef _WIN64
|
|
NTSTATUS
|
|
AfdTransmitPackets32 (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
OUT PAFD_TPACKETS_INFO_INTERNAL *TpInfo,
|
|
OUT BOOLEAN *FileError,
|
|
OUT ULONG *MaxPacketSize
|
|
);
|
|
#endif //_WIN64
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, AfdTransmitPackets )
|
|
#ifdef _WIN64
|
|
#pragma alloc_text( PAGE, AfdTransmitPackets32 )
|
|
#endif //_WIN64
|
|
#pragma alloc_text( PAGE, AfdTPacketsWorker )
|
|
#pragma alloc_text( PAGEAFD, AfdPerformTpDisconnect )
|
|
#pragma alloc_text( PAGE, AfdBuildPacketChain )
|
|
#pragma alloc_text( PAGEAFD, AfdCleanupPacketChain )
|
|
#pragma alloc_text( PAGEAFD, AfdTPacketsSend )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTPacketsSend )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTPDetachedSend )
|
|
#pragma alloc_text( PAGEAFD, AfdTPacketsFindSendIrp)
|
|
#pragma alloc_text( PAGE, AfdTPacketsMdlRead )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTPacketsMdlRead )
|
|
#pragma alloc_text( PAGE, AfdMdlReadComplete )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartMdlReadComplete )
|
|
#pragma alloc_text( PAGE, AfdLRMdlReadComplete )
|
|
#pragma alloc_text( PAGE, AfdLRProcessFileMdlList )
|
|
#pragma alloc_text( PAGE, AfdTPacketsBufferRead )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartTPacketsBufferRead )
|
|
#pragma alloc_text( PAGEAFD, AfdTPacketsContinueAfterRead )
|
|
#pragma alloc_text( PAGEAFD, AfdCompleteTPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdAbortTPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdCancelTPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdCompleteClosePendedTPackets )
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
#pragma alloc_text( PAGEAFD, AfdTPacketsEnableSendAndDisconnect )
|
|
#endif
|
|
#pragma alloc_text( PAGEAFD, AfdQueueTransmit )
|
|
#pragma alloc_text( PAGEAFD, AfdStartNextQueuedTransmit )
|
|
#pragma alloc_text( PAGEAFD, AfdStartTPacketsWorker )
|
|
#pragma alloc_text( PAGE, AfdTPacketsApcKernelRoutine )
|
|
#pragma alloc_text( PAGE, AfdTPacketsApcRundownRoutine )
|
|
#pragma alloc_text( PAGEAFD, AfdEnqueueTPacketsIrp )
|
|
#pragma alloc_text( PAGEAFD, AfdEnqueueTpSendIrp )
|
|
#pragma alloc_text( PAGEAFD, AfdSendQueuedTPSend )
|
|
#pragma alloc_text( PAGEAFD, AfdStartNextTPacketsIrp )
|
|
#pragma alloc_text( PAGEAFD, AfdGetTPacketsReference )
|
|
#if REFERENCE_DEBUG
|
|
#pragma alloc_text( PAGEAFD, AfdReferenceTPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdDereferenceTPackets )
|
|
#pragma alloc_text( PAGEAFD, AfdUpdateTPacketsTrack )
|
|
#endif
|
|
#pragma alloc_text( PAGE, AfdGetTpInfoFast )
|
|
#ifdef _AFD_VARIABLE_STACK_
|
|
#pragma alloc_text( PAGE, AfdGetTpInfoWithMaxStackSize )
|
|
#pragma alloc_text( PAGE, AfdComputeTpInfoSize )
|
|
#else //_AFD_VARIABLE_STACK_
|
|
#pragma alloc_text( INIT, AfdComputeTpInfoSize )
|
|
#endif //_AFD_VARIABLE_STACK_
|
|
#pragma alloc_text( PAGEAFD, AfdReturnTpInfo )
|
|
#pragma alloc_text( PAGEAFD, AfdAllocateTpInfo )
|
|
#pragma alloc_text( PAGEAFD, AfdInitializeTpInfo )
|
|
#pragma alloc_text( PAGEAFD, AfdFreeTpInfo )
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdTransmitPackets (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initial entrypoint for handling transmit packets IRPs. This routine
|
|
verifies parameters, initializes data structures to be used for
|
|
the request, and initiates the I/O.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to a transmit file IRP.
|
|
|
|
IrpSp - Our stack location for this IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the request was initiated successfully, or a
|
|
failure status code if there was an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
NTSTATUS status;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = NULL;
|
|
BOOLEAN fileError = FALSE;
|
|
ULONG maxPacketSize = 0, maxSendBytes;
|
|
PAFD_CONNECTION connection = NULL;
|
|
|
|
|
|
PAGED_CODE ();
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Special hack to let the user mode dll know that it
|
|
// should try SAN provider instead.
|
|
//
|
|
|
|
if (IS_SAN_ENDPOINT (endpoint)) {
|
|
status = STATUS_INVALID_PARAMETER_12;
|
|
goto complete;
|
|
}
|
|
|
|
|
|
//
|
|
// The endpoint must be connected and underlying transport must support
|
|
// TdiSend (not just TdiSendDatagram).
|
|
//
|
|
if ( (endpoint->Type != AfdBlockTypeVcConnecting &&
|
|
(endpoint->Type != AfdBlockTypeDatagram ||
|
|
!IS_TDI_DGRAM_CONNECTION(endpoint))) ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
status = AfdTransmitPackets32 (Irp, IrpSp, &tpInfo, &fileError, &maxPacketSize);
|
|
if (!NT_SUCCESS (status)) {
|
|
goto complete;
|
|
}
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
AFD_TPACKETS_INFO params;
|
|
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(AFD_TPACKETS_INFO) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Because we're using type 3 (neither) I/O for this IRP, the I/O
|
|
// system does no verification on the user buffer. Therefore, we
|
|
// must manually check it for validity inside a try-except block.
|
|
// We also leverage the try-except to validate and lock down the
|
|
// head and/or tail buffers specified by the caller.
|
|
//
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
PFILE_OBJECT fileObject;
|
|
HANDLE fileHandle;
|
|
ULONG lastSmallBuffer, currentLength, xLength;
|
|
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
sizeof (AFD_TPACKETS_INFO),
|
|
PROBE_ALIGNMENT (AFD_TPACKETS_INFO)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
params = *((PAFD_TPACKETS_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
|
|
|
|
//
|
|
// Validate any flags specified in the request.
|
|
//
|
|
|
|
if ( ((params.Flags &
|
|
~(AFD_TF_WRITE_BEHIND |
|
|
AFD_TF_DISCONNECT |
|
|
AFD_TF_REUSE_SOCKET |
|
|
AFD_TF_WORKER_KIND_MASK) )
|
|
!= 0 )
|
|
||
|
|
((params.Flags & AFD_TF_WORKER_KIND_MASK)
|
|
== AFD_TF_WORKER_KIND_MASK)
|
|
||
|
|
|
|
(endpoint->Type==AfdBlockTypeDatagram &&
|
|
(params.Flags & (AFD_TF_DISCONNECT |
|
|
AFD_TF_REUSE_SOCKET))
|
|
!=0) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Protect from overflow
|
|
//
|
|
if ((params.ElementArray==NULL) ||
|
|
(params.ElementCount==0) ||
|
|
(params.ElementCount>(MAXULONG/sizeof (params.ElementArray[0])))) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
//
|
|
// If transmit worker is not specified, use system default setting
|
|
//
|
|
if ((params.Flags & AFD_TF_WORKER_KIND_MASK)==AFD_TF_USE_DEFAULT_WORKER) {
|
|
params.Flags |= AfdDefaultTransmitWorker;
|
|
}
|
|
|
|
//
|
|
// Allocate tpackets info for the request
|
|
//
|
|
tpInfo = AfdGetTpInfo (endpoint, params.ElementCount);
|
|
if (tpInfo==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
tpInfo->ElementCount = 0;
|
|
|
|
tpInfo->SendPacketLength = params.SendSize;
|
|
if (tpInfo->SendPacketLength==0)
|
|
tpInfo->SendPacketLength = AfdTransmitIoLength;
|
|
//
|
|
// Probe and copy/walk the array of the elements to transmit.
|
|
//
|
|
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
ProbeForRead(
|
|
params.ElementArray,
|
|
sizeof (TRANSMIT_PACKETS_ELEMENT)*params.ElementCount,
|
|
PROBE_ALIGNMENT (TRANSMIT_PACKETS_ELEMENT)
|
|
);
|
|
}
|
|
|
|
lastSmallBuffer = 0;
|
|
currentLength = 0;
|
|
xLength = 0;
|
|
tpInfo->RemainingPkts = 0;
|
|
fileHandle = NULL;
|
|
AFD_W4_INIT fileObject = NULL; // Depends on variable above, but
|
|
// compiler does not see
|
|
// the connection.
|
|
for (; tpInfo->ElementCount<params.ElementCount; tpInfo->ElementCount++) {
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT pel;
|
|
pel = &tpInfo->ElementArray[tpInfo->ElementCount];
|
|
pel->Flags = params.ElementArray[tpInfo->ElementCount].dwElFlags;
|
|
if ( ((pel->Flags & (~(TP_MEMORY|TP_FILE|TP_EOP)))!=0) ||
|
|
((pel->Flags & (TP_MEMORY|TP_FILE))
|
|
==(TP_MEMORY|TP_FILE)) ||
|
|
((pel->Flags & (TP_MEMORY|TP_FILE))==0) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
pel->Length = params.ElementArray[tpInfo->ElementCount].cLength;
|
|
if (pel->Flags & TP_FILE) {
|
|
HANDLE hFile = params.ElementArray[tpInfo->ElementCount].hFile;
|
|
|
|
|
|
//
|
|
// Check if we already cached the file object
|
|
//
|
|
if (fileHandle==NULL || hFile!=fileHandle) {
|
|
//
|
|
// Get a referenced pointer to the file object
|
|
// for the file that we're going to transmit. This call
|
|
// will fail if the file handle specified by the caller
|
|
// is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
hFile,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
Irp->RequestorMode,
|
|
(PVOID *)&fileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
fileError = TRUE;
|
|
goto complete;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Use our 1-element file info cache.
|
|
//
|
|
ObReferenceObject (fileObject);
|
|
}
|
|
AfdRecordFileRef();
|
|
|
|
//
|
|
// Save the file object instead of handle.
|
|
//
|
|
pel->FileObject = fileObject;
|
|
|
|
pel->FileOffset = params.ElementArray[
|
|
tpInfo->ElementCount].nFileOffset;
|
|
|
|
if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) &&
|
|
(pel->FileOffset.QuadPart == 0) ) {
|
|
//
|
|
// Use current offset if file is opened syncronously
|
|
// and offset is not specified.
|
|
//
|
|
|
|
pel->FileOffset = fileObject->CurrentByteOffset;
|
|
}
|
|
|
|
if ( pel->Length == 0 ) {
|
|
//
|
|
// Length was not specified, figure out the
|
|
// size of the entire file
|
|
//
|
|
|
|
FILE_STANDARD_INFORMATION fileInfo;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
status = ZwQueryInformationFile(
|
|
hFile,
|
|
&ioStatusBlock,
|
|
&fileInfo,
|
|
sizeof(fileInfo),
|
|
FileStandardInformation
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
fileError = TRUE;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Make sure that offset is within the file
|
|
//
|
|
if (pel->FileOffset.QuadPart < 0
|
|
||
|
|
pel->FileOffset.QuadPart > fileInfo.EndOfFile.QuadPart
|
|
||
|
|
(fileInfo.EndOfFile.QuadPart -
|
|
pel->FileOffset.QuadPart > MAXLONG)) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
status = STATUS_INVALID_PARAMETER;
|
|
fileError = TRUE;
|
|
goto complete;
|
|
|
|
}
|
|
pel->Length = (ULONG)(fileInfo.EndOfFile.QuadPart -
|
|
pel->FileOffset.QuadPart);
|
|
}
|
|
else if (pel->FileOffset.QuadPart<0) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
status = STATUS_INVALID_PARAMETER;
|
|
fileError = TRUE;
|
|
goto complete;
|
|
|
|
}
|
|
//
|
|
// Update our 1-element file information cache
|
|
//
|
|
fileHandle = hFile;
|
|
|
|
}
|
|
else {
|
|
ASSERT (pel->Flags & TP_MEMORY);
|
|
//
|
|
// For memory object just save the buffer pointer
|
|
// (length is saved above), we'll probe and lock/copy
|
|
// the data as we send it.
|
|
//
|
|
pel->Buffer = params.ElementArray[
|
|
tpInfo->ElementCount].pBuffer;
|
|
if (pel->Length<=AfdTPacketsCopyThreshold) {
|
|
if (lastSmallBuffer!=0 &&
|
|
(lastSmallBuffer+=pel->Length) <= AfdTPacketsCopyThreshold &&
|
|
(currentLength+lastSmallBuffer) <= tpInfo->SendPacketLength) {
|
|
(pel-1)->Flags |= TP_COMBINE;
|
|
}
|
|
else
|
|
lastSmallBuffer = pel->Length;
|
|
if (!(pel->Flags & TP_EOP))
|
|
goto NoBufferReset;
|
|
}
|
|
|
|
}
|
|
lastSmallBuffer = 0;
|
|
|
|
NoBufferReset:
|
|
if (pel->Flags & TP_EOP) {
|
|
currentLength = 0;
|
|
}
|
|
else {
|
|
currentLength = (currentLength+pel->Length)%tpInfo->SendPacketLength;
|
|
}
|
|
|
|
//
|
|
// Compute the total number of packets that we will send.
|
|
// This is necessary so that once we are close to the end
|
|
// we can buffer the remaining data and stop processing
|
|
// early.
|
|
//
|
|
if (tpInfo->RemainingPkts!=MAXULONG) {
|
|
ULONG n;
|
|
ULONGLONG x;
|
|
//
|
|
// Add length of the element to data left from the
|
|
// previous one.
|
|
//
|
|
x = xLength + pel->Length;
|
|
|
|
//
|
|
// Compute total number of packets pased on max packet
|
|
// length.
|
|
//
|
|
n = tpInfo->RemainingPkts + (ULONG)(xLength/tpInfo->SendPacketLength);
|
|
|
|
//
|
|
// Compute the length of the last incomplete packet to
|
|
// be combined with the next element.
|
|
//
|
|
xLength = (ULONG)(x%tpInfo->SendPacketLength);
|
|
|
|
//
|
|
// Compute the max size of the packet
|
|
//
|
|
if (x>tpInfo->SendPacketLength)
|
|
maxPacketSize = tpInfo->SendPacketLength; // This is absolute max.
|
|
else if (maxPacketSize<xLength)
|
|
maxPacketSize = xLength;
|
|
|
|
if (n>=tpInfo->RemainingPkts && n<MAXULONG) {
|
|
tpInfo->RemainingPkts = n;
|
|
if (pel->Flags & TP_EOP) {
|
|
if (xLength!=0 || pel->Length==0) {
|
|
tpInfo->RemainingPkts += 1;
|
|
xLength = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
tpInfo->RemainingPkts = MAXULONG;
|
|
}
|
|
}
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
|
|
goto complete;
|
|
}
|
|
//
|
|
// Initialize flags.
|
|
//
|
|
AFD_GET_TPIC(Irp)->Flags = params.Flags;
|
|
|
|
}
|
|
|
|
|
|
if (endpoint->Type==AfdBlockTypeVcConnecting) {
|
|
|
|
//
|
|
// Setting AFD_TF_REUSE_SOCKET implies that a disconnect is desired.
|
|
// Also, setting this flag means that no more I/O is legal on the
|
|
// endpoint until the transmit request has been completed, so
|
|
// set up the endpoint's state so that I/O fails.
|
|
//
|
|
|
|
if ( (AFD_GET_TPIC(Irp)->Flags & (AFD_TF_REUSE_SOCKET|AFD_TF_DISCONNECT)) != 0 ) {
|
|
//
|
|
// Make sure we only execute one transmitfile transmitpackets request
|
|
// at a time on a given endpoint with disconnect option.
|
|
//
|
|
if (!AFD_START_STATE_CHANGE (endpoint, endpoint->State)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Revalidate the state of the endpoint/connection so that
|
|
// state change is valid (e.g. not already closing).
|
|
//
|
|
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
if ( (AFD_GET_TPIC(Irp)->Flags & AFD_TF_REUSE_SOCKET) != 0 ) {
|
|
AFD_GET_TPIC(Irp)->Flags |= AFD_TF_DISCONNECT;
|
|
endpoint->State = AfdEndpointStateTransmitClosing;
|
|
}
|
|
//
|
|
// Make sure we won't loose this connection.
|
|
// until we enqueue the IRP
|
|
//
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
REFERENCE_CONNECTION (connection);
|
|
}
|
|
else if (!AFD_PREVENT_STATE_CHANGE (endpoint)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
else if (endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
else {
|
|
//
|
|
// Make sure we won't loose this connection.
|
|
// until we enqueue the IRP
|
|
//
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
REFERENCE_CONNECTION (connection);
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
}
|
|
|
|
//
|
|
// Connection endpoint, get connection file object and device
|
|
//
|
|
tpInfo->TdiFileObject = connection->FileObject;
|
|
tpInfo->TdiDeviceObject = connection->DeviceObject;
|
|
maxSendBytes = connection->MaxBufferredSendBytes;
|
|
UPDATE_TPACKETS2 (Irp, "Connection object handle: 0x%lX",
|
|
HandleToUlong (endpoint->Common.VcConnecting.Connection->Handle));
|
|
|
|
}
|
|
else if (!AFD_PREVENT_STATE_CHANGE (endpoint)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
else if (endpoint->Type != AfdBlockTypeDatagram ||
|
|
endpoint->State != AfdEndpointStateConnected ||
|
|
!IS_TDI_DGRAM_CONNECTION(endpoint)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
goto complete;
|
|
}
|
|
else {
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
//
|
|
// Datagram endpoint, get address file object and device
|
|
// Note that there is no danger in address or file object
|
|
// disappearing since datagram endpoint cannot be re-used.
|
|
//
|
|
tpInfo->TdiFileObject = endpoint->AddressFileObject;
|
|
tpInfo->TdiDeviceObject = endpoint->AddressDeviceObject;
|
|
maxSendBytes = endpoint->Common.Datagram.MaxBufferredSendBytes;
|
|
UPDATE_TPACKETS2 (Irp, "Address object handle: 0x%lX", HandleToUlong (endpoint->AddressHandle));
|
|
}
|
|
|
|
|
|
//
|
|
// Compute the total number of IRPS to use based
|
|
// on SO_SNDBUF setting and maximum packet size
|
|
// (we do not want to buffer more than SO_SNDBUF).
|
|
//
|
|
{
|
|
ULONG irpCount;
|
|
if (maxPacketSize==0) {
|
|
maxPacketSize = tpInfo->SendPacketLength;
|
|
}
|
|
|
|
irpCount = maxSendBytes/maxPacketSize;
|
|
if (irpCount>AFD_TP_MIN_SEND_IRPS) {
|
|
if (irpCount>AFD_TP_MAX_SEND_IRPS) {
|
|
tpInfo->NumSendIrps = AFD_TP_MAX_SEND_IRPS;
|
|
}
|
|
else {
|
|
tpInfo->NumSendIrps = (USHORT)irpCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save tpacket info in the IRP
|
|
//
|
|
Irp->AssociatedIrp.SystemBuffer = tpInfo;
|
|
|
|
//
|
|
// Clear the Flink in the IRP to indicate this IRP is not queued.
|
|
// Blink is set to indicate that IRP was not counted towards
|
|
// active maximum (so if it is completed, we do not start the next one).
|
|
//
|
|
|
|
Irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
Irp->Tail.Overlay.ListEntry.Blink = (PVOID)1;
|
|
|
|
//
|
|
// Initialize the IRP result fields
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// We are going to pend this IRP
|
|
//
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// Initialize queuing and state information.
|
|
//
|
|
AFD_GET_TPIC(Irp)->Next = NULL;
|
|
AFD_GET_TPIC(Irp)->ReferenceCount = 1;
|
|
AFD_GET_TPIC(Irp)->StateFlags = AFD_TP_WORKER_SCHEDULED;
|
|
|
|
if ((InterlockedCompareExchangePointer ((PVOID *)&endpoint->Irp,
|
|
Irp,
|
|
NULL)==NULL) ||
|
|
!AfdEnqueueTPacketsIrp (endpoint, Irp)) {
|
|
|
|
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelTPackets );
|
|
|
|
//
|
|
// Check to see if this Irp has been cancelled.
|
|
//
|
|
|
|
if ( !endpoint->EndpointCleanedUp && !Irp->Cancel ) {
|
|
//
|
|
// Determine if we can really start this file transmit now. If we've
|
|
// exceeded the configured maximum number of active TransmitFile/Packets
|
|
// requests, then append this IRP to the TransmitFile/Packets queue
|
|
// and set a flag in the transmit info structure to indicate that
|
|
// this IRP is queued.
|
|
//
|
|
if( AfdMaxActiveTransmitFileCount == 0 ||
|
|
!AfdQueueTransmit (Irp)) {
|
|
|
|
UPDATE_ENDPOINT (endpoint);
|
|
//
|
|
// Start I/O
|
|
//
|
|
AfdTPacketsWorker (Irp);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Abort the request
|
|
// Note that neither cancel nor endpoint cleanup can complete
|
|
// the IRP since we hold the reference to the tpInfo structure.
|
|
//
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
|
|
//
|
|
// Remove the initial reference and force completion.
|
|
//
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
}
|
|
}
|
|
|
|
if (connection!=NULL) {
|
|
DEREFERENCE_CONNECTION (connection);
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
|
complete:
|
|
|
|
//
|
|
// Tell the caller that we encountered an error
|
|
// when accessing file not socket.
|
|
//
|
|
if (fileError && IrpSp->Parameters.DeviceIoControl.OutputBufferLength>=sizeof (BOOLEAN)) {
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
try {
|
|
ProbeAndWriteBoolean ((BOOLEAN *)Irp->UserBuffer, TRUE);
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
}
|
|
} else {
|
|
*((BOOLEAN *)Irp->UserBuffer) = TRUE;
|
|
}
|
|
}
|
|
|
|
ASSERT ( endpoint->Irp != Irp );
|
|
|
|
if (tpInfo!=NULL) {
|
|
//
|
|
// AfdReturnTpInfo will dereference all file objects we
|
|
// managed to reference.
|
|
//
|
|
AfdReturnTpInfo (tpInfo);
|
|
}
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTransmitPackets: Failing Irp-%p,endpoint-%p,status-%lx\n",
|
|
Irp,endpoint,status));
|
|
}
|
|
//
|
|
// Complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
NTSTATUS
|
|
AfdTransmitPackets32 (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp,
|
|
OUT PAFD_TPACKETS_INFO_INTERNAL *TpInfo,
|
|
OUT BOOLEAN *FileError,
|
|
OUT ULONG *MaxPacketSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
32-bit thunk.
|
|
Initial entrypoint for handling transmit packets IRPs. This routine
|
|
verifies parameters, initializes data structures to be used for
|
|
the request, and initiates the I/O.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to a transmit file IRP.
|
|
|
|
IrpSp - Our stack location for this IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the request was initiated successfully, or a
|
|
failure status code if there was an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = NULL;
|
|
AFD_TPACKETS_INFO32 params;
|
|
ULONG maxPacketSize;
|
|
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(AFD_TPACKETS_INFO32) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Because we're using type 3 (neither) I/O for this IRP, the I/O
|
|
// system does no verification on the user buffer. Therefore, we
|
|
// must manually check it for validity inside a try-except block.
|
|
// We also leverage the try-except to validate and lock down the
|
|
// head and/or tail buffers specified by the caller.
|
|
//
|
|
|
|
try {
|
|
PFILE_OBJECT fileObject;
|
|
HANDLE fileHandle;
|
|
ULONG lastSmallBuffer, currentLength, xLength;
|
|
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
sizeof (AFD_TPACKETS_INFO32),
|
|
PROBE_ALIGNMENT32 (AFD_TPACKETS_INFO32)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
params = *((PAFD_TPACKETS_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
|
|
|
|
//
|
|
// Validate any flags specified in the request.
|
|
//
|
|
|
|
if ( ((params.Flags &
|
|
~(AFD_TF_WRITE_BEHIND |
|
|
AFD_TF_DISCONNECT |
|
|
AFD_TF_REUSE_SOCKET |
|
|
AFD_TF_WORKER_KIND_MASK) )
|
|
!= 0 )
|
|
||
|
|
((params.Flags & AFD_TF_WORKER_KIND_MASK)
|
|
== AFD_TF_WORKER_KIND_MASK)
|
|
||
|
|
|
|
(endpoint->Type==AfdBlockTypeDatagram &&
|
|
(params.Flags & (AFD_TF_DISCONNECT |
|
|
AFD_TF_REUSE_SOCKET))
|
|
!=0) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Protect from overflow
|
|
//
|
|
if ((UlongToPtr(params.ElementArray)==NULL) ||
|
|
(params.ElementCount==0) ||
|
|
(params.ElementCount>(MAXULONG/sizeof (TRANSMIT_PACKETS_ELEMENT32)))) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// If transmit worker is not specified, use system default setting
|
|
//
|
|
if ((params.Flags & AFD_TF_WORKER_KIND_MASK)==AFD_TF_USE_DEFAULT_WORKER) {
|
|
params.Flags |= AfdDefaultTransmitWorker;
|
|
}
|
|
|
|
//
|
|
// Allocate tpackets info for the request
|
|
//
|
|
tpInfo = AfdGetTpInfo (endpoint, params.ElementCount);
|
|
if (tpInfo==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
tpInfo->ElementCount = 0;
|
|
|
|
tpInfo->SendPacketLength = params.SendSize;
|
|
if (tpInfo->SendPacketLength==0)
|
|
tpInfo->SendPacketLength = AfdTransmitIoLength;
|
|
//
|
|
// Probe and copy/walk the array of the elements to transmit.
|
|
//
|
|
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
ProbeForRead(
|
|
params.ElementArray,
|
|
sizeof (TRANSMIT_PACKETS_ELEMENT32)*params.ElementCount,
|
|
PROBE_ALIGNMENT32 (TRANSMIT_PACKETS_ELEMENT32)
|
|
);
|
|
}
|
|
|
|
lastSmallBuffer = 0;
|
|
currentLength = 0;
|
|
xLength = 0;
|
|
tpInfo->RemainingPkts = 0;
|
|
maxPacketSize = 0;
|
|
fileHandle = NULL;
|
|
AFD_W4_INIT fileObject = NULL; // Depends on variable above, but compiler
|
|
// does not see the connection.
|
|
for (; tpInfo->ElementCount<params.ElementCount; tpInfo->ElementCount++) {
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT pel;
|
|
pel = &tpInfo->ElementArray[tpInfo->ElementCount];
|
|
pel->Flags = ((TRANSMIT_PACKETS_ELEMENT32*)UlongToPtr(params.ElementArray))[tpInfo->ElementCount].dwElFlags;
|
|
if ( ((pel->Flags & (~(TP_MEMORY|TP_FILE|TP_EOP)))!=0) ||
|
|
((pel->Flags & (TP_MEMORY|TP_FILE))
|
|
==(TP_MEMORY|TP_FILE)) ||
|
|
((pel->Flags & (TP_MEMORY|TP_FILE))==0) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
pel->Length = ((TRANSMIT_PACKETS_ELEMENT*)UlongToPtr(params.ElementArray))[tpInfo->ElementCount].cLength;
|
|
if (pel->Flags & TP_FILE) {
|
|
HANDLE hFile = ((TRANSMIT_PACKETS_ELEMENT32*)UlongToPtr(params.ElementArray))[tpInfo->ElementCount].hFile;
|
|
|
|
|
|
//
|
|
// Check if we already cached the file object
|
|
//
|
|
if (fileHandle==NULL || hFile!=fileHandle) {
|
|
//
|
|
// Get a referenced pointer to the file object
|
|
// for the file that we're going to transmit. This call
|
|
// will fail if the file handle specified by the caller
|
|
// is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
hFile,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
Irp->RequestorMode,
|
|
(PVOID *)&fileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
*FileError = TRUE;
|
|
goto complete;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Use our 1-element file info cache.
|
|
//
|
|
ObReferenceObject (fileObject);
|
|
}
|
|
AfdRecordFileRef();
|
|
|
|
//
|
|
// Save the file object instead of handle.
|
|
//
|
|
pel->FileObject = fileObject;
|
|
|
|
pel->FileOffset = ((TRANSMIT_PACKETS_ELEMENT32*)UlongToPtr(params.ElementArray))[
|
|
tpInfo->ElementCount].nFileOffset;
|
|
|
|
if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) &&
|
|
(pel->FileOffset.QuadPart == 0) ) {
|
|
//
|
|
// Use current offset if file is opened syncronously
|
|
// and offset is not specified.
|
|
//
|
|
|
|
pel->FileOffset = fileObject->CurrentByteOffset;
|
|
}
|
|
|
|
if ( pel->Length == 0 ) {
|
|
//
|
|
// Length was not specified, figure out the
|
|
// size of the entire file
|
|
//
|
|
|
|
FILE_STANDARD_INFORMATION fileInfo;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
status = ZwQueryInformationFile(
|
|
hFile,
|
|
&ioStatusBlock,
|
|
&fileInfo,
|
|
sizeof(fileInfo),
|
|
FileStandardInformation
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
*FileError = TRUE;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Make sure that offset is within the file
|
|
//
|
|
if (pel->FileOffset.QuadPart < 0
|
|
||
|
|
pel->FileOffset.QuadPart > fileInfo.EndOfFile.QuadPart
|
|
||
|
|
(fileInfo.EndOfFile.QuadPart -
|
|
pel->FileOffset.QuadPart > MAXLONG)) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
status = STATUS_INVALID_PARAMETER;
|
|
*FileError = TRUE;
|
|
goto complete;
|
|
|
|
}
|
|
pel->Length = (ULONG)(fileInfo.EndOfFile.QuadPart -
|
|
pel->FileOffset.QuadPart);
|
|
}
|
|
else if (pel->FileOffset.QuadPart<0) {
|
|
//
|
|
// Bump element count so that file object
|
|
// is dereferenced in cleanup
|
|
//
|
|
tpInfo->ElementCount += 1;
|
|
status = STATUS_INVALID_PARAMETER;
|
|
*FileError = TRUE;
|
|
goto complete;
|
|
}
|
|
//
|
|
// Update our 1-element file information cache
|
|
//
|
|
fileHandle = hFile;
|
|
|
|
}
|
|
else {
|
|
ASSERT (pel->Flags & TP_MEMORY);
|
|
//
|
|
// For memory object just save the buffer pointer
|
|
// (length is saved above), we'll probe and lock/copy
|
|
// the data as we send it.
|
|
//
|
|
pel->Buffer = UlongToPtr(((TRANSMIT_PACKETS_ELEMENT32*)UlongToPtr(params.ElementArray))[
|
|
tpInfo->ElementCount].pBuffer);
|
|
|
|
if (pel->Length<=AfdTPacketsCopyThreshold) {
|
|
if (lastSmallBuffer!=0 &&
|
|
(lastSmallBuffer+=pel->Length) <= AfdTPacketsCopyThreshold &&
|
|
(currentLength+lastSmallBuffer) <= tpInfo->SendPacketLength) {
|
|
(pel-1)->Flags |= TP_COMBINE;
|
|
}
|
|
else
|
|
lastSmallBuffer = pel->Length;
|
|
if (!(pel->Flags & TP_EOP))
|
|
goto NoBufferReset;
|
|
}
|
|
|
|
}
|
|
lastSmallBuffer = 0;
|
|
|
|
NoBufferReset:
|
|
if (pel->Flags & TP_EOP) {
|
|
currentLength = 0;
|
|
}
|
|
else {
|
|
currentLength = (currentLength+pel->Length)%tpInfo->SendPacketLength;
|
|
}
|
|
|
|
//
|
|
// Compute the total number of packets that we will send.
|
|
// This is necessary so that once we are close to the end
|
|
// we can buffer the remaining data and stop processing
|
|
// early.
|
|
//
|
|
if (tpInfo->RemainingPkts!=MAXULONG) {
|
|
ULONG n;
|
|
ULONGLONG x;
|
|
//
|
|
// Add length of the element to data left from the
|
|
// previous one.
|
|
//
|
|
x = xLength + pel->Length;
|
|
|
|
//
|
|
// Compute total number of packets pased on max packet
|
|
// length.
|
|
//
|
|
n = tpInfo->RemainingPkts + (ULONG)(xLength/tpInfo->SendPacketLength);
|
|
|
|
//
|
|
// Compute the length of the last incomplete packet to
|
|
// be combined with the next element.
|
|
//
|
|
xLength = (ULONG)(x%tpInfo->SendPacketLength);
|
|
|
|
//
|
|
// Compute the max size of the packet
|
|
//
|
|
if (x>tpInfo->SendPacketLength)
|
|
maxPacketSize = tpInfo->SendPacketLength; // This is absolute max.
|
|
else if (maxPacketSize<xLength)
|
|
maxPacketSize = xLength;
|
|
|
|
if (n>=tpInfo->RemainingPkts && n<MAXULONG) {
|
|
tpInfo->RemainingPkts = n;
|
|
if (pel->Flags & TP_EOP) {
|
|
if (xLength!=0 || pel->Length==0) {
|
|
tpInfo->RemainingPkts += 1;
|
|
xLength = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
tpInfo->RemainingPkts = MAXULONG;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Initialize flags and return max packet size.
|
|
//
|
|
AFD_GET_TPIC(Irp)->Flags = params.Flags;
|
|
*MaxPacketSize = maxPacketSize;
|
|
|
|
complete:
|
|
|
|
*TpInfo = tpInfo;
|
|
return status;
|
|
}
|
|
|
|
#endif //_WIN64
|
|
|
|
|
|
VOID
|
|
AfdTPacketsWorker (
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmit packet engine
|
|
Scheduled as system work item or kernel APC
|
|
Arguments:
|
|
|
|
Context - pointer to TransmitPackets info for the request
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP TpIrp = Context;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
NTSTATUS status;
|
|
LONG iteration = 0;
|
|
|
|
PAGED_CODE ();
|
|
|
|
#if AFD_PERF_DBG
|
|
tpInfo->WorkersExecuted += 1;
|
|
#endif
|
|
UPDATE_TPACKETS2 (TpIrp, "Enter TPWorker, next element: 0x%lX", tpInfo->NextElement);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsWorker:"
|
|
" Entering for endpoint-%p,tp_info-%p,elem-%d\n",
|
|
IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext,
|
|
tpInfo,tpInfo->NextElement));
|
|
}
|
|
|
|
//
|
|
// Continue while we have more elements to transmit or something to free
|
|
//
|
|
do {
|
|
PAFD_BUFFER_HEADER pd;
|
|
|
|
//
|
|
// Check if we need to release packet chain that was already sent.
|
|
//
|
|
if ((tpInfo->HeadMdl!=NULL) && (tpInfo->TailMdl==&tpInfo->HeadMdl)) {
|
|
AfdCleanupPacketChain (TpIrp, TRUE);
|
|
}
|
|
|
|
//
|
|
// Check if we are done.
|
|
//
|
|
if (tpInfo->NextElement>=tpInfo->ElementCount) {
|
|
//
|
|
// Handle special case of using TransmitFile to just disconnect
|
|
// (and possibly reuse) the socket.
|
|
//
|
|
if (tpInfo->ElementCount==0) {
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(TpIrp);
|
|
PAFD_ENDPOINT endpoint = irpSp->FileObject->FsContext;
|
|
if (AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_DISCONNECT) {
|
|
ASSERT (endpoint->Type==AfdBlockTypeVcConnecting);
|
|
ASSERT (endpoint->Common.VcConnecting.Connection!=NULL);
|
|
|
|
AfdPerformTpDisconnect (TpIrp);
|
|
status = STATUS_PENDING;
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Well, no disconnect and nothing to transmit.
|
|
// Why were called at all? We'll have to handle this anyway.
|
|
//
|
|
AFD_SET_TP_FLAGS (TpIrp, AFD_TP_SENDS_POSTED);
|
|
if (AFD_GET_TPIC(TpIrp)->Next!=NULL) {
|
|
AfdStartNextTPacketsIrp (endpoint, TpIrp);
|
|
}
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_PENDING;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Start building new chain
|
|
//
|
|
|
|
status = AfdBuildPacketChain (TpIrp, &pd);
|
|
|
|
if (status==STATUS_SUCCESS) {
|
|
USHORT sendIrp;
|
|
//
|
|
// New chain is ready, find and IRP to send it
|
|
//
|
|
sendIrp = AfdTPacketsFindSendIrp (TpIrp);
|
|
|
|
if (sendIrp!=tpInfo->NumSendIrps) {
|
|
//
|
|
// Found send IRP, perform send and continue.
|
|
//
|
|
status = AfdTPacketsSend (TpIrp, sendIrp);
|
|
}
|
|
else {
|
|
//
|
|
// Exit worker waiting for sends to complete.
|
|
//
|
|
status = STATUS_PENDING;
|
|
}
|
|
}
|
|
else if (status==STATUS_PENDING) {
|
|
//
|
|
// Need to perform a read.
|
|
// If read complete in-line, success is returned,
|
|
// otherwise, we'll get STATUS_PENDING or error
|
|
//
|
|
if (AFD_USE_CACHE (pd->FileObject)) {
|
|
status = AfdTPacketsMdlRead (TpIrp, pd);
|
|
}
|
|
else {
|
|
status = AfdTPacketsBufferRead (TpIrp, pd);
|
|
}
|
|
}
|
|
//
|
|
// Continue while everything completes in-line with success
|
|
// Limit number of iterations if we are at APC level.
|
|
//
|
|
}
|
|
while (status==STATUS_SUCCESS && iteration++<tpInfo->NumSendIrps);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
if (status==STATUS_SUCCESS) {
|
|
//
|
|
// Exceeded number of iterations.
|
|
// Reschedule the APC. Transfer the reference to the
|
|
// worker.
|
|
//
|
|
ASSERT (iteration==tpInfo->NumSendIrps+1);
|
|
ASSERT (AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED);
|
|
UPDATE_TPACKETS2 (TpIrp, "Rescheduling tp worker, NextElement: 0x%lX",
|
|
tpInfo->NextElement);
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, TpIrp);
|
|
return;
|
|
}
|
|
else {
|
|
ASSERT (status==STATUS_PENDING);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Something failed, abort.
|
|
//
|
|
AfdAbortTPackets (TpIrp, status);
|
|
}
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsWorker:"
|
|
" Exiting for endpoint-%p,tp_info-%p,elem-%d\n",
|
|
IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext,
|
|
tpInfo,tpInfo->NextElement));
|
|
}
|
|
//
|
|
// Remove the reference added when we scheduled the worker.
|
|
//
|
|
DEREFERENCE_TPACKETS (TpIrp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdBuildPacketChain (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER *Pd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds MDL chain for a packet using packet descriptors.
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packets IRP
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - packet is fully built
|
|
STATUS_PENDING - file read is required
|
|
other - failure.
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
BOOLEAN attached = FALSE;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
PAFD_ENDPOINT endpoint = IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext;
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT combinePel = NULL;
|
|
ULONG combineLen = 0;
|
|
|
|
//
|
|
// Either we have something built or both MDL and PD are empty
|
|
//
|
|
ASSERT (tpInfo->PdLength>0 ||
|
|
((tpInfo->HeadMdl==NULL || tpInfo->HeadMdl->ByteCount==0)
|
|
&& (tpInfo->HeadPd==NULL || tpInfo->HeadPd->DataLength==0)) );
|
|
|
|
//
|
|
// Continue while we haven't got a complet packet and
|
|
// have elements to process
|
|
//
|
|
|
|
while (status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT pel;
|
|
ULONG length;
|
|
PMDL mdl;
|
|
|
|
//
|
|
// Get next element to process
|
|
//
|
|
pel = &tpInfo->ElementArray[tpInfo->NextElement];
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain: tpInfo-%p, pel:%p\n",
|
|
tpInfo, pel));
|
|
}
|
|
//
|
|
// Snag the element length
|
|
//
|
|
|
|
length = pel->Length;
|
|
if (length+tpInfo->PdLength>tpInfo->SendPacketLength) {
|
|
//
|
|
// We hit packet length limit, take what we can
|
|
//
|
|
length = tpInfo->SendPacketLength-tpInfo->PdLength;
|
|
//
|
|
// Indicate that we are done
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tpInfo-%p, exceeded send length(%ld)\n",
|
|
tpInfo, tpInfo->SendPacketLength));
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We are finished with the current element. We will consume it
|
|
// (or fail), go to the next one
|
|
//
|
|
tpInfo->NextElement += 1;
|
|
|
|
//
|
|
// Check for a complete packet or manual packetization flag set
|
|
// by the application or just end of element array
|
|
//
|
|
if ((length+tpInfo->PdLength==tpInfo->SendPacketLength) ||
|
|
(pel->Flags & TP_EOP) ||
|
|
(tpInfo->NextElement>=tpInfo->ElementCount)) {
|
|
status = STATUS_SUCCESS;
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tpInfo-%p, full length, EOP, or last %ld\n",
|
|
tpInfo, tpInfo->NextElement));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust the remaining lenght of data in the current element
|
|
// and the length of the packet that we are building.
|
|
//
|
|
pel->Length -= length;
|
|
tpInfo->PdLength += length;
|
|
|
|
if (length == 0) {
|
|
|
|
//
|
|
// Only add 0-length MDL if nothing else is in the chain.
|
|
//
|
|
|
|
if (tpInfo->TailMdl == &tpInfo->HeadMdl) {
|
|
|
|
tpInfo->PdNeedsPps = TRUE; // Don't have a buffer to get an IRP from.
|
|
mdl = IoAllocateMdl (tpInfo, 1, FALSE, FALSE, NULL);
|
|
if (mdl==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
MmBuildMdlForNonPagedPool( mdl );
|
|
mdl->ByteCount = 0;
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tpInfo-%p, 0-length MDL %p\n",
|
|
tpInfo,mdl));
|
|
}
|
|
|
|
//
|
|
// Insert MDL into the MDL chain
|
|
//
|
|
|
|
*(tpInfo->TailMdl) = mdl;
|
|
tpInfo->TailMdl = &(mdl->Next);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the chain is not empty...
|
|
//
|
|
|
|
if (tpInfo->TailMdl != &tpInfo->HeadMdl) {
|
|
|
|
//
|
|
// Check to see if we should remove any 0-length MDL.
|
|
// If one exists, it is guaranteed to be the last and only one
|
|
// in the chain.
|
|
//
|
|
|
|
mdl = (PMDL)CONTAINING_RECORD(tpInfo->TailMdl, MDL, Next);
|
|
|
|
if (mdl->ByteCount == 0) {
|
|
|
|
if (tpInfo->HeadMdl == mdl) {
|
|
|
|
IoFreeMdl(mdl);
|
|
mdl = NULL;
|
|
|
|
tpInfo->HeadMdl = NULL;
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
|
|
} else {
|
|
|
|
PMDL tempMdl = tpInfo->HeadMdl;
|
|
|
|
while (tempMdl->Next != NULL) {
|
|
|
|
if (tempMdl->Next == mdl) {
|
|
|
|
IoFreeMdl(mdl);
|
|
mdl = NULL;
|
|
|
|
tempMdl->Next = NULL;
|
|
tpInfo->TailMdl = &(tempMdl->Next);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tempMdl = tempMdl->Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(mdl == NULL);
|
|
|
|
}
|
|
|
|
} // if (tpInfo->TailMdl != &tpInfo->HeadMdl)
|
|
|
|
if (pel->Flags & TP_MEMORY) {
|
|
|
|
//
|
|
// Memory block processing
|
|
//
|
|
if (pel->Flags & TP_MDL) {
|
|
tpInfo->PdNeedsPps = TRUE; // Need to make sure that process
|
|
// memory is there until send completes.
|
|
//
|
|
// This a pre-built MDL (TransmitFile header or trailer buffer)
|
|
//
|
|
if (pel->Mdl->ByteCount==length) {
|
|
mdl = pel->Mdl;
|
|
pel->Mdl = NULL;
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tpInfo-%p, pre-built mdl-%p(%lx)\n",
|
|
tpInfo, mdl, mdl->ByteCount));
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We can't send the whole thing at once since it is
|
|
// bigger than the packet lenght, build partial MDL
|
|
// for this - it is very unlikely scenario for header
|
|
// and/or trailer.
|
|
//
|
|
mdl = IoAllocateMdl (pel->Buffer,
|
|
length,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (mdl==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
IoBuildPartialMdl(
|
|
pel->Mdl,
|
|
mdl,
|
|
pel->Buffer,
|
|
length
|
|
);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tpInfo-%p, partial mdl %p(%lx)\n",
|
|
tpInfo,mdl,mdl->ByteCount));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we are not in the context of the process that
|
|
// initiated the request, we will need to attach
|
|
// to it to be able to access the memory.
|
|
//
|
|
if (IoGetCurrentProcess ()!=IoGetRequestorProcess (TpIrp)) {
|
|
ASSERT (!attached);
|
|
ASSERT (!KeIsAttachedProcess ());
|
|
ASSERT (AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_USE_SYSTEM_THREAD);
|
|
|
|
KeAttachProcess (
|
|
PsGetKernelProcess(
|
|
IoGetRequestorProcess (TpIrp)));
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,attached to %p\n",
|
|
tpInfo,PsGetKernelProcess(
|
|
IoGetRequestorProcess (TpIrp))));
|
|
}
|
|
//
|
|
// Set the flag so that we know to detach at exit
|
|
//
|
|
attached = TRUE;
|
|
}
|
|
|
|
if (length>AfdTPacketsCopyThreshold) {
|
|
tpInfo->PdNeedsPps = TRUE; // Need to make sure that process
|
|
// memory is there until send completes.
|
|
//
|
|
// Memory block is larger than our large (page)
|
|
// pre-allocated buffer.
|
|
// It is better to probe and lock it
|
|
// First allocate the MDL
|
|
//
|
|
mdl = IoAllocateMdl (pel->Buffer,
|
|
length,
|
|
FALSE,
|
|
TRUE,
|
|
NULL);
|
|
if (mdl==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,big mdl-%p(%p,%lx)\n",
|
|
tpInfo,mdl,pel->Buffer,length));
|
|
}
|
|
|
|
|
|
//
|
|
// Probe and lock app's memory
|
|
//
|
|
try {
|
|
MmProbeAndLockPages (mdl,
|
|
TpIrp->RequestorMode,
|
|
IoReadAccess
|
|
);
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
break;
|
|
}
|
|
}
|
|
else if (pel->Flags & TP_COMBINE) {
|
|
//
|
|
// This memory can be combined with the
|
|
// next piece in one buffer.
|
|
//
|
|
if (combinePel==NULL) {
|
|
combinePel = pel;
|
|
combineLen = length;
|
|
}
|
|
else {
|
|
combineLen += length;
|
|
ASSERT (combineLen<=AfdTPacketsCopyThreshold);
|
|
}
|
|
ASSERT (pel->Length==0);
|
|
pel->Length = length;
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,small buffer (%x) to be combined with next\n",
|
|
tpInfo,length));
|
|
}
|
|
continue;
|
|
}
|
|
else {
|
|
//
|
|
// 'Small' memory block, better to copy
|
|
// into pre-allocated (lookaside list) buffer.
|
|
//
|
|
PAFD_BUFFER afdBuffer = NULL;
|
|
PUCHAR buf;
|
|
ULONG bufferLen = length + (combinePel ? combineLen : 0);
|
|
|
|
try {
|
|
afdBuffer = AfdGetBufferRaiseOnFailure (
|
|
endpoint,
|
|
bufferLen,
|
|
0,
|
|
endpoint->OwningProcess);
|
|
buf = afdBuffer->Buffer;
|
|
if (combinePel!=NULL) {
|
|
//
|
|
// See if wee need to combine previous elements
|
|
//
|
|
ASSERT (combineLen+length<=AfdTPacketsCopyThreshold);
|
|
ASSERT (combineLen>0);
|
|
while (combinePel!=pel) {
|
|
if ( TpIrp->RequestorMode != KernelMode ) {
|
|
//
|
|
// Probe before copying
|
|
//
|
|
ProbeForRead (combinePel->Buffer,
|
|
combinePel->Length,
|
|
sizeof (UCHAR));
|
|
}
|
|
RtlCopyMemory (buf, combinePel->Buffer, combinePel->Length);
|
|
buf += combinePel->Length;
|
|
#if DBG
|
|
ASSERT (combineLen >= combinePel->Length);
|
|
combineLen -= combinePel->Length;
|
|
#endif
|
|
combinePel++;
|
|
}
|
|
|
|
//
|
|
// Reset the local.
|
|
//
|
|
ASSERT (combineLen==0);
|
|
combinePel = NULL;
|
|
}
|
|
|
|
if ( TpIrp->RequestorMode != KernelMode ) {
|
|
//
|
|
// Probe before copying
|
|
//
|
|
ProbeForRead (pel->Buffer,
|
|
length,
|
|
sizeof (UCHAR));
|
|
}
|
|
RtlCopyMemory (buf, pel->Buffer, length);
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
if (afdBuffer!=NULL) {
|
|
AfdReturnBuffer (&afdBuffer->Header,
|
|
endpoint->OwningProcess);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Initialize the buffer structure so that we do not
|
|
// mistake it for file buffer descriptor and insert
|
|
// it into the packet chain
|
|
//
|
|
afdBuffer->FileObject = NULL;
|
|
afdBuffer->Next = NULL;
|
|
(*tpInfo->TailPd) = &afdBuffer->Header;
|
|
tpInfo->TailPd = &(afdBuffer->Next);
|
|
|
|
mdl = afdBuffer->Mdl;
|
|
//
|
|
// Adjust MDL length to the amount of data that we
|
|
// actualy sending from the buffer.
|
|
//
|
|
mdl->ByteCount = bufferLen;
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,small buffer-%p(%p,%lx)\n",
|
|
tpInfo,(PVOID)afdBuffer,mdl,length));
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Insert MDL into the MDL chain
|
|
//
|
|
*(tpInfo->TailMdl) = mdl;
|
|
tpInfo->TailMdl = &(mdl->Next);
|
|
|
|
//
|
|
// Advance app's buffer pointer
|
|
//
|
|
pel->Buffer = ((PUCHAR)pel->Buffer) + length;
|
|
|
|
} else { // if (pel->Flags & TP_MEMORY)
|
|
|
|
//
|
|
// This must be a file block
|
|
//
|
|
ASSERT ((pel->Flags & TP_FILE)!=0);
|
|
ASSERT (length!=0);
|
|
|
|
if (AFD_USE_CACHE(pel->FileObject)) {
|
|
|
|
//
|
|
// Caching file system, get it from cache.
|
|
// We just need a buffer tag to save buffer info
|
|
// so we can return it back to the cache when we
|
|
// are done sending.
|
|
//
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PAFD_BUFFER_TAG afdBufferTag;
|
|
|
|
tpInfo->PdNeedsPps = TRUE; // Need to free the MDL back to file
|
|
// system at passive/APC level
|
|
|
|
try {
|
|
afdBufferTag = AfdGetBufferTagRaiseOnFailure (
|
|
0,
|
|
endpoint->OwningProcess);
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy file parameters to the packet descriptor.
|
|
//
|
|
afdBufferTag->FileOffset = pel->FileOffset;
|
|
afdBufferTag->FileObject = pel->FileObject;
|
|
pel->FileOffset.QuadPart += length;
|
|
afdBufferTag->DataLength = length;
|
|
|
|
//
|
|
// Set fileMdl to NULL because FsRtlMdlRead attempts to
|
|
// chain the MDLs it returns off the input MDL variable.
|
|
//
|
|
afdBufferTag->Mdl = NULL;
|
|
|
|
//
|
|
// Attempt to use the fast path to get file data MDLs
|
|
// directly from the cache.
|
|
//
|
|
if (FsRtlMdlRead(
|
|
afdBufferTag->FileObject,
|
|
&afdBufferTag->FileOffset,
|
|
length,
|
|
0,
|
|
&afdBufferTag->Mdl,
|
|
&ioStatus
|
|
)) {
|
|
if ( ioStatus.Information < length) {
|
|
//
|
|
// Could not read the whole thing, must be end of file
|
|
//
|
|
status = AfdMdlReadComplete (
|
|
afdBufferTag->FileObject,
|
|
afdBufferTag->Mdl,
|
|
&afdBufferTag->FileOffset);
|
|
if (NT_SUCCESS (status)) {
|
|
AfdReturnBuffer (&afdBufferTag->Header,
|
|
endpoint->OwningProcess);
|
|
}
|
|
else {
|
|
REFERENCE_ENDPOINT (endpoint);
|
|
afdBufferTag->Context = endpoint;
|
|
AfdLRMdlReadComplete (&afdBufferTag->Header);
|
|
}
|
|
status = STATUS_END_OF_FILE;
|
|
break;
|
|
}
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,file tag-%p(%p,%lx:%I64x)\n",
|
|
tpInfo,afdBufferTag,afdBufferTag->Mdl,length,
|
|
pel->FileOffset.QuadPart));
|
|
}
|
|
//
|
|
// Insert the file MDL into the chain
|
|
//
|
|
mdl = *(tpInfo->TailMdl) = afdBufferTag->Mdl;
|
|
while (mdl->Next!=NULL)
|
|
mdl = mdl->Next;
|
|
tpInfo->TailMdl = &mdl->Next;
|
|
|
|
//
|
|
// Insert buffer tag into the chain too.
|
|
//
|
|
afdBufferTag->Next = NULL;
|
|
(*tpInfo->TailPd) = &afdBufferTag->Header;
|
|
tpInfo->TailPd = &(afdBufferTag->Next);
|
|
}
|
|
else {
|
|
//
|
|
// File is not in the cache, return STATUS_PENDING
|
|
// so that the Tpacket worker knows to
|
|
// perform MDL read via IRP interface
|
|
//
|
|
if (status==STATUS_SUCCESS) {
|
|
afdBufferTag->PartialMessage = FALSE;
|
|
}
|
|
else {
|
|
ASSERT (status==STATUS_MORE_PROCESSING_REQUIRED);
|
|
afdBufferTag->PartialMessage = TRUE;
|
|
}
|
|
afdBufferTag->Next = NULL;
|
|
*Pd = &afdBufferTag->Header;
|
|
status = STATUS_PENDING;
|
|
break;
|
|
}
|
|
|
|
} else { // if (AFD_USE_CACHE(pel->FileObject))
|
|
|
|
PAFD_BUFFER afdBuffer;
|
|
|
|
//
|
|
// Non-cacheable file system, need buffered read.
|
|
// Get the buffer first.
|
|
//
|
|
|
|
try {
|
|
afdBuffer = AfdGetBufferRaiseOnFailure (
|
|
endpoint,
|
|
length,
|
|
0,
|
|
endpoint->OwningProcess);
|
|
}
|
|
except (AFD_EXCEPTION_FILTER (status)) {
|
|
ASSERT (NT_ERROR (status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Copy file parameters to the packet descriptor.
|
|
// and return STATUS_PENDING, so that Tpacket worker knows
|
|
// to issue an IRP for buffered read.
|
|
//
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p,file buffer-%p(%p,%lx:%I64x)\n",
|
|
tpInfo,(PVOID)afdBuffer,afdBuffer->Mdl,length,
|
|
pel->FileOffset.QuadPart));
|
|
}
|
|
afdBuffer->FileOffset = pel->FileOffset;
|
|
afdBuffer->FileObject = pel->FileObject;
|
|
pel->FileOffset.QuadPart += length;
|
|
afdBuffer->DataLength = length;
|
|
afdBuffer->Mdl->ByteCount = length;
|
|
afdBuffer->Next = NULL;
|
|
if (status==STATUS_SUCCESS) {
|
|
afdBuffer->PartialMessage = FALSE;
|
|
}
|
|
else {
|
|
ASSERT (status==STATUS_MORE_PROCESSING_REQUIRED);
|
|
afdBuffer->PartialMessage = TRUE;
|
|
}
|
|
*Pd = &afdBuffer->Header;
|
|
status = STATUS_PENDING;
|
|
break;
|
|
|
|
} // if (AFD_USE_CACHE(pel->FileObject))
|
|
|
|
} // if (pel->Flags & TP_MEMORY)
|
|
|
|
} // if (length == 0)
|
|
|
|
} // while (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
|
|
if (attached) {
|
|
//
|
|
// If we attached to the calling, detach before exiting.
|
|
//
|
|
ASSERT (KeIsAttachedProcess ());
|
|
ASSERT (AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_USE_SYSTEM_THREAD);
|
|
KeDetachProcess ();
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain:"
|
|
" tp_info-%p, detached\n",
|
|
tpInfo));
|
|
}
|
|
}
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdBuildPacketChain: tp_info-%p,returning %lx\n",
|
|
tpInfo, status));
|
|
}
|
|
|
|
ASSERT (combinePel==NULL || !NT_SUCCESS (status));
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdCleanupPacketChain (
|
|
PIRP TpIrp,
|
|
BOOLEAN BelowDispatch
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up (releases all resources in) the packet chain.
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packet IRP
|
|
BelowDispatch - call is made below DISPATCH_LEVEL, can return MDL to file system
|
|
|
|
Return Value:
|
|
|
|
TRUE - all packets/MDLs are freed
|
|
FALSE - could not return MDLs to file system (when called at DISPATCH)
|
|
--*/
|
|
{
|
|
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
PAFD_BUFFER_HEADER pd = tpInfo->HeadPd;
|
|
PAFD_ENDPOINT endpoint = IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext;
|
|
|
|
ASSERT (tpInfo->HeadMdl!=NULL);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCleanupPacketChain: tp_info-%p,mdl-%p,pd-%p\n",
|
|
tpInfo,tpInfo->HeadMdl,tpInfo->HeadPd));
|
|
}
|
|
//
|
|
// Continue while we have any MDL's left
|
|
//
|
|
while (tpInfo->HeadMdl) {
|
|
//
|
|
// Advance to the next MDL
|
|
//
|
|
PMDL mdl;
|
|
|
|
mdl = tpInfo->HeadMdl;
|
|
tpInfo->HeadMdl = mdl->Next;
|
|
|
|
if (pd!=NULL) {
|
|
//
|
|
// We still have descriptors in the chain to compare against.
|
|
//
|
|
|
|
if (mdl==pd->Mdl) {
|
|
//
|
|
// This MDL has associated descriptor - file or buffered memory
|
|
// First remove this descriptor from the chain.
|
|
//
|
|
tpInfo->HeadPd = pd->Next;
|
|
if (pd->FileObject!=NULL && AFD_USE_CACHE (pd->FileObject)) {
|
|
|
|
if (BelowDispatch) {
|
|
//
|
|
// Cached file, the descriptor is just a tag with info
|
|
// to return MDL to the cache, do it.
|
|
//
|
|
PAFD_BUFFER_TAG afdBufferTag = CONTAINING_RECORD (pd, AFD_BUFFER_TAG, Header);
|
|
ULONG size = MmGetMdlByteCount (mdl);
|
|
PMDL lastMdl = mdl;
|
|
NTSTATUS status;
|
|
//
|
|
// Scan MDL chain till we find the last one for this file
|
|
// segment.
|
|
//
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCleanupPacketChain:"
|
|
" tp_info-%p,file tag-%p(%p,%lx:%I64x)\n",
|
|
tpInfo,afdBufferTag,mdl,afdBufferTag->DataLength,
|
|
afdBufferTag->FileOffset.QuadPart));
|
|
}
|
|
while (size<pd->DataLength) {
|
|
size += MmGetMdlByteCount (tpInfo->HeadMdl);
|
|
lastMdl = tpInfo->HeadMdl;
|
|
tpInfo->HeadMdl = tpInfo->HeadMdl->Next;
|
|
}
|
|
lastMdl->Next = NULL;
|
|
ASSERT (size==pd->DataLength);
|
|
//
|
|
// Return the MDL chain to file cache
|
|
//
|
|
status = AfdMdlReadComplete (
|
|
afdBufferTag->FileObject,
|
|
mdl,
|
|
&afdBufferTag->FileOffset);
|
|
if (NT_SUCCESS (status)) {
|
|
//
|
|
// Success free the corresponding buffer tag
|
|
//
|
|
AfdReturnBuffer (pd, endpoint->OwningProcess);
|
|
}
|
|
else {
|
|
//
|
|
// Failure, queue the descriptor to the low resource
|
|
// list to be processed by our global timer when
|
|
// (hopefully) enough memory will be available to do
|
|
// the work.
|
|
// We need to reference the endpoint since buffer tag
|
|
// may have been charged against the process that owns
|
|
// the endpoint.
|
|
//
|
|
REFERENCE_ENDPOINT (endpoint);
|
|
afdBufferTag->Context = endpoint;
|
|
AfdLRMdlReadComplete (&afdBufferTag->Header);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// If we are at dispatch, we can't free MDLs to file
|
|
// system, return to the caller.
|
|
//
|
|
|
|
tpInfo->HeadPd = pd;
|
|
tpInfo->HeadMdl = mdl;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Buffer with either file or memory data, just return
|
|
// it back to the pool.
|
|
//
|
|
PAFD_BUFFER afdBuffer = CONTAINING_RECORD (pd, AFD_BUFFER, Header);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCleanupPacketChain:"
|
|
" tp_info-%p,file buffer-%p(%p,%lx:%I64x)\n",
|
|
tpInfo,(PVOID)afdBuffer,mdl,afdBuffer->DataLength,
|
|
afdBuffer->FileOffset.QuadPart));
|
|
}
|
|
afdBuffer->Mdl->Next = NULL;
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer (pd, endpoint->OwningProcess);
|
|
}
|
|
|
|
//
|
|
// Move to the next descriptor in the chain.
|
|
//
|
|
pd = tpInfo->HeadPd;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stand-alone MDL with memory data
|
|
// Just unlock the pages if they were locked and return it.
|
|
// We never lock memory in partial MDLs, only in their source MDL
|
|
//
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCleanupPacketChain: tp_info-%p, mdl-%p(%p,%x,%x)\n",
|
|
tpInfo,mdl,
|
|
MmGetMdlVirtualAddress(mdl),
|
|
MmGetMdlByteCount (mdl),
|
|
mdl->MdlFlags));
|
|
}
|
|
mdl->Next = NULL;
|
|
if (mdl->MdlFlags & MDL_SOURCE_IS_NONPAGED_POOL) {
|
|
ASSERT (mdl->MappedSystemVa==(PVOID)tpInfo);
|
|
ASSERT (mdl->ByteCount==0);
|
|
mdl->ByteCount = 1;
|
|
}
|
|
else if (mdl->MdlFlags & MDL_PAGES_LOCKED &&
|
|
!(mdl->MdlFlags & MDL_PARTIAL)) {
|
|
MmUnlockPages (mdl);
|
|
}
|
|
IoFreeMdl (mdl);
|
|
}
|
|
|
|
ASSERT (tpInfo->TailMdl == &tpInfo->HeadMdl);
|
|
ASSERT (tpInfo->HeadPd == NULL);
|
|
ASSERT (tpInfo->TailPd == &tpInfo->HeadPd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AfdTPacketsSend (
|
|
PIRP TpIrp,
|
|
USHORT SendIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes the packets chain of the TpInfo and sends it.
|
|
Places back the chain sent before, so it can be freed.
|
|
If requested by the app and the last element is being sent,
|
|
initiates the disconnect.
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packet irp
|
|
SendIrp - index of the IRP to use for this send.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - send was queued to the transport OK
|
|
other - send failed
|
|
--*/
|
|
{
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (TpIrp);
|
|
PMDL tempMdl=NULL;
|
|
PAFD_BUFFER_HEADER tempPd=NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIRP irp, sendIrp=NULL;
|
|
PIO_COMPLETION_ROUTINE sendCompletion = AfdRestartTPacketsSend;
|
|
|
|
ASSERT (AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED);
|
|
|
|
irp = tpInfo->SendIrp[SendIrp];
|
|
|
|
//
|
|
// See if we can use IRP built into the AFD buffer.
|
|
// We do this for last series of the packets only so
|
|
// we can effectively buffer the data and complete the
|
|
// IRP early.
|
|
//
|
|
|
|
if (tpInfo->RemainingPkts!=MAXULONG) {
|
|
tpInfo->RemainingPkts -= 1;
|
|
|
|
//
|
|
// The conditions are:
|
|
// - number of remaining packets is less then total
|
|
// outstanding IRPs we can have
|
|
// - the packet does not need post-processing at below
|
|
// DPC level and/or in context of the thread/process
|
|
// - we actually have afd buffer to borrow the IRP.
|
|
//
|
|
|
|
if (tpInfo->RemainingPkts < (ULONG)tpInfo->NumSendIrps &&
|
|
!tpInfo->PdNeedsPps &&
|
|
tpInfo->HeadPd!=NULL) {
|
|
|
|
PAFD_BUFFER afdBuffer = CONTAINING_RECORD (tpInfo->HeadPd,
|
|
AFD_BUFFER,
|
|
Header);
|
|
PAFD_ENDPOINT endpoint = irpSp->FileObject->FsContext;
|
|
|
|
ASSERT (afdBuffer->BufferLength!=0);
|
|
ASSERT (afdBuffer->Irp!=NULL);
|
|
sendIrp = afdBuffer->Irp;
|
|
|
|
REFERENCE_ENDPOINT(endpoint);
|
|
afdBuffer->Context = endpoint;
|
|
|
|
sendCompletion = AfdRestartTPDetachedSend;
|
|
|
|
//
|
|
// There will be no completion flag reset - we do not have
|
|
// to wait for this one.
|
|
//
|
|
|
|
AFD_CLEAR_TP_FLAGS (TpIrp, AFD_TP_SEND_COMP_PENDING(SendIrp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (irp!=NULL) {
|
|
//
|
|
// Get the old data from the IRP.
|
|
//
|
|
ASSERT (irp->Overlay.AsynchronousParameters.UserApcRoutine==(PVOID)(ULONG_PTR)SendIrp);
|
|
tempPd = irp->Overlay.AsynchronousParameters.UserApcContext;
|
|
tempMdl = irp->MdlAddress;
|
|
if (sendIrp==NULL) {
|
|
//
|
|
// No special send IRP, the data will be reset with
|
|
// data to be sent
|
|
//
|
|
sendIrp = irp;
|
|
}
|
|
else {
|
|
//
|
|
// We are not going to use this IRP, reset data to NULL.
|
|
//
|
|
irp->Overlay.AsynchronousParameters.UserApcContext = NULL;
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
}
|
|
else if (sendIrp==NULL) {
|
|
//
|
|
// We need to allocate an IRP.
|
|
//
|
|
ASSERT (SendIrp>=AFD_TP_MIN_SEND_IRPS);
|
|
tpInfo->SendIrp[SendIrp] = IoAllocateIrp (tpInfo->TdiDeviceObject->StackSize, TRUE);
|
|
if (tpInfo->SendIrp[SendIrp]==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
AfdAbortTPackets (TpIrp, status);
|
|
return status;
|
|
}
|
|
sendIrp = irp = tpInfo->SendIrp[SendIrp];
|
|
irp->Overlay.AsynchronousParameters.UserApcRoutine=(PIO_APC_ROUTINE)(ULONG_PTR)SendIrp;
|
|
}
|
|
|
|
//
|
|
// Exchange the packet and MDL chains between send IRP and
|
|
// the tpInfo structure
|
|
//
|
|
sendIrp->Overlay.AsynchronousParameters.UserApcContext = tpInfo->HeadPd;
|
|
|
|
tpInfo->HeadPd = tempPd;
|
|
tpInfo->TailPd = &tpInfo->HeadPd;
|
|
|
|
|
|
//
|
|
// Build send IRP. Used combined send and disconnect if necessary
|
|
// and possible.
|
|
//
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
if ((AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_DISCONNECT) &&
|
|
(tpInfo->PdLength>0) && // Must be sending something or no S&D
|
|
(tpInfo->NextElement>=tpInfo->ElementCount) &&
|
|
AfdTPacketsEnableSendAndDisconnect (TpIrp)) {
|
|
AFD_SET_TP_FLAGS (TpIrp, AFD_TP_SEND_AND_DISCONNECT);
|
|
TdiBuildSend (sendIrp,
|
|
tpInfo->TdiDeviceObject,
|
|
tpInfo->TdiFileObject,
|
|
sendCompletion,
|
|
TpIrp,
|
|
tpInfo->HeadMdl,
|
|
TDI_SEND_AND_DISCONNECT,
|
|
tpInfo->PdLength
|
|
);
|
|
}
|
|
else {
|
|
TdiBuildSend (sendIrp,
|
|
tpInfo->TdiDeviceObject,
|
|
tpInfo->TdiFileObject,
|
|
sendCompletion,
|
|
TpIrp,
|
|
tpInfo->HeadMdl,
|
|
0,
|
|
tpInfo->PdLength
|
|
);
|
|
}
|
|
|
|
#else //TDI_SERVICE_SEND_AND_DISCONNECT
|
|
|
|
TdiBuildSend (sendIrp,
|
|
tpInfo->TdiDeviceObject,
|
|
tpInfo->TdiFileObject,
|
|
sendCompletion,
|
|
TpIrp,
|
|
tpInfo->HeadMdl,
|
|
0,
|
|
tpInfo->PdLength
|
|
);
|
|
#endif //TDI_SERVICE_SEND_AND_DISCONNECT
|
|
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsSend: tpInfo-%p, sending Irp: %p\n",
|
|
tpInfo, irp));
|
|
}
|
|
if (sendCompletion==AfdRestartTPacketsSend) {
|
|
//
|
|
// Reference the tpInfo structure so it does not go away
|
|
// till send IRP completes and pass the IRP to the transport.
|
|
//
|
|
REFERENCE_TPACKETS (TpIrp);
|
|
|
|
IoCallDriver (tpInfo->TdiDeviceObject, sendIrp);
|
|
}
|
|
else {
|
|
//
|
|
// No need to reference as we are not going to wait for
|
|
// completion.
|
|
//
|
|
status = IoCallDriver (tpInfo->TdiDeviceObject, sendIrp);
|
|
if (NT_SUCCESS (status)) {
|
|
//
|
|
// Change STATUS_PENDING to success not to confuse the caller
|
|
// and add byte count under assumption of success (if it fails
|
|
// later, connection will be dropped and we don't guarantee
|
|
// anything for datagrams anyway).
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
#ifdef _WIN64
|
|
InterlockedExchangeAdd64 (
|
|
(PLONG64)&TpIrp->IoStatus.Information,
|
|
tpInfo->PdLength
|
|
);
|
|
#else //_WIN64
|
|
InterlockedExchangeAdd (
|
|
(PLONG)&TpIrp->IoStatus.Information,
|
|
tpInfo->PdLength);
|
|
#endif //_WIN64
|
|
}
|
|
else {
|
|
//
|
|
// If send fails, we'll have to abort here since completion routine
|
|
// will not have access to the TpIrp
|
|
//
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsSend: tpInfo-%p, detached send failed: %lx\n",
|
|
tpInfo, status));
|
|
}
|
|
AfdAbortTPackets (TpIrp, status);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Setup the chain in the tpInfo so it can be freed.
|
|
//
|
|
tpInfo->HeadMdl = tempMdl;
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
tpInfo->PdLength = 0;
|
|
tpInfo->PdNeedsPps = FALSE;
|
|
|
|
if (tpInfo->NextElement>=tpInfo->ElementCount) {
|
|
PAFD_ENDPOINT endpoint = irpSp->FileObject->FsContext;
|
|
if (!(AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_DISCONNECT)) {
|
|
AFD_SET_TP_FLAGS (TpIrp, AFD_TP_SENDS_POSTED);
|
|
if (AFD_GET_TPIC (TpIrp)->Next!=NULL) {
|
|
AfdStartNextTPacketsIrp (endpoint, TpIrp);
|
|
}
|
|
}
|
|
else
|
|
//
|
|
// If necessary and not using combined S&D,
|
|
// sumbit disconnect IRP to the transport
|
|
//
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
if (!(AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_SEND_AND_DISCONNECT))
|
|
#endif // TDI_SERVICE_SEND_AND_DISCONNECT
|
|
{
|
|
ASSERT (endpoint->Type==AfdBlockTypeVcConnecting);
|
|
ASSERT (endpoint->Common.VcConnecting.Connection!=NULL);
|
|
AfdPerformTpDisconnect (TpIrp);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the flag indicating to the completion routine that we are done
|
|
//
|
|
AFD_CLEAR_TP_FLAGS (TpIrp, AFD_TP_SEND_CALL_PENDING(SendIrp));
|
|
UPDATE_TPACKETS2 (TpIrp, "Submitted SendIrp: 0x%lX", SendIrp);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for TPackets send.
|
|
Does another send if packet is ready or schedules worker
|
|
to do the same.
|
|
Arguments:
|
|
DeviceObject - AfdDeviceObject
|
|
Irp - send IRP being completed
|
|
Context - TpIrp
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - tell IO subsystem to stop
|
|
processing the IRP (it is handled internally).
|
|
--*/
|
|
{
|
|
PIRP tpIrp;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
PAFD_ENDPOINT endpoint;
|
|
USHORT sendIrp;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
tpIrp = Context;
|
|
tpInfo = tpIrp->AssociatedIrp.SystemBuffer;
|
|
endpoint = IoGetCurrentIrpStackLocation (tpIrp)->FileObject->FsContext;
|
|
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Figure out which IRP is being completed.
|
|
//
|
|
sendIrp = (USHORT)(ULONG_PTR)Irp->Overlay.AsynchronousParameters.UserApcRoutine;
|
|
ASSERT (tpInfo->SendIrp[sendIrp]==Irp);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartTPacketsSend: tp_info-%p,Irp-%p,status-%lx\n",
|
|
tpInfo,
|
|
Irp,
|
|
Irp->IoStatus.Status
|
|
));
|
|
}
|
|
|
|
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
LONG stateFlags, newStateFlags;
|
|
BOOLEAN needWorker;
|
|
|
|
UPDATE_TPACKETS2 (tpIrp, "Send completed with 0x%lX bytes",
|
|
(ULONG)Irp->IoStatus.Information);
|
|
//
|
|
// Successfull completion, update transfer count.
|
|
// We don't hold the spinlock, so we need to use interlocked operation.
|
|
// Wish we have a common one for both 64 and 32 bit platforms.
|
|
//
|
|
#ifdef _WIN64
|
|
InterlockedExchangeAdd64 ((PLONG64)&tpIrp->IoStatus.Information,
|
|
Irp->IoStatus.Information);
|
|
#else //_WIN64
|
|
InterlockedExchangeAdd ((PLONG)&tpIrp->IoStatus.Information,
|
|
Irp->IoStatus.Information);
|
|
#endif //_WIN64
|
|
|
|
do {
|
|
ULONG sendMask;
|
|
newStateFlags = stateFlags = AFD_GET_TPIC(tpIrp)->StateFlags;
|
|
//
|
|
// See if dispatch routine has not completed yet or
|
|
// the request is aborted or worker is aready running
|
|
// Or if two consequtive requests are in dispatch routines.
|
|
//
|
|
if ( (newStateFlags & (AFD_TP_ABORT_PENDING |
|
|
AFD_TP_WORKER_SCHEDULED |
|
|
AFD_TP_SEND_CALL_PENDING(sendIrp))) ||
|
|
((sendMask = (newStateFlags & AFD_TP_SEND_MASK)) &
|
|
( (sendMask>>2) |
|
|
(sendMask<<(AFD_TP_MAX_SEND_IRPS*2-2)) ) )
|
|
) {
|
|
|
|
//
|
|
// Can't continue, just clear completion flag
|
|
//
|
|
newStateFlags &= ~AFD_TP_SEND_COMP_PENDING(sendIrp);
|
|
needWorker = FALSE;
|
|
}
|
|
else {
|
|
//
|
|
// Take control over worker scheduling and
|
|
// mark IRP as busy.
|
|
//
|
|
needWorker = TRUE;
|
|
newStateFlags |= AFD_TP_WORKER_SCHEDULED;
|
|
if (tpInfo->HeadMdl!=NULL) {
|
|
newStateFlags |= AFD_TP_SEND_CALL_PENDING(sendIrp);
|
|
}
|
|
else {
|
|
newStateFlags &= ~AFD_TP_SEND_COMP_PENDING(sendIrp);
|
|
}
|
|
}
|
|
|
|
}
|
|
while (InterlockedCompareExchange (
|
|
(PLONG)&AFD_GET_TPIC(tpIrp)->StateFlags,
|
|
newStateFlags,
|
|
stateFlags)!=stateFlags);
|
|
if (needWorker) {
|
|
//
|
|
// We can do processing here, see if there is something to send
|
|
//
|
|
if (tpInfo->HeadMdl) {
|
|
//
|
|
// Yes, do it
|
|
//
|
|
AfdTPacketsSend (tpIrp, sendIrp);
|
|
}
|
|
//
|
|
// Start worker to prepare new stuff/free what we sent before.
|
|
// We transfer the reference we added when we queued the request
|
|
// to the worker.
|
|
//
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, tpIrp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
else {
|
|
//
|
|
// Worker was already running or request aborted or dispatch
|
|
// routine not completed yet.
|
|
//
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Failure, abort the request even if dispatch did not complete.
|
|
// We do not know if dispatch routine is going to return error status,
|
|
// it may just return STATUS_PENDING and we then loose the error code.
|
|
// Double abort is harmless.
|
|
//
|
|
AFD_CLEAR_TP_FLAGS (tpIrp, AFD_TP_SEND_COMP_PENDING(sendIrp));
|
|
AfdAbortTPackets (tpIrp, Irp->IoStatus.Status);
|
|
}
|
|
|
|
//
|
|
// Remove the reference we added when we queued the request.
|
|
//
|
|
DEREFERENCE_TPACKETS (tpIrp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdRestartTPDetachedSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for detached TPackets send.
|
|
Just frees the buffers.
|
|
|
|
Arguments:
|
|
DeviceObject - AfdDeviceObject
|
|
Irp - send IRP being completed
|
|
Context - Ignore (TpIrp is stored for debugging purposes only).
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - tell IO subsystem to stop
|
|
processing the IRP (it is handled internally).
|
|
--*/
|
|
{
|
|
PAFD_BUFFER afdBuffer = Irp->Overlay.AsynchronousParameters.UserApcContext;
|
|
PAFD_ENDPOINT endpoint = afdBuffer->Context;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
UNREFERENCED_PARAMETER (Context);
|
|
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartTPDetachedSend: Irp-%p,status-%lx\n",
|
|
Irp,
|
|
Irp->IoStatus.Status
|
|
));
|
|
}
|
|
|
|
do {
|
|
PAFD_BUFFER tempBuffer = afdBuffer;
|
|
afdBuffer = CONTAINING_RECORD (afdBuffer->Next, AFD_BUFFER, Header);
|
|
tempBuffer->Next = NULL;
|
|
tempBuffer->Mdl->Next = NULL;
|
|
tempBuffer->Mdl->ByteCount = tempBuffer->BufferLength;
|
|
AfdReturnBuffer (&tempBuffer->Header, endpoint->OwningProcess);
|
|
}
|
|
while (afdBuffer!=NULL);
|
|
|
|
DEREFERENCE_ENDPOINT(endpoint);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
USHORT
|
|
AfdTPacketsFindSendIrp (
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Finds the send IRP that is not currently in use and marks
|
|
it as busy
|
|
Arguments:
|
|
TpIrp - Transmit packets Irp
|
|
|
|
Return Value:
|
|
0-based index of send irp or TpInfo->NumSendIrps if all IRPs are i use
|
|
--*/
|
|
{
|
|
|
|
LONG stateFlags, newStateFlags;
|
|
USHORT sendIrp;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT( AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED );
|
|
|
|
do {
|
|
newStateFlags = stateFlags = AFD_GET_TPIC(TpIrp)->StateFlags;
|
|
if (newStateFlags & AFD_TP_ABORT_PENDING) {
|
|
//
|
|
// Abort is in progress, bail
|
|
//
|
|
sendIrp = tpInfo->NumSendIrps;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if any send IRP is not in use
|
|
//
|
|
for (sendIrp=0; sendIrp<tpInfo->NumSendIrps; sendIrp++) {
|
|
if ((newStateFlags & AFD_TP_SEND_BUSY(sendIrp))==0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sendIrp!=tpInfo->NumSendIrps) {
|
|
//
|
|
// Found send IRP, mark it as busy
|
|
//
|
|
newStateFlags |= AFD_TP_SEND_BUSY(sendIrp);
|
|
}
|
|
else {
|
|
//
|
|
// No send IRPs, suspend the worker.
|
|
//
|
|
newStateFlags &= (~AFD_TP_WORKER_SCHEDULED);
|
|
}
|
|
}
|
|
while (InterlockedCompareExchange (
|
|
(PLONG)&AFD_GET_TPIC(TpIrp)->StateFlags,
|
|
newStateFlags,
|
|
stateFlags)!=stateFlags);
|
|
|
|
return sendIrp;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdTPacketsMdlRead (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER Pd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs IRP based MDL read (invoked when cache read fails).
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packet irp
|
|
Pd - descriptor with file parameters for the read
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - read was completed in-line
|
|
STATUS_PENDING - read was queued to file system driver,
|
|
will complete lated
|
|
other - read failed
|
|
--*/
|
|
{
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
ASSERT( AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED );
|
|
|
|
//
|
|
// Find the device object and allocate IRP of appropriate size
|
|
// if current one does not fit or is not available at all.
|
|
//
|
|
deviceObject = IoGetRelatedDeviceObject (Pd->FileObject);
|
|
if ((tpInfo->ReadIrp==NULL) ||
|
|
(tpInfo->ReadIrp->StackCount<deviceObject->StackSize)) {
|
|
if (tpInfo->ReadIrp!=NULL) {
|
|
IoFreeIrp (tpInfo->ReadIrp);
|
|
}
|
|
|
|
tpInfo->ReadIrp = IoAllocateIrp (deviceObject->StackSize, FALSE);
|
|
if (tpInfo->ReadIrp==NULL) {
|
|
PAFD_ENDPOINT endpoint;
|
|
endpoint = IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
AfdReturnBuffer (Pd, endpoint->OwningProcess);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark IRP as busy and set it up
|
|
//
|
|
AFD_SET_TP_FLAGS (TpIrp, AFD_TP_READ_BUSY);
|
|
|
|
irp = tpInfo->ReadIrp;
|
|
|
|
|
|
irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Set the synchronous flag in the IRP to tell the file system
|
|
// that we are aware of the fact that this IRP will be completed
|
|
// synchronously. This means that we must supply our own thread
|
|
// for the operation and that the disk read will occur
|
|
// synchronously in this thread if the data is not cached.
|
|
//
|
|
|
|
irp->Flags |= IRP_SYNCHRONOUS_API;
|
|
|
|
|
|
irp->Overlay.AsynchronousParameters.UserApcContext = Pd;
|
|
//
|
|
// Set current thread for IoSetHardErrorOrVerifyDevice.
|
|
//
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread ();
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_READ;
|
|
irpSp->MinorFunction = IRP_MN_MDL;
|
|
irpSp->FileObject = Pd->FileObject;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AfdRestartTPacketsMdlRead,
|
|
TpIrp,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT( irpSp->Parameters.Read.Key == 0 );
|
|
|
|
//
|
|
// Finish building the read IRP.
|
|
//
|
|
|
|
irpSp->Parameters.Read.Length = Pd->DataLength;
|
|
irpSp->Parameters.Read.ByteOffset = Pd->FileOffset;
|
|
|
|
REFERENCE_TPACKETS (TpIrp);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsMdlRead: tp_info-%p,Irp-%p,file-%pd,offset-%I64x,length-%lx\n",
|
|
tpInfo,
|
|
irp,
|
|
Pd->FileObject,
|
|
Pd->FileOffset.QuadPart,
|
|
Pd->DataLength));
|
|
}
|
|
|
|
IoCallDriver (deviceObject, irp);
|
|
|
|
if (((AFD_CLEAR_TP_FLAGS (TpIrp, AFD_TP_READ_CALL_PENDING)
|
|
& (AFD_TP_READ_COMP_PENDING|AFD_TP_ABORT_PENDING))==0) &&
|
|
NT_SUCCESS (irp->IoStatus.Status) &&
|
|
AfdTPacketsContinueAfterRead (TpIrp)) {
|
|
//
|
|
// Read completed successfully inline and post-processing was successfull,
|
|
// tell the worker to go on.
|
|
//
|
|
UPDATE_TPACKETS2 (TpIrp, "MdlRead completed inline with 0x%lX bytes",
|
|
(ULONG)irp->IoStatus.Information);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// Read has not completed yet or post processing failed,
|
|
// worker should bail and we will continue when read completes
|
|
// or in final completion routine (AfdCompleteTPackets).
|
|
//
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsMdlRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for IRP based MDL read.
|
|
Does another send if packet is ready or schedules worker
|
|
to do the same.
|
|
Arguments:
|
|
DeviceObject - AfdDeviceObject
|
|
Irp - read IRP being completed
|
|
Context - TpIrp
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - tell IO subsystem to stop
|
|
processing the IRP (it is handled internally).
|
|
--*/
|
|
{
|
|
PIRP tpIrp = Context;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = tpIrp->AssociatedIrp.SystemBuffer;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_BUFFER_HEADER pd;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
endpoint = IoGetCurrentIrpStackLocation (tpIrp)->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT (AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED);
|
|
ASSERT (tpInfo->ReadIrp == Irp);
|
|
|
|
pd = Irp->Overlay.AsynchronousParameters.UserApcContext;
|
|
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartTPacketsMdlRead:"
|
|
" tp_info-%p,Irp-%p,status-%lx,length-%p,mdl-%p\n",
|
|
tpInfo,
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information,
|
|
Irp->MdlAddress));
|
|
}
|
|
|
|
//
|
|
// Insert MDL into the current chain.
|
|
//
|
|
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
PMDL mdl = *(tpInfo->TailMdl) = Irp->MdlAddress;
|
|
while (mdl->Next!=NULL)
|
|
mdl = mdl->Next;
|
|
tpInfo->TailMdl = &mdl->Next;
|
|
|
|
pd->Mdl = Irp->MdlAddress;
|
|
|
|
|
|
//
|
|
// If FS driver hits EOF, it will still return
|
|
// success to us and we need to handle this case.
|
|
//
|
|
if (pd->DataLength==Irp->IoStatus.Information) {
|
|
|
|
(*tpInfo->TailPd) = pd;
|
|
tpInfo->TailPd = &(pd->Next);
|
|
|
|
Irp->MdlAddress = NULL;
|
|
|
|
if (((AFD_CLEAR_TP_FLAGS (tpIrp, AFD_TP_READ_COMP_PENDING)
|
|
& (AFD_TP_READ_CALL_PENDING|AFD_TP_ABORT_PENDING))==0) &&
|
|
AfdTPacketsContinueAfterRead (tpIrp)) {
|
|
//
|
|
// Read dispatch has already returned and post-processing
|
|
// was successfull, schedule the worker to continue processing
|
|
// We transfer the reference that we added when we queued the
|
|
// read to the worker.
|
|
//
|
|
|
|
UPDATE_TPACKETS2 (tpIrp, "MdlRead completed in restart with 0x%lX bytes",
|
|
(ULONG)Irp->IoStatus.Information);
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, tpIrp);
|
|
}
|
|
else {
|
|
DEREFERENCE_TPACKETS (tpIrp);
|
|
}
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
//
|
|
// FS driver read less than we expected.
|
|
// We must have hit end of file.
|
|
// Save the real packet length so we can cleanup correctly and
|
|
// force abort with STATUS_END_OF_FILE.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_END_OF_FILE;
|
|
pd->DataLength = (ULONG)Irp->IoStatus.Information;
|
|
}
|
|
else {
|
|
ASSERT (Irp->MdlAddress == NULL);
|
|
}
|
|
AFD_CLEAR_TP_FLAGS (tpIrp, AFD_TP_READ_COMP_PENDING);
|
|
AfdAbortTPackets (tpIrp, Irp->IoStatus.Status);
|
|
|
|
if (pd->Mdl==NULL) {
|
|
//
|
|
// No MDL was returned by the file system.
|
|
// We can free the packed descriptor immediately.
|
|
//
|
|
AfdReturnBuffer (pd, endpoint->OwningProcess);
|
|
}
|
|
else {
|
|
//
|
|
// File system did return MDL to us.
|
|
// Save the descriptor so the MDL can be
|
|
// properly returned back to the file system
|
|
// by the cleanup routine.
|
|
//
|
|
(*tpInfo->TailPd) = pd;
|
|
tpInfo->TailPd = &(pd->Next);
|
|
}
|
|
DEREFERENCE_TPACKETS (tpIrp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdMdlReadComplete (
|
|
PFILE_OBJECT FileObject,
|
|
PMDL FileMdl,
|
|
PLARGE_INTEGER FileOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns MDL to the file system / cache manager
|
|
|
|
Arguments:
|
|
FileObject - file object from which MDL comes
|
|
FileMdl - MDL itself
|
|
FileOffset - offset in the file where MDL data begins
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - operation completed immediately
|
|
STATUS_PENDING - request is sumbitted to file system driver
|
|
other - operation failed.
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_OBJECT deviceObject;
|
|
ASSERT (KeGetCurrentIrql()<=APC_LEVEL);
|
|
|
|
if( FsRtlMdlReadComplete (
|
|
FileObject,
|
|
FileMdl) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Fast path failed, so create a new IRP.
|
|
//
|
|
|
|
deviceObject = IoGetRelatedDeviceObject (FileObject);
|
|
irp = IoAllocateIrp(
|
|
deviceObject->StackSize,
|
|
FALSE
|
|
);
|
|
|
|
if( irp == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Setup the IRP.
|
|
//
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
irp->MdlAddress = FileMdl;
|
|
|
|
irpSp->MajorFunction = IRP_MJ_READ;
|
|
irpSp->MinorFunction = IRP_MN_MDL | IRP_MN_COMPLETE;
|
|
|
|
irpSp->Parameters.Read.Length = 0;
|
|
while (FileMdl!=NULL) {
|
|
irpSp->Parameters.Read.Length += FileMdl->ByteCount;
|
|
FileMdl = FileMdl->Next;
|
|
}
|
|
|
|
irpSp->Parameters.Read.ByteOffset = *FileOffset;
|
|
|
|
irpSp->Parameters.Read.Key = 0;
|
|
|
|
//
|
|
// Reference file object so it does not go away till this
|
|
// IRP completes
|
|
//
|
|
ObReferenceObject (FileObject);
|
|
AfdRecordFileRef ();
|
|
|
|
irpSp->FileObject = FileObject;
|
|
irpSp->DeviceObject = deviceObject;
|
|
|
|
//
|
|
// Submit the IRP
|
|
//
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AfdRestartMdlReadComplete,
|
|
FileObject,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdMdlReadComplete: file-%p,Irp-%p,offset-%I64x\n",
|
|
FileObject,
|
|
irp,
|
|
FileOffset->QuadPart));
|
|
}
|
|
IoCallDriver (deviceObject, irp);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartMdlReadComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for IRPs issued by AfdMdlReadComplete. The only
|
|
purpose of this completion routine is to free the IRPs created by
|
|
AfdMdlReadComplete() and release file object reference.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Unused.
|
|
|
|
Irp - The completed IRP.
|
|
|
|
Context - FileObject on which MDL is returned
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - AFD takes care of the IRP
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_OBJECT FileObject = Context;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartMdlReadComplete: Irp-%p,status-%lx,length-%p\n",
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information));
|
|
}
|
|
|
|
//
|
|
// Dereference the file object
|
|
//
|
|
ObDereferenceObject (FileObject);
|
|
AfdRecordFileDeref ();
|
|
|
|
//
|
|
// Free the IRP since it's no longer needed.
|
|
//
|
|
|
|
IoFreeIrp( Irp );
|
|
|
|
//
|
|
// Return STATUS_MORE_PROCESSING_REQUIRED so that IoCompleteRequest
|
|
// will stop working on the IRP.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // AfdRestartMdlReadComplete
|
|
|
|
|
|
VOID
|
|
AfdLRMdlReadComplete (
|
|
PAFD_BUFFER_HEADER Pd
|
|
)
|
|
{
|
|
if (InterlockedPushEntrySList (
|
|
&AfdLRFileMdlList,
|
|
(PSLIST_ENTRY)&Pd->SList)==NULL) {
|
|
AfdLRListAddItem (&AfdLRFileMdlListItem,
|
|
AfdLRProcessFileMdlList);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdLRProcessFileMdlList (
|
|
PAFD_LR_LIST_ITEM Item
|
|
)
|
|
{
|
|
PSLIST_ENTRY localList;
|
|
BOOLEAN res = TRUE;
|
|
|
|
ASSERT (Item==&AfdLRFileMdlListItem);
|
|
DEBUG Item->SListLink.Next = UlongToPtr(0xBAADF00D);
|
|
|
|
localList = InterlockedFlushSList (&AfdLRFileMdlList);
|
|
|
|
while (localList!=NULL) {
|
|
PAFD_BUFFER_HEADER pd;
|
|
NTSTATUS status;
|
|
pd = CONTAINING_RECORD (localList, AFD_BUFFER_HEADER, SList);
|
|
localList = localList->Next;
|
|
|
|
if (pd->BufferLength==0) {
|
|
PAFD_BUFFER_TAG afdBufferTag = CONTAINING_RECORD (
|
|
pd,
|
|
AFD_BUFFER_TAG,
|
|
Header);
|
|
PAFD_ENDPOINT endpoint = afdBufferTag->Context;
|
|
status = AfdMdlReadComplete (afdBufferTag->FileObject,
|
|
afdBufferTag->Mdl,
|
|
&afdBufferTag->FileOffset);
|
|
if (NT_SUCCESS (status)) {
|
|
AfdReturnBuffer (&afdBufferTag->Header,
|
|
endpoint->OwningProcess);
|
|
DEREFERENCE_ENDPOINT (endpoint);
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
PAFD_BUFFER afdBuffer = CONTAINING_RECORD (
|
|
pd,
|
|
AFD_BUFFER,
|
|
Header);
|
|
PAFD_CONNECTION connection = afdBuffer->Context;
|
|
status = AfdMdlReadComplete (afdBuffer->FileObject,
|
|
afdBuffer->Mdl->Next,
|
|
&afdBuffer->FileOffset);
|
|
if (NT_SUCCESS (status)) {
|
|
afdBuffer->Mdl->Next = NULL;
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer (&afdBuffer->Header,
|
|
connection->OwningProcess);
|
|
DEREFERENCE_CONNECTION (connection);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (InterlockedPushEntrySList (
|
|
&AfdLRFileMdlList,
|
|
(PSLIST_ENTRY)&pd->SList)==NULL) {
|
|
ASSERT (Item->SListLink.Next==UlongToPtr(0xBAADF00D));
|
|
res = FALSE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdTPacketsBufferRead (
|
|
PIRP TpIrp,
|
|
PAFD_BUFFER_HEADER Pd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs buffered file read for file systems that do
|
|
not support caching
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packet irp
|
|
Pd - descriptor with file parameters for the read
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - read was completed in-line
|
|
STATUS_PENDING - read was queued to file system driver,
|
|
will complete lated
|
|
other - read failed
|
|
--*/
|
|
{
|
|
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIRP irp;
|
|
PAFD_BUFFER afdBuffer;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
ASSERT( AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED );
|
|
|
|
afdBuffer = CONTAINING_RECORD (Pd, AFD_BUFFER, Header);
|
|
|
|
//
|
|
// Find the device object and allocate IRP of appropriate size
|
|
// if current one does not fit or is not available at all.
|
|
//
|
|
deviceObject = IoGetRelatedDeviceObject (afdBuffer->FileObject);
|
|
|
|
if ((tpInfo->ReadIrp==NULL) ||
|
|
(tpInfo->ReadIrp->StackCount<deviceObject->StackSize)) {
|
|
if (tpInfo->ReadIrp!=NULL) {
|
|
IoFreeIrp (tpInfo->ReadIrp);
|
|
}
|
|
|
|
if (afdBuffer->Irp->StackCount<deviceObject->StackSize) {
|
|
tpInfo->ReadIrp = IoAllocateIrp (deviceObject->StackSize, FALSE);
|
|
if (tpInfo->ReadIrp==NULL) {
|
|
PAFD_ENDPOINT endpoint;
|
|
endpoint = IoGetCurrentIrpStackLocation (TpIrp)->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
Pd->Mdl->ByteCount = Pd->BufferLength;
|
|
AfdReturnBuffer (Pd, endpoint->OwningProcess);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else {
|
|
tpInfo->ReadIrp = afdBuffer->Irp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark IRP as busy and set it up
|
|
//
|
|
|
|
AFD_SET_TP_FLAGS (TpIrp, AFD_TP_READ_BUSY);
|
|
|
|
irp = tpInfo->ReadIrp;
|
|
|
|
|
|
//
|
|
// Setup and sumbit the IRP
|
|
//
|
|
irp->MdlAddress = afdBuffer->Mdl;
|
|
irp->AssociatedIrp.SystemBuffer = afdBuffer->Buffer;
|
|
irp->UserBuffer = afdBuffer->Buffer;
|
|
|
|
//
|
|
// Set current thread for IoSetHardErrorOrVerifyDevice.
|
|
//
|
|
irp->Tail.Overlay.Thread = PsGetCurrentThread ();
|
|
irp->Overlay.AsynchronousParameters.UserApcContext = Pd;
|
|
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
irpSp->MajorFunction = IRP_MJ_READ;
|
|
irpSp->MinorFunction = IRP_MN_NORMAL;
|
|
irpSp->FileObject = afdBuffer->FileObject;
|
|
|
|
|
|
irpSp->Parameters.Read.Length = Pd->DataLength;
|
|
irpSp->Parameters.Read.ByteOffset = Pd->FileOffset;
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
AfdRestartTPacketsBufferRead,
|
|
TpIrp,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
|
|
REFERENCE_TPACKETS (TpIrp);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTPacketsBufferRead:"
|
|
" Initiating read, tp_info-%p,file-%p,buffer-%p,length-%lx,offset-%I64x\n",
|
|
tpInfo,
|
|
afdBuffer->FileObject,
|
|
(PVOID)afdBuffer,
|
|
afdBuffer->DataLength,
|
|
afdBuffer->FileOffset.QuadPart
|
|
));
|
|
}
|
|
|
|
IoCallDriver (deviceObject, irp);
|
|
|
|
if (((AFD_CLEAR_TP_FLAGS (TpIrp, AFD_TP_READ_CALL_PENDING)
|
|
& (AFD_TP_READ_COMP_PENDING|AFD_TP_ABORT_PENDING))==0) &&
|
|
NT_SUCCESS (irp->IoStatus.Status) &&
|
|
AfdTPacketsContinueAfterRead (TpIrp)) {
|
|
//
|
|
// Read completed successfully inline and post-processing was successfull,
|
|
// tell the worker to go on.
|
|
//
|
|
UPDATE_TPACKETS2(TpIrp, "BufRead completed inline with 0x%lX bytes",
|
|
(ULONG)irp->IoStatus.Information);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
//
|
|
// Read has not completed yet or post processing failed,
|
|
// worker should bail and we will continue when read completes
|
|
// or in final completion routine (AfdCompleteTPackets).
|
|
//
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
AfdRestartTPacketsBufferRead (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion routine for buffered read.
|
|
Does another send if packet is ready or schedules worker
|
|
to do the same.
|
|
Arguments:
|
|
DeviceObject - AfdDeviceObject
|
|
Irp - read IRP being completed
|
|
Context - TpIrp
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED - tell IO subsystem to stop
|
|
processing the IRP (it is handled internally).
|
|
--*/
|
|
{
|
|
PIRP tpIrp = Context;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = tpIrp->AssociatedIrp.SystemBuffer;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_BUFFER afdBuffer;
|
|
ULONG flags;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
endpoint = IoGetCurrentIrpStackLocation (tpIrp)->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
ASSERT (AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED);
|
|
ASSERT (tpInfo->ReadIrp == Irp ||
|
|
AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_ABORT_PENDING);
|
|
|
|
afdBuffer = Irp->Overlay.AsynchronousParameters.UserApcContext;
|
|
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartTPacketsBufferRead: tp_info-%p,Irp-%p,status-%lx,length-%p\n",
|
|
tpInfo,
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information));
|
|
}
|
|
|
|
//
|
|
// Insert MDL into the current chain
|
|
// even if fs driver failed so that common
|
|
// cleanup routine takes care of its disposal
|
|
// together with AfdBuffer.
|
|
//
|
|
*(tpInfo->TailMdl) = afdBuffer->Mdl;
|
|
tpInfo->TailMdl = &(afdBuffer->Mdl->Next);
|
|
ASSERT (*(tpInfo->TailMdl)==NULL);
|
|
|
|
(*tpInfo->TailPd) = &afdBuffer->Header;
|
|
tpInfo->TailPd = &(afdBuffer->Next);
|
|
ASSERT (*(tpInfo->TailPd)==NULL);
|
|
|
|
flags = AFD_CLEAR_TP_FLAGS (tpIrp, AFD_TP_READ_COMP_PENDING);
|
|
|
|
if (Irp==afdBuffer->Irp) {
|
|
//
|
|
// If abort is aready in progress, we need to use
|
|
// interlocked exchange to synchronize with
|
|
// AfdAbortTPackets which may be attempting to cancel
|
|
// this IRP.
|
|
//
|
|
if (flags & AFD_TP_ABORT_PENDING) {
|
|
#if DBG
|
|
PIRP irp =
|
|
#endif
|
|
InterlockedExchangePointer ((PVOID *)&tpInfo->ReadIrp, NULL);
|
|
ASSERT (irp==Irp || irp==(PVOID)-1);
|
|
}
|
|
else {
|
|
tpInfo->ReadIrp = NULL;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS (Irp->IoStatus.Status)) {
|
|
if (((flags & (AFD_TP_READ_CALL_PENDING|AFD_TP_ABORT_PENDING))==0) &&
|
|
AfdTPacketsContinueAfterRead (tpIrp)) {
|
|
//
|
|
// Read dispatch has already returned and post-processing
|
|
// was successfull, schedule the worker to continue processing
|
|
// We transfer the reference that we added when we queued the
|
|
// read to the worker.
|
|
//
|
|
UPDATE_TPACKETS2 (tpIrp, "BufRead completed in restart with %08x bytes", (ULONG)Irp->IoStatus.Information);
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, tpIrp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
}
|
|
else {
|
|
AfdAbortTPackets (tpIrp, Irp->IoStatus.Status);
|
|
}
|
|
|
|
DEREFERENCE_TPACKETS (tpIrp);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
BOOLEAN
|
|
AfdTPacketsContinueAfterRead (
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Read post-processing common for cached and non-cached case.
|
|
Queues new send if packet is complete and send IRP is available
|
|
Arguments:
|
|
TpInfo - transmit packets IRP
|
|
Return Value:
|
|
TRUE - continue processing
|
|
FALSE - processing cannot be continued because there are no
|
|
available send IRPs
|
|
--*/
|
|
|
|
{
|
|
PAFD_BUFFER_HEADER pd;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
BOOLEAN res = TRUE;
|
|
|
|
pd = CONTAINING_RECORD (tpInfo->TailPd, AFD_BUFFER_HEADER, Next);
|
|
if (!pd->PartialMessage) {
|
|
USHORT sendIrp;
|
|
|
|
sendIrp = AfdTPacketsFindSendIrp (TpIrp);
|
|
if (sendIrp!=tpInfo->NumSendIrps) {
|
|
NTSTATUS status;
|
|
status = AfdTPacketsSend (TpIrp, sendIrp);
|
|
res = (BOOLEAN)NT_SUCCESS (status);
|
|
}
|
|
else {
|
|
res = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Need to complete the packet chain before we can send again
|
|
//
|
|
ASSERT (tpInfo->PdLength<tpInfo->SendPacketLength);
|
|
pd->PartialMessage = FALSE;
|
|
UPDATE_TPACKETS2 (TpIrp, "Continue building packet after read, cur len: 0x%lX",
|
|
tpInfo->PdLength);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdCompleteTPackets (
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is called when all activity on transmit IRP request is completed
|
|
and reference count drops to 0. It cleans up remaining resources and
|
|
completes the IRP or initiates endpoint reuse if so requested
|
|
Arguments:
|
|
|
|
Context - TransmitInfo associated with the request
|
|
Return Value:
|
|
|
|
None
|
|
--*/
|
|
{
|
|
|
|
do {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PIRP tpIrp = Context;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (tpIrp);
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = tpIrp->AssociatedIrp.SystemBuffer;
|
|
PAFD_ENDPOINT endpoint;
|
|
PIRP nextIrp = NULL;
|
|
|
|
ASSERT (AFD_GET_TPIC(tpIrp)->ReferenceCount==0);
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
ASSERT ((AFD_GET_TPIC(tpIrp)->StateFlags
|
|
& (AFD_TP_SEND_BUSY(0) |
|
|
AFD_TP_SEND_BUSY(1) |
|
|
AFD_TP_READ_BUSY)) == 0);
|
|
|
|
|
|
if (tpInfo!=NULL) {
|
|
LONG sendIrp;
|
|
KIRQL currentIrql;
|
|
currentIrql = KeGetCurrentIrql ();
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCompleteTPackets: tp_info-%p, irql-%x\n",
|
|
tpInfo, currentIrql));
|
|
}
|
|
|
|
UPDATE_TPACKETS2 (tpIrp, "CompleteTPackets @ irql 0x%lX", currentIrql);
|
|
|
|
//
|
|
// Cleanup what's in the TpInfo structure
|
|
//
|
|
if (tpInfo->HeadMdl!=NULL) {
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
tpInfo->TailPd = &tpInfo->HeadPd;
|
|
if (!AfdCleanupPacketChain (tpIrp, currentIrql<=APC_LEVEL)) {
|
|
ASSERT (currentIrql>APC_LEVEL);
|
|
AfdStartTPacketsWorker (AfdCompleteTPackets, tpIrp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup what remains in IRPs
|
|
//
|
|
for (sendIrp=0; sendIrp<tpInfo->NumSendIrps ; sendIrp++) {
|
|
if (tpInfo->SendIrp[sendIrp]!=NULL) {
|
|
if (tpInfo->SendIrp[sendIrp]->MdlAddress!=NULL) {
|
|
tpInfo->HeadMdl = tpInfo->SendIrp[sendIrp]->MdlAddress;
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
tpInfo->SendIrp[sendIrp]->MdlAddress = NULL;
|
|
tpInfo->HeadPd = tpInfo->SendIrp[sendIrp]->Overlay.AsynchronousParameters.UserApcContext;
|
|
tpInfo->TailPd = &tpInfo->HeadPd;
|
|
tpInfo->SendIrp[sendIrp]->Overlay.AsynchronousParameters.UserApcContext = NULL;
|
|
if (!AfdCleanupPacketChain (tpIrp, currentIrql<=APC_LEVEL)) {
|
|
ASSERT (currentIrql>APC_LEVEL);
|
|
AfdStartTPacketsWorker (AfdCompleteTPackets, tpIrp);
|
|
return;
|
|
}
|
|
|
|
}
|
|
tpInfo->SendIrp[sendIrp]->Cancel = FALSE; // So we can reuse it.
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free read IRP if we used one
|
|
//
|
|
if (tpInfo->ReadIrp!=NULL) {
|
|
IoFreeIrp (tpInfo->ReadIrp);
|
|
tpInfo->ReadIrp = NULL;
|
|
}
|
|
}
|
|
|
|
ASSERT (tpIrp->Tail.Overlay.ListEntry.Flink == NULL);
|
|
|
|
//
|
|
// If request succeeded and reuse is required, attempt to
|
|
// initiate it
|
|
//
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
if ( NT_SUCCESS(tpIrp->IoStatus.Status) &&
|
|
(AFD_GET_TPIC(tpIrp)->Flags & AFD_TF_REUSE_SOCKET) != 0 ) {
|
|
|
|
PAFD_CONNECTION connection;
|
|
|
|
IS_VC_ENDPOINT (endpoint);
|
|
|
|
//
|
|
// Check if we still have endpoint and connection intact
|
|
// under the lock. If this is not the case, we won't try
|
|
// to reuse it (it must have been closed or aborted).
|
|
//
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
if (connection!=NULL) {
|
|
|
|
ASSERT (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND ||
|
|
connection->Aborted);
|
|
//
|
|
// Remember that there is a transmit IRP pended on the endpoint,
|
|
// so that when we're freeing up the connection we also complete
|
|
// the transmit IRP.
|
|
//
|
|
|
|
connection->ClosePendedTransmit = TRUE;
|
|
|
|
//
|
|
// Since we are going to effectively close this connection,
|
|
// remember that we have started cleanup on this connection.
|
|
// This allows AfdDeleteConnectedReference to remove the
|
|
// connected reference when appropriate.
|
|
//
|
|
|
|
connection->CleanupBegun = TRUE;
|
|
|
|
//
|
|
// Delete the endpoint's reference to the connection in
|
|
// preparation for reusing this endpoint.
|
|
//
|
|
|
|
endpoint->Common.VcConnecting.Connection = NULL;
|
|
|
|
//
|
|
// This is to simplify debugging.
|
|
// If connection is not being closed by the transport
|
|
// we want to be able to find it in the debugger faster
|
|
// then thru !poolfind AfdC.
|
|
//
|
|
endpoint->WorkItem.Context = connection;
|
|
|
|
//
|
|
// Save pointer to connection in case disconnect needs
|
|
// to be aborted (thru abortive close of connection)
|
|
//
|
|
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = connection;
|
|
|
|
//
|
|
// We are going to free TPackets info since we are done
|
|
// with sends and no longer need this.
|
|
// This will also be our indication that we are in the
|
|
// reuse state (for the cancel routine).
|
|
//
|
|
tpIrp->AssociatedIrp.SystemBuffer = (PVOID)-1;
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
if ((AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_SEND_AND_DISCONNECT)
|
|
&& !connection->DisconnectIndicated
|
|
&& !connection->Aborted) {
|
|
ASSERT (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND);
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdDisconnectEventHandler (endpoint,
|
|
connection,
|
|
0, NULL, 0, NULL,
|
|
TDI_DISCONNECT_RELEASE
|
|
);
|
|
DEREFERENCE_CONNECTION2 (connection, "S&D disconnect", 0);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
//
|
|
// Attempt to remove the connected reference.
|
|
//
|
|
|
|
AfdDeleteConnectedReference( connection, TRUE );
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
DEREFERENCE_CONNECTION2 (connection,
|
|
"No S&D disconnect, flags: 0x%lX",
|
|
connection->ConnectionStateFlags);
|
|
}
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCompleteTPackets: tp_info-%p, initiating reuse\n",
|
|
tpInfo));
|
|
}
|
|
|
|
if (tpInfo!=NULL) {
|
|
AfdReturnTpInfo (tpInfo);
|
|
}
|
|
//
|
|
// DO NOT access the IRP after this point, since it may have
|
|
// been completed inside AfdDereferenceConnection!
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
UPDATE_ENDPOINT (endpoint);
|
|
}
|
|
|
|
//
|
|
// Check if we need to start or cancel another IRP to
|
|
// continue processing.
|
|
//
|
|
while (AFD_GET_TPIC(tpIrp)->Next!=NULL) {
|
|
nextIrp = AFD_GET_TPIRP(AFD_GET_TPIC(tpIrp)->Next);
|
|
if (endpoint->EndpointCleanedUp ||
|
|
(((AFD_GET_TPIC(nextIrp)->Flags & AFD_TF_DISCONNECT)==0 ||
|
|
nextIrp->AssociatedIrp.SystemBuffer!=NULL) &&
|
|
IS_VC_ENDPOINT (endpoint) &&
|
|
endpoint->Common.VcConnecting.Connection!=NULL &&
|
|
endpoint->Common.VcConnecting.Connection->Aborted) ) {
|
|
//
|
|
// Endpoint is being cleaned up or connection aborted,
|
|
// we attempt to cancel next IRP so that all of them
|
|
// are cleaned up eventually. Exception is pure disconnect
|
|
// IRP and endpoint is not cleaned up.
|
|
//
|
|
if ((AFD_GET_TPIC(nextIrp)->StateFlags & AFD_TP_QUEUED)!=0) {
|
|
AFD_GET_TPIC (nextIrp)->StateFlags &=~AFD_TP_QUEUED;
|
|
if ((AFD_GET_TPIC(nextIrp)->StateFlags & AFD_TP_SEND)!=0) {
|
|
AFD_GET_TPIC(tpIrp)->Next = AFD_GET_TPIC(nextIrp)->Next;
|
|
ASSERT (AFD_GET_TPIC(nextIrp)->ReferenceCount == 1);
|
|
AFD_GET_TPIC(nextIrp)->ReferenceCount = 0;
|
|
AFD_GET_TPIC(nextIrp)->StateFlags |= AFD_TP_ABORT_PENDING;
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdSendQueuedTPSend (endpoint, nextIrp);
|
|
nextIrp = NULL;
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
continue;
|
|
}
|
|
nextIrp->Cancel = TRUE;
|
|
}
|
|
else if (AfdGetTPacketsReference (nextIrp)) {
|
|
nextIrp->Cancel = TRUE;
|
|
ASSERT ((AFD_GET_TPIC (nextIrp)->StateFlags & AFD_TP_SEND)==0);
|
|
}
|
|
else {
|
|
//
|
|
// The IRP must already being completed, it will
|
|
// start the next one if necessary.
|
|
//
|
|
ASSERT ((AFD_GET_TPIC (nextIrp)->StateFlags & AFD_TP_SEND)==0);
|
|
nextIrp = NULL;
|
|
}
|
|
}
|
|
else if (endpoint->Irp==tpIrp &&
|
|
(AFD_GET_TPIC(nextIrp)->StateFlags & AFD_TP_QUEUED)!=0) {
|
|
//
|
|
// We have a nextIrp following the one we are completing.
|
|
// which is still queued and haven't been started
|
|
// - try to start it.
|
|
//
|
|
AFD_GET_TPIC(nextIrp)->StateFlags &= ~AFD_TP_QUEUED;
|
|
|
|
//
|
|
// If we finished all the sends, we should have started
|
|
// another IRP before.
|
|
//
|
|
ASSERT ((AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_SENDS_POSTED)==0);
|
|
//
|
|
// If nextIrp is a plain send IRP, we need to process it inline.
|
|
//
|
|
if ((AFD_GET_TPIC(nextIrp)->StateFlags & AFD_TP_SEND)!=0) {
|
|
AFD_GET_TPIC(tpIrp)->Next = AFD_GET_TPIC(nextIrp)->Next;
|
|
ASSERT (AFD_GET_TPIC(nextIrp)->ReferenceCount == 1);
|
|
AFD_GET_TPIC(nextIrp)->ReferenceCount = 0;
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdSendQueuedTPSend (endpoint, nextIrp);
|
|
nextIrp = NULL;
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
continue;
|
|
}
|
|
//
|
|
// This IRP couldn't have been counted towards
|
|
// active maximum.
|
|
//
|
|
ASSERT (nextIrp->Tail.Overlay.ListEntry.Blink == (PVOID)1);
|
|
}
|
|
else {
|
|
nextIrp = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remove the IRP being completed from the list
|
|
//
|
|
{
|
|
PIRP pIrp;
|
|
|
|
if (endpoint->Irp==tpIrp) {
|
|
endpoint->Irp = (AFD_GET_TPIC(tpIrp)->Next!=NULL)
|
|
? AFD_GET_TPIRP(AFD_GET_TPIC(tpIrp)->Next)
|
|
: NULL;
|
|
}
|
|
else {
|
|
pIrp = endpoint->Irp;
|
|
while (AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next)!=tpIrp)
|
|
pIrp = AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next);
|
|
AFD_GET_TPIC(pIrp)->Next = AFD_GET_TPIC(tpIrp)->Next;
|
|
}
|
|
}
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if ((AFD_GET_TPIC (tpIrp)->StateFlags & AFD_TP_SEND)!=0) {
|
|
//
|
|
// For plain send IRP we need to simmulate send completion
|
|
// as if it was failed by the transport driver.
|
|
// We can only get here if send IRP was cancelled.
|
|
//
|
|
ASSERT (!NT_SUCCESS (tpIrp->IoStatus.Status));
|
|
ASSERT (tpIrp->Tail.Overlay.ListEntry.Blink != NULL);
|
|
ASSERT (AFD_GET_TPIC(tpIrp)->StateFlags & AFD_TP_ABORT_PENDING);
|
|
ASSERT (tpInfo==NULL);
|
|
AfdSendQueuedTPSend (endpoint, tpIrp);
|
|
}
|
|
else {
|
|
BOOLEAN checkQueue;
|
|
|
|
if (IoSetCancelRoutine( tpIrp, NULL ) == NULL) {
|
|
KIRQL cancelIrql;
|
|
|
|
//
|
|
// The cancel routine has or is about to run. Synchonize with
|
|
// the cancel routine by acquiring and releasing the cancel
|
|
// and endpoint spinlocks. The cancel routine won't touch
|
|
// the IRP as it will see that its reference count is 0.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock (&cancelIrql);
|
|
ASSERT( tpIrp->Cancel );
|
|
IoReleaseCancelSpinLock (cancelIrql);
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
}
|
|
|
|
if (AFD_GET_TPIC(tpIrp)->Flags & AFD_TF_DISCONNECT) {
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
}
|
|
|
|
checkQueue = (BOOLEAN)(tpIrp->Tail.Overlay.ListEntry.Blink == NULL);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCompleteTPackets: tp_info-%p, completing IRP-%p\n",
|
|
tpInfo, tpIrp));
|
|
}
|
|
if (tpInfo!=NULL) {
|
|
tpIrp->AssociatedIrp.SystemBuffer = NULL;
|
|
AfdReturnTpInfo (tpInfo);
|
|
}
|
|
else {
|
|
ASSERT (tpIrp->AssociatedIrp.SystemBuffer==NULL);
|
|
}
|
|
|
|
|
|
UPDATE_ENDPOINT2 (endpoint, "Completing TP irp with status/bytes sent: 0x%lX",
|
|
NT_SUCCESS (tpIrp->IoStatus.Status)
|
|
? (ULONG)tpIrp->IoStatus.Information
|
|
: tpIrp->IoStatus.Status);
|
|
|
|
|
|
IoCompleteRequest( tpIrp, AfdPriorityBoost );
|
|
|
|
//
|
|
// If we're enforcing a maximum active TransmitFile count,
|
|
// and this Irp was counted towards active maximum, then
|
|
// check the list of queued TransmitFile requests and start the
|
|
// next one.
|
|
//
|
|
|
|
if( (AfdMaxActiveTransmitFileCount > 0) &&
|
|
checkQueue ) {
|
|
|
|
AfdStartNextQueuedTransmit();
|
|
|
|
}
|
|
}
|
|
|
|
if (nextIrp!=NULL) {
|
|
LONG result;
|
|
|
|
if (nextIrp->Cancel) {
|
|
//
|
|
// If endpoint being cleaned-up/aborted, just abort the IRP
|
|
// an dereference it.
|
|
//
|
|
AfdAbortTPackets (nextIrp, STATUS_CANCELLED);
|
|
DEREFERENCE_TPACKETS_R (nextIrp, result);
|
|
if (result==0) {
|
|
//
|
|
// Avoid recursion, execute the completion inline.
|
|
//
|
|
Context = nextIrp;
|
|
continue;
|
|
}
|
|
}
|
|
else if (nextIrp->AssociatedIrp.SystemBuffer!=NULL) {
|
|
if (AfdMaxActiveTransmitFileCount==0 ||
|
|
!AfdQueueTransmit (nextIrp)) {
|
|
UPDATE_ENDPOINT (endpoint);
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, nextIrp);
|
|
}
|
|
}
|
|
else {
|
|
UPDATE_ENDPOINT (endpoint);
|
|
AfdPerformTpDisconnect (nextIrp);
|
|
DEREFERENCE_TPACKETS_R (nextIrp, result);
|
|
if (result==0) {
|
|
Context = nextIrp;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
while (1);
|
|
}
|
|
|
|
VOID
|
|
AfdAbortTPackets (
|
|
PIRP TpIrp,
|
|
NTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used to stop the transmit file request in progress
|
|
and save the status which would be reported to the app as the
|
|
cause of failure
|
|
|
|
Arguments:
|
|
|
|
TransmitInfo - trasnmit info structure associated with the request
|
|
Status - status code for the error that caused the abort
|
|
Return Value:
|
|
|
|
None
|
|
|
|
|
|
--*/
|
|
{
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
LONG stateFlags, newStateFlags;
|
|
USHORT sendIrp;
|
|
|
|
do {
|
|
newStateFlags = stateFlags = AFD_GET_TPIC(TpIrp)->StateFlags;
|
|
if (newStateFlags & AFD_TP_ABORT_PENDING) {
|
|
return;
|
|
}
|
|
|
|
newStateFlags |= AFD_TP_ABORT_PENDING;
|
|
}
|
|
while (InterlockedCompareExchange (
|
|
(PLONG)&AFD_GET_TPIC(TpIrp)->StateFlags,
|
|
newStateFlags,
|
|
stateFlags)!=stateFlags);
|
|
if (NT_SUCCESS (TpIrp->IoStatus.Status)) {
|
|
TpIrp->IoStatus.Status = Status;
|
|
UPDATE_TPACKETS2 (TpIrp, "Abort with status: 0x%lX", Status);
|
|
}
|
|
|
|
if (tpInfo!=NULL) {
|
|
//
|
|
// Cancel any outstanding IRPs. It is safe to cancel IRPs even if
|
|
// they are already completed and before they are submitted
|
|
// (although we try to avoid doing this unnecessarily).
|
|
// Note that the completion pending flag can be set even
|
|
// before the irp is allocated, so check for NULL is important.
|
|
// However, after IRP is allocated and assigned, it is not freed
|
|
// until transmit packets is completed.
|
|
//
|
|
|
|
for (sendIrp=0; sendIrp<tpInfo->NumSendIrps; sendIrp++) {
|
|
if (tpInfo->SendIrp[sendIrp]!=NULL &&
|
|
AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_SEND_COMP_PENDING(sendIrp)) {
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAbortTPackets: tp_info-%p, canceling send irp1-%p\n",
|
|
tpInfo,
|
|
tpInfo->SendIrp[sendIrp]));
|
|
}
|
|
UPDATE_TPACKETS2 (TpIrp, "Aborting send irp 0x%lX", sendIrp);
|
|
IoCancelIrp (tpInfo->SendIrp[sendIrp]);
|
|
}
|
|
}
|
|
|
|
if (AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_READ_COMP_PENDING) {
|
|
do {
|
|
PIRP irp;
|
|
|
|
//
|
|
// Check if completion routine did not manage
|
|
// to reset this IRP (because it was part of
|
|
// AFD buffer structure - buffered reads case).
|
|
//
|
|
irp = tpInfo->ReadIrp;
|
|
ASSERT (irp!=(PVOID)-1);
|
|
if (irp==NULL) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set this field to a "special" value so that
|
|
// we know if we need to reset it back to previous
|
|
// value when we are done with the IRP or if completion
|
|
// rouine done this already.
|
|
//
|
|
else if (InterlockedCompareExchangePointer (
|
|
(PVOID *)&tpInfo->ReadIrp,
|
|
(PVOID)-1,
|
|
irp)==irp) {
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdAbortTPackets: tp_info-%p, canceling read irp-%p\n",
|
|
tpInfo,
|
|
irp));
|
|
}
|
|
UPDATE_TPACKETS2 (TpIrp, "Aborting read IRP", 0);
|
|
IoCancelIrp (irp);
|
|
|
|
//
|
|
// Reset the field to its original value
|
|
// unless completion routine already done this for us.
|
|
//
|
|
#if DBG
|
|
irp =
|
|
#endif
|
|
InterlockedCompareExchangePointer (
|
|
(PVOID *)&tpInfo->ReadIrp,
|
|
irp,
|
|
(PVOID)-1);
|
|
ASSERT (irp==NULL || irp==(PVOID)-1);
|
|
break;
|
|
}
|
|
}
|
|
while (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AfdCancelTPackets (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The cancel routine for transmit packets requests.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - ignored.
|
|
|
|
Irp - a pointer to the transmit packets IRP to cancel.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIO_STACK_LOCATION irpSp;
|
|
PAFD_ENDPOINT endpoint;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
AFD_LOCK_QUEUE_HANDLE transmitLockHandle;
|
|
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
//
|
|
// Initialize some locals and grab the endpoint spin lock.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
tpInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT (KeGetCurrentIrql ()==DISPATCH_LEVEL);
|
|
AfdAcquireSpinLockAtDpcLevel( &endpoint->SpinLock, &lockHandle);
|
|
|
|
//
|
|
// If this transmit IRP is on the TransmitFile queue, remove it.
|
|
//
|
|
|
|
AfdAcquireSpinLockAtDpcLevel( &AfdQueuedTransmitFileSpinLock,
|
|
&transmitLockHandle);
|
|
|
|
if (!(AFD_GET_TPIC (Irp)->StateFlags & AFD_TP_SEND) &&
|
|
Irp->Tail.Overlay.ListEntry.Flink != NULL ) {
|
|
|
|
//
|
|
// We can release cancel spinlock as we hold endpoint lock now.
|
|
// and we made sure that we have IRP reference (by nature of
|
|
// it being queued.
|
|
//
|
|
|
|
IoReleaseCancelSpinLock (DISPATCH_LEVEL);
|
|
|
|
ASSERT (tpInfo!=NULL && tpInfo!=(PVOID)-1);
|
|
|
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|
|
|
//
|
|
// Reset Flink to indicate that IRP is no longer in the queue
|
|
// Note that Blink is not reset so that completion routine knows
|
|
// that this IRP was not counted towards active maximum and thus
|
|
// new IRP should not be initiated when this one is being
|
|
// completed.
|
|
//
|
|
|
|
Irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
ASSERT (Irp->Tail.Overlay.ListEntry.Blink!=NULL);
|
|
|
|
AfdReleaseSpinLockFromDpcLevel( &AfdQueuedTransmitFileSpinLock, &transmitLockHandle );
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
KeLowerIrql (Irp->CancelIrql);
|
|
|
|
//
|
|
// Although we know that there is nothing to abort, we call
|
|
// this routine to set the status code in the IRP.
|
|
//
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCancelTPackets: Removed from the queue, tp_info-%p, irp-%p\n",
|
|
tpInfo, Irp));
|
|
}
|
|
//
|
|
// Remove initial reference
|
|
//
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
}
|
|
else {
|
|
KIRQL cancelIrql = Irp->CancelIrql;
|
|
|
|
AfdReleaseSpinLockFromDpcLevel( &AfdQueuedTransmitFileSpinLock,
|
|
&transmitLockHandle);
|
|
|
|
|
|
|
|
if ((AFD_GET_TPIC(Irp)->StateFlags & AFD_TP_QUEUED)!=0 ||
|
|
AfdGetTPacketsReference (Irp)) {
|
|
//
|
|
// We can release cancel spinlock as we hold endpoint lock now
|
|
// and we made sure that we have IRP reference (queued or explicit).
|
|
//
|
|
IoReleaseCancelSpinLock (DISPATCH_LEVEL);
|
|
|
|
if ((AFD_GET_TPIC(Irp)->StateFlags & AFD_TP_QUEUED)!=0) {
|
|
AFD_CLEAR_TP_FLAGS (Irp, AFD_TP_QUEUED);
|
|
}
|
|
else {
|
|
ASSERT ((AFD_GET_TPIC (Irp)->StateFlags & AFD_TP_SEND)==0);
|
|
}
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
if (cancelIrql!=DISPATCH_LEVEL) {
|
|
KeLowerIrql (cancelIrql);
|
|
}
|
|
|
|
//
|
|
// Transmit is still in progress or queued, perform the abort
|
|
//
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
|
|
//
|
|
// Remove extra reference that we either added above or
|
|
// the initial one in case of queued IRP.
|
|
//
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
}
|
|
else if (tpInfo==(PVOID)-1) {
|
|
//
|
|
// Endpoint is being disconnected and reused.
|
|
// Abort the connection and complete the IRP
|
|
//
|
|
BOOLEAN result;
|
|
PAFD_CONNECTION connection;
|
|
BOOLEAN checkQueue = (BOOLEAN)(Irp->Tail.Overlay.ListEntry.Blink == NULL);
|
|
|
|
|
|
//
|
|
// We can release cancel spinlock as we hold endpoint lock now
|
|
// and know for the fact that reuse code hasn't been executed
|
|
// yet (it takes endpoint spinlock and resets the SystemBuffer
|
|
// NULL).
|
|
//
|
|
IoReleaseCancelSpinLock (DISPATCH_LEVEL);
|
|
|
|
|
|
//
|
|
// Remove the IRP being completed from the list
|
|
// so that reuse routine won't find and complete it again
|
|
//
|
|
|
|
ASSERT (AFD_GET_TPIC(Irp)->Next==NULL);
|
|
if (endpoint->Irp==Irp) {
|
|
endpoint->Irp = NULL;
|
|
}
|
|
else {
|
|
PIRP pIrp;
|
|
pIrp = endpoint->Irp;
|
|
while (AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next)!=Irp)
|
|
pIrp = AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next);
|
|
AFD_GET_TPIC(pIrp)->Next = NULL;
|
|
}
|
|
|
|
//
|
|
// Reset the pointer so we do not confuse the IO subsystem
|
|
//
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
|
|
//
|
|
// Abort the connection if disconnect isn't already completing
|
|
// (check for that by checking ref count on connection. If
|
|
// already 0, then we don't need to do anything)
|
|
//
|
|
// We stored a pointer to the connection in Type3InputBuffer
|
|
// We are guaranteed that connection structure is still around
|
|
// since this IRP is still around.
|
|
//
|
|
connection = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
ASSERT(connection != NULL);
|
|
|
|
CHECK_REFERENCE_CONNECTION (connection, result);
|
|
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
|
|
if (result) {
|
|
//
|
|
// Abort the connection; that will trigger cleanup of any
|
|
// other operations on this endpoint
|
|
//
|
|
|
|
AfdAbortConnection( connection ); // dereferences connection
|
|
}
|
|
|
|
if (Irp->CancelIrql!=DISPATCH_LEVEL) {
|
|
KeLowerIrql (Irp->CancelIrql);
|
|
}
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCancelTPackets: Completing, irp-%p\n",
|
|
Irp));
|
|
}
|
|
|
|
UPDATE_ENDPOINT (endpoint);
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest (Irp, AfdPriorityBoost);
|
|
|
|
|
|
//
|
|
// If we're enforcing a maximum active TransmitFile count,
|
|
// and this Irp was counted towards active maximum, then
|
|
// check the list of queued TransmitFile requests and start the
|
|
// next one.
|
|
//
|
|
|
|
if( AfdMaxActiveTransmitFileCount > 0 && checkQueue) {
|
|
|
|
AfdStartNextQueuedTransmit();
|
|
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Everything is done anyway, let go.
|
|
//
|
|
AfdReleaseSpinLockFromDpcLevel (&endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// We can release cancel spinlock as we hold endpoint lock now
|
|
// and we made sure that we have IRP reference (queued or explicit).
|
|
//
|
|
IoReleaseCancelSpinLock (cancelIrql);
|
|
}
|
|
}
|
|
|
|
} // AfdCancelTPackets
|
|
|
|
|
|
|
|
VOID
|
|
AfdCompleteClosePendedTPackets (
|
|
PAFD_ENDPOINT Endpoint
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes a transmit IRP that was waiting for the connection to be
|
|
completely disconnected.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - the endpoint on which the transmit request is pending.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PIRP tpIrp;
|
|
BOOLEAN checkQueue;
|
|
|
|
AfdAcquireSpinLock( &Endpoint->SpinLock, &lockHandle );
|
|
|
|
//
|
|
// First make sure that thre is really a reuse request pended on
|
|
// this endpoint. We do this while holding the appropriate locks
|
|
// to close the timing window that would exist otherwise, since
|
|
// the caller may not have had the locks when making the test.
|
|
//
|
|
|
|
tpIrp = Endpoint->Irp;
|
|
while (tpIrp!=NULL && tpIrp->AssociatedIrp.SystemBuffer != (PVOID)-1) {
|
|
tpIrp = AFD_GET_TPIRP(AFD_GET_TPIC(tpIrp)->Next);
|
|
}
|
|
|
|
if ( tpIrp == NULL ||
|
|
tpIrp->AssociatedIrp.SystemBuffer != (PVOID)-1) {
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCompleteClosePendedTPackets: Irp is gone, endpoint-%p",
|
|
Endpoint));
|
|
}
|
|
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Reset the system buffer so we don't confuse the IO
|
|
// subsystem and synchronize with the cancel routine.
|
|
//
|
|
tpIrp->AssociatedIrp.SystemBuffer = NULL;
|
|
|
|
//
|
|
// Remove the IRP being completed from the list
|
|
//
|
|
ASSERT (AFD_GET_TPIC(tpIrp)->Next==NULL);
|
|
if (Endpoint->Irp==tpIrp) {
|
|
Endpoint->Irp = NULL;
|
|
}
|
|
else {
|
|
PIRP pIrp;
|
|
pIrp = Endpoint->Irp;
|
|
while (AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next)!=tpIrp)
|
|
pIrp = AFD_GET_TPIRP(AFD_GET_TPIC(pIrp)->Next);
|
|
AFD_GET_TPIC(pIrp)->Next = NULL;
|
|
}
|
|
|
|
//
|
|
// Make sure to refresh the endpoint BEFORE completing the transmit
|
|
// IRP. This is because the user-mode caller may reuse the endpoint
|
|
// as soon as the IRP completes, and there would be a timing window
|
|
// between reuse of the endpoint and the refresh otherwise.
|
|
//
|
|
|
|
AfdRefreshEndpoint( Endpoint );
|
|
|
|
//
|
|
// Release the lock before completing the transmit IRP--it is
|
|
// illegal to call IoCompleteRequest while holding a spin lock.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &Endpoint->SpinLock, &lockHandle );
|
|
AFD_END_STATE_CHANGE (Endpoint);
|
|
|
|
//
|
|
// Reset the cancel routine in the IRP before attempting to complete
|
|
// it.
|
|
//
|
|
|
|
if ( IoSetCancelRoutine( tpIrp, NULL ) == NULL ) {
|
|
KIRQL cancelIrql;
|
|
//
|
|
// The cancel routine has or is about to run. Synchonize with
|
|
// the cancel routine by acquiring and releasing the cancel
|
|
// and endpoint spinlocks. The cancel routine won't touch
|
|
// the IRP as it will see that tpInfo pointer was reset in the
|
|
// IRP.
|
|
//
|
|
IoAcquireCancelSpinLock (&cancelIrql);
|
|
ASSERT( tpIrp->Cancel );
|
|
IoReleaseCancelSpinLock (cancelIrql);
|
|
|
|
}
|
|
|
|
ASSERT (tpIrp->IoStatus.Status==STATUS_SUCCESS);
|
|
|
|
|
|
checkQueue = (BOOLEAN)(tpIrp->Tail.Overlay.ListEntry.Blink == NULL);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdCompleteClosePendedTPackets: Completing irp-%p",
|
|
tpIrp));
|
|
}
|
|
//
|
|
// Finally, we can complete the transmit request.
|
|
//
|
|
UPDATE_ENDPOINT(Endpoint);
|
|
|
|
IoCompleteRequest( tpIrp, AfdPriorityBoost );
|
|
|
|
//
|
|
// If we're enforcing a maximum active TransmitFile count,
|
|
// and this Irp was counted towards active maximum, then
|
|
// check the list of queued TransmitFile requests and start the
|
|
// next one.
|
|
//
|
|
|
|
if( (AfdMaxActiveTransmitFileCount > 0) &&
|
|
checkQueue) {
|
|
|
|
AfdStartNextQueuedTransmit();
|
|
|
|
}
|
|
|
|
} // AfdCompleteClosePendedTPackets
|
|
|
|
#ifdef TDI_SERVICE_SEND_AND_DISCONNECT
|
|
BOOLEAN
|
|
AfdTPacketsEnableSendAndDisconnect (
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if combined send and disconnect can be used and update
|
|
endpoint state appropriately
|
|
|
|
Arguments:
|
|
|
|
TpIrp - transmit packets IRP
|
|
|
|
Return Value:
|
|
|
|
TRUE - S&D can be used, endpoint state updated.
|
|
FALSE - no, use normal disconnect (which updates the state by itself).
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (TpIrp);
|
|
PAFD_ENDPOINT endpoint;
|
|
BOOLEAN res = FALSE;
|
|
|
|
endpoint = irpSp->FileObject->FsContext;
|
|
if ( AfdUseTdiSendAndDisconnect &&
|
|
(AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_REUSE_SOCKET) &&
|
|
(endpoint->TdiServiceFlags & TDI_SERVICE_SEND_AND_DISCONNECT)) {
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
ASSERT (IS_VC_ENDPOINT (endpoint));
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
if (!(endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) &&
|
|
endpoint->Common.VcConnecting.Connection!=NULL &&
|
|
endpoint->Common.VcConnecting.Connection->ConnectDataBuffers==NULL
|
|
) {
|
|
endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_SEND;
|
|
res = TRUE;
|
|
UPDATE_TPACKETS2 (TpIrp, "Enabling S&D", 0);
|
|
}
|
|
else {
|
|
UPDATE_TPACKETS2 (TpIrp, "Disabling S&D, disconnect mode: 0x%lX",
|
|
endpoint->DisconnectMode);
|
|
}
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
}
|
|
else {
|
|
UPDATE_TPACKETS2 (TpIrp,
|
|
"Not enabling S&D, flags: 0x%lX",
|
|
AFD_GET_TPIC(TpIrp)->Flags);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
#endif // TDI_SERVICE_SEND_AND_DISCONNECT
|
|
|
|
BOOLEAN
|
|
AfdQueueTransmit (
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check transmit IRP can be process immediately or needs to be put
|
|
in the queue because of exceeded simmulteneous send limit
|
|
|
|
Arguments:
|
|
|
|
Irp - TransmitIrp
|
|
Return Value:
|
|
|
|
TRUE - Irp was queued (or just completed since it was cancelled before), can't send
|
|
FALSE - We are below the limit, go ahead and send.
|
|
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
AfdAcquireSpinLock (&AfdQueuedTransmitFileSpinLock, &lockHandle);
|
|
|
|
if (Irp->Cancel) {
|
|
ASSERT (Irp->Tail.Overlay.ListEntry.Flink==NULL);
|
|
ASSERT (Irp->Tail.Overlay.ListEntry.Blink!=NULL);
|
|
AfdReleaseSpinLock (&AfdQueuedTransmitFileSpinLock, &lockHandle);
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
return TRUE;
|
|
}
|
|
else if( AfdActiveTransmitFileCount >= AfdMaxActiveTransmitFileCount ) {
|
|
|
|
InsertTailList(
|
|
&AfdQueuedTransmitFileListHead,
|
|
&Irp->Tail.Overlay.ListEntry
|
|
);
|
|
UPDATE_TPACKETS2 (Irp, "Queuing, current count: 0x%lX", AfdActiveTransmitFileCount);
|
|
|
|
AfdReleaseSpinLock (&AfdQueuedTransmitFileSpinLock, &lockHandle);
|
|
UPDATE_ENDPOINT (IoGetCurrentIrpStackLocation (Irp)->FileObject->FsContext);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdQueueTransmit: Queuing Irp-%p,endpoint-%p,tp_info-%p\n",
|
|
Irp,
|
|
IoGetCurrentIrpStackLocation (Irp)->FileObject->FsContext,
|
|
Irp->AssociatedIrp.SystemBuffer));
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
|
|
AfdActiveTransmitFileCount++;
|
|
|
|
ASSERT (Irp->Tail.Overlay.ListEntry.Flink==NULL);
|
|
//
|
|
// Mark the IRP as counted towards maximum (so we start the next
|
|
// one when it is completed);
|
|
//
|
|
Irp->Tail.Overlay.ListEntry.Blink = NULL;
|
|
AfdReleaseSpinLock (&AfdQueuedTransmitFileSpinLock, &lockHandle);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdStartNextQueuedTransmit(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Starts next transmit file in the queue if the number of pending
|
|
request drops below maximum
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PLIST_ENTRY listEntry;
|
|
PIRP irp;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
|
|
//
|
|
// This should only be called if we're actually enforcing a maximum
|
|
// TransmitFile count.
|
|
//
|
|
|
|
ASSERT( AfdMaxActiveTransmitFileCount > 0 );
|
|
|
|
//
|
|
// The TransmitFile request queue is protected by a
|
|
// spinlock, so grab that lock before examining the queue.
|
|
//
|
|
|
|
AfdAcquireSpinLock( &AfdQueuedTransmitFileSpinLock, &lockHandle );
|
|
|
|
//
|
|
// This routine is only called after a pended TransmitFile IRP
|
|
// completes, so account for that completion here.
|
|
//
|
|
|
|
ASSERT( AfdActiveTransmitFileCount > 0 );
|
|
AfdActiveTransmitFileCount--;
|
|
|
|
if( !IsListEmpty( &AfdQueuedTransmitFileListHead ) ) {
|
|
|
|
//
|
|
// Dequeue exactly one IRP from the list, then start the
|
|
// TransmitFile.
|
|
//
|
|
|
|
listEntry = RemoveHeadList(
|
|
&AfdQueuedTransmitFileListHead
|
|
);
|
|
|
|
irp = CONTAINING_RECORD(
|
|
listEntry,
|
|
IRP,
|
|
Tail.Overlay.ListEntry
|
|
);
|
|
|
|
tpInfo = irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT( tpInfo != NULL );
|
|
|
|
//
|
|
// Mark this TransmitFile request as no longer queued.
|
|
// and counted towards active maximum.
|
|
//
|
|
|
|
irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
irp->Tail.Overlay.ListEntry.Blink = NULL;
|
|
|
|
//
|
|
// Adjust the count, release the spinlock, then queue the
|
|
// TransmitFile.
|
|
//
|
|
|
|
AfdActiveTransmitFileCount++;
|
|
ASSERT( AfdActiveTransmitFileCount <= AfdMaxActiveTransmitFileCount );
|
|
|
|
|
|
UPDATE_TPACKETS2 (irp,"Restarting from queue, count: 0x%lX", AfdActiveTransmitFileCount);
|
|
AfdReleaseSpinLock( &AfdQueuedTransmitFileSpinLock, &lockHandle );
|
|
|
|
ASSERT (irp->AssociatedIrp.SystemBuffer!=NULL);
|
|
UPDATE_ENDPOINT (IoGetCurrentIrpStackLocation (irp)->FileObject->FsContext);
|
|
//
|
|
// Schedule the worker for the transmit.
|
|
//
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, irp);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the spinlock before returning.
|
|
//
|
|
|
|
AfdReleaseSpinLock( &AfdQueuedTransmitFileSpinLock, &lockHandle );
|
|
}
|
|
|
|
} // AfdStartNextQueuedTransmit
|
|
|
|
|
|
BOOLEAN
|
|
AfdEnqueueTPacketsIrp (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if transmit IRP can be process immediately or needs to be put
|
|
in the queue because there is already an active transmit IRP on the
|
|
endpoint.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - endpoint to check
|
|
Irp - TransmitIrp
|
|
Return Value:
|
|
|
|
TRUE - Irp was queued, can't send
|
|
FALSE - There are no other IRPs on the endpoint, can send now.
|
|
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
PIRP oldIrp;
|
|
BOOLEAN busy = FALSE;
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// Use interlocked operation since another thread can claim the
|
|
// spot without spinlock if it is NULL.
|
|
// Note, that the IRP field cannot be cleared outside of spinlock
|
|
// or changed if it is not NULL..
|
|
//
|
|
oldIrp = InterlockedCompareExchangePointer (
|
|
(PVOID *)&Endpoint->Irp,
|
|
TpIrp,
|
|
NULL
|
|
);
|
|
if (oldIrp!=NULL) {
|
|
//
|
|
// Scan till the end of the list.
|
|
//
|
|
while (AFD_GET_TPIC(oldIrp)->Next!=NULL) {
|
|
oldIrp = AFD_GET_TPIRP(AFD_GET_TPIC(oldIrp)->Next);
|
|
}
|
|
//
|
|
// Use interlocked operation to update the pointer
|
|
// to ensure ordering of the memory accesses when we
|
|
// check this field after setting the send flag
|
|
//
|
|
InterlockedExchangePointer (
|
|
(PVOID *)&AFD_GET_TPIC (oldIrp)->Next,
|
|
AFD_GET_TPIC(TpIrp));
|
|
|
|
//
|
|
// Another IRP is still pending, check if there are still more sends
|
|
// in that IRP.
|
|
//
|
|
if ((AFD_GET_TPIC(oldIrp)->StateFlags & AFD_TP_SENDS_POSTED)==0) {
|
|
IoSetCancelRoutine (TpIrp, AfdCancelTPackets);
|
|
if (!TpIrp->Cancel && !Endpoint->EndpointCleanedUp) {
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
AFD_GET_TPIC (TpIrp)->StateFlags |= AFD_TP_QUEUED;
|
|
busy = TRUE;
|
|
}
|
|
else {
|
|
TpIrp->Cancel = TRUE;
|
|
}
|
|
}
|
|
}
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
return busy;
|
|
}
|
|
|
|
VOID
|
|
AfdStartNextTPacketsIrp (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if there are other IRPs enqueued behind the one we about
|
|
to finish with (perform last send) and start it.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - endpoint to check
|
|
Irp - TransmitIrp
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
ASSERT (AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_SENDS_POSTED);
|
|
while (AFD_GET_TPIC (TpIrp)->Next!=NULL) {
|
|
PIRP nextIrp = AFD_GET_TPIRP(AFD_GET_TPIC(TpIrp)->Next);
|
|
if ((AFD_GET_TPIC(nextIrp)->StateFlags & AFD_TP_QUEUED)!=0) {
|
|
//
|
|
// Mark IRP is not queued anymore and process it.
|
|
//
|
|
AFD_GET_TPIC(nextIrp)->StateFlags &= ~AFD_TP_QUEUED;
|
|
//
|
|
// If newIrp is a plain send IRP, we need to process it inline.
|
|
//
|
|
if ((AFD_GET_TPIC (nextIrp)->StateFlags & AFD_TP_SEND)!=0) {
|
|
AFD_GET_TPIC(TpIrp)->Next = AFD_GET_TPIC(nextIrp)->Next;
|
|
ASSERT (AFD_GET_TPIC(nextIrp)->ReferenceCount==1);
|
|
AFD_GET_TPIC(nextIrp)->ReferenceCount = 0;
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
AfdSendQueuedTPSend (Endpoint, nextIrp);
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
}
|
|
else {
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// This IRP couldn't have been counted towards
|
|
// active maximum.
|
|
//
|
|
ASSERT (nextIrp->Tail.Overlay.ListEntry.Blink == (PVOID)1);
|
|
if (nextIrp->AssociatedIrp.SystemBuffer!=NULL) {
|
|
if( AfdMaxActiveTransmitFileCount == 0 ||
|
|
!AfdQueueTransmit (nextIrp)) {
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
//
|
|
// Start I/O
|
|
//
|
|
|
|
AfdStartTPacketsWorker (AfdTPacketsWorker, nextIrp);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// We never count DisconnectEx towards active maximum.
|
|
// Just queue the disconnect.
|
|
//
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
AfdPerformTpDisconnect (nextIrp);
|
|
DEREFERENCE_TPACKETS (nextIrp);
|
|
}
|
|
return ;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// This IRP is probably being cancelled for some reason.
|
|
// Move to the next one.
|
|
//
|
|
ASSERT ((AFD_GET_TPIC(nextIrp)->StateFlags &
|
|
(AFD_TP_SEND|AFD_TP_AFD_SEND))==0);
|
|
TpIrp = nextIrp;
|
|
}
|
|
}
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
return ;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AfdEnqueueTpSendIrp (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP SendIrp,
|
|
BOOLEAN AfdIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if send IRP can be processed immediately or needs to be put
|
|
in the queue because there is already an active transmit IRP on the
|
|
endpoint.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - endpoint to check
|
|
Irp - SendIrp
|
|
AfdIrp - TRUE if IRP was allocated internally by afd
|
|
Return Value:
|
|
|
|
TRUE - Irp was queued, can't send
|
|
FALSE - There are no other IRPs on the endpoint, can send now.
|
|
|
|
--*/
|
|
{
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
BOOLEAN busy = FALSE;
|
|
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
//
|
|
// We do not have to use interlocked operation here since we
|
|
// do not synchronize when someone submits send and tpackets
|
|
// from two different threads concurrently.
|
|
//
|
|
if (!Endpoint->EndpointCleanedUp && Endpoint->Irp!=NULL) {
|
|
PIRP oldIrp;
|
|
|
|
oldIrp = Endpoint->Irp;
|
|
//
|
|
// Scan till the end of the list.
|
|
//
|
|
while (AFD_GET_TPIC(oldIrp)->Next!=NULL) {
|
|
oldIrp = AFD_GET_TPIRP(AFD_GET_TPIC(oldIrp)->Next);
|
|
}
|
|
|
|
//
|
|
// Another IRP is still pending, check if there are still more sends
|
|
// in that IRP.
|
|
//
|
|
if ((AFD_GET_TPIC(oldIrp)->StateFlags & AFD_TP_SENDS_POSTED)==0) {
|
|
AFD_GET_TPIC(SendIrp)->Next = NULL;
|
|
AFD_GET_TPIC(SendIrp)->Flags = 0;
|
|
AFD_GET_TPIC(SendIrp)->ReferenceCount = 1;
|
|
AFD_GET_TPIC(SendIrp)->StateFlags = AFD_TP_QUEUED| AFD_TP_SEND |
|
|
(AfdIrp ? AFD_TP_AFD_SEND : 0);
|
|
//
|
|
// Check application IRP for cancellation. AFD IRP can never
|
|
// be cancelled since they don't have cancel routine installed.
|
|
//
|
|
if (!AfdIrp) {
|
|
IoMarkIrpPending (SendIrp);
|
|
IoSetCancelRoutine (SendIrp, AfdCancelTPackets);
|
|
if (SendIrp->Cancel) {
|
|
//
|
|
// IRP was cancelled, send routine will just complete it.
|
|
//
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
AfdSendQueuedTPSend (Endpoint, SendIrp);
|
|
return TRUE;
|
|
}
|
|
}
|
|
//
|
|
// Use interlocked operation to update the pointer
|
|
// to ensure ordering of the memory accesses when we
|
|
// check this field after setting the send flag
|
|
//
|
|
InterlockedExchangePointer (
|
|
(PVOID *)&AFD_GET_TPIC (oldIrp)->Next,
|
|
AFD_GET_TPIC(SendIrp));
|
|
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
busy = TRUE;
|
|
}
|
|
}
|
|
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
return busy;
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdSendQueuedTPSend (
|
|
PAFD_ENDPOINT Endpoint,
|
|
PIRP SendIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sumbits the Send IRP in the TPackets IRP queue to the transport.
|
|
Just completes it, if it is canceled or endpoint is cleaned up.
|
|
|
|
Arguments:
|
|
|
|
Endpoint - endpoint to check
|
|
SendIrp - SendIrp
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
cancelRoutine = IoSetCancelRoutine (SendIrp, NULL);
|
|
ASSERT (cancelRoutine==NULL ||
|
|
(AFD_GET_TPIC(SendIrp)->StateFlags & AFD_TP_AFD_SEND)==0);
|
|
|
|
if (SendIrp->Cancel ||
|
|
Endpoint->EndpointCleanedUp ||
|
|
(AFD_GET_TPIC(SendIrp)->StateFlags & AFD_TP_ABORT_PENDING)) {
|
|
//
|
|
// If IRP is being cancelled, synchronize with cancel routine
|
|
//
|
|
if (SendIrp->Cancel) {
|
|
KIRQL cancelIrql;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
//
|
|
// AFD IRPs cannot be cancelled - don't have cancel routine and
|
|
// not inserted in thread lists.
|
|
//
|
|
ASSERT ((AFD_GET_TPIC(SendIrp)->StateFlags & AFD_TP_AFD_SEND)==0);
|
|
IoAcquireCancelSpinLock (&cancelIrql);
|
|
IoReleaseCancelSpinLock (cancelIrql);
|
|
AfdAcquireSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
AfdReleaseSpinLock (&Endpoint->SpinLock, &lockHandle);
|
|
}
|
|
SendIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
SendIrp->IoStatus.Information = 0;
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
#if DBG
|
|
if ((AFD_GET_TPIC(SendIrp)->StateFlags &AFD_TP_AFD_SEND)==0) {
|
|
PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation (SendIrp);
|
|
if (!AfdRecordOutstandingIrpDebug (Endpoint,
|
|
irpSp->DeviceObject,
|
|
SendIrp,
|
|
__FILE__,
|
|
__LINE__)) {
|
|
return ;
|
|
}
|
|
}
|
|
#endif
|
|
IoSetNextIrpStackLocation (SendIrp);
|
|
IoCompleteRequest (SendIrp, AfdPriorityBoost);
|
|
}
|
|
else {
|
|
PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation (SendIrp);
|
|
UPDATE_ENDPOINT (Endpoint);
|
|
if ((AFD_GET_TPIC(SendIrp)->StateFlags &AFD_TP_AFD_SEND)==0) {
|
|
AfdIoCallDriver (Endpoint, irpSp->DeviceObject, SendIrp);
|
|
}
|
|
else {
|
|
IoCallDriver (irpSp->DeviceObject, SendIrp);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AfdStartTPacketsWorker (
|
|
PWORKER_THREAD_ROUTINE WorkerRoutine,
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Posts work item to be executed at IRQL above DPC_LEVEL so that
|
|
file system can be accessed. It uses one of three methods
|
|
of doing this: special kernel APC, normal kernel APC, or
|
|
system thread (work queue item).
|
|
|
|
Arguments:
|
|
|
|
WorkerRoutine - routine to execute
|
|
TransmitInfo - associated transmit info (also parameter to
|
|
the worker routine).
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = TpIrp->AssociatedIrp.SystemBuffer;
|
|
ASSERT ((AFD_GET_TPIC(TpIrp)->StateFlags & AFD_TP_WORKER_SCHEDULED)
|
|
|| (AFD_GET_TPIC(TpIrp)->ReferenceCount==0));
|
|
|
|
switch (AFD_GET_TPIC(TpIrp)->Flags & AFD_TF_WORKER_KIND_MASK) {
|
|
case AFD_TF_USE_KERNEL_APC:
|
|
//
|
|
// Initialize normal APC but with normal routine set
|
|
// to special value so we know to run the worker
|
|
// inside the special routine of normal APC and queue it
|
|
//
|
|
KeInitializeApc (&tpInfo->Apc,
|
|
PsGetThreadTcb (TpIrp->Tail.Overlay.Thread),
|
|
TpIrp->ApcEnvironment,
|
|
AfdTPacketsApcKernelRoutine,
|
|
AfdTPacketsApcRundownRoutine,
|
|
(PKNORMAL_ROUTINE)-1,
|
|
KernelMode,
|
|
NULL
|
|
);
|
|
if (KeInsertQueueApc (&tpInfo->Apc,
|
|
(PVOID)WorkerRoutine,
|
|
TpIrp,
|
|
AfdPriorityBoost))
|
|
return;
|
|
//
|
|
// If APC cannot be inserted into the queue, drop
|
|
// to use the system worker thread
|
|
//
|
|
break;
|
|
case AFD_TF_USE_SYSTEM_THREAD:
|
|
//
|
|
// This is the default case which is also used if everything else fails,
|
|
// so just break out.
|
|
//
|
|
break;
|
|
default:
|
|
ASSERT (!"Uknown worker type!");
|
|
__assume (0);
|
|
}
|
|
|
|
ExInitializeWorkItem (&tpInfo->WorkItem,
|
|
WorkerRoutine,
|
|
TpIrp
|
|
);
|
|
ExQueueWorkItem (&tpInfo->WorkItem, DelayedWorkQueue);
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdTPacketsApcKernelRoutine (
|
|
IN struct _KAPC *Apc,
|
|
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN OUT PVOID *NormalContext,
|
|
IN OUT PVOID *SystemArgument1,
|
|
IN OUT PVOID *SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Special kernel apc routine. Executed in the context of
|
|
the target thread at APC_LEVEL
|
|
|
|
Arguments:
|
|
NormalRoutine - pointer containing address of normal routine (it will
|
|
be NULL for special kernel APC and not NULL for normal
|
|
kernel APC)
|
|
|
|
SystemArgument1 - pointer to the address of worker routine to execute
|
|
SystemArgument2 - pointer to the argument to pass to worker routine
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER (NormalContext);
|
|
#if DBG
|
|
try {
|
|
ASSERT (CONTAINING_RECORD (Apc,AFD_TPACKETS_INFO_INTERNAL,Apc)
|
|
==((PIRP)*SystemArgument2)->AssociatedIrp.SystemBuffer);
|
|
#else
|
|
UNREFERENCED_PARAMETER (Apc);
|
|
#endif
|
|
|
|
//
|
|
// Normal APC, but we are requested to run in its special
|
|
// routine which avoids raising and lowering IRQL
|
|
//
|
|
ASSERT (*NormalRoutine==(PKNORMAL_ROUTINE)-1);
|
|
*NormalRoutine = NULL;
|
|
((PWORKER_THREAD_ROUTINE)(ULONG_PTR)*SystemArgument1) (*SystemArgument2);
|
|
#if DBG
|
|
}
|
|
except (AfdApcExceptionFilter (GetExceptionInformation (),
|
|
__FILE__,
|
|
__LINE__)) {
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdTPacketsApcRundownRoutine (
|
|
IN struct _KAPC *Apc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
APC rundown routine. Executed if APC cannot be delivered for
|
|
some reason (thread exiting).
|
|
We just fall back to system threads to execute the worker
|
|
|
|
Arguments:
|
|
|
|
Apc - APC structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP tpIrp;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
PWORKER_THREAD_ROUTINE workerRoutine;
|
|
#if DBG
|
|
try {
|
|
#endif
|
|
|
|
workerRoutine = (PWORKER_THREAD_ROUTINE)(ULONG_PTR)Apc->SystemArgument1;
|
|
tpIrp = Apc->SystemArgument2;
|
|
tpInfo = tpIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
ASSERT (tpInfo==CONTAINING_RECORD (Apc,AFD_TPACKETS_INFO_INTERNAL,Apc));
|
|
|
|
ExInitializeWorkItem (&tpInfo->WorkItem,
|
|
workerRoutine,
|
|
tpIrp
|
|
);
|
|
ExQueueWorkItem (&tpInfo->WorkItem, DelayedWorkQueue);
|
|
#if DBG
|
|
}
|
|
except (AfdApcExceptionFilter (GetExceptionInformation (),
|
|
__FILE__,
|
|
__LINE__)) {
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AfdGetTPacketsReference (
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Obtain a reference to the TPackets IRP if it is not 0.
|
|
|
|
Arguments:
|
|
|
|
Irp - IRP to reference
|
|
|
|
Return Value:
|
|
|
|
TRUE - succeeded
|
|
FALSE - IRP is on the completion path, no need to reference.
|
|
|
|
--*/
|
|
{
|
|
LONG count;
|
|
|
|
//
|
|
// Only if transmit info is not completed yet, cancel all the IRPs
|
|
// We release the spinlock while cancelling the IRP, so we need
|
|
// to make sure that one of the cancelled IRPs won't initiate
|
|
// completion while we trying to cancel the other IRPs
|
|
//
|
|
do {
|
|
count = AFD_GET_TPIC(Irp)->ReferenceCount;
|
|
if (count==0) {
|
|
break;
|
|
}
|
|
}
|
|
while (InterlockedCompareExchange ((PLONG)
|
|
&AFD_GET_TPIC(Irp)->ReferenceCount,
|
|
(count+1),
|
|
count)!=count);
|
|
|
|
return (BOOLEAN)(count!=0);
|
|
}
|
|
|
|
//
|
|
// Debug reference/dereference code, validates reference count
|
|
// and saves tracing information.
|
|
//
|
|
#if REFERENCE_DEBUG
|
|
VOID
|
|
AfdReferenceTPackets (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
)
|
|
{
|
|
LONG count;
|
|
|
|
do {
|
|
count = AFD_GET_TPIC(Irp)->ReferenceCount;
|
|
ASSERT (count>0);
|
|
}
|
|
while (InterlockedCompareExchange ((PLONG)
|
|
&AFD_GET_TPIC(Irp)->ReferenceCount,
|
|
(count+1),
|
|
count)!=count);
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer) {
|
|
AFD_UPDATE_REFERENCE_DEBUG (
|
|
(PAFD_TPACKETS_INFO_INTERNAL)Irp->AssociatedIrp.SystemBuffer,
|
|
count+1,
|
|
LocationId,
|
|
Param);
|
|
}
|
|
|
|
}
|
|
|
|
LONG
|
|
AfdDereferenceTPackets (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
)
|
|
{
|
|
LONG count;
|
|
|
|
if (Irp->AssociatedIrp.SystemBuffer) {
|
|
AFD_UPDATE_REFERENCE_DEBUG (
|
|
(PAFD_TPACKETS_INFO_INTERNAL)Irp->AssociatedIrp.SystemBuffer,
|
|
AFD_GET_TPIC(Irp)->ReferenceCount-1,
|
|
LocationId,
|
|
Param);
|
|
}
|
|
|
|
count = InterlockedDecrement ((PLONG)
|
|
&AFD_GET_TPIC(Irp)->ReferenceCount);
|
|
ASSERT (count>=0);
|
|
return count;
|
|
}
|
|
|
|
VOID
|
|
AfdUpdateTPacketsTrack (
|
|
IN PIRP Irp,
|
|
IN LONG LocationId,
|
|
IN ULONG Param
|
|
)
|
|
{
|
|
if (Irp->AssociatedIrp.SystemBuffer) {
|
|
AFD_UPDATE_REFERENCE_DEBUG (
|
|
(PAFD_TPACKETS_INFO_INTERNAL)Irp->AssociatedIrp.SystemBuffer,
|
|
AFD_GET_TPIC(Irp)->ReferenceCount,
|
|
LocationId,
|
|
Param);
|
|
}
|
|
}
|
|
#endif // REFERENCE_DEBUG
|
|
|
|
|
|
PAFD_TPACKETS_INFO_INTERNAL
|
|
FASTCALL
|
|
AfdGetTpInfoFast (
|
|
ULONG ElementCount
|
|
)
|
|
{
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
|
|
ASSERT (ElementCount<=(MAXULONG/sizeof (AFD_TRANSMIT_PACKETS_ELEMENT)));
|
|
|
|
tpInfo = ExAllocateFromNPagedLookasideList(&AfdLookasideLists->TpInfoList);
|
|
if (tpInfo!=NULL) {
|
|
ASSERT (tpInfo->ReadIrp==NULL);
|
|
ASSERT (tpInfo->NumSendIrps==AFD_TP_MIN_SEND_IRPS);
|
|
|
|
tpInfo->HeadMdl = NULL;
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
tpInfo->HeadPd = NULL;
|
|
tpInfo->TailPd = &tpInfo->HeadPd;
|
|
tpInfo->PdLength = 0;
|
|
tpInfo->PdNeedsPps = FALSE;
|
|
tpInfo->NextElement = 0;
|
|
tpInfo->RemainingPkts = MAXULONG;
|
|
|
|
#if REFERENCE_DEBUG
|
|
tpInfo->CurrentReferenceSlot = -1;
|
|
RtlZeroMemory (&tpInfo->ReferenceDebug,
|
|
sizeof (tpInfo->ReferenceDebug));
|
|
#endif
|
|
#if AFD_PERF_DBG
|
|
tpInfo->WorkersExecuted = 0;
|
|
#endif
|
|
if (ElementCount<=AfdDefaultTpInfoElementCount) {
|
|
return tpInfo;
|
|
}
|
|
|
|
try {
|
|
tpInfo->ElementArray =
|
|
AFD_ALLOCATE_POOL_WITH_QUOTA (
|
|
NonPagedPool,
|
|
ElementCount*sizeof (AFD_TRANSMIT_PACKETS_ELEMENT),
|
|
AFD_TRANSMIT_INFO_POOL_TAG);
|
|
tpInfo->ArrayAllocated = TRUE;
|
|
return tpInfo;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
AfdReturnTpInfo (tpInfo);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef _AFD_VARIABLE_STACK_
|
|
PAFD_TPACKETS_INFO_INTERNAL
|
|
FASTCALL
|
|
AfdGetTpInfoWithMaxStackSize (
|
|
ULONG ElementCount
|
|
)
|
|
{
|
|
ULONG size;
|
|
PVOID memoryBlock;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo;
|
|
|
|
size = AfdComputeTpInfoSize (ElementCount, AfdMaxStackSize);
|
|
if (size<ElementCount*sizeof (AFD_TRANSMIT_PACKETS_ELEMENT)) {
|
|
return NULL;
|
|
}
|
|
|
|
memoryBlock = AFD_ALLOCATE_POOL (
|
|
NonPagedPool,
|
|
size,
|
|
AFD_TRANSMIT_INFO_POOL_TAG);
|
|
|
|
if (memoryBlock==NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
tpInfo = AfdInitializeTpInfo (memoryBlock, ElementCount, AfdMaxStackSize);
|
|
tpInfo->HeadMdl = NULL;
|
|
tpInfo->TailMdl = &tpInfo->HeadMdl;
|
|
tpInfo->HeadPd = NULL;
|
|
tpInfo->TailPd = &tpInfo->HeadPd;
|
|
tpInfo->PdLength = 0;
|
|
tpInfo->PdNeedsPps = FALSE;
|
|
tpInfo->NextElement = 0;
|
|
tpInfo->RemainingPkts = MAXULONG;
|
|
|
|
#if REFERENCE_DEBUG
|
|
tpInfo->CurrentReferenceSlot = -1;
|
|
RtlZeroMemory (&tpInfo->ReferenceDebug,
|
|
sizeof (tpInfo->ReferenceDebug));
|
|
#endif
|
|
#if AFD_PERF_DBG
|
|
tpInfo->WorkersExecuted = 0;
|
|
#endif
|
|
return tpInfo;
|
|
}
|
|
|
|
#endif // _AFD_VARIABLE_STACK_
|
|
VOID
|
|
AfdReturnTpInfo (
|
|
PAFD_TPACKETS_INFO_INTERNAL TpInfo
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Validate that built-in send IRPs are properly deinitialized.
|
|
//
|
|
|
|
#if DBG
|
|
for (i=0; i<AFD_TP_MIN_SEND_IRPS; i++) {
|
|
ASSERT (TpInfo->SendIrp[i]->MdlAddress == NULL);
|
|
ASSERT (TpInfo->SendIrp[i]->Overlay.AsynchronousParameters.UserApcContext == NULL);
|
|
ASSERT (TpInfo->SendIrp[i]->Cancel==FALSE);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Dispose of extra allocated IRPs.
|
|
//
|
|
while (TpInfo->NumSendIrps>AFD_TP_MIN_SEND_IRPS) {
|
|
TpInfo->NumSendIrps -= 1;
|
|
if (TpInfo->SendIrp[TpInfo->NumSendIrps]!=NULL) {
|
|
IoFreeIrp (TpInfo->SendIrp[TpInfo->NumSendIrps]);
|
|
TpInfo->SendIrp[TpInfo->NumSendIrps] = NULL;
|
|
}
|
|
}
|
|
|
|
if (TpInfo->ReadIrp!=NULL) {
|
|
IoFreeIrp (TpInfo->ReadIrp);
|
|
TpInfo->ReadIrp = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Cleanup all file objects and MDLs that we may have already referenced
|
|
//
|
|
for (i=0; i<TpInfo->ElementCount; i++) {
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT pel;
|
|
|
|
pel = &TpInfo->ElementArray[i];
|
|
if (pel->Flags & TP_FILE) {
|
|
if (pel->FileObject!=NULL) {
|
|
ObDereferenceObject( pel->FileObject );
|
|
AfdRecordFileDeref();
|
|
}
|
|
}
|
|
else if (pel->Flags & TP_MDL) {
|
|
ASSERT (pel->Flags & TP_MEMORY);
|
|
if (pel->Mdl!=NULL) {
|
|
if (pel->Mdl->MdlFlags & MDL_PAGES_LOCKED) {
|
|
MmUnlockPages (pel->Mdl);
|
|
}
|
|
IoFreeMdl (pel->Mdl);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free non-default sized array of packets if necessary.
|
|
//
|
|
if (TpInfo->ArrayAllocated) {
|
|
AFD_FREE_POOL (TpInfo->ElementArray, AFD_TRANSMIT_INFO_POOL_TAG);
|
|
TpInfo->ElementArray = ALIGN_UP_TO_TYPE_POINTER (
|
|
(PUCHAR)TpInfo+sizeof (AFD_TPACKETS_INFO_INTERNAL),
|
|
AFD_TRANSMIT_PACKETS_ELEMENT);
|
|
TpInfo->ArrayAllocated = FALSE;
|
|
}
|
|
else {
|
|
ASSERT (TpInfo->ElementCount<=AfdDefaultTpInfoElementCount);
|
|
ASSERT (TpInfo->ElementArray == ALIGN_UP_TO_TYPE_POINTER (
|
|
(PUCHAR)TpInfo+sizeof (AFD_TPACKETS_INFO_INTERNAL),
|
|
AFD_TRANSMIT_PACKETS_ELEMENT));
|
|
}
|
|
|
|
#if AFD_PERF_DBG
|
|
InterlockedExchangeAdd (&AfdTPWorkersExecuted, TpInfo->WorkersExecuted);
|
|
InterlockedIncrement (&AfdTPRequests);
|
|
#endif
|
|
#ifdef _AFD_VARIABLE_STACK_
|
|
if (TpInfo->SendIrp[0]->StackCount==AfdTdiStackSize) {
|
|
#else // _AFD_VARIABLE_STACK_
|
|
ASSERT (TpInfo->SendIrp[0]->StackCount==AfdTdiStackSize);
|
|
#endif // _AFD_VARIABLE_STACK_
|
|
ExFreeToNPagedLookasideList( &AfdLookasideLists->TpInfoList, TpInfo );
|
|
#ifdef _AFD_VARIABLE_STACK_
|
|
}
|
|
else {
|
|
ASSERT (TpInfo->SendIrp[0]->StackCount>AfdTdiStackSize);
|
|
ASSERT (TpInfo->SendIrp[0]->StackCount<=AfdMaxStackSize);
|
|
AfdFreeTpInfo (TpInfo);
|
|
}
|
|
#endif // _AFD_VARIABLE_STACK_
|
|
}
|
|
|
|
|
|
ULONG
|
|
AfdComputeTpInfoSize (
|
|
ULONG ElementCount,
|
|
CCHAR IrpStackCount
|
|
)
|
|
{
|
|
USHORT irpSize = (USHORT)ALIGN_UP_TO_TYPE(IoSizeOfIrp (IrpStackCount), IRP);
|
|
return
|
|
ALIGN_UP_TO_TYPE(
|
|
ALIGN_UP_TO_TYPE (
|
|
sizeof (AFD_TPACKETS_INFO_INTERNAL),
|
|
AFD_TRANSMIT_PACKETS_ELEMENT ) +
|
|
ElementCount*sizeof (AFD_TRANSMIT_PACKETS_ELEMENT),
|
|
IRP ) +
|
|
irpSize*AFD_TP_MIN_SEND_IRPS;
|
|
}
|
|
|
|
PVOID
|
|
AfdAllocateTpInfo (
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used by the lookaside list allocation function to allocate a new
|
|
AFD TpInfo structure. The returned structure will be fully
|
|
initialized.
|
|
|
|
Arguments:
|
|
|
|
PoolType - passed to ExAllocatePoolWithTag.
|
|
|
|
NumberOfBytes - the number of bytes required for the tp info structure
|
|
|
|
Tag - passed to ExAllocatePoolWithTag.
|
|
|
|
Return Value:
|
|
|
|
PVOID - a fully initialized TpInfo, or NULL if the allocation
|
|
attempt fails.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID memoryBlock;
|
|
memoryBlock = AFD_ALLOCATE_POOL (
|
|
PoolType,
|
|
NumberOfBytes,
|
|
Tag);
|
|
|
|
if (memoryBlock!=NULL) {
|
|
AfdInitializeTpInfo (memoryBlock, AfdDefaultTpInfoElementCount, AfdTdiStackSize);
|
|
}
|
|
return memoryBlock;
|
|
}
|
|
|
|
PAFD_TPACKETS_INFO_INTERNAL
|
|
AfdInitializeTpInfo (
|
|
PVOID MemoryBlock,
|
|
ULONG ElementCount,
|
|
CCHAR StackSize
|
|
)
|
|
{
|
|
USHORT irpSize = IoSizeOfIrp (StackSize);
|
|
LONG i;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = MemoryBlock;
|
|
|
|
RtlZeroMemory (tpInfo, sizeof (*tpInfo));
|
|
|
|
tpInfo->ElementArray = ALIGN_UP_TO_TYPE_POINTER (
|
|
(PUCHAR)tpInfo+sizeof (AFD_TPACKETS_INFO_INTERNAL),
|
|
AFD_TRANSMIT_PACKETS_ELEMENT);
|
|
|
|
tpInfo->NumSendIrps = AFD_TP_MIN_SEND_IRPS;
|
|
tpInfo->SendIrp[0] = ALIGN_UP_TO_TYPE_POINTER (
|
|
&tpInfo->ElementArray[ElementCount],
|
|
IRP);
|
|
IoInitializeIrp (tpInfo->SendIrp[0], irpSize, StackSize);
|
|
tpInfo->SendIrp[0]->Overlay.AsynchronousParameters.UserApcRoutine = (PVOID)0;
|
|
for (i=1; i<AFD_TP_MIN_SEND_IRPS; i++) {
|
|
tpInfo->SendIrp[i] = ALIGN_UP_TO_TYPE_POINTER (
|
|
(PUCHAR)tpInfo->SendIrp[i-1]+irpSize,
|
|
IRP);
|
|
IoInitializeIrp (tpInfo->SendIrp[i], irpSize, StackSize);
|
|
tpInfo->SendIrp[i]->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE)(UINT_PTR)i;
|
|
}
|
|
|
|
return tpInfo;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
AfdFreeTpInfo (
|
|
PVOID TpInfo
|
|
)
|
|
{
|
|
ASSERT (((PAFD_TPACKETS_INFO_INTERNAL)TpInfo)->ElementArray == ALIGN_UP_TO_TYPE_POINTER (
|
|
(PUCHAR)TpInfo+sizeof (AFD_TPACKETS_INFO_INTERNAL),
|
|
AFD_TRANSMIT_PACKETS_ELEMENT));
|
|
AFD_FREE_POOL (TpInfo, AFD_TRANSMIT_INFO_POOL_TAG);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* T R A N S M I T F I L E I M P L E M E N T A T I O N
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
|
NTSTATUS
|
|
AfdRestartFastTransmitSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdDoMdlReadComplete (
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
AfdFastTransmitApcRundownRoutine (
|
|
IN struct _KAPC *Apc
|
|
);
|
|
|
|
VOID
|
|
AfdFastTransmitApcKernelRoutine (
|
|
IN struct _KAPC *Apc,
|
|
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN OUT PVOID *NormalContext,
|
|
IN OUT PVOID *SystemArgument1,
|
|
IN OUT PVOID *SystemArgument2
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGEAFD, AfdFastTransmitFile )
|
|
#pragma alloc_text( PAGEAFD, AfdRestartFastTransmitSend )
|
|
#pragma alloc_text( PAGE, AfdFastTransmitApcKernelRoutine )
|
|
#pragma alloc_text( PAGE, AfdFastTransmitApcRundownRoutine )
|
|
#pragma alloc_text( PAGE, AfdDoMdlReadComplete )
|
|
#pragma alloc_text( PAGE, AfdTransmitFile )
|
|
#pragma alloc_text( PAGE, AfdSuperDisconnect )
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
AfdFastTransmitFile (
|
|
IN PAFD_ENDPOINT endpoint,
|
|
IN PAFD_TRANSMIT_FILE_INFO transmitInfo,
|
|
OUT PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to perform a fast TransmitFile call. This will succeed
|
|
only if the caller requests write behind, the file data to be sent
|
|
is small, and the data is in the file system cache.
|
|
|
|
Arguments:
|
|
|
|
endpoint - the endpoint of interest.
|
|
|
|
transmitInfo - AFD_TRANSMIT_FILE_INFO structure.
|
|
|
|
IoStatus - points to the IO status block that will be set on successful
|
|
return from this function.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the fast path was successful; FALSE if we need to do through
|
|
the normal path.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_CONNECTION connection;
|
|
PAFD_BUFFER afdBuffer;
|
|
ULARGE_INTEGER sendLength;
|
|
PFILE_OBJECT fileObject;
|
|
BOOLEAN success;
|
|
BOOLEAN sendCountersUpdated;
|
|
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
|
ULONG fileWriteLength, bufferLength;
|
|
NTSTATUS status;
|
|
LARGE_INTEGER fileOffset;
|
|
PMDL fileMdl;
|
|
PIRP irp;
|
|
|
|
//
|
|
// If the endpoint is shut down in any way, bail out of fast IO.
|
|
//
|
|
|
|
if ( endpoint->DisconnectMode != 0 ||
|
|
endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the TDI provider for this endpoint supports bufferring,
|
|
// don't use fast IO.
|
|
//
|
|
|
|
if ( IS_TDI_BUFFERRING(endpoint) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that the flags are specified such that a fast-path
|
|
// TransmitFile is reasonable. The caller must have specified
|
|
// the write-behind flag, but not the disconnect or reuse
|
|
// socket flags.
|
|
//
|
|
|
|
if ( ((transmitInfo->Flags &
|
|
(~(AFD_TF_WRITE_BEHIND |
|
|
AFD_TF_DISCONNECT |
|
|
AFD_TF_REUSE_SOCKET |
|
|
AFD_TF_WORKER_KIND_MASK))) != 0 ) ||
|
|
((transmitInfo->Flags & AFD_TF_WORKER_KIND_MASK)
|
|
== AFD_TF_WORKER_KIND_MASK) ||
|
|
((transmitInfo->Flags &(~AFD_TF_WORKER_KIND_MASK))
|
|
!= AFD_TF_WRITE_BEHIND) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
IF_DEBUG(FAST_IO) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastTransmitFile: attempting fast IO on endp %p, "
|
|
"conn %p\n", endpoint, endpoint->Common.VcConnecting.Connection));
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize locals so that cleanup is easier.
|
|
//
|
|
|
|
fileObject = NULL;
|
|
sendCountersUpdated = FALSE;
|
|
fileMdl = NULL;
|
|
afdBuffer = NULL;
|
|
AFD_W4_INIT irp = NULL; // Depends on variable above, but compiler does not see
|
|
// the connection.
|
|
|
|
//
|
|
// Calculate the length the entire send.
|
|
//
|
|
|
|
if (transmitInfo->FileHandle!=NULL) {
|
|
fileWriteLength = transmitInfo->WriteLength.LowPart;
|
|
}
|
|
else {
|
|
fileWriteLength = 0;
|
|
}
|
|
|
|
sendLength.QuadPart = (ULONGLONG)transmitInfo->HeadLength +
|
|
(ULONGLONG)fileWriteLength +
|
|
(ULONGLONG)transmitInfo->TailLength;
|
|
|
|
//
|
|
// Require the following for the fast path:
|
|
//
|
|
// - There be no limitation on the count of simultaneous
|
|
// TransmitFile calls. The fast path would work around
|
|
// this limit, if it exists.
|
|
// - The caller must specify the write length (if it specified file at all).
|
|
// - The write length must be less than the configured maximum.
|
|
// - If the entire send is larger than an AFD buffer page,
|
|
// we're going to use FsRtlMdlRead, so for purposes of
|
|
// simplicity there must be:
|
|
// - a head buffer, and
|
|
// - no tail buffer
|
|
// - The configured maximum will always be less than 4GB.
|
|
// - The head buffer, if any, fits on a single page.
|
|
//
|
|
|
|
if (AfdMaxActiveTransmitFileCount != 0
|
|
|
|
||
|
|
|
|
(transmitInfo->FileHandle!=NULL &&
|
|
(fileWriteLength == 0 ||
|
|
transmitInfo->Offset.QuadPart <0 ))
|
|
|
|
||
|
|
|
|
sendLength.QuadPart > AfdMaxFastTransmit
|
|
|
|
||
|
|
|
|
( sendLength.LowPart > AfdMaxFastCopyTransmit &&
|
|
(transmitInfo->HeadLength == 0 ||
|
|
transmitInfo->TailLength != 0 ) )
|
|
|
|
||
|
|
|
|
transmitInfo->WriteLength.HighPart != 0
|
|
|
|
||
|
|
|
|
transmitInfo->HeadLength > AfdBufferLengthForOnePage ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
if (connection==NULL) {
|
|
//
|
|
// connection might have been cleaned up by transmit file.
|
|
//
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
return FALSE;
|
|
}
|
|
ASSERT( connection->Type == AfdBlockTypeConnection );
|
|
|
|
//
|
|
// Determine whether there is already too much send data
|
|
// pending on the connection. If there is too much send
|
|
// data, don't do the fast path.
|
|
//
|
|
|
|
if ( AfdShouldSendBlock( endpoint, connection, sendLength.LowPart ) ) {
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
goto complete;
|
|
}
|
|
//
|
|
// Add a reference to the connection object since the send
|
|
// request will complete asynchronously.
|
|
//
|
|
REFERENCE_CONNECTION( connection );
|
|
|
|
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
|
|
|
|
|
//
|
|
// AfdShouldSendBlock() updates the send counters in the AFD
|
|
// connection object. Remember this fact so that we can clean
|
|
// them up if the fast path fails after this point.
|
|
//
|
|
|
|
sendCountersUpdated = TRUE;
|
|
|
|
//
|
|
// Grab an AFD buffer large enough to hold the entire send.
|
|
//
|
|
|
|
if (sendLength.LowPart>AfdMaxFastCopyTransmit) {
|
|
bufferLength = transmitInfo->HeadLength;
|
|
}
|
|
else {
|
|
bufferLength = sendLength.LowPart;
|
|
}
|
|
|
|
if (bufferLength<max (sizeof(KAPC),sizeof (WORK_QUEUE_ITEM))) {
|
|
bufferLength = max (sizeof(KAPC),sizeof (WORK_QUEUE_ITEM));
|
|
}
|
|
|
|
afdBuffer = AfdGetBuffer( endpoint, bufferLength, 0,
|
|
connection->OwningProcess );
|
|
if ( afdBuffer == NULL ) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Initialize buffer fields for proper cleanup.
|
|
//
|
|
|
|
irp = afdBuffer->Irp;
|
|
afdBuffer->Irp->Tail.Overlay.Thread = NULL;
|
|
afdBuffer->FileObject = NULL;
|
|
|
|
//
|
|
// We use exception handler because buffers are user
|
|
// mode pointers
|
|
//
|
|
try {
|
|
|
|
//
|
|
// Copy in the head and tail buffers, if necessary. Note that if we
|
|
// are goint to use MDL read, then there cannot be a tail buffer because of
|
|
// the check at the beginning of this function.
|
|
//
|
|
|
|
if ( transmitInfo->HeadLength > 0 ) {
|
|
RtlCopyMemory(
|
|
afdBuffer->Buffer,
|
|
transmitInfo->Head,
|
|
transmitInfo->HeadLength
|
|
);
|
|
}
|
|
|
|
if ( transmitInfo->TailLength > 0 ) {
|
|
RtlCopyMemory(
|
|
(PCHAR)afdBuffer->Buffer + transmitInfo->HeadLength +
|
|
fileWriteLength,
|
|
transmitInfo->Tail,
|
|
transmitInfo->TailLength
|
|
);
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER_NO_STATUS() ) {
|
|
|
|
goto complete;
|
|
}
|
|
|
|
if (transmitInfo->FileHandle!=NULL) {
|
|
//
|
|
// Get a referenced pointer to the file object for the file that
|
|
// we're going to transmit. This call will fail if the file
|
|
// handle specified by the caller is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
transmitInfo->FileHandle,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
ExGetPreviousMode (),
|
|
(PVOID *)&fileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
goto complete;
|
|
}
|
|
AfdRecordFileRef();
|
|
|
|
//
|
|
// If the file system doesn't support the fast cache manager
|
|
// interface, bail and go through the IRP path.
|
|
//
|
|
|
|
if( !AFD_USE_CACHE(fileObject)) {
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Grab the file offset into a local so that we know that the
|
|
// offset pointer we pass to FsRtlCopyRead is valid.
|
|
//
|
|
|
|
fileOffset = transmitInfo->Offset;
|
|
|
|
if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) &&
|
|
(fileOffset.QuadPart == 0) ) {
|
|
//
|
|
// Use current offset if file is opened syncronously
|
|
// and offset is not specified.
|
|
//
|
|
|
|
fileOffset = fileObject->CurrentByteOffset;
|
|
}
|
|
//
|
|
// Get the file data. If the amount of file data is small, copy
|
|
// it directly into the AFD buffer. If it is large, get an MDL
|
|
// chain for the data and chain it on to the AFD buffer chain.
|
|
//
|
|
|
|
if ( sendLength.LowPart <= AfdMaxFastCopyTransmit ) {
|
|
|
|
success = FsRtlCopyRead(
|
|
fileObject,
|
|
&fileOffset,
|
|
fileWriteLength,
|
|
FALSE,
|
|
0,
|
|
(PCHAR)afdBuffer->Buffer + transmitInfo->HeadLength,
|
|
IoStatus,
|
|
IoGetRelatedDeviceObject( fileObject )
|
|
);
|
|
|
|
//
|
|
// We're done with the file object, so deference it now.
|
|
//
|
|
|
|
ObDereferenceObject( fileObject );
|
|
AfdRecordFileDeref();
|
|
fileObject = NULL;
|
|
|
|
if ( !success ) {
|
|
#if AFD_PERF_DBG
|
|
InterlockedIncrement (&AfdFastTfReadFailed);
|
|
#endif
|
|
goto complete;
|
|
}
|
|
|
|
} else {
|
|
|
|
success = FsRtlMdlRead(
|
|
fileObject,
|
|
&fileOffset,
|
|
fileWriteLength,
|
|
0,
|
|
&fileMdl,
|
|
IoStatus
|
|
);
|
|
|
|
if (success) {
|
|
//
|
|
// Save the file object in the AFD buffer. The send restart
|
|
// routine will handle dereferencing the file object and
|
|
// returning the file MDLs to the system.
|
|
//
|
|
|
|
afdBuffer->FileObject = fileObject;
|
|
afdBuffer->FileOffset = fileOffset;
|
|
|
|
//
|
|
// If caller asked us to use kernel APC to execute the request,
|
|
// allocate and queue the IRP to the current thread to make
|
|
// sure it won't go away until IRP is completed.
|
|
//
|
|
if ((((transmitInfo->Flags & AFD_TF_WORKER_KIND_MASK)
|
|
== AFD_TF_USE_KERNEL_APC) ||
|
|
(((transmitInfo->Flags & AFD_TF_WORKER_KIND_MASK)
|
|
== AFD_TF_USE_DEFAULT_WORKER) &&
|
|
(AfdDefaultTransmitWorker==AFD_TF_USE_KERNEL_APC))) ) {
|
|
//
|
|
// Allocation will occur right before we call the
|
|
// transport, set IRP to null to trigger this.
|
|
//
|
|
irp = NULL;
|
|
}
|
|
}
|
|
else {
|
|
#if AFD_PERF_DBG
|
|
InterlockedIncrement (&AfdFastTfReadFailed);
|
|
#endif
|
|
goto complete;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we read less information than was requested, we must have
|
|
// hit the end of the file. Fail the transmit request, since
|
|
// this can only happen if the caller requested that we send
|
|
// more data than the file currently contains.
|
|
//
|
|
|
|
if ( IoStatus->Information < fileWriteLength ) {
|
|
goto complete;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have to rebuild the MDL in the AFD buffer structure to
|
|
// represent exactly the number of bytes we're going to be
|
|
// sending. If the AFD buffer has all the send data, indicate
|
|
// that. If we did MDL file I/O, then chain the file data on
|
|
// to the head MDL.
|
|
//
|
|
|
|
if ( fileMdl == NULL ) {
|
|
afdBuffer->Mdl->ByteCount = sendLength.LowPart;
|
|
} else {
|
|
afdBuffer->Mdl->ByteCount = transmitInfo->HeadLength;
|
|
afdBuffer->Mdl->Next = fileMdl;
|
|
}
|
|
|
|
//
|
|
// We can have only one transmit file operation on endpoint
|
|
// at a time. Treat is as a state change
|
|
//
|
|
if (AFD_START_STATE_CHANGE (endpoint, endpoint->State)) {
|
|
|
|
//
|
|
// Verify state under protection of state change flag
|
|
//
|
|
if (endpoint->State!=AfdEndpointStateConnected) {
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Save connection to dereference in completion routine.
|
|
//
|
|
afdBuffer->Context = connection;
|
|
|
|
if (irp==NULL) {
|
|
//
|
|
// Need to allocate IRP and let the io subsystem queue
|
|
// it to the current thread, so we can run APC upon
|
|
// IRP completion.
|
|
//
|
|
irp = TdiBuildInternalDeviceControlIrp (
|
|
TDI_SEND,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
NULL,
|
|
&AfdDontCareIoStatus // we will have our completion
|
|
// routine installed in the IRP which will get the
|
|
// status, so we do not care if IO system writes
|
|
// it there for us, but still must provide valid
|
|
// storage to avoid failure.
|
|
);
|
|
|
|
if (irp==NULL) {
|
|
//
|
|
// Could not allocate IRP, use worker threads
|
|
//
|
|
irp = afdBuffer->Irp;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT (irp==afdBuffer->Irp);
|
|
}
|
|
|
|
//
|
|
// Use the IRP in the AFD buffer structure to give to the TDI
|
|
// provider. Build the TDI send request.
|
|
//
|
|
|
|
TdiBuildSend(
|
|
irp,
|
|
connection->DeviceObject,
|
|
connection->FileObject,
|
|
AfdRestartFastTransmitSend,
|
|
afdBuffer,
|
|
afdBuffer->Mdl,
|
|
0,
|
|
sendLength.LowPart
|
|
);
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdFastTransmit: Starting send for endp-%p,file-%p,"
|
|
"afdBuffer-%p,length-%ld.\n",
|
|
endpoint,fileObject,(PVOID)afdBuffer,sendLength.LowPart));
|
|
}
|
|
|
|
//
|
|
// Call the transport to actually perform the send.
|
|
//
|
|
|
|
status = IoCallDriver( connection->DeviceObject, irp );
|
|
|
|
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
|
|
//
|
|
// The fast path succeeded--complete the call. Note that we
|
|
// change the status code from what was returned by the TDI
|
|
// provider into STATUS_SUCCESS. This is because we don't want
|
|
// to complete the IRP with STATUS_PENDING etc.
|
|
//
|
|
|
|
if ( NT_SUCCESS(status) ) {
|
|
IoStatus->Information = sendLength.LowPart;
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
return TRUE;
|
|
}
|
|
else {
|
|
// The restart routine will handle cleanup
|
|
// and we cannot duplicate cleanup in the
|
|
// case of a failure or exception below.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The call failed for some reason. Fail fast IO.
|
|
//
|
|
|
|
|
|
complete:
|
|
|
|
if ( fileMdl != NULL ) {
|
|
ASSERT (afdBuffer!=NULL);
|
|
status = AfdMdlReadComplete( fileObject, fileMdl, &fileOffset );
|
|
if (!NT_SUCCESS (status)) {
|
|
afdBuffer->Context = connection;
|
|
REFERENCE_CONNECTION (connection);
|
|
ASSERT (afdBuffer->FileObject==fileObject);
|
|
ASSERT (afdBuffer->Mdl->Next==fileMdl);
|
|
ASSERT (afdBuffer->FileOffset.QuadPart==fileOffset.QuadPart);
|
|
AfdLRMdlReadComplete (&afdBuffer->Header);
|
|
afdBuffer = NULL;
|
|
fileObject = NULL;
|
|
}
|
|
}
|
|
|
|
if ( fileObject != NULL ) {
|
|
ObDereferenceObject( fileObject );
|
|
AfdRecordFileDeref();
|
|
}
|
|
|
|
if ( afdBuffer != NULL ) {
|
|
ASSERT ((irp==NULL) || (irp==afdBuffer->Irp));
|
|
afdBuffer->Mdl->Next = NULL;
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
|
}
|
|
|
|
if ( sendCountersUpdated ) {
|
|
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
connection->VcBufferredSendBytes -= sendLength.LowPart;
|
|
connection->VcBufferredSendCount -= 1;
|
|
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
|
DEREFERENCE_CONNECTION (connection);
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // AfdFastTransmitFile
|
|
|
|
NTSTATUS
|
|
AfdRestartFastTransmitSend (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for fast transmit send IRPs.
|
|
It initiates the completion of the request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - ignored.
|
|
|
|
Irp - the send IRP that is completing.
|
|
|
|
Context - a pointer to the AfdBuffer structure with the buffer that
|
|
was sent.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED which indicates to the I/O system
|
|
that it should stop completion processing of this IRP. User request
|
|
has already been completed on the fast path, we just free resources here.
|
|
|
|
--*/
|
|
{
|
|
PAFD_BUFFER afdBuffer = Context;
|
|
PAFD_CONNECTION connection = afdBuffer->Context;
|
|
NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdRestartFastTransmitSend: Completing send for file-%p,"
|
|
"afdBuffer-%p,status-%lx.\n",
|
|
afdBuffer->FileObject,(PVOID)afdBuffer,Irp->IoStatus.Status));
|
|
}
|
|
|
|
AfdProcessBufferSend (connection, Irp);
|
|
|
|
//
|
|
// If file object is not NULL we need to
|
|
// return MDL back to file system driver/cache
|
|
//
|
|
if (afdBuffer->FileObject!=NULL) {
|
|
|
|
//
|
|
// If we used a separate IRP, then
|
|
// the caller requested that we do processing
|
|
// inside kernel APC, otherwise, we are using
|
|
// system worker threads.
|
|
//
|
|
if (afdBuffer->Irp!=Irp) {
|
|
//
|
|
// The IRP is owned by IO subsystem.
|
|
// We must let it complete and free the IRP, hence
|
|
// return STATUS_SUCCESS and remove MDL field as IO
|
|
// subsytem cannot handle non-paged pool memory in MDL
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// If IRP was not cancelled, attempt to initialize and queue APC
|
|
// Otherwise, the thread is probably exiting and we won't be
|
|
// able to queue apc anyway.
|
|
//
|
|
if (!Irp->Cancel) {
|
|
ASSERT (afdBuffer->BufferLength>=sizeof(KAPC));
|
|
KeInitializeApc (afdBuffer->Buffer,
|
|
PsGetThreadTcb (Irp->Tail.Overlay.Thread),
|
|
Irp->ApcEnvironment,
|
|
AfdFastTransmitApcKernelRoutine,
|
|
AfdFastTransmitApcRundownRoutine,
|
|
(PKNORMAL_ROUTINE)-1,
|
|
KernelMode,
|
|
NULL
|
|
);
|
|
if (KeInsertQueueApc (afdBuffer->Buffer,
|
|
afdBuffer,
|
|
afdBuffer->FileObject,
|
|
AfdPriorityBoost)) {
|
|
//
|
|
// Success, we are done.
|
|
//
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Can't queue APC, revert to system worker threads
|
|
//
|
|
}
|
|
|
|
ASSERT (afdBuffer->BufferLength>=sizeof(WORK_QUEUE_ITEM));
|
|
ExInitializeWorkItem (
|
|
(PWORK_QUEUE_ITEM)afdBuffer->Buffer,
|
|
AfdDoMdlReadComplete,
|
|
afdBuffer
|
|
);
|
|
ExQueueWorkItem (afdBuffer->Buffer, DelayedWorkQueue);
|
|
}
|
|
else {
|
|
|
|
ASSERT (afdBuffer->Irp==Irp);
|
|
ASSERT (afdBuffer->Mdl->Next == NULL);
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
|
DEREFERENCE_CONNECTION (connection);
|
|
}
|
|
|
|
exit:
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
AfdFastTransmitApcKernelRoutine (
|
|
IN struct _KAPC *Apc,
|
|
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN OUT PVOID *NormalContext,
|
|
IN OUT PVOID *SystemArgument1,
|
|
IN OUT PVOID *SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Special kernel apc routine. Executed in the context of
|
|
the target thread at APC_LEVEL
|
|
|
|
Arguments:
|
|
NormalRoutine - pointer containing address of normal routine (it will
|
|
be NULL for special kernel APC and not NULL for normal
|
|
kernel APC)
|
|
|
|
SystemArgument1 - pointer to the address of worker routine to execute
|
|
SyetemArgument2 - pointer to the argument to pass to worker routine
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAFD_BUFFER afdBuffer;
|
|
|
|
UNREFERENCED_PARAMETER (NormalContext);
|
|
PAGED_CODE ();
|
|
|
|
afdBuffer = *SystemArgument1;
|
|
#if DBG
|
|
try {
|
|
ASSERT (Apc==afdBuffer->Buffer);
|
|
ASSERT (afdBuffer->FileObject==*SystemArgument2);
|
|
#else
|
|
UNREFERENCED_PARAMETER (Apc);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
#endif
|
|
|
|
//
|
|
// Normal APC, but we are requested to run in its special
|
|
// routine which avoids raising and lowering IRQL
|
|
//
|
|
|
|
ASSERT (*NormalRoutine==(PKNORMAL_ROUTINE)-1);
|
|
*NormalRoutine = NULL;
|
|
AfdDoMdlReadComplete (afdBuffer);
|
|
#if DBG
|
|
}
|
|
except (AfdApcExceptionFilter (GetExceptionInformation (),
|
|
__FILE__,
|
|
__LINE__)) {
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
AfdFastTransmitApcRundownRoutine (
|
|
IN struct _KAPC *Apc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
APC rundown routine. Executed if APC cannot be delivered for
|
|
some reason (thread exiting).
|
|
We just fall back to system threads to execute the worker
|
|
|
|
Arguments:
|
|
|
|
Apc - APC structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAFD_BUFFER afdBuffer;
|
|
|
|
PAGED_CODE ();
|
|
#if DBG
|
|
try {
|
|
#endif
|
|
afdBuffer = Apc->SystemArgument1;
|
|
ASSERT (Apc==afdBuffer->Buffer);
|
|
ASSERT (afdBuffer->FileObject==Apc->SystemArgument2);
|
|
|
|
//
|
|
// APC could not be run, revert to system worker thread
|
|
//
|
|
|
|
ExInitializeWorkItem (
|
|
(PWORK_QUEUE_ITEM)afdBuffer->Buffer,
|
|
AfdDoMdlReadComplete,
|
|
afdBuffer
|
|
);
|
|
|
|
ExQueueWorkItem (afdBuffer->Buffer, DelayedWorkQueue);
|
|
#if DBG
|
|
}
|
|
except (AfdApcExceptionFilter (GetExceptionInformation (),
|
|
__FILE__,
|
|
__LINE__)) {
|
|
DbgBreakPoint ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
AfdDoMdlReadComplete (
|
|
PVOID Context
|
|
)
|
|
{
|
|
PAFD_BUFFER afdBuffer = Context;
|
|
PAFD_CONNECTION connection = afdBuffer->Context;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Return mdl to the file system
|
|
//
|
|
status = AfdMdlReadComplete(
|
|
afdBuffer->FileObject,
|
|
afdBuffer->Mdl->Next,
|
|
&afdBuffer->Irp->Overlay.AllocationSize
|
|
);
|
|
if (NT_SUCCESS (status)) {
|
|
//
|
|
// Release file object reference (the AfdMdlReadComplete makes its own
|
|
// reference if it needs to return MDL asynchronously.
|
|
//
|
|
ObDereferenceObject( afdBuffer->FileObject );
|
|
AfdRecordFileDeref();
|
|
|
|
afdBuffer->Mdl->Next = NULL;
|
|
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
|
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
|
DEREFERENCE_CONNECTION (connection);
|
|
}
|
|
else {
|
|
AfdLRMdlReadComplete (&afdBuffer->Header);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdTransmitFile (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initial entrypoint for handling transmit file IRPs. This routine
|
|
verifies parameters, initializes data structures to be used for
|
|
the request, and initiates the I/O.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to a transmit file IRP.
|
|
|
|
IrpSp - Our stack location for this IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the request was initiated successfully, or a
|
|
failure status code if there was an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
NTSTATUS status;
|
|
AFD_TRANSMIT_FILE_INFO params;
|
|
PAFD_TPACKETS_INFO_INTERNAL tpInfo = NULL;
|
|
PAFD_TRANSMIT_PACKETS_ELEMENT pel;
|
|
PAFD_CONNECTION connection;
|
|
BOOLEAN fileError = FALSE;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Special hack to let the user mode dll know that it
|
|
// should try SAN provider instead.
|
|
//
|
|
|
|
if (IS_SAN_ENDPOINT (endpoint)) {
|
|
status = STATUS_INVALID_PARAMETER_12;
|
|
goto complete;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(AFD_TRANSMIT_FILE_INFO32) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(AFD_TRANSMIT_FILE_INFO) ) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Because we're using type 3 (neither) I/O for this IRP, the I/O
|
|
// system does no verification on the user buffer. Therefore, we
|
|
// must manually check it for validity inside a try-except block.
|
|
// We also leverage the try-except to validate and lock down the
|
|
// head and/or tail buffers specified by the caller.
|
|
//
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_TRANSMIT_FILE_INFO32 pInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
pInfo,
|
|
sizeof(AFD_TRANSMIT_FILE_INFO32),
|
|
PROBE_ALIGNMENT32 (AFD_TRANSMIT_FILE_INFO32)
|
|
);
|
|
}
|
|
params.Offset = pInfo->Offset;
|
|
params.WriteLength = pInfo->WriteLength;
|
|
params.SendPacketLength = pInfo->SendPacketLength;
|
|
params.FileHandle = pInfo->FileHandle;
|
|
params.Head = UlongToPtr(pInfo->Head);
|
|
params.HeadLength = pInfo->HeadLength;
|
|
params.Tail = UlongToPtr(pInfo->Tail);
|
|
params.TailLength = pInfo->TailLength;
|
|
params.Flags = pInfo->Flags;
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
sizeof(AFD_TRANSMIT_FILE_INFO),
|
|
PROBE_ALIGNMENT (AFD_TRANSMIT_FILE_INFO)
|
|
);
|
|
}
|
|
|
|
params = *((PAFD_TRANSMIT_FILE_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Validate any flags specified in the request.
|
|
// and make sure that file offset is positive (of course if file is specified)
|
|
//
|
|
|
|
if ( ((params.Flags &
|
|
~(AFD_TF_WRITE_BEHIND | AFD_TF_DISCONNECT | AFD_TF_REUSE_SOCKET | AFD_TF_WORKER_KIND_MASK) )
|
|
!= 0 ) ||
|
|
((params.Flags & AFD_TF_WORKER_KIND_MASK) == AFD_TF_WORKER_KIND_MASK) ||
|
|
(params.FileHandle!=NULL && params.Offset.QuadPart<0)){
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// If transmit worker is not specified, use system default setting
|
|
//
|
|
if ((params.Flags & AFD_TF_WORKER_KIND_MASK)==AFD_TF_USE_DEFAULT_WORKER) {
|
|
params.Flags |= AfdDefaultTransmitWorker;
|
|
}
|
|
|
|
|
|
tpInfo = AfdGetTpInfo (endpoint, 3);
|
|
if (tpInfo==NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
tpInfo->ElementCount = 0;
|
|
|
|
tpInfo->SendPacketLength = params.SendPacketLength;
|
|
if (tpInfo->SendPacketLength==0)
|
|
tpInfo->SendPacketLength = AfdTransmitIoLength;
|
|
//
|
|
// If the caller specified head and/or tail buffers, probe and
|
|
// lock the buffers so that we have MDLs we can use to send the
|
|
// buffers.
|
|
//
|
|
|
|
if ( params.HeadLength > 0 ) {
|
|
pel = &tpInfo->ElementArray[tpInfo->ElementCount++];
|
|
pel->Buffer = params.Head;
|
|
pel->Length = params.HeadLength;
|
|
pel->Flags = TP_MEMORY;
|
|
|
|
if (params.Flags & AFD_TF_USE_SYSTEM_THREAD) {
|
|
pel->Flags |= TP_MDL;
|
|
pel->Mdl = IoAllocateMdl(
|
|
params.Head,
|
|
params.HeadLength,
|
|
FALSE, // SecondaryBuffer
|
|
TRUE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
if ( pel->Mdl == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
MmProbeAndLockPages( pel->Mdl, Irp->RequestorMode, IoReadAccess );
|
|
}
|
|
}
|
|
|
|
if (params.FileHandle!=NULL) {
|
|
pel = &tpInfo->ElementArray[tpInfo->ElementCount++];
|
|
pel->Flags = TP_FILE;
|
|
pel->FileOffset = params.Offset;
|
|
|
|
//
|
|
// Get a referenced pointer to the file object for the file that
|
|
// we're going to transmit. This call will fail if the file handle
|
|
// specified by the caller is invalid.
|
|
//
|
|
|
|
status = ObReferenceObjectByHandle(
|
|
params.FileHandle,
|
|
FILE_READ_DATA,
|
|
*IoFileObjectType,
|
|
Irp->RequestorMode,
|
|
(PVOID *)&pel->FileObject,
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
//
|
|
// Unbump element count, so that uninitialied memory
|
|
// is NOT improperly dereferenced in cleanup.
|
|
//
|
|
tpInfo->ElementCount -= 1;
|
|
//
|
|
// Tell the caller that we encountered an error
|
|
// when accessing file not socket.
|
|
//
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength>=sizeof (BOOLEAN)) {
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
ProbeAndWriteBoolean ((BOOLEAN *)Irp->UserBuffer, TRUE);
|
|
}
|
|
else {
|
|
*((BOOLEAN *)Irp->UserBuffer) = TRUE;
|
|
}
|
|
}
|
|
goto complete;
|
|
}
|
|
|
|
AfdRecordFileRef();
|
|
|
|
|
|
//
|
|
// Use pre-allocated buffers by default when
|
|
// file is not cached
|
|
//
|
|
if (params.SendPacketLength==0 && !AFD_USE_CACHE(pel->FileObject)) {
|
|
tpInfo->SendPacketLength = AfdLargeBufferSize;
|
|
}
|
|
|
|
if ( (pel->FileObject->Flags & FO_SYNCHRONOUS_IO) &&
|
|
(pel->FileOffset.QuadPart == 0) ) {
|
|
//
|
|
// Use current offset if file is opened syncronously
|
|
// and offset is not specified.
|
|
//
|
|
|
|
pel->FileOffset = pel->FileObject->CurrentByteOffset;
|
|
}
|
|
|
|
if ( params.WriteLength.QuadPart == 0 ) {
|
|
//
|
|
// Length was not specified, figure out the
|
|
// size of the entire file
|
|
//
|
|
|
|
FILE_STANDARD_INFORMATION fileInfo;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
|
|
status = ZwQueryInformationFile(
|
|
params.FileHandle,
|
|
&ioStatusBlock,
|
|
&fileInfo,
|
|
sizeof(fileInfo),
|
|
FileStandardInformation
|
|
);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
fileError = TRUE;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Make sure that offset is within the file
|
|
//
|
|
if (pel->FileOffset.QuadPart<0
|
|
||
|
|
pel->FileOffset.QuadPart>fileInfo.EndOfFile.QuadPart
|
|
||
|
|
(fileInfo.EndOfFile.QuadPart
|
|
- pel->FileOffset.QuadPart>MAXLONG)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
fileError = TRUE;
|
|
goto complete;
|
|
|
|
}
|
|
pel->Length = (ULONG)(fileInfo.EndOfFile.QuadPart - pel->FileOffset.QuadPart);
|
|
}
|
|
else if (params.WriteLength.QuadPart<=MAXLONG &&
|
|
pel->FileOffset.QuadPart>=0) {
|
|
pel->Length = (ULONG)params.WriteLength.QuadPart;
|
|
}
|
|
else {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
fileError = TRUE;
|
|
goto complete;
|
|
}
|
|
}
|
|
|
|
|
|
if ( params.TailLength > 0 ) {
|
|
|
|
pel = &tpInfo->ElementArray[tpInfo->ElementCount++];
|
|
pel->Buffer = params.Tail;
|
|
pel->Length = params.TailLength;
|
|
pel->Flags = TP_MEMORY;
|
|
|
|
if (params.Flags & AFD_TF_USE_SYSTEM_THREAD) {
|
|
pel->Flags |= TP_MDL;
|
|
pel->Mdl = IoAllocateMdl(
|
|
params.Tail,
|
|
params.TailLength,
|
|
FALSE, // SecondaryBuffer
|
|
TRUE, // ChargeQuota
|
|
NULL // Irp
|
|
);
|
|
if ( pel->Mdl == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto complete;
|
|
}
|
|
|
|
MmProbeAndLockPages( pel->Mdl, Irp->RequestorMode, IoReadAccess );
|
|
}
|
|
}
|
|
|
|
AFD_GET_TPIC(Irp)->Flags = params.Flags;
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
goto complete;
|
|
}
|
|
|
|
|
|
//
|
|
// If disconnect is desired, send the state change to make sure
|
|
// we can only perform one at a time and validate the state of
|
|
// the endpoint.
|
|
//
|
|
if (AFD_GET_TPIC(Irp)->Flags & (AFD_TF_REUSE_SOCKET|AFD_TF_DISCONNECT)) {
|
|
if (!AFD_START_STATE_CHANGE (endpoint, endpoint->State)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete_state_change;
|
|
}
|
|
//
|
|
// Setting AFD_TF_REUSE_SOCKET implies that a disconnect is desired.
|
|
// Also, setting this flag means that no more I/O is legal on the
|
|
// endpoint until the transmit request has been completed, so
|
|
// set up the endpoint's state so that I/O fails.
|
|
//
|
|
if ( (AFD_GET_TPIC(Irp)->Flags & AFD_TF_REUSE_SOCKET) != 0 ) {
|
|
AFD_GET_TPIC(Irp)->Flags |= AFD_TF_DISCONNECT;
|
|
endpoint->State = AfdEndpointStateTransmitClosing;
|
|
}
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
REFERENCE_CONNECTION (connection);
|
|
}
|
|
else {
|
|
if (!AFD_PREVENT_STATE_CHANGE (endpoint)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
//
|
|
// We still need validate the state of the endpoint
|
|
// even if we do not perform the disconnect.
|
|
//
|
|
if ( endpoint->Type != AfdBlockTypeVcConnecting ||
|
|
endpoint->State != AfdEndpointStateConnected ) {
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
connection = endpoint->Common.VcConnecting.Connection;
|
|
REFERENCE_CONNECTION (connection);
|
|
AFD_REALLOW_STATE_CHANGE (endpoint);
|
|
}
|
|
|
|
|
|
//
|
|
// Connection endpoint, get connection file object and device
|
|
//
|
|
tpInfo->TdiFileObject = connection->FileObject;
|
|
tpInfo->TdiDeviceObject = connection->DeviceObject;
|
|
|
|
UPDATE_TPACKETS2 (Irp, "Connection object handle: 0x%lX",
|
|
HandleToUlong(connection->Handle));
|
|
//
|
|
// Save tpacket info in the IRP
|
|
//
|
|
Irp->AssociatedIrp.SystemBuffer = tpInfo;
|
|
|
|
//
|
|
// Clear the Flink in the IRP to indicate this IRP is not queued.
|
|
// Blink is set to indicate that IRP was not counted towards
|
|
// active maximum (so if it is completed, we do not start the next one).
|
|
//
|
|
|
|
Irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
Irp->Tail.Overlay.ListEntry.Blink = (PVOID)1;
|
|
|
|
//
|
|
// Initialize the IRP result fields
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// We are going to pend this IRP
|
|
//
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// Initialize queuing and state information.
|
|
//
|
|
AFD_GET_TPIC (Irp)->Next = NULL;
|
|
AFD_GET_TPIC(Irp)->ReferenceCount = 1;
|
|
AFD_GET_TPIC(Irp)->StateFlags = AFD_TP_WORKER_SCHEDULED;
|
|
|
|
if ((InterlockedCompareExchangePointer ((PVOID *)&endpoint->Irp,
|
|
Irp,
|
|
NULL)==NULL) ||
|
|
!AfdEnqueueTPacketsIrp (endpoint, Irp)) {
|
|
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelTPackets );
|
|
//
|
|
// Check to see if this Irp has been cancelled.
|
|
//
|
|
|
|
if ( !endpoint->EndpointCleanedUp && !Irp->Cancel ) {
|
|
//
|
|
// Determine if we can really start this file transmit now. If we've
|
|
// exceeded the configured maximum number of active TransmitFile/Packets
|
|
// requests, then append this IRP to the TransmitFile/Packets queue and set
|
|
// a flag in the transmit info structure to indicate that this IRP
|
|
// is queued.
|
|
//
|
|
|
|
if( AfdMaxActiveTransmitFileCount == 0 || !AfdQueueTransmit (Irp)) {
|
|
//
|
|
// Start I/O
|
|
//
|
|
UPDATE_ENDPOINT(endpoint);
|
|
AfdTPacketsWorker (Irp);
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Abort the request
|
|
// Note that neither cancel nor endpoint cleanup can complete
|
|
// the IRP since we hold the reference to the tpInfo structure.
|
|
//
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
|
|
//
|
|
// Remove the initial reference and force completion.
|
|
//
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
}
|
|
}
|
|
|
|
DEREFERENCE_CONNECTION (connection);
|
|
return STATUS_PENDING;
|
|
|
|
|
|
complete_state_change:
|
|
|
|
ASSERT ( tpInfo != NULL );
|
|
ASSERT ( endpoint->Irp != Irp );
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
|
|
complete:
|
|
|
|
//
|
|
// If necessary, tell the caller that we encountered an error
|
|
// when accessing file not socket.
|
|
//
|
|
|
|
if (fileError && IrpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(BOOLEAN)) {
|
|
if (Irp->RequestorMode != KernelMode) {
|
|
try {
|
|
ProbeAndWriteBoolean((BOOLEAN *)Irp->UserBuffer, TRUE);
|
|
} except(AFD_EXCEPTION_FILTER(status)) {
|
|
ASSERT(NT_ERROR(status));
|
|
}
|
|
}
|
|
else {
|
|
*((BOOLEAN *)Irp->UserBuffer) = TRUE;
|
|
}
|
|
}
|
|
|
|
if (tpInfo!=NULL) {
|
|
AfdReturnTpInfo (tpInfo);
|
|
}
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdTransmitPackets: Failing Irp-%p,endpoint-%p,status-%lx\n",
|
|
Irp,endpoint,status));
|
|
}
|
|
|
|
//
|
|
// Complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
|
|
} // AfdTransmitFile
|
|
|
|
|
|
NTSTATUS
|
|
FASTCALL
|
|
AfdSuperDisconnect (
|
|
IN PIRP Irp,
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initial entrypoint for handling transmit file IRPs. This routine
|
|
verifies parameters, initializes data structures to be used for
|
|
the request, and initiates the I/O.
|
|
|
|
Arguments:
|
|
|
|
Irp - a pointer to a transmit file IRP.
|
|
|
|
IrpSp - Our stack location for this IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the request was initiated successfully, or a
|
|
failure status code if there was an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAFD_ENDPOINT endpoint;
|
|
NTSTATUS status;
|
|
AFD_SUPER_DISCONNECT_INFO params;
|
|
|
|
PAGED_CODE ();
|
|
|
|
//
|
|
// Initial request validity checks: is the endpoint connected, is
|
|
// the input buffer large enough, etc.
|
|
//
|
|
|
|
endpoint = IrpSp->FileObject->FsContext;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
//
|
|
// Special hack to let the user mode dll know that it
|
|
// should try SAN provider instead.
|
|
//
|
|
|
|
if (IS_SAN_ENDPOINT (endpoint)) {
|
|
status = STATUS_INVALID_PARAMETER_12;
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Because we're using type 3 (neither) I/O for this IRP, the I/O
|
|
// system does no verification on the user buffer. Therefore, we
|
|
// must manually check it for validity inside a try-except block.
|
|
// We also leverage the try-except to validate and lock down the
|
|
// head and/or tail buffers specified by the caller.
|
|
//
|
|
|
|
AFD_W4_INIT status = STATUS_SUCCESS;
|
|
try {
|
|
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess (Irp)) {
|
|
PAFD_SUPER_DISCONNECT_INFO32 pInfo = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
pInfo,
|
|
sizeof(AFD_SUPER_DISCONNECT_INFO32),
|
|
PROBE_ALIGNMENT32 (AFD_SUPER_DISCONNECT_INFO32)
|
|
);
|
|
}
|
|
params.Flags = ((PAFD_SUPER_DISCONNECT_INFO32)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer)->Flags;
|
|
}
|
|
else
|
|
#endif _WIN64
|
|
{
|
|
if( Irp->RequestorMode != KernelMode ) {
|
|
//
|
|
// Validate the control buffer.
|
|
//
|
|
|
|
ProbeForReadSmallStructure(
|
|
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer,
|
|
sizeof(AFD_SUPER_DISCONNECT_INFO),
|
|
PROBE_ALIGNMENT (AFD_SUPER_DISCONNECT_INFO)
|
|
);
|
|
}
|
|
|
|
params = *((PAFD_SUPER_DISCONNECT_INFO)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer);
|
|
}
|
|
|
|
|
|
//
|
|
// Validate any flags specified in the request.
|
|
// and make sure that file offset is positive (of course if file is specified)
|
|
//
|
|
|
|
if ( (params.Flags & (~AFD_TF_REUSE_SOCKET)) != 0 ){
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto complete;
|
|
}
|
|
|
|
} except( AFD_EXCEPTION_FILTER (status) ) {
|
|
ASSERT (NT_ERROR (status));
|
|
goto complete;
|
|
}
|
|
|
|
//
|
|
// Store disconnect parameters/flags in the IRP.
|
|
// AFD_TF_DISCONNECT implied in the API
|
|
|
|
AFD_GET_TPIC(Irp)->Flags = params.Flags | AFD_TF_DISCONNECT;
|
|
|
|
if (!AFD_START_STATE_CHANGE (endpoint, endpoint->State)) {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Allow disconnect if we are in connected state or
|
|
// in transmit closing (the previous transmit file/packets
|
|
// with reuse must have failed) and reuse is requested.
|
|
// The second condition still allows the application to reuse aborted
|
|
// or otherwise failed socket.
|
|
//
|
|
if (endpoint->Type == AfdBlockTypeVcConnecting &&
|
|
(endpoint->State == AfdEndpointStateConnected ||
|
|
(endpoint->State == AfdEndpointStateTransmitClosing &&
|
|
(params.Flags & AFD_TF_REUSE_SOCKET)!=0
|
|
)
|
|
)
|
|
) {
|
|
//
|
|
// Setting AFD_TF_REUSE_SOCKET implies that a disconnect is desired.
|
|
// Also, setting this flag means that no more I/O is legal on the
|
|
// endpoint until the transmit request has been completed, so
|
|
// set up the endpoint's state so that I/O fails.
|
|
//
|
|
|
|
if ( (params.Flags & AFD_TF_REUSE_SOCKET) != 0 ) {
|
|
endpoint->State = AfdEndpointStateTransmitClosing;
|
|
}
|
|
}
|
|
else {
|
|
status = STATUS_INVALID_CONNECTION;
|
|
goto complete_state_change;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Set tpacket info to NULL to indicate pure disconnect IRP
|
|
//
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
|
|
//
|
|
// Clear the Flink in the IRP to indicate this IRP is not queued.
|
|
// Blink is set to indicate that IRP was not counted towards
|
|
// active maximum (so if it is completed, we do not start the next one).
|
|
//
|
|
|
|
Irp->Tail.Overlay.ListEntry.Flink = NULL;
|
|
Irp->Tail.Overlay.ListEntry.Blink = (PVOID)1;
|
|
|
|
//
|
|
// Initialize the IRP result fields
|
|
//
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// We are going to pend this IRP
|
|
//
|
|
IoMarkIrpPending( Irp );
|
|
|
|
//
|
|
// Initialize queuing and state information.
|
|
//
|
|
AFD_GET_TPIC (Irp)->Next = NULL;
|
|
AFD_GET_TPIC(Irp)->ReferenceCount = 1;
|
|
AFD_GET_TPIC(Irp)->StateFlags = AFD_TP_WORKER_SCHEDULED;
|
|
|
|
if ((InterlockedCompareExchangePointer ((PVOID *)&endpoint->Irp,
|
|
Irp,
|
|
NULL)==NULL) ||
|
|
!AfdEnqueueTPacketsIrp (endpoint, Irp)) {
|
|
|
|
|
|
IoSetCancelRoutine( Irp, AfdCancelTPackets );
|
|
//
|
|
// Check to see if this Irp has been cancelled.
|
|
//
|
|
|
|
if ( !endpoint->EndpointCleanedUp && !Irp->Cancel ) {
|
|
//
|
|
// Start I/O
|
|
//
|
|
UPDATE_ENDPOINT (endpoint);
|
|
AfdPerformTpDisconnect (Irp);
|
|
}
|
|
else {
|
|
//
|
|
// Abort the request
|
|
// Note that neither cancel nor endpoint cleanup can complete
|
|
// the IRP since we hold the reference to the tpInfo structure.
|
|
//
|
|
AfdAbortTPackets (Irp, STATUS_CANCELLED);
|
|
|
|
}
|
|
|
|
//
|
|
// Remove the initial reference and force completion processing.
|
|
//
|
|
|
|
DEREFERENCE_TPACKETS (Irp);
|
|
}
|
|
|
|
|
|
return STATUS_PENDING;
|
|
|
|
|
|
complete_state_change:
|
|
|
|
ASSERT ( endpoint->Irp != Irp );
|
|
AFD_END_STATE_CHANGE (endpoint);
|
|
|
|
complete:
|
|
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdSuperDisconnect: Failing Irp-%p,endpoint-%p,status-%lx\n",
|
|
Irp,endpoint,status));
|
|
}
|
|
//
|
|
// Complete the request.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest( Irp, 0 );
|
|
|
|
return status;
|
|
|
|
} // AfdSuperDisconnect
|
|
|
|
|
|
VOID
|
|
AfdPerformTpDisconnect (
|
|
PIRP TpIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
DisconnectEx engine
|
|
Arguments:
|
|
|
|
TpIrp - pointer to TransmitPackets Irp for the request
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (TpIrp);
|
|
PAFD_ENDPOINT endpoint = irpSp->FileObject->FsContext;
|
|
NTSTATUS status;
|
|
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
|
|
|
status = AfdBeginDisconnect(
|
|
endpoint,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS (status)) {
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdPerformTpDisconnect: Initiated disconnect, tpIrp-%p,status-%lx\n",
|
|
TpIrp, status));
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Disconnect failed, we'll have to abort below.
|
|
//
|
|
IF_DEBUG (TRANSMIT) {
|
|
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
|
"AfdPerformTpDisconnect: TpInfo-%p, begin discon failed: %lx\n",
|
|
TpIrp, status));
|
|
}
|
|
AfdAbortTPackets (TpIrp, status);
|
|
}
|
|
}
|