From 594c2c424b9db583c8e2ba6bd63975fa543a2f55 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 5 Mar 2005 11:27:15 +0000 Subject: [PATCH] - 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 --- reactos/ntoskrnl/include/internal/io.h | 5 + reactos/ntoskrnl/io/cancel.c | 177 ++++++++++++++++++++++++- reactos/ntoskrnl/io/iomgr.c | 23 +++- reactos/ntoskrnl/io/irp.c | 27 ++-- reactos/ntoskrnl/ps/kill.c | 3 + 5 files changed, 213 insertions(+), 22 deletions(-) diff --git a/reactos/ntoskrnl/include/internal/io.h b/reactos/ntoskrnl/include/internal/io.h index 823c153c197..b23f6121742 100644 --- a/reactos/ntoskrnl/include/internal/io.h +++ b/reactos/ntoskrnl/include/internal/io.h @@ -426,6 +426,11 @@ IopBootLog(PUNICODE_STRING DriverName, BOOLEAN Success); VOID IopSaveBootLogToFile(VOID); +/* cancel.c */ + +VOID STDCALL +IoCancelThreadIo(PETHREAD Thread); + /* errlog.c */ NTSTATUS diff --git a/reactos/ntoskrnl/io/cancel.c b/reactos/ntoskrnl/io/cancel.c index f5c4312eeb9..1ce6919fb34 100644 --- a/reactos/ntoskrnl/io/cancel.c +++ b/reactos/ntoskrnl/io/cancel.c @@ -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); } /* diff --git a/reactos/ntoskrnl/io/iomgr.c b/reactos/ntoskrnl/io/iomgr.c index 81852d21362..eff2a12bcc0 100644 --- a/reactos/ntoskrnl/io/iomgr.c +++ b/reactos/ntoskrnl/io/iomgr.c @@ -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); diff --git a/reactos/ntoskrnl/io/irp.c b/reactos/ntoskrnl/io/irp.c index d898dc4f1c8..ed98e86d973 100644 --- a/reactos/ntoskrnl/io/irp.c +++ b/reactos/ntoskrnl/io/irp.c @@ -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); } diff --git a/reactos/ntoskrnl/ps/kill.c b/reactos/ntoskrnl/ps/kill.c index 1837f096b5a..acd9271c3e5 100644 --- a/reactos/ntoskrnl/ps/kill.c +++ b/reactos/ntoskrnl/ps/kill.c @@ -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);