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