zephyr/subsys/logging/event_logger.c
Savinay Dharmappa 0106059ff0 subsys: logging: event_logger: Fix CPU fault
A CPU fault occurs when sys_k_event_logger_get_wait()
api invoked with config KERNEL_EVENT_LOGGER_THREAD
enabled.

sys_k_event_logger_get_wait() supposed to pend  on a
semaphore when all the events from kernel event logger
are read.But when sys_k_event_logger_get_wait() inovked
with config KERNEL_EVENT_LOGGER_THREAD(i.e captures thread
events) subsquent call inside this function will write to
kernel event log buffer to capture pend event.This will
release the semaphore on which sys_k_event_logger_get_wait()
was pending hence thread gets unpend before swap gets called.

Which in other words a thread which is invoking
sys_k_event_logger_get_wait()(i. e sem_count = 0) get pends
and unpends in single function flow when KERNEL_EVENT_LOGGER_THREAD
enabled.

This would cause overlapping of the stack address where
return address of "_pend_current_thread" stored with esp of
callee saved(i. e thread->callee_saved.esp). Thus return adrress
of "_pend_current_thread" would be overwitten with zero. Which
in turn causes CPU fault.

The thread invoking sys_k_event_logger_get_wait() supposed
to only read the events of the threads which logged to kernel event
logger buffer. But it should not write to kernel event logger
buffer. Otherwise it would cause the race condition explained above.

Signed-off-by: Savinay Dharmappa <savinay.dharmappa@intel.com>
2017-11-02 22:22:54 -04:00

165 lines
4.6 KiB
C

/*
* Copyright (c) 2015 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Event logger support.
*/
#include <logging/event_logger.h>
#include <misc/ring_buffer.h>
#include <kernel_structs.h>
void sys_event_logger_init(struct event_logger *logger,
u32_t *logger_buffer, u32_t buffer_size)
{
sys_ring_buf_init(&logger->ring_buf, buffer_size, logger_buffer);
k_sem_init(&(logger->sync_sema), 0, UINT_MAX);
}
static void event_logger_put(struct event_logger *logger, u16_t event_id,
u32_t *event_data, u8_t data_size,
void (*sem_give_fn)(struct k_sem *))
{
int ret;
unsigned int key;
key = irq_lock();
ret = sys_ring_buf_put(&logger->ring_buf, event_id,
logger->ring_buf.dropped_put_count, event_data,
data_size);
if (ret == 0) {
logger->ring_buf.dropped_put_count = 0;
/* inform that there is event data available on the buffer */
sem_give_fn(&(logger->sync_sema));
}
irq_unlock(key);
}
void sys_event_logger_put(struct event_logger *logger, u16_t event_id,
u32_t *event_data, u8_t data_size)
{
/* The thread invoking sys_k_event_logger_get_wait() supposed
* to only read the events of the threads which logged to kernel event
* logger buffer. But it should not write to kernel event logger
* buffer. Otherwise it would cause the race condition.
*/
struct k_thread *event_logger_thread =
(struct k_thread *)sys_dlist_peek_head(&(logger->sync_sema.wait_q));
if (_current != event_logger_thread) {
event_logger_put(logger, event_id, event_data,
data_size, k_sem_give);
}
}
/**
* @brief Send an event message to the logger with a non preemptible
* behavior.
*
* @details Add an event message to the ring buffer and signal the sync
* semaphore using the internal function _sem_give_non_preemptible to inform
* that there are event messages available, avoiding the preemptible
* behavior when the function is called from a task. This function
* should be only used for special cases where the sys_event_logger_put
* does not satisfy the needs.
*
* @param logger Pointer to the event logger used.
* @param event_id The identification of the profiler event.
* @param data Pointer to the data of the message.
* @param data_size Size of the buffer in 32-bit words.
*
* @return No return value.
*/
void _sys_event_logger_put_non_preemptible(struct event_logger *logger,
u16_t event_id, u32_t *event_data, u8_t data_size)
{
extern void _sem_give_non_preemptible(struct k_sem *sem);
/* The thread invoking sys_k_event_logger_get_wait() supposed
* to only read the events of the threads which logged to kernel event
* logger buffer. But it should not write to kernel event logger
* buffer. Otherwise it would cause the race condition.
*/
struct k_thread *event_logger_thread =
(struct k_thread *)sys_dlist_peek_head(&(logger->sync_sema.wait_q));
if (_current != event_logger_thread) {
event_logger_put(logger, event_id, event_data, data_size,
_sem_give_non_preemptible);
}
}
static int event_logger_get(struct event_logger *logger,
u16_t *event_id, u8_t *dropped_event_count,
u32_t *buffer, u8_t *buffer_size)
{
int ret;
ret = sys_ring_buf_get(&logger->ring_buf, event_id, dropped_event_count,
buffer, buffer_size);
if (likely(!ret)) {
return *buffer_size;
}
switch (ret) {
case -EMSGSIZE:
/* if the user can not retrieve the message, we increase the
* semaphore to indicate that the message remains in the buffer
*/
k_sem_give(&(logger->sync_sema));
return -EMSGSIZE;
case -EAGAIN:
return 0;
default:
return ret;
}
}
int sys_event_logger_get(struct event_logger *logger, u16_t *event_id,
u8_t *dropped_event_count, u32_t *buffer,
u8_t *buffer_size)
{
if (k_sem_take(&(logger->sync_sema), K_NO_WAIT) == 0) {
return event_logger_get(logger, event_id, dropped_event_count,
buffer, buffer_size);
}
return 0;
}
int sys_event_logger_get_wait(struct event_logger *logger, u16_t *event_id,
u8_t *dropped_event_count, u32_t *buffer,
u8_t *buffer_size)
{
k_sem_take(&(logger->sync_sema), K_FOREVER);
return event_logger_get(logger, event_id, dropped_event_count, buffer,
buffer_size);
}
#ifdef CONFIG_SYS_CLOCK_EXISTS
int sys_event_logger_get_wait_timeout(struct event_logger *logger,
u16_t *event_id,
u8_t *dropped_event_count,
u32_t *buffer, u8_t *buffer_size,
u32_t timeout)
{
if (k_sem_take(&(logger->sync_sema), __ticks_to_ms(timeout))) {
return event_logger_get(logger, event_id, dropped_event_count,
buffer, buffer_size);
}
return 0;
}
#endif /* CONFIG_SYS_CLOCK_EXISTS */