Source code of Windows XP (NT5)
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.

95 lines
3.4 KiB

  1. IRP Cancellation
  2. 1. An internal spinlock (ntoskrnl!IopCancelSpinLock) is always
  3. acquired before invoking a cancel routine. The previous IRQL
  4. can be found at pIrp->CancelIrql. The cancel routine MUST release
  5. the spinlock by calling IoReleaseCancelSpinLock() before returning.
  6. Code may call IoAcquireCancelSpinLock() as necessary, but don't
  7. party on it too much. It's a huge source of contention within the
  8. kernel.
  9. 2. You can never complete an IRP that still has a non-NULL cancel
  10. routine pointer. You MUST set the cancel routine pointer to NULL
  11. before calling IoCompleteRequest().
  12. 3. You cannot set a cancel routine and then pass the IRP to another
  13. driver. If you're going to pass the IRP to another driver (say, a
  14. lower-level driver) you MUST set the cancel routine to NULL before
  15. calling IoCallDriver().
  16. 4. IoSetCancelRoutine() returns the previous cancel routine pointer.
  17. It's possible that you are in the midst of completing an IRP and
  18. call IoSetCancelRoutine() to set the cancel routine pointer to
  19. NULL, and IoSetCancelRoutine() returns NULL. This typically means
  20. that the cancel routine is running or about to run Real Soon Now.
  21. 5. The pIrp->Cancel flag is set in IoCancelIrp() BEFORE the cancel
  22. routine pointer is extracted from the IRP and invoked.
  23. 6. IoCancelIrp extracts the Irp using IoSetCancelRoutine(NULL) . So
  24. you can detect if it has extracted your cancel routine by later
  25. calling IoSetCancelRoutine (which returns the previous value) .
  26. 7. Drivers check pIrp->Cancel AFTER setting a CancelRoutine in order
  27. to detect the case of the Irp being cancelled while your setting
  28. the CancelRoutine. If it is TRUE, using #4 above to detect if
  29. IoCancelIrp knows about your CancelRoutine. If it does not,
  30. you must complete the irp yourself. An easy way to do this is
  31. to manually call your CancelRoutine.
  32. IoSetCancelRoutine(pIrp, &CancelRoutine);
  33. if (pIrp->Cancel)
  34. {
  35. //
  36. // Irp was canceled.
  37. //
  38. //
  39. // Did IoCancelIrp consume our CancelRoutine?
  40. //
  41. if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
  42. {
  43. //
  44. // Nope. This means our cancle routing will
  45. // NOT be called by IoCancelIrp. We better call it
  46. //
  47. IoAcquireCancelSpinLock(&pIrp->CancelIrql);
  48. CancelRoutine(g_pDeviceObject, pIrp);
  49. }
  50. //
  51. // do not use pIrp anymore
  52. //
  53. }
  54. else
  55. {
  56. //
  57. // safe to queue pIrp. If it is cancelled the CancelRoutine
  58. // will be called.
  59. //
  60. }
  61. 7. x
  62. Pending
  63. 1. If you return STATUS_PENDING from an IOCTL handler, then you
  64. must first call IoMarkIrpPending().
  65. 2. If you take an incoming IRP, build another stack location,
  66. and call IoCallDriver(), you must set a completion routine and
  67. the completion routine must propagate the pending flag:
  68. if( pIrp->PendingReturned )
  69. {
  70. IoMarkIrpPending( pIrp );
  71. }
  72. 3. x