diff --git a/doc/reference/logging/index.rst b/doc/reference/logging/index.rst index 1fa655a30cc..cd3c0f6fafc 100644 --- a/doc/reference/logging/index.rst +++ b/doc/reference/logging/index.rst @@ -97,6 +97,11 @@ oldest one are discarded. :option:`CONFIG_LOG_MODE_NO_OVERFLOW`: When logger cannot allocate new message it is discarded. +:option:`CONFIG_LOG_BLOCK_IN_THREAD`: If enabled and new log message cannot +be allocated thread context will block for up to +:option:`CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS` or until log message is +allocated. + :option:`CONFIG_LOG_DEFAULT_LEVEL`: Default level, sets the logging level used by modules that are not setting their own logging level. diff --git a/subsys/logging/Kconfig b/subsys/logging/Kconfig index 530b9cb9ddf..ccd6b29c878 100644 --- a/subsys/logging/Kconfig +++ b/subsys/logging/Kconfig @@ -132,6 +132,26 @@ config LOG_MODE_NO_OVERFLOW endchoice +config LOG_BLOCK_IN_THREAD + bool "On log full block in thread context" + help + When enabled logger will block (if in the thread context) when + internal logger buffer is full and new message cannot be allocated. + +if LOG_BLOCK_IN_THREAD + +config LOG_BLOCK_IN_THREAD_TIMEOUT_MS + int "Maximum time (in milliseconds) thread can be blocked" + default 1000 + range -1 10000 + help + If new buffer for a log message cannot be allocated in that time, log + message is dropped. Forever blocking (-1) is possible however may lead + to the logger deadlock if logging is enabled in threads used for + logging (e.g. logger or shell thread). + +endif # LOG_BLOCK_IN_THREAD + config LOG_PROCESS_TRIGGER_THRESHOLD int "Amount of buffered logs which triggers processing thread." default 10 diff --git a/subsys/logging/log_msg.c b/subsys/logging/log_msg.c index ad3942fcc5f..541a976f544 100644 --- a/subsys/logging/log_msg.c +++ b/subsys/logging/log_msg.c @@ -31,6 +31,12 @@ BUILD_ASSERT_MSG((sizeof(union log_msg_head_data) == #define CONFIG_LOG_BUFFER_SIZE 0 #endif +/* Define needed when CONFIG_LOG_BLOCK_IN_THREAD is disabled to satisfy + * compiler. */ +#ifndef CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS +#define CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS 0 +#endif + #define MSG_SIZE sizeof(union log_msg_chunk) #define NUM_OF_MSGS (CONFIG_LOG_BUFFER_SIZE / MSG_SIZE) @@ -43,10 +49,34 @@ void log_msg_pool_init(void) k_mem_slab_init(&log_msg_pool, log_msg_pool_buf, MSG_SIZE, NUM_OF_MSGS); } +/* Return true if interrupts were locked in the context of this call. */ +static bool is_irq_locked(void) +{ + unsigned int key = z_arch_irq_lock(); + bool ret = z_arch_irq_unlocked(key); + + z_arch_irq_unlock(key); + return ret; +} + +/* Check if context can be blocked and pend on available memory slab. Context + * can be blocked if in a thread and interrupts are not locked. + */ +static bool block_on_alloc(void) +{ + if (!IS_ENABLED(CONFIG_LOG_BLOCK_IN_THREAD)) { + return false; + } + + return (!k_is_in_isr() && !is_irq_locked()); +} + union log_msg_chunk *log_msg_chunk_alloc(void) { union log_msg_chunk *msg = NULL; - int err = k_mem_slab_alloc(&log_msg_pool, (void **)&msg, K_NO_WAIT); + int err = k_mem_slab_alloc(&log_msg_pool, (void **)&msg, + block_on_alloc() ? + CONFIG_LOG_BLOCK_IN_THREAD_TIMEOUT_MS : K_NO_WAIT); if (err != 0) { msg = log_msg_no_space_handle();