mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-08-06 15:15:15 +08:00
fix(nvs_flash): Fix recovery from power-off while page is being freed
Currently when page is being freed, items are individually moved from FREEING page to ACTIVE page and erased. If power-off happens during the process, the remaining entries are moved to ACTIVE page during recovery. The problem with this approach is there may not be enough space on ACTIVE page for all items if an item was partially written before power-off and erased during recovery. This change moves all the items from FREEING to ACTIVE page and then erased the FREEING page, If power-off happens during the process, then ACTIVE page is erased and the process is restarted. esp-idf commit ID: 7ae1df1c
This commit is contained in:
@ -386,7 +386,7 @@ void Page::updateFirstUsedEntry(size_t index, size_t span)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t Page::moveItem(Page& other)
|
esp_err_t Page::copyItems(Page& other)
|
||||||
{
|
{
|
||||||
if (mFirstUsedEntry == INVALID_ENTRY) {
|
if (mFirstUsedEntry == INVALID_ENTRY) {
|
||||||
return ESP_ERR_NVS_NOT_FOUND;
|
return ESP_ERR_NVS_NOT_FOUND;
|
||||||
@ -400,29 +400,41 @@ esp_err_t Page::moveItem(Page& other)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item entry;
|
Item entry;
|
||||||
auto err = readEntry(mFirstUsedEntry, entry);
|
size_t readEntryIndex = mFirstUsedEntry;
|
||||||
|
|
||||||
|
while (readEntryIndex < ENTRY_COUNT) {
|
||||||
|
|
||||||
|
if (mEntryTable.get(readEntryIndex) != EntryState::WRITTEN) {
|
||||||
|
assert(readEntryIndex != mFirstUsedEntry);
|
||||||
|
readEntryIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto err = readEntry(readEntryIndex, entry);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
other.mHashList.insert(entry, other.mNextFreeEntry);
|
other.mHashList.insert(entry, other.mNextFreeEntry);
|
||||||
err = other.writeEntry(entry);
|
err = other.writeEntry(entry);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t span = entry.span;
|
size_t span = entry.span;
|
||||||
size_t end = mFirstUsedEntry + span;
|
size_t end = readEntryIndex + span;
|
||||||
|
|
||||||
assert(mFirstUsedEntry != INVALID_ENTRY || span == 1);
|
assert(end <= ENTRY_COUNT);
|
||||||
|
|
||||||
for (size_t i = mFirstUsedEntry + 1; i < end; ++i) {
|
for (size_t i = readEntryIndex + 1; i < end; ++i) {
|
||||||
readEntry(i, entry);
|
readEntry(i, entry);
|
||||||
err = other.writeEntry(entry);
|
err = other.writeEntry(entry);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return eraseEntryAndSpan(mFirstUsedEntry);
|
readEntryIndex = end;
|
||||||
|
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t Page::mLoadEntryTable()
|
esp_err_t Page::mLoadEntryTable()
|
||||||
|
@ -127,7 +127,7 @@ public:
|
|||||||
|
|
||||||
esp_err_t markFreeing();
|
esp_err_t markFreeing();
|
||||||
|
|
||||||
esp_err_t moveItem(Page& other);
|
esp_err_t copyItems(Page& other);
|
||||||
|
|
||||||
esp_err_t erase();
|
esp_err_t erase();
|
||||||
|
|
||||||
|
@ -67,7 +67,9 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
|
|||||||
if (lastItemIndex != SIZE_MAX) {
|
if (lastItemIndex != SIZE_MAX) {
|
||||||
auto last = PageManager::TPageListIterator(&lastPage);
|
auto last = PageManager::TPageListIterator(&lastPage);
|
||||||
for (auto it = begin(); it != last; ++it) {
|
for (auto it = begin(); it != last; ++it) {
|
||||||
if (it->eraseItem(item.nsIndex, item.datatype, item.key) == ESP_OK) {
|
|
||||||
|
if ((it->state() != Page::PageState::FREEING) &&
|
||||||
|
(it->eraseItem(item.nsIndex, item.datatype, item.key) == ESP_OK)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,23 +79,26 @@ esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
|
|||||||
for (auto it = begin(); it!= end(); ++it) {
|
for (auto it = begin(); it!= end(); ++it) {
|
||||||
if (it->state() == Page::PageState::FREEING) {
|
if (it->state() == Page::PageState::FREEING) {
|
||||||
Page* newPage = &mPageList.back();
|
Page* newPage = &mPageList.back();
|
||||||
if (newPage->state() != Page::PageState::ACTIVE) {
|
if (newPage->state() == Page::PageState::ACTIVE) {
|
||||||
|
auto err = newPage->erase();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
mPageList.erase(newPage);
|
||||||
|
mFreePageList.push_back(newPage);
|
||||||
|
}
|
||||||
auto err = activatePage();
|
auto err = activatePage();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
newPage = &mPageList.back();
|
newPage = &mPageList.back();
|
||||||
}
|
|
||||||
while (true) {
|
err = it->copyItems(*newPage);
|
||||||
auto err = it->moveItem(*newPage);
|
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
|
||||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
|
||||||
break;
|
|
||||||
} else if (err != ESP_OK) {
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto err = it->erase();
|
err = it->erase();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -156,14 +161,10 @@ esp_err_t PageManager::requestNewPage()
|
|||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
while (true) {
|
err = erasedPage->copyItems(*newPage);
|
||||||
err = erasedPage->moveItem(*newPage);
|
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
|
||||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
|
||||||
break;
|
|
||||||
} else if (err != ESP_OK) {
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
err = erasedPage->erase();
|
err = erasedPage->erase();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
|
Reference in New Issue
Block a user