1 /*
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
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
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 *
28 */
29
30 #include "thr_private.h"
31 #include "thr_umtx.h"
32
33 #ifndef HAS__UMTX_OP_ERR
_umtx_op_err(void * obj,int op,u_long val,void * uaddr,void * uaddr2)34 int _umtx_op_err(void *obj, int op, u_long val, void *uaddr, void *uaddr2)
35 {
36 if (_umtx_op(obj, op, val, uaddr, uaddr2) == -1)
37 return (errno);
38 return (0);
39 }
40 #endif
41
42 void
_thr_umutex_init(struct umutex * mtx)43 _thr_umutex_init(struct umutex *mtx)
44 {
45 static struct umutex default_mtx = DEFAULT_UMUTEX;
46
47 *mtx = default_mtx;
48 }
49
50 void
_thr_urwlock_init(struct urwlock * rwl)51 _thr_urwlock_init(struct urwlock *rwl)
52 {
53 static struct urwlock default_rwl = DEFAULT_URWLOCK;
54 *rwl = default_rwl;
55 }
56
57 int
__thr_umutex_lock(struct umutex * mtx,uint32_t id)58 __thr_umutex_lock(struct umutex *mtx, uint32_t id)
59 {
60 uint32_t owner;
61
62 if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
63 for (;;) {
64 /* wait in kernel */
65 _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0);
66
67 owner = mtx->m_owner;
68 if ((owner & ~UMUTEX_CONTESTED) == 0 &&
69 atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
70 return (0);
71 }
72 }
73
74 return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0);
75 }
76
77 #define SPINLOOPS 1000
78
79 int
__thr_umutex_lock_spin(struct umutex * mtx,uint32_t id)80 __thr_umutex_lock_spin(struct umutex *mtx, uint32_t id)
81 {
82 uint32_t owner;
83
84 if (!_thr_is_smp)
85 return __thr_umutex_lock(mtx, id);
86
87 if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
88 for (;;) {
89 int count = SPINLOOPS;
90 while (count--) {
91 owner = mtx->m_owner;
92 if ((owner & ~UMUTEX_CONTESTED) == 0) {
93 if (atomic_cmpset_acq_32(
94 &mtx->m_owner,
95 owner, id|owner)) {
96 return (0);
97 }
98 }
99 CPU_SPINWAIT;
100 }
101
102 /* wait in kernel */
103 _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0, 0, 0);
104 }
105 }
106
107 return _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0, 0, 0);
108 }
109
110 int
__thr_umutex_timedlock(struct umutex * mtx,uint32_t id,const struct timespec * abstime)111 __thr_umutex_timedlock(struct umutex *mtx, uint32_t id,
112 const struct timespec *abstime)
113 {
114 struct _umtx_time *tm_p, timeout;
115 size_t tm_size;
116 uint32_t owner;
117 int ret;
118
119 if (abstime == NULL) {
120 tm_p = NULL;
121 tm_size = 0;
122 } else {
123 timeout._clockid = CLOCK_REALTIME;
124 timeout._flags = UMTX_ABSTIME;
125 timeout._timeout = *abstime;
126 tm_p = &timeout;
127 tm_size = sizeof(timeout);
128 }
129
130 for (;;) {
131 if ((mtx->m_flags & (UMUTEX_PRIO_PROTECT | UMUTEX_PRIO_INHERIT)) == 0) {
132
133 /* wait in kernel */
134 ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_WAIT, 0,
135 (void *)tm_size, __DECONST(void *, tm_p));
136
137 /* now try to lock it */
138 owner = mtx->m_owner;
139 if ((owner & ~UMUTEX_CONTESTED) == 0 &&
140 atomic_cmpset_acq_32(&mtx->m_owner, owner, id|owner))
141 return (0);
142 } else {
143 ret = _umtx_op_err(mtx, UMTX_OP_MUTEX_LOCK, 0,
144 (void *)tm_size, __DECONST(void *, tm_p));
145 if (ret == 0)
146 break;
147 }
148 if (ret == ETIMEDOUT)
149 break;
150 }
151 return (ret);
152 }
153
154 int
__thr_umutex_unlock(struct umutex * mtx,uint32_t id)155 __thr_umutex_unlock(struct umutex *mtx, uint32_t id)
156 {
157 return _umtx_op_err(mtx, UMTX_OP_MUTEX_UNLOCK, 0, 0, 0);
158 }
159
160 int
__thr_umutex_trylock(struct umutex * mtx)161 __thr_umutex_trylock(struct umutex *mtx)
162 {
163 return _umtx_op_err(mtx, UMTX_OP_MUTEX_TRYLOCK, 0, 0, 0);
164 }
165
166 int
__thr_umutex_set_ceiling(struct umutex * mtx,uint32_t ceiling,uint32_t * oldceiling)167 __thr_umutex_set_ceiling(struct umutex *mtx, uint32_t ceiling,
168 uint32_t *oldceiling)
169 {
170 return _umtx_op_err(mtx, UMTX_OP_SET_CEILING, ceiling, oldceiling, 0);
171 }
172
173 int
_thr_umtx_wait(volatile long * mtx,long id,const struct timespec * timeout)174 _thr_umtx_wait(volatile long *mtx, long id, const struct timespec *timeout)
175 {
176 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
177 timeout->tv_nsec <= 0)))
178 return (ETIMEDOUT);
179 return _umtx_op_err(__DEVOLATILE(void *, mtx), UMTX_OP_WAIT, id, 0,
180 __DECONST(void*, timeout));
181 }
182
183 int
_thr_umtx_wait_uint(volatile u_int * mtx,u_int id,const struct timespec * timeout,int shared)184 _thr_umtx_wait_uint(volatile u_int *mtx, u_int id, const struct timespec *timeout, int shared)
185 {
186 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
187 timeout->tv_nsec <= 0)))
188 return (ETIMEDOUT);
189 return _umtx_op_err(__DEVOLATILE(void *, mtx),
190 shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id, 0,
191 __DECONST(void*, timeout));
192 }
193
194 int
_thr_umtx_timedwait_uint(volatile u_int * mtx,u_int id,int clockid,const struct timespec * abstime,int shared)195 _thr_umtx_timedwait_uint(volatile u_int *mtx, u_int id, int clockid,
196 const struct timespec *abstime, int shared)
197 {
198 struct _umtx_time *tm_p, timeout;
199 size_t tm_size;
200
201 if (abstime == NULL) {
202 tm_p = NULL;
203 tm_size = 0;
204 } else {
205 timeout._clockid = clockid;
206 timeout._flags = UMTX_ABSTIME;
207 timeout._timeout = *abstime;
208 tm_p = &timeout;
209 tm_size = sizeof(timeout);
210 }
211
212 return _umtx_op_err(__DEVOLATILE(void *, mtx),
213 shared ? UMTX_OP_WAIT_UINT : UMTX_OP_WAIT_UINT_PRIVATE, id,
214 (void *)tm_size, __DECONST(void *, tm_p));
215 }
216
217 int
_thr_umtx_wake(volatile void * mtx,int nr_wakeup,int shared)218 _thr_umtx_wake(volatile void *mtx, int nr_wakeup, int shared)
219 {
220 return _umtx_op_err(__DEVOLATILE(void *, mtx), shared ? UMTX_OP_WAKE : UMTX_OP_WAKE_PRIVATE,
221 nr_wakeup, 0, 0);
222 }
223
224 void
_thr_ucond_init(struct ucond * cv)225 _thr_ucond_init(struct ucond *cv)
226 {
227 bzero(cv, sizeof(struct ucond));
228 }
229
230 int
_thr_ucond_wait(struct ucond * cv,struct umutex * m,const struct timespec * timeout,int flags)231 _thr_ucond_wait(struct ucond *cv, struct umutex *m,
232 const struct timespec *timeout, int flags)
233 {
234 if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 &&
235 timeout->tv_nsec <= 0))) {
236 struct pthread *curthread = _get_curthread();
237 _thr_umutex_unlock(m, TID(curthread));
238 return (ETIMEDOUT);
239 }
240 return _umtx_op_err(cv, UMTX_OP_CV_WAIT, flags,
241 m, __DECONST(void*, timeout));
242 }
243
244 int
_thr_ucond_signal(struct ucond * cv)245 _thr_ucond_signal(struct ucond *cv)
246 {
247 if (!cv->c_has_waiters)
248 return (0);
249 return _umtx_op_err(cv, UMTX_OP_CV_SIGNAL, 0, NULL, NULL);
250 }
251
252 int
_thr_ucond_broadcast(struct ucond * cv)253 _thr_ucond_broadcast(struct ucond *cv)
254 {
255 if (!cv->c_has_waiters)
256 return (0);
257 return _umtx_op_err(cv, UMTX_OP_CV_BROADCAST, 0, NULL, NULL);
258 }
259
260 int
__thr_rwlock_rdlock(struct urwlock * rwlock,int flags,const struct timespec * tsp)261 __thr_rwlock_rdlock(struct urwlock *rwlock, int flags,
262 const struct timespec *tsp)
263 {
264 struct _umtx_time timeout, *tm_p;
265 size_t tm_size;
266
267 if (tsp == NULL) {
268 tm_p = NULL;
269 tm_size = 0;
270 } else {
271 timeout._timeout = *tsp;
272 timeout._flags = UMTX_ABSTIME;
273 timeout._clockid = CLOCK_REALTIME;
274 tm_p = &timeout;
275 tm_size = sizeof(timeout);
276 }
277 return _umtx_op_err(rwlock, UMTX_OP_RW_RDLOCK, flags, (void *)tm_size, tm_p);
278 }
279
280 int
__thr_rwlock_wrlock(struct urwlock * rwlock,const struct timespec * tsp)281 __thr_rwlock_wrlock(struct urwlock *rwlock, const struct timespec *tsp)
282 {
283 struct _umtx_time timeout, *tm_p;
284 size_t tm_size;
285
286 if (tsp == NULL) {
287 tm_p = NULL;
288 tm_size = 0;
289 } else {
290 timeout._timeout = *tsp;
291 timeout._flags = UMTX_ABSTIME;
292 timeout._clockid = CLOCK_REALTIME;
293 tm_p = &timeout;
294 tm_size = sizeof(timeout);
295 }
296 return _umtx_op_err(rwlock, UMTX_OP_RW_WRLOCK, 0, (void *)tm_size, tm_p);
297 }
298
299 int
__thr_rwlock_unlock(struct urwlock * rwlock)300 __thr_rwlock_unlock(struct urwlock *rwlock)
301 {
302 return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL);
303 }
304
305 void
_thr_rwl_rdlock(struct urwlock * rwlock)306 _thr_rwl_rdlock(struct urwlock *rwlock)
307 {
308 int ret;
309
310 for (;;) {
311 if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0)
312 return;
313 ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL);
314 if (ret == 0)
315 return;
316 if (ret != EINTR)
317 PANIC("rdlock error");
318 }
319 }
320
321 void
_thr_rwl_wrlock(struct urwlock * rwlock)322 _thr_rwl_wrlock(struct urwlock *rwlock)
323 {
324 int ret;
325
326 for (;;) {
327 if (_thr_rwlock_trywrlock(rwlock) == 0)
328 return;
329 ret = __thr_rwlock_wrlock(rwlock, NULL);
330 if (ret == 0)
331 return;
332 if (ret != EINTR)
333 PANIC("wrlock error");
334 }
335 }
336
337 void
_thr_rwl_unlock(struct urwlock * rwlock)338 _thr_rwl_unlock(struct urwlock *rwlock)
339 {
340 if (_thr_rwlock_unlock(rwlock))
341 PANIC("unlock error");
342 }
343