mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-21 15:25:36 +03:00
Flush SD card pages in write order
This commit is contained in:
parent
392ec7412a
commit
75d5c64b33
1 changed files with 83 additions and 48 deletions
|
@ -123,16 +123,27 @@ typedef union afatfsFATSector_t {
|
||||||
} afatfsFATSector_t;
|
} afatfsFATSector_t;
|
||||||
|
|
||||||
typedef struct afatfsCacheBlockDescriptor_t {
|
typedef struct afatfsCacheBlockDescriptor_t {
|
||||||
|
/*
|
||||||
|
* The physical sector index on disk that this cached block corresponds to
|
||||||
|
*/
|
||||||
uint32_t sectorIndex;
|
uint32_t sectorIndex;
|
||||||
|
|
||||||
|
// We use an increasing timestamp to identify cache access times.
|
||||||
|
|
||||||
|
// This is the timestamp that this sector was first marked dirty at (so we can flush sectors in write-order).
|
||||||
|
uint32_t writeTimestamp;
|
||||||
|
|
||||||
|
// This is the last time the sector was accessed
|
||||||
|
uint32_t accessTimestamp;
|
||||||
|
|
||||||
afatfsCacheBlockState_e state;
|
afatfsCacheBlockState_e state;
|
||||||
uint32_t lastUse;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The state of this block must not transition (do not flush to disk, do not discard). This is useful for a sector
|
* The state of this block must not transition (do not flush to disk, do not discard). This is useful for a sector
|
||||||
* which is currently being written to by the application (so flushing it would be a waste of time).
|
* which is currently being written to by the application (so flushing it would be a waste of time).
|
||||||
*
|
*
|
||||||
* This is a binary state rather than a counter because we assume that only one file will be responsible for the
|
* This is a binary state rather than a counter because we assume that only one party will be responsible for and
|
||||||
* sectors it locks.
|
* so consider locking a given sector.
|
||||||
*/
|
*/
|
||||||
unsigned locked:1;
|
unsigned locked:1;
|
||||||
|
|
||||||
|
@ -356,7 +367,9 @@ typedef struct afatfs_t {
|
||||||
uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
|
uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
|
||||||
afatfsCacheBlockDescriptor_t cacheDescriptor[AFATFS_NUM_CACHE_SECTORS];
|
afatfsCacheBlockDescriptor_t cacheDescriptor[AFATFS_NUM_CACHE_SECTORS];
|
||||||
uint32_t cacheTimer;
|
uint32_t cacheTimer;
|
||||||
int cacheUnflushedEntries; // The number of cache entries in the AFATFS_CACHE_STATE_DIRTY or AFATFS_CACHE_STATE_WRITING states
|
|
||||||
|
int cacheDirtyEntries; // The number of cache entries in the AFATFS_CACHE_STATE_DIRTY state
|
||||||
|
bool cacheFlushInProgress;
|
||||||
|
|
||||||
afatfsFile_t openFiles[AFATFS_MAX_OPEN_FILES];
|
afatfsFile_t openFiles[AFATFS_MAX_OPEN_FILES];
|
||||||
|
|
||||||
|
@ -498,31 +511,21 @@ static int afatfs_getCacheDescriptorIndexForBuffer(uint8_t *memory)
|
||||||
|
|
||||||
static afatfsCacheBlockDescriptor_t* afatfs_getCacheDescriptorForBuffer(uint8_t *memory)
|
static afatfsCacheBlockDescriptor_t* afatfs_getCacheDescriptorForBuffer(uint8_t *memory)
|
||||||
{
|
{
|
||||||
int index = afatfs_getCacheDescriptorIndexForBuffer(memory);
|
return afatfs.cacheDescriptor + afatfs_getCacheDescriptorIndexForBuffer(memory);
|
||||||
|
|
||||||
if (index > -1) {
|
|
||||||
return afatfs.cacheDescriptor + index;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void afatfs_cacheSectorMarkDirty(afatfsCacheBlockDescriptor_t *descriptor)
|
||||||
* Mark the cached sector that the given memory pointer lies inside as dirty.
|
|
||||||
*/
|
|
||||||
static void afatfs_cacheSectorMarkDirty(uint8_t *memory)
|
|
||||||
{
|
{
|
||||||
afatfsCacheBlockDescriptor_t *descriptor = afatfs_getCacheDescriptorForBuffer(memory);
|
if (descriptor->state != AFATFS_CACHE_STATE_DIRTY) {
|
||||||
|
descriptor->writeTimestamp = ++afatfs.cacheTimer;
|
||||||
if (descriptor && descriptor->state != AFATFS_CACHE_STATE_DIRTY) {
|
|
||||||
descriptor->state = AFATFS_CACHE_STATE_DIRTY;
|
descriptor->state = AFATFS_CACHE_STATE_DIRTY;
|
||||||
afatfs.cacheUnflushedEntries++;
|
afatfs.cacheDirtyEntries++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void afatfs_cacheSectorInit(afatfsCacheBlockDescriptor_t *descriptor, uint32_t sectorIndex, bool locked)
|
static void afatfs_cacheSectorInit(afatfsCacheBlockDescriptor_t *descriptor, uint32_t sectorIndex, bool locked)
|
||||||
{
|
{
|
||||||
descriptor->lastUse = ++afatfs.cacheTimer;
|
descriptor->accessTimestamp = descriptor->writeTimestamp = ++afatfs.cacheTimer;
|
||||||
descriptor->sectorIndex = sectorIndex;
|
descriptor->sectorIndex = sectorIndex;
|
||||||
descriptor->locked = locked;
|
descriptor->locked = locked;
|
||||||
descriptor->discardable = 0;
|
descriptor->discardable = 0;
|
||||||
|
@ -563,6 +566,8 @@ static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation, uint32_
|
||||||
(void) operation;
|
(void) operation;
|
||||||
(void) callbackData;
|
(void) callbackData;
|
||||||
|
|
||||||
|
afatfs.cacheFlushInProgress = false;
|
||||||
|
|
||||||
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
|
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
|
||||||
/* Keep in mind that someone may have marked the sector as dirty after writing had already begun. In this case we must leave
|
/* Keep in mind that someone may have marked the sector as dirty after writing had already begun. In this case we must leave
|
||||||
* it marked as dirty because those modifications may have been made too late to make it to the disk!
|
* it marked as dirty because those modifications may have been made too late to make it to the disk!
|
||||||
|
@ -573,10 +578,10 @@ static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation, uint32_
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
// Write failed, remark the sector as dirty
|
// Write failed, remark the sector as dirty
|
||||||
afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_DIRTY;
|
afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_DIRTY;
|
||||||
|
afatfs.cacheDirtyEntries++;
|
||||||
} else {
|
} else {
|
||||||
afatfs_assert(afatfs_cacheSectorGetMemory(i) == buffer);
|
afatfs_assert(afatfs_cacheSectorGetMemory(i) == buffer);
|
||||||
|
|
||||||
afatfs.cacheUnflushedEntries--;
|
|
||||||
afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_IN_SYNC;
|
afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_IN_SYNC;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -585,30 +590,28 @@ static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation, uint32_
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to flush the dirty cache entry with the given index to the SDcard. Returns true if the SDcard accepted
|
* Attempt to flush the dirty cache entry with the given index to the SDcard.
|
||||||
* the write, false if the card was busy.
|
|
||||||
*/
|
*/
|
||||||
static bool afatfs_cacheFlushSector(int cacheIndex)
|
static void afatfs_cacheFlushSector(int cacheIndex)
|
||||||
{
|
{
|
||||||
if (afatfs.cacheDescriptor[cacheIndex].state != AFATFS_CACHE_STATE_DIRTY)
|
|
||||||
return true; // Already flushed
|
|
||||||
|
|
||||||
switch (sdcard_writeBlock(afatfs.cacheDescriptor[cacheIndex].sectorIndex, afatfs_cacheSectorGetMemory(cacheIndex), afatfs_sdcardWriteComplete, 0)) {
|
switch (sdcard_writeBlock(afatfs.cacheDescriptor[cacheIndex].sectorIndex, afatfs_cacheSectorGetMemory(cacheIndex), afatfs_sdcardWriteComplete, 0)) {
|
||||||
case SDCARD_OPERATION_IN_PROGRESS:
|
case SDCARD_OPERATION_IN_PROGRESS:
|
||||||
// The card will call us back later when the buffer transmission finishes
|
// The card will call us back later when the buffer transmission finishes
|
||||||
|
afatfs.cacheDirtyEntries--;
|
||||||
afatfs.cacheDescriptor[cacheIndex].state = AFATFS_CACHE_STATE_WRITING;
|
afatfs.cacheDescriptor[cacheIndex].state = AFATFS_CACHE_STATE_WRITING;
|
||||||
return true;
|
afatfs.cacheFlushInProgress = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case SDCARD_OPERATION_SUCCESS:
|
case SDCARD_OPERATION_SUCCESS:
|
||||||
// Buffer is already transmitted
|
// Buffer is already transmitted
|
||||||
afatfs.cacheUnflushedEntries--;
|
afatfs.cacheDirtyEntries--;
|
||||||
afatfs.cacheDescriptor[cacheIndex].state = AFATFS_CACHE_STATE_IN_SYNC;
|
afatfs.cacheDescriptor[cacheIndex].state = AFATFS_CACHE_STATE_IN_SYNC;
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
case SDCARD_OPERATION_BUSY:
|
case SDCARD_OPERATION_BUSY:
|
||||||
case SDCARD_OPERATION_FAILURE:
|
case SDCARD_OPERATION_FAILURE:
|
||||||
default:
|
default:
|
||||||
return false;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +670,7 @@ static int afatfs_allocateCacheSector(uint32_t sectorIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bump the last access time
|
// Bump the last access time
|
||||||
afatfs.cacheDescriptor[i].lastUse = ++afatfs.cacheTimer;
|
afatfs.cacheDescriptor[i].accessTimestamp = ++afatfs.cacheTimer;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,9 +682,9 @@ static int afatfs_allocateCacheSector(uint32_t sectorIndex)
|
||||||
if (!afatfs.cacheDescriptor[i].locked && afatfs.cacheDescriptor[i].retainCount == 0) {
|
if (!afatfs.cacheDescriptor[i].locked && afatfs.cacheDescriptor[i].retainCount == 0) {
|
||||||
if (afatfs.cacheDescriptor[i].discardable) {
|
if (afatfs.cacheDescriptor[i].discardable) {
|
||||||
discardableIndex = i;
|
discardableIndex = i;
|
||||||
} else if (afatfs.cacheDescriptor[i].lastUse < oldestSyncedSectorLastUse) {
|
} else if (afatfs.cacheDescriptor[i].accessTimestamp < oldestSyncedSectorLastUse) {
|
||||||
// This block could be evicted from the cache to make room for us since it's idle and not dirty
|
// This block could be evicted from the cache to make room for us since it's idle and not dirty
|
||||||
oldestSyncedSectorLastUse = afatfs.cacheDescriptor[i].lastUse;
|
oldestSyncedSectorLastUse = afatfs.cacheDescriptor[i].accessTimestamp;
|
||||||
oldestSyncedSectorIndex = i;
|
oldestSyncedSectorIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,15 +716,25 @@ static int afatfs_allocateCacheSector(uint32_t sectorIndex)
|
||||||
*/
|
*/
|
||||||
bool afatfs_flush()
|
bool afatfs_flush()
|
||||||
{
|
{
|
||||||
if (afatfs.cacheUnflushedEntries > 0) {
|
if (afatfs.cacheDirtyEntries > 0) {
|
||||||
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
|
uint32_t earliestSectorTime = 0xFFFFFFFF;
|
||||||
if (afatfs.cacheDescriptor[i].state == AFATFS_CACHE_STATE_DIRTY && !afatfs.cacheDescriptor[i].locked) {
|
int earliestSectorIndex = -1;
|
||||||
afatfs_cacheFlushSector(i);
|
|
||||||
|
|
||||||
// That flush will take time to complete so we may as well tell caller to come back later
|
for (int i = 0; i < AFATFS_NUM_CACHE_SECTORS; i++) {
|
||||||
return false;
|
if (afatfs.cacheDescriptor[i].state == AFATFS_CACHE_STATE_DIRTY && !afatfs.cacheDescriptor[i].locked
|
||||||
|
&& (earliestSectorIndex == -1 || afatfs.cacheDescriptor[i].writeTimestamp < earliestSectorTime)
|
||||||
|
) {
|
||||||
|
earliestSectorIndex = i;
|
||||||
|
earliestSectorTime = afatfs.cacheDescriptor[i].writeTimestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (earliestSectorIndex > -1) {
|
||||||
|
afatfs_cacheFlushSector(earliestSectorIndex);
|
||||||
|
|
||||||
|
// That flush will take time to complete so we may as well tell caller to come back later
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -813,8 +826,7 @@ static void afatfs_fileGetCursorClusterAndSector(afatfsFilePtr_t file, uint32_t
|
||||||
case AFATFS_CACHE_STATE_WRITING:
|
case AFATFS_CACHE_STATE_WRITING:
|
||||||
case AFATFS_CACHE_STATE_IN_SYNC:
|
case AFATFS_CACHE_STATE_IN_SYNC:
|
||||||
if ((sectorFlags & AFATFS_CACHE_WRITE) != 0) {
|
if ((sectorFlags & AFATFS_CACHE_WRITE) != 0) {
|
||||||
afatfs.cacheDescriptor[cacheSectorIndex].state = AFATFS_CACHE_STATE_DIRTY;
|
afatfs_cacheSectorMarkDirty(&afatfs.cacheDescriptor[cacheSectorIndex]);
|
||||||
afatfs.cacheUnflushedEntries++;
|
|
||||||
}
|
}
|
||||||
// Fall through
|
// Fall through
|
||||||
|
|
||||||
|
@ -1217,8 +1229,17 @@ static afatfsOperationStatus_e afatfs_FATFillWithPattern(afatfsFATPattern_e patt
|
||||||
|
|
||||||
result = afatfs_cacheSector(fatPhysicalSector, §or.bytes, cacheFlags);
|
result = afatfs_cacheSector(fatPhysicalSector, §or.bytes, cacheFlags);
|
||||||
|
|
||||||
if (result != AFATFS_OPERATION_SUCCESS)
|
if (result != AFATFS_OPERATION_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef AFATFS_DEBUG_VERBOSE
|
||||||
|
if (pattern == AFATFS_FAT_PATTERN_FREE) {
|
||||||
|
fprintf(stderr, "Marking cluster %u to %u as free in FAT sector %u...\n", *startCluster, endCluster, fatPhysicalSector);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Writing FAT chain from cluster %u to %u in FAT sector %u...\n", *startCluster, endCluster, fatPhysicalSector);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (pattern) {
|
switch (pattern) {
|
||||||
case AFATFS_FAT_PATTERN_TERMINATED_CHAIN:
|
case AFATFS_FAT_PATTERN_TERMINATED_CHAIN:
|
||||||
|
@ -1283,6 +1304,10 @@ static afatfsOperationStatus_e afatfs_saveDirectoryEntry(afatfsFilePtr_t file)
|
||||||
|
|
||||||
result = afatfs_cacheSector(file->directoryEntryPos.sectorNumberPhysical, §or, AFATFS_CACHE_READ | AFATFS_CACHE_WRITE);
|
result = afatfs_cacheSector(file->directoryEntryPos.sectorNumberPhysical, §or, AFATFS_CACHE_READ | AFATFS_CACHE_WRITE);
|
||||||
|
|
||||||
|
#ifdef AFATFS_DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "Saving directory entry for %.*s to sector %u...\n", FAT_FILENAME_LENGTH, file->directoryEntry.filename, file->directoryEntryPos.sectorNumberPhysical);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (result == AFATFS_OPERATION_SUCCESS) {
|
if (result == AFATFS_OPERATION_SUCCESS) {
|
||||||
// (sub)directories don't store a filesize in their directory entry:
|
// (sub)directories don't store a filesize in their directory entry:
|
||||||
if (file->type == AFATFS_FILE_TYPE_DIRECTORY) {
|
if (file->type == AFATFS_FILE_TYPE_DIRECTORY) {
|
||||||
|
@ -2149,7 +2174,7 @@ static afatfsOperationStatus_e afatfs_allocateDirectoryEntry(afatfsFilePtr_t dir
|
||||||
while ((result = afatfs_findNext(directory, finder, dirEntry)) == AFATFS_OPERATION_SUCCESS) {
|
while ((result = afatfs_findNext(directory, finder, dirEntry)) == AFATFS_OPERATION_SUCCESS) {
|
||||||
if (*dirEntry) {
|
if (*dirEntry) {
|
||||||
if (fat_isDirectoryEntryEmpty(*dirEntry) || fat_isDirectoryEntryTerminator(*dirEntry)) {
|
if (fat_isDirectoryEntryEmpty(*dirEntry) || fat_isDirectoryEntryTerminator(*dirEntry)) {
|
||||||
afatfs_cacheSectorMarkDirty((uint8_t*) *dirEntry);
|
afatfs_cacheSectorMarkDirty(afatfs_getCacheDescriptorForBuffer((uint8_t*) *dirEntry));
|
||||||
|
|
||||||
afatfs_findLast(directory);
|
afatfs_findLast(directory);
|
||||||
return AFATFS_OPERATION_SUCCESS;
|
return AFATFS_OPERATION_SUCCESS;
|
||||||
|
@ -2384,6 +2409,10 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
|
||||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||||
memcpy(entry, &file->directoryEntry, sizeof(file->directoryEntry));
|
memcpy(entry, &file->directoryEntry, sizeof(file->directoryEntry));
|
||||||
|
|
||||||
|
#ifdef AFATFS_DEBUG_VERBOSE
|
||||||
|
fprintf(stderr, "Adding directory entry for %.*s to sector %u\n", FAT_FILENAME_LENGTH, entry->filename, file->directoryEntryPos.sectorNumberPhysical);
|
||||||
|
#endif
|
||||||
|
|
||||||
opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
|
opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
|
||||||
goto doMore;
|
goto doMore;
|
||||||
} else if (status == AFATFS_OPERATION_FAILURE) {
|
} else if (status == AFATFS_OPERATION_FAILURE) {
|
||||||
|
@ -2585,11 +2614,12 @@ static void afatfs_fcloseContinue(afatfsFilePtr_t file)
|
||||||
/*
|
/*
|
||||||
* Directories don't update their parent directory entries over time, because their fileSize field in the directory
|
* Directories don't update their parent directory entries over time, because their fileSize field in the directory
|
||||||
* never changes (when we add the first cluster to the directory we save the directory entry at that point and it
|
* never changes (when we add the first cluster to the directory we save the directory entry at that point and it
|
||||||
* doesn't change afterwards).
|
* doesn't change afterwards). So don't bother trying to save their directory entries during fclose().
|
||||||
*
|
*
|
||||||
* So don't bother trying to save their directory entries during fclose().
|
* Also if we only opened the file for read then we didn't change the directory entry either.
|
||||||
*/
|
*/
|
||||||
if (file->type != AFATFS_FILE_TYPE_DIRECTORY && file->type != AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY) {
|
if (file->type != AFATFS_FILE_TYPE_DIRECTORY && file->type != AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
|
||||||
|
&& (file->mode & (AFATFS_FILE_MODE_APPEND | AFATFS_FILE_MODE_WRITE | AFATFS_FILE_MODE_CREATE)) != 0) {
|
||||||
if (afatfs_saveDirectoryEntry(file) != AFATFS_OPERATION_SUCCESS) {
|
if (afatfs_saveDirectoryEntry(file) != AFATFS_OPERATION_SUCCESS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3267,8 +3297,13 @@ bool afatfs_destroy()
|
||||||
openFileCount++;
|
openFileCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
afatfs_poll();
|
||||||
|
|
||||||
if (!afatfs_flush()) {
|
if (!afatfs_flush()) {
|
||||||
afatfs_poll();
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afatfs.cacheFlushInProgress) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue