in fs/spiffs/src/spiffs_gc.c [449:970]
static int spiffs_gc_clean(FAR struct spiffs_s *fs, int16_t blkndx)
{
const int entries_per_page = (SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t));
int ret = OK;
/* This is the global localizer being pushed and popped */
struct spiffs_gc_s gc; /* Our stack frame/state */
FAR int16_t *objlu_buf;
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
int16_t cur_pgndx = 0;
int cur_entry = 0;
spiffs_gcinfo("Cleaning block %04x\n", blkndx);
memset(&gc, 0, sizeof(struct spiffs_gc_s));
gc.state = FIND_OBJ_DATA;
objlu_buf = (FAR int16_t *)fs->lu_work;
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (struct spiffs_page_objndx_s *)fs->work;
if (fs->free_blkndx == blkndx)
{
/* Move free cursor to next block, cannot use free pages from the block
* we want to clean
*/
fs->free_blkndx = (blkndx + 1) % SPIFFS_GEO_BLOCK_COUNT(fs);
fs->free_entry = 0;
spiffs_gcinfo("Move free cursor to block=%04x\n", fs->free_blkndx);
}
while (ret >= 0 && gc.state != FINISHED)
{
int obj_lookup_page;
uint8_t scan;
spiffs_gcinfo("state=%d entry=%d\n", gc.state, cur_entry);
gc.objid_found = false; /* Reset (to no found data page) */
/* Scan through lookup pages */
obj_lookup_page = cur_entry / entries_per_page;
scan = 1;
/* Check each object lookup page */
while (scan && ret >= 0 &&
obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
{
int entry_offset = obj_lookup_page * entries_per_page;
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work);
/* Check each object lookup entry */
while (scan && ret >= 0 &&
cur_entry - entry_offset < entries_per_page &&
cur_entry <
(int)(SPIFFS_GEO_PAGES_PER_BLOCK(fs) -
SPIFFS_OBJ_LOOKUP_PAGES(fs)))
{
int16_t id = objlu_buf[cur_entry - entry_offset];
cur_pgndx =
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, cur_entry);
/* Act upon object id depending on gc state */
switch (gc.state)
{
case FIND_OBJ_DATA:
/* Find a data page. */
if (id != SPIFFS_OBJID_DELETED &&
id != SPIFFS_OBJID_FREE &&
((id & SPIFFS_OBJID_NDXFLAG) == 0))
{
/* Found a data page, stop scanning and handle in
* switch case below
*/
spiffs_gcinfo(
"Found data page, state=%d, objid=%04x\n",
gc.state, id);
gc.objid_found = true;
gc.cur_objid = id;
gc.cur_data_pgndx = cur_pgndx;
scan = 0;
}
break;
case MOVE_OBJ_DATA:
/* Evacuate found data pages for corresponding object index
* we have in memory, update memory representation
*/
if (id == gc.cur_objid)
{
struct spiffs_page_header_s phdr;
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
sizeof(struct spiffs_page_header_s),
(FAR uint8_t *)&phdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n",
ret);
return ret;
}
spiffs_gcinfo("Found data page %04x:%04x @%04x\n",
gc.cur_objid, phdr.spndx, cur_pgndx);
if (SPIFFS_OBJNDX_ENTRY_SPNDX(fs, phdr.spndx) !=
gc.cur_objndx_spndx)
{
spiffs_gcinfo(
"No objndx spndx match, take in another run\n");
}
else
{
int16_t new_data_pgndx;
if (phdr.flags & SPIFFS_PH_FLAG_DELET)
{
/* Move page */
ret = spiffs_page_move(fs, 0, 0, id, &phdr,
cur_pgndx, &new_data_pgndx);
spiffs_gcinfo(
"Move objndx=%04x:%04x page=%04x to %04x\n",
gc.cur_objid, phdr.spndx,
cur_pgndx, new_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move(): %d\n",
ret);
return ret;
}
/* Move wipes obj_lu, reload it */
ret =
spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0,
blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->lu_work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read(): %d\n",
ret);
return ret;
}
}
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_gcinfo(
"Wipe objndx=%04x:%04x page=%04x\n",
id, phdr.spndx, cur_pgndx);
ret = spiffs_page_delete(fs, cur_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete(): %d\n",
ret);
return ret;
}
new_data_pgndx = SPIFFS_OBJID_FREE;
}
/* Update memory representation of object index
* page with new data page
*/
if (gc.cur_objndx_spndx == 0)
{
/* Update object index header page */
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))
[phdr.spndx] = new_data_pgndx;
spiffs_gcinfo(
"Wrote page=%04x to objhdr entry=%04x\n",
new_data_pgndx,
(int)SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx));
}
else
{
/* Update object index page */
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx)] =
new_data_pgndx;
spiffs_gcinfo(
"Wrote page=%04x to objndx entry=%04x\n",
new_data_pgndx,
(int)SPIFFS_OBJNDX_ENTRY(fs, phdr.spndx));
}
}
}
break;
case MOVE_OBJ_NDX:
/* Find and evacuate object index pages */
if (id != SPIFFS_OBJID_DELETED &&
id != SPIFFS_OBJID_FREE &&
(id & SPIFFS_OBJID_NDXFLAG) != 0)
{
/* Found an index object id */
struct spiffs_page_header_s phdr;
int16_t new_pgndx;
/* Load header */
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0,
SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
sizeof(struct spiffs_page_header_s),
(FAR uint8_t *)&phdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n",
ret);
return ret;
}
if (phdr.flags & SPIFFS_PH_FLAG_DELET)
{
/* Move page */
ret = spiffs_page_move(fs, 0, 0, id, &phdr,
cur_pgndx, &new_pgndx);
spiffs_gcinfo(
"Move objndx=%04x:%04x page=%04x to %04x\n",
id, phdr.spndx, cur_pgndx, new_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n",
ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)&phdr,
SPIFFS_EV_NDXMOV, id,
phdr.spndx, new_pgndx, 0);
/* Move wipes obj_lu, reload it */
ret =
spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0,
blkndx * SPIFFS_GEO_BLOCK_SIZE(fs) +
SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n",
ret);
return ret;
}
}
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_gcinfo("Wipe objndx=%04x:%04x page=%04x\n",
id, phdr.spndx, cur_pgndx);
ret = spiffs_page_delete(fs, cur_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_event() failed: %d\n",
ret);
return ret;
}
spiffs_fobj_event(fs, NULL,
SPIFFS_EV_NDXDEL, id,
phdr.spndx, cur_pgndx, 0);
}
}
break;
default:
scan = 0;
break;
}
cur_entry++;
}
obj_lookup_page++; /* No need to check scan variable here,
* obj_lookup_page is set in start of loop
*/
}
if (ret < 0)
{
break;
}
/* State finalization and switch */
switch (gc.state)
{
case FIND_OBJ_DATA:
if (gc.objid_found)
{
struct spiffs_page_header_s phdr;
int16_t objndx_pgndx;
/* Handle found data page. Find out corresponding objndx page
* and load it to memory
*/
gc.stored_scan_entry_index = cur_entry; /* Push cursor */
cur_entry = 0; /* Sestart scan from start */
gc.state = MOVE_OBJ_DATA;
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx),
sizeof(struct spiffs_page_header_s),
(FAR uint8_t *)&phdr);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_event() failed: %d\n", ret);
return ret;
}
gc.cur_objndx_spndx =
SPIFFS_OBJNDX_ENTRY_SPNDX(fs, phdr.spndx);
spiffs_gcinfo("Find objndx spndx=%04x\n", gc.cur_objndx_spndx);
ret = spiffs_objlu_find_id_and_span(fs,
gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
gc.cur_objndx_spndx, 0,
&objndx_pgndx);
if (ret == -ENOENT)
{
/* 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_gcinfo("objndx not found! Wipe page %04x\n",
gc.cur_data_pgndx);
ret = spiffs_page_delete(fs, gc.cur_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
return ret;
}
/* 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;
}
if (ret < 0)
{
return ret;
}
spiffs_gcinfo("Found object index at page=%04x\n",
objndx_pgndx);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0,
SPIFFS_PAGE_TO_PADDR(fs, objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
/* Cannot allow a gc if the presumed index in fact is no
* index, a check must run or lot of data may be lost
*/
ret = spiffs_validate_objndx(&objndx->phdr,
gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
gc.cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
gc.cur_objndx_pgndx = objndx_pgndx;
}
else
{
/* No more data pages found, passed through all block, start
* evacuating object indices
*/
gc.state = MOVE_OBJ_NDX;
cur_entry = 0; /* Restart entry scan index */
}
break;
case MOVE_OBJ_DATA:
{
int16_t new_objndx_pgndx;
/* Store modified objndx (hdr) page residing in memory now that
* all data pages belonging to this object index and residing in
* the block we want to evacuate
*/
gc.state = FIND_OBJ_DATA;
cur_entry = gc.stored_scan_entry_index; /* pop cursor */
if (gc.cur_objndx_spndx == 0)
{
/* Store object index header page */
ret = spiffs_fobj_update_ndxhdr(fs, 0,
gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
gc.cur_objndx_pgndx, fs->work, 0,
0, &new_objndx_pgndx);
spiffs_gcinfo("Store modified objhdr page=%04x:%04x\n",
new_objndx_pgndx, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
else
{
/* Store object index page */
ret = spiffs_page_move(fs, 0, fs->work,
gc.cur_objid | SPIFFS_OBJID_NDXFLAG,
0, gc.cur_objndx_pgndx,
&new_objndx_pgndx);
spiffs_gcinfo("Store modified objndx page=%04x:%04x\n",
new_objndx_pgndx, objndx->phdr.spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)fs->work,
SPIFFS_EV_NDXUPD, gc.cur_objid,
objndx->phdr.spndx,
new_objndx_pgndx, 0);
}
}
break;
case MOVE_OBJ_NDX:
/* Scanned through all blocks, no more object indices found - our
* work here is done
*/
gc.state = FINISHED;
break;
default:
cur_entry = 0;
break;
}
spiffs_gcinfo("state=%d\n", gc.state);
}
return ret;
}