1 //-----------------------------------------------------------------------------
2 // Borrowed initially from https://github.com/pellepl/spiffs
3 // Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)
4 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // See LICENSE.txt for the text of the license.
17 //-----------------------------------------------------------------------------
19 #include "spiffs_nucleus.h"
23 // Erases a logical block and updates the erase counter.
24 // If cache is enabled, all pages that might be cached in this block
26 static s32_t
spiffs_gc_erase_block(
28 spiffs_block_ix bix
) {
31 SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl
"\n", bix
);
32 res
= spiffs_erase_block(fs
, bix
);
33 SPIFFS_CHECK_RES(res
);
38 for (i
= 0; i
< SPIFFS_PAGES_PER_BLOCK(fs
); i
++) {
39 spiffs_cache_drop_page(fs
, SPIFFS_PAGE_FOR_BLOCK(fs
, bix
) + i
);
46 // Searches for blocks where all entries are deleted - if one is found,
47 // the block is erased. Compared to the non-quick gc, the quick one ensures
48 // that no updates are needed on existing objects on pages that are erased.
49 s32_t
spiffs_gc_quick(
50 spiffs
*fs
, u16_t max_free_pages
) {
51 s32_t res
= SPIFFS_OK
;
52 u32_t blocks
= fs
->block_count
;
53 spiffs_block_ix cur_block
= 0;
54 u32_t cur_block_addr
= 0;
56 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
58 SPIFFS_GC_DBGF("gc_quick: running\n");
63 int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
65 // find fully deleted blocks
67 while (res
== SPIFFS_OK
&& blocks
--) {
68 u16_t deleted_pages_in_block
= 0;
69 u16_t free_pages_in_block
= 0;
71 int obj_lookup_page
= 0;
72 // check each object lookup page
73 while (res
== SPIFFS_OK
&& obj_lookup_page
< (int)SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
74 int entry_offset
= obj_lookup_page
* entries_per_page
;
75 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
76 0, cur_block_addr
+ SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
78 while (res
== SPIFFS_OK
&&
79 cur_entry
- entry_offset
< entries_per_page
&&
80 cur_entry
< (int)(SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
))) {
81 spiffs_obj_id obj_id
= obj_lu_buf
[cur_entry
- entry_offset
];
82 if (obj_id
== SPIFFS_OBJ_ID_DELETED
) {
83 deleted_pages_in_block
++;
84 } else if (obj_id
== SPIFFS_OBJ_ID_FREE
) {
85 // kill scan, go for next block
86 free_pages_in_block
++;
87 if (free_pages_in_block
> max_free_pages
) {
88 obj_lookup_page
= SPIFFS_OBJ_LOOKUP_PAGES(fs
);
89 res
= 1; // kill object lu loop
93 // kill scan, go for next block
94 obj_lookup_page
= SPIFFS_OBJ_LOOKUP_PAGES(fs
);
95 res
= 1; // kill object lu loop
101 } // per object lookup page
102 if (res
== 1) res
= SPIFFS_OK
;
104 if (res
== SPIFFS_OK
&&
105 deleted_pages_in_block
+ free_pages_in_block
== SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
) &&
106 free_pages_in_block
<= max_free_pages
) {
107 // found a fully deleted block
108 fs
->stats_p_deleted
-= deleted_pages_in_block
;
109 res
= spiffs_gc_erase_block(fs
, cur_block
);
115 cur_block_addr
+= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
118 if (res
== SPIFFS_OK
) {
119 res
= SPIFFS_ERR_NO_DELETED_BLOCKS
;
124 // Checks if garbage collecting is necessary. If so a candidate block is found,
125 // cleansed and erased
126 s32_t
spiffs_gc_check(
131 (SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
)) * (fs
->block_count
- 2)
132 - fs
->stats_p_allocated
- fs
->stats_p_deleted
;
135 if (fs
->free_blocks
> 3 &&
136 (s32_t
)len
< free_pages
* (s32_t
)SPIFFS_DATA_PAGE_SIZE(fs
)) {
140 u32_t needed_pages
= (len
+ SPIFFS_DATA_PAGE_SIZE(fs
) - 1) / SPIFFS_DATA_PAGE_SIZE(fs
);
141 // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
142 // 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);
143 // return SPIFFS_ERR_FULL;
145 if ((s32_t
)needed_pages
> (s32_t
)(free_pages
+ fs
->stats_p_deleted
)) {
146 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
);
147 return SPIFFS_ERR_FULL
;
151 SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi
": run gc free_blocks:"_SPIPRIi
" pfree:"_SPIPRIi
" pallo:"_SPIPRIi
" pdele:"_SPIPRIi
" ["_SPIPRIi
"] len:"_SPIPRIi
" of "_SPIPRIi
"\n",
153 fs
->free_blocks
, free_pages
, fs
->stats_p_allocated
, fs
->stats_p_deleted
, (free_pages
+ fs
->stats_p_allocated
+ fs
->stats_p_deleted
),
154 len
, (u32_t
)(free_pages
* SPIFFS_DATA_PAGE_SIZE(fs
)));
156 spiffs_block_ix
*cands
;
158 spiffs_block_ix cand
;
159 s32_t prev_free_pages
= free_pages
;
160 // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
161 res
= spiffs_gc_find_candidate(fs
, &cands
, &count
, free_pages
<= 0);
162 SPIFFS_CHECK_RES(res
);
164 SPIFFS_GC_DBGF("gc_check: no candidates, return\n");
165 return (s32_t
)needed_pages
< free_pages
? SPIFFS_OK
: SPIFFS_ERR_FULL
;
172 //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
173 res
= spiffs_gc_clean(fs
, cand
);
176 SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi
", result "_SPIPRIi
"\n", cand
, res
);
178 SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi
", result "_SPIPRIi
"\n", cand
, res
);
180 SPIFFS_CHECK_RES(res
);
182 res
= spiffs_gc_erase_page_stats(fs
, cand
);
183 SPIFFS_CHECK_RES(res
);
185 res
= spiffs_gc_erase_block(fs
, cand
);
186 SPIFFS_CHECK_RES(res
);
189 (SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
)) * (fs
->block_count
- 2)
190 - fs
->stats_p_allocated
- fs
->stats_p_deleted
;
192 if (prev_free_pages
<= 0 && prev_free_pages
== free_pages
) {
193 // abort early to reduce wear, at least tried once
194 SPIFFS_GC_DBGF("gc_check: early abort, no result on gc when fs crammed\n");
198 } while (++tries
< SPIFFS_GC_MAX_RUNS
&& (fs
->free_blocks
<= 2 ||
199 (s32_t
)len
> free_pages
* (s32_t
)SPIFFS_DATA_PAGE_SIZE(fs
)));
202 (SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
)) * (fs
->block_count
- 2)
203 - fs
->stats_p_allocated
- fs
->stats_p_deleted
;
204 if ((s32_t
)len
> free_pages
* (s32_t
)SPIFFS_DATA_PAGE_SIZE(fs
)) {
205 res
= SPIFFS_ERR_FULL
;
208 SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi
" dirty, blocks "_SPIPRIi
" free, "_SPIPRIi
" pages free, "_SPIPRIi
" tries, res "_SPIPRIi
"\n",
209 fs
->stats_p_allocated
+ fs
->stats_p_deleted
,
210 fs
->free_blocks
, free_pages
, tries
, res
);
215 // Updates page statistics for a block that is about to be erased
216 s32_t
spiffs_gc_erase_page_stats(
218 spiffs_block_ix bix
) {
219 s32_t res
= SPIFFS_OK
;
220 int obj_lookup_page
= 0;
221 int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
222 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
227 // check each object lookup page
228 while (res
== SPIFFS_OK
&& obj_lookup_page
< (int)SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
229 int entry_offset
= obj_lookup_page
* entries_per_page
;
230 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
231 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
);
233 while (res
== SPIFFS_OK
&&
234 cur_entry
- entry_offset
< entries_per_page
&& cur_entry
< (int)(SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
))) {
235 spiffs_obj_id obj_id
= obj_lu_buf
[cur_entry
- entry_offset
];
236 if (obj_id
== SPIFFS_OBJ_ID_FREE
) {
237 } else if (obj_id
== SPIFFS_OBJ_ID_DELETED
) {
245 } // per object lookup page
246 SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi
" pdele:"_SPIPRIi
"\n", allo
, dele
);
247 fs
->stats_p_allocated
-= allo
;
248 fs
->stats_p_deleted
-= dele
;
252 // Finds block candidates to erase
253 s32_t
spiffs_gc_find_candidate(
255 spiffs_block_ix
**block_candidates
,
256 int *candidate_count
,
258 s32_t res
= SPIFFS_OK
;
259 u32_t blocks
= fs
->block_count
;
260 spiffs_block_ix cur_block
= 0;
261 u32_t cur_block_addr
= 0;
262 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
265 // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
266 int max_candidates
= MIN(fs
->block_count
, (SPIFFS_CFG_LOG_PAGE_SZ(fs
) - 8) / (sizeof(spiffs_block_ix
) + sizeof(s32_t
)));
267 *candidate_count
= 0;
268 memset(fs
->work
, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs
));
270 // divide up work area into block indices and scores
271 spiffs_block_ix
*cand_blocks
= (spiffs_block_ix
*)fs
->work
;
272 s32_t
*cand_scores
= (s32_t
*)(fs
->work
+ max_candidates
* sizeof(spiffs_block_ix
));
274 // align cand_scores on s32_t boundary
275 cand_scores
= (s32_t
*)(((intptr_t)cand_scores
+ sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
277 *block_candidates
= cand_blocks
;
279 int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
282 while (res
== SPIFFS_OK
&& blocks
--) {
283 u16_t deleted_pages_in_block
= 0;
284 u16_t used_pages_in_block
= 0;
286 int obj_lookup_page
= 0;
287 // check each object lookup page
288 while (res
== SPIFFS_OK
&& obj_lookup_page
< (int)SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
289 int entry_offset
= obj_lookup_page
* entries_per_page
;
290 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
291 0, cur_block_addr
+ SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
293 while (res
== SPIFFS_OK
&&
294 cur_entry
- entry_offset
< entries_per_page
&&
295 cur_entry
< (int)(SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
))) {
296 spiffs_obj_id obj_id
= obj_lu_buf
[cur_entry
- entry_offset
];
297 if (obj_id
== SPIFFS_OBJ_ID_FREE
) {
298 // when a free entry is encountered, scan logic ensures that all following entries are free also
299 res
= 1; // kill object lu loop
301 } else if (obj_id
== SPIFFS_OBJ_ID_DELETED
) {
302 deleted_pages_in_block
++;
304 used_pages_in_block
++;
309 } // per object lookup page
310 if (res
== 1) res
= SPIFFS_OK
;
312 // calculate score and insert into candidate table
313 // stoneage sort, but probably not so many blocks
314 if (res
== SPIFFS_OK
/*&& deleted_pages_in_block > 0*/) {
316 spiffs_obj_id erase_count
;
317 res
= _spiffs_rd(fs
, SPIFFS_OP_C_READ
| SPIFFS_OP_T_OBJ_LU2
, 0,
318 SPIFFS_ERASE_COUNT_PADDR(fs
, cur_block
),
319 sizeof(spiffs_obj_id
), (u8_t
*)&erase_count
);
320 SPIFFS_CHECK_RES(res
);
322 spiffs_obj_id erase_age
;
323 if (fs
->max_erase_count
> erase_count
) {
324 erase_age
= fs
->max_erase_count
- erase_count
;
326 erase_age
= SPIFFS_OBJ_ID_FREE
- (erase_count
- fs
->max_erase_count
);
330 deleted_pages_in_block
* SPIFFS_GC_HEUR_W_DELET
+
331 used_pages_in_block
* SPIFFS_GC_HEUR_W_USED
+
332 erase_age
* (fs_crammed
? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE
);
334 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
);
335 while (cand_ix
< max_candidates
) {
336 if (cand_blocks
[cand_ix
] == (spiffs_block_ix
) - 1) {
337 cand_blocks
[cand_ix
] = cur_block
;
338 cand_scores
[cand_ix
] = score
;
340 } else if (cand_scores
[cand_ix
] < score
) {
341 int reorder_cand_ix
= max_candidates
- 2;
342 while (reorder_cand_ix
>= cand_ix
) {
343 cand_blocks
[reorder_cand_ix
+ 1] = cand_blocks
[reorder_cand_ix
];
344 cand_scores
[reorder_cand_ix
+ 1] = cand_scores
[reorder_cand_ix
];
347 cand_blocks
[cand_ix
] = cur_block
;
348 cand_scores
[cand_ix
] = score
;
353 (*candidate_count
)++;
358 cur_block_addr
+= SPIFFS_CFG_LOG_BLOCK_SZ(fs
);
369 } spiffs_gc_clean_state
;
372 spiffs_gc_clean_state state
;
373 spiffs_obj_id cur_obj_id
;
374 spiffs_span_ix cur_objix_spix
;
375 spiffs_page_ix cur_objix_pix
;
376 spiffs_page_ix cur_data_pix
;
377 int stored_scan_entry_index
;
381 // Empties given block by moving all data into free pages of another block
384 // scan object lookup for object data pages
385 // for first found id, check spix and load corresponding object index page to memory
386 // push object scan lookup entry index
387 // rescan object lookup, find data pages with same id and referenced by same object index
388 // move data page, update object index in memory
389 // when reached end of lookup, store updated object index
390 // pop object scan lookup entry index
391 // repeat loop until end of object lookup
392 // scan object lookup again for remaining object index pages, move to new page in other block
394 s32_t
spiffs_gc_clean(spiffs
*fs
, spiffs_block_ix bix
) {
395 s32_t res
= SPIFFS_OK
;
396 const int entries_per_page
= (SPIFFS_CFG_LOG_PAGE_SZ(fs
) / sizeof(spiffs_obj_id
));
397 // this is the global localizer being pushed and popped
399 spiffs_obj_id
*obj_lu_buf
= (spiffs_obj_id
*)fs
->lu_work
;
400 spiffs_gc gc
; // our stack frame/state
401 spiffs_page_ix cur_pix
= 0;
402 spiffs_page_object_ix_header
*objix_hdr
= (spiffs_page_object_ix_header
*)fs
->work
;
403 spiffs_page_object_ix
*objix
= (spiffs_page_object_ix
*)fs
->work
;
405 SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl
"\n", bix
);
407 memset(&gc
, 0, sizeof(spiffs_gc
));
408 gc
.state
= FIND_OBJ_DATA
;
410 if (fs
->free_cursor_block_ix
== bix
) {
411 // move free cursor to next block, cannot use free pages from the block we want to clean
412 fs
->free_cursor_block_ix
= (bix
+ 1) % fs
->block_count
;
413 fs
->free_cursor_obj_lu_entry
= 0;
414 SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl
"\n", fs
->free_cursor_block_ix
);
417 while (res
== SPIFFS_OK
&& gc
.state
!= FINISHED
) {
418 SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi
" entry:"_SPIPRIi
"\n", gc
.state
, cur_entry
);
419 gc
.obj_id_found
= 0; // reset (to no found data page)
421 // scan through lookup pages
422 int obj_lookup_page
= cur_entry
/ entries_per_page
;
424 // check each object lookup page
425 while (scan
&& res
== SPIFFS_OK
&& obj_lookup_page
< (int)SPIFFS_OBJ_LOOKUP_PAGES(fs
)) {
426 int entry_offset
= obj_lookup_page
* entries_per_page
;
427 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
428 0, bix
* SPIFFS_CFG_LOG_BLOCK_SZ(fs
) + SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
),
429 SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
430 // check each object lookup entry
431 while (scan
&& res
== SPIFFS_OK
&&
432 cur_entry
- entry_offset
< entries_per_page
&& cur_entry
< (int)(SPIFFS_PAGES_PER_BLOCK(fs
) - SPIFFS_OBJ_LOOKUP_PAGES(fs
))) {
433 spiffs_obj_id obj_id
= obj_lu_buf
[cur_entry
- entry_offset
];
434 cur_pix
= SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs
, bix
, cur_entry
);
436 // act upon object id depending on gc state
440 if (obj_id
!= SPIFFS_OBJ_ID_DELETED
&& obj_id
!= SPIFFS_OBJ_ID_FREE
&&
441 ((obj_id
& SPIFFS_OBJ_ID_IX_FLAG
) == 0)) {
442 // found a data page, stop scanning and handle in switch case below
443 SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi
" - found obj id "_SPIPRIid
"\n", gc
.state
, obj_id
);
445 gc
.cur_obj_id
= obj_id
;
446 gc
.cur_data_pix
= cur_pix
;
451 // evacuate found data pages for corresponding object index we have in memory,
452 // update memory representation
453 if (obj_id
== gc
.cur_obj_id
) {
454 spiffs_page_header p_hdr
;
455 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
456 0, SPIFFS_PAGE_TO_PADDR(fs
, cur_pix
), sizeof(spiffs_page_header
), (u8_t
*)&p_hdr
);
457 SPIFFS_CHECK_RES(res
);
458 SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid
":"_SPIPRIsp
" @ "_SPIPRIpg
"\n", gc
.cur_obj_id
, p_hdr
.span_ix
, cur_pix
);
459 if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, p_hdr
.span_ix
) != gc
.cur_objix_spix
) {
460 SPIFFS_GC_DBGF("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
462 spiffs_page_ix new_data_pix
;
463 if (p_hdr
.flags
& SPIFFS_PH_FLAG_DELET
) {
465 res
= spiffs_page_move(fs
, 0, 0, obj_id
, &p_hdr
, cur_pix
, &new_data_pix
);
466 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
);
467 SPIFFS_CHECK_RES(res
);
468 // move wipes obj_lu, reload it
469 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
470 0, bix
* SPIFFS_CFG_LOG_BLOCK_SZ(fs
) + SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
),
471 SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
472 SPIFFS_CHECK_RES(res
);
474 // page is deleted but not deleted in lookup, scrap it -
475 // might seem unnecessary as we will erase this block, but
476 // we might get aborted
477 SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid
":"_SPIPRIsp
" page "_SPIPRIpg
"\n", obj_id
, p_hdr
.span_ix
, cur_pix
);
478 res
= spiffs_page_delete(fs
, cur_pix
);
479 SPIFFS_CHECK_RES(res
);
480 new_data_pix
= SPIFFS_OBJ_ID_FREE
;
482 // update memory representation of object index page with new data page
483 if (gc
.cur_objix_spix
== 0) {
484 // update object index header page
485 ((spiffs_page_ix
*)((u8_t
*)objix_hdr
+ sizeof(spiffs_page_object_ix_header
)))[p_hdr
.span_ix
] = new_data_pix
;
486 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
));
488 // update object index page
489 ((spiffs_page_ix
*)((u8_t
*)objix
+ sizeof(spiffs_page_object_ix
)))[SPIFFS_OBJ_IX_ENTRY(fs
, p_hdr
.span_ix
)] = new_data_pix
;
490 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
));
496 // find and evacuate object index pages
497 if (obj_id
!= SPIFFS_OBJ_ID_DELETED
&& obj_id
!= SPIFFS_OBJ_ID_FREE
&&
498 (obj_id
& SPIFFS_OBJ_ID_IX_FLAG
)) {
499 // found an index object id
500 spiffs_page_header p_hdr
;
501 spiffs_page_ix new_pix
;
503 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
504 0, SPIFFS_PAGE_TO_PADDR(fs
, cur_pix
), sizeof(spiffs_page_header
), (u8_t
*)&p_hdr
);
505 SPIFFS_CHECK_RES(res
);
506 if (p_hdr
.flags
& SPIFFS_PH_FLAG_DELET
) {
508 res
= spiffs_page_move(fs
, 0, 0, obj_id
, &p_hdr
, cur_pix
, &new_pix
);
509 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
);
510 SPIFFS_CHECK_RES(res
);
511 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)&p_hdr
,
512 SPIFFS_EV_IX_MOV
, obj_id
, p_hdr
.span_ix
, new_pix
, 0);
513 // move wipes obj_lu, reload it
514 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU
| SPIFFS_OP_C_READ
,
515 0, bix
* SPIFFS_CFG_LOG_BLOCK_SZ(fs
) + SPIFFS_PAGE_TO_PADDR(fs
, obj_lookup_page
),
516 SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->lu_work
);
517 SPIFFS_CHECK_RES(res
);
519 // page is deleted but not deleted in lookup, scrap it -
520 // might seem unnecessary as we will erase this block, but
521 // we might get aborted
522 SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid
":"_SPIPRIsp
" page "_SPIPRIpg
"\n", obj_id
, p_hdr
.span_ix
, cur_pix
);
523 res
= spiffs_page_delete(fs
, cur_pix
);
524 if (res
== SPIFFS_OK
) {
525 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)0,
526 SPIFFS_EV_IX_DEL
, obj_id
, p_hdr
.span_ix
, cur_pix
, 0);
529 SPIFFS_CHECK_RES(res
);
538 obj_lookup_page
++; // no need to check scan variable here, obj_lookup_page is set in start of loop
539 } // per object lookup page
540 if (res
!= SPIFFS_OK
) break;
542 // state finalization and switch
545 if (gc
.obj_id_found
) {
546 // handle found data page -
547 // find out corresponding obj ix page and load it to memory
548 spiffs_page_header p_hdr
;
549 spiffs_page_ix objix_pix
;
550 gc
.stored_scan_entry_index
= cur_entry
; // push cursor
551 cur_entry
= 0; // restart scan from start
552 gc
.state
= MOVE_OBJ_DATA
;
553 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
554 0, SPIFFS_PAGE_TO_PADDR(fs
, cur_pix
), sizeof(spiffs_page_header
), (u8_t
*)&p_hdr
);
555 SPIFFS_CHECK_RES(res
);
556 gc
.cur_objix_spix
= SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs
, p_hdr
.span_ix
);
557 SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp
"\n", gc
.cur_objix_spix
);
558 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
);
559 if (res
== SPIFFS_ERR_NOT_FOUND
) {
560 // on borked systems we might get an ERR_NOT_FOUND here -
561 // this is handled by simply deleting the page as it is not referenced
563 SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg
"\n", gc
.cur_data_pix
);
564 res
= spiffs_page_delete(fs
, gc
.cur_data_pix
);
565 SPIFFS_CHECK_RES(res
);
566 // then we restore states and continue scanning for data pages
567 cur_entry
= gc
.stored_scan_entry_index
; // pop cursor
568 gc
.state
= FIND_OBJ_DATA
;
571 SPIFFS_CHECK_RES(res
);
572 SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg
"\n", objix_pix
);
573 res
= _spiffs_rd(fs
, SPIFFS_OP_T_OBJ_LU2
| SPIFFS_OP_C_READ
,
574 0, SPIFFS_PAGE_TO_PADDR(fs
, objix_pix
), SPIFFS_CFG_LOG_PAGE_SZ(fs
), fs
->work
);
575 SPIFFS_CHECK_RES(res
);
576 // cannot allow a gc if the presumed index in fact is no index, a
577 // check must run or lot of data may be lost
578 SPIFFS_VALIDATE_OBJIX(objix
->p_hdr
, gc
.cur_obj_id
| SPIFFS_OBJ_ID_IX_FLAG
, gc
.cur_objix_spix
);
579 gc
.cur_objix_pix
= objix_pix
;
581 // no more data pages found, passed thru all block, start evacuating object indices
582 gc
.state
= MOVE_OBJ_IX
;
583 cur_entry
= 0; // restart entry scan index
586 case MOVE_OBJ_DATA
: {
587 // store modified objix (hdr) page residing in memory now that all
588 // data pages belonging to this object index and residing in the block
589 // we want to evacuate
590 spiffs_page_ix new_objix_pix
;
591 gc
.state
= FIND_OBJ_DATA
;
592 cur_entry
= gc
.stored_scan_entry_index
; // pop cursor
593 if (gc
.cur_objix_spix
== 0) {
594 // store object index header page
595 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
);
596 SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg
":"_SPIPRIsp
"\n", new_objix_pix
, 0);
597 SPIFFS_CHECK_RES(res
);
599 // store object index page
600 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
);
601 SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg
":"_SPIPRIsp
"\n", new_objix_pix
, objix
->p_hdr
.span_ix
);
602 SPIFFS_CHECK_RES(res
);
603 spiffs_cb_object_event(fs
, (spiffs_page_object_ix
*)fs
->work
,
604 SPIFFS_EV_IX_UPD
, gc
.cur_obj_id
, objix
->p_hdr
.span_ix
, new_objix_pix
, 0);
609 // scanned thru all block, no more object indices found - our work here is done
616 SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi
"\n", gc
.state
);
617 } // while state != FINISHED
623 #endif // !SPIFFS_READ_ONLY