- Implement NtCancelIoFile.

- Queue the IRP to correct thread in IoQueueThreadIrp.
- Cancel I/O requests when thread is about to be terminated.
- Do not queue close requests to thread IRP list.

svn path=/trunk/; revision=13824
This commit is contained in:
Filip Navara 2005-03-05 11:27:15 +00:00
parent 3d5ac6b2be
commit 594c2c424b
5 changed files with 213 additions and 22 deletions

View file

@ -426,6 +426,11 @@ IopBootLog(PUNICODE_STRING DriverName, BOOLEAN Success);
VOID
IopSaveBootLogToFile(VOID);
/* cancel.c */
VOID STDCALL
IoCancelThreadIo(PETHREAD Thread);
/* errlog.c */
NTSTATUS

View file

@ -20,12 +20,181 @@ static KSPIN_LOCK CancelSpinLock;
/* FUNCTIONS *****************************************************************/
/**
* @name NtCancelIoFile
*
* Cancel all pending I/O operations in the current thread for specified
* file object.
*
* @param FileHandle
* Handle to file object to cancel requests for. No specific
* access rights are needed.
* @param IoStatusBlock
* Pointer to status block which is filled with final completition
* status on successful return.
*
* @return Status.
*
* @implemented
*/
NTSTATUS STDCALL
NtCancelIoFile (IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock)
NtCancelIoFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock)
{
UNIMPLEMENTED;
return(STATUS_NOT_IMPLEMENTED);
NTSTATUS Status;
PFILE_OBJECT FileObject;
PETHREAD Thread;
PLIST_ENTRY IrpEntry;
PIRP Irp;
KIRQL OldIrql;
BOOLEAN OurIrpsInList = FALSE;
LARGE_INTEGER Interval;
if ((ULONG_PTR)IoStatusBlock >= MmUserProbeAddress &&
KeGetPreviousMode() == UserMode)
return STATUS_ACCESS_VIOLATION;
Status = ObReferenceObjectByHandle(FileHandle, 0, IoFileObjectType,
KeGetPreviousMode(), (PVOID*)&FileObject,
NULL);
if (!NT_SUCCESS(Status))
return Status;
/* IRP cancellations are synchronized at APC_LEVEL. */
OldIrql = KfRaiseIrql(APC_LEVEL);
/*
* Walk the list of active IRPs and cancel the ones that belong to
* our file object.
*/
Thread = PsGetCurrentThread();
for (IrpEntry = Thread->IrpList.Flink;
IrpEntry != &Thread->IrpList;
IrpEntry = IrpEntry->Flink)
{
Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
if (Irp->Tail.Overlay.OriginalFileObject == FileObject)
{
IoCancelIrp(Irp);
/* Don't break here, we want to cancel all IRPs for the file object. */
OurIrpsInList = TRUE;
}
}
KfLowerIrql(OldIrql);
while (OurIrpsInList)
{
OurIrpsInList = FALSE;
/* Wait a short while and then look if all our IRPs were completed. */
Interval.QuadPart = -1000000; /* 100 milliseconds */
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
OldIrql = KfRaiseIrql(APC_LEVEL);
/*
* Look in the list if all IRPs for the specified file object
* are completed (or cancelled). If someone sends a new IRP
* for our file object while we're here we can happily loop
* forever.
*/
for (IrpEntry = Thread->IrpList.Flink;
IrpEntry != &Thread->IrpList;
IrpEntry = IrpEntry->Flink)
{
Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
if (Irp->Tail.Overlay.OriginalFileObject == FileObject)
{
OurIrpsInList = TRUE;
break;
}
}
KfLowerIrql(OldIrql);
}
_SEH_TRY
{
IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = 0;
Status = STATUS_SUCCESS;
}
_SEH_HANDLE
{
Status = STATUS_UNSUCCESSFUL;
}
_SEH_END;
ObDereferenceObject(FileObject);
return Status;
}
/**
* @name IoCancelThreadIo
*
* Cancel all pending I/O request associated with specified thread.
*
* @param Thread
* Thread to cancel requests for.
*/
VOID STDCALL
IoCancelThreadIo(PETHREAD Thread)
{
PLIST_ENTRY IrpEntry;
PIRP Irp;
KIRQL OldIrql;
ULONG Retries = 3000;
LARGE_INTEGER Interval;
OldIrql = KfRaiseIrql(APC_LEVEL);
/*
* Start by cancelling all the IRPs in the current thread queue.
*/
for (IrpEntry = Thread->IrpList.Flink;
IrpEntry != &Thread->IrpList;
IrpEntry = IrpEntry->Flink)
{
Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
IoCancelIrp(Irp);
}
/*
* Wait till all the IRPs are completed or cancelled.
*/
while (!IsListEmpty(&Thread->IrpList))
{
KfLowerIrql(OldIrql);
/* Wait a short while and then look if all our IRPs were completed. */
Interval.QuadPart = -1000000; /* 100 milliseconds */
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
/*
* Don't stay here forever if some broken driver doesn't complete
* the IRP.
*/
if (Retries-- == 0)
{
/* FIXME: Handle this gracefully. */
DPRINT1("Thread with dead IRPs!");
ASSERT(FALSE);
}
OldIrql = KfRaiseIrql(APC_LEVEL);
}
KfLowerIrql(OldIrql);
}
/*

View file

@ -105,15 +105,24 @@ IopDeleteFile(PVOID ObjectBody)
UserMode);
#endif
KeResetEvent( &FileObject->Event );
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_CLOSE,
FileObject->DeviceObject,
NULL,
0,
NULL,
&FileObject->Event,
NULL);
Irp = IoAllocateIrp(FileObject->DeviceObject->StackSize, TRUE);
if (Irp == NULL)
{
/*
* FIXME: This case should eventually be handled. We should wait
* until enough memory is available to allocate the IRP.
*/
ASSERT(FALSE);
}
Irp->UserEvent = &FileObject->Event;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Flags |= IRP_CLOSE_OPERATION;
StackPtr = IoGetNextIrpStackLocation(Irp);
StackPtr->MajorFunction = IRP_MJ_CLOSE;
StackPtr->DeviceObject = FileObject->DeviceObject;
StackPtr->FileObject = FileObject;
Status = IoCallDriver(FileObject->DeviceObject, Irp);

View file

@ -268,7 +268,6 @@ IoAllocateIrp(CCHAR StackSize,
return(NULL);
}
RtlZeroMemory(Irp, IoSizeOfIrp(StackSize));
IoInitializeIrp(Irp,
IoSizeOfIrp(StackSize),
StackSize);
@ -364,6 +363,9 @@ IofCompleteRequest(PIRP Irp,
ULONG MasterIrpCount;
PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
/* This should never happen! */
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
while ((Mdl = Irp->MdlAddress))
{
@ -386,6 +388,9 @@ IofCompleteRequest(PIRP Irp,
/* Windows NT File System Internals, page 165 */
if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
{
/* This should never happen! */
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
/*
* If MDL_IO_PAGE_READ is set, then the caller is responsible
* for deallocating of the mdl.
@ -582,19 +587,19 @@ IoGetTopLevelIrp(VOID)
VOID STDCALL
IoQueueThreadIrp(IN PIRP Irp)
{
/* undefine this when (if ever) implementing irp cancellation */
#if 0
KIRQL oldIrql;
KIRQL OldIrql;
oldIrql = KfRaiseIrql(APC_LEVEL);
OldIrql = KfRaiseIrql(APC_LEVEL);
/* Synchronous irp's are queued to requestor thread. If they are not completed
when the thread exits, they are canceled (cleaned up).
-Gunnar */
InsertTailList(&PsGetCurrentThread()->IrpList, &Irp->ThreadListEntry);
/*
* Synchronous irp's are queued to requestor thread. If they are not
* completed when the thread exits, they are canceled (cleaned up).
* - Gunnar
*/
InsertTailList(&Irp->Tail.Overlay.Thread->IrpList, &Irp->ThreadListEntry);
KfLowerIrql(oldIrql);
#endif
KfLowerIrql(OldIrql);
}

View file

@ -158,6 +158,9 @@ PsTerminateCurrentThread(NTSTATUS ExitStatus)
PsLockProcess(CurrentProcess, FALSE);
/* Cancel I/O for the thread. */
IoCancelThreadIo(CurrentThread);
/* Remove the thread from the thread list of its process */
RemoveEntryList(&CurrentThread->ThreadListEntry);
Last = IsListEmpty(&CurrentProcess->ThreadListHead);