Skip to content

Commit 4a6b99b

Browse files
committed
feat(nvs): Optimize read-only NVS loading
1 parent 851e869 commit 4a6b99b

File tree

3 files changed

+59
-61
lines changed

3 files changed

+59
-61
lines changed

components/nvs_flash/host_test/nvs_page_test/main/test_fixtures.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class PartitionEmulationFixture {
110110
size_t columns = size / column_size;
111111
size_t column;
112112

113-
for(column = 0; column < columns; ++column)
113+
for(column = 0; column < columns; column = column + 1)
114114
{
115115
// read column
116116
if((err = esp_partition_read_raw(&esp_partition, dst_offset + (column * column_size), buff, column_size)) != ESP_OK) return err;

components/nvs_flash/src/nvs_pagemanager.cpp

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -56,77 +56,79 @@ esp_err_t PageManager::load(Partition *partition, uint32_t baseSector, uint32_t
5656

5757
// if power went out after a new item for the given key was written,
5858
// but before the old one was erased, we end up with a duplicate item
59-
Page& lastPage = back();
60-
size_t lastItemIndex = SIZE_MAX;
61-
Item item;
62-
size_t itemIndex = 0;
63-
while (lastPage.findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
64-
itemIndex += item.span;
65-
lastItemIndex = itemIndex;
66-
}
67-
68-
if (lastItemIndex != SIZE_MAX) {
69-
auto last = PageManager::TPageListIterator(&lastPage);
70-
TPageListIterator it;
59+
if (!partition->get_readonly()) {
60+
Page& lastPage = back();
61+
size_t lastItemIndex = SIZE_MAX;
62+
Item item;
63+
size_t itemIndex = 0;
64+
while (lastPage.findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
65+
itemIndex += item.span;
66+
lastItemIndex = itemIndex;
67+
}
7168

72-
for (it = begin(); it != last; ++it) {
69+
if (lastItemIndex != SIZE_MAX) {
70+
auto last = PageManager::TPageListIterator(&lastPage);
71+
TPageListIterator it;
7372

74-
if ((it->state() != Page::PageState::FREEING) &&
75-
(it->eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex) == ESP_OK)) {
76-
break;
77-
}
78-
}
79-
if ((it == last) && (item.datatype == ItemType::BLOB_IDX)) {
80-
/* Rare case in which the blob was stored using old format, but power went just after writing
81-
* blob index during modification. Loop again and delete the old version blob*/
8273
for (it = begin(); it != last; ++it) {
8374

8475
if ((it->state() != Page::PageState::FREEING) &&
85-
(it->eraseItem(item.nsIndex, ItemType::BLOB, item.key, item.chunkIndex) == ESP_OK)) {
76+
(it->eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex) == ESP_OK)) {
8677
break;
8778
}
8879
}
80+
if ((it == last) && (item.datatype == ItemType::BLOB_IDX)) {
81+
/* Rare case in which the blob was stored using old format, but power went just after writing
82+
* blob index during modification. Loop again and delete the old version blob*/
83+
for (it = begin(); it != last; ++it) {
84+
85+
if ((it->state() != Page::PageState::FREEING) &&
86+
(it->eraseItem(item.nsIndex, ItemType::BLOB, item.key, item.chunkIndex) == ESP_OK)) {
87+
break;
88+
}
89+
}
90+
}
8991
}
90-
}
9192

92-
// check if power went out while page was being freed
93-
for (auto it = begin(); it!= end(); ++it) {
94-
if (it->state() == Page::PageState::FREEING) {
95-
Page* newPage = &mPageList.back();
96-
if (newPage->state() == Page::PageState::ACTIVE) {
97-
auto err = newPage->erase();
93+
// check if power went out while page was being freed
94+
for (auto it = begin(); it!= end(); ++it) {
95+
if (it->state() == Page::PageState::FREEING) {
96+
Page* newPage = &mPageList.back();
97+
if (newPage->state() == Page::PageState::ACTIVE) {
98+
auto err = newPage->erase();
99+
if (err != ESP_OK) {
100+
return err;
101+
}
102+
mPageList.erase(newPage);
103+
mFreePageList.push_back(newPage);
104+
}
105+
auto err = activatePage();
98106
if (err != ESP_OK) {
99107
return err;
100108
}
101-
mPageList.erase(newPage);
102-
mFreePageList.push_back(newPage);
103-
}
104-
auto err = activatePage();
105-
if (err != ESP_OK) {
106-
return err;
107-
}
108-
newPage = &mPageList.back();
109+
newPage = &mPageList.back();
109110

110-
err = it->copyItems(*newPage);
111-
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
112-
return err;
113-
}
111+
err = it->copyItems(*newPage);
112+
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
113+
return err;
114+
}
114115

115-
err = it->erase();
116-
if (err != ESP_OK) {
117-
return err;
118-
}
116+
err = it->erase();
117+
if (err != ESP_OK) {
118+
return err;
119+
}
119120

120-
Page* p = static_cast<Page*>(it);
121-
mPageList.erase(it);
122-
mFreePageList.push_back(p);
123-
break;
121+
Page* p = static_cast<Page*>(it);
122+
mPageList.erase(it);
123+
mFreePageList.push_back(p);
124+
break;
125+
}
124126
}
125-
}
126127

127-
// partition should have at least one free page if it is not read-only
128-
if (!partition->get_readonly() && mFreePageList.empty()) {
129-
return ESP_ERR_NVS_NO_FREE_PAGES;
128+
// partition should have at least one free page if it is not read-only
129+
if (mFreePageList.empty()) {
130+
return ESP_ERR_NVS_NO_FREE_PAGES;
131+
}
130132
}
131133

132134
return ESP_OK;

examples/storage/parttool/pytest_parttool_example.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,12 @@ def test_examples_parttool(dut: Dut) -> None:
3636
cmds = [
3737
'read_partition --partition-type=data --partition-subtype=nvs --output custom1.bin',
3838
'erase_partition --partition-name=custom',
39-
'write_partition --partition-name=custom --input custom.bin',
39+
'write_partition --partition-name=custom --input custom.bin --ignore-readonly',
4040
'get_partition_info --partition-boot-default --info size',
4141
]
4242

4343
for cmd in cmds:
44-
try:
45-
subprocess.check_call(BASE_CMD + cmd.split())
46-
except subprocess.CalledProcessError as e:
47-
print(e.output)
48-
raise
44+
subprocess.check_call(BASE_CMD + cmd.split())
4945

5046
clean_files = ['custom.bin', 'custom1.bin']
5147
for clean_file in clean_files:

0 commit comments

Comments
 (0)