Skip to content

Commit bde4da2

Browse files
Protect core/Newlib mutexes from task preemption under FreeRTOS (#798)
Fixes #795 Replace all CoreMutex and Newlib mutex accesses with FreeRTOS calls when in FreeRTOS mode. Avoid issues with hange/etc. due to priority inversion. No changes to normal operating mode. Add a FreeRTOS stress test that caught the issue fixed here.
1 parent b4b1c39 commit bde4da2

File tree

8 files changed

+614
-34
lines changed

8 files changed

+614
-34
lines changed

cores/rp2040/CoreMutex.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,39 @@
2424
#pragma once
2525

2626
#include "pico/mutex.h"
27+
#include "_freertos.h"
2728

2829
class CoreMutex {
2930
public:
3031
CoreMutex(mutex_t *mutex, bool debugEnable = true) {
31-
uint32_t owner;
3232
_mutex = mutex;
3333
_acquired = false;
34-
if (!mutex_try_enter(_mutex, &owner)) {
35-
if (owner == get_core_num()) { // Deadlock!
36-
if (debugEnable) {
37-
DEBUGCORE("CoreMutex - Deadlock detected!\n");
34+
if (__isFreeRTOS) {
35+
auto m = __get_freertos_mutex_for_ptr(mutex);
36+
__freertos_mutex_take(m);
37+
} else {
38+
uint32_t owner;
39+
if (!mutex_try_enter(_mutex, &owner)) {
40+
if (owner == get_core_num()) { // Deadlock!
41+
if (debugEnable) {
42+
DEBUGCORE("CoreMutex - Deadlock detected!\n");
43+
}
44+
return;
3845
}
39-
return;
46+
mutex_enter_blocking(_mutex);
4047
}
41-
mutex_enter_blocking(_mutex);
4248
}
4349
_acquired = true;
4450
}
4551

4652
~CoreMutex() {
4753
if (_acquired) {
48-
mutex_exit(_mutex);
54+
if (__isFreeRTOS) {
55+
auto m = __get_freertos_mutex_for_ptr(_mutex);
56+
__freertos_mutex_give(m);
57+
} else {
58+
mutex_exit(_mutex);
59+
}
4960
}
5061
}
5162

cores/rp2040/RP2040Support.h

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,9 @@
3232
#include "ccount.pio.h"
3333
#include <malloc.h>
3434

35-
extern "C" volatile bool __otherCoreIdled;
35+
#include "_freertos.h"
3636

37-
// Halt the FreeRTOS PendSV task switching magic
38-
extern "C" int __holdUpPendSV;
39-
40-
// FreeRTOS weak functions, to be overridden when we really are running FreeRTOS
41-
extern "C" {
42-
extern void vTaskSuspendAll() __attribute__((weak));
43-
extern int32_t xTaskResumeAll() __attribute__((weak));
44-
typedef struct tskTaskControlBlock * TaskHandle_t;
45-
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
46-
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
47-
}
37+
extern "C" volatile bool __otherCoreIdled;
4838

4939
class _MFIFO {
5040
public:

cores/rp2040/_freertos.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
_freertos.cpp - Internal core definitions for FreeRTOS
3+
4+
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
This library is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
Lesser General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public
17+
License along with this library; if not, write to the Free Software
18+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
#include "_freertos.h"
22+
#include "pico/mutex.h"
23+
#include <stdlib.h>
24+
25+
typedef struct {
26+
mutex_t *src;
27+
SemaphoreHandle_t dst;
28+
} FMMap;
29+
30+
static FMMap *_map = nullptr;
31+
extern "C" SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m) {
32+
if (!_map) {
33+
_map = (FMMap *)calloc(sizeof(FMMap), 16);
34+
}
35+
// Pre-existing map
36+
for (int i = 0; i < 16; i++) {
37+
if (m == _map[i].src) {
38+
return _map[i].dst;
39+
}
40+
}
41+
// Make a new mutex
42+
auto fm = __freertos_mutex_create();
43+
for (int i = 0; i < 16; i++) {
44+
if (_map[i].src == nullptr) {
45+
_map[i].src = m;
46+
_map[i].dst = fm;
47+
return fm;
48+
}
49+
}
50+
return nullptr; // Need to make space for more mutex maps!
51+
}

cores/rp2040/_freertos.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
_freertos.h - Internal core definitions for FreeRTOS
3+
4+
Copyright (c) 2022 Earle F. Philhower, III <earlephilhower@yahoo.com>
5+
6+
This library is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU Lesser General Public
8+
License as published by the Free Software Foundation; either
9+
version 2.1 of the License, or (at your option) any later version.
10+
11+
This library is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
Lesser General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public
17+
License along with this library; if not, write to the Free Software
18+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*/
20+
21+
#pragma once
22+
#include "pico/mutex.h"
23+
24+
// Cannot include refs to FreeRTOS's actual semaphore calls because they
25+
// are implemented as macros, so we have a wrapper in our variant hook
26+
// to handle it.
27+
28+
extern bool __isFreeRTOS;
29+
30+
// FreeRTOS has been set up
31+
extern volatile bool __freeRTOSinitted;
32+
33+
extern "C" {
34+
#ifndef INC_FREERTOS_H
35+
struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */
36+
typedef struct QueueDefinition * QueueHandle_t;
37+
typedef QueueHandle_t SemaphoreHandle_t;
38+
#endif
39+
40+
extern SemaphoreHandle_t __freertos_mutex_create() __attribute__((weak));
41+
extern SemaphoreHandle_t _freertos_recursive_mutex_create() __attribute__((weak));
42+
43+
extern void __freertos_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
44+
extern int __freertos_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
45+
extern void __freertos_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
46+
47+
extern void __freertos_recursive_mutex_take(SemaphoreHandle_t mtx) __attribute__((weak));
48+
extern int __freertos_recursive_mutex_try_take(SemaphoreHandle_t mtx) __attribute__((weak));
49+
extern void __freertos_recursive_mutex_give(SemaphoreHandle_t mtx) __attribute__((weak));
50+
51+
#ifndef INC_FREERTOS_H
52+
extern void vTaskSuspendAll() __attribute__((weak));
53+
extern int32_t xTaskResumeAll() __attribute__((weak));
54+
55+
typedef struct tskTaskControlBlock * TaskHandle_t;
56+
extern void vTaskPreemptionDisable(TaskHandle_t p) __attribute__((weak));
57+
extern void vTaskPreemptionEnable(TaskHandle_t p) __attribute__((weak));
58+
#endif
59+
60+
extern SemaphoreHandle_t __get_freertos_mutex_for_ptr(mutex_t *m);
61+
}
62+
63+
// Halt the FreeRTOS PendSV task switching magic
64+
extern "C" int __holdUpPendSV;

cores/rp2040/lock.cpp

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <pico/mutex.h>
2626
#include <sys/lock.h>
2727

28+
#include "_freertos.h"
29+
2830
// HACK ALERT
2931
// Pico-SDK defines mutex which can be at global scope, but when the auto_init_
3032
// macros are used they are defined as static. Newlib needs global access to
@@ -42,12 +44,75 @@ auto_init_mutex(__lock___dd_hash_mutex);
4244
auto_init_mutex(__lock___arc4random_mutex);
4345
#undef static
4446

47+
// FreeRTOS hack - Allow Newlib to use FreeRTOS mutexes which preserve TASKID which
48+
// is needed to support multithread
49+
50+
SemaphoreHandle_t __lock___sinit_recursive_mutex_freertos;
51+
SemaphoreHandle_t __lock___sfp_recursive_mutex_freertos;
52+
SemaphoreHandle_t __lock___atexit_recursive_mutex_freertos;
53+
SemaphoreHandle_t __lock___at_quick_exit_mutex_freertos;
54+
SemaphoreHandle_t __lock___malloc_recursive_mutex_freertos;
55+
SemaphoreHandle_t __lock___env_recursive_mutex_freertos;
56+
SemaphoreHandle_t __lock___tz_mutex_freertos;
57+
SemaphoreHandle_t __lock___dd_hash_mutex_freertos;
58+
SemaphoreHandle_t __lock___arc4random_mutex_freertos;
59+
60+
void __initFreeRTOSMutexes() {
61+
__lock___sinit_recursive_mutex_freertos = _freertos_recursive_mutex_create();
62+
__lock___sfp_recursive_mutex_freertos = _freertos_recursive_mutex_create();
63+
__lock___atexit_recursive_mutex_freertos = _freertos_recursive_mutex_create();
64+
__lock___at_quick_exit_mutex_freertos = __freertos_mutex_create();
65+
__lock___malloc_recursive_mutex_freertos = _freertos_recursive_mutex_create();
66+
__lock___env_recursive_mutex_freertos = _freertos_recursive_mutex_create();
67+
__lock___tz_mutex_freertos = __freertos_mutex_create();
68+
__lock___dd_hash_mutex_freertos = __freertos_mutex_create();
69+
__lock___arc4random_mutex_freertos = __freertos_mutex_create();
70+
}
71+
72+
static SemaphoreHandle_t __getFreeRTOSMutex(_LOCK_T lock) {
73+
mutex_t *l = (mutex_t *)lock;
74+
if (l == &__lock___at_quick_exit_mutex) {
75+
return __lock___at_quick_exit_mutex_freertos;
76+
} else if (l == &__lock___tz_mutex) {
77+
return __lock___tz_mutex_freertos;
78+
} else if (l == &__lock___dd_hash_mutex) {
79+
return __lock___dd_hash_mutex_freertos;
80+
} else if (l == &__lock___arc4random_mutex) {
81+
return __lock___arc4random_mutex_freertos;
82+
}
83+
return nullptr;
84+
}
85+
86+
static SemaphoreHandle_t __getFreeRTOSRecursiveMutex(_LOCK_T lock) {
87+
recursive_mutex_t *l = (recursive_mutex_t *)lock;
88+
if (l == &__lock___sinit_recursive_mutex) {
89+
return __lock___sinit_recursive_mutex_freertos;
90+
} else if (l == &__lock___sfp_recursive_mutex) {
91+
return __lock___sfp_recursive_mutex_freertos;
92+
} else if (l == &__lock___atexit_recursive_mutex) {
93+
return __lock___atexit_recursive_mutex_freertos;
94+
} else if (l == &__lock___malloc_recursive_mutex) {
95+
return __lock___malloc_recursive_mutex_freertos;
96+
} else if (l == &__lock___env_recursive_mutex) {
97+
return __lock___env_recursive_mutex_freertos;
98+
}
99+
return nullptr;
100+
}
101+
45102
void __retarget_lock_init(_LOCK_T *lock) {
46-
mutex_init((mutex_t*) lock);
103+
if (__freeRTOSinitted) {
104+
/* Already done in initFreeRTOSMutexes() */
105+
} else {
106+
mutex_init((mutex_t*) lock);
107+
}
47108
}
48109

49110
void __retarget_lock_init_recursive(_LOCK_T *lock) {
50-
recursive_mutex_init((recursive_mutex_t*) lock);
111+
if (__freeRTOSinitted) {
112+
/* Already done in initFreeRTOSMutexes() */
113+
} else {
114+
recursive_mutex_init((recursive_mutex_t*) lock);
115+
}
51116
}
52117

53118
void __retarget_lock_close(_LOCK_T lock) {
@@ -59,25 +124,59 @@ void __retarget_lock_close_recursive(_LOCK_T lock) {
59124
}
60125

61126
void __retarget_lock_acquire(_LOCK_T lock) {
62-
mutex_enter_blocking((mutex_t*)lock);
127+
if (__freeRTOSinitted) {
128+
auto mtx = __getFreeRTOSMutex(lock);
129+
__freertos_mutex_take(mtx);
130+
} else {
131+
mutex_enter_blocking((mutex_t*)lock);
132+
}
63133
}
64134

65135
void __retarget_lock_acquire_recursive(_LOCK_T lock) {
66-
recursive_mutex_enter_blocking((recursive_mutex_t*)lock);
136+
if (__freeRTOSinitted) {
137+
auto mtx = __getFreeRTOSRecursiveMutex(lock);
138+
__freertos_recursive_mutex_take(mtx);
139+
} else {
140+
recursive_mutex_enter_blocking((recursive_mutex_t*)lock);
141+
}
67142
}
68143

69144
int __retarget_lock_try_acquire(_LOCK_T lock) {
70-
return mutex_try_enter((mutex_t *)lock, NULL);
145+
int ret;
146+
if (__freeRTOSinitted) {
147+
auto mtx = __getFreeRTOSMutex(lock);
148+
ret = __freertos_mutex_try_take(mtx);
149+
} else {
150+
ret = mutex_try_enter((mutex_t *)lock, NULL);
151+
}
152+
return ret;
71153
}
72154

73155
int __retarget_lock_try_acquire_recursive(_LOCK_T lock) {
74-
return recursive_mutex_try_enter((recursive_mutex_t*)lock, NULL);
156+
int ret;
157+
if (__freeRTOSinitted) {
158+
auto mtx = __getFreeRTOSRecursiveMutex(lock);
159+
ret = __freertos_recursive_mutex_try_take(mtx);
160+
} else {
161+
ret = recursive_mutex_try_enter((recursive_mutex_t*)lock, NULL);
162+
}
163+
return ret;
75164
}
76165

77166
void __retarget_lock_release(_LOCK_T lock) {
78-
mutex_exit((mutex_t*)lock);
167+
if (__freeRTOSinitted) {
168+
auto mtx = __getFreeRTOSMutex(lock);
169+
__freertos_mutex_give(mtx);
170+
} else {
171+
mutex_exit((mutex_t*)lock);
172+
}
79173
}
80174

81175
void __retarget_lock_release_recursive(_LOCK_T lock) {
82-
recursive_mutex_exit((recursive_mutex_t*)lock);
176+
if (__freeRTOSinitted) {
177+
auto mtx = __getFreeRTOSRecursiveMutex(lock);
178+
__freertos_recursive_mutex_give(mtx);
179+
} else {
180+
recursive_mutex_exit((recursive_mutex_t*)lock);
181+
}
83182
}

cores/rp2040/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ extern void loop();
4242
extern void initFreeRTOS() __attribute__((weak));
4343
extern void startFreeRTOS() __attribute__((weak));
4444
bool __isFreeRTOS;
45+
volatile bool __freeRTOSinitted;
46+
4547

4648
// Weak empty variant initialization. May be redefined by variant files.
4749
void initVariant() __attribute__((weak));

0 commit comments

Comments
 (0)