spiffs_gc.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. #include "spiffs.h"
  2. #include "spiffs_nucleus.h"
  3. // Erases a logical block and updates the erase counter.
  4. // If cache is enabled, all pages that might be cached in this block
  5. // is dropped.
  6. static s32_t spiffs_gc_erase_block(
  7. spiffs *fs,
  8. spiffs_block_ix bix) {
  9. s32_t res;
  10. u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix);
  11. s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  12. SPIFFS_GC_DBG("gc: erase block %i\n", bix);
  13. // here we ignore res, just try erasing the block
  14. while (size > 0) {
  15. SPIFFS_GC_DBG("gc: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
  16. (void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
  17. addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
  18. size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
  19. }
  20. fs->free_blocks++;
  21. // register erase count for this block
  22. res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
  23. SPIFFS_ERASE_COUNT_PADDR(fs, bix),
  24. sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count);
  25. SPIFFS_CHECK_RES(res);
  26. fs->max_erase_count++;
  27. if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
  28. fs->max_erase_count = 0;
  29. }
  30. #if SPIFFS_CACHE
  31. {
  32. u32_t i;
  33. for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
  34. spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
  35. }
  36. }
  37. #endif
  38. return res;
  39. }
  40. // Searches for blocks where all entries are deleted - if one is found,
  41. // the block is erased. Compared to the non-quick gc, the quick one ensures
  42. // that no updates are needed on existing objects on pages that are erased.
  43. s32_t spiffs_gc_quick(
  44. spiffs *fs) {
  45. s32_t res = SPIFFS_OK;
  46. u32_t blocks = fs->block_count;
  47. spiffs_block_ix cur_block = 0;
  48. u32_t cur_block_addr = 0;
  49. int cur_entry = 0;
  50. spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  51. SPIFFS_GC_DBG("gc_quick: running\n", cur_block);
  52. #if SPIFFS_GC_STATS
  53. fs->stats_gc_runs++;
  54. #endif
  55. int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  56. // find fully deleted blocks
  57. // check each block
  58. while (res == SPIFFS_OK && blocks--) {
  59. u16_t deleted_pages_in_block = 0;
  60. int obj_lookup_page = 0;
  61. // check each object lookup page
  62. while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  63. int entry_offset = obj_lookup_page * entries_per_page;
  64. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  65. 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  66. // check each entry
  67. while (res == SPIFFS_OK &&
  68. cur_entry - entry_offset < entries_per_page &&
  69. cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
  70. spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
  71. if (obj_id == SPIFFS_OBJ_ID_DELETED) {
  72. deleted_pages_in_block++;
  73. } else if (obj_id == SPIFFS_OBJ_ID_FREE) {
  74. // kill scan, go for next block
  75. obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
  76. res = 1; // kill object lu loop
  77. break;
  78. } else {
  79. // kill scan, go for next block
  80. obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
  81. res = 1; // kill object lu loop
  82. break;
  83. }
  84. cur_entry++;
  85. } // per entry
  86. obj_lookup_page++;
  87. } // per object lookup page
  88. if (res == 1) res = SPIFFS_OK;
  89. if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  90. // found a fully deleted block
  91. fs->stats_p_deleted -= deleted_pages_in_block;
  92. res = spiffs_gc_erase_block(fs, cur_block);
  93. return res;
  94. }
  95. cur_entry = 0;
  96. cur_block++;
  97. cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  98. } // per block
  99. return res;
  100. }
  101. // Checks if garbaga collecting is necessary. If so a candidate block is found,
  102. // cleansed and erased
  103. s32_t spiffs_gc_check(
  104. spiffs *fs,
  105. u32_t len) {
  106. s32_t res;
  107. s32_t free_pages =
  108. (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
  109. - fs->stats_p_allocated - fs->stats_p_deleted;
  110. int tries = 0;
  111. if (fs->free_blocks > 3 &&
  112. (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
  113. return SPIFFS_OK;
  114. }
  115. u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
  116. if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
  117. return SPIFFS_ERR_FULL;
  118. }
  119. //printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len);
  120. do {
  121. SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
  122. tries,
  123. fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
  124. len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
  125. spiffs_block_ix *cands;
  126. int count;
  127. spiffs_block_ix cand;
  128. res = spiffs_gc_find_candidate(fs, &cands, &count);
  129. SPIFFS_CHECK_RES(res);
  130. if (count == 0) {
  131. SPIFFS_GC_DBG("gc_check: no candidates, return\n");
  132. return res;
  133. }
  134. #if SPIFFS_GC_STATS
  135. fs->stats_gc_runs++;
  136. #endif
  137. cand = cands[0];
  138. fs->cleaning = 1;
  139. //printf("gcing: cleaning block %i\n", cand);
  140. res = spiffs_gc_clean(fs, cand);
  141. fs->cleaning = 0;
  142. if (res < 0) {
  143. SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
  144. } else {
  145. SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
  146. }
  147. SPIFFS_CHECK_RES(res);
  148. res = spiffs_gc_erase_page_stats(fs, cand);
  149. SPIFFS_CHECK_RES(res);
  150. res = spiffs_gc_erase_block(fs, cand);
  151. SPIFFS_CHECK_RES(res);
  152. free_pages =
  153. (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
  154. - fs->stats_p_allocated - fs->stats_p_deleted;
  155. } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
  156. (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
  157. free_pages =
  158. (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
  159. - fs->stats_p_allocated - fs->stats_p_deleted;
  160. if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
  161. res = SPIFFS_ERR_FULL;
  162. }
  163. SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
  164. fs->stats_p_allocated + fs->stats_p_deleted,
  165. fs->free_blocks, free_pages, tries, res);
  166. return res;
  167. }
  168. // Updates page statistics for a block that is about to be erased
  169. s32_t spiffs_gc_erase_page_stats(
  170. spiffs *fs,
  171. spiffs_block_ix bix) {
  172. s32_t res = SPIFFS_OK;
  173. int obj_lookup_page = 0;
  174. int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  175. spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  176. int cur_entry = 0;
  177. u32_t dele = 0;
  178. u32_t allo = 0;
  179. // check each object lookup page
  180. while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  181. int entry_offset = obj_lookup_page * entries_per_page;
  182. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  183. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  184. // check each entry
  185. while (res == SPIFFS_OK &&
  186. cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
  187. spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
  188. if (obj_id == SPIFFS_OBJ_ID_FREE) {
  189. } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
  190. dele++;
  191. } else {
  192. allo++;
  193. }
  194. cur_entry++;
  195. } // per entry
  196. obj_lookup_page++;
  197. } // per object lookup page
  198. SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
  199. fs->stats_p_allocated -= allo;
  200. fs->stats_p_deleted -= dele;
  201. return res;
  202. }
  203. // Finds block candidates to erase
  204. s32_t spiffs_gc_find_candidate(
  205. spiffs *fs,
  206. spiffs_block_ix **block_candidates,
  207. int *candidate_count) {
  208. s32_t res = SPIFFS_OK;
  209. u32_t blocks = fs->block_count;
  210. spiffs_block_ix cur_block = 0;
  211. u32_t cur_block_addr = 0;
  212. spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  213. int cur_entry = 0;
  214. // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
  215. int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
  216. *candidate_count = 0;
  217. c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
  218. // divide up work area into block indices and scores
  219. // todo alignment?
  220. spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
  221. s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
  222. *block_candidates = cand_blocks;
  223. int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  224. // check each block
  225. while (res == SPIFFS_OK && blocks--) {
  226. u16_t deleted_pages_in_block = 0;
  227. u16_t used_pages_in_block = 0;
  228. int obj_lookup_page = 0;
  229. // check each object lookup page
  230. while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  231. int entry_offset = obj_lookup_page * entries_per_page;
  232. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  233. 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  234. // check each entry
  235. while (res == SPIFFS_OK &&
  236. cur_entry - entry_offset < entries_per_page &&
  237. cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
  238. spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
  239. if (obj_id == SPIFFS_OBJ_ID_FREE) {
  240. // when a free entry is encountered, scan logic ensures that all following entries are free also
  241. res = 1; // kill object lu loop
  242. break;
  243. } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
  244. deleted_pages_in_block++;
  245. } else {
  246. used_pages_in_block++;
  247. }
  248. cur_entry++;
  249. } // per entry
  250. obj_lookup_page++;
  251. } // per object lookup page
  252. if (res == 1) res = SPIFFS_OK;
  253. // calculate score and insert into candidate table
  254. // stoneage sort, but probably not so many blocks
  255. if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
  256. // read erase count
  257. spiffs_obj_id erase_count;
  258. res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
  259. SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
  260. sizeof(spiffs_obj_id), (u8_t *)&erase_count);
  261. SPIFFS_CHECK_RES(res);
  262. spiffs_obj_id erase_age;
  263. if (fs->max_erase_count > erase_count) {
  264. erase_age = fs->max_erase_count - erase_count;
  265. } else {
  266. erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
  267. }
  268. s32_t score =
  269. deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
  270. used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
  271. erase_age * SPIFFS_GC_HEUR_W_ERASE_AGE;
  272. int cand_ix = 0;
  273. SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
  274. while (cand_ix < max_candidates) {
  275. if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
  276. cand_blocks[cand_ix] = cur_block;
  277. cand_scores[cand_ix] = score;
  278. break;
  279. } else if (cand_scores[cand_ix] < score) {
  280. int reorder_cand_ix = max_candidates - 2;
  281. while (reorder_cand_ix >= cand_ix) {
  282. cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
  283. cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
  284. reorder_cand_ix--;
  285. }
  286. cand_blocks[cand_ix] = cur_block;
  287. cand_scores[cand_ix] = score;
  288. break;
  289. }
  290. cand_ix++;
  291. }
  292. (*candidate_count)++;
  293. }
  294. cur_entry = 0;
  295. cur_block++;
  296. cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
  297. } // per block
  298. return res;
  299. }
  300. typedef enum {
  301. FIND_OBJ_DATA,
  302. MOVE_OBJ_DATA,
  303. MOVE_OBJ_IX,
  304. FINISHED
  305. } spiffs_gc_clean_state;
  306. typedef struct {
  307. spiffs_gc_clean_state state;
  308. spiffs_obj_id cur_obj_id;
  309. spiffs_span_ix cur_objix_spix;
  310. spiffs_page_ix cur_objix_pix;
  311. int stored_scan_entry_index;
  312. u8_t obj_id_found;
  313. } spiffs_gc;
  314. // Empties given block by moving all data into free pages of another block
  315. // Strategy:
  316. // loop:
  317. // scan object lookup for object data pages
  318. // for first found id, check spix and load corresponding object index page to memory
  319. // push object scan lookup entry index
  320. // rescan object lookup, find data pages with same id and referenced by same object index
  321. // move data page, update object index in memory
  322. // when reached end of lookup, store updated object index
  323. // pop object scan lookup entry index
  324. // repeat loop until end of object lookup
  325. // scan object lookup again for remaining object index pages, move to new page in other block
  326. //
  327. s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
  328. s32_t res = SPIFFS_OK;
  329. int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  330. int cur_entry = 0;
  331. spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  332. spiffs_gc gc;
  333. spiffs_page_ix cur_pix = 0;
  334. spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
  335. spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
  336. SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
  337. c_memset(&gc, 0, sizeof(spiffs_gc));
  338. gc.state = FIND_OBJ_DATA;
  339. if (fs->free_cursor_block_ix == bix) {
  340. // move free cursor to next block, cannot use free pages from the block we want to clean
  341. fs->free_cursor_block_ix = (bix+1)%fs->block_count;
  342. fs->free_cursor_obj_lu_entry = 0;
  343. SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
  344. }
  345. while (res == SPIFFS_OK && gc.state != FINISHED) {
  346. SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
  347. gc.obj_id_found = 0;
  348. // scan through lookup pages
  349. int obj_lookup_page = cur_entry / entries_per_page;
  350. u8_t scan = 1;
  351. // check each object lookup page
  352. while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  353. int entry_offset = obj_lookup_page * entries_per_page;
  354. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  355. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  356. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  357. // check each entry
  358. while (scan && res == SPIFFS_OK &&
  359. cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
  360. spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
  361. cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
  362. // act upon object id depending on gc state
  363. switch (gc.state) {
  364. case FIND_OBJ_DATA:
  365. if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
  366. ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
  367. SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
  368. gc.obj_id_found = 1;
  369. gc.cur_obj_id = obj_id;
  370. scan = 0;
  371. }
  372. break;
  373. case MOVE_OBJ_DATA:
  374. if (obj_id == gc.cur_obj_id) {
  375. spiffs_page_header p_hdr;
  376. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  377. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  378. SPIFFS_CHECK_RES(res);
  379. SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
  380. if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
  381. SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
  382. } else {
  383. spiffs_page_ix new_data_pix;
  384. if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
  385. // move page
  386. res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
  387. SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
  388. SPIFFS_CHECK_RES(res);
  389. // move wipes obj_lu, reload it
  390. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  391. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  392. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  393. SPIFFS_CHECK_RES(res);
  394. } else {
  395. // page is deleted but not deleted in lookup, scrap it
  396. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
  397. res = spiffs_page_delete(fs, cur_pix);
  398. SPIFFS_CHECK_RES(res);
  399. new_data_pix = SPIFFS_OBJ_ID_FREE;
  400. }
  401. // update memory representation of object index page with new data page
  402. if (gc.cur_objix_spix == 0) {
  403. // update object index header page
  404. ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
  405. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
  406. } else {
  407. // update object index page
  408. ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
  409. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
  410. }
  411. }
  412. }
  413. break;
  414. case MOVE_OBJ_IX:
  415. if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
  416. (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
  417. // found an index object id
  418. spiffs_page_header p_hdr;
  419. spiffs_page_ix new_pix;
  420. // load header
  421. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  422. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  423. SPIFFS_CHECK_RES(res);
  424. if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
  425. // move page
  426. res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
  427. SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
  428. SPIFFS_CHECK_RES(res);
  429. spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
  430. // move wipes obj_lu, reload it
  431. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  432. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  433. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  434. SPIFFS_CHECK_RES(res);
  435. } else {
  436. // page is deleted but not deleted in lookup, scrap it
  437. SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
  438. res = spiffs_page_delete(fs, cur_pix);
  439. if (res == SPIFFS_OK) {
  440. spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
  441. }
  442. }
  443. SPIFFS_CHECK_RES(res);
  444. }
  445. break;
  446. default:
  447. scan = 0;
  448. break;
  449. }
  450. cur_entry++;
  451. } // per entry
  452. obj_lookup_page++;
  453. } // per object lookup page
  454. if (res != SPIFFS_OK) break;
  455. // state finalization and switch
  456. switch (gc.state) {
  457. case FIND_OBJ_DATA:
  458. if (gc.obj_id_found) {
  459. // find out corresponding obj ix page and load it to memory
  460. spiffs_page_header p_hdr;
  461. spiffs_page_ix objix_pix;
  462. gc.stored_scan_entry_index = cur_entry;
  463. cur_entry = 0;
  464. gc.state = MOVE_OBJ_DATA;
  465. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  466. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  467. SPIFFS_CHECK_RES(res);
  468. gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
  469. SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
  470. res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
  471. SPIFFS_CHECK_RES(res);
  472. SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
  473. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  474. 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
  475. SPIFFS_CHECK_RES(res);
  476. SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
  477. gc.cur_objix_pix = objix_pix;
  478. } else {
  479. gc.state = MOVE_OBJ_IX;
  480. cur_entry = 0; // restart entry scan index
  481. }
  482. break;
  483. case MOVE_OBJ_DATA: {
  484. // store modified objix (hdr) page
  485. spiffs_page_ix new_objix_pix;
  486. gc.state = FIND_OBJ_DATA;
  487. cur_entry = gc.stored_scan_entry_index;
  488. if (gc.cur_objix_spix == 0) {
  489. // store object index header page
  490. res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
  491. SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
  492. SPIFFS_CHECK_RES(res);
  493. } else {
  494. // store object index page
  495. res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
  496. SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
  497. SPIFFS_CHECK_RES(res);
  498. spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
  499. }
  500. }
  501. break;
  502. case MOVE_OBJ_IX:
  503. gc.state = FINISHED;
  504. break;
  505. default:
  506. cur_entry = 0;
  507. break;
  508. }
  509. SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
  510. } // while state != FINISHED
  511. return res;
  512. }