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