/*++ Copyright (c) 1989 Microsoft Corporation Module Name: close.c Abstract: This module contains code for cleanup and close IRPs. Author: David Treadwell (davidtr) 18-Mar-1992 Revision History: --*/ #include "afdp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, AfdClose ) #pragma alloc_text( PAGEAFD, AfdCleanup ) #endif NTSTATUS AfdCleanup ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This is the routine that handles Cleanup IRPs in AFD. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { NTSTATUS status; PAFD_ENDPOINT endpoint; PAFD_CONNECTION connection; KIRQL oldIrql1, oldIrql2; KIRQL cancelIrql; PLIST_ENTRY listEntry; LARGE_INTEGER processExitTime; endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); IF_DEBUG(OPEN_CLOSE) { KdPrint(( "AfdCleanup: cleanup on file object %lx, endpoint %lx, connection %lx\n", IrpSp->FileObject, endpoint, AFD_CONNECTION_FROM_ENDPOINT( endpoint ) )); } // // Get the process exit time while still at low IRQL. // processExitTime = PsGetProcessExitTime( ); // // Indicate that there was a local close on this endpoint. If there // are any outstanding polls on this endpoint, they will be // completed now. // AfdIndicatePollEvent( endpoint, AFD_POLL_LOCAL_CLOSE_BIT, STATUS_SUCCESS ); // // Remember that the endpoint has been cleaned up. This is important // because it allows AfdRestartAccept to know that the endpoint has // been cleaned up and that it should toss the connection. // AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); ASSERT( endpoint->EndpointCleanedUp == FALSE ); endpoint->EndpointCleanedUp = TRUE; connection = AFD_CONNECTION_FROM_ENDPOINT( endpoint ); ASSERT( connection == NULL || connection->Type == AfdBlockTypeConnection ); // // Complete any outstanding wait for listen IRPs on the endpoint. // if ( endpoint->Type == AfdBlockTypeVcListening ) { while ( !IsListEmpty( &endpoint->Common.VcListening.ListeningIrpListHead ) ) { PIRP waitForListenIrp; listEntry = RemoveHeadList( &endpoint->Common.VcListening.ListeningIrpListHead ); waitForListenIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); // // Release the AFD spin lock so that we can complete the // wait for listen IRP. // AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); // // Cancel the IRP. // waitForListenIrp->IoStatus.Status = STATUS_CANCELLED; waitForListenIrp->IoStatus.Information = 0; // // Reset the cancel routine in the IRP. // IoAcquireCancelSpinLock( &cancelIrql ); IoSetCancelRoutine( waitForListenIrp, NULL ); IoReleaseCancelSpinLock( cancelIrql ); IoCompleteRequest( waitForListenIrp, AfdPriorityBoost ); // // Reacquire the AFD spin lock for the next pass through the // loop. // AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); } // // Free all queued (free, unaccepted, and returned) connections // on the endpoint. // AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); AfdFreeQueuedConnections( endpoint ); AfdAcquireSpinLock( &AfdSpinLock, &oldIrql1 ); AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); endpoint->Common.VcListening.FailedConnectionAdds = 0; } UPDATE_CONN( connection, 0 ); // // If this is a connected non-datagram socket and the send side has // not been disconnected and there is no outstanding data to be // received, begin a graceful disconnect on the connection. If there // is unreceived data out outstanding IO, abort the connection. // if ( endpoint->State == AfdEndpointStateConnected && connection != NULL && !IS_DGRAM_ENDPOINT(endpoint) && ( (endpoint->DisconnectMode & AFD_ABORTIVE_DISCONNECT) == 0) && ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_SEND) == 0 || ( !endpoint->TdiBufferring && connection->Common.NonBufferring.ReceiveBytesInTransport > 0 ) ) && !connection->AbortIndicated ) { ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ); if ( IS_DATA_ON_CONNECTION( connection ) || IS_EXPEDITED_DATA_ON_CONNECTION( connection ) || processExitTime.QuadPart != 0 || endpoint->OutstandingIrpCount != 0 || ( !endpoint->TdiBufferring && (!IsListEmpty( &connection->VcReceiveIrpListHead ) || !IsListEmpty( &connection->VcSendIrpListHead )) ) ) { #if DBG if ( IS_DATA_ON_CONNECTION( connection ) ) { KdPrint(( "AfdCleanup: unrecv'd data on endp %lx, aborting. " "%ld ind, %ld taken, %ld out\n", endpoint, connection->Common.Bufferring.ReceiveBytesIndicated, connection->Common.Bufferring.ReceiveBytesTaken, connection->Common.Bufferring.ReceiveBytesOutstanding )); } if ( IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) { KdPrint(( "AfdCleanup: unrecv'd exp data on endp %lx, aborting. " "%ld ind, %ld taken, %ld out\n", endpoint, connection->Common.Bufferring.ReceiveExpeditedBytesIndicated, connection->Common.Bufferring.ReceiveExpeditedBytesTaken, connection->Common.Bufferring.ReceiveExpeditedBytesOutstanding )); } if ( processExitTime.QuadPart != 0 ) { KdPrint(( "AfdCleanup: process exiting w/o closesocket, " "aborting endp %lx\n", endpoint )); } if ( endpoint->OutstandingIrpCount != 0 ) { KdPrint(( "AfdCleanup: 3 IRPs outstanding on endpoint %lx, " "aborting.\n", endpoint )); } #endif AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); (VOID)AfdBeginAbort( connection ); } else { endpoint->DisconnectMode |= AFD_PARTIAL_DISCONNECT_RECEIVE; AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); (VOID)AfdBeginDisconnect( endpoint, NULL, NULL ); } } else { AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdReleaseSpinLock( &AfdSpinLock, oldIrql1 ); } // // If this a datagram endpoint, cancel all IRPs and free any buffers // of data. Note that if the state of the endpoint is just "open" // (not bound, etc.) then we can't have any pended IRPs or datagrams // on the endpoint. Also, the lists of IRPs and datagrams may not // yet been initialized if the state is just open. // if ( endpoint->State != AfdEndpointStateOpen && endpoint->Type == AfdBlockTypeDatagram ) { // // Reset the counts of datagrams bufferred on the endpoint. // This prevents anyone from thinking that there is bufferred // data on the endpoint. // endpoint->BufferredDatagramCount = 0; endpoint->BufferredDatagramBytes = 0; // // Cancel all receive datagram and peek datagram IRPs on the // endpoint. // AfdCompleteIrpList( &endpoint->ReceiveDatagramIrpListHead, &endpoint->SpinLock, STATUS_CANCELLED, AfdCleanupReceiveDatagramIrp ); AfdCompleteIrpList( &endpoint->PeekDatagramIrpListHead, &endpoint->SpinLock, STATUS_CANCELLED, AfdCleanupReceiveDatagramIrp ); } // // If this is a datagram endpoint, return the process quota which we // charged when the endpoint was created. // if ( endpoint->Type == AfdBlockTypeDatagram ) { PsReturnPoolQuota( endpoint->OwningProcess, NonPagedPool, endpoint->Common.Datagram.MaxBufferredSendBytes + endpoint->Common.Datagram.MaxBufferredReceiveBytes ); AfdRecordQuotaHistory( endpoint->OwningProcess, -(LONG)(endpoint->Common.Datagram.MaxBufferredSendBytes + endpoint->Common.Datagram.MaxBufferredReceiveBytes), "Cleanup dgrm", endpoint ); AfdRecordPoolQuotaReturned( endpoint->Common.Datagram.MaxBufferredSendBytes + endpoint->Common.Datagram.MaxBufferredReceiveBytes ); } // // If this is a connected VC endpoint on a nonbufferring TDI provider, // cancel all outstanding send and receive IRPs. // if ( connection != NULL ) { if ( !endpoint->TdiBufferring ) { AfdCompleteIrpList( &connection->VcReceiveIrpListHead, &endpoint->SpinLock, STATUS_CANCELLED, NULL ); AfdCompleteIrpList( &connection->VcSendIrpListHead, &endpoint->SpinLock, STATUS_CANCELLED, NULL ); } // // Remember that we have started cleanup on this connection. // We know that we'll never get a request on the connection // after we start cleanup on the connection. // AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); connection->CleanupBegun = TRUE; AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); // // Attempt to remove the connected reference. // AfdDeleteConnectedReference( connection, FALSE ); } // // If there is a transmit IRP on the endpoint, cancel it. // IoAcquireCancelSpinLock( &cancelIrql ); AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql2 ); if ( endpoint->TransmitIrp != NULL ) { endpoint->TransmitIrp->CancelIrql = cancelIrql; AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); AfdCancelTransmit( NULL, endpoint->TransmitIrp ); } else { AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql2 ); IoReleaseCancelSpinLock( cancelIrql ); } // // Remember the new state of the endpoint. // //endpoint->State = AfdEndpointStateCleanup; // // Reset relevent event handlers on the endpoint. This prevents // getting indications after we free the endpoint and connection // objects. We should not be able to get new connects after this // handle has been cleaned up. // // Note that these calls can fail if, for example, DHCP changes the // host's IP address while the endpoint is active. // if ( endpoint->AddressHandle != NULL ) { if ( endpoint->State == AfdEndpointStateListening ) { status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_CONNECT, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); } if ( IS_DGRAM_ENDPOINT(endpoint) ) { status = AfdSetEventHandler( endpoint->AddressFileObject, TDI_EVENT_RECEIVE_DATAGRAM, NULL, NULL ); //ASSERT( NT_SUCCESS(status) ); } } InterlockedIncrement( &AfdEndpointsCleanedUp ); return STATUS_SUCCESS; } // AfdCleanup NTSTATUS AfdClose ( IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++ Routine Description: This is the routine that handles Close IRPs in AFD. It dereferences the endpoint specified in the IRP, which will result in the endpoint being freed when all other references go away. Arguments: Irp - Pointer to I/O request packet. IrpSp - pointer to the IO stack location to use for this request. Return Value: NTSTATUS -- Indicates whether the request was successfully queued. --*/ { PAFD_ENDPOINT endpoint; PAGED_CODE( ); endpoint = IrpSp->FileObject->FsContext; ASSERT( IS_AFD_ENDPOINT_TYPE( endpoint ) ); IF_DEBUG(OPEN_CLOSE) { KdPrint(( "AfdClose: closing file object %lx, endpoint %lx\n", IrpSp->FileObject, endpoint )); } AfdCloseEndpoint( endpoint ); return STATUS_SUCCESS; } // AfdClose