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.

1107 lines
25 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. workque.c
  5. Abstract:
  6. This module handles the communication between the NT redirector
  7. FSP and the NT redirector FSD.
  8. It defines routines that queue requests to the FSD, and routines
  9. that remove requests from the FSD work queue.
  10. Author:
  11. Larry Osterman (LarryO) 30-May-1990
  12. Revision History:
  13. 30-May-1990 LarryO
  14. Created
  15. --*/
  16. #include "precomp.h"
  17. #pragma hdrstop
  18. VOID
  19. BowserCriticalThreadWorker(
  20. IN PVOID Ctx
  21. );
  22. VOID
  23. BowserDelayedThreadWorker(
  24. IN PVOID Ctx
  25. );
  26. KSPIN_LOCK
  27. BowserIrpContextInterlock = {0};
  28. LIST_ENTRY
  29. BowserIrpContextList = {0};
  30. KSPIN_LOCK
  31. BowserIrpQueueSpinLock = {0};
  32. #ifdef ALLOC_PRAGMA
  33. #pragma alloc_text(PAGE, BowserAllocateIrpContext)
  34. #pragma alloc_text(PAGE, BowserFreeIrpContext)
  35. #pragma alloc_text(PAGE, BowserInitializeIrpContext)
  36. #pragma alloc_text(PAGE, BowserpUninitializeIrpContext)
  37. #pragma alloc_text(PAGE, BowserInitializeIrpQueue)
  38. #pragma alloc_text(PAGE, BowserQueueNonBufferRequest)
  39. #pragma alloc_text(INIT, BowserpInitializeIrpQueue)
  40. #pragma alloc_text(PAGE4BROW, BowserUninitializeIrpQueue)
  41. #pragma alloc_text(PAGE4BROW, BowserQueueNonBufferRequestReferenced)
  42. #pragma alloc_text(PAGE4BROW, BowserCancelQueuedIoForFile)
  43. #pragma alloc_text(PAGE4BROW, BowserTimeoutQueuedIrp)
  44. #endif
  45. //
  46. // Variables describing browsers use of a Critical system thread.
  47. //
  48. BOOLEAN BowserCriticalThreadRunning = FALSE;
  49. LIST_ENTRY BowserCriticalThreadQueue;
  50. WORK_QUEUE_ITEM BowserCriticalThreadWorkItem;
  51. VOID
  52. BowserQueueCriticalWorkItem (
  53. IN PWORK_QUEUE_ITEM WorkItem
  54. )
  55. /*++
  56. Routine Description:
  57. This routine queues an item onto the critical work queue.
  58. This routine ensures that at most one critical system thread is consumed
  59. by the browser by actually queing this item onto a browser specific queue
  60. then enqueing a critical work queue item that processes that queue.
  61. Arguments:
  62. WorkItem -- Work item to be processed on the critical work queue.
  63. Return Value:
  64. NONE
  65. --*/
  66. {
  67. KIRQL OldIrql;
  68. //
  69. // Insert the queue entry into the browser specific queue.
  70. //
  71. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  72. InsertTailList( &BowserCriticalThreadQueue, &WorkItem->List );
  73. //
  74. // If the browser doesn't have a critical system thread running,
  75. // start one now.
  76. //
  77. if ( !BowserCriticalThreadRunning ) {
  78. //
  79. // Mark that the thread is running now
  80. //
  81. BowserCriticalThreadRunning = TRUE;
  82. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  83. ExInitializeWorkItem( &BowserCriticalThreadWorkItem,
  84. BowserCriticalThreadWorker,
  85. NULL );
  86. ExQueueWorkItem(&BowserCriticalThreadWorkItem, CriticalWorkQueue );
  87. } else {
  88. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  89. }
  90. }
  91. VOID
  92. BowserCriticalThreadWorker(
  93. IN PVOID Ctx
  94. )
  95. /*++
  96. Routine Description:
  97. This routine processes critical browser workitems.
  98. This routine runs in a critical system thread. It is the only critical
  99. system thread used by the browser.
  100. Arguments:
  101. Ctx - Not used
  102. Return Value:
  103. NONE
  104. --*/
  105. {
  106. KIRQL OldIrql;
  107. PLIST_ENTRY Entry;
  108. PWORK_QUEUE_ITEM WorkItem;
  109. UNREFERENCED_PARAMETER( Ctx );
  110. //
  111. // Loop processing work items
  112. //
  113. while( TRUE ) {
  114. //
  115. // If the queue is empty,
  116. // indicate that this thread is no longer running.
  117. // return.
  118. //
  119. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  120. if ( IsListEmpty( &BowserCriticalThreadQueue ) ) {
  121. BowserCriticalThreadRunning = FALSE;
  122. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  123. return;
  124. }
  125. //
  126. // Remove an entry from the queue.
  127. //
  128. Entry = RemoveHeadList( &BowserCriticalThreadQueue );
  129. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  130. WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
  131. //
  132. // Call the queued routine
  133. //
  134. (*WorkItem->WorkerRoutine)(WorkItem->Parameter);
  135. }
  136. }
  137. //
  138. // Variables describing browsers use of a Delayed system thread.
  139. //
  140. BOOLEAN BowserDelayedThreadRunning = FALSE;
  141. LIST_ENTRY BowserDelayedThreadQueue;
  142. WORK_QUEUE_ITEM BowserDelayedThreadWorkItem;
  143. VOID
  144. BowserQueueDelayedWorkItem (
  145. IN PWORK_QUEUE_ITEM WorkItem
  146. )
  147. /*++
  148. Routine Description:
  149. This routine queues an item onto the Delayed work queue.
  150. This routine ensures that at most one Delayed system thread is consumed
  151. by the browser by actually queing this item onto a browser specific queue
  152. then enqueing a Delayed work queue item that processes that queue.
  153. Arguments:
  154. WorkItem -- Work item to be processed on the Delayed work queue.
  155. Return Value:
  156. NONE
  157. --*/
  158. {
  159. KIRQL OldIrql;
  160. //
  161. // Insert the queue entry into the browser specific queue.
  162. //
  163. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  164. InsertTailList( &BowserDelayedThreadQueue, &WorkItem->List );
  165. //
  166. // If the browser doesn't have a Delayed system thread running,
  167. // start one now.
  168. //
  169. if ( !BowserDelayedThreadRunning ) {
  170. //
  171. // Mark that the thread is running now
  172. //
  173. BowserDelayedThreadRunning = TRUE;
  174. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  175. ExInitializeWorkItem( &BowserDelayedThreadWorkItem,
  176. BowserDelayedThreadWorker,
  177. NULL );
  178. ExQueueWorkItem(&BowserDelayedThreadWorkItem, DelayedWorkQueue );
  179. } else {
  180. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  181. }
  182. }
  183. VOID
  184. BowserDelayedThreadWorker(
  185. IN PVOID Ctx
  186. )
  187. /*++
  188. Routine Description:
  189. This routine processes Delayed browser workitems.
  190. This routine runs in a Delayed system thread. It is the only Delayed
  191. system thread used by the browser.
  192. Arguments:
  193. Ctx - Not used
  194. Return Value:
  195. NONE
  196. --*/
  197. {
  198. KIRQL OldIrql;
  199. PLIST_ENTRY Entry;
  200. PWORK_QUEUE_ITEM WorkItem;
  201. UNREFERENCED_PARAMETER( Ctx );
  202. //
  203. // Loop processing work items
  204. //
  205. while( TRUE ) {
  206. //
  207. // If the queue is empty,
  208. // indicate that this thread is no longer running.
  209. // return.
  210. //
  211. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  212. if ( IsListEmpty( &BowserDelayedThreadQueue ) ) {
  213. BowserDelayedThreadRunning = FALSE;
  214. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  215. return;
  216. }
  217. //
  218. // Remove an entry from the queue.
  219. //
  220. Entry = RemoveHeadList( &BowserDelayedThreadQueue );
  221. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  222. WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
  223. //
  224. // Call the queued routine
  225. //
  226. (*WorkItem->WorkerRoutine)(WorkItem->Parameter);
  227. }
  228. }
  229. PIRP_CONTEXT
  230. BowserAllocateIrpContext (
  231. VOID
  232. )
  233. /*++
  234. Routine Description:
  235. Initialize a work queue structure, allocating all structures used for it.
  236. Arguments:
  237. None
  238. Return Value:
  239. PIRP_CONTEXT - Newly allocated Irp Context.
  240. --*/
  241. {
  242. PIRP_CONTEXT IrpContext;
  243. PAGED_CODE();
  244. if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&BowserIrpContextList, &BowserIrpContextInterlock)) == NULL) {
  245. //
  246. // If there are no IRP contexts in the "zone", allocate a new
  247. // Irp context from non paged pool.
  248. //
  249. IrpContext = ALLOCATE_POOL(NonPagedPool, sizeof(IRP_CONTEXT), POOL_IRPCONTEXT);
  250. if (IrpContext == NULL) {
  251. InternalError(("Could not allocate pool for IRP context\n"));
  252. }
  253. return IrpContext;
  254. }
  255. return IrpContext;
  256. }
  257. VOID
  258. BowserFreeIrpContext (
  259. PIRP_CONTEXT IrpContext
  260. )
  261. /*++
  262. Routine Description:
  263. Initialize a work queue structure, allocating all structures used for it.
  264. Arguments:
  265. PIRP_CONTEXT IrpContext - Irp Context to free.
  266. None
  267. Return Value:
  268. --*/
  269. {
  270. PAGED_CODE();
  271. //
  272. // We use the first two longwords of the IRP context as a list entry
  273. // when we free it to the zone.
  274. //
  275. ExInterlockedInsertTailList(&BowserIrpContextList, (PLIST_ENTRY )IrpContext,
  276. &BowserIrpContextInterlock);
  277. }
  278. VOID
  279. BowserInitializeIrpContext (
  280. VOID
  281. )
  282. /*++
  283. Routine Description:
  284. Initialize the Irp Context system
  285. Arguments:
  286. None.
  287. Return Value:
  288. None.
  289. --*/
  290. {
  291. PAGED_CODE();
  292. KeInitializeSpinLock(&BowserIrpContextInterlock);
  293. InitializeListHead(&BowserIrpContextList);
  294. }
  295. VOID
  296. BowserpUninitializeIrpContext(
  297. VOID
  298. )
  299. {
  300. PAGED_CODE();
  301. while (!IsListEmpty(&BowserIrpContextList)) {
  302. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)RemoveHeadList(&BowserIrpContextList);
  303. FREE_POOL(IrpContext);
  304. }
  305. }
  306. VOID
  307. BowserInitializeIrpQueue(
  308. PIRP_QUEUE Queue
  309. )
  310. {
  311. PAGED_CODE();
  312. InitializeListHead(&Queue->Queue);
  313. }
  314. VOID
  315. BowserUninitializeIrpQueue(
  316. PIRP_QUEUE Queue
  317. )
  318. {
  319. KIRQL OldIrql, CancelIrql;
  320. PDRIVER_CANCEL pDriverCancel;
  321. PLIST_ENTRY Entry;
  322. PIRP Request;
  323. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  324. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  325. //
  326. // Now remove this IRP from the request chain.
  327. //
  328. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  329. while (!IsListEmpty(&Queue->Queue)) {
  330. Entry = RemoveHeadList(&Queue->Queue);
  331. Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  332. // clear cancel routine
  333. Request->IoStatus.Information = 0;
  334. Request->Cancel = FALSE;
  335. pDriverCancel = IoSetCancelRoutine(Request, NULL);
  336. // Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
  337. if ( pDriverCancel ) {
  338. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  339. BowserCompleteRequest(Request, STATUS_CANCELLED);
  340. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  341. }
  342. // otherwise the cancel routine is running at the moment.
  343. }
  344. ASSERT (IsListEmpty(&Queue->Queue));
  345. //
  346. // Make sure no more entries are inserted on this queue.
  347. //
  348. Queue->Queue.Flink = NULL;
  349. Queue->Queue.Blink = NULL;
  350. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  351. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  352. }
  353. VOID
  354. BowserCancelQueuedRequest(
  355. IN PDEVICE_OBJECT DeviceObject OPTIONAL,
  356. IN PIRP Irp
  357. )
  358. /*++
  359. Routine Description:
  360. This routine will cancel a queued IRP.
  361. Arguments:
  362. IN PIRP Irp - Supplies the IRP to cancel.
  363. IN PKSPIN_LOCK SpinLock - Supplies a pointer to the spin lock protecting the
  364. queue
  365. IN PLIST_ENTRY Queue - Supplies a pointer to the head of the queue.
  366. Note: See bug history for more: 294055, 306281, 124178, 124180, 131773...
  367. --*/
  368. {
  369. KIRQL OldIrql;
  370. PLIST_ENTRY Entry, NextEntry;
  371. PIRP Request;
  372. PIRP_QUEUE Queue;
  373. PIO_STACK_LOCATION NextStack = IoGetNextIrpStackLocation(Irp);
  374. LIST_ENTRY CancelList;
  375. ASSERT ( Irp->CancelRoutine == NULL );
  376. InitializeListHead(&CancelList);
  377. //
  378. // Release IOmgr set cancel IRP spinlock & acquire the local
  379. // queue protection spinlock.
  380. //
  381. IoReleaseCancelSpinLock( Irp->CancelIrql );
  382. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  383. //
  384. // Now remove this IRP from the request chain.
  385. //
  386. //
  387. // A pointer to the queue is stored in the next stack location.
  388. //
  389. Queue = (PIRP_QUEUE)NextStack->Parameters.Others.Argument4;
  390. if (Queue != NULL && Queue->Queue.Flink != NULL) {
  391. for (Entry = Queue->Queue.Flink ;
  392. Entry != &Queue->Queue ;
  393. Entry = NextEntry) {
  394. Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  395. if (Request->Cancel) {
  396. // we're in a cancel routine so the global cancel spinlock is locked
  397. NextEntry = Entry->Flink;
  398. RemoveEntryList(Entry);
  399. Request->IoStatus.Information = 0;
  400. Request->IoStatus.Status = STATUS_CANCELLED;
  401. IoSetCancelRoutine(Request, NULL);
  402. InsertTailList(&CancelList,Entry);
  403. } else {
  404. NextEntry = Entry->Flink;
  405. }
  406. }
  407. }
  408. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  409. while (!IsListEmpty(&CancelList)) {
  410. Entry = RemoveHeadList(&CancelList);
  411. Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  412. BowserCompleteRequest(Request, Request->IoStatus.Status);
  413. }
  414. UNREFERENCED_PARAMETER(DeviceObject);
  415. }
  416. NTSTATUS
  417. BowserQueueNonBufferRequest(
  418. IN PIRP Irp,
  419. IN PIRP_QUEUE Queue,
  420. IN PDRIVER_CANCEL CancelRoutine
  421. )
  422. /*++
  423. Routine Description:
  424. Queue an IRP in the specified queue.
  425. This routine cannot be called at an IRQ level above APC_LEVEL.
  426. Arguments:
  427. Irp - Supplies the IRP to queue.
  428. Queue - Supplies a pointer to the head of the queue.
  429. CancelRoutine - Address of routine to call if the IRP is cancelled.
  430. --*/
  431. {
  432. NTSTATUS Status;
  433. //
  434. // This routine itself is paged code which calls the discardable code
  435. // in BowserQueueNonBufferRequestReferenced().
  436. //
  437. PAGED_CODE();
  438. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  439. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  440. Status = BowserQueueNonBufferRequestReferenced( Irp,
  441. Queue,
  442. CancelRoutine );
  443. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  444. return Status;
  445. }
  446. NTSTATUS
  447. BowserQueueNonBufferRequestReferenced(
  448. IN PIRP Irp,
  449. IN PIRP_QUEUE Queue,
  450. IN PDRIVER_CANCEL CancelRoutine
  451. )
  452. /*++
  453. Routine Description:
  454. Queue an IRP in the specified queue.
  455. This routine can only be called if the BowserDiscardableCodeSection
  456. is already referenced. It can be called at any IRQ level.
  457. Arguments:
  458. Irp - Supplies the IRP to queue.
  459. Queue - Supplies a pointer to the head of the queue.
  460. CancelRoutine - Address of routine to call if the IRP is cancelled.
  461. --*/
  462. {
  463. KIRQL OldIrql, CancelIrql;
  464. LARGE_INTEGER CurrentTickCount;
  465. PIO_STACK_LOCATION NextStackLocation;
  466. BOOL bReleaseSpinlocks;
  467. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  468. // DbgPrint("Queue IRP %lx to queue %lx\n", Irp, Queue);
  469. //
  470. // Insert the request into the request announcement list.
  471. //
  472. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  473. if (Queue->Queue.Flink == NULL) {
  474. ASSERT (Queue->Queue.Blink == NULL);
  475. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  476. return(STATUS_CANCELLED);
  477. }
  478. //
  479. // Flag that this request is going to be pending.
  480. //
  481. IoMarkIrpPending(Irp);
  482. InsertTailList(&Queue->Queue, &Irp->Tail.Overlay.ListEntry);
  483. //
  484. // Make sure there's room enough in the stack location for this.
  485. //
  486. ASSERT (Irp->CurrentLocation <= Irp->StackCount);
  487. NextStackLocation = IoGetNextIrpStackLocation(Irp);
  488. //
  489. // Stick the current tick count into the next IRP stack location
  490. // for this IRP. This allows us to figure out if these IRP's have been
  491. // around for "too long".
  492. //
  493. // Beware:the IRP stack location is unaligned.
  494. //
  495. KeQueryTickCount( &CurrentTickCount );
  496. *((LARGE_INTEGER UNALIGNED *)&NextStackLocation->Parameters.Others.Argument1) =
  497. CurrentTickCount;
  498. //
  499. // Link the queue into the IRP.
  500. //
  501. NextStackLocation->Parameters.Others.Argument4 = (PVOID)Queue;
  502. // WARNING: double spinlock condition
  503. IoAcquireCancelSpinLock(&CancelIrql);
  504. bReleaseSpinlocks = TRUE;
  505. if (Irp->Cancel) {
  506. //
  507. // The Irp is in cancellable state:
  508. // if CancelRoutine == NULL, the routine is currently running
  509. // Otherwise, we need to cancel it ourselves
  510. //
  511. if ( Irp->CancelRoutine ) {
  512. // cacelable:
  513. // - rm is valid since we're still holding BowserIrpQueueSpinLock
  514. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  515. // release spinlocks before completing the request
  516. IoReleaseCancelSpinLock(CancelIrql);
  517. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  518. bReleaseSpinlocks = FALSE;
  519. // complete.
  520. BowserCompleteRequest ( Irp, STATUS_CANCELLED );
  521. }
  522. // else CancelRoutine is running
  523. } else {
  524. IoSetCancelRoutine(Irp, CancelRoutine);
  525. }
  526. if ( bReleaseSpinlocks ) {
  527. // release spinlocks
  528. IoReleaseCancelSpinLock(CancelIrql);
  529. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  530. }
  531. return STATUS_PENDING;
  532. }
  533. VOID
  534. BowserTimeoutQueuedIrp(
  535. IN PIRP_QUEUE Queue,
  536. IN ULONG NumberOfSecondsToTimeOut
  537. )
  538. /*++
  539. Routine Description:
  540. This routine will scan an IRP queue and time out any requests that have
  541. been on the queue for "too long"
  542. Arguments:
  543. IN PIRP_QUEUE Queue - Supplies the Queue to scan.
  544. IN ULONG NumberOfSecondsToTimeOut - Supplies the number of seconds a request
  545. should remain on the queue.
  546. Return Value:
  547. None
  548. This routine will also complete any canceled queued requests it finds (on
  549. general principles).
  550. --*/
  551. {
  552. PIRP Irp;
  553. KIRQL OldIrql, CancelIrql;
  554. PDRIVER_CANCEL pDriverCancel;
  555. PLIST_ENTRY Entry, NextEntry;
  556. LARGE_INTEGER Timeout;
  557. LIST_ENTRY CancelList;
  558. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  559. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  560. InitializeListHead(&CancelList);
  561. //
  562. // Compute the timeout time into 100ns units.
  563. //
  564. Timeout.QuadPart = (LONGLONG)NumberOfSecondsToTimeOut * (LONGLONG)(10000*1000);
  565. //
  566. // Now convert the timeout into a number of ticks.
  567. //
  568. Timeout.QuadPart = Timeout.QuadPart / (LONGLONG)KeQueryTimeIncrement();
  569. ASSERT (Timeout.HighPart == 0);
  570. // DbgPrint("Dequeue irp from queue %lx...", Queue);
  571. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  572. for (Entry = Queue->Queue.Flink ;
  573. Entry != &Queue->Queue ;
  574. Entry = NextEntry) {
  575. Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  576. //
  577. // If the request was canceled, this is a convenient time to cancel
  578. // it.
  579. //
  580. if (Irp->Cancel) {
  581. NextEntry = Entry->Flink;
  582. pDriverCancel = IoSetCancelRoutine(Irp, NULL);
  583. // Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
  584. if ( pDriverCancel ) {
  585. Irp->IoStatus.Information = 0;
  586. Irp->IoStatus.Status = STATUS_CANCELLED;
  587. RemoveEntryList(Entry);
  588. InsertTailList(&CancelList,Entry);
  589. }
  590. // otherwise the cancel routine is running at the moment.
  591. //
  592. // Now check to see if this request is "too old". If it is, complete
  593. // it with an error.
  594. //
  595. } else {
  596. PIO_STACK_LOCATION NextIrpStackLocation;
  597. LARGE_INTEGER CurrentTickCount;
  598. LARGE_INTEGER RequestTime;
  599. LARGE_INTEGER Temp;
  600. NextIrpStackLocation = IoGetNextIrpStackLocation(Irp);
  601. //
  602. // Snapshot the current tickcount.
  603. //
  604. KeQueryTickCount(&CurrentTickCount);
  605. //
  606. // Figure out how many seconds this request has been active for
  607. //
  608. Temp.LowPart = (*((LARGE_INTEGER UNALIGNED *)&NextIrpStackLocation->Parameters.Others.Argument1)).LowPart;
  609. Temp.HighPart= (*((LARGE_INTEGER UNALIGNED *)&NextIrpStackLocation->Parameters.Others.Argument1)).HighPart;
  610. RequestTime.QuadPart = CurrentTickCount.QuadPart - Temp.QuadPart;
  611. ASSERT (RequestTime.HighPart == 0);
  612. //
  613. // If this request has lasted "too long", then time it
  614. // out.
  615. //
  616. if (RequestTime.LowPart > Timeout.LowPart) {
  617. NextEntry = Entry->Flink;
  618. pDriverCancel = IoSetCancelRoutine(Irp, NULL);
  619. // Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
  620. if ( pDriverCancel ) {
  621. Irp->IoStatus.Information = 0;
  622. Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
  623. RemoveEntryList(Entry);
  624. InsertTailList(&CancelList,Entry);
  625. }
  626. // otherwise it the cancel routine is running
  627. } else {
  628. NextEntry = Entry->Flink;
  629. }
  630. }
  631. }
  632. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  633. while (!IsListEmpty(&CancelList)) {
  634. Entry = RemoveHeadList(&CancelList);
  635. Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  636. BowserCompleteRequest(Irp, Irp->IoStatus.Status);
  637. }
  638. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  639. // DbgPrint("%lx.\n", Irp);
  640. }
  641. PIRP
  642. BowserDequeueQueuedIrp(
  643. IN PIRP_QUEUE Queue
  644. )
  645. {
  646. PIRP Irp;
  647. KIRQL OldIrql;
  648. PLIST_ENTRY IrpEntry;
  649. // DbgPrint("Dequeue irp from queue %lx...", Queue);
  650. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  651. if (IsListEmpty(&Queue->Queue)) {
  652. //
  653. // There are no waiting request announcement FsControls, so
  654. // return success.
  655. //
  656. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  657. // DbgPrint("No entry found.\n");
  658. return NULL;
  659. }
  660. IrpEntry = RemoveHeadList(&Queue->Queue);
  661. Irp = CONTAINING_RECORD(IrpEntry, IRP, Tail.Overlay.ListEntry);
  662. IoAcquireCancelSpinLock(&Irp->CancelIrql);
  663. //
  664. // Remove the cancel request for this IRP.
  665. //
  666. Irp->Cancel = FALSE;
  667. IoSetCancelRoutine(Irp, NULL);
  668. IoReleaseCancelSpinLock(Irp->CancelIrql);
  669. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  670. // DbgPrint("%lx.\n", Irp);
  671. return Irp;
  672. }
  673. VOID
  674. BowserCancelQueuedIoForFile(
  675. IN PIRP_QUEUE Queue,
  676. IN PFILE_OBJECT FileObject
  677. )
  678. {
  679. KIRQL OldIrql;
  680. PLIST_ENTRY Entry, NextEntry;
  681. PDRIVER_CANCEL pDriverCancel;
  682. PIRP Request;
  683. LIST_ENTRY CancelList;
  684. BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
  685. DISCARDABLE_CODE( BowserDiscardableCodeSection );
  686. InitializeListHead(&CancelList);
  687. //
  688. // Walk the outstanding IRP list for this
  689. //
  690. ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
  691. for (Entry = Queue->Queue.Flink ;
  692. Entry != &Queue->Queue ;
  693. Entry = NextEntry) {
  694. Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  695. //
  696. // If the request was canceled, blow it away.
  697. //
  698. if (Request->Cancel) {
  699. NextEntry = Entry->Flink;
  700. // This is the cancel routine setting of cancel routine ptr to NULL.
  701. pDriverCancel = IoSetCancelRoutine(Request, NULL);
  702. // Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
  703. if ( pDriverCancel ) {
  704. RemoveEntryList(Entry);
  705. Request->IoStatus.Information = 0;
  706. Request->IoStatus.Status = STATUS_CANCELLED;
  707. InsertTailList(&CancelList,Entry);
  708. }
  709. // otherwise the cancel routine is running currently.
  710. //
  711. // If the request was for this file object, blow it away.
  712. //
  713. } else if (Request->Tail.Overlay.OriginalFileObject == FileObject) {
  714. NextEntry = Entry->Flink;
  715. // This is the cancel routine setting of cancel routine ptr to NULL.
  716. pDriverCancel = IoSetCancelRoutine(Request, NULL);
  717. // Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
  718. if ( pDriverCancel ) {
  719. RemoveEntryList(Entry);
  720. Request->IoStatus.Information = 0;
  721. Request->IoStatus.Status = STATUS_FILE_CLOSED;
  722. InsertTailList(&CancelList,Entry);
  723. }
  724. // otherwise the cancel routine is running currently.
  725. } else {
  726. NextEntry = Entry->Flink;
  727. }
  728. }
  729. RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
  730. while (!IsListEmpty(&CancelList)) {
  731. Entry = RemoveHeadList(&CancelList);
  732. Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
  733. BowserCompleteRequest(Request, Request->IoStatus.Status);
  734. }
  735. BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
  736. }
  737. VOID
  738. BowserpInitializeIrpQueue(
  739. VOID
  740. )
  741. {
  742. KeInitializeSpinLock(&BowserIrpQueueSpinLock);
  743. InitializeListHead( &BowserCriticalThreadQueue );
  744. InitializeListHead( &BowserDelayedThreadQueue );
  745. }