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.

470 lines
12 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. remlock.c
  5. Abstract:
  6. This code implements remove locks.
  7. Authors:
  8. Peter Wieland
  9. Kenneth Ray
  10. Environment:
  11. kernel mode only
  12. Notes:
  13. Revision History:
  14. --*/
  15. #include "pnpmgrp.h"
  16. #pragma hdrstop
  17. #include <remlock.h>
  18. #pragma alloc_text(PAGE, IoInitializeRemoveLockEx)
  19. #pragma alloc_text(PAGE, IoReleaseRemoveLockAndWaitEx)
  20. #define MinutesToTicks(x) \
  21. (ULONGLONG) KeQueryTimeIncrement() * \
  22. 10 * \
  23. 1000 * \
  24. 1000 * \
  25. 60 * \
  26. x
  27. // 10 -> microseconds, 1000 -> miliseconds, 1000 -> seconds, 60 -> minutes
  28. typedef struct _IO_PRIVATE_REMOVE_LOCK {
  29. IO_REMOVE_LOCK_COMMON_BLOCK Common;
  30. IO_REMOVE_LOCK_DBG_BLOCK Dbg;
  31. } IO_PRIVATE_REMOVE_LOCK, *PIO_PRIVATE_REMOVE_LOCK;
  32. #define FREESIZE sizeof (IO_REMOVE_LOCK_COMMON_BLOCK)
  33. #define CHECKEDSIZE sizeof (IO_PRIVATE_REMOVE_LOCK)
  34. NTSYSAPI
  35. VOID
  36. NTAPI
  37. IoInitializeRemoveLockEx(
  38. IN PIO_REMOVE_LOCK PublicLock,
  39. IN ULONG AllocateTag, // Used only on checked kernels
  40. IN ULONG MaxLockedMinutes, // Used only on checked kernels
  41. IN ULONG HighWatermark, // Used only on checked kernels
  42. IN ULONG RemlockSize // are we checked or free
  43. )
  44. /*++
  45. Routine Description:
  46. This routine is called to initialize the remove lock for a device object.
  47. --*/
  48. {
  49. PIO_PRIVATE_REMOVE_LOCK Lock = (PIO_PRIVATE_REMOVE_LOCK) PublicLock;
  50. PAGED_CODE ();
  51. ASSERTMSG("HighWatermark too large, use 0 if you dont know a reasonable value",
  52. (HighWatermark < MAXLONG));
  53. if (Lock) {
  54. switch (RemlockSize) {
  55. case CHECKEDSIZE:
  56. Lock->Dbg.Signature = IO_REMOVE_LOCK_SIG;
  57. Lock->Dbg.HighWatermark = HighWatermark;
  58. Lock->Dbg.MaxLockedTicks = MinutesToTicks (MaxLockedMinutes);
  59. Lock->Dbg.AllocateTag = AllocateTag;
  60. KeInitializeSpinLock (&Lock->Dbg.Spin);
  61. Lock->Dbg.LowMemoryCount = 0;
  62. Lock->Dbg.Blocks = NULL;
  63. //
  64. // fall through
  65. //
  66. case FREESIZE:
  67. Lock->Common.Removed = FALSE;
  68. Lock->Common.IoCount = 1;
  69. KeInitializeEvent(&Lock->Common.RemoveEvent,
  70. SynchronizationEvent,
  71. FALSE);
  72. break;
  73. default:
  74. break;
  75. }
  76. }
  77. }
  78. NTSYSAPI
  79. NTSTATUS
  80. NTAPI
  81. IoAcquireRemoveLockEx(
  82. IN PIO_REMOVE_LOCK PublicLock,
  83. IN OPTIONAL PVOID Tag,
  84. IN PCSTR File,
  85. IN ULONG Line,
  86. IN ULONG RemlockSize // are we checked or free
  87. )
  88. /*++
  89. Routine Description:
  90. This routine is called to acquire the remove lock for a device object.
  91. While the lock is held, the caller can assume that no pending pnp REMOVE
  92. requests will be completed.
  93. The lock should be acquired immediately upon entering a dispatch routine.
  94. It should also be acquired before creating any new reference to the
  95. device object if there's a chance of releasing the reference before the
  96. new one is done.
  97. Arguments:
  98. RemoveLock - A pointer to an initialized REMOVE_LOCK structure.
  99. Tag - Used for tracking lock allocation and release. If an irp is
  100. specified when acquiring the lock then the same Tag must be
  101. used to release the lock before the Tag is completed.
  102. File - set to __FILE__ as the location in the code where the lock was taken.
  103. Line - set to __LINE__.
  104. Return Value:
  105. Returns whether or not the remove lock was obtained.
  106. If successful the caller should continue with work calling
  107. IoReleaseRemoveLock when finished.
  108. If not successful the lock was not obtained. The caller should abort the
  109. work but not call IoReleaseRemoveLock.
  110. --*/
  111. {
  112. PIO_PRIVATE_REMOVE_LOCK Lock = (PIO_PRIVATE_REMOVE_LOCK) PublicLock;
  113. LONG lockValue;
  114. NTSTATUS status;
  115. PIO_REMOVE_LOCK_TRACKING_BLOCK trackingBlock;
  116. //
  117. // Grab the remove lock
  118. //
  119. lockValue = InterlockedIncrement(&Lock->Common.IoCount);
  120. ASSERTMSG("IoAcquireRemoveLock - lock value was negative : ",
  121. (lockValue > 0));
  122. if (! Lock->Common.Removed) {
  123. switch (RemlockSize) {
  124. case CHECKEDSIZE:
  125. ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
  126. ((0 == Lock->Dbg.HighWatermark) ||
  127. ((ULONG)lockValue <= Lock->Dbg.HighWatermark)));
  128. trackingBlock = ExAllocatePoolWithTag(
  129. NonPagedPool,
  130. sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK),
  131. Lock->Dbg.AllocateTag);
  132. if (NULL == trackingBlock) {
  133. // ASSERTMSG ("insufficient resources", FALSE);
  134. InterlockedIncrement (& Lock->Dbg.LowMemoryCount);
  135. //
  136. // Let the acquire go through but without adding the
  137. // tracking block.
  138. // When we are later releasing the lock, but the tracking
  139. // block does not exist, deduct from this value to see if the
  140. // release was still valuable.
  141. //
  142. } else {
  143. KIRQL oldIrql;
  144. RtlZeroMemory (trackingBlock,
  145. sizeof (IO_REMOVE_LOCK_TRACKING_BLOCK));
  146. trackingBlock->Tag = Tag;
  147. trackingBlock->File = File;
  148. trackingBlock->Line = Line;
  149. KeQueryTickCount(&trackingBlock->TimeLocked);
  150. ExAcquireSpinLock (&Lock->Dbg.Spin, &oldIrql);
  151. trackingBlock->Link = Lock->Dbg.Blocks;
  152. Lock->Dbg.Blocks = trackingBlock;
  153. ExReleaseSpinLock(&Lock->Dbg.Spin, oldIrql);
  154. }
  155. break;
  156. case FREESIZE:
  157. break;
  158. default:
  159. break;
  160. }
  161. status = STATUS_SUCCESS;
  162. } else {
  163. if (0 == InterlockedDecrement (&Lock->Common.IoCount)) {
  164. KeSetEvent (&Lock->Common.RemoveEvent, 0, FALSE);
  165. }
  166. status = STATUS_DELETE_PENDING;
  167. }
  168. return status;
  169. }
  170. NTSYSAPI
  171. VOID
  172. NTAPI
  173. IoReleaseRemoveLockEx(
  174. IN PIO_REMOVE_LOCK PublicLock,
  175. IN PVOID Tag,
  176. IN ULONG RemlockSize // are we checked or free
  177. )
  178. /*++
  179. Routine Description:
  180. This routine is called to release the remove lock on the device object. It
  181. must be called when finished using a previously locked reference to the
  182. device object. If an Tag was specified when acquiring the lock then the
  183. same Tag must be specified when releasing the lock.
  184. When the lock count reduces to zero, this routine will signal the waiting
  185. event to release the waiting thread deleting the device object protected
  186. by this lock.
  187. Arguments:
  188. DeviceObject - the device object to lock
  189. Tag - The tag (if any) specified when acquiring the lock. This is used
  190. for lock tracking purposes
  191. Return Value:
  192. none
  193. --*/
  194. {
  195. PIO_PRIVATE_REMOVE_LOCK Lock = (PIO_PRIVATE_REMOVE_LOCK) PublicLock;
  196. LONG lockValue;
  197. KIRQL oldIrql;
  198. LARGE_INTEGER ticks;
  199. LONGLONG difference;
  200. BOOLEAN found;
  201. PIO_REMOVE_LOCK_TRACKING_BLOCK last;
  202. PIO_REMOVE_LOCK_TRACKING_BLOCK current;
  203. switch (RemlockSize) {
  204. case CHECKEDSIZE:
  205. //
  206. // Check the tick count and make sure this thing hasn't been locked
  207. // for more than MaxLockedMinutes.
  208. //
  209. found = FALSE;
  210. ExAcquireSpinLock(&Lock->Dbg.Spin, &oldIrql);
  211. last = (Lock->Dbg.Blocks);
  212. current = last;
  213. KeQueryTickCount((&ticks));
  214. while (NULL != current) {
  215. if (Lock->Dbg.MaxLockedTicks) {
  216. difference = ticks.QuadPart - current->TimeLocked.QuadPart;
  217. if (Lock->Dbg.MaxLockedTicks < difference) {
  218. IopDbgPrint(( IOP_ERROR_LEVEL,
  219. "IoReleaseRemoveLock: Lock %#08lx (tag %#08lx) "
  220. "locked for %I64d ticks - TOO LONG\n",
  221. Lock,
  222. current->Tag,
  223. difference));
  224. IopDbgPrint(( IOP_ERROR_LEVEL,
  225. "IoReleaseRemoveLock: Lock acquired in file "
  226. "%s on line %d\n",
  227. current->File,
  228. current->Line));
  229. ASSERT(FALSE);
  230. }
  231. }
  232. if ((!found) && (current->Tag == Tag)) {
  233. found = TRUE;
  234. if (current == Lock->Dbg.Blocks) {
  235. Lock->Dbg.Blocks = current->Link;
  236. ExFreePool (current);
  237. current = Lock->Dbg.Blocks;
  238. } else {
  239. last->Link = current->Link;
  240. ExFreePool (current);
  241. current = last->Link;
  242. }
  243. continue;
  244. }
  245. last = current;
  246. current = current->Link;
  247. }
  248. ExReleaseSpinLock(&Lock->Dbg.Spin, oldIrql);
  249. if (!found) {
  250. //
  251. // Check to see if we have any credits in our Low Memory Count.
  252. // In this fassion we can tell if we have acquired any locks without
  253. // the memory for adding tracking blocks.
  254. //
  255. if (InterlockedDecrement (& Lock->Dbg.LowMemoryCount) < 0) {
  256. //
  257. // We have just released a lock that neither had a corresponding
  258. // tracking block, nor a credit in LowMemoryCount.
  259. //
  260. InterlockedIncrement (& Lock->Dbg.LowMemoryCount);
  261. IopDbgPrint (( IOP_ERROR_LEVEL,
  262. "IoReleaseRemoveLock: Couldn't find Tag %#08lx "
  263. "in the lock tracking list\n",
  264. Tag));
  265. ASSERT(FALSE);
  266. }
  267. }
  268. break;
  269. case FREESIZE:
  270. break;
  271. default:
  272. break;
  273. }
  274. lockValue = InterlockedDecrement(&Lock->Common.IoCount);
  275. ASSERT(0 <= lockValue);
  276. if (0 == lockValue) {
  277. ASSERT (Lock->Common.Removed);
  278. //
  279. // The device needs to be removed. Signal the remove event
  280. // that it's safe to go ahead.
  281. //
  282. KeSetEvent(&Lock->Common.RemoveEvent,
  283. IO_NO_INCREMENT,
  284. FALSE);
  285. }
  286. return;
  287. }
  288. NTSYSAPI
  289. VOID
  290. NTAPI
  291. IoReleaseRemoveLockAndWaitEx (
  292. IN PIO_REMOVE_LOCK PublicLock,
  293. IN PVOID Tag,
  294. IN ULONG RemlockSize // are we checked or free
  295. )
  296. /*++
  297. Routine Description:
  298. This routine is called when the client would like to delete the remove-
  299. locked resource.
  300. This routine will block until all the remove locks have completed.
  301. This routine MUST be called after acquiring once more the lock.
  302. Arguments:
  303. RemoveLock -
  304. Return Value:
  305. none
  306. --*/
  307. {
  308. PIO_PRIVATE_REMOVE_LOCK Lock = (PIO_PRIVATE_REMOVE_LOCK) PublicLock;
  309. LONG ioCount;
  310. PAGED_CODE ();
  311. Lock->Common.Removed = TRUE;
  312. ioCount = InterlockedDecrement (&Lock->Common.IoCount);
  313. ASSERT (0 < ioCount);
  314. if (0 < InterlockedDecrement (&Lock->Common.IoCount)) {
  315. KeWaitForSingleObject (&Lock->Common.RemoveEvent,
  316. Executive,
  317. KernelMode,
  318. FALSE,
  319. NULL);
  320. }
  321. switch (RemlockSize) {
  322. case CHECKEDSIZE:
  323. ASSERT (Lock->Dbg.Blocks);
  324. if (Tag != Lock->Dbg.Blocks->Tag) {
  325. IopDbgPrint (( IOP_ERROR_LEVEL,
  326. "IoRelaseRemoveLockAndWait last tag invalid %x %x\n",
  327. Tag,
  328. Lock->Dbg.Blocks->Tag));
  329. ASSERT (Tag != Lock->Dbg.Blocks->Tag);
  330. }
  331. ExFreePool (Lock->Dbg.Blocks);
  332. break;
  333. case FREESIZE:
  334. break;
  335. default:
  336. break;
  337. }
  338. }