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 
16 LOG_MODULE_REGISTER(pthread_mutex, CONFIG_PTHREAD_MUTEX_LOG_LEVEL);
17 
18 static struct k_spinlock pthread_mutex_spinlock;
19 
20 int64_t timespec_to_timeoutms(const struct timespec *abstime);
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 static struct k_mutex posix_mutex_pool[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
32 static uint8_t posix_mutex_type[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
33 SYS_BITARRAY_DEFINE_STATIC(posix_mutex_bitarray, CONFIG_MAX_PTHREAD_MUTEX_COUNT);
34 
35 /*
36  * We reserve the MSB to mark a pthread_mutex_t as initialized (from the
37  * perspective of the application). With a linear space, this means that
38  * the theoretical pthread_mutex_t range is [0,2147483647].
39  */
40 BUILD_ASSERT(CONFIG_MAX_PTHREAD_MUTEX_COUNT < PTHREAD_OBJ_MASK_INIT,
41 	"CONFIG_MAX_PTHREAD_MUTEX_COUNT is too high");
42 
posix_mutex_to_offset(struct k_mutex * m)43 static inline size_t posix_mutex_to_offset(struct k_mutex *m)
44 {
45 	return m - posix_mutex_pool;
46 }
47 
to_posix_mutex_idx(pthread_mutex_t mut)48 static inline size_t to_posix_mutex_idx(pthread_mutex_t mut)
49 {
50 	return mark_pthread_obj_uninitialized(mut);
51 }
52 
get_posix_mutex(pthread_mutex_t mu)53 static struct k_mutex *get_posix_mutex(pthread_mutex_t mu)
54 {
55 	int actually_initialized;
56 	size_t bit = to_posix_mutex_idx(mu);
57 
58 	/* if the provided mutex does not claim to be initialized, its invalid */
59 	if (!is_pthread_obj_initialized(mu)) {
60 		LOG_ERR("Mutex is uninitialized (%x)", mu);
61 		return NULL;
62 	}
63 
64 	/* Mask off the MSB to get the actual bit index */
65 	if (sys_bitarray_test_bit(&posix_mutex_bitarray, bit, &actually_initialized) < 0) {
66 		LOG_ERR("Mutex is invalid (%x)", mu);
67 		return NULL;
68 	}
69 
70 	if (actually_initialized == 0) {
71 		/* The mutex claims to be initialized but is actually not */
72 		LOG_ERR("Mutex claims to be initialized (%x)", mu);
73 		return NULL;
74 	}
75 
76 	return &posix_mutex_pool[bit];
77 }
78 
to_posix_mutex(pthread_mutex_t * mu)79 struct k_mutex *to_posix_mutex(pthread_mutex_t *mu)
80 {
81 	int err;
82 	size_t bit;
83 	struct k_mutex *m;
84 
85 	if (*mu != PTHREAD_MUTEX_INITIALIZER) {
86 		return get_posix_mutex(*mu);
87 	}
88 
89 	/* Try and automatically associate a posix_mutex */
90 	if (sys_bitarray_alloc(&posix_mutex_bitarray, 1, &bit) < 0) {
91 		LOG_ERR("Unable to allocate pthread_mutex_t");
92 		return NULL;
93 	}
94 
95 	/* Record the associated posix_mutex in mu and mark as initialized */
96 	*mu = mark_pthread_obj_initialized(bit);
97 
98 	/* Initialize the posix_mutex */
99 	m = &posix_mutex_pool[bit];
100 
101 	err = k_mutex_init(m);
102 	__ASSERT_NO_MSG(err == 0);
103 
104 	return m;
105 }
106 
acquire_mutex(pthread_mutex_t * mu,k_timeout_t timeout)107 static int acquire_mutex(pthread_mutex_t *mu, k_timeout_t timeout)
108 {
109 	int type;
110 	size_t bit;
111 	int ret = 0;
112 	struct k_mutex *m;
113 	k_spinlock_key_t key;
114 
115 	key = k_spin_lock(&pthread_mutex_spinlock);
116 
117 	m = to_posix_mutex(mu);
118 	if (m == NULL) {
119 		k_spin_unlock(&pthread_mutex_spinlock, key);
120 		return EINVAL;
121 	}
122 
123 	LOG_DBG("Locking mutex %p with timeout %llx", m, timeout.ticks);
124 
125 	bit = posix_mutex_to_offset(m);
126 	type = posix_mutex_type[bit];
127 
128 	if (m->owner == k_current_get()) {
129 		switch (type) {
130 		case PTHREAD_MUTEX_NORMAL:
131 			if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
132 				k_spin_unlock(&pthread_mutex_spinlock, key);
133 				LOG_ERR("Timeout locking mutex %p", m);
134 				return EBUSY;
135 			}
136 			/* On most POSIX systems, this usually results in an infinite loop */
137 			k_spin_unlock(&pthread_mutex_spinlock, key);
138 			LOG_ERR("Attempt to relock non-recursive mutex %p", m);
139 			do {
140 				(void)k_sleep(K_FOREVER);
141 			} while (true);
142 			CODE_UNREACHABLE;
143 			break;
144 		case PTHREAD_MUTEX_RECURSIVE:
145 			if (m->lock_count >= MUTEX_MAX_REC_LOCK) {
146 				LOG_ERR("Mutex %p locked recursively too many times", m);
147 				ret = EAGAIN;
148 			}
149 			break;
150 		case PTHREAD_MUTEX_ERRORCHECK:
151 			LOG_ERR("Attempt to recursively lock non-recursive mutex %p", m);
152 			ret = EDEADLK;
153 			break;
154 		default:
155 			__ASSERT(false, "invalid pthread type %d", type);
156 			ret = EINVAL;
157 			break;
158 		}
159 	}
160 	k_spin_unlock(&pthread_mutex_spinlock, key);
161 
162 	if (ret == 0) {
163 		ret = k_mutex_lock(m, timeout);
164 		if (ret == -EAGAIN) {
165 			LOG_ERR("Timeout locking mutex %p", m);
166 			/*
167 			 * special quirk - k_mutex_lock() returns EAGAIN if a timeout occurs, but
168 			 * for pthreads, that means something different
169 			 */
170 			ret = ETIMEDOUT;
171 		}
172 	}
173 
174 	if (ret < 0) {
175 		LOG_ERR("k_mutex_unlock() failed: %d", ret);
176 		ret = -ret;
177 	}
178 
179 	if (ret == 0) {
180 		LOG_DBG("Locked mutex %p", m);
181 	}
182 
183 	return ret;
184 }
185 
186 /**
187  * @brief Lock POSIX mutex with non-blocking call.
188  *
189  * See IEEE 1003.1
190  */
pthread_mutex_trylock(pthread_mutex_t * m)191 int pthread_mutex_trylock(pthread_mutex_t *m)
192 {
193 	return acquire_mutex(m, K_NO_WAIT);
194 }
195 
196 /**
197  * @brief Lock POSIX mutex with timeout.
198  *
199  *
200  * See IEEE 1003.1
201  */
pthread_mutex_timedlock(pthread_mutex_t * m,const struct timespec * abstime)202 int pthread_mutex_timedlock(pthread_mutex_t *m,
203 			    const struct timespec *abstime)
204 {
205 	int32_t timeout = (int32_t)timespec_to_timeoutms(abstime);
206 	return acquire_mutex(m, K_MSEC(timeout));
207 }
208 
209 /**
210  * @brief Initialize POSIX mutex.
211  *
212  * See IEEE 1003.1
213  */
pthread_mutex_init(pthread_mutex_t * mu,const pthread_mutexattr_t * _attr)214 int pthread_mutex_init(pthread_mutex_t *mu, const pthread_mutexattr_t *_attr)
215 {
216 	size_t bit;
217 	struct k_mutex *m;
218 	const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
219 
220 	*mu = PTHREAD_MUTEX_INITIALIZER;
221 
222 	m = to_posix_mutex(mu);
223 	if (m == NULL) {
224 		return ENOMEM;
225 	}
226 
227 	bit = posix_mutex_to_offset(m);
228 	if (attr == NULL) {
229 		posix_mutex_type[bit] = def_attr.type;
230 	} else {
231 		posix_mutex_type[bit] = attr->type;
232 	}
233 
234 	LOG_DBG("Initialized mutex %p", m);
235 
236 	return 0;
237 }
238 
239 
240 /**
241  * @brief Lock POSIX mutex with blocking call.
242  *
243  * See IEEE 1003.1
244  */
pthread_mutex_lock(pthread_mutex_t * m)245 int pthread_mutex_lock(pthread_mutex_t *m)
246 {
247 	return acquire_mutex(m, K_FOREVER);
248 }
249 
250 /**
251  * @brief Unlock POSIX mutex.
252  *
253  * See IEEE 1003.1
254  */
pthread_mutex_unlock(pthread_mutex_t * mu)255 int pthread_mutex_unlock(pthread_mutex_t *mu)
256 {
257 	int ret;
258 	struct k_mutex *m;
259 
260 	m = get_posix_mutex(*mu);
261 	if (m == NULL) {
262 		return EINVAL;
263 	}
264 
265 	ret = k_mutex_unlock(m);
266 	if (ret < 0) {
267 		LOG_ERR("k_mutex_unlock() failed: %d", ret);
268 		return -ret;
269 	}
270 
271 	__ASSERT_NO_MSG(ret == 0);
272 	LOG_DBG("Unlocked mutex %p", m);
273 
274 	return 0;
275 }
276 
277 /**
278  * @brief Destroy POSIX mutex.
279  *
280  * See IEEE 1003.1
281  */
pthread_mutex_destroy(pthread_mutex_t * mu)282 int pthread_mutex_destroy(pthread_mutex_t *mu)
283 {
284 	int err;
285 	size_t bit;
286 	struct k_mutex *m;
287 
288 	m = get_posix_mutex(*mu);
289 	if (m == NULL) {
290 		return EINVAL;
291 	}
292 
293 	bit = to_posix_mutex_idx(*mu);
294 	err = sys_bitarray_free(&posix_mutex_bitarray, 1, bit);
295 	__ASSERT_NO_MSG(err == 0);
296 
297 	LOG_DBG("Destroyed mutex %p", m);
298 
299 	return 0;
300 }
301 
302 /**
303  * @brief Read protocol attribute for mutex.
304  *
305  * See IEEE 1003.1
306  */
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)307 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
308 				  int *protocol)
309 {
310 	*protocol = PTHREAD_PRIO_NONE;
311 	return 0;
312 }
313 
314 /**
315  * @brief Read type attribute for mutex.
316  *
317  * See IEEE 1003.1
318  */
pthread_mutexattr_gettype(const pthread_mutexattr_t * _attr,int * type)319 int pthread_mutexattr_gettype(const pthread_mutexattr_t *_attr, int *type)
320 {
321 	const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
322 
323 	*type = attr->type;
324 	return 0;
325 }
326 
327 /**
328  * @brief Set type attribute for mutex.
329  *
330  * See IEEE 1003.1
331  */
pthread_mutexattr_settype(pthread_mutexattr_t * _attr,int type)332 int pthread_mutexattr_settype(pthread_mutexattr_t *_attr, int type)
333 {
334 	struct pthread_mutexattr *attr = (struct pthread_mutexattr *)_attr;
335 	int retc = EINVAL;
336 
337 	if ((type == PTHREAD_MUTEX_NORMAL) ||
338 	    (type == PTHREAD_MUTEX_RECURSIVE) ||
339 	    (type == PTHREAD_MUTEX_ERRORCHECK)) {
340 		attr->type = type;
341 		retc = 0;
342 	}
343 
344 	return retc;
345 }
346 
pthread_mutex_pool_init(void)347 static int pthread_mutex_pool_init(void)
348 {
349 	int err;
350 	size_t i;
351 
352 	for (i = 0; i < CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
353 		err = k_mutex_init(&posix_mutex_pool[i]);
354 		__ASSERT_NO_MSG(err == 0);
355 	}
356 
357 	return 0;
358 }
359 SYS_INIT(pthread_mutex_pool_init, PRE_KERNEL_1, 0);
360