cache.cpp
来自「WinCE5.0部分核心源码」· C++ 代码 · 共 876 行 · 第 1/2 页
CPP
876 行
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
#include "cache.h"
CCache::CCache (HDSK hDsk, DWORD dwStart, DWORD dwEnd, DWORD dwCacheSize, DWORD dwBlockSize, DWORD dwCreateFlags) :
m_hDsk (hDsk),
m_dwStart (dwStart),
m_dwEnd (dwEnd),
m_dwCacheSize (dwCacheSize),
m_dwBlockSize (dwBlockSize),
m_dwCreateFlags (dwCreateFlags),
m_dwInternalFlags(0),
m_pCacheLookup (NULL),
m_dwDirtyCount(0),
m_hDirtySectorsEvent(NULL),
m_hLazyWriterThread(NULL)
{
}
CCache::~CCache()
{
if (IsWriteBack()) {
// Notify lazy-writer thread it is time to exit.
SetEventData (m_hDirtySectorsEvent, EVENT_DATA_EXIT);
SetEvent (m_hDirtySectorsEvent);
// Wait for the lazy-writer thread to terminate within 20 seconds.
DWORD dwReturn = WaitForSingleObject (m_hLazyWriterThread, 20000);
if (dwReturn == WAIT_TIMEOUT)
TerminateThread (m_hLazyWriterThread, 0);
CloseHandle (m_hLazyWriterThread);
CloseHandle (m_hDirtySectorsEvent);
}
EnterCriticalSection (&m_cs);
DWORD iBuffer;
DWORD dwTotalBuffers = (m_dwCacheSize * m_dwBlockSize + BUFFER_SIZE - 1) / BUFFER_SIZE;
for (iBuffer = 0; iBuffer < dwTotalBuffers; iBuffer++) {
VirtualFree (m_apBufferPool[iBuffer], 0, MEM_RELEASE);
}
LocalFree (m_apBufferPool);
LocalFree (m_pCacheLookup);
LocalFree (m_pStatus);
LeaveCriticalSection (&m_cs);
DeleteCriticalSection (&m_cs);
}
DWORD CCache::ReadWriteDisk (DWORD dwIOCTL, DWORD dwSectorNum, DWORD dwNumSectors, PVOID pBuffer)
{
SG_REQ sg;
BOOL fSuccess;
sg.sr_start = dwSectorNum;
sg.sr_num_sec = dwNumSectors;
sg.sr_num_sg = 1;
sg.sr_status = ERROR_NOT_SUPPORTED; // not used by ATADisk
sg.sr_callback = NULL;
sg.sr_sglist[0].sb_buf = (LPBYTE) MapPtrToProcess(pBuffer, GetCurrentProcess());
sg.sr_sglist[0].sb_len = m_dwBlockSize * dwNumSectors;
fSuccess = FSDMGR_DiskIoControl(m_hDsk, dwIOCTL, &sg, sizeof(sg),
NULL, 0, NULL, NULL);
DEBUGMSG(ZONE_IO, (L"ReadWriteDisk: Command: %s, Start Sector: %d, Num Sectors: %d\r\n",
dwIOCTL == DISK_IOCTL_READ ? L"Read" : L"Write", dwSectorNum, dwNumSectors));
if (!fSuccess) {
DWORD dwError = GetLastError();
if (dwError == ERROR_SUCCESS) {
DEBUGMSG(ZONE_IO, (L"ReadWriteDisk: Warning - Error code not set by block driver. Setting to ERROR_GEN_FAILURE."));
dwError = ERROR_GEN_FAILURE;
}
DEBUGMSG(ZONE_ERROR, (L"Read/Write Sector failed (%d) on Sector %d\r\n", dwError, dwSectorNum));
return dwError;
}
return ERROR_SUCCESS;
}
VOID CCache::ReadFromCacheBuffer (DWORD dwCacheIndex, LPBYTE pBuffer)
{
DWORD dwByteOffset = dwCacheIndex * m_dwBlockSize;
LPBYTE pCacheBuffer = m_apBufferPool[dwByteOffset / BUFFER_SIZE] + (dwByteOffset % BUFFER_SIZE);
memcpy (pBuffer, pCacheBuffer, m_dwBlockSize);
}
VOID CCache::WriteToCacheBuffer (DWORD dwCacheIndex, LPBYTE pBuffer, BOOL fDirty)
{
DWORD dwByteOffset = dwCacheIndex * m_dwBlockSize;
LPBYTE pCacheBuffer = m_apBufferPool[dwByteOffset / BUFFER_SIZE] + (dwByteOffset % BUFFER_SIZE);
memcpy (pCacheBuffer, pBuffer, m_dwBlockSize);
if (IsWriteBack() && !IsDirty(dwCacheIndex) && fDirty) {
SetDirtyStatus (dwCacheIndex);
}
}
VOID CCache::WarmCache()
{
DWORD iBuffer;
DWORD dwTotalBuffers = (m_dwCacheSize * m_dwBlockSize + BUFFER_SIZE - 1) / BUFFER_SIZE;
DWORD dwSectorsPerBuffer = BUFFER_SIZE / m_dwBlockSize;
// Read all data into cache buffers
for (iBuffer = 0; iBuffer < dwTotalBuffers; iBuffer++) {
DWORD dwNumSectors = dwSectorsPerBuffer;
// If this is the last buffer, only warm what is needed
if ((iBuffer == (dwTotalBuffers - 1)) && (m_dwCacheSize % dwSectorsPerBuffer)) {
dwNumSectors = m_dwCacheSize % dwSectorsPerBuffer;
}
ReadWriteDisk (DISK_IOCTL_READ, iBuffer * dwSectorsPerBuffer, dwNumSectors, m_apBufferPool[iBuffer]);
}
// Update lookup table
for (DWORD i = 0; i < m_dwCacheSize; i++) {
m_pCacheLookup[i] = i;
}
}
VOID CCache::SetDirtyStatus (DWORD dwIndex)
{
if (m_dwDirtyCount == 0)
SetEvent (m_hDirtySectorsEvent);
m_pStatus[dwIndex] |= STATUS_DIRTY;
m_dwDirtyCount++;
}
VOID CCache::ClearDirtyStatus (DWORD dwStartIndex, DWORD dwEndIndex)
{
DWORD i;
for (i = dwStartIndex; i <= dwEndIndex; i++) {
m_pStatus[i] &= ~STATUS_DIRTY;
}
ASSERT (m_dwDirtyCount >= (dwEndIndex - dwStartIndex + 1));
m_dwDirtyCount -= (dwEndIndex - dwStartIndex + 1);
if (m_dwDirtyCount == 0)
ResetEvent (m_hDirtySectorsEvent);
}
/* CCache::CommitCacheSectors
*
* Commits a set of consecutively cached sectors.
*
* ENTRY
* dwStartIndex - The starting cache index to commit
* dwEndIndex - The ending cache index to commit
*
* EXIT
* Returns appropriate error code.
*/
DWORD CCache::CommitCacheSectors (DWORD dwStartIndex, DWORD dwEndIndex)
{
DWORD iBuffer, i;
DWORD dwSectorsPerBuffer = BUFFER_SIZE / m_dwBlockSize;
DWORD dwStartBuffer = dwStartIndex / dwSectorsPerBuffer;
DWORD dwEndBuffer = dwEndIndex / dwSectorsPerBuffer;
ASSERT (dwStartBuffer <= dwEndBuffer);
DWORD dwTempStartBuffer = dwStartBuffer;
DWORD dwTempStartIndex = dwStartIndex;
while (dwTempStartBuffer <= dwEndBuffer) {
DWORD dwTempEndBuffer = dwEndBuffer;
DWORD dwTempEndIndex = dwEndIndex;
// Only allow a max of MAX_SG_BUF buffers in the request
if (dwTempStartBuffer + MAX_SG_BUF <= dwEndBuffer) {
dwTempEndBuffer = dwTempStartBuffer + MAX_SG_BUF - 1;
dwTempEndIndex = (dwTempEndBuffer + 1) * dwSectorsPerBuffer - 1;
}
DWORD dwSizeSg = sizeof(SG_REQ) + (dwTempEndBuffer - dwTempStartBuffer) * sizeof(SG_BUF);
PSG_REQ psg = (PSG_REQ) LocalAlloc (LMEM_FIXED, dwSizeSg);
if (!psg) {
DEBUGMSG(ZONE_APIS, (TEXT("CommitCacheSectors: Memory allocation failed.\r\n")));
return ERROR_OUTOFMEMORY;
}
DWORD dwResult;
psg->sr_start = m_pCacheLookup[dwTempStartIndex];
psg->sr_num_sec = dwTempEndIndex - dwTempStartIndex + 1;
psg->sr_num_sg = dwTempEndBuffer - dwTempStartBuffer + 1;
psg->sr_status = ERROR_NOT_SUPPORTED; // not used by ATADisk
psg->sr_callback = NULL;
for (iBuffer = dwTempStartBuffer, i = 0; iBuffer <= dwTempEndBuffer; iBuffer++, i++) {
// Determine the starting location within the buffer. This will only be non-zero for the first buffer.
DWORD dwStartBufIndex = (iBuffer == dwTempStartBuffer) ? (dwTempStartIndex % dwSectorsPerBuffer) : 0;
// Determine the number of sectors to process for this current buffer.
DWORD dwNumSectors = (iBuffer == dwTempEndBuffer) ?
(dwTempEndIndex % dwSectorsPerBuffer) - dwStartBufIndex + 1 : dwSectorsPerBuffer - dwStartBufIndex;
psg->sr_sglist[i].sb_buf = (LPBYTE) MapPtrToProcess(m_apBufferPool[iBuffer] + m_dwBlockSize * dwStartBufIndex, GetCurrentProcess());
psg->sr_sglist[i].sb_len = m_dwBlockSize * dwNumSectors;
}
dwResult = FSDMGR_DiskIoControl(m_hDsk, DISK_IOCTL_WRITE, psg, dwSizeSg, NULL, 0, NULL, NULL);
DEBUGMSG(ZONE_IO, (L"CommitCacheSectors: Command: Write, Start Sector: %d, Num Sectors: %d\r\n", psg->sr_start, psg->sr_num_sec));
if (!dwResult) {
DEBUGMSG(ZONE_ERROR, (L"CommitCacheSectors failed on Sectors [%d, %d]\r\n", m_pCacheLookup[dwTempStartIndex], m_pCacheLookup[dwTempEndIndex]));
SetLastError(psg->sr_status);
LocalFree (psg);
return psg->sr_status;
}
// Clear the dirty bits if write is successful
ClearDirtyStatus (dwTempStartIndex, dwTempEndIndex);
LocalFree (psg);
dwTempStartBuffer = dwTempEndBuffer + 1;
dwTempStartIndex = dwTempEndIndex + 1;
}
return ERROR_SUCCESS;
}
/* CCache::CommitDirtySectors
*
* Commits all cached dirty sectors within the desired range.
*
* ENTRY
* dwStartSector - Starting sector to search for sectors to commit
* dwNumSectors - Number of sectors to search
* dwOption - COMMIT_ALL_SECTORS to commit all dirty sectors that falls in the desired range
* - COMMIT_DIFF_SECTORS to commit the cached sector only if it is different
* from the sector passed in, since anything different will be replaced.
* - COMMIT_SPECIFIED_SECTORS to commit the cached sector only if it is the same as
* the sector passed in, indicating that we want to flush those sectors.
*
* EXIT
* Returns appropriate error code.
*/
VOID CCache::CommitDirtySectors (DWORD dwStartSector, DWORD dwNumSectors, DWORD dwOption)
{
DWORD dwEndSector = dwStartSector + dwNumSectors - 1;
DWORD dwStartCacheIndex = GetIndex(dwStartSector);
DWORD dwEndCacheIndex = GetIndex(dwEndSector);
DWORD dwNumCacheSectors = dwNumSectors;
// If the number of sectors to write is greater than the cache size, then flush the whole cache
if ((dwOption == COMMIT_DIFF_SECTORS) && (dwNumSectors > m_dwCacheSize)) {
dwOption = COMMIT_ALL_SECTORS;
dwStartCacheIndex = 0;
dwEndCacheIndex = m_dwCacheSize - 1;
dwNumCacheSectors = m_dwCacheSize;
}
DWORD dwCurrentCacheIndex = dwStartCacheIndex;
DWORD dwCurrentSector = dwStartSector;
while (dwNumCacheSectors) {
// If we've reached the end of the cache and still have more sectors to
// commit, wrap around to the beginning
if (dwCurrentCacheIndex == m_dwCacheSize) {
dwCurrentCacheIndex = 0;
}
// If COMMIT_ALL_SECTORS is set, commit any cached dirty sector
// If COMMIT_REPLACE_SECTORS is set, commit any dirty sector only if it is not the
// same sector that we plan to write to.
// If COMMIT_FLUSH_SECTORS is set, commit any dirty sector only if it is the
// same sector that the caller wanted flushed.
if (IsDirty(dwCurrentCacheIndex) &&
((dwOption == COMMIT_ALL_SECTORS) ||
((dwOption == COMMIT_DIFF_SECTORS) && (m_pCacheLookup[dwCurrentCacheIndex] != dwCurrentSector)) ||
((dwOption == COMMIT_SPECIFIED_SECTORS) && (m_pCacheLookup[dwCurrentCacheIndex] == dwCurrentSector))))
{
DWORD dwPrevCachedSector;
dwStartCacheIndex = dwCurrentCacheIndex;
// Found a dirty sector that we want to commit. Commit all consecutive
// sectors starting with this one that are dirty
do {
dwPrevCachedSector = m_pCacheLookup[dwCurrentCacheIndex++];
dwNumCacheSectors--;
dwCurrentSector++;
} while (dwNumCacheSectors && IsDirty(dwCurrentCacheIndex) &&
(m_pCacheLookup[dwCurrentCacheIndex] == dwPrevCachedSector+1) &&
(dwCurrentCacheIndex < m_dwCacheSize));
// Commit these dirty sectors
CommitCacheSectors (dwStartCacheIndex, dwCurrentCacheIndex - 1);
}
else
{
dwNumCacheSectors--;
dwCurrentCacheIndex++;
dwCurrentSector++;
}
}
}
/* CCache::CommitNextDirtyRun
*
* Commits the next set of consecutively cached sectors starting
* the cache index passed in.
*
* ENTRY
* dwStartIndex - Starting cache index to search for sectors to commit
*
* EXIT
* Returns appropriate error code.
*/
DWORD CCache::CommitNextDirtyRun (DWORD dwStartIndex)
{
DWORD dwEndIndex;
DWORD dwPrevCachedSector;
// Find first dirty cache index
while (dwStartIndex < m_dwCacheSize && !IsDirty(dwStartIndex)) {
dwStartIndex++;
}
if (dwStartIndex == m_dwCacheSize) {
return 0;
}
// Commit all consecutive sectors starting with this one
// that are dirty
dwEndIndex = dwStartIndex + 1;
dwPrevCachedSector = m_pCacheLookup[dwStartIndex];
while ((dwEndIndex < m_dwCacheSize) && IsDirty(dwEndIndex) &&
(m_pCacheLookup[dwEndIndex] == dwPrevCachedSector+1))
{
dwPrevCachedSector++;
dwEndIndex++;
}
CommitCacheSectors(dwStartIndex, dwEndIndex-1);
// Return the next starting point.
return (dwEndIndex == m_dwCacheSize) ? 0 : dwEndIndex;
}
/* CCache::DeleteCachedSectors
*
* Removes the specified sectors from the cache and unmarks them dirty.
*
* ENTRY
* pInfo - Structure containing start sector and num sectors to remove.
*
* EXIT
* TRUE on success.
*/
BOOL CCache::DeleteCachedSectors (PDELETE_SECTOR_INFO pInfo)
{
DWORD dwSector;
DWORD dwRet;
EnterCriticalSection (&m_cs);
for (dwSector = pInfo->startsector; dwSector < pInfo->startsector + pInfo->numsectors; dwSector++) {
InvalidateSector(dwSector);
}
LeaveCriticalSection (&m_cs);
// If delete sectors is not implemented on block driver, we are done.
if (m_dwInternalFlags & FLAG_DISABLE_SEND_DELETE) {
return TRUE;
}
// Call delete sectors on the disk if it is implemented
if (!FSDMGR_DiskIoControl(m_hDsk, IOCTL_DISK_DELETE_SECTORS, pInfo, sizeof(DELETE_SECTOR_INFO),
NULL, 0, &dwRet, NULL))
{
m_dwInternalFlags |= FLAG_DISABLE_SEND_DELETE;
}
return TRUE;
}
/* CCache::LazyWriterThread
*
* For write-back cache, this thread commits dirty sectors
*
* ENTRY
* None.
*
* EXIT
* None.
*/
VOID CCache::LazyWriterThread ()
{
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?