mirror of
https://github.com/pellepl/spiffs.git
synced 2026-09-06 09:10:00 +00:00
New feature - Controlled erase. #23
Added SPIFFS_gc_quick and SPIFFS_gc
This commit is contained in:
@@ -47,6 +47,7 @@ extern "C" {
|
||||
#define SPIFFS_ERR_ERASE_FAIL -10027
|
||||
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
|
||||
|
||||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
|
||||
|
||||
#define SPIFFS_ERR_INTERNAL -10050
|
||||
|
||||
@@ -440,6 +441,12 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
|
||||
*/
|
||||
s32_t SPIFFS_check(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Searches for a block with only deleted entries. If found, it is erased.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_erase_deleted_block(spiffs *fs);
|
||||
|
||||
|
||||
/**
|
||||
* Returns number of total bytes available and number of used bytes.
|
||||
@@ -465,14 +472,61 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
|
||||
* SPIFFS_format.
|
||||
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
|
||||
* SPIFFS_unmount first.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
s32_t SPIFFS_format(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Returns nonzero if spiffs is mounted, or zero if unmounted.
|
||||
* @param fs the file system struct
|
||||
*/
|
||||
u8_t SPIFFS_mounted(spiffs *fs);
|
||||
|
||||
/**
|
||||
* Tries to find a block where most or all pages are deleted, and erase that
|
||||
* block if found. Does not care for wear levelling. Will not move pages
|
||||
* around.
|
||||
* If parameter max_free_pages are set to 0, only blocks with only deleted
|
||||
* pages will be selected.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* Setting max_free_pages to anything larger than zero will eventually wear
|
||||
* flash more as a block containing free pages can be erased.
|
||||
*
|
||||
* Will set err_no to SPIFFS_OK if a block was found and erased,
|
||||
* SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
|
||||
* or other error.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param max_free_pages maximum number allowed free pages in block
|
||||
*/
|
||||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
/**
|
||||
* Will try to make room for given amount of bytes in the filesystem by moving
|
||||
* pages and erasing blocks.
|
||||
* If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
|
||||
* there already is this amount (or more) of free space, SPIFFS_gc will
|
||||
* silently return. It is recommended to call SPIFFS_info before invoking
|
||||
* this method in order to determine what amount of bytes to give.
|
||||
*
|
||||
* NB: the garbage collector is automatically called when spiffs needs free
|
||||
* pages. The reason for this function is to give possibility to do background
|
||||
* tidying when user knows the system is idle.
|
||||
*
|
||||
* Use with care.
|
||||
*
|
||||
* @param fs the file system struct
|
||||
* @param size amount of bytes that should be freed
|
||||
*/
|
||||
s32_t SPIFFS_gc(spiffs *fs, u32_t size);
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
/**
|
||||
* Prints out a visualization of the filesystem.
|
||||
|
||||
+15
-6
@@ -28,7 +28,7 @@ static s32_t spiffs_gc_erase_block(
|
||||
// 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) {
|
||||
spiffs *fs, u16_t max_free_pages) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
@@ -47,6 +47,7 @@ s32_t spiffs_gc_quick(
|
||||
// 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
|
||||
@@ -63,9 +64,12 @@ s32_t spiffs_gc_quick(
|
||||
deleted_pages_in_block++;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// kill scan, go for next block
|
||||
obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
|
||||
res = 1; // kill object lu loop
|
||||
break;
|
||||
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);
|
||||
@@ -78,7 +82,9 @@ s32_t spiffs_gc_quick(
|
||||
} // per object lookup page
|
||||
if (res == 1) res = SPIFFS_OK;
|
||||
|
||||
if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
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);
|
||||
@@ -90,10 +96,13 @@ s32_t spiffs_gc_quick(
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
res = SPIFFS_ERR_NO_DELETED_BLOCKS;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Checks if garbaga collecting is necessary. If so a candidate block is found,
|
||||
// Checks if garbage collecting is necessary. If so a candidate block is found,
|
||||
// cleansed and erased
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
|
||||
@@ -855,6 +855,34 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
|
||||
s32_t res;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
res = spiffs_gc_quick(fs, max_free_pages);
|
||||
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
|
||||
s32_t res;
|
||||
SPIFFS_API_CHECK_CFG(fs);
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
res = spiffs_gc_check(fs, size);
|
||||
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
SPIFFS_UNLOCK(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if SPIFFS_TEST_VISUALISATION
|
||||
s32_t SPIFFS_vis(spiffs *fs) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
|
||||
@@ -389,7 +389,10 @@ s32_t spiffs_obj_lu_find_free(
|
||||
int *lu_entry) {
|
||||
s32_t res;
|
||||
if (!fs->cleaning && fs->free_blocks < 2) {
|
||||
res = spiffs_gc_quick(fs);
|
||||
res = spiffs_gc_quick(fs, 0);
|
||||
if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (fs->free_blocks < 2) {
|
||||
return SPIFFS_ERR_FULL;
|
||||
|
||||
@@ -665,7 +665,7 @@ s32_t spiffs_gc_clean(
|
||||
spiffs_block_ix bix);
|
||||
|
||||
s32_t spiffs_gc_quick(
|
||||
spiffs *fs);
|
||||
spiffs *fs, u16_t max_free_pages);
|
||||
|
||||
// ---------------
|
||||
|
||||
|
||||
@@ -1048,6 +1048,73 @@ TEST(lseek_read) {
|
||||
TEST_END(lseek_read)
|
||||
|
||||
|
||||
TEST(gc_quick)
|
||||
{
|
||||
char name[32];
|
||||
int f;
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS);
|
||||
int files = (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))/2;
|
||||
int res;
|
||||
|
||||
// negative, try quick gc on clean sys
|
||||
res = SPIFFS_gc_quick(FS, 0);
|
||||
TEST_CHECK(res < 0);
|
||||
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS);
|
||||
|
||||
// fill block with files
|
||||
for (f = 0; f < files; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = test_create_and_write_file(name, size, 1);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
for (f = 0; f < files; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = read_and_verify(name);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
// remove all files in block
|
||||
for (f = 0; f < files; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = SPIFFS_remove(FS, name);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
|
||||
// do a quick gc
|
||||
res = SPIFFS_gc_quick(FS, 0);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// fill another block with files but two pages
|
||||
for (f = 0; f < files - 1; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = test_create_and_write_file(name, size, 1);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
for (f = 0; f < files - 1; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = read_and_verify(name);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
// remove all files in block leaving two free pages in block
|
||||
for (f = 0; f < files - 1; f++) {
|
||||
sprintf(name, "file%i", f);
|
||||
res = SPIFFS_remove(FS, name);
|
||||
TEST_CHECK(res >= 0);
|
||||
}
|
||||
|
||||
// negative, try quick gc where no fully deleted blocks exist
|
||||
res = SPIFFS_gc_quick(FS, 0);
|
||||
TEST_CHECK(res < 0);
|
||||
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS);
|
||||
|
||||
// positive, try quick gc where allowing two free pages
|
||||
res = SPIFFS_gc_quick(FS, 2);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
}
|
||||
TEST_END(gc_quick)
|
||||
|
||||
|
||||
TEST(write_small_file_chunks_1)
|
||||
{
|
||||
int res = test_create_and_write_file("smallfile", 256, 1);
|
||||
|
||||
@@ -372,6 +372,12 @@ void fs_reset() {
|
||||
fs_reset_specific(0, SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE);
|
||||
}
|
||||
|
||||
void fs_load_dump(char *fname) {
|
||||
int pfd = open(fname, O_RDONLY, S_IRUSR | S_IWUSR);
|
||||
read(pfd, area, sizeof(area));
|
||||
close(pfd);
|
||||
}
|
||||
|
||||
void set_flash_ops_log(int enable) {
|
||||
log_flash_ops = enable;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user