Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016 Wind River Systems, Inc.
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
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
17 : : * reverse order in which it was acquired. Furthermore each subsequent mutex
18 : : * 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 : :
29 : : #include <kernel.h>
30 : : #include <kernel_structs.h>
31 : : #include <toolchain.h>
32 : : #include <ksched.h>
33 : : #include <wait_q.h>
34 : : #include <errno.h>
35 : : #include <init.h>
36 : : #include <syscall_handler.h>
37 : : #include <tracing/tracing.h>
38 : : #include <sys/check.h>
39 : : #include <logging/log.h>
40 : : LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
41 : :
42 : : /* 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 : : */
47 : : static struct k_spinlock lock;
48 : :
49 : 0 : int z_impl_k_mutex_init(struct k_mutex *mutex)
50 : : {
51 : 0 : mutex->owner = NULL;
52 : 0 : mutex->lock_count = 0U;
53 : :
54 : 0 : z_waitq_init(&mutex->wait_q);
55 : :
56 : 0 : z_object_init(mutex);
57 : :
58 : : SYS_PORT_TRACING_OBJ_INIT(k_mutex, mutex, 0);
59 : :
60 : 0 : return 0;
61 : : }
62 : :
63 : : #ifdef CONFIG_USERSPACE
64 : : static inline int z_vrfy_k_mutex_init(struct k_mutex *mutex)
65 : : {
66 : : Z_OOPS(Z_SYSCALL_OBJ_INIT(mutex, K_OBJ_MUTEX));
67 : : return z_impl_k_mutex_init(mutex);
68 : : }
69 : : #include <syscalls/k_mutex_init_mrsh.c>
70 : : #endif
71 : :
72 : 0 : static int32_t new_prio_for_inheritance(int32_t target, int32_t limit)
73 : : {
74 [ # # ]: 0 : int new_prio = z_is_prio_higher(target, limit) ? target : limit;
75 : :
76 : 0 : new_prio = z_get_new_prio_with_ceiling(new_prio);
77 : :
78 : 0 : return new_prio;
79 : : }
80 : :
81 : 0 : static bool adjust_owner_prio(struct k_mutex *mutex, int32_t new_prio)
82 : : {
83 [ # # ]: 0 : if (mutex->owner->base.prio != new_prio) {
84 : :
85 [ # # # # ]: 0 : LOG_DBG("%p (ready (y/n): %c) prio changed to %d (was %d)",
86 : : mutex->owner, z_is_thread_ready(mutex->owner) ?
87 : : 'y' : 'n',
88 : : new_prio, mutex->owner->base.prio);
89 : :
90 : 0 : return z_set_prio(mutex->owner, new_prio);
91 : : }
92 : 0 : return false;
93 : : }
94 : :
95 : 0 : int z_impl_k_mutex_lock(struct k_mutex *mutex, k_timeout_t timeout)
96 : : {
97 : : int new_prio;
98 : : k_spinlock_key_t key;
99 : 0 : bool resched = false;
100 : :
101 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
102 : :
103 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, lock, mutex, timeout);
104 : :
105 : 0 : key = k_spin_lock(&lock);
106 : :
107 [ # # # # ]: 0 : if (likely((mutex->lock_count == 0U) || (mutex->owner == _current))) {
108 : :
109 : 0 : mutex->owner_orig_prio = (mutex->lock_count == 0U) ?
110 [ # # ]: 0 : _current->base.prio :
111 : : mutex->owner_orig_prio;
112 : :
113 : 0 : mutex->lock_count++;
114 : 0 : mutex->owner = _current;
115 : :
116 [ # # ]: 0 : LOG_DBG("%p took mutex %p, count: %d, orig prio: %d",
117 : : _current, mutex, mutex->lock_count,
118 : : mutex->owner_orig_prio);
119 : :
120 : 0 : k_spin_unlock(&lock, key);
121 : :
122 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
123 : :
124 : 0 : return 0;
125 : : }
126 : :
127 [ # # ]: 0 : if (unlikely(K_TIMEOUT_EQ(timeout, K_NO_WAIT))) {
128 : 0 : k_spin_unlock(&lock, key);
129 : :
130 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EBUSY);
131 : :
132 : 0 : return -EBUSY;
133 : : }
134 : :
135 : : SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mutex, lock, mutex, timeout);
136 : :
137 : 0 : new_prio = new_prio_for_inheritance(_current->base.prio,
138 : 0 : mutex->owner->base.prio);
139 : :
140 [ # # ]: 0 : LOG_DBG("adjusting prio up on mutex %p", mutex);
141 : :
142 [ # # ]: 0 : if (z_is_prio_higher(new_prio, mutex->owner->base.prio)) {
143 : 0 : resched = adjust_owner_prio(mutex, new_prio);
144 : : }
145 : :
146 : 0 : int got_mutex = z_pend_curr(&lock, key, &mutex->wait_q, timeout);
147 : :
148 [ # # ]: 0 : LOG_DBG("on mutex %p got_mutex value: %d", mutex, got_mutex);
149 : :
150 [ # # # # ]: 0 : LOG_DBG("%p got mutex %p (y/n): %c", _current, mutex,
151 : : got_mutex ? 'y' : 'n');
152 : :
153 [ # # ]: 0 : if (got_mutex == 0) {
154 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, 0);
155 : 0 : return 0;
156 : : }
157 : :
158 : : /* timed out */
159 : :
160 [ # # ]: 0 : LOG_DBG("%p timeout on mutex %p", _current, mutex);
161 : :
162 : 0 : key = k_spin_lock(&lock);
163 : :
164 : 0 : struct k_thread *waiter = z_waitq_head(&mutex->wait_q);
165 : :
166 : 0 : new_prio = (waiter != NULL) ?
167 [ # # ]: 0 : new_prio_for_inheritance(waiter->base.prio, mutex->owner_orig_prio) :
168 : : mutex->owner_orig_prio;
169 : :
170 [ # # ]: 0 : LOG_DBG("adjusting prio down on mutex %p", mutex);
171 : :
172 [ # # # # ]: 0 : resched = adjust_owner_prio(mutex, new_prio) || resched;
173 : :
174 [ # # ]: 0 : if (resched) {
175 : 0 : z_reschedule(&lock, key);
176 : : } else {
177 : 0 : k_spin_unlock(&lock, key);
178 : : }
179 : :
180 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, lock, mutex, timeout, -EAGAIN);
181 : :
182 : 0 : return -EAGAIN;
183 : : }
184 : :
185 : : #ifdef CONFIG_USERSPACE
186 : : static inline int z_vrfy_k_mutex_lock(struct k_mutex *mutex,
187 : : k_timeout_t timeout)
188 : : {
189 : : Z_OOPS(Z_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
190 : : return z_impl_k_mutex_lock(mutex, timeout);
191 : : }
192 : : #include <syscalls/k_mutex_lock_mrsh.c>
193 : : #endif
194 : :
195 : 0 : int z_impl_k_mutex_unlock(struct k_mutex *mutex)
196 : : {
197 : : struct k_thread *new_owner;
198 : :
199 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "mutexes cannot be used inside ISRs");
200 : :
201 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mutex, unlock, mutex);
202 : :
203 [ # # ]: 0 : CHECKIF(mutex->owner == NULL) {
204 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EINVAL);
205 : :
206 : 0 : return -EINVAL;
207 : : }
208 : : /*
209 : : * The current thread does not own the mutex.
210 : : */
211 [ # # ]: 0 : CHECKIF(mutex->owner != _current) {
212 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, -EPERM);
213 : :
214 : 0 : return -EPERM;
215 : : }
216 : :
217 : : /*
218 : : * Attempt to unlock a mutex which is unlocked. mutex->lock_count
219 : : * cannot be zero if the current thread is equal to mutex->owner,
220 : : * therefore no underflow check is required. Use assert to catch
221 : : * undefined behavior.
222 : : */
223 [ # # ]: 0 : __ASSERT_NO_MSG(mutex->lock_count > 0U);
224 : :
225 : 0 : z_sched_lock();
226 : :
227 [ # # ]: 0 : LOG_DBG("mutex %p lock_count: %d", mutex, mutex->lock_count);
228 : :
229 : : /*
230 : : * If we are the owner and count is greater than 1, then decrement
231 : : * the count and return and keep current thread as the owner.
232 : : */
233 [ # # ]: 0 : if (mutex->lock_count > 1U) {
234 : 0 : mutex->lock_count--;
235 : 0 : goto k_mutex_unlock_return;
236 : : }
237 : :
238 : 0 : k_spinlock_key_t key = k_spin_lock(&lock);
239 : :
240 : 0 : adjust_owner_prio(mutex, mutex->owner_orig_prio);
241 : :
242 : : /* Get the new owner, if any */
243 : 0 : new_owner = z_unpend_first_thread(&mutex->wait_q);
244 : :
245 : 0 : mutex->owner = new_owner;
246 : :
247 [ # # # # ]: 0 : LOG_DBG("new owner of mutex %p: %p (prio: %d)",
248 : : mutex, new_owner, new_owner ? new_owner->base.prio : -1000);
249 : :
250 [ # # ]: 0 : if (new_owner != NULL) {
251 : : /*
252 : : * new owner is already of higher or equal prio than first
253 : : * waiter since the wait queue is priority-based: no need to
254 : : * adjust its priority
255 : : */
256 : 0 : mutex->owner_orig_prio = new_owner->base.prio;
257 : 0 : arch_thread_return_value_set(new_owner, 0);
258 : 0 : z_ready_thread(new_owner);
259 : 0 : z_reschedule(&lock, key);
260 : : } else {
261 : 0 : mutex->lock_count = 0U;
262 : 0 : k_spin_unlock(&lock, key);
263 : : }
264 : :
265 : :
266 : 0 : k_mutex_unlock_return:
267 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mutex, unlock, mutex, 0);
268 : :
269 : 0 : k_sched_unlock();
270 : :
271 : 0 : return 0;
272 : : }
273 : :
274 : : #ifdef CONFIG_USERSPACE
275 : : static inline int z_vrfy_k_mutex_unlock(struct k_mutex *mutex)
276 : : {
277 : : Z_OOPS(Z_SYSCALL_OBJ(mutex, K_OBJ_MUTEX));
278 : : return z_impl_k_mutex_unlock(mutex);
279 : : }
280 : : #include <syscalls/k_mutex_unlock_mrsh.c>
281 : : #endif
|