nvs_pagemanager.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include "nvs_pagemanager.hpp"
  14. namespace nvs
  15. {
  16. esp_err_t PageManager::load(uint32_t baseSector, uint32_t sectorCount)
  17. {
  18. mBaseSector = baseSector;
  19. mPageCount = sectorCount;
  20. mPageList.clear();
  21. mFreePageList.clear();
  22. mPages.reset(new (nothrow) Page[sectorCount]);
  23. if (!mPages) return ESP_ERR_NO_MEM;
  24. for (uint32_t i = 0; i < sectorCount; ++i) {
  25. auto err = mPages[i].load(baseSector + i);
  26. if (err != ESP_OK) {
  27. return err;
  28. }
  29. uint32_t seqNumber;
  30. if (mPages[i].getSeqNumber(seqNumber) != ESP_OK) {
  31. mFreePageList.push_back(&mPages[i]);
  32. } else {
  33. auto pos = std::find_if(std::begin(mPageList), std::end(mPageList), [=](const Page& page) -> bool {
  34. uint32_t otherSeqNumber;
  35. return page.getSeqNumber(otherSeqNumber) == ESP_OK && otherSeqNumber > seqNumber;
  36. });
  37. if (pos == mPageList.end()) {
  38. mPageList.push_back(&mPages[i]);
  39. } else {
  40. mPageList.insert(pos, &mPages[i]);
  41. }
  42. }
  43. }
  44. if (mPageList.empty()) {
  45. mSeqNumber = 0;
  46. return activatePage();
  47. } else {
  48. uint32_t lastSeqNo;
  49. ESP_ERROR_CHECK( mPageList.back().getSeqNumber(lastSeqNo) );
  50. mSeqNumber = lastSeqNo + 1;
  51. }
  52. // if power went out after a new item for the given key was written,
  53. // but before the old one was erased, we end up with a duplicate item
  54. Page& lastPage = back();
  55. size_t lastItemIndex = SIZE_MAX;
  56. Item item;
  57. size_t itemIndex = 0;
  58. while (lastPage.findItem(Page::NS_ANY, ItemType::ANY, nullptr, itemIndex, item) == ESP_OK) {
  59. itemIndex += item.span;
  60. lastItemIndex = itemIndex;
  61. }
  62. if (lastItemIndex != SIZE_MAX) {
  63. auto last = PageManager::TPageListIterator(&lastPage);
  64. TPageListIterator it;
  65. for (it = begin(); it != last; ++it) {
  66. if ((it->state() != Page::PageState::FREEING) &&
  67. (it->eraseItem(item.nsIndex, item.datatype, item.key, item.chunkIndex) == ESP_OK)) {
  68. break;
  69. }
  70. }
  71. if ((it == last) && (item.datatype == ItemType::BLOB_IDX)) {
  72. /* Rare case in which the blob was stored using old format, but power went just after writing
  73. * blob index during modification. Loop again and delete the old version blob*/
  74. for (it = begin(); it != last; ++it) {
  75. if ((it->state() != Page::PageState::FREEING) &&
  76. (it->eraseItem(item.nsIndex, ItemType::BLOB, item.key, item.chunkIndex) == ESP_OK)) {
  77. break;
  78. }
  79. }
  80. }
  81. }
  82. // check if power went out while page was being freed
  83. for (auto it = begin(); it!= end(); ++it) {
  84. if (it->state() == Page::PageState::FREEING) {
  85. Page* newPage = &mPageList.back();
  86. if (newPage->state() == Page::PageState::ACTIVE) {
  87. auto err = newPage->erase();
  88. if (err != ESP_OK) {
  89. return err;
  90. }
  91. mPageList.erase(newPage);
  92. mFreePageList.push_back(newPage);
  93. }
  94. auto err = activatePage();
  95. if (err != ESP_OK) {
  96. return err;
  97. }
  98. newPage = &mPageList.back();
  99. err = it->copyItems(*newPage);
  100. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
  101. return err;
  102. }
  103. err = it->erase();
  104. if (err != ESP_OK) {
  105. return err;
  106. }
  107. Page* p = static_cast<Page*>(it);
  108. mPageList.erase(it);
  109. mFreePageList.push_back(p);
  110. break;
  111. }
  112. }
  113. // partition should have at least one free page
  114. if (mFreePageList.size() == 0) {
  115. return ESP_ERR_NVS_NO_FREE_PAGES;
  116. }
  117. return ESP_OK;
  118. }
  119. esp_err_t PageManager::requestNewPage()
  120. {
  121. if (mFreePageList.empty()) {
  122. return ESP_ERR_NVS_INVALID_STATE;
  123. }
  124. // do we have at least two free pages? in that case no erasing is required
  125. if (mFreePageList.size() >= 2) {
  126. return activatePage();
  127. }
  128. // find the page with the higest number of erased items
  129. TPageListIterator maxUnusedItemsPageIt;
  130. size_t maxUnusedItems = 0;
  131. for (auto it = begin(); it != end(); ++it) {
  132. auto unused = Page::ENTRY_COUNT - it->getUsedEntryCount();
  133. if (unused > maxUnusedItems) {
  134. maxUnusedItemsPageIt = it;
  135. maxUnusedItems = unused;
  136. }
  137. }
  138. if (maxUnusedItems == 0) {
  139. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  140. }
  141. esp_err_t err = activatePage();
  142. if (err != ESP_OK) {
  143. return err;
  144. }
  145. Page* newPage = &mPageList.back();
  146. Page* erasedPage = maxUnusedItemsPageIt;
  147. #ifndef NDEBUG
  148. size_t usedEntries = erasedPage->getUsedEntryCount();
  149. #endif
  150. err = erasedPage->markFreeing();
  151. if (err != ESP_OK) {
  152. return err;
  153. }
  154. err = erasedPage->copyItems(*newPage);
  155. if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
  156. return err;
  157. }
  158. err = erasedPage->erase();
  159. if (err != ESP_OK) {
  160. return err;
  161. }
  162. #ifndef NDEBUG
  163. assert(usedEntries == newPage->getUsedEntryCount());
  164. #endif
  165. mPageList.erase(maxUnusedItemsPageIt);
  166. mFreePageList.push_back(erasedPage);
  167. return ESP_OK;
  168. }
  169. esp_err_t PageManager::activatePage()
  170. {
  171. if (mFreePageList.empty()) {
  172. return ESP_ERR_NVS_NOT_ENOUGH_SPACE;
  173. }
  174. Page* p = &mFreePageList.front();
  175. if (p->state() == Page::PageState::CORRUPT) {
  176. auto err = p->erase();
  177. if (err != ESP_OK) {
  178. return err;
  179. }
  180. }
  181. mFreePageList.pop_front();
  182. mPageList.push_back(p);
  183. p->setSeqNumber(mSeqNumber);
  184. ++mSeqNumber;
  185. return ESP_OK;
  186. }
  187. esp_err_t PageManager::fillStats(nvs_stats_t& nvsStats)
  188. {
  189. nvsStats.used_entries = 0;
  190. nvsStats.free_entries = 0;
  191. nvsStats.total_entries = 0;
  192. esp_err_t err = ESP_OK;
  193. // list of used pages
  194. for (auto p = mPageList.begin(); p != mPageList.end(); ++p) {
  195. err = p->calcEntries(nvsStats);
  196. if (err != ESP_OK) {
  197. return err;
  198. }
  199. }
  200. // free pages
  201. nvsStats.total_entries += mFreePageList.size() * Page::ENTRY_COUNT;
  202. nvsStats.free_entries += mFreePageList.size() * Page::ENTRY_COUNT;
  203. return err;
  204. }
  205. } // namespace nvs