diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/st/request.c | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/private/ntos/tdi/st/request.c b/private/ntos/tdi/st/request.c new file mode 100644 index 000000000..de553c0cc --- /dev/null +++ b/private/ntos/tdi/st/request.c @@ -0,0 +1,801 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + This module contains code which implements the TP_REQUEST object. + Routines are provided to create, destroy, reference, and dereference, + transport request objects. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" + + +VOID +StTdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when a request + such as TdiSend, TdiReceive, TdiSendDatagram, TdiReceiveDatagram, etc., + encounters a timeout. This routine cleans up the activity and cancels it. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_REQUEST block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + PTP_CONNECTION Connection; + PIO_STACK_LOCATION IrpSp; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + Request = (PTP_REQUEST)DeferredContext; + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + Request->Flags &= ~REQUEST_FLAGS_TIMER; + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + + // + // find reason for timeout + // + + IrpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + if (IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) { + switch (IrpSp->MinorFunction) { + + // + // none of these should time out. + // + + case TDI_SEND: + case TDI_ACCEPT: + case TDI_SET_INFORMATION: + case TDI_SET_EVENT_HANDLER: + case TDI_SEND_DATAGRAM: + case TDI_RECEIVE_DATAGRAM: + case TDI_RECEIVE: + + ASSERT (FALSE); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StCompleteRequest (Request, STATUS_IO_TIMEOUT, 0); + break; + + + case TDI_LISTEN: + case TDI_CONNECT: + + Connection = (PTP_CONNECTION)(Request->Context); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Since these requests are part of the connection + // itself, we just stop the connection and the + // request will get torn down then. If we get the + // situation where the request times out before + // it is queued to the connection, then the code + // that is about to queue it will check the STOPPING + // flag and complete it then. + // + + StStopConnection (Connection, STATUS_IO_TIMEOUT); + break; + + case TDI_DISCONNECT: + + // + // We don't create requests for TDI_DISCONNECT any more. + // + + ASSERT(FALSE); + break; + + default: + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + break; + + } // end of switch + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + + StDereferenceRequest ("Timeout", Request); // for the timeout + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StDereferenceRequest ("Timeout: stopping", Request); // for the timeout + + } + + return; + +} /* RequestTimeoutHandler */ + + +VOID +StAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ) + +/*++ + +Routine Description: + + This routine allocates a request packet from nonpaged pool and initializes + it to a known state. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a place where this routine will return + a pointer to a transport request structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_REQUEST Request; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_REQUEST)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate request: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 104); + *TransportRequest = NULL; + return; + } + + Request = (PTP_REQUEST)ExAllocatePool (NonPagedPool, sizeof (TP_REQUEST)); + if (Request == NULL) { + PANIC("ST: Could not allocate request: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 204); + *TransportRequest = NULL; + return; + } + RtlZeroMemory (Request, sizeof(TP_REQUEST)); + + DeviceContext->MemoryUsage += sizeof(TP_REQUEST); + ++DeviceContext->RequestAllocated; + + Request->Type = ST_REQUEST_SIGNATURE; + Request->Size = sizeof (TP_REQUEST); + + Request->Provider = DeviceContext; + Request->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Request->SpinLock); + KeInitializeDpc (&Request->Dpc, StTdiRequestTimeoutHandler, (PVOID)Request); + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + + *TransportRequest = Request; + +} /* StAllocateRequest */ + + +VOID +StDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ) + +/*++ + +Routine Description: + + This routine frees a request packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a transport request structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportRequest); + --DeviceContext->RequestAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_REQUEST); + +} /* StDeallocateRequest */ + + +NTSTATUS +StCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ) + +/*++ + +Routine Description: + + This routine creates a transport request and associates it with the + specified IRP, context, and queue. All major requests, including + TdiSend, TdiSendDatagram, TdiReceive, and TdiReceiveDatagram requests, + are composed in this manner. + +Arguments: + + Irp - Pointer to an IRP which was received by the transport for this + request. + + Context - Pointer to anything to associate this request with. This + value is not interpreted except at request cancelation time. + + Flags - A set of bitflags indicating the disposition of this request. + + Timeout - Timeout value (if non-zero) to start a timer for this request. + If zero, then no timer is activated for the request. + + TpRequest - If the function returns STATUS_SUCCESS, this will return + pointer to the TP_REQUEST structure allocated. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PLIST_ENTRY p; + PIO_STACK_LOCATION irpSp; + + + irpSp = IoGetCurrentIrpStackLocation(Irp); + DeviceContext = (PDEVICE_CONTEXT)irpSp->FileObject->DeviceObject; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->RequestPool); + if (p == &DeviceContext->RequestPool) { + + if ((DeviceContext->RequestMaxAllocated == 0) || + (DeviceContext->RequestAllocated < DeviceContext->RequestMaxAllocated)) { + + StAllocateRequest (DeviceContext, &Request); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 404); + Request = NULL; + + } + + if (Request == NULL) { + ++DeviceContext->RequestExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: Could not allocate request object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + } + + ++DeviceContext->RequestInUse; + if (DeviceContext->RequestInUse > DeviceContext->RequestMaxInUse) { + ++DeviceContext->RequestMaxInUse; + } + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // fill out the request. + // + + // Request->Provider = DeviceContext; + Request->IoRequestPacket = Irp; + Request->Buffer2 = Buffer2; + Request->Buffer2Length = Buffer2Length; + Request->Flags = Flags; + Request->Context = Context; + Request->ReferenceCount = 1; // initialize reference count. + + if ((Timeout.LowPart == 0) && (Timeout.HighPart == 0)) { + + // no timeout + } else { + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + StReferenceRequest ("Create: timer", Request); // one for the timer + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + } + + *TpRequest = Request; + + return STATUS_SUCCESS; +} /* StCreateRequest */ + + +VOID +StDestroyRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine returns a request block to the free pool. + +Arguments: + + Request - Pointer to a TP_REQUEST block to return to the free pool. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + + // + // Return the request to the caller with whatever status is in the IRP. + // + + // + // Now dereference the owner of this request so that we are safe when + // we finally tear down the {connection, address}. The problem we're + // facing here is that we can't allow the user to assume semantics; + // the end of life for a connection must truly be the real end of life. + // for that to occur, we reference the owning object when the request is + // created and we dereference it just before we return it to the pool. + // + + switch (Request->Owner) { + case ConnectionType: + if (!(Request->Flags & REQUEST_FLAGS_DELAY)) { + StDereferenceConnection ("Removing Connection",((PTP_CONNECTION)Request->Context)); + } + break; + + case AddressType: + StDereferenceAddress ("Removing Address", ((PTP_ADDRESS)Request->Context)); + break; + + case DeviceContextType: + StDereferenceDeviceContext ("Removing Address", ((PDEVICE_CONTEXT)Request->Context)); + break; + } + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + DeviceContext = Request->Provider; + + if (Request->Flags & REQUEST_FLAGS_DELAY) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + InsertTailList( + &DeviceContext->IrpCompletionQueue, + &Request->IoRequestPacket->Tail.Overlay.ListEntry); + + } else { + + IoCompleteRequest (Request->IoRequestPacket, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + } + + // + // Put the request back on the free list. NOTE: we have the + // lock held here. + // + + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + --DeviceContext->RequestInUse; + + if ((DeviceContext->RequestAllocated - DeviceContext->RequestInUse) > + DeviceContext->RequestInitAllocated) { + StDeallocateRequest (DeviceContext, Request); + } else { + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} /* StDestroyRequest */ + + +VOID +StRefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport request. + +Arguments: + + Request - Pointer to a TP_REQUEST block. + +Return Value: + + none. + +--*/ + +{ + ASSERT (Request->ReferenceCount > 0); + + InterlockedIncrement (&Request->ReferenceCount); + +} /* StRefRequest */ + + +VOID +StDerefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine dereferences a transport request by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + StDestroyRequest to remove it from the system. + +Arguments: + + Request - Pointer to a transport request object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Request->ReferenceCount); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + StDestroyRequest (Request); + } + +} /* StDerefRequest */ + + +VOID +StCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport request object, completing the I/O, + stopping the timeout, and freeing up the request object itself. + +Arguments: + + Request - Pointer to a transport request object. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIRP Irp; + NTSTATUS FinalStatus = Status; + BOOLEAN TimerWasSet; + + ASSERT (Status != STATUS_PENDING); + + if (Request->Flags & REQUEST_FLAGS_SEND_RCV) { + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Request->Flags |= REQUEST_FLAGS_STOPPING; + Irp = Request->IoRequestPacket; + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + StDereferenceRequest ("Complete", Request); // remove creation reference. + return; + } + + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + Request->Flags |= REQUEST_FLAGS_STOPPING; + + // + // Cancel the pending timeout on this request. Not all requests + // have their timer set. If this request has the TIMER bit set, + // then the timer needs to be cancelled. If it cannot be cancelled, + // then the timer routine will be run, so we just return and let + // the timer routine worry about cleaning up this request. + // + + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + TimerWasSet = KeCancelTimer (&Request->Timer); + + if (TimerWasSet) { + StDereferenceRequest ("Complete: stop timer", Request); + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Irp = Request->IoRequestPacket; + + + // + // Install the return code in the IRP so that when we call StDestroyRequest, + // it will get completed with the proper return status. + // + + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + // + // The entire transport is done with this request. + // + + StDereferenceRequest ("Complete", Request); // remove creation reference. + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + +} /* StCompleteRequest */ + + +VOID +StRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a send IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ASSERT (IRP_REFCOUNT(IrpSp) > 0); + + InterlockedIncrement (&IRP_REFCOUNT(IrpSp)); + +} /* StRefSendIrp */ + + +VOID +StDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport send IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + + NOTE: This assume that IRP_CONNECTION(IrpSp) has been changed + to point to the IRP instead of the connection. + +Arguments: + + Request - Pointer to a transport send IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&IRP_REFCOUNT(IrpSp)); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + + PIRP Irp = (PIRP)IRP_CONNECTION(IrpSp); + + IRP_REFCOUNT(IrpSp) = 0; + IRP_CONNECTION (IrpSp) = NULL; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + +} /* StDerefSendIrp */ + + +VOID +StCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport send IRP. + +Arguments: + + Irp - Pointer to a send IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_CONNECTION(IrpSp); + + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + IRP_CONNECTION(IrpSp) = Irp; + + StDereferenceSendIrp ("Complete", IrpSp); // remove creation reference. + + StDereferenceConnection ("Removing Connection", Connection); + +} /* StCompleteSendIrp */ |