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.

1223 lines
30 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. blcache.c
  5. Abstract:
  6. This module implements general purpose disk caching based on
  7. ranges [blrange.c] but it is used mainly for the file system
  8. metadata caching on load & system devices. In order to use caching
  9. on a device, you must make sure that there is only one unique
  10. BlFileTable entry for that device and the same device is not
  11. opened & cached simultaneously multiple times under different
  12. device ids. Otherwise there will be cache inconsistencies, since
  13. cached data and structures are maintained based on device id. Also
  14. you must make sure to stop caching when the device is closed.
  15. Author:
  16. Cenk Ergan (cenke) 14-Jan-2000
  17. Revision History:
  18. --*/
  19. #include "blcache.h"
  20. #ifdef i386
  21. #include "bldrx86.h"
  22. #endif
  23. #if defined(_IA64_)
  24. #include "bldria64.h"
  25. #endif
  26. //
  27. // Define global variables.
  28. //
  29. //
  30. // This is the boot loader disk cache with all its bells and whistles.
  31. //
  32. BL_DISKCACHE BlDiskCache = {0};
  33. //
  34. // Useful defines for alignment and size in memory allocations.
  35. //
  36. //
  37. // This define is used with BlAllocateAlignedDescriptor to allocate 64KB
  38. // aligned memory. It is the number of pages for 64KB.
  39. //
  40. #define BL_DISKCACHE_64KB_ALIGNED (0x10000 >> PAGE_SHIFT)
  41. //
  42. // Prototypes for internal functions.
  43. //
  44. PBL_DISK_SUBCACHE
  45. BlDiskCacheFindCacheForDevice(
  46. ULONG DeviceId
  47. );
  48. BOOLEAN
  49. BlDiskCacheMergeRangeRoutine (
  50. PBLCRANGE_ENTRY pDestEntry,
  51. PBLCRANGE_ENTRY pSrcEntry
  52. );
  53. VOID
  54. BlDiskCacheFreeRangeRoutine (
  55. PBLCRANGE_ENTRY pRangeEntry
  56. );
  57. PBLCRANGE_ENTRY
  58. BlDiskCacheAllocateRangeEntry (
  59. VOID
  60. );
  61. VOID
  62. BlDiskCacheFreeRangeEntry (
  63. PBLCRANGE_ENTRY pEntry
  64. );
  65. //
  66. // Disk cache functions' implementation.
  67. //
  68. ARC_STATUS
  69. BlDiskCacheInitialize(
  70. VOID
  71. )
  72. /*++
  73. Routine Description:
  74. This routine initializes the global state for the boot loader
  75. disk cache, allocates the necessary memory etc.
  76. Arguments:
  77. None.
  78. Return Value:
  79. ESUCCESS - Disk caching is initialized and is on line.
  80. ARC_STATUS - There was a problem. Disk caching is not online.
  81. --*/
  82. {
  83. ARC_STATUS Status = ESUCCESS;
  84. ULONG DevIdx;
  85. ULONG BlockIdx;
  86. ULONG EntryIdx;
  87. ULONG ActualBase;
  88. ULONG SizeInPages;
  89. ULONG OldUsableBase, OldUsableLimit;
  90. //
  91. // If we have already initialized, return success right away
  92. // denoting that the disk cache is on line. Returning failure
  93. // from this function when called after the disk cache has already
  94. // been initialized may be ambiguous, i.e. as if the disk cache
  95. // failed to initialize and is not started. So we return ESUCCESS.
  96. //
  97. if (BlDiskCache.Initialized)
  98. {
  99. return ESUCCESS;
  100. }
  101. //
  102. // Try to allocate the tables & buffers for caching. We don't
  103. // allocate them together in one big allocation, because it may be
  104. // harder for the memory manager to give us that. This way
  105. // although some memory may be wasted [since returned memory is
  106. // multiple of PAGE_SIZE], two seperate free memory blocks may be
  107. // utilized.
  108. //
  109. OldUsableBase = BlUsableBase;
  110. OldUsableLimit = BlUsableLimit;
  111. BlUsableBase = BL_DISK_CACHE_RANGE_LOW;
  112. BlUsableLimit = BL_DISK_CACHE_RANGE_HIGH;
  113. Status = BlAllocateAlignedDescriptor(LoaderOsloaderHeap,
  114. 0,
  115. (BL_DISKCACHE_SIZE >> PAGE_SHIFT) + 1,
  116. BL_DISKCACHE_64KB_ALIGNED,
  117. &ActualBase);
  118. if (Status != ESUCCESS) goto cleanup;
  119. BlDiskCache.DataBuffer = (PVOID) (KSEG0_BASE | (ActualBase << PAGE_SHIFT));
  120. SizeInPages = (BL_DISKCACHE_NUM_BLOCKS * sizeof(BLCRANGE_ENTRY) >> PAGE_SHIFT) + 1;
  121. Status = BlAllocateDescriptor(LoaderOsloaderHeap,
  122. 0,
  123. SizeInPages,
  124. &ActualBase);
  125. if (Status != ESUCCESS) goto cleanup;
  126. BlDiskCache.EntryBuffer = (PVOID) (KSEG0_BASE | (ActualBase << PAGE_SHIFT));
  127. //
  128. // Make sure all entries in the device cache lookup table are
  129. // marked uninitialized.
  130. //
  131. for (DevIdx = 0; DevIdx < BL_DISKCACHE_DEVICE_TABLE_SIZE; DevIdx++)
  132. {
  133. BlDiskCache.DeviceTable[DevIdx].Initialized = FALSE;
  134. }
  135. //
  136. // Initialize free entry list.
  137. //
  138. InitializeListHead(&BlDiskCache.FreeEntryList);
  139. //
  140. // Initialize EntryBuffer used for "allocating" & "freeing"
  141. // range entries for the cached range lists.
  142. //
  143. for (EntryIdx = 0; EntryIdx < BL_DISKCACHE_NUM_BLOCKS; EntryIdx++)
  144. {
  145. //
  146. // Add this entry to the free list.
  147. //
  148. InsertHeadList(&BlDiskCache.FreeEntryList,
  149. &BlDiskCache.EntryBuffer[EntryIdx].UserLink);
  150. //
  151. // Point the UserData field to a BLOCK_SIZE chunk of the
  152. // DataBuffer.
  153. //
  154. BlDiskCache.EntryBuffer[EntryIdx].UserData =
  155. BlDiskCache.DataBuffer + (EntryIdx * BL_DISKCACHE_BLOCK_SIZE);
  156. }
  157. //
  158. // Initialize the MRU blocks list head.
  159. //
  160. InitializeListHead(&BlDiskCache.MRUBlockList);
  161. //
  162. // Mark ourselves initialized.
  163. //
  164. BlDiskCache.Initialized = TRUE;
  165. Status = ESUCCESS;
  166. DPRINT(("DK: Disk cache initialized.\n"));
  167. cleanup:
  168. BlUsableBase = OldUsableBase;
  169. BlUsableLimit = OldUsableLimit;
  170. if (Status != ESUCCESS) {
  171. if (BlDiskCache.DataBuffer) {
  172. ActualBase = (ULONG)((ULONG_PTR)BlDiskCache.DataBuffer & (~KSEG0_BASE)) >>PAGE_SHIFT;
  173. BlFreeDescriptor(ActualBase);
  174. }
  175. if (BlDiskCache.EntryBuffer) {
  176. ActualBase = (ULONG)((ULONG_PTR)BlDiskCache.EntryBuffer & (~KSEG0_BASE)) >>PAGE_SHIFT;
  177. BlFreeDescriptor(ActualBase);
  178. }
  179. DPRINT(("DK: Disk cache initialization failed.\n"));
  180. }
  181. return Status;
  182. }
  183. VOID
  184. BlDiskCacheUninitialize(
  185. VOID
  186. )
  187. /*++
  188. Routine Description:
  189. This routine uninitializes the boot loader disk cache: flushes &
  190. disables caches, free's allocated memory etc.
  191. Arguments:
  192. None.
  193. Return Value:
  194. None.
  195. --*/
  196. {
  197. ULONG DevIdx;
  198. ULONG ActualBase;
  199. //
  200. // Stop caching for all devices.
  201. //
  202. for (DevIdx = 0; DevIdx < BL_DISKCACHE_DEVICE_TABLE_SIZE; DevIdx++)
  203. {
  204. if (BlDiskCache.DeviceTable[DevIdx].Initialized)
  205. {
  206. BlDiskCacheStopCachingOnDevice(DevIdx);
  207. }
  208. }
  209. //
  210. // Free allocated memory.
  211. //
  212. if (BlDiskCache.DataBuffer)
  213. {
  214. ActualBase = (ULONG)((ULONG_PTR)BlDiskCache.DataBuffer & (~KSEG0_BASE)) >> PAGE_SHIFT;
  215. BlFreeDescriptor(ActualBase);
  216. }
  217. if (BlDiskCache.EntryBuffer)
  218. {
  219. ActualBase = (ULONG)((ULONG_PTR)BlDiskCache.EntryBuffer & (~KSEG0_BASE)) >> PAGE_SHIFT;
  220. BlFreeDescriptor(ActualBase);
  221. }
  222. //
  223. // Mark the disk cache uninitialized.
  224. //
  225. BlDiskCache.Initialized = FALSE;
  226. DPRINT(("DK: Disk cache uninitialized.\n"));
  227. return;
  228. }
  229. PBL_DISK_SUBCACHE
  230. BlDiskCacheFindCacheForDevice(
  231. ULONG DeviceId
  232. )
  233. /*++
  234. Routine Description:
  235. Return a cache header for a device id.
  236. Arguments:
  237. DeviceId - Device that we want to access cached.
  238. Return Value:
  239. Pointer to cache header or NULL if one could not be found.
  240. --*/
  241. {
  242. ULONG CurIdx;
  243. PBL_DISK_SUBCACHE pFreeEntry = NULL;
  244. //
  245. // If we have not done global disk cache initialization or we
  246. // could not allocate memory for caching data we could not have
  247. // started caching.
  248. //
  249. if ((!BlDiskCache.Initialized) || (BlDiskCache.DataBuffer == NULL))
  250. {
  251. return NULL;
  252. }
  253. //
  254. // Go through the table to see if there is an intialized cache for
  255. // this device.
  256. //
  257. for (CurIdx = 0; CurIdx < BL_DISKCACHE_DEVICE_TABLE_SIZE; CurIdx++)
  258. {
  259. if (BlDiskCache.DeviceTable[CurIdx].Initialized &&
  260. BlDiskCache.DeviceTable[CurIdx].DeviceId == DeviceId)
  261. {
  262. return &BlDiskCache.DeviceTable[CurIdx];
  263. }
  264. }
  265. //
  266. // Could not find an initialized cache for this device.
  267. //
  268. return NULL;
  269. }
  270. PBL_DISK_SUBCACHE
  271. BlDiskCacheStartCachingOnDevice(
  272. ULONG DeviceId
  273. )
  274. /*++
  275. Routine Description:
  276. Attempt at start caching on the specified device by allocating a
  277. cache header and initializing it. If caching is already enabled
  278. on that device, return existing cache header.
  279. Arguments:
  280. DeviceId - Device that we want to access cached.
  281. Return Value:
  282. Pointer to created / found cache header or NULL if there was a
  283. problem.
  284. --*/
  285. {
  286. ULONG CurIdx;
  287. PBL_DISK_SUBCACHE pFoundEntry;
  288. PBL_DISK_SUBCACHE pFreeEntry = NULL;
  289. //
  290. // If we have not done global disk cache initialization or we
  291. // could not allocate memory for caching data, we could not have
  292. // started caching.
  293. //
  294. if ((!BlDiskCache.Initialized) || (BlDiskCache.DataBuffer == NULL))
  295. {
  296. return NULL;
  297. }
  298. //
  299. // First see if we already cache this device.
  300. //
  301. if (pFoundEntry = BlDiskCacheFindCacheForDevice(DeviceId))
  302. {
  303. return pFoundEntry;
  304. }
  305. //
  306. // Go through the device table to find an empty slot.
  307. //
  308. for (CurIdx = 0; CurIdx < BL_DISKCACHE_DEVICE_TABLE_SIZE; CurIdx++)
  309. {
  310. if (!BlDiskCache.DeviceTable[CurIdx].Initialized)
  311. {
  312. pFreeEntry = &BlDiskCache.DeviceTable[CurIdx];
  313. break;
  314. }
  315. }
  316. if (!pFreeEntry)
  317. {
  318. //
  319. // There were no free entries.
  320. //
  321. return NULL;
  322. }
  323. //
  324. // Initialize & return cache entry.
  325. //
  326. pFreeEntry->DeviceId = DeviceId;
  327. BlRangeListInitialize(&pFreeEntry->Ranges,
  328. BlDiskCacheMergeRangeRoutine,
  329. BlDiskCacheFreeRangeRoutine);
  330. pFreeEntry->Initialized = TRUE;
  331. DPRINT(("DK: Started cache on device %u.\n", DeviceId));
  332. return pFreeEntry;
  333. }
  334. VOID
  335. BlDiskCacheStopCachingOnDevice(
  336. ULONG DeviceId
  337. )
  338. /*++
  339. Routine Description:
  340. Stop caching for DeviceId if we are and flush the cache.
  341. Arguments:
  342. DeviceId - Device that we want to stop caching.
  343. Return Value:
  344. None.
  345. --*/
  346. {
  347. PBL_DISK_SUBCACHE pCache;
  348. //
  349. // If we have not done global disk cache initialization or we
  350. // could not allocate memory for caching data, we could not have
  351. // started caching.
  352. //
  353. if ((!BlDiskCache.Initialized) || (BlDiskCache.DataBuffer == NULL))
  354. {
  355. return;
  356. }
  357. //
  358. // Find the cache.
  359. //
  360. pCache = BlDiskCacheFindCacheForDevice(DeviceId);
  361. if (pCache)
  362. {
  363. //
  364. // Free all the allocated ranges.
  365. //
  366. BlRangeListRemoveAllRanges(&pCache->Ranges);
  367. //
  368. // Mark the cache entry free.
  369. //
  370. pCache->Initialized = FALSE;
  371. DPRINT(("DK: Stopped cache on device %u.\n", DeviceId));
  372. }
  373. }
  374. ARC_STATUS
  375. BlDiskCacheRead (
  376. ULONG DeviceId,
  377. PLARGE_INTEGER pOffset,
  378. PVOID Buffer,
  379. ULONG Length,
  380. PULONG pCount,
  381. BOOLEAN CacheNewData
  382. )
  383. /*++
  384. Routine Description:
  385. Perform a cached read from the device. Copy over parts that are in
  386. the cache, and perform ArcRead on DeviceId for parts that are
  387. not. The data read by ArcRead will be added to the cache if
  388. CacheNewData is TRUE.
  389. NOTE. Do not call this function directly with Length > 64KB. It
  390. uses a fixed size buffer for containing list of overlapping cached
  391. ranges, and if they don't all fit into the buffer, it bails out and
  392. calls ArcRead directly [i.e. non-cached].
  393. Arguments:
  394. DeviceId - Device to read from.
  395. Offset - Offset to read from.
  396. Buffer - To read data into.
  397. Length - Number of bytes to read into Buffer from Offset on DeviceId.
  398. pCount - Number of bytes read.
  399. CacheNewData - Data that is not in the cache but was read from the
  400. disk is added to the cache.
  401. Return Value:
  402. Status.
  403. NOTE: DeviceId's (Seek) Position is undefined after this call.
  404. --*/
  405. {
  406. PBL_DISK_SUBCACHE pCache;
  407. ARC_STATUS Status;
  408. LARGE_INTEGER LargeEndOffset;
  409. BLCRANGE ReadRange;
  410. //
  411. // We use ResultsBuffer for both finding overlapping range entries and
  412. // distinct ranges.
  413. //
  414. UCHAR ResultsBuffer[BL_DISKCACHE_FIND_RANGES_BUF_SIZE];
  415. ULONG ResultsBufferSize = BL_DISKCACHE_FIND_RANGES_BUF_SIZE;
  416. PBLCRANGE_ENTRY *pOverlaps = (PBLCRANGE_ENTRY *) ResultsBuffer;
  417. ULONG NumOverlaps;
  418. PBLCRANGE pDistinctRanges = (PBLCRANGE) ResultsBuffer;
  419. ULONG NumDistincts;
  420. ULONG OverlapIdx;
  421. ULONG DistinctIdx;
  422. ULONGLONG StartOffset;
  423. ULONGLONG EndOffset;
  424. ULONGLONG ReadOffset;
  425. LARGE_INTEGER LIReadOffset;
  426. PUCHAR pSrc;
  427. PUCHAR pDest;
  428. PUCHAR pDestEnd;
  429. ULONG CopyLength;
  430. ULONG ReadLength;
  431. ULONG BytesRead;
  432. PUCHAR EndOfCallersBuffer = ((PUCHAR) Buffer) + Length;
  433. LIST_ENTRY *pLastMRUEntrysLink;
  434. PBLCRANGE_ENTRY pNewCacheEntry;
  435. PBLCRANGE_ENTRY pLastMRUEntry;
  436. ULONGLONG HeadBlockOffset;
  437. ULONGLONG TailBlockOffset;
  438. ULONG HeadBytesOffset;
  439. ULONG NumTailBytes;
  440. DPRINT(("DK: READ(%5u,%016I64x,%08x,%8u,%d)\n", DeviceId,
  441. pOffset->QuadPart, Buffer, Length, (DWORD)CacheNewData));
  442. //
  443. // Reset the number of bytes read.
  444. //
  445. *pCount = 0;
  446. //
  447. // Note where the device's position has to be after a successful
  448. // completion of the request.
  449. //
  450. LargeEndOffset.QuadPart = pOffset->QuadPart + Length;
  451. //
  452. // Look for a cache for this device.
  453. //
  454. pCache = BlDiskCacheFindCacheForDevice(DeviceId);
  455. if (pCache)
  456. {
  457. //
  458. // Determine read range.
  459. //
  460. ReadRange.Start = pOffset->QuadPart;
  461. ReadRange.End = ReadRange.Start + Length;
  462. //
  463. // If any part of the read range is in the cache, copy it over
  464. // into the buffer. First find out all cache entries that
  465. // contain data for this range. This function returns an array
  466. // of pointers to overlapping entries.
  467. //
  468. if (!BlRangeListFindOverlaps(&pCache->Ranges,
  469. &ReadRange,
  470. pOverlaps,
  471. ResultsBufferSize,
  472. &NumOverlaps))
  473. {
  474. goto SkipCache;
  475. }
  476. for (OverlapIdx = 0; OverlapIdx < NumOverlaps; OverlapIdx++)
  477. {
  478. //
  479. // Move this cache entry to the head of the MRU list.
  480. //
  481. RemoveEntryList(&pOverlaps[OverlapIdx]->UserLink);
  482. InsertHeadList(&BlDiskCache.MRUBlockList,
  483. &pOverlaps[OverlapIdx]->UserLink);
  484. //
  485. // Copy cached part. This is the overlap between the readrange
  486. // and this overlapping range, i.e. max of starts, min of ends.
  487. //
  488. StartOffset = BLCMAX(pOverlaps[OverlapIdx]->Range.Start,
  489. (ULONGLONG) pOffset->QuadPart);
  490. EndOffset = BLCMIN(pOverlaps[OverlapIdx]->Range.End,
  491. ((ULONGLONG) pOffset->QuadPart) + Length);
  492. CopyLength = (ULONG) (EndOffset - StartOffset);
  493. pSrc = ((PUCHAR) pOverlaps[OverlapIdx]->UserData) +
  494. (StartOffset - pOverlaps[OverlapIdx]->Range.Start);
  495. pDest = ((PUCHAR) Buffer) +
  496. (StartOffset - (ULONGLONG) pOffset->QuadPart);
  497. DPRINT(("DK: CopyCached:%08x,%08x,%d\n", pDest, pSrc, CopyLength));
  498. DASSERT((pDest < (PUCHAR) Buffer) ||
  499. (pDest + CopyLength > EndOfCallersBuffer));
  500. RtlCopyMemory(pDest, pSrc, CopyLength);
  501. *pCount += CopyLength;
  502. }
  503. if (*pCount == Length)
  504. {
  505. //
  506. // The full request was satisfied from the cache. Seek to
  507. // where the device position should be if the request was
  508. // read from the device.
  509. //
  510. if (ArcSeek(DeviceId, &LargeEndOffset, SeekAbsolute) != ESUCCESS)
  511. {
  512. goto SkipCache;
  513. }
  514. return ESUCCESS;
  515. }
  516. //
  517. // Identify distinct ranges that are not in the cache.
  518. //
  519. if (!BlRangeListFindDistinctRanges(&pCache->Ranges,
  520. &ReadRange,
  521. pDistinctRanges,
  522. ResultsBufferSize,
  523. &NumDistincts))
  524. {
  525. goto SkipCache;
  526. }
  527. //
  528. // Read the distinct ranges from the disk and copy them into
  529. // caller's buffer. This function returns an array of
  530. // BLCRANGE's that are subranges of the requested range that
  531. // do not overlap with any ranges in the cache.
  532. //
  533. for (DistinctIdx = 0; DistinctIdx < NumDistincts; DistinctIdx++)
  534. {
  535. if (CacheNewData)
  536. {
  537. //
  538. // Not only do we have to read uncached parts from the disk,
  539. // we also have to add them to our cache.
  540. //
  541. StartOffset = pDistinctRanges[DistinctIdx].Start;
  542. EndOffset = pDistinctRanges[DistinctIdx].End;
  543. pDest = ((PUCHAR) Buffer) +
  544. (StartOffset - pOffset->QuadPart);
  545. ReadLength = BL_DISKCACHE_BLOCK_SIZE;
  546. ReadOffset = StartOffset & (~(BL_DISKCACHE_BLOCK_SIZE - 1));
  547. //
  548. // Make note of Head & Tail block offsets and number
  549. // of bytes, so it is easy to recognize that we will
  550. // copy only a part of the data we read from the disk
  551. // into the callers buffer. Setting these up here, we
  552. // don't have to handle the four cases seperately: i.e.
  553. // - when the block we read is the head [i.e. first] block
  554. // and the range starts from an offset into the block
  555. // - when the block we read is the tail [i.e. last] block
  556. // and the range extends only a number of bytes into it.
  557. // - when the block we read is both the head and the tail.
  558. // - when the block we read is from the middle of the range
  559. // and all of it is in the range.
  560. //
  561. HeadBlockOffset = StartOffset & (~(BL_DISKCACHE_BLOCK_SIZE - 1));
  562. TailBlockOffset = EndOffset & (~(BL_DISKCACHE_BLOCK_SIZE - 1));
  563. HeadBytesOffset = (ULONG)(StartOffset & (BL_DISKCACHE_BLOCK_SIZE - 1));
  564. NumTailBytes = (ULONG)(EndOffset & (BL_DISKCACHE_BLOCK_SIZE - 1));
  565. //
  566. // We need to read this range from the disk in
  567. // BLOCK_SIZE aligned BLOCK_SIZE chunks, build new
  568. // cache entries to add to the cached ranges list and
  569. // copy it to the target buffer.
  570. //
  571. pDestEnd = ((PUCHAR)Buffer) + (EndOffset - pOffset->QuadPart);
  572. while (pDest < pDestEnd)
  573. {
  574. //
  575. // First get our hands on a new cache entry.
  576. //
  577. pNewCacheEntry = BlDiskCacheAllocateRangeEntry();
  578. if (!pNewCacheEntry)
  579. {
  580. //
  581. // We will free the last MRU entry and use that.
  582. //
  583. if (IsListEmpty(&BlDiskCache.MRUBlockList))
  584. {
  585. goto SkipCache;
  586. }
  587. //
  588. // Identify the last MRU entry.
  589. //
  590. pLastMRUEntrysLink = BlDiskCache.MRUBlockList.Blink;
  591. pLastMRUEntry = CONTAINING_RECORD(pLastMRUEntrysLink,
  592. BLCRANGE_ENTRY,
  593. UserLink);
  594. //
  595. // Remove the entry from cached list. When the
  596. // entry is freed, it is removed from MRU and
  597. // put onto the free list.
  598. //
  599. BlRangeListRemoveRange(&pCache->Ranges,
  600. &pLastMRUEntry->Range);
  601. //
  602. // Now try allocating a new entry.
  603. //
  604. pNewCacheEntry = BlDiskCacheAllocateRangeEntry();
  605. if (!pNewCacheEntry) {
  606. goto SkipCache;
  607. }
  608. }
  609. //
  610. // Read BLOCK_SIZE from device into the cache
  611. // entry's buffer.
  612. //
  613. pNewCacheEntry->Range.Start = ReadOffset;
  614. pNewCacheEntry->Range.End = ReadOffset + ReadLength;
  615. LIReadOffset.QuadPart = ReadOffset;
  616. if (ArcSeek(DeviceId,
  617. &LIReadOffset,
  618. SeekAbsolute) != ESUCCESS)
  619. {
  620. BlDiskCacheFreeRangeEntry(pNewCacheEntry);
  621. goto SkipCache;
  622. }
  623. if (ArcRead(DeviceId,
  624. pNewCacheEntry->UserData,
  625. ReadLength,
  626. &BytesRead) != ESUCCESS)
  627. {
  628. BlDiskCacheFreeRangeEntry(pNewCacheEntry);
  629. goto SkipCache;
  630. }
  631. if (BytesRead != ReadLength)
  632. {
  633. BlDiskCacheFreeRangeEntry(pNewCacheEntry);
  634. goto SkipCache;
  635. }
  636. //
  637. // Add this range to cached ranges.
  638. //
  639. if (!BlRangeListAddRange(&pCache->Ranges, pNewCacheEntry))
  640. {
  641. BlDiskCacheFreeRangeEntry(pNewCacheEntry);
  642. goto SkipCache;
  643. }
  644. //
  645. // Put this cache entry at the head of MRU.
  646. //
  647. InsertHeadList(&BlDiskCache.MRUBlockList,
  648. &pNewCacheEntry->UserLink);
  649. //
  650. // Now copy read data into callers buffer. Adjust
  651. // the source pointer and the number of bytes to
  652. // copy depending on whether the block we are going
  653. // to copy from is the head or tail block, or both.
  654. //
  655. CopyLength = ReadLength;
  656. pSrc = pNewCacheEntry->UserData;
  657. if (ReadOffset == HeadBlockOffset)
  658. {
  659. CopyLength -= HeadBytesOffset;
  660. pSrc += HeadBytesOffset;
  661. }
  662. if (ReadOffset == TailBlockOffset)
  663. {
  664. CopyLength -= (BL_DISKCACHE_BLOCK_SIZE - NumTailBytes);
  665. }
  666. DPRINT(("DK: CopyNew:%08x,%08x,%d\n", pDest, pSrc, CopyLength));
  667. DASSERT((pDest < (PUCHAR) Buffer) ||
  668. (pDest + CopyLength > EndOfCallersBuffer));
  669. RtlCopyMemory(pDest, pSrc, CopyLength);
  670. //
  671. // Set new ReadOffset.
  672. //
  673. ReadOffset += ReadLength;
  674. //
  675. // Update pDest & number of bytes we've filled in
  676. // so far.
  677. //
  678. pDest += CopyLength;
  679. *pCount += CopyLength;
  680. }
  681. }
  682. else
  683. {
  684. //
  685. // We don't need to cache what we read. Just read the
  686. // range from the disk.
  687. //
  688. StartOffset = pDistinctRanges[DistinctIdx].Start;
  689. pDest = ((PUCHAR) Buffer) +
  690. (StartOffset - pOffset->QuadPart);
  691. ReadLength = (ULONG) (pDistinctRanges[DistinctIdx].End -
  692. pDistinctRanges[DistinctIdx].Start);
  693. LIReadOffset.QuadPart = StartOffset;
  694. if (ArcSeek(DeviceId,
  695. &LIReadOffset,
  696. SeekAbsolute) != ESUCCESS)
  697. {
  698. goto SkipCache;
  699. }
  700. DPRINT(("DK: ReadDistinct:%016I64x,%08x,%8d\n",
  701. LIReadOffset.QuadPart, pDest, ReadLength));
  702. if (ArcRead(DeviceId,
  703. pDest,
  704. ReadLength,
  705. &BytesRead) != ESUCCESS)
  706. {
  707. goto SkipCache;
  708. }
  709. if (BytesRead != ReadLength)
  710. {
  711. goto SkipCache;
  712. }
  713. *pCount += BytesRead;
  714. }
  715. }
  716. //
  717. // We should have read Length bytes.
  718. //
  719. ASSERT(*pCount == Length);
  720. //
  721. // Seek to where the device position should be if the request was
  722. // read from the device.
  723. //
  724. if (ArcSeek(DeviceId, &LargeEndOffset, SeekAbsolute) != ESUCCESS)
  725. {
  726. goto SkipCache;
  727. }
  728. return ESUCCESS;
  729. }
  730. //
  731. // If we hit a problem satisfying the request through the cache,
  732. // we'll jump here to try the normal read.
  733. //
  734. SkipCache:
  735. //
  736. // Reset the number of bytes read.
  737. //
  738. *pCount = 0;
  739. //
  740. // If no cache was found or data could not be read from the cache,
  741. // hand over to ArcRead.
  742. //
  743. if ((Status = ArcSeek(DeviceId, pOffset, SeekAbsolute)) != ESUCCESS)
  744. {
  745. return Status;
  746. }
  747. DPRINT(("DK: SkipCacheRead:%016I64x,%08x,%d\n",
  748. LIReadOffset.QuadPart, pDest, CopyLength));
  749. Status = ArcRead(DeviceId, Buffer, Length, pCount);
  750. return Status;
  751. }
  752. ARC_STATUS
  753. BlDiskCacheWrite (
  754. ULONG DeviceId,
  755. PLARGE_INTEGER pOffset,
  756. PVOID Buffer,
  757. ULONG Length,
  758. PULONG pCount
  759. )
  760. /*++
  761. Routine Description:
  762. Perform a write to the cached device. Currently simply invalidate
  763. any cached data around that range and call ArcWrite.
  764. Arguments:
  765. DeviceId - Device to write to.
  766. Offset - Offset to write beginning from.
  767. Buffer - Data to write.
  768. Length - Number of bytes to write.
  769. pCount - Number of bytes written.
  770. Return Value:
  771. Status.
  772. NOTE: DeviceId's (Seek) Position is undefined after this call.
  773. --*/
  774. {
  775. PBL_DISK_SUBCACHE pCache;
  776. ARC_STATUS Status;
  777. BLCRANGE WriteRange;
  778. DPRINT(("DK: WRITE(%5u,%016I64x,%08x,%8u)\n",
  779. DeviceId, pOffset->QuadPart, Buffer, Length));
  780. pCache = BlDiskCacheFindCacheForDevice(DeviceId);
  781. //
  782. // If a cache was found, invalidate cached data around
  783. // the range.
  784. //
  785. if (pCache)
  786. {
  787. WriteRange.Start = pOffset->QuadPart;
  788. WriteRange.End = WriteRange.Start + Length;
  789. //
  790. // The free-range-entry routine we initialized the rangelist
  791. // with will remove entries from the MRU list in addition to
  792. // free'ing them. So all we have to do is to call RemoveRange.
  793. //
  794. BlRangeListRemoveRange(&pCache->Ranges, &WriteRange);
  795. }
  796. if ((Status = ArcSeek(DeviceId, pOffset, SeekAbsolute)) != ESUCCESS)
  797. {
  798. return Status;
  799. }
  800. Status = ArcWrite(DeviceId, Buffer, Length, pCount);
  801. return Status;
  802. }
  803. BOOLEAN
  804. BlDiskCacheMergeRangeRoutine (
  805. PBLCRANGE_ENTRY pDestEntry,
  806. PBLCRANGE_ENTRY pSrcEntry
  807. )
  808. /*++
  809. Routine Description:
  810. This routine is passed to rangelist initialization, so rangelist
  811. functions can use it to merge two cache blocks that are
  812. consecutive.
  813. Arguments:
  814. pDestEntry, pSrcEntry - Two entries to merge.
  815. Return Value:
  816. FALSE.
  817. --*/
  818. {
  819. //
  820. // We don't want anything to get merged, because our block size is
  821. // fixed. So we always return FALSE.
  822. //
  823. return FALSE;
  824. }
  825. VOID
  826. BlDiskCacheFreeRangeRoutine (
  827. PBLCRANGE_ENTRY pRangeEntry
  828. )
  829. /*++
  830. Routine Description:
  831. This routine is passed to rangelist initialization, so rangelist
  832. functions can use it to free a cache block entry and its data.
  833. Arguments:
  834. pRangeEntry - Range entry to free.
  835. Return Value:
  836. None.
  837. --*/
  838. {
  839. //
  840. // Remove from the MRU list.
  841. //
  842. RemoveEntryList(&pRangeEntry->UserLink);
  843. //
  844. // Call the function to free the range entry.
  845. //
  846. BlDiskCacheFreeRangeEntry(pRangeEntry);
  847. return;
  848. }
  849. PBLCRANGE_ENTRY
  850. BlDiskCacheAllocateRangeEntry (
  851. VOID
  852. )
  853. /*++
  854. Routine Description:
  855. This routine allocates a range entry used to describe a cached
  856. range on a device. Its UserData points to a BLOCK_SIZE of memory
  857. to contain the cached data.
  858. Arguments:
  859. None.
  860. Return Value:
  861. Pointer to a range entry or NULL if out of memory.
  862. --*/
  863. {
  864. PBLCRANGE_ENTRY pFreeEntry = NULL;
  865. LIST_ENTRY *pFreeEntryLink;
  866. //
  867. // If the free list is not empty, remove an entry and return it.
  868. //
  869. if (!IsListEmpty(&BlDiskCache.FreeEntryList))
  870. {
  871. pFreeEntryLink = RemoveHeadList(&BlDiskCache.FreeEntryList);
  872. pFreeEntry = CONTAINING_RECORD(pFreeEntryLink,
  873. BLCRANGE_ENTRY,
  874. UserLink);
  875. }
  876. return pFreeEntry;
  877. }
  878. VOID
  879. BlDiskCacheFreeRangeEntry (
  880. PBLCRANGE_ENTRY pEntry
  881. )
  882. /*++
  883. Routine Description:
  884. This routine frees a range entry. Currently it simply inserts it
  885. back on the FreeList, so it can be "allocated" when we need
  886. another range entry.
  887. Arguments:
  888. pEntry - Pointer to the entry to be freed.
  889. Return Value:
  890. None.
  891. --*/
  892. {
  893. //
  894. // Insert this entry back on the free list.
  895. //
  896. InsertHeadList(&BlDiskCache.FreeEntryList,
  897. &pEntry->UserLink);
  898. return;
  899. }