Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1335 lines
37 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. arbitrat.c
  5. Abstract:
  6. DiskArbitration, DiskReservationThread
  7. Author:
  8. Gor Nishanov (gorn) 5-Jun-1998
  9. Revision History:
  10. gorn: different arbitration algorithm implemented
  11. --*/
  12. //
  13. // Cannot use DoReserve/Release/BreakReserve from
  14. // filter.c . Because of hold io we won't be
  15. // able to open \Device\HarddiskX\ParitionY device
  16. //
  17. #define DoReserve DoReserve_don_t_use
  18. #define DoRelease DoRelease_don_t_use
  19. #define DoBreakReserve DoBreakReserve_don_t_use
  20. #include "disksp.h"
  21. #include "lmcons.h"
  22. #include "diskarbp.h"
  23. #include "arbitrat.h"
  24. #include "newmount.h"
  25. #undef DoReserve
  26. #undef DoRelease
  27. #undef DoBreakReserve
  28. #define LOG_CURRENT_MODULE LOG_MODULE_DISK
  29. #define DISKARB_MAX_WORK_THREADS 1
  30. #define DISKARB_WORK_THREAD_PRIORITY THREAD_PRIORITY_ABOVE_NORMAL
  31. #define ARBITRATION_ATTEMPTS_SZ "ArbitrationAttempts"
  32. #define ARBITRATION_SLEEP_SZ "ArbitrationSleepBeforeRetry"
  33. #define RESERVATION_TIMER (1000*RESERVE_TIMER) // Reservation timer in milliseconds //
  34. // RESERVE_TIMER is defined in diskarbp.h //
  35. #define WAIT_FOR_RESERVATION_TO_BE_RESTORED (RESERVATION_TIMER + 2000)
  36. #define BUS_SETTLE_TIME (2000)
  37. #define FAST_MUTEX_DELAY (1000)
  38. #define DEFAULT_SLEEP_BEFORE_RETRY (9999)
  39. #define MIN_SLEEP_BEFORE_RETRY (0)
  40. #define MAX_SLEEP_BEFORE_RETRY (30000)
  41. #define DEFAULT_ARBITRATION_ATTEMPTS (1)
  42. #define MIN_ARBITRATION_ATTEMPTS (1)
  43. #define MAX_ARBITRATION_ATTEMPTS (9)
  44. //
  45. // Variables Local To Arbitration Module
  46. //
  47. static DWORD ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
  48. static DWORD ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
  49. static CRITICAL_SECTION ArbitrationLock;
  50. static PCLRTL_WORK_QUEUE WorkQueue = 0;
  51. static BOOLEAN AllGlobalsInitialized = FALSE;
  52. static BOOLEAN LegacyMode = FALSE;
  53. static UCHAR NodeName[MAX_COMPUTERNAME_LENGTH + 1];
  54. enum { NAME_LENGTH = min(MAX_COMPUTERNAME_LENGTH,
  55. sizeof ( ((PARBITRATION_ID)0)->NodeSignature ) ) };
  56. DWORD
  57. ArbitrateOnce(
  58. IN PDISK_RESOURCE ResourceEntry,
  59. IN HANDLE FileHandle,
  60. LPVOID buf
  61. );
  62. DWORD
  63. VerifySectorSize(
  64. IN OUT PDISK_RESOURCE ResourceEntry,
  65. IN HANDLE FileHandle
  66. );
  67. DWORD
  68. DoReadWrite(
  69. IN PDISK_RESOURCE ResourceEntry,
  70. IN ULONG Operation,
  71. IN HANDLE FileHandle,
  72. IN DWORD BlockNumber,
  73. IN PVOID Buffer
  74. );
  75. DWORD
  76. DiskReservationThread(
  77. IN PDISK_RESOURCE ResourceEntry
  78. );
  79. VOID
  80. ReadArbitrationParameters(
  81. VOID
  82. );
  83. DWORD
  84. AsyncCheckReserve(
  85. IN OUT PDISK_RESOURCE ResourceEntry
  86. );
  87. DWORD
  88. DoArbEscape(
  89. IN PDISK_RESOURCE ResourceEntry,
  90. IN HANDLE FileHandle,
  91. IN ULONG Operation,
  92. IN PWCHAR OperationName,
  93. IN PVOID OutBuffer,
  94. IN ULONG OutBufferSize
  95. );
  96. #define DoBlockRead(RE,FH,BN,BUF) DoReadWrite(RE, AE_READ, FH, BN, BUF)
  97. #define DoBlockWrite(RE,FH,BN,BUF) DoReadWrite(RE, AE_WRITE, FH, BN, BUF)
  98. #define DoReserve(FH,RE) DoArbEscape(RE,FH,AE_RESERVE,L"Reserve",NULL,0)
  99. #define DoRelease(FH,RE) DoArbEscape(RE,FH,AE_RELEASE,L"Release",NULL,0)
  100. #define DoBreakReserve(FH,RE) DoArbEscape(RE,FH,AE_RESET,L"BusReset",NULL,0)
  101. #define GetSectorSize(RE,FH,buf) DoArbEscape(RE,FH,AE_SECTORSIZE,L"GetSectorSize",buf,sizeof(ULONG) )
  102. #define PokeDiskStack(RE,FH) DoArbEscape(RE,FH,AE_POKE,L"GetPartInfo",NULL,0)
  103. #define OldFashionedRIP(ResEntry) \
  104. ( ( (ResEntry)->StopTimerHandle != NULL) || ( (ResEntry)->DiskInfo.ControlHandle != NULL) )
  105. /**************************************************************************************/
  106. VOID
  107. ArbitrationInitialize(
  108. VOID
  109. )
  110. /*++
  111. Routine Description:
  112. To be called from DllProcessAttach
  113. Arguments:
  114. Return Value:
  115. --*/
  116. {
  117. InitializeCriticalSection( &ArbitrationLock );
  118. //
  119. // Read ArbitrationAttempts and ArbitratationSleepBeforeRetry from the registry
  120. //
  121. ReadArbitrationParameters();
  122. }
  123. VOID
  124. ArbitrationCleanup(
  125. VOID
  126. )
  127. /*++
  128. Routine Description:
  129. To be called from DllProcessDetach
  130. Arguments:
  131. Return Value:
  132. --*/
  133. {
  134. DeleteCriticalSection( &ArbitrationLock );
  135. }
  136. VOID
  137. DestroyArbWorkQueue(
  138. VOID
  139. )
  140. /*++
  141. Routine Description:
  142. To be called from DllProcessDetach
  143. Arguments:
  144. Return Value:
  145. --*/
  146. {
  147. if (WorkQueue) {
  148. ClRtlDestroyWorkQueue(WorkQueue);
  149. WorkQueue = NULL;
  150. }
  151. }
  152. DWORD
  153. CreateArbWorkQueue(
  154. IN RESOURCE_HANDLE ResourceHandle
  155. )
  156. /*++
  157. Routine Description:
  158. Arguments:
  159. Return Value:
  160. --*/
  161. {
  162. if (WorkQueue) {
  163. return ERROR_SUCCESS;
  164. }
  165. //
  166. // Create a work queue to process overlapped I/O completions
  167. //
  168. WorkQueue = ClRtlCreateWorkQueue(
  169. DISKARB_MAX_WORK_THREADS,
  170. DISKARB_WORK_THREAD_PRIORITY
  171. );
  172. if (WorkQueue == NULL) {
  173. DWORD status = GetLastError();
  174. (DiskpLogEvent)(
  175. ResourceHandle,
  176. LOG_ERROR,
  177. L"[DiskArb] Unable to create work queue. Error: %1!u!.\n",
  178. status );
  179. return status;
  180. }
  181. return ERROR_SUCCESS;
  182. }
  183. DWORD ArbitrationInitializeGlobals(
  184. IN OUT PDISK_RESOURCE ResourceEntry
  185. )
  186. /*++
  187. Routine Description:
  188. Additional initialization of global variables.
  189. The ones that might fail and we want to
  190. to log the failure.
  191. Otherwise we could have just added the stuff we are doing here
  192. to ArbitrationInitialize which is called from DllEntryPoint.
  193. Currently we are using it only to initialize ArbitrationWork queue.
  194. Called with ArbitrationLock held
  195. Arguments:
  196. Return Value:
  197. --*/
  198. {
  199. DWORD status;
  200. DWORD NameSize;
  201. NameSize = sizeof(NodeName);
  202. RtlZeroMemory(NodeName, NameSize);
  203. if( !GetComputerName( NodeName, &NameSize ) ) {
  204. status = GetLastError();
  205. (DiskpLogEvent)(
  206. ResourceEntry->ResourceHandle,
  207. LOG_ERROR,
  208. L"[Arb] GetComputerName failed, error %1!u!.\n", status);
  209. return status;
  210. }
  211. LegacyMode = FALSE;
  212. AllGlobalsInitialized = TRUE;
  213. return ERROR_SUCCESS;
  214. }
  215. DWORD
  216. ArbitrationInfoInit(
  217. IN OUT PDISK_RESOURCE ResourceEntry
  218. )
  219. {
  220. DWORD status = ERROR_SUCCESS;
  221. EnterCriticalSection( &ArbitrationLock );
  222. if (!AllGlobalsInitialized) {
  223. status = ArbitrationInitializeGlobals(ResourceEntry);
  224. }
  225. LeaveCriticalSection( &ArbitrationLock );
  226. if(status != ERROR_SUCCESS) {
  227. return status;
  228. }
  229. InitializeCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
  230. return ERROR_SUCCESS;
  231. }
  232. VOID
  233. ArbitrationInfoCleanup(
  234. IN OUT PDISK_RESOURCE ResourceEntry
  235. )
  236. {
  237. (DiskpLogEvent)(
  238. ResourceEntry->ResourceHandle,
  239. LOG_INFORMATION,
  240. L"[DiskArb] ArbitrationInfoCleanup.\n");
  241. DeleteCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
  242. return;
  243. }
  244. BOOL
  245. DoesNotNeedExpensiveReservations(
  246. IN PDISK_RESOURCE ResourceEntry)
  247. {
  248. return (ResourceEntry->LostQuorum) == NULL;
  249. }
  250. void
  251. ComputeArbitrationId(
  252. IN PDISK_RESOURCE ResourceEntry,
  253. OUT PARBITRATION_ID UniqueId
  254. )
  255. /*++
  256. Routine Description:
  257. Arguments:
  258. Return Value:
  259. --*/
  260. {
  261. RtlZeroMemory(UniqueId, sizeof(ARBITRATION_ID));
  262. GetSystemTimeAsFileTime( (LPFILETIME) &(UniqueId->SystemTime) );
  263. RtlCopyMemory(UniqueId->NodeSignature, NodeName, NAME_LENGTH );
  264. } // ComputeArbitrationId //
  265. DWORD
  266. ArbitrateOnce(
  267. IN PDISK_RESOURCE ResourceEntry,
  268. IN HANDLE FileHandle,
  269. LPVOID buf
  270. )
  271. /*++
  272. Routine Description:
  273. Perform full arbitration for a disk. Once arbitration has succeeded,
  274. a thread is started that will keep reservations on the disk.
  275. Arguments:
  276. ResourceEntry - the disk info structure for the disk.
  277. FileHandle - the file handle to use for arbitration.
  278. Return Value:
  279. ERROR_SUCCESS if successful.
  280. A Win32 error code on failure.
  281. --*/
  282. {
  283. DWORD status;
  284. ARBITRATION_ID id, old_y, empty;
  285. if (LegacyMode) {
  286. status = DoReserve( FileHandle, ResourceEntry );
  287. if ( status != ERROR_SUCCESS ) {
  288. //
  289. // We will attempt to break the reservation of the other system, in case
  290. // it has gone down.
  291. //
  292. status = DoBreakReserve( FileHandle, ResourceEntry );
  293. if ( status != ERROR_SUCCESS ) {
  294. (DiskpLogEvent)(
  295. ResourceEntry->ResourceHandle,
  296. LOG_ERROR,
  297. L"Failed to break reservation, error %1!u!.\n",
  298. status );
  299. }
  300. //
  301. // Sleep for twice the RESERVATION_TIMER period to allow the
  302. // remote system to replace its reservation.
  303. //
  304. Sleep((2 * RESERVATION_TIMER) + 100);
  305. status = DoReserve( FileHandle, ResourceEntry );
  306. }
  307. if (status != ERROR_SUCCESS) {
  308. return status;
  309. }
  310. goto WinnerCode;
  311. } // Legacy Mode //
  312. PokeDiskStack(ResourceEntry, FileHandle);
  313. ComputeArbitrationId(ResourceEntry, &id);
  314. RtlZeroMemory( &empty, sizeof(empty) );
  315. RtlZeroMemory( &old_y, sizeof(old_y) );
  316. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  317. if ( (status == ERROR_SUCCESS)
  318. && ( (0 == memcmp(&empty, buf, sizeof(empty)) ) // clean release
  319. ||(0 == memcmp(&id.NodeSignature,
  320. &(((PARBITRATION_ID)buf)->NodeSignature),
  321. sizeof(id.NodeSignature) ) ) // we dropped this disk
  322. )
  323. )
  324. {
  325. // Disk was voluntary released
  326. // or we are picking up the disk that was dropped by us
  327. // and nobody was using it while we were away
  328. //
  329. // => Fast Arbitration
  330. CopyMemory( &old_y ,buf, sizeof(old_y) );
  331. goto FastMutex;
  332. }
  333. if (status != ERROR_SUCCESS) {
  334. // Breaker //
  335. (DiskpLogEvent)(
  336. ResourceEntry->ResourceHandle,
  337. LOG_INFORMATION,
  338. L"[DiskArb]We are about to break reserve.\n");
  339. status = DoBreakReserve( FileHandle, ResourceEntry );
  340. if( ERROR_SUCCESS != status ) {
  341. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  342. LOG_ERROR,
  343. L"[DiskArb]Failed to break reservation, error %1!u!.\n",
  344. status
  345. );
  346. return status;
  347. }
  348. Sleep( BUS_SETTLE_TIME );
  349. PokeDiskStack(ResourceEntry, FileHandle);
  350. #if 0
  351. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  352. #else
  353. CopyMemory(buf, &id, sizeof(id)); id.SeqNo.QuadPart ++;
  354. status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
  355. #endif
  356. if(status != ERROR_SUCCESS) { return status; }
  357. } else {
  358. (DiskpLogEvent)(
  359. ResourceEntry->ResourceHandle,
  360. LOG_INFORMATION,
  361. L"[DiskArb]No reservation found. Read'n'wait.\n");
  362. Sleep( BUS_SETTLE_TIME ); // so that reader would not get an advantages
  363. }
  364. CopyMemory(&old_y, buf, sizeof(ARBITRATION_ID));
  365. Sleep( WAIT_FOR_RESERVATION_TO_BE_RESTORED );
  366. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  367. if(status != ERROR_SUCCESS) { return status; }
  368. if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {;} else
  369. if( 0 != memcmp(&old_y, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
  370. // Fast Mutex Code //
  371. FastMutex:
  372. // write(x, id) //
  373. CopyMemory(buf, &id, sizeof(id));
  374. status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_X, buf);
  375. if(status != ERROR_SUCCESS) { return status; }
  376. // if(y != old_y && y != empty) return FALSE; //
  377. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  378. if(status != ERROR_SUCCESS) { return status; }
  379. if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {;} else
  380. if( 0 != memcmp(&old_y, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
  381. // write(y, id) //
  382. CopyMemory(buf, &id, sizeof(id));
  383. status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
  384. if(status != ERROR_SUCCESS) { return status; }
  385. // if(x != id) ...
  386. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_X, buf);
  387. if(status != ERROR_SUCCESS) { return status; }
  388. if( 0 != memcmp(&id, buf, sizeof(ARBITRATION_ID)) ) {
  389. Sleep(FAST_MUTEX_DELAY);
  390. // if(y == 0) goto FastMutex //
  391. // if(y != id) return FALSE //
  392. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  393. if(status != ERROR_SUCCESS) { return status; }
  394. if( 0 == memcmp(&empty, buf, sizeof(ARBITRATION_ID)) ) {
  395. RtlZeroMemory( &old_y, sizeof(old_y) );
  396. goto FastMutex;
  397. }
  398. if( 0 != memcmp(&id, buf, sizeof(ARBITRATION_ID)) ) { return ERROR_QUORUM_OWNER_ALIVE; }
  399. }
  400. WinnerCode:
  401. status = StartPersistentReservations(ResourceEntry, FileHandle);
  402. return(status);
  403. } // ArbitrateOnce //
  404. DWORD
  405. DiskArbitration(
  406. IN PDISK_RESOURCE ResourceEntry,
  407. IN HANDLE FileHandle
  408. )
  409. /*++
  410. Routine Description:
  411. Perform arbitration for a disk. Once arbitration has succeeded,
  412. a thread is started that will keep reservations on the disk.
  413. If arbitration fails, the routine will retry to arbitrate in ArbitratationSleepBeforeRetry
  414. milliseconds. A number of arbitration attempts is controlled by ArbitrationAttempts variable.
  415. ArbitrationAttempts and ArbitratationSleepBeforeRetry are read from the registry on
  416. start up.
  417. Arguments:
  418. ResourceEntry - the disk info structure for the disk.
  419. FileHandle - the file handle to use for arbitration.
  420. Return Value:
  421. ERROR_SUCCESS if successful.
  422. A Win32 error code on failure.
  423. --*/
  424. {
  425. DWORD status;
  426. int repeat;
  427. LPVOID unalignedBuf = 0;
  428. LPVOID buf = 0;
  429. __try {
  430. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  431. LOG_INFORMATION,
  432. L"[DiskArb] Arbitration Parameters (%1!u! %2!u!).\n",
  433. ArbitrationAttempts, ArbitratationSleepBeforeRetry);
  434. EnterCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
  435. //
  436. // If we already are performing reservations, then just leave now.
  437. //
  438. if ( ReservationInProgress(ResourceEntry) ) {
  439. status = ERROR_SUCCESS;
  440. __leave;
  441. }
  442. status = VerifySectorSize(ResourceEntry, FileHandle);
  443. if ( status != ERROR_SUCCESS ) {
  444. // VerifySectorSize logs an error //
  445. __leave;
  446. }
  447. unalignedBuf = LocalAlloc(LMEM_FIXED, ResourceEntry->ArbitrationInfo.SectorSize * 2);
  448. if( unalignedBuf == 0 ) {
  449. status = GetLastError();
  450. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  451. LOG_ERROR,
  452. L"[DiskArb]Failed to allocate arbitration buffer X, error %1!u!.\n", status );
  453. __leave;
  454. }
  455. // Alignment code assumes that ResourceEntry->ArbitrationInfo.SectorSize is the power of two //
  456. buf = (LPVOID)( ((ULONG_PTR)unalignedBuf + ResourceEntry->ArbitrationInfo.SectorSize
  457. ) & ~((ULONG_PTR)(ResourceEntry->ArbitrationInfo.SectorSize - 1))
  458. );
  459. ZeroMemory(buf, ResourceEntry->ArbitrationInfo.SectorSize);
  460. repeat = ArbitrationAttempts;
  461. for(;;) {
  462. status = ArbitrateOnce(ResourceEntry, FileHandle, buf);
  463. if(status == ERROR_SUCCESS) {
  464. break;
  465. }
  466. if(--repeat <= 0) {
  467. break;
  468. }
  469. Sleep(ArbitratationSleepBeforeRetry);
  470. }
  471. if(status != ERROR_SUCCESS) {
  472. __leave;
  473. }
  474. (DiskpLogEvent)(
  475. ResourceEntry->ResourceHandle,
  476. LOG_WARNING,
  477. L"[DiskArb]Assume ownership of the device.\n");
  478. } __finally {
  479. LeaveCriticalSection( &(ResourceEntry->ArbitrationInfo.DiskLock) );
  480. if(unalignedBuf) {
  481. LocalFree(unalignedBuf);
  482. }
  483. }
  484. return(status);
  485. } // DiskArbitration //
  486. DWORD
  487. DoArbEscape(
  488. IN PDISK_RESOURCE ResourceEntry,
  489. IN HANDLE FileHandle,
  490. IN ULONG Operation,
  491. IN PWCHAR OperationName,
  492. IN PVOID OutBuffer,
  493. IN ULONG OutBufferSize
  494. )
  495. {
  496. DWORD bytesReturned;
  497. DWORD status;
  498. DWORD LogLevel = LOG_INFORMATION;
  499. ARBITRATION_READ_WRITE_PARAMS params;
  500. params.Operation = Operation;
  501. params.SectorSize = 0;
  502. params.SectorNo = 0;
  503. params.Buffer = 0;
  504. params.Signature = ResourceEntry->DiskInfo.Params.Signature;
  505. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  506. LOG_INFORMATION,
  507. L"[DiskArb] Issuing %1!ws! on signature %2!x!.\n",
  508. OperationName,
  509. params.Signature );
  510. status = DeviceIoControl( FileHandle,
  511. IOCTL_DISK_CLUSTER_ARBITRATION_ESCAPE,
  512. &params,
  513. sizeof(params),
  514. OutBuffer,
  515. OutBufferSize,
  516. &bytesReturned,
  517. FALSE );
  518. if( status == FALSE) {
  519. status = GetLastError();
  520. LogLevel = LOG_ERROR;
  521. } else {
  522. status = ERROR_SUCCESS;
  523. }
  524. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  525. LogLevel,
  526. L"[DiskArb] %1!ws! completed, status %2!u!.\n",
  527. OperationName, status );
  528. return status;
  529. }
  530. DWORD
  531. DoReadWrite(
  532. IN PDISK_RESOURCE ResourceEntry,
  533. IN ULONG Operation,
  534. IN HANDLE FileHandle,
  535. IN DWORD BlockNumber,
  536. IN PVOID Buffer
  537. )
  538. /*++
  539. Routine Description:
  540. Arguments:
  541. Return Value:
  542. --*/
  543. {
  544. DWORD bytesReturned;
  545. DWORD status;
  546. PWCHAR opname = (Operation == AE_READ)?L"read ":L"write";
  547. ARBITRATION_READ_WRITE_PARAMS params;
  548. params.Operation = Operation;
  549. params.SectorSize = ResourceEntry->ArbitrationInfo.SectorSize;
  550. params.SectorNo = BlockNumber;
  551. params.Buffer = Buffer;
  552. params.Signature = ResourceEntry->DiskInfo.Params.Signature;
  553. status = DeviceIoControl( FileHandle,
  554. IOCTL_DISK_CLUSTER_ARBITRATION_ESCAPE,
  555. &params,
  556. sizeof(params),
  557. NULL,
  558. 0,
  559. &bytesReturned,
  560. FALSE );
  561. if( status == 0) {
  562. status = GetLastError();
  563. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  564. LOG_ERROR,
  565. L"[DiskArb]Failed to %1!ws! (sector %2!u!), error %3!u!.\n",
  566. opname,
  567. BlockNumber,
  568. status );
  569. return status;
  570. } else {
  571. #if 0
  572. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  573. LOG_INFORMATION,
  574. L"[DiskArb]Successful %1!ws! (sector %2!u!).\n",
  575. opname,
  576. BlockNumber,
  577. #else
  578. WCHAR buf[64];
  579. mbstowcs(buf, ((PARBITRATION_ID)Buffer)->NodeSignature, sizeof(((PARBITRATION_ID)Buffer)->NodeSignature));
  580. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  581. LOG_INFORMATION,
  582. L"[DiskArb]Successful %1!ws! (sector %2!u!) [%3!ws!:%4!u!] (%5!x!,%6!08x!:%7!08x!).\n",
  583. opname,
  584. BlockNumber,
  585. buf,
  586. ((PARBITRATION_ID)Buffer)->SeqNo.LowPart,
  587. ((PARBITRATION_ID)Buffer)->SeqNo.HighPart,
  588. ((PARBITRATION_ID)Buffer)->SystemTime.LowPart,
  589. ((PARBITRATION_ID)Buffer)->SystemTime.HighPart
  590. );
  591. #endif
  592. }
  593. return ERROR_SUCCESS;
  594. } // DoReadWrite //
  595. DWORD
  596. VerifySectorSize(
  597. IN OUT PDISK_RESOURCE ResourceEntry,
  598. IN HANDLE FileHandle
  599. )
  600. /*++
  601. Routine Description:
  602. The routine checks whether
  603. a ResourceEntry->ArbitrationInfo.SectorSize has a value assigned to it.
  604. If ResourceEntry->ArbitrationInfo.SectorSize is 0 then the routine tries
  605. to obtain a correct sector size using GetDriveGeometry IOCTL.
  606. Arguments:
  607. Return Value:
  608. ERROR_SUCCESS
  609. or
  610. Error Code returned by IOCTL_DISK_GET_DRIVE_GEOMETRY
  611. Comment:
  612. The routine always succeeds. If it cannot obtain
  613. disk geometry it will use a default sector size.
  614. --*/
  615. {
  616. BOOL success;
  617. DWORD bytesReturned;
  618. DWORD status;
  619. DWORD sectorSize;
  620. if (ResourceEntry->ArbitrationInfo.SectorSize)
  621. {
  622. return ERROR_SUCCESS;
  623. }
  624. status = GetSectorSize(ResourceEntry, FileHandle, &sectorSize);
  625. if (status == ERROR_SUCCESS) {
  626. ResourceEntry->ArbitrationInfo.SectorSize = sectorSize;
  627. } else {
  628. ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
  629. // GetDiskGeometry logs an error //
  630. return status;
  631. }
  632. // ArbitrationInfo.SectorSize should be at least 64 bytes //
  633. if( ResourceEntry->ArbitrationInfo.SectorSize < sizeof(ARBITRATION_ID) ) {
  634. (DiskpLogEvent)(
  635. ResourceEntry->ResourceHandle,
  636. LOG_ERROR,
  637. L"[DiskArb] ArbitrationInfo.SectorSize is too small %1!u!\n", ResourceEntry->ResourceHandle);
  638. ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
  639. return ERROR_INSUFFICIENT_BUFFER;
  640. }
  641. // ArbitrationInfo.SectorSize should be a power of two //
  642. if( (ResourceEntry->ArbitrationInfo.SectorSize & (ResourceEntry->ArbitrationInfo.SectorSize - 1)) != 0 ) {
  643. (DiskpLogEvent)(
  644. ResourceEntry->ResourceHandle,
  645. LOG_ERROR,
  646. L"[DiskArb] ArbitrationInfo.SectorSize is not a power of two %1!u!\n", ResourceEntry->ResourceHandle);
  647. ResourceEntry->ArbitrationInfo.SectorSize = DEFAULT_SECTOR_SIZE;
  648. return ERROR_INSUFFICIENT_BUFFER;
  649. }
  650. (DiskpLogEvent)(
  651. ResourceEntry->ResourceHandle,
  652. LOG_INFORMATION,
  653. L"[DiskArb] ArbitrationInfo.SectorSize is %1!u!\n", ResourceEntry->ArbitrationInfo.SectorSize);
  654. return ERROR_SUCCESS;
  655. } // VerifySectorSize //
  656. VOID
  657. ReadArbitrationParameters(
  658. VOID
  659. )
  660. /*++
  661. Routine Description:
  662. Reads
  663. DWORD ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
  664. DWORD ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
  665. from the registry
  666. Arguments:
  667. NONE
  668. Return Value:
  669. NONE
  670. --*/
  671. {
  672. DWORD status;
  673. HKEY key;
  674. DWORD size;
  675. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  676. "Cluster",
  677. 0,
  678. KEY_READ,
  679. &key );
  680. if ( status != ERROR_SUCCESS ) {
  681. ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
  682. ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
  683. return;
  684. }
  685. size = sizeof(ArbitrationAttempts);
  686. status = RegQueryValueEx(key,
  687. ARBITRATION_ATTEMPTS_SZ,
  688. 0,
  689. NULL,
  690. (LPBYTE)&ArbitrationAttempts,
  691. &size);
  692. if(status != ERROR_SUCCESS) {
  693. ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
  694. }
  695. if(ArbitrationAttempts < MIN_ARBITRATION_ATTEMPTS
  696. || ArbitrationAttempts > MAX_ARBITRATION_ATTEMPTS)
  697. {
  698. ArbitrationAttempts = DEFAULT_ARBITRATION_ATTEMPTS;
  699. }
  700. size = sizeof(ArbitratationSleepBeforeRetry);
  701. status = RegQueryValueEx(key,
  702. ARBITRATION_SLEEP_SZ,
  703. 0,
  704. NULL,
  705. (LPBYTE)&ArbitratationSleepBeforeRetry,
  706. &size);
  707. if(status != ERROR_SUCCESS) {
  708. ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
  709. }
  710. //
  711. // Removed this part of the check:
  712. // ArbitratationSleepBeforeRetry < MIN_SLEEP_BEFORE_RETRY
  713. // as DWORD/ULONG cannot be less than zero and it always evaluated
  714. // to FALSE.
  715. //
  716. if(ArbitratationSleepBeforeRetry > MAX_SLEEP_BEFORE_RETRY)
  717. {
  718. ArbitratationSleepBeforeRetry = DEFAULT_SLEEP_BEFORE_RETRY;
  719. }
  720. RegCloseKey(key);
  721. } // ReadArbitrationParameters //
  722. VOID
  723. CompletionRoutine(
  724. IN PCLRTL_WORK_ITEM WorkItem,
  725. IN DWORD Status,
  726. IN DWORD BytesTransferred,
  727. IN ULONG_PTR IoContext
  728. )
  729. /*++
  730. Routine Description:
  731. Arguments:
  732. Return Value:
  733. --*/
  734. {
  735. PDISK_RESOURCE ResourceEntry;
  736. if( IoContext ) {
  737. ResourceEntry = (PDISK_RESOURCE)IoContext;
  738. (DiskpLogEvent)(
  739. ResourceEntry->ResourceHandle,
  740. LOG_INFORMATION,
  741. L"[DiskArb] CompletionRoutine, status %1!u!.\n", Status);
  742. } else {
  743. PARBITRATION_INFO info = CONTAINING_RECORD(
  744. WorkItem, // Expr //
  745. ARBITRATION_INFO,
  746. WorkItem); // FieldName //
  747. ResourceEntry = CONTAINING_RECORD(
  748. info,
  749. DISK_RESOURCE,
  750. ArbitrationInfo);
  751. (DiskpLogEvent)(
  752. ResourceEntry->ResourceHandle,
  753. LOG_INFORMATION,
  754. L"[DiskArb] CompletionRoutine starts.\n", Status);
  755. }
  756. if (Status == ERROR_SUCCESS) {
  757. if (ResourceEntry->ArbitrationInfo.StopReserveInProgress) {
  758. return;
  759. }
  760. //
  761. // Repost the request
  762. //
  763. Status = AsyncCheckReserve(ResourceEntry);
  764. if (Status == ERROR_SUCCESS) {
  765. return;
  766. }
  767. }
  768. //
  769. // Some kind of error occurred,
  770. // but if we are in the middle of StopReserve
  771. // then everything is fine.
  772. //
  773. if (ResourceEntry->ArbitrationInfo.StopReserveInProgress) {
  774. return;
  775. }
  776. (DiskpLogEvent)(
  777. ResourceEntry->ResourceHandle,
  778. LOG_ERROR,
  779. L"[DiskArb] CompletionRoutine: reservation lost!\n");
  780. ClusResLogSystemEventByKey(ResourceEntry->ResourceKey,
  781. LOG_CRITICAL,
  782. RES_DISK_RESERVATION_LOST);
  783. //
  784. // Callout to cluster service to indicate that quorum has
  785. // been lost.
  786. //
  787. if (ResourceEntry->LostQuorum != NULL) {
  788. (ResourceEntry->LostQuorum)(ResourceEntry->ResourceHandle);
  789. }
  790. ResourceEntry->DiskInfo.FailStatus = Status;
  791. ResourceEntry->Reserved = FALSE;
  792. return;
  793. } // CompletionRoutine //
  794. DWORD
  795. AsyncCheckReserve(
  796. IN OUT PDISK_RESOURCE ResourceEntry
  797. )
  798. /*++
  799. Routine Description:
  800. Description
  801. Arguments:
  802. FileHandle - Handle for device to check reserve.
  803. ResourceHandle - The resource handle for reporting errors
  804. Return Value:
  805. Error Status - zero if success.
  806. --*/
  807. {
  808. BOOL success;
  809. DWORD errorCode;
  810. DWORD bytesReturned;
  811. PARBITRATION_INFO Info = &ResourceEntry->ArbitrationInfo;
  812. (DiskpLogEvent)(
  813. ResourceEntry->ResourceHandle,
  814. LOG_INFORMATION,
  815. L"[DiskArb] posting AsyncCheckReserve request.\n");
  816. ClRtlInitializeWorkItem(
  817. &(Info->WorkItem),
  818. CompletionRoutine,
  819. ResourceEntry
  820. );
  821. success = DeviceIoControl( Info->ControlHandle,
  822. IOCTL_DISK_CLUSTER_ALIVE_CHECK,
  823. &Info->InputData,
  824. sizeof(Info->InputData),
  825. &Info->OutputData,
  826. sizeof(Info->OutputData),
  827. &bytesReturned,
  828. &Info->WorkItem.Overlapped);
  829. if ( !success ) {
  830. errorCode = GetLastError();
  831. if( errorCode == ERROR_IO_PENDING ) {
  832. (DiskpLogEvent)(
  833. ResourceEntry->ResourceHandle,
  834. LOG_INFORMATION,
  835. L"[DiskArb] ********* IO_PENDING **********.\n");
  836. return ERROR_SUCCESS;
  837. }
  838. if ( !success ) {
  839. errorCode = GetLastError();
  840. (DiskpLogEvent)(
  841. ResourceEntry->ResourceHandle,
  842. LOG_ERROR,
  843. L"[DiskArb] error checking disk reservation thread, error %1!u!.\n",
  844. errorCode);
  845. return(errorCode);
  846. }
  847. }
  848. (DiskpLogEvent)(
  849. ResourceEntry->ResourceHandle,
  850. LOG_ERROR,
  851. L"[DiskArb], Premature completion of CheckReserve.\n");
  852. return(ERROR_CAN_NOT_COMPLETE);
  853. } // AsyncCheckReserve
  854. DWORD
  855. StartPersistentReservations(
  856. IN OUT PDISK_RESOURCE ResourceEntry,
  857. IN HANDLE FileHandle
  858. )
  859. /*++
  860. Routine Description:
  861. Starts driver level persistent reservations.
  862. Also starts a user-mode thread to keep an eye on driver level reservations.
  863. Arguments:
  864. ResourceEntry - the disk info structure for the disk.
  865. FileHandle - the file handle to use for arbitration.
  866. Return Value:
  867. ERROR_SUCCESS if successful.
  868. A Win32 error code on failure.
  869. --*/
  870. {
  871. DWORD status;
  872. //
  873. // If we already are performing reservations, then just leave now.
  874. //
  875. if ( ReservationInProgress(ResourceEntry) ) {
  876. return(ERROR_SUCCESS);
  877. }
  878. CL_ASSERT(WorkQueue != NULL);
  879. VerifySectorSize(ResourceEntry, FileHandle);
  880. status = DoReserve( FileHandle, ResourceEntry );
  881. if(status != ERROR_SUCCESS) {
  882. return status;
  883. }
  884. {
  885. START_RESERVE_DATA params;
  886. DWORD paramsSize;
  887. DWORD NameSize = sizeof(params.NodeSignature);
  888. // Preparing parameters to call StartReserveEx //
  889. params.DiskSignature = ResourceEntry->DiskInfo.Params.Signature;
  890. params.Version = START_RESERVE_DATA_V1_SIG;
  891. params.ArbitrationSector = BLOCK_Y;
  892. params.SectorSize = ResourceEntry->ArbitrationInfo.SectorSize;
  893. params.NodeSignatureSize = sizeof(params.NodeSignature);
  894. RtlZeroMemory(params.NodeSignature, sizeof(params.NodeSignature) );
  895. RtlCopyMemory(params.NodeSignature, NodeName, NAME_LENGTH );
  896. #if 0
  897. // When we have a reliable way of determining
  898. // whether this disk resource is a quorum
  899. // this code can be enabled
  900. if ( DoesNotNeedExpensiveReservations(ResourceEntry) ) {
  901. paramsSize = sizeof( params.DiskSignature );
  902. } else {
  903. paramsSize = sizeof( params );
  904. }
  905. #else
  906. paramsSize = sizeof( params );
  907. #endif
  908. status = StartReserveEx( &ResourceEntry->ArbitrationInfo.ControlHandle,
  909. &params,
  910. paramsSize,
  911. ResourceEntry->ResourceHandle );
  912. }
  913. if ( status != ERROR_SUCCESS ) {
  914. (DiskpLogEvent)(
  915. ResourceEntry->ResourceHandle,
  916. LOG_ERROR,
  917. L"[DiskArb]Failed to start driver reservation thread, error %1!u!.\n",
  918. status );
  919. DoRelease( FileHandle, ResourceEntry );
  920. return(status);
  921. }
  922. ResourceEntry->ArbitrationInfo.StopReserveInProgress = FALSE;
  923. status = ClRtlAssociateIoHandleWorkQueue(
  924. WorkQueue,
  925. ResourceEntry->ArbitrationInfo.ControlHandle,
  926. (ULONG_PTR)ResourceEntry
  927. );
  928. if ( status != ERROR_SUCCESS ) {
  929. (DiskpLogEvent)(
  930. ResourceEntry->ResourceHandle,
  931. LOG_ERROR,
  932. L"[DiskArb] ClRtlAssociateIoHandleWorkQueue failed, error %1!u!.\n",
  933. status );
  934. StopPersistentReservations( ResourceEntry );
  935. DoRelease( FileHandle, ResourceEntry );
  936. return(status);
  937. }
  938. ClRtlInitializeWorkItem(
  939. &(ResourceEntry->ArbitrationInfo.WorkItem),
  940. CompletionRoutine,
  941. 0
  942. );
  943. status = ClRtlPostItemWorkQueue(
  944. WorkQueue,
  945. &ResourceEntry->ArbitrationInfo.WorkItem,
  946. 0,0);
  947. if ( status != ERROR_SUCCESS ) {
  948. (DiskpLogEvent)(
  949. ResourceEntry->ResourceHandle,
  950. LOG_ERROR,
  951. L"[DiskArb] ClRtlPostItemWorkQueue failed, error %1!u!.\n",
  952. status );
  953. StopPersistentReservations( ResourceEntry );
  954. DoRelease( FileHandle, ResourceEntry );
  955. return(status);
  956. }
  957. ResourceEntry->Reserved = TRUE;
  958. return ERROR_SUCCESS;
  959. } // StartPersistentReservations //
  960. DWORD
  961. CleanupArbitrationSector(
  962. IN PDISK_RESOURCE ResourceEntry
  963. )
  964. /*++
  965. Routine Description:
  966. Arguments:
  967. ResourceEntry - the disk info structure for the disk.
  968. Return Value:
  969. ERROR_SUCCESS if successful.
  970. A Win32 error code on failure.
  971. --*/
  972. {
  973. HANDLE FileHandle = DiskspClusDiskZero;
  974. DWORD status;
  975. LPVOID unalignedBuf = 0;
  976. PARBITRATION_ID buf = 0;
  977. try {
  978. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  979. LOG_INFORMATION,
  980. L"[ArbCleanup] Verifying sector size. \n" );
  981. VerifySectorSize(ResourceEntry, FileHandle);
  982. unalignedBuf = LocalAlloc(LMEM_FIXED, ResourceEntry->ArbitrationInfo.SectorSize * 2);
  983. if( unalignedBuf == 0 ) {
  984. status = GetLastError();
  985. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  986. LOG_ERROR,
  987. L"[ArbCleanup] Failed to allocate buffer, error %1!u!.\n", status );
  988. leave;
  989. }
  990. // Alignment code assumes that ResourceEntry->ArbitrationInfo.SectorSize is the power of two //
  991. buf = (PARBITRATION_ID)
  992. (
  993. ( (ULONG_PTR)unalignedBuf + ResourceEntry->ArbitrationInfo.SectorSize )
  994. & ~((ULONG_PTR)(ResourceEntry->ArbitrationInfo.SectorSize - 1))
  995. );
  996. ZeroMemory(buf, ResourceEntry->ArbitrationInfo.SectorSize);
  997. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  998. LOG_INFORMATION,
  999. L"[ArbCleanup] Reading arbitration block. \n" );
  1000. status = DoBlockRead(ResourceEntry, FileHandle, BLOCK_Y, buf);
  1001. if (status != ERROR_SUCCESS) { leave; }
  1002. if( 0 != memcmp(buf->NodeSignature, NodeName, NAME_LENGTH) ) {
  1003. //
  1004. // Somebody is challenging us. No need to clean up the sector
  1005. //
  1006. status = ERROR_OPERATION_ABORTED;
  1007. leave;
  1008. }
  1009. ZeroMemory(buf, ResourceEntry->ArbitrationInfo.SectorSize);
  1010. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  1011. LOG_INFORMATION,
  1012. L"[ArbCleanup] Writing arbitration block. \n" );
  1013. status = DoBlockWrite(ResourceEntry, FileHandle, BLOCK_Y, buf);
  1014. if(status != ERROR_SUCCESS) {
  1015. leave;
  1016. }
  1017. } finally {
  1018. if(unalignedBuf) {
  1019. LocalFree(unalignedBuf);
  1020. }
  1021. }
  1022. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  1023. LOG_INFORMATION,
  1024. L"[ArbCleanup] Returning status %1!u!. \n", status );
  1025. return(status);
  1026. } // CleanupArbitrationSector //
  1027. VOID
  1028. StopPersistentReservations(
  1029. IN OUT PDISK_RESOURCE ResourceEntry
  1030. )
  1031. /*++
  1032. Routine Description:
  1033. Arguments:
  1034. Return Value:
  1035. --*/
  1036. {
  1037. HANDLE localHandle;
  1038. (DiskpLogEvent)(
  1039. ResourceEntry->ResourceHandle,
  1040. LOG_INFORMATION,
  1041. L"[DiskArb] StopPersistentReservations is called.\n");
  1042. //
  1043. // ReservationInProgress returns current contents of
  1044. // ResourceEntry->ArbitrationInfo.ControlHandle
  1045. //
  1046. localHandle = ReservationInProgress(ResourceEntry);
  1047. if ( localHandle ) {
  1048. DWORD status;
  1049. HANDLE ExchangeResult;
  1050. ExchangeResult = InterlockedCompareExchangePointer(
  1051. &ResourceEntry->ArbitrationInfo.ControlHandle,
  1052. 0,
  1053. localHandle);
  1054. if (ExchangeResult == localHandle) {
  1055. //
  1056. // Only one thread is allowed in here
  1057. //
  1058. ResourceEntry->ArbitrationInfo.StopReserveInProgress = TRUE;
  1059. (DiskpLogEvent)(
  1060. ResourceEntry->ResourceHandle,
  1061. LOG_INFORMATION,
  1062. L"[DiskArb] Stopping reservation thread.\n");
  1063. //
  1064. // Close the Control Handle, which stops the reservation thread and
  1065. // dismounts the volume, releases the disk, and marks it offline.
  1066. //
  1067. status = StopReserve( localHandle,
  1068. ResourceEntry->ResourceHandle );
  1069. if ( status != ERROR_SUCCESS ) {
  1070. (DiskpLogEvent)(
  1071. ResourceEntry->ResourceHandle,
  1072. LOG_ERROR,
  1073. L"Cleanup, error stopping reservation thread, error %1!u!.\n",
  1074. status);
  1075. }
  1076. status = CleanupArbitrationSector( ResourceEntry );
  1077. if (status != ERROR_SUCCESS) {
  1078. (DiskpLogEvent)(
  1079. ResourceEntry->ResourceHandle,
  1080. LOG_ERROR,
  1081. L"Cleanup, error cleaning arbitration sector, error %1!u!.\n",
  1082. status);
  1083. }
  1084. }
  1085. }
  1086. (DiskpLogEvent)(
  1087. ResourceEntry->ResourceHandle,
  1088. LOG_INFORMATION,
  1089. L"[DiskArb] StopPersistentReservations is complete.\n");
  1090. ResourceEntry->ArbitrationInfo.ControlHandle = NULL;
  1091. ResourceEntry->Reserved = FALSE;
  1092. ResourceEntry->LostQuorum = NULL;
  1093. }