Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1265 lines
37 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
poll.c
Abstract:
Contains AfdPoll to handle IOCTL_AFD_POLL.
Author:
David Treadwell (davidtr) 4-Apr-1992
Revision History:
--*/
#include "afdp.h"
typedef struct _AFD_POLL_ENDPOINT_INFO {
PAFD_ENDPOINT Endpoint;
PFILE_OBJECT FileObject;
HANDLE Handle;
ULONG PollEvents;
} AFD_POLL_ENDPOINT_INFO, *PAFD_POLL_ENDPOINT_INFO;
typedef struct _AFD_POLL_INFO_INTERNAL {
LIST_ENTRY PollListEntry;
ULONG NumberOfEndpoints;
PIRP Irp;
KDPC Dpc;
KTIMER Timer;
BOOLEAN Unique;
BOOLEAN TimerStarted;
AFD_POLL_ENDPOINT_INFO EndpointInfo[1];
} AFD_POLL_INFO_INTERNAL, *PAFD_POLL_INFO_INTERNAL;
VOID
AfdCancelPoll (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
AfdFreePollInfo (
IN PAFD_POLL_INFO_INTERNAL PollInfoInternal
);
VOID
AfdTimeoutPoll (
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdPoll )
#pragma alloc_text( PAGEAFD, AfdCancelPoll )
#pragma alloc_text( PAGEAFD, AfdFreePollInfo )
#pragma alloc_text( PAGEAFD, AfdTimeoutPoll )
#endif
NTSTATUS
AfdPoll (
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
)
{
NTSTATUS status;
PAFD_POLL_INFO pollInfo;
PAFD_POLL_HANDLE_INFO pollHandleInfo;
PAFD_POLL_INFO_INTERNAL pollInfoInternal;
PAFD_POLL_INFO_INTERNAL freePollInfo = NULL;
ULONG pollInfoInternalSize;
PAFD_POLL_ENDPOINT_INFO pollEndpointInfo;
ULONG i;
KIRQL oldIrql1, oldIrql2;
//
// Set up locals.
//
pollInfo = Irp->AssociatedIrp.SystemBuffer;
IF_DEBUG(POLL) {
KdPrint(( "AfdPoll: poll IRP %lx, IrpSp %lx, handles %ld, "
"TO %lx,%lx\n",
Irp, IrpSp,
pollInfo->NumberOfHandles,
pollInfo->Timeout.HighPart, pollInfo->Timeout.LowPart ));
}
Irp->IoStatus.Information = 0;
//
// Determine how large the internal poll information structure will
// be and allocate space for it from nonpaged pool. It must be
// nonpaged since this will be accesses in event handlers.
//
pollInfoInternalSize = sizeof(AFD_POLL_INFO_INTERNAL) +
(pollInfo->NumberOfHandles + 1) * sizeof(AFD_POLL_ENDPOINT_INFO);
pollInfoInternal = AFD_ALLOCATE_POOL(
NonPagedPool,
pollInfoInternalSize,
AFD_POLL_POOL_TAG
);
if ( pollInfoInternal == NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
//
// Initialize the internal information buffer.
//
pollInfoInternal->Irp = Irp;
pollInfoInternal->NumberOfEndpoints = 0;
pollInfoInternal->Unique = pollInfo->Unique;
pollHandleInfo = pollInfo->Handles;
pollEndpointInfo = pollInfoInternal->EndpointInfo;
for ( i = 0; i < pollInfo->NumberOfHandles; i++ ) {
status = ObReferenceObjectByHandle(
pollHandleInfo->Handle,
0L, // DesiredAccess
*IoFileObjectType,
KernelMode,
(PVOID *)&pollEndpointInfo->FileObject,
NULL
);
if ( !NT_SUCCESS(status) ) {
AfdFreePollInfo( pollInfoInternal );
goto complete;
}
//
// Make sure that this is an AFD endpoint and not some other
// random file handle.
//
if ( pollEndpointInfo->FileObject->DeviceObject != AfdDeviceObject ) {
ObDereferenceObject( pollEndpointInfo->FileObject );
status = STATUS_INVALID_HANDLE;
AfdFreePollInfo( pollInfoInternal );
goto complete;
}
pollEndpointInfo->PollEvents = pollHandleInfo->PollEvents;
pollEndpointInfo->Handle = pollHandleInfo->Handle;
pollEndpointInfo->Endpoint = pollEndpointInfo->FileObject->FsContext;
ASSERT( InterlockedIncrement( &pollEndpointInfo->Endpoint->ObReferenceBias ) > 0 );
//
// Remember that there has been a poll on this endpoint. This flag
// allows us to optimize AfdIndicatePollEvent() for endpoints that have
// never been polled, which is a common case.
//
pollEndpointInfo->Endpoint->PollCalled = TRUE;
IF_DEBUG(POLL) {
KdPrint(( "AfdPoll: event %lx, endp %lx, conn %lx, handle %lx, "
"info %lx\n",
pollEndpointInfo->PollEvents,
pollEndpointInfo->Endpoint,
AFD_CONNECTION_FROM_ENDPOINT( pollEndpointInfo->Endpoint ),
pollEndpointInfo->Handle,
pollEndpointInfo ));
}
REFERENCE_ENDPOINT( pollEndpointInfo->Endpoint );
//
// Increment pointers in the poll info structures.
//
pollHandleInfo++;
pollEndpointInfo++;
pollInfoInternal->NumberOfEndpoints++;
}
//
// Set up a cancel routine in the IRP so that the IRP will be
// completed correctly if it gets canceled. First check whether the
// IRP has already been canceled.
//
IoAcquireCancelSpinLock( &oldIrql1 );
if ( Irp->Cancel ) {
//
// The IRP has already been canceled. Free the internal
// poll information structure and complete the IRP.
//
IoReleaseCancelSpinLock( oldIrql1 );
AfdFreePollInfo( pollInfoInternal );
status = STATUS_CANCELLED;
goto complete;
} else {
IoSetCancelRoutine( Irp, AfdCancelPoll );
}
//
// Hold the AFD spin lock while we check for endpoints that already
// satisfy a condition to synchronize between this operation and
// a call to AfdIndicatePollEvent. We release the spin lock
// after all the endpoints have been checked and the internal
// poll info structure is on the global list so AfdIndicatePollEvent
// can find it if necessary.
//
// Note that we continue to hold the cancel spin lock while we do
// this in order to prevent the IRP from being cancelled before it
// is on the global list of poll IRPs. If we didn't do this, the
// IRP could get cancelled, but AfdCancelPoll wouldn't find it and
// the IO would never get cancelled.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql2 );
//
// If this is a unique poll, determine whether there is another
// unique poll on this endpoint. If there is an existing unique
// poll, cancel it. This request will supercede the existing
// request.
//
if ( pollInfo->Unique ) {
PLIST_ENTRY listEntry;
for ( listEntry = AfdPollListHead.Flink;
listEntry != &AfdPollListHead;
listEntry = listEntry->Flink ) {
PAFD_POLL_INFO_INTERNAL testInfo;
BOOLEAN timerCancelSucceeded;
testInfo = CONTAINING_RECORD(
listEntry,
AFD_POLL_INFO_INTERNAL,
PollListEntry
);
if ( testInfo->Unique &&
testInfo->EndpointInfo[0].FileObject ==
pollInfoInternal->EndpointInfo[0].FileObject ) {
IF_DEBUG(POLL) {
KdPrint(( "AfdPoll: found existing unique poll IRP %lx "
"for file object %lx, context %lx, cancelling.\n",
testInfo->Irp,
testInfo->EndpointInfo[0].FileObject,
testInfo ));
}
//
// Cancel the IRP manually rather than calling
// AfdCancelPoll because we already hold the
// AfdSpinLock, we can't acquire it recursively, and we
// don't want to release it. Remove the poll structure
// from the global list.
//
RemoveEntryList( &testInfo->PollListEntry );
//
// Cancel the timer.
//
if ( testInfo->TimerStarted ) {
timerCancelSucceeded = KeCancelTimer( &testInfo->Timer );
} else {
timerCancelSucceeded = TRUE;
}
//
// Complete the IRP with STATUS_CANCELLED as the status.
//
testInfo->Irp->IoStatus.Information = 0;
testInfo->Irp->IoStatus.Status = STATUS_CANCELLED;
IoSetCancelRoutine( testInfo->Irp, NULL );
IoCompleteRequest( testInfo->Irp, AfdPriorityBoost );
//
// Remember the poll info structure so that we'll free
// before we exit. We cannot free it now because we're
// holding the AfdSpinLock. Note that if cancelling the
// timer failed, then the timer is already running and
// it will free the poll info structure, but not
// complete the IRP since we complete it and NULL it
// here.
//
if ( timerCancelSucceeded ) {
freePollInfo = testInfo;
} else {
pollInfoInternal->Irp = NULL;
}
//
// There should be only one outstanding unique poll IRP
// on any given file object, so quit looking for another
// now that we've found one.
//
break;
}
}
}
//
// We're done with the input structure provided by the caller. Now
// walk through the internal structure and determine whether any of
// the specified endpoints are ready for the specified condition.
//
pollInfo->NumberOfHandles = 0;
pollHandleInfo = pollInfo->Handles;
pollEndpointInfo = pollInfoInternal->EndpointInfo;
for ( i = 0; i < pollInfoInternal->NumberOfEndpoints; i++ ) {
BOOLEAN found;
PAFD_ENDPOINT endpoint;
PAFD_CONNECTION connection;
found = FALSE;
endpoint = pollEndpointInfo->Endpoint;
ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) );
connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint );
ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection );
pollHandleInfo->PollEvents = 0;
pollHandleInfo->Status = STATUS_SUCCESS;
//
// Check each possible event and, if it is being polled, whether
// the endpoint is ready for that event. If the endpoint is
// ready, write information about the endpoint into the output
// buffer.
//
if ( (pollEndpointInfo->PollEvents & AFD_POLL_RECEIVE) != 0 ) {
//
// For most endpoints, a receive poll is completed when
// data arrived that does not have a posted receive.
// For listening endpoints, however, a receive poll
// completes when there is a connection available to be
// accepted.
//
if ( endpoint->State != AfdEndpointStateListening ) {
if ( (connection != NULL &&
IS_DATA_ON_CONNECTION( connection )) ||
(IS_DGRAM_ENDPOINT(endpoint) &&
ARE_DATAGRAMS_ON_ENDPOINT( endpoint )) ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE;
found = TRUE;
}
//
// If the endpoint is set up for inline reception of
// expedited data, then any expedited data should
// be indicated as normal data.
//
if ( connection != NULL && endpoint->InLine &&
IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE;
found = TRUE;
}
} else {
//
// This is really a poll to see whether a connection is
// available for an immediate accept. Convert the events
// and do the check below in the accept poll handling.
//
pollEndpointInfo->PollEvents &= ~AFD_POLL_RECEIVE;
pollEndpointInfo->PollEvents |= AFD_POLL_ACCEPT;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_RECEIVE_EXPEDITED) != 0 ) {
//
// If the endpoint is set up for inline reception of
// expedited data, do not indicate as expedited data.
//
if ( connection != NULL && !endpoint->InLine &&
IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_RECEIVE_EXPEDITED;
found = TRUE;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_SEND) != 0 ) {
//
// For unconnected non-datagram endpoints, a send poll
// should complete when a connect operation completes.
// Therefore, if this is an non-datagram endpoint which is
// not connected, do not complete the poll until the connect
// completes.
//
if ( endpoint->State == AfdEndpointStateConnected ||
IS_DGRAM_ENDPOINT(endpoint) ) {
//
// It should always be possible to do a nonblocking send
// on a datagram endpoint. For nonbufferring VC
// endpoints, check whether a blocking error has
// occurred. If so, it will not be possible to do a
// nonblocking send until a send possible indication
// arrives.
//
// For bufferring endpoints (TDI provider does not
// buffer), check whether we have too much send data
// outstanding.
//
if ( IS_DGRAM_ENDPOINT(endpoint)
||
( endpoint->TdiBufferring &&
connection->VcNonBlockingSendPossible )
||
( !endpoint->TdiBufferring &&
connection->VcBufferredSendBytes <
connection->MaxBufferredSendBytes &&
connection->VcBufferredSendCount <
connection->MaxBufferredSendCount )
||
connection->AbortIndicated ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_SEND;
found = TRUE;
}
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_ACCEPT) != 0 ) {
if ( endpoint->Type == AfdBlockTypeVcListening &&
!IsListEmpty( &endpoint->Common.VcListening.UnacceptedConnectionListHead ) ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_ACCEPT;
found = TRUE;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_CONNECT) != 0 ) {
//
// If the endpoint is now connected, complete this event.
//
if ( endpoint->State == AfdEndpointStateConnected ) {
ASSERT( NT_SUCCESS(endpoint->Common.VcConnecting.ConnectStatus) );
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_CONNECT;
found = TRUE;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_CONNECT_FAIL) != 0 ) {
//
// This is a poll to see whether a connect has failed
// recently. If the connect status indicates an error,
// then complete the poll.
//
if ( endpoint->State == AfdEndpointStateBound &&
!NT_SUCCESS(endpoint->Common.VcConnecting.ConnectStatus) ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_CONNECT_FAIL;
pollHandleInfo->Status =
endpoint->Common.VcConnecting.ConnectStatus;
found = TRUE;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_DISCONNECT) != 0 ) {
if ( connection != NULL && connection->DisconnectIndicated ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_DISCONNECT;
found = TRUE;
}
}
if ( (pollEndpointInfo->PollEvents & AFD_POLL_ABORT) != 0 ) {
if ( connection != NULL && connection->AbortIndicated ) {
pollHandleInfo->Handle = pollEndpointInfo->Handle;
pollHandleInfo->PollEvents |= AFD_POLL_ABORT;
found = TRUE;
}
}
//
// If the handle had a current event that was requested, update
// the count of handles in the output buffer and increment the
// pointer to the output buffer.
//
if ( found ) {
pollInfo->NumberOfHandles++;
pollHandleInfo++;
}
pollEndpointInfo++;
}
//
// If we found any endpoints that are ready, free the poll information
// structure and complete the request.
//
if ( pollInfo->NumberOfHandles > 0 ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 );
IoReleaseCancelSpinLock( oldIrql1 );
AfdFreePollInfo( pollInfoInternal );
Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo;
status = STATUS_SUCCESS;
goto complete;
}
//
// None of the endpoints are in the correct state. If a timeout was
// specified, place the poll information on the global list and set
// up a DPC and timer so that we know when to complete the IRP.
//
if ( pollInfo->Timeout.LowPart != 0 && pollInfo->Timeout.HighPart != 0 ) {
IF_DEBUG(POLL) {
KdPrint(( "AfdPoll: no current events for poll IRP %lx, "
"info %lx\n", Irp, pollInfoInternal ));
}
//
// Set up the information field of the IO status block to indicate
// that an output buffer with no handles should be returned.
// AfdIndicatePollEvent will modify this if necessary.
//
Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo;
//
// Put a pointer to the internal poll info struct into the IRP
// so that the cancel routine can find it.
//
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pollInfoInternal;
//
// Place the internal poll info struct on the global list.
//
InsertTailList( &AfdPollListHead, &pollInfoInternal->PollListEntry );
//
// If the timeout is infinite, then don't set up a timer and
// DPC. Otherwise, set up a timer so we can timeout the poll
// request if appropriate.
//
if ( pollInfo->Timeout.HighPart != 0x7FFFFFFF ) {
pollInfoInternal->TimerStarted = TRUE;
KeInitializeDpc(
&pollInfoInternal->Dpc,
AfdTimeoutPoll,
pollInfoInternal
);
KeInitializeTimer( &pollInfoInternal->Timer );
KeSetTimer(
&pollInfoInternal->Timer,
pollInfo->Timeout,
&pollInfoInternal->Dpc
);
} else {
pollInfoInternal->TimerStarted = FALSE;
}
} else {
//
// A timeout equal to 0 was specified; free the internal
// structure and complete the request with no endpoints in the
// output buffer.
//
IF_DEBUG(POLL) {
KdPrint(( "AfdPoll: zero timeout on poll IRP %lx and no "
"current events--completing.\n", Irp ));
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 );
IoReleaseCancelSpinLock( oldIrql1 );
AfdFreePollInfo( pollInfoInternal );
Irp->IoStatus.Information = (ULONG)pollHandleInfo - (ULONG)pollInfo;
status = STATUS_SUCCESS;
goto complete;
}
//
// Mark the IRP pending and release the spin locks. At this
// point the IRP may get completed or cancelled.
//
IoMarkIrpPending( Irp );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql2 );
IoReleaseCancelSpinLock( oldIrql1 );
//
// If we need to free a cancelled poll info structure, do it now.
//
if ( freePollInfo != NULL ) {
AfdFreePollInfo( freePollInfo );
}
//
// Return pending. The IRP will be completed when an appropriate
// event is indicated by the TDI provider, when the timeout is hit,
// or when the IRP is cancelled.
//
return STATUS_PENDING;
complete:
//
// If we need to free a cancelled poll info structure, do it now.
//
if ( freePollInfo != NULL ) {
AfdFreePollInfo( freePollInfo );
}
Irp->IoStatus.Status = status;
IoAcquireCancelSpinLock( &oldIrql1 );
IoSetCancelRoutine( Irp, NULL );
IoReleaseCancelSpinLock( oldIrql1 );
IoCompleteRequest( Irp, AfdPriorityBoost );
return status;
} // AfdPoll
VOID
AfdCancelPoll (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PAFD_POLL_INFO_INTERNAL pollInfoInternal;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
BOOLEAN found = FALSE;
BOOLEAN timerCancelSucceeded;
PIO_STACK_LOCATION irpSp;
irpSp = IoGetCurrentIrpStackLocation( Irp );
pollInfoInternal =
(PAFD_POLL_INFO_INTERNAL)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
IF_DEBUG(POLL) {
KdPrint(( "AfdCancelPoll called for IRP %lx\n", Irp ));
}
//
// Just release the cancel spin lock--we don't need it for
// synchronization because of the mechanism we use below.
//
IoReleaseCancelSpinLock( Irp->CancelIrql );
//
// Get the AFD spin lock and attempt to find the poll structure on
// the list of outstanding polls.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
for ( listEntry = AfdPollListHead.Flink;
listEntry != &AfdPollListHead;
listEntry = listEntry->Flink ) {
PAFD_POLL_INFO_INTERNAL testInfo;
testInfo = CONTAINING_RECORD(
listEntry,
AFD_POLL_INFO_INTERNAL,
PollListEntry
);
if ( testInfo == pollInfoInternal ) {
found = TRUE;
break;
}
}
//
// If we didn't find the poll structure on the list, then the
// indication handler got called prior to the spinlock acquisition
// above and it is already off the list. Just return and do
// nothing, as the indication handler completed the IRP.
//
if ( !found ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
IF_DEBUG(POLL) {
KdPrint(( "AfdCancelPoll: poll info %lx not found on list.\n",
pollInfoInternal ));
}
return;
}
//
// Remove the poll structure from the global list.
//
IF_DEBUG(POLL) {
KdPrint(( "AfdCancelPoll: poll info %lx found on list, completing.\n",
pollInfoInternal ));
}
RemoveEntryList( &pollInfoInternal->PollListEntry );
//
// Cancel the timer and reset the IRP pointer in the internal
// poll information structure. NULLing the IRP field
// prevents the timer routine from completing the IRP.
//
if ( pollInfoInternal->TimerStarted ) {
timerCancelSucceeded = KeCancelTimer( &pollInfoInternal->Timer );
} else {
timerCancelSucceeded = TRUE;
}
pollInfoInternal->Irp = NULL;
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Complete the IRP with STATUS_CANCELLED as the status.
//
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest( Irp, AfdPriorityBoost );
//
// Free the poll information structure if the cancel succeeded. If
// the cancel of the timer did not succeed, then the timer is
// already running and the timer DPC will free the internal
// poll info.
//
if ( timerCancelSucceeded ) {
AfdFreePollInfo( pollInfoInternal );
}
return;
} // AfdCancelPoll
VOID
AfdFreePollInfo (
IN PAFD_POLL_INFO_INTERNAL PollInfoInternal
)
{
ULONG i;
PAFD_POLL_ENDPOINT_INFO pollEndpointInfo;
IF_DEBUG(POLL) {
KdPrint(( "AfdFreePollInfo: freeing info struct at %lx\n",
PollInfoInternal ));
}
// *** Note that this routine does not remove the poll information
// structure from the global list--that is the responsibility
// of the caller!
//
// Walk the list of endpoints in the poll information structure and
// dereference each one.
//
pollEndpointInfo = PollInfoInternal->EndpointInfo;
for ( i = 0; i < PollInfoInternal->NumberOfEndpoints; i++ ) {
ASSERT( InterlockedDecrement( &pollEndpointInfo->Endpoint->ObReferenceBias ) >= 0 );
DEREFERENCE_ENDPOINT( pollEndpointInfo->Endpoint );
ObDereferenceObject( pollEndpointInfo->FileObject );
pollEndpointInfo++;
}
//
// Free the structure itself and return.
//
AFD_FREE_POOL(
PollInfoInternal,
AFD_POLL_POOL_TAG
);
return;
} // AfdFreePollInfo
VOID
AfdIndicatePollEvent (
IN PAFD_ENDPOINT Endpoint,
IN ULONG PollEventBit,
IN NTSTATUS Status
)
/*++
Routine Description:
Called to complete polls with a specific event or events.
Arguments:
Endpoint - the endpoint on which the action occurred.
PollEventBit - the event which occurred. Note that this is one of
the AFD_POLL_*_BIT values, and therefore specifies exactly one event.
Status - the status of the event, if any.
Return Value:
None.
--*/
{
LIST_ENTRY completePollListHead;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
PAFD_POLL_INFO_INTERNAL pollInfoInternal;
PAFD_POLL_INFO pollInfo;
PIRP irp;
PIO_STACK_LOCATION irpSp;
ULONG eventSelectEvent;
ULONG pollEvent;
//
// Compute the actual poll event bitmask.
//
// Note that AFD_POLL_ABORT_BIT implies AFD_POLL_SEND_BIT.
//
pollEvent = 1 << PollEventBit;
eventSelectEvent = pollEvent;
if( PollEventBit == AFD_POLL_ABORT_BIT ) {
pollEvent |= AFD_POLL_SEND;
}
//
// If we have never had a poll IRP on this endpoint, skip over the
// expensive looping below.
//
if ( Endpoint->PollCalled ) {
//
// Initialize the list of poll info structures that we'll be
// completing for this event.
//
InitializeListHead( &completePollListHead );
//
// Walk the global list of polls, searching for any the are waiting
// for the specified event on the specified endpoint.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
for ( listEntry = AfdPollListHead.Flink;
listEntry != &AfdPollListHead;
listEntry = listEntry->Flink ) {
PAFD_POLL_ENDPOINT_INFO pollEndpointInfo;
ULONG i;
ULONG foundCount = 0;
pollInfoInternal = CONTAINING_RECORD(
listEntry,
AFD_POLL_INFO_INTERNAL,
PollListEntry
);
pollInfo = pollInfoInternal->Irp->AssociatedIrp.SystemBuffer;
IF_DEBUG(POLL) {
KdPrint(( "AfdIndicatePollEvent: pollInfoInt %lx "
"IRP %lx pollInfo %lx event %lx status %lx\n",
pollInfoInternal, pollInfoInternal->Irp, pollInfo,
pollEvent, Status ));
}
//
// Walk the poll structure looking for matching endpoints.
//
pollEndpointInfo = pollInfoInternal->EndpointInfo;
for ( i = 0; i < pollInfoInternal->NumberOfEndpoints; i++ ) {
IF_DEBUG(POLL) {
KdPrint(( "AfdIndicatePollEvent: pollEndpointInfo = %lx, "
"comparing %lx, %lx\n",
pollEndpointInfo, pollEndpointInfo->Endpoint,
Endpoint ));
}
//
// Regardless of whether the caller requested to be told about
// local closes, we'll complete the IRP if an endpoint
// is being closed. When they close an endpoint, all IO on
// the endpoint must be completed.
//
if ( Endpoint == pollEndpointInfo->Endpoint &&
( (pollEvent & pollEndpointInfo->PollEvents) != 0
||
PollEventBit == AFD_POLL_LOCAL_CLOSE_BIT ) ) {
ASSERT( pollInfo->NumberOfHandles == foundCount );
IF_DEBUG(POLL) {
KdPrint(( "AfdIndicatePollEvent: endpoint %lx found "
" for event %lx\n",
pollEndpointInfo->Endpoint, pollEvent ));
}
pollInfo->NumberOfHandles++;
pollInfo->Handles[foundCount].Handle = pollEndpointInfo->Handle;
pollInfo->Handles[foundCount].PollEvents =
(pollEvent &
(pollEndpointInfo->PollEvents | AFD_POLL_LOCAL_CLOSE));
pollInfo->Handles[foundCount].Status = Status;
foundCount++;
}
pollEndpointInfo++;
}
//
// If we found any matching endpoints, remove the poll information
// structure from the global list, complete the IRP, and free the
// poll information structure.
//
if ( foundCount != 0 ) {
BOOLEAN timerCancelSucceeded;
//
// We need to release the spin lock to call AfdFreePollInfo,
// since it calls AfdDereferenceEndpoint which in turn needs
// to acquire the spin lock, and recursive spin lock
// acquisitions result in deadlock. However, we can't
// release the lock of else the state of the poll list could
// change, e.g. the next entry could get freed. Remove
// this entry from the global list and place it on a local
// list. We'll complete all the poll IRPs after walking
// the entire list.
//
RemoveEntryList( &pollInfoInternal->PollListEntry );
irp = pollInfoInternal->Irp;
irpSp = IoGetCurrentIrpStackLocation( irp );
InsertTailList(
&completePollListHead,
&irp->Tail.Overlay.ListEntry
);
//
// Cancel the timer on the poll so that it does not fire.
//
if ( pollInfoInternal->TimerStarted ) {
timerCancelSucceeded = KeCancelTimer( &pollInfoInternal->Timer );
} else {
timerCancelSucceeded = TRUE;
}
//
// If the cancel of the timer failed, then we don't want to
// free this structure since the timer routine is running.
// Let the timer routine free the structure.
//
if ( timerCancelSucceeded ) {
irpSp->Parameters.DeviceIoControl.IoControlCode =
(ULONG)pollInfoInternal;
} else {
irpSp->Parameters.DeviceIoControl.IoControlCode = (ULONG)NULL;
}
//
// Also reset the IRP field of the internal poll info
// structure so that the timer routine will not attempt to
// complete the IRP.
//
pollInfoInternal->Irp = NULL;
//
// Set up the IRP for completion now, since we have all needed
// information here.
//
irp->IoStatus.Information =
(ULONG)&pollInfo->Handles[foundCount] - (ULONG)pollInfo;
irp->IoStatus.Status = STATUS_SUCCESS;
}
}
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Now walk the list of polls we need to actually complete. Free
// the poll info structures as we go.
//
while ( !IsListEmpty( &completePollListHead ) ) {
listEntry = RemoveHeadList( &completePollListHead );
ASSERT( listEntry != &completePollListHead );
irp = CONTAINING_RECORD(
listEntry,
IRP,
Tail.Overlay.ListEntry
);
irpSp = IoGetCurrentIrpStackLocation( irp );
pollInfoInternal =
(PAFD_POLL_INFO_INTERNAL)irpSp->Parameters.DeviceIoControl.IoControlCode;
IoAcquireCancelSpinLock( &oldIrql );
IoSetCancelRoutine( irp, NULL );
IoReleaseCancelSpinLock( oldIrql );
IoCompleteRequest( irp, AfdPriorityBoost );
//
// Free the poll info structure, if necessary.
//
if ( pollInfoInternal != NULL ) {
AfdFreePollInfo( pollInfoInternal );
}
}
}
//
// Acquire the lock protecting the endpoint.
//
AfdAcquireSpinLock( &Endpoint->SpinLock, &oldIrql );
//
// Signal the associated event object.
//
AfdIndicateEventSelectEvent(
Endpoint,
PollEventBit,
Status
);
AfdReleaseSpinLock( &Endpoint->SpinLock, oldIrql );
return;
} // AfdIndicatePollEvent
VOID
AfdTimeoutPoll (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PAFD_POLL_INFO_INTERNAL pollInfoInternal = DeferredContext;
PIRP irp;
PLIST_ENTRY listEntry;
KIRQL oldIrql;
BOOLEAN found = FALSE;
//
// Get the AFD spin lock and attempt to find the poll structure on
// the list of outstanding polls.
//
AfdAcquireSpinLock( &AfdSpinLock, &oldIrql );
for ( listEntry = AfdPollListHead.Flink;
listEntry != &AfdPollListHead;
listEntry = listEntry->Flink ) {
PAFD_POLL_INFO_INTERNAL testInfo;
testInfo = CONTAINING_RECORD(
listEntry,
AFD_POLL_INFO_INTERNAL,
PollListEntry
);
if ( testInfo == pollInfoInternal ) {
found = TRUE;
break;
}
}
ASSERT( pollInfoInternal->TimerStarted );
//
// If we didn't find the poll structure on the list, then the
// indication handler got called prior to the spinlock acquisition
// above and it is already off the list. Just return and do
// nothing, as the indication handler completed the IRP.
//
// We must free the internal information structure in this case,
// since the indication handler will not free it. The indication
// handler cannot free the structure because the structure contains
// the timer object, which must remain intact until this routine
// is entered.
//
if ( !found ) {
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
IF_DEBUG(POLL) {
KdPrint(( "AfdTimeoutPoll: poll info %lx not found on list.\n",
pollInfoInternal ));
}
ASSERT( pollInfoInternal->Irp == NULL );
AfdFreePollInfo( pollInfoInternal );
return;
}
//
// The IRP should not have been completed at this point.
//
ASSERT( pollInfoInternal->Irp != NULL );
irp = pollInfoInternal->Irp;
//
// Remove the poll structure from the global list.
//
IF_DEBUG(POLL) {
KdPrint(( "AfdTimeoutPoll: poll info %lx found on list, completing.\n",
pollInfoInternal ));
}
RemoveEntryList( &pollInfoInternal->PollListEntry );
AfdReleaseSpinLock( &AfdSpinLock, oldIrql );
//
// Complete the IRP pointed to in the poll structure. The
// Information field has already been set up by AfdPoll, as well as
// the output buffer.
//
IoAcquireCancelSpinLock( &oldIrql );
IoSetCancelRoutine( irp, NULL );
IoReleaseCancelSpinLock( oldIrql );
IoCompleteRequest( irp, AfdPriorityBoost );
//
// Free the poll information structure.
//
AfdFreePollInfo( pollInfoInternal );
return;
} // AfdTimeoutPoll