3333 lines
110 KiB
C
3333 lines
110 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fastio.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines for handling fast ("turbo") IO
|
||
in AFD.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 12-Oct-1992
|
||
|
||
Revision History:
|
||
VadimE 14-Jan-1998 Restructurred the code.
|
||
|
||
--*/
|
||
|
||
#include "afdp.h"
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastConnectionReceive (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_RECV_INFO recvInfo,
|
||
IN ULONG recvLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
);
|
||
|
||
BOOLEAN
|
||
AfdFastDatagramReceive (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_RECV_MESSAGE_INFO recvInfo,
|
||
IN ULONG recvLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
);
|
||
|
||
BOOLEAN
|
||
AfdFastConnectionSend (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_SEND_INFO sendInfo,
|
||
IN ULONG sendLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
);
|
||
|
||
BOOLEAN
|
||
AfdFastDatagramSend (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_SEND_DATAGRAM_INFO sendInfo,
|
||
IN ULONG sendLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
);
|
||
|
||
NTSTATUS
|
||
AfdRestartFastDatagramSend (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, AfdFastIoDeviceControl )
|
||
#pragma alloc_text( PAGE, AfdFastIoRead )
|
||
#pragma alloc_text( PAGE, AfdFastIoWrite )
|
||
#pragma alloc_text( PAGEAFD, AfdFastDatagramSend )
|
||
#pragma alloc_text( PAGEAFD, AfdFastDatagramReceive )
|
||
#pragma alloc_text( PAGEAFD, AfdFastConnectionSend )
|
||
#pragma alloc_text( PAGEAFD, AfdFastConnectionReceive )
|
||
#pragma alloc_text( PAGEAFD, AfdRestartFastDatagramSend )
|
||
#pragma alloc_text( PAGEAFD, AfdShouldSendBlock )
|
||
#endif
|
||
|
||
|
||
#if AFD_PERF_DBG
|
||
BOOLEAN
|
||
AfdFastIoReadReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
);
|
||
|
||
BOOLEAN
|
||
AfdFastIoRead (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
{
|
||
BOOLEAN success;
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
|
||
success = AfdFastIoReadReal (
|
||
FileObject,
|
||
FileOffset,
|
||
Length,
|
||
Wait,
|
||
LockKey,
|
||
Buffer,
|
||
IoStatus,
|
||
DeviceObject
|
||
);
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastReadsSucceeded);
|
||
ASSERT (IoStatus->Status == STATUS_SUCCESS ||
|
||
IoStatus->Status == STATUS_DEVICE_NOT_READY );
|
||
} else {
|
||
InterlockedIncrement (&AfdFastReadsFailed);
|
||
}
|
||
return success;
|
||
}
|
||
|
||
BOOLEAN
|
||
AfdFastIoReadReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
|
||
#else // AFD_PERF_DBG
|
||
|
||
BOOLEAN
|
||
AfdFastIoRead (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
#endif // AFD_PERF_DBG
|
||
{
|
||
|
||
PAFD_ENDPOINT endpoint;
|
||
WSABUF buf;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// All we want to do is pass the request through to the TDI provider
|
||
// if possible. If not, we want to bail out of this code path back
|
||
// onto the main code path (with IRPs) with as little performance
|
||
// overhead as possible.
|
||
//
|
||
// Thus this routine only does general preliminary checks and input
|
||
// parameter validation. If it is determined that fast io path is
|
||
// likely to succeed, an operation specific routine is called
|
||
// to handle all the details.
|
||
//
|
||
|
||
endpoint = FileObject->FsContext;
|
||
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
||
|
||
|
||
//
|
||
// If fast IO recv is disabled
|
||
// or the endpoint is shut down in any way
|
||
// or the endpoint isn't connected yet
|
||
// or the TDI provider for this endpoint supports bufferring,
|
||
// we do not want to do fast IO on it
|
||
//
|
||
if (endpoint->DisableFastIoRecv ||
|
||
endpoint->DisconnectMode != 0 ||
|
||
endpoint->State != AfdEndpointStateConnected ||
|
||
IS_TDI_BUFFERRING(endpoint)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Fake buffer array.
|
||
//
|
||
|
||
buf.buf = Buffer;
|
||
buf.len = Length;
|
||
|
||
//
|
||
// Call routine based on endpoint type
|
||
//
|
||
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
||
//
|
||
// Fake input parameter strucuture
|
||
//
|
||
AFD_RECV_MESSAGE_INFO msgInfo;
|
||
|
||
msgInfo.dgi.BufferArray = &buf;
|
||
msgInfo.dgi.BufferCount = 1;
|
||
msgInfo.dgi.AfdFlags = AFD_OVERLAPPED;
|
||
msgInfo.dgi.TdiFlags = TDI_RECEIVE_NORMAL;
|
||
msgInfo.dgi.Address = NULL;
|
||
msgInfo.dgi.AddressLength = 0;
|
||
msgInfo.ControlBuffer = NULL;
|
||
msgInfo.ControlLength = NULL;
|
||
msgInfo.MsgFlags = NULL;
|
||
|
||
return AfdFastDatagramReceive(
|
||
endpoint,
|
||
&msgInfo,
|
||
Length,
|
||
IoStatus
|
||
);
|
||
}
|
||
else if (IS_VC_ENDPOINT(endpoint)) {
|
||
//
|
||
// Fake input parameter strucuture
|
||
//
|
||
AFD_RECV_INFO recvInfo;
|
||
|
||
recvInfo.BufferArray = &buf;
|
||
recvInfo.BufferCount = 1;
|
||
recvInfo.AfdFlags = AFD_OVERLAPPED;
|
||
recvInfo.TdiFlags = TDI_RECEIVE_NORMAL;
|
||
|
||
return AfdFastConnectionReceive (
|
||
endpoint,
|
||
&recvInfo,
|
||
Length,
|
||
IoStatus);
|
||
}
|
||
else
|
||
return FALSE;
|
||
|
||
} // AfdFastIoRead
|
||
|
||
#if AFD_PERF_DBG
|
||
BOOLEAN
|
||
AfdFastIoWriteReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
);
|
||
|
||
BOOLEAN
|
||
AfdFastIoWrite (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
OUT PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
{
|
||
BOOLEAN success;
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
|
||
success = AfdFastIoWriteReal (
|
||
FileObject,
|
||
FileOffset,
|
||
Length,
|
||
Wait,
|
||
LockKey,
|
||
Buffer,
|
||
IoStatus,
|
||
DeviceObject
|
||
);
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastWritesSucceeded);
|
||
ASSERT (IoStatus->Status == STATUS_SUCCESS ||
|
||
IoStatus->Status == STATUS_DEVICE_NOT_READY);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastWritesFailed);
|
||
}
|
||
return success;
|
||
}
|
||
|
||
BOOLEAN
|
||
AfdFastIoWriteReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
IN PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
|
||
#else // AFD_PERF_DBG
|
||
|
||
BOOLEAN
|
||
AfdFastIoWrite (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN BOOLEAN Wait,
|
||
IN ULONG LockKey,
|
||
IN PVOID Buffer,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
#endif // AFD_PERF_DBG
|
||
{
|
||
|
||
|
||
PAFD_ENDPOINT endpoint;
|
||
WSABUF buf;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// All we want to do is pass the request through to the TDI provider
|
||
// if possible. If not, we want to bail out of this code path back
|
||
// onto the main code path (with IRPs) with as little performance
|
||
// overhead as possible.
|
||
//
|
||
// Thus this routine only does general preliminary checks and input
|
||
// parameter validation. If it is determined that fast io path is
|
||
// likely to succeed, an operation specific routine is called
|
||
// to handle all the details.
|
||
//
|
||
|
||
endpoint = FileObject->FsContext;
|
||
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
||
|
||
|
||
//
|
||
// If fast IO send is disabled
|
||
// or the endpoint is shut down in any way
|
||
// or the endpoint isn't connected yet
|
||
// or the TDI provider for this endpoint supports bufferring,
|
||
// we do not want to do fast IO on it
|
||
//
|
||
if (endpoint->DisableFastIoSend ||
|
||
endpoint->DisconnectMode != 0 ||
|
||
endpoint->State != AfdEndpointStateConnected ||
|
||
IS_TDI_BUFFERRING(endpoint) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Fake buffer array.
|
||
//
|
||
buf.buf = Buffer;
|
||
buf.len = Length;
|
||
|
||
//
|
||
// Call routine based on endpoint type
|
||
//
|
||
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
||
//
|
||
// Fake input parameter strucuture
|
||
//
|
||
AFD_SEND_DATAGRAM_INFO sendInfo;
|
||
|
||
sendInfo.BufferArray = &buf;
|
||
sendInfo.BufferCount = 1;
|
||
sendInfo.AfdFlags = AFD_OVERLAPPED;
|
||
sendInfo.TdiConnInfo.RemoteAddress = NULL;
|
||
sendInfo.TdiConnInfo.RemoteAddressLength = 0;
|
||
|
||
return AfdFastDatagramSend(
|
||
endpoint,
|
||
&sendInfo,
|
||
Length,
|
||
IoStatus
|
||
);
|
||
}
|
||
else if (IS_VC_ENDPOINT (endpoint)) {
|
||
//
|
||
// Fake input parameter strucuture
|
||
//
|
||
AFD_SEND_INFO sendInfo;
|
||
|
||
sendInfo.BufferArray = &buf;
|
||
sendInfo.BufferCount = 1;
|
||
sendInfo.AfdFlags = AFD_OVERLAPPED;
|
||
sendInfo.TdiFlags = 0;
|
||
|
||
return AfdFastConnectionSend (
|
||
endpoint,
|
||
&sendInfo,
|
||
Length,
|
||
IoStatus);
|
||
}
|
||
else
|
||
return FALSE;
|
||
} // AfdFastIoWrite
|
||
|
||
#if AFD_PERF_DBG
|
||
|
||
BOOLEAN
|
||
AfdFastIoDeviceControlReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN BOOLEAN Wait,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN ULONG InputBufferLength,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
IN ULONG OutputBufferLength,
|
||
IN ULONG IoControlCode,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
);
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastIoDeviceControl (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN BOOLEAN Wait,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN ULONG InputBufferLength,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
IN ULONG OutputBufferLength,
|
||
IN ULONG IoControlCode,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
{
|
||
BOOLEAN success;
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
|
||
success = AfdFastIoDeviceControlReal (
|
||
FileObject,
|
||
Wait,
|
||
InputBuffer,
|
||
InputBufferLength,
|
||
OutputBuffer,
|
||
OutputBufferLength,
|
||
IoControlCode,
|
||
IoStatus,
|
||
DeviceObject
|
||
);
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == LOW_LEVEL );
|
||
|
||
switch ( IoControlCode ) {
|
||
|
||
case IOCTL_AFD_SEND:
|
||
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastSendsSucceeded);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastSendsFailed);
|
||
}
|
||
break;
|
||
|
||
case IOCTL_AFD_RECEIVE:
|
||
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastReceivesSucceeded);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastReceivesFailed);
|
||
}
|
||
break;
|
||
|
||
case IOCTL_AFD_SEND_DATAGRAM:
|
||
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastSendDatagramsSucceeded);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastSendDatagramsFailed);
|
||
}
|
||
break;
|
||
|
||
case IOCTL_AFD_RECEIVE_MESSAGE:
|
||
case IOCTL_AFD_RECEIVE_DATAGRAM:
|
||
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastReceiveDatagramsSucceeded);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastReceiveDatagramsFailed);
|
||
}
|
||
break;
|
||
case IOCTL_AFD_TRANSMIT_FILE:
|
||
|
||
if ( success ) {
|
||
InterlockedIncrement (&AfdFastTfSucceeded);
|
||
} else {
|
||
InterlockedIncrement (&AfdFastTfFailed);
|
||
}
|
||
break;
|
||
}
|
||
|
||
|
||
return success;
|
||
|
||
} // AfdFastIoDeviceControl
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastIoDeviceControlReal (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN BOOLEAN Wait,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN ULONG InputBufferLength,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
IN ULONG OutputBufferLength,
|
||
IN ULONG IoControlCode,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
#else
|
||
BOOLEAN
|
||
AfdFastIoDeviceControl (
|
||
IN struct _FILE_OBJECT *FileObject,
|
||
IN BOOLEAN Wait,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN ULONG InputBufferLength,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
IN ULONG OutputBufferLength,
|
||
IN ULONG IoControlCode,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN struct _DEVICE_OBJECT *DeviceObject
|
||
)
|
||
#endif
|
||
{
|
||
PAFD_ENDPOINT endpoint;
|
||
KPROCESSOR_MODE previousMode;
|
||
BOOLEAN res;
|
||
PAFD_IMMEDIATE_CALL proc;
|
||
ULONG request;
|
||
|
||
#ifdef _WIN64
|
||
WSABUF localArray[8];
|
||
LPWSABUF pArray = localArray;
|
||
#endif
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// All we want to do is pass the request through to the TDI provider
|
||
// if possible. If not, we want to bail out of this code path back
|
||
// onto the main code path (with IRPs) with as little performance
|
||
// overhead as possible.
|
||
//
|
||
// Thus this routine only does general preliminary checks and input
|
||
// parameter validation. If it is determined that fast io path is
|
||
// likely to succeed, an operation specific routine is called
|
||
// to handle all the details.
|
||
//
|
||
|
||
//
|
||
// First get the endpoint pointer and previous mode for input parameter
|
||
// validation.
|
||
//
|
||
|
||
endpoint = FileObject->FsContext;
|
||
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
|
||
|
||
previousMode = ExGetPreviousMode ();
|
||
|
||
//
|
||
// Switch based on control code
|
||
//
|
||
switch (IoControlCode) {
|
||
case IOCTL_AFD_RECEIVE:
|
||
{
|
||
union {
|
||
AFD_RECV_INFO recvInfo;
|
||
AFD_RECV_MESSAGE_INFO msgInfo;
|
||
} u;
|
||
ULONG recvLength;
|
||
|
||
#if !defined (_ALPHA_)
|
||
//
|
||
// Alpha compiler does not see that the following FIELD_OFFSET
|
||
// is in fact a constant expression.
|
||
//
|
||
//
|
||
// Check the validity of the union above.
|
||
//
|
||
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.BufferArray)
|
||
== FIELD_OFFSET (AFD_RECV_INFO, BufferArray));
|
||
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.BufferCount)
|
||
== FIELD_OFFSET (AFD_RECV_INFO, BufferCount));
|
||
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.AfdFlags)
|
||
== FIELD_OFFSET (AFD_RECV_INFO, AfdFlags));
|
||
C_ASSERT (FIELD_OFFSET (AFD_RECV_MESSAGE_INFO, dgi.TdiFlags)
|
||
== FIELD_OFFSET (AFD_RECV_INFO, TdiFlags));
|
||
|
||
//
|
||
#endif //!defined (_ALPHA_)
|
||
//
|
||
// If fast IO send is disabled
|
||
// or the endpoint is shut down in any way
|
||
// or the endpoint isn't connected yet
|
||
// or the TDI provider for this endpoint supports bufferring,
|
||
// we do not want to do fast IO on it
|
||
//
|
||
if (endpoint->DisableFastIoRecv ||
|
||
endpoint->DisconnectMode != 0 ||
|
||
endpoint->State != AfdEndpointStateConnected ||
|
||
IS_TDI_BUFFERRING(endpoint) ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
try {
|
||
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_RECV_INFO32 recvInfo32;
|
||
LPWSABUF32 tempArray;
|
||
ULONG i;
|
||
|
||
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(*recvInfo32) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*recvInfo32),
|
||
PROBE_ALIGNMENT32(AFD_RECV_INFO32));
|
||
}
|
||
|
||
recvInfo32 = InputBuffer;
|
||
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
tempArray = recvInfo32->BufferArray;
|
||
u.recvInfo.BufferCount = recvInfo32->BufferCount;
|
||
u.recvInfo.AfdFlags = recvInfo32->AfdFlags;
|
||
u.recvInfo.TdiFlags = recvInfo32->TdiFlags;
|
||
|
||
//
|
||
// If fast IO is not possible or this is not a normal receive.
|
||
// bail.
|
||
//
|
||
if( (u.recvInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
||
u.recvInfo.TdiFlags != TDI_RECEIVE_NORMAL ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us and
|
||
// calculate the length of the send buffer.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((tempArray == NULL) ||
|
||
(u.recvInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(u.recvInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
tempArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
u.recvInfo.BufferCount * sizeof (WSABUF32), // Length
|
||
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
||
);
|
||
}
|
||
|
||
if (u.recvInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
||
try {
|
||
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
||
NonPagedPool,
|
||
sizeof (WSABUF)*u.recvInfo.BufferCount,
|
||
AFD_TEMPORARY_POOL_TAG);
|
||
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
||
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
||
ASSERT (pArray!=NULL);
|
||
}
|
||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
pArray = localArray;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i=0; i<u.recvInfo.BufferCount; i++) {
|
||
pArray[i].buf = tempArray[i].buf;
|
||
pArray[i].len = tempArray[i].len;
|
||
}
|
||
|
||
u.recvInfo.BufferArray = pArray;
|
||
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(u.recvInfo) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (u.recvInfo),
|
||
PROBE_ALIGNMENT(AFD_RECV_INFO));
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
|
||
u.recvInfo = *((PAFD_RECV_INFO)InputBuffer);
|
||
//
|
||
// If fast IO is not possible or this is not a normal receive.
|
||
// bail.
|
||
//
|
||
if( (u.recvInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
||
u.recvInfo.TdiFlags != TDI_RECEIVE_NORMAL ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us and
|
||
// calculate the length of the send buffer.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((u.recvInfo.BufferArray == NULL) ||
|
||
(u.recvInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(u.recvInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
u.recvInfo.BufferArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
u.recvInfo.BufferCount * sizeof (WSABUF), // Length
|
||
PROBE_ALIGNMENT(WSABUF) // Alignment
|
||
);
|
||
}
|
||
}
|
||
|
||
recvLength = AfdCalcBufferArrayByteLength(
|
||
u.recvInfo.BufferArray,
|
||
u.recvInfo.BufferCount
|
||
);
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Call routine based on endpoint type
|
||
//
|
||
if ( IS_DGRAM_ENDPOINT(endpoint) ) {
|
||
u.msgInfo.dgi.Address = NULL;
|
||
u.msgInfo.dgi.AddressLength = 0;
|
||
u.msgInfo.ControlBuffer = NULL;
|
||
u.msgInfo.ControlLength = NULL;
|
||
u.msgInfo.MsgFlags = NULL;
|
||
|
||
|
||
res = AfdFastDatagramReceive(
|
||
endpoint,
|
||
&u.msgInfo,
|
||
recvLength,
|
||
IoStatus
|
||
);
|
||
}
|
||
else if (IS_VC_ENDPOINT (endpoint)) {
|
||
res = AfdFastConnectionReceive (
|
||
endpoint,
|
||
&u.recvInfo,
|
||
recvLength,
|
||
IoStatus);
|
||
}
|
||
else
|
||
res = FALSE;
|
||
|
||
}
|
||
break;
|
||
|
||
case IOCTL_AFD_RECEIVE_DATAGRAM:
|
||
case IOCTL_AFD_RECEIVE_MESSAGE:
|
||
{
|
||
AFD_RECV_MESSAGE_INFO msgInfo;
|
||
ULONG recvLength;
|
||
|
||
if (endpoint->DisableFastIoRecv ||
|
||
!IS_DGRAM_ENDPOINT(endpoint) ||
|
||
((endpoint->State != AfdEndpointStateBound ) &&
|
||
(endpoint->State != AfdEndpointStateConnected)) ) {
|
||
return FALSE;
|
||
}
|
||
if (IoControlCode==IOCTL_AFD_RECEIVE_MESSAGE) {
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_RECV_MESSAGE_INFO32 msgInfo32;
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(*msgInfo32) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*msgInfo32),
|
||
PROBE_ALIGNMENT32 (AFD_RECV_MESSAGE_INFO32));
|
||
}
|
||
|
||
msgInfo32 = InputBuffer;
|
||
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
msgInfo.ControlBuffer = msgInfo32->ControlBuffer;
|
||
msgInfo.ControlLength = msgInfo32->ControlLength;
|
||
msgInfo.MsgFlags = msgInfo32->MsgFlags;
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
|
||
if( InputBufferLength < sizeof(msgInfo) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Capture the input structure.
|
||
//
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (msgInfo),
|
||
PROBE_ALIGNMENT (AFD_RECV_MESSAGE_INFO));
|
||
}
|
||
msgInfo = *(PAFD_RECV_MESSAGE_INFO)InputBuffer;
|
||
}
|
||
if (previousMode != KernelMode ) {
|
||
|
||
ProbeForWrite (msgInfo.MsgFlags,
|
||
sizeof (*msgInfo.MsgFlags),
|
||
PROBE_ALIGNMENT (ULONG));
|
||
ProbeForWrite (msgInfo.ControlLength,
|
||
sizeof (*msgInfo.ControlLength),
|
||
PROBE_ALIGNMENT (ULONG));
|
||
//
|
||
// Checking of recvInfo->Address is postponed till
|
||
// we know the length of the address.
|
||
//
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
msgInfo.ControlBuffer = NULL;
|
||
msgInfo.ControlLength = NULL;
|
||
msgInfo.MsgFlags = NULL;
|
||
}
|
||
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
try {
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_RECV_DATAGRAM_INFO32 recvInfo32;
|
||
LPWSABUF32 tempArray;
|
||
ULONG i;
|
||
|
||
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(*recvInfo32) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*recvInfo32),
|
||
PROBE_ALIGNMENT32 (AFD_RECV_DATAGRAM_INFO32));
|
||
}
|
||
|
||
recvInfo32 = InputBuffer;
|
||
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
tempArray = recvInfo32->BufferArray;
|
||
msgInfo.dgi.BufferCount = recvInfo32->BufferCount;
|
||
msgInfo.dgi.AfdFlags = recvInfo32->AfdFlags;
|
||
msgInfo.dgi.TdiFlags = recvInfo32->TdiFlags;
|
||
msgInfo.dgi.Address = recvInfo32->Address;
|
||
msgInfo.dgi.AddressLength = recvInfo32->AddressLength;
|
||
|
||
//
|
||
// If fast IO is not possible or this is not a normal receive.
|
||
// bail.
|
||
//
|
||
if( (msgInfo.dgi.AfdFlags & AFD_NO_FAST_IO) ||
|
||
msgInfo.dgi.TdiFlags != TDI_RECEIVE_NORMAL ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us and
|
||
// calculate the length of the send buffer.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((tempArray == NULL) ||
|
||
(msgInfo.dgi.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(msgInfo.dgi.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
tempArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
msgInfo.dgi.BufferCount * sizeof (WSABUF32), // Length
|
||
PROBE_ALIGNMENT (WSABUF32) // Alignment
|
||
);
|
||
}
|
||
|
||
if (msgInfo.dgi.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
||
try {
|
||
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
||
NonPagedPool,
|
||
sizeof (WSABUF)*msgInfo.dgi.BufferCount,
|
||
AFD_TEMPORARY_POOL_TAG);
|
||
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
||
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
||
ASSERT (pArray!=NULL);
|
||
}
|
||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
pArray = localArray;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i=0; i<msgInfo.dgi.BufferCount; i++) {
|
||
pArray[i].buf = tempArray[i].buf;
|
||
pArray[i].len = tempArray[i].len;
|
||
}
|
||
|
||
msgInfo.dgi.BufferArray = pArray;
|
||
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
if( InputBufferLength < sizeof(AFD_RECV_DATAGRAM_INFO) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Capture the input structure.
|
||
//
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (AFD_RECV_DATAGRAM_INFO),
|
||
PROBE_ALIGNMENT (AFD_RECV_DATAGRAM_INFO));
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
|
||
msgInfo.dgi = *(PAFD_RECV_DATAGRAM_INFO)InputBuffer;
|
||
|
||
//
|
||
// If fast IO is disabled or this is not a simple
|
||
// recv, fail
|
||
//
|
||
|
||
if( (msgInfo.dgi.AfdFlags & AFD_NO_FAST_IO) != 0 ||
|
||
msgInfo.dgi.TdiFlags != TDI_RECEIVE_NORMAL ||
|
||
( (msgInfo.dgi.Address == NULL) ^
|
||
(msgInfo.dgi.AddressLength == NULL) ) ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us.
|
||
// and calculate total recv length.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((msgInfo.dgi.BufferArray == NULL) ||
|
||
(msgInfo.dgi.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(msgInfo.dgi.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
msgInfo.dgi.BufferArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
msgInfo.dgi.BufferCount * sizeof (WSABUF), // Length
|
||
PROBE_ALIGNMENT(WSABUF) // Alignment
|
||
);
|
||
}
|
||
}
|
||
|
||
recvLength = AfdCalcBufferArrayByteLength(
|
||
msgInfo.dgi.BufferArray,
|
||
msgInfo.dgi.BufferCount
|
||
);
|
||
|
||
if (previousMode != KernelMode ) {
|
||
if (msgInfo.dgi.AddressLength!=NULL) {
|
||
ProbeForWrite (msgInfo.dgi.AddressLength,
|
||
sizeof (*msgInfo.dgi.AddressLength),
|
||
PROBE_ALIGNMENT (ULONG));
|
||
}
|
||
//
|
||
// Checking of recvInfo->Address is postponed till
|
||
// we know the length of the address.
|
||
//
|
||
|
||
}
|
||
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
res = FALSE;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Attempt to perform fast IO on the endpoint.
|
||
//
|
||
|
||
res = AfdFastDatagramReceive(
|
||
endpoint,
|
||
&msgInfo,
|
||
recvLength,
|
||
IoStatus
|
||
);
|
||
|
||
}
|
||
break;
|
||
|
||
case IOCTL_AFD_SEND:
|
||
{
|
||
union {
|
||
AFD_SEND_INFO sendInfo;
|
||
AFD_SEND_DATAGRAM_INFO sendInfoDg;
|
||
} u;
|
||
ULONG sendLength;
|
||
|
||
//
|
||
// Check the validity of the union above.
|
||
//
|
||
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, BufferArray)
|
||
== FIELD_OFFSET (AFD_SEND_INFO, BufferArray));
|
||
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, BufferCount)
|
||
== FIELD_OFFSET (AFD_SEND_INFO, BufferCount));
|
||
C_ASSERT (FIELD_OFFSET (AFD_SEND_DATAGRAM_INFO, AfdFlags)
|
||
== FIELD_OFFSET (AFD_SEND_INFO, AfdFlags));
|
||
|
||
//
|
||
// If fast IO send is disabled
|
||
// or the endpoint is shut down in any way
|
||
// or the endpoint isn't connected yet
|
||
// or the TDI provider for this endpoint supports bufferring,
|
||
// we do not want to do fast IO on it
|
||
//
|
||
if (endpoint->DisableFastIoSend ||
|
||
endpoint->DisconnectMode != 0 ||
|
||
endpoint->State != AfdEndpointStateConnected ||
|
||
IS_TDI_BUFFERRING(endpoint) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
try {
|
||
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_SEND_INFO32 sendInfo32;
|
||
LPWSABUF32 tempArray;
|
||
ULONG i;
|
||
|
||
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(*sendInfo32) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*sendInfo32),
|
||
PROBE_ALIGNMENT32 (AFD_SEND_INFO32));
|
||
}
|
||
|
||
sendInfo32 = InputBuffer;
|
||
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
tempArray = sendInfo32->BufferArray;
|
||
u.sendInfo.BufferCount = sendInfo32->BufferCount;
|
||
u.sendInfo.AfdFlags = sendInfo32->AfdFlags;
|
||
u.sendInfo.TdiFlags = sendInfo32->TdiFlags;
|
||
|
||
//
|
||
// If fast IO is not possible or this is not a normal receive.
|
||
// bail.
|
||
//
|
||
if( (u.sendInfo.AfdFlags & AFD_NO_FAST_IO) ||
|
||
u.sendInfo.TdiFlags != 0 ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us and
|
||
// calculate the length of the send buffer.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((tempArray == NULL) ||
|
||
(u.sendInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(u.sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
tempArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
u.sendInfo.BufferCount * sizeof (WSABUF32), // Length
|
||
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
||
);
|
||
}
|
||
|
||
if (u.sendInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
||
try {
|
||
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
||
NonPagedPool,
|
||
sizeof (WSABUF)*u.sendInfo.BufferCount,
|
||
AFD_TEMPORARY_POOL_TAG);
|
||
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
||
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
||
ASSERT (pArray!=NULL);
|
||
}
|
||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
pArray = localArray;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i=0; i<u.sendInfo.BufferCount; i++) {
|
||
pArray[i].buf = tempArray[i].buf;
|
||
pArray[i].len = tempArray[i].len;
|
||
}
|
||
|
||
u.sendInfo.BufferArray = pArray;
|
||
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(u.sendInfo) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (u.sendInfo),
|
||
PROBE_ALIGNMENT(AFD_SEND_INFO));
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
u.sendInfo = *((PAFD_SEND_INFO)InputBuffer);
|
||
|
||
if( (u.sendInfo.AfdFlags & AFD_NO_FAST_IO) != 0 ||
|
||
u.sendInfo.TdiFlags != 0 ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
if ((u.sendInfo.BufferArray == NULL) ||
|
||
(u.sendInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(u.sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
u.sendInfo.BufferArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
u.sendInfo.BufferCount * sizeof (WSABUF), // Length
|
||
PROBE_ALIGNMENT(WSABUF) // Alignment
|
||
);
|
||
}
|
||
|
||
}
|
||
sendLength = AfdCalcBufferArrayByteLength(
|
||
u.sendInfo.BufferArray,
|
||
u.sendInfo.BufferCount
|
||
);
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
if (IS_DGRAM_ENDPOINT (endpoint)) {
|
||
u.sendInfoDg.TdiConnInfo.RemoteAddress = NULL;
|
||
u.sendInfoDg.TdiConnInfo.RemoteAddressLength = 0;
|
||
res = AfdFastDatagramSend (
|
||
endpoint,
|
||
&u.sendInfoDg,
|
||
sendLength,
|
||
IoStatus);
|
||
}
|
||
else if (IS_VC_ENDPOINT (endpoint)) {
|
||
res = AfdFastConnectionSend (
|
||
endpoint,
|
||
&u.sendInfo,
|
||
sendLength,
|
||
IoStatus);
|
||
}
|
||
else
|
||
res = FALSE;
|
||
}
|
||
|
||
break;
|
||
case IOCTL_AFD_SEND_DATAGRAM:
|
||
{
|
||
AFD_SEND_DATAGRAM_INFO sendInfo;
|
||
ULONG sendLength;
|
||
|
||
|
||
if (endpoint->DisableFastIoSend ||
|
||
!IS_DGRAM_ENDPOINT(endpoint) ||
|
||
((endpoint->State != AfdEndpointStateBound ) &&
|
||
(endpoint->State != AfdEndpointStateConnected)) ) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
try {
|
||
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_SEND_DATAGRAM_INFO32 sendInfo32;
|
||
LPWSABUF32 tempArray;
|
||
ULONG i;
|
||
|
||
|
||
//
|
||
// If the input structure isn't large enough, return error.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(*sendInfo32) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*sendInfo32),
|
||
PROBE_ALIGNMENT32(AFD_SEND_DATAGRAM_INFO32));
|
||
}
|
||
|
||
sendInfo32 = InputBuffer;
|
||
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
tempArray = sendInfo32->BufferArray;
|
||
sendInfo.BufferCount = sendInfo32->BufferCount;
|
||
sendInfo.AfdFlags = sendInfo32->AfdFlags;
|
||
sendInfo.TdiConnInfo.RemoteAddress = sendInfo32->TdiConnInfo.RemoteAddress;
|
||
sendInfo.TdiConnInfo.RemoteAddressLength = sendInfo32->TdiConnInfo.RemoteAddressLength;
|
||
|
||
//
|
||
// If fast IO is not possible bail.
|
||
//
|
||
if(sendInfo.AfdFlags & AFD_NO_FAST_IO) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us and
|
||
// calculate the length of the send buffer.
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((tempArray == NULL) ||
|
||
(sendInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF32))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
tempArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
sendInfo.BufferCount * sizeof (WSABUF32), // Length
|
||
PROBE_ALIGNMENT32(WSABUF32) // Alignment
|
||
);
|
||
}
|
||
|
||
if (sendInfo.BufferCount>sizeof(localArray)/sizeof(localArray[0])) {
|
||
try {
|
||
pArray = AFD_ALLOCATE_POOL_WITH_QUOTA (
|
||
NonPagedPool,
|
||
sizeof (WSABUF)*sendInfo.BufferCount,
|
||
AFD_TEMPORARY_POOL_TAG);
|
||
// AFD_ALLOCATE_POOL_WITH_QUOTA macro sets
|
||
// POOL_RAISE_IF_ALLOCATION_FAILURE flag
|
||
ASSERT (pArray!=NULL);
|
||
}
|
||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
pArray = localArray;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (i=0; i<sendInfo.BufferCount; i++) {
|
||
pArray[i].buf = tempArray[i].buf;
|
||
pArray[i].len = tempArray[i].len;
|
||
}
|
||
|
||
sendInfo.BufferArray = pArray;
|
||
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
//
|
||
// If the input structure isn't large enough, bail on fast IO.
|
||
//
|
||
|
||
if( InputBufferLength < sizeof(sendInfo) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = STATUS_INVALID_PARAMETER;
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Validate the input structure if it comes from the user mode
|
||
// application
|
||
//
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (sendInfo),
|
||
PROBE_ALIGNMENT(AFD_SEND_DATAGRAM_INFO));
|
||
|
||
}
|
||
|
||
//
|
||
// Make local copies of the embeded pointer and parameters
|
||
// that we will be using more than once in case malicios
|
||
// application attempts to change them while we are
|
||
// validating
|
||
//
|
||
|
||
sendInfo = *((PAFD_SEND_DATAGRAM_INFO)InputBuffer);
|
||
//
|
||
// If fast IO is disabled, bail
|
||
//
|
||
|
||
if( (sendInfo.AfdFlags & AFD_NO_FAST_IO) != 0) {
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate all the pointers that app gave to us
|
||
// and calculate total send length
|
||
// Buffers in the array will be validated in the
|
||
// process of copying
|
||
//
|
||
|
||
if ((sendInfo.BufferArray == NULL) ||
|
||
(sendInfo.BufferCount == 0) ||
|
||
// Check for integer overflow (disabled by compiler)
|
||
(sendInfo.BufferCount>(MAXULONG/sizeof (WSABUF))) ) {
|
||
ExRaiseStatus(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead(
|
||
sendInfo.BufferArray, // Address
|
||
// Note check for overflow above (should actually be
|
||
// done here by the compiler generating code
|
||
// that causes exception on integer overflow)
|
||
sendInfo.BufferCount * sizeof (WSABUF), // Length
|
||
PROBE_ALIGNMENT(WSABUF) // Alignment
|
||
);
|
||
}
|
||
}
|
||
|
||
sendLength = AfdCalcBufferArrayByteLength(
|
||
sendInfo.BufferArray,
|
||
sendInfo.BufferCount
|
||
);
|
||
|
||
if (previousMode != KernelMode ) {
|
||
ProbeForRead (
|
||
sendInfo.TdiConnInfo.RemoteAddress, // Address
|
||
sendInfo.TdiConnInfo.RemoteAddressLength, // Length,
|
||
sizeof (UCHAR) // Aligment
|
||
);
|
||
}
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
//
|
||
// Attempt to perform fast IO on the endpoint.
|
||
//
|
||
|
||
res = AfdFastDatagramSend(
|
||
endpoint,
|
||
&sendInfo,
|
||
sendLength,
|
||
IoStatus
|
||
);
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case IOCTL_AFD_TRANSMIT_FILE:
|
||
{
|
||
|
||
AFD_TRANSMIT_FILE_INFO userTransmitInfo;
|
||
try {
|
||
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
PAFD_TRANSMIT_FILE_INFO32 userTransmitInfo32;
|
||
if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO32) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (*userTransmitInfo32),
|
||
PROBE_ALIGNMENT32(AFD_TRANSMIT_FILE_INFO32));
|
||
|
||
userTransmitInfo32 = InputBuffer;
|
||
userTransmitInfo.Offset = userTransmitInfo32->Offset;
|
||
userTransmitInfo.WriteLength = userTransmitInfo32->WriteLength;
|
||
userTransmitInfo.SendPacketLength = userTransmitInfo32->SendPacketLength;
|
||
userTransmitInfo.FileHandle = userTransmitInfo32->FileHandle;
|
||
userTransmitInfo.Head = userTransmitInfo32->Head;
|
||
userTransmitInfo.HeadLength = userTransmitInfo32->HeadLength;
|
||
userTransmitInfo.Tail = userTransmitInfo32->Tail;
|
||
userTransmitInfo.TailLength = userTransmitInfo32->TailLength;
|
||
userTransmitInfo.Flags = userTransmitInfo32->Flags;
|
||
|
||
|
||
if (userTransmitInfo.HeadLength>0)
|
||
ProbeForRead (userTransmitInfo.Head,
|
||
userTransmitInfo.HeadLength,
|
||
sizeof (UCHAR));
|
||
if (userTransmitInfo.TailLength>0)
|
||
ProbeForRead (userTransmitInfo.Tail,
|
||
userTransmitInfo.TailLength,
|
||
sizeof (UCHAR));
|
||
}
|
||
else
|
||
{
|
||
ASSERT (FALSE);
|
||
}
|
||
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
if ( InputBufferLength < sizeof(AFD_TRANSMIT_FILE_INFO) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (previousMode != KernelMode) {
|
||
ProbeForRead (InputBuffer,
|
||
sizeof (userTransmitInfo),
|
||
PROBE_ALIGNMENT(AFD_TRANSMIT_FILE_INFO));
|
||
userTransmitInfo = *((PAFD_TRANSMIT_FILE_INFO)InputBuffer);
|
||
if (userTransmitInfo.HeadLength>0)
|
||
ProbeForRead (userTransmitInfo.Head,
|
||
userTransmitInfo.HeadLength,
|
||
sizeof (UCHAR));
|
||
if (userTransmitInfo.TailLength>0)
|
||
ProbeForRead (userTransmitInfo.Tail,
|
||
userTransmitInfo.TailLength,
|
||
sizeof (UCHAR));
|
||
}
|
||
else {
|
||
userTransmitInfo = *((PAFD_TRANSMIT_FILE_INFO)InputBuffer);
|
||
}
|
||
}
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
res = FALSE;
|
||
break;
|
||
}
|
||
|
||
res = AfdFastTransmitFile (endpoint,
|
||
&userTransmitInfo,
|
||
IoStatus);
|
||
|
||
}
|
||
|
||
return res;
|
||
|
||
default:
|
||
request = _AFD_REQUEST(IoControlCode);
|
||
if( request < AFD_NUM_IOCTLS &&
|
||
AfdIoctlTable[request] == IoControlCode &&
|
||
AfdImmediateCallDispatch[request]!=NULL) {
|
||
|
||
proc = AfdImmediateCallDispatch[request];
|
||
IoStatus->Status = (*proc) (
|
||
FileObject,
|
||
IoControlCode,
|
||
previousMode,
|
||
InputBuffer,
|
||
InputBufferLength,
|
||
OutputBuffer,
|
||
OutputBufferLength,
|
||
&IoStatus->Information
|
||
);
|
||
|
||
ASSERT (IoStatus->Status!=STATUS_PENDING);
|
||
res = TRUE;
|
||
}
|
||
else {
|
||
res = FALSE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
#ifdef _WIN64
|
||
|
||
if (pArray!=localArray) {
|
||
AFD_FREE_POOL (pArray, AFD_TEMPORARY_POOL_TAG);
|
||
}
|
||
#endif
|
||
|
||
return res;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastConnectionSend (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_SEND_INFO sendInfo,
|
||
IN ULONG sendLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
)
|
||
{
|
||
PAFD_BUFFER afdBuffer;
|
||
PAFD_CONNECTION connection;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
NTSTATUS status;
|
||
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
||
endpoint->Type == AfdBlockTypeVcBoth );
|
||
|
||
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 );
|
||
|
||
//
|
||
// If the connection has been aborted, then we don't want to try
|
||
// fast IO on it.
|
||
//
|
||
|
||
if ( connection->CleanupBegun || connection->AbortIndicated ) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// Determine whether we can do fast IO with this send. In order
|
||
// to perform fast IO, there must be no other sends pended on this
|
||
// connection and there must be enough space left for bufferring
|
||
// the requested amount of data.
|
||
//
|
||
|
||
if ( AfdShouldSendBlock( endpoint, connection, sendLength ) ) {
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
//
|
||
// If this is a nonblocking endpoint, fail the request here and
|
||
// save going through the regular path.
|
||
//
|
||
|
||
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port), but we know
|
||
// that it is not overlapped
|
||
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Add a reference to the connection object since the send
|
||
// request will complete asynchronously.
|
||
//
|
||
|
||
REFERENCE_CONNECTION( connection );
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
IF_DEBUG(FAST_IO) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastConnectionSend: attempting fast IO on endp %p, conn %p\n",
|
||
endpoint, connection));
|
||
}
|
||
|
||
//
|
||
// Next get an AFD buffer structure that contains an IRP and a
|
||
// buffer to hold the data.
|
||
//
|
||
|
||
afdBuffer = AfdGetBuffer( sendLength, 0, connection->OwningProcess );
|
||
|
||
if ( afdBuffer == NULL) {
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
connection->VcBufferredSendBytes -= sendLength;
|
||
connection->VcBufferredSendCount -= 1;
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
DEREFERENCE_CONNECTION (connection);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// We have to rebuild the MDL in the AFD buffer structure to
|
||
// represent exactly the number of bytes we're going to be
|
||
// sending.
|
||
//
|
||
|
||
afdBuffer->Mdl->ByteCount = sendLength;
|
||
|
||
//
|
||
// Remember the connection in the AFD buffer structure. We need
|
||
// this in order to access the connection in the restart routine.
|
||
//
|
||
|
||
afdBuffer->Context = connection;
|
||
|
||
//
|
||
// Copy the user's data into the AFD buffer.
|
||
//
|
||
|
||
if( sendLength > 0 ) {
|
||
|
||
try {
|
||
|
||
AfdCopyBufferArrayToBuffer(
|
||
afdBuffer->Buffer,
|
||
sendLength,
|
||
sendInfo->BufferArray,
|
||
sendInfo->BufferCount
|
||
);
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
||
AfdReturnBuffer( &afdBuffer->Header, connection->OwningProcess );
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
connection->VcBufferredSendBytes -= sendLength;
|
||
connection->VcBufferredSendCount -= 1;
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
DEREFERENCE_CONNECTION (connection);
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Use the IRP in the AFD buffer structure to give to the TDI
|
||
// provider. Build the TDI send request.
|
||
//
|
||
|
||
TdiBuildSend(
|
||
afdBuffer->Irp,
|
||
connection->DeviceObject,
|
||
connection->FileObject,
|
||
AfdRestartBufferSend,
|
||
afdBuffer,
|
||
afdBuffer->Mdl,
|
||
0,
|
||
sendLength
|
||
);
|
||
|
||
|
||
//
|
||
// Call the transport to actually perform the send.
|
||
//
|
||
|
||
status = IoCallDriver(
|
||
connection->DeviceObject,
|
||
afdBuffer->Irp
|
||
);
|
||
|
||
//
|
||
// Complete the user's IRP as appropriate. 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;
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// The call failed for some reason. Fail fast IO.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastConnectionReceive (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_RECV_INFO recvInfo,
|
||
IN ULONG recvLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
)
|
||
{
|
||
PLIST_ENTRY listEntry;
|
||
ULONG totalOffset, partialLength;
|
||
PAFD_BUFFER_HEADER afdBuffer, partialAfdBuffer=NULL;
|
||
PAFD_CONNECTION connection;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
LIST_ENTRY bufferListHead;
|
||
BOOLEAN retryReceive = FALSE; // Retry receive if additional data
|
||
// was indicated by the transport and buffered
|
||
// while we were copying current batch.
|
||
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
||
endpoint->Type == AfdBlockTypeVcBoth );
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
IoStatus->Information = 0;
|
||
|
||
Retry:
|
||
|
||
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);
|
||
//
|
||
// If we have already copied something before retrying,
|
||
// return success, next receive will report the error.
|
||
//
|
||
return retryReceive;
|
||
}
|
||
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
IF_DEBUG(FAST_IO) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastConnectionReceive: attempting fast IO on endp %p, conn %p\n",
|
||
endpoint, connection));
|
||
}
|
||
|
||
|
||
//
|
||
// Determine whether we'll be able to perform fast IO. In order
|
||
// to do fast IO, there must be some bufferred data on the
|
||
// connection, there must not be any pended receives on the
|
||
// connection, and there must not be any bufferred expedited
|
||
// data on the connection. This last requirement is for
|
||
// the sake of simplicity only.
|
||
//
|
||
|
||
if ( !IsListEmpty( &connection->VcReceiveIrpListHead ) ||
|
||
connection->VcBufferredExpeditedCount != 0 ||
|
||
connection->DisconnectIndicated ||
|
||
connection->AbortIndicated) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
//
|
||
// If we have already copied something before retrying,
|
||
// return success, next receive will report the error.
|
||
//
|
||
return retryReceive;
|
||
}
|
||
|
||
if (connection->VcBufferredReceiveCount == 0) {
|
||
ASSERT( IsListEmpty( &connection->VcReceiveBufferListHead ) );
|
||
|
||
//
|
||
// If this is a nonblocking endpoint, fail the request here and
|
||
// save going through the regular path.
|
||
if (!retryReceive &&
|
||
endpoint->NonBlocking &&
|
||
!(recvInfo->AfdFlags & AFD_OVERLAPPED)) {
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastConnectionReceive: Endp %p, Active %lx\n",
|
||
endpoint,
|
||
endpoint->EventsActive
|
||
));
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
//
|
||
// If we have already copied something before retrying,
|
||
// return success, next receive will report the error.
|
||
//
|
||
return retryReceive;
|
||
}
|
||
|
||
ASSERT( !IsListEmpty( &connection->VcReceiveBufferListHead ) );
|
||
|
||
//
|
||
// Get a pointer to the first bufferred AFD buffer structure on
|
||
// the connection.
|
||
//
|
||
|
||
afdBuffer = CONTAINING_RECORD(
|
||
connection->VcReceiveBufferListHead.Flink,
|
||
AFD_BUFFER_HEADER,
|
||
BufferListEntry
|
||
);
|
||
|
||
ASSERT( !afdBuffer->ExpeditedData );
|
||
|
||
//
|
||
// For message endpoints if the buffer contains a partial message
|
||
// or doesn't fit into the buffer, bail out.
|
||
// We don't want the added complexity of handling
|
||
// partial messages in the fast path.
|
||
//
|
||
|
||
if ( IS_MESSAGE_ENDPOINT(endpoint) &&
|
||
(afdBuffer->PartialMessage || afdBuffer->DataLength>recvLength)) {
|
||
//
|
||
// We shouldn't be retry-ing for message oriented endpoint
|
||
// since we only allow fast path if complete message is available.
|
||
//
|
||
ASSERT (retryReceive == FALSE);
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Remeber current offset before we update
|
||
// information field (it is not 0 if we are
|
||
// re-trying).
|
||
//
|
||
totalOffset = (ULONG)IoStatus->Information;
|
||
|
||
|
||
InitializeListHead( &bufferListHead );
|
||
|
||
//
|
||
// Reference the connection object so it doen't go away
|
||
// until we return the buffer.
|
||
//
|
||
REFERENCE_CONNECTION (connection);
|
||
|
||
//
|
||
// Loop getting AFD buffers that will fill in the user's
|
||
// buffer with as much data as will fit, or else with a
|
||
// single buffer if this is not a stream endpoint. We don't
|
||
// actually do the copy within this loop because this loop
|
||
// must occur while holding a lock, and we cannot hold a
|
||
// lock while copying the data into the user's buffer
|
||
// because the user's buffer is not locked and we cannot
|
||
// take a page fault at raised IRQL.
|
||
//
|
||
|
||
while (IoStatus->Information<recvLength) {
|
||
ASSERT( connection->VcBufferredReceiveBytes >= afdBuffer->DataLength );
|
||
ASSERT( connection->VcBufferredReceiveCount > 0 );
|
||
|
||
if (recvLength-IoStatus->Information>=afdBuffer->DataLength) {
|
||
//
|
||
// If we can copy the whole buffer, remove it from the connection's list of
|
||
// buffers and place it on our local list of buffers.
|
||
//
|
||
|
||
RemoveEntryList( &afdBuffer->BufferListEntry );
|
||
InsertTailList( &bufferListHead, &afdBuffer->BufferListEntry );
|
||
|
||
//
|
||
// Update the count of bytes on the connection.
|
||
//
|
||
|
||
connection->VcBufferredReceiveBytes -= afdBuffer->DataLength;
|
||
connection->VcBufferredReceiveCount -= 1;
|
||
IoStatus->Information += afdBuffer->DataLength;
|
||
|
||
|
||
//
|
||
// If this is a stream endpoint and more buffers are available,
|
||
// try to fit the next one it as well..
|
||
//
|
||
|
||
if (!IS_MESSAGE_ENDPOINT(endpoint) &&
|
||
!IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
|
||
|
||
afdBuffer = CONTAINING_RECORD(
|
||
connection->VcReceiveBufferListHead.Flink,
|
||
AFD_BUFFER_HEADER,
|
||
BufferListEntry
|
||
);
|
||
|
||
ASSERT( !afdBuffer->ExpeditedData );
|
||
continue;
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Copy just a part of the buffer that fits and
|
||
// increment its reference count so it doesn't get
|
||
// destroyed until we done copying.
|
||
//
|
||
ASSERT (!IS_MESSAGE_ENDPOINT (endpoint));
|
||
|
||
partialLength = recvLength-(ULONG)IoStatus->Information;
|
||
partialAfdBuffer = afdBuffer;
|
||
partialAfdBuffer->DataLength -= partialLength;
|
||
partialAfdBuffer->DataOffset += partialLength;
|
||
InterlockedIncrement (&partialAfdBuffer->RefCount);
|
||
connection->VcBufferredReceiveBytes -= partialLength;
|
||
IoStatus->Information = recvLength;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
||
|
||
if( !IsListEmpty( &connection->VcReceiveBufferListHead )) {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
retryReceive = FALSE;
|
||
}
|
||
else {
|
||
//
|
||
// We got all the data buffered. It is possible
|
||
// that while we are copying data, more gets indicated
|
||
// by the transport since we copy at passive level
|
||
// and indication occur at DPC (or even on another processor).
|
||
// We'll check again after copying, so we return as much data
|
||
// as possible to the application (to improve performance).
|
||
// For message oriented transports we can only
|
||
// deliver one message at a time and we shouldn't be on the fast path
|
||
// if we do not have a complete message.
|
||
// If application has EventSelect/select/AsyncSelect outstanding.
|
||
// we can't copy more data as well since it would receive a signal
|
||
// to come back and we would already consumed the data. We are not
|
||
// concerned with the case when application calls some form of select
|
||
// while we are in this routine because signaling is not guaranteed
|
||
// to be multithread safe (e.g. if select comes right before we take
|
||
// the spinlock in the beginning of this routine, application will
|
||
// get false signal as well).
|
||
//
|
||
retryReceive = IoStatus->Information<recvLength &&
|
||
!IS_MESSAGE_ENDPOINT (endpoint) &&
|
||
(endpoint->EventsEnabled & AFD_POLL_RECEIVE)==0 &&
|
||
!endpoint->PollCalled;
|
||
|
||
//
|
||
// Disable fast IO path to avoid performance penalty
|
||
// of unneccessarily going through it.
|
||
//
|
||
if (!endpoint->NonBlocking)
|
||
endpoint->DisableFastIoRecv = TRUE;
|
||
}
|
||
|
||
//
|
||
// If there is indicated but unreceived data in the TDI provider,
|
||
// and we have available buffer space, fire off an IRP to receive
|
||
// the data.
|
||
//
|
||
|
||
if ( connection->VcReceiveBytesInTransport > 0
|
||
|
||
&&
|
||
|
||
connection->VcBufferredReceiveBytes <
|
||
connection->MaxBufferredReceiveBytes
|
||
|
||
) {
|
||
|
||
ULONG bytesToReceive;
|
||
PAFD_BUFFER newAfdBuffer;
|
||
|
||
//
|
||
// Remember the count of data that we're going to receive,
|
||
// then reset the fields in the connection where we keep
|
||
// track of how much data is available in the transport.
|
||
// We reset it here before releasing the lock so that
|
||
// another thread doesn't try to receive the data at the
|
||
// same time as us.
|
||
//
|
||
|
||
if ( connection->VcReceiveBytesInTransport > AfdLargeBufferSize ) {
|
||
bytesToReceive = connection->VcReceiveBytesInTransport;
|
||
} else {
|
||
bytesToReceive = AfdLargeBufferSize;
|
||
}
|
||
|
||
//
|
||
// Get an AFD buffer structure to hold the data.
|
||
//
|
||
|
||
newAfdBuffer = AfdGetBuffer( bytesToReceive, 0,
|
||
connection->OwningProcess );
|
||
if ( newAfdBuffer == NULL ) {
|
||
//
|
||
// If we were unable to get a buffer, just remember
|
||
// that we still have data in transport
|
||
//
|
||
|
||
if (connection->VcBufferredReceiveBytes == 0 &&
|
||
!connection->OnLRList) {
|
||
//
|
||
// Since we do not have any data buffered, application
|
||
// is not notified and will never call with recv.
|
||
// We will have to put this on low resource list
|
||
// and attempt to allocate memory and pull the data
|
||
// later.
|
||
//
|
||
connection->OnLRList = TRUE;
|
||
REFERENCE_CONNECTION (connection);
|
||
AfdLRListAddItem (&connection->LRListItem, AfdLRRepostReceive);
|
||
}
|
||
else {
|
||
UPDATE_CONN (connection);
|
||
}
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
} else {
|
||
|
||
connection->VcReceiveBytesInTransport = 0;
|
||
ASSERT (InterlockedDecrement (&connection->VcReceiveIrpsInTransport)==-1);
|
||
|
||
//
|
||
// We need to remember the connection in the AFD buffer
|
||
// because we'll need to access it in the completion
|
||
// routine.
|
||
//
|
||
|
||
newAfdBuffer->Context = connection;
|
||
|
||
//
|
||
// Acquire connection reference to be released in completion routine
|
||
//
|
||
|
||
REFERENCE_CONNECTION (connection);
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// Finish building the receive IRP to give to the TDI provider.
|
||
//
|
||
|
||
TdiBuildReceive(
|
||
newAfdBuffer->Irp,
|
||
connection->DeviceObject,
|
||
connection->FileObject,
|
||
AfdRestartBufferReceive,
|
||
newAfdBuffer,
|
||
newAfdBuffer->Mdl,
|
||
TDI_RECEIVE_NORMAL,
|
||
(CLONG)bytesToReceive
|
||
);
|
||
|
||
//
|
||
// Hand off the IRP to the TDI provider.
|
||
//
|
||
|
||
(VOID)IoCallDriver(
|
||
connection->DeviceObject,
|
||
newAfdBuffer->Irp
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
|
||
//
|
||
// We have in a local list all the data we'll use for this
|
||
// IO. Start copying data to the user buffer.
|
||
//
|
||
|
||
while ( !IsListEmpty( &bufferListHead ) ) {
|
||
|
||
//
|
||
// Take the first buffer from the list.
|
||
//
|
||
|
||
listEntry = RemoveHeadList( &bufferListHead );
|
||
afdBuffer = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_BUFFER_HEADER,
|
||
BufferListEntry
|
||
);
|
||
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
||
|
||
if( afdBuffer->DataLength > 0 ) {
|
||
|
||
ASSERTMSG (
|
||
"NIC Driver freed the packet before it was returned!!!",
|
||
!afdBuffer->NdisPacket ||
|
||
(MmIsAddressValid (afdBuffer->Context) &&
|
||
MmIsAddressValid (MmGetSystemAddressForMdl (afdBuffer->Mdl))) );
|
||
try {
|
||
|
||
//
|
||
// Copy the data in the buffer to the user buffer.
|
||
//
|
||
|
||
AfdCopyMdlChainToBufferArray(
|
||
recvInfo->BufferArray,
|
||
totalOffset,
|
||
recvInfo->BufferCount,
|
||
afdBuffer->Mdl,
|
||
afdBuffer->DataOffset,
|
||
afdBuffer->DataLength
|
||
);
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
//
|
||
// If an exception is hit, there is the possibility of
|
||
// data corruption. However, it is nearly impossible to
|
||
// avoid this in all cases, so just throw out the
|
||
// remainder of the data that we would have copied to
|
||
// the user buffer.
|
||
//
|
||
|
||
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
||
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
||
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
||
}
|
||
|
||
while ( !IsListEmpty( &bufferListHead ) ) {
|
||
listEntry = RemoveHeadList( &bufferListHead );
|
||
afdBuffer = CONTAINING_RECORD(
|
||
listEntry,
|
||
AFD_BUFFER_HEADER,
|
||
BufferListEntry
|
||
);
|
||
DEBUG afdBuffer->BufferListEntry.Flink = NULL;
|
||
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
||
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
||
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We'll have to abort since there is a possibility of data corruption.
|
||
// Shame on application for giving us bogus buffers.
|
||
// This also releases the reference that we needed to return the buffer.
|
||
//
|
||
AfdAbortConnection (connection);
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
return FALSE;
|
||
}
|
||
|
||
totalOffset += afdBuffer->DataLength;
|
||
}
|
||
|
||
//
|
||
// We're done with the AFD buffer.
|
||
//
|
||
|
||
if (afdBuffer->RefCount==1 || // Can't change once off the list
|
||
InterlockedDecrement (&afdBuffer->RefCount)==0) {
|
||
AfdReturnBuffer( afdBuffer, connection->OwningProcess );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copy any partial buffers
|
||
//
|
||
if (partialAfdBuffer) {
|
||
ASSERT (partialLength>0);
|
||
ASSERTMSG (
|
||
"NIC Driver freed the packet before it was returned!!!",
|
||
!partialAfdBuffer->NdisPacket ||
|
||
(MmIsAddressValid (partialAfdBuffer->Context) &&
|
||
MmIsAddressValid (MmGetSystemAddressForMdl (partialAfdBuffer->Mdl))) );
|
||
try {
|
||
|
||
//
|
||
// Copy the data in the buffer to the user buffer.
|
||
//
|
||
|
||
AfdCopyMdlChainToBufferArray(
|
||
recvInfo->BufferArray,
|
||
totalOffset,
|
||
recvInfo->BufferCount,
|
||
partialAfdBuffer->Mdl,
|
||
partialAfdBuffer->DataOffset-partialLength,
|
||
partialLength
|
||
);
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
if (InterlockedDecrement (&partialAfdBuffer->RefCount)==0) {
|
||
ASSERT (partialAfdBuffer->BufferListEntry.Flink == NULL);
|
||
AfdReturnBuffer( partialAfdBuffer, connection->OwningProcess );
|
||
}
|
||
//
|
||
// We'll have to abort since there is a possibility of data corruption.
|
||
// Shame on application for giving us bogus buffers.
|
||
// This also releases the reference that we needed to return the buffer.
|
||
//
|
||
AfdAbortConnection (connection);
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
return FALSE;
|
||
}
|
||
|
||
if (InterlockedDecrement (&partialAfdBuffer->RefCount)==0) {
|
||
ASSERT (partialAfdBuffer->BufferListEntry.Flink == NULL);
|
||
AfdReturnBuffer( partialAfdBuffer, connection->OwningProcess );
|
||
}
|
||
|
||
totalOffset += partialLength;
|
||
}
|
||
|
||
ASSERT (IoStatus->Information==totalOffset);
|
||
|
||
|
||
//
|
||
// If more data is available, we need to retry and attempt to completely
|
||
// fill application's buffer.
|
||
//
|
||
|
||
if (retryReceive && (endpoint->EventsActive & AFD_POLL_RECEIVE)) {
|
||
ASSERT (IoStatus->Information<recvLength && !IS_MESSAGE_ENDPOINT (endpoint));
|
||
DEREFERENCE_CONNECTION2 (connection, "Fast retry receive 0x%lX bytes", (ULONG)IoStatus->Information);
|
||
goto Retry;
|
||
}
|
||
else {
|
||
//
|
||
// Release the reference needed to return the buffer(s).
|
||
//
|
||
DEREFERENCE_CONNECTION2 (connection, "Fast receive 0x%lX bytes", (ULONG)IoStatus->Information);
|
||
}
|
||
|
||
ASSERT( IoStatus->Information <= recvLength );
|
||
ASSERT (IoStatus->Status == STATUS_SUCCESS);
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastDatagramSend (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_SEND_DATAGRAM_INFO sendInfo,
|
||
IN ULONG sendLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
)
|
||
{
|
||
|
||
PAFD_BUFFER afdBuffer = NULL;
|
||
NTSTATUS status;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
|
||
|
||
//
|
||
// If this is a send for more than the threshold number of
|
||
// bytes, don't use the fast path. We don't allow larger sends
|
||
// in the fast path because of the extra data copy it entails,
|
||
// which is more expensive for large buffers. For smaller
|
||
// buffers, however, the cost of the copy is small compared to
|
||
// the IO system overhead of the slow path.
|
||
//
|
||
// We also copy and return for non-blocking endpoints regardless
|
||
// of the size. That's what we are supposed to do according
|
||
// to the spec.
|
||
//
|
||
|
||
if ( !endpoint->NonBlocking && sendLength > AfdFastSendDatagramThreshold ) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If we already buffered to many sends, go the long way.
|
||
//
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
if ( endpoint->DgBufferredSendBytes >=
|
||
endpoint->Common.Datagram.MaxBufferredSendBytes &&
|
||
endpoint->DgBufferredSendBytes>0) {
|
||
|
||
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
||
endpoint->EventsActive &= ~AFD_POLL_SEND;
|
||
endpoint->EnableSendEvent = TRUE;
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastIoDeviceControl: Endp %p, Active %lX\n",
|
||
endpoint,
|
||
endpoint->EventsActive
|
||
));
|
||
}
|
||
}
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
//
|
||
// If this is a nonblocking endpoint, fail the request here and
|
||
// save going through the regular path.(check for non-blocking is
|
||
// below, otherwise status code is ignored).
|
||
//
|
||
|
||
status = STATUS_DEVICE_NOT_READY;
|
||
goto errorset;
|
||
}
|
||
|
||
endpoint->DgBufferredSendBytes += sendLength;
|
||
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
IF_DEBUG(FAST_IO) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastDatagramSend: attempting fast IO on endp %p\n",
|
||
endpoint));
|
||
}
|
||
|
||
|
||
//
|
||
// Get an AFD buffer to use for the request. We'll copy the
|
||
// user's data to the AFD buffer then submit the IRP in the AFD
|
||
// buffer to the TDI provider.
|
||
|
||
if ((sendInfo->TdiConnInfo.RemoteAddressLength==0) &&
|
||
!IS_TDI_DGRAM_CONNECTION(endpoint)) {
|
||
retry:
|
||
try {
|
||
//
|
||
// Get an AFD buffer to use for the request. We'll copy the
|
||
// user to the AFD buffer then submit the IRP in the AFD
|
||
// buffer to the TDI provider.
|
||
//
|
||
|
||
afdBuffer = AfdGetBufferRaiseOnFailure(
|
||
sendLength,
|
||
endpoint->Common.Datagram.RemoteAddressLength,
|
||
endpoint->OwningProcess
|
||
);
|
||
}
|
||
except (AFD_EXCEPTION_FILTER (&status)) {
|
||
goto exit;
|
||
}
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// If the endpoint is not connected, fail.
|
||
//
|
||
|
||
if ( endpoint->State != AfdEndpointStateConnected ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
||
status = STATUS_INVALID_CONNECTION;
|
||
goto exit;
|
||
}
|
||
|
||
if (afdBuffer->AllocatedAddressLength <
|
||
endpoint->Common.Datagram.RemoteAddressLength ) {
|
||
//
|
||
// Apparently connection address length has changed
|
||
// on us while we were allocating the buffer.
|
||
// This is extremely unlikely (even if endpoint got
|
||
// connected to a different address, the length is unlikely
|
||
// to change), but we must handle this, just try again.
|
||
//
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
||
goto retry;
|
||
}
|
||
//
|
||
// Copy the address to the AFD buffer.
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
afdBuffer->TdiInfo.RemoteAddress,
|
||
endpoint->Common.Datagram.RemoteAddress,
|
||
endpoint->Common.Datagram.RemoteAddressLength
|
||
);
|
||
|
||
afdBuffer->TdiInfo.RemoteAddressLength = endpoint->Common.Datagram.RemoteAddressLength;
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
}
|
||
else {
|
||
try {
|
||
afdBuffer = AfdGetBufferRaiseOnFailure( sendLength, sendInfo->TdiConnInfo.RemoteAddressLength,
|
||
endpoint->OwningProcess);
|
||
//
|
||
// Copy address if necessary.
|
||
//
|
||
if (sendInfo->TdiConnInfo.RemoteAddressLength!=0) {
|
||
RtlCopyMemory(
|
||
afdBuffer->TdiInfo.RemoteAddress,
|
||
sendInfo->TdiConnInfo.RemoteAddress,
|
||
sendInfo->TdiConnInfo.RemoteAddressLength
|
||
);
|
||
|
||
//
|
||
// Validate internal consistency of the transport address structure.
|
||
// Note that we HAVE to do this after copying since the malicious
|
||
// application can change the content of the buffer on us any time
|
||
// and our check will be bypassed.
|
||
//
|
||
if ((((PTRANSPORT_ADDRESS)afdBuffer->TdiInfo.RemoteAddress)->TAAddressCount!=1) ||
|
||
(LONG)sendInfo->TdiConnInfo.RemoteAddressLength<
|
||
FIELD_OFFSET (TRANSPORT_ADDRESS,
|
||
Address[0].Address[((PTRANSPORT_ADDRESS)afdBuffer->TdiInfo.RemoteAddress)->Address[0].AddressLength])) {
|
||
ExRaiseStatus (STATUS_INVALID_PARAMETER);
|
||
}
|
||
}
|
||
} except( AFD_EXCEPTION_FILTER(&status) ) {
|
||
if (afdBuffer!=NULL) {
|
||
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
||
}
|
||
goto exit;
|
||
}
|
||
|
||
afdBuffer->TdiInfo.RemoteAddressLength = sendInfo->TdiConnInfo.RemoteAddressLength;
|
||
}
|
||
|
||
//
|
||
// Copy the output buffer to the AFD buffer.
|
||
//
|
||
|
||
try {
|
||
|
||
AfdCopyBufferArrayToBuffer(
|
||
afdBuffer->Buffer,
|
||
sendLength,
|
||
sendInfo->BufferArray,
|
||
sendInfo->BufferCount
|
||
);
|
||
|
||
//
|
||
// Store the length of the data and the address we're going to
|
||
// send.
|
||
//
|
||
afdBuffer->DataLength = sendLength;
|
||
|
||
} except( AFD_EXCEPTION_FILTER(&status) ) {
|
||
|
||
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
||
goto exit;
|
||
}
|
||
|
||
|
||
if (IS_TDI_DGRAM_CONNECTION(endpoint)
|
||
&& (afdBuffer->TdiInfo.RemoteAddressLength==0)) {
|
||
TdiBuildSend(
|
||
afdBuffer->Irp,
|
||
endpoint->AddressDeviceObject,
|
||
endpoint->AddressFileObject,
|
||
AfdRestartFastDatagramSend,
|
||
afdBuffer,
|
||
afdBuffer->Irp->MdlAddress,
|
||
0,
|
||
sendLength
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// Set up the input TDI information to point to the destination
|
||
// address.
|
||
//
|
||
|
||
afdBuffer->TdiInfo.Options = NULL;
|
||
afdBuffer->TdiInfo.OptionsLength = 0;
|
||
afdBuffer->TdiInfo.UserData = NULL;
|
||
afdBuffer->TdiInfo.UserDataLength = 0;
|
||
|
||
|
||
//
|
||
// Initialize the IRP in the AFD buffer to do a fast datagram send.
|
||
//
|
||
|
||
TdiBuildSendDatagram(
|
||
afdBuffer->Irp,
|
||
endpoint->AddressDeviceObject,
|
||
endpoint->AddressFileObject,
|
||
AfdRestartFastDatagramSend,
|
||
afdBuffer,
|
||
afdBuffer->Irp->MdlAddress,
|
||
sendLength,
|
||
&afdBuffer->TdiInfo
|
||
);
|
||
}
|
||
|
||
//
|
||
// Change the MDL in the AFD buffer to specify only the number
|
||
// of bytes we're actually sending. This is a requirement of TDI--
|
||
// the MDL chain cannot describe a longer buffer than the send
|
||
// request.
|
||
//
|
||
|
||
afdBuffer->Mdl->ByteCount = sendLength;
|
||
|
||
//
|
||
// Reference the endpoint so that it does not go away until the send
|
||
// completes. This is necessary to ensure that a send which takes a
|
||
// very long time and lasts longer than the process will not cause a
|
||
// crash when the send datragram finally completes.
|
||
//
|
||
|
||
REFERENCE_ENDPOINT2( endpoint, "AfdFastDatagramSend, length", sendLength );
|
||
|
||
//
|
||
// Set the context to NULL initially so that if the IRP is completed
|
||
// by the stack before IoCallDriver returns, the completion routine
|
||
// does not free the buffer (and IRP in it) and we can figure out
|
||
// what the final status of the operation was and report it to the
|
||
// application
|
||
//
|
||
|
||
afdBuffer->Context = NULL;
|
||
|
||
//
|
||
// Give the IRP to the TDI provider. If the request fails
|
||
// immediately, then fail fast IO. If the request fails later on,
|
||
// there's nothing we can do about it.
|
||
//
|
||
|
||
status = IoCallDriver(
|
||
endpoint->AddressDeviceObject,
|
||
afdBuffer->Irp
|
||
);
|
||
|
||
//
|
||
// Check if completion routine has already been called and we
|
||
// can figure out what the final status is
|
||
//
|
||
if (InterlockedCompareExchangePointer (
|
||
&afdBuffer->Context,
|
||
endpoint,
|
||
NULL)!=NULL) {
|
||
BOOLEAN indicateSendEvent;
|
||
//
|
||
// Completion routine has been called, pick the final status
|
||
// and dereference the endpoint and free the buffer
|
||
//
|
||
status = afdBuffer->Irp->IoStatus.Status;
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
endpoint->DgBufferredSendBytes -= sendLength;
|
||
if (endpoint->DgBufferredSendBytes <
|
||
endpoint->Common.Datagram.MaxBufferredSendBytes ||
|
||
endpoint->DgBufferredSendBytes==0) {
|
||
indicateSendEvent = TRUE;
|
||
AfdIndicateEventSelectEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
||
}
|
||
else {
|
||
indicateSendEvent = FALSE;
|
||
}
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
if (indicateSendEvent) {
|
||
AfdIndicatePollEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
||
}
|
||
|
||
AfdReturnBuffer (&afdBuffer->Header, endpoint->OwningProcess);
|
||
|
||
DEREFERENCE_ENDPOINT2 (endpoint, "AfdFastDatagramSend-inline completion, status", status );
|
||
}
|
||
//else Completion routine has not been called, we set the pointer
|
||
// to the endpoint in the buffer context, so it can derefernce it
|
||
// and knows to free the buffer
|
||
//
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
IoStatus->Information = sendLength;
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
return TRUE;
|
||
} else {
|
||
goto errorset;
|
||
}
|
||
|
||
exit:
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
endpoint->DgBufferredSendBytes -= sendLength;
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
errorset:
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port),
|
||
if ( endpoint->NonBlocking && !( sendInfo->AfdFlags & AFD_OVERLAPPED ) ) {
|
||
// We know that it is not overlapped
|
||
IoStatus->Status = status;
|
||
return TRUE;
|
||
}
|
||
else {
|
||
return FALSE;
|
||
}
|
||
} // AfdFastDatagramSend
|
||
|
||
|
||
NTSTATUS
|
||
AfdRestartFastDatagramSend (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PAFD_BUFFER afdBuffer;
|
||
PAFD_ENDPOINT endpoint;
|
||
ULONG sendLength;
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
|
||
afdBuffer = Context;
|
||
ASSERT (IS_VALID_AFD_BUFFER (afdBuffer));
|
||
|
||
//
|
||
// Reset the AFD buffer structure.
|
||
//
|
||
|
||
ASSERT( afdBuffer->Irp == Irp );
|
||
|
||
sendLength = afdBuffer->Mdl->ByteCount;
|
||
ASSERT (afdBuffer->DataLength==sendLength);
|
||
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
||
|
||
|
||
//
|
||
// If call succeeded, transport should have sent the number of bytes requested
|
||
//
|
||
ASSERT (Irp->IoStatus.Status!=STATUS_SUCCESS ||
|
||
Irp->IoStatus.Information==sendLength);
|
||
//
|
||
// Find the endpoint used for this request if
|
||
// the IoCallDriver call has completed already
|
||
//
|
||
|
||
endpoint = InterlockedCompareExchangePointer (&afdBuffer->Context,
|
||
(PVOID)-1,
|
||
NULL);
|
||
if (endpoint!=NULL) {
|
||
BOOLEAN indicateSendEvent;
|
||
#if REFERENCE_DEBUG
|
||
NTSTATUS status;
|
||
#endif
|
||
//
|
||
// IoCallDriver has completed, free the buffer and
|
||
// dereference endpoint here
|
||
//
|
||
ASSERT( IS_DGRAM_ENDPOINT(endpoint) );
|
||
|
||
|
||
AfdAcquireSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
endpoint->DgBufferredSendBytes -= sendLength;
|
||
if (endpoint->DgBufferredSendBytes <
|
||
endpoint->Common.Datagram.MaxBufferredSendBytes ||
|
||
endpoint->DgBufferredSendBytes==0) {
|
||
AfdIndicateEventSelectEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
||
indicateSendEvent = TRUE;
|
||
}
|
||
else
|
||
indicateSendEvent = FALSE;
|
||
AfdReleaseSpinLock (&endpoint->SpinLock, &lockHandle);
|
||
|
||
if (indicateSendEvent) {
|
||
AfdIndicatePollEvent (endpoint, AFD_POLL_SEND, STATUS_SUCCESS);
|
||
}
|
||
//
|
||
// Get rid of the reference we put on the endpoint when we started
|
||
// this I/O.
|
||
//
|
||
|
||
#if REFERENCE_DEBUG
|
||
status = Irp->IoStatus.Status;
|
||
#endif
|
||
AfdReturnBuffer( &afdBuffer->Header, endpoint->OwningProcess );
|
||
|
||
DEREFERENCE_ENDPOINT2 (endpoint, "AfdRestartFastDatagramSend, status", status );
|
||
|
||
}
|
||
// else IoCallDriver is not done yet, it will free the buffer
|
||
// and endpoint when done (it will look at final status and
|
||
// report it to the application).
|
||
|
||
//
|
||
// Tell the IO system to stop processing this IRP.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // AfdRestartFastSendDatagram
|
||
|
||
|
||
|
||
BOOLEAN
|
||
AfdFastDatagramReceive (
|
||
IN PAFD_ENDPOINT endpoint,
|
||
IN PAFD_RECV_MESSAGE_INFO msgInfo,
|
||
IN ULONG recvLength,
|
||
OUT PIO_STATUS_BLOCK IoStatus
|
||
)
|
||
{
|
||
AFD_LOCK_QUEUE_HANDLE lockHandle;
|
||
PLIST_ENTRY listEntry;
|
||
PAFD_BUFFER_HEADER afdBuffer;
|
||
PTRANSPORT_ADDRESS tdiAddress;
|
||
ULONG length;
|
||
|
||
|
||
|
||
IF_DEBUG(FAST_IO) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastDatagramReceive: attempting fast IO on endp %p\n",
|
||
endpoint));
|
||
}
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
|
||
//
|
||
// If there are no datagrams available to be received, don't
|
||
// bother with the fast path.
|
||
//
|
||
if ( !ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) ) {
|
||
|
||
//
|
||
// If this is a nonblocking endpoint, fail the request here and
|
||
// save going through the regular path.
|
||
//
|
||
|
||
if ( endpoint->NonBlocking && !( msgInfo->dgi.AfdFlags & AFD_OVERLAPPED ) ) {
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastDatagramReceive: Endp %p, Active %lX\n",
|
||
endpoint,
|
||
endpoint->EventsActive
|
||
));
|
||
}
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port), but we know here
|
||
// that call is not overlapped
|
||
IoStatus->Status = STATUS_DEVICE_NOT_READY;
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
return TRUE;
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// There is at least one datagram bufferred on the endpoint. Use it
|
||
// for this receive.
|
||
//
|
||
|
||
listEntry = RemoveHeadList( &endpoint->ReceiveDatagramBufferListHead );
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER_HEADER, BufferListEntry );
|
||
|
||
//
|
||
// If the datagram is too large or it is an error indication
|
||
// fail fast IO.
|
||
//
|
||
|
||
if ( (afdBuffer->DataLength > recvLength) ||
|
||
!NT_SUCCESS (afdBuffer->Status)) {
|
||
InsertHeadList(
|
||
&endpoint->ReceiveDatagramBufferListHead,
|
||
&afdBuffer->BufferListEntry
|
||
);
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Update counts of bufferred datagrams and bytes on the endpoint.
|
||
//
|
||
|
||
endpoint->DgBufferredReceiveCount--;
|
||
endpoint->DgBufferredReceiveBytes -= afdBuffer->DataLength;
|
||
|
||
//
|
||
// Release the lock and copy the datagram into the user buffer. We
|
||
// can't continue to hold the lock, because it is not legal to take
|
||
// an exception at raised IRQL. Releasing the lock may result in a
|
||
// misordered datagram if there is an exception in copying to the
|
||
// user's buffer, but that is the application's fault for giving us a bogus
|
||
// pointer. Besides, datagram order is not guaranteed.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
try {
|
||
|
||
if (afdBuffer->DataLength>0) {
|
||
AfdCopyMdlChainToBufferArray(
|
||
msgInfo->dgi.BufferArray,
|
||
0,
|
||
msgInfo->dgi.BufferCount,
|
||
afdBuffer->Mdl,
|
||
0,
|
||
afdBuffer->DataLength
|
||
);
|
||
}
|
||
|
||
//
|
||
// If we need to return the source address, copy it to the
|
||
// user's output buffer.
|
||
//
|
||
|
||
if ( msgInfo->dgi.Address != NULL ) {
|
||
|
||
tdiAddress = afdBuffer->TdiInfo.RemoteAddress;
|
||
|
||
length = tdiAddress->Address[0].AddressLength +
|
||
sizeof(u_short); // sa_family
|
||
|
||
if( *msgInfo->dgi.AddressLength < length ) {
|
||
|
||
ExRaiseAccessViolation();
|
||
|
||
}
|
||
|
||
if (ExGetPreviousMode ()!=KernelMode) {
|
||
ProbeForWrite (msgInfo->dgi.Address,
|
||
length,
|
||
sizeof (UCHAR));
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
msgInfo->dgi.Address,
|
||
&tdiAddress->Address[0].AddressType,
|
||
length
|
||
);
|
||
|
||
*msgInfo->dgi.AddressLength = length;
|
||
}
|
||
|
||
if (msgInfo->ControlLength!=NULL) {
|
||
if (afdBuffer->DatagramFlags & TDI_RECEIVE_CONTROL_INFO &&
|
||
afdBuffer->DataOffset>0) {
|
||
PAFD_BUFFER buf = CONTAINING_RECORD (afdBuffer, AFD_BUFFER, Header);
|
||
ASSERT (msgInfo->MsgFlags!=NULL);
|
||
ASSERT (buf->BufferLength != AfdBufferTagSize);
|
||
length = buf->DataOffset;
|
||
#ifdef _WIN64
|
||
if (IoIs32bitProcess (NULL)) {
|
||
length = AfdComputeCMSGLength32 (
|
||
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
||
length);
|
||
|
||
if (length>*msgInfo->ControlLength) {
|
||
ExRaiseAccessViolation ();
|
||
}
|
||
if (ExGetPreviousMode ()!=KernelMode) {
|
||
ProbeForWrite (msgInfo->ControlBuffer,
|
||
length,
|
||
sizeof (UCHAR));
|
||
}
|
||
AfdCopyCMSGBuffer32 (
|
||
msgInfo->ControlBuffer,
|
||
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
||
length);
|
||
}
|
||
else
|
||
#endif // _WIN64
|
||
{
|
||
if (length>*msgInfo->ControlLength) {
|
||
ExRaiseAccessViolation ();
|
||
}
|
||
|
||
if (ExGetPreviousMode ()!=KernelMode) {
|
||
ProbeForWrite (msgInfo->ControlBuffer,
|
||
length,
|
||
sizeof (UCHAR));
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
msgInfo->ControlBuffer,
|
||
(PUCHAR)buf->Buffer+afdBuffer->DataLength,
|
||
length
|
||
);
|
||
}
|
||
|
||
}
|
||
else {
|
||
length = 0;
|
||
}
|
||
|
||
*msgInfo->ControlLength = length;
|
||
}
|
||
|
||
if (msgInfo->MsgFlags!=NULL) {
|
||
ULONG flags = 0;
|
||
if (afdBuffer->DatagramFlags & TDI_RECEIVE_BROADCAST)
|
||
flags |= MSG_BCAST;
|
||
if (afdBuffer->DatagramFlags & TDI_RECEIVE_MULTICAST)
|
||
flags |= MSG_MCAST;
|
||
*msgInfo->MsgFlags = flags;
|
||
}
|
||
|
||
IoStatus->Information = afdBuffer->DataLength;
|
||
IoStatus->Status = STATUS_SUCCESS;
|
||
|
||
} except( AFD_EXCEPTION_FILTER(NULL) ) {
|
||
|
||
//
|
||
// Put the buffer back on the endpoint's list.
|
||
//
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
InsertHeadList(
|
||
&endpoint->ReceiveDatagramBufferListHead,
|
||
&afdBuffer->BufferListEntry
|
||
);
|
||
|
||
endpoint->DgBufferredReceiveCount++;
|
||
endpoint->DgBufferredReceiveBytes += afdBuffer->DataLength;
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
// Fast io can't handle error returns
|
||
// if call is overlapped (completion port)
|
||
// IoStatus->Status = GetExceptionCode ();
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Clear the receive data active bit. If there's more data
|
||
// available, set the corresponding event.
|
||
//
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
||
|
||
if( ARE_DATAGRAMS_ON_ENDPOINT( endpoint ) ) {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
else {
|
||
//
|
||
// Disable fast IO path to avoid performance penalty
|
||
// of going through it.
|
||
//
|
||
if (!endpoint->NonBlocking)
|
||
endpoint->DisableFastIoRecv = TRUE;
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, &lockHandle );
|
||
|
||
//
|
||
// The fast IO worked! Clean up and return to the user.
|
||
//
|
||
|
||
AfdReturnBuffer( afdBuffer, endpoint->OwningProcess );
|
||
|
||
ASSERT (IoStatus->Status == STATUS_SUCCESS);
|
||
return TRUE;
|
||
|
||
} // AfdFastDatagramReceive
|
||
|
||
|
||
BOOLEAN
|
||
AfdShouldSendBlock (
|
||
IN PAFD_ENDPOINT Endpoint,
|
||
IN PAFD_CONNECTION Connection,
|
||
IN ULONG SendLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines whether a nonblocking send can be performed on the
|
||
connection, and if the send is possible, updates the connection's
|
||
send tracking information.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - the AFD endpoint for the send.
|
||
|
||
Connection - the AFD connection for the send.
|
||
|
||
SendLength - the number of bytes that the caller wants to send.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the there is not too much data on the endpoint to perform
|
||
the send; FALSE otherwise.
|
||
|
||
Note:
|
||
This routine assumes that endpoint spinlock is held when calling it.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Determine whether we can do fast IO with this send. In order
|
||
// to perform fast IO, there must be no other sends pended on this
|
||
// connection and there must be enough space left for bufferring
|
||
// the requested amount of data.
|
||
//
|
||
|
||
|
||
if ( !IsListEmpty( &Connection->VcSendIrpListHead )
|
||
|
||
||
|
||
|
||
Connection->VcBufferredSendBytes >= Connection->MaxBufferredSendBytes
|
||
) {
|
||
|
||
//
|
||
// If this is a nonblocking endpoint, fail the request here and
|
||
// save going through the regular path.
|
||
//
|
||
|
||
if ( Endpoint->NonBlocking ) {
|
||
Endpoint->EventsActive &= ~AFD_POLL_SEND;
|
||
Endpoint->EnableSendEvent = TRUE;
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_TRACE_LEVEL,
|
||
"AfdFastIoDeviceControl: Endp %p, Active %lX\n",
|
||
Endpoint,
|
||
Endpoint->EventsActive
|
||
));
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Update count of send bytes pending on the connection.
|
||
//
|
||
|
||
Connection->VcBufferredSendBytes += SendLength;
|
||
Connection->VcBufferredSendCount += 1;
|
||
|
||
//
|
||
// Indicate to the caller that it is OK to proceed with the send.
|
||
//
|
||
|
||
return FALSE;
|
||
|
||
} // AfdShouldSendBlock
|
||
|