blob: d06522a042087ab91553c5de00381072e91ae934 [file] [log] [blame]
Benjamin Walsh456c6da2016-09-02 18:55:39 -04001/*
2 * Copyright (c) 2016 Wind River Systems, Inc.
3 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08004 * SPDX-License-Identifier: Apache-2.0
Benjamin Walsh456c6da2016-09-02 18:55:39 -04005 */
6
7/**
8 * @file @brief mutex kernel services
9 *
10 * This module contains routines for handling mutex locking and unlocking.
11 *
12 * Mutexes implement a priority inheritance algorithm that boosts the priority
13 * level of the owning thread to match the priority level of the highest
14 * priority thread waiting on the mutex.
15 *
16 * Each mutex that contributes to priority inheritance must be released in the
Ramakrishna Pallala67426262017-10-24 19:04:43 -040017 * reverse order in which it was acquired. Furthermore each subsequent mutex
Benjamin Walsh456c6da2016-09-02 18:55:39 -040018 * that contributes to raising the owning thread's priority level must be
19 * acquired at a point after the most recent "bumping" of the priority level.
20 *
21 * For example, if thread A has two mutexes contributing to the raising of its
22 * priority level, the second mutex M2 must be acquired by thread A after
23 * thread A's priority level was bumped due to owning the first mutex M1.
24 * When releasing the mutex, thread A must release M2 before it releases M1.
25 * Failure to follow this nested model may result in threads running at
26 * unexpected priority levels (too high, or too low).
27 */
28
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +020029#include <zephyr/kernel.h>
30#include <zephyr/kernel_structs.h>
31#include <zephyr/toolchain.h>
Stephanos Ioannidis2d746042019-10-25 00:08:21 +090032#include <ksched.h>
Anas Nashif8634c3b2023-08-29 17:03:12 +000033#include <wait_q.h>
Benjamin Walsh456c6da2016-09-02 18:55:39 -040034#include <errno.h>
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +020035#include <zephyr/init.h>
Anas Nashif4e396172023-09-26 22:46:01 +000036#include <zephyr/internal/syscall_handler.h>
Gerard Marull-Paretascffefc82022-05-06 11:04:23 +020037#include <zephyr/tracing/tracing.h>
38#include <zephyr/sys/check.h>
39#include <zephyr/logging/log.h>
Krzysztof Chruscinski3ed80832020-11-26 19:32:34 +010040LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
Benjamin Walsh456c6da2016-09-02 18:55:39 -040041
Andy Ross7df02162018-07-25 13:55:59 -070042/* We use a global spinlock here because some of the synchronization
43 * is protecting things like owner thread priorities which aren't
44 * "part of" a single k_mutex. Should move those bits of the API
45 * under the scheduler lock so we can break this up.
46 */
47static struct k_spinlock lock;
48
Peter Mitsis6df8efe2023-05-11 14:06:46 -040049#ifdef CONFIG_OBJ_CORE_MUTEX
50static struct k_obj_type obj_type_mutex;
51#endif
52
Anas Nashif86bb2d02019-05-04 10:18:13 -040053int z_impl_k_mutex_init(struct k_mutex *mutex)
Benjamin Walsh456c6da2016-09-02 18:55:39 -040054{
55 mutex->owner = NULL;
Patrik Flykt24d71432019-03-26 19:57:45 -060056 mutex->lock_count = 0U;
Benjamin Walsh456c6da2016-09-02 18:55:39 -040057
Patrik Flykt4344e272019-03-08 14:19:05 -070058 z_waitq_init(&mutex->wait_q);
Benjamin Walsh456c6da2016-09-02 18:55:39 -040059
Anas Nashifc91cad72023-09-26 21:32:13 +000060 k_object_init(mutex);
Torbjörn Lekselled6148a2021-03-26 10:28:23 +010061
Peter Mitsis6df8efe2023-05-11 14:06:46 -040062#ifdef CONFIG_OBJ_CORE_MUTEX
63 k_obj_core_init_and_link(K_OBJ_CORE(mutex), &obj_type_mutex);
64#endif
65
Torbjörn Lekselled6148a2021-03-26 10:28:23 +010066 SYS_PORT_TRACING_OBJ_INIT(k_mutex, mutex, 0);
Anas Nashif86bb2d02019-05-04 10:18:13 -040067
68 return 0;
Benjamin Walsh456c6da2016-09-02 18:55:39 -040069}
70
Andrew Boie2f7519b2017-09-29 03:33:06 -070071#ifdef CONFIG_USERSPACE
Anas Nashif86bb2d02019-05-04 10:18:13 -040072static inline int z_vrfy_k_mutex_init(struct k_mutex *mutex)
Andrew Boie2f7519b2017-09-29 03:33:06 -070073{
Anas Nashif9c4d8812023-09-27 11:09:45 +000074 Z_OOPS(K_SYSCALL_OBJ_INIT(mutex, K_OBJ_MUTEX));
Anas Nashif86bb2d02019-05-04 10:18:13 -040075 return z_impl_k_mutex_init(mutex);
Andrew Boie2f7519b2017-09-29 03:33:06 -070076}
Andy Ross65649742019-08-06 13:34:31 -070077#include <syscalls/k_mutex_init_mrsh.c>
Andrew Boie2f7519b2017-09-29 03:33:06 -070078#endif
79
Kumar Galaa1b77fd2020-05-27 11:26:57 -050080static int32_t new_prio_for_inheritance(int32_t target, int32_t limit)
Benjamin Walsh456c6da2016-09-02 18:55:39 -040081{
Patrik Flykt4344e272019-03-08 14:19:05 -070082 int new_prio = z_is_prio_higher(target, limit) ? target : limit;
Benjamin Walsh456c6da2016-09-02 18:55:39 -040083
Patrik Flykt4344e272019-03-08 14:19:05 -070084 new_prio = z_get_new_prio_with_ceiling(new_prio);
Benjamin Walsh456c6da2016-09-02 18:55:39 -040085
86 return new_prio;
87}
88
Kumar Galaa1b77fd2020-05-27 11:26:57 -050089static bool adjust_owner_prio(struct k_mutex *mutex, int32_t new_prio)
Benjamin Walsh456c6da2016-09-02 18:55:39 -040090{
Benjamin Walshf6ca7de2016-11-08 10:36:50 -050091 if (mutex->owner->base.prio != new_prio) {
Benjamin Walsh456c6da2016-09-02 18:55:39 -040092
Anas Nashif2c5d4042019-12-02 10:24:08 -050093 LOG_DBG("%p (ready (y/n): %c) prio changed to %d (was %d)",
Patrik Flykt4344e272019-03-08 14:19:05 -070094 mutex->owner, z_is_thread_ready(mutex->owner) ?
Benjamin Walsh456c6da2016-09-02 18:55:39 -040095 'y' : 'n',
Benjamin Walsha4e033f2016-11-18 16:08:24 -050096 new_prio, mutex->owner->base.prio);
Benjamin Walsh456c6da2016-09-02 18:55:39 -040097
Andy Ross6f139802019-08-20 11:21:28 -070098 return z_set_prio(mutex->owner, new_prio);
Benjamin Walsh456c6da2016-09-02 18:55:39 -040099 }
Andy Ross6f139802019-08-20 11:21:28 -0700100 return false;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400101}
102
Andy Ross78327382020-03-05 15:18:14 -0800103int z_impl_k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout)
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400104{
Flavio Ceolin0866d182018-08-14 17:57:08 -0700105 int new_prio;
Andy Ross7df02162018-07-25 13:55:59 -0700106 k_spinlock_key_t key;
Andy Ross6f139802019-08-20 11:21:28 -0700107 bool resched = false;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400108
Andrew Boie6af97932020-05-27 11:48:30 -0700109 __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
110
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100111 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, lock, mutex, timeout);
112
Andy Ross6f139802019-08-20 11:21:28 -0700113 key = k_spin_lock(&lock);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400114
Adithya Baglody87e592e2018-10-25 14:10:52 +0530115 if (likely((mutex->lock_count == 0U) || (mutex->owner == _current))) {
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400116
Adithya Baglody87e592e2018-10-25 14:10:52 +0530117 mutex->owner_orig_prio = (mutex->lock_count == 0U) ?
Benjamin Walshf6ca7de2016-11-08 10:36:50 -0500118 _current->base.prio :
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400119 mutex->owner_orig_prio;
120
121 mutex->lock_count++;
122 mutex->owner = _current;
123
Anas Nashif2c5d4042019-12-02 10:24:08 -0500124 LOG_DBG("%p took mutex %p, count: %d, orig prio: %d",
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400125 _current, mutex, mutex->lock_count,
126 mutex->owner_orig_prio);
127
Andy Ross6f139802019-08-20 11:21:28 -0700128 k_spin_unlock(&lock, key);
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100129
130 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400131
132 return 0;
133 }
134
Andy Ross78327382020-03-05 15:18:14 -0800135 if (unlikely(K_TIMEOUT_EQ(timeout, K_NO_WAIT))) {
Andy Ross6f139802019-08-20 11:21:28 -0700136 k_spin_unlock(&lock, key);
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100137
138 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EBUSY);
139
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400140 return -EBUSY;
141 }
142
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100143 SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mutex, lock, mutex, timeout);
144
Benjamin Walshf6ca7de2016-11-08 10:36:50 -0500145 new_prio = new_prio_for_inheritance(_current->base.prio,
146 mutex->owner->base.prio);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400147
Anas Nashif2c5d4042019-12-02 10:24:08 -0500148 LOG_DBG("adjusting prio up on mutex %p", mutex);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400149
Patrik Flykt4344e272019-03-08 14:19:05 -0700150 if (z_is_prio_higher(new_prio, mutex->owner->base.prio)) {
Andy Ross6f139802019-08-20 11:21:28 -0700151 resched = adjust_owner_prio(mutex, new_prio);
Benjamin Walsh4bfa0052017-01-14 18:57:58 -0500152 }
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400153
Patrik Flykt4344e272019-03-08 14:19:05 -0700154 int got_mutex = z_pend_curr(&lock, key, &mutex->wait_q, timeout);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400155
Anas Nashif2c5d4042019-12-02 10:24:08 -0500156 LOG_DBG("on mutex %p got_mutex value: %d", mutex, got_mutex);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400157
Anas Nashif2c5d4042019-12-02 10:24:08 -0500158 LOG_DBG("%p got mutex %p (y/n): %c", _current, mutex,
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400159 got_mutex ? 'y' : 'n');
160
161 if (got_mutex == 0) {
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100162 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400163 return 0;
164 }
165
166 /* timed out */
167
Anas Nashif2c5d4042019-12-02 10:24:08 -0500168 LOG_DBG("%p timeout on mutex %p", _current, mutex);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400169
Andy Ross6f139802019-08-20 11:21:28 -0700170 key = k_spin_lock(&lock);
171
Qi Yang89c4a072022-08-12 10:44:38 +0800172 /*
173 * Check if mutex was unlocked after this thread was unpended.
174 * If so, skip adjusting owner's priority down.
175 */
176 if (likely(mutex->owner != NULL)) {
177 struct k_thread *waiter = z_waitq_head(&mutex->wait_q);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400178
Qi Yang89c4a072022-08-12 10:44:38 +0800179 new_prio = (waiter != NULL) ?
180 new_prio_for_inheritance(waiter->base.prio, mutex->owner_orig_prio) :
181 mutex->owner_orig_prio;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400182
Qi Yang89c4a072022-08-12 10:44:38 +0800183 LOG_DBG("adjusting prio down on mutex %p", mutex);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400184
Qi Yang89c4a072022-08-12 10:44:38 +0800185 resched = adjust_owner_prio(mutex, new_prio) || resched;
186 }
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400187
Andy Ross6f139802019-08-20 11:21:28 -0700188 if (resched) {
189 z_reschedule(&lock, key);
190 } else {
191 k_spin_unlock(&lock, key);
192 }
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400193
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100194 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EAGAIN);
195
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400196 return -EAGAIN;
197}
198
Andrew Boie2f7519b2017-09-29 03:33:06 -0700199#ifdef CONFIG_USERSPACE
Andy Ross78327382020-03-05 15:18:14 -0800200static inline int z_vrfy_k_mutex_lock(struct k_mutex *mutex,
201 k_timeout_t timeout)
Andrew Boie2f7519b2017-09-29 03:33:06 -0700202{
Anas Nashif9c4d8812023-09-27 11:09:45 +0000203 Z_OOPS(K_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
Andy Ross65649742019-08-06 13:34:31 -0700204 return z_impl_k_mutex_lock(mutex, timeout);
Andrew Boie2f7519b2017-09-29 03:33:06 -0700205}
Andy Ross65649742019-08-06 13:34:31 -0700206#include <syscalls/k_mutex_lock_mrsh.c>
Andrew Boie2f7519b2017-09-29 03:33:06 -0700207#endif
208
Anas Nashif86bb2d02019-05-04 10:18:13 -0400209int z_impl_k_mutex_unlock(struct k_mutex *mutex)
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400210{
Flavio Ceolin43693632018-11-01 15:17:03 -0700211 struct k_thread *new_owner;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400212
Andrew Boie6af97932020-05-27 11:48:30 -0700213 __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
214
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100215 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, unlock, mutex);
216
Anas Nashif86bb2d02019-05-04 10:18:13 -0400217 CHECKIF(mutex->owner == NULL) {
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100218 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EINVAL);
219
Anas Nashif86bb2d02019-05-04 10:18:13 -0400220 return -EINVAL;
221 }
222 /*
223 * The current thread does not own the mutex.
224 */
225 CHECKIF(mutex->owner != _current) {
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100226 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EPERM);
227
Anas Nashif86bb2d02019-05-04 10:18:13 -0400228 return -EPERM;
229 }
230
231 /*
232 * Attempt to unlock a mutex which is unlocked. mutex->lock_count
233 * cannot be zero if the current thread is equal to mutex->owner,
234 * therefore no underflow check is required. Use assert to catch
235 * undefined behavior.
236 */
237 __ASSERT_NO_MSG(mutex->lock_count > 0U);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400238
Anas Nashif2c5d4042019-12-02 10:24:08 -0500239 LOG_DBG("mutex %p lock_count: %d", mutex, mutex->lock_count);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400240
Anas Nashif86bb2d02019-05-04 10:18:13 -0400241 /*
242 * If we are the owner and count is greater than 1, then decrement
243 * the count and return and keep current thread as the owner.
244 */
Chih Hung Yu0ef77d42021-06-30 14:40:16 +0800245 if (mutex->lock_count > 1U) {
Nicolás Bértolo258fd2d2018-12-04 14:49:49 -0300246 mutex->lock_count--;
Adithya Baglody87e592e2018-10-25 14:10:52 +0530247 goto k_mutex_unlock_return;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400248 }
249
Andy Ross7df02162018-07-25 13:55:59 -0700250 k_spinlock_key_t key = k_spin_lock(&lock);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400251
252 adjust_owner_prio(mutex, mutex->owner_orig_prio);
253
Anas Nashif86bb2d02019-05-04 10:18:13 -0400254 /* Get the new owner, if any */
Patrik Flykt4344e272019-03-08 14:19:05 -0700255 new_owner = z_unpend_first_thread(&mutex->wait_q);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400256
Andy Ross5792ee62018-04-05 08:55:47 -0700257 mutex->owner = new_owner;
258
Anas Nashif2c5d4042019-12-02 10:24:08 -0500259 LOG_DBG("new owner of mutex %p: %p (prio: %d)",
Benjamin Walsha4e033f2016-11-18 16:08:24 -0500260 mutex, new_owner, new_owner ? new_owner->base.prio : -1000);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400261
Flavio Ceolin4218d5f2018-09-17 09:39:51 -0700262 if (new_owner != NULL) {
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400263 /*
264 * new owner is already of higher or equal prio than first
265 * waiter since the wait queue is priority-based: no need to
Nazar Kazakovf483b1b2022-03-16 21:07:43 +0000266 * adjust its priority
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400267 */
Benjamin Walshf6ca7de2016-11-08 10:36:50 -0500268 mutex->owner_orig_prio = new_owner->base.prio;
Andy Ross70220002019-11-21 09:38:38 -0800269 arch_thread_return_value_set(new_owner, 0);
270 z_ready_thread(new_owner);
271 z_reschedule(&lock, key);
Nicolás Bértolo258fd2d2018-12-04 14:49:49 -0300272 } else {
Patrik Flykt24d71432019-03-26 19:57:45 -0600273 mutex->lock_count = 0U;
Andy Ross7df02162018-07-25 13:55:59 -0700274 k_spin_unlock(&lock, key);
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400275 }
276
Andy Ross5792ee62018-04-05 08:55:47 -0700277
Adithya Baglody87e592e2018-10-25 14:10:52 +0530278k_mutex_unlock_return:
Torbjörn Lekselled6148a2021-03-26 10:28:23 +0100279 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, 0);
Anas Nashif86bb2d02019-05-04 10:18:13 -0400280
281 return 0;
Benjamin Walsh456c6da2016-09-02 18:55:39 -0400282}
Andrew Boie2f7519b2017-09-29 03:33:06 -0700283
284#ifdef CONFIG_USERSPACE
Anas Nashif86bb2d02019-05-04 10:18:13 -0400285static inline int z_vrfy_k_mutex_unlock(struct k_mutex *mutex)
Leandro Pereirabf44bac2018-04-05 09:25:01 -0700286{
Anas Nashif9c4d8812023-09-27 11:09:45 +0000287 Z_OOPS(K_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
Anas Nashif86bb2d02019-05-04 10:18:13 -0400288 return z_impl_k_mutex_unlock(mutex);
Leandro Pereirabf44bac2018-04-05 09:25:01 -0700289}
Andy Ross65649742019-08-06 13:34:31 -0700290#include <syscalls/k_mutex_unlock_mrsh.c>
Andrew Boie2f7519b2017-09-29 03:33:06 -0700291#endif
Peter Mitsis6df8efe2023-05-11 14:06:46 -0400292
293#ifdef CONFIG_OBJ_CORE_MUTEX
294static int init_mutex_obj_core_list(void)
295{
296 /* Initialize mutex object type */
297
298 z_obj_type_init(&obj_type_mutex, K_OBJ_TYPE_MUTEX_ID,
299 offsetof(struct k_mutex, obj_core));
300
301 /* Initialize and link statically defined mutexs */
302
303 STRUCT_SECTION_FOREACH(k_mutex, mutex) {
304 k_obj_core_init_and_link(K_OBJ_CORE(mutex), &obj_type_mutex);
305 }
306
307 return 0;
308}
309
310SYS_INIT(init_mutex_obj_core_list, PRE_KERNEL_1,
311 CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
312#endif