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:
parent
9d3bc5e936
commit
7989801966
|
@ -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:
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue