1885 lines
53 KiB
C
1885 lines
53 KiB
C
/*++
|
||
|
||
Copyright (c) 1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
recvvc.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines for handling data receive for connection-
|
||
oriented endpoints.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 21-Oct-1993
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "afdp.h"
|
||
|
||
VOID
|
||
AfdCancelReceive (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
PIRP
|
||
AfdGetPendedReceiveIrp (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN BOOLEAN Expedited
|
||
);
|
||
|
||
PAFD_BUFFER
|
||
AfdGetReceiveBuffer (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN ULONG ReceiveFlags,
|
||
IN PAFD_BUFFER StartingAfdBuffer OPTIONAL
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGEAFD, AfdBReceive )
|
||
#pragma alloc_text( PAGEAFD, AfdBReceiveEventHandler )
|
||
#pragma alloc_text( PAGEAFD, AfdBReceiveExpeditedEventHandler )
|
||
#pragma alloc_text( PAGEAFD, AfdCancelReceive )
|
||
#pragma alloc_text( PAGEAFD, AfdGetPendedReceiveIrp )
|
||
#pragma alloc_text( PAGEAFD, AfdGetReceiveBuffer )
|
||
#pragma alloc_text( PAGEAFD, AfdRestartBufferReceive )
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
AfdBReceive (
|
||
IN PIRP Irp,
|
||
IN PIO_STACK_LOCATION IrpSp,
|
||
IN ULONG RecvFlags,
|
||
IN ULONG AfdFlags,
|
||
IN ULONG RecvLength
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
KIRQL oldIrql;
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
ULONG bytesReceived;
|
||
BOOLEAN peek;
|
||
PAFD_BUFFER afdBuffer;
|
||
BOOLEAN completeMessage;
|
||
BOOLEAN partialReceivePossible;
|
||
PAFD_BUFFER newAfdBuffer;
|
||
|
||
//
|
||
// Set up some local variables.
|
||
//
|
||
|
||
endpoint = IrpSp->FileObject->FsContext;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
|
||
connection = endpoint->Common.VcConnecting.Connection;
|
||
ASSERT( connection != NULL );
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
//
|
||
// Determine if this is a peek operation.
|
||
//
|
||
|
||
ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != 0 );
|
||
ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_EITHER );
|
||
|
||
peek = ( RecvFlags & TDI_RECEIVE_PEEK ) != 0;
|
||
|
||
//
|
||
// Determine whether it is legal to complete this receive with a
|
||
// partial message.
|
||
//
|
||
|
||
if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
|
||
|
||
partialReceivePossible = TRUE;
|
||
|
||
} else {
|
||
|
||
if ( (RecvFlags & TDI_RECEIVE_PARTIAL) != 0 ) {
|
||
partialReceivePossible = TRUE;
|
||
} else {
|
||
partialReceivePossible = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reset the InputBufferLength field of our stack location. We'll
|
||
// use this to keep track of how much data we've placed into the IRP
|
||
// so far.
|
||
//
|
||
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
|
||
|
||
//
|
||
// If this is an inline endpoint, then either type of receive data
|
||
// can be used to satisfy this receive.
|
||
//
|
||
|
||
if ( endpoint->InLine ) {
|
||
RecvFlags |= TDI_RECEIVE_EITHER;
|
||
}
|
||
|
||
//
|
||
// Check whether the remote end has aborted the connection, in which
|
||
// case we should complete the receive.
|
||
//
|
||
|
||
if ( connection->AbortIndicated ) {
|
||
status = STATUS_CONNECTION_RESET;
|
||
goto complete;
|
||
}
|
||
|
||
//
|
||
// Try to get data already bufferred on the connection to satisfy
|
||
// this receive.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &Irp->CancelIrql );
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
||
|
||
if( RecvFlags & TDI_RECEIVE_EXPEDITED ) {
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE_EXPEDITED;
|
||
}
|
||
|
||
if( RecvFlags & TDI_RECEIVE_NORMAL ) {
|
||
endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
|
||
}
|
||
|
||
IF_DEBUG(EVENT_SELECT) {
|
||
KdPrint((
|
||
"AfdBReceive: Endp %08lX, Active %08lX\n",
|
||
endpoint,
|
||
endpoint->EventsActive
|
||
));
|
||
}
|
||
|
||
newAfdBuffer = NULL;
|
||
afdBuffer = NULL;
|
||
afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer );
|
||
|
||
while ( afdBuffer != NULL ) {
|
||
|
||
//
|
||
// Copy the data to the MDL in the IRP. Note that we do not
|
||
// handle the case where, for a stream type endpoint, the
|
||
// receive IRP is large enough to take multiple data buffers
|
||
// worth of information. Our method works fine, albeit a little
|
||
// slower. The faster, where the output buffer is filled up as
|
||
// much as possible, is done in the fast path. We should only
|
||
// be here if we hit a timing window between a fast path attempt
|
||
// and a receive indication.
|
||
//
|
||
|
||
|
||
if ( Irp->MdlAddress != NULL ) {
|
||
|
||
status = TdiCopyBufferToMdl(
|
||
afdBuffer->Buffer,
|
||
afdBuffer->DataOffset,
|
||
afdBuffer->DataLength,
|
||
Irp->MdlAddress,
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
||
&bytesReceived
|
||
);
|
||
|
||
} else {
|
||
|
||
if ( afdBuffer->DataLength == 0 ) {
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
bytesReceived = 0;
|
||
}
|
||
|
||
ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW );
|
||
|
||
ASSERT( afdBuffer->PartialMessage == TRUE || afdBuffer->PartialMessage == FALSE );
|
||
|
||
completeMessage = !afdBuffer->PartialMessage;
|
||
|
||
//
|
||
// If this wasn't a peek IRP, update information on the
|
||
// connection based on whether the entire buffer of data was
|
||
// taken.
|
||
//
|
||
|
||
if ( !peek ) {
|
||
|
||
//
|
||
// If all the data in the buffer was taken, remove the buffer
|
||
// from the connection's list and return it to the buffer pool.
|
||
//
|
||
|
||
if (status == STATUS_SUCCESS) {
|
||
|
||
ASSERT(afdBuffer->DataLength == bytesReceived);
|
||
|
||
//
|
||
// Update the counts of bytes bufferred on the connection.
|
||
//
|
||
|
||
if ( afdBuffer->ExpeditedData ) {
|
||
|
||
ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived );
|
||
ASSERT( connection->VcBufferredExpeditedCount > 0 );
|
||
|
||
connection->VcBufferredExpeditedBytes -= bytesReceived;
|
||
connection->VcBufferredExpeditedCount -= 1;
|
||
|
||
} else {
|
||
|
||
ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived );
|
||
ASSERT( connection->VcBufferredReceiveCount > 0 );
|
||
|
||
connection->VcBufferredReceiveBytes -= bytesReceived;
|
||
connection->VcBufferredReceiveCount -= 1;
|
||
}
|
||
|
||
RemoveEntryList( &afdBuffer->BufferListEntry );
|
||
|
||
afdBuffer->DataOffset = 0;
|
||
afdBuffer->ExpeditedData = FALSE;
|
||
|
||
AfdReturnBuffer( afdBuffer );
|
||
|
||
//
|
||
// Reset the afdBuffer local so that we know that the
|
||
// buffer is gone.
|
||
//
|
||
|
||
afdBuffer = NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Update the counts of bytes bufferred on the connection.
|
||
//
|
||
|
||
if ( afdBuffer->ExpeditedData ) {
|
||
ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived );
|
||
connection->VcBufferredExpeditedBytes -= bytesReceived;
|
||
} else {
|
||
ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived );
|
||
connection->VcBufferredReceiveBytes -= bytesReceived;
|
||
}
|
||
|
||
//
|
||
// Not all of the buffer's data was taken. Update the
|
||
// counters in the AFD buffer structure to reflect the
|
||
// amount of data that was actually received.
|
||
//
|
||
ASSERT(afdBuffer->DataLength > bytesReceived);
|
||
|
||
afdBuffer->DataOffset += bytesReceived;
|
||
afdBuffer->DataLength -= bytesReceived;
|
||
|
||
ASSERT( afdBuffer->DataOffset < afdBuffer->BufferLength );
|
||
}
|
||
|
||
//
|
||
// 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->VcReceiveCountInTransport > 0
|
||
|
||
&&
|
||
|
||
connection->VcBufferredReceiveBytes <
|
||
connection->MaxBufferredReceiveBytes
|
||
|
||
&&
|
||
|
||
connection->VcBufferredReceiveCount <
|
||
connection->MaxBufferredReceiveCount ) {
|
||
|
||
CLONG bytesToReceive;
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
ASSERT( connection->VcReceiveCountInTransport == 1 );
|
||
connection->VcReceiveBytesInTransport = 0;
|
||
connection->VcReceiveCountInTransport = 0;
|
||
|
||
//
|
||
// Get an AFD buffer structure to hold the data.
|
||
//
|
||
|
||
newAfdBuffer = AfdGetBuffer( bytesToReceive, 0 );
|
||
if ( newAfdBuffer == NULL ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
AfdBeginAbort( connection );
|
||
status = STATUS_LOCAL_DISCONNECT;
|
||
goto complete;
|
||
}
|
||
|
||
//
|
||
// We need to remember the connection in the AFD buffer
|
||
// because we'll need to access it in the completion
|
||
// routine.
|
||
//
|
||
|
||
newAfdBuffer->Context = connection;
|
||
|
||
//
|
||
// 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,
|
||
bytesToReceive
|
||
);
|
||
|
||
//
|
||
// Wait to hand off the IRP until we can safely release
|
||
// the endpoint lock.
|
||
//
|
||
}
|
||
}
|
||
|
||
//
|
||
// For stream type endpoints, it does not make sense to return
|
||
// STATUS_BUFFER_OVERFLOW. That status is only sensible for
|
||
// message-oriented transports.
|
||
//
|
||
|
||
if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// We've set up all return information. If we got a full
|
||
// message OR if we can complete with a partial message OR if
|
||
// the IRP is full of data, clean up and complete the IRP.
|
||
//
|
||
|
||
if ( completeMessage || partialReceivePossible ||
|
||
status == STATUS_BUFFER_OVERFLOW ) {
|
||
|
||
if( ( RecvFlags & TDI_RECEIVE_NORMAL ) &&
|
||
IS_DATA_ON_CONNECTION( connection ) ) {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
|
||
if( ( RecvFlags & TDI_RECEIVE_EXPEDITED ) &&
|
||
IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
|
||
|
||
AfdIndicateEventSelectEvent(
|
||
endpoint,
|
||
endpoint->InLine
|
||
? AFD_POLL_RECEIVE_BIT
|
||
: AFD_POLL_RECEIVE_EXPEDITED_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// If there was data bufferred in the transport, fire off
|
||
// the IRP to receive it.
|
||
//
|
||
|
||
if ( newAfdBuffer != NULL ) {
|
||
(VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp );
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = bytesReceived +
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
||
|
||
|
||
IoCompleteRequest( Irp, 0 );
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Update the count of bytes we've received so far into the IRP,
|
||
// get another buffer of data, and continue.
|
||
//
|
||
|
||
IrpSp->Parameters.DeviceIoControl.InputBufferLength += bytesReceived;
|
||
afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer );
|
||
}
|
||
|
||
//
|
||
// If there was no data bufferred on the endpoint and the connection
|
||
// has been disconnected by the remote end, complete the receive
|
||
// with 0 bytes read if this is a stream endpoint, or a failure
|
||
// code if this is a message endpoint.
|
||
//
|
||
|
||
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
|
||
connection->DisconnectIndicated ) {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_GRACEFUL_DISCONNECT;
|
||
}
|
||
|
||
goto complete;
|
||
}
|
||
|
||
//
|
||
// If this is a nonblocking endpoint and the request was a normal
|
||
// receive (as opposed to a read IRP), fail the request. We don't
|
||
// fail reads under the asumption that if the application is doing
|
||
// reads they don't want nonblocking behavior.
|
||
//
|
||
|
||
if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
|
||
endpoint->NonBlocking && !( AfdFlags & AFD_OVERLAPPED ) ) {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
status = STATUS_DEVICE_NOT_READY;
|
||
goto complete;
|
||
}
|
||
|
||
//
|
||
// We'll have to pend the IRP. Remember the receive flags in the
|
||
// Type3InputBuffer field of our IO stack location.
|
||
//
|
||
|
||
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)RecvFlags;
|
||
|
||
//
|
||
// Place the IRP on the connection's list of pended receive IRPs and
|
||
// mark the IRP ad pended.
|
||
//
|
||
|
||
InsertTailList(
|
||
&connection->VcReceiveIrpListHead,
|
||
&Irp->Tail.Overlay.ListEntry
|
||
);
|
||
|
||
IoMarkIrpPending( Irp );
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Set up the cancellation routine in the IRP. If the IRP has already
|
||
// been cancelled, just call the cancellation routine here.
|
||
//
|
||
|
||
if ( Irp->Cancel ) {
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
AfdCancelReceive( IrpSp->DeviceObject, Irp );
|
||
return STATUS_CANCELLED;
|
||
}
|
||
|
||
IoSetCancelRoutine( Irp, AfdCancelReceive );
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
//
|
||
// If there was data bufferred in the transport, fire off the IRP to
|
||
// receive it. We have to wait until here because it is not legal
|
||
// to do an IoCallDriver() while holding a spin lock.
|
||
//
|
||
|
||
if ( newAfdBuffer != NULL ) {
|
||
(VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp );
|
||
}
|
||
|
||
return STATUS_PENDING;
|
||
|
||
complete:
|
||
|
||
Irp->IoStatus.Status = status;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IoCompleteRequest( Irp, 0 );
|
||
|
||
return status;
|
||
|
||
} // AfdBReceive
|
||
|
||
|
||
NTSTATUS
|
||
AfdBReceiveEventHandler (
|
||
IN PVOID TdiEventContext,
|
||
IN CONNECTION_CONTEXT ConnectionContext,
|
||
IN ULONG ReceiveFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT ULONG *BytesTaken,
|
||
IN PVOID Tsdu,
|
||
OUT PIRP *IoRequestPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles receive events for nonbufferring transports.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
KIRQL cancelIrql;
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
PLIST_ENTRY listEntry;
|
||
PAFD_BUFFER afdBuffer;
|
||
PIRP irp;
|
||
ULONG requiredAfdBufferSize;
|
||
NTSTATUS status;
|
||
ULONG receiveLength;
|
||
BOOLEAN userIrp;
|
||
BOOLEAN expedited;
|
||
BOOLEAN completeMessage;
|
||
|
||
DEBUG receiveLength = 0xFFFFFFFF;
|
||
|
||
connection = (PAFD_CONNECTION)ConnectionContext;
|
||
ASSERT( connection != NULL );
|
||
|
||
endpoint = connection->Endpoint;
|
||
ASSERT( endpoint != NULL );
|
||
*BytesTaken = 0;
|
||
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
||
endpoint->Type == AfdBlockTypeVcListening );
|
||
ASSERT( !connection->DisconnectIndicated );
|
||
|
||
ASSERT( !endpoint->TdiBufferring );
|
||
ASSERT( endpoint->EndpointType == AfdEndpointTypeStream ||
|
||
endpoint->EndpointType == AfdEndpointTypeSequencedPacket ||
|
||
endpoint->EndpointType == AfdEndpointTypeReliableMessage );
|
||
|
||
#if AFD_PERF_DBG
|
||
if ( BytesAvailable == BytesIndicated ) {
|
||
AfdFullReceiveIndications++;
|
||
} else {
|
||
AfdPartialReceiveIndications++;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// If the receive side of the endpoint has been shut down, tell the
|
||
// provider that we took all the data and reset the connection.
|
||
// Also, account for these bytes in our count of bytes taken from
|
||
// the transport.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &cancelIrql );
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
||
|
||
if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ||
|
||
endpoint->EndpointCleanedUp ) {
|
||
|
||
#if DBG
|
||
DbgPrint( "AfdBReceiveEventHandler: receive shutdown, "
|
||
"%ld bytes, aborting endp %lx\n",
|
||
BytesAvailable, endpoint );
|
||
#endif
|
||
|
||
*BytesTaken = BytesAvailable;
|
||
|
||
//
|
||
// Abort the connection. Note that if the abort attempt fails
|
||
// we can't do anything about it.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
(VOID)AfdBeginAbort( connection );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Figure out whether this is a receive indication for normal
|
||
// or expedited data, and whether this is a complete message.
|
||
//
|
||
|
||
expedited = (BOOLEAN)( (ReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0 );
|
||
|
||
ASSERT( expedited || connection->VcReceiveBytesInTransport == 0 );
|
||
ASSERT( expedited || connection->VcReceiveCountInTransport == 0 );
|
||
|
||
completeMessage = (BOOLEAN)((ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE) != 0);
|
||
|
||
//
|
||
// Check whether there are any IRPs waiting on the connection. If
|
||
// there is such an IRP and normal data is being indicated, use the
|
||
// IRP to receive the data.
|
||
//
|
||
|
||
if ( !IsListEmpty( &connection->VcReceiveIrpListHead ) && !expedited ) {
|
||
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
ASSERT( *BytesTaken == 0 );
|
||
|
||
listEntry = RemoveHeadList( &connection->VcReceiveIrpListHead );
|
||
|
||
//
|
||
// Get a pointer to the IRP and reset the cancel routine in
|
||
// the IRP. The IRP is no longer cancellable.
|
||
//
|
||
|
||
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
||
IoSetCancelRoutine( irp, NULL );
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( irp );
|
||
|
||
//
|
||
// If the IRP is not large enough to hold the available data, or
|
||
// if it is a peek or expedited receive IRP, or if we've already
|
||
// placed some data into the IRP, then we'll just buffer the
|
||
// data manually and complete the IRP in the receive completion
|
||
// routine.
|
||
//
|
||
|
||
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength >=
|
||
BytesAvailable &&
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
|
||
(ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer == 0 &&
|
||
!endpoint->TdiMessageMode ) {
|
||
|
||
//
|
||
// If all of the data was indicated to us here AND this is a
|
||
// complete message in and of itself, then just copy the
|
||
// data to the IRP and complete the IRP.
|
||
//
|
||
|
||
if ( completeMessage && BytesIndicated == BytesAvailable ) {
|
||
|
||
//
|
||
// The IRP is off the endpoint's list and is no longer
|
||
// cancellable. We can release the locks we hold.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
//
|
||
// Set BytesTaken to indicate that we've taken all the
|
||
// data. We do it here because we already have
|
||
// BytesAvailable in a register, which probably won't
|
||
// be true after making function calls.
|
||
//
|
||
|
||
*BytesTaken = BytesAvailable;
|
||
|
||
//
|
||
// Copy the data to the IRP.
|
||
//
|
||
|
||
if ( irp->MdlAddress != NULL ) {
|
||
|
||
status = TdiCopyBufferToMdl(
|
||
Tsdu,
|
||
0,
|
||
BytesAvailable,
|
||
irp->MdlAddress,
|
||
0,
|
||
&irp->IoStatus.Information
|
||
);
|
||
|
||
} else {
|
||
|
||
ASSERT( BytesAvailable == 0 );
|
||
status = STATUS_SUCCESS;
|
||
irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
//
|
||
// We should never get STATUS_BUFFER_OVERFLOW from
|
||
// TdiCopyBufferToMdl() because the user's buffer
|
||
// should have been large enough to hold all the data.
|
||
//
|
||
|
||
ASSERT( status == STATUS_SUCCESS );
|
||
|
||
//
|
||
// We have already set up the status field of the IRP
|
||
// when we pended the IRP, so there's no need to
|
||
// set it again here.
|
||
//
|
||
|
||
ASSERT( irp->IoStatus.Status == STATUS_SUCCESS );
|
||
|
||
//
|
||
// Complete the IRP. We've already set BytesTaken
|
||
// to tell the provider that we have taken all the data.
|
||
//
|
||
|
||
IoCompleteRequest( irp, AfdPriorityBoost );
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Some of the data was not indicated, so remember that we
|
||
// want to pass back this IRP to the TDI provider. Passing
|
||
// back this IRP directly is good because it avoids having
|
||
// to copy the data from one of our buffers into the user's
|
||
// buffer.
|
||
//
|
||
|
||
userIrp = TRUE;
|
||
requiredAfdBufferSize = 0;
|
||
|
||
receiveLength =
|
||
AfdIgnorePushBitOnReceives
|
||
? BytesAvailable
|
||
: irpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The first pended IRP is too tiny to hold all the
|
||
// available data or else it is a peek or expedited receive
|
||
// IRP. Put the IRP back on the head of the list and buffer
|
||
// the data and complete the IRP in the restart routine.
|
||
//
|
||
|
||
InsertHeadList(
|
||
&connection->VcReceiveIrpListHead,
|
||
&irp->Tail.Overlay.ListEntry
|
||
);
|
||
|
||
userIrp = FALSE;
|
||
requiredAfdBufferSize = BytesAvailable;
|
||
receiveLength = BytesAvailable;
|
||
}
|
||
|
||
} else if ( !expedited ) {
|
||
|
||
ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
|
||
|
||
//
|
||
// Check whether we've already bufferred the maximum amount of
|
||
// data that we'll allow ourselves to buffer for this
|
||
// connection. If we're at the limit, then we need to exert
|
||
// back pressure by not accepting this indicated data (flow
|
||
// control).
|
||
//
|
||
// Note that we have no flow control mechanisms for expedited
|
||
// data. We always accept any expedited data that is indicated
|
||
// to us.
|
||
//
|
||
|
||
if ( connection->VcBufferredReceiveBytes >=
|
||
connection->MaxBufferredReceiveBytes
|
||
|
||
||
|
||
|
||
connection->VcBufferredReceiveCount >=
|
||
connection->MaxBufferredReceiveCount ) {
|
||
|
||
ASSERT( connection->VcReceiveBytesInTransport == 0 );
|
||
ASSERT( connection->VcReceiveCountInTransport == 0 );
|
||
|
||
//
|
||
// Just remember the amount of data that is available. When
|
||
// buffer space frees up, we'll actually receive this data.
|
||
//
|
||
|
||
connection->VcReceiveBytesInTransport = BytesAvailable;
|
||
connection->VcReceiveCountInTransport = 1;
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
return STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
//
|
||
// There were no prepended IRPs. We'll have to buffer the data
|
||
// here in AFD. If all of the available data is being indicated
|
||
// to us AND this is a complete message, just copy the data
|
||
// here.
|
||
//
|
||
|
||
if ( completeMessage && BytesIndicated == BytesAvailable ) {
|
||
|
||
//
|
||
// We don't need the cancel spin lock any more, so we can
|
||
// release it. However, since we acquired the cancel spin lock
|
||
// after the endpoint spin lock and we still need the endpoint
|
||
// spin lock, be careful to switch the IRQLs.
|
||
//
|
||
|
||
IoReleaseCancelSpinLock( oldIrql );
|
||
oldIrql = cancelIrql;
|
||
|
||
//
|
||
// Get an AFD buffer to hold the data.
|
||
//
|
||
|
||
afdBuffer = AfdGetBuffer( BytesAvailable, 0 );
|
||
|
||
if ( afdBuffer == NULL ) {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
|
||
//
|
||
// If we couldn't get a buffer, abort the connection.
|
||
// This is pretty brutal, but the only alternative is
|
||
// to attempt to receive the data sometime later, which
|
||
// is very complicated to implement.
|
||
//
|
||
|
||
AfdBeginAbort( connection );
|
||
*BytesTaken = BytesAvailable;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Use the special function to copy the data instead of
|
||
// RtlCopyMemory in case the data is coming from a special
|
||
// place (DMA, etc.) which cannot work with RtlCopyMemory.
|
||
//
|
||
|
||
TdiCopyLookaheadData(
|
||
afdBuffer->Buffer,
|
||
Tsdu,
|
||
BytesAvailable,
|
||
ReceiveFlags
|
||
);
|
||
|
||
//
|
||
// Store the data length and set the offset to 0.
|
||
//
|
||
|
||
afdBuffer->DataLength = BytesAvailable;
|
||
ASSERT( afdBuffer->DataOffset == 0 );
|
||
|
||
afdBuffer->PartialMessage = FALSE;
|
||
|
||
//
|
||
// Place the buffer on this connection's list of bufferred data
|
||
// and update the count of data bytes on the connection.
|
||
//
|
||
|
||
InsertTailList(
|
||
&connection->VcReceiveBufferListHead,
|
||
&afdBuffer->BufferListEntry
|
||
);
|
||
|
||
connection->VcBufferredReceiveBytes += BytesAvailable;
|
||
connection->VcBufferredReceiveCount += 1;
|
||
|
||
//
|
||
// All done. Release the lock and tell the provider that we
|
||
// took all the data.
|
||
//
|
||
|
||
*BytesTaken = BytesAvailable;
|
||
|
||
//
|
||
// Indicate that it is possible to receive on the endpoint now.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// There were no prepended IRPs and not all of the data was
|
||
// indicated to us. We'll have to buffer it by handing an IRP
|
||
// back to the TDI privider.
|
||
//
|
||
// Note that in this case we sometimes hand a large buffer to
|
||
// the TDI provider. We do this so that it can hold off
|
||
// completion of our IRP until it gets EOM or the buffer is
|
||
// filled. This reduces the number of receive indications that
|
||
// the TDI provider has to perform and also reduces the number
|
||
// of kernel/user transitions the application will perform
|
||
// because we'll tend to complete receives with larger amounts
|
||
// of data.
|
||
//
|
||
// We do not hand back a "large" AFD buffer if the indicated data
|
||
// is greater than the large buffer size or if the TDI provider
|
||
// is message mode. The reason for not giving big buffers back
|
||
// to message providers is that they will hold on to the buffer
|
||
// until a full message is received and this would be incorrect
|
||
// behavior on a SOCK_STREAM.
|
||
//
|
||
|
||
userIrp = FALSE;
|
||
|
||
if ( AfdLargeBufferSize >= BytesAvailable &&
|
||
!AfdIgnorePushBitOnReceives &&
|
||
!endpoint->TdiMessageMode ) {
|
||
requiredAfdBufferSize = AfdLargeBufferSize;
|
||
receiveLength = AfdLargeBufferSize;
|
||
} else {
|
||
requiredAfdBufferSize = BytesAvailable;
|
||
receiveLength = BytesAvailable;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We're being indicated with expedited data. Buffer it and
|
||
// complete any pended IRPs in the restart routine. We always
|
||
// buffer expedited data to save complexity and because expedited
|
||
// data is not an important performance case.
|
||
//
|
||
// !!! do we need to perform flow control with expedited data?
|
||
//
|
||
|
||
userIrp = FALSE;
|
||
requiredAfdBufferSize = BytesAvailable;
|
||
receiveLength = BytesAvailable;
|
||
}
|
||
|
||
//
|
||
// We're able to buffer the data. First acquire a buffer of
|
||
// appropriate size.
|
||
//
|
||
|
||
afdBuffer = AfdGetBuffer( requiredAfdBufferSize, 0 );
|
||
|
||
if ( afdBuffer == NULL ) {
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
//
|
||
// If we couldn't get a buffer, abort the connection. This is
|
||
// pretty brutal, but the only alternative is to attempt to
|
||
// receive the data sometime later, which is very complicated to
|
||
// implement.
|
||
//
|
||
|
||
AfdBeginAbort( connection );
|
||
|
||
*BytesTaken = BytesAvailable;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// We'll have to format up an IRP and give it to the provider to
|
||
// handle. We don't need any locks to do this--the restart routine
|
||
// will check whether new receive IRPs were pended on the endpoint.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
//
|
||
// Use the IRP in the AFD buffer if appropriate. If userIrp is
|
||
// TRUE, then the local variable irp will already point to the
|
||
// user's IRP which we'll use for this IO.
|
||
//
|
||
|
||
if ( !userIrp ) {
|
||
irp = afdBuffer->Irp;
|
||
ASSERT( afdBuffer->Mdl == irp->MdlAddress );
|
||
}
|
||
|
||
//
|
||
// We need to remember the connection in the AFD buffer because
|
||
// we'll need to access it in the completion routine.
|
||
//
|
||
|
||
afdBuffer->Context = connection;
|
||
|
||
//
|
||
// Remember the type of data that we're receiving.
|
||
//
|
||
|
||
afdBuffer->ExpeditedData = expedited;
|
||
afdBuffer->PartialMessage = !completeMessage;
|
||
|
||
//
|
||
// Finish building the receive IRP to give to the TDI provider.
|
||
//
|
||
|
||
ASSERT( receiveLength != 0xFFFFFFFF );
|
||
|
||
TdiBuildReceive(
|
||
irp,
|
||
connection->DeviceObject,
|
||
connection->FileObject,
|
||
AfdRestartBufferReceive,
|
||
afdBuffer,
|
||
irp->MdlAddress,
|
||
ReceiveFlags & TDI_RECEIVE_EITHER,
|
||
receiveLength
|
||
);
|
||
|
||
//
|
||
// Make the next stack location current. Normally IoCallDriver would
|
||
// do this, but since we're bypassing that, we do it directly.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation( irp );
|
||
|
||
*IoRequestPacket = irp;
|
||
*BytesTaken = 0;
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // AfdBReceiveEventHandler
|
||
|
||
|
||
NTSTATUS
|
||
AfdBReceiveExpeditedEventHandler (
|
||
IN PVOID TdiEventContext,
|
||
IN CONNECTION_CONTEXT ConnectionContext,
|
||
IN ULONG ReceiveFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT ULONG *BytesTaken,
|
||
IN PVOID Tsdu,
|
||
OUT PIRP *IoRequestPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles receive expedited events for nonbufferring transports.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
return AfdBReceiveEventHandler (
|
||
TdiEventContext,
|
||
ConnectionContext,
|
||
ReceiveFlags | TDI_RECEIVE_EXPEDITED,
|
||
BytesIndicated,
|
||
BytesAvailable,
|
||
BytesTaken,
|
||
Tsdu,
|
||
IoRequestPacket
|
||
);
|
||
|
||
} // AfdBReceiveExpeditedEventHandler
|
||
|
||
|
||
NTSTATUS
|
||
AfdRestartBufferReceive (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Handles completion of bufferred receives that were started in the
|
||
receive indication handler.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - not used.
|
||
|
||
Irp - the IRP that is completing.
|
||
|
||
Context - the endpoint which received the data.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - if this is our IRP, then always
|
||
STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we
|
||
own the IRP and the IO system should stop processing the it.
|
||
|
||
If this is a user's IRP, then STATUS_SUCCESS to indicate that
|
||
IO completion should continue.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
KIRQL oldIrql;
|
||
KIRQL cancelIrql;
|
||
PAFD_BUFFER afdBuffer;
|
||
PLIST_ENTRY listEntry;
|
||
LIST_ENTRY completeIrpListHead;
|
||
NTSTATUS status;
|
||
PIRP userIrp;
|
||
BOOLEAN expedited;
|
||
NTSTATUS irpStatus;
|
||
|
||
afdBuffer = Context;
|
||
|
||
connection = afdBuffer->Context;
|
||
endpoint = connection->Endpoint;
|
||
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
|
||
endpoint->Type == AfdBlockTypeVcListening );
|
||
|
||
ASSERT( !endpoint->TdiBufferring );
|
||
ASSERT( endpoint->EndpointType == AfdEndpointTypeStream ||
|
||
endpoint->EndpointType == AfdEndpointTypeSequencedPacket ||
|
||
endpoint->EndpointType == AfdEndpointTypeReliableMessage );
|
||
|
||
//
|
||
// If the IRP being completed is actually a user's IRP, set it up
|
||
// for completion and allow IO completion to finish.
|
||
//
|
||
|
||
if ( Irp != afdBuffer->Irp ) {
|
||
|
||
//
|
||
// Free the AFD buffer we've been using to track this request.
|
||
//
|
||
|
||
AfdReturnBuffer( afdBuffer );
|
||
|
||
//
|
||
// If pending has be returned for this IRP then mark the current
|
||
// stack as pending.
|
||
//
|
||
|
||
if ( Irp->PendingReturned ) {
|
||
IoMarkIrpPending( Irp );
|
||
}
|
||
|
||
//
|
||
// Tell the IO system that it is OK to continue with IO
|
||
// completion.
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// If the receive failed, abort the connection.
|
||
//
|
||
|
||
irpStatus = Irp->IoStatus.Status;
|
||
|
||
if ( !NT_SUCCESS(irpStatus) ) {
|
||
|
||
//
|
||
// We treat STATUS_BUFFER_OVERFLOW just like STATUS_RECEIVE_PARTIAL.
|
||
//
|
||
|
||
if ( irpStatus == STATUS_BUFFER_OVERFLOW ) {
|
||
|
||
irpStatus = STATUS_RECEIVE_PARTIAL;
|
||
|
||
} else {
|
||
|
||
afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
|
||
AfdReturnBuffer( afdBuffer );
|
||
|
||
//
|
||
// !!! We can't abort the connection if the connection has
|
||
// not yet been accepted because we'll still be pointing
|
||
// at the listening endpoint. We should do something,
|
||
// however. How common is this failure?
|
||
//
|
||
|
||
KdPrint(( "AfdRestartBufferReceive: IRP %lx failed on endp %lx\n",
|
||
irpStatus, endpoint ));
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remember the length of the received data.
|
||
//
|
||
|
||
afdBuffer->DataLength = Irp->IoStatus.Information;
|
||
|
||
//
|
||
// Initialize the local list we'll use to complete any receive IRPs.
|
||
// We use a list like this because we may need to complete multiple
|
||
// IRPs and we usually cannot complete IRPs at any random point due
|
||
// to any locks we might hold.
|
||
//
|
||
|
||
InitializeListHead( &completeIrpListHead );
|
||
|
||
//
|
||
// If there are any pended IRPs on the connection, complete as
|
||
// appropriate with the new information. Note that we'll try to
|
||
// complete as many pended IRPs as possible with this new buffer of
|
||
// data.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &cancelIrql );
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
||
|
||
expedited = afdBuffer->ExpeditedData;
|
||
|
||
while ( afdBuffer != NULL &&
|
||
(userIrp = AfdGetPendedReceiveIrp(
|
||
connection,
|
||
expedited )) != NULL ) {
|
||
|
||
PIO_STACK_LOCATION irpSp;
|
||
ULONG receiveFlags;
|
||
ULONG bytesCopied = 0;
|
||
BOOLEAN peek;
|
||
BOOLEAN partialReceivePossible;
|
||
|
||
//
|
||
// Set up some locals.
|
||
//
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( userIrp );
|
||
|
||
receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
peek = (BOOLEAN)( (receiveFlags & TDI_RECEIVE_PEEK) != 0 );
|
||
|
||
|
||
if ( endpoint->EndpointType == AfdEndpointTypeStream ||
|
||
(receiveFlags & TDI_RECEIVE_PARTIAL) != 0 ) {
|
||
partialReceivePossible = TRUE;
|
||
} else {
|
||
partialReceivePossible = FALSE;
|
||
}
|
||
|
||
//
|
||
// We're about to complete the IRP, so reset its cancel routine.
|
||
//
|
||
|
||
IoSetCancelRoutine( userIrp, NULL );
|
||
|
||
//
|
||
// Copy data to the user's IRP.
|
||
//
|
||
|
||
if ( userIrp->MdlAddress != NULL ) {
|
||
|
||
status = TdiCopyBufferToMdl(
|
||
afdBuffer->Buffer,
|
||
afdBuffer->DataOffset,
|
||
afdBuffer->DataLength,
|
||
userIrp->MdlAddress,
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength,
|
||
&bytesCopied
|
||
);
|
||
|
||
userIrp->IoStatus.Information =
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength + bytesCopied;
|
||
|
||
} else {
|
||
|
||
if ( afdBuffer->DataLength == 0 ) {
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
userIrp->IoStatus.Information = 0;
|
||
}
|
||
|
||
ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW );
|
||
|
||
//
|
||
// If the IRP was not a peek IRP, update the AFD buffer
|
||
// accordingly. If it was a peek IRP then the data should be
|
||
// reread, so keep it around.
|
||
//
|
||
|
||
if ( !peek ) {
|
||
|
||
//
|
||
// If we copied all of the data from the buffer to the IRP,
|
||
// free the AFD buffer structure.
|
||
//
|
||
|
||
if ( status == STATUS_SUCCESS ) {
|
||
|
||
ASSERT(afdBuffer->DataLength == bytesCopied);
|
||
|
||
afdBuffer->DataOffset = 0;
|
||
afdBuffer->ExpeditedData = FALSE;
|
||
|
||
AfdReturnBuffer( afdBuffer );
|
||
afdBuffer = NULL;
|
||
|
||
//
|
||
// *** NOTE THAT AFTER THIS POINT WE CANNOT TOUCH EITHER
|
||
// THE AFD BUFFER OR THE IRP!
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// There is more data left in the buffer. Update counts in
|
||
// the AFD buffer structure.
|
||
//
|
||
|
||
ASSERT(afdBuffer->DataLength > bytesCopied);
|
||
|
||
afdBuffer->DataOffset += bytesCopied;
|
||
afdBuffer->DataLength -= bytesCopied;
|
||
|
||
ASSERT(afdBuffer->DataOffset < afdBuffer->BufferLength);
|
||
}
|
||
}
|
||
|
||
//
|
||
// For stream type endpoints, it does not make sense to return
|
||
// STATUS_BUFFER_OVERFLOW. That status is only sensible for
|
||
// message-oriented transports. We have already set up the
|
||
// status field of the IRP when we pended it, so we don't
|
||
// need to do it again here.
|
||
//
|
||
|
||
if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
|
||
ASSERT( userIrp->IoStatus.Status == STATUS_SUCCESS );
|
||
} else {
|
||
userIrp->IoStatus.Status = status;
|
||
}
|
||
|
||
//
|
||
// We can complete the IRP under any of the following
|
||
// conditions:
|
||
//
|
||
// - the buffer contains a complete message of data.
|
||
//
|
||
// - it is OK to complete the IRP with a partial message.
|
||
//
|
||
// - the IRP is already full of data.
|
||
//
|
||
|
||
if ( irpStatus == STATUS_SUCCESS
|
||
|
||
||
|
||
|
||
partialReceivePossible
|
||
|
||
||
|
||
|
||
status == STATUS_BUFFER_OVERFLOW ) {
|
||
|
||
//
|
||
// Add the IRP to the list of IRPs we'll need to complete once we
|
||
// can release locks.
|
||
//
|
||
|
||
InsertTailList(
|
||
&completeIrpListHead,
|
||
&userIrp->Tail.Overlay.ListEntry
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Update the count of data placed into the IRP thus far.
|
||
//
|
||
|
||
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength += bytesCopied;
|
||
|
||
//
|
||
// Put the IRP back on the connection's list of pended IRPs.
|
||
//
|
||
|
||
InsertHeadList(
|
||
&connection->VcReceiveIrpListHead,
|
||
&userIrp->Tail.Overlay.ListEntry
|
||
);
|
||
|
||
//
|
||
// Stop processing this buffer for now.
|
||
//
|
||
// !!! This could cause a problem if there is a regular
|
||
// receive pended behind a peek IRP! But that is a
|
||
// pretty unlikely scenario.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there is any data left, place the buffer at the end of the
|
||
// connection's list of bufferred data and update counts of data on
|
||
// the connection.
|
||
//
|
||
|
||
if ( afdBuffer != NULL ) {
|
||
|
||
InsertTailList(
|
||
&connection->VcReceiveBufferListHead,
|
||
&afdBuffer->BufferListEntry
|
||
);
|
||
|
||
if ( expedited ) {
|
||
connection->VcBufferredExpeditedBytes += afdBuffer->DataLength;
|
||
connection->VcBufferredExpeditedCount += 1;
|
||
} else {
|
||
connection->VcBufferredReceiveBytes += afdBuffer->DataLength;
|
||
connection->VcBufferredReceiveCount += 1;
|
||
}
|
||
|
||
//
|
||
// Remember whether we got a full or partial receive in the
|
||
// AFD buffer.
|
||
//
|
||
|
||
if ( irpStatus == STATUS_RECEIVE_PARTIAL ||
|
||
irpStatus == STATUS_RECEIVE_PARTIAL_EXPEDITED ) {
|
||
afdBuffer->PartialMessage = TRUE;
|
||
} else {
|
||
afdBuffer->PartialMessage = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release locks and indicate that there is bufferred data on the
|
||
// endpoint.
|
||
//
|
||
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
IoReleaseCancelSpinLock( cancelIrql );
|
||
|
||
//
|
||
// If there was leftover data, complete polls as necessary. Indicate
|
||
// expedited data if the endpoint is not InLine and expedited data
|
||
// was received; otherwise, indicate normal data.
|
||
//
|
||
|
||
if ( afdBuffer != NULL ) {
|
||
|
||
if ( expedited && !endpoint->InLine ) {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE_EXPEDITED_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
} else {
|
||
|
||
AfdIndicatePollEvent(
|
||
endpoint,
|
||
AFD_POLL_RECEIVE_BIT,
|
||
STATUS_SUCCESS
|
||
);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Complete IRPs as necessary.
|
||
//
|
||
|
||
while ( !IsListEmpty( &completeIrpListHead ) ) {
|
||
|
||
listEntry = RemoveHeadList( &completeIrpListHead );
|
||
userIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
||
|
||
IoCompleteRequest( userIrp, AfdPriorityBoost );
|
||
}
|
||
|
||
//
|
||
// Tell the IO system to stop processing the AFD IRP, since we now
|
||
// own it as part of the AFD buffer.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // AfdRestartBufferReceive
|
||
|
||
|
||
VOID
|
||
AfdCancelReceive (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cancels a receive IRP that is pended in AFD.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - not used.
|
||
|
||
Irp - the IRP to cancel.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpSp;
|
||
PAFD_ENDPOINT endpoint;
|
||
PAFD_CONNECTION connection;
|
||
KIRQL oldIrql;
|
||
|
||
//
|
||
// Get the endpoint pointer from our IRP stack location and the
|
||
// connection pointer from the endpoint.
|
||
//
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
endpoint = irpSp->FileObject->FsContext;
|
||
ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
|
||
|
||
connection = endpoint->Common.VcConnecting.Connection;
|
||
ASSERT( connection->Type == AfdBlockTypeConnection );
|
||
|
||
//
|
||
// Remove the IRP from the connection's IRP list, synchronizing with
|
||
// the endpoint lock which protects the lists. Note that the IRP
|
||
// *must* be on one of the connection's lists if we are getting
|
||
// called here--anybody that removes the IRP from the list must do
|
||
// so while holding the cancel spin lock and reset the cancel
|
||
// routine to NULL before releasing the cancel spin lock.
|
||
//
|
||
|
||
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
|
||
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
||
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
|
||
|
||
//
|
||
// Reset the cancel routine in the IRP.
|
||
//
|
||
|
||
IoSetCancelRoutine( Irp, NULL );
|
||
|
||
//
|
||
// Release the cancel spin lock and complete the IRP with a
|
||
// cancellation status code.
|
||
//
|
||
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
IoCompleteRequest( Irp, AfdPriorityBoost );
|
||
|
||
return;
|
||
|
||
} // AfdCancelReceive
|
||
|
||
|
||
PAFD_BUFFER
|
||
AfdGetReceiveBuffer (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN ULONG ReceiveFlags,
|
||
IN PAFD_BUFFER StartingAfdBuffer OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns a pointer to a receive data buffer that contains the
|
||
appropriate type of data. Note that this routine DOES NOT remove
|
||
the buffer structure from the list it is on.
|
||
|
||
This routine MUST be called with the connection's endpoint lock
|
||
held!
|
||
|
||
Arguments:
|
||
|
||
Connection - a pointer to the connection to search for data.
|
||
|
||
ReceiveFlags - the type of receive data to look for.
|
||
|
||
StartingAfdBuffer - if non-NULL, start looking for a buffer AFTER
|
||
this buffer.
|
||
|
||
Return Value:
|
||
|
||
PAFD_BUFFER - a pointer to an AFD buffer of the appropriate data type,
|
||
or NULL if there was no appropriate buffer on the connection.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY listEntry;
|
||
PAFD_BUFFER afdBuffer;
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
|
||
|
||
//
|
||
// Start with the first AFD buffer on the connection.
|
||
//
|
||
|
||
listEntry = Connection->VcReceiveBufferListHead.Flink;
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
|
||
|
||
//
|
||
// If a starting AFD buffer was specified, walk past that buffer in
|
||
// the connection list.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( StartingAfdBuffer ) ) {
|
||
|
||
while ( TRUE ) {
|
||
|
||
if ( afdBuffer == StartingAfdBuffer ) {
|
||
listEntry = listEntry->Flink;
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
|
||
break;
|
||
}
|
||
|
||
listEntry = listEntry->Flink;
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
|
||
|
||
ASSERT( listEntry != &Connection->VcReceiveBufferListHead );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Act based on the type of data we're trying to get.
|
||
//
|
||
|
||
switch ( ReceiveFlags & TDI_RECEIVE_EITHER ) {
|
||
|
||
case TDI_RECEIVE_NORMAL:
|
||
|
||
//
|
||
// Walk the connection's list of data buffers until we find the
|
||
// first data buffer that is of the appropriate type.
|
||
//
|
||
|
||
while ( listEntry != &Connection->VcReceiveBufferListHead &&
|
||
afdBuffer->ExpeditedData ) {
|
||
|
||
listEntry = afdBuffer->BufferListEntry.Flink;
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
|
||
}
|
||
|
||
if ( listEntry != &Connection->VcReceiveBufferListHead ) {
|
||
return afdBuffer;
|
||
} else {
|
||
return NULL;
|
||
}
|
||
|
||
case TDI_RECEIVE_EITHER :
|
||
|
||
//
|
||
// Just return the first buffer, if there is one.
|
||
//
|
||
|
||
if ( listEntry != &Connection->VcReceiveBufferListHead ) {
|
||
return afdBuffer;
|
||
} else {
|
||
return NULL;
|
||
}
|
||
|
||
case TDI_RECEIVE_EXPEDITED:
|
||
|
||
if ( Connection->VcBufferredExpeditedCount == 0 ) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Walk the connection's list of data buffers until we find the
|
||
// first data buffer that is of the appropriate type.
|
||
//
|
||
|
||
while ( listEntry != &Connection->VcReceiveBufferListHead &&
|
||
!afdBuffer->ExpeditedData ) {
|
||
|
||
listEntry = afdBuffer->BufferListEntry.Flink;
|
||
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
|
||
}
|
||
|
||
if ( listEntry != &Connection->VcReceiveBufferListHead ) {
|
||
return afdBuffer;
|
||
} else {
|
||
return NULL;
|
||
}
|
||
|
||
default:
|
||
|
||
ASSERT( !"Invalid ReceiveFlags" );
|
||
return NULL;
|
||
}
|
||
|
||
} // AfdGetReceiveBuffer
|
||
|
||
|
||
PIRP
|
||
AfdGetPendedReceiveIrp (
|
||
IN PAFD_CONNECTION Connection,
|
||
IN BOOLEAN Expedited
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes a receive IRP from the connection's list of receive IRPs.
|
||
Only returns an IRP which is valid for the specified type of
|
||
data, normal or expedited. If there are no IRPs pended or only
|
||
IRPs of the wrong type, returns NULL.
|
||
|
||
This routine MUST be called with the connection's endpoint lock
|
||
held!
|
||
|
||
Arguments:
|
||
|
||
Connection - a pointer to the connection to search for an IRP.
|
||
|
||
Expedited - TRUE if this routine should return a receive IRP which
|
||
can receive expedited data.
|
||
|
||
Return Value:
|
||
|
||
PIRP - a pointer to an IRP which can receive data of the specified
|
||
type. The IRP IS removed from the connection's list of pended
|
||
receive IRPs.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
ULONG receiveFlags;
|
||
PLIST_ENTRY listEntry;
|
||
|
||
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
|
||
|
||
//
|
||
// Walk the list of pended receive IRPs looking for one which can
|
||
// be completed with the specified type of data.
|
||
//
|
||
|
||
for ( listEntry = Connection->VcReceiveIrpListHead.Flink;
|
||
listEntry != &Connection->VcReceiveIrpListHead;
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
//
|
||
// Get a pointer to the IRP and our stack location in the IRP.
|
||
//
|
||
|
||
irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
|
||
irpSp = IoGetCurrentIrpStackLocation( irp );
|
||
|
||
//
|
||
// Determine whether this IRP can receive the data type we need.
|
||
//
|
||
|
||
receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
receiveFlags &= TDI_RECEIVE_EITHER;
|
||
ASSERT( receiveFlags != 0 );
|
||
|
||
if ( receiveFlags == TDI_RECEIVE_NORMAL && !Expedited ) {
|
||
|
||
//
|
||
// We have a normal receive and normal data. Remove this
|
||
// IRP from the connection's list and return it.
|
||
//
|
||
|
||
RemoveEntryList( listEntry );
|
||
return irp;
|
||
}
|
||
|
||
if ( receiveFlags == TDI_RECEIVE_EITHER ) {
|
||
|
||
//
|
||
// This is an "either" receive. It can take the data
|
||
// regardless of the data type.
|
||
//
|
||
|
||
RemoveEntryList( listEntry );
|
||
return irp;
|
||
}
|
||
|
||
if ( receiveFlags == TDI_RECEIVE_EXPEDITED && Expedited ) {
|
||
|
||
//
|
||
// We have an expedited receive and expedited data. Remove
|
||
// this IRP from the connection's list and return it.
|
||
//
|
||
|
||
RemoveEntryList( listEntry );
|
||
return irp;
|
||
}
|
||
|
||
//
|
||
// This IRP did not meet our criteria. Continue scanning the
|
||
// connection's list of pended IRPs for a good IRP.
|
||
//
|
||
}
|
||
|
||
//
|
||
// There were no IRPs which could be completed with the specified
|
||
// type of data.
|
||
//
|
||
|
||
return NULL;
|
||
|
||
} // AfdGetPendedReceiveIrp
|