Leaked source code of windows server 2003
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.

730 lines
17 KiB

  1. #include <pch.h>
  2. #include "irplist.h"
  3. void
  4. IrpList_InitEx(
  5. PIRP_LIST IrpList,
  6. PKSPIN_LOCK ListLock,
  7. PDRIVER_CANCEL CancelRoutine,
  8. PIRP_COMPLETION_ROUTINE IrpCompletionRoutine
  9. )
  10. /*++
  11. Routine Description:
  12. Initialize the IrpList
  13. Arguments:
  14. IrpList - Pointer to the IrpList structure
  15. ListLock - Pointer to the spinlock for the IrpList
  16. CancelRoutine - Routine to be called when an Irp on the IrpList
  17. is cancelled
  18. IrpCompletionRoutine - Optional Completion routine for an Irp on the IrpList
  19. Return Value:
  20. VOID
  21. --*/
  22. {
  23. LockedList_Init(&IrpList->LockedList, ListLock);
  24. ASSERT(CancelRoutine != NULL);
  25. IrpList->CancelRoutine = CancelRoutine;
  26. IrpList->IrpCompletionRoutine = IrpCompletionRoutine;
  27. }
  28. NTSTATUS
  29. IrpList_EnqueueLocked(
  30. PIRP_LIST IrpList,
  31. PIRP Irp,
  32. BOOLEAN StoreListInIrp,
  33. BOOLEAN InsertTail
  34. )
  35. /*++
  36. Routine Description:
  37. Enqueues an Irp on the IrpList.
  38. Assumes the caller has acquired the IrpList Spinlock.
  39. Arguments:
  40. IrpList - Pointer to the IrpList structure
  41. Irp - Pointer to the Irp to Enqueue
  42. StoreListInIrp - Set to TRUE is the Irp will be used to store the
  43. list entry
  44. InsertTail - Set to TRUE if Irp is to be enqueued at
  45. the tail of the IrpList.
  46. Return Value:
  47. NTSTATUS - STATUS_SUCCESS or appropriate error code
  48. --*/
  49. {
  50. PDRIVER_CANCEL oldCancelRoutine;
  51. NTSTATUS status;
  52. //
  53. // must set a cancel routine before checking the Cancel flag
  54. //
  55. oldCancelRoutine = IoSetCancelRoutine(Irp, IrpList->CancelRoutine);
  56. ASSERT(oldCancelRoutine == NULL);
  57. if (Irp->Cancel) {
  58. //
  59. // This IRP has already been cancelled, so complete it now.
  60. // We must clear the cancel routine before completing the IRP.
  61. // We must release the spinlock before calling out of the driver.
  62. //
  63. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  64. if (oldCancelRoutine != NULL) {
  65. //
  66. // The cancel routine was NOT called
  67. //
  68. ASSERT(oldCancelRoutine == IrpList->CancelRoutine);
  69. status = STATUS_CANCELLED;
  70. }
  71. else {
  72. //
  73. // The cancel routine was called. As soon as we drop the spinlock,
  74. // it will dequeue and complete the IRP. Increase the count because
  75. // the cancel routine will decrement it.
  76. //
  77. IrpList->LockedList.Count++;
  78. InitializeListHead(&Irp->Tail.Overlay.ListEntry);
  79. IoMarkIrpPending(Irp);
  80. status = Irp->IoStatus.Status = STATUS_PENDING;
  81. //
  82. // save a ptr to this structure in the Irp for the cancel routine.
  83. //
  84. if (StoreListInIrp) {
  85. Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
  86. }
  87. }
  88. }
  89. else {
  90. if (InsertTail) {
  91. LL_ADD_TAIL(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
  92. }
  93. else {
  94. LL_ADD_HEAD(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
  95. }
  96. IoMarkIrpPending(Irp);
  97. status = Irp->IoStatus.Status = STATUS_PENDING;
  98. //
  99. // save a ptr to this structure in the Irp for the cancel routine.
  100. //
  101. if (StoreListInIrp) {
  102. Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
  103. }
  104. }
  105. return status;
  106. }
  107. NTSTATUS
  108. IrpList_EnqueueEx(
  109. PIRP_LIST IrpList,
  110. PIRP Irp,
  111. BOOLEAN StoreListInIrp
  112. )
  113. /*++
  114. Routine Description:
  115. Enqueues an Irp on the IrpList.
  116. Arguments:
  117. IrpList - Pointer to the IrpList structure
  118. Irp - Pointer to the Irp to Enqueue
  119. StoreListInIrp - Set to TRUE is the Irp will be used to store the
  120. list entry
  121. Return Value:
  122. NTSTATUS - STATUS_SUCCESS or appropriate error code
  123. --*/
  124. {
  125. NTSTATUS status;
  126. KIRQL irql;
  127. LL_LOCK(&IrpList->LockedList, &irql);
  128. status = IrpList_EnqueueLocked(IrpList, Irp, StoreListInIrp, TRUE);
  129. LL_UNLOCK(&IrpList->LockedList, irql);
  130. return status;
  131. }
  132. BOOLEAN
  133. IrpList_MakeNonCancellable(
  134. PIRP_LIST IrpList,
  135. PIRP Irp
  136. )
  137. /*++
  138. Routine Description:
  139. Sets the Irp Cancel routine to NULL, making it non-cancellable.
  140. Arguments:
  141. IrpList - Pointer to the IrpList structure
  142. Irp - Pointer to the Irp to Enqueue
  143. Return Value:
  144. BOOLEAN - TRUE if we succeeded in making the Irp non-cancellable.
  145. --*/
  146. {
  147. PDRIVER_CANCEL oldCancelRoutine;
  148. BOOLEAN result;
  149. result = FALSE;
  150. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
  151. //
  152. // IoCancelIrp() could have just been called on this IRP.
  153. // What we're interested in is not whether IoCancelIrp() was called
  154. // (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
  155. // is about to call) our cancel routine. To check that, check the result
  156. // of the test-and-set macro IoSetCancelRoutine.
  157. //
  158. if (oldCancelRoutine != NULL) {
  159. //
  160. // Cancel routine not called for this IRP. Return this IRP.
  161. //
  162. ASSERT (oldCancelRoutine == IrpList->CancelRoutine);
  163. Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = NULL;
  164. result = TRUE;
  165. }
  166. else {
  167. //
  168. // This IRP was just cancelled and the cancel routine was (or will
  169. // be) called. The cancel routine will complete this IRP as soon as
  170. // we drop the spinlock. So don't do anything with the IRP.
  171. //
  172. // Also, the cancel routine will try to dequeue the IRP, so make the
  173. // IRP's listEntry point to itself.
  174. //
  175. ASSERT(Irp->Cancel);
  176. InitializeListHead(&Irp->Tail.Overlay.ListEntry);
  177. result = FALSE;
  178. }
  179. return result;
  180. }
  181. PIRP
  182. IrpList_DequeueLocked(
  183. PIRP_LIST IrpList
  184. )
  185. {
  186. /*++
  187. Routine Description:
  188. Dequeue an IRP from the head of the IrpList.
  189. Assumes the caller has acquired the IrpList SpinLock.
  190. Arguments:
  191. IrpList - Pointer to the IrpList structure
  192. Return Value:
  193. PIRP - Pointer to the Irp dequeued form the IrpList.
  194. NULL if no IRP is avaliable.
  195. --*/
  196. PIRP nextIrp = NULL;
  197. PLIST_ENTRY ple;
  198. nextIrp = NULL;
  199. while (nextIrp == NULL && !IsListEmpty(&IrpList->LockedList.ListHead)){
  200. ple = LL_REMOVE_HEAD(&IrpList->LockedList);
  201. //
  202. // Get the next IRP off the queue and clear the cancel routine
  203. //
  204. nextIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
  205. if (IrpList_MakeNonCancellable(IrpList, nextIrp) == FALSE) {
  206. nextIrp = NULL;
  207. }
  208. }
  209. return nextIrp;
  210. }
  211. PIRP
  212. IrpList_Dequeue(
  213. PIRP_LIST IrpList
  214. )
  215. /*++
  216. Routine Description:
  217. Dequeue an IRP from the head of the IrpList.
  218. Arguments:
  219. IrpList - Pointer to the IrpList structure
  220. Return Value:
  221. PIRP - Pointer to the Irp dequeued form the IrpList.
  222. NULL if no IRP is avaliable.
  223. --*/
  224. {
  225. PIRP irp;
  226. KIRQL irql;
  227. LL_LOCK(&IrpList->LockedList, &irql);
  228. irp = IrpList_DequeueLocked(IrpList);
  229. LL_UNLOCK(&IrpList->LockedList, irql);
  230. return irp;
  231. }
  232. BOOLEAN
  233. IrpList_DequeueIrp(
  234. PIRP_LIST IrpList,
  235. PIRP Irp
  236. )
  237. /*++
  238. Routine Description:
  239. Dequeue a specific IRP from the IrpList.
  240. Arguments:
  241. IrpList - Pointer to the IrpList structure
  242. Irp - Pointer to an IRP contained in the IrpList.
  243. Return Value:
  244. BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
  245. --*/
  246. {
  247. KIRQL irql;
  248. BOOLEAN result;
  249. LL_LOCK(&IrpList->LockedList, &irql);
  250. result = IrpList_DequeueIrpLocked(IrpList, Irp);
  251. LL_UNLOCK(&IrpList->LockedList, irql);
  252. return result;
  253. }
  254. BOOLEAN
  255. IrpList_DequeueIrpLocked(
  256. PIRP_LIST IrpList,
  257. PIRP Irp
  258. )
  259. /*++
  260. Routine Description:
  261. Dequeue a specific IRP from the IrpList
  262. Assumes the caller has acquired the IrpList spinlock
  263. Arguments:
  264. IrpList - Pointer to the IrpList structure
  265. Irp - Pointer to an IRP contained in the IrpList.
  266. Return Value:
  267. BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
  268. --*/
  269. {
  270. PLIST_ENTRY ple;
  271. PIRP pIrp;
  272. BOOLEAN result;
  273. result = FALSE;
  274. for (ple = IrpList->LockedList.ListHead.Flink;
  275. ple != &IrpList->LockedList.ListHead;
  276. ple = ple->Flink) {
  277. pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
  278. if (pIrp == Irp) {
  279. RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
  280. IrpList->LockedList.Count--;
  281. result = IrpList_MakeNonCancellable(IrpList, pIrp);
  282. break;
  283. }
  284. }
  285. return result;
  286. }
  287. ULONG
  288. IrpList_ProcessAndDrain(
  289. PIRP_LIST IrpList,
  290. PFNPROCESSIRP FnProcessIrp,
  291. PVOID Context,
  292. PLIST_ENTRY DrainHead
  293. )
  294. /*++
  295. Routine Description:
  296. Remove all cancellable Irps from the IrpList and process.
  297. Arguments:
  298. IrpList - Pointer to the IrpList structure
  299. FnProcessIrp - Function to process the Irp
  300. Context - Context to pass into the FnProcessIrp
  301. DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
  302. Return Value:
  303. ULONG - Number of IRPs processed
  304. --*/
  305. {
  306. ULONG count;
  307. KIRQL irql;
  308. LL_LOCK(&IrpList->LockedList, &irql);
  309. count = IrpList_ProcessAndDrainLocked(
  310. IrpList, FnProcessIrp, Context, DrainHead);
  311. LL_UNLOCK(&IrpList->LockedList, irql);
  312. return count;
  313. }
  314. ULONG
  315. IrpList_ProcessAndDrainLocked(
  316. PIRP_LIST IrpList,
  317. PFNPROCESSIRP FnProcessIrp,
  318. PVOID Context,
  319. PLIST_ENTRY DrainHead
  320. )
  321. /*++
  322. Routine Description:
  323. Remove all cancellable Irps from the IrpList and process
  324. Assumes that the caller has acquired the IrpList Spinlock.
  325. Arguments:
  326. IrpList - Pointer to the IrpList structure
  327. FnProcessIrp - Function to process the Irp
  328. Context - Context to pass into the FnProcessIrp
  329. DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
  330. Return Value:
  331. ULONG - Number of IRPs processed
  332. --*/
  333. {
  334. PLIST_ENTRY ple;
  335. PIRP pIrp;
  336. NTSTATUS status;
  337. ULONG count;
  338. count = 0;
  339. ASSERT(FnProcessIrp != NULL);
  340. for (ple = IrpList->LockedList.ListHead.Flink;
  341. ple != &IrpList->LockedList.ListHead;
  342. /* ple = ple->Flink */) {
  343. pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
  344. //
  345. // Advance immediately so we don't lose the next link in the list in
  346. // case we remove the current irp.
  347. //
  348. ple = ple->Flink;
  349. if (FnProcessIrp(Context, pIrp)) {
  350. RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
  351. IrpList->LockedList.Count--;
  352. if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
  353. InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
  354. count++;
  355. }
  356. }
  357. }
  358. return count;
  359. }
  360. ULONG
  361. IrpList_DrainLocked(
  362. PIRP_LIST IrpList,
  363. PLIST_ENTRY DrainHead
  364. )
  365. /*++
  366. Routine Description:
  367. Remove all cancellable Irps from the IrpList and queue to the DrainHead
  368. Arguments:
  369. IrpList - Pointer to the IrpList structure
  370. DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
  371. Return Value:
  372. ULONG - Number of IRPs processed
  373. --*/
  374. {
  375. PIRP irp;
  376. ULONG count;
  377. count = 0;
  378. while (TRUE) {
  379. irp = IrpList_DequeueLocked(IrpList);
  380. if (irp != NULL) {
  381. InsertTailList(DrainHead, &irp->Tail.Overlay.ListEntry);
  382. count++;
  383. }
  384. else {
  385. break;
  386. }
  387. }
  388. ASSERT(LL_GET_COUNT(&IrpList->LockedList) == 0);
  389. return count;
  390. }
  391. ULONG
  392. IrpList_DrainLockedByFileObject(
  393. PIRP_LIST IrpList,
  394. PLIST_ENTRY DrainHead,
  395. PFILE_OBJECT FileObject
  396. )
  397. /*++
  398. Routine Description:
  399. Remove all cancellable Irps for specified FileObject
  400. from the IrpList and queue to the DrainHead
  401. Arguments:
  402. IrpList - Pointer to the IrpList structure
  403. DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
  404. FileObject - Pointer to specified FILE_OBJECT
  405. Return Value:
  406. ULONG - Number of IRPs processed
  407. --*/
  408. {
  409. PIRP pIrp;
  410. PDRIVER_CANCEL pOldCancelRoutine;
  411. PIO_STACK_LOCATION pStack;
  412. ULONG count;
  413. PLIST_ENTRY ple;
  414. count = 0;
  415. ASSERT(FileObject != NULL);
  416. for (ple = IrpList->LockedList.ListHead.Flink;
  417. ple != &IrpList->LockedList.ListHead;
  418. /* ple = ple->Flink */) {
  419. pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
  420. //
  421. // Advance immediately so we don't lose the next link in the list in
  422. // case we remove the current irp.
  423. //
  424. ple = ple->Flink;
  425. pStack = IoGetCurrentIrpStackLocation(pIrp);
  426. if (pStack->FileObject == FileObject) {
  427. RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
  428. IrpList->LockedList.Count--;
  429. if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
  430. InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
  431. count++;
  432. }
  433. }
  434. }
  435. return count;
  436. }
  437. ULONG
  438. IrpList_Drain(
  439. PIRP_LIST IrpList,
  440. PLIST_ENTRY DrainHead
  441. )
  442. /*++
  443. Routine Description:
  444. Remove all cancellable IRPs and queue to DrainHead
  445. Arguments:
  446. IrpList - Pointer to the IrpList structure
  447. DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
  448. Return Value:
  449. ULONG - Number of IRPs processed
  450. --*/
  451. {
  452. ULONG count;
  453. KIRQL irql;
  454. LL_LOCK(&IrpList->LockedList, &irql);
  455. count = IrpList_DrainLocked(IrpList, DrainHead);
  456. LL_UNLOCK(&IrpList->LockedList, irql);
  457. return count;
  458. }
  459. void
  460. IrpList_HandleCancel(
  461. PIRP_LIST IrpList,
  462. PDEVICE_OBJECT DeviceObject,
  463. PIRP Irp
  464. )
  465. /*++
  466. Routine Description:
  467. Cancel Routine for IRPs on the IrpList
  468. Arguments:
  469. IrpList - Pointer to the IrpList structure
  470. DeviceObject - Device Object, used for IrpCompletionRoutine
  471. Return Value:
  472. VOID
  473. --*/
  474. {
  475. KIRQL irql;
  476. //
  477. // Release the global cancel spinlock.
  478. // Do this while not holding any other spinlocks so that we exit at the
  479. // right IRQL.
  480. //
  481. IoReleaseCancelSpinLock (Irp->CancelIrql);
  482. //
  483. // Dequeue and complete the IRP. The enqueue and dequeue functions
  484. // synchronize properly so that if this cancel routine is called,
  485. // the dequeue is safe and only the cancel routine will complete the IRP.
  486. //
  487. LL_LOCK(&IrpList->LockedList, &irql);
  488. if (!IsListEmpty(&Irp->Tail.Overlay.ListEntry)) {
  489. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  490. IrpList->LockedList.Count--;
  491. }
  492. LL_UNLOCK(&IrpList->LockedList, irql);
  493. if (IrpList->IrpCompletionRoutine != NULL) {
  494. (void) IrpList->IrpCompletionRoutine(
  495. DeviceObject, Irp, STATUS_CANCELLED);
  496. }
  497. else {
  498. //
  499. // Complete the IRP. This is a call outside the driver, so all
  500. // spinlocks must be released by this point.
  501. //
  502. Irp->IoStatus.Status = STATUS_CANCELLED;
  503. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  504. }
  505. }
  506. void
  507. IrpList_CancelRoutine(
  508. PDEVICE_OBJECT DeviceObject,
  509. PIRP Irp
  510. )
  511. {
  512. PSCUTIL_EXTENSION pExt;
  513. KIRQL oldIrql;
  514. PIO_STACK_LOCATION irpSp;
  515. pExt = *((PSCUTIL_EXTENSION*) DeviceObject->DeviceExtension);
  516. irpSp = IoGetNextIrpStackLocation(Irp);
  517. if (irpSp->CompletionRoutine) {
  518. Irp->IoStatus.Status = STATUS_CANCELLED;
  519. irpSp->CompletionRoutine(DeviceObject,
  520. Irp,
  521. irpSp->Context);
  522. }
  523. IoReleaseRemoveLock(pExt->RemoveLock,
  524. Irp);
  525. DecIoCount(pExt);
  526. IrpList_HandleCancel(IRP_LIST_FROM_IRP(Irp),
  527. DeviceObject,
  528. Irp);
  529. }