summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/st/request.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/st/request.c801
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 */