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.
1330 lines
38 KiB
1330 lines
38 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
arbitrat.c
|
|
|
|
Abstract:
|
|
|
|
DiskArbitration, DiskReservationThread
|
|
|
|
Author:
|
|
|
|
Gor Nishanov (gorn) 5-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
gorn: different arbitration algorithm implemented
|
|
|
|
--*/
|
|
|
|
//
|
|
// Cannot use DoReserve/Release/BreakReserve from
|
|
// filter.c . Because of hold io we won't be
|
|
// able to open \Device\HarddiskX\ParitionY device
|
|
//
|
|
|
|
#define DoReserve DoReserve_don_t_use
|
|
#define DoRelease DoRelease_don_t_use
|
|
#define DoBreakReserve DoBreakReserve_don_t_use
|
|
|
|
#include "disksp.h"
|
|
|
|
#include "diskarbp.h"
|
|
#include "arbitrat.h"
|
|
#include "newmount.h"
|
|
#include <strsafe.h> // Should be included last.
|
|
|
|
#undef DoReserve
|
|
#undef DoRelease
|
|
#undef DoBreakReserve
|
|
|
|
#define LOG_CURRENT_MODULE LOG_MODULE_DISK
|
|
|
|
#define DISKARB_MAX_WORK_THREADS 1
|
|
#define DISKARB_WORK_THREAD_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL
|
|
|
|
#define ARBITRATION_ATTEMPTS_SZ L"ArbitrationAttempts"
|
|
#define ARBITRATION_SLEEP_SZ L"ArbitrationSleepBeforeRetry"
|
|
|
|
#define RESERVATION_TIMER (1000*RESERVE_TIMER) // Reservation timer in milliseconds //
|
|
// RESERVE_TIMER is defined in diskarbp.h //
|
|
|
|
#define WAIT_FOR_RESERVATION_TO_BE_RESTORED (RESERVATION_TIMER + 2000)
|
|
#define BUS_SETTLE_TIME (2000)
|
|
#define FAST_MUTEX_DELAY (1000)
|
|
|
|
#define DEFAULT_SLEEP_BEFORE_RETRY (500)
|
|
#define MIN_SLEEP_BEFORE_RETRY (0)
|
|
#define MAX_SLEEP_BEFORE_RETRY (30000)
|
|
|
|
#define DEFAULT_ARBITRATION_ATTEMPTS (5)
|
|
#define MIN_ARBITRATION_ATTEMPTS (1)
|
|
#define MAX_ARBITRATION_ATTEMPTS (9)
|
|
|
|
//
|
|
// Variables Local To Arbitration Module
|
|
//
|
|
|
|
static DWORD ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
|
|
static DWORD ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
|
|
static CRITICAL_SECTION ArbitrationLock;
|
|
static PCLRTL_WORK_QUEUE WorkQueue = 0;
|
|
static BOOLEAN AllGlobalsInitialized = FALSE;
|
|
static UCHAR NodeName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
|
|
enum { NAME_LENGTH = min(MAX_COMPUTERNAME_LENGTH,
|
|
sizeof ( ((PARBITRATION_ID)0)->NodeSignature ) ) };
|
|
|
|
DWORD
|
|
ArbitrateOnce(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle,
|
|
LPVOID buf
|
|
);
|
|
|
|
DWORD
|
|
VerifySectorSize(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle
|
|
);
|
|
|
|
DWORD
|
|
DoReadWrite(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN ULONG Operation,
|
|
IN HANDLE FileHandle,
|
|
IN DWORD BlockNumber,
|
|
IN PVOID Buffer
|
|
);
|
|
|
|
DWORD
|
|
DiskReservationThread(
|
|
IN PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
VOID
|
|
ReadArbitrationParameters(
|
|
VOID
|
|
);
|
|
|
|
DWORD
|
|
AsyncCheckReserve(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
);
|
|
|
|
DWORD
|
|
DoArbEscape(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle,
|
|
IN ULONG Operation,
|
|
IN PWCHAR OperationName,
|
|
IN PVOID OutBuffer,
|
|
IN ULONG OutBufferSize
|
|
);
|
|
|
|
|
|
#define DoBlockRead(RE,FH,BN,BUF) DoReadWrite(RE, AE_READ, FH, BN, BUF)
|
|
#define DoBlockWrite(RE,FH,BN,BUF) DoReadWrite(RE, AE_WRITE, FH, BN, BUF)
|
|
#define DoReserve(FH,RE) DoArbEscape(RE,FH,AE_RESERVE,L"Reserve",NULL,0)
|
|
#define DoRelease(FH,RE) DoArbEscape(RE,FH,AE_RELEASE,L"Release",NULL,0)
|
|
#define DoBreakReserve(FH,RE) DoArbEscape(RE,FH,AE_RESET,L"BusReset",NULL,0)
|
|
#define GetSectorSize(RE,FH,buf) DoArbEscape(RE,FH,AE_SECTORSIZE,L"GetSectorSize",buf,sizeof(ULONG) )
|
|
#define PokeDiskStack(RE,FH) DoArbEscape(RE,FH,AE_POKE,L"GetPartInfo",NULL,0)
|
|
|
|
|
|
#define OldFashionedRIP(ResEntry) \
|
|
( ( (ResEntry)->StopTimerHandle != NULL) || ( (ResEntry)->DiskInfo.ControlHandle != NULL) )
|
|
|
|
/**************************************************************************************/
|
|
|
|
VOID
|
|
ArbitrationInitialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
To be called from DllProcessAttach
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
|
|
{
|
|
InitializeCriticalSection( &ArbitrationLock );
|
|
|
|
//
|
|
// Read ArbitrationAttempts and ArbitratationSleepBeforeRetry from the registry
|
|
//
|
|
ReadArbitrationParameters();
|
|
}
|
|
|
|
VOID
|
|
ArbitrationCleanup(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
To be called from DllProcessDetach
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DeleteCriticalSection( &ArbitrationLock );
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyArbWorkQueue(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
To be called from DllProcessDetach
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
if (WorkQueue) {
|
|
ClRtlDestroyWorkQueue(WorkQueue);
|
|
WorkQueue = NULL;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
CreateArbWorkQueue(
|
|
IN RESOURCE_HANDLE ResourceHandle
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
if (WorkQueue) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
//
|
|
// Create a work queue to process overlapped I/O completions
|
|
//
|
|
WorkQueue = ClRtlCreateWorkQueue(
|
|
DISKARB_MAX_WORK_THREADS,
|
|
DISKARB_WORK_THREAD_PRIORITY
|
|
);
|
|
|
|
if (WorkQueue == NULL) {
|
|
DWORD status = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Unable to create work queue. Error: %1!u!.\n",
|
|
status );
|
|
return status;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD ArbitrationInitializeGlobals(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Additional initialization of global variables.
|
|
The ones that might fail and we want to
|
|
to log the failure.
|
|
|
|
Otherwise we could have just added the stuff we are doing here
|
|
to ArbitrationInitialize which is called from DllEntryPoint.
|
|
|
|
Currently we are using it only to initialize ArbitrationWork queue.
|
|
|
|
Called with ArbitrationLock held
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
DWORD NameSize;
|
|
|
|
NameSize = sizeof(NodeName);
|
|
RtlZeroMemory(NodeName, NameSize);
|
|
if( !GetComputerNameA( NodeName, &NameSize ) ) {
|
|
status = GetLastError();
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] GetComputerName failed, error %1!u!.\n", status);
|
|
return status;
|
|
}
|
|
|
|
AllGlobalsInitialized = TRUE;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
ArbitrationInfoInit(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
EnterCriticalSection( &ArbitrationLock );
|
|
if (!AllGlobalsInitialized) {
|
|
status = ArbitrationInitializeGlobals(ResourceEntry);
|
|
}
|
|
LeaveCriticalSection( &ArbitrationLock );
|
|
if(status != ERROR_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
InitializeCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
ArbitrationInfoCleanup(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
{
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] ArbitrationInfoCleanup.\n");
|
|
DeleteCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
BOOL
|
|
DoesNotNeedExpensiveReservations(
|
|
IN PDISK_RESOURCE ResourceEntry)
|
|
{
|
|
return (ResourceEntry->LostQuorum) == NULL;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
ComputeArbitrationId(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
OUT PARBITRATION_ID UniqueId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Arguments:
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
RtlZeroMemory(UniqueId, sizeof(ARBITRATION_ID));
|
|
GetSystemTimeAsFileTime( (LPFILETIME) &(UniqueId->SystemTime) );
|
|
RtlCopyMemory(UniqueId->NodeSignature, NodeName, NAME_LENGTH );
|
|
} // ComputeArbitrationId //
|
|
|
|
|
|
|
|
DWORD
|
|
ArbitrateOnce(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle,
|
|
LPVOID buf
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform full arbitration for a disk. Once arbitration has succeeded,
|
|
a thread is started that will keep reservations on the disk.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - the disk info structure for the disk.
|
|
|
|
FileHandle - the file handle to use for arbitration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
ARBITRATION_ID id, old_y, empty;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Read the partition info to insure the disk is accessible.\n");
|
|
PokeDiskStack(ResourceEntry, FileHandle);
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Arbitrate for ownership of the disk by reading/writing various disk sectors.\n");
|
|
|
|
ComputeArbitrationId(ResourceEntry, &id);
|
|
RtlZeroMemory( &empty, sizeof(empty) );
|
|
RtlZeroMemory( &old_y, sizeof(old_y) );
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
|
|
if ( (status == ERROR_SUCCESS)
|
|
&& ( (0 == memcmp(&empty, buf, sizeof(empty)) ) // clean release
|
|
||(0 == memcmp(&id.NodeSignature,
|
|
&(((PARBITRATION_ID)buf)->NodeSignature),
|
|
sizeof(id.NodeSignature) ) ) // we dropped this disk
|
|
)
|
|
)
|
|
{
|
|
// Disk was voluntary released
|
|
// or we are picking up the disk that was dropped by us
|
|
// and nobody was using it while we were away
|
|
//
|
|
// => Fast Arbitration
|
|
CopyMemory( &old_y ,buf, sizeof(old_y) );
|
|
goto FastMutex;
|
|
}
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
// Breaker //
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] We are about to break reserve.\n");
|
|
status = DoBreakReserve( FileHandle, ResourceEntry );
|
|
if( ERROR_SUCCESS != status ) {
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Failed to break reservation, error %1!u!.\n",
|
|
status
|
|
);
|
|
return status;
|
|
}
|
|
Sleep( BUS_SETTLE_TIME );
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Read the partition info from the disk to insure disk is accessible.\n");
|
|
PokeDiskStack(ResourceEntry, FileHandle);
|
|
#if 0
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
#else
|
|
CopyMemory(buf, &id, sizeof(id)); id.SeqNo.QuadPart ++;
|
|
status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
#endif
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
} else {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] No reservation found. Read'n'wait.\n");
|
|
Sleep( BUS_SETTLE_TIME ); // so that reader would not get an advantages
|
|
}
|
|
CopyMemory(&old_y, buf, sizeof(ARBITRATION_ID));
|
|
|
|
Sleep( WAIT_FOR_RESERVATION_TO_BE_RESTORED );
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {;} else
|
|
if( 0 != memcmp(&old_y, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
|
|
// Fast Mutex Code //
|
|
|
|
FastMutex:
|
|
// write(x, id) //
|
|
CopyMemory(buf, &id, sizeof(id));
|
|
status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_X, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
|
|
// if(y != old_y && y != empty) return FALSE; //
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
|
|
if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {;} else
|
|
if( 0 != memcmp(&old_y, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
|
|
|
|
// write(y, id) //
|
|
CopyMemory(buf, &id, sizeof(id));
|
|
status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
|
|
// if(x != id) ...
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_X, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
|
|
if( 0 != memcmp(&id, buf, sizeof(ARBITRATION_ID)) ) {
|
|
Sleep(FAST_MUTEX_DELAY);
|
|
|
|
// if(y == 0) goto FastMutex //
|
|
// if(y != id) return FALSE //
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if(status != ERROR_SUCCESS) { return status; }
|
|
if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {
|
|
RtlZeroMemory( &old_y, sizeof(old_y) );
|
|
goto FastMutex;
|
|
}
|
|
if( 0 != memcmp(&id, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
|
|
}
|
|
|
|
status = StartPersistentReservations(ResourceEntry, FileHandle);
|
|
return(status);
|
|
|
|
} // ArbitrateOnce //
|
|
|
|
|
|
DWORD
|
|
DiskArbitration(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform arbitration for a disk. Once arbitration has succeeded,
|
|
a thread is started that will keep reservations on the disk.
|
|
If arbitration fails, the routine will retry to arbitrate in ArbitratationSleepBeforeRetry
|
|
milliseconds. A number of arbitration attempts is controlled by ArbitrationAttempts variable.
|
|
|
|
ArbitrationAttempts and ArbitratationSleepBeforeRetry are read from the registry on
|
|
start up.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - the disk info structure for the disk.
|
|
|
|
FileHandle - the file handle to use for arbitration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
int repeat;
|
|
LPVOID unalignedBuf = 0;
|
|
LPVOID buf = 0;
|
|
|
|
__try {
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Arbitration Parameters: ArbAttempts %1!u!, SleepBeforeRetry %2!u! ms.\n",
|
|
ArbitrationAttempts, ArbitratationSleepBeforeRetry);
|
|
EnterCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
|
|
|
|
//
|
|
// If we already are performing reservations, then just leave now.
|
|
//
|
|
if ( ReservationInProgress(ResourceEntry) ) {
|
|
status = ERROR_SUCCESS;
|
|
__leave;
|
|
}
|
|
status = VerifySectorSize(ResourceEntry, FileHandle);
|
|
if ( status != ERROR_SUCCESS ) {
|
|
// VerifySectorSize logs an error //
|
|
__leave;
|
|
}
|
|
|
|
unalignedBuf = LocalAlloc(LPTR, ResourceEntry->ArbitrationInfo.SectorSize * 2);
|
|
if( unalignedBuf == 0 ) {
|
|
status = GetLastError();
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Failed to allocate arbitration buffer X, error %1!u!.\n", status );
|
|
__leave;
|
|
}
|
|
// Alignment code assumes that ResourceEntry->ArbitrationInfo.SectorSize is the power of two //
|
|
buf = (LPVOID)( ((ULONG_PTR)unalignedBuf + ResourceEntry->ArbitrationInfo.SectorSize
|
|
) & ~((ULONG_PTR)(ResourceEntry->ArbitrationInfo.SectorSize - 1))
|
|
);
|
|
|
|
repeat = ArbitrationAttempts;
|
|
for(;;) {
|
|
status = ArbitrateOnce(ResourceEntry, FileHandle, buf);
|
|
if(status == ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
if(--repeat <= 0) {
|
|
break;
|
|
}
|
|
Sleep(ArbitratationSleepBeforeRetry);
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"[DiskArb] Retry arbitration, %1!u! attempts left \n", repeat );
|
|
|
|
}
|
|
if(status != ERROR_SUCCESS) {
|
|
__leave;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"[DiskArb] Assume ownership of the device.\n");
|
|
|
|
} __finally {
|
|
LeaveCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
|
|
if(unalignedBuf) {
|
|
LocalFree(unalignedBuf);
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // DiskArbitration //
|
|
|
|
|
|
DWORD
|
|
DoArbEscape(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle,
|
|
IN ULONG Operation,
|
|
IN PWCHAR OperationName,
|
|
IN PVOID OutBuffer,
|
|
IN ULONG OutBufferSize
|
|
)
|
|
{
|
|
DWORD bytesReturned;
|
|
DWORD status;
|
|
DWORD LogLevel = LOG_INFORMATION;
|
|
ARBITRATION_READ_WRITE_PARAMS params;
|
|
|
|
params.Operation = Operation;
|
|
params.SectorSize = 0;
|
|
params.SectorNo = 0;
|
|
params.Buffer = 0;
|
|
params.Signature = ResourceEntry->DiskInfo.Params.Signature;
|
|
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Issuing %1!ws! on signature %2!x!.\n",
|
|
OperationName,
|
|
params.Signature );
|
|
|
|
status = DeviceIoControl( FileHandle,
|
|
IOCTL_DISK_CLUSTER_ARBITRATION_ESCAPE,
|
|
¶ms,
|
|
sizeof(params),
|
|
OutBuffer,
|
|
OutBufferSize,
|
|
&bytesReturned,
|
|
FALSE );
|
|
|
|
if( status == FALSE) {
|
|
status = GetLastError();
|
|
LogLevel = LOG_ERROR;
|
|
} else {
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LogLevel,
|
|
L"[DiskArb] %1!ws! completed, status %2!u!.\n",
|
|
OperationName, status );
|
|
return status;
|
|
}
|
|
|
|
DWORD
|
|
DoReadWrite(
|
|
IN PDISK_RESOURCE ResourceEntry,
|
|
IN ULONG Operation,
|
|
IN HANDLE FileHandle,
|
|
IN DWORD BlockNumber,
|
|
IN PVOID Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
DWORD bytesReturned;
|
|
DWORD status;
|
|
PWCHAR opname = (Operation == AE_READ)?L"read ":L"write";
|
|
ARBITRATION_READ_WRITE_PARAMS params;
|
|
|
|
params.Operation = Operation;
|
|
params.SectorSize = ResourceEntry->ArbitrationInfo.SectorSize;
|
|
params.SectorNo = BlockNumber;
|
|
params.Buffer = Buffer;
|
|
params.Signature = ResourceEntry->DiskInfo.Params.Signature;
|
|
|
|
status = DeviceIoControl( FileHandle,
|
|
IOCTL_DISK_CLUSTER_ARBITRATION_ESCAPE,
|
|
¶ms,
|
|
sizeof(params),
|
|
NULL,
|
|
0,
|
|
&bytesReturned,
|
|
FALSE );
|
|
|
|
if( status == 0) {
|
|
status = GetLastError();
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Failed to %1!ws! (sector %2!u!), error %3!u!.\n",
|
|
opname,
|
|
BlockNumber,
|
|
status );
|
|
return status;
|
|
} else {
|
|
#if 0
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Successful %1!ws! (sector %2!u!).\n",
|
|
opname,
|
|
BlockNumber,
|
|
#else
|
|
WCHAR buf[64];
|
|
mbstowcs(buf, ((PARBITRATION_ID)Buffer)->NodeSignature, sizeof(((PARBITRATION_ID)Buffer)->NodeSignature));
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Successful %1!ws! (sector %2!u!) [%3!ws!:%4!u!] (%5!x!,%6!08x!:%7!08x!).\n",
|
|
opname,
|
|
BlockNumber,
|
|
buf,
|
|
((PARBITRATION_ID)Buffer)->SeqNo.LowPart,
|
|
((PARBITRATION_ID)Buffer)->SeqNo.HighPart,
|
|
((PARBITRATION_ID)Buffer)->SystemTime.LowPart,
|
|
((PARBITRATION_ID)Buffer)->SystemTime.HighPart
|
|
);
|
|
#endif
|
|
}
|
|
return ERROR_SUCCESS;
|
|
} // DoReadWrite //
|
|
|
|
|
|
DWORD
|
|
VerifySectorSize(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine checks whether
|
|
a ResourceEntry->ArbitrationInfo.SectorSize has a value assigned to it.
|
|
If ResourceEntry->ArbitrationInfo.SectorSize is 0 then the routine tries
|
|
to obtain a correct sector size using GetDriveGeometry IOCTL.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
or
|
|
Error Code returned by IOCTL_DISK_GET_DRIVE_GEOMETRY
|
|
|
|
Comment:
|
|
|
|
The routine always succeeds. If it cannot obtain
|
|
disk geometry it will use a default sector size.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
DWORD sectorSize;
|
|
|
|
if (ResourceEntry->ArbitrationInfo.SectorSize)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Read disk geometry to get the sector size.\n" );
|
|
status = GetSectorSize(ResourceEntry, FileHandle, §orSize);
|
|
if (status == ERROR_SUCCESS) {
|
|
ResourceEntry->ArbitrationInfo.SectorSize = sectorSize;
|
|
} else {
|
|
ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
|
|
// GetDiskGeometry logs an error //
|
|
return status;
|
|
}
|
|
|
|
// ArbitrationInfo.SectorSize should be at least 64 bytes //
|
|
if( ResourceEntry->ArbitrationInfo.SectorSize < sizeof(ARBITRATION_ID) ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] ArbitrationInfo.SectorSize is too small %1!u!\n", ResourceEntry->ResourceHandle);
|
|
ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
// ArbitrationInfo.SectorSize should be a power of two //
|
|
if( (ResourceEntry->ArbitrationInfo.SectorSize & (ResourceEntry->ArbitrationInfo.SectorSize - 1)) != 0 ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] ArbitrationInfo.SectorSize is not a power of two %1!u!\n", ResourceEntry->ResourceHandle);
|
|
ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] ArbitrationInfo.SectorSize is %1!u!\n", ResourceEntry->ArbitrationInfo.SectorSize);
|
|
return ERROR_SUCCESS;
|
|
} // VerifySectorSize //
|
|
|
|
|
|
VOID
|
|
ReadArbitrationParameters(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads
|
|
|
|
DWORD ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
|
|
DWORD ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
|
|
|
|
from the registry
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
HKEY key;
|
|
DWORD size;
|
|
|
|
status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
L"Cluster",
|
|
0,
|
|
KEY_READ,
|
|
&key );
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
|
|
ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
|
|
return;
|
|
}
|
|
size = sizeof(ArbitrationAttempts);
|
|
status = RegQueryValueEx(key,
|
|
ARBITRATION_ATTEMPTS_SZ,
|
|
0,
|
|
NULL,
|
|
(LPBYTE)&ArbitrationAttempts,
|
|
&size);
|
|
|
|
if(status != ERROR_SUCCESS) {
|
|
ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
|
|
}
|
|
if(ArbitrationAttempts < MIN_ARBITRATION_ATTEMPTS
|
|
|| ArbitrationAttempts > MAX_ARBITRATION_ATTEMPTS)
|
|
{
|
|
ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
|
|
}
|
|
|
|
size = sizeof(ArbitratationSleepBeforeRetry);
|
|
status = RegQueryValueEx(key,
|
|
ARBITRATION_SLEEP_SZ,
|
|
0,
|
|
NULL,
|
|
(LPBYTE)&ArbitratationSleepBeforeRetry,
|
|
&size);
|
|
|
|
if(status != ERROR_SUCCESS) {
|
|
ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
|
|
}
|
|
//
|
|
// Removed this part of the check:
|
|
// ArbitratationSleepBeforeRetry < MIN_SLEEP_BEFORE_RETRY
|
|
// as DWORD/ULONG cannot be less than zero and it always evaluated
|
|
// to FALSE.
|
|
//
|
|
if(ArbitratationSleepBeforeRetry > MAX_SLEEP_BEFORE_RETRY)
|
|
{
|
|
ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
|
|
}
|
|
RegCloseKey(key);
|
|
} // ReadArbitrationParameters //
|
|
|
|
|
|
|
|
VOID
|
|
CompletionRoutine(
|
|
IN PCLRTL_WORK_ITEM WorkItem,
|
|
IN DWORD Status,
|
|
IN DWORD BytesTransferred,
|
|
IN ULONG_PTR IoContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
PDISK_RESOURCE ResourceEntry;
|
|
|
|
if( IoContext ) {
|
|
ResourceEntry = (PDISK_RESOURCE)IoContext;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] CompletionRoutine, status %1!u!.\n", Status);
|
|
|
|
} else {
|
|
PARBITRATION_INFO info = CONTAINING_RECORD(
|
|
WorkItem, // Expr //
|
|
ARBITRATION_INFO,
|
|
WorkItem); // FieldName //
|
|
|
|
ResourceEntry = CONTAINING_RECORD(
|
|
info,
|
|
DISK_RESOURCE,
|
|
ArbitrationInfo);
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] CompletionRoutine starts.\n", Status);
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
if (ResourceEntry->ArbitrationInfo.StopReserveInProgress) {
|
|
return;
|
|
}
|
|
//
|
|
// Repost the request
|
|
//
|
|
Status = AsyncCheckReserve(ResourceEntry);
|
|
if (Status == ERROR_SUCCESS) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Some kind of error occurred,
|
|
// but if we are in the middle of StopReserve
|
|
// then everything is fine.
|
|
//
|
|
if (ResourceEntry->ArbitrationInfo.StopReserveInProgress) {
|
|
return;
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
Status == ERROR_OPERATION_ABORTED ? LOG_WARNING : LOG_ERROR,
|
|
L"[DiskArb] CompletionRoutine: reservation lost! Status %1!u! \n", Status );
|
|
|
|
if ( ERROR_OPERATION_ABORTED != Status ) {
|
|
ClusResLogSystemEventByKey(ResourceEntry->ResourceKey,
|
|
LOG_CRITICAL,
|
|
RES_DISK_RESERVATION_LOST);
|
|
}
|
|
|
|
//
|
|
// Callout to cluster service to indicate that quorum has
|
|
// been lost.
|
|
//
|
|
|
|
if (ResourceEntry->LostQuorum != NULL) {
|
|
(ResourceEntry->LostQuorum)(ResourceEntry->ResourceHandle);
|
|
}
|
|
ResourceEntry->DiskInfo.FailStatus = Status;
|
|
ResourceEntry->Reserved = FALSE;
|
|
|
|
return;
|
|
|
|
} // CompletionRoutine //
|
|
|
|
DWORD
|
|
AsyncCheckReserve(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Description
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Handle for device to check reserve.
|
|
ResourceHandle - The resource handle for reporting errors
|
|
|
|
Return Value:
|
|
|
|
Error Status - zero if success.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL success;
|
|
DWORD errorCode;
|
|
DWORD bytesReturned;
|
|
PARBITRATION_INFO Info = &ResourceEntry->ArbitrationInfo;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Posting request to check reserve progress.\n");
|
|
|
|
ClRtlInitializeWorkItem(
|
|
&(Info->WorkItem),
|
|
CompletionRoutine,
|
|
ResourceEntry
|
|
);
|
|
|
|
success = DeviceIoControl( Info->ControlHandle,
|
|
IOCTL_DISK_CLUSTER_ALIVE_CHECK,
|
|
&Info->InputData,
|
|
sizeof(Info->InputData),
|
|
&Info->OutputData,
|
|
sizeof(Info->OutputData),
|
|
&bytesReturned,
|
|
&Info->WorkItem.Overlapped);
|
|
|
|
if ( !success ) {
|
|
errorCode = GetLastError();
|
|
|
|
if( errorCode == ERROR_IO_PENDING ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] ********* IO_PENDING ********** - Request to insure reserves working is now posted.\n");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if ( ERROR_OPERATION_ABORTED == errorCode ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_WARNING,
|
|
L"[DiskArb] disk reservation thread canceled, status %1!u!.\n",
|
|
errorCode);
|
|
} else {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] error checking disk reservation thread, error %1!u!.\n",
|
|
errorCode);
|
|
}
|
|
return(errorCode);
|
|
}
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Premature completion of check reserve I/O.\n");
|
|
|
|
return(ERROR_CAN_NOT_COMPLETE);
|
|
|
|
} // AsyncCheckReserve
|
|
|
|
|
|
DWORD
|
|
StartPersistentReservations(
|
|
IN OUT PDISK_RESOURCE ResourceEntry,
|
|
IN HANDLE FileHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts driver level persistent reservations.
|
|
Also starts a user-mode thread to keep an eye on driver level reservations.
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - the disk info structure for the disk.
|
|
|
|
FileHandle - the file handle to use for arbitration.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
|
|
//
|
|
// If we already are performing reservations, then just leave now.
|
|
//
|
|
if ( ReservationInProgress(ResourceEntry) ) {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
CL_ASSERT(WorkQueue != NULL);
|
|
VerifySectorSize(ResourceEntry, FileHandle);
|
|
|
|
status = DoReserve( FileHandle, ResourceEntry );
|
|
if(status != ERROR_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
{
|
|
START_RESERVE_DATA params;
|
|
DWORD paramsSize;
|
|
|
|
// Preparing parameters to call StartReserveEx //
|
|
params.DiskSignature = ResourceEntry->DiskInfo.Params.Signature;
|
|
params.Version = START_RESERVE_DATA_V1_SIG;
|
|
params.ArbitrationSector = BLOCK_Y;
|
|
params.SectorSize = ResourceEntry->ArbitrationInfo.SectorSize;
|
|
params.NodeSignatureSize = sizeof(params.NodeSignature);
|
|
RtlZeroMemory(params.NodeSignature, sizeof(params.NodeSignature) );
|
|
RtlCopyMemory(params.NodeSignature, NodeName, NAME_LENGTH );
|
|
|
|
#if 0
|
|
// When we have a reliable way of determining
|
|
// whether this disk resource is a quorum
|
|
// this code can be enabled
|
|
if ( DoesNotNeedExpensiveReservations(ResourceEntry) ) {
|
|
paramsSize = sizeof( params.DiskSignature );
|
|
} else {
|
|
paramsSize = sizeof( params );
|
|
}
|
|
#else
|
|
paramsSize = sizeof( params );
|
|
#endif
|
|
|
|
status = StartReserveEx( &ResourceEntry->ArbitrationInfo.ControlHandle,
|
|
¶ms,
|
|
paramsSize,
|
|
ResourceEntry->ResourceHandle );
|
|
}
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Failed to start driver reservation thread, error %1!u!.\n",
|
|
status );
|
|
DoRelease( FileHandle, ResourceEntry );
|
|
return(status);
|
|
}
|
|
|
|
ResourceEntry->ArbitrationInfo.StopReserveInProgress = FALSE;
|
|
status = ClRtlAssociateIoHandleWorkQueue(
|
|
WorkQueue,
|
|
ResourceEntry->ArbitrationInfo.ControlHandle,
|
|
(ULONG_PTR)ResourceEntry
|
|
);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] ClRtlAssociateIoHandleWorkQueue failed, error %1!u!.\n",
|
|
status );
|
|
StopPersistentReservations( ResourceEntry );
|
|
DoRelease( FileHandle, ResourceEntry );
|
|
return(status);
|
|
}
|
|
|
|
ClRtlInitializeWorkItem(
|
|
&(ResourceEntry->ArbitrationInfo.WorkItem),
|
|
CompletionRoutine,
|
|
0
|
|
);
|
|
|
|
status = ClRtlPostItemWorkQueue(
|
|
WorkQueue,
|
|
&ResourceEntry->ArbitrationInfo.WorkItem,
|
|
0,0);
|
|
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] ClRtlPostItemWorkQueue failed, error %1!u!.\n",
|
|
status );
|
|
StopPersistentReservations( ResourceEntry );
|
|
DoRelease( FileHandle, ResourceEntry );
|
|
return(status);
|
|
}
|
|
ResourceEntry->Reserved = TRUE;
|
|
|
|
return ERROR_SUCCESS;
|
|
} // StartPersistentReservations //
|
|
|
|
DWORD
|
|
CleanupArbitrationSector(
|
|
IN PDISK_RESOURCE ResourceEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
ResourceEntry - the disk info structure for the disk.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful.
|
|
A Win32 error code on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE FileHandle = DiskspClusDiskZero;
|
|
DWORD status;
|
|
LPVOID unalignedBuf = 0;
|
|
PARBITRATION_ID buf = 0;
|
|
try {
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[ArbCleanup] Verifying sector size. \n" );
|
|
VerifySectorSize(ResourceEntry, FileHandle);
|
|
|
|
unalignedBuf = LocalAlloc(LMEM_FIXED, ResourceEntry->ArbitrationInfo.SectorSize * 2);
|
|
if( unalignedBuf == 0 ) {
|
|
status = GetLastError();
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[ArbCleanup] Failed to allocate buffer, error %1!u!.\n", status );
|
|
leave;
|
|
}
|
|
// Alignment code assumes that ResourceEntry->ArbitrationInfo.SectorSize is the power of two //
|
|
buf = (PARBITRATION_ID)
|
|
(
|
|
( (ULONG_PTR)unalignedBuf + ResourceEntry->ArbitrationInfo.SectorSize )
|
|
& ~((ULONG_PTR)(ResourceEntry->ArbitrationInfo.SectorSize - 1))
|
|
);
|
|
ZeroMemory(buf, ResourceEntry->ArbitrationInfo.SectorSize);
|
|
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[ArbCleanup] Reading arbitration block. \n" );
|
|
status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if (status != ERROR_SUCCESS) { leave; }
|
|
if( 0 != memcmp(buf->NodeSignature, NodeName, NAME_LENGTH) ) {
|
|
//
|
|
// Somebody is challenging us. No need to clean up the sector
|
|
//
|
|
status = ERROR_OPERATION_ABORTED;
|
|
leave;
|
|
}
|
|
|
|
ZeroMemory(buf, ResourceEntry->ArbitrationInfo.SectorSize);
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[ArbCleanup] Writing arbitration block. \n" );
|
|
status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
|
|
if(status != ERROR_SUCCESS) {
|
|
leave;
|
|
}
|
|
|
|
} finally {
|
|
if(unalignedBuf) {
|
|
LocalFree(unalignedBuf);
|
|
}
|
|
}
|
|
|
|
(DiskpLogEvent)( ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[ArbCleanup] Returning status %1!u!. \n", status );
|
|
|
|
return(status);
|
|
|
|
} // CleanupArbitrationSector //
|
|
|
|
|
|
VOID
|
|
StopPersistentReservations(
|
|
IN OUT PDISK_RESOURCE ResourceEntry
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Arguments:
|
|
Return Value:
|
|
--*/
|
|
{
|
|
HANDLE localHandle;
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] StopPersistentReservations is called.\n");
|
|
|
|
//
|
|
// ReservationInProgress returns current contents of
|
|
// ResourceEntry->ArbitrationInfo.ControlHandle
|
|
//
|
|
localHandle = ReservationInProgress(ResourceEntry);
|
|
if ( localHandle ) {
|
|
DWORD status;
|
|
HANDLE ExchangeResult;
|
|
|
|
ExchangeResult = InterlockedCompareExchangePointer(
|
|
&ResourceEntry->ArbitrationInfo.ControlHandle,
|
|
0,
|
|
localHandle);
|
|
if (ExchangeResult == localHandle) {
|
|
//
|
|
// Only one thread is allowed in here
|
|
//
|
|
|
|
ResourceEntry->ArbitrationInfo.StopReserveInProgress = TRUE;
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] Stopping reservation thread.\n");
|
|
|
|
//
|
|
// Close the Control Handle, which stops the reservation thread and
|
|
// dismounts the volume, releases the disk, and marks it offline.
|
|
//
|
|
status = StopReserve( localHandle,
|
|
ResourceEntry->ResourceHandle );
|
|
if ( status != ERROR_SUCCESS ) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Error stopping reservation thread, error %1!u!.\n",
|
|
status);
|
|
}
|
|
|
|
status = CleanupArbitrationSector( ResourceEntry );
|
|
if (status != ERROR_SUCCESS) {
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_ERROR,
|
|
L"[DiskArb] Error cleaning arbitration sector, error %1!u!.\n",
|
|
status);
|
|
}
|
|
}
|
|
}
|
|
|
|
(DiskpLogEvent)(
|
|
ResourceEntry->ResourceHandle,
|
|
LOG_INFORMATION,
|
|
L"[DiskArb] StopPersistentReservations is complete.\n");
|
|
|
|
ResourceEntry->ArbitrationInfo.ControlHandle = NULL;
|
|
ResourceEntry->Reserved = FALSE;
|
|
ResourceEntry->LostQuorum = NULL;
|
|
}
|
|
|