NT4/private/ntos/afd/blkconn.c
2020-09-30 17:12:29 +02:00

1454 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
blkconn.c
Abstract:
This module contains allocate, free, close, reference, and dereference
routines for AFD connections.
Author:
David Treadwell (davidtr) 10-Mar-1992
Revision History:
--*/
#include "afdp.h"
VOID
AfdFreeConnection (
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdAbortConnection )
#pragma alloc_text( PAGE, AfdAddFreeConnection )
#pragma alloc_text( PAGE, AfdAllocateConnection )
#pragma alloc_text( PAGE, AfdCreateConnection )
#pragma alloc_text( PAGE, AfdFreeConnection )
#pragma alloc_text( PAGEAFD, AfdDereferenceConnection )
#if REFERENCE_DEBUG
#pragma alloc_text( PAGEAFD, AfdReferenceConnection )
#endif
#pragma alloc_text( PAGEAFD, AfdGetFreeConnection )
#pragma alloc_text( PAGEAFD, AfdGetReturnedConnection )
#pragma alloc_text( PAGEAFD, AfdGetUnacceptedConnection )
#pragma alloc_text( PAGEAFD, AfdAddConnectedReference )
#pragma alloc_text( PAGEAFD, AfdDeleteConnectedReference )
#endif
#if GLOBAL_REFERENCE_DEBUG
AFD_GLOBAL_REFERENCE_DEBUG AfdGlobalReference[MAX_GLOBAL_REFERENCE];
LONG AfdGlobalReferenceSlot = -1;
#endif
#if AFD_PERF_DBG
#define CONNECTION_REUSE_DISABLED (AfdDisableConnectionReuse)
#else
#define CONNECTION_REUSE_DISABLED (FALSE)
#endif
VOID
AfdAbortConnection (
IN PAFD_CONNECTION Connection
)
{
NTSTATUS status;
ASSERT( Connection != NULL );
ASSERT( Connection->ConnectedReferenceAdded );
//
// Abort the connection. We need to set the CleanupBegun flag
// before initiating the abort so that the connected reference
// will get properly removed in AfdRestartAbort.
//
// Note that if AfdBeginAbort fails then AfdRestartAbort will not
// get invoked, so we must remove the connected reference ourselves.
//
Connection->CleanupBegun = TRUE;
status = AfdBeginAbort( Connection );
if( !NT_SUCCESS(status) ) {
Connection->AbortIndicated = TRUE;
AfdDeleteConnectedReference( Connection, FALSE );
}
//
// Remove the active reference.
//
DEREFERENCE_CONNECTION( Connection );
} // AfdAbortConnection
NTSTATUS
AfdAddFreeConnection (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Adds a connection object to an endpoints pool of connections available
to satisfy a connect indication.
Arguments:
Endpoint - a pointer to the endpoint to which to add a connection.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
PAFD_CONNECTION connection;
NTSTATUS status;
PAGED_CODE( );
ASSERT( Endpoint->Type == AfdBlockTypeVcListening );
//
// Create a new connection block and associated connection object.
//
status = AfdCreateConnection(
&Endpoint->TransportInfo->TransportDeviceName,
Endpoint->AddressHandle,
Endpoint->TdiBufferring,
Endpoint->InLine,
Endpoint->OwningProcess,
&connection
);
if ( !NT_SUCCESS(status) ) {
return status;
}
ASSERT( Endpoint->TdiBufferring == connection->TdiBufferring );
//
// Set up the handle in the listening connection structure and place
// the connection on the endpoint's list of listening connections.
//
ExInterlockedInsertTailList(
&Endpoint->Common.VcListening.FreeConnectionListHead,
&connection->ListEntry,
&AfdSpinLock
);
InterlockedIncrement(
&Endpoint->Common.VcListening.FreeConnectionCount
);
return STATUS_SUCCESS;
} // AfdAddFreeConnection
PAFD_CONNECTION
AfdAllocateConnection (
VOID
)
{
PAFD_CONNECTION connection;
PAGED_CODE( );
//
// Allocate a buffer to hold the endpoint structure.
//
connection = AFD_ALLOCATE_POOL(
NonPagedPool,
sizeof(AFD_CONNECTION),
AFD_CONNECTION_POOL_TAG
);
if ( connection == NULL ) {
return NULL;
}
RtlZeroMemory( connection, sizeof(AFD_CONNECTION) );
//
// Initialize the reference count to 1 to account for the caller's
// reference. Connection blocks are temporary--as soon as the last
// reference goes away, so does the connection. There is no active
// reference on a connection block.
//
connection->ReferenceCount = 1;
//
// Initialize the connection structure.
//
connection->Type = AfdBlockTypeConnection;
connection->State = AfdConnectionStateFree;
//connection->Handle = NULL;
//connection->FileObject = NULL;
//connection->RemoteAddress = NULL;
//connection->Endpoint = NULL;
//connection->ReceiveBytesIndicated = 0;
//connection->ReceiveBytesTaken = 0;
//connection->ReceiveBytesOutstanding = 0;
//connection->ReceiveExpeditedBytesIndicated = 0;
//connection->ReceiveExpeditedBytesTaken = 0;
//connection->ReceiveExpeditedBytesOutstanding = 0;
//connection->ConnectDataBuffers = NULL;
//connection->DisconnectIndicated = FALSE;
//connection->AbortIndicated = FALSE;
//connection->ConnectedReferenceAdded = FALSE;
//connection->SpecialCondition = FALSE;
//connection->CleanupBegun = FALSE;
//connection->OwningProcess = NULL;
//connection->ClosePendedTransmit = FALSE;
#if REFERENCE_DEBUG
connection->CurrentReferenceSlot = -1;
RtlZeroMemory(
&connection->ReferenceDebug,
sizeof(AFD_REFERENCE_DEBUG) * MAX_REFERENCE
);
#endif
//
// Return a pointer to the new connection to the caller.
//
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdAllocateConnection: connection at %lx\n", connection ));
}
return connection;
} // AfdAllocateConnection
NTSTATUS
AfdCreateConnection (
IN PUNICODE_STRING TransportDeviceName,
IN HANDLE AddressHandle,
IN BOOLEAN TdiBufferring,
IN BOOLEAN InLine,
IN PEPROCESS ProcessToCharge,
OUT PAFD_CONNECTION *Connection
)
/*++
Routine Description:
Allocates a connection block and creates a connection object to
go with the block. This routine also associates the connection
with the specified address handle (if any).
Arguments:
TransportDeviceName - Name to use when creating the connection object.
AddressHandle - a handle to an address object for the specified
transport. If specified (non NULL), the connection object that
is created is associated with the address object.
TdiBufferring - whether the TDI provider supports data bufferring.
Only passed so that it can be stored in the connection
structure.
InLine - if TRUE, the endpoint should be created in OOB inline
mode.
ProcessToCharge - the process which should be charged the quota
for this connection.
Connection - receives a pointer to the new connection.
Return Value:
NTSTATUS -- Indicates the status of the request.
--*/
{
NTSTATUS status;
IO_STATUS_BLOCK ioStatusBlock;
OBJECT_ATTRIBUTES objectAttributes;
CHAR eaBuffer[sizeof(FILE_FULL_EA_INFORMATION) - 1 +
TDI_CONNECTION_CONTEXT_LENGTH + 1 +
sizeof(CONNECTION_CONTEXT)];
PFILE_FULL_EA_INFORMATION ea;
CONNECTION_CONTEXT UNALIGNED *ctx;
PAFD_CONNECTION connection;
PAGED_CODE( );
//
// Attempt to charge this process quota for the data bufferring we
// will do on its behalf.
//
try {
PsChargePoolQuota(
ProcessToCharge,
NonPagedPool,
AfdReceiveWindowSize + AfdSendWindowSize
);
} except ( EXCEPTION_EXECUTE_HANDLER ) {
#if DBG
DbgPrint( "AfdCreateConnection: PsChargePoolQuota failed.\n" );
#endif
return STATUS_QUOTA_EXCEEDED;
}
//
// Allocate a connection block.
//
connection = AfdAllocateConnection( );
if ( connection == NULL ) {
PsReturnPoolQuota(
ProcessToCharge,
NonPagedPool,
AfdReceiveWindowSize + AfdSendWindowSize
);
return STATUS_INSUFFICIENT_RESOURCES;
}
AfdRecordQuotaHistory(
ProcessToCharge,
(LONG)(AfdReceiveWindowSize+AfdSendWindowSize),
"CreateConn ",
connection
);
AfdRecordPoolQuotaCharged( AfdReceiveWindowSize + AfdSendWindowSize );
//
// Remember the process that got charged the pool quota for this
// connection object. Also reference the process to which we're
// going to charge the quota so that it is still around when we
// return the quota.
//
ASSERT( connection->OwningProcess == NULL );
connection->OwningProcess = ProcessToCharge;
ObReferenceObject( ProcessToCharge );
//
// If the provider does not buffer, initialize appropriate lists in
// the connection object.
//
connection->TdiBufferring = TdiBufferring;
if ( !TdiBufferring ) {
InitializeListHead( &connection->VcReceiveIrpListHead );
InitializeListHead( &connection->VcSendIrpListHead );
InitializeListHead( &connection->VcReceiveBufferListHead );
connection->VcBufferredReceiveBytes = 0;
connection->VcBufferredExpeditedBytes = 0;
connection->VcBufferredReceiveCount = 0;
connection->VcBufferredExpeditedCount = 0;
connection->VcReceiveBytesInTransport = 0;
connection->VcReceiveCountInTransport = 0;
connection->VcBufferredSendBytes = 0;
connection->VcBufferredSendCount = 0;
} else {
connection->VcNonBlockingSendPossible = TRUE;
connection->VcZeroByteReceiveIndicated = FALSE;
}
//
// Set up the send and receive window with default maximums.
//
connection->MaxBufferredReceiveBytes = AfdReceiveWindowSize;
connection->MaxBufferredReceiveCount =
(CSHORT)(AfdReceiveWindowSize / AfdBufferMultiplier);
connection->MaxBufferredSendBytes = AfdSendWindowSize;
connection->MaxBufferredSendCount =
(CSHORT)(AfdSendWindowSize / AfdBufferMultiplier);
//
// We need to open a connection object to the TDI provider for this
// endpoint. First create the EA for the connection context and the
// object attributes structure which will be used for all the
// connections we open here.
//
ea = (PFILE_FULL_EA_INFORMATION)eaBuffer;
ea->NextEntryOffset = 0;
ea->Flags = 0;
ea->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
ea->EaValueLength = sizeof(CONNECTION_CONTEXT);
RtlMoveMemory( ea->EaName, TdiConnectionContext, ea->EaNameLength + 1 );
//
// Use the pointer to the connection block as the connection context.
//
ctx = (CONNECTION_CONTEXT UNALIGNED *)&ea->EaName[ea->EaNameLength + 1];
*ctx = (CONNECTION_CONTEXT)connection;
InitializeObjectAttributes(
&objectAttributes,
TransportDeviceName,
OBJ_CASE_INSENSITIVE, // attributes
NULL,
NULL
);
//
// Do the actual open of the connection object.
//
KeAttachProcess( AfdSystemProcess );
status = ZwCreateFile(
&connection->Handle,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL, // AllocationSize
0, // FileAttributes
0, // ShareAccess
0, // CreateDisposition
0, // CreateOptions
eaBuffer,
FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
ea->EaNameLength + 1 + ea->EaValueLength
);
if ( NT_SUCCESS(status) ) {
status = ioStatusBlock.Status;
}
if ( !NT_SUCCESS(status) ) {
KeDetachProcess( );
DEREFERENCE_CONNECTION( connection );
return status;
}
AfdRecordConnOpened();
//
// Reference the connection's file object.
//
status = ObReferenceObjectByHandle(
connection->Handle,
0,
(POBJECT_TYPE) NULL,
KernelMode,
(PVOID *)&connection->FileObject,
NULL
);
ASSERT( NT_SUCCESS(status) );
IF_DEBUG(OPEN_CLOSE) {
KdPrint(( "AfdCreateConnection: file object for connection %lx at "
"%lx\n", connection, connection->FileObject ));
}
AfdRecordConnRef();
//
// Remember the device object to which we need to give requests for
// this connection object. We can't just use the
// fileObject->DeviceObject pointer because there may be a device
// attached to the transport protocol.
//
connection->DeviceObject =
IoGetRelatedDeviceObject( connection->FileObject );
//
// Associate the connection with the address object on the endpoint if
// an address handle was specified.
//
if ( AddressHandle != NULL ) {
TDI_REQUEST_USER_ASSOCIATE associateRequest;
associateRequest.AddressHandle = AddressHandle;
status = ZwDeviceIoControlFile(
connection->Handle,
NULL, // EventHandle
NULL, // APC Routine
NULL, // APC Context
&ioStatusBlock,
IOCTL_TDI_ASSOCIATE_ADDRESS,
(PVOID)&associateRequest, // InputBuffer
sizeof(associateRequest), // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if ( status == STATUS_PENDING ) {
status = ZwWaitForSingleObject( connection->Handle, TRUE, NULL );
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock.Status;
}
}
KeDetachProcess( );
//
// If requested, set the connection to be inline.
//
if ( InLine ) {
status = AfdSetInLineMode( connection, TRUE );
if ( !NT_SUCCESS(status) ) {
DEREFERENCE_CONNECTION( connection );
return status;
}
}
//
// Set up the connection pointer and return.
//
*Connection = connection;
UPDATE_CONN( connection, connection->FileObject );
return STATUS_SUCCESS;
} // AfdCreateConnection
VOID
AfdFreeConnection (
IN PVOID Context
)
{
NTSTATUS status;
PAFD_CONNECTION connection;
BOOLEAN reuseConnection;
PAFD_ENDPOINT listeningEndpoint;
PAGED_CODE( );
ASSERT( Context != NULL );
connection = CONTAINING_RECORD(
Context,
AFD_CONNECTION,
WorkItem
);
ASSERT( connection->ReferenceCount == 0 );
ASSERT( connection->Type == AfdBlockTypeConnection );
//
// Determine whether we can reuse this connection object. We reuse
// connection objects to assist performance when the connection
// object is from a listening endpoint.
//
if ( connection->Endpoint != NULL &&
!CONNECTION_REUSE_DISABLED &&
!connection->Endpoint->EndpointCleanedUp &&
connection->Endpoint->Type == AfdBlockTypeVcConnecting &&
connection->Endpoint->Common.VcConnecting.ListenEndpoint != NULL ) {
UPDATE_CONN( connection, 0 );
//
// Reference the listening endpoint so that it does not
// go away while we are cleaning up this connection object
// for reuse.
//
listeningEndpoint = connection->Endpoint->Common.VcConnecting.ListenEndpoint;
ASSERT( listeningEndpoint->Type == AfdBlockTypeVcListening );
REFERENCE_ENDPOINT( listeningEndpoint );
reuseConnection = TRUE;
} else {
UPDATE_CONN( connection, 0 );
reuseConnection = FALSE;
//
// Free and dereference the various objects on the connection.
// Close and dereference the TDI connection object on the endpoint,
// if any.
//
if ( connection->Handle != NULL ) {
IO_STATUS_BLOCK ioStatusBlock;
HANDLE handle;
KeAttachProcess( AfdSystemProcess );
handle = connection->Handle;
connection->Handle = NULL;
//
// Disassociate this connection object from the address object.
//
status = ZwDeviceIoControlFile(
handle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&ioStatusBlock, // IoStatusBlock
IOCTL_TDI_DISASSOCIATE_ADDRESS, // IoControlCode
NULL, // InputBuffer
0, // InputBufferLength
NULL, // OutputBuffer
0 // OutputBufferLength
);
if( status == STATUS_PENDING ) {
status = ZwWaitForSingleObject(
handle,
TRUE,
NULL
);
ASSERT( NT_SUCCESS(status) );
status = ioStatusBlock.Status;
}
// ASSERT( NT_SUCCESS(status) );
//
// Close the handle.
//
status = ZwClose( handle );
ASSERT( NT_SUCCESS(status) );
AfdRecordConnClosed();
KeDetachProcess( );
}
if ( connection->FileObject != NULL ) {
ObDereferenceObject( connection->FileObject );
connection->FileObject = NULL;
AfdRecordConnDeref();
}
//
// Return the quota we charged to this process when we allocated
// the connection object.
//
PsReturnPoolQuota(
connection->OwningProcess,
NonPagedPool,
connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes
);
AfdRecordQuotaHistory(
connection->OwningProcess,
-(LONG)(connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes),
"ConnDealloc ",
connection
);
AfdRecordPoolQuotaReturned(
connection->MaxBufferredReceiveBytes + connection->MaxBufferredSendBytes
);
//
// Dereference the process that got the quota charge.
//
ASSERT( connection->OwningProcess != NULL );
ObDereferenceObject( connection->OwningProcess );
connection->OwningProcess = NULL;
}
if ( !connection->TdiBufferring && connection->VcDisconnectIrp != NULL ) {
IoFreeIrp( connection->VcDisconnectIrp );
connection->VcDisconnectIrp = NULL;
}
//
// If we're going to reuse this connection, don't free the remote
// address structure--we'll reuse it as well.
//
if ( connection->RemoteAddress != NULL && !reuseConnection ) {
AFD_FREE_POOL(
connection->RemoteAddress,
AFD_REMOTE_ADDRESS_POOL_TAG
);
connection->RemoteAddress = NULL;
}
if ( connection->ConnectDataBuffers != NULL ) {
AfdFreeConnectDataBuffers( connection->ConnectDataBuffers );
connection->ConnectDataBuffers = NULL;
}
//
// If this is a bufferring connection, remove all the AFD buffers
// from the connection's lists and free them.
//
if ( !connection->TdiBufferring ) {
PAFD_BUFFER afdBuffer;
PLIST_ENTRY listEntry;
ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) );
while ( !IsListEmpty( &connection->VcReceiveBufferListHead ) ) {
listEntry = RemoveHeadList( &connection->VcReceiveBufferListHead );
afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
afdBuffer->DataOffset = 0;
afdBuffer->ExpeditedData = FALSE;
AfdReturnBuffer( afdBuffer );
}
}
if ( connection->Endpoint != NULL ) {
//
// If there is a transmit file IRP on the endpoint, complete it.
//
if ( connection->ClosePendedTransmit ) {
AfdCompleteClosePendedTransmit( connection->Endpoint );
}
DEREFERENCE_ENDPOINT( connection->Endpoint );
connection->Endpoint = NULL;
}
//
// Either free the actual connection block or put it back on the
// listening endpoint's list of available connection objects.
//
if ( reuseConnection ) {
//
// Reinitialize various fields in the connection object.
//
connection->ReferenceCount = 1;
ASSERT( connection->Type == AfdBlockTypeConnection );
connection->State = AfdConnectionStateFree;
connection->DisconnectIndicated = FALSE;
connection->AbortIndicated = FALSE;
connection->ConnectedReferenceAdded = FALSE;
connection->SpecialCondition = FALSE;
connection->CleanupBegun = FALSE;
connection->ClosePendedTransmit = FALSE;
if ( !connection->TdiBufferring ) {
ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
ASSERT( IsListEmpty( &connection->VcSendIrpListHead ) );
ASSERT( IsListEmpty( &connection->VcReceiveBufferListHead ) );
connection->VcBufferredReceiveBytes = 0;
connection->VcBufferredExpeditedBytes = 0;
connection->VcBufferredReceiveCount = 0;
connection->VcBufferredExpeditedCount = 0;
connection->VcReceiveBytesInTransport = 0;
connection->VcReceiveCountInTransport = 0;
connection->VcBufferredSendBytes = 0;
connection->VcBufferredSendCount = 0;
} else {
connection->VcNonBlockingSendPossible = TRUE;
connection->VcZeroByteReceiveIndicated = FALSE;
}
//
// Place the connection on the listening endpoint's list of
// available connections.
//
ExInterlockedInsertHeadList(
&listeningEndpoint->Common.VcListening.FreeConnectionListHead,
&connection->ListEntry,
&AfdSpinLock
);
//
// Reduce the count of failed connection adds on the listening
// endpoint to account for this connection object which we're
// adding back onto the queue.
//
InterlockedDecrement(
&listeningEndpoint->Common.VcListening.FailedConnectionAdds
);
InterlockedIncrement(
&listeningEndpoint->Common.VcListening.FreeConnectionCount
);
//
// Get rid of the reference we added to the listening endpoint
// above.
//
DEREFERENCE_ENDPOINT( listeningEndpoint );
} else {
#if ENABLE_ABORT_TIMER_HACK
//
// Free any attached abort timer.
//
if( connection->AbortTimerInfo != NULL ) {
AFD_FREE_POOL(
connection->AbortTimerInfo,
AFD_ABORT_TIMER_HACK_POOL_TAG
);
}
#endif // ENABLE_ABORT_TIMER_HACK
//
// Free the space that holds the connection itself.
//
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdFreeConnection: Freeing connection at %lx\n", connection ));
}
connection->Type = 0xAFDF;
AFD_FREE_POOL(
connection,
AFD_CONNECTION_POOL_TAG
);
}
} // AfdFreeConnection
#if REFERENCE_DEBUG
VOID
AfdDereferenceConnection (
IN PAFD_CONNECTION Connection,
IN PVOID Info1,
IN PVOID Info2
)
#else
VOID
AfdDereferenceConnection (
IN PAFD_CONNECTION Connection
)
#endif
{
LONG result;
KIRQL oldIrql;
ASSERT( Connection->Type == AfdBlockTypeConnection );
ASSERT( Connection->ReferenceCount > 0 );
ASSERT( Connection->ReferenceCount != 0xD1000000 );
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdDereferenceConnection: connection %lx, new refcnt %ld\n",
Connection, Connection->ReferenceCount-1 ));
}
//
// Note that if we're tracking refcnts, we *must* call
// AfdUpdateConnectionTrack before doing the dereference. This is
// because the connection object might go away in another thread as
// soon as we do the dereference. However, because of this,
// the refcnt we store with this may sometimes be incorrect.
//
#if REFERENCE_DEBUG
AfdUpdateConnectionTrack(
Connection,
Connection->ReferenceCount - 1,
Info1,
Info2,
0xFFFFFFFF
);
#endif
//
// We must hold AfdSpinLock while doing the dereference and check
// for free. This is because some code makes the assumption that
// the connection structure will not go away while AfdSpinLock is
// held, and that code references the endpoint before releasing
// AfdSpinLock. If we did the InterlockedDecrement() without the
// lock held, our count may go to zero, that code may reference the
// connection, and then a double free might occur.
//
// It is still valuable to use the interlocked routines for
// increment and decrement of structures because it allows us to
// avoid having to hold the spin lock for a reference.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// Perform the actual decrement of the refernce count. Note that we
// use the intrinsic functions for this, because of their
// performance benefit.
//
result = InterlockedDecrement( &Connection->ReferenceCount );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// If the reference count is now 0, free the connection in an
// executive worker thread.
//
if ( result == 0 ) {
AfdQueueWorkItem(
AfdFreeConnection,
&Connection->WorkItem
);
}
} // AfdDereferenceConnection
PAFD_CONNECTION
AfdGetFreeConnection (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Takes a connection off of the endpoint's queue of listening
connections.
Arguments:
Endpoint - a pointer to the endpoint from which to get a connection.
Return Value:
AFD_CONNECTION - a pointer to an AFD connection block.
--*/
{
PAFD_CONNECTION connection;
PLIST_ENTRY listEntry;
ASSERT( Endpoint->Type == AfdBlockTypeVcListening );
//
// Remove the first entry from the list. If the list is empty,
// return NULL.
//
listEntry = ExInterlockedRemoveHeadList(
&Endpoint->Common.VcListening.FreeConnectionListHead,
&AfdSpinLock
);
if ( listEntry == NULL ) {
return NULL;
}
InterlockedDecrement(
&Endpoint->Common.VcListening.FreeConnectionCount
);
//
// Find the connection pointer from the list entry and return a
// pointer to the connection object.
//
connection = CONTAINING_RECORD(
listEntry,
AFD_CONNECTION,
ListEntry
);
return connection;
} // AfdGetFreeConnection
PAFD_CONNECTION
AfdGetReturnedConnection (
IN PAFD_ENDPOINT Endpoint,
IN ULONG Sequence
)
/*++
Routine Description:
Takes a connection off of the endpoint's queue of returned
connections.
Arguments:
Endpoint - a pointer to the endpoint from which to get a connection.
Sequence - the sequence the connection must match. This is actually
a pointer to the connection. If NULL, the first returned
connection is used.
Return Value:
AFD_CONNECTION - a pointer to an AFD connection block.
--*/
{
PAFD_CONNECTION connection;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
ASSERT( Endpoint->Type == AfdBlockTypeVcListening );
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
//
// Walk the endpoint's list of returned connections until we reach
// the end or until we find one with a matching sequence.
//
for ( listEntry = Endpoint->Common.VcListening.ReturnedConnectionListHead.Flink;
listEntry != &Endpoint->Common.VcListening.ReturnedConnectionListHead;
listEntry = listEntry->Flink ) {
connection = CONTAINING_RECORD(
listEntry,
AFD_CONNECTION,
ListEntry
);
if ( Sequence == (ULONG)connection || Sequence == 0 ) {
//
// Found the connection we were looking for. Remove
// the connection from the list, release the spin lock,
// and return the connection.
//
RemoveEntryList( listEntry );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return connection;
}
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
return NULL;
} // AfdGetReturnedConnection
PAFD_CONNECTION
AfdGetUnacceptedConnection (
IN PAFD_ENDPOINT Endpoint
)
/*++
Routine Description:
Takes a connection of the endpoint's queue of unaccpted connections.
*** NOTE: This routine must be called with AfdSpinLock held!!
Arguments:
Endpoint - a pointer to the endpoint from which to get a connection.
Return Value:
AFD_CONNECTION - a pointer to an AFD connection block.
--*/
{
PAFD_CONNECTION connection;
PLIST_ENTRY listEntry;
ASSERT( Endpoint->Type == AfdBlockTypeVcListening );
ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
if ( IsListEmpty( &Endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
return NULL;
}
//
// Dequeue a listening connection and remember its handle.
//
listEntry = RemoveHeadList( &Endpoint->Common.VcListening.UnacceptedConnectionListHead );
connection = CONTAINING_RECORD( listEntry, AFD_CONNECTION, ListEntry );
return connection;
} // AfdGetUnacceptedConnection
#if REFERENCE_DEBUG
VOID
AfdReferenceConnection (
IN PAFD_CONNECTION Connection,
IN PVOID Info1,
IN PVOID Info2
)
{
LONG result;
ASSERT( Connection->Type == AfdBlockTypeConnection );
ASSERT( Connection->ReferenceCount > 0 );
ASSERT( Connection->ReferenceCount != 0xD1000000 );
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdReferenceConnection: connection %lx, new refcnt %ld\n",
Connection, Connection->ReferenceCount+1 ));
}
//
// Do the actual increment of the reference count.
//
result = InterlockedIncrement( &Connection->ReferenceCount );
#if REFERENCE_DEBUG
AfdUpdateConnectionTrack(
Connection,
result,
Info1,
Info2,
1
);
#endif
} // AfdReferenceConnection
#endif
VOID
AfdAddConnectedReference (
IN PAFD_CONNECTION Connection
)
/*++
Routine Description:
Adds the connected reference to an AFD connection block. The
connected reference is special because it prevents the connection
object from being freed until we receive a disconnect event, or know
through some other means that the virtual circuit is disconnected.
Arguments:
Connection - a pointer to an AFD connection block.
Return Value:
None.
--*/
{
KIRQL oldIrql;
AfdAcquireSpinLock( &Connection->Endpoint->SpinLock, &oldIrql );
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdAddConnectedReference: connection %lx, new refcnt %ld\n",
Connection, Connection->ReferenceCount+1 ));
}
ASSERT( !Connection->ConnectedReferenceAdded );
ASSERT( Connection->Type == AfdBlockTypeConnection );
//
// Increment the reference count and remember that the connected
// reference has been placed on the connection object.
//
Connection->ConnectedReferenceAdded = TRUE;
AfdRecordConnectedReferencesAdded();
AfdReleaseSpinLock( &Connection->Endpoint->SpinLock, oldIrql );
REFERENCE_CONNECTION( Connection );
} // AfdAddConnectedReference
VOID
AfdDeleteConnectedReference (
IN PAFD_CONNECTION Connection,
IN BOOLEAN EndpointLockHeld
)
/*++
Routine Description:
Removes the connected reference to an AFD connection block. If the
connected reference has already been removed, this routine does
nothing. The connected reference should be removed as soon as we
know that it is OK to close the connection object handle, but not
before. Removing this reference too soon could abort a connection
which shouldn't get aborted.
Arguments:
Connection - a pointer to an AFD connection block.
EndpointLockHeld - TRUE if the caller already has the endpoint
spin lock. The lock remains held on exit.
Return Value:
None.
--*/
{
KIRQL oldIrql;
PAFD_ENDPOINT endpoint;
#if REFERENCE_DEBUG
PVOID caller, callersCaller;
RtlGetCallersAddress( &caller, &callersCaller );
#endif
endpoint = Connection->Endpoint;
if ( !EndpointLockHeld ) {
AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
}
//
// Only do a dereference if the connected reference is still active
// on the connectiuon object.
//
if ( Connection->ConnectedReferenceAdded ) {
//
// Three things must be true before we can remove the connected
// reference:
//
// 1) There must be no sends outstanding on the connection if
// the TDI provider does not support bufferring. This is
// because AfdRestartBufferSend() looks at the connection
// object.
//
// 2) Cleanup must have started on the endpoint. Until we get a
// cleanup IRP on the endpoint, we could still get new sends.
//
// 3) We have been indicated with a disconnect on the
// connection. We want to keep the connection object around
// until we get a disconnect indication in order to avoid
// premature closes on the connection object resulting in an
// unintended abort. If the transport does not support
// orderly release, then this condition is not necessary.
//
if ( (Connection->TdiBufferring ||
Connection->VcBufferredSendCount == 0)
&&
Connection->CleanupBegun
&&
(Connection->AbortIndicated || Connection->DisconnectIndicated ||
( (endpoint->TransportInfo->ProviderInfo.ServiceFlags &
TDI_SERVICE_ORDERLY_RELEASE) == 0)) ) {
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdDeleteConnectedReference: connection %lx, "
"new refcnt %ld\n",
Connection, Connection->ReferenceCount-1 ));
}
//
// Be careful about the order of things here. We must FIRST
// reset the flag, then release the spin lock and call
// AfdDereferenceConnection(). Note that it is illegal to
// call AfdDereferenceConnection() with a spin lock held.
//
Connection->ConnectedReferenceAdded = FALSE;
AfdRecordConnectedReferencesDeleted();
if ( !EndpointLockHeld ) {
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
}
DEREFERENCE_CONNECTION( Connection );
} else {
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdDeleteConnectedReference: connection %lx, "
"%ld sends pending\n",
Connection, Connection->VcBufferredSendCount ));
}
#if REFERENCE_DEBUG
{
ULONG action;
action = 0;
if ( !Connection->TdiBufferring &&
Connection->VcBufferredSendCount != 0 ) {
action |= 0xA0000000;
}
if ( !Connection->CleanupBegun ) {
action |= 0x0B000000;
}
if ( !Connection->AbortIndicated && !Connection->DisconnectIndicated ) {
action |= 0x00C00000;
}
UPDATE_CONN( Connection, action );
}
#endif
//
// Remember that the connected reference deletion is still
// pending, i.e. there is a special condition on the
// endpoint. This will cause AfdRestartBufferSend() to do
// the actual dereference when the last send completes.
//
Connection->SpecialCondition = TRUE;
if ( !EndpointLockHeld ) {
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
}
}
} else {
IF_DEBUG(CONNECTION) {
KdPrint(( "AfdDeleteConnectedReference: already removed on "
" connection %lx, refcnt %ld\n",
Connection, Connection->ReferenceCount ));
}
if ( !EndpointLockHeld ) {
AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
}
}
return;
} // AfdDeleteConnectedReference
#if REFERENCE_DEBUG
VOID
AfdUpdateConnectionTrack (
IN PAFD_CONNECTION Connection,
IN LONG NewReferenceCount,
IN PVOID Info1,
IN PVOID Info2,
IN ULONG Action
)
{
PAFD_REFERENCE_DEBUG slot;
LONG newSlot;
newSlot = InterlockedIncrement( &Connection->CurrentReferenceSlot );
slot = &Connection->ReferenceDebug[newSlot % MAX_REFERENCE];
slot->Info1 = Info1;
slot->Info2 = Info2;
slot->Action = Action;
slot->NewCount = NewReferenceCount;
#if GLOBAL_REFERENCE_DEBUG
{
PAFD_GLOBAL_REFERENCE_DEBUG globalSlot;
newSlot = InterlockedIncrement( &AfdGlobalReferenceSlot );
globalSlot = &AfdGlobalReference[newSlot % MAX_GLOBAL_REFERENCE];
globalSlot->Info1 = Info1;
globalSlot->Info2 = Info2;
globalSlot->Action = Action;
globalSlot->NewCount = NewReferenceCount;
globalSlot->Connection = Connection;
KeQueryTickCount( &globalSlot->TickCounter );
}
#endif
} // AfdUpdateConnectionTrack
#endif