0e1b8366c0993a1809879ceb41f9da9416b56e43
[platform/core/security/tef-optee_os.git] / core / arch / arm / kernel / mutex.c
1 /*
2  * Copyright (c) 2015, Linaro Limited
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <kernel/mutex.h>
29 #include <kernel/panic.h>
30 #include <kernel/spinlock.h>
31 #include <kernel/thread.h>
32 #include <trace.h>
33
34 void mutex_init(struct mutex *m)
35 {
36         *m = (struct mutex)MUTEX_INITIALIZER;
37 }
38
39 static void __mutex_lock(struct mutex *m, const char *fname, int lineno)
40 {
41         assert_have_no_spinlock();
42         assert(thread_get_id_may_fail() != -1);
43
44         while (true) {
45                 uint32_t old_itr_status;
46                 enum mutex_value old_value;
47                 struct wait_queue_elem wqe;
48
49                 /*
50                  * If the mutex is locked we need to initialize the wqe
51                  * before releasing the spinlock to guarantee that we don't
52                  * miss the wakeup from mutex_unlock().
53                  *
54                  * If the mutex is unlocked we don't need to use the wqe at
55                  * all.
56                  */
57
58                 old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
59                 cpu_spin_lock(&m->spin_lock);
60
61                 old_value = m->value;
62                 if (old_value == MUTEX_VALUE_LOCKED) {
63                         wq_wait_init(&m->wq, &wqe);
64                 } else {
65                         m->value = MUTEX_VALUE_LOCKED;
66                         thread_add_mutex(m);
67                 }
68
69                 cpu_spin_unlock(&m->spin_lock);
70                 thread_unmask_exceptions(old_itr_status);
71
72                 if (old_value == MUTEX_VALUE_LOCKED) {
73                         /*
74                          * Someone else is holding the lock, wait in normal
75                          * world for the lock to become available.
76                          */
77                         wq_wait_final(&m->wq, &wqe, m, fname, lineno);
78                 } else
79                         return;
80         }
81 }
82
83 static void __mutex_unlock(struct mutex *m, const char *fname, int lineno)
84 {
85         uint32_t old_itr_status;
86
87         assert_have_no_spinlock();
88         assert(thread_get_id_may_fail() != -1);
89
90         old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
91         cpu_spin_lock(&m->spin_lock);
92
93         if (m->value != MUTEX_VALUE_LOCKED)
94                 panic();
95
96         thread_rem_mutex(m);
97         m->value = MUTEX_VALUE_UNLOCKED;
98
99         cpu_spin_unlock(&m->spin_lock);
100         thread_unmask_exceptions(old_itr_status);
101
102         wq_wake_one(&m->wq, m, fname, lineno);
103 }
104
105 static bool __mutex_trylock(struct mutex *m, const char *fname __unused,
106                         int lineno __unused)
107 {
108         uint32_t old_itr_status;
109         enum mutex_value old_value;
110
111         assert_have_no_spinlock();
112         assert(thread_get_id_may_fail() != -1);
113
114         old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
115         cpu_spin_lock(&m->spin_lock);
116
117         old_value = m->value;
118         if (old_value == MUTEX_VALUE_UNLOCKED) {
119                 m->value = MUTEX_VALUE_LOCKED;
120                 thread_add_mutex(m);
121         }
122
123         cpu_spin_unlock(&m->spin_lock);
124         thread_unmask_exceptions(old_itr_status);
125
126         return old_value == MUTEX_VALUE_UNLOCKED;
127 }
128
129 #ifdef CFG_MUTEX_DEBUG
130 void mutex_unlock_debug(struct mutex *m, const char *fname, int lineno)
131 {
132         __mutex_unlock(m, fname, lineno);
133 }
134
135 void mutex_lock_debug(struct mutex *m, const char *fname, int lineno)
136 {
137         __mutex_lock(m, fname, lineno);
138 }
139
140 bool mutex_trylock_debug(struct mutex *m, const char *fname, int lineno)
141 {
142         return __mutex_trylock(m, fname, lineno);
143 }
144 #else
145 void mutex_unlock(struct mutex *m)
146 {
147         __mutex_unlock(m, NULL, -1);
148 }
149
150 void mutex_lock(struct mutex *m)
151 {
152         __mutex_lock(m, NULL, -1);
153 }
154
155 bool mutex_trylock(struct mutex *m)
156 {
157         return __mutex_trylock(m, NULL, -1);
158 }
159 #endif
160
161
162
163 void mutex_destroy(struct mutex *m)
164 {
165         /*
166          * Caller guarantees that no one will try to take the mutex so
167          * there's no need to take the spinlock before accessing it.
168          */
169         if (m->value != MUTEX_VALUE_UNLOCKED)
170                 panic();
171         if (!wq_is_empty(&m->wq))
172                 panic("waitqueue not empty");
173 }
174
175 void condvar_init(struct condvar *cv)
176 {
177         *cv = (struct condvar)CONDVAR_INITIALIZER;
178 }
179
180 void condvar_destroy(struct condvar *cv)
181 {
182         if (cv->m && wq_have_condvar(&cv->m->wq, cv))
183                 panic();
184
185         condvar_init(cv);
186 }
187
188 static void cv_signal(struct condvar *cv, bool only_one, const char *fname,
189                         int lineno)
190 {
191         uint32_t old_itr_status;
192         struct mutex *m;
193
194         old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
195         cpu_spin_lock(&cv->spin_lock);
196         m = cv->m;
197         cpu_spin_unlock(&cv->spin_lock);
198         thread_unmask_exceptions(old_itr_status);
199
200         if (m)
201                 wq_promote_condvar(&m->wq, cv, only_one, m, fname, lineno);
202
203 }
204
205 #ifdef CFG_MUTEX_DEBUG
206 void condvar_signal_debug(struct condvar *cv, const char *fname, int lineno)
207 {
208         cv_signal(cv, true /* only one */, fname, lineno);
209 }
210
211 void condvar_broadcast_debug(struct condvar *cv, const char *fname, int lineno)
212 {
213         cv_signal(cv, false /* all */, fname, lineno);
214 }
215
216 #else
217 void condvar_signal(struct condvar *cv)
218 {
219         cv_signal(cv, true /* only one */, NULL, -1);
220 }
221
222 void condvar_broadcast(struct condvar *cv)
223 {
224         cv_signal(cv, false /* all */, NULL, -1);
225 }
226 #endif /*CFG_MUTEX_DEBUG*/
227
228 static void __condvar_wait(struct condvar *cv, struct mutex *m,
229                         const char *fname, int lineno)
230 {
231         uint32_t old_itr_status;
232         struct wait_queue_elem wqe;
233
234         old_itr_status = thread_mask_exceptions(THREAD_EXCP_ALL);
235
236         /* Link this condvar to this mutex until reinitialized */
237         cpu_spin_lock(&cv->spin_lock);
238         if (cv->m && cv->m != m)
239                 panic("invalid mutex");
240
241         cv->m = m;
242         cpu_spin_unlock(&cv->spin_lock);
243
244         cpu_spin_lock(&m->spin_lock);
245
246         /* Add to mutex wait queue as a condvar waiter */
247         wq_wait_init_condvar(&m->wq, &wqe, cv);
248
249         /* Unlock the mutex */
250         if (m->value != MUTEX_VALUE_LOCKED)
251                 panic();
252
253         thread_rem_mutex(m);
254         m->value = MUTEX_VALUE_UNLOCKED;
255
256         cpu_spin_unlock(&m->spin_lock);
257
258         thread_unmask_exceptions(old_itr_status);
259
260         /* Wake eventual waiters */
261         wq_wake_one(&m->wq, m, fname, lineno);
262
263         wq_wait_final(&m->wq, &wqe, m, fname, lineno);
264
265         mutex_lock(m);
266 }
267
268 #ifdef CFG_MUTEX_DEBUG
269 void condvar_wait_debug(struct condvar *cv, struct mutex *m,
270                         const char *fname, int lineno)
271 {
272         __condvar_wait(cv, m, fname, lineno);
273 }
274 #else
275 void condvar_wait(struct condvar *cv, struct mutex *m)
276 {
277         __condvar_wait(cv, m, NULL, -1);
278 }
279 #endif