mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
core: implement core_mutex_mitigate_priority_inversion
This commit is contained in:
parent
29841ef895
commit
8be03dc055
@ -39,6 +39,9 @@ config MODULE_CORE_MSG_BUS
|
||||
help
|
||||
Messaging Bus API for inter process message broadcast.
|
||||
|
||||
config MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
bool "Use priority inheritance to mitigate priority inversion for mutexes"
|
||||
|
||||
config MODULE_CORE_PANIC
|
||||
bool "Kernel crash handling module"
|
||||
default y
|
||||
|
@ -12,6 +12,12 @@
|
||||
* @ingroup core_sync
|
||||
* @brief Mutex for thread synchronization
|
||||
*
|
||||
* @warning By default, no mitigation against priority inversion is
|
||||
* employed. If your application is subject to priority inversion
|
||||
* and cannot tolerate the additional delay this can cause, use
|
||||
* module `core_mutex_priority_inheritance` to employ
|
||||
* priority inheritance as mitigation.
|
||||
*
|
||||
* Mutex Implementation Basics
|
||||
* ===========================
|
||||
*
|
||||
@ -127,6 +133,24 @@ typedef struct {
|
||||
* @internal
|
||||
*/
|
||||
list_node_t queue;
|
||||
#if defined(DOXYGEN) || defined(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE)
|
||||
/**
|
||||
* @brief The current owner of the mutex or `NULL`
|
||||
* @note Only available if module core_mutex_priority_inheritance
|
||||
* is used.
|
||||
*
|
||||
* If either the mutex is not locked or the mutex is not locked by a thread
|
||||
* (e.g. because it is used to synchronize a thread with an ISR completion),
|
||||
* this will have the value of `NULL`.
|
||||
*/
|
||||
kernel_pid_t owner;
|
||||
/**
|
||||
* @brief Original priority of the owner
|
||||
* @note Only available if module core_mutex_priority_inheritance
|
||||
* is used.
|
||||
*/
|
||||
uint8_t owner_original_priority;
|
||||
#endif
|
||||
} mutex_t;
|
||||
|
||||
/**
|
||||
@ -141,16 +165,21 @@ typedef struct {
|
||||
uint8_t cancelled; /**< Flag whether the mutex has been cancelled */
|
||||
} mutex_cancel_t;
|
||||
|
||||
#ifndef __cplusplus
|
||||
/**
|
||||
* @brief Static initializer for mutex_t.
|
||||
* @details This initializer is preferable to mutex_init().
|
||||
*/
|
||||
#define MUTEX_INIT { { NULL } }
|
||||
# define MUTEX_INIT { .queue = { .next = NULL } }
|
||||
|
||||
/**
|
||||
* @brief Static initializer for mutex_t with a locked mutex
|
||||
*/
|
||||
#define MUTEX_INIT_LOCKED { { MUTEX_LOCKED } }
|
||||
# define MUTEX_INIT_LOCKED { .queue = { .next = MUTEX_LOCKED } }
|
||||
#else
|
||||
# define MUTEX_INIT {}
|
||||
# define MUTEX_INIT_LOCKED { { MUTEX_LOCKED } }
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* @cond INTERNAL
|
||||
@ -231,6 +260,15 @@ static inline int mutex_trylock(mutex_t *mutex)
|
||||
|
||||
if (mutex->queue.next == NULL) {
|
||||
mutex->queue.next = MUTEX_LOCKED;
|
||||
#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
mutex->owner = KERNEL_PID_UNDEF;
|
||||
thread_t *t = thread_get_active();
|
||||
/* in case mutex_trylock() is not called from thread context */
|
||||
if (t) {
|
||||
mutex->owner = t->pid;
|
||||
mutex->owner_original_priority = t->priority;
|
||||
}
|
||||
#endif
|
||||
retval = 1;
|
||||
}
|
||||
irq_restore(irq_state);
|
||||
|
31
core/mutex.c
31
core/mutex.c
@ -66,6 +66,17 @@ static inline __attribute__((always_inline)) void _block(mutex_t *mutex,
|
||||
thread_add_to_list(&mutex->queue, me);
|
||||
}
|
||||
|
||||
#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
thread_t *owner = thread_get(mutex->owner);
|
||||
if ((owner) && (owner->priority > me->priority)) {
|
||||
DEBUG("PID[%" PRIkernel_pid "] prio of %" PRIkernel_pid
|
||||
": %u --> %u\n",
|
||||
thread_getpid(), mutex->owner,
|
||||
(unsigned)owner->priority, (unsigned)me->priority);
|
||||
sched_change_priority(owner, me->priority);
|
||||
}
|
||||
#endif
|
||||
|
||||
irq_restore(irq_state);
|
||||
thread_yield_higher();
|
||||
/* We were woken up by scheduler. Waker removed us from queue. */
|
||||
@ -80,6 +91,11 @@ void mutex_lock(mutex_t *mutex)
|
||||
if (mutex->queue.next == NULL) {
|
||||
/* mutex is unlocked. */
|
||||
mutex->queue.next = MUTEX_LOCKED;
|
||||
#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
thread_t *me = thread_get_active();
|
||||
mutex->owner = me->pid;
|
||||
mutex->owner_original_priority = me->priority;
|
||||
#endif
|
||||
DEBUG("PID[%" PRIkernel_pid "] mutex_lock(): early out.\n",
|
||||
thread_getpid());
|
||||
irq_restore(irq_state);
|
||||
@ -108,6 +124,11 @@ int mutex_lock_cancelable(mutex_cancel_t *mc)
|
||||
if (mutex->queue.next == NULL) {
|
||||
/* mutex is unlocked. */
|
||||
mutex->queue.next = MUTEX_LOCKED;
|
||||
#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
thread_t *me = thread_get_active();
|
||||
mutex->owner = me->pid;
|
||||
mutex->owner_original_priority = me->priority;
|
||||
#endif
|
||||
DEBUG("PID[%" PRIkernel_pid "] mutex_lock_cancelable() early out.\n",
|
||||
thread_getpid());
|
||||
irq_restore(irq_state);
|
||||
@ -136,6 +157,16 @@ void mutex_unlock(mutex_t *mutex)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE
|
||||
thread_t *owner = thread_get(mutex->owner);
|
||||
if ((owner) && (owner->priority != mutex->owner_original_priority)) {
|
||||
DEBUG("PID[%" PRIkernel_pid "] prio %u --> %u\n",
|
||||
owner->pid,
|
||||
(unsigned)owner->priority, (unsigned)owner->priority);
|
||||
sched_change_priority(owner, mutex->owner_original_priority);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mutex->queue.next == MUTEX_LOCKED) {
|
||||
mutex->queue.next = NULL;
|
||||
/* the mutex was locked and no thread was waiting for it */
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
*/
|
||||
using native_handle_type = mutex_t*;
|
||||
|
||||
inline constexpr mutex() noexcept : m_mtx{{0}} {}
|
||||
inline constexpr mutex() noexcept : m_mtx{} {}
|
||||
~mutex();
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user