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.

520 lines
13 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. LbcbSup.c
  5. Abstract:
  6. This module provides support for manipulating log buffer control blocks.
  7. Author:
  8. Brian Andrew [BrianAn] 20-June-1991
  9. Revision History:
  10. --*/
  11. #include "lfsprocs.h"
  12. //
  13. // The debug trace level
  14. //
  15. #define Dbg (DEBUG_TRACE_LBCB_SUP)
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, LfsFlushLbcb)
  18. #pragma alloc_text(PAGE, LfsFlushToLsnPriv)
  19. #pragma alloc_text(PAGE, LfsGetLbcb)
  20. #endif
  21. VOID
  22. LfsFlushLbcb (
  23. IN PLFCB Lfcb,
  24. IN PLBCB Lbcb
  25. )
  26. /*++
  27. Routine Description:
  28. This routine is called to make sure the data within an Lbcb makes it out
  29. to disk. The Lbcb must either already be in the workque or it must be
  30. a restart Lbcb.
  31. Arguments:
  32. Lfcb - This is the file control block for the log file.
  33. Lbcb - This is the Lbcb to flush.
  34. Return Value:
  35. None.
  36. --*/
  37. {
  38. LSN LastLsn;
  39. PLSN FlushedLsn;
  40. PAGED_CODE();
  41. DebugTrace( +1, Dbg, "LfsFlushLbcb: Entered\n", 0 );
  42. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  43. DebugTrace( 0, Dbg, "Lbcb -> %08lx\n", Lbcb );
  44. LastLsn = Lbcb->LastEndLsn;
  45. //
  46. // If this is a restart area we use the restart counter in the
  47. // Lfcb. Otherwise we can use the LastFlushedLsn value in the
  48. // Lfcb. This way we can determine that the Lbcb that interests
  49. // us has made it out to disk.
  50. //
  51. if (LfsLbcbIsRestart( Lbcb )) {
  52. FlushedLsn = &Lfcb->LastFlushedRestartLsn;
  53. } else {
  54. FlushedLsn = &Lfcb->LastFlushedLsn;
  55. }
  56. //
  57. // We loop here until the desired Lsn has made it to disk.
  58. // If we are able to do the I/O, we will perform it.
  59. //
  60. do {
  61. //
  62. //
  63. // If we can do the Io, call down to flush the Lfcb.
  64. //
  65. if (Lfcb->LfsIoState == LfsNoIoInProgress) {
  66. LfsFlushLfcb( Lfcb, Lbcb );
  67. break;
  68. }
  69. //
  70. // Otherwise we release the Lfcb and immediately wait on the event.
  71. //
  72. Lfcb->Waiters += 1;
  73. LfsReleaseLfcb( Lfcb );
  74. KeWaitForSingleObject( &Lfcb->Sync->Event,
  75. Executive,
  76. KernelMode,
  77. FALSE,
  78. NULL );
  79. LfsAcquireLfcb( Lfcb );
  80. Lfcb->Waiters -= 1;
  81. } while ( LastLsn.QuadPart > FlushedLsn->QuadPart );
  82. DebugTrace( -1, Dbg, "LfsFlushLbcb: Exit\n", 0 );
  83. return;
  84. }
  85. VOID
  86. LfsFlushToLsnPriv (
  87. IN PLFCB Lfcb,
  88. IN LSN Lsn
  89. )
  90. /*++
  91. Routine Description:
  92. This routine is the worker routine which performs the work of flushing
  93. a particular Lsn to disk. This routine is always called with the
  94. Lfcb acquired. This routines makes no guarantee about whether the Lfcb
  95. is acquired on exit.
  96. Arguments:
  97. Lfcb - This is the file control block for the log file.
  98. Lsn - This is the Lsn to flush to disk.
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. BOOLEAN UseLastRecordLbcb = FALSE;
  104. PLBCB LastRecordLbcb = NULL;
  105. PAGED_CODE();
  106. DebugTrace( +1, Dbg, "LfsFlushToLsnPriv: Entered\n", 0 );
  107. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  108. DebugTrace( 0, Dbg, "Lsn (Low) -> %08lx\n", Lsn.LowPart );
  109. DebugTrace( 0, Dbg, "Lsn (High) -> %08lx\n", Lsn.HighPart );
  110. //
  111. // We check if the Lsn is in the valid range. Raising an
  112. // exception if not.
  113. //
  114. if (Lsn.QuadPart > Lfcb->RestartArea->CurrentLsn.QuadPart) {
  115. UseLastRecordLbcb = TRUE;
  116. }
  117. //
  118. // If the Lsn has already been flushed we are done.
  119. // Otherwise we need to look through the workqueues and the
  120. // active queue.
  121. //
  122. if (Lsn.QuadPart > Lfcb->LastFlushedLsn.QuadPart) {
  123. PLIST_ENTRY ThisEntry;
  124. PLBCB ThisLbcb;
  125. //
  126. // Check the workqueue first. We are looking for the last
  127. // buffer block of a log page block which contains this
  128. // Lsn.
  129. //
  130. ThisEntry = Lfcb->LbcbWorkque.Flink;
  131. //
  132. // We keep looping.
  133. //
  134. while (TRUE) {
  135. ThisLbcb = CONTAINING_RECORD( ThisEntry,
  136. LBCB,
  137. WorkqueLinks );
  138. //
  139. // We pass over any restart areas. We also skip any
  140. // Lbcb's which do not contain the end of a log record.
  141. //
  142. if (!LfsLbcbIsRestart( ThisLbcb )
  143. && FlagOn( ThisLbcb->Flags, LOG_PAGE_LOG_RECORD_END )) {
  144. LastRecordLbcb = ThisLbcb;
  145. //
  146. // If the last complete Lsn in this Lbcb is greater or equal
  147. // to the desired Lsn, we exit the loop.
  148. //
  149. if (ThisLbcb->LastEndLsn.QuadPart >= Lsn.QuadPart) {
  150. break;
  151. }
  152. }
  153. //
  154. // Otherwise move to the next Lbcb.
  155. //
  156. ThisEntry = ThisEntry->Flink;
  157. //
  158. // If we have reached the end of the list then break out. We
  159. // were given an Lsn which is larger than any flushed Lsn so
  160. // we will just flush to the end of the log file.
  161. //
  162. if (ThisEntry == &Lfcb->LbcbWorkque) {
  163. if (UseLastRecordLbcb) {
  164. ThisLbcb = LastRecordLbcb;
  165. }
  166. break;
  167. }
  168. }
  169. if (ThisLbcb != NULL) {
  170. //
  171. // If we are not supporting a packed log file and this Lbcb is from
  172. // the active queue, we need to check that losing the tail of the
  173. // will not swallow up any of our reserved space.
  174. //
  175. if (!FlagOn( Lfcb->Flags, LFCB_PACK_LOG )
  176. && FlagOn( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE )) {
  177. LONGLONG CurrentAvail;
  178. LONGLONG UnusedBytes;
  179. //
  180. // Find the unused bytes.
  181. //
  182. UnusedBytes = 0;
  183. LfsCurrentAvailSpace( Lfcb,
  184. &CurrentAvail,
  185. (PULONG)&UnusedBytes );
  186. CurrentAvail = CurrentAvail - Lfcb->TotalUndoCommitment;
  187. if (UnusedBytes > CurrentAvail) {
  188. DebugTrace( -1, Dbg, "Have to preserve these bytes for possible aborts\n", 0 );
  189. ExRaiseStatus( STATUS_LOG_FILE_FULL );
  190. }
  191. //
  192. // We want to make sure we don't write any more data into this
  193. // page. Remove this from the active queue.
  194. //
  195. RemoveEntryList( &ThisLbcb->ActiveLinks );
  196. ClearFlag( ThisLbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
  197. }
  198. //
  199. // We now have the Lbcb we want to flush to disk.
  200. //
  201. LfsFlushLbcb( Lfcb, ThisLbcb );
  202. }
  203. }
  204. DebugTrace( -1, Dbg, "LfsFlushToLsnPriv: Exit\n", 0 );
  205. return;
  206. }
  207. PLBCB
  208. LfsGetLbcb (
  209. IN PLFCB Lfcb
  210. )
  211. /*++
  212. Routine Description:
  213. This routine is called to add a Lbcb to the active queue.
  214. Arguments:
  215. Lfcb - This is the file control block for the log file.
  216. Return Value:
  217. PLBCB - Pointer to the Lbcb allocated.
  218. --*/
  219. {
  220. PLBCB Lbcb = NULL;
  221. PVOID PageHeader;
  222. PBCB PageHeaderBcb = NULL;
  223. BOOLEAN WrappedOrUsaError;
  224. PAGED_CODE();
  225. DebugTrace( +1, Dbg, "LfsGetLbcb: Entered\n", 0 );
  226. DebugTrace( 0, Dbg, "Lfcb -> %08lx\n", Lfcb );
  227. //
  228. // Use a try-finally to facilitate cleanup.
  229. //
  230. try {
  231. //
  232. // Pin the desired record page.
  233. //
  234. LfsPreparePinWriteData( Lfcb,
  235. Lfcb->NextLogPage,
  236. (ULONG)Lfcb->LogPageSize,
  237. FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL ),
  238. &PageHeader,
  239. &PageHeaderBcb );
  240. #ifdef LFS_CLUSTER_CHECK
  241. //
  242. // Check the page to see if there is already data on this page with the current sequence
  243. // number. Useful to track cases where ntfs didn't find the correct end of the log or
  244. // where the cluster service has the volume mounted twice.
  245. //
  246. if (LfsTestCheckLbcb &&
  247. *((PULONG) PageHeader) == LFS_SIGNATURE_RECORD_PAGE_ULONG) {
  248. LSN LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn;
  249. //
  250. // This is not an exhaustive test but should be sufficient to catch the typical case.
  251. //
  252. ASSERT( FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL ) ||
  253. (LfsLsnToSeqNumber( Lfcb, LastLsn ) < (ULONGLONG) Lfcb->SeqNumber) ||
  254. (Lfcb->NextLogPage == Lfcb->FirstLogPage) );
  255. }
  256. #endif
  257. //
  258. // Put our signature into the page so we won't fail if we
  259. // see a previous 'BAAD' signature.
  260. //
  261. *((PULONG) PageHeader) = LFS_SIGNATURE_RECORD_PAGE_ULONG;
  262. //
  263. // Now allocate an Lbcb.
  264. //
  265. LfsAllocateLbcb( Lfcb, &Lbcb );
  266. //
  267. // If we are at the beginning of the file we test that the
  268. // sequence number won't wrap to 0.
  269. //
  270. if (!FlagOn( Lfcb->Flags, LFCB_NO_LAST_LSN | LFCB_REUSE_TAIL )
  271. && ( Lfcb->NextLogPage == Lfcb->FirstLogPage )) {
  272. Lfcb->SeqNumber = Lfcb->SeqNumber + 1;
  273. //
  274. // If the sequence number is going from 0 to 1, then
  275. // this is the first time the log file has wrapped. We want
  276. // to remember this because it means that we can now do
  277. // large spiral writes.
  278. //
  279. if (Int64ShllMod32( Lfcb->SeqNumber, Lfcb->FileDataBits ) == 0) {
  280. DebugTrace( 0, Dbg, "Log sequence number about to wrap: Lfcb -> %08lx\n", Lfcb );
  281. KeBugCheckEx( FILE_SYSTEM, 4, 0, 0, 0 );
  282. }
  283. //
  284. // If this number is greater or equal to the wrap sequence number in
  285. // the Lfcb, set the wrap flag in the Lbcb.
  286. //
  287. if (!FlagOn( Lfcb->Flags, LFCB_LOG_WRAPPED )
  288. && ( Lfcb->SeqNumber >= Lfcb->SeqNumberForWrap )) {
  289. SetFlag( Lbcb->LbcbFlags, LBCB_LOG_WRAPPED );
  290. SetFlag( Lfcb->Flags, LFCB_LOG_WRAPPED );
  291. }
  292. }
  293. //
  294. // Now initialize the rest of the Lbcb fields.
  295. //
  296. Lbcb->FileOffset = Lfcb->NextLogPage;
  297. Lbcb->SeqNumber = Lfcb->SeqNumber;
  298. Lbcb->BufferOffset = Lfcb->LogPageDataOffset;
  299. //
  300. // Store the next page in the Lfcb.
  301. //
  302. LfsNextLogPageOffset( Lfcb,
  303. Lfcb->NextLogPage,
  304. &Lfcb->NextLogPage,
  305. &WrappedOrUsaError );
  306. Lbcb->Length = Lfcb->LogPageSize;
  307. Lbcb->PageHeader = PageHeader;
  308. Lbcb->LogPageBcb = PageHeaderBcb;
  309. Lbcb->ResourceThread = ExGetCurrentResourceThread();
  310. Lbcb->ResourceThread = (ERESOURCE_THREAD) ((ULONG) Lbcb->ResourceThread | 3);
  311. //
  312. // If we are reusing a previous page then set a flag in
  313. // the Lbcb to indicate that we should flush a copy
  314. // first.
  315. //
  316. if (FlagOn( Lfcb->Flags, LFCB_REUSE_TAIL )) {
  317. SetFlag( Lbcb->LbcbFlags, LBCB_FLUSH_COPY );
  318. ClearFlag( Lfcb->Flags, LFCB_REUSE_TAIL );
  319. (ULONG)Lbcb->BufferOffset = Lfcb->ReusePageOffset;
  320. Lbcb->Flags = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Flags;
  321. Lbcb->LastLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Copy.LastLsn;
  322. Lbcb->LastEndLsn = ((PLFS_RECORD_PAGE_HEADER) PageHeader)->Header.Packed.LastEndLsn;
  323. }
  324. //
  325. // Put the Lbcb on the active queue
  326. //
  327. InsertTailList( &Lfcb->LbcbActive, &Lbcb->ActiveLinks );
  328. SetFlag( Lbcb->LbcbFlags, LBCB_ON_ACTIVE_QUEUE );
  329. //
  330. // Now that we have succeeded, set the owner thread to Thread + 1 so the resource
  331. // package will know not to peek in this thread. It may be deallocated before
  332. // we release the Bcb during flush.
  333. //
  334. CcSetBcbOwnerPointer( Lbcb->LogPageBcb, (PVOID) Lbcb->ResourceThread );
  335. } finally {
  336. DebugUnwind( LfsGetLbcb );
  337. //
  338. // If an error occurred, we need to clean up any blocks which
  339. // have not been added to the active queue.
  340. //
  341. if (AbnormalTermination()) {
  342. if (Lbcb != NULL) {
  343. LfsDeallocateLbcb( Lfcb, Lbcb );
  344. Lbcb = NULL;
  345. }
  346. //
  347. // Unpin the system page if pinned.
  348. //
  349. if (PageHeaderBcb != NULL) {
  350. CcUnpinData( PageHeaderBcb );
  351. }
  352. }
  353. DebugTrace( -1, Dbg, "LfsGetLbcb: Exit\n", 0 );
  354. }
  355. return Lbcb;
  356. }