2 #include "spiffs_nucleus.h"
6 // Erases a logical block and updates the erase counter.
7 // If cache is enabled, all pages that might be cached in this block
9 static s32_t
spiffs_gc_erase_block(
11 spiffs_block_ix bix
) {
14 SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl
"\n", bix
);
15 res
= spiffs_erase_block(fs
, bix
);
16 SPIFFS_CHECK_RES(res
);
21 for (i
= 0; i
< SPIFFS_PAGES_PER_BLOCK(fs
); i
++) {
22 spiffs_cache_drop_page(fs
, SPIFFS_PAGE_FOR_BLOCK(fs
, bix
) + i
);
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;
39 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
41 SPIFFS_GC_DBGF("gc_quick: running\n");
46 int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
48 // find fully deleted blocks
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
);
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
76 // kill scan, go for next block
77 obj_lookup_page
= SPIFFS_OBJ_LOOKUP_PAGES(fs
);
78 res
= 1; // kill object lu loop
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
);
98 cur_block_addr
+= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
101 if (res
== SPIFFS_OK
) {
102 res
= SPIFFS_ERR_NO_DELETED_BLOCKS
;
107 // Checks if garbage collecting is necessary. If so a candidate block is found,
108 // cleansed and erased
109 s32_t
spiffs_gc_check(
114 (SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
)) * (fs
->block_count
- 2)
115 - fs
->stats_p_allocated
- fs
->stats_p_deleted
;
118 if (fs
->free_blocks
> 3 &&
119 (s32_t
)len
< free_pages
* (s32_t
)SPIFFS_DATA_PAGE_SIZE(fs
)) {
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;
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
;
134 SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi
": run gc free_blocks:"_SPIPRIi
" pfree:"_SPIPRIi
" pallo:"_SPIPRIi
" pdele:"_SPIPRIi
" ["_SPIPRIi
"] len:"_SPIPRIi
" of "_SPIPRIi
"\n",
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
;
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
);
147 SPIFFS_GC_DBGF("gc_check: no candidates, return\n");
148 return (s32_t
)needed_pages
< free_pages
? SPIFFS_OK
: SPIFFS_ERR_FULL
;
155 //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
156 res
= spiffs_gc_clean(fs
, cand
);
159 SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi
", result "_SPIPRIi
"\n", cand
, res
);
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
);
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");
181 } while (++tries
< SPIFFS_GC_MAX_RUNS
&& (fs
->free_blocks
<= 2 ||
182 (s32_t
)len
> free_pages
* (s32_t
)SPIFFS_DATA_PAGE_SIZE(fs
)));
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
);
198 // Updates page statistics for a block that is about to be erased
199 s32_t
spiffs_gc_erase_page_stats(
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
;
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
);
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
) {
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
;
235 // Finds block candidates to erase
236 s32_t
spiffs_gc_find_candidate(
238 spiffs_block_ix
**block_candidates
,
239 int *candidate_count
,
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
;
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
));
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
);
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
284 } else if (obj_id
== SPIFFS_OBJ_ID_DELETED
) {
285 deleted_pages_in_block
++;
287 used_pages_in_block
++;
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*/) {
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
;
309 erase_age
= SPIFFS_OBJ_ID_FREE
- (erase_count
- fs
->max_erase_count
);
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
);
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
;
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
];
330 cand_blocks
[cand_ix
] = cur_block
;
331 cand_scores
[cand_ix
] = score
;
336 (*candidate_count
)++;
341 cur_block_addr
+= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
352 } spiffs_gc_clean_state
;
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
;
364 // Empties given block by moving all data into free pages of another block
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
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
;
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
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
);
428 gc
.cur_obj_id
= obj_id
;
429 gc
.cur_data_pix
= cur_pix
;
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");
445 spiffs_page_ix new_data_pix
;
446 if (p_hdr
.flags
& SPIFFS_PH_FLAG_DELET
) {
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
);
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
));
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
));
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
;
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
) {
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
);
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
);
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
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
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
;
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
;
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
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
);
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);
592 // scanned thru all block, no more object indices found - our work here is done
599 SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi
"\n", gc
.state
);
600 } // while state != FINISHED
606 #endif // !SPIFFS_READ_ONLY