1 /*
2  * Copyright (c) 2017 Intel Corporation
3  * Copyright (c) 2023 Meta
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "posix_internal.h"
9 
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/posix/pthread.h>
14 #include <zephyr/sys/bitarray.h>
15 #include <zephyr/sys/sem.h>
16 
17 LOG_MODULE_REGISTER(pthread_mutex, CONFIG_PTHREAD_MUTEX_LOG_LEVEL);
18 
19 static SYS_SEM_DEFINE(lock, 1, 1);
20 
21 int64_t timespec_to_timeoutms(const struct timespec *abstime);
22 
23 #define MUTEX_MAX_REC_LOCK 32767
24 
25 /*
26  *  Default mutex attrs.
27  */
28 static const struct pthread_mutexattr def_attr = {
29 	.type = PTHREAD_MUTEX_DEFAULT,
30 };
31 
32 static struct k_mutex posix_mutex_pool[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
33 static uint8_t posix_mutex_type[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
34 SYS_BITARRAY_DEFINE_STATIC(posix_mutex_bitarray, CONFIG_MAX_PTHREAD_MUTEX_COUNT);
35 
36 /*
37  * We reserve the MSB to mark a pthread_mutex_t as initialized (from the
38  * perspective of the application). With a linear space, this means that
39  * the theoretical pthread_mutex_t range is [0,2147483647].
40  */
41 BUILD_ASSERT(CONFIG_MAX_PTHREAD_MUTEX_COUNT < PTHREAD_OBJ_MASK_INIT,
42 	"CONFIG_MAX_PTHREAD_MUTEX_COUNT is too high");
43 
posix_mutex_to_offset(struct k_mutex * m)44 static inline size_t posix_mutex_to_offset(struct k_mutex *m)
45 {
46 	return m - posix_mutex_pool;
47 }
48 
to_posix_mutex_idx(pthread_mutex_t mut)49 static inline size_t to_posix_mutex_idx(pthread_mutex_t mut)
50 {
51 	return mark_pthread_obj_uninitialized(mut);
52 }
53 
get_posix_mutex(pthread_mutex_t mu)54 static struct k_mutex *get_posix_mutex(pthread_mutex_t mu)
55 {
56 	int actually_initialized;
57 	size_t bit = to_posix_mutex_idx(mu);
58 
59 	/* if the provided mutex does not claim to be initialized, its invalid */
60 	if (!is_pthread_obj_initialized(mu)) {
61 		LOG_DBG("Mutex is uninitialized (%x)", mu);
62 		return NULL;
63 	}
64 
65 	/* Mask off the MSB to get the actual bit index */
66 	if (sys_bitarray_test_bit(&posix_mutex_bitarray, bit, &actually_initialized) < 0) {
67 		LOG_DBG("Mutex is invalid (%x)", mu);
68 		return NULL;
69 	}
70 
71 	if (actually_initialized == 0) {
72 		/* The mutex claims to be initialized but is actually not */
73 		LOG_DBG("Mutex claims to be initialized (%x)", mu);
74 		return NULL;
75 	}
76 
77 	return &posix_mutex_pool[bit];
78 }
79 
to_posix_mutex(pthread_mutex_t * mu)80 struct k_mutex *to_posix_mutex(pthread_mutex_t *mu)
81 {
82 	int err;
83 	size_t bit;
84 	struct k_mutex *m;
85 
86 	if (*mu != PTHREAD_MUTEX_INITIALIZER) {
87 		return get_posix_mutex(*mu);
88 	}
89 
90 	/* Try and automatically associate a posix_mutex */
91 	if (sys_bitarray_alloc(&posix_mutex_bitarray, 1, &bit) < 0) {
92 		LOG_DBG("Unable to allocate pthread_mutex_t");
93 		return NULL;
94 	}
95 
96 	/* Record the associated posix_mutex in mu and mark as initialized */
97 	*mu = mark_pthread_obj_initialized(bit);
98 
99 	/* Initialize the posix_mutex */
100 	m = &posix_mutex_pool[bit];
101 
102 	err = k_mutex_init(m);
103 	__ASSERT_NO_MSG(err == 0);
104 
105 	return m;
106 }
107 
acquire_mutex(pthread_mutex_t * mu,k_timeout_t timeout)108 static int acquire_mutex(pthread_mutex_t *mu, k_timeout_t timeout)
109 {
110 	int type = -1;
111 	size_t bit = -1;
112 	int ret = EINVAL;
113 	size_t lock_count = -1;
114 	struct k_mutex *m = NULL;
115 	struct k_thread *owner = NULL;
116 
117 	SYS_SEM_LOCK(&lock) {
118 		m = to_posix_mutex(mu);
119 		if (m == NULL) {
120 			ret = EINVAL;
121 			SYS_SEM_LOCK_BREAK;
122 		}
123 
124 		LOG_DBG("Locking mutex %p with timeout %llx", m, timeout.ticks);
125 
126 		ret = 0;
127 		bit = posix_mutex_to_offset(m);
128 		type = posix_mutex_type[bit];
129 		owner = m->owner;
130 		lock_count = m->lock_count;
131 	}
132 
133 	if (ret != 0) {
134 		goto handle_error;
135 	}
136 
137 	if (owner == k_current_get()) {
138 		switch (type) {
139 		case PTHREAD_MUTEX_NORMAL:
140 			if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
141 				LOG_DBG("Timeout locking mutex %p", m);
142 				ret = EBUSY;
143 				break;
144 			}
145 			/* On most POSIX systems, this usually results in an infinite loop */
146 			LOG_DBG("Attempt to relock non-recursive mutex %p", m);
147 			do {
148 				(void)k_sleep(K_FOREVER);
149 			} while (true);
150 			CODE_UNREACHABLE;
151 			break;
152 		case PTHREAD_MUTEX_RECURSIVE:
153 			if (lock_count >= MUTEX_MAX_REC_LOCK) {
154 				LOG_DBG("Mutex %p locked recursively too many times", m);
155 				ret = EAGAIN;
156 			}
157 			break;
158 		case PTHREAD_MUTEX_ERRORCHECK:
159 			LOG_DBG("Attempt to recursively lock non-recursive mutex %p", m);
160 			ret = EDEADLK;
161 			break;
162 		default:
163 			__ASSERT(false, "invalid pthread type %d", type);
164 			ret = EINVAL;
165 			break;
166 		}
167 	}
168 
169 	if (ret == 0) {
170 		ret = k_mutex_lock(m, timeout);
171 		if (ret == -EAGAIN) {
172 			LOG_DBG("Timeout locking mutex %p", m);
173 			/*
174 			 * special quirk - k_mutex_lock() returns EAGAIN if a timeout occurs, but
175 			 * for pthreads, that means something different
176 			 */
177 			ret = ETIMEDOUT;
178 		}
179 	}
180 
181 handle_error:
182 	if (ret < 0) {
183 		LOG_DBG("k_mutex_unlock() failed: %d", ret);
184 		ret = -ret;
185 	}
186 
187 	if (ret == 0) {
188 		LOG_DBG("Locked mutex %p", m);
189 	}
190 
191 	return ret;
192 }
193 
194 /**
195  * @brief Lock POSIX mutex with non-blocking call.
196  *
197  * See IEEE 1003.1
198  */
pthread_mutex_trylock(pthread_mutex_t * m)199 int pthread_mutex_trylock(pthread_mutex_t *m)
200 {
201 	return acquire_mutex(m, K_NO_WAIT);
202 }
203 
204 /**
205  * @brief Lock POSIX mutex with timeout.
206  *
207  *
208  * See IEEE 1003.1
209  */
pthread_mutex_timedlock(pthread_mutex_t * m,const struct timespec * abstime)210 int pthread_mutex_timedlock(pthread_mutex_t *m,
211 			    const struct timespec *abstime)
212 {
213 	int32_t timeout = (int32_t)timespec_to_timeoutms(abstime);
214 	return acquire_mutex(m, K_MSEC(timeout));
215 }
216 
217 /**
218  * @brief Initialize POSIX mutex.
219  *
220  * See IEEE 1003.1
221  */
pthread_mutex_init(pthread_mutex_t * mu,const pthread_mutexattr_t * _attr)222 int pthread_mutex_init(pthread_mutex_t *mu, const pthread_mutexattr_t *_attr)
223 {
224 	size_t bit;
225 	struct k_mutex *m;
226 	const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
227 
228 	*mu = PTHREAD_MUTEX_INITIALIZER;
229 
230 	m = to_posix_mutex(mu);
231 	if (m == NULL) {
232 		return ENOMEM;
233 	}
234 
235 	bit = posix_mutex_to_offset(m);
236 	if (attr == NULL) {
237 		posix_mutex_type[bit] = def_attr.type;
238 	} else {
239 		posix_mutex_type[bit] = attr->type;
240 	}
241 
242 	LOG_DBG("Initialized mutex %p", m);
243 
244 	return 0;
245 }
246 
247 
248 /**
249  * @brief Lock POSIX mutex with blocking call.
250  *
251  * See IEEE 1003.1
252  */
pthread_mutex_lock(pthread_mutex_t * m)253 int pthread_mutex_lock(pthread_mutex_t *m)
254 {
255 	return acquire_mutex(m, K_FOREVER);
256 }
257 
258 /**
259  * @brief Unlock POSIX mutex.
260  *
261  * See IEEE 1003.1
262  */
pthread_mutex_unlock(pthread_mutex_t * mu)263 int pthread_mutex_unlock(pthread_mutex_t *mu)
264 {
265 	int ret;
266 	struct k_mutex *m;
267 
268 	m = get_posix_mutex(*mu);
269 	if (m == NULL) {
270 		return EINVAL;
271 	}
272 
273 	ret = k_mutex_unlock(m);
274 	if (ret < 0) {
275 		LOG_DBG("k_mutex_unlock() failed: %d", ret);
276 		return -ret;
277 	}
278 
279 	__ASSERT_NO_MSG(ret == 0);
280 	LOG_DBG("Unlocked mutex %p", m);
281 
282 	return 0;
283 }
284 
285 /**
286  * @brief Destroy POSIX mutex.
287  *
288  * See IEEE 1003.1
289  */
pthread_mutex_destroy(pthread_mutex_t * mu)290 int pthread_mutex_destroy(pthread_mutex_t *mu)
291 {
292 	int err;
293 	size_t bit;
294 	struct k_mutex *m;
295 
296 	m = get_posix_mutex(*mu);
297 	if (m == NULL) {
298 		return EINVAL;
299 	}
300 
301 	bit = to_posix_mutex_idx(*mu);
302 	err = sys_bitarray_free(&posix_mutex_bitarray, 1, bit);
303 	__ASSERT_NO_MSG(err == 0);
304 
305 	LOG_DBG("Destroyed mutex %p", m);
306 
307 	return 0;
308 }
309 
310 /**
311  * @brief Read protocol attribute for mutex.
312  *
313  * See IEEE 1003.1
314  */
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)315 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
316 				  int *protocol)
317 {
318 	if ((attr == NULL) || (protocol == NULL)) {
319 		return EINVAL;
320 	}
321 
322 	*protocol = PTHREAD_PRIO_NONE;
323 	return 0;
324 }
325 
326 /**
327  * @brief Set protocol attribute for mutex.
328  *
329  * See IEEE 1003.1
330  */
pthread_mutexattr_setprotocol(pthread_mutexattr_t * attr,int protocol)331 int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol)
332 {
333 	if (attr == NULL) {
334 		return EINVAL;
335 	}
336 
337 	switch (protocol) {
338 	case PTHREAD_PRIO_NONE:
339 		return 0;
340 	case PTHREAD_PRIO_INHERIT:
341 		return ENOTSUP;
342 	case PTHREAD_PRIO_PROTECT:
343 		return ENOTSUP;
344 	default:
345 		return EINVAL;
346 	}
347 }
348 
pthread_mutexattr_init(pthread_mutexattr_t * attr)349 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
350 {
351 	struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
352 
353 	if (a == NULL) {
354 		return EINVAL;
355 	}
356 
357 	a->type = PTHREAD_MUTEX_DEFAULT;
358 	a->initialized = true;
359 
360 	return 0;
361 }
362 
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)363 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
364 {
365 	struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
366 
367 	if (a == NULL || !a->initialized) {
368 		return EINVAL;
369 	}
370 
371 	*a = (struct pthread_mutexattr){0};
372 
373 	return 0;
374 }
375 
376 /**
377  * @brief Read type attribute for mutex.
378  *
379  * See IEEE 1003.1
380  */
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * type)381 int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
382 {
383 	const struct pthread_mutexattr *a = (const struct pthread_mutexattr *)attr;
384 
385 	if (a == NULL || type == NULL || !a->initialized) {
386 		return EINVAL;
387 	}
388 
389 	*type = a->type;
390 
391 	return 0;
392 }
393 
394 /**
395  * @brief Set type attribute for mutex.
396  *
397  * See IEEE 1003.1
398  */
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)399 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
400 {
401 	struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
402 
403 	if (a == NULL || !a->initialized) {
404 		return EINVAL;
405 	}
406 
407 	switch (type) {
408 	case PTHREAD_MUTEX_NORMAL:
409 	case PTHREAD_MUTEX_RECURSIVE:
410 	case PTHREAD_MUTEX_ERRORCHECK:
411 		a->type = type;
412 		return 0;
413 	default:
414 		return EINVAL;
415 	}
416 }
417 
418 #ifdef CONFIG_POSIX_THREAD_PRIO_PROTECT
pthread_mutex_getprioceiling(const pthread_mutex_t * mutex,int * prioceiling)419 int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling)
420 {
421 	ARG_UNUSED(mutex);
422 	ARG_UNUSED(prioceiling);
423 
424 	return ENOSYS;
425 }
426 
pthread_mutex_setprioceiling(pthread_mutex_t * mutex,int prioceiling,int * old_ceiling)427 int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling)
428 {
429 	ARG_UNUSED(mutex);
430 	ARG_UNUSED(prioceiling);
431 	ARG_UNUSED(old_ceiling);
432 
433 	return ENOSYS;
434 }
435 
pthread_mutexattr_getprioceiling(const pthread_mutexattr_t * attr,int * prioceiling)436 int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *prioceiling)
437 {
438 	ARG_UNUSED(attr);
439 	ARG_UNUSED(prioceiling);
440 
441 	return ENOSYS;
442 }
443 
pthread_mutexattr_setprioceiling(pthread_mutexattr_t * attr,int prioceiling)444 int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling)
445 {
446 	ARG_UNUSED(attr);
447 	ARG_UNUSED(prioceiling);
448 
449 	return ENOSYS;
450 }
451 
452 #endif /* CONFIG_POSIX_THREAD_PRIO_PROTECT */
453 
pthread_mutex_pool_init(void)454 static int pthread_mutex_pool_init(void)
455 {
456 	int err;
457 	size_t i;
458 
459 	for (i = 0; i < CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
460 		err = k_mutex_init(&posix_mutex_pool[i]);
461 		__ASSERT_NO_MSG(err == 0);
462 	}
463 
464 	return 0;
465 }
466 SYS_INIT(pthread_mutex_pool_init, PRE_KERNEL_1, 0);
467