1815 lines
50 KiB
C
1815 lines
50 KiB
C
/*++
|
||
|
||
Copyright (c) 2000-2001 Microsoft Corporation
|
||
|
||
Module Name:
|
||
initsync.c
|
||
|
||
Abstract:
|
||
Initial Sync Controller Command Server. The initial sync controller command server
|
||
controls the sequencing of vvjoins for a new member of the replica set.
|
||
This command server is active only during the time the replica set is in seeding state.
|
||
During this time the cxtions are paused and unpaused by this command server to make
|
||
sure that only 1 vvjoin is in process at any given time. The priority on the cxtion
|
||
is used to decide the order of vvjoins.
|
||
|
||
Following flags are used by this command server.
|
||
|
||
CXTION_FLAGS_INIT_SYNC : Location = PCXTION->Flags
|
||
^^^^^^^^^^^^^^^^^^^^^^
|
||
This flag is set on all inbound connections that are added to a replica set when
|
||
it is in the seeding state. This flag is set when the connection is initialized
|
||
in OutLogAddNewPartner() and it is reset when the cxtion completes vvjoin in
|
||
InitSyncVvJoinDone. While this flag is set and the replica is in seeding state
|
||
all decisions to join are made by the initsync command server. Once the cxtion
|
||
completes vvjoin and this flag is cleared the cxtion is free to join at any time.
|
||
This flag is persistent in the DB,
|
||
|
||
CXTION_FLAGS_PAUSED : Location = PCXTION->Flags
|
||
^^^^^^^^^^^^^^^^^^^
|
||
If a connection has this flag set then it is not allowed to join with its
|
||
inbound partner. The command server clears this flag in order. All cxtions
|
||
that have the CXTION_FLAGS_INIT_SYNC set start off as paused. They get unpaused
|
||
when its their turn to vvjoin.
|
||
|
||
CXTION_FLAGS_HUNG_INIT_SYNC : Location = PCXTION->Flags
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
This flag is set on a cxtion to indicate that there has been no progress on this
|
||
connection for a specified time (timeout) since a NEED_JOIN was sent. If the current
|
||
working connection gets in this state the command server may decide to unpause
|
||
the next cxtion on list.
|
||
|
||
CONFIG_FLAG_ONLINE : Location PREPLICA->CnfFlags
|
||
^^^^^^^^^^^^^^^^^^
|
||
Presence of this flag on the replica indicates that this member has successfully
|
||
completed one pass through the inbound connections and is ready to go online. Until
|
||
a member is online it will not join with its outbound partners. For a sysvol replica
|
||
set going online also means sysvolready is set to 1. A replica set may be in
|
||
online state but it may still be seeding. This depends on the priorities set on
|
||
the cxtions.
|
||
|
||
CONFIG_FLAG_SEEDING : Location PREPLICA->CnfFlags
|
||
^^^^^^^^^^^^^^^^^^^
|
||
We don't get here unless the replica set is in seeding state. A new replica
|
||
starts up in seeding state unless it is the primary member of the replica set.
|
||
|
||
|
||
******************************* CXTION OPTIONS **********************************
|
||
|
||
The options attribute on the "NTDS Connection" object is used to specify the
|
||
priority for a given connection. Options is a 32 bit value. The highest bit is
|
||
used to indicate if the schedule should be ignored while vvjoining. The next 3
|
||
bits are used to spevify a priority from 0-7. The following two masks are used
|
||
to get the values.
|
||
|
||
#define FRSCONN_PRIORITY_MASK 0x70000000
|
||
#define NTDSCONN_OPT_IGNORE_SCHEDULE_MASK 0x80000000
|
||
|
||
The ignore scheduel bit is not checked while the connection is in INIT_SYNC state.
|
||
|
||
The priorities are interpretted as shown in the table below.
|
||
|
||
Priority : Behavior
|
||
Class
|
||
|
||
0 : Volatile connections have this priority.
|
||
Highest priority class,
|
||
Try every cxtion in this class,
|
||
skip to next class on communication errors.
|
||
|
||
1-2 : Do not proceed to next class until done with all connections
|
||
of this class.
|
||
|
||
3-4 : Do not proceed to next class until done with at least one
|
||
connection of this class.
|
||
|
||
5-7 : Try every cxtion in this class,
|
||
Skip to next class on communication errors.
|
||
|
||
8 : FRSCONN_MAX_PRIORITY - A priority of '0' in the DS corresponds
|
||
to this priority. We need to do this to maintain old
|
||
behavior for connections that do not have a priority set.
|
||
Do not proceed to next class until done with at least one
|
||
connection from this class or any of the other classes.
|
||
|
||
|
||
The command server forms a sorted list of the connections based on their priorities
|
||
and then starts vvjoins on these connections one at a time. After every vvjoin
|
||
is done it checks if the current class is satisfied based on the table above. If
|
||
it is then cxtions from the next class are picked up and so on. When the last
|
||
class is satisfied the member goes into Online state and is free to join with its
|
||
outbound partners. Remaining connections will continue to vvjoin serially and when
|
||
all are done the member goes out of seeding state.
|
||
|
||
**********************************************************************************
|
||
|
||
Author:
|
||
Sudarshan Chitre - 27th April 2001
|
||
|
||
Environment
|
||
User mode winnt
|
||
|
||
--*/
|
||
|
||
#include <ntreppch.h>
|
||
#pragma hdrstop
|
||
|
||
#undef DEBSUB
|
||
#undef DEBSUB
|
||
#define DEBSUB "INITSYNC:"
|
||
|
||
#include <frs.h>
|
||
#include <tablefcn.h>
|
||
#include <perrepsr.h>
|
||
#include <ntdsapi.h>
|
||
//
|
||
// Struct for the Initial Sync Controller Command Server
|
||
// Contains info about the queues and the threads
|
||
//
|
||
COMMAND_SERVER InitSyncCs;
|
||
ULONG MaxInitSyncCsThreads;
|
||
|
||
|
||
extern PGEN_TABLE ReplicasByGuid;
|
||
//
|
||
// Replica Command server functions called from here.
|
||
//
|
||
VOID
|
||
RcsSubmitReplicaCxtionJoin(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN BOOL Later
|
||
);
|
||
|
||
VOID
|
||
RcsUpdateReplicaSetMember(
|
||
IN PREPLICA Replica
|
||
);
|
||
|
||
VOID
|
||
RcsJoinCxtion(
|
||
IN PCOMMAND_PACKET Cmd
|
||
);
|
||
|
||
VOID
|
||
RcsEmptyPreExistingDir(
|
||
IN PREPLICA Replica
|
||
);
|
||
|
||
BOOL
|
||
RcsSetSysvolReady(
|
||
IN DWORD NewSysvolReady
|
||
);
|
||
|
||
VOID
|
||
RcsReplicaSetRegistry(
|
||
IN PREPLICA Replica
|
||
);
|
||
|
||
|
||
//
|
||
// Send Command server functions called from here.
|
||
//
|
||
VOID
|
||
SndCsSubmitCommPkt2(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN PCHANGE_ORDER_ENTRY Coe,
|
||
IN BOOL SetTimeout,
|
||
IN PCOMM_PACKET CommPkt
|
||
);
|
||
|
||
PCOMM_PACKET
|
||
CommBuildCommPkt(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN ULONG Command,
|
||
IN PGEN_TABLE VVector,
|
||
IN PCOMMAND_PACKET Cmd,
|
||
IN PCHANGE_ORDER_COMMAND Coc
|
||
);
|
||
|
||
|
||
BOOL
|
||
InitSyncPriorityClassSemanticsA(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This function determines if the current state of the cxtions satisfies
|
||
Semantics 'A' for priority class 'PriorityClass'
|
||
|
||
This class is satisfied if all cxtions from this class have completed
|
||
the initial sync.
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
PriorityClass - Priority class to evaluate for.
|
||
|
||
Return Value:
|
||
TRUE if ok to move to next class, FALSE otherwise.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncPriorityClassSemanticsA:"
|
||
|
||
PCXTION Cxtion;
|
||
PVOID Key;
|
||
PGEN_ENTRY Entry;
|
||
DWORD NumInClass = 0;
|
||
DWORD NumComplete = 0;
|
||
DWORD NumCommError = 0;
|
||
DWORD NumNotComplete = 0;
|
||
|
||
|
||
DPRINT1(5,":IS: InitSyncPriorityClassSemanticsA called with priority %d\n", PriorityClass);
|
||
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
|
||
Cxtion = Entry->Data;
|
||
|
||
if (Cxtion->Priority > PriorityClass) {
|
||
//
|
||
// We have done evaluating the class in question.
|
||
//
|
||
break;
|
||
} else if (Cxtion->Priority < PriorityClass) {
|
||
continue;
|
||
}
|
||
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
|
||
DPRINT5(5, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
|
||
NumInClass++;
|
||
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
NumComplete++;
|
||
} else if ((Cxtion->Penalty != 0) ||
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
|
||
//
|
||
// Either there was some comm error trying to talk to this
|
||
// partner or this partner has not sent us packets in a while.
|
||
//
|
||
NumCommError++;
|
||
NumNotComplete++;
|
||
} else {
|
||
NumNotComplete++;
|
||
}
|
||
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
//
|
||
// This class is satisfied if all cxtions from this class have completed
|
||
// the initial sync.
|
||
//
|
||
if (NumInClass == NumComplete) {
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
InitSyncPriorityClassSemanticsB(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This function determines if the current state of the cxtions satisfies
|
||
Semantics 'B' for priority class 'PriorityClass'
|
||
|
||
This class is satisfied if atleast one cxtion from this class has completed
|
||
the initial sync..
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
PriorityClass - Priority class to evaluate for.
|
||
|
||
Return Value:
|
||
TRUE if ok to move to next class, FALSE otherwise.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncPriorityClassSemanticsB:"
|
||
|
||
PCXTION Cxtion;
|
||
PVOID Key;
|
||
PGEN_ENTRY Entry;
|
||
DWORD NumInClass = 0;
|
||
DWORD NumComplete = 0;
|
||
DWORD NumCommError = 0;
|
||
DWORD NumNotComplete = 0;
|
||
|
||
|
||
DPRINT1(5,":IS: InitSyncPriorityClassSemanticsB called with priority %d\n", PriorityClass);
|
||
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
|
||
Cxtion = Entry->Data;
|
||
|
||
if (Cxtion->Priority > PriorityClass) {
|
||
//
|
||
// We have done evaluating the class in question.
|
||
//
|
||
break;
|
||
} else if (Cxtion->Priority < PriorityClass) {
|
||
continue;
|
||
}
|
||
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
|
||
DPRINT5(5, " :IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
|
||
NumInClass++;
|
||
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
NumComplete++;
|
||
} else if ((Cxtion->Penalty != 0) ||
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
|
||
//
|
||
// Either there was some comm error trying to talk to this
|
||
// partner or this partner has not sent us packets in a while.
|
||
//
|
||
NumCommError++;
|
||
NumNotComplete++;
|
||
} else {
|
||
NumNotComplete++;
|
||
}
|
||
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
//
|
||
// This class is satisfied if atleast one cxtion from this class has completed
|
||
// the initial sync..
|
||
//
|
||
if ((NumInClass == 0) || (NumComplete >= 1)) {
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
InitSyncPriorityClassSemanticsC(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This function determines if the current state of the cxtions satisfies
|
||
Semantics 'C' for priority class 'PriorityClass'
|
||
|
||
This class is satisfied when all cxtions in this class have been attempted.
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
PriorityClass - Priority class to evaluate for.
|
||
|
||
Return Value:
|
||
TRUE if ok to move to next class, FALSE otherwise.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncPriorityClassSemanticsC:"
|
||
|
||
PCXTION Cxtion;
|
||
PVOID Key;
|
||
PGEN_ENTRY Entry;
|
||
DWORD NumInClass = 0;
|
||
DWORD NumComplete = 0;
|
||
DWORD NumCommError = 0;
|
||
DWORD NumNotComplete = 0;
|
||
|
||
|
||
DPRINT1(5,":IS: InitSyncPriorityClassSemanticsC called with priority %d\n", PriorityClass);
|
||
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
|
||
Cxtion = Entry->Data;
|
||
|
||
if (Cxtion->Priority > PriorityClass) {
|
||
//
|
||
// We have done evaluating the class in question.
|
||
//
|
||
break;
|
||
} else if (Cxtion->Priority < PriorityClass) {
|
||
continue;
|
||
}
|
||
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
|
||
DPRINT5(5, "Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
|
||
NumInClass++;
|
||
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
NumComplete++;
|
||
} else if ((Cxtion->Penalty != 0) ||
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC))){
|
||
//
|
||
// Either there was some comm error trying to talk to this
|
||
// partner or this partner has not sent us packets in a while.
|
||
//
|
||
NumCommError++;
|
||
NumNotComplete++;
|
||
} else {
|
||
NumNotComplete++;
|
||
}
|
||
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
//
|
||
// This class is satisfied when all cxtions in this class have been attempted.
|
||
//
|
||
if (NumInClass == (NumComplete + NumCommError)) {
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
InitSyncPriorityClassSemanticsD(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This function determines if the current state of the cxtions satisfies
|
||
Semantics 'D' for priority class 'PriorityClass'
|
||
|
||
|
||
This class is satisfied if atleast one cxtion has completed
|
||
the initial sync..
|
||
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
PriorityClass - Priority class to evaluate for.
|
||
|
||
Return Value:
|
||
TRUE if ok to move to next class, FALSE otherwise.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncPriorityClassSemanticsD:"
|
||
|
||
PCXTION Cxtion;
|
||
PVOID Key;
|
||
PGEN_ENTRY Entry;
|
||
DWORD NumTotal = 0;
|
||
DWORD NumComplete = 0;
|
||
|
||
DPRINT1(5,":IS: InitSyncPriorityClassSemanticsB called with priority %d\n", PriorityClass);
|
||
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
|
||
DPRINT5(5, "Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
|
||
NumTotal++;
|
||
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
NumComplete++;
|
||
}
|
||
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
//
|
||
// This class is satisfied if atleast one cxtion has completed
|
||
// the initial sync..
|
||
//
|
||
if ((NumTotal == 0) || (NumComplete >= 1)) {
|
||
return TRUE;
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This is a static array of functions that determines what rules are applied to
|
||
// what priority class. The rules are in form of functions above that return
|
||
// true or false depending on whether the rule is satisfied or not.
|
||
//
|
||
BOOL (*InitSyncPriorityClassSemantic [])(PREPLICA,DWORD) = {
|
||
InitSyncPriorityClassSemanticsC, // Rules for priority class 0
|
||
InitSyncPriorityClassSemanticsA, // Rules for priority class 1
|
||
InitSyncPriorityClassSemanticsA, // Rules for priority class 2
|
||
InitSyncPriorityClassSemanticsB, // Rules for priority class 3
|
||
InitSyncPriorityClassSemanticsB, // Rules for priority class 4
|
||
InitSyncPriorityClassSemanticsC, // Rules for priority class 5
|
||
InitSyncPriorityClassSemanticsC, // Rules for priority class 6
|
||
InitSyncPriorityClassSemanticsC, // Rules for priority class 7
|
||
InitSyncPriorityClassSemanticsD // Rules for priority class 8
|
||
};
|
||
|
||
|
||
BOOL
|
||
InitSyncIsPriorityClassSatisfied(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Use the priority class semantics to decide what is the max class we can work on,
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
PriorityClass - Priority class to evaluate for.
|
||
|
||
Return Value:
|
||
TRUE if ok to move to next class, FALSE otherwise.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncGetMaxAllowedPriority:"
|
||
|
||
BOOL (* PrioritySemanticFunction)(PREPLICA,DWORD);
|
||
|
||
PrioritySemanticFunction = InitSyncPriorityClassSemantic[PriorityClass];
|
||
|
||
return (*PrioritySemanticFunction)(Replica,PriorityClass);
|
||
}
|
||
|
||
|
||
DWORD
|
||
InitSyncGetMaxAllowedPriority(
|
||
IN PREPLICA Replica
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Use the priority class semantics to decide what is the max class we can work on,
|
||
|
||
Arguments:
|
||
Replica - Replica in question.
|
||
|
||
Return Value:
|
||
Maximum allowed priority class.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncGetMaxAllowedPriority:"
|
||
|
||
DWORD MaxAllowedPriorityClass = 0;
|
||
|
||
//
|
||
// Evaluate if each priority is satisfied or not.
|
||
//
|
||
|
||
while ((MaxAllowedPriorityClass <= FRSCONN_MAX_PRIORITY) &&
|
||
InitSyncIsPriorityClassSatisfied(Replica,MaxAllowedPriorityClass)) {
|
||
++MaxAllowedPriorityClass;
|
||
}
|
||
|
||
DPRINT2(4,":IS: Replica %ws - MaxAllowedPriorityClass = %d\n",
|
||
Replica->SetName->Name, MaxAllowedPriorityClass);
|
||
|
||
return MaxAllowedPriorityClass;
|
||
}
|
||
|
||
|
||
DWORD
|
||
InitSyncGetWorkingPriority(
|
||
IN PREPLICA Replica
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Get the current priority class we are working on,
|
||
|
||
Arguments:
|
||
Replica.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncGetWorkingPriority:"
|
||
|
||
DWORD WorkingPriority = 0;
|
||
PVOID Key;
|
||
PCXTION Cxtion;
|
||
|
||
Key = NULL;
|
||
while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
|
||
DPRINT5(5, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
if (WorkingPriority < Cxtion->Priority) {
|
||
WorkingPriority = Cxtion->Priority;
|
||
}
|
||
}
|
||
|
||
DPRINT2(4,":IS: Replica %ws - CurrentWorkingPriorityClass = %d\n",
|
||
Replica->SetName->Name, WorkingPriority);
|
||
|
||
return WorkingPriority;
|
||
}
|
||
|
||
VOID
|
||
InitSyncCsSubmitTransfer(
|
||
IN PCOMMAND_PACKET Cmd,
|
||
IN USHORT Command
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Transfer a request to the initial sync controller command server.
|
||
|
||
Arguments:
|
||
Cmd - Command packet.
|
||
Command - Command to convert to.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCsSubmitTransfer:"
|
||
//
|
||
// Submit a request to allocate staging area
|
||
//
|
||
Cmd->TargetQueue = RsReplica(Cmd)->InitSyncQueue;
|
||
Cmd->Command = Command;
|
||
RsTimeout(Cmd) = 0;
|
||
DPRINT3(4,":IS: Transfer 0x%08x (0x%08x) to %ws\n",
|
||
Command, Cmd, RsReplica(Cmd)->SetName->Name);
|
||
FrsSubmitCommandServer(&InitSyncCs, Cmd);
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncCmdPktCompletionRoutine(
|
||
IN PCOMMAND_PACKET Cmd,
|
||
IN PVOID Arg
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Completion routine for InitSync command packets. Free some specific fields
|
||
and send the command on to the generic command packet completion routine
|
||
for freeing.
|
||
|
||
Arguments:
|
||
Cmd - Command packet.
|
||
Arg - Cmd->CompletionArg
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCmdPktCompletionRoutine:"
|
||
|
||
DPRINT1(4, ":IS: InitSync completion 0x%08x\n", Cmd);
|
||
|
||
FrsFreeGName(RsCxtion(Cmd));
|
||
|
||
//
|
||
// Send the packet on to the generic completion routine for freeing
|
||
//
|
||
FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL);
|
||
FrsCompleteCommand(Cmd, Cmd->ErrorStatus);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncSubmitToInitSyncCs(
|
||
IN PREPLICA Replica,
|
||
IN USHORT Command
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Submit a command to a initial sync command server.
|
||
|
||
Arguments:
|
||
Replica - existing replica
|
||
Command
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncSubmitToInitSyncCs:"
|
||
PCOMMAND_PACKET Cmd;
|
||
|
||
//
|
||
// Allocate a command packet
|
||
//
|
||
Cmd = FrsAllocCommand(Replica->InitSyncQueue, Command);
|
||
FrsSetCompletionRoutine(Cmd, InitSyncCmdPktCompletionRoutine, NULL);
|
||
|
||
//
|
||
// Address of replica set.
|
||
//
|
||
RsReplica(Cmd) = Replica;
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncSubmitToInitSyncCs cmd");
|
||
FrsSubmitCommandServer(&InitSyncCs, Cmd);
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncDelSubmitToInitSyncCs(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN USHORT Command,
|
||
IN DWORD TimeoutInMilliseconds
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Submit a new command to the delayed command server to be submitted to the
|
||
initial sync controller command server after "TimeoutInMilliseconds" ms.
|
||
|
||
Arguments:
|
||
Replica - existing replica
|
||
Cxtion - existing connection
|
||
Command
|
||
TimeoutInMilliseconds - This command will be submitted to the Init Sync
|
||
Command server after these many ms.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncDelSubmitToInitSyncCs:"
|
||
PCOMMAND_PACKET Cmd;
|
||
|
||
//
|
||
// Allocate a command packet
|
||
//
|
||
Cmd = FrsAllocCommand(Replica->InitSyncQueue, Command);
|
||
FrsSetCompletionRoutine(Cmd, InitSyncCmdPktCompletionRoutine, NULL);
|
||
|
||
//
|
||
// Address of replica set.
|
||
//
|
||
RsReplica(Cmd) = Replica;
|
||
|
||
Cmd->TargetQueue = Replica->InitSyncQueue;
|
||
|
||
//
|
||
// CommPkts are used to detect init sync hangs.
|
||
//
|
||
if (Cxtion != NULL) {
|
||
RsCxtion(Cmd) = FrsDupGName(Cxtion->Name);
|
||
RsCommPkts(Cmd) = Cxtion->CommPkts;
|
||
}
|
||
|
||
RsTimeout(Cmd) = TimeoutInMilliseconds;
|
||
//
|
||
// This command will come back to us in in a little bit.
|
||
//
|
||
DPRINT2(4,":IS: Submit Cmd (0x%08x), Command 0x%88x\n", Cmd, Cmd->Command);
|
||
|
||
FrsDelCsSubmitSubmit(&InitSyncCs, Cmd, RsTimeout(Cmd));
|
||
}
|
||
|
||
|
||
BOOL
|
||
InitSyncCsDelCsSubmit(
|
||
IN PCOMMAND_PACKET Cmd,
|
||
IN USHORT Command,
|
||
IN DWORD TimeoutInMilliseconds
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Submit this command to the delayed command server to be submitted to the
|
||
initial sync controller command server after "TimeoutInMilliseconds" ms.
|
||
|
||
Arguments:
|
||
Cmd
|
||
Command
|
||
TimeoutInMilliseconds - This command will be submitted to the Init Sync
|
||
Command server after these many ms.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCsDelCsSubmit:"
|
||
|
||
//
|
||
// Extend the retry time (but not too long)
|
||
//
|
||
|
||
Cmd->Command = Command;
|
||
RsTimeout(Cmd) = TimeoutInMilliseconds;
|
||
//
|
||
// This command will come back to us in in a little bit.
|
||
//
|
||
DPRINT2(4,":IS: Submit Cmd (0x%08x), Command 0x%08x\n", Cmd, Cmd->Command);
|
||
|
||
FrsDelCsSubmitSubmit(&InitSyncCs, Cmd, RsTimeout(Cmd));
|
||
return (TRUE);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
InitSyncBuildMasterList(
|
||
IN PREPLICA Replica
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Build the master list from the Replica->Cxtions table.
|
||
|
||
Arguments:
|
||
Replica
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncBuildMasterList:"
|
||
|
||
PCXTION Cxtion = NULL;
|
||
PVOID Key;
|
||
|
||
FRS_ASSERT(Replica->InitSyncCxtionsMasterList != NULL);
|
||
//
|
||
// If we already have a master list then empty it,
|
||
//
|
||
GTabEmptyTable(Replica->InitSyncCxtionsMasterList,NULL);
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// Take all inbound connections and put them in the master list.
|
||
//
|
||
Key = NULL;
|
||
while (Cxtion = GTabNextDatum(Replica->Cxtions, &Key)) {
|
||
|
||
//
|
||
// Skip the journal conneciton.
|
||
//
|
||
if (Cxtion->JrnlCxtion) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// We are interested in inbound connections only.
|
||
//
|
||
if (!Cxtion->Inbound) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// We clear the paused bit on connections that have completed the initial sync.
|
||
// These connections are free to join anytime. We still need them in the master
|
||
// list as they are needed to verify if the priority class is satisfied.
|
||
//
|
||
if (!CxtionFlagIs(Cxtion, CXTION_FLAGS_INIT_SYNC)) {
|
||
if (CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED)) {
|
||
ClearCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED); // In case it is still set.
|
||
CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Unpaused");
|
||
}
|
||
} else {
|
||
//
|
||
// Initially all connections in INIT_SYNC state are paused.
|
||
//
|
||
SetCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED);
|
||
CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Paused");
|
||
}
|
||
|
||
|
||
GTabInsertEntry(Replica->InitSyncCxtionsMasterList, Cxtion, &Cxtion->Priority, NULL);
|
||
}
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncBuildWorkingList(
|
||
IN PREPLICA Replica,
|
||
IN DWORD PriorityClass,
|
||
IN BOOL ResetList
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Build the working list from the Replica->InitSyncCxtionsMasterList table.
|
||
|
||
Arguments:
|
||
Replica
|
||
PriorityClass - Max Priority Class to pull from master list.
|
||
ResetList - Rebuild the working list.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncBuildWorkingList:"
|
||
|
||
PCXTION Cxtion = NULL;
|
||
PVOID Key;
|
||
PGEN_ENTRY Entry;
|
||
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// If we already have a working list then empty it and create a new one,
|
||
//
|
||
if ((Replica->InitSyncCxtionsWorkingList != NULL) && ResetList) {
|
||
|
||
GTabEmptyTable(Replica->InitSyncCxtionsWorkingList,NULL);
|
||
}
|
||
|
||
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
|
||
//
|
||
// Take all connections from the master list that are <= PriorityClass and
|
||
// put them on the current working list.
|
||
//
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
|
||
Cxtion = Entry->Data;
|
||
|
||
if (Cxtion->Priority <= PriorityClass) {
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
GTabInsertEntry(Replica->InitSyncCxtionsWorkingList, Cxtion, Cxtion->Name->Guid, NULL);
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
}
|
||
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
DPRINT1(4, ":IS: +++ Printing InitSyncCxtionsMasterList for Replica %ws\n", Replica->SetName->Name);
|
||
Key = NULL;
|
||
GTabLockTable(Replica->InitSyncCxtionsMasterList);
|
||
while (Entry = GTabNextEntryNoLock(Replica->InitSyncCxtionsMasterList, &Key)) {
|
||
do {
|
||
Cxtion = Entry->Data;
|
||
DPRINT5(4, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
Entry = Entry->Dups;
|
||
} while ( Entry != NULL );
|
||
}
|
||
GTabUnLockTable(Replica->InitSyncCxtionsMasterList);
|
||
|
||
DPRINT1(4, ":IS: +++ Printing InitSyncCxtionsWorkingList for Replica %ws\n", Replica->SetName->Name);
|
||
|
||
Key = NULL;
|
||
while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
|
||
DPRINT5(4, ":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
}
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncStartSync(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Start the initial sync on this replica.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncStartSync:"
|
||
PREPLICA Replica;
|
||
PCXTION Cxtion;
|
||
PVOID Key;
|
||
DWORD CxtionPriority;
|
||
PGEN_ENTRY Entry;
|
||
DWORD MaxAllowedPriority;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncStartSync entry");
|
||
FRS_PRINT_TYPE(4, Replica);
|
||
|
||
//
|
||
// The replica has to be in SEEDING state to get this command.
|
||
//
|
||
|
||
FRS_ASSERT(BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_SEEDING));
|
||
|
||
//
|
||
// If we have already started a initial sync on this replica then
|
||
// nothing to do here.
|
||
//
|
||
if (Replica->InitSyncCxtionsMasterList != NULL) {
|
||
return;
|
||
}
|
||
|
||
Replica->InitSyncCxtionsMasterList = GTabAllocNumberTable();
|
||
Replica->InitSyncCxtionsWorkingList = GTabAllocTable();
|
||
|
||
|
||
InitSyncBuildMasterList(Replica);
|
||
|
||
MaxAllowedPriority = InitSyncGetMaxAllowedPriority(Replica);
|
||
|
||
InitSyncBuildWorkingList(Replica,MaxAllowedPriority,TRUE);
|
||
|
||
InitSyncCsSubmitTransfer(Cmd, CMD_INITSYNC_JOIN_NEXT);
|
||
|
||
InitSyncSubmitToInitSyncCs(Replica,CMD_INITSYNC_KEEP_ALIVE);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncKeepAlive(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This command keeps coming back to us until all cxtions have completed the.
|
||
initial sync. It is used to detect hangs.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncKeepAlive:"
|
||
PREPLICA Replica;
|
||
PVOID Key;
|
||
PCXTION Cxtion;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncKeepAlive entry");
|
||
|
||
//
|
||
// Stop calling keepalive once we are out of seeding state.
|
||
//
|
||
if (!BooleanFlagOn(Replica->CnfFlags,CONFIG_FLAG_SEEDING)) {
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Do not call JOIN_NEXT as long as there is one cxtion that is not paused and
|
||
// not hung,
|
||
//
|
||
Key = NULL;
|
||
while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED) &&
|
||
CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) &&
|
||
!CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
|
||
DPRINT5(5, ":IS: Working on - Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
Cxtion->Priority,Cxtion->Penalty, Cxtion->CommPkts, Cxtion->Name->Name,
|
||
(CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
goto RETURN;
|
||
}
|
||
}
|
||
|
||
InitSyncSubmitToInitSyncCs(Replica,CMD_INITSYNC_JOIN_NEXT);
|
||
|
||
RETURN:
|
||
|
||
InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_KEEP_ALIVE, 240 * 1000);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncCompletedOnePass(
|
||
IN PREPLICA Replica
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initial sync completed one pass for this replica. Take the appropriate action.
|
||
Share out the sysvols here.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCompletedOnePass:"
|
||
|
||
|
||
DPRINT1(4,":IS: Replica %ws has successfully completed one pass of Seeding.\n",Replica->SetName->Name);
|
||
|
||
SetFlag(Replica->CnfFlags, CONFIG_FLAG_ONLINE);
|
||
Replica->NeedsUpdate = TRUE;
|
||
RcsUpdateReplicaSetMember(Replica);
|
||
|
||
//
|
||
// Set sysvol ready to 1 if this is the sysvol replica set
|
||
// and we haven't shared out the sysvol yet.
|
||
//
|
||
if (FRS_RSTYPE_IS_SYSVOL(Replica->ReplicaSetType) &&
|
||
!Replica->IsSysvolReady) {
|
||
RcsReplicaSetRegistry(Replica);
|
||
Replica->IsSysvolReady = RcsSetSysvolReady(1);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncCompleted(
|
||
IN PREPLICA Replica
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initial sync completed for this replica. Take the appropriate action.
|
||
Get out of seeding state.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCompleted:"
|
||
|
||
|
||
ClearFlag(Replica->CnfFlags, CONFIG_FLAG_SEEDING);
|
||
Replica->NeedsUpdate = TRUE;
|
||
RcsUpdateReplicaSetMember(Replica);
|
||
|
||
//
|
||
// Half-hearted attempt to delete the empty directories from
|
||
// the preexisting directory
|
||
//
|
||
RcsEmptyPreExistingDir(Replica);
|
||
|
||
//
|
||
// Cleanup the state associated with the InitSync state.
|
||
//
|
||
DPRINT1(4,":IS: Replica %ws has successfully completed Seeding.\n",Replica->SetName->Name);
|
||
|
||
Replica->InitSyncCxtionsMasterList = GTabFreeTable(Replica->InitSyncCxtionsMasterList,NULL);
|
||
Replica->InitSyncCxtionsWorkingList = GTabFreeTable(Replica->InitSyncCxtionsWorkingList,NULL);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncJoinNext(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Join the next in line.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncJoinNext:"
|
||
PREPLICA Replica;
|
||
PCXTION Cxtion;
|
||
// PCXTION NewCxtion;
|
||
PVOID Key;
|
||
DWORD CxtionPriority;
|
||
PGEN_ENTRY Entry;
|
||
DWORD NumUnPaused;
|
||
DWORD MasterMaxAllowedPriority;
|
||
DWORD CurrentWorkingPriority;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncJoinNext entry");
|
||
FRS_PRINT_TYPE(4, Replica);
|
||
|
||
//
|
||
// If the replica has already seeded then we do not have anything to process.
|
||
//
|
||
if (!BooleanFlagOn(Replica->CnfFlags, CONFIG_FLAG_SEEDING)) {
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get the maximum allowed priority by scanning the master list.
|
||
// We need to do this because new connections might have been added to
|
||
// the master list that have higher priority than the current working priority
|
||
// of the working list.
|
||
//
|
||
MasterMaxAllowedPriority = InitSyncGetMaxAllowedPriority(Replica);
|
||
CurrentWorkingPriority = InitSyncGetWorkingPriority(Replica);
|
||
|
||
if (MasterMaxAllowedPriority < CurrentWorkingPriority) {
|
||
//
|
||
// One or more new connections with lower priority might have been added.
|
||
// reset the working list to reflect that.
|
||
//
|
||
InitSyncBuildWorkingList(Replica,MasterMaxAllowedPriority,TRUE);
|
||
|
||
} else if (MasterMaxAllowedPriority > CurrentWorkingPriority) {
|
||
//
|
||
// Connections from next priority class can be added to the working list.
|
||
//
|
||
InitSyncBuildWorkingList(Replica,MasterMaxAllowedPriority,FALSE);
|
||
}
|
||
|
||
if (MasterMaxAllowedPriority > FRSCONN_MAX_PRIORITY) {
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncJoinNext completed one pass");
|
||
InitSyncCompletedOnePass(Replica);
|
||
}
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
Key = NULL;
|
||
NumUnPaused = 0;
|
||
while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
|
||
|
||
//
|
||
// If the cxtion has already completed the initial sync then skip it.
|
||
//
|
||
if (!CxtionFlagIs(Cxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
continue;
|
||
}
|
||
|
||
++NumUnPaused;
|
||
|
||
if (CxtionFlagIs(Cxtion,CXTION_FLAGS_PAUSED)) {
|
||
ClearCxtionFlag(Cxtion, CXTION_FLAGS_PAUSED);
|
||
CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC Unpaused");
|
||
}
|
||
|
||
if (CxtionFlagIs(Cxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
|
||
ClearCxtionFlag(Cxtion, CXTION_FLAGS_HUNG_INIT_SYNC);
|
||
CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC clear hung state");
|
||
}
|
||
|
||
CXTION_STATE_TRACE(3, Cxtion, Replica, 0, "F, INITSYNC sending NEED_JOIN");
|
||
|
||
//
|
||
// Submit a cmd with the replica command server to send need_join over this
|
||
// cxtion,
|
||
//
|
||
RcsSubmitReplicaCxtionJoin(Replica, Cxtion, FALSE);
|
||
//
|
||
// Submit a cmd with the init sync command server to track the progress of this
|
||
// cxtion. This cmd will look for hung init sync.
|
||
//
|
||
InitSyncDelSubmitToInitSyncCs(Replica,Cxtion,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
|
||
}
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// Done synching all connections. Reset seeding state.
|
||
//
|
||
if (NumUnPaused == 0) {
|
||
|
||
InitSyncCompleted(Replica);
|
||
RcsSubmitTransferToRcs(Cmd, CMD_VVJOIN_DONE_UNJOIN);
|
||
|
||
} else {
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncCheckProgress(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Check the progress of this connectio. Put it in hung state if no packets have
|
||
been received on this connection for some time.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCheckProgress:"
|
||
PREPLICA Replica;
|
||
PCXTION InCxtion;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
//
|
||
// Retire this command if we are out of seeding state.
|
||
//
|
||
if (!BooleanFlagOn(Replica->CnfFlags,CONFIG_FLAG_SEEDING)) {
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
return;
|
||
}
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncCheckProgress entry");
|
||
|
||
//
|
||
// Find and check the cxtion
|
||
//
|
||
InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);
|
||
|
||
if (InCxtion != NULL) {
|
||
DPRINT5(5,":IS: Priority = %d, Penalty = %d, CommPkts = %d : %ws - %ws\n",
|
||
InCxtion->Priority,InCxtion->Penalty, InCxtion->CommPkts, InCxtion->Name->Name,
|
||
(CxtionFlagIs(InCxtion,CXTION_FLAGS_INIT_SYNC) ? L"Init sync pending" :
|
||
L"Init sync done"));
|
||
}
|
||
|
||
//
|
||
// Retire this command if the cxtion has been paused or if the connection is
|
||
// out of seeding state.
|
||
//
|
||
if (!InCxtion || CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED) ||
|
||
!CxtionFlagIs(InCxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
|
||
if (CxtionFlagIs(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
|
||
ClearCxtionFlag(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC);
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC clear hung state");
|
||
}
|
||
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
goto RETURN;
|
||
}
|
||
|
||
//
|
||
// Do not declare hung if the schedule is off and we are asked to follow schedule.
|
||
//
|
||
if (!NTDSCONN_IGNORE_SCHEDULE(InCxtion->Options) &&
|
||
CxtionFlagIs(InCxtion,CXTION_FLAGS_SCHEDULE_OFF)){
|
||
InitSyncCsDelCsSubmit(Cmd,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
|
||
goto RETURN;
|
||
}
|
||
|
||
if (RsCommPkts(Cmd) != InCxtion->CommPkts) {
|
||
RsCommPkts(Cmd) = InCxtion->CommPkts;
|
||
InitSyncCsDelCsSubmit(Cmd,CMD_INITSYNC_CHECK_PROGRESS,300*1000);
|
||
} else {
|
||
SetCxtionFlag(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC);
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC Hung");
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
}
|
||
|
||
RETURN:
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncStartJoin(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
We have received start join from a inbound partner. We send need joins to all the
|
||
connections in the current class. When we get start_join from one of the partners
|
||
we pick that one to vvjoin with first and put the remaining connections in the
|
||
paused state.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncStartJoin:"
|
||
PREPLICA Replica;
|
||
PCXTION InCxtion;
|
||
PCXTION Cxtion;
|
||
PCXTION NewCxtion;
|
||
PVOID Key;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
REPLICA_STATE_TRACE(3, Cmd, Replica, 0, "F, InitSyncStartJoin entry");
|
||
FRS_PRINT_TYPE(4, Replica);
|
||
|
||
//
|
||
// Find and check the cxtion
|
||
//
|
||
InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);
|
||
|
||
if (!InCxtion) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Ignore the start join if this connection is paused.
|
||
//
|
||
if (CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED)) {
|
||
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, Paused - ignore start join");
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
} else {
|
||
//
|
||
// If we were waiting for our partner to respond or think we
|
||
// have already joined then either start the join process or
|
||
// resend our join info.
|
||
//
|
||
if (CxtionStateIs(InCxtion, CxtionStateUnjoined) ||
|
||
CxtionStateIs(InCxtion, CxtionStateJoined)) {
|
||
if (CxtionStateIs(InCxtion, CxtionStateUnjoined)) {
|
||
SetCxtionState(InCxtion, CxtionStateStart);
|
||
}
|
||
//
|
||
// Start the join process or resend the join info
|
||
//
|
||
// This is the first inbound partner that responded to the
|
||
// NEED_JOIN. Start vvjoin with this inbound partner and
|
||
// pause all other cxtions in the working list that are still in
|
||
// INIT_SYNC state.
|
||
//
|
||
|
||
Key = NULL;
|
||
while (Cxtion = GTabNextDatum(Replica->InitSyncCxtionsWorkingList, &Key)) {
|
||
DPRINT2(4, "P - %d : %ws\n", Cxtion->Priority, Cxtion->Name->Name);
|
||
FRS_PRINT_TYPE(4, Cxtion);
|
||
|
||
NewCxtion = GTabLookup(Replica->Cxtions,Cxtion->Name->Guid,NULL);
|
||
if (NewCxtion != NULL) {
|
||
if (!GUIDS_EQUAL(NewCxtion->Name->Guid,InCxtion->Name->Guid) &&
|
||
CxtionFlagIs(NewCxtion,CXTION_FLAGS_INIT_SYNC)) {
|
||
SetCxtionFlag(NewCxtion,CXTION_FLAGS_PAUSED);
|
||
CXTION_STATE_TRACE(3, NewCxtion, Replica, 0, "F, INITSYNC Paused");
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Clear the CXTION_FLAGS_HUNG_INIT_SYNC flag if it was set.
|
||
//
|
||
if (CxtionFlagIs(InCxtion,CXTION_FLAGS_HUNG_INIT_SYNC)) {
|
||
ClearCxtionFlag(InCxtion, CXTION_FLAGS_HUNG_INIT_SYNC);
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC clear hung state");
|
||
}
|
||
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, RcsJoinCxtion call");
|
||
RcsJoinCxtion(Cmd);
|
||
} else {
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, Cannot start join");
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncVvJoinDone(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
vvjoin is done join next one.
|
||
|
||
Arguments:
|
||
Cmd.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncVvJoinDone:"
|
||
PREPLICA Replica;
|
||
PCXTION InCxtion;
|
||
PCOMM_PACKET CPkt;
|
||
|
||
Replica = RsReplica(Cmd);
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// Find and check the cxtion
|
||
//
|
||
InCxtion = GTabLookupNoLock(Replica->Cxtions, RsCxtion(Cmd)->Guid, NULL);
|
||
|
||
if (!InCxtion) {
|
||
return;
|
||
}
|
||
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC complete");
|
||
|
||
//
|
||
// This connection has completed initial sync.
|
||
// It is OK to join this connection at will now.
|
||
//
|
||
if (CxtionFlagIs(InCxtion,CXTION_FLAGS_PAUSED)) {
|
||
ClearCxtionFlag(InCxtion, CXTION_FLAGS_PAUSED);
|
||
CXTION_STATE_TRACE(3, InCxtion, Replica, 0, "F, INITSYNC Unpaused");
|
||
}
|
||
ClearCxtionFlag(InCxtion, CXTION_FLAGS_INIT_SYNC);
|
||
|
||
//
|
||
// If this is volatile connection unjoin our upstream partner.
|
||
//
|
||
//
|
||
if (CxtionFlagIs(InCxtion, CXTION_FLAGS_VOLATILE)) {
|
||
CPkt = CommBuildCommPkt(Replica, InCxtion, CMD_UNJOIN_REMOTE, NULL, NULL, NULL);
|
||
SndCsSubmitCommPkt2(Replica, InCxtion, NULL, FALSE, CPkt);
|
||
}
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
InitSyncCsSubmitTransfer(Cmd,CMD_INITSYNC_JOIN_NEXT);
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
MainInitSyncCs(
|
||
PVOID Arg
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Entry point for a thread serving the initial sync controller command server.
|
||
|
||
Arguments:
|
||
Arg - thread
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "MainInitSyncCs:"
|
||
DWORD WStatus = ERROR_SUCCESS;
|
||
PCOMMAND_PACKET Cmd;
|
||
PFRS_QUEUE IdledQueue;
|
||
PFRS_THREAD FrsThread = (PFRS_THREAD)Arg;
|
||
|
||
//
|
||
// Thread is pointing at the correct command server
|
||
//
|
||
FRS_ASSERT(FrsThread->Data == &InitSyncCs);
|
||
FrsThread->Exit = ThSupExitWithTombstone;
|
||
|
||
DPRINT(4, "Initial Sync command server has started.\n");
|
||
|
||
//
|
||
// Try-Finally
|
||
//
|
||
try {
|
||
|
||
//
|
||
// Capture exception.
|
||
//
|
||
try {
|
||
|
||
//
|
||
// Pull entries off the queue and process them
|
||
//
|
||
cant_exit_yet:
|
||
while (Cmd = FrsGetCommandServerIdled(&InitSyncCs, &IdledQueue)) {
|
||
|
||
switch (Cmd->Command) {
|
||
|
||
case CMD_INITSYNC_START_SYNC:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncStartSync(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_JOIN_NEXT:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncJoinNext(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_START_JOIN:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncStartJoin(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_VVJOIN_DONE:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncVvJoinDone(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_KEEP_ALIVE:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncKeepAlive(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_CHECK_PROGRESS:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
InitSyncCheckProgress(Cmd);
|
||
break;
|
||
|
||
case CMD_INITSYNC_UNJOIN:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
|
||
InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_JOINED,10*1000);
|
||
break;
|
||
|
||
case CMD_INITSYNC_JOINED:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
|
||
InitSyncCsDelCsSubmit(Cmd, CMD_INITSYNC_COMM_TIMEOUT,10*1000);
|
||
break;
|
||
|
||
case CMD_INITSYNC_COMM_TIMEOUT:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
REPLICA_STATE_TRACE(3, Cmd, RsReplica(Cmd), 0, "F, processing");
|
||
break;
|
||
|
||
default:
|
||
DPRINT3(4, "InitSync: Received Cmd 0x%08x Command 0x%08x, TargetQueue 0x%08x\n", Cmd, Cmd->Command, Cmd->TargetQueue);
|
||
FrsCompleteCommand(Cmd, ERROR_INVALID_FUNCTION);
|
||
break;
|
||
}
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
}
|
||
//
|
||
// Exit
|
||
//
|
||
FrsExitCommandServer(&InitSyncCs, FrsThread);
|
||
goto cant_exit_yet;
|
||
|
||
//
|
||
// Get exception status.
|
||
//
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
}
|
||
|
||
|
||
} finally {
|
||
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
if (AbnormalTermination()) {
|
||
WStatus = ERROR_OPERATION_ABORTED;
|
||
}
|
||
}
|
||
|
||
DPRINT_WS(4, "MainInitSyncCs finally.", WStatus);
|
||
|
||
//
|
||
// Trigger FRS shutdown if we terminated abnormally.
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
DPRINT(4, "MainInitSyncCs terminated abnormally, forcing service shutdown.\n");
|
||
FrsIsShuttingDown = TRUE;
|
||
SetEvent(ShutDownEvent);
|
||
}
|
||
}
|
||
|
||
return (WStatus);
|
||
}
|
||
|
||
|
||
VOID
|
||
InitSyncCsInitialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initialize the Initial Sync Controller command server.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "InitSyncCsInitialize:"
|
||
//
|
||
// Initialize the command server
|
||
//
|
||
|
||
CfgRegReadDWord(FKC_MAX_INITSYNCCS_THREADS, NULL, 0, &MaxInitSyncCsThreads);
|
||
|
||
FrsInitializeCommandServer(&InitSyncCs, MaxInitSyncCsThreads, L"InitSyncCs", MainInitSyncCs);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
VOID
|
||
ShutDownInitSyncCs(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Shutdown the Initial Sync Controller command server.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "ShutDownInitSyncCs:"
|
||
|
||
PVOID Key;
|
||
PREPLICA Replica;
|
||
|
||
//
|
||
// Rundown all known queues. New queue entries will bounce.
|
||
//
|
||
Key = NULL;
|
||
while (Replica = GTabNextDatum(ReplicasByGuid, &Key)) {
|
||
REPLICA_STATE_TRACE(3, NULL, Replica, 0, "F, Rundown InitSync cmd srv");
|
||
if (Replica->InitSyncQueue != NULL) {
|
||
FrsRunDownCommandServer(&InitSyncCs, Replica->InitSyncQueue);
|
||
}
|
||
}
|
||
|
||
FrsRunDownCommandServer(&InitSyncCs, &InitSyncCs.Queue);
|
||
}
|
||
|
||
|