xref: /freebsd/lib/libthr/thread/thr_umtx.c (revision 40a8ac8f62b535d30349faf28cf47106b7041b83)
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
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
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
51 _thr_urwlock_init(struct urwlock *rwl)
52 {
53 	static struct urwlock default_rwl = DEFAULT_URWLOCK;
54 	*rwl = default_rwl;
55 }
56 
57 int
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
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
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
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
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
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
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
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
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
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
225 _thr_ucond_init(struct ucond *cv)
226 {
227 	bzero(cv, sizeof(struct ucond));
228 }
229 
230 int
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
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
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
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
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
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
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
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
338 _thr_rwl_unlock(struct urwlock *rwlock)
339 {
340 	if (_thr_rwlock_unlock(rwlock))
341 		PANIC("unlock error");
342 }
343