|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
queue.c
Abstract:
This module implements IRP queue processing routines for ws2ifsl.sys driver.
Author:
Vadim Eydelman (VadimE) Dec-1996
Revision History:
Vadim Eydelman (VadimE) Oct-1997, rewrite to properly handle IRP cancellation --*/
#include "precomp.h"
//
// Private prototypes
//
VOID QueueKernelRoutine ( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine, IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 );
VOID SignalRequest ( IN PIFSL_PROCESS_CTX ProcessCtx );
VOID RequestRundownRoutine ( IN struct _KAPC *Apc );
VOID FlushRequestQueue ( PIFSL_QUEUE Queue );
VOID QueuedCancelRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
VOID SignalCancel ( IN PIFSL_PROCESS_CTX ProcessCtx );
VOID CancelRundownRoutine ( IN struct _KAPC *Apc );
VOID FlushCancelQueue ( PIFSL_QUEUE Queue );
#pragma alloc_text(PAGE, InitializeRequestQueue)
#pragma alloc_text(PAGE, InitializeCancelQueue)
VOID InitializeRequestQueue ( IN PIFSL_PROCESS_CTX ProcessCtx, IN PKTHREAD ApcThread, IN KPROCESSOR_MODE ApcMode, IN PKNORMAL_ROUTINE ApcRoutine, IN PVOID ApcContext ) /*++
Routine Description:
Initializes request queue object
Arguments: ProcessCtx - process context to which queue belongs ApcThread - thread to which to queue APC requests for processing ApcMode - mode of the caller (should be user) ApcRoutine - routine that processes requests ApcContext - context to pass to the routine in addition to request parameters
Return Value: None --*/ { PAGED_CODE ();
InitializeListHead (&ProcessCtx->RequestQueue.ListHead); ProcessCtx->RequestQueue.Busy = FALSE; KeInitializeSpinLock (&ProcessCtx->RequestQueue.Lock); KeInitializeApc (&ProcessCtx->RequestQueue.Apc, ApcThread, OriginalApcEnvironment, QueueKernelRoutine, RequestRundownRoutine, ApcRoutine, ApcMode, ApcContext); }
BOOLEAN QueueRequest ( IN PIFSL_PROCESS_CTX ProcessCtx, IN PIRP Irp ) /*++
Routine Description:
Queues IRP to IFSL request queue and signals to user mode DLL to start processing if it is not busy already.
Arguments: ProcessCtx - process context in which to queue Irp - request to be queued
Return Value: TRUE - IRP was queued FALSE - IRP was already cancelled
--*/ { BOOLEAN res; KIRQL oldIRQL; PIFSL_QUEUE queue = &ProcessCtx->RequestQueue;
IoSetCancelRoutine (Irp, QueuedCancelRoutine); KeAcquireSpinLock (&queue->Lock, &oldIRQL); if (!Irp->Cancel) { //
// Request is not cancelled, insert it into the queue
//
InsertTailList (&queue->ListHead, &Irp->Tail.Overlay.ListEntry); Irp->Tail.Overlay.IfslRequestQueue = queue;
//
// If queue wasn't busy, signal to user mode DLL to pick up new request
//
if (!queue->Busy) { ASSERT (queue->ListHead.Flink==&Irp->Tail.Overlay.ListEntry); SignalRequest (ProcessCtx); ASSERT (queue->Busy); } res = TRUE; } else { res = FALSE; } KeReleaseSpinLock (&queue->Lock, oldIRQL);
return res;
} // QueueRequest
PIRP DequeueRequest ( PIFSL_PROCESS_CTX ProcessCtx, ULONG UniqueId, BOOLEAN *more ) /*++
Routine Description:
Removes IRP from IFSL request queue.
Arguments: ProcessCtx - process context from which to remove UniqueId - unique request id more - set to TRUE if there are more requests in the queue
Return Value: IRP - pointer to the IRP NULL - the request was not in the queue
--*/ { KIRQL oldIRQL; PIRP irp; PIFSL_QUEUE queue = &ProcessCtx->RequestQueue;
KeAcquireSpinLock (&queue->Lock, &oldIRQL); irp = CONTAINING_RECORD (queue->ListHead.Flink, IRP, Tail.Overlay.ListEntry); if (!IsListEmpty (&queue->ListHead) && (irp->Tail.Overlay.IfslRequestId==UlongToPtr(UniqueId))) { //
// Queue is not empty and first request matches passed in parameters,
// dequeue and return it
//
ASSERT (queue->Busy); RemoveEntryList (&irp->Tail.Overlay.ListEntry); irp->Tail.Overlay.IfslRequestQueue = NULL; } else { irp = NULL; }
if (IsListEmpty (&queue->ListHead)) { //
// Queue is now empty, change its state, so that new request knows to
// signal to user mode DLL
//
queue->Busy = FALSE; } else { //
// There is another request pending, signal it now
//
SignalRequest (ProcessCtx); ASSERT (queue->Busy); } //
// Hint the caller that we just signalled, so it does not have to wait on event
//
*more = queue->Busy;
KeReleaseSpinLock (&queue->Lock, oldIRQL); return irp; }
VOID SignalRequest ( IN PIFSL_PROCESS_CTX ProcessCtx ) /*++
Routine Description:
Fills request parameters & signals user mode DLL to process the request
Arguments: ProcessCtx - our context for the process which IRP belongs to
Return Value: None Note: SHOULD ONLY BE CALLED WITH QUEUE SPINLOCK HELD --*/ { PIRP irp; PIO_STACK_LOCATION irpSp; ULONG bufferLen;
ASSERT (!IsListEmpty (&ProcessCtx->RequestQueue.ListHead));
irp = CONTAINING_RECORD ( ProcessCtx->RequestQueue.ListHead.Flink, IRP, Tail.Overlay.ListEntry ); irpSp = IoGetCurrentIrpStackLocation (irp);
switch (irpSp->MajorFunction) { case IRP_MJ_READ: bufferLen = irpSp->Parameters.Read.Length;; break; case IRP_MJ_WRITE: bufferLen = irpSp->Parameters.Write.Length; break; case IRP_MJ_DEVICE_CONTROL: switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_AFD_RECEIVE_DATAGRAM: bufferLen = ADDR_ALIGN(irpSp->Parameters.DeviceIoControl.OutputBufferLength) + irpSp->Parameters.DeviceIoControl.InputBufferLength; break; case IOCTL_AFD_RECEIVE: bufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength; break; case IOCTL_AFD_SEND_DATAGRAM: bufferLen = ADDR_ALIGN(irpSp->Parameters.DeviceIoControl.OutputBufferLength) + irpSp->Parameters.DeviceIoControl.InputBufferLength; break; } break; case IRP_MJ_PNP: bufferLen = sizeof (HANDLE); break; default: ASSERT (FALSE); break; }
if (KeInsertQueueApc (&ProcessCtx->RequestQueue.Apc, irp->Tail.Overlay.IfslRequestId, UlongToPtr(bufferLen), IO_NETWORK_INCREMENT)) { WsProcessPrint (ProcessCtx, DBG_QUEUE, ("WS2IFSL-%04lx SignalRequest: Irp %p (id %ld) on socket %p.\n", ProcessCtx->UniqueId, irp, irp->Tail.Overlay.IfslRequestId, irpSp->FileObject)); ProcessCtx->RequestQueue.Busy = TRUE; } else { WsProcessPrint (ProcessCtx, DBG_QUEUE|DBG_FAILURES, ("WS2IFSL-%04lx KeInsertQueueApc failed: Irp %p (id %ld) on socket %p.\n", ProcessCtx->UniqueId, irp, irp->Tail.Overlay.IfslRequestId, irpSp->FileObject)); //
// APC queing failed, cancel all outstanding requests.
//
FlushRequestQueue (&ProcessCtx->RequestQueue); }
} // SignalRequest
VOID QueueKernelRoutine ( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine, IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 ) { NOTHING; }
VOID RequestRundownRoutine ( IN struct _KAPC *Apc ) /*++
Routine Description:
APC rundown routine for request queue APC Flushes the queue and marks it as not busy so new request fail immediately as well. Arguments: APC - cancel queue APC structure Return Value: None --*/ { PIFSL_QUEUE Queue; KIRQL oldIrql;
Queue = CONTAINING_RECORD (Apc, IFSL_QUEUE, Apc); KeAcquireSpinLock (&Queue->Lock, &oldIrql); Queue->Busy = FALSE; FlushRequestQueue (Queue); KeReleaseSpinLock (&Queue->Lock, oldIrql); }
VOID FlushRequestQueue ( PIFSL_QUEUE Queue ) /*++
Routine Description:
Flushes and completes IRPs in the request queue Arguments: Queue - request queue to flush Return Value: None Note: SHOULD ONLY BE CALLED WITH QUEUE SPINLOCK HELD --*/ { while (!IsListEmpty (&Queue->ListHead)) { PIRP irp = CONTAINING_RECORD (Queue->ListHead.Flink, IRP, Tail.Overlay.ListEntry); RemoveEntryList (&irp->Tail.Overlay.ListEntry); irp->Tail.Overlay.IfslRequestQueue = NULL; KeReleaseSpinLockFromDpcLevel (&Queue->Lock); irp->IoStatus.Information = 0; irp->IoStatus.Status = STATUS_CANCELLED; CompleteSocketIrp (irp); KeAcquireSpinLockAtDpcLevel (&Queue->Lock); } }
VOID CleanupQueuedRequests ( IN PIFSL_PROCESS_CTX ProcessCtx, IN PFILE_OBJECT SocketFile, OUT PLIST_ENTRY IrpList ) /*++
Routine Description:
Cleans up all IRPs associated with a socket file object from the request queue
Arguments: ProcessCtx - process context to which queue belongs SocketFile - socket file object for which to remove requests IrpList - list head to hold the IRPs removed from the queue
Return Value: None --*/ { KIRQL oldIRQL; PLIST_ENTRY entry; PIFSL_QUEUE queue = &ProcessCtx->RequestQueue;
KeAcquireSpinLock (&queue->Lock, &oldIRQL); entry = queue->ListHead.Flink; while (entry!=&queue->ListHead) { PIRP irp = CONTAINING_RECORD (entry, IRP, Tail.Overlay.ListEntry); PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp);
entry = entry->Flink; if (irpSp->FileObject==SocketFile) { RemoveEntryList (&irp->Tail.Overlay.ListEntry); irp->Tail.Overlay.IfslRequestQueue = NULL; InsertTailList (IrpList, &irp->Tail.Overlay.ListEntry); } } KeReleaseSpinLock (&queue->Lock, oldIRQL); }
VOID QueuedCancelRoutine ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
Driver cancel routine for socket request waiting in the queue to be reported to user mode DLL.
Arguments: DeviceObject - WS2IFSL device object Irp - Irp to be cancelled
Return Value: None --*/ { PIO_STACK_LOCATION irpSp; PIFSL_SOCKET_CTX SocketCtx; PIFSL_PROCESS_CTX ProcessCtx;
irpSp = IoGetCurrentIrpStackLocation (Irp);
SocketCtx = irpSp->FileObject->FsContext; ProcessCtx = SocketCtx->ProcessRef->FsContext;
WsProcessPrint (ProcessCtx, DBG_QUEUE, ("WS2IFSL-%04lx CancelQueuedRequest: Socket %p , Irp %p\n", ProcessCtx->UniqueId, irpSp->FileObject, Irp)); KeAcquireSpinLockAtDpcLevel (&ProcessCtx->RequestQueue.Lock); if (Irp->Tail.Overlay.IfslRequestQueue!=NULL) { ASSERT (Irp->Tail.Overlay.IfslRequestQueue==&ProcessCtx->RequestQueue); //
// Request was in the queue, remove and cancel it here
//
RemoveEntryList (&Irp->Tail.Overlay.ListEntry); Irp->Tail.Overlay.IfslRequestQueue = NULL; KeReleaseSpinLockFromDpcLevel (&ProcessCtx->RequestQueue.Lock); IoReleaseCancelSpinLock (Irp->CancelIrql);
Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; CompleteSocketIrp (Irp); } else { //
// Request was not in the queue, whoever removed should note the
// cancel flag and properly deal with it
//
KeReleaseSpinLockFromDpcLevel (&ProcessCtx->RequestQueue.Lock); IoReleaseCancelSpinLock (Irp->CancelIrql); //
// Don't touch IRP after this as we do not own it anymore
//
} }
VOID InitializeCancelQueue ( IN PIFSL_PROCESS_CTX ProcessCtx, IN PKTHREAD ApcThread, IN KPROCESSOR_MODE ApcMode, IN PKNORMAL_ROUTINE ApcRoutine, IN PVOID ApcContext ) /*++
Routine Description:
Initializes cancel queue object
Arguments: ProcessCtx - process context to which queue belongs ApcThread - thread to which to queue APC requests for processing ApcMode - mode of the caller (should be user) ApcRoutine - routine that processes requests ApcContext - context to pass to the routine in addition to request parameters
Return Value: None --*/ { PAGED_CODE ();
InitializeListHead (&ProcessCtx->CancelQueue.ListHead); ProcessCtx->CancelQueue.Busy = FALSE; KeInitializeSpinLock (&ProcessCtx->CancelQueue.Lock); KeInitializeApc (&ProcessCtx->CancelQueue.Apc, ApcThread, OriginalApcEnvironment, QueueKernelRoutine, CancelRundownRoutine, ApcRoutine, ApcMode, ApcContext); }
VOID QueueCancel ( IN PIFSL_PROCESS_CTX ProcessCtx, IN PIFSL_CANCEL_CTX CancelCtx ) /*++
Routine Description:
Queues cancel request to IFSL cancel queue and signals to user mode DLL to start processing if it is not busy already.
Arguments: ProcessCtx - process context in which to queue CancelCtx - request to be queued
Return Value: None
--*/ { KIRQL oldIRQL; PIFSL_QUEUE queue = &ProcessCtx->CancelQueue; KeAcquireSpinLock (&queue->Lock, &oldIRQL); InsertTailList (&queue->ListHead, &CancelCtx->ListEntry); ASSERT (CancelCtx->ListEntry.Flink != NULL); if (!queue->Busy) { ASSERT (queue->ListHead.Flink==&CancelCtx->ListEntry); SignalCancel (ProcessCtx); ASSERT (ProcessCtx->CancelQueue.Busy); } KeReleaseSpinLock (&queue->Lock, oldIRQL);
} // QueueCancel
PIFSL_CANCEL_CTX DequeueCancel ( PIFSL_PROCESS_CTX ProcessCtx, ULONG UniqueId, BOOLEAN *more ) /*++
Routine Description:
Removes cancel request from IFSL cancel queue.
Arguments: ProcessCtx - process context from which to remove UniqueId - unique cancel request id more - set to TRUE if there are more requests in the queue
Return Value: CTX - pointer to cancel request context NULL - the request was not in the queue
--*/ { KIRQL oldIRQL; PIFSL_CANCEL_CTX cancelCtx; PIFSL_QUEUE queue = &ProcessCtx->CancelQueue;
KeAcquireSpinLock (&queue->Lock, &oldIRQL); cancelCtx = CONTAINING_RECORD ( queue->ListHead.Flink, IFSL_CANCEL_CTX, ListEntry ); if (!IsListEmpty (&queue->ListHead) && (cancelCtx->UniqueId==UniqueId)) { //
// Queue is not empty and first request matches passed in parameters,
// dequeue and return it
//
ASSERT (queue->Busy); RemoveEntryList (&cancelCtx->ListEntry); cancelCtx->ListEntry.Flink = NULL; } else cancelCtx = NULL;
if (IsListEmpty (&queue->ListHead)) { //
// Queue is now empty, change its state, so that new request knows to
// signal to user mode DLL
//
queue->Busy = FALSE; } else { //
// There is another request pending, signal it now
//
SignalCancel (ProcessCtx); ASSERT (queue->Busy); } //
// Hint the caller that we just signalled, so it does not have to wait on event
//
*more = queue->Busy;
KeReleaseSpinLock (&queue->Lock, oldIRQL); return cancelCtx; }
VOID SignalCancel ( IN PIFSL_PROCESS_CTX ProcessCtx ) /*++
Routine Description:
Fills request parameters & signals user mode DLL to process the request
Arguments: ProcessCtx - our context for the process which cancel request belongs to
Return Value: None
Note: SHOULD ONLY BE CALLED WITH QUEUE SPINLOCK HELD
--*/ { PIFSL_CANCEL_CTX cancelCtx; PIFSL_SOCKET_CTX SocketCtx;
ASSERT (!IsListEmpty (&ProcessCtx->CancelQueue.ListHead));
ProcessCtx->CancelQueue.Busy = TRUE;
cancelCtx = CONTAINING_RECORD ( ProcessCtx->CancelQueue.ListHead.Flink, IFSL_CANCEL_CTX, ListEntry ); SocketCtx = cancelCtx->SocketFile->FsContext;
if (KeInsertQueueApc (&ProcessCtx->CancelQueue.Apc, UlongToPtr(cancelCtx->UniqueId), SocketCtx->DllContext, IO_NETWORK_INCREMENT)) { WsProcessPrint (ProcessCtx, DBG_QUEUE, ("WS2IFSL-%04lx SignalCancel: Context %p on socket %p (h %p).\n", ProcessCtx->UniqueId, cancelCtx, cancelCtx->SocketFile, SocketCtx->DllContext)); } else { //
// APC queing failed, cancel all outstanding requests.
//
FlushCancelQueue (&ProcessCtx->CancelQueue); }
} // SignalCancel
VOID FlushCancelQueue ( PIFSL_QUEUE Queue ) /*++
Routine Description:
Flushes and frees entries in the cancel queue Arguments: Queue - request queue to flush Return Value: None Note: SHOULD ONLY BE CALLED WITH QUEUE SPINLOCK HELD --*/ { while (!IsListEmpty (&Queue->ListHead)) { PIFSL_CANCEL_CTX cancelCtx = CONTAINING_RECORD ( Queue->ListHead.Flink, IFSL_CANCEL_CTX, ListEntry ); RemoveEntryList (&cancelCtx->ListEntry); cancelCtx->ListEntry.Flink = NULL; FreeSocketCancel (cancelCtx); } }
VOID CancelRundownRoutine ( IN struct _KAPC *Apc ) /*++
Routine Description:
APC rundown routine for cancel queue APC Flushes the queue and marks it as not busy so new request fail immediately as well. Arguments: APC - cancel queue APC structure Return Value: None --*/ { PIFSL_QUEUE Queue; KIRQL oldIrql;
Queue = CONTAINING_RECORD (Apc, IFSL_QUEUE, Apc); KeAcquireSpinLock (&Queue->Lock, &oldIrql); Queue->Busy = FALSE; FlushCancelQueue (Queue); KeReleaseSpinLock (&Queue->Lock, oldIrql); }
BOOLEAN RemoveQueuedCancel ( PIFSL_PROCESS_CTX ProcessCtx, PIFSL_CANCEL_CTX CancelCtx ) /*++
Routine Description:
Remove cancel request from the cancel queue if it is there
Arguments: ProcessCtx - process context to which queue belongs CancelCtx - request to remove
Return Value: None --*/ { KIRQL oldIRQL; BOOLEAN res;
// Acquire queue lock
KeAcquireSpinLock (&ProcessCtx->CancelQueue.Lock, &oldIRQL); res = (CancelCtx->ListEntry.Flink!=NULL); if (res) { RemoveEntryList (&CancelCtx->ListEntry); CancelCtx->ListEntry.Flink = NULL; } KeReleaseSpinLock (&ProcessCtx->CancelQueue.Lock, oldIRQL); return res; }
|