|
|
/*++
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
|