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.

417 lines
12 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1990 - 1998
  3. Module Name:
  4. lock.c
  5. Abstract:
  6. This is the NT SCSI port driver.
  7. Environment:
  8. kernel mode only
  9. Notes:
  10. This module is a driver dll for scsi miniports.
  11. Revision History:
  12. --*/
  13. #include "classp.h"
  14. #include "debug.h"
  15. LONG LockHighWatermark = 0;
  16. LONG LockLowWatermark = 0;
  17. LONG MaxLockedMinutes = 5;
  18. //
  19. // Structure used for tracking remove lock allocations in checked builds
  20. //
  21. typedef struct _REMOVE_TRACKING_BLOCK {
  22. struct _REMOVE_TRACKING_BLOCK *NextBlock;
  23. PVOID Tag;
  24. LARGE_INTEGER TimeLocked;
  25. PCSTR File;
  26. ULONG Line;
  27. } REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
  28. /*++////////////////////////////////////////////////////////////////////////////
  29. ClassAcquireRemoveLockEx()
  30. Routine Description:
  31. This routine is called to acquire the remove lock on the device object.
  32. While the lock is held, the caller can assume that no pending pnp REMOVE
  33. requests will be completed.
  34. The lock should be acquired immediately upon entering a dispatch routine.
  35. It should also be acquired before creating any new reference to the
  36. device object if there's a chance of releasing the reference before the
  37. new one is done.
  38. This routine will return TRUE if the lock was successfully acquired or
  39. FALSE if it cannot be because the device object has already been removed.
  40. Arguments:
  41. DeviceObject - the device object to lock
  42. Tag - Used for tracking lock allocation and release. If an irp is
  43. specified when acquiring the lock then the same Tag must be
  44. used to release the lock before the Tag is completed.
  45. Return Value:
  46. The value of the IsRemoved flag in the device extension. If this is
  47. non-zero then the device object has received a Remove irp and non-cleanup
  48. IRP's should fail.
  49. If the value is REMOVE_COMPLETE, the caller should not even release the
  50. lock.
  51. --*/
  52. ULONG
  53. ClassAcquireRemoveLockEx(
  54. IN PDEVICE_OBJECT DeviceObject,
  55. IN OPTIONAL PVOID Tag,
  56. IN PCSTR File,
  57. IN ULONG Line
  58. )
  59. {
  60. PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  61. LONG lockValue;
  62. //
  63. // Grab the remove lock
  64. //
  65. lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
  66. #if DBG
  67. DebugPrint((ClassDebugRemoveLock, "ClassAcquireRemoveLock: "
  68. "Acquired for Object %p & irp %p - count is %d\n",
  69. DeviceObject, Tag, lockValue));
  70. ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
  71. (lockValue > 0));
  72. ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
  73. ((LockHighWatermark == 0) ||
  74. (lockValue != LockHighWatermark)));
  75. if (commonExtension->IsRemoved != REMOVE_COMPLETE){
  76. PREMOVE_TRACKING_BLOCK trackingBlock;
  77. trackingBlock = ExAllocatePool(NonPagedPool,
  78. sizeof(REMOVE_TRACKING_BLOCK));
  79. if(trackingBlock == NULL) {
  80. KIRQL oldIrql;
  81. KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
  82. &oldIrql);
  83. commonExtension->RemoveTrackingUntrackedCount++;
  84. DebugPrint((ClassDebugWarning, ">>>>>ClassAcquireRemoveLock: "
  85. "Cannot track Tag %p - currently %d untracked requsts\n",
  86. Tag, commonExtension->RemoveTrackingUntrackedCount));
  87. KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
  88. oldIrql);
  89. }
  90. else {
  91. PREMOVE_TRACKING_BLOCK *removeTrackingList =
  92. &((PREMOVE_TRACKING_BLOCK) commonExtension->RemoveTrackingList);
  93. KIRQL oldIrql;
  94. trackingBlock->Tag = Tag;
  95. trackingBlock->File = File;
  96. trackingBlock->Line = Line;
  97. KeQueryTickCount((&trackingBlock->TimeLocked));
  98. KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
  99. &oldIrql);
  100. while(*removeTrackingList != NULL) {
  101. if((*removeTrackingList)->Tag > Tag) {
  102. break;
  103. }
  104. if((*removeTrackingList)->Tag == Tag) {
  105. DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
  106. "already tracking Tag %p\n", Tag));
  107. DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
  108. "acquired in file %s on line %d\n",
  109. (*removeTrackingList)->File,
  110. (*removeTrackingList)->Line));
  111. ASSERT(FALSE);
  112. }
  113. removeTrackingList = &((*removeTrackingList)->NextBlock);
  114. }
  115. trackingBlock->NextBlock = *removeTrackingList;
  116. *removeTrackingList = trackingBlock;
  117. KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
  118. oldIrql);
  119. }
  120. }
  121. #endif
  122. return (commonExtension->IsRemoved);
  123. }
  124. /*++////////////////////////////////////////////////////////////////////////////
  125. ClassReleaseRemoveLock()
  126. Routine Description:
  127. This routine is called to release the remove lock on the device object. It
  128. must be called when finished using a previously locked reference to the
  129. device object. If an Tag was specified when acquiring the lock then the
  130. same Tag must be specified when releasing the lock.
  131. When the lock count reduces to zero, this routine will signal the waiting
  132. remove Tag to delete the device object. As a result the DeviceObject
  133. pointer should not be used again once the lock has been released.
  134. Arguments:
  135. DeviceObject - the device object to lock
  136. Tag - The irp (if any) specified when acquiring the lock. This is used
  137. for lock tracking purposes
  138. Return Value:
  139. none
  140. --*/
  141. VOID
  142. ClassReleaseRemoveLock(
  143. IN PDEVICE_OBJECT DeviceObject,
  144. IN OPTIONAL PIRP Tag
  145. )
  146. {
  147. PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  148. LONG lockValue;
  149. #if DBG
  150. PREMOVE_TRACKING_BLOCK *listEntry =
  151. &((PREMOVE_TRACKING_BLOCK) commonExtension->RemoveTrackingList);
  152. BOOLEAN found = FALSE;
  153. LONGLONG maxCount;
  154. BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
  155. KIRQL oldIrql;
  156. if(isRemoved) {
  157. DBGTRAP(("ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
  158. InterlockedDecrement(&(commonExtension->RemoveLock));
  159. return;
  160. }
  161. //
  162. // Check the tick count and make sure this thing hasn't been locked
  163. // for more than MaxLockedMinutes.
  164. //
  165. maxCount = KeQueryTimeIncrement() * 10; // microseconds
  166. maxCount *= 1000; // milliseconds
  167. maxCount *= 1000; // seconds
  168. maxCount *= 60; // minutes
  169. maxCount *= MaxLockedMinutes;
  170. DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
  171. "maxCount = %0I64x\n", maxCount));
  172. KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
  173. &oldIrql);
  174. while(*listEntry != NULL) {
  175. PREMOVE_TRACKING_BLOCK block;
  176. LARGE_INTEGER difference;
  177. block = *listEntry;
  178. KeQueryTickCount((&difference));
  179. difference.QuadPart -= block->TimeLocked.QuadPart;
  180. DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
  181. "Object %p (tag %p) locked for %I64d ticks\n",
  182. DeviceObject, block->Tag, difference.QuadPart));
  183. if(difference.QuadPart >= maxCount) {
  184. DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
  185. "Object %p (tag %p) locked for %I64d ticks - TOO LONG\n",
  186. DeviceObject, block->Tag, difference.QuadPart));
  187. DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
  188. "Lock acquired in file %s on line %d\n",
  189. block->File, block->Line));
  190. ASSERT(FALSE);
  191. }
  192. if((found == FALSE) && ((*listEntry)->Tag == Tag)) {
  193. *listEntry = block->NextBlock;
  194. ExFreePool(block);
  195. found = TRUE;
  196. } else {
  197. listEntry = &((*listEntry)->NextBlock);
  198. }
  199. }
  200. if(!found) {
  201. if(commonExtension->RemoveTrackingUntrackedCount == 0) {
  202. DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
  203. "Couldn't find Tag %p in the lock tracking list\n",
  204. Tag));
  205. ASSERT(FALSE);
  206. } else {
  207. DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
  208. "Couldn't find Tag %p in the lock tracking list - "
  209. "may be one of the %d untracked requests still "
  210. "outstanding\n",
  211. Tag,
  212. commonExtension->RemoveTrackingUntrackedCount));
  213. commonExtension->RemoveTrackingUntrackedCount--;
  214. ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
  215. }
  216. }
  217. KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
  218. oldIrql);
  219. #endif
  220. lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
  221. DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
  222. "Released for Object %p & irp %p - count is %d\n",
  223. DeviceObject, Tag, lockValue));
  224. ASSERT(lockValue >= 0);
  225. ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
  226. ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
  227. if(lockValue == 0) {
  228. ASSERT(commonExtension->IsRemoved);
  229. //
  230. // The device needs to be removed. Signal the remove event
  231. // that it's safe to go ahead.
  232. //
  233. DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
  234. "Release for object %p & irp %p caused lock to go to zero\n",
  235. DeviceObject, Tag));
  236. KeSetEvent(&commonExtension->RemoveEvent,
  237. IO_NO_INCREMENT,
  238. FALSE);
  239. }
  240. return;
  241. }
  242. /*++////////////////////////////////////////////////////////////////////////////
  243. ClassCompleteRequest()
  244. Routine Description:
  245. This routine is a wrapper around (and should be used instead of)
  246. IoCompleteRequest. It is used primarily for debugging purposes.
  247. The routine will assert if the Irp being completed is still holding
  248. the release lock.
  249. Arguments:
  250. DeviceObject - the device object that was handling this request
  251. Irp - the irp to be completed by IoCompleteRequest
  252. PriorityBoost - the priority boost to pass to IoCompleteRequest
  253. Return Value:
  254. none
  255. --*/
  256. VOID
  257. ClassCompleteRequest(
  258. IN PDEVICE_OBJECT DeviceObject,
  259. IN PIRP Irp,
  260. IN CCHAR PriorityBoost
  261. )
  262. {
  263. #if DBG
  264. PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
  265. PREMOVE_TRACKING_BLOCK *listEntry =
  266. &((PREMOVE_TRACKING_BLOCK) commonExtension->RemoveTrackingList);
  267. KIRQL oldIrql;
  268. KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
  269. &oldIrql);
  270. while(*listEntry != NULL) {
  271. if((*listEntry)->Tag == Irp) {
  272. break;
  273. }
  274. listEntry = &((*listEntry)->NextBlock);
  275. }
  276. if(*listEntry != NULL) {
  277. DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
  278. "Irp %p completed while still holding the remove lock\n",
  279. Irp));
  280. DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
  281. "Lock acquired in file %s on line %d\n",
  282. (*listEntry)->File, (*listEntry)->Line));
  283. ASSERT(FALSE);
  284. }
  285. KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
  286. #endif
  287. IoCompleteRequest(Irp, PriorityBoost);
  288. return;
  289. } // end ClassCompleteRequest()