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.

725 lines
19 KiB

  1. /*++
  2. Copyright (c) 1991, 1992, 1993 Microsoft Corporation
  3. Module Name:
  4. utils.c
  5. Abstract:
  6. This module contains code that perform queueing and completion
  7. manipulation on requests.
  8. Author:
  9. Anthony V. Ercolano 26-Sep-1991
  10. Environment:
  11. Kernel mode
  12. Revision History :
  13. --*/
  14. #include "precomp.h"
  15. VOID SerialRundownIrpRefs(IN PIRP *CurrentOpIrp, IN PKTIMER IntervalTimer, IN PKTIMER TotalTimer);
  16. #ifdef ALLOC_PRAGMA
  17. #endif
  18. VOID
  19. SerialKillAllReadsOrWrites(IN PDEVICE_OBJECT DeviceObject,
  20. IN PLIST_ENTRY QueueToClean,
  21. IN PIRP *CurrentOpIrp)
  22. /*++
  23. Routine Description:
  24. This function is used to cancel all queued and the current irps
  25. for reads or for writes.
  26. Arguments:
  27. DeviceObject - A pointer to the serial device object.
  28. QueueToClean - A pointer to the queue which we're going to clean out.
  29. CurrentOpIrp - Pointer to a pointer to the current irp.
  30. Return Value:
  31. None.
  32. --*/
  33. {
  34. KIRQL cancelIrql;
  35. PDRIVER_CANCEL cancelRoutine;
  36. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  37. //
  38. // We acquire the cancel spin lock. This will prevent the
  39. // irps from moving around.
  40. //
  41. IoAcquireCancelSpinLock(&cancelIrql);
  42. //
  43. // Clean the list from back to front.
  44. //
  45. while (!IsListEmpty(QueueToClean))
  46. {
  47. PIRP currentLastIrp = CONTAINING_RECORD(QueueToClean->Blink, IRP, Tail.Overlay.ListEntry);
  48. SpxIRPCounter(pPort, currentLastIrp, IRP_DEQUEUED); // Decrement counter for performance stats.
  49. RemoveEntryList(QueueToClean->Blink);
  50. cancelRoutine = currentLastIrp->CancelRoutine;
  51. currentLastIrp->CancelIrql = cancelIrql;
  52. currentLastIrp->CancelRoutine = NULL;
  53. currentLastIrp->Cancel = TRUE;
  54. cancelRoutine(DeviceObject, currentLastIrp);
  55. IoAcquireCancelSpinLock(&cancelIrql);
  56. }
  57. //
  58. // The queue is clean. Now go after the current if
  59. // it's there.
  60. //
  61. if(*CurrentOpIrp)
  62. {
  63. cancelRoutine = (*CurrentOpIrp)->CancelRoutine;
  64. (*CurrentOpIrp)->Cancel = TRUE;
  65. //
  66. // If the current irp is not in a cancelable state
  67. // then it *will* try to enter one and the above
  68. // assignment will kill it. If it already is in
  69. // a cancelable state then the following will kill it.
  70. //
  71. if(cancelRoutine)
  72. {
  73. (*CurrentOpIrp)->CancelRoutine = NULL;
  74. (*CurrentOpIrp)->CancelIrql = cancelIrql;
  75. //
  76. // This irp is already in a cancelable state. We simply
  77. // mark it as canceled and call the cancel routine for it.
  78. //
  79. cancelRoutine(DeviceObject, *CurrentOpIrp);
  80. }
  81. else
  82. {
  83. IoReleaseCancelSpinLock(cancelIrql);
  84. }
  85. }
  86. else
  87. {
  88. IoReleaseCancelSpinLock(cancelIrql);
  89. }
  90. }
  91. VOID
  92. SerialGetNextIrp(IN PPORT_DEVICE_EXTENSION pPort,
  93. IN PIRP *CurrentOpIrp,
  94. IN PLIST_ENTRY QueueToProcess,
  95. OUT PIRP *NextIrp,
  96. IN BOOLEAN CompleteCurrent)
  97. /*++
  98. Routine Description:
  99. This function is used to make the head of the particular
  100. queue the current irp. It also completes the what
  101. was the old current irp if desired.
  102. Arguments:
  103. CurrentOpIrp - Pointer to a pointer to the currently active
  104. irp for the particular work list. Note that
  105. this item is not actually part of the list.
  106. QueueToProcess - The list to pull the new item off of.
  107. NextIrp - The next Irp to process. Note that CurrentOpIrp
  108. will be set to this value under protection of the
  109. cancel spin lock. However, if *NextIrp is NULL when
  110. this routine returns, it is not necessaryly true the
  111. what is pointed to by CurrentOpIrp will also be NULL.
  112. The reason for this is that if the queue is empty
  113. when we hold the cancel spin lock, a new irp may come
  114. in immediately after we release the lock.
  115. CompleteCurrent - If TRUE then this routine will complete the
  116. irp pointed to by the pointer argument
  117. CurrentOpIrp.
  118. Return Value:
  119. None.
  120. --*/
  121. {
  122. KIRQL oldIrql;
  123. PIRP oldIrp;
  124. IoAcquireCancelSpinLock(&oldIrql);
  125. oldIrp = *CurrentOpIrp;
  126. if(oldIrp)
  127. {
  128. if(CompleteCurrent)
  129. ASSERT(!oldIrp->CancelRoutine);
  130. }
  131. //
  132. // Check to see if there is a new irp to start up.
  133. //
  134. if(!IsListEmpty(QueueToProcess))
  135. {
  136. PLIST_ENTRY headOfList;
  137. headOfList = RemoveHeadList(QueueToProcess);
  138. *CurrentOpIrp = CONTAINING_RECORD(headOfList, IRP, Tail.Overlay.ListEntry);
  139. SpxIRPCounter(pPort, *CurrentOpIrp, IRP_DEQUEUED); // Decrement counter for performance stats.
  140. IoSetCancelRoutine(*CurrentOpIrp, NULL);
  141. }
  142. else
  143. {
  144. *CurrentOpIrp = NULL;
  145. }
  146. *NextIrp = *CurrentOpIrp;
  147. IoReleaseCancelSpinLock(oldIrql);
  148. if(CompleteCurrent)
  149. {
  150. if(oldIrp)
  151. {
  152. SerialDump(SERIRPPATH, ("Complete Irp: %x\n", oldIrp));
  153. SpxIRPCounter(pPort, oldIrp, IRP_COMPLETED); // Increment counter for performance stats.
  154. IoCompleteRequest(oldIrp, IO_SERIAL_INCREMENT);
  155. }
  156. }
  157. }
  158. VOID
  159. SerialTryToCompleteCurrent(IN PPORT_DEVICE_EXTENSION pPort,
  160. IN PKSYNCHRONIZE_ROUTINE SynchRoutine OPTIONAL,
  161. IN KIRQL IrqlForRelease,
  162. IN NTSTATUS StatusToUse,
  163. IN PIRP *CurrentOpIrp,
  164. IN PLIST_ENTRY QueueToProcess OPTIONAL,
  165. IN PKTIMER IntervalTimer OPTIONAL,
  166. IN PKTIMER TotalTimer OPTIONAL,
  167. IN PSERIAL_START_ROUTINE Starter OPTIONAL,
  168. IN PSERIAL_GET_NEXT_ROUTINE GetNextIrp OPTIONAL,
  169. IN LONG RefType)
  170. /*++
  171. Routine Description:
  172. This routine attempts to kill all of the reasons there are
  173. references on the current read/write. If everything can be killed
  174. it will complete this read/write and try to start another.
  175. NOTE: This routine assumes that it is called with the cancel
  176. spinlock held.
  177. Arguments:
  178. Extension - Simply a pointer to the device extension.
  179. SynchRoutine - A routine that will synchronize with the isr
  180. and attempt to remove the knowledge of the
  181. current irp from the isr. NOTE: This pointer
  182. can be null.
  183. IrqlForRelease - This routine is called with the cancel spinlock held.
  184. This is the irql that was current when the cancel
  185. spinlock was acquired.
  186. StatusToUse - The irp's status field will be set to this value, if
  187. this routine can complete the irp.
  188. Return Value:
  189. None.
  190. --*/
  191. {
  192. //
  193. // We can decrement the reference to "remove" the fact
  194. // that the caller no longer will be accessing this irp.
  195. //
  196. // If RefType was not the ISR reference
  197. if(RefType != SERIAL_REF_ISR)
  198. {
  199. SERIAL_CLEAR_REFERENCE(*CurrentOpIrp, RefType); // Clear the reference
  200. }
  201. else
  202. {
  203. if(SERIAL_REFERENCE_COUNT(*CurrentOpIrp) & RefType) // If reference is for ISR and is still set.
  204. SERIAL_CLEAR_REFERENCE(*CurrentOpIrp, RefType); // Clear it.
  205. }
  206. if(SynchRoutine)
  207. KeSynchronizeExecution(pPort->Interrupt, SynchRoutine, pPort);
  208. //
  209. // Try to run down all other references to this irp.
  210. //
  211. SerialRundownIrpRefs(CurrentOpIrp, IntervalTimer, TotalTimer);
  212. //
  213. // See if the ref count is zero after trying to kill everybody else.
  214. //
  215. if(!SERIAL_REFERENCE_COUNT(*CurrentOpIrp))
  216. {
  217. PIRP newIrp;
  218. //
  219. // The ref count was zero so we should complete this request.
  220. // The following call will also cause the current irp to be completed.
  221. //
  222. (*CurrentOpIrp)->IoStatus.Status = StatusToUse;
  223. if(StatusToUse == STATUS_CANCELLED)
  224. (*CurrentOpIrp)->IoStatus.Information = 0;
  225. if(GetNextIrp)
  226. {
  227. IoReleaseCancelSpinLock(IrqlForRelease);
  228. GetNextIrp(pPort, CurrentOpIrp, QueueToProcess, &newIrp, TRUE);
  229. if(newIrp)
  230. Starter(pPort);
  231. }
  232. else
  233. {
  234. PIRP oldIrp = *CurrentOpIrp;
  235. //
  236. // There was no get next routine. We will simply complete
  237. // the irp. We should make sure that we null out the
  238. // pointer to the pointer to this irp.
  239. //
  240. *CurrentOpIrp = NULL;
  241. IoReleaseCancelSpinLock(IrqlForRelease);
  242. SerialDump(SERIRPPATH, ("Complete Irp: %x\n", oldIrp));
  243. SpxIRPCounter(pPort, oldIrp, IRP_COMPLETED); // Increment counter for performance stats.
  244. IoCompleteRequest(oldIrp, IO_SERIAL_INCREMENT);
  245. }
  246. }
  247. else
  248. {
  249. IoReleaseCancelSpinLock(IrqlForRelease);
  250. }
  251. }
  252. VOID
  253. SerialRundownIrpRefs(IN PIRP *CurrentOpIrp,
  254. IN PKTIMER IntervalTimer OPTIONAL,
  255. IN PKTIMER TotalTimer OPTIONAL)
  256. /*++
  257. Routine Description:
  258. This routine runs through the various items that *could*
  259. have a reference to the current read/write. It try's to kill
  260. the reason. If it does succeed in killing the reason it
  261. will decrement the reference count on the irp.
  262. NOTE: This routine assumes that it is called with the cancel
  263. spin lock held.
  264. Arguments:
  265. CurrentOpIrp - Pointer to a pointer to current irp for the
  266. particular operation.
  267. IntervalTimer - Pointer to the interval timer for the operation.
  268. NOTE: This could be null.
  269. TotalTimer - Pointer to the total timer for the operation.
  270. NOTE: This could be null.
  271. Return Value:
  272. None.
  273. --*/
  274. {
  275. //
  276. // This routine is called with the cancel spin lock held
  277. // so we know only one thread of execution can be in here
  278. // at one time.
  279. //
  280. //
  281. // First we see if there is still a cancel routine. If
  282. // so then we can decrement the count by one.
  283. //
  284. if((*CurrentOpIrp)->CancelRoutine)
  285. {
  286. SERIAL_CLEAR_REFERENCE(*CurrentOpIrp, SERIAL_REF_CANCEL);
  287. IoSetCancelRoutine(*CurrentOpIrp, NULL);
  288. }
  289. if(IntervalTimer)
  290. {
  291. //
  292. // Try to cancel the operations interval timer. If the operation
  293. // returns true then the timer did have a reference to the
  294. // irp. Since we've canceled this timer that reference is
  295. // no longer valid and we can decrement the reference count.
  296. //
  297. // If the cancel returns false then this means either of two things:
  298. //
  299. // a) The timer has already fired.
  300. //
  301. // b) There never was an interval timer.
  302. //
  303. // In the case of "b" there is no need to decrement the reference
  304. // count since the "timer" never had a reference to it.
  305. //
  306. // In the case of "a", then the timer itself will be coming
  307. // along and decrement it's reference. Note that the caller
  308. // of this routine might actually be the this timer, but it
  309. // has already decremented the reference.
  310. //
  311. if(KeCancelTimer(IntervalTimer))
  312. {
  313. SERIAL_CLEAR_REFERENCE(*CurrentOpIrp, SERIAL_REF_INT_TIMER);
  314. }
  315. }
  316. if(TotalTimer)
  317. {
  318. //
  319. // Try to cancel the operations total timer. If the operation
  320. // returns true then the timer did have a reference to the
  321. // irp. Since we've canceled this timer that reference is
  322. // no longer valid and we can decrement the reference count.
  323. //
  324. // If the cancel returns false then this means either of two things:
  325. //
  326. // a) The timer has already fired.
  327. //
  328. // b) There never was an total timer.
  329. //
  330. // In the case of "b" there is no need to decrement the reference
  331. // count since the "timer" never had a reference to it.
  332. //
  333. // In the case of "a", then the timer itself will be coming
  334. // along and decrement it's reference. Note that the caller
  335. // of this routine might actually be the this timer, but it
  336. // has already decremented the reference.
  337. //
  338. if(KeCancelTimer(TotalTimer))
  339. {
  340. SERIAL_CLEAR_REFERENCE(*CurrentOpIrp, SERIAL_REF_TOTAL_TIMER);
  341. }
  342. }
  343. }
  344. NTSTATUS
  345. SerialStartOrQueue(IN PPORT_DEVICE_EXTENSION pPort,
  346. IN PIRP Irp,
  347. IN PLIST_ENTRY QueueToExamine,
  348. IN PIRP *CurrentOpIrp,
  349. IN PSERIAL_START_ROUTINE Starter)
  350. /*++
  351. Routine Description:
  352. This routine is used to either start or queue any requst
  353. that can be queued in the driver.
  354. Arguments:
  355. Extension - Points to the serial device extension.
  356. Irp - The irp to either queue or start. In either
  357. case the irp will be marked pending.
  358. QueueToExamine - The queue the irp will be place on if there
  359. is already an operation in progress.
  360. CurrentOpIrp - Pointer to a pointer to the irp the is current
  361. for the queue. The pointer pointed to will be
  362. set with to Irp if what CurrentOpIrp points to
  363. is NULL.
  364. Starter - The routine to call if the queue is empty.
  365. Return Value:
  366. This routine will return STATUS_PENDING if the queue is
  367. not empty. Otherwise, it will return the status returned
  368. from the starter routine (or cancel, if the cancel bit is
  369. on in the irp).
  370. --*/
  371. {
  372. KIRQL oldIrql;
  373. IoAcquireCancelSpinLock(&oldIrql);
  374. //
  375. // If this is a write irp then take the amount of characters
  376. // to write and add it to the count of characters to write.
  377. //
  378. if(IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_WRITE)
  379. {
  380. pPort->TotalCharsQueued += IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length;
  381. } else if((IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_DEVICE_CONTROL)
  382. && ((IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR)
  383. ||(IoGetCurrentIrpStackLocation(Irp) ->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER)))
  384. {
  385. pPort->TotalCharsQueued++;
  386. }
  387. if((IsListEmpty(QueueToExamine)) && !(*CurrentOpIrp))
  388. {
  389. //
  390. // There were no current operation. Mark this one as
  391. // current and start it up.
  392. //
  393. *CurrentOpIrp = Irp;
  394. IoReleaseCancelSpinLock(oldIrql);
  395. return Starter(pPort);
  396. }
  397. else
  398. {
  399. //
  400. // We don't know how long the irp will be in the
  401. // queue. So we need to handle cancel.
  402. //
  403. if(Irp->Cancel)
  404. {
  405. IoReleaseCancelSpinLock(oldIrql);
  406. Irp->IoStatus.Status = STATUS_CANCELLED;
  407. SerialDump(SERIRPPATH, ("Complete Irp: %x\n", Irp));
  408. SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
  409. IoCompleteRequest(Irp,0);
  410. return STATUS_CANCELLED;
  411. }
  412. else
  413. {
  414. Irp->IoStatus.Status = STATUS_PENDING;
  415. IoMarkIrpPending(Irp);
  416. InsertTailList(QueueToExamine, &Irp->Tail.Overlay.ListEntry);
  417. IoSetCancelRoutine(Irp, SerialCancelQueued);
  418. SpxIRPCounter(pPort, Irp, IRP_QUEUED); // Increment counter for performance stats.
  419. IoReleaseCancelSpinLock(oldIrql);
  420. return STATUS_PENDING;
  421. }
  422. }
  423. }
  424. VOID
  425. SerialCancelQueued(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  426. /*++
  427. Routine Description:
  428. This routine is used to cancel Irps that currently reside on
  429. a queue.
  430. Arguments:
  431. DeviceObject - Pointer to the device object for this device
  432. Irp - Pointer to the IRP to be canceled.
  433. Return Value:
  434. None.
  435. --*/
  436. {
  437. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  438. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
  439. Irp->IoStatus.Status = STATUS_CANCELLED;
  440. Irp->IoStatus.Information = 0;
  441. RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  442. SpxIRPCounter(pPort, Irp, IRP_DEQUEUED); // Decrement counter for performance stats.
  443. //
  444. // If this is a write irp then take the amount of characters
  445. // to write and subtract it from the count of characters to write.
  446. //
  447. if(irpSp->MajorFunction == IRP_MJ_WRITE)
  448. {
  449. pPort->TotalCharsQueued -= irpSp->Parameters.Write.Length;
  450. }
  451. else if(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
  452. {
  453. //
  454. // If it's an immediate then we need to decrement the
  455. // count of chars queued. If it's a resize then we
  456. // need to deallocate the pool that we're passing on
  457. // to the "resizing" routine.
  458. //
  459. if((irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR)
  460. || (irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER))
  461. {
  462. pPort->TotalCharsQueued--;
  463. }
  464. else if(irpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_SET_QUEUE_SIZE)
  465. {
  466. //
  467. // We shoved the pointer to the memory into the
  468. // the type 3 buffer pointer which we KNOW we
  469. // never use.
  470. //
  471. ASSERT(irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
  472. ExFreePool(irpSp->Parameters.DeviceIoControl.Type3InputBuffer);
  473. irpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  474. }
  475. }
  476. IoReleaseCancelSpinLock(Irp->CancelIrql);
  477. SerialDump(SERIRPPATH, ("Complete Irp: %x\n", Irp));
  478. SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
  479. IoCompleteRequest(Irp, IO_SERIAL_INCREMENT);
  480. }
  481. NTSTATUS
  482. SerialCompleteIfError(PDEVICE_OBJECT DeviceObject, PIRP Irp)
  483. /*++
  484. Routine Description:
  485. If the current irp is not an IOCTL_SERIAL_GET_COMMSTATUS request and
  486. there is an error and the application requested abort on errors,
  487. then cancel the irp.
  488. Arguments:
  489. DeviceObject - Pointer to the device object for this device
  490. Irp - Pointer to the IRP to test.
  491. Return Value:
  492. STATUS_SUCCESS or STATUS_CANCELLED.
  493. --*/
  494. {
  495. PPORT_DEVICE_EXTENSION pPort = DeviceObject->DeviceExtension;
  496. NTSTATUS status = STATUS_SUCCESS;
  497. if((pPort->HandFlow.ControlHandShake & SERIAL_ERROR_ABORT) && pPort->ErrorWord)
  498. {
  499. PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
  500. //
  501. // There is a current error in the driver. No requests should
  502. // come through except for the GET_COMMSTATUS.
  503. //
  504. if((irpSp->MajorFunction != IRP_MJ_DEVICE_CONTROL)
  505. || (irpSp->Parameters.DeviceIoControl.IoControlCode != IOCTL_SERIAL_GET_COMMSTATUS))
  506. {
  507. status = STATUS_CANCELLED;
  508. Irp->IoStatus.Status = STATUS_CANCELLED;
  509. Irp->IoStatus.Information = 0;
  510. SerialDump(SERIRPPATH, ("Complete Irp: %x\n",Irp));
  511. SpxIRPCounter(pPort, Irp, IRP_COMPLETED); // Increment counter for performance stats.
  512. IoCompleteRequest(Irp,0);
  513. }
  514. }
  515. return status;
  516. }