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.

166 lines
5.8 KiB

  1. #include "pch.h"
  2. VOID
  3. P2InitIrpQueueContext(
  4. IN PIRPQUEUE_CONTEXT IrpQueueContext
  5. )
  6. {
  7. InitializeListHead( &IrpQueueContext->irpQueue );
  8. KeInitializeSpinLock( &IrpQueueContext->irpQueueSpinLock );
  9. }
  10. VOID
  11. P2CancelQueuedIrp(
  12. IN PIRPQUEUE_CONTEXT IrpQueueContext,
  13. IN PIRP Irp
  14. )
  15. {
  16. KIRQL oldIrql;
  17. // Release the global cancel spin lock. Do this while not holding
  18. // any other spin locks so that we exit at the right IRQL.
  19. IoReleaseCancelSpinLock( Irp->CancelIrql );
  20. //
  21. // Dequeue and complete the IRP. The enqueue and dequeue
  22. // functions synchronize properly so that if this cancel routine
  23. // is called, the dequeue is safe and only the cancel routine
  24. // will complete the IRP. Hold the spin lock for the IRP queue
  25. // while we do this.
  26. //
  27. KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
  28. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  29. KeReleaseSpinLock( &IrpQueueContext->irpQueueSpinLock, oldIrql);
  30. // Complete the IRP. This is a call outside the driver, so all
  31. // spin locks must be released by this point.
  32. P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
  33. return;
  34. }
  35. NTSTATUS
  36. P2QueueIrp(
  37. IN PIRP Irp,
  38. IN PIRPQUEUE_CONTEXT IrpQueueContext,
  39. IN PDRIVER_CANCEL CancelRoutine
  40. )
  41. {
  42. PDRIVER_CANCEL oldCancelRoutine;
  43. KIRQL oldIrql;
  44. NTSTATUS status = STATUS_PENDING;
  45. KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
  46. // Queue the IRP and call IoMarkIrpPending to indicate that the
  47. // IRP may complete on a different thread.
  48. //
  49. // N.B. It's okay to call these inside the spin lock because
  50. // they're macros, not functions.
  51. IoMarkIrpPending( Irp );
  52. InsertTailList( &IrpQueueContext->irpQueue, &Irp->Tail.Overlay.ListEntry );
  53. // Must set a Cancel routine before checking the Cancel flag.
  54. #pragma warning( push )
  55. #pragma warning( disable : 4054 4055 )
  56. oldCancelRoutine = IoSetCancelRoutine( Irp, CancelRoutine );
  57. #pragma warning( pop )
  58. ASSERT( !oldCancelRoutine );
  59. if( Irp->Cancel ){
  60. // The IRP was canceled. Check whether our cancel routine was called.
  61. #pragma warning( push )
  62. #pragma warning( disable : 4054 4055 )
  63. oldCancelRoutine = IoSetCancelRoutine( Irp, NULL );
  64. #pragma warning( pop )
  65. if( oldCancelRoutine ) {
  66. // The cancel routine was NOT called.
  67. // So dequeue the IRP now and complete it after releasing the spinlock.
  68. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  69. status = Irp->IoStatus.Status = STATUS_CANCELLED;
  70. }
  71. else {
  72. // The cancel routine WAS called. As soon as we drop our
  73. // spin lock it will dequeue and complete the IRP. So
  74. // leave the IRP in the queue and otherwise don't touch
  75. // it. Return pending since we're not completing the IRP
  76. // here.
  77. }
  78. }
  79. KeReleaseSpinLock(&IrpQueueContext->irpQueueSpinLock, oldIrql);
  80. // Normally you shouldn't call IoMarkIrpPending and return a
  81. // status other than STATUS_PENDING. But you can break this rule
  82. // if you complete the IRP.
  83. if( status != STATUS_PENDING ) {
  84. P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information );
  85. }
  86. return status;
  87. }
  88. PIRP
  89. P2DequeueIrp(
  90. IN PIRPQUEUE_CONTEXT IrpQueueContext,
  91. IN PDRIVER_CANCEL CancelRoutine
  92. )
  93. {
  94. KIRQL oldIrql;
  95. PIRP nextIrp = NULL;
  96. KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
  97. while( !nextIrp && !IsListEmpty( &IrpQueueContext->irpQueue ) ){
  98. PDRIVER_CANCEL oldCancelRoutine;
  99. PLIST_ENTRY listEntry = RemoveHeadList( &IrpQueueContext ->irpQueue );
  100. // Get the next IRP off the queue.
  101. nextIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
  102. // Clear the IRP's cancel routine
  103. #pragma warning( push )
  104. #pragma warning( disable : 4054 4055 )
  105. oldCancelRoutine = IoSetCancelRoutine( nextIrp, NULL );
  106. #pragma warning( pop )
  107. // IoCancelIrp() could have just been called on this IRP.
  108. // What we're interested in is not whether IoCancelIrp() was called (nextIrp->Cancel flag set),
  109. // but whether IoCancelIrp() called (or is about to call) our cancel routine.
  110. // To check that, check the result of the test-and-set macro IoSetCancelRoutine.
  111. if( oldCancelRoutine ) {
  112. // Cancel routine not called for this IRP. Return this IRP.
  113. #if DBG
  114. ASSERT( oldCancelRoutine == CancelRoutine );
  115. #else
  116. UNREFERENCED_PARAMETER( CancelRoutine );
  117. #endif
  118. } else {
  119. // This IRP was just canceled and the cancel routine was (or will be) called.
  120. // The cancel routine will complete this IRP as soon as we drop the spin lock,
  121. // so don't do anything with the IRP.
  122. // Also, the cancel routine will try to dequeue the IRP,
  123. // so make the IRP's listEntry point to itself.
  124. ASSERT( nextIrp->Cancel );
  125. InitializeListHead( &nextIrp->Tail.Overlay.ListEntry );
  126. nextIrp = NULL;
  127. }
  128. }
  129. KeReleaseSpinLock( &IrpQueueContext ->irpQueueSpinLock, oldIrql );
  130. return nextIrp;
  131. }
  132. VOID
  133. P2CancelRoutine(
  134. IN PDEVICE_OBJECT DevObj,
  135. IN PIRP Irp
  136. )
  137. // this routine is driver specific - most other routines in this file are generic
  138. {
  139. PFDO_EXTENSION fdx = DevObj->DeviceExtension;
  140. PIRPQUEUE_CONTEXT irpQueueContext = &fdx->IrpQueueContext;
  141. P2CancelQueuedIrp( irpQueueContext, Irp );
  142. }