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.

488 lines
14 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. rundown.c
  5. Abstract:
  6. This module houses routine that do safe rundown of data stuctures.
  7. The basic principle of these routines is to allow fast protection of a data structure that is torn down
  8. by a single thread. Threads wishing to access the data structure attempt to obtain rundown protection via
  9. calling ExAcquireRundownProtection. If this function returns TRUE then accesses are safe until the protected
  10. thread calls ExReleaseRundownProtection. The single teardown thread calls ExWaitForRundownProtectionRelease
  11. to mark the rundown structure as being run down and the call will return once all protected threads have
  12. released their protection references.
  13. Rundown protection is not a lock. Multiple threads may gain rundown protection at the same time.
  14. The rundown structure has the following format:
  15. Bottom bit set : This is a pointer to a rundown wait block (aligned on at least a word boundary)
  16. Bottom bit clear : This is a count of the total number of accessors multiplied by 2 granted rundown protection.
  17. Author:
  18. Neill Clift (NeillC) 18-Apr-2000
  19. Revision History:
  20. --*/
  21. #include "exp.h"
  22. #pragma hdrstop
  23. #ifdef ALLOC_PRAGMA
  24. // These routines are now marked as NONPAGED because they are being used
  25. // in the paging path by file system filters.
  26. //#pragma alloc_text(PAGE, ExfAcquireRundownProtection)
  27. //#pragma alloc_text(PAGE, ExfReleaseRundownProtection)
  28. //#pragma alloc_text(PAGE, ExAcquireRundownProtectionEx)
  29. //#pragma alloc_text(PAGE, ExReleaseRundownProtectionEx)
  30. #pragma alloc_text(PAGE, ExfWaitForRundownProtectionRelease)
  31. #pragma alloc_text(PAGE, ExfReInitializeRundownProtection)
  32. #pragma alloc_text(PAGE, ExfInitializeRundownProtection)
  33. #pragma alloc_text(PAGE, ExfRundownCompleted)
  34. #endif
  35. //
  36. // This is a block held on the local stack of the rundown thread.
  37. //
  38. typedef struct _EX_RUNDOWN_WAIT_BLOCK {
  39. ULONG Count;
  40. KEVENT WakeEvent;
  41. } EX_RUNDOWN_WAIT_BLOCK, *PEX_RUNDOWN_WAIT_BLOCK;
  42. NTKERNELAPI
  43. VOID
  44. FASTCALL
  45. ExfInitializeRundownProtection (
  46. IN PEX_RUNDOWN_REF RunRef
  47. )
  48. /*++
  49. Routine Description:
  50. Initialize rundown protection structure
  51. Arguments:
  52. RunRef - Rundown block to be referenced
  53. Return Value:
  54. None
  55. --*/
  56. {
  57. RunRef->Count = 0;
  58. }
  59. NTKERNELAPI
  60. VOID
  61. FASTCALL
  62. ExfReInitializeRundownProtection (
  63. IN PEX_RUNDOWN_REF RunRef
  64. )
  65. /*++
  66. Routine Description:
  67. Reinitialize rundown protection structure after its been rundown
  68. Arguments:
  69. RunRef - Rundown block to be referenced
  70. Return Value:
  71. None
  72. --*/
  73. {
  74. PAGED_CODE ();
  75. ASSERT ((RunRef->Count&EX_RUNDOWN_ACTIVE) != 0);
  76. InterlockedExchangePointer (&RunRef->Ptr, NULL);
  77. }
  78. NTKERNELAPI
  79. VOID
  80. FASTCALL
  81. ExfRundownCompleted (
  82. IN PEX_RUNDOWN_REF RunRef
  83. )
  84. /*++
  85. Routine Description:
  86. Mark rundown block has having completed rundown so we can wait again safely.
  87. Arguments:
  88. RunRef - Rundown block to be referenced
  89. Return Value:
  90. None
  91. --*/
  92. {
  93. PAGED_CODE ();
  94. ASSERT ((RunRef->Count&EX_RUNDOWN_ACTIVE) != 0);
  95. InterlockedExchangePointer (&RunRef->Ptr, (PVOID) EX_RUNDOWN_ACTIVE);
  96. }
  97. NTKERNELAPI
  98. BOOLEAN
  99. FASTCALL
  100. ExfAcquireRundownProtection (
  101. IN PEX_RUNDOWN_REF RunRef
  102. )
  103. /*++
  104. Routine Description:
  105. Reference a rundown block preventing rundown occuring if it hasn't already started
  106. This routine is NON-PAGED because it is being called on the paging path.
  107. Arguments:
  108. RunRef - Rundown block to be referenced
  109. Return Value:
  110. BOOLEAN - TRUE - rundown protection was acquired, FALSE - rundown is active or completed
  111. --*/
  112. {
  113. ULONG_PTR Value, NewValue;
  114. Value = RunRef->Count;
  115. do {
  116. //
  117. // If rundown has started return with an error
  118. //
  119. if (Value & EX_RUNDOWN_ACTIVE) {
  120. return FALSE;
  121. }
  122. //
  123. // Rundown hasn't started yet so attempt to increment the unsage count.
  124. //
  125. NewValue = Value + EX_RUNDOWN_COUNT_INC;
  126. NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  127. (PVOID) NewValue,
  128. (PVOID) Value);
  129. if (NewValue == Value) {
  130. return TRUE;
  131. }
  132. //
  133. // somebody else changed the variable before we did. Either a protection call came and went or rundown was
  134. // initiated. We just repeat the whole loop again.
  135. //
  136. Value = NewValue;
  137. } while (TRUE);
  138. }
  139. NTKERNELAPI
  140. BOOLEAN
  141. FASTCALL
  142. ExAcquireRundownProtectionEx (
  143. IN PEX_RUNDOWN_REF RunRef,
  144. IN ULONG Count
  145. )
  146. /*++
  147. Routine Description:
  148. Reference a rundown block preventing rundown occuring if it hasn't already started
  149. This routine is NON-PAGED because it is being called on the paging path.
  150. Arguments:
  151. RunRef - Rundown block to be referenced
  152. Count - Number of references to add
  153. Return Value:
  154. BOOLEAN - TRUE - rundown protection was acquired, FALSE - rundown is active or completed
  155. --*/
  156. {
  157. ULONG_PTR Value, NewValue;
  158. Value = RunRef->Count;
  159. do {
  160. //
  161. // If rundown has started return with an error
  162. //
  163. if (Value & EX_RUNDOWN_ACTIVE) {
  164. return FALSE;
  165. }
  166. //
  167. // Rundown hasn't started yet so attempt to increment the unsage count.
  168. //
  169. NewValue = Value + EX_RUNDOWN_COUNT_INC * Count;
  170. NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  171. (PVOID) NewValue,
  172. (PVOID) Value);
  173. if (NewValue == Value) {
  174. return TRUE;
  175. }
  176. //
  177. // somebody else changed the variable before we did. Either a protection call came and went or rundown was
  178. // initiated. We just repeat the whole loop again.
  179. //
  180. Value = NewValue;
  181. } while (TRUE);
  182. }
  183. NTKERNELAPI
  184. VOID
  185. FASTCALL
  186. ExfReleaseRundownProtection (
  187. IN PEX_RUNDOWN_REF RunRef
  188. )
  189. /*++
  190. Routine Description:
  191. Dereference a rundown block and wake the rundown thread if we are the last to exit
  192. This routine is NON-PAGED because it is being called on the paging path.
  193. Arguments:
  194. RunRef - Rundown block to have its reference released
  195. Return Value:
  196. None
  197. --*/
  198. {
  199. ULONG_PTR Value, NewValue;
  200. Value = RunRef->Count;
  201. do {
  202. //
  203. // If the block is already marked for rundown then decrement the wait block count and wake the
  204. // rundown thread if we are the last
  205. //
  206. if (Value & EX_RUNDOWN_ACTIVE) {
  207. PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
  208. //
  209. // Rundown is active. since we are one of the threads blocking rundown we have the right to follow
  210. // the pointer and decrement the active count. If we are the last thread then we have the right to
  211. // wake up the waiter. After doing this we can't touch the data structures again.
  212. //
  213. WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK) (Value & (~EX_RUNDOWN_ACTIVE));
  214. ASSERT (WaitBlock->Count > 0);
  215. if (InterlockedDecrement ((PLONG)&WaitBlock->Count) == 0) {
  216. //
  217. // We are the last thread out. Wake up the waiter.
  218. //
  219. KeSetEvent (&WaitBlock->WakeEvent, 0, FALSE);
  220. }
  221. return;
  222. } else {
  223. //
  224. // Rundown isn't active. Just try and decrement the count. Some other protector thread way come and/or
  225. // go as we do this or rundown might be initiated. We detect this because the exchange will fail and
  226. // we have to retry
  227. //
  228. ASSERT (Value >= EX_RUNDOWN_COUNT_INC);
  229. NewValue = Value - EX_RUNDOWN_COUNT_INC;
  230. NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  231. (PVOID) NewValue,
  232. (PVOID) Value);
  233. if (NewValue == Value) {
  234. return;
  235. }
  236. Value = NewValue;
  237. }
  238. } while (TRUE);
  239. }
  240. NTKERNELAPI
  241. VOID
  242. FASTCALL
  243. ExReleaseRundownProtectionEx (
  244. IN PEX_RUNDOWN_REF RunRef,
  245. IN ULONG Count
  246. )
  247. /*++
  248. Routine Description:
  249. Dereference a rundown block and wake the rundown thread if we are the last to exit
  250. This routine is NON-PAGED because it is being called on the paging path.
  251. Arguments:
  252. RunRef - Rundown block to have its reference released
  253. Count - Number of reference to remove
  254. Return Value:
  255. None
  256. --*/
  257. {
  258. ULONG_PTR Value, NewValue;
  259. Value = RunRef->Count;
  260. do {
  261. //
  262. // If the block is already marked for rundown then decrement the wait block count and wake the
  263. // rundown thread if we are the last
  264. //
  265. if (Value & EX_RUNDOWN_ACTIVE) {
  266. PEX_RUNDOWN_WAIT_BLOCK WaitBlock;
  267. //
  268. // Rundown is active. since we are one of the threads blocking rundown we have the right to follow
  269. // the pointer and decrement the active count. If we are the last thread then we have the right to
  270. // wake up the waiter. After doing this we can't touch the data structures again.
  271. //
  272. WaitBlock = (PEX_RUNDOWN_WAIT_BLOCK) (Value & (~EX_RUNDOWN_ACTIVE));
  273. ASSERT (WaitBlock->Count >= Count);
  274. if (InterlockedExchangeAdd ((PLONG)&WaitBlock->Count, -(LONG)Count) == (LONG) Count) {
  275. //
  276. // We are the last thread out. Wake up the waiter.
  277. //
  278. KeSetEvent (&WaitBlock->WakeEvent, 0, FALSE);
  279. }
  280. return;
  281. } else {
  282. //
  283. // Rundown isn't active. Just try and decrement the count. Some other protector thread way come and/or
  284. // go as we do this or rundown might be initiated. We detect this because the exchange will fail and
  285. // we have to retry
  286. //
  287. ASSERT (Value >= EX_RUNDOWN_COUNT_INC * Count);
  288. NewValue = Value - EX_RUNDOWN_COUNT_INC * Count;
  289. NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  290. (PVOID) NewValue,
  291. (PVOID) Value);
  292. if (NewValue == Value) {
  293. return;
  294. }
  295. Value = NewValue;
  296. }
  297. } while (TRUE);
  298. }
  299. NTKERNELAPI
  300. VOID
  301. FASTCALL
  302. ExfWaitForRundownProtectionRelease (
  303. IN PEX_RUNDOWN_REF RunRef
  304. )
  305. /*++
  306. Routine Description:
  307. Wait till all outstanding rundown protection calls have exited
  308. Arguments:
  309. RunRef - Pointer to a rundown structure
  310. Return Value:
  311. None
  312. --*/
  313. {
  314. EX_RUNDOWN_WAIT_BLOCK WaitBlock;
  315. PKEVENT Event;
  316. ULONG_PTR Value, NewValue;
  317. ULONG WaitCount;
  318. PAGED_CODE ();
  319. //
  320. // Fast path. this should be the normal case. If Value is zero then there are no current accessors and we have
  321. // marked the rundown structure as rundown. If the value is EX_RUNDOWN_ACTIVE then the structure has already
  322. // been rundown and ExRundownCompleted. This second case allows for callers that might initiate rundown
  323. // multiple times (like handle table rundown) to have subsequent rundowns become noops.
  324. //
  325. Value = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  326. (PVOID) EX_RUNDOWN_ACTIVE,
  327. (PVOID) 0);
  328. if (Value == 0 || Value == EX_RUNDOWN_ACTIVE) {
  329. return;
  330. }
  331. //
  332. // Slow path
  333. //
  334. Event = NULL;
  335. do {
  336. //
  337. // Extract total number of waiters. Its biased by 2 so we can hanve the rundown active bit.
  338. //
  339. WaitCount = (ULONG) (Value >> EX_RUNDOWN_COUNT_SHIFT);
  340. //
  341. // If there are some accessors present then initialize and event (once only).
  342. //
  343. if (WaitCount > 0 && Event == NULL) {
  344. Event = &WaitBlock.WakeEvent;
  345. KeInitializeEvent (Event, SynchronizationEvent, FALSE);
  346. }
  347. //
  348. // Store the wait count in the wait block. Waiting threads will start to decrement this as they exit
  349. // if our exchange succeeds. Its possible for accessors to come and go between our initial fetch and
  350. // the interlocked swap. This doesn't matter so long as there is the same number of outstanding accessors
  351. // to wait for.
  352. //
  353. WaitBlock.Count = WaitCount;
  354. NewValue = ((ULONG_PTR) &WaitBlock) | EX_RUNDOWN_ACTIVE;
  355. NewValue = (ULONG_PTR) InterlockedCompareExchangePointer (&RunRef->Ptr,
  356. (PVOID) NewValue,
  357. (PVOID) Value);
  358. if (NewValue == Value) {
  359. if (WaitCount > 0) {
  360. KeWaitForSingleObject (Event,
  361. Executive,
  362. KernelMode,
  363. FALSE,
  364. NULL);
  365. ASSERT (WaitBlock.Count == 0);
  366. }
  367. return;
  368. }
  369. Value = NewValue;
  370. ASSERT ((Value&EX_RUNDOWN_ACTIVE) == 0);
  371. } while (TRUE);
  372. }