storage/stream: Add persistent write progress to stream_flash

Add additional API to stream_flash that can be used to make
stream write progress persistent using the settings subsystem.
This functionality makes it possible to resume a write operation
after it was interrupted, e.g. by power loss.

Signed-off-by: Jonathan Nilsen <Jonathan.Nilsen@nordicsemi.no>
This commit is contained in:
Jonathan Nilsen 2021-03-15 15:38:48 +01:00 committed by Maureen Helm
commit 0e6ac008a0
6 changed files with 376 additions and 1 deletions

View file

@ -17,6 +17,15 @@ config STREAM_FLASH_ERASE
If disabled an external actor must erase the flash area being written
to.
config STREAM_FLASH_PROGRESS
bool "Persistent stream write progress"
depends on SETTINGS
depends on !SETTINGS_NONE
help
Enable API for loading and storing the current write progress to flash
using the settings subsystem. In case of power failure or device
reset, the API can be used to resume writing from the latest state.
module = STREAM_FLASH
module-str = stream flash
source "subsys/logging/Kconfig.template.log_config"

View file

@ -16,6 +16,62 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_STREAM_FLASH_LOG_LEVEL);
#include <storage/stream_flash.h>
#ifdef CONFIG_STREAM_FLASH_PROGRESS
#include <settings/settings.h>
static int settings_direct_loader(const char *key, size_t len,
settings_read_cb read_cb, void *cb_arg,
void *param)
{
struct stream_flash_ctx *ctx = (struct stream_flash_ctx *) param;
/* Handle the subtree if it is an exact key match. */
if (settings_name_next(key, NULL) == 0) {
size_t bytes_written = 0;
ssize_t len = read_cb(cb_arg, &bytes_written,
sizeof(bytes_written));
if (len != sizeof(ctx->bytes_written)) {
LOG_ERR("Unable to read bytes_written from storage");
return len;
}
/* Check that loaded progress is not outdated. */
if (bytes_written >= ctx->bytes_written) {
ctx->bytes_written = bytes_written;
} else {
LOG_WRN("Loaded outdated bytes_written %zu < %zu",
bytes_written, ctx->bytes_written);
return 0;
}
#ifdef CONFIG_STREAM_FLASH_ERASE
int rc;
struct flash_pages_info page;
off_t offset = (off_t) (ctx->offset + ctx->bytes_written) - 1;
/* Update the last erased page to avoid deleting already
* written data.
*/
if (ctx->bytes_written > 0) {
rc = flash_get_page_info_by_offs(ctx->fdev, offset,
&page);
if (rc != 0) {
LOG_ERR("Error %d while getting page info", rc);
return rc;
}
ctx->last_erased_page_start_offset = page.start_offset;
} else {
ctx->last_erased_page_start_offset = -1;
}
#endif /* CONFIG_STREAM_FLASH_ERASE */
}
return 0;
}
#endif /* CONFIG_STREAM_FLASH_PROGRESS */
#ifdef CONFIG_STREAM_FLASH_ERASE
int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off)
@ -201,6 +257,15 @@ int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
return -EFAULT;
}
#ifdef CONFIG_STREAM_FLASH_PROGRESS
int rc = settings_subsys_init();
if (rc != 0) {
LOG_ERR("Error %d initializing settings subsystem", rc);
return rc;
}
#endif
struct _inspect_flash inspect_flash_ctx = {
.buf_len = buf_len,
.total_size = 0
@ -241,3 +306,62 @@ int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
return 0;
}
#ifdef CONFIG_STREAM_FLASH_PROGRESS
int stream_flash_progress_load(struct stream_flash_ctx *ctx,
const char *settings_key)
{
if (!ctx || !settings_key) {
return -EFAULT;
}
int rc = settings_load_subtree_direct(settings_key,
settings_direct_loader,
(void *) ctx);
if (rc != 0) {
LOG_ERR("Error %d while loading progress for \"%s\"",
rc, settings_key);
}
return rc;
}
int stream_flash_progress_save(struct stream_flash_ctx *ctx,
const char *settings_key)
{
if (!ctx || !settings_key) {
return -EFAULT;
}
int rc = settings_save_one(settings_key,
&ctx->bytes_written,
sizeof(ctx->bytes_written));
if (rc != 0) {
LOG_ERR("Error %d while storing progress for \"%s\"",
rc, settings_key);
}
return rc;
}
int stream_flash_progress_clear(struct stream_flash_ctx *ctx,
const char *settings_key)
{
if (!ctx || !settings_key) {
return -EFAULT;
}
int rc = settings_delete(settings_key);
if (rc != 0) {
LOG_ERR("Error %d while deleting progress for \"%s\"",
rc, settings_key);
}
return rc;
}
#endif /* CONFIG_STREAM_FLASH_PROGRESS */