2022-01-06 09:19:46 +08:00
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/pellepl/spiffs
// Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
2019-07-23 04:56:06 +08:00
# include "spiffs.h"
# include "spiffs_nucleus.h"
# if !SPIFFS_READ_ONLY
// Erases a logical block and updates the erase counter.
// If cache is enabled, all pages that might be cached in this block
// is dropped.
static s32_t spiffs_gc_erase_block (
spiffs * fs ,
spiffs_block_ix bix ) {
2019-07-24 03:33:52 +08:00
s32_t res ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
SPIFFS_GC_DBG ( " gc: erase block " _SPIPRIbl " \n " , bix ) ;
res = spiffs_erase_block ( fs , bix ) ;
SPIFFS_CHECK_RES ( res ) ;
2019-07-23 04:56:06 +08:00
# if SPIFFS_CACHE
2019-07-24 03:33:52 +08:00
{
u32_t i ;
for ( i = 0 ; i < SPIFFS_PAGES_PER_BLOCK ( fs ) ; i + + ) {
spiffs_cache_drop_page ( fs , SPIFFS_PAGE_FOR_BLOCK ( fs , bix ) + i ) ;
}
2019-07-23 04:56:06 +08:00
}
# endif
2019-07-24 03:33:52 +08:00
return res ;
2019-07-23 04:56:06 +08:00
}
// Searches for blocks where all entries are deleted - if one is found,
// the block is erased. Compared to the non-quick gc, the quick one ensures
// that no updates are needed on existing objects on pages that are erased.
s32_t spiffs_gc_quick (
spiffs * fs , u16_t max_free_pages ) {
2019-07-24 03:33:52 +08:00
s32_t res = SPIFFS_OK ;
u32_t blocks = fs - > block_count ;
spiffs_block_ix cur_block = 0 ;
u32_t cur_block_addr = 0 ;
int cur_entry = 0 ;
spiffs_obj_id * obj_lu_buf = ( spiffs_obj_id * ) fs - > lu_work ;
SPIFFS_GC_DBGF ( " gc_quick: running \n " ) ;
2019-07-23 04:56:06 +08:00
# if SPIFFS_GC_STATS
2019-07-24 03:33:52 +08:00
fs - > stats_gc_runs + + ;
2019-07-23 04:56:06 +08:00
# endif
2019-07-24 03:33:52 +08:00
int entries_per_page = ( SPIFFS_CFG_LOG_PAGE_SZ ( fs ) / sizeof ( spiffs_obj_id ) ) ;
// find fully deleted blocks
// check each block
while ( res = = SPIFFS_OK & & blocks - - ) {
u16_t deleted_pages_in_block = 0 ;
u16_t free_pages_in_block = 0 ;
int obj_lookup_page = 0 ;
// check each object lookup page
while ( res = = SPIFFS_OK & & obj_lookup_page < ( int ) SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) {
int entry_offset = obj_lookup_page * entries_per_page ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
0 , cur_block_addr + SPIFFS_PAGE_TO_PADDR ( fs , obj_lookup_page ) , SPIFFS_CFG_LOG_PAGE_SZ ( fs ) , fs - > lu_work ) ;
// check each entry
while ( res = = SPIFFS_OK & &
cur_entry - entry_offset < entries_per_page & &
cur_entry < ( int ) ( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) ) {
spiffs_obj_id obj_id = obj_lu_buf [ cur_entry - entry_offset ] ;
if ( obj_id = = SPIFFS_OBJ_ID_DELETED ) {
deleted_pages_in_block + + ;
} else if ( obj_id = = SPIFFS_OBJ_ID_FREE ) {
// kill scan, go for next block
free_pages_in_block + + ;
if ( free_pages_in_block > max_free_pages ) {
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ;
res = 1 ; // kill object lu loop
break ;
}
} else {
// kill scan, go for next block
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ;
res = 1 ; // kill object lu loop
break ;
}
cur_entry + + ;
} // per entry
obj_lookup_page + + ;
} // per object lookup page
if ( res = = 1 ) res = SPIFFS_OK ;
if ( res = = SPIFFS_OK & &
deleted_pages_in_block + free_pages_in_block = = SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) & &
free_pages_in_block < = max_free_pages ) {
// found a fully deleted block
fs - > stats_p_deleted - = deleted_pages_in_block ;
res = spiffs_gc_erase_block ( fs , cur_block ) ;
return res ;
2019-07-23 04:56:06 +08:00
}
2019-07-24 03:33:52 +08:00
cur_entry = 0 ;
cur_block + + ;
cur_block_addr + = SPIFFS_CFG_LOG_BLOCK_SZ ( fs ) ;
} // per block
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
if ( res = = SPIFFS_OK ) {
res = SPIFFS_ERR_NO_DELETED_BLOCKS ;
}
return res ;
2019-07-23 04:56:06 +08:00
}
// Checks if garbage collecting is necessary. If so a candidate block is found,
// cleansed and erased
s32_t spiffs_gc_check (
spiffs * fs ,
u32_t len ) {
2019-07-24 03:33:52 +08:00
s32_t res ;
s32_t free_pages =
( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) * ( fs - > block_count - 2 )
- fs - > stats_p_allocated - fs - > stats_p_deleted ;
int tries = 0 ;
if ( fs - > free_blocks > 3 & &
( s32_t ) len < free_pages * ( s32_t ) SPIFFS_DATA_PAGE_SIZE ( fs ) ) {
return SPIFFS_OK ;
}
u32_t needed_pages = ( len + SPIFFS_DATA_PAGE_SIZE ( fs ) - 1 ) / SPIFFS_DATA_PAGE_SIZE ( fs ) ;
2019-07-23 04:56:06 +08:00
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
// 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);
// return SPIFFS_ERR_FULL;
// }
2019-07-24 03:33:52 +08:00
if ( ( s32_t ) needed_pages > ( s32_t ) ( free_pages + fs - > stats_p_deleted ) ) {
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 ) ;
return SPIFFS_ERR_FULL ;
2019-07-23 04:56:06 +08:00
}
2019-07-24 03:33:52 +08:00
do {
SPIFFS_GC_DBG ( " \n gc_check # " _SPIPRIi " : run gc free_blocks: " _SPIPRIi " pfree: " _SPIPRIi " pallo: " _SPIPRIi " pdele: " _SPIPRIi " [ " _SPIPRIi " ] len: " _SPIPRIi " of " _SPIPRIi " \n " ,
tries ,
fs - > free_blocks , free_pages , fs - > stats_p_allocated , fs - > stats_p_deleted , ( free_pages + fs - > stats_p_allocated + fs - > stats_p_deleted ) ,
len , ( u32_t ) ( free_pages * SPIFFS_DATA_PAGE_SIZE ( fs ) ) ) ;
spiffs_block_ix * cands ;
int count ;
spiffs_block_ix cand ;
s32_t prev_free_pages = free_pages ;
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
res = spiffs_gc_find_candidate ( fs , & cands , & count , free_pages < = 0 ) ;
SPIFFS_CHECK_RES ( res ) ;
if ( count = = 0 ) {
SPIFFS_GC_DBGF ( " gc_check: no candidates, return \n " ) ;
return ( s32_t ) needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL ;
}
2019-07-23 04:56:06 +08:00
# if SPIFFS_GC_STATS
2019-07-24 03:33:52 +08:00
fs - > stats_gc_runs + + ;
2019-07-23 04:56:06 +08:00
# endif
2019-07-24 03:33:52 +08:00
cand = cands [ 0 ] ;
fs - > cleaning = 1 ;
//SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
res = spiffs_gc_clean ( fs , cand ) ;
fs - > cleaning = 0 ;
if ( res < 0 ) {
SPIFFS_GC_DBG ( " gc_check: cleaning block " _SPIPRIi " , result " _SPIPRIi " \n " , cand , res ) ;
} else {
SPIFFS_GC_DBG ( " gc_check: cleaning block " _SPIPRIi " , result " _SPIPRIi " \n " , cand , res ) ;
}
SPIFFS_CHECK_RES ( res ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
res = spiffs_gc_erase_page_stats ( fs , cand ) ;
SPIFFS_CHECK_RES ( res ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
res = spiffs_gc_erase_block ( fs , cand ) ;
SPIFFS_CHECK_RES ( res ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
free_pages =
( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) * ( fs - > block_count - 2 )
- fs - > stats_p_allocated - fs - > stats_p_deleted ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
if ( prev_free_pages < = 0 & & prev_free_pages = = free_pages ) {
// abort early to reduce wear, at least tried once
SPIFFS_GC_DBGF ( " gc_check: early abort, no result on gc when fs crammed \n " ) ;
break ;
}
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
} while ( + + tries < SPIFFS_GC_MAX_RUNS & & ( fs - > free_blocks < = 2 | |
( s32_t ) len > free_pages * ( s32_t ) SPIFFS_DATA_PAGE_SIZE ( fs ) ) ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
free_pages =
2019-07-23 04:56:06 +08:00
( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) * ( fs - > block_count - 2 )
- fs - > stats_p_allocated - fs - > stats_p_deleted ;
2019-07-24 03:33:52 +08:00
if ( ( s32_t ) len > free_pages * ( s32_t ) SPIFFS_DATA_PAGE_SIZE ( fs ) ) {
res = SPIFFS_ERR_FULL ;
}
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
SPIFFS_GC_DBG ( " gc_check: finished, " _SPIPRIi " dirty, blocks " _SPIPRIi " free, " _SPIPRIi " pages free, " _SPIPRIi " tries, res " _SPIPRIi " \n " ,
fs - > stats_p_allocated + fs - > stats_p_deleted ,
fs - > free_blocks , free_pages , tries , res ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
return res ;
2019-07-23 04:56:06 +08:00
}
// Updates page statistics for a block that is about to be erased
s32_t spiffs_gc_erase_page_stats (
spiffs * fs ,
spiffs_block_ix bix ) {
2019-07-24 03:33:52 +08:00
s32_t res = SPIFFS_OK ;
int obj_lookup_page = 0 ;
int entries_per_page = ( SPIFFS_CFG_LOG_PAGE_SZ ( fs ) / sizeof ( spiffs_obj_id ) ) ;
spiffs_obj_id * obj_lu_buf = ( spiffs_obj_id * ) fs - > lu_work ;
int cur_entry = 0 ;
u32_t dele = 0 ;
u32_t allo = 0 ;
// check each object lookup page
while ( res = = SPIFFS_OK & & obj_lookup_page < ( int ) SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) {
int entry_offset = obj_lookup_page * entries_per_page ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
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 ) ;
// check each entry
while ( res = = SPIFFS_OK & &
cur_entry - entry_offset < entries_per_page & & cur_entry < ( int ) ( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) ) {
spiffs_obj_id obj_id = obj_lu_buf [ cur_entry - entry_offset ] ;
if ( obj_id = = SPIFFS_OBJ_ID_FREE ) {
} else if ( obj_id = = SPIFFS_OBJ_ID_DELETED ) {
dele + + ;
} else {
allo + + ;
}
cur_entry + + ;
} // per entry
obj_lookup_page + + ;
} // per object lookup page
SPIFFS_GC_DBG ( " gc_check: wipe pallo: " _SPIPRIi " pdele: " _SPIPRIi " \n " , allo , dele ) ;
fs - > stats_p_allocated - = allo ;
fs - > stats_p_deleted - = dele ;
return res ;
2019-07-23 04:56:06 +08:00
}
// Finds block candidates to erase
s32_t spiffs_gc_find_candidate (
spiffs * fs ,
spiffs_block_ix * * block_candidates ,
int * candidate_count ,
char fs_crammed ) {
2019-07-24 03:33:52 +08:00
s32_t res = SPIFFS_OK ;
u32_t blocks = fs - > block_count ;
spiffs_block_ix cur_block = 0 ;
u32_t cur_block_addr = 0 ;
spiffs_obj_id * obj_lu_buf = ( spiffs_obj_id * ) fs - > lu_work ;
int cur_entry = 0 ;
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
int max_candidates = MIN ( fs - > block_count , ( SPIFFS_CFG_LOG_PAGE_SZ ( fs ) - 8 ) / ( sizeof ( spiffs_block_ix ) + sizeof ( s32_t ) ) ) ;
* candidate_count = 0 ;
memset ( fs - > work , 0xff , SPIFFS_CFG_LOG_PAGE_SZ ( fs ) ) ;
// divide up work area into block indices and scores
spiffs_block_ix * cand_blocks = ( spiffs_block_ix * ) fs - > work ;
s32_t * cand_scores = ( s32_t * ) ( fs - > work + max_candidates * sizeof ( spiffs_block_ix ) ) ;
// align cand_scores on s32_t boundary
cand_scores = ( s32_t * ) ( ( ( intptr_t ) cand_scores + sizeof ( intptr_t ) - 1 ) & ~ ( sizeof ( intptr_t ) - 1 ) ) ;
* block_candidates = cand_blocks ;
int entries_per_page = ( SPIFFS_CFG_LOG_PAGE_SZ ( fs ) / sizeof ( spiffs_obj_id ) ) ;
// check each block
while ( res = = SPIFFS_OK & & blocks - - ) {
u16_t deleted_pages_in_block = 0 ;
u16_t used_pages_in_block = 0 ;
int obj_lookup_page = 0 ;
// check each object lookup page
while ( res = = SPIFFS_OK & & obj_lookup_page < ( int ) SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) {
int entry_offset = obj_lookup_page * entries_per_page ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
0 , cur_block_addr + SPIFFS_PAGE_TO_PADDR ( fs , obj_lookup_page ) , SPIFFS_CFG_LOG_PAGE_SZ ( fs ) , fs - > lu_work ) ;
// check each entry
while ( res = = SPIFFS_OK & &
cur_entry - entry_offset < entries_per_page & &
cur_entry < ( int ) ( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) ) {
spiffs_obj_id obj_id = obj_lu_buf [ cur_entry - entry_offset ] ;
if ( obj_id = = SPIFFS_OBJ_ID_FREE ) {
// when a free entry is encountered, scan logic ensures that all following entries are free also
res = 1 ; // kill object lu loop
break ;
} else if ( obj_id = = SPIFFS_OBJ_ID_DELETED ) {
deleted_pages_in_block + + ;
} else {
used_pages_in_block + + ;
}
cur_entry + + ;
} // per entry
obj_lookup_page + + ;
} // per object lookup page
if ( res = = 1 ) res = SPIFFS_OK ;
// calculate score and insert into candidate table
// stoneage sort, but probably not so many blocks
if ( res = = SPIFFS_OK /*&& deleted_pages_in_block > 0*/ ) {
// read erase count
spiffs_obj_id erase_count ;
res = _spiffs_rd ( fs , SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2 , 0 ,
SPIFFS_ERASE_COUNT_PADDR ( fs , cur_block ) ,
sizeof ( spiffs_obj_id ) , ( u8_t * ) & erase_count ) ;
SPIFFS_CHECK_RES ( res ) ;
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
spiffs_obj_id erase_age ;
if ( fs - > max_erase_count > erase_count ) {
erase_age = fs - > max_erase_count - erase_count ;
} else {
erase_age = SPIFFS_OBJ_ID_FREE - ( erase_count - fs - > max_erase_count ) ;
}
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
s32_t score =
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
erase_age * ( fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE ) ;
int cand_ix = 0 ;
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 ) ;
while ( cand_ix < max_candidates ) {
if ( cand_blocks [ cand_ix ] = = ( spiffs_block_ix ) - 1 ) {
cand_blocks [ cand_ix ] = cur_block ;
cand_scores [ cand_ix ] = score ;
break ;
} else if ( cand_scores [ cand_ix ] < score ) {
int reorder_cand_ix = max_candidates - 2 ;
while ( reorder_cand_ix > = cand_ix ) {
cand_blocks [ reorder_cand_ix + 1 ] = cand_blocks [ reorder_cand_ix ] ;
cand_scores [ reorder_cand_ix + 1 ] = cand_scores [ reorder_cand_ix ] ;
reorder_cand_ix - - ;
}
cand_blocks [ cand_ix ] = cur_block ;
cand_scores [ cand_ix ] = score ;
break ;
}
cand_ix + + ;
}
( * candidate_count ) + + ;
2019-07-23 04:56:06 +08:00
}
2019-07-24 03:33:52 +08:00
cur_entry = 0 ;
cur_block + + ;
cur_block_addr + = SPIFFS_CFG_LOG_BLOCK_SZ ( fs ) ;
} // per block
2019-07-23 04:56:06 +08:00
2019-07-24 03:33:52 +08:00
return res ;
2019-07-23 04:56:06 +08:00
}
typedef enum {
2019-07-24 03:33:52 +08:00
FIND_OBJ_DATA ,
MOVE_OBJ_DATA ,
MOVE_OBJ_IX ,
FINISHED
2019-07-23 04:56:06 +08:00
} spiffs_gc_clean_state ;
typedef struct {
2019-07-24 03:33:52 +08:00
spiffs_gc_clean_state state ;
spiffs_obj_id cur_obj_id ;
spiffs_span_ix cur_objix_spix ;
spiffs_page_ix cur_objix_pix ;
spiffs_page_ix cur_data_pix ;
int stored_scan_entry_index ;
u8_t obj_id_found ;
2019-07-23 04:56:06 +08:00
} spiffs_gc ;
// Empties given block by moving all data into free pages of another block
// Strategy:
// loop:
// scan object lookup for object data pages
// for first found id, check spix and load corresponding object index page to memory
// push object scan lookup entry index
// rescan object lookup, find data pages with same id and referenced by same object index
// move data page, update object index in memory
// when reached end of lookup, store updated object index
// pop object scan lookup entry index
// repeat loop until end of object lookup
// scan object lookup again for remaining object index pages, move to new page in other block
//
s32_t spiffs_gc_clean ( spiffs * fs , spiffs_block_ix bix ) {
2019-07-24 03:33:52 +08:00
s32_t res = SPIFFS_OK ;
const int entries_per_page = ( SPIFFS_CFG_LOG_PAGE_SZ ( fs ) / sizeof ( spiffs_obj_id ) ) ;
// this is the global localizer being pushed and popped
int cur_entry = 0 ;
spiffs_obj_id * obj_lu_buf = ( spiffs_obj_id * ) fs - > lu_work ;
spiffs_gc gc ; // our stack frame/state
spiffs_page_ix cur_pix = 0 ;
spiffs_page_object_ix_header * objix_hdr = ( spiffs_page_object_ix_header * ) fs - > work ;
spiffs_page_object_ix * objix = ( spiffs_page_object_ix * ) fs - > work ;
SPIFFS_GC_DBG ( " gc_clean: cleaning block " _SPIPRIbl " \n " , bix ) ;
memset ( & gc , 0 , sizeof ( spiffs_gc ) ) ;
gc . state = FIND_OBJ_DATA ;
if ( fs - > free_cursor_block_ix = = bix ) {
// move free cursor to next block, cannot use free pages from the block we want to clean
fs - > free_cursor_block_ix = ( bix + 1 ) % fs - > block_count ;
fs - > free_cursor_obj_lu_entry = 0 ;
SPIFFS_GC_DBG ( " gc_clean: move free cursor to block " _SPIPRIbl " \n " , fs - > free_cursor_block_ix ) ;
}
while ( res = = SPIFFS_OK & & gc . state ! = FINISHED ) {
SPIFFS_GC_DBG ( " gc_clean: state = " _SPIPRIi " entry: " _SPIPRIi " \n " , gc . state , cur_entry ) ;
gc . obj_id_found = 0 ; // reset (to no found data page)
// scan through lookup pages
int obj_lookup_page = cur_entry / entries_per_page ;
u8_t scan = 1 ;
// check each object lookup page
while ( scan & & res = = SPIFFS_OK & & obj_lookup_page < ( int ) SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) {
int entry_offset = obj_lookup_page * entries_per_page ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
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 ) ;
// check each object lookup entry
while ( scan & & res = = SPIFFS_OK & &
cur_entry - entry_offset < entries_per_page & & cur_entry < ( int ) ( SPIFFS_PAGES_PER_BLOCK ( fs ) - SPIFFS_OBJ_LOOKUP_PAGES ( fs ) ) ) {
spiffs_obj_id obj_id = obj_lu_buf [ cur_entry - entry_offset ] ;
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX ( fs , bix , cur_entry ) ;
// act upon object id depending on gc state
switch ( gc . state ) {
case FIND_OBJ_DATA :
// find a data page
if ( obj_id ! = SPIFFS_OBJ_ID_DELETED & & obj_id ! = SPIFFS_OBJ_ID_FREE & &
( ( obj_id & SPIFFS_OBJ_ID_IX_FLAG ) = = 0 ) ) {
// found a data page, stop scanning and handle in switch case below
SPIFFS_GC_DBG ( " gc_clean: FIND_DATA state: " _SPIPRIi " - found obj id " _SPIPRIid " \n " , gc . state , obj_id ) ;
gc . obj_id_found = 1 ;
gc . cur_obj_id = obj_id ;
gc . cur_data_pix = cur_pix ;
scan = 0 ;
}
break ;
case MOVE_OBJ_DATA :
// evacuate found data pages for corresponding object index we have in memory,
// update memory representation
if ( obj_id = = gc . cur_obj_id ) {
spiffs_page_header p_hdr ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ ,
0 , SPIFFS_PAGE_TO_PADDR ( fs , cur_pix ) , sizeof ( spiffs_page_header ) , ( u8_t * ) & p_hdr ) ;
SPIFFS_CHECK_RES ( res ) ;
SPIFFS_GC_DBG ( " gc_clean: MOVE_DATA found data page " _SPIPRIid " : " _SPIPRIsp " @ " _SPIPRIpg " \n " , gc . cur_obj_id , p_hdr . span_ix , cur_pix ) ;
if ( SPIFFS_OBJ_IX_ENTRY_SPAN_IX ( fs , p_hdr . span_ix ) ! = gc . cur_objix_spix ) {
SPIFFS_GC_DBGF ( " gc_clean: MOVE_DATA no objix spix match, take in another run \n " ) ;
} else {
spiffs_page_ix new_data_pix ;
if ( p_hdr . flags & SPIFFS_PH_FLAG_DELET ) {
// move page
res = spiffs_page_move ( fs , 0 , 0 , obj_id , & p_hdr , cur_pix , & new_data_pix ) ;
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 ) ;
SPIFFS_CHECK_RES ( res ) ;
// move wipes obj_lu, reload it
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
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 ) ;
SPIFFS_CHECK_RES ( res ) ;
} else {
// page is deleted but not deleted in lookup, scrap it -
// might seem unnecessary as we will erase this block, but
// we might get aborted
SPIFFS_GC_DBG ( " gc_clean: MOVE_DATA wipe objix " _SPIPRIid " : " _SPIPRIsp " page " _SPIPRIpg " \n " , obj_id , p_hdr . span_ix , cur_pix ) ;
res = spiffs_page_delete ( fs , cur_pix ) ;
SPIFFS_CHECK_RES ( res ) ;
new_data_pix = SPIFFS_OBJ_ID_FREE ;
}
// update memory representation of object index page with new data page
if ( gc . cur_objix_spix = = 0 ) {
// update object index header page
( ( spiffs_page_ix * ) ( ( u8_t * ) objix_hdr + sizeof ( spiffs_page_object_ix_header ) ) ) [ p_hdr . span_ix ] = new_data_pix ;
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 ) ) ;
} else {
// update object index page
( ( spiffs_page_ix * ) ( ( u8_t * ) objix + sizeof ( spiffs_page_object_ix ) ) ) [ SPIFFS_OBJ_IX_ENTRY ( fs , p_hdr . span_ix ) ] = new_data_pix ;
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 ) ) ;
}
}
}
break ;
case MOVE_OBJ_IX :
// find and evacuate object index pages
if ( obj_id ! = SPIFFS_OBJ_ID_DELETED & & obj_id ! = SPIFFS_OBJ_ID_FREE & &
( obj_id & SPIFFS_OBJ_ID_IX_FLAG ) ) {
// found an index object id
spiffs_page_header p_hdr ;
spiffs_page_ix new_pix ;
// load header
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ ,
0 , SPIFFS_PAGE_TO_PADDR ( fs , cur_pix ) , sizeof ( spiffs_page_header ) , ( u8_t * ) & p_hdr ) ;
SPIFFS_CHECK_RES ( res ) ;
if ( p_hdr . flags & SPIFFS_PH_FLAG_DELET ) {
// move page
res = spiffs_page_move ( fs , 0 , 0 , obj_id , & p_hdr , cur_pix , & new_pix ) ;
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 ) ;
SPIFFS_CHECK_RES ( res ) ;
spiffs_cb_object_event ( fs , ( spiffs_page_object_ix * ) & p_hdr ,
SPIFFS_EV_IX_MOV , obj_id , p_hdr . span_ix , new_pix , 0 ) ;
// move wipes obj_lu, reload it
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ ,
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 ) ;
SPIFFS_CHECK_RES ( res ) ;
} else {
// page is deleted but not deleted in lookup, scrap it -
// might seem unnecessary as we will erase this block, but
// we might get aborted
SPIFFS_GC_DBG ( " gc_clean: MOVE_OBJIX wipe objix " _SPIPRIid " : " _SPIPRIsp " page " _SPIPRIpg " \n " , obj_id , p_hdr . span_ix , cur_pix ) ;
res = spiffs_page_delete ( fs , cur_pix ) ;
if ( res = = SPIFFS_OK ) {
spiffs_cb_object_event ( fs , ( spiffs_page_object_ix * ) 0 ,
SPIFFS_EV_IX_DEL , obj_id , p_hdr . span_ix , cur_pix , 0 ) ;
}
}
SPIFFS_CHECK_RES ( res ) ;
}
break ;
default :
scan = 0 ;
break ;
} // switch gc state
cur_entry + + ;
} // per entry
obj_lookup_page + + ; // no need to check scan variable here, obj_lookup_page is set in start of loop
} // per object lookup page
if ( res ! = SPIFFS_OK ) break ;
// state finalization and switch
2019-07-23 04:56:06 +08:00
switch ( gc . state ) {
2019-07-24 03:33:52 +08:00
case FIND_OBJ_DATA :
if ( gc . obj_id_found ) {
// handle found data page -
// find out corresponding obj ix page and load it to memory
spiffs_page_header p_hdr ;
spiffs_page_ix objix_pix ;
gc . stored_scan_entry_index = cur_entry ; // push cursor
cur_entry = 0 ; // restart scan from start
gc . state = MOVE_OBJ_DATA ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ ,
0 , SPIFFS_PAGE_TO_PADDR ( fs , cur_pix ) , sizeof ( spiffs_page_header ) , ( u8_t * ) & p_hdr ) ;
SPIFFS_CHECK_RES ( res ) ;
gc . cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX ( fs , p_hdr . span_ix ) ;
SPIFFS_GC_DBG ( " gc_clean: FIND_DATA find objix span_ix: " _SPIPRIsp " \n " , gc . cur_objix_spix ) ;
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 ) ;
if ( res = = SPIFFS_ERR_NOT_FOUND ) {
// on borked systems we might get an ERR_NOT_FOUND here -
// this is handled by simply deleting the page as it is not referenced
// from anywhere
SPIFFS_GC_DBG ( " gc_clean: FIND_OBJ_DATA objix not found! Wipe page " _SPIPRIpg " \n " , gc . cur_data_pix ) ;
res = spiffs_page_delete ( fs , gc . cur_data_pix ) ;
SPIFFS_CHECK_RES ( res ) ;
// then we restore states and continue scanning for data pages
cur_entry = gc . stored_scan_entry_index ; // pop cursor
gc . state = FIND_OBJ_DATA ;
break ; // done
}
SPIFFS_CHECK_RES ( res ) ;
SPIFFS_GC_DBG ( " gc_clean: FIND_DATA found object index at page " _SPIPRIpg " \n " , objix_pix ) ;
res = _spiffs_rd ( fs , SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ ,
0 , SPIFFS_PAGE_TO_PADDR ( fs , objix_pix ) , SPIFFS_CFG_LOG_PAGE_SZ ( fs ) , fs - > work ) ;
SPIFFS_CHECK_RES ( res ) ;
// cannot allow a gc if the presumed index in fact is no index, a
// check must run or lot of data may be lost
SPIFFS_VALIDATE_OBJIX ( objix - > p_hdr , gc . cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG , gc . cur_objix_spix ) ;
gc . cur_objix_pix = objix_pix ;
} else {
// no more data pages found, passed thru all block, start evacuating object indices
gc . state = MOVE_OBJ_IX ;
cur_entry = 0 ; // restart entry scan index
}
break ;
case MOVE_OBJ_DATA : {
// store modified objix (hdr) page residing in memory now that all
// data pages belonging to this object index and residing in the block
// we want to evacuate
spiffs_page_ix new_objix_pix ;
gc . state = FIND_OBJ_DATA ;
cur_entry = gc . stored_scan_entry_index ; // pop cursor
if ( gc . cur_objix_spix = = 0 ) {
// store object index header page
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 ) ;
SPIFFS_GC_DBG ( " gc_clean: MOVE_DATA store modified objix_hdr page, " _SPIPRIpg " : " _SPIPRIsp " \n " , new_objix_pix , 0 ) ;
SPIFFS_CHECK_RES ( res ) ;
} else {
// store object index page
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 ) ;
SPIFFS_GC_DBG ( " gc_clean: MOVE_DATA store modified objix page, " _SPIPRIpg " : " _SPIPRIsp " \n " , new_objix_pix , objix - > p_hdr . span_ix ) ;
SPIFFS_CHECK_RES ( res ) ;
spiffs_cb_object_event ( fs , ( spiffs_page_object_ix * ) fs - > work ,
SPIFFS_EV_IX_UPD , gc . cur_obj_id , objix - > p_hdr . span_ix , new_objix_pix , 0 ) ;
}
2019-07-23 04:56:06 +08:00
}
2019-07-24 03:33:52 +08:00
break ;
case MOVE_OBJ_IX :
// scanned thru all block, no more object indices found - our work here is done
gc . state = FINISHED ;
break ;
default :
cur_entry = 0 ;
break ;
} // switch gc.state
SPIFFS_GC_DBG ( " gc_clean: state-> " _SPIPRIi " \n " , gc . state ) ;
} // while state != FINISHED
return res ;
2019-07-23 04:56:06 +08:00
}
# endif // !SPIFFS_READ_ONLY