spiffs: add esp_spiffs_gc function to force garbage collection

Closes https://github.com/espressif/esp-idf/issues/8626
This commit is contained in:
Ivan Grokhotkov 2022-03-22 17:50:01 +01:00
parent 199d72c19c
commit 7c65370d84
No known key found for this signature in database
GPG Key ID: 1E050E141B280628
5 changed files with 135 additions and 1 deletions

View File

@ -42,7 +42,7 @@ menu "SPIFFS Configuration"
config SPIFFS_GC_MAX_RUNS
int "Set Maximum GC Runs"
default 10
range 1 255
range 1 10000
help
Define maximum number of GC runs to perform to reach desired free pages.

View File

@ -378,6 +378,24 @@ esp_err_t esp_spiffs_format(const char* partition_label)
return ESP_OK;
}
esp_err_t esp_spiffs_gc(const char* partition_label, size_t size_to_gc)
{
int index;
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
return ESP_ERR_INVALID_STATE;
}
int res = SPIFFS_gc(_efs[index]->fs, size_to_gc);
if (res != SPIFFS_OK) {
ESP_LOGE(TAG, "SPIFFS_gc failed, %d", res);
SPIFFS_clearerr(_efs[index]->fs);
if (res == SPIFFS_ERR_FULL) {
return ESP_ERR_NOT_FINISHED;
}
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
{
assert(conf->base_path);

View File

@ -95,6 +95,35 @@ esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size
*/
esp_err_t esp_spiffs_check(const char* partition_label);
/**
* @brief Perform garbage collection in SPIFFS partition
*
* Call this function to run GC and ensure that at least the given amount of
* space is available in the partition. This function will fail with ESP_ERR_NOT_FINISHED
* if it is not possible to reclaim the requested space (that is, not enough free
* or deleted pages in the filesystem). This function will also fail if it fails to
* reclaim the requested space after CONFIG_SPIFFS_GC_MAX_RUNS number of GC iterations.
* On one GC iteration, SPIFFS will erase one logical block (4kB). Therefore the value
* of CONFIG_SPIFFS_GC_MAX_RUNS should be set at least to the maximum expected size_to_gc,
* divided by 4096. For example, if the application expects to make room for a 1MB file and
* calls esp_spiffs_gc(label, 1024 * 1024), CONFIG_SPIFFS_GC_MAX_RUNS should be set to
* at least 256.
* On the other hand, increasing CONFIG_SPIFFS_GC_MAX_RUNS value increases the maximum
* amount of time for which any SPIFFS GC or write operation may potentially block.
*
* @param partition_label Label of the partition to be garbage-collected.
* The partition must be already mounted.
* @param size_to_gc The number of bytes that the GC process should attempt
* to make available.
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_FINISHED if GC fails to reclaim the size given by size_to_gc
* - ESP_ERR_INVALID_STATE if the partition is not mounted
* - ESP_FAIL on all other errors
*/
esp_err_t esp_spiffs_gc(const char* partition_label, size_t size_to_gc);
#ifdef __cplusplus
}
#endif

View File

@ -22,6 +22,7 @@
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_partition.h"
#include "esp_random.h"
#include "esp_rom_sys.h"
const char* spiffs_test_hello_str = "Hello, World!\n";
@ -828,3 +829,85 @@ TEST_CASE("utime() works well", "[spiffs]")
test_teardown();
}
#endif // CONFIG_SPIFFS_USE_MTIME
static void test_spiffs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool is_write)
{
const size_t buf_count = file_size / buf_size;
FILE* f = fopen(filename, (is_write) ? "wb" : "rb");
TEST_ASSERT_NOT_NULL(f);
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
for (size_t n = 0; n < buf_count; ++n) {
if (is_write) {
TEST_ASSERT_EQUAL(buf_size, write(fileno(f), buf, buf_size));
} else {
if (read(fileno(f), buf, buf_size) != buf_size) {
printf("reading at n=%d, eof=%d", n, feof(f));
TEST_FAIL();
}
}
}
struct timeval tv_end;
gettimeofday(&tv_end, NULL);
TEST_ASSERT_EQUAL(0, fclose(f));
float t_s = tv_end.tv_sec - tv_start.tv_sec + 1e-6f * (tv_end.tv_usec - tv_start.tv_usec);
printf("%s %d bytes (block size %d) in %.3fms (%.3f MB/s)\n",
(is_write)?"Wrote":"Read", file_size, buf_size, t_s * 1e3,
file_size / (1024.0f * 1024.0f * t_s));
}
TEST_CASE("write/read speed test", "[spiffs][timeout=60]")
{
/* Erase partition before running the test to get consistent results */
const esp_partition_t* part = get_test_data_partition();
esp_partition_erase_range(part, 0, part->size);
test_setup();
const size_t buf_size = 16 * 1024;
uint32_t* buf = (uint32_t*) calloc(1, buf_size);
esp_fill_random(buf, buf_size);
const size_t file_size = 256 * 1024;
const char* file = "/spiffs/256k.bin";
test_spiffs_rw_speed(file, buf, 4 * 1024, file_size, true);
TEST_ASSERT_EQUAL(0, unlink(file));
TEST_ESP_OK(esp_spiffs_gc(spiffs_test_partition_label, file_size));
test_spiffs_rw_speed(file, buf, 8 * 1024, file_size, true);
TEST_ASSERT_EQUAL(0, unlink(file));
TEST_ESP_OK(esp_spiffs_gc(spiffs_test_partition_label, file_size));
test_spiffs_rw_speed(file, buf, 16 * 1024, file_size, true);
test_spiffs_rw_speed(file, buf, 4 * 1024, file_size, false);
test_spiffs_rw_speed(file, buf, 8 * 1024, file_size, false);
test_spiffs_rw_speed(file, buf, 16 * 1024, file_size, false);
TEST_ASSERT_EQUAL(0, unlink(file));
TEST_ESP_OK(esp_spiffs_gc(spiffs_test_partition_label, file_size));
free(buf);
test_teardown();
}
TEST_CASE("SPIFFS garbage-collect", "[spiffs][timeout=60]")
{
// should fail until the partition is initialized
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_spiffs_gc(spiffs_test_partition_label, 4096));
test_setup();
// reclaiming one block should be possible
TEST_ESP_OK(esp_spiffs_gc(spiffs_test_partition_label, 4096));
// shouldn't be possible to reclaim more than the partition size
const esp_partition_t* part = get_test_data_partition();
TEST_ESP_ERR(ESP_ERR_NOT_FINISHED, esp_spiffs_gc(spiffs_test_partition_label, part->size * 2));
test_teardown();
}

View File

@ -26,3 +26,7 @@ CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000
CONFIG_MQTT_TEST_BROKER_URI="mqtt://${EXAMPLE_MQTT_BROKER_TCP}"
# Set sufficient number of GC runs for SPIFFS:
# size of the storage partition divided by flash sector size.
# See esp_spiffs_gc description for more info.
CONFIG_SPIFFS_GC_MAX_RUNS=132