1
0
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:
Marian Buschsieweke 2022-03-09 21:20:57 +01:00
parent 29841ef895
commit 8be03dc055
No known key found for this signature in database
GPG Key ID: CB8E3238CE715A94
4 changed files with 75 additions and 3 deletions

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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();
/**