1 /*
2 * Copyright (c) 2023, Meta
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "posix_internal.h"
8
9 #include <zephyr/init.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/posix/pthread.h>
12 #include <zephyr/sys/bitarray.h>
13
14 union _spinlock_storage {
15 struct k_spinlock lock;
16 uint8_t byte;
17 };
18 #if !defined(CONFIG_CPP) && !defined(CONFIG_SMP) && !defined(CONFIG_SPIN_VALIDATE)
19 BUILD_ASSERT(sizeof(struct k_spinlock) == 0,
20 "please remove the _spinlock_storage workaround if, at some point, k_spinlock is no "
21 "longer zero bytes when CONFIG_SMP=n && CONFIG_SPIN_VALIDATE=n");
22 #endif
23
24 static union _spinlock_storage posix_spinlock_pool[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT];
25 static k_spinlock_key_t posix_spinlock_key[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT];
26 SYS_BITARRAY_DEFINE_STATIC(posix_spinlock_bitarray, CONFIG_MAX_PTHREAD_SPINLOCK_COUNT);
27
28 /*
29 * We reserve the MSB to mark a pthread_spinlock_t as initialized (from the
30 * perspective of the application). With a linear space, this means that
31 * the theoretical pthread_spinlock_t range is [0,2147483647].
32 */
33 BUILD_ASSERT(CONFIG_MAX_PTHREAD_SPINLOCK_COUNT < PTHREAD_OBJ_MASK_INIT,
34 "CONFIG_MAX_PTHREAD_SPINLOCK_COUNT is too high");
35
posix_spinlock_to_offset(struct k_spinlock * l)36 static inline size_t posix_spinlock_to_offset(struct k_spinlock *l)
37 {
38 return (union _spinlock_storage *)l - posix_spinlock_pool;
39 }
40
to_posix_spinlock_idx(pthread_spinlock_t lock)41 static inline size_t to_posix_spinlock_idx(pthread_spinlock_t lock)
42 {
43 return mark_pthread_obj_uninitialized(lock);
44 }
45
get_posix_spinlock(pthread_spinlock_t * lock)46 static struct k_spinlock *get_posix_spinlock(pthread_spinlock_t *lock)
47 {
48 size_t bit;
49 int actually_initialized;
50
51 if (lock == NULL) {
52 return NULL;
53 }
54
55 /* if the provided spinlock does not claim to be initialized, its invalid */
56 bit = to_posix_spinlock_idx(*lock);
57 if (!is_pthread_obj_initialized(*lock)) {
58 return NULL;
59 }
60
61 /* Mask off the MSB to get the actual bit index */
62 if (sys_bitarray_test_bit(&posix_spinlock_bitarray, bit, &actually_initialized) < 0) {
63 return NULL;
64 }
65
66 if (actually_initialized == 0) {
67 /* The spinlock claims to be initialized but is actually not */
68 return NULL;
69 }
70
71 return (struct k_spinlock *)&posix_spinlock_pool[bit];
72 }
73
pthread_spin_init(pthread_spinlock_t * lock,int pshared)74 int pthread_spin_init(pthread_spinlock_t *lock, int pshared)
75 {
76 int ret;
77 size_t bit;
78
79 if (lock == NULL ||
80 !(pshared == PTHREAD_PROCESS_PRIVATE || pshared == PTHREAD_PROCESS_SHARED)) {
81 /* not specified as part of POSIX but this is the Linux behavior */
82 return EINVAL;
83 }
84
85 ret = sys_bitarray_alloc(&posix_spinlock_bitarray, 1, &bit);
86 if (ret < 0) {
87 return ENOMEM;
88 }
89
90 *lock = mark_pthread_obj_initialized(bit);
91
92 return 0;
93 }
94
pthread_spin_destroy(pthread_spinlock_t * lock)95 int pthread_spin_destroy(pthread_spinlock_t *lock)
96 {
97 int err;
98 size_t bit;
99 struct k_spinlock *l;
100
101 l = get_posix_spinlock(lock);
102 if (l == NULL) {
103 /* not specified as part of POSIX but this is the Linux behavior */
104 return EINVAL;
105 }
106
107 bit = posix_spinlock_to_offset(l);
108 err = sys_bitarray_free(&posix_spinlock_bitarray, 1, bit);
109 __ASSERT_NO_MSG(err == 0);
110
111 return 0;
112 }
113
pthread_spin_lock(pthread_spinlock_t * lock)114 int pthread_spin_lock(pthread_spinlock_t *lock)
115 {
116 size_t bit;
117 struct k_spinlock *l;
118
119 l = get_posix_spinlock(lock);
120 if (l == NULL) {
121 /* not specified as part of POSIX but this is the Linux behavior */
122 return EINVAL;
123 }
124
125 bit = posix_spinlock_to_offset(l);
126 posix_spinlock_key[bit] = k_spin_lock(l);
127
128 return 0;
129 }
130
pthread_spin_trylock(pthread_spinlock_t * lock)131 int pthread_spin_trylock(pthread_spinlock_t *lock)
132 {
133 size_t bit;
134 struct k_spinlock *l;
135
136 l = get_posix_spinlock(lock);
137 if (l == NULL) {
138 /* not specified as part of POSIX but this is the Linux behavior */
139 return EINVAL;
140 }
141
142 bit = posix_spinlock_to_offset(l);
143 return k_spin_trylock(l, &posix_spinlock_key[bit]);
144 }
145
pthread_spin_unlock(pthread_spinlock_t * lock)146 int pthread_spin_unlock(pthread_spinlock_t *lock)
147 {
148 size_t bit;
149 struct k_spinlock *l;
150
151 l = get_posix_spinlock(lock);
152 if (l == NULL) {
153 /* not specified as part of POSIX but this is the Linux behavior */
154 return EINVAL;
155 }
156
157 bit = posix_spinlock_to_offset(l);
158 k_spin_unlock(l, posix_spinlock_key[bit]);
159
160 return 0;
161 }
162