spiffs_gc.c 22 KB

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