fs/nvs: Improve init speed and remove fs->locked

This patch removes the free space calculation from nvs initialization.
The available space can be calculated if required using the routine
nvs_calc_free_space.

This patch also removes the locked state of nvs, it is not possible to
get in a locked state.

Changes:

Removed locked state and free_space from the nvs structure.

nvs_reinit(): has been replaced with by an internal only function
_nvs_startup().

nvs_write(): removed the possibility to place the file system in a
locked state, if to many gc operations are required it will return
-ENOSPC.

ssize_t nvs_calc_free_space(): introduced, calculates the free space
that is available in the nvs file system.

Signed-off-by: Laczen JMS <laczenjms@gmail.com>
This commit is contained in:
Laczen JMS 2019-01-31 09:34:19 +01:00 committed by Anas Nashif
parent 9d3bc5e936
commit 7989801966
3 changed files with 101 additions and 123 deletions

View file

@ -34,8 +34,7 @@ is unchanged no write to flash is performed.
To protect the flash area against frequent erases it is important that there is
sufficient free space. NVS has a protection mechanism to avoid getting in a
endless loop of flash page erases when there is limited free space. When such
an endless loop is detected NVS is placed in a locked state and becomes a
read-only file system.
a loop is detected NVS returns that there is no more space available.
For NVS the file system is declared as:

View file

@ -12,6 +12,8 @@ extern "C" {
#endif
#include <sys/types.h>
#include <kernel.h>
#include <device.h>
/**
* @brief Non-volatile Storage
* @defgroup nvs Non-volatile Storage
@ -38,8 +40,6 @@ extern "C" {
* multiple of pagesize
* @param sector_count Amount of sectors in the file systems
* @param write_block_size Alignment size
* @param locked State of the filesystem, locked = true means the filesystem is
* read only
* @param nvs_lock Mutex
* @param flash_device Flash Device
*/
@ -47,18 +47,12 @@ struct nvs_fs {
off_t offset; /* filesystem offset in flash */
u32_t ate_wra; /* next alloc table entry write address */
u32_t data_wra; /* next data write address */
u32_t free_space; /* free space available in file system */
u16_t sector_size; /* filesystem is divided into sectors,
* sector size should be multiple of pagesize
*/
u16_t sector_count; /* amount of sectors in the filesystem */
u8_t write_block_size; /* write block size for alignment */
u8_t write_block_size; /* write block size for alignment */
bool locked; /* the filesystem is locked after an error occurred
* when it is locked it becomes a read-only filesystem
* it can be unlocked by calling nvs_init again, this
* will destroy all stored data
*/
struct k_mutex nvs_lock;
struct device *flash_device;
};
@ -86,18 +80,6 @@ struct nvs_fs {
*/
int nvs_init(struct nvs_fs *fs, const char *dev_name);
/**
* @brief nvs_reinit
*
* Reinitializes a NVS file system in flash, if the filesystem is locked this
* will erase the filesystem.
*
* @param fs Pointer to file system
* @retval 0 Success
* @retval -ERRNO errno code if error
*/
int nvs_reinit(struct nvs_fs *fs);
/**
* @brief nvs_clear
*
@ -172,6 +154,20 @@ ssize_t nvs_read(struct nvs_fs *fs, u16_t id, void *data, size_t len);
ssize_t nvs_read_hist(struct nvs_fs *fs, u16_t id, void *data, size_t len,
u16_t cnt);
/**
* @brief nvs_calc_free_space
*
* Calculate the available free space in the file system.
*
* @param fs Pointer to file system
*
* @return Number of bytes free. On success, it will be equal to the number
* of bytes that can still be written to the file system. Calculating the
* free space is a time consuming operation, especially on spi flash.
* On error returns -ERRNO code.
*/
ssize_t nvs_calc_free_space(struct nvs_fs *fs);
/**
* @}
*/

View file

@ -314,10 +314,6 @@ static int _nvs_flash_wrt_entry(struct nvs_fs *fs, u16_t id, const void *data,
return rc;
}
if (len != 0) {
fs->free_space -= _nvs_al_size(fs, len);
fs->free_space -= ate_size;
}
return 0;
}
/* end of flash routines */
@ -532,73 +528,7 @@ static int _nvs_gc(struct nvs_fs *fs)
return 0;
}
static int _nvs_update_free_space(struct nvs_fs *fs)
{
int rc;
struct nvs_ate step_ate, wlk_ate;
u32_t step_addr, wlk_addr;
size_t ate_size;
ate_size = _nvs_al_size(fs, sizeof(struct nvs_ate));
fs->free_space = 0;
for (u16_t i = 1; i < fs->sector_count; i++) {
fs->free_space += (fs->sector_size - ate_size);
}
step_addr = fs->ate_wra;
while (1) {
rc = _nvs_prev_ate(fs, &step_addr, &step_ate);
if (rc) {
return rc;
}
wlk_addr = fs->ate_wra;
while (1) {
rc = _nvs_prev_ate(fs, &wlk_addr, &wlk_ate);
if (rc) {
return rc;
}
if ((wlk_ate.id == step_ate.id) ||
(wlk_addr == fs->ate_wra)) {
break;
}
}
if ((wlk_addr == step_addr) && step_ate.len &&
(!_nvs_ate_crc8_check(&step_ate))) {
/* count needed */
fs->free_space -= _nvs_al_size(fs, step_ate.len);
fs->free_space -= ate_size;
}
if (step_addr == fs->ate_wra) {
break;
}
}
return 0;
}
int nvs_clear(struct nvs_fs *fs)
{
int rc;
off_t addr;
for (u16_t i = 0; i < fs->sector_count; i++) {
addr = i << ADDR_SECT_SHIFT;
rc = _nvs_flash_erase_sector(fs, addr);
if (rc) {
return rc;
}
}
return 0;
}
int nvs_reinit(struct nvs_fs *fs)
static int _nvs_startup(struct nvs_fs *fs)
{
int rc;
struct nvs_ate last_ate;
@ -607,7 +537,7 @@ int nvs_reinit(struct nvs_fs *fs)
* should never happen as this is verified in nvs_init() but both
* Coverity and GCC believe the contrary.
*/
u32_t addr = 0;
u32_t addr = 0U;
k_mutex_lock(&fs->nvs_lock, K_FOREVER);
@ -705,12 +635,26 @@ int nvs_reinit(struct nvs_fs *fs)
}
}
rc = _nvs_update_free_space(fs);
end:
k_mutex_unlock(&fs->nvs_lock);
return rc;
}
int nvs_clear(struct nvs_fs *fs)
{
int rc;
off_t addr;
for (u16_t i = 0; i < fs->sector_count; i++) {
addr = i << ADDR_SECT_SHIFT;
rc = _nvs_flash_erase_sector(fs, addr);
if (rc) {
return rc;
}
}
return 0;
}
int nvs_init(struct nvs_fs *fs, const char *dev_name)
{
@ -738,8 +682,7 @@ int nvs_init(struct nvs_fs *fs, const char *dev_name)
return -EINVAL;
}
fs->locked = false;
rc = nvs_reinit(fs);
rc = _nvs_startup(fs);
if (rc) {
return rc;
}
@ -751,7 +694,6 @@ int nvs_init(struct nvs_fs *fs, const char *dev_name)
LOG_INF("data wra: %d, %x",
(fs->data_wra >> ADDR_SECT_SHIFT),
(fs->data_wra & ADDR_OFFS_MASK));
LOG_INF("Free space: %d", fs->free_space);
return 0;
}
@ -761,26 +703,24 @@ ssize_t nvs_write(struct nvs_fs *fs, u16_t id, const void *data, size_t len)
int rc, gc_count;
size_t ate_size, data_size;
struct nvs_ate wlk_ate;
u32_t wlk_addr, rd_addr, freed_space;
u32_t wlk_addr, rd_addr;
u16_t sector_freespace;
ate_size = _nvs_al_size(fs, sizeof(struct nvs_ate));
data_size = _nvs_al_size(fs, len);
if ((len > (fs->sector_size - 2 * ate_size)) ||
/* The maximum data size is sector size - 3 ate
* where: 1 ate for data, 1 ate for sector close
* and 1 ate to always allow a delete.
*/
if ((len > (fs->sector_size - 3 * ate_size)) ||
((len > 0) && (data == NULL))) {
return -EINVAL;
}
if (fs->locked) {
return -EROFS;
}
/* find latest entry with same id */
wlk_addr = fs->ate_wra;
rd_addr = wlk_addr;
freed_space = 0U;
while (1) {
rd_addr = wlk_addr;
@ -813,27 +753,25 @@ ssize_t nvs_write(struct nvs_fs *fs, u16_t id, const void *data, size_t len)
return rc;
}
}
/* data different, calculate freed space */
freed_space = _nvs_al_size(fs, wlk_ate.len);
freed_space += ate_size;
}
k_mutex_lock(&fs->nvs_lock, K_FOREVER);
fs->free_space += freed_space;
if (fs->free_space < (data_size + ate_size)) {
rc = -ENOSPC;
goto end;
}
gc_count = 0;
while (1) {
if (gc_count == fs->sector_count) {
rc = -EROFS;
/* gc'ed all sectors, no extra space will be created
* by extra gc.
*/
rc = -ENOSPC;
goto end;
}
sector_freespace = fs->ate_wra - fs->data_wra;
/* Leave space for delete ate */
if (sector_freespace >= data_size + ate_size) {
rc = _nvs_flash_wrt_entry(fs, id, data, len);
if (rc) {
goto end;
@ -855,12 +793,6 @@ ssize_t nvs_write(struct nvs_fs *fs, u16_t id, const void *data, size_t len)
rc = len;
end:
k_mutex_unlock(&fs->nvs_lock);
if (rc < 0) {
fs->free_space -= freed_space;
}
if (rc == -EROFS) {
fs->locked = true;
}
return rc;
}
@ -928,3 +860,54 @@ ssize_t nvs_read(struct nvs_fs *fs, u16_t id, void *data, size_t len)
rc = nvs_read_hist(fs, id, data, len, 0);
return rc;
}
ssize_t nvs_calc_free_space(struct nvs_fs *fs)
{
int rc;
struct nvs_ate step_ate, wlk_ate;
u32_t step_addr, wlk_addr;
size_t ate_size, free_space;
ate_size = _nvs_al_size(fs, sizeof(struct nvs_ate));
free_space = 0;
for (u16_t i = 1; i < fs->sector_count; i++) {
free_space += (fs->sector_size - ate_size);
}
step_addr = fs->ate_wra;
while (1) {
rc = _nvs_prev_ate(fs, &step_addr, &step_ate);
if (rc) {
return rc;
}
wlk_addr = fs->ate_wra;
while (1) {
rc = _nvs_prev_ate(fs, &wlk_addr, &wlk_ate);
if (rc) {
return rc;
}
if ((wlk_ate.id == step_ate.id) ||
(wlk_addr == fs->ate_wra)) {
break;
}
}
if ((wlk_addr == step_addr) && step_ate.len &&
(!_nvs_ate_crc8_check(&step_ate))) {
/* count needed */
free_space -= _nvs_al_size(fs, step_ate.len);
free_space -= ate_size;
}
if (step_addr == fs->ate_wra) {
break;
}
}
return free_space;
}