Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

607 рядки
25 KiB

  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 "_SPIPRIbl"\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:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\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:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\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 #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\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, (u32_t)(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. //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\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 "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
  143. } else {
  144. SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\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, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\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:"_SPIPRIi" pdele:"_SPIPRIi"\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*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_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:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\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. spiffs_page_ix cur_data_pix;
  318. int stored_scan_entry_index;
  319. u8_t obj_id_found;
  320. } spiffs_gc;
  321. // Empties given block by moving all data into free pages of another block
  322. // Strategy:
  323. // loop:
  324. // scan object lookup for object data pages
  325. // for first found id, check spix and load corresponding object index page to memory
  326. // push object scan lookup entry index
  327. // rescan object lookup, find data pages with same id and referenced by same object index
  328. // move data page, update object index in memory
  329. // when reached end of lookup, store updated object index
  330. // pop object scan lookup entry index
  331. // repeat loop until end of object lookup
  332. // scan object lookup again for remaining object index pages, move to new page in other block
  333. //
  334. s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
  335. s32_t res = SPIFFS_OK;
  336. const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
  337. // this is the global localizer being pushed and popped
  338. int cur_entry = 0;
  339. spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
  340. spiffs_gc gc; // our stack frame/state
  341. spiffs_page_ix cur_pix = 0;
  342. spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
  343. spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
  344. SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
  345. memset(&gc, 0, sizeof(spiffs_gc));
  346. gc.state = FIND_OBJ_DATA;
  347. if (fs->free_cursor_block_ix == bix) {
  348. // move free cursor to next block, cannot use free pages from the block we want to clean
  349. fs->free_cursor_block_ix = (bix+1)%fs->block_count;
  350. fs->free_cursor_obj_lu_entry = 0;
  351. SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
  352. }
  353. while (res == SPIFFS_OK && gc.state != FINISHED) {
  354. SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
  355. gc.obj_id_found = 0; // reset (to no found data page)
  356. // scan through lookup pages
  357. int obj_lookup_page = cur_entry / entries_per_page;
  358. u8_t scan = 1;
  359. // check each object lookup page
  360. while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
  361. int entry_offset = obj_lookup_page * entries_per_page;
  362. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  363. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  364. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  365. // check each object lookup entry
  366. while (scan && res == SPIFFS_OK &&
  367. cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
  368. spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
  369. cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
  370. // act upon object id depending on gc state
  371. switch (gc.state) {
  372. case FIND_OBJ_DATA:
  373. // find a data page
  374. if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
  375. ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
  376. // found a data page, stop scanning and handle in switch case below
  377. SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
  378. gc.obj_id_found = 1;
  379. gc.cur_obj_id = obj_id;
  380. gc.cur_data_pix = cur_pix;
  381. scan = 0;
  382. }
  383. break;
  384. case MOVE_OBJ_DATA:
  385. // evacuate found data pages for corresponding object index we have in memory,
  386. // update memory representation
  387. if (obj_id == gc.cur_obj_id) {
  388. spiffs_page_header p_hdr;
  389. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  390. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  391. SPIFFS_CHECK_RES(res);
  392. SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
  393. if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
  394. SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
  395. } else {
  396. spiffs_page_ix new_data_pix;
  397. if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
  398. // move page
  399. res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
  400. SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
  401. SPIFFS_CHECK_RES(res);
  402. // move wipes obj_lu, reload it
  403. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  404. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  405. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  406. SPIFFS_CHECK_RES(res);
  407. } else {
  408. // page is deleted but not deleted in lookup, scrap it -
  409. // might seem unnecessary as we will erase this block, but
  410. // we might get aborted
  411. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
  412. res = spiffs_page_delete(fs, cur_pix);
  413. SPIFFS_CHECK_RES(res);
  414. new_data_pix = SPIFFS_OBJ_ID_FREE;
  415. }
  416. // update memory representation of object index page with new data page
  417. if (gc.cur_objix_spix == 0) {
  418. // update object index header page
  419. ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
  420. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
  421. } else {
  422. // update object index page
  423. ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
  424. SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
  425. }
  426. }
  427. }
  428. break;
  429. case MOVE_OBJ_IX:
  430. // find and evacuate object index pages
  431. if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
  432. (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
  433. // found an index object id
  434. spiffs_page_header p_hdr;
  435. spiffs_page_ix new_pix;
  436. // load header
  437. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  438. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  439. SPIFFS_CHECK_RES(res);
  440. if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
  441. // move page
  442. res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
  443. SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
  444. SPIFFS_CHECK_RES(res);
  445. spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
  446. SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
  447. // move wipes obj_lu, reload it
  448. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
  449. 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
  450. SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
  451. SPIFFS_CHECK_RES(res);
  452. } else {
  453. // page is deleted but not deleted in lookup, scrap it -
  454. // might seem unnecessary as we will erase this block, but
  455. // we might get aborted
  456. SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
  457. res = spiffs_page_delete(fs, cur_pix);
  458. if (res == SPIFFS_OK) {
  459. spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
  460. SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
  461. }
  462. }
  463. SPIFFS_CHECK_RES(res);
  464. }
  465. break;
  466. default:
  467. scan = 0;
  468. break;
  469. } // switch gc state
  470. cur_entry++;
  471. } // per entry
  472. obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
  473. } // per object lookup page
  474. if (res != SPIFFS_OK) break;
  475. // state finalization and switch
  476. switch (gc.state) {
  477. case FIND_OBJ_DATA:
  478. if (gc.obj_id_found) {
  479. // handle found data page -
  480. // find out corresponding obj ix page and load it to memory
  481. spiffs_page_header p_hdr;
  482. spiffs_page_ix objix_pix;
  483. gc.stored_scan_entry_index = cur_entry; // push cursor
  484. cur_entry = 0; // restart scan from start
  485. gc.state = MOVE_OBJ_DATA;
  486. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  487. 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
  488. SPIFFS_CHECK_RES(res);
  489. gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
  490. SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
  491. 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);
  492. if (res == SPIFFS_ERR_NOT_FOUND) {
  493. // on borked systems we might get an ERR_NOT_FOUND here -
  494. // this is handled by simply deleting the page as it is not referenced
  495. // from anywhere
  496. SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
  497. res = spiffs_page_delete(fs, gc.cur_data_pix);
  498. SPIFFS_CHECK_RES(res);
  499. // then we restore states and continue scanning for data pages
  500. cur_entry = gc.stored_scan_entry_index; // pop cursor
  501. gc.state = FIND_OBJ_DATA;
  502. break; // done
  503. }
  504. SPIFFS_CHECK_RES(res);
  505. SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
  506. res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
  507. 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
  508. SPIFFS_CHECK_RES(res);
  509. // cannot allow a gc if the presumed index in fact is no index, a
  510. // check must run or lot of data may be lost
  511. SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
  512. gc.cur_objix_pix = objix_pix;
  513. } else {
  514. // no more data pages found, passed thru all block, start evacuating object indices
  515. gc.state = MOVE_OBJ_IX;
  516. cur_entry = 0; // restart entry scan index
  517. }
  518. break;
  519. case MOVE_OBJ_DATA: {
  520. // store modified objix (hdr) page residing in memory now that all
  521. // data pages belonging to this object index and residing in the block
  522. // we want to evacuate
  523. spiffs_page_ix new_objix_pix;
  524. gc.state = FIND_OBJ_DATA;
  525. cur_entry = gc.stored_scan_entry_index; // pop cursor
  526. if (gc.cur_objix_spix == 0) {
  527. // store object index header page
  528. 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, 0, &new_objix_pix);
  529. SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
  530. SPIFFS_CHECK_RES(res);
  531. } else {
  532. // store object index page
  533. 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);
  534. SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
  535. SPIFFS_CHECK_RES(res);
  536. spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
  537. SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
  538. }
  539. }
  540. break;
  541. case MOVE_OBJ_IX:
  542. // scanned thru all block, no more object indices found - our work here is done
  543. gc.state = FINISHED;
  544. break;
  545. default:
  546. cur_entry = 0;
  547. break;
  548. } // switch gc.state
  549. SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
  550. } // while state != FINISHED
  551. return res;
  552. }
  553. #endif // !SPIFFS_READ_ONLY