windows-nt/Source/XPSP1/NT/net/sockets/winsock2/ws2help/ws2ifsl/queue.c
2020-09-26 16:20:57 +08:00

803 lines
19 KiB
C

/*++
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",
PsGetCurrentProcessId(),
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",
PsGetCurrentProcessId(),
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",
PsGetCurrentProcessId(),
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",
PsGetCurrentProcessId(),
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;
}