diff --git a/source/cache.c b/source/cache.c index 59514ae..9fcf24f 100644 --- a/source/cache.c +++ b/source/cache.c @@ -108,6 +108,35 @@ static u32 accessTime(){ return accessCounter; } +static void FlushAndInvalidateCachePage(CACHE* cache,unsigned int page) { + if(cache->cacheEntries[page].dirty == true) { + // Write the page back to disc + _FAT_disc_writeSectors (cache->disc, cache->cacheEntries[page].sector, cache->cacheEntries[page].count, cache->cacheEntries[page].cache); + } + //Invalidate + cache->cacheEntries[page].sector = CACHE_FREE; + cache->cacheEntries[page].last_access = 0; + cache->cacheEntries[page].count = 0; + cache->cacheEntries[page].dirty = false; + +} + +void InvalidateCachePagesSharingSectors(CACHE* cache, sec_t sector, sec_t numSectors, unsigned int valid_page) { + return; + unsigned int i; + if(cache==NULL)return; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + + for (i = 0; i < numberOfPages ; i++) { + if(i==valid_page) continue; + if ( ( cacheEntries[i].sector > sector+numSectors ) || + ( cacheEntries[i].sector+cacheEntries[i].count < sector ) ) continue; + + FlushAndInvalidateCachePage(cache,i); + } +} + /* Retrieve a sector's page from the cache. If it is not found in the cache, load it into the cache and return the page it was loaded to. @@ -144,7 +173,7 @@ static unsigned int _FAT_cache_getSector (CACHE* cache, sec_t sector, void* buff } cacheEntries[oldUsed].dirty = false; } - + InvalidateCachePagesSharingSectors(cache,sector,sectorsPerPage,oldUsed); // Load the new sector into the cache if (!_FAT_disc_readSectors (cache->disc, sector, sectorsPerPage, cacheEntries[oldUsed].cache)) { return false; @@ -203,13 +232,13 @@ bool _FAT_cache_getSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* cacheEntries[oldUsed].dirty = false; } - + cacheEntries[oldUsed].sector = sector; cacheEntries[oldUsed].count = cache->sectorsPerPage; + InvalidateCachePagesSharingSectors(cache,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,oldUsed); if (!_FAT_disc_readSectors (cache->disc, sector, cacheEntries[oldUsed].count, cacheEntries[oldUsed].cache)) { return false; } - cacheEntries[oldUsed].sector = sector; // Increment the usage count, don't reset it // This creates a paging policy of least used PAGE, not sector cacheEntries[oldUsed].last_access = accessTime(); @@ -344,6 +373,74 @@ bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t return false; } +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) { + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + sec_t sec; + sec_t secs_to_write; + + unsigned int oldUsed = 0; + unsigned int oldAccess = cacheEntries[0].last_access; + + while(numSectors>0) + { + i=0; + while (i < numberOfPages ) { + if ( (sector>=cacheEntries[i].sector && sector < cacheEntries[i].sector+cacheEntries[i].count) || + (sector == cacheEntries[i].sector+cacheEntries[i].count && cacheEntries[i].count < cache->sectorsPerPage)) { + sec=sector-cacheEntries[i].sector; + secs_to_write=cache->sectorsPerPage-sec; + if(secs_to_write>numSectors)secs_to_write=numSectors; + memcpy(cacheEntries[i].cache + (sec*BYTES_PER_READ), buffer, secs_to_write*BYTES_PER_READ); + cacheEntries[i].last_access = accessTime(); + cacheEntries[i].dirty = true; + cacheEntries[i].count = sec + secs_to_write; + numSectors=numSectors-secs_to_write; + if(numSectors==0) return true; + buffer+=secs_to_write*BYTES_PER_READ; + sector+=secs_to_write; + i=-1; // recheck all pages again + oldUsed = 0; + oldAccess = cacheEntries[0].last_access; + + } + else // While searching for the desired sector, also search for the least recently used page + if ( (cacheEntries[i].sector == CACHE_FREE) || (cacheEntries[i].last_access < oldAccess) ) { + oldUsed = i; + oldAccess = cacheEntries[i].last_access; + } + i++; + } + // If it didn't, replace the least recently used cache page with the desired sector + if ((cacheEntries[oldUsed].sector != CACHE_FREE) && (cacheEntries[oldUsed].dirty == true)) { + // Write the page back to disc if it has been written to + if (!_FAT_disc_writeSectors (cache->disc, cacheEntries[oldUsed].sector, cacheEntries[oldUsed].count, cacheEntries[oldUsed].cache)) { + return false; + } + cacheEntries[oldUsed].dirty = false; + } + + secs_to_write=numSectors; + if(secs_to_write>cache->sectorsPerPage)secs_to_write=cache->sectorsPerPage; + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = secs_to_write; + + memcpy(cacheEntries[oldUsed].cache, buffer, secs_to_write*BYTES_PER_READ); + buffer+=secs_to_write*BYTES_PER_READ; + sector+=secs_to_write; + numSectors=numSectors-secs_to_write; + + // Increment the usage count, don't reset it + // This creates a paging policy of least used PAGE, not sector + cacheEntries[oldUsed].last_access = accessTime(); + cacheEntries[oldUsed].dirty = true; + if(numSectors==0) return true; + oldUsed = 0; + oldAccess = cacheEntries[0].last_access; + } + return false; +} /* Flushes all dirty pages to disc, clearing the dirty flag. diff --git a/source/cache.h b/source/cache.h index bd9e10b..09d4cef 100644 --- a/source/cache.h +++ b/source/cache.h @@ -1,16 +1,16 @@ /* cache.h - The cache is not visible to the user. It should be flushed + The cache is not visible to the user. It should be flushed when any file is closed or changes are made to the filesystem. - - This cache implements a least-used-page replacement policy. This will - distribute sectors evenly over the pages, so if less than the maximum + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum pages are used at once, they should all eventually remain in the cache. This also has the benefit of throwing out old sectors, so as not to keep too many stale pages around. Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -45,7 +45,7 @@ typedef struct { sec_t sector; unsigned int count; - unsigned int last_access; + unsigned int last_access; bool dirty; uint8_t* cache; } CACHE_ENTRY; @@ -109,12 +109,14 @@ static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ); } +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); + /* Write any dirty sectors back to disc and clear out the contents of the cache */ bool _FAT_cache_flush (CACHE* cache); -/* +/* Clear out the contents of the cache without writing any dirty sectors first */ void _FAT_cache_invalidate (CACHE* cache); diff --git a/source/fatfile.c b/source/fatfile.c index 9e04714..92282b7 100644 --- a/source/fatfile.c +++ b/source/fatfile.c @@ -522,6 +522,7 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { uint8_t zeroBuffer [BYTES_PER_READ] = {0}; uint32_t remain; uint32_t tempNextCluster; + unsigned int sector; position.byte = file->filesize % BYTES_PER_READ; position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ; @@ -571,8 +572,8 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { position.cluster = tempNextCluster; } - _FAT_disc_writeSectors (partition->disc, - _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 1, zeroBuffer); + sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector; + _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer); remain -= BYTES_PER_READ; position.sector ++; @@ -730,7 +731,7 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { } if ((tempVar > 0) && flagNoError) { - if (!_FAT_disc_writeSectors (partition->disc, + if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr)) { flagNoError = false; @@ -784,8 +785,8 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { #endif (chunkSize + partition->bytesPerCluster <= remain)); - if ( !_FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector(partition, position.cluster), - chunkSize / BYTES_PER_READ, ptr)) + if ( !_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / BYTES_PER_READ, ptr)) { flagNoError = false; r->_errno = EIO; @@ -806,8 +807,7 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { // Write remaining sectors tempVar = remain / BYTES_PER_READ; // Number of sectors left if ((tempVar > 0) && flagNoError) { - if (!_FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster), - tempVar, ptr)) + if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) { flagNoError = false; r->_errno = EIO; @@ -849,8 +849,6 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { file->filesize = file->currentPosition; } } - - _FAT_syncToDisc(file); _FAT_unlock(&partition->lock); return len;