1346 lines
38 KiB
C
1346 lines
38 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
frssndcs.c
|
||
|
||
Abstract:
|
||
|
||
This command server sends packets over the comm layer.
|
||
|
||
SndCsInitialize: - Alloc handle table, read reg pars, Create/init SndCs,
|
||
Alloc comm queue array, attach comm queues to SndCs
|
||
control queue.
|
||
SndCsUnInitialize: - Free handle table, delete comm queues.
|
||
SndCsShutDown: - Run Down Comm queues, Run down SndCs.
|
||
|
||
SndCsExit: - Cancel all RPC calls on the FrsThread->Handle.
|
||
|
||
SndCsAssignCommQueue: - Assign a comm queue to cxtion.
|
||
SndCsCreateCxtion: - Create a join Guid for connection, assign comm queue
|
||
SndCsDestroyCxtion: - Invalidate cxtion join guid and umbind RPC handle
|
||
SndCsUnBindHandles: - Unbind all RPC handles associated with given target server
|
||
|
||
SndCsCxtionTimeout: - No activity on this connection, request an UNJOIN.
|
||
SndCsCheckCxtion: - Check that the join guid is still valid, set the timer if needed.
|
||
SndCsDispatchCommError: - Transfer a comm packet to the appropriate command server
|
||
for error processing.
|
||
|
||
SndCsCommTimeout: - Cancel hung RPC send threads and age the RPC handle cache.
|
||
|
||
SndCsSubmitCommPkt: - Submit comm packet to the Send Cs comm queue for the
|
||
target Cxtion.
|
||
SndCsSubmitCommPkt2: - Ditto (with arg variation)
|
||
SndCsSubmitCmd: - Used for submitting a CMD_JOINING_AFTER_FLUSH to a Send Cs queue.
|
||
|
||
SndCsMain: - Send command server processing loop. Dispatches requests
|
||
off the comm queues.
|
||
|
||
Author:
|
||
Billy J. Fuller 28-May-1997
|
||
|
||
Environment
|
||
User mode winnt
|
||
|
||
--*/
|
||
|
||
#include <ntreppch.h>
|
||
#pragma hdrstop
|
||
|
||
#include <frs.h>
|
||
#include <perrepsr.h>
|
||
|
||
//
|
||
// Struct for the Send Command Server
|
||
// Contains info about the queues and the threads
|
||
//
|
||
COMMAND_SERVER SndCs;
|
||
|
||
//
|
||
// Comm queues are attached to the SndCs command server above.
|
||
// A cxtion is assigned a comm queue when its creates or assigns a join guid
|
||
// (session). The cxtion uses that comm queue for as long as the join guid is
|
||
// valid. This insures packet order through the comm layer.
|
||
//
|
||
// Reserve comm queue 0 for join requests to partners whose previous rpc call
|
||
// took longer than MinJoinRetry to error off.
|
||
//
|
||
#define MAX_COMM_QUEUE_NUMBER (32)
|
||
|
||
FRS_QUEUE CommQueues[MAX_COMM_QUEUE_NUMBER];
|
||
|
||
DWORD CommQueueRoundRobin = 1;
|
||
|
||
//
|
||
// Cxtion times out if partner response takes too long
|
||
//
|
||
DWORD CommTimeoutInMilliSeconds; // timeout in msec
|
||
ULONGLONG CommTimeoutCheck; // timeout in 100 nsec units
|
||
|
||
//
|
||
// rpc handle cache.
|
||
//
|
||
// Each entry contains a connection guid and a list of handles protected by
|
||
// a lock. Each comm packet sent to a given connection first tries to get
|
||
// previously bound handle from the handle cache, creating a new one if necc.
|
||
//
|
||
// Note: DAO, I don't understand why this is needed, Mario says RPC already
|
||
// allows multiple RPC calls on the same binding handle. Ask Billy.
|
||
//
|
||
PGEN_TABLE GHandleTable;
|
||
|
||
|
||
|
||
VOID
|
||
CommCompletionRoutine(
|
||
PCOMMAND_PACKET,
|
||
PVOID
|
||
);
|
||
|
||
VOID
|
||
FrsCreateJoinGuid(
|
||
OUT GUID *OutGuid
|
||
);
|
||
|
||
VOID
|
||
FrsDelCsCompleteSubmit(
|
||
IN PCOMMAND_PACKET DelCmd,
|
||
IN ULONG Timeout
|
||
);
|
||
|
||
PFRS_THREAD
|
||
ThSupEnumThreads(
|
||
PFRS_THREAD FrsThread
|
||
);
|
||
|
||
VOID
|
||
SndCsUnBindHandles(
|
||
IN PGNAME To
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Unbind any handles associated with To in preparation for "join".
|
||
|
||
Arguments:
|
||
To
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsUnBindHandles:"
|
||
PGHANDLE GHandle;
|
||
PHANDLE_LIST HandleList;
|
||
|
||
DPRINT1(4, "Unbinding all handles for %ws\n", To->Name);
|
||
|
||
//
|
||
// Find the anchor for all of the bound, rpc handles to the server "To"
|
||
//
|
||
GHandle = GTabLookup(GHandleTable, To->Guid, NULL);
|
||
if (GHandle == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Unbind the handles
|
||
//
|
||
EnterCriticalSection(&GHandle->Lock);
|
||
|
||
while (HandleList = GHandle->HandleList) {
|
||
GHandle->HandleList = HandleList->Next;
|
||
FrsRpcUnBindFromServer(&HandleList->RpcHandle);
|
||
FrsFree(HandleList);
|
||
}
|
||
|
||
LeaveCriticalSection(&GHandle->Lock);
|
||
}
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
SndCsAssignCommQueue(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
A cxtion is assigned a comm queue when its creates or assigns a join guid
|
||
(session). The cxtion uses that comm queue for as long as the join guid is
|
||
valid. This insures packet order through the comm layer. Old packets have
|
||
an invalid join guid and are either not sent or ignored on the receiving side.
|
||
|
||
Reserve comm queue 0 for join requests to partners whose previous rpc call
|
||
took longer than MinJoinRetry to error off.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
Comm queue number (1 .. MAX_COMM_QUEUE_NUMBER - 1).
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsAssignCommQueue:"
|
||
DWORD CommQueueIndex;
|
||
|
||
//
|
||
// Pseudo round robin. Avoid locks by checking bounds.
|
||
//
|
||
CommQueueIndex = CommQueueRoundRobin++;
|
||
if (CommQueueRoundRobin >= MAX_COMM_QUEUE_NUMBER) {
|
||
CommQueueRoundRobin = 1;
|
||
}
|
||
if (CommQueueIndex >= MAX_COMM_QUEUE_NUMBER) {
|
||
CommQueueIndex = 1;
|
||
}
|
||
|
||
DPRINT1(4, "Assigned Comm Queue %d\n", CommQueueIndex);
|
||
return CommQueueIndex;
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsCreateCxtion(
|
||
IN OUT PCXTION Cxtion
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Create a new join guid and comm queue for this cxtion.
|
||
|
||
Assumes: Caller has CXTION_TABLE lock.
|
||
|
||
Arguments:
|
||
Cxtion
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsCreateCxtion:"
|
||
|
||
DPRINT1(4, ":X: %ws: Creating join guid.\n", Cxtion->Name->Name);
|
||
|
||
FrsCreateJoinGuid(&Cxtion->JoinGuid);
|
||
|
||
SetCxtionFlag(Cxtion, CXTION_FLAGS_JOIN_GUID_VALID |
|
||
CXTION_FLAGS_UNJOIN_GUID_VALID);
|
||
|
||
//
|
||
// Assign a comm queue. A cxtion must use the same comm queue for a given
|
||
// session (join guid) to maintain packet order. Old packets have an
|
||
// invalid join guid and are either not sent or ignored on the receiving side.
|
||
//
|
||
Cxtion->CommQueueIndex = SndCsAssignCommQueue();
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsDestroyCxtion(
|
||
IN PCXTION Cxtion,
|
||
IN DWORD CxtionFlags
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Destroy a cxtion's join guid and unbind handles.
|
||
|
||
Assumes: Caller has CXTION_TABLE lock.
|
||
|
||
Arguments:
|
||
Cxtion - Cxtion being destroyed.
|
||
CxtionFlags - Caller specifies which state flags are cleared.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsDestroyCxtion:"
|
||
|
||
//
|
||
// Nothing to do
|
||
//
|
||
if (Cxtion == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Invalidate the join guid. Packets to be sent to this connection are
|
||
// errored off because of their invalid join guid.
|
||
// Packets received are errored off for the same reason.
|
||
//
|
||
DPRINT2(4, ":X: %ws: Destroying join guid (%08x)\n", Cxtion->Name->Name, CxtionFlags);
|
||
|
||
ClearCxtionFlag(Cxtion, CxtionFlags |
|
||
CXTION_FLAGS_JOIN_GUID_VALID |
|
||
CXTION_FLAGS_TIMEOUT_SET);
|
||
|
||
//
|
||
// Unbind the old handles. They aren't very useful without a
|
||
// valid join guid. This function is called out of FrsFreeType() when
|
||
// freeing a cxtion; hence the partner field may not be filled in. Don't
|
||
// unbind handles if there is no partner.
|
||
//
|
||
if ((Cxtion->Partner != NULL) &&
|
||
(Cxtion->Partner->Guid != NULL) &&
|
||
!Cxtion->JrnlCxtion) {
|
||
SndCsUnBindHandles(Cxtion->Partner);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsCxtionTimeout(
|
||
IN PCOMMAND_PACKET TimeoutCmd,
|
||
IN PVOID Ignore
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The cxtion has not received a reply from its partner for quite
|
||
awhile. Unjoin the cxtion.
|
||
|
||
Arguments:
|
||
|
||
TimeoutCmd -- Timeout command packet
|
||
Ignore
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsCxtionTimeout:"
|
||
PREPLICA Replica;
|
||
PCXTION Cxtion;
|
||
|
||
//
|
||
// Not a true timeout; just some error condition. Probably
|
||
// shutdown. Ignore it.
|
||
//
|
||
if (!WIN_SUCCESS(TimeoutCmd->ErrorStatus)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Pull out params from command packet
|
||
//
|
||
Replica = SRReplica(TimeoutCmd);
|
||
Cxtion = SRCxtion(TimeoutCmd);
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// The timeout is associated with a different join guid; ignore it
|
||
//
|
||
if (!CxtionFlagIs(Cxtion, CXTION_FLAGS_TIMEOUT_SET) ||
|
||
!CxtionFlagIs(Cxtion, CXTION_FLAGS_JOIN_GUID_VALID) ||
|
||
!GUIDS_EQUAL(&SRJoinGuid(TimeoutCmd), &Cxtion->JoinGuid)) {
|
||
ClearCxtionFlag(Cxtion, CXTION_FLAGS_TIMEOUT_SET);
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Increment the Communication Timeouts counter for both the
|
||
// replica set and the connection.
|
||
//
|
||
PM_INC_CTR_REPSET(Replica, CommTimeouts, 1);
|
||
PM_INC_CTR_CXTION(Cxtion, CommTimeouts, 1);
|
||
|
||
ClearCxtionFlag(Cxtion, CXTION_FLAGS_TIMEOUT_SET);
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
|
||
RcsSubmitReplicaCxtion(Replica, Cxtion, CMD_UNJOIN);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOL
|
||
SndCsCheckCxtion(
|
||
IN PCOMMAND_PACKET Cmd
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Check that the join guid is still valid, set the timer if needed.
|
||
|
||
Arguments:
|
||
Cmd
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsCheckCxtion:"
|
||
PREPLICA Replica;
|
||
PCXTION Cxtion;
|
||
ULONG WaitTime;
|
||
|
||
Replica = SRReplica(Cmd);
|
||
Cxtion = SRCxtion(Cmd);
|
||
|
||
//
|
||
// Nothing to check
|
||
//
|
||
if (!SRJoinGuidValid(Cmd) &&
|
||
!SRSetTimeout(Cmd) &&
|
||
!VOLATILE_OUTBOUND_CXTION(Cxtion)) {
|
||
return TRUE;
|
||
}
|
||
|
||
LOCK_CXTION_TABLE(Replica);
|
||
|
||
//
|
||
// Check that our session id (join guid) is still valid
|
||
//
|
||
if (SRJoinGuidValid(Cmd)) {
|
||
|
||
if (!CxtionFlagIs(Cxtion, CXTION_FLAGS_JOIN_GUID_VALID) ||
|
||
!GUIDS_EQUAL(&SRJoinGuid(Cmd), &Cxtion->JoinGuid)) {
|
||
DPRINT1(4, "++ %ws: Join guid is INVALID.\n", Cxtion->Name->Name);
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If our partner doesn't respond in time, unjoin the cxtion.
|
||
//
|
||
// *** NOTE *** Since the following is using state in the Cxtion struct
|
||
// to record timeout info, only one fetch request can be active at a time.
|
||
// Look at the timeout code to see what it will do.
|
||
//
|
||
// :SP1: Volatile connection cleanup.
|
||
//
|
||
// A volatile connection is used to seed sysvols after dcpromo. If there
|
||
// is inactivity on a volatile outbound connection for more than
|
||
// FRS_VOLATILE_CONNECTION_MAX_IDLE_TIME then this connection is unjoined.
|
||
// An unjoin on a volatile outbound connection triggers a delete on that
|
||
// connection. This is to prevent the case where staging files are kept
|
||
// for ever on the parent for a volatile connection.
|
||
//
|
||
if (SRSetTimeout(Cmd) || VOLATILE_OUTBOUND_CXTION(Cxtion)) {
|
||
|
||
if (!CxtionFlagIs(Cxtion, CXTION_FLAGS_TIMEOUT_SET)) {
|
||
|
||
if (Cxtion->CommTimeoutCmd == NULL) {
|
||
Cxtion->CommTimeoutCmd = FrsAllocCommand(NULL, CMD_UNKNOWN);
|
||
FrsSetCompletionRoutine(Cxtion->CommTimeoutCmd, SndCsCxtionTimeout, NULL);
|
||
|
||
SRCxtion(Cxtion->CommTimeoutCmd) = Cxtion;
|
||
SRReplica(Cxtion->CommTimeoutCmd) = Replica;
|
||
}
|
||
|
||
//
|
||
// Update join guid, cmd packet may be left over from previous join.
|
||
//
|
||
COPY_GUID(&SRJoinGuid(Cxtion->CommTimeoutCmd), &Cxtion->JoinGuid);
|
||
|
||
WaitTime = (VOLATILE_OUTBOUND_CXTION(Cxtion) ?
|
||
FRS_VOLATILE_CONNECTION_MAX_IDLE_TIME : CommTimeoutInMilliSeconds);
|
||
|
||
WaitSubmit(Cxtion->CommTimeoutCmd, WaitTime, CMD_DELAYED_COMPLETE);
|
||
|
||
SetCxtionFlag(Cxtion, CXTION_FLAGS_TIMEOUT_SET);
|
||
}
|
||
}
|
||
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
DWORD
|
||
SndCsDispatchCommError(
|
||
PCOMM_PACKET CommPkt
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Transfering a comm packet to the appropriate command server
|
||
for error processing.
|
||
|
||
Arguments:
|
||
CommPkt - comm packet that couldn't be sent
|
||
|
||
Return Value:
|
||
WIN32 Status
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsDispatchCommError:"
|
||
DWORD WStatus;
|
||
|
||
DPRINT1(4, "Comm pkt in error %08x\n", CommPkt);
|
||
|
||
switch(CommPkt->CsId) {
|
||
|
||
case CS_RS:
|
||
WStatus = RcsSubmitCommPktWithErrorToRcs(CommPkt);
|
||
break;
|
||
|
||
default:
|
||
DPRINT1(0, "Unknown command server id %d\n", CommPkt->CsId);
|
||
WStatus = ERROR_INVALID_FUNCTION;
|
||
}
|
||
|
||
DPRINT1_WS(0, "Could not process comm pkt with error %08x;", CommPkt, WStatus);
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
SndCsExit(
|
||
PFRS_THREAD FrsThread
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Immediate cancel of all outstanding RPC calls for the thread
|
||
identified by FrsThread. Set the tombstone to 5 seconds from
|
||
now. If this thread does not exit within that time, any calls
|
||
to ThSupWaitThread() will return a timeout error.
|
||
|
||
Arguments:
|
||
FrsThread
|
||
|
||
Return Value:
|
||
ERROR_SUCCESS
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsExit:"
|
||
|
||
if (HANDLE_IS_VALID(FrsThread->Handle)) {
|
||
DPRINT1(4, ":X: Canceling RPC requests for thread %ws\n", FrsThread->Name);
|
||
RpcCancelThreadEx(FrsThread->Handle, 0);
|
||
}
|
||
|
||
return ThSupExitWithTombstone(FrsThread);
|
||
}
|
||
|
||
|
||
DWORD
|
||
SndCsMain(
|
||
PVOID Arg
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Entry point for a thread serving the Send Command Server.
|
||
|
||
Arguments:
|
||
Arg - thread
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsMain:"
|
||
DWORD WStatus;
|
||
PFRS_QUEUE IdledQueue;
|
||
PCOMMAND_PACKET Cmd;
|
||
PGHANDLE GHandle;
|
||
PHANDLE_LIST HandleList;
|
||
PCXTION Cxtion;
|
||
PREPLICA Replica;
|
||
ULARGE_INTEGER Now;
|
||
PFRS_THREAD FrsThread = (PFRS_THREAD)Arg;
|
||
|
||
//
|
||
// Thread is pointing at the correct command server
|
||
//
|
||
FRS_ASSERT(FrsThread->Data == &SndCs);
|
||
|
||
//
|
||
// Immediate cancel of outstanding RPC calls during shutdown
|
||
//
|
||
RpcMgmtSetCancelTimeout(0);
|
||
FrsThread->Exit = SndCsExit;
|
||
|
||
//
|
||
// Try-Finally
|
||
//
|
||
try {
|
||
|
||
//
|
||
// Capture exception.
|
||
//
|
||
try {
|
||
//
|
||
// Pull entries off the "send" queue and send them on
|
||
//
|
||
cant_exit_yet:
|
||
while (Cmd = FrsGetCommandServerIdled(&SndCs, &IdledQueue)) {
|
||
|
||
Cxtion = SRCxtion(Cmd);
|
||
Replica = SRReplica(Cmd);
|
||
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndDeQ");
|
||
|
||
//
|
||
// The RPC interface was initialized at this time but the
|
||
// advent of the API RPC interfaces necessitated moving
|
||
// the RPC initialization into MainMustInit. The event
|
||
// and the CMD_INIT_SUBSYSTEM command are left intact
|
||
// until such time as they are deemed completely unneeded.
|
||
//
|
||
if (Cmd->Command == CMD_INIT_SUBSYSTEM) {
|
||
//
|
||
// All done
|
||
//
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
SetEvent(CommEvent);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Send the Cmd to Cs
|
||
//
|
||
if (Cmd->Command == CMD_SND_CMD) {
|
||
FrsSubmitCommandServer(SRCs(Cmd), SRCmd(Cmd));
|
||
SRCmd(Cmd) = NULL;
|
||
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
continue;
|
||
}
|
||
|
||
FRS_ASSERT(SRCommPkt(Cmd));
|
||
FRS_ASSERT(SRTo(Cmd));
|
||
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndStart");
|
||
|
||
if (FrsIsShuttingDown) {
|
||
//
|
||
// Complete w/error
|
||
//
|
||
WStatus = ERROR_PROCESS_ABORTED;
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndFail - shutting down");
|
||
FrsCompleteCommand(Cmd, WStatus);
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check that the join guid (if any) is still valid
|
||
//
|
||
if (!SndCsCheckCxtion(Cmd)) {
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndFail - stale join guid");
|
||
//
|
||
// Unjoin the replica\cxtion (if applicable)
|
||
//
|
||
SndCsDispatchCommError(SRCommPkt(Cmd));
|
||
|
||
//
|
||
// Complete w/error
|
||
//
|
||
FrsCompleteCommand(Cmd, ERROR_OPERATION_ABORTED);
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Grab a bound rpc handle for this connection target.
|
||
//
|
||
GTabLockTable(GHandleTable);
|
||
GHandle = GTabLookupNoLock(GHandleTable, SRTo(Cmd)->Guid, NULL);
|
||
if (GHandle == NULL) {
|
||
GHandle = FrsAllocType(GHANDLE_TYPE);
|
||
COPY_GUID(&GHandle->Guid, SRTo(Cmd)->Guid);
|
||
GTabInsertEntryNoLock(GHandleTable, GHandle, &GHandle->Guid, NULL);
|
||
}
|
||
GTabUnLockTable(GHandleTable);
|
||
|
||
//
|
||
// Grab the first handle entry off the list
|
||
//
|
||
EnterCriticalSection(&GHandle->Lock);
|
||
GHandle->Ref = TRUE;
|
||
HandleList = GHandle->HandleList;
|
||
if (HandleList != NULL) {
|
||
GHandle->HandleList = HandleList->Next;
|
||
}
|
||
LeaveCriticalSection(&GHandle->Lock);
|
||
|
||
WStatus = ERROR_SUCCESS;
|
||
if (HandleList == NULL) {
|
||
//
|
||
// No free handles bound to the destination server available.
|
||
// Allocate a new one.
|
||
// Note: Need to add a binding handle throttle.
|
||
// Note: Why don't we use the same handle for multiple calls?
|
||
//
|
||
HandleList = FrsAlloc(sizeof(HANDLE_LIST));
|
||
if (FrsIsShuttingDown) {
|
||
WStatus = ERROR_PROCESS_ABORTED;
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndFail - shutting down");
|
||
} else {
|
||
//
|
||
// Rpc call is cancelled if it doesn't return from
|
||
// the rpc runtime in time. This is because TCP/IP
|
||
// doesn't timeout if the server reboots.
|
||
//
|
||
GetSystemTimeAsFileTime((FILETIME *)&FrsThread->StartTime);
|
||
WStatus = FrsRpcBindToServer(SRTo(Cmd),
|
||
SRPrincName(Cmd),
|
||
SRAuthLevel(Cmd),
|
||
&HandleList->RpcHandle);
|
||
//
|
||
// Associate a penalty with the cxtion based on the
|
||
// time needed to fail the rpc bind call. The penalty
|
||
// is applied against joins to prevent a dead machine
|
||
// from tying up Snd threads as they wait to timeout
|
||
// repeated joins.
|
||
//
|
||
if (Cxtion != NULL) {
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
GetSystemTimeAsFileTime((FILETIME *)&Now);
|
||
if (Now.QuadPart > FrsThread->StartTime.QuadPart) {
|
||
Cxtion->Penalty += (ULONG)(Now.QuadPart -
|
||
FrsThread->StartTime.QuadPart) /
|
||
(1000 * 10);
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndFail - Binding Penalty");
|
||
DPRINT1(4, "++ SndFail - Binding Penalty - %d\n", Cxtion->Penalty);
|
||
}
|
||
}
|
||
}
|
||
//
|
||
// Reset RPC Cancel timeout for thread. No longer in rpc call.
|
||
//
|
||
FrsThread->StartTime.QuadPart = QUADZERO;
|
||
}
|
||
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
HandleList = FrsFree(HandleList);
|
||
COMMAND_SND_COMM_TRACE(0, Cmd, WStatus, "SndFail - binding");
|
||
//
|
||
// Increment the Bindings in error counter for both the
|
||
// replica set and the connection.
|
||
//
|
||
PM_INC_CTR_REPSET(Replica, BindingsError, 1);
|
||
PM_INC_CTR_CXTION(Cxtion, BindingsError, 1);
|
||
|
||
} else {
|
||
//
|
||
// Increment the Bindings counter for both the
|
||
// replica set and the connection.
|
||
//
|
||
PM_INC_CTR_REPSET(Replica, Bindings, 1);
|
||
PM_INC_CTR_CXTION(Cxtion, Bindings, 1);
|
||
}
|
||
}
|
||
|
||
if (WIN_SUCCESS(WStatus)) {
|
||
//
|
||
// Bind was successful and join guid is okay; send it on
|
||
//
|
||
try {
|
||
//
|
||
// Rpc call is cancelled if it doesn't return from
|
||
// the rpc runtime in time. This is because TCP/IP
|
||
// doesn't timeout if the server reboots.
|
||
//
|
||
GetSystemTimeAsFileTime((FILETIME *)&FrsThread->StartTime);
|
||
if (FrsIsShuttingDown) {
|
||
WStatus = ERROR_PROCESS_ABORTED;
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndFail - shutting down");
|
||
} else {
|
||
WStatus = FrsRpcSendCommPkt(HandleList->RpcHandle, SRCommPkt(Cmd));
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
COMMAND_SND_COMM_TRACE(0, Cmd, WStatus, "SndFail - rpc call");
|
||
} else {
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndSuccess");
|
||
}
|
||
}
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
GET_EXCEPTION_CODE(WStatus);
|
||
COMMAND_SND_COMM_TRACE(0, Cmd, WStatus, "SndFail - rpc exception");
|
||
}
|
||
//
|
||
// Associate a penalty with the cxtion based on the time needed
|
||
// to fail the rpc call. The penalty is applied against joins
|
||
// to prevent a dead machine from tying up Snd threads as they
|
||
// wait to timeout repeated joins.
|
||
//
|
||
if (Cxtion != NULL) {
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
GetSystemTimeAsFileTime((FILETIME *)&Now);
|
||
if (Now.QuadPart > FrsThread->StartTime.QuadPart) {
|
||
Cxtion->Penalty += (ULONG)(Now.QuadPart -
|
||
FrsThread->StartTime.QuadPart) /
|
||
(1000 * 10);
|
||
COMMAND_SND_COMM_TRACE(0, Cmd, WStatus, "SndFail - Send Penalty");
|
||
DPRINT1(4, "++ SndFail - Send Penalty - %d\n", Cxtion->Penalty);
|
||
}
|
||
} else {
|
||
Cxtion->Penalty = 0;
|
||
}
|
||
}
|
||
//
|
||
// Reset RPC Cancel timeout for thread. No longer in rpc call.
|
||
//
|
||
FrsThread->StartTime.QuadPart = QUADZERO;
|
||
|
||
//
|
||
// The binding may be out of date; discard it
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
//
|
||
// Increment the Packets sent with error counter for both the
|
||
// replica set and the connection.
|
||
//
|
||
PM_INC_CTR_REPSET(Replica, PacketsSentError, 1);
|
||
PM_INC_CTR_CXTION(Cxtion, PacketsSentError, 1);
|
||
|
||
if (!FrsIsShuttingDown) {
|
||
FrsRpcUnBindFromServer(&HandleList->RpcHandle);
|
||
}
|
||
HandleList = FrsFree(HandleList);
|
||
} else {
|
||
//
|
||
// Increment the Packets sent and bytes sent counter for both the
|
||
// replica set and the connection.
|
||
//
|
||
PM_INC_CTR_REPSET(Replica, PacketsSent, 1);
|
||
PM_INC_CTR_CXTION(Cxtion, PacketsSent, 1);
|
||
PM_INC_CTR_REPSET(Replica, PacketsSentBytes, SRCommPkt(Cmd)->PktLen);
|
||
PM_INC_CTR_CXTION(Cxtion, PacketsSentBytes, SRCommPkt(Cmd)->PktLen);
|
||
}
|
||
}
|
||
|
||
//
|
||
// We are done with the rpc handle. Return it to the list for
|
||
// another thread to use. This saves rebinding for every call.
|
||
//
|
||
if (HandleList) {
|
||
EnterCriticalSection(&GHandle->Lock);
|
||
GHandle->Ref = TRUE;
|
||
HandleList->Next = GHandle->HandleList;
|
||
GHandle->HandleList = HandleList;
|
||
LeaveCriticalSection(&GHandle->Lock);
|
||
}
|
||
|
||
//
|
||
// Don't retry. The originator of the command packet is
|
||
// responsible for retry. We DO NOT want multiple layers of
|
||
// retries because that can lead to frustratingly long timeouts.
|
||
//
|
||
if (!WIN_SUCCESS(WStatus)) {
|
||
//
|
||
// Discard future packets that depend on this join
|
||
//
|
||
LOCK_CXTION_TABLE(Replica);
|
||
SndCsDestroyCxtion(Cxtion, 0);
|
||
UNLOCK_CXTION_TABLE(Replica);
|
||
//
|
||
// Unjoin the replica\cxtion (if applicable)
|
||
//
|
||
SndCsDispatchCommError(SRCommPkt(Cmd));
|
||
}
|
||
|
||
FrsCompleteCommand(Cmd, WStatus);
|
||
FrsRtlUnIdledQueue(IdledQueue);
|
||
|
||
} // end of while()
|
||
|
||
//
|
||
// Exit
|
||
//
|
||
FrsExitCommandServer(&SndCs, 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(0, "SndCsMain finally.", WStatus);
|
||
|
||
//
|
||
// Trigger FRS shutdown if we terminated abnormally.
|
||
//
|
||
if (!WIN_SUCCESS(WStatus) && (WStatus != ERROR_PROCESS_ABORTED)) {
|
||
DPRINT(0, "SndCsMain terminated abnormally, forcing service shutdown.\n");
|
||
FrsIsShuttingDown = TRUE;
|
||
SetEvent(ShutDownEvent);
|
||
} else {
|
||
WStatus = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
|
||
return WStatus;
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsCommTimeout(
|
||
IN PCOMMAND_PACKET Cmd,
|
||
IN PVOID Arg
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Age the handle cache and cancel hung rpc requests every so often.
|
||
Submit Cmd back onto the Delayed-Command-Server's queue.
|
||
|
||
Arguments:
|
||
Cmd - delayed command packet
|
||
Arg - Unused
|
||
|
||
Return Value:
|
||
None. Cmd is submitted to DelCs.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsCommTimeout:"
|
||
|
||
DWORD WStatus;
|
||
PFRS_THREAD FrsThread;
|
||
ULARGE_INTEGER Now;
|
||
PVOID Key;
|
||
PGHANDLE GHandle;
|
||
PHANDLE_LIST HandleList;
|
||
extern ULONG RpcAgedBinds;
|
||
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndChk - Age and Hung");
|
||
|
||
//
|
||
// Age the handle cache
|
||
//
|
||
GTabLockTable(GHandleTable);
|
||
Key = NULL;
|
||
|
||
while (GHandle = GTabNextDatumNoLock(GHandleTable, &Key)) {
|
||
EnterCriticalSection(&GHandle->Lock);
|
||
|
||
if (!GHandle->Ref) {
|
||
|
||
while (HandleList = GHandle->HandleList) {
|
||
GHandle->HandleList = HandleList->Next;
|
||
++RpcAgedBinds;
|
||
FrsRpcUnBindFromServer(&HandleList->RpcHandle);
|
||
FrsFree(HandleList);
|
||
DPRINT(5, "++ FrsRpcUnBindFromServer\n");
|
||
}
|
||
}
|
||
GHandle->Ref = FALSE;
|
||
LeaveCriticalSection(&GHandle->Lock);
|
||
}
|
||
GTabUnLockTable(GHandleTable);
|
||
|
||
//
|
||
// Cancel hung rpc requests
|
||
//
|
||
GetSystemTimeAsFileTime((FILETIME *)&Now);
|
||
|
||
FrsThread = NULL;
|
||
while (FrsThread = ThSupEnumThreads(FrsThread)) {
|
||
|
||
//
|
||
// If frs is shutting down; skip it
|
||
//
|
||
if (FrsIsShuttingDown) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Some other thread; skip it
|
||
//
|
||
if (FrsThread->Main != SndCsMain) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// SndCs thread; Is it in an rpc call?
|
||
//
|
||
if (FrsThread->StartTime.QuadPart == QUADZERO) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Is the thread running? If not, skip it
|
||
//
|
||
if (!FrsThread->Running ||
|
||
!HANDLE_IS_VALID(FrsThread->Handle)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Is it hung in an rpc call?
|
||
//
|
||
if ((FrsThread->StartTime.QuadPart < Now.QuadPart) &&
|
||
((Now.QuadPart - FrsThread->StartTime.QuadPart) >= CommTimeoutCheck)) {
|
||
//
|
||
// Yep, cancel the rpc call
|
||
//
|
||
WStatus = RpcCancelThreadEx(FrsThread->Handle, 0);
|
||
DPRINT1_WS(4, "++ RpcCancelThread(%d);", FrsThread->Id, WStatus);
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, WStatus, "SndChk - Cancel");
|
||
}
|
||
}
|
||
|
||
//
|
||
// Re-submit the command packet to the delayed command server
|
||
//
|
||
if (!FrsIsShuttingDown) {
|
||
FrsDelCsCompleteSubmit(Cmd, CommTimeoutInMilliSeconds << 1);
|
||
} else {
|
||
//
|
||
// Send the packet on to the generic completion routine
|
||
//
|
||
FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL);
|
||
FrsCompleteCommand(Cmd, Cmd->ErrorStatus);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsInitialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Initialize the send command server subsystem.
|
||
|
||
Alloc handle table, read reg pars, Create/init SndCs Alloc comm queue
|
||
array, attach comm queues to SndCs control queue.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
TRUE - command server has been started
|
||
FALSE - Not
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsInitialize:"
|
||
ULONG Status;
|
||
DWORD i;
|
||
PCOMMAND_PACKET Cmd;
|
||
ULONG MaxThreads;
|
||
|
||
//
|
||
// Get the timeout value and convert to 100 nsec units.
|
||
//
|
||
CfgRegReadDWord(FKC_COMM_TIMEOUT, NULL, 0, &CommTimeoutInMilliSeconds);
|
||
|
||
DPRINT1(4, ":S: CommTimeout is %d msec\n", CommTimeoutInMilliSeconds);
|
||
|
||
CommTimeoutCheck = ((ULONGLONG)CommTimeoutInMilliSeconds) * 1000 * 10;
|
||
DPRINT1(4, ":S: CommTimeout is %08x %08x 100-nsec\n",
|
||
PRINTQUAD(CommTimeoutCheck));
|
||
|
||
//
|
||
// Binding handle table
|
||
//
|
||
GHandleTable = GTabAllocTable();
|
||
|
||
//
|
||
// Comm layer command server
|
||
//
|
||
CfgRegReadDWord(FKC_SNDCS_MAXTHREADS_PAR, NULL, 0, &MaxThreads);
|
||
FrsInitializeCommandServer(&SndCs, MaxThreads, L"SndCs", SndCsMain);
|
||
//
|
||
// A short array of comm queues to increase parallelism. Each Comm queue
|
||
// is attached to the Send cmd server control queue. Each cxtion gets
|
||
// assigned to a comm queue when it "JOINS" to preserve packet order.
|
||
//
|
||
for (i = 0; i < MAX_COMM_QUEUE_NUMBER; ++i) {
|
||
FrsInitializeQueue(&CommQueues[i], &SndCs.Control);
|
||
}
|
||
|
||
//
|
||
// Start the comm layer
|
||
//
|
||
Cmd = FrsAllocCommand(&SndCs.Queue, CMD_INIT_SUBSYSTEM);
|
||
FrsSubmitCommandServer(&SndCs, Cmd);
|
||
|
||
//
|
||
// Age the handle cache and check for hung rpc calls
|
||
//
|
||
Cmd = FrsAllocCommand(&SndCs.Queue, CMD_VERIFY_SERVICE);
|
||
FrsSetCompletionRoutine(Cmd, SndCsCommTimeout, NULL);
|
||
|
||
FrsDelCsCompleteSubmit(Cmd, CommTimeoutInMilliSeconds << 1);
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsUnInitialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Uninitialize the send command server subsystem.
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
TRUE - command server has been started
|
||
FALSE - Not
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsUnInitialize:"
|
||
DWORD i;
|
||
|
||
GTabFreeTable(GHandleTable, FrsFreeType);
|
||
|
||
//
|
||
// A short array of comm queues to increase parallelism.
|
||
//
|
||
for (i = 0; i < MAX_COMM_QUEUE_NUMBER; ++i) {
|
||
FrsRtlDeleteQueue(&CommQueues[i]);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsSubmitCommPkt(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN PCHANGE_ORDER_ENTRY Coe,
|
||
IN GUID *JoinGuid,
|
||
IN BOOL SetTimeout,
|
||
IN PCOMM_PACKET CommPkt,
|
||
IN DWORD CommQueueIndex
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Submit a comm packet to the "send" command server for the target Cxtion.
|
||
|
||
Arguments:
|
||
Replica - Replica struct ptr
|
||
|
||
Cxtion - Target connection for comm packet.
|
||
|
||
Coe - Change order entry for related stage file fetch comm packet.
|
||
This is used track the change orders that have a fetch request outstanding
|
||
on a given inbound connection. Used by Forced Unjoins to send the
|
||
CO thru the Retry path.
|
||
NOTE: When Coe is supplied then SetTimeout should also be TRUE.
|
||
|
||
JoinGuid - Current join Guid from Cxtion or null if starting join seq.
|
||
|
||
SetTimeout - TRUE if caller wants a timeout set on this send request.
|
||
It means that the caller is eventually expecting a response
|
||
back. E.G. usually set on stage file fetch requests.
|
||
|
||
CommPkt - Communication packet data to send.
|
||
|
||
CommQueueIndex - Index of communication queue to use, generally allocated
|
||
for each Cxtion struct.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsSubmitCommPkt:"
|
||
PCOMMAND_PACKET Cmd;
|
||
|
||
FRS_ASSERT(CommQueueIndex < MAX_COMM_QUEUE_NUMBER);
|
||
|
||
//
|
||
// WARN: we assume that this function is called single-threaded per replica.
|
||
// Davidor - Would be nice if the friggen comment above said why? I
|
||
// currently don't see the reason for this.
|
||
// Maybe: its the time out code in SndCsCheckCxtion()?
|
||
//
|
||
if (Coe != NULL) {
|
||
//
|
||
// Anytime we are supplying a Coe argument we are expecting a response
|
||
// so verify that SetTimeout was requested and put the Coe in the
|
||
// Cxtion's Coe table so we can send it through the retry path at
|
||
// unjoin (or timeout).
|
||
//
|
||
FRS_ASSERT(SetTimeout);
|
||
LOCK_CXTION_COE_TABLE(Replica, Cxtion);
|
||
GTabInsertEntry(Cxtion->CoeTable, Coe, &Coe->Cmd.ChangeOrderGuid, NULL);
|
||
UNLOCK_CXTION_COE_TABLE(Replica, Cxtion);
|
||
}
|
||
|
||
Cmd = FrsAllocCommand(&CommQueues[CommQueueIndex], CMD_SND_COMM_PACKET);
|
||
|
||
SRTo(Cmd) = FrsBuildGName(FrsDupGuid(Cxtion->Partner->Guid),
|
||
FrsWcsDup(Cxtion->PartnerDnsName));
|
||
SRReplica(Cmd) = Replica;
|
||
SRCxtion(Cmd) = Cxtion;
|
||
|
||
if (JoinGuid) {
|
||
COPY_GUID(&SRJoinGuid(Cmd), JoinGuid);
|
||
SRJoinGuidValid(Cmd) = TRUE;
|
||
}
|
||
|
||
//
|
||
// Partner principal name and Authentication level.
|
||
//
|
||
SRPrincName(Cmd) = FrsWcsDup(Cxtion->PartnerPrincName);
|
||
SRAuthLevel(Cmd) = Cxtion->PartnerAuthLevel;
|
||
|
||
SRSetTimeout(Cmd) = SetTimeout;
|
||
SRCommPkt(Cmd) = CommPkt;
|
||
|
||
FrsSetCompletionRoutine(Cmd, CommCompletionRoutine, NULL);
|
||
|
||
//
|
||
// Check Comm packet consistency and put Send cmd on sent CS queue.
|
||
//
|
||
if (CommCheckPkt(CommPkt)) {
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndEnQComm");
|
||
FrsSubmitCommandServer(&SndCs, Cmd);
|
||
} else {
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndEnQERROR");
|
||
FrsCompleteCommand(Cmd, ERROR_INVALID_BLOCK);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SndCsSubmitCommPkt2(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN PCHANGE_ORDER_ENTRY Coe,
|
||
IN BOOL SetTimeout,
|
||
IN PCOMM_PACKET CommPkt
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Submit a comm packet to the "send" command server using the assigned
|
||
comm queue for the target Cxtion.
|
||
|
||
The Comm queue index and the join Guid come from the cxtion struct.
|
||
|
||
Arguments:
|
||
See arg description for SndCsSubmitCommPkt.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsSubmitCommPkt2:"
|
||
|
||
SndCsSubmitCommPkt(Replica,
|
||
Cxtion,
|
||
Coe,
|
||
&Cxtion->JoinGuid,
|
||
SetTimeout,
|
||
CommPkt,
|
||
Cxtion->CommQueueIndex);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsSubmitCmd(
|
||
IN PREPLICA Replica,
|
||
IN PCXTION Cxtion,
|
||
IN PCOMMAND_SERVER FlushCs,
|
||
IN PCOMMAND_PACKET FlushCmd,
|
||
IN DWORD CommQueueIndex
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Submit the FlushCmd packet to the "send" command server for the
|
||
target Cxtion. The FlushCmd packet will be submitted to
|
||
FlushCs once it bubbles to the top of the queue. I.e., once
|
||
the queue has been flushed.
|
||
|
||
Arguments:
|
||
Replica - replica set
|
||
Cxtion - cxtion identifies send queue
|
||
FlushCs - Command server to receive Cmd
|
||
FlushCmd - Command packet to send to Cs
|
||
CommQueueIndex - Identifies the comm queue
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsSubmitCmd:"
|
||
PCOMMAND_PACKET Cmd;
|
||
|
||
FRS_ASSERT(CommQueueIndex < MAX_COMM_QUEUE_NUMBER);
|
||
|
||
//
|
||
// Alloc the cmd packet and set the target queue and the command.
|
||
//
|
||
Cmd = FrsAllocCommand(&CommQueues[CommQueueIndex], CMD_SND_CMD);
|
||
|
||
//
|
||
// Destination Partner Guid / Dns Name
|
||
//
|
||
SRTo(Cmd) = FrsBuildGName(FrsDupGuid(Cxtion->Partner->Guid),
|
||
FrsWcsDup(Cxtion->PartnerDnsName));
|
||
SRReplica(Cmd) = Replica;
|
||
SRCxtion(Cmd) = Cxtion;
|
||
SRCs(Cmd) = FlushCs;
|
||
SRCmd(Cmd) = FlushCmd;
|
||
SRPrincName(Cmd) = FrsWcsDup(Cxtion->PartnerPrincName);
|
||
SRAuthLevel(Cmd) = Cxtion->PartnerAuthLevel;
|
||
|
||
FrsSetCompletionRoutine(Cmd, CommCompletionRoutine, NULL);
|
||
|
||
COMMAND_SND_COMM_TRACE(4, Cmd, ERROR_SUCCESS, "SndEnQCmd");
|
||
FrsSubmitCommandServer(&SndCs, Cmd);
|
||
}
|
||
|
||
|
||
VOID
|
||
SndCsShutDown(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Shutdown the send command server
|
||
|
||
Arguments:
|
||
None.
|
||
|
||
Return Value:
|
||
None.
|
||
--*/
|
||
{
|
||
#undef DEBSUB
|
||
#define DEBSUB "SndCsShutDown:"
|
||
DWORD i;
|
||
|
||
//
|
||
// A short array of comm queues to increase parallelism.
|
||
//
|
||
for (i = 0; i < MAX_COMM_QUEUE_NUMBER; ++i) {
|
||
FrsRunDownCommandServer(&SndCs, &CommQueues[i]);
|
||
}
|
||
|
||
FrsRunDownCommandServer(&SndCs, &SndCs.Queue);
|
||
}
|