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.

352 lines
8.6 KiB

  1. /*++
  2. Copyright (C) 1991-5 Microsoft Corporation
  3. Module Name:
  4. overlap.cxx
  5. Abstract:
  6. This module contains code specific to the overlapped io manager.
  7. The purpose of this module is to help serialize io that overlaps with
  8. each other. This class is used by stripes with parity to help prevent
  9. corruption caused by race conditions when computing parity.
  10. Author:
  11. Norbert Kusters 2-Feb-1995
  12. Environment:
  13. kernel mode only
  14. Notes:
  15. Revision History:
  16. --*/
  17. extern "C" {
  18. #include <ntddk.h>
  19. }
  20. #include <ftdisk.h>
  21. #ifdef ALLOC_PRAGMA
  22. #pragma code_seg("PAGE")
  23. #endif
  24. NTSTATUS
  25. OVERLAPPED_IO_MANAGER::Initialize(
  26. IN ULONG BucketSize
  27. )
  28. /*++
  29. Routine Description:
  30. This routine initializes an overlapped io manager.
  31. Arguments:
  32. BucketSize - Supplies the bucket size. Any I/O to this class may
  33. not span more than one bucket. In the case of stripes
  34. with parity, the bucket size is the stripe size. A
  35. bucket size of 0 means that there are no buckets by
  36. which requests would be partitioned (alt. an "infinite"
  37. bucket size).
  38. Return Value:
  39. NTSTATUS
  40. --*/
  41. {
  42. ULONG i;
  43. _numQueues = 256;
  44. _bucketSize = BucketSize;
  45. if (!_bucketSize) {
  46. _bucketSize = STRIPE_SIZE;
  47. _numQueues = 1;
  48. }
  49. _spinLock = (PKSPIN_LOCK)
  50. ExAllocatePool(NonPagedPool, _numQueues*sizeof(KSPIN_LOCK));
  51. if (!_spinLock) {
  52. return STATUS_INSUFFICIENT_RESOURCES;
  53. }
  54. _ioQueue = (PLIST_ENTRY)
  55. ExAllocatePool(NonPagedPool, _numQueues*sizeof(LIST_ENTRY));
  56. if (!_ioQueue) {
  57. ExFreePool(_spinLock);
  58. _spinLock = NULL;
  59. return STATUS_INSUFFICIENT_RESOURCES;
  60. }
  61. for (i = 0; i < _numQueues; i++) {
  62. KeInitializeSpinLock(&_spinLock[i]);
  63. InitializeListHead(&_ioQueue[i]);
  64. }
  65. return STATUS_SUCCESS;
  66. }
  67. #ifdef ALLOC_PRAGMA
  68. #pragma code_seg("PAGELK")
  69. #endif
  70. VOID
  71. OVERLAPPED_IO_MANAGER::AcquireIoRegion(
  72. IN OUT POVERLAP_TP TransferPacket,
  73. IN BOOLEAN AllMembers
  74. )
  75. /*++
  76. Routine Description:
  77. This routine queues the given transfer packet for its desired region.
  78. The transfer packet completion routine will be called when the region
  79. is free. 'ReleaseIoRegion' must be called to allow other transfer packets
  80. to pass for a region that overlaps with one that is acquired.
  81. Arguments:
  82. TransferPacket - Supplies the IO region and completion routine.
  83. AllMembers - Supplies whether or not to allocate this region
  84. on all members.
  85. Return Value:
  86. None.
  87. --*/
  88. {
  89. ULONG queueNumber;
  90. PLIST_ENTRY q;
  91. PKSPIN_LOCK spin;
  92. KIRQL irql;
  93. PLIST_ENTRY l;
  94. POVERLAP_TP p;
  95. ASSERT(!TransferPacket->InQueue);
  96. TransferPacket->AllMembers = AllMembers;
  97. TransferPacket->OverlappedIoManager = this;
  98. // Search the queue for a request that overlaps with this one.
  99. // If there is no overlap then call the completion routine
  100. // for this transfer packet. Either way, queue this transfer
  101. // packet at the end of the queue.
  102. queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
  103. q = &_ioQueue[queueNumber];
  104. spin = &_spinLock[queueNumber];
  105. KeAcquireSpinLock(spin, &irql);
  106. for (l = q->Blink; l != q; l = l->Blink) {
  107. p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
  108. if ((TransferPacket->AllMembers || p->AllMembers ||
  109. p->TargetVolume == TransferPacket->TargetVolume) &&
  110. p->Offset < TransferPacket->Offset + TransferPacket->Length &&
  111. TransferPacket->Offset < p->Offset + p->Length) {
  112. break;
  113. }
  114. }
  115. InsertTailList(q, &TransferPacket->OverlapQueue);
  116. TransferPacket->InQueue = TRUE;
  117. KeReleaseSpinLock(spin, irql);
  118. if (l == q) {
  119. TransferPacket->CompletionRoutine(TransferPacket);
  120. } else {
  121. // DbgPrint("Overlap: Transfer packet %x stuck in behind %x\n", TransferPacket, p);
  122. }
  123. }
  124. VOID
  125. OVERLAPPED_IO_MANAGER::ReleaseIoRegion(
  126. IN OUT POVERLAP_TP TransferPacket
  127. )
  128. /*++
  129. Routine Description:
  130. This routine releases the IO region held by this packet and
  131. wakes up waiting transfer packets.
  132. Arguments:
  133. TransferPacket - Supplies the TransferPacket whose region is to be
  134. released.
  135. Return Value:
  136. None.
  137. --*/
  138. {
  139. ULONG queueNumber;
  140. PLIST_ENTRY q;
  141. PKSPIN_LOCK spin;
  142. LIST_ENTRY completionList;
  143. KIRQL irql;
  144. PLIST_ENTRY l, ll;
  145. POVERLAP_TP p, pp;
  146. if (!TransferPacket->InQueue) {
  147. return;
  148. }
  149. queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
  150. q = &_ioQueue[queueNumber];
  151. spin = &_spinLock[queueNumber];
  152. InitializeListHead(&completionList);
  153. KeAcquireSpinLock(spin, &irql);
  154. l = TransferPacket->OverlapQueue.Flink;
  155. RemoveEntryList(&TransferPacket->OverlapQueue);
  156. TransferPacket->InQueue = FALSE;
  157. for (; l != q; l = l->Flink) {
  158. p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
  159. if ((TransferPacket->AllMembers || p->AllMembers ||
  160. p->TargetVolume == TransferPacket->TargetVolume) &&
  161. TransferPacket->Offset < p->Offset + p->Length &&
  162. p->Offset < TransferPacket->Offset + TransferPacket->Length) {
  163. // This is a candidate for allocation, make sure that it
  164. // is clear to run by checking for any other contention.
  165. for (ll = p->OverlapQueue.Blink; ll != q; ll = ll->Blink) {
  166. pp = CONTAINING_RECORD(ll, OVERLAP_TP, OverlapQueue);
  167. if ((p->AllMembers || pp->AllMembers ||
  168. p->TargetVolume == pp->TargetVolume) &&
  169. pp->Offset < p->Offset + p->Length &&
  170. p->Offset < pp->Offset + pp->Length) {
  171. break;
  172. }
  173. }
  174. if (ll == q) {
  175. InsertTailList(&completionList, &p->CompletionList);
  176. // DbgPrint("Overlap: Releasing packet %x that was behind %x\n", p, TransferPacket);
  177. }
  178. }
  179. }
  180. KeReleaseSpinLock(spin, irql);
  181. while (!IsListEmpty(&completionList)) {
  182. l = RemoveHeadList(&completionList);
  183. p = CONTAINING_RECORD(l, OVERLAP_TP, CompletionList);
  184. p->CompletionRoutine(p);
  185. }
  186. }
  187. VOID
  188. OVERLAPPED_IO_MANAGER::PromoteToAllMembers(
  189. IN OUT POVERLAP_TP TransferPacket
  190. )
  191. /*++
  192. Routine Description:
  193. This routine promotes an already allocated transfer packet to
  194. all members.
  195. Arguments:
  196. TransferPacket - Supplies a transfer packet that is already in
  197. the overlapped io queue.
  198. Return Value:
  199. None.
  200. --*/
  201. {
  202. ULONG queueNumber;
  203. PLIST_ENTRY q;
  204. PKSPIN_LOCK spin;
  205. KIRQL irql;
  206. PLIST_ENTRY l;
  207. POVERLAP_TP p;
  208. if (TransferPacket->AllMembers) {
  209. TransferPacket->CompletionRoutine(TransferPacket);
  210. return;
  211. }
  212. // DbgPrint("Overlap: Promoting %x to all members.\n", TransferPacket);
  213. queueNumber = (ULONG) ((TransferPacket->Offset/_bucketSize)%_numQueues);
  214. q = &_ioQueue[queueNumber];
  215. spin = &_spinLock[queueNumber];
  216. KeAcquireSpinLock(spin, &irql);
  217. ASSERT(!TransferPacket->AllMembers);
  218. TransferPacket->AllMembers = TRUE;
  219. for (l = q->Blink; l != &TransferPacket->OverlapQueue; l = l->Blink) {
  220. p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
  221. if (!p->AllMembers &&
  222. p->TargetVolume != TransferPacket->TargetVolume &&
  223. TransferPacket->Offset < p->Offset + p->Length &&
  224. p->Offset < TransferPacket->Offset + TransferPacket->Length) {
  225. break;
  226. }
  227. }
  228. if (l != &TransferPacket->OverlapQueue) {
  229. RemoveEntryList(&TransferPacket->OverlapQueue);
  230. InsertHeadList(l, &TransferPacket->OverlapQueue);
  231. // DbgPrint("Moving behind %x.\n", p);
  232. }
  233. for (l = TransferPacket->OverlapQueue.Blink; l != q; l = l->Blink) {
  234. p = CONTAINING_RECORD(l, OVERLAP_TP, OverlapQueue);
  235. if (TransferPacket->Offset < p->Offset + p->Length &&
  236. p->Offset < TransferPacket->Offset + TransferPacket->Length) {
  237. break;
  238. }
  239. }
  240. KeReleaseSpinLock(spin, irql);
  241. if (l == q) {
  242. TransferPacket->CompletionRoutine(TransferPacket);
  243. } else {
  244. // DbgPrint("Overlap: Waiting for %x.\n", p);
  245. }
  246. }
  247. OVERLAPPED_IO_MANAGER::~OVERLAPPED_IO_MANAGER(
  248. )
  249. {
  250. if (_spinLock) {
  251. ExFreePool(_spinLock);
  252. _spinLock = NULL;
  253. }
  254. if (_ioQueue) {
  255. ExFreePool(_ioQueue);
  256. _ioQueue = NULL;
  257. }
  258. }