text
[RRG-proxmark3.git] / armsrc / spiffs_gc.c
blob76ff094c992dd2dc923522c53b367f3f93b34b00
1 #include "spiffs.h"
2 #include "spiffs_nucleus.h"
4 #if !SPIFFS_READ_ONLY
6 // Erases a logical block and updates the erase counter.
7 // If cache is enabled, all pages that might be cached in this block
8 // is dropped.
9 static s32_t spiffs_gc_erase_block(
10 spiffs *fs,
11 spiffs_block_ix bix) {
12 s32_t res;
14 SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
15 res = spiffs_erase_block(fs, bix);
16 SPIFFS_CHECK_RES(res);
18 #if SPIFFS_CACHE
20 u32_t i;
21 for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
22 spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
25 #endif
26 return res;
29 // Searches for blocks where all entries are deleted - if one is found,
30 // the block is erased. Compared to the non-quick gc, the quick one ensures
31 // that no updates are needed on existing objects on pages that are erased.
32 s32_t spiffs_gc_quick(
33 spiffs *fs, u16_t max_free_pages) {
34 s32_t res = SPIFFS_OK;
35 u32_t blocks = fs->block_count;
36 spiffs_block_ix cur_block = 0;
37 u32_t cur_block_addr = 0;
38 int cur_entry = 0;
39 spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
41 SPIFFS_GC_DBGF("gc_quick: running\n");
42 #if SPIFFS_GC_STATS
43 fs->stats_gc_runs++;
44 #endif
46 int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
48 // find fully deleted blocks
49 // check each block
50 while (res == SPIFFS_OK && blocks--) {
51 u16_t deleted_pages_in_block = 0;
52 u16_t free_pages_in_block = 0;
54 int obj_lookup_page = 0;
55 // check each object lookup page
56 while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
57 int entry_offset = obj_lookup_page * entries_per_page;
58 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
59 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
60 // check each entry
61 while (res == SPIFFS_OK &&
62 cur_entry - entry_offset < entries_per_page &&
63 cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
64 spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
65 if (obj_id == SPIFFS_OBJ_ID_DELETED) {
66 deleted_pages_in_block++;
67 } else if (obj_id == SPIFFS_OBJ_ID_FREE) {
68 // kill scan, go for next block
69 free_pages_in_block++;
70 if (free_pages_in_block > max_free_pages) {
71 obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
72 res = 1; // kill object lu loop
73 break;
75 } else {
76 // kill scan, go for next block
77 obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
78 res = 1; // kill object lu loop
79 break;
81 cur_entry++;
82 } // per entry
83 obj_lookup_page++;
84 } // per object lookup page
85 if (res == 1) res = SPIFFS_OK;
87 if (res == SPIFFS_OK &&
88 deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
89 free_pages_in_block <= max_free_pages) {
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;
96 cur_entry = 0;
97 cur_block++;
98 cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
99 } // per block
101 if (res == SPIFFS_OK) {
102 res = SPIFFS_ERR_NO_DELETED_BLOCKS;
104 return res;
107 // Checks if garbage collecting is necessary. If so a candidate block is found,
108 // cleansed and erased
109 s32_t spiffs_gc_check(
110 spiffs *fs,
111 u32_t len) {
112 s32_t res;
113 s32_t free_pages =
114 (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
115 - fs->stats_p_allocated - fs->stats_p_deleted;
116 int tries = 0;
118 if (fs->free_blocks > 3 &&
119 (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
120 return SPIFFS_OK;
123 u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
124 // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
125 // 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);
126 // return SPIFFS_ERR_FULL;
127 // }
128 if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
129 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);
130 return SPIFFS_ERR_FULL;
133 do {
134 SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n",
135 tries,
136 fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages + fs->stats_p_allocated + fs->stats_p_deleted),
137 len, (u32_t)(free_pages * SPIFFS_DATA_PAGE_SIZE(fs)));
139 spiffs_block_ix *cands;
140 int count;
141 spiffs_block_ix cand;
142 s32_t prev_free_pages = free_pages;
143 // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
144 res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
145 SPIFFS_CHECK_RES(res);
146 if (count == 0) {
147 SPIFFS_GC_DBGF("gc_check: no candidates, return\n");
148 return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
150 #if SPIFFS_GC_STATS
151 fs->stats_gc_runs++;
152 #endif
153 cand = cands[0];
154 fs->cleaning = 1;
155 //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
156 res = spiffs_gc_clean(fs, cand);
157 fs->cleaning = 0;
158 if (res < 0) {
159 SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
160 } else {
161 SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
163 SPIFFS_CHECK_RES(res);
165 res = spiffs_gc_erase_page_stats(fs, cand);
166 SPIFFS_CHECK_RES(res);
168 res = spiffs_gc_erase_block(fs, cand);
169 SPIFFS_CHECK_RES(res);
171 free_pages =
172 (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
173 - fs->stats_p_allocated - fs->stats_p_deleted;
175 if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
176 // abort early to reduce wear, at least tried once
177 SPIFFS_GC_DBGF("gc_check: early abort, no result on gc when fs crammed\n");
178 break;
181 } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
182 (s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
184 free_pages =
185 (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
186 - fs->stats_p_allocated - fs->stats_p_deleted;
187 if ((s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
188 res = SPIFFS_ERR_FULL;
191 SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n",
192 fs->stats_p_allocated + fs->stats_p_deleted,
193 fs->free_blocks, free_pages, tries, res);
195 return res;
198 // Updates page statistics for a block that is about to be erased
199 s32_t spiffs_gc_erase_page_stats(
200 spiffs *fs,
201 spiffs_block_ix bix) {
202 s32_t res = SPIFFS_OK;
203 int obj_lookup_page = 0;
204 int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
205 spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
206 int cur_entry = 0;
207 u32_t dele = 0;
208 u32_t allo = 0;
210 // check each object lookup page
211 while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
212 int entry_offset = obj_lookup_page * entries_per_page;
213 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
214 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);
215 // check each entry
216 while (res == SPIFFS_OK &&
217 cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
218 spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
219 if (obj_id == SPIFFS_OBJ_ID_FREE) {
220 } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
221 dele++;
222 } else {
223 allo++;
225 cur_entry++;
226 } // per entry
227 obj_lookup_page++;
228 } // per object lookup page
229 SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele);
230 fs->stats_p_allocated -= allo;
231 fs->stats_p_deleted -= dele;
232 return res;
235 // Finds block candidates to erase
236 s32_t spiffs_gc_find_candidate(
237 spiffs *fs,
238 spiffs_block_ix **block_candidates,
239 int *candidate_count,
240 char fs_crammed) {
241 s32_t res = SPIFFS_OK;
242 u32_t blocks = fs->block_count;
243 spiffs_block_ix cur_block = 0;
244 u32_t cur_block_addr = 0;
245 spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
246 int cur_entry = 0;
248 // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
249 int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs) - 8) / (sizeof(spiffs_block_ix) + sizeof(s32_t)));
250 *candidate_count = 0;
251 memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
253 // divide up work area into block indices and scores
254 spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
255 s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
257 // align cand_scores on s32_t boundary
258 cand_scores = (s32_t *)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
260 *block_candidates = cand_blocks;
262 int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
264 // check each block
265 while (res == SPIFFS_OK && blocks--) {
266 u16_t deleted_pages_in_block = 0;
267 u16_t used_pages_in_block = 0;
269 int obj_lookup_page = 0;
270 // check each object lookup page
271 while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
272 int entry_offset = obj_lookup_page * entries_per_page;
273 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
274 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
275 // check each entry
276 while (res == SPIFFS_OK &&
277 cur_entry - entry_offset < entries_per_page &&
278 cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
279 spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
280 if (obj_id == SPIFFS_OBJ_ID_FREE) {
281 // when a free entry is encountered, scan logic ensures that all following entries are free also
282 res = 1; // kill object lu loop
283 break;
284 } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
285 deleted_pages_in_block++;
286 } else {
287 used_pages_in_block++;
289 cur_entry++;
290 } // per entry
291 obj_lookup_page++;
292 } // per object lookup page
293 if (res == 1) res = SPIFFS_OK;
295 // calculate score and insert into candidate table
296 // stoneage sort, but probably not so many blocks
297 if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
298 // read erase count
299 spiffs_obj_id erase_count;
300 res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
301 SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
302 sizeof(spiffs_obj_id), (u8_t *)&erase_count);
303 SPIFFS_CHECK_RES(res);
305 spiffs_obj_id erase_age;
306 if (fs->max_erase_count > erase_count) {
307 erase_age = fs->max_erase_count - erase_count;
308 } else {
309 erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
312 s32_t score =
313 deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
314 used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
315 erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
316 int cand_ix = 0;
317 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);
318 while (cand_ix < max_candidates) {
319 if (cand_blocks[cand_ix] == (spiffs_block_ix) - 1) {
320 cand_blocks[cand_ix] = cur_block;
321 cand_scores[cand_ix] = score;
322 break;
323 } else if (cand_scores[cand_ix] < score) {
324 int reorder_cand_ix = max_candidates - 2;
325 while (reorder_cand_ix >= cand_ix) {
326 cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
327 cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
328 reorder_cand_ix--;
330 cand_blocks[cand_ix] = cur_block;
331 cand_scores[cand_ix] = score;
332 break;
334 cand_ix++;
336 (*candidate_count)++;
339 cur_entry = 0;
340 cur_block++;
341 cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
342 } // per block
344 return res;
347 typedef enum {
348 FIND_OBJ_DATA,
349 MOVE_OBJ_DATA,
350 MOVE_OBJ_IX,
351 FINISHED
352 } spiffs_gc_clean_state;
354 typedef struct {
355 spiffs_gc_clean_state state;
356 spiffs_obj_id cur_obj_id;
357 spiffs_span_ix cur_objix_spix;
358 spiffs_page_ix cur_objix_pix;
359 spiffs_page_ix cur_data_pix;
360 int stored_scan_entry_index;
361 u8_t obj_id_found;
362 } spiffs_gc;
364 // Empties given block by moving all data into free pages of another block
365 // Strategy:
366 // loop:
367 // scan object lookup for object data pages
368 // for first found id, check spix and load corresponding object index page to memory
369 // push object scan lookup entry index
370 // rescan object lookup, find data pages with same id and referenced by same object index
371 // move data page, update object index in memory
372 // when reached end of lookup, store updated object index
373 // pop object scan lookup entry index
374 // repeat loop until end of object lookup
375 // scan object lookup again for remaining object index pages, move to new page in other block
377 s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
378 s32_t res = SPIFFS_OK;
379 const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
380 // this is the global localizer being pushed and popped
381 int cur_entry = 0;
382 spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
383 spiffs_gc gc; // our stack frame/state
384 spiffs_page_ix cur_pix = 0;
385 spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
386 spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
388 SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
390 memset(&gc, 0, sizeof(spiffs_gc));
391 gc.state = FIND_OBJ_DATA;
393 if (fs->free_cursor_block_ix == bix) {
394 // move free cursor to next block, cannot use free pages from the block we want to clean
395 fs->free_cursor_block_ix = (bix + 1) % fs->block_count;
396 fs->free_cursor_obj_lu_entry = 0;
397 SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
400 while (res == SPIFFS_OK && gc.state != FINISHED) {
401 SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
402 gc.obj_id_found = 0; // reset (to no found data page)
404 // scan through lookup pages
405 int obj_lookup_page = cur_entry / entries_per_page;
406 u8_t scan = 1;
407 // check each object lookup page
408 while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
409 int entry_offset = obj_lookup_page * entries_per_page;
410 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
411 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
412 SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
413 // check each object lookup entry
414 while (scan && res == SPIFFS_OK &&
415 cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
416 spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset];
417 cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
419 // act upon object id depending on gc state
420 switch (gc.state) {
421 case FIND_OBJ_DATA:
422 // find a data page
423 if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
424 ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
425 // found a data page, stop scanning and handle in switch case below
426 SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
427 gc.obj_id_found = 1;
428 gc.cur_obj_id = obj_id;
429 gc.cur_data_pix = cur_pix;
430 scan = 0;
432 break;
433 case MOVE_OBJ_DATA:
434 // evacuate found data pages for corresponding object index we have in memory,
435 // update memory representation
436 if (obj_id == gc.cur_obj_id) {
437 spiffs_page_header p_hdr;
438 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
439 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr);
440 SPIFFS_CHECK_RES(res);
441 SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
442 if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
443 SPIFFS_GC_DBGF("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
444 } else {
445 spiffs_page_ix new_data_pix;
446 if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
447 // move page
448 res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
449 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);
450 SPIFFS_CHECK_RES(res);
451 // move wipes obj_lu, reload it
452 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
453 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
454 SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
455 SPIFFS_CHECK_RES(res);
456 } else {
457 // page is deleted but not deleted in lookup, scrap it -
458 // might seem unnecessary as we will erase this block, but
459 // we might get aborted
460 SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
461 res = spiffs_page_delete(fs, cur_pix);
462 SPIFFS_CHECK_RES(res);
463 new_data_pix = SPIFFS_OBJ_ID_FREE;
465 // update memory representation of object index page with new data page
466 if (gc.cur_objix_spix == 0) {
467 // update object index header page
468 ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
469 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));
470 } else {
471 // update object index page
472 ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
473 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));
477 break;
478 case MOVE_OBJ_IX:
479 // find and evacuate object index pages
480 if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
481 (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
482 // found an index object id
483 spiffs_page_header p_hdr;
484 spiffs_page_ix new_pix;
485 // load header
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 if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
490 // move page
491 res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
492 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);
493 SPIFFS_CHECK_RES(res);
494 spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
495 SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
496 // move wipes obj_lu, reload it
497 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
498 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
499 SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
500 SPIFFS_CHECK_RES(res);
501 } else {
502 // page is deleted but not deleted in lookup, scrap it -
503 // might seem unnecessary as we will erase this block, but
504 // we might get aborted
505 SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
506 res = spiffs_page_delete(fs, cur_pix);
507 if (res == SPIFFS_OK) {
508 spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
509 SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
512 SPIFFS_CHECK_RES(res);
514 break;
515 default:
516 scan = 0;
517 break;
518 } // switch gc state
519 cur_entry++;
520 } // per entry
521 obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
522 } // per object lookup page
523 if (res != SPIFFS_OK) break;
525 // state finalization and switch
526 switch (gc.state) {
527 case FIND_OBJ_DATA:
528 if (gc.obj_id_found) {
529 // handle found data page -
530 // find out corresponding obj ix page and load it to memory
531 spiffs_page_header p_hdr;
532 spiffs_page_ix objix_pix;
533 gc.stored_scan_entry_index = cur_entry; // push cursor
534 cur_entry = 0; // restart scan from start
535 gc.state = MOVE_OBJ_DATA;
536 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
537 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr);
538 SPIFFS_CHECK_RES(res);
539 gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
540 SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
541 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);
542 if (res == SPIFFS_ERR_NOT_FOUND) {
543 // on borked systems we might get an ERR_NOT_FOUND here -
544 // this is handled by simply deleting the page as it is not referenced
545 // from anywhere
546 SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
547 res = spiffs_page_delete(fs, gc.cur_data_pix);
548 SPIFFS_CHECK_RES(res);
549 // then we restore states and continue scanning for data pages
550 cur_entry = gc.stored_scan_entry_index; // pop cursor
551 gc.state = FIND_OBJ_DATA;
552 break; // done
554 SPIFFS_CHECK_RES(res);
555 SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
556 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
557 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
558 SPIFFS_CHECK_RES(res);
559 // cannot allow a gc if the presumed index in fact is no index, a
560 // check must run or lot of data may be lost
561 SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
562 gc.cur_objix_pix = objix_pix;
563 } else {
564 // no more data pages found, passed thru all block, start evacuating object indices
565 gc.state = MOVE_OBJ_IX;
566 cur_entry = 0; // restart entry scan index
568 break;
569 case MOVE_OBJ_DATA: {
570 // store modified objix (hdr) page residing in memory now that all
571 // data pages belonging to this object index and residing in the block
572 // we want to evacuate
573 spiffs_page_ix new_objix_pix;
574 gc.state = FIND_OBJ_DATA;
575 cur_entry = gc.stored_scan_entry_index; // pop cursor
576 if (gc.cur_objix_spix == 0) {
577 // store object index header page
578 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);
579 SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
580 SPIFFS_CHECK_RES(res);
581 } else {
582 // store object index page
583 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);
584 SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
585 SPIFFS_CHECK_RES(res);
586 spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
587 SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
590 break;
591 case MOVE_OBJ_IX:
592 // scanned thru all block, no more object indices found - our work here is done
593 gc.state = FINISHED;
594 break;
595 default:
596 cur_entry = 0;
597 break;
598 } // switch gc.state
599 SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
600 } // while state != FINISHED
603 return res;
606 #endif // !SPIFFS_READ_ONLY