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 + -
显示快捷键?