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.

1210 lines
37 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. vvector.c
  5. Abstract:
  6. The version vector is a dampening mechanism that prevents replicating
  7. the same change to the same machine more than once.
  8. The version keeps track of the last change that has been received
  9. by a machine or the last change that was sent to a machine.
  10. A new change order is checked against the version vector before
  11. it is given to the change order accept thread. If dampened, the
  12. sender receives an ACK. Along with the ACK is the current
  13. version for the specified originator. This allows the sender
  14. to update its outbound cxtion version vector and dampen
  15. change orders before they are sent.
  16. Author:
  17. Billy J. Fuller 18-Apr-1997
  18. David A. Orbits 15-Oct-97 :
  19. Revise to retire CO's in order so all COs coming from the same
  20. originator propagate in order. Integrate with ChgOrdIssueCleanup()
  21. and restructure locking.
  22. Environment
  23. User mode winnt
  24. --*/
  25. #include <ntreppch.h>
  26. #pragma hdrstop
  27. #undef DEBSUB
  28. #define DEBSUB "VVECTOR:"
  29. #include <frs.h>
  30. #include <tablefcn.h>
  31. ULONG
  32. ChgOrdIssueCleanup(
  33. PTHREAD_CTX ThreadCtx,
  34. PREPLICA Replica,
  35. PCHANGE_ORDER_ENTRY ChangeOrder,
  36. ULONG CleanUpFlags
  37. );
  38. ULONG
  39. VVReserveRetireSlot(
  40. IN PREPLICA Replica,
  41. IN PCHANGE_ORDER_ENTRY Coe
  42. )
  43. /*++
  44. Routine Description:
  45. A replica can have many outstanding change orders from any given
  46. originator. The change orders can complete out of sequence but
  47. we don't want to update the version vector with a later version
  48. if a earlier version is still in progress. The pending versions
  49. are kept on the duplicate list.
  50. A pending version transitions to "retired" when its change
  51. order is retired. After the database is updated, the version
  52. is committed.
  53. The incore version vector is then updated with the youngest
  54. version (largest VSN) in the list that has been committed.
  55. Change orders always issue in order by orginator VSN (except for retries)
  56. so the version vector update and propagation to the outbound log also
  57. occur in order.
  58. PERF - We should be using the existing table lock.
  59. Arguments:
  60. Replica -- ptr to the replica struct for the version vector.
  61. Coe -- ptr to the change order entry.
  62. Return Value:
  63. FrsError status.
  64. --*/
  65. {
  66. #undef DEBSUB
  67. #define DEBSUB "VVReserveRetireSlot:"
  68. PVV_RETIRE_SLOT RetireSlot;
  69. PVV_ENTRY MasterVVEntry;
  70. PGEN_TABLE VV = Replica->VVector;
  71. PCHANGE_ORDER_COMMAND Coc = &Coe->Cmd;
  72. PLIST_ENTRY InsertBeforeEntry = NULL;
  73. //
  74. // If this CO has already done the VV update or had it executed then done.
  75. //
  76. if (CO_IFLAG_ON(Coe, CO_IFLAG_VVRETIRE_EXEC) ||
  77. CO_FLAG_ON(Coe, CO_FLAG_VV_ACTIVATED)) {
  78. return FrsErrorSuccess;
  79. }
  80. //
  81. // If this is a out of order CO then it should not update out VV.
  82. // The CO_FLAG_SKIP_VV_UPDATE flag is preserved and also sent to our
  83. // downstream.
  84. //
  85. if (CO_FLAG_ON(Coe, CO_FLAG_OUT_OF_ORDER)) {
  86. SET_CO_FLAG(Coe, CO_FLAG_SKIP_VV_UPDATE);
  87. }
  88. //
  89. // A call to reserve must be matched with a call to retire before
  90. // another call to reserve can be made for the same change order.
  91. // The only exception is that once a slot is activated it can stay on the
  92. // list after the CO has retired or has been marked for retry. In this case
  93. // a duplicate remote CO could arrive and be issued.
  94. //
  95. LOCK_GEN_TABLE(VV);
  96. MasterVVEntry = GTabLookupNoLock(VV, &Coc->OriginatorGuid, NULL);
  97. if (MasterVVEntry) {
  98. DPRINT1(4, "Coc->FrsVsn : %08x %08x\n",
  99. PRINTQUAD(Coc->FrsVsn));
  100. DPRINT1(4, "MasterVVEntry->GVsn.Vsn : %08x %08x\n",
  101. PRINTQUAD(MasterVVEntry->GVsn.Vsn));
  102. //
  103. // If we are trying to reserve a slot for a CO with a lower VSN then
  104. // mark the CO as out of order and do not reserve a slot.
  105. //
  106. if (MasterVVEntry->GVsn.Vsn >= Coc->FrsVsn) {
  107. SET_CO_FLAG(Coe, CO_FLAG_OUT_OF_ORDER);
  108. SET_CO_FLAG(Coe, CO_FLAG_SKIP_VV_UPDATE);
  109. DPRINT(4, "CO with older VSN received.\n");
  110. UNLOCK_GEN_TABLE(VV);
  111. return FrsErrorSuccess;
  112. }
  113. ForEachListEntryLock( MasterVVEntry, VV_RETIRE_SLOT, Link,
  114. // The iterator pE is of type PVV_RETIRE_SLOT.
  115. DPRINT1(4, "pE->Vsn : %08x %08x\n",
  116. PRINTQUAD(pE->Vsn));
  117. if (pE->Vsn > Coc->FrsVsn) {
  118. SET_CO_FLAG(Coe, CO_FLAG_OUT_OF_ORDER);
  119. SET_CO_FLAG(Coe, CO_FLAG_SKIP_VV_UPDATE);
  120. InsertBeforeEntry = &pE->Link;
  121. break;
  122. } else if (pE->Vsn == Coc->FrsVsn) {
  123. //
  124. // Slot exists. Check if it is activated.
  125. //
  126. if (pE->ChangeOrder != NULL) {
  127. //
  128. // This is probably a duplicate CO.
  129. //
  130. CHANGE_ORDER_TRACE(3, Coe, "VVResrv Activated Retire Slot Exists");
  131. UNLOCK_GEN_TABLE(VV);
  132. return FrsErrorKeyDuplicate;
  133. } else {
  134. CHANGE_ORDER_TRACE(3, Coe, "VVResrv Retire Slot Exists");
  135. UNLOCK_GEN_TABLE(VV);
  136. return FrsErrorSuccess;
  137. }
  138. }
  139. );
  140. }
  141. //
  142. // This change order does not have a reserved slot
  143. //
  144. // If new originator; create a new version vector entry.
  145. //
  146. if (!MasterVVEntry) {
  147. //
  148. // New version vector entry. We don't have to hold locks because the
  149. // only time a new version vector entry is created is when change
  150. // order accept is processing a change order.
  151. //
  152. MasterVVEntry = FrsAlloc(sizeof(VV_ENTRY));
  153. InitializeListHead(&MasterVVEntry->ListHead);
  154. COPY_GUID(&MasterVVEntry->GVsn.Guid, &Coc->OriginatorGuid);
  155. MasterVVEntry->GVsn.Vsn = QUADZERO;
  156. //
  157. // Add it to the version vector table.
  158. //
  159. GTabInsertEntryNoLock(VV, MasterVVEntry, &MasterVVEntry->GVsn.Guid, NULL);
  160. }
  161. CHANGE_ORDER_TRACE(3, Coe, "VVReserve Slot");
  162. //
  163. // Allocate a version vector retire slot.
  164. //
  165. RetireSlot = FrsAlloc(sizeof(VV_RETIRE_SLOT));
  166. RetireSlot->Vsn = Coc->FrsVsn;
  167. RetireSlot->RetireSlotFlags = 0;
  168. if (COC_FLAG_ON(Coc, CO_FLAG_OUT_OF_ORDER)) {
  169. RetireSlot->RetireSlotFlags |= VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER;
  170. }
  171. if (InsertBeforeEntry != NULL) {
  172. InsertTailList(InsertBeforeEntry, &RetireSlot->Link);
  173. } else {
  174. //
  175. // The retire slot is linked to the list tail to maintain Issue order.
  176. //
  177. InsertTailList(&MasterVVEntry->ListHead, &RetireSlot->Link);
  178. }
  179. VV_PRINT(4, L"End of Reserve Retire Slot", VV);
  180. UNLOCK_GEN_TABLE(VV);
  181. return FrsErrorSuccess;
  182. }
  183. ULONG
  184. VVRetireChangeOrder(
  185. IN PTHREAD_CTX ThreadCtx,
  186. IN PREPLICA Replica,
  187. IN PCHANGE_ORDER_ENTRY ChangeOrder,
  188. IN ULONG CleanUpFlags
  189. )
  190. /*++
  191. Routine Description:
  192. Activate or discard the retire slot reserved for this change order.
  193. The ChangeOrder pointer and the CleanUpFlags are saved in the slot entry.
  194. If the retire slot is now at the head of the list the version vector
  195. can be updated, the change order propagated to the outbound log and the
  196. slot entry is freed. The update process continues with the new head
  197. entry if that slot is activated.
  198. The incore version vector is updated after the database is updated.
  199. Both are updated with the VSN of the most recent entry that is processed.
  200. * NOTE * -- A remote CO that is discarded still needs to Ack the inbound
  201. partner. The caller must handle this since a discard request
  202. to an entry that is not activated just causes the entry to be removed
  203. from the list and freed. The version vector should NOT be updated by
  204. the caller in this case since the update may be out of order. If it
  205. is necessary to update the VV then you must activate the retire slot
  206. (not setting the ISCU_INS_OUTLOG cleanup flag). The caller can still
  207. trigger the inbound partner ACK out of order since that does not affect
  208. the version vector. Or you can pass in the ISCU_ACK_INBOUND cleanup flag
  209. when you activate the entry.
  210. Arguments:
  211. ThreadCtx -- Ptr to the DB thread context to use for calls to Issue cleanup.
  212. Replica -- Replica set context.
  213. ChangeOrder -- Change order to activate or discard.
  214. CleanUpFlags -- Cleanup flags saved in the slot entry for use when
  215. VV is updated and CO is propagated.
  216. Return Value:
  217. FRS STATUS
  218. FrsErrorVVSlotNotFound -- Returned when no VVSlot is found for an out of order
  219. change order. This means that no Issue Cleanup
  220. actions will be initiated here on behalf of the=is
  221. CO. So the caller better take care of it.
  222. --*/
  223. {
  224. #undef DEBSUB
  225. #define DEBSUB "VVRetireChangeOrder:"
  226. #define FlagChk(_flag_) BooleanFlagOn(CleanUpFlags, _flag_)
  227. ULONG FStatus;
  228. PVV_RETIRE_SLOT RetireSlot;
  229. PVV_RETIRE_SLOT NextRetireSlot;
  230. PVV_ENTRY MasterVVEntry;
  231. PCHANGE_ORDER_COMMAND Coc = &ChangeOrder->Cmd;
  232. PGEN_TABLE VV = Replica->VVector;
  233. BOOL First;
  234. ULONG Flags;
  235. ULONGLONG UpdateVsn;
  236. PLIST_ENTRY Entry;
  237. PLIST_ENTRY pNext;
  238. GUID OriginatorGuid;
  239. BOOL SkipVVUpdate;
  240. BOOL Blocking;
  241. PIDTABLE_RECORD IDTableRec;
  242. //
  243. // Find the originator's entry in the version vector
  244. //
  245. LOCK_GEN_TABLE(VV);
  246. VV_PRINT(5, L"Start of Retire Change Order", VV);
  247. //
  248. // Nothing to do if CO says we are VV Retired.
  249. //
  250. if (CO_IFLAG_ON(ChangeOrder, CO_IFLAG_VVRETIRE_EXEC)) {
  251. UNLOCK_GEN_TABLE(VV);
  252. CHANGE_ORDER_TRACE(3, ChangeOrder, "VVRetire Err SAR");
  253. return FrsErrorSuccess;
  254. }
  255. //
  256. // Make a copy of the Guid. May need it after CO is deleted.
  257. //
  258. OriginatorGuid = Coc->OriginatorGuid;
  259. MasterVVEntry = GTabLookupNoLock(VV, &OriginatorGuid, NULL);
  260. if (MasterVVEntry == NULL) {
  261. //
  262. // Out of order change orders now participate in vv reitre logic.
  263. //
  264. if (FlagChk(ISCU_ACTIVATE_VV_DISCARD)) {
  265. UNLOCK_GEN_TABLE(VV);
  266. CHANGE_ORDER_TRACE(3, ChangeOrder, "VVRetire OK");
  267. return FrsErrorVVSlotNotFound;
  268. }
  269. }
  270. FRS_ASSERT(MasterVVEntry);
  271. //
  272. // Find the retire slot for this change order.
  273. //
  274. RetireSlot = NULL;
  275. First = TRUE;
  276. ForEachListEntryLock( MasterVVEntry, VV_RETIRE_SLOT, Link,
  277. // The iterator pE is of type PVV_RETIRE_SLOT.
  278. if (pE->Vsn == Coc->FrsVsn) {
  279. RetireSlot = pE;
  280. break;
  281. }
  282. if (!BooleanFlagOn(pE->RetireSlotFlags, VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER)) {
  283. First = FALSE;
  284. }
  285. );
  286. if (RetireSlot == NULL) {
  287. //
  288. //
  289. // Out of order change orders now participate in vv reitre logic.
  290. //
  291. //
  292. if (FlagChk(ISCU_ACTIVATE_VV_DISCARD) || (MasterVVEntry->GVsn.Vsn >= Coc->FrsVsn)) {
  293. UNLOCK_GEN_TABLE(VV);
  294. CHANGE_ORDER_TRACE(3, ChangeOrder, "VVRetire OK (not found)");
  295. return FrsErrorVVSlotNotFound;
  296. }
  297. }
  298. FRS_ASSERT(RetireSlot != NULL);
  299. // if the CO is aborted and the CO is not activated then free the slot.
  300. // if the CO is aborted and the CO is activated AND the VSN would have
  301. // moved the master VSN backwards then suppress the update.
  302. //
  303. //
  304. // This change order might have been marked out of order after
  305. // a slot was reserved.
  306. //
  307. if (CO_FLAG_ON(ChangeOrder, CO_FLAG_OUT_OF_ORDER)) {
  308. SET_CO_FLAG(ChangeOrder, CO_FLAG_SKIP_VV_UPDATE);
  309. RetireSlot->RetireSlotFlags |= VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER;
  310. }
  311. FRS_PRINT_TYPE(5, ChangeOrder);
  312. //
  313. // Activate or discard the affected slot
  314. //
  315. if (!FlagChk(ISCU_ACTIVATE_VV_DISCARD)) {
  316. //
  317. // The change order has passsed the point of initial retire.
  318. // Activate the slot by saving the pointer and bumping the ref count.
  319. //
  320. // Note: The change order can still be aborted or retried (e.g. Install
  321. // fails).
  322. //
  323. FRS_ASSERT(RetireSlot->ChangeOrder == NULL);
  324. INCREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  325. RetireSlot->ChangeOrder = ChangeOrder;
  326. RetireSlot->CleanUpFlags = CleanUpFlags;
  327. CHANGE_ORDER_TRACE(3, ChangeOrder, "VV Slot Activated");
  328. } else {
  329. //
  330. // Discard the slot only if it is a non activated abort co. For
  331. // all other cos we keep the slot around.
  332. //
  333. if ((RetireSlot->ChangeOrder == NULL) &&
  334. (CO_FLAG_ON(ChangeOrder, CO_FLAG_ABORT_CO) ||
  335. COE_FLAG_ON(ChangeOrder, COE_FLAG_STAGE_ABORTED) ||
  336. CO_STATE_IS(ChangeOrder, IBCO_ABORTING))) {
  337. FrsRemoveEntryList(&RetireSlot->Link);
  338. FrsFree(RetireSlot);
  339. CHANGE_ORDER_TRACE(3, ChangeOrder, "VV ActSlot Discarded");
  340. goto PROCESS_LIST;
  341. } else if (CO_FLAG_ON(ChangeOrder, CO_FLAG_RETRY) || CO_FLAG_ON(ChangeOrder, CO_FLAG_OUT_OF_ORDER)){
  342. SET_CO_FLAG(ChangeOrder, CO_FLAG_OUT_OF_ORDER);
  343. SET_CO_FLAG(ChangeOrder, CO_FLAG_SKIP_VV_UPDATE);
  344. RetireSlot->RetireSlotFlags |= VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER;
  345. }
  346. if (RetireSlot->ChangeOrder != NULL) {
  347. CHANGE_ORDER_TRACE(3, ChangeOrder, "VV ActSlot Do not Discard");
  348. RetireSlot->CleanUpFlags |= CleanUpFlags;
  349. ClearFlag(RetireSlot->CleanUpFlags, (ISCU_INS_OUTLOG |
  350. ISCU_INS_OUTLOG_NEW_GUID));
  351. }
  352. //
  353. // We were trying to discard the slot.
  354. // If this CO is a local CO and it is occupying the first non out of order
  355. // slot and if there is a activated CO following it in the retire list then mark
  356. // this CO to be out of order so we can make progress.
  357. //
  358. if ((First == TRUE) && CO_FLAG_ON(ChangeOrder, CO_FLAG_LOCALCO) &&
  359. !BooleanFlagOn(RetireSlot->RetireSlotFlags, VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER)) {
  360. Entry = GetListNext(&RetireSlot->Link);
  361. Blocking = FALSE;
  362. while (Entry != &MasterVVEntry->ListHead) {
  363. NextRetireSlot = CONTAINING_RECORD(Entry, VV_RETIRE_SLOT, Link);
  364. if (NextRetireSlot->ChangeOrder != NULL) {
  365. Blocking = TRUE;
  366. break;
  367. } else if (!BooleanFlagOn(NextRetireSlot->RetireSlotFlags, VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER)) {
  368. break;
  369. }
  370. Entry = GetListNext(Entry);
  371. }
  372. //
  373. // This local CO is blocking other COs so mark it out of order and move on.
  374. // We go through all the trouble above to minimize the number of COs
  375. // marked Out of Order.
  376. //
  377. if (Blocking == TRUE) {
  378. CHANGE_ORDER_TRACE(3, ChangeOrder, "Set CO OofO");
  379. SET_CO_FLAG(ChangeOrder, CO_FLAG_OUT_OF_ORDER);
  380. SET_CO_FLAG(ChangeOrder, CO_FLAG_SKIP_VV_UPDATE);
  381. RetireSlot->RetireSlotFlags |= VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER;
  382. } else {
  383. DPRINT(5, "Non blocking local co.\n");
  384. }
  385. }
  386. }
  387. PROCESS_LIST:
  388. //
  389. // If this change order is not or was not next in line to propagate then
  390. // it waits for the prior change orders to finish before updating
  391. // the version vector with this VSN.
  392. //
  393. if (!First) {
  394. VV_PRINT(4, L"End of Retire Change Order", VV);
  395. UNLOCK_GEN_TABLE(VV);
  396. return FrsErrorSuccess;
  397. }
  398. //
  399. // If we are already doing retires on this originator then the thread doing
  400. // it will pick up our entry next. Otherwise we do it.
  401. // This Flag is used by the VV code to serialize database updates with
  402. // respect to a given originator. It avoids holding the GEN_TABLE lock
  403. // across database disk operations but keeps another thread from racing
  404. // with us to do a VV update on the same originator record.
  405. //
  406. if (BooleanFlagOn(MasterVVEntry->CleanUpFlags, VV_ENTRY_RETIRE_ACTIVE)) {
  407. UNLOCK_GEN_TABLE(VV);
  408. return FrsErrorSuccess;
  409. }
  410. SetFlag(MasterVVEntry->CleanUpFlags, VV_ENTRY_RETIRE_ACTIVE);
  411. //
  412. // Propagate Change Orders for all activated retire slots at front of list.
  413. //
  414. SkipVVUpdate = FALSE;
  415. Entry = &MasterVVEntry->ListHead;
  416. while (!IsListEmpty(&MasterVVEntry->ListHead) && (Entry != GetListTail(&MasterVVEntry->ListHead))) {
  417. Entry = GetListNext(Entry);
  418. RetireSlot = CONTAINING_RECORD(Entry, VV_RETIRE_SLOT, Link);
  419. //
  420. // If not retired then done.
  421. //
  422. if (RetireSlot->ChangeOrder == NULL) {
  423. if (!BooleanFlagOn(RetireSlot->RetireSlotFlags, VV_RETIRE_SLOT_FLAG_OUT_OF_ORDER)) {
  424. break;
  425. }
  426. SkipVVUpdate = TRUE;
  427. continue;
  428. }
  429. CHANGE_ORDER_TRACE(3, RetireSlot->ChangeOrder, "VV RetireSlot & Update");
  430. //
  431. // If this is the last entry to retire, update the VV table in database.
  432. // If we crash during processing of a series of retiring VV slots the
  433. // worst that can happen is that our VV entry for this originator is
  434. // a little old. When we join we will request files based on this
  435. // Version Vector entry that we already have. These COs will be
  436. // rejected so the actual files are not fetched.
  437. //
  438. Flags = 0;
  439. IDTableRec = (PIDTABLE_RECORD)(RetireSlot->ChangeOrder->RtCtx->IDTable.pDataRecord);
  440. if ((SkipVVUpdate == TRUE) || CO_FLAG_ON(RetireSlot->ChangeOrder, CO_FLAG_SKIP_VV_UPDATE)) {
  441. SET_CO_FLAG(RetireSlot->ChangeOrder, CO_FLAG_SKIP_VV_UPDATE);
  442. SetIdRecVVFlag(IDTableRec, IDREC_VVFLAGS_SKIP_VV_UPDATE);
  443. Flags = ISCU_UPDATE_IDT_VVFLAGS;
  444. } else if (IsIdRecVVFlagSet(IDTableRec,IDREC_VVFLAGS_SKIP_VV_UPDATE)) {
  445. ClearIdRecVVFlag(IDTableRec, IDREC_VVFLAGS_SKIP_VV_UPDATE);
  446. Flags = ISCU_UPDATE_IDT_VVFLAGS;
  447. }
  448. if (!CO_FLAG_ON(RetireSlot->ChangeOrder, CO_FLAG_SKIP_VV_UPDATE)) {
  449. pNext = GetListNext(&RetireSlot->Link);
  450. if ((pNext == &MasterVVEntry->ListHead) ||
  451. (CONTAINING_RECORD(pNext, VV_RETIRE_SLOT, Link)->ChangeOrder == NULL)){
  452. Flags |= ISCU_UPDATEVV_DB;
  453. }
  454. }
  455. //
  456. // Reset the entry to the first entry on list so that after removing current entry
  457. // we can rescan the list. We can not continue scanning form where we left
  458. // off because below we drop the VV lock and so another thread could come in and
  459. // change the entries on the list.
  460. //
  461. Entry = &MasterVVEntry->ListHead;
  462. SkipVVUpdate = FALSE;
  463. // Entry = GetListTail(Entry);
  464. FrsRemoveEntryList(&RetireSlot->Link);
  465. //
  466. // Complete the propagation of the postponed change order, drop our
  467. // reference and clear ISCU_ACTIVATE_VV so we don't come back here
  468. // recursively. The dropped ref could free the CO so don't try to
  469. // deref it.
  470. //
  471. Flags |= RetireSlot->CleanUpFlags | ISCU_FREEMEM_CLEANUP;
  472. ClearFlag(Flags, ISCU_ACTIVATE_VV);
  473. //
  474. // If this CO has been aborted then don't insert it into the Outbound
  475. // log. Partner ack (if remote) and other cleanup is still needed.
  476. //
  477. if (CO_IFLAG_ON(RetireSlot->ChangeOrder, CO_IFLAG_CO_ABORT)) {
  478. ClearFlag(Flags, (ISCU_INS_OUTLOG |
  479. ISCU_INS_OUTLOG_NEW_GUID));
  480. }
  481. //
  482. // This is to deal with the case of a crash after a remote CO has
  483. // installed or after a local CO has gened the staging file but the
  484. // VV prop is blocked by another CO. In the latter case the CO would
  485. // be marked activated but not executed. Or the remote CO could still be in
  486. // retry because of rename deferred, etc, but the vvretire is already done.
  487. // Code at startup uses this to sort things out.
  488. //
  489. SET_CO_IFLAG(RetireSlot->ChangeOrder, CO_IFLAG_VVRETIRE_EXEC);
  490. //
  491. // Update the master version vector before we drop the lock so reserve
  492. // can filter out of order remote COs from a different inbound partner
  493. // correctly. These could come straight in or be retry COs.
  494. //
  495. if (!CO_FLAG_ON(RetireSlot->ChangeOrder, CO_FLAG_SKIP_VV_UPDATE)) {
  496. UpdateVsn = RetireSlot->Vsn;
  497. DPRINT2(5, "Updating MasterVVEntry from %08x %08x to %08x %08x\n",
  498. PRINTQUAD(MasterVVEntry->GVsn.Vsn), PRINTQUAD(UpdateVsn));
  499. FRS_ASSERT(UpdateVsn >= MasterVVEntry->GVsn.Vsn);
  500. MasterVVEntry->GVsn.Vsn = UpdateVsn;
  501. }
  502. //
  503. // Drop the table lock so others can do lookups, reserve slots or do
  504. // retires while we are doing database operations.
  505. // We still have the Dbs VV lock so another thread can't
  506. // get into this loop and cause a race to update the database VV table.
  507. // And since the RetireSlot is already off the list the retry thread
  508. // can't get a reference to it.
  509. //
  510. UNLOCK_GEN_TABLE(VV);
  511. FStatus = ChgOrdIssueCleanup(ThreadCtx,
  512. Replica,
  513. RetireSlot->ChangeOrder,
  514. Flags);
  515. DPRINT_FS(0,"ERROR - ChgOrdIssueCleanup failed.", FStatus);
  516. FRS_ASSERT(FStatus == FrsErrorSuccess);
  517. //
  518. // Free up the memory of the retire slot.
  519. //
  520. FrsFree(RetireSlot);
  521. LOCK_GEN_TABLE(VV);
  522. }
  523. //
  524. // Clear the retire active flag so the next thread that activates the
  525. // first entry on the list can enter the retire loop.
  526. //
  527. ClearFlag(MasterVVEntry->CleanUpFlags, VV_ENTRY_RETIRE_ACTIVE);
  528. VV_PRINT(4, L"End of Retire Change Order", VV);
  529. UNLOCK_GEN_TABLE(VV);
  530. return FrsErrorSuccess;
  531. }
  532. PCHANGE_ORDER_ENTRY
  533. VVReferenceRetireSlot(
  534. IN PREPLICA Replica,
  535. IN PCHANGE_ORDER_COMMAND CoCmd
  536. )
  537. /*++
  538. Routine Description:
  539. Look for an activated retire slot for this Guid/Vsn pair.
  540. If found and the connection guid in the change order matches then
  541. increment the reference count and return the Change order pointer.
  542. Arguments:
  543. Replica -- ptr to the replica struct for the version vector.
  544. CoCmd -- ptr to change order command that we are trying to match.
  545. Return Value:
  546. A ptr to the change order if found or NULL.
  547. --*/
  548. {
  549. #undef DEBSUB
  550. #define DEBSUB "VVReferenceRetireSlot:"
  551. ULONGLONG FrsVsn;
  552. PVV_ENTRY MasterVVEntry;
  553. PGEN_TABLE VV = Replica->VVector;
  554. PCHANGE_ORDER_ENTRY ChangeOrder = NULL;
  555. GUID *OriginatorGuid;
  556. GUID *CxtionGuid;
  557. GUID *CoGuid;
  558. FrsVsn = CoCmd->FrsVsn;
  559. OriginatorGuid = &CoCmd->OriginatorGuid;
  560. CxtionGuid = &CoCmd->CxtionGuid;
  561. LOCK_GEN_TABLE(VV);
  562. MasterVVEntry = GTabLookupNoLock(VV, OriginatorGuid, NULL);
  563. if (MasterVVEntry) {
  564. ForEachListEntryLock( MasterVVEntry, VV_RETIRE_SLOT, Link,
  565. // The iterator pE is of type PVV_RETIRE_SLOT.
  566. if (pE->Vsn == FrsVsn) {
  567. if ((pE->ChangeOrder != NULL) &&
  568. GUIDS_EQUAL(&pE->ChangeOrder->Cmd.CxtionGuid, CxtionGuid)) {
  569. //
  570. // Found a match. But need to also check for a CO Guid match.
  571. //
  572. CoGuid = &CoCmd->ChangeOrderGuid;
  573. if (!GUIDS_EQUAL(CoGuid, &pE->ChangeOrder->Cmd.ChangeOrderGuid)) {
  574. //
  575. // The CO Guid's do not match. The CO on the VV Retire
  576. // chain has a matching OriginatorGuid, a matching VSN
  577. // and a matching CxtionGuid so it is the same CO but
  578. // we got a duplicate with a new CO Guid. One way this
  579. // can happen is if M1 was doing a VVJOIN from M2 and
  580. // M2 had a CO for file X in the retry install state.
  581. // When the CO on M2 finally finishes it must re-insert
  582. // the CO into the outbound log, assigning the CO a new
  583. // CO Guid. The CO that was sent as part of the VVJoin
  584. // operation could have the same OriginatorGuid, FrsVsn
  585. // and Cxtion Guid, causing a match above. In addition
  586. // since M2 proped the incomming CO into the outlog
  587. // after it fetched the staging file from its upstream
  588. // partner it will have to re-insert the CO a second
  589. // time if it was forced to go thru the retry install
  590. // loop. This is because it can't know how the propped
  591. // CO was ordered relative to the VVJoin generated CO.
  592. // This bites. (313427)
  593. //
  594. DPRINT(0, "WARN - COGuid Mismatch on VVretireSlot hit\n");
  595. CHANGE_ORDER_TRACE(0, pE->ChangeOrder, "No VVRef COGuid Mismatch-1");
  596. CHANGE_ORDER_COMMAND_TRACE(0, CoCmd, "No VVRef COGuid Mismatch-2");
  597. } else {
  598. //
  599. // Match is OK.
  600. //
  601. ChangeOrder = pE->ChangeOrder;
  602. INCREMENT_CHANGE_ORDER_REF_COUNT(ChangeOrder);
  603. CHANGE_ORDER_TRACE(3, ChangeOrder, "VV Ref CO");
  604. }
  605. }
  606. break;
  607. }
  608. );
  609. }
  610. UNLOCK_GEN_TABLE(VV);
  611. return ChangeOrder;
  612. }
  613. VOID
  614. VVUpdate(
  615. IN PGEN_TABLE VV,
  616. IN ULONGLONG Vsn,
  617. IN GUID *Guid
  618. )
  619. /*++
  620. Routine Description:
  621. Update the version vector if the new vsn is greater than
  622. the current version. Or if the entry does not yet exist in VV.
  623. Arguments:
  624. VV
  625. Vsn
  626. Guid
  627. Return Value:
  628. None.
  629. --*/
  630. {
  631. #undef DEBSUB
  632. #define DEBSUB "VVUpdate:"
  633. PVV_ENTRY VVEntry;
  634. //
  635. // Locate the originator's entry in the version vector
  636. //
  637. LOCK_GEN_TABLE(VV);
  638. VVEntry = GTabLookupNoLock(VV, Guid, NULL);
  639. if (VVEntry) {
  640. if (Vsn > VVEntry->GVsn.Vsn) {
  641. //
  642. // Update the existing entry's vsn
  643. //
  644. VVEntry->GVsn.Vsn = Vsn;
  645. }
  646. } else {
  647. //
  648. // Insert the new entry
  649. //
  650. VVEntry = FrsAlloc(sizeof(VV_ENTRY));
  651. VVEntry->GVsn.Vsn = Vsn;
  652. COPY_GUID(&VVEntry->GVsn.Guid, Guid);
  653. InitializeListHead(&VVEntry->ListHead);
  654. GTabInsertEntryNoLock(VV, VVEntry, &VVEntry->GVsn.Guid, NULL);
  655. }
  656. UNLOCK_GEN_TABLE(VV);
  657. }
  658. VOID
  659. VVInsertOutbound(
  660. IN PGEN_TABLE VV,
  661. IN PGVSN GVsn
  662. )
  663. /*++
  664. Routine Description:
  665. Insert the given gvsn (guid, vsn) into the version vector.
  666. The GVsn is addressed by the gen table, don't delete it or
  667. change its guid!
  668. WARN - This function should only be used when creating
  669. the outbound version vector.
  670. Arguments:
  671. VV - version vector to update
  672. GVsn - record to insert
  673. Return Value:
  674. None.
  675. --*/
  676. {
  677. #undef DEBSUB
  678. #define DEBSUB "VVInsertOutbound:"
  679. GTabInsertEntry(VV, GVsn, &GVsn->Guid, NULL);
  680. }
  681. VOID
  682. VVUpdateOutbound(
  683. IN PGEN_TABLE VV,
  684. IN PGVSN GVsn
  685. )
  686. /*++
  687. Routine Description:
  688. Update the version vector if the new vsn is greater than
  689. the current version. Or if the entry does not yet exist in VV.
  690. This function is intended for use only with the version vector
  691. associated with an outbound cxtion because that version vector
  692. uses GVSN's as the version vector entry. This saves memory.
  693. Arguments:
  694. VV
  695. GVsn
  696. Return Value:
  697. None.
  698. --*/
  699. {
  700. #undef DEBSUB
  701. #define DEBSUB "VVUpdateOutbound:"
  702. PGVSN OldGVsn;
  703. //
  704. // Probably a command packet without a RsGVsn()
  705. //
  706. if (!GVsn) {
  707. return;
  708. }
  709. //
  710. // Find the originator's entry in the version vector
  711. //
  712. LOCK_GEN_TABLE(VV);
  713. OldGVsn = GTabLookupNoLock(VV, &GVsn->Guid, NULL);
  714. if (OldGVsn) {
  715. //
  716. // Update the version if it is greater
  717. //
  718. if (GVsn->Vsn > OldGVsn->Vsn) {
  719. OldGVsn->Vsn = GVsn->Vsn;
  720. }
  721. FrsFree(GVsn);
  722. }
  723. UNLOCK_GEN_TABLE(VV);
  724. if (!OldGVsn) {
  725. //
  726. // Create a new entry
  727. //
  728. VVInsertOutbound(VV, GVsn);
  729. }
  730. }
  731. BOOL
  732. VVHasVsnNoLock(
  733. IN PGEN_TABLE VV,
  734. IN GUID *OriginatorGuid,
  735. IN ULONGLONG Vsn
  736. )
  737. /*++
  738. Routine Description:
  739. Check if the change order's Vsn is "in" the VV
  740. Arguments:
  741. VV
  742. OriginatorGuid
  743. Vsn
  744. Return Value:
  745. TRUE - Vsn is in version vector
  746. FALSE - Not
  747. --*/
  748. {
  749. #undef DEBSUB
  750. #define DEBSUB "VVHasVsnNoLock:"
  751. BOOL Ret = FALSE;
  752. PGVSN GVsn;
  753. PGEN_ENTRY Entry;
  754. //
  755. // Locate the originator's entry in the version vector
  756. // The caller holds the table lock across the compare because
  757. // the 64-bit vsn is not updated atomically. Don't
  758. // hold the VV lock because that lock is held
  759. // across db updates.
  760. //
  761. Entry = GTabLookupEntryNoLock(VV, OriginatorGuid, NULL);
  762. if (Entry) {
  763. FRS_ASSERT(!Entry->Dups);
  764. GVsn = Entry->Data;
  765. Ret = (Vsn <= (ULONGLONG)GVsn->Vsn);
  766. }
  767. return Ret;
  768. }
  769. BOOL
  770. VVHasOriginatorNoLock(
  771. IN PGEN_TABLE VV,
  772. IN GUID *OriginatorGuid
  773. )
  774. /*++
  775. Routine Description:
  776. Check if the supplied originator guid is present in the version vector.
  777. Arguments:
  778. VV
  779. OriginatorGuid
  780. Return Value:
  781. TRUE - Originator guid is present in version vector
  782. --*/
  783. {
  784. #undef DEBSUB
  785. #define DEBSUB "VVHasOriginatorNoLock:"
  786. //
  787. // Locate the originator's entry in the version vector
  788. // The caller holds the table lock across the compare because
  789. // the 64-bit vsn is not updated atomically. Don't
  790. // hold the VV lock because that lock is held
  791. // across db updates.
  792. //
  793. return (GTabLookupEntryNoLock(VV, OriginatorGuid, NULL) != NULL);
  794. }
  795. BOOL
  796. VVHasVsn(
  797. IN PGEN_TABLE VV,
  798. IN PCHANGE_ORDER_COMMAND Coc
  799. )
  800. /*++
  801. Routine Description:
  802. Check if the change order's Vsn is "in" the VV
  803. Arguments:
  804. VV
  805. Coc
  806. Return Value:
  807. TRUE - Vsn is in version vector
  808. FALSE - Not
  809. --*/
  810. {
  811. #undef DEBSUB
  812. #define DEBSUB "VVHasVsn:"
  813. BOOL Ret = FALSE;
  814. //
  815. // This change order is out of order and hence its vsn
  816. // cannot be compared with the vsn in the version vector.
  817. //
  818. if (BooleanFlagOn(Coc->Flags, CO_FLAG_OUT_OF_ORDER)) {
  819. return FALSE;
  820. }
  821. //
  822. // Locate the originator's entry in the version vector
  823. // Hold the table lock across the compare because
  824. // the 64-bit vsn is not updated atomically. Don't
  825. // hold the VV lock because that lock is held
  826. // across db updates.
  827. //
  828. LOCK_GEN_TABLE(VV);
  829. Ret = VVHasVsnNoLock(VV, &Coc->OriginatorGuid, Coc->FrsVsn);
  830. UNLOCK_GEN_TABLE(VV);
  831. return Ret;
  832. }
  833. PGVSN
  834. VVGetGVsn(
  835. IN PGEN_TABLE VV,
  836. IN GUID *Guid
  837. )
  838. /*++
  839. Routine Description:
  840. Lookup the Vsn for Guid in VV.
  841. Arguments:
  842. VV
  843. Guid
  844. Return Value:
  845. Copy of the GVsn or NULL
  846. --*/
  847. {
  848. #undef DEBSUB
  849. #define DEBSUB "VVGetGVsn:"
  850. PGVSN GVsn = NULL;
  851. PGEN_ENTRY Entry;
  852. //
  853. // Locate the originator's entry in the version vector
  854. // Hold the table lock across the compare because
  855. // the 64-bit vsn is not updated atomically. Don't
  856. // hold the VV lock because that lock is held
  857. // across db updates.
  858. //
  859. LOCK_GEN_TABLE(VV);
  860. Entry = GTabLookupEntryNoLock(VV, Guid, NULL);
  861. if (Entry) {
  862. FRS_ASSERT(!Entry->Dups);
  863. GVsn = Entry->Data;
  864. GVsn = FrsBuildGVsn(&GVsn->Guid, GVsn->Vsn);
  865. }
  866. UNLOCK_GEN_TABLE(VV);
  867. return (GVsn);
  868. }
  869. PGEN_TABLE
  870. VVDupOutbound(
  871. IN PGEN_TABLE VV
  872. )
  873. /*++
  874. Routine Description:
  875. Duplicate the version vector as an outbound version vector.
  876. An outbound version vector is composed of GVSNs instead of
  877. VV_ENTRYs to save space. BUT, since the first entry in a
  878. VV_ENTRY is a GVSN, this routin can duplicate any version
  879. vector.
  880. Arguments:
  881. Outbound - version vector to duplicate as an outbound version vector
  882. Return Value:
  883. Outbound version vector
  884. --*/
  885. {
  886. #undef DEBSUB
  887. #define DEBSUB "VVDupOutbound:"
  888. PVOID Key;
  889. PGVSN GVsn;
  890. PGEN_TABLE NewVV;
  891. //
  892. // No vv, nothing to do
  893. //
  894. if (!VV) {
  895. return NULL;
  896. }
  897. //
  898. // Allocate duplicate version vector
  899. //
  900. NewVV = GTabAllocTable();
  901. //
  902. // Fill it up
  903. //
  904. LOCK_GEN_TABLE(VV);
  905. Key = NULL;
  906. while (GVsn = GTabNextDatumNoLock(VV, &Key)) {
  907. GVsn = FrsBuildGVsn(&GVsn->Guid, GVsn->Vsn);
  908. GTabInsertEntryNoLock(NewVV, GVsn, &GVsn->Guid, NULL);
  909. }
  910. UNLOCK_GEN_TABLE(VV);
  911. //
  912. // Done
  913. //
  914. return NewVV;
  915. }
  916. PVOID
  917. VVFreeOutbound(
  918. IN PGEN_TABLE VV
  919. )
  920. /*++
  921. Routine Description:
  922. Delete the version vector for an outbound cxtion
  923. Arguments:
  924. VV - version vector to update
  925. Return Value:
  926. None.
  927. --*/
  928. {
  929. #undef DEBSUB
  930. #define DEBSUB "VVFreeOutbound:"
  931. return GTabFreeTable(VV, FrsFree);
  932. }
  933. VOID
  934. VVFree(
  935. IN PGEN_TABLE VV
  936. )
  937. /*++
  938. Routine Description:
  939. Delete the version vector for the replica
  940. Arguments:
  941. VV - version vector to update
  942. Return Value:
  943. None.
  944. --*/
  945. {
  946. #undef DEBSUB
  947. #define DEBSUB "VVFree:"
  948. PVOID Key;
  949. PVV_ENTRY MasterVVEntry;
  950. Key = NULL;
  951. if (VV) while (MasterVVEntry = GTabNextDatum(VV, &Key)) {
  952. ForEachListEntryLock( MasterVVEntry, VV_RETIRE_SLOT, Link,
  953. // The iterator pE is of type PVV_RETIRE_SLOT.
  954. FrsFree(pE);
  955. );
  956. }
  957. GTabFreeTable(VV, FrsFree);
  958. }
  959. #if DBG
  960. VOID
  961. VVPrint(
  962. IN ULONG Severity,
  963. IN PWCHAR Header,
  964. IN PGEN_TABLE VV,
  965. IN BOOL IsOutbound
  966. )
  967. /*++
  968. Routine Description:
  969. Print a version vector
  970. Caller must have acquired the VV table lock so se can safely enumerate
  971. the list. i.e. LOCK_GEN_TABLE(VV).
  972. Arguments:
  973. Severity
  974. Header
  975. VV
  976. IsOutbound
  977. Return Value:
  978. None.
  979. --*/
  980. {
  981. #undef DEBSUB
  982. #define DEBSUB "VVPrint:"
  983. PVOID Key;
  984. PVV_ENTRY MasterVVEntry;
  985. CHAR Guid[GUID_CHAR_LEN + 1];
  986. DPRINT2(Severity, "VV for %ws: %08x\n", Header, VV);
  987. Key = NULL;
  988. if (VV) while (MasterVVEntry = GTabNextDatumNoLock(VV, &Key)) {
  989. GuidToStr(&MasterVVEntry->GVsn.Guid, Guid);
  990. DPRINT2(Severity, "\t%s = %08x %08x\n", Guid, PRINTQUAD(MasterVVEntry->GVsn.Vsn));
  991. if (!IsOutbound) {
  992. ForEachListEntryLock( MasterVVEntry, VV_RETIRE_SLOT, Link,
  993. // The iterator pE is of type PVV_RETIRE_SLOT.
  994. DPRINT3(Severity, "\t\t%08x %08x CO: %08x RetireSlotFlags: %08x\n",
  995. PRINTQUAD(pE->Vsn), pE->ChangeOrder, pE->RetireSlotFlags);
  996. );
  997. } else {
  998. DPRINT1(Severity, "\t\t%08x %08x\n", PRINTQUAD(MasterVVEntry->GVsn.Vsn));
  999. }
  1000. }
  1001. }
  1002. #endif DBG