Skip to content

Commit

Permalink
FrFTL: Performance improvement, eliminate 10% unnecessary flash progr…
Browse files Browse the repository at this point in the history
…amming.
  • Loading branch information
richardclli committed Oct 13, 2023
1 parent 47f21a7 commit 020396f
Showing 1 changed file with 105 additions and 59 deletions.
164 changes: 105 additions & 59 deletions radio/src/drivers/frftl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ struct PageBuffer {
uint16_t logicalPageNo; // Required for first program or reprogram
uint16_t physicalPageNo;
uint8_t lock; // Page locked for delayed update
uint8_t sectorEraseRequired; // Record which sector need to be erased before update
uint8_t sectorProgramRequired; // Record which sector need to be programmed
uint8_t pMode;
};

Expand Down Expand Up @@ -292,6 +292,30 @@ static PageBuffer* rawFindReplacableBuffer(FrFTL* ftl)
return buffer;
}

static void movePageBufferToLRUTail(FrFTL* ftl, PageBuffer* buffer)
{
// Take buffer out from the linked list
if (buffer->lruNext) {
buffer->lruNext->lruPrev = buffer->lruPrev;
} else {
// Already is tail, done
return;
}

if (buffer->lruPrev) {
buffer->lruPrev->lruNext = buffer->lruNext;
} else {
// Head detected, need to update head pointer
ftl->bufferHead = buffer->lruNext;
}

// Put buffer to buffer tail
((PageBuffer*) ftl->bufferTail)->lruNext = buffer;
buffer->lruPrev = (PageBuffer*) ftl->bufferTail;
buffer->lruNext = nullptr;
ftl->bufferTail = buffer;
}

static PageBuffer* findReplacableBuffer(FrFTL* ftl)
{
PageBuffer* buffer = rawFindReplacableBuffer(ftl);
Expand Down Expand Up @@ -350,7 +374,7 @@ static PageBuffer* loadPhysicalPageInBuffer(FrFTL* ftl, uint16_t logicalPageNo,
currentBuffer->logicalPageNo = logicalPageNo;
currentBuffer->physicalPageNo = physicalPageNo;
currentBuffer->lock = UNLOCKED;
currentBuffer->sectorEraseRequired = 0;
currentBuffer->sectorProgramRequired = 0;
currentBuffer->pMode = NONE;

// Add new page to hash table
Expand All @@ -373,7 +397,7 @@ static PageBuffer* initPhysicalPageInBuffer(FrFTL* ftl, uint16_t logicalPageNo,
currentBuffer->logicalPageNo = logicalPageNo;
currentBuffer->physicalPageNo = physicalPageNo;
currentBuffer->lock = LOCKED;
currentBuffer->sectorEraseRequired = 0;
currentBuffer->sectorProgramRequired = 0;
currentBuffer->pMode = ERASE_PROGRAM;
memset(currentBuffer->page.data, 0xff, PAGE_SIZE);

Expand Down Expand Up @@ -517,17 +541,49 @@ static uint16_t allocatePhysicalPage(FrFTL* ftl)
return physicalPageNo;
}

static bool programPage(FrFTL* ftl, PageBuffer* buffer, bool doErase)
{
const FrFTLOps* cb = ftl->callbacks;
uint32_t pageAddr = buffer->physicalPageNo * PAGE_SIZE;

if (doErase && getPhysicalPageState(ftl, buffer->physicalPageNo) != ERASED) {
// Do erase on the fly
if (!cb->flashErase(pageAddr)) {
return false;
}
}

if (buffer->logicalPageNo < ftl->ttPageCount) {
buffer->sectorProgramRequired = 0xff;
}

// Sector by sector programming:
// As flash requires 256 bytes per program command, it will be more efficient to program by sector
uint8_t* dataPtr = buffer->page.data;
for (uint8_t i = 0; i < SECTORS_PER_PAGE; i++)
{
uint8_t sectMask = 1 << i;
if ((buffer->sectorProgramRequired & sectMask) != 0) {
if (!cb->flashProgram(pageAddr, dataPtr, SECTOR_SIZE))
{
return false;
}
}
pageAddr += SECTOR_SIZE;
dataPtr += SECTOR_SIZE;
}

return true;
}

static bool programPageInBuffer(FrFTL* ftl, PageBuffer* buffer)
{
uint32_t addr;
uint16_t newPhysicalPageNo;
uint16_t oldPhysicalPageNo;

const FrFTLOps* cb = ftl->callbacks;
switch (buffer->pMode) {
case PROGRAM:
// Program only
if (!cb->flashProgram(buffer->physicalPageNo * PAGE_SIZE,
buffer->page.data, PAGE_SIZE)) {
if (!programPage(ftl, buffer, false)) {
return false;
}
setPhysicalPageState(ftl, buffer->physicalPageNo, USED);
Expand All @@ -536,14 +592,7 @@ static bool programPageInBuffer(FrFTL* ftl, PageBuffer* buffer)
}
break;
case ERASE_PROGRAM:
addr = buffer->physicalPageNo * PAGE_SIZE;
if (getPhysicalPageState(ftl, buffer->physicalPageNo) != ERASED) {
// Do erase on the fly
if (!cb->flashErase(addr)) {
return false;
}
}
if (!cb->flashProgram(addr, buffer->page.data, PAGE_SIZE)) {
if (!programPage(ftl, buffer, true)) {
return false;
}
setPhysicalPageState(ftl, buffer->physicalPageNo, USED);
Expand All @@ -553,44 +602,29 @@ static bool programPageInBuffer(FrFTL* ftl, PageBuffer* buffer)
break;
case RELOCATE_ERASE_PROGRAM:
// Reprogram
newPhysicalPageNo = allocatePhysicalPage(ftl);
if (newPhysicalPageNo == 0xffff) {
oldPhysicalPageNo = buffer->physicalPageNo;
setPhysicalPageState(ftl, oldPhysicalPageNo, ERASE_REQUIRED);
removePageFromHashTable(ftl, oldPhysicalPageNo);
buffer->physicalPageNo = allocatePhysicalPage(ftl);
if (buffer->physicalPageNo == 0xffff) {
return false;
}

if (buffer->logicalPageNo < ftl->ttPageCount) {
if (buffer->logicalPageNo == 0) {
// MTT need update physicalPageNo
buffer->page.tt.physicalPageNo[0] = newPhysicalPageNo;
buffer->page.tt.physicalPageNo[0] = buffer->physicalPageNo;
}

// TT page, need update serial and CRC
buffer->page.tt.header.serial++;
buffer->page.tt.header.crc16 = calcCRC(&buffer->page.tt.header);
} else {
// Data page, need to check if any sectors are mark trimmed and fill it
// with 0xff
for (uint8_t i = 0; i < SECTORS_PER_PAGE; i++) {
uint8_t sectMask = 1 << i;
if ((buffer->sectorEraseRequired & sectMask) != 0) {
memset(buffer->page.data + i * SECTOR_SIZE, 0xff, SECTOR_SIZE);
}
}
}

addr = newPhysicalPageNo * PAGE_SIZE;
if (getPhysicalPageState(ftl, buffer->physicalPageNo) != ERASED) {
// Do erase on the fly
if (!cb->flashErase(addr)) {
return false;
}
}
if (!cb->flashProgram(addr, buffer->page.data, PAGE_SIZE)) {
if (!programPage(ftl, buffer, true)) {
return false;
}
setPhysicalPageState(ftl, buffer->physicalPageNo, ERASE_REQUIRED);
removePageFromHashTable(ftl, buffer->physicalPageNo);
buffer->physicalPageNo = newPhysicalPageNo;

setPhysicalPageState(ftl, buffer->physicalPageNo, USED);
addPageToHashTable(ftl, buffer);
if (buffer->logicalPageNo == 0) {
Expand Down Expand Up @@ -653,7 +687,7 @@ bool ftlSync(FrFTL* ftl)

// Unlock buffer
currentBuffer->lock = UNLOCKED;
currentBuffer->sectorEraseRequired = 0;
currentBuffer->sectorProgramRequired = 0;
currentBuffer->pMode = NONE;
}
}
Expand Down Expand Up @@ -757,7 +791,6 @@ bool ftlWrite(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors,
if ((pageInfo.sectStatus & sectMask) != 0) {
// Sector never write, append information
pageInfo.sectStatus &= ~sectMask;
dataBuffer->sectorEraseRequired &= ~sectMask;
if (!updatePageInfo(ftl, &pageInfo, logicalPageNo)) {
return false;
}
Expand All @@ -769,13 +802,15 @@ bool ftlWrite(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors,
}
memcpy(dataBuffer->page.data + pageSectorNo * SECTOR_SIZE, buf,
SECTOR_SIZE);
dataBuffer->sectorProgramRequired |= sectMask;
} else {
// Sector already written, use replace write
// Lock data page for delayed update with reprogram
dataBuffer->lock = LOCKED;
dataBuffer->pMode = RELOCATE_ERASE_PROGRAM;
memcpy(dataBuffer->page.data + pageSectorNo * SECTOR_SIZE, buf,
SECTOR_SIZE);
dataBuffer->sectorProgramRequired = ~pageInfo.sectStatus;

if (!lockTTPages(ftl, logicalPageNo)) {
return false;
Expand Down Expand Up @@ -860,11 +895,15 @@ bool ftlTrim(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors)
pageInfo.physicalPageNo = 0xffff; // Invalidate page info
dataBuffer->physicalPageNo = 0xffff; // Invalidate buffer
dataBuffer->lock = UNLOCKED;

// Move freed page buffer to LRU tail
movePageBufferToLRUTail(ftl, dataBuffer);

} else {
// Locked for delayed relocate, fill and program
dataBuffer->lock = LOCKED;
dataBuffer->pMode = RELOCATE_ERASE_PROGRAM;
dataBuffer->sectorEraseRequired |= sectMask;
dataBuffer->sectorProgramRequired = ~pageInfo.sectStatus;
}

// Update page info
Expand Down Expand Up @@ -1014,29 +1053,35 @@ static bool loadFTL(FrFTL* ftl)

PageBuffer* mtt =
loadPhysicalPageInBuffer(ftl, 0, currentPhysicalMTTPageNo);
if (!mtt) {

// Check if MTT valid
if (!mtt || mtt->page.tt.physicalPageNo[0] != currentPhysicalMTTPageNo) {
return false;
}

for (uint16_t i = 0; i < ftl->ttPageCount; i++)
{
uint16_t ttPhysicalPageNo = mtt->page.tt.physicalPageNo[i];
PageBuffer* tt = mtt;

// Check wrong range of physical page no.
if (ttPhysicalPageNo >= ftl->physicalPageCount) {
return false;
}
for (uint16_t i = 0; i < ftl->ttPageCount; i++) {
if (i > 0) {
// Not MTT, read TT page and check integrity
uint16_t ttPhysicalPageNo = mtt->page.tt.physicalPageNo[i];

// Load TT page
PageBuffer* tt = loadPhysicalPageInBuffer(ftl, i, ttPhysicalPageNo);
if (!tt) {
return false;
}
// Check wrong range of physical page no.
if (ttPhysicalPageNo >= ftl->physicalPageCount) {
return false;
}

// Check if TT valid
if (tt->page.tt.header.magicStart != TT_PAGE_MAGIC ||
tt->page.tt.header.crc16 != calcCRC(&tt->page.tt.header)) {
return false;
// Load TT page
tt = loadPhysicalPageInBuffer(ftl, i, ttPhysicalPageNo);
if (!tt) {
return false;
}

// Check if TT valid
if (tt->page.tt.header.magicStart != TT_PAGE_MAGIC ||
tt->page.tt.header.crc16 != calcCRC(&tt->page.tt.header)) {
return false;
}
}

// Mark used page
Expand Down Expand Up @@ -1106,4 +1151,5 @@ void ftlDeInit(FrFTL* ftl)
{
free(ftl->pageBuffer);
free(ftl->physicalPageState);
free(ftl->hashTable);
}

0 comments on commit 020396f

Please sign in to comment.