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