diff --git a/subsys/logging/CMakeLists.txt b/subsys/logging/CMakeLists.txt index eb806e7825a2..f0672c673be4 100644 --- a/subsys/logging/CMakeLists.txt +++ b/subsys/logging/CMakeLists.txt @@ -9,4 +9,5 @@ if (CONFIG_LOG_FORWARDER_RPC OR CONFIG_LOG_BACKEND_RPC) zephyr_library_sources_ifdef(CONFIG_LOG_FORWARDER_RPC log_forwarder_rpc.c) zephyr_library_sources_ifdef(CONFIG_LOG_BACKEND_RPC log_backend_rpc.c) zephyr_library_sources_ifdef(CONFIG_LOG_BACKEND_RPC_HISTORY_STORAGE_RAM log_backend_rpc_history_ram.c) + zephyr_library_sources_ifdef(CONFIG_LOG_BACKEND_RPC_HISTORY_STORAGE_FCB log_backend_rpc_history_fcb.c) endif() diff --git a/subsys/logging/Kconfig b/subsys/logging/Kconfig index 122068179b36..9db406645450 100644 --- a/subsys/logging/Kconfig +++ b/subsys/logging/Kconfig @@ -53,6 +53,14 @@ choice LOG_BACKEND_RPC_HISTORY_STORAGE_CHOICE config LOG_BACKEND_RPC_HISTORY_STORAGE_RAM bool "RAM buffer" +config LOG_BACKEND_RPC_HISTORY_STORAGE_FCB + bool "Flash Circular Buffer" + help + Stores the log history in Flash Circular Buffer located in the "log_history" + flash partition. Note that enabling this option may significantly increase + the flash wear, so this option is not meant the production environments, + and extra caution should be taken when using this functionality. + endchoice # LOG_BACKEND_RPC_HISTORY_STORAGE_CHOICE config LOG_BACKEND_RPC_HISTORY_UPLOAD_THREAD_STACK_SIZE @@ -64,12 +72,17 @@ config LOG_BACKEND_RPC_HISTORY_UPLOAD_CHUNK_SIZE default 1024 config LOG_BACKEND_RPC_HISTORY_SIZE - int "Log history size" - depends on LOG_BACKEND_RPC_HISTORY_STORAGE_RAM - default 16384 + hex "Log history size" + default 0x4000 if LOG_BACKEND_RPC_HISTORY_STORAGE_RAM + default 0x8000 if LOG_BACKEND_RPC_HISTORY_STORAGE_FCB help Size of the ring buffer used to store the log history, in bytes. +config LOG_BACKEND_RPC_HISTORY_STORAGE_FCB_NUM_SECTORS + int "Log history FCB maximum number of flash sectors" + default 64 + depends on LOG_BACKEND_RPC_HISTORY_STORAGE_FCB + endif # LOG_BACKEND_RPC_HISTORY config LOG_BACKEND_RPC_CRASH_LOG diff --git a/subsys/logging/log_backend_rpc_history_fcb.c b/subsys/logging/log_backend_rpc_history_fcb.c new file mode 100644 index 000000000000..e008eb7283cb --- /dev/null +++ b/subsys/logging/log_backend_rpc_history_fcb.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "log_backend_rpc_history.h" + +#include +#include + +#define LOG_HISTORY_MAGIC 0x7d2ac863 +#define LOG_HISTORY_AREA FIXED_PARTITION_ID(log_history) + +static struct fcb fcb; +static struct flash_sector fcb_sectors[CONFIG_LOG_BACKEND_RPC_HISTORY_STORAGE_FCB_NUM_SECTORS]; +static struct fcb_entry last_popped; +static bool erase_oldest; +static K_MUTEX_DEFINE(fcb_lock); + +void log_rpc_history_init(void) +{ + int rc; + uint32_t sector_cnt = ARRAY_SIZE(fcb_sectors); + + rc = flash_area_get_sectors(LOG_HISTORY_AREA, §or_cnt, fcb_sectors); + + if (rc) { + goto out; + } + + fcb.f_magic = LOG_HISTORY_MAGIC; + fcb.f_sectors = fcb_sectors; + fcb.f_sector_cnt = (uint8_t)sector_cnt; + erase_oldest = true; + + rc = fcb_init(LOG_HISTORY_AREA, &fcb); + + if (rc) { + goto out; + } + + rc = fcb_clear(&fcb); + +out: + __ASSERT_NO_MSG(rc == 0); +} + +void log_rpc_history_push(const union log_msg_generic *msg) +{ + int rc; + size_t len; + struct fcb_entry entry; + + len = log_msg_generic_get_wlen(&msg->buf) * sizeof(uint32_t); + + k_mutex_lock(&fcb_lock, K_FOREVER); + rc = fcb_append(&fcb, len, &entry); + + if (rc == -ENOSPC && erase_oldest) { + /* + * The log history FCB is full but overwriting is enabled. + * Erase the oldest FCB page and try again. + * Note that the "last popped" location is cleared so that the next log transfer + * starts from the updated oldest log message. + */ + rc = fcb_rotate(&fcb); + + if (rc) { + goto out; + } + + memset(&last_popped, 0, sizeof(last_popped)); + rc = fcb_append(&fcb, len, &entry); + } + + if (rc) { + goto out; + } + + rc = flash_area_write(fcb.fap, FCB_ENTRY_FA_DATA_OFF(entry), msg, len); + + if (rc) { + goto out; + } + + rc = fcb_append_finish(&fcb, &entry); + +out: + k_mutex_unlock(&fcb_lock); + +#ifdef LOG_HISTORY_DEBUG + __ASSERT_NO_MSG(rc == 0); +#endif +} + +void log_rpc_history_set_overwriting(bool overwriting) +{ + k_mutex_lock(&fcb_lock, K_FOREVER); + + erase_oldest = overwriting; + + k_mutex_unlock(&fcb_lock); +} + +union log_msg_generic *log_rpc_history_pop(void) +{ + int rc; + struct fcb_entry entry = last_popped; + union log_msg_generic *msg = NULL; + + k_mutex_lock(&fcb_lock, K_FOREVER); + rc = fcb_getnext(&fcb, &entry); + + if (rc) { + rc = 0; + goto out; + } + + msg = (union log_msg_generic *)k_malloc(entry.fe_data_len); + + if (!msg) { + goto out; + } + + rc = flash_area_read(fcb.fap, FCB_ENTRY_FA_DATA_OFF(entry), msg, entry.fe_data_len); + + if (rc) { + goto out; + } + + last_popped = entry; + + while (fcb.f_oldest != last_popped.fe_sector) { + rc = fcb_rotate(&fcb); + + if (rc) { + goto out; + } + } + +out: + k_mutex_unlock(&fcb_lock); + +#ifdef LOG_HISTORY_DEBUG + __ASSERT_NO_MSG(rc == 0); +#endif + + return msg; +} + +void log_rpc_history_free(const union log_msg_generic *msg) +{ + k_free((void *)msg); +} diff --git a/subsys/partition_manager/CMakeLists.txt b/subsys/partition_manager/CMakeLists.txt index f712fac28e5f..3b5917debf6f 100644 --- a/subsys/partition_manager/CMakeLists.txt +++ b/subsys/partition_manager/CMakeLists.txt @@ -135,6 +135,10 @@ if(CONFIG_WIFI_NRF70 AND CONFIG_NRF_WIFI_PATCHES_EXT_FLASH_STORE) ncs_add_partition_manager_config(pm.yml.wifi) endif() +if(CONFIG_LOG_BACKEND_RPC_HISTORY_STORAGE_FCB) + ncs_add_partition_manager_config(pm.yml.log_history) +endif() + # We are using partition manager if we are a child image or if we are # the root image and the 'partition_manager' target exists. zephyr_compile_definitions( diff --git a/subsys/partition_manager/pm.yml.log_history b/subsys/partition_manager/pm.yml.log_history new file mode 100644 index 000000000000..7d76774dbed8 --- /dev/null +++ b/subsys/partition_manager/pm.yml.log_history @@ -0,0 +1,7 @@ +#include + +log_history: + size: CONFIG_LOG_BACKEND_RPC_HISTORY_SIZE + placement: + before: [end] + align: {start: CONFIG_FPROTECT_BLOCK_SIZE}