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.

475 lines
11 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. gumevent.c
  5. Abstract:
  6. Cluster event handling routines for the Global Update Manager
  7. Author:
  8. John Vert (jvert) 22-Apr-1996
  9. Revision History:
  10. --*/
  11. #include "gump.h"
  12. #include <bitset.h>
  13. //
  14. // Event handling is divided into two parts, sync and async. The sync
  15. // part is executed by all nodes during phase 4 cleanup of the regroup.
  16. // The sync handler must be very fast, since we run in the context of
  17. // of the regroup thread.
  18. //
  19. // The async part is executed as a work thread and we finish handling
  20. // nodes down event.
  21. //
  22. // Flag to denote if we need to replay the last update in the async.
  23. // event handler.
  24. static BOOL GumReplay = FALSE;
  25. //
  26. // Flag to denote if we are in the middle of a dispatch
  27. //
  28. static BOOL GumUpdatePending = FALSE;
  29. //
  30. // Node generation number stuff
  31. //
  32. static DWORD GumNodeGeneration[ClusterMinNodeId + ClusterDefaultMaxNodes] = {0};
  33. DWORD
  34. GumpGetNodeGenNum(PGUM_INFO GumInfo, DWORD NodeId)
  35. /*++
  36. Routine Description:
  37. Return current generation number for specified node. If node is already dead,
  38. we return the previous generation number so that furture calls to
  39. gumwaitnodedown, gumdispatchstart, gumdispatchend fail without checking if
  40. the node is alive or dead.
  41. Arguments:
  42. NodeId - Node number
  43. Return Value:
  44. Node's current generation number
  45. --*/
  46. {
  47. DWORD dwCur;
  48. CL_ASSERT(NodeId < NmMaxNodeId);
  49. EnterCriticalSection(&GumpLock);
  50. dwCur = GumNodeGeneration[NodeId];
  51. if (GumInfo->ActiveNode[NodeId] == FALSE) {
  52. //
  53. // Node is already dead, return previous sequence number
  54. //
  55. dwCur--;
  56. }
  57. LeaveCriticalSection(&GumpLock);
  58. return (dwCur);
  59. }
  60. void
  61. GumpWaitNodeDown(DWORD NodeId, DWORD Gennum)
  62. /*++
  63. Routine Description:
  64. Wait till specified node has transitioned into down event.
  65. Arguments:
  66. NodeId - node id
  67. Gennum - node's generation number before down event
  68. Return Value:
  69. ERROR_SUCCESS
  70. --*/
  71. {
  72. CL_ASSERT(NodeId < NmMaxNodeId);
  73. EnterCriticalSection(&GumpLock);
  74. if (Gennum != GumNodeGeneration[NodeId]) {
  75. LeaveCriticalSection(&GumpLock);
  76. return;
  77. }
  78. //
  79. // Increment the waiter count, then go wait on the semaphore.
  80. //
  81. ++GumNodeWait[NodeId].WaiterCount;
  82. LeaveCriticalSection(&GumpLock);
  83. WaitForSingleObject(GumNodeWait[NodeId].hSemaphore, INFINITE);
  84. }
  85. BOOL
  86. GumpDispatchStart(DWORD NodeId, DWORD Gennum)
  87. /*++
  88. Routine Description:
  89. Mark start of a dispatch. If the generation number supplied is
  90. old, we fail the dispatch since the node has transitioned.
  91. Arguments:
  92. NodeId - node id
  93. Gennum - node's generation number before down event
  94. Return Value:
  95. TRUE - node state is fine, go ahead with dispatch
  96. FLASE - node has transitioned, abort dispatch
  97. --*/
  98. {
  99. //
  100. // If the sequence number has changed return False, else
  101. // return true.
  102. CL_ASSERT(NodeId < NmMaxNodeId);
  103. EnterCriticalSection(&GumpLock);
  104. if (Gennum != GumNodeGeneration[NodeId]) {
  105. LeaveCriticalSection(&GumpLock);
  106. return (FALSE);
  107. }
  108. //
  109. // Signal that we are in the middle of an update
  110. //
  111. GumUpdatePending = TRUE;
  112. LeaveCriticalSection(&GumpLock);
  113. return (TRUE);
  114. }
  115. void
  116. GumpDispatchAbort()
  117. /*++
  118. Routine Description:
  119. Abort and mark end of current dispatch. Just reset the pending flag.
  120. This is used when the dispatch routine failed, and we don't need to
  121. replay it for other nodes.
  122. Arguments:
  123. none
  124. Return Value:
  125. none
  126. --*/
  127. {
  128. EnterCriticalSection(&GumpLock);
  129. GumUpdatePending = FALSE;
  130. LeaveCriticalSection(&GumpLock);
  131. }
  132. void
  133. GumpDispatchEnd(DWORD NodeId, DWORD Gennum)
  134. /*++
  135. Routine Description:
  136. Mark end of a dispatch. If the generation number supplied is
  137. old and we need to reapply update, we replay update for
  138. other nodes.
  139. Arguments:
  140. NodeId - node id
  141. Gennum - node's generation number before down event
  142. Return Value:
  143. none
  144. --*/
  145. {
  146. //
  147. // If the sequence number has changed while the
  148. // update was happening, we need to replay it
  149. CL_ASSERT(NodeId < NmMaxNodeId);
  150. EnterCriticalSection(&GumpLock);
  151. GumUpdatePending = FALSE;
  152. if (Gennum != GumNodeGeneration[NodeId] && GumReplay) {
  153. GumReplay = FALSE;
  154. LeaveCriticalSection(&GumpLock);
  155. GumpReUpdate(GumpLastUpdateType, NmLocalNodeId);
  156. } else {
  157. LeaveCriticalSection(&GumpLock);
  158. }
  159. }
  160. DWORD
  161. WINAPI
  162. GumpEventHandler(
  163. IN CLUSTER_EVENT Event,
  164. IN PVOID Context
  165. )
  166. /*++
  167. Routine Description:
  168. Processes nodes down cluster events. Replay last update and wake up
  169. any pending threads.
  170. Arguments:
  171. Event - Supplies the type of cluster event.
  172. Context - Supplies the event-specific context
  173. Return Value:
  174. ERROR_SUCCESS
  175. --*/
  176. {
  177. BITSET DownedNodes = (BITSET)((ULONG_PTR)Context);
  178. DWORD NodeId;
  179. if (Event != CLUSTER_EVENT_NODE_DOWN_EX) {
  180. return(ERROR_SUCCESS);
  181. }
  182. CL_ASSERT(BitsetIsNotMember(NmLocalNodeId, DownedNodes));
  183. EnterCriticalSection(&GumpLock);
  184. ClRtlLogPrint(LOG_NOISE,
  185. "[GUM] Nodes down: %1!04X!. Locker=%2!u!, Locking=%3!d!\n",
  186. DownedNodes,
  187. GumpLockerNode,
  188. GumpLockingNode
  189. );
  190. //
  191. //since all gum updates are synchronized and the last buffer
  192. //and last update type are shared across all updates, we dont have
  193. //to reissue the update for all types, only for the last update type.
  194. //SS: note we we use the last GumInfo structure for now since GumInfo
  195. //structures are still maintained for everygum update type
  196. if ( GumReplay && GumUpdatePending == FALSE)
  197. {
  198. // XXX: These should be if statements and panic this node instead.
  199. CL_ASSERT(GumpLockerNode == NmLocalNodeId);
  200. CL_ASSERT(GumpLockingNode == NmLocalNodeId);
  201. GumReplay = FALSE;
  202. LeaveCriticalSection(&GumpLock);
  203. GumpReUpdate(GumpLastUpdateType, NmLocalNodeId);
  204. } else {
  205. LeaveCriticalSection(&GumpLock);
  206. }
  207. ClRtlLogPrint(LOG_NOISE,
  208. "[GUM] Node down processing completed: %1!04X!.\n",
  209. DownedNodes
  210. );
  211. return(ERROR_SUCCESS);
  212. }
  213. DWORD
  214. WINAPI
  215. GumpSyncEventHandler(
  216. IN CLUSTER_EVENT Event,
  217. IN PVOID Context
  218. )
  219. /*++
  220. Routine Description:
  221. Processes nodes down cluster events. Update locker/locking nodes
  222. state and decide if we need to replay last update in async handler.
  223. Arguments:
  224. Event - Supplies the type of cluster event.
  225. Context - Supplies the event-specific context
  226. Return Value:
  227. ERROR_SUCCESS
  228. --*/
  229. {
  230. BITSET DownedNodes = (BITSET)((ULONG_PTR)Context);
  231. DWORD NodeId;
  232. if (Event != CLUSTER_EVENT_NODE_DOWN_EX) {
  233. return(ERROR_SUCCESS);
  234. }
  235. CL_ASSERT(BitsetIsNotMember(NmLocalNodeId, DownedNodes));
  236. EnterCriticalSection(&GumpLock);
  237. ClRtlLogPrint(LOG_NOISE,
  238. "[GUM] Sync Nodes down: %1!04X!. Locker=%2!u!, Locking=%3!d!\n",
  239. DownedNodes,
  240. GumpLockerNode,
  241. GumpLockingNode
  242. );
  243. //
  244. // remove downed nodes from any further GUM updates
  245. //
  246. for(NodeId = ClusterMinNodeId; NodeId <= NmMaxNodeId; ++NodeId) {
  247. if (BitsetIsMember(NodeId, DownedNodes))
  248. {
  249. GUM_UPDATE_TYPE UpdateType;
  250. for (UpdateType = 0; UpdateType < GumUpdateMaximum; UpdateType++)
  251. {
  252. GumTable[UpdateType].ActiveNode[NodeId] = FALSE;
  253. }
  254. //
  255. // Advance node generation number
  256. //
  257. GumNodeGeneration[NodeId]++;
  258. }
  259. }
  260. //
  261. // Update LockerNode/LockingNode if necessary
  262. //
  263. //since all gum updates are synchronized and the last buffer
  264. //and last update type are shared across all updates, we dont have
  265. //to reissue the update for all types, only for the last update type.
  266. //SS: note we we use the last GumInfo structure for now since GumInfo
  267. //structures are still maintained for everygum update type
  268. if ( (GumpLockerNode == NmLocalNodeId) &&
  269. (BitsetIsMember(GumpLockingNode, DownedNodes)) )
  270. {
  271. //
  272. // This node is the locker and the lock is currently held
  273. // by one of the failed nodes. Take ownership of the lock and
  274. // reissue the update to all remaining nodes.
  275. //
  276. EnterCriticalSection(&GumpUpdateLock);
  277. ClRtlLogPrint(LOG_NOISE,
  278. "[GUM] GumpEventHandler taking ownership of the lock from the node %1!d!.\n",
  279. GumpLockingNode
  280. );
  281. GumpLockingNode = NmLocalNodeId;
  282. LeaveCriticalSection(&GumpUpdateLock);
  283. //
  284. // Reissue update in async phase.
  285. //
  286. GumReplay = TRUE;
  287. }
  288. else if ( BitsetIsMember(GumpLockerNode, DownedNodes) )
  289. {
  290. //
  291. // One of the failed nodes was the locker node, so select a new
  292. // locker node now.
  293. //
  294. // Find the node with the next ID after the previous locker node.
  295. //
  296. DWORD j;
  297. for (j=GumpLockerNode+1; j != GumpLockerNode; j++) {
  298. if (j==(NmMaxNodeId+1)) {
  299. j = ClusterMinNodeId;
  300. CL_ASSERT(j != GumpLockerNode);
  301. }
  302. if (GumTable[0].ActiveNode[j]) {
  303. ClRtlLogPrint(LOG_NOISE,
  304. "[GUM] GumpEventHandler New Locker node is node %1!d!\n",
  305. j);
  306. GumpLockerNode = j;
  307. break;
  308. }
  309. }
  310. //
  311. // If this node has been promoted to be the new locker node,
  312. // reissue the last update we saw.
  313. //
  314. if (GumpLockerNode == NmLocalNodeId)
  315. {
  316. //
  317. // Manually acquire the lock here. The update has already
  318. // been issued on this node.
  319. //
  320. EnterCriticalSection(&GumpUpdateLock);
  321. CL_ASSERT(GumpLockingNode == (DWORD)-1);
  322. GumpLockingNode = NmLocalNodeId;
  323. LeaveCriticalSection(&GumpUpdateLock);
  324. //
  325. // Reissue update in async phase.
  326. //
  327. GumReplay = TRUE;
  328. }
  329. }
  330. //
  331. // Wake any threads waiting for the nodes to transition to down.
  332. //
  333. for(NodeId = ClusterMinNodeId; NodeId <= NmMaxNodeId; ++NodeId) {
  334. if (BitsetIsMember(NodeId, DownedNodes))
  335. {
  336. if (GumNodeWait[NodeId].WaiterCount != 0) {
  337. ReleaseSemaphore(GumNodeWait[NodeId].hSemaphore,
  338. GumNodeWait[NodeId].WaiterCount,
  339. NULL);
  340. GumNodeWait[NodeId].WaiterCount = 0;
  341. }
  342. }
  343. }
  344. ClRtlLogPrint(LOG_NOISE,
  345. "[GUM] Sync Nodes down processing completed: %1!04X!.\n",
  346. DownedNodes
  347. );
  348. LeaveCriticalSection(&GumpLock);
  349. return(ERROR_SUCCESS);
  350. }