阅读:1411回复:4
急救 取消Irp 太久了!!!
取消一个正在control pipe上等待资料的irp,使用IoCancelIrp()需要多久的时间才能让它中断工作呢?
在自已的测试中发现竟然要高达数百ms, 这个结果让我的传输工作大打折扣,请问有没有人碰过类似的情况,如何处理? |
|
沙发#
发布于:2004-07-21 08:42
取消一个正在control pipe上等待资料的irp,使用IoCancelIrp()需要多久的时间才能让它中断工作呢? 恐怕要看lower device是怎么管理irp队列的 |
|
|
板凳#
发布于:2004-07-21 08:57
有些事不是你能管的了的......
|
|
|
地板#
发布于:2005-07-26 16:13
WDM IRP-Handling Tips
To retain ownership of an IRP in an IRP handler function, return STATUS_PENDING. To retain ownership of an IRP in a completion routine, return STATUS_MORE_PROCESSING_REQUIRED. If you free the IRP in your completion routine, also return STATUS_MORE_PROCESSING_REQUIRED. If you queue an IRP (whether completed or not), you must set a cancel routine. Right after setting a cancel routine, check that the IRP was not already cancelled. Your cancel routine must call IoReleaseCancelSpinLock to release the global cancel spinlock whether or not the cancel routine actually completes any IRPs. Do this after acquiring your local spinlock and before calling outside the driver to complete the cancelled IRP. Only the cancel routine needs to release the cancel spinlock. If you discover that the IRP was cancelled as you're about to queue it, and you never set the cancel routine, then IoCancelIrp releases the cancel spinlock for you. IoCancelIrp either calls a cancel routine or releases the cancel spinlock itself, not both. When dequeuing an IRP, Irp->Cancel TRUE indicates that the IRP was cancelled. It does not indicate whether your cancel routine was called. The way to determine that is to test and clear the cancel routine using IoSetCancelRoutine(Irp, NULL). If the result is NULL, the cancel routine was called; otherwise, it was not called. Whenever you are about to return STATUS_PENDING for an IRP, call the IoMarkIrpPending macro on that IRP. Do this while still holding the local spinlock. If you reuse an IRP, make sure to first call IoInitializeIrp, which clears the Cancel field. Never call outside your driver while holding a spinlock. Functions that execute only at IRQL<DISPATCH_ LEVEL can be made pageable. However, if any part of the function executes while holding a spinlock, that code will execute at a higher IRQL, so the code and any data that it touches must be locked. For unsupported IRPs, your dispatch function should either pass the IRP down to the next driver or complete it without changing the default status. |
|
地下室#
发布于:2005-07-26 16:57
//
// An IRPLOCK allows for safe cancellation. The idea is to protect the IRP // while the canceller is calling IoCancelIrp. This is done by wrapping the // call in InterlockedExchange(s). The roles are as follows: // // Initiator/completion: Cancelable --> IoCallDriver() --> Completed // Canceller: CancelStarted --> IoCancelIrp() --> CancelCompleted // // No cancellation: // Cancelable-->Completed // // Cancellation, IoCancelIrp returns before completion: // Cancelable --> CancelStarted --> CancelCompleted --> Completed // // Canceled after completion: // Cancelable --> Completed -> CancelStarted // // Cancellation, IRP completed during call to IoCancelIrp(): // Cancelable --> CancelStarted -> Completed --> CancelCompleted // // The transition from CancelStarted to Completed tells the completer to block // postprocessing (IRP ownership is transferred to the canceller). Similarly, // the canceller learns it owns IRP postprocessing (free, completion, etc) // during a Completed->CancelCompleted transition. // NTSTATUS MakeSynchronousIoctlWithTimeOut( IN PDEVICE_OBJECT TopOfDeviceStack, IN ULONG IoctlControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, IN ULONG Milliseconds ) /*++ Arguments: TopOfDeviceStack - IoctlControlCode - Value of the IOCTL request. InputBuffer - Buffer to be sent to the TopOfDeviceStack. InputBufferLength - Size of buffer to be sent to the TopOfDeviceStack. OutputBuffer - Buffer for received data from the TopOfDeviceStack. OutputBufferLength - Size of receive buffer from the TopOfDeviceStack. Milliseconds - Timeout value in Milliseconds Return Value: NT status code --*/ { NTSTATUS status; PIRP irp; KEVENT event; IO_STATUS_BLOCK ioStatus; LARGE_INTEGER dueTime; IRPLOCK lock; KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest ( IoctlControlCode, TopOfDeviceStack, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, FALSE, // External ioctl &event, &ioStatus); if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } lock = IRPLOCK_CANCELABLE; IoSetCompletionRoutine( irp, MakeSynchronousIoctlWithTimeOutCompletion, &lock, TRUE, TRUE, TRUE ); status = IoCallDriver(TopOfDeviceStack, irp); if (status == STATUS_PENDING) { dueTime.QuadPart = -10000 * Milliseconds; status = KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, &dueTime ); if (status == STATUS_TIMEOUT) { if (InterlockedExchange((PVOID)&lock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) { // // You got it to the IRP before it was completed. You can cancel // the IRP without fear of losing it, because the completion routine // does not let go of the IRP until you allow it. // IoCancelIrp(irp); // // Release the completion routine. If it already got there, // then you need to complete it yourself. Otherwise, you got // through IoCancelIrp before the IRP completed entirely. // if (InterlockedExchange(&lock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) { IoCompleteRequest(irp, IO_NO_INCREMENT); } } KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); ioStatus.Status = status; // Return STATUS_TIMEOUT } else { status = ioStatus.Status; } } return status; } NTSTATUS MakeSynchronousIoctlWithTimeOutCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PLONG lock; lock = (PLONG) Context; if (InterlockedExchange((PVOID)&lock, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED) { // // Main line code has got the control of the IRP. It will // now take the responsibility of completing the IRP. // Therefore... return STATUS_MORE_PROCESSING_REQUIRED; } return STATUS_CONTINUE_COMPLETION ; } |
|