You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
17 KiB
730 lines
17 KiB
#include <pch.h>
|
|
#include "irplist.h"
|
|
|
|
void
|
|
IrpList_InitEx(
|
|
PIRP_LIST IrpList,
|
|
PKSPIN_LOCK ListLock,
|
|
PDRIVER_CANCEL CancelRoutine,
|
|
PIRP_COMPLETION_ROUTINE IrpCompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the IrpList
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
ListLock - Pointer to the spinlock for the IrpList
|
|
|
|
CancelRoutine - Routine to be called when an Irp on the IrpList
|
|
is cancelled
|
|
|
|
IrpCompletionRoutine - Optional Completion routine for an Irp on the IrpList
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
LockedList_Init(&IrpList->LockedList, ListLock);
|
|
|
|
ASSERT(CancelRoutine != NULL);
|
|
IrpList->CancelRoutine = CancelRoutine;
|
|
IrpList->IrpCompletionRoutine = IrpCompletionRoutine;
|
|
}
|
|
|
|
NTSTATUS
|
|
IrpList_EnqueueLocked(
|
|
PIRP_LIST IrpList,
|
|
PIRP Irp,
|
|
BOOLEAN StoreListInIrp,
|
|
BOOLEAN InsertTail
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enqueues an Irp on the IrpList.
|
|
Assumes the caller has acquired the IrpList Spinlock.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
Irp - Pointer to the Irp to Enqueue
|
|
|
|
StoreListInIrp - Set to TRUE is the Irp will be used to store the
|
|
list entry
|
|
|
|
InsertTail - Set to TRUE if Irp is to be enqueued at
|
|
the tail of the IrpList.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS or appropriate error code
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// must set a cancel routine before checking the Cancel flag
|
|
//
|
|
oldCancelRoutine = IoSetCancelRoutine(Irp, IrpList->CancelRoutine);
|
|
ASSERT(oldCancelRoutine == NULL);
|
|
|
|
if (Irp->Cancel) {
|
|
//
|
|
// This IRP has already been cancelled, so complete it now.
|
|
// We must clear the cancel routine before completing the IRP.
|
|
// We must release the spinlock before calling out of the driver.
|
|
//
|
|
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
|
|
if (oldCancelRoutine != NULL) {
|
|
//
|
|
// The cancel routine was NOT called
|
|
//
|
|
ASSERT(oldCancelRoutine == IrpList->CancelRoutine);
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else {
|
|
//
|
|
// The cancel routine was called. As soon as we drop the spinlock,
|
|
// it will dequeue and complete the IRP. Increase the count because
|
|
// the cancel routine will decrement it.
|
|
//
|
|
IrpList->LockedList.Count++;
|
|
|
|
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(Irp);
|
|
status = Irp->IoStatus.Status = STATUS_PENDING;
|
|
|
|
//
|
|
// save a ptr to this structure in the Irp for the cancel routine.
|
|
//
|
|
if (StoreListInIrp) {
|
|
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (InsertTail) {
|
|
LL_ADD_TAIL(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
|
|
}
|
|
else {
|
|
LL_ADD_HEAD(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
|
|
}
|
|
IoMarkIrpPending(Irp);
|
|
status = Irp->IoStatus.Status = STATUS_PENDING;
|
|
|
|
//
|
|
// save a ptr to this structure in the Irp for the cancel routine.
|
|
//
|
|
if (StoreListInIrp) {
|
|
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IrpList_EnqueueEx(
|
|
PIRP_LIST IrpList,
|
|
PIRP Irp,
|
|
BOOLEAN StoreListInIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enqueues an Irp on the IrpList.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
Irp - Pointer to the Irp to Enqueue
|
|
|
|
StoreListInIrp - Set to TRUE is the Irp will be used to store the
|
|
list entry
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_SUCCESS or appropriate error code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL irql;
|
|
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
status = IrpList_EnqueueLocked(IrpList, Irp, StoreListInIrp, TRUE);
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IrpList_MakeNonCancellable(
|
|
PIRP_LIST IrpList,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the Irp Cancel routine to NULL, making it non-cancellable.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
Irp - Pointer to the Irp to Enqueue
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if we succeeded in making the Irp non-cancellable.
|
|
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_CANCEL oldCancelRoutine;
|
|
BOOLEAN result;
|
|
|
|
result = FALSE;
|
|
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
|
|
|
|
//
|
|
// IoCancelIrp() could have just been called on this IRP.
|
|
// What we're interested in is not whether IoCancelIrp() was called
|
|
// (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
|
|
// is about to call) our cancel routine. To check that, check the result
|
|
// of the test-and-set macro IoSetCancelRoutine.
|
|
//
|
|
if (oldCancelRoutine != NULL) {
|
|
//
|
|
// Cancel routine not called for this IRP. Return this IRP.
|
|
//
|
|
ASSERT (oldCancelRoutine == IrpList->CancelRoutine);
|
|
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = NULL;
|
|
result = TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// This IRP was just cancelled and the cancel routine was (or will
|
|
// be) called. The cancel routine will complete this IRP as soon as
|
|
// we drop the spinlock. So don't do anything with the IRP.
|
|
//
|
|
// Also, the cancel routine will try to dequeue the IRP, so make the
|
|
// IRP's listEntry point to itself.
|
|
//
|
|
ASSERT(Irp->Cancel);
|
|
|
|
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
|
|
result = FALSE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
PIRP
|
|
IrpList_DequeueLocked(
|
|
PIRP_LIST IrpList
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dequeue an IRP from the head of the IrpList.
|
|
Assumes the caller has acquired the IrpList SpinLock.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
|
|
Return Value:
|
|
|
|
PIRP - Pointer to the Irp dequeued form the IrpList.
|
|
NULL if no IRP is avaliable.
|
|
|
|
--*/
|
|
PIRP nextIrp = NULL;
|
|
PLIST_ENTRY ple;
|
|
|
|
nextIrp = NULL;
|
|
while (nextIrp == NULL && !IsListEmpty(&IrpList->LockedList.ListHead)){
|
|
ple = LL_REMOVE_HEAD(&IrpList->LockedList);
|
|
|
|
//
|
|
// Get the next IRP off the queue and clear the cancel routine
|
|
//
|
|
nextIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
|
|
if (IrpList_MakeNonCancellable(IrpList, nextIrp) == FALSE) {
|
|
nextIrp = NULL;
|
|
}
|
|
}
|
|
|
|
return nextIrp;
|
|
}
|
|
|
|
PIRP
|
|
IrpList_Dequeue(
|
|
PIRP_LIST IrpList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dequeue an IRP from the head of the IrpList.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
|
|
Return Value:
|
|
|
|
PIRP - Pointer to the Irp dequeued form the IrpList.
|
|
NULL if no IRP is avaliable.
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
KIRQL irql;
|
|
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
irp = IrpList_DequeueLocked(IrpList);
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
return irp;
|
|
}
|
|
|
|
BOOLEAN
|
|
IrpList_DequeueIrp(
|
|
PIRP_LIST IrpList,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dequeue a specific IRP from the IrpList.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
Irp - Pointer to an IRP contained in the IrpList.
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
BOOLEAN result;
|
|
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
result = IrpList_DequeueIrpLocked(IrpList, Irp);
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOLEAN
|
|
IrpList_DequeueIrpLocked(
|
|
PIRP_LIST IrpList,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dequeue a specific IRP from the IrpList
|
|
Assumes the caller has acquired the IrpList spinlock
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
Irp - Pointer to an IRP contained in the IrpList.
|
|
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ple;
|
|
PIRP pIrp;
|
|
BOOLEAN result;
|
|
|
|
result = FALSE;
|
|
|
|
for (ple = IrpList->LockedList.ListHead.Flink;
|
|
ple != &IrpList->LockedList.ListHead;
|
|
ple = ple->Flink) {
|
|
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
|
|
|
|
if (pIrp == Irp) {
|
|
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
|
IrpList->LockedList.Count--;
|
|
|
|
result = IrpList_MakeNonCancellable(IrpList, pIrp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ULONG
|
|
IrpList_ProcessAndDrain(
|
|
PIRP_LIST IrpList,
|
|
PFNPROCESSIRP FnProcessIrp,
|
|
PVOID Context,
|
|
PLIST_ENTRY DrainHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all cancellable Irps from the IrpList and process.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
FnProcessIrp - Function to process the Irp
|
|
|
|
Context - Context to pass into the FnProcessIrp
|
|
|
|
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
|
|
|
|
|
|
Return Value:
|
|
|
|
ULONG - Number of IRPs processed
|
|
|
|
--*/
|
|
{
|
|
ULONG count;
|
|
KIRQL irql;
|
|
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
count = IrpList_ProcessAndDrainLocked(
|
|
IrpList, FnProcessIrp, Context, DrainHead);
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
return count;
|
|
}
|
|
|
|
ULONG
|
|
IrpList_ProcessAndDrainLocked(
|
|
PIRP_LIST IrpList,
|
|
PFNPROCESSIRP FnProcessIrp,
|
|
PVOID Context,
|
|
PLIST_ENTRY DrainHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all cancellable Irps from the IrpList and process
|
|
Assumes that the caller has acquired the IrpList Spinlock.
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
FnProcessIrp - Function to process the Irp
|
|
|
|
Context - Context to pass into the FnProcessIrp
|
|
|
|
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
|
|
|
|
|
|
Return Value:
|
|
|
|
ULONG - Number of IRPs processed
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ple;
|
|
PIRP pIrp;
|
|
NTSTATUS status;
|
|
ULONG count;
|
|
|
|
count = 0;
|
|
ASSERT(FnProcessIrp != NULL);
|
|
|
|
for (ple = IrpList->LockedList.ListHead.Flink;
|
|
ple != &IrpList->LockedList.ListHead;
|
|
/* ple = ple->Flink */) {
|
|
|
|
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Advance immediately so we don't lose the next link in the list in
|
|
// case we remove the current irp.
|
|
//
|
|
ple = ple->Flink;
|
|
|
|
if (FnProcessIrp(Context, pIrp)) {
|
|
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
|
IrpList->LockedList.Count--;
|
|
|
|
if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
|
|
InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
ULONG
|
|
IrpList_DrainLocked(
|
|
PIRP_LIST IrpList,
|
|
PLIST_ENTRY DrainHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all cancellable Irps from the IrpList and queue to the DrainHead
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
|
|
|
|
Return Value:
|
|
|
|
ULONG - Number of IRPs processed
|
|
|
|
--*/
|
|
{
|
|
PIRP irp;
|
|
ULONG count;
|
|
|
|
count = 0;
|
|
|
|
while (TRUE) {
|
|
irp = IrpList_DequeueLocked(IrpList);
|
|
if (irp != NULL) {
|
|
InsertTailList(DrainHead, &irp->Tail.Overlay.ListEntry);
|
|
count++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(LL_GET_COUNT(&IrpList->LockedList) == 0);
|
|
|
|
return count;
|
|
}
|
|
|
|
ULONG
|
|
IrpList_DrainLockedByFileObject(
|
|
PIRP_LIST IrpList,
|
|
PLIST_ENTRY DrainHead,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all cancellable Irps for specified FileObject
|
|
from the IrpList and queue to the DrainHead
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
|
|
|
|
FileObject - Pointer to specified FILE_OBJECT
|
|
|
|
Return Value:
|
|
|
|
ULONG - Number of IRPs processed
|
|
|
|
--*/
|
|
{
|
|
PIRP pIrp;
|
|
PDRIVER_CANCEL pOldCancelRoutine;
|
|
PIO_STACK_LOCATION pStack;
|
|
ULONG count;
|
|
PLIST_ENTRY ple;
|
|
|
|
count = 0;
|
|
ASSERT(FileObject != NULL);
|
|
|
|
for (ple = IrpList->LockedList.ListHead.Flink;
|
|
ple != &IrpList->LockedList.ListHead;
|
|
/* ple = ple->Flink */) {
|
|
|
|
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Advance immediately so we don't lose the next link in the list in
|
|
// case we remove the current irp.
|
|
//
|
|
ple = ple->Flink;
|
|
|
|
pStack = IoGetCurrentIrpStackLocation(pIrp);
|
|
if (pStack->FileObject == FileObject) {
|
|
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
|
|
IrpList->LockedList.Count--;
|
|
|
|
if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
|
|
InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
ULONG
|
|
IrpList_Drain(
|
|
PIRP_LIST IrpList,
|
|
PLIST_ENTRY DrainHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all cancellable IRPs and queue to DrainHead
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
|
|
|
|
Return Value:
|
|
|
|
ULONG - Number of IRPs processed
|
|
|
|
--*/
|
|
{
|
|
ULONG count;
|
|
KIRQL irql;
|
|
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
count = IrpList_DrainLocked(IrpList, DrainHead);
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
return count;
|
|
}
|
|
|
|
void
|
|
IrpList_HandleCancel(
|
|
PIRP_LIST IrpList,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancel Routine for IRPs on the IrpList
|
|
|
|
Arguments:
|
|
|
|
IrpList - Pointer to the IrpList structure
|
|
|
|
DeviceObject - Device Object, used for IrpCompletionRoutine
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
|
|
//
|
|
// Release the global cancel spinlock.
|
|
// Do this while not holding any other spinlocks so that we exit at the
|
|
// right IRQL.
|
|
//
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
//
|
|
// Dequeue and complete the IRP. The enqueue and dequeue functions
|
|
// synchronize properly so that if this cancel routine is called,
|
|
// the dequeue is safe and only the cancel routine will complete the IRP.
|
|
//
|
|
LL_LOCK(&IrpList->LockedList, &irql);
|
|
if (!IsListEmpty(&Irp->Tail.Overlay.ListEntry)) {
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
IrpList->LockedList.Count--;
|
|
}
|
|
LL_UNLOCK(&IrpList->LockedList, irql);
|
|
|
|
if (IrpList->IrpCompletionRoutine != NULL) {
|
|
(void) IrpList->IrpCompletionRoutine(
|
|
DeviceObject, Irp, STATUS_CANCELLED);
|
|
}
|
|
else {
|
|
//
|
|
// Complete the IRP. This is a call outside the driver, so all
|
|
// spinlocks must be released by this point.
|
|
//
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
IrpList_CancelRoutine(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
{
|
|
|
|
PSCUTIL_EXTENSION pExt;
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
pExt = *((PSCUTIL_EXTENSION*) DeviceObject->DeviceExtension);
|
|
|
|
irpSp = IoGetNextIrpStackLocation(Irp);
|
|
if (irpSp->CompletionRoutine) {
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
irpSp->CompletionRoutine(DeviceObject,
|
|
Irp,
|
|
irpSp->Context);
|
|
}
|
|
|
|
IoReleaseRemoveLock(pExt->RemoveLock,
|
|
Irp);
|
|
|
|
DecIoCount(pExt);
|
|
|
|
IrpList_HandleCancel(IRP_LIST_FROM_IRP(Irp),
|
|
DeviceObject,
|
|
Irp);
|
|
|
|
}
|