]>
Commit | Line | Data |
---|---|---|
b1322259 | 1 | /* |
0c679f55 | 2 | * Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved. |
71a04cfc | 3 | * |
0e9725bc | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
b1322259 RS |
5 | * this file except in compliance with the License. You can obtain a copy |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
71a04cfc AG |
8 | */ |
9 | ||
9750b4d3 RB |
10 | /* We need to use the OPENSSL_fork_*() deprecated APIs */ |
11 | #define OPENSSL_SUPPRESS_DEPRECATED | |
12 | ||
71a04cfc | 13 | #include <openssl/crypto.h> |
d0e1a0ae | 14 | #include <crypto/cryptlib.h> |
2cb068fb | 15 | #include <crypto/sparse_array.h> |
5f8dd0f8 | 16 | #include "internal/cryptlib.h" |
2cb068fb | 17 | #include "internal/threads_common.h" |
d0e1a0ae NH |
18 | #include "internal/rcu.h" |
19 | #include "rcu_internal.h" | |
71a04cfc | 20 | |
3bcac460 NH |
21 | #if defined(__clang__) && defined(__has_feature) |
22 | # if __has_feature(thread_sanitizer) | |
23 | # define __SANITIZE_THREAD__ | |
24 | # endif | |
25 | #endif | |
26 | ||
27 | #if defined(__SANITIZE_THREAD__) | |
28 | # include <sanitizer/tsan_interface.h> | |
29 | # define TSAN_FAKE_UNLOCK(x) __tsan_mutex_pre_unlock((x), 0); \ | |
30 | __tsan_mutex_post_unlock((x), 0) | |
31 | ||
32 | # define TSAN_FAKE_LOCK(x) __tsan_mutex_pre_lock((x), 0); \ | |
33 | __tsan_mutex_post_lock((x), 0, 0) | |
34 | #else | |
35 | # define TSAN_FAKE_UNLOCK(x) | |
36 | # define TSAN_FAKE_LOCK(x) | |
37 | #endif | |
38 | ||
d6dda392 VK |
39 | #if defined(__sun) |
40 | # include <atomic.h> | |
41 | #endif | |
42 | ||
d39de479 KK |
43 | #if defined(__apple_build_version__) && __apple_build_version__ < 6000000 |
44 | /* | |
45 | * OS/X 10.7 and 10.8 had a weird version of clang which has __ATOMIC_ACQUIRE and | |
46 | * __ATOMIC_ACQ_REL but which expects only one parameter for __atomic_is_lock_free() | |
47 | * rather than two which has signature __atomic_is_lock_free(sizeof(_Atomic(T))). | |
48 | * All of this makes impossible to use __atomic_is_lock_free here. | |
49 | * | |
50 | * See: https://github.com/llvm/llvm-project/commit/a4c2602b714e6c6edb98164550a5ae829b2de760 | |
51 | */ | |
81f39349 | 52 | # define BROKEN_CLANG_ATOMICS |
d39de479 KK |
53 | #endif |
54 | ||
71a04cfc AG |
55 | #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && !defined(OPENSSL_SYS_WINDOWS) |
56 | ||
84952925 DMSP |
57 | # if defined(OPENSSL_SYS_UNIX) |
58 | # include <sys/types.h> | |
59 | # include <unistd.h> | |
81f39349 | 60 | # endif |
84952925 | 61 | |
0d407456 RB |
62 | # include <assert.h> |
63 | ||
7408d587 NH |
64 | /* |
65 | * The Non-Stop KLT thread model currently seems broken in its rwlock | |
66 | * implementation | |
67 | */ | |
68 | # if defined(PTHREAD_RWLOCK_INITIALIZER) && !defined(_KLT_MODEL_) | |
ec93a292 DK |
69 | # define USE_RWLOCK |
70 | # endif | |
2accf3f7 | 71 | |
a02077d4 RL |
72 | /* |
73 | * For all GNU/clang atomic builtins, we also need fallbacks, to cover all | |
74 | * other compilers. | |
75 | ||
76 | * Unfortunately, we can't do that with some "generic type", because there's no | |
77 | * guarantee that the chosen generic type is large enough to cover all cases. | |
78 | * Therefore, we implement fallbacks for each applicable type, with composed | |
79 | * names that include the type they handle. | |
80 | * | |
81 | * (an anecdote: we previously tried to use |void *| as the generic type, with | |
82 | * the thought that the pointer itself is the largest type. However, this is | |
83 | * not true on 32-bit pointer platforms, as a |uint64_t| is twice as large) | |
84 | * | |
85 | * All applicable ATOMIC_ macros take the intended type as first parameter, so | |
86 | * they can map to the correct fallback function. In the GNU/clang case, that | |
87 | * parameter is simply ignored. | |
88 | */ | |
89 | ||
90 | /* | |
91 | * Internal types used with the ATOMIC_ macros, to make it possible to compose | |
92 | * fallback function names. | |
93 | */ | |
94 | typedef void *pvoid; | |
a02077d4 RL |
95 | |
96 | # if defined(__GNUC__) && defined(__ATOMIC_ACQUIRE) && !defined(BROKEN_CLANG_ATOMICS) \ | |
97 | && !defined(USE_ATOMIC_FALLBACKS) | |
a6f512a1 | 98 | # define ATOMIC_LOAD_N(t, p, o) __atomic_load_n(p, o) |
a02077d4 RL |
99 | # define ATOMIC_STORE_N(t, p, v, o) __atomic_store_n(p, v, o) |
100 | # define ATOMIC_STORE(t, p, v, o) __atomic_store(p, v, o) | |
81f39349 | 101 | # define ATOMIC_ADD_FETCH(p, v, o) __atomic_add_fetch(p, v, o) |
81f39349 | 102 | # define ATOMIC_SUB_FETCH(p, v, o) __atomic_sub_fetch(p, v, o) |
81f39349 | 103 | # else |
d0e1a0ae NH |
104 | static pthread_mutex_t atomic_sim_lock = PTHREAD_MUTEX_INITIALIZER; |
105 | ||
a02077d4 | 106 | # define IMPL_fallback_atomic_load_n(t) \ |
36ba4192 | 107 | static ossl_inline t fallback_atomic_load_n_##t(t *p) \ |
a02077d4 RL |
108 | { \ |
109 | t ret; \ | |
110 | \ | |
111 | pthread_mutex_lock(&atomic_sim_lock); \ | |
112 | ret = *p; \ | |
113 | pthread_mutex_unlock(&atomic_sim_lock); \ | |
114 | return ret; \ | |
115 | } | |
ce6b2f98 | 116 | IMPL_fallback_atomic_load_n(uint32_t) |
a02077d4 RL |
117 | IMPL_fallback_atomic_load_n(uint64_t) |
118 | IMPL_fallback_atomic_load_n(pvoid) | |
119 | ||
120 | # define ATOMIC_LOAD_N(t, p, o) fallback_atomic_load_n_##t(p) | |
121 | ||
122 | # define IMPL_fallback_atomic_store_n(t) \ | |
36ba4192 | 123 | static ossl_inline t fallback_atomic_store_n_##t(t *p, t v) \ |
a02077d4 RL |
124 | { \ |
125 | t ret; \ | |
126 | \ | |
127 | pthread_mutex_lock(&atomic_sim_lock); \ | |
128 | ret = *p; \ | |
129 | *p = v; \ | |
130 | pthread_mutex_unlock(&atomic_sim_lock); \ | |
131 | return ret; \ | |
132 | } | |
ce6b2f98 | 133 | IMPL_fallback_atomic_store_n(uint32_t) |
d0e1a0ae | 134 | |
a02077d4 | 135 | # define ATOMIC_STORE_N(t, p, v, o) fallback_atomic_store_n_##t(p, v) |
d0e1a0ae | 136 | |
a02077d4 | 137 | # define IMPL_fallback_atomic_store(t) \ |
36ba4192 | 138 | static ossl_inline void fallback_atomic_store_##t(t *p, t *v) \ |
a02077d4 RL |
139 | { \ |
140 | pthread_mutex_lock(&atomic_sim_lock); \ | |
141 | *p = *v; \ | |
142 | pthread_mutex_unlock(&atomic_sim_lock); \ | |
143 | } | |
a02077d4 RL |
144 | IMPL_fallback_atomic_store(pvoid) |
145 | ||
146 | # define ATOMIC_STORE(t, p, v, o) fallback_atomic_store_##t(p, v) | |
147 | ||
a02077d4 RL |
148 | /* |
149 | * The fallbacks that follow don't need any per type implementation, as | |
150 | * they are designed for uint64_t only. If there comes a time when multiple | |
151 | * types need to be covered, it's relatively easy to refactor them the same | |
152 | * way as the fallbacks above. | |
153 | */ | |
d0e1a0ae | 154 | |
36ba4192 | 155 | static ossl_inline uint64_t fallback_atomic_add_fetch(uint64_t *p, uint64_t v) |
d0e1a0ae NH |
156 | { |
157 | uint64_t ret; | |
158 | ||
159 | pthread_mutex_lock(&atomic_sim_lock); | |
160 | *p += v; | |
161 | ret = *p; | |
162 | pthread_mutex_unlock(&atomic_sim_lock); | |
163 | return ret; | |
164 | } | |
165 | ||
81f39349 | 166 | # define ATOMIC_ADD_FETCH(p, v, o) fallback_atomic_add_fetch(p, v) |
d0e1a0ae | 167 | |
36ba4192 | 168 | static ossl_inline uint64_t fallback_atomic_sub_fetch(uint64_t *p, uint64_t v) |
d0e1a0ae NH |
169 | { |
170 | uint64_t ret; | |
171 | ||
172 | pthread_mutex_lock(&atomic_sim_lock); | |
173 | *p -= v; | |
174 | ret = *p; | |
175 | pthread_mutex_unlock(&atomic_sim_lock); | |
176 | return ret; | |
177 | } | |
178 | ||
81f39349 | 179 | # define ATOMIC_SUB_FETCH(p, v, o) fallback_atomic_sub_fetch(p, v) |
81f39349 | 180 | # endif |
d0e1a0ae | 181 | |
d0e1a0ae NH |
182 | /* |
183 | * This is the core of an rcu lock. It tracks the readers and writers for the | |
184 | * current quiescence point for a given lock. Users is the 64 bit value that | |
185 | * stores the READERS/ID as defined above | |
186 | * | |
187 | */ | |
188 | struct rcu_qp { | |
189 | uint64_t users; | |
190 | }; | |
191 | ||
192 | struct thread_qp { | |
193 | struct rcu_qp *qp; | |
194 | unsigned int depth; | |
195 | CRYPTO_RCU_LOCK *lock; | |
196 | }; | |
197 | ||
81f39349 | 198 | # define MAX_QPS 10 |
d0e1a0ae NH |
199 | /* |
200 | * This is the per thread tracking data | |
201 | * that is assigned to each thread participating | |
202 | * in an rcu qp | |
203 | * | |
204 | * qp points to the qp that it last acquired | |
205 | * | |
206 | */ | |
207 | struct rcu_thr_data { | |
208 | struct thread_qp thread_qps[MAX_QPS]; | |
209 | }; | |
210 | ||
211 | /* | |
212 | * This is the internal version of a CRYPTO_RCU_LOCK | |
213 | * it is cast from CRYPTO_RCU_LOCK | |
214 | */ | |
215 | struct rcu_lock_st { | |
216 | /* Callbacks to call for next ossl_synchronize_rcu */ | |
217 | struct rcu_cb_item *cb_items; | |
218 | ||
24d16d3a NH |
219 | /* The context we are being created against */ |
220 | OSSL_LIB_CTX *ctx; | |
221 | ||
d0e1a0ae NH |
222 | /* Array of quiescent points for synchronization */ |
223 | struct rcu_qp *qp_group; | |
224 | ||
7097d2e0 AD |
225 | /* rcu generation counter for in-order retirement */ |
226 | uint32_t id_ctr; | |
227 | ||
d0e1a0ae | 228 | /* Number of elements in qp_group array */ |
ce6b2f98 | 229 | uint32_t group_count; |
d0e1a0ae NH |
230 | |
231 | /* Index of the current qp in the qp_group array */ | |
ce6b2f98 | 232 | uint32_t reader_idx; |
d0e1a0ae NH |
233 | |
234 | /* value of the next id_ctr value to be retired */ | |
235 | uint32_t next_to_retire; | |
236 | ||
237 | /* index of the next free rcu_qp in the qp_group */ | |
ce6b2f98 | 238 | uint32_t current_alloc_idx; |
d0e1a0ae NH |
239 | |
240 | /* number of qp's in qp_group array currently being retired */ | |
241 | uint32_t writers_alloced; | |
242 | ||
243 | /* lock protecting write side operations */ | |
244 | pthread_mutex_t write_lock; | |
245 | ||
246 | /* lock protecting updates to writers_alloced/current_alloc_idx */ | |
247 | pthread_mutex_t alloc_lock; | |
248 | ||
249 | /* signal to wake threads waiting on alloc_lock */ | |
250 | pthread_cond_t alloc_signal; | |
251 | ||
252 | /* lock to enforce in-order retirement */ | |
253 | pthread_mutex_t prior_lock; | |
254 | ||
255 | /* signal to wake threads waiting on prior_lock */ | |
256 | pthread_cond_t prior_signal; | |
257 | }; | |
258 | ||
d0e1a0ae NH |
259 | /* Read side acquisition of the current qp */ |
260 | static struct rcu_qp *get_hold_current_qp(struct rcu_lock_st *lock) | |
261 | { | |
ce6b2f98 | 262 | uint32_t qp_idx; |
d0e1a0ae NH |
263 | |
264 | /* get the current qp index */ | |
265 | for (;;) { | |
a532f230 BE |
266 | qp_idx = ATOMIC_LOAD_N(uint32_t, &lock->reader_idx, __ATOMIC_RELAXED); |
267 | ||
d0e1a0ae NH |
268 | /* |
269 | * Notes on use of __ATOMIC_ACQUIRE | |
270 | * We need to ensure the following: | |
271 | * 1) That subsequent operations aren't optimized by hoisting them above | |
272 | * this operation. Specifically, we don't want the below re-load of | |
273 | * qp_idx to get optimized away | |
274 | * 2) We want to ensure that any updating of reader_idx on the write side | |
275 | * of the lock is flushed from a local cpu cache so that we see any | |
276 | * updates prior to the load. This is a non-issue on cache coherent | |
277 | * systems like x86, but is relevant on other arches | |
d0e1a0ae | 278 | */ |
5949918f BE |
279 | ATOMIC_ADD_FETCH(&lock->qp_group[qp_idx].users, (uint64_t)1, |
280 | __ATOMIC_ACQUIRE); | |
d0e1a0ae NH |
281 | |
282 | /* if the idx hasn't changed, we're good, else try again */ | |
9f4d8c63 | 283 | if (qp_idx == ATOMIC_LOAD_N(uint32_t, &lock->reader_idx, |
5949918f | 284 | __ATOMIC_RELAXED)) |
d0e1a0ae NH |
285 | break; |
286 | ||
5949918f BE |
287 | ATOMIC_SUB_FETCH(&lock->qp_group[qp_idx].users, (uint64_t)1, |
288 | __ATOMIC_RELAXED); | |
d0e1a0ae NH |
289 | } |
290 | ||
291 | return &lock->qp_group[qp_idx]; | |
292 | } | |
293 | ||
24d16d3a NH |
294 | static void ossl_rcu_free_local_data(void *arg) |
295 | { | |
296 | OSSL_LIB_CTX *ctx = arg; | |
2cb068fb | 297 | struct rcu_thr_data *data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, ctx); |
f7252d73 | 298 | |
2cb068fb | 299 | CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, ctx, NULL); |
24d16d3a NH |
300 | OPENSSL_free(data); |
301 | } | |
302 | ||
d0e1a0ae NH |
303 | void ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock) |
304 | { | |
305 | struct rcu_thr_data *data; | |
306 | int i, available_qp = -1; | |
307 | ||
308 | /* | |
309 | * we're going to access current_qp here so ask the | |
310 | * processor to fetch it | |
311 | */ | |
2cb068fb | 312 | data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx); |
d0e1a0ae NH |
313 | |
314 | if (data == NULL) { | |
315 | data = OPENSSL_zalloc(sizeof(*data)); | |
316 | OPENSSL_assert(data != NULL); | |
2cb068fb | 317 | CRYPTO_THREAD_set_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx, data); |
24d16d3a | 318 | ossl_init_thread_start(NULL, lock->ctx, ossl_rcu_free_local_data); |
d0e1a0ae NH |
319 | } |
320 | ||
321 | for (i = 0; i < MAX_QPS; i++) { | |
322 | if (data->thread_qps[i].qp == NULL && available_qp == -1) | |
323 | available_qp = i; | |
324 | /* If we have a hold on this lock already, we're good */ | |
325 | if (data->thread_qps[i].lock == lock) { | |
326 | data->thread_qps[i].depth++; | |
327 | return; | |
328 | } | |
329 | } | |
330 | ||
331 | /* | |
332 | * if we get here, then we don't have a hold on this lock yet | |
333 | */ | |
334 | assert(available_qp != -1); | |
335 | ||
336 | data->thread_qps[available_qp].qp = get_hold_current_qp(lock); | |
337 | data->thread_qps[available_qp].depth = 1; | |
338 | data->thread_qps[available_qp].lock = lock; | |
339 | } | |
340 | ||
341 | void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock) | |
342 | { | |
343 | int i; | |
2cb068fb | 344 | struct rcu_thr_data *data = CRYPTO_THREAD_get_local_ex(CRYPTO_THREAD_LOCAL_RCU_KEY, lock->ctx); |
d0e1a0ae NH |
345 | uint64_t ret; |
346 | ||
347 | assert(data != NULL); | |
348 | ||
349 | for (i = 0; i < MAX_QPS; i++) { | |
350 | if (data->thread_qps[i].lock == lock) { | |
351 | /* | |
5949918f BE |
352 | * we have to use __ATOMIC_RELEASE here |
353 | * to ensure that all preceding read instructions complete | |
354 | * before the decrement is visible to ossl_synchronize_rcu | |
d0e1a0ae NH |
355 | */ |
356 | data->thread_qps[i].depth--; | |
357 | if (data->thread_qps[i].depth == 0) { | |
9f4d8c63 | 358 | ret = ATOMIC_SUB_FETCH(&data->thread_qps[i].qp->users, |
5949918f | 359 | (uint64_t)1, __ATOMIC_RELEASE); |
d0e1a0ae NH |
360 | OPENSSL_assert(ret != UINT64_MAX); |
361 | data->thread_qps[i].qp = NULL; | |
362 | data->thread_qps[i].lock = NULL; | |
363 | } | |
364 | return; | |
365 | } | |
366 | } | |
367 | /* | |
39fe3e5d DP |
368 | * If we get here, we're trying to unlock a lock that we never acquired - |
369 | * that's fatal. | |
d0e1a0ae NH |
370 | */ |
371 | assert(0); | |
372 | } | |
373 | ||
374 | /* | |
375 | * Write side allocation routine to get the current qp | |
376 | * and replace it with a new one | |
377 | */ | |
5949918f | 378 | static struct rcu_qp *update_qp(CRYPTO_RCU_LOCK *lock, uint32_t *curr_id) |
d0e1a0ae | 379 | { |
ce6b2f98 | 380 | uint32_t current_idx; |
d0e1a0ae NH |
381 | |
382 | pthread_mutex_lock(&lock->alloc_lock); | |
383 | ||
384 | /* | |
385 | * we need at least one qp to be available with one | |
386 | * left over, so that readers can start working on | |
387 | * one that isn't yet being waited on | |
388 | */ | |
389 | while (lock->group_count - lock->writers_alloced < 2) | |
390 | /* we have to wait for one to be free */ | |
391 | pthread_cond_wait(&lock->alloc_signal, &lock->alloc_lock); | |
392 | ||
393 | current_idx = lock->current_alloc_idx; | |
394 | ||
395 | /* Allocate the qp */ | |
396 | lock->writers_alloced++; | |
397 | ||
398 | /* increment the allocation index */ | |
399 | lock->current_alloc_idx = | |
400 | (lock->current_alloc_idx + 1) % lock->group_count; | |
401 | ||
5949918f | 402 | *curr_id = lock->id_ctr; |
d0e1a0ae NH |
403 | lock->id_ctr++; |
404 | ||
ce6b2f98 | 405 | ATOMIC_STORE_N(uint32_t, &lock->reader_idx, lock->current_alloc_idx, |
5949918f | 406 | __ATOMIC_RELAXED); |
d0e1a0ae | 407 | |
4a1a7fe5 BE |
408 | /* |
409 | * this should make sure that the new value of reader_idx is visible in | |
410 | * get_hold_current_qp, directly after incrementing the users count | |
411 | */ | |
412 | ATOMIC_ADD_FETCH(&lock->qp_group[current_idx].users, (uint64_t)0, | |
413 | __ATOMIC_RELEASE); | |
414 | ||
d0e1a0ae NH |
415 | /* wake up any waiters */ |
416 | pthread_cond_signal(&lock->alloc_signal); | |
417 | pthread_mutex_unlock(&lock->alloc_lock); | |
418 | return &lock->qp_group[current_idx]; | |
419 | } | |
420 | ||
421 | static void retire_qp(CRYPTO_RCU_LOCK *lock, struct rcu_qp *qp) | |
422 | { | |
423 | pthread_mutex_lock(&lock->alloc_lock); | |
424 | lock->writers_alloced--; | |
425 | pthread_cond_signal(&lock->alloc_signal); | |
426 | pthread_mutex_unlock(&lock->alloc_lock); | |
427 | } | |
428 | ||
429 | static struct rcu_qp *allocate_new_qp_group(CRYPTO_RCU_LOCK *lock, | |
7097d2e0 | 430 | uint32_t count) |
d0e1a0ae NH |
431 | { |
432 | struct rcu_qp *new = | |
433 | OPENSSL_zalloc(sizeof(*new) * count); | |
434 | ||
435 | lock->group_count = count; | |
436 | return new; | |
437 | } | |
438 | ||
439 | void ossl_rcu_write_lock(CRYPTO_RCU_LOCK *lock) | |
440 | { | |
441 | pthread_mutex_lock(&lock->write_lock); | |
3bcac460 | 442 | TSAN_FAKE_UNLOCK(&lock->write_lock); |
d0e1a0ae NH |
443 | } |
444 | ||
445 | void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock) | |
446 | { | |
3bcac460 | 447 | TSAN_FAKE_LOCK(&lock->write_lock); |
d0e1a0ae NH |
448 | pthread_mutex_unlock(&lock->write_lock); |
449 | } | |
450 | ||
451 | void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock) | |
452 | { | |
453 | struct rcu_qp *qp; | |
454 | uint64_t count; | |
5949918f | 455 | uint32_t curr_id; |
d0e1a0ae NH |
456 | struct rcu_cb_item *cb_items, *tmpcb; |
457 | ||
3bcac460 NH |
458 | pthread_mutex_lock(&lock->write_lock); |
459 | cb_items = lock->cb_items; | |
460 | lock->cb_items = NULL; | |
461 | pthread_mutex_unlock(&lock->write_lock); | |
d0e1a0ae | 462 | |
5949918f BE |
463 | qp = update_qp(lock, &curr_id); |
464 | ||
465 | /* retire in order */ | |
466 | pthread_mutex_lock(&lock->prior_lock); | |
467 | while (lock->next_to_retire != curr_id) | |
468 | pthread_cond_wait(&lock->prior_signal, &lock->prior_lock); | |
d0e1a0ae NH |
469 | |
470 | /* | |
471 | * wait for the reader count to reach zero | |
472 | * Note the use of __ATOMIC_ACQUIRE here to ensure that any | |
5949918f | 473 | * prior __ATOMIC_RELEASE write operation in ossl_rcu_read_unlock |
d0e1a0ae | 474 | * is visible prior to our read |
5949918f | 475 | * however this is likely just necessary to silence a tsan warning |
a532f230 BE |
476 | * because the read side should not do any write operation |
477 | * outside the atomic itself | |
d0e1a0ae NH |
478 | */ |
479 | do { | |
a02077d4 | 480 | count = ATOMIC_LOAD_N(uint64_t, &qp->users, __ATOMIC_ACQUIRE); |
5949918f | 481 | } while (count != (uint64_t)0); |
d0e1a0ae | 482 | |
6e7be995 BE |
483 | lock->next_to_retire++; |
484 | pthread_cond_broadcast(&lock->prior_signal); | |
485 | pthread_mutex_unlock(&lock->prior_lock); | |
486 | ||
d0e1a0ae NH |
487 | retire_qp(lock, qp); |
488 | ||
489 | /* handle any callbacks that we have */ | |
490 | while (cb_items != NULL) { | |
491 | tmpcb = cb_items; | |
492 | cb_items = cb_items->next; | |
493 | tmpcb->fn(tmpcb->data); | |
494 | OPENSSL_free(tmpcb); | |
495 | } | |
496 | } | |
497 | ||
7d284560 NH |
498 | /* |
499 | * Note: This call assumes its made under the protection of | |
500 | * ossl_rcu_write_lock | |
501 | */ | |
d0e1a0ae NH |
502 | int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data) |
503 | { | |
504 | struct rcu_cb_item *new = | |
505 | OPENSSL_zalloc(sizeof(*new)); | |
506 | ||
507 | if (new == NULL) | |
508 | return 0; | |
509 | ||
510 | new->data = data; | |
511 | new->fn = cb; | |
7d284560 NH |
512 | |
513 | new->next = lock->cb_items; | |
514 | lock->cb_items = new; | |
d0e1a0ae NH |
515 | |
516 | return 1; | |
517 | } | |
518 | ||
519 | void *ossl_rcu_uptr_deref(void **p) | |
520 | { | |
a02077d4 | 521 | return ATOMIC_LOAD_N(pvoid, p, __ATOMIC_ACQUIRE); |
d0e1a0ae NH |
522 | } |
523 | ||
524 | void ossl_rcu_assign_uptr(void **p, void **v) | |
525 | { | |
a02077d4 | 526 | ATOMIC_STORE(pvoid, p, v, __ATOMIC_RELEASE); |
d0e1a0ae NH |
527 | } |
528 | ||
24d16d3a | 529 | CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers, OSSL_LIB_CTX *ctx) |
d0e1a0ae NH |
530 | { |
531 | struct rcu_lock_st *new; | |
532 | ||
25f8e2c1 | 533 | /* |
a532f230 | 534 | * We need a minimum of 2 qp's |
25f8e2c1 | 535 | */ |
a532f230 BE |
536 | if (num_writers < 2) |
537 | num_writers = 2; | |
d0e1a0ae | 538 | |
24d16d3a NH |
539 | ctx = ossl_lib_ctx_get_concrete(ctx); |
540 | if (ctx == NULL) | |
541 | return 0; | |
542 | ||
d0e1a0ae NH |
543 | new = OPENSSL_zalloc(sizeof(*new)); |
544 | if (new == NULL) | |
545 | return NULL; | |
546 | ||
24d16d3a | 547 | new->ctx = ctx; |
d0e1a0ae NH |
548 | pthread_mutex_init(&new->write_lock, NULL); |
549 | pthread_mutex_init(&new->prior_lock, NULL); | |
550 | pthread_mutex_init(&new->alloc_lock, NULL); | |
551 | pthread_cond_init(&new->prior_signal, NULL); | |
552 | pthread_cond_init(&new->alloc_signal, NULL); | |
25f8e2c1 NH |
553 | |
554 | new->qp_group = allocate_new_qp_group(new, num_writers); | |
d0e1a0ae NH |
555 | if (new->qp_group == NULL) { |
556 | OPENSSL_free(new); | |
557 | new = NULL; | |
558 | } | |
25f8e2c1 | 559 | |
d0e1a0ae NH |
560 | return new; |
561 | } | |
562 | ||
563 | void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock) | |
564 | { | |
565 | struct rcu_lock_st *rlock = (struct rcu_lock_st *)lock; | |
566 | ||
567 | if (lock == NULL) | |
568 | return; | |
569 | ||
570 | /* make sure we're synchronized */ | |
571 | ossl_synchronize_rcu(rlock); | |
572 | ||
573 | OPENSSL_free(rlock->qp_group); | |
574 | /* There should only be a single qp left now */ | |
575 | OPENSSL_free(rlock); | |
576 | } | |
577 | ||
71a04cfc AG |
578 | CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void) |
579 | { | |
ec93a292 | 580 | # ifdef USE_RWLOCK |
7de2b9c4 RS |
581 | CRYPTO_RWLOCK *lock; |
582 | ||
d0e1a0ae | 583 | if ((lock = OPENSSL_zalloc(sizeof(pthread_rwlock_t))) == NULL) |
7de2b9c4 | 584 | /* Don't set error, to avoid recursion blowup. */ |
71a04cfc AG |
585 | return NULL; |
586 | ||
0b2fc928 F |
587 | if (pthread_rwlock_init(lock, NULL) != 0) { |
588 | OPENSSL_free(lock); | |
71a04cfc | 589 | return NULL; |
0b2fc928 | 590 | } |
ec93a292 DK |
591 | # else |
592 | pthread_mutexattr_t attr; | |
7de2b9c4 RS |
593 | CRYPTO_RWLOCK *lock; |
594 | ||
d0e1a0ae | 595 | if ((lock = OPENSSL_zalloc(sizeof(pthread_mutex_t))) == NULL) |
7de2b9c4 | 596 | /* Don't set error, to avoid recursion blowup. */ |
2accf3f7 DK |
597 | return NULL; |
598 | ||
e60147fe RS |
599 | /* |
600 | * We don't use recursive mutexes, but try to catch errors if we do. | |
601 | */ | |
2accf3f7 | 602 | pthread_mutexattr_init(&attr); |
6870c1e7 RB |
603 | # if !defined (__TANDEM) && !defined (_SPT_MODEL_) |
604 | # if !defined(NDEBUG) && !defined(OPENSSL_NO_MUTEX_ERRORCHECK) | |
e60147fe | 605 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); |
6870c1e7 RB |
606 | # endif |
607 | # else | |
608 | /* The SPT Thread Library does not define MUTEX attributes. */ | |
e60147fe | 609 | # endif |
5d5eed44 | 610 | |
2accf3f7 DK |
611 | if (pthread_mutex_init(lock, &attr) != 0) { |
612 | pthread_mutexattr_destroy(&attr); | |
613 | OPENSSL_free(lock); | |
614 | return NULL; | |
615 | } | |
5d5eed44 | 616 | |
2accf3f7 | 617 | pthread_mutexattr_destroy(&attr); |
ec93a292 | 618 | # endif |
71a04cfc AG |
619 | |
620 | return lock; | |
621 | } | |
622 | ||
cd3f8c1b | 623 | __owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock) |
71a04cfc | 624 | { |
ec93a292 | 625 | # ifdef USE_RWLOCK |
606de509 | 626 | if (!ossl_assert(pthread_rwlock_rdlock(lock) == 0)) |
71a04cfc | 627 | return 0; |
ec93a292 | 628 | # else |
e60147fe RS |
629 | if (pthread_mutex_lock(lock) != 0) { |
630 | assert(errno != EDEADLK && errno != EBUSY); | |
2accf3f7 | 631 | return 0; |
e60147fe | 632 | } |
ec93a292 | 633 | # endif |
71a04cfc AG |
634 | |
635 | return 1; | |
636 | } | |
637 | ||
cd3f8c1b | 638 | __owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock) |
71a04cfc | 639 | { |
ec93a292 | 640 | # ifdef USE_RWLOCK |
606de509 | 641 | if (!ossl_assert(pthread_rwlock_wrlock(lock) == 0)) |
71a04cfc | 642 | return 0; |
ec93a292 | 643 | # else |
e60147fe RS |
644 | if (pthread_mutex_lock(lock) != 0) { |
645 | assert(errno != EDEADLK && errno != EBUSY); | |
2accf3f7 | 646 | return 0; |
e60147fe | 647 | } |
ec93a292 | 648 | # endif |
71a04cfc AG |
649 | |
650 | return 1; | |
651 | } | |
652 | ||
653 | int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock) | |
654 | { | |
ec93a292 | 655 | # ifdef USE_RWLOCK |
71a04cfc AG |
656 | if (pthread_rwlock_unlock(lock) != 0) |
657 | return 0; | |
ec93a292 | 658 | # else |
e60147fe RS |
659 | if (pthread_mutex_unlock(lock) != 0) { |
660 | assert(errno != EPERM); | |
2accf3f7 | 661 | return 0; |
e60147fe | 662 | } |
ec93a292 | 663 | # endif |
71a04cfc AG |
664 | |
665 | return 1; | |
666 | } | |
667 | ||
668 | void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock) | |
669 | { | |
670 | if (lock == NULL) | |
671 | return; | |
672 | ||
ec93a292 | 673 | # ifdef USE_RWLOCK |
71a04cfc | 674 | pthread_rwlock_destroy(lock); |
ec93a292 | 675 | # else |
2accf3f7 | 676 | pthread_mutex_destroy(lock); |
ec93a292 | 677 | # endif |
71a04cfc AG |
678 | OPENSSL_free(lock); |
679 | ||
680 | return; | |
681 | } | |
682 | ||
683 | int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void)) | |
684 | { | |
685 | if (pthread_once(once, init) != 0) | |
686 | return 0; | |
687 | ||
688 | return 1; | |
689 | } | |
690 | ||
691 | int CRYPTO_THREAD_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *)) | |
692 | { | |
693 | if (pthread_key_create(key, cleanup) != 0) | |
694 | return 0; | |
695 | ||
696 | return 1; | |
697 | } | |
698 | ||
699 | void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key) | |
700 | { | |
701 | return pthread_getspecific(*key); | |
702 | } | |
703 | ||
704 | int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val) | |
705 | { | |
706 | if (pthread_setspecific(*key, val) != 0) | |
707 | return 0; | |
708 | ||
709 | return 1; | |
710 | } | |
711 | ||
712 | int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key) | |
713 | { | |
714 | if (pthread_key_delete(*key) != 0) | |
715 | return 0; | |
716 | ||
717 | return 1; | |
718 | } | |
719 | ||
720 | CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void) | |
721 | { | |
722 | return pthread_self(); | |
723 | } | |
724 | ||
725 | int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b) | |
726 | { | |
727 | return pthread_equal(a, b); | |
728 | } | |
729 | ||
730 | int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock) | |
731 | { | |
d39de479 | 732 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) |
1beca676 RL |
733 | if (__atomic_is_lock_free(sizeof(*val), val)) { |
734 | *ret = __atomic_add_fetch(val, amount, __ATOMIC_ACQ_REL); | |
735 | return 1; | |
736 | } | |
d6dda392 VK |
737 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) |
738 | /* This will work for all future Solaris versions. */ | |
739 | if (ret != NULL) { | |
740 | *ret = atomic_add_int_nv((volatile unsigned int *)val, amount); | |
741 | return 1; | |
742 | } | |
1beca676 | 743 | # endif |
d5e742de | 744 | if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) |
71a04cfc AG |
745 | return 0; |
746 | ||
747 | *val += amount; | |
748 | *ret = *val; | |
749 | ||
750 | if (!CRYPTO_THREAD_unlock(lock)) | |
751 | return 0; | |
71a04cfc AG |
752 | |
753 | return 1; | |
754 | } | |
755 | ||
16beec98 GV |
756 | int CRYPTO_atomic_add64(uint64_t *val, uint64_t op, uint64_t *ret, |
757 | CRYPTO_RWLOCK *lock) | |
758 | { | |
759 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) | |
760 | if (__atomic_is_lock_free(sizeof(*val), val)) { | |
761 | *ret = __atomic_add_fetch(val, op, __ATOMIC_ACQ_REL); | |
762 | return 1; | |
763 | } | |
764 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
765 | /* This will work for all future Solaris versions. */ | |
766 | if (ret != NULL) { | |
767 | *ret = atomic_add_64_nv(val, op); | |
768 | return 1; | |
769 | } | |
770 | # endif | |
771 | if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) | |
772 | return 0; | |
773 | *val += op; | |
774 | *ret = *val; | |
775 | ||
776 | if (!CRYPTO_THREAD_unlock(lock)) | |
777 | return 0; | |
778 | ||
779 | return 1; | |
780 | } | |
781 | ||
782 | int CRYPTO_atomic_and(uint64_t *val, uint64_t op, uint64_t *ret, | |
783 | CRYPTO_RWLOCK *lock) | |
784 | { | |
785 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) | |
786 | if (__atomic_is_lock_free(sizeof(*val), val)) { | |
787 | *ret = __atomic_and_fetch(val, op, __ATOMIC_ACQ_REL); | |
788 | return 1; | |
789 | } | |
790 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
791 | /* This will work for all future Solaris versions. */ | |
792 | if (ret != NULL) { | |
793 | *ret = atomic_and_64_nv(val, op); | |
794 | return 1; | |
795 | } | |
796 | # endif | |
797 | if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) | |
798 | return 0; | |
799 | *val &= op; | |
800 | *ret = *val; | |
801 | ||
802 | if (!CRYPTO_THREAD_unlock(lock)) | |
803 | return 0; | |
804 | ||
805 | return 1; | |
806 | } | |
807 | ||
d5e742de MC |
808 | int CRYPTO_atomic_or(uint64_t *val, uint64_t op, uint64_t *ret, |
809 | CRYPTO_RWLOCK *lock) | |
810 | { | |
d39de479 | 811 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) |
d5e742de MC |
812 | if (__atomic_is_lock_free(sizeof(*val), val)) { |
813 | *ret = __atomic_or_fetch(val, op, __ATOMIC_ACQ_REL); | |
814 | return 1; | |
815 | } | |
816 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
817 | /* This will work for all future Solaris versions. */ | |
818 | if (ret != NULL) { | |
819 | *ret = atomic_or_64_nv(val, op); | |
820 | return 1; | |
821 | } | |
822 | # endif | |
823 | if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) | |
824 | return 0; | |
825 | *val |= op; | |
826 | *ret = *val; | |
827 | ||
828 | if (!CRYPTO_THREAD_unlock(lock)) | |
829 | return 0; | |
830 | ||
831 | return 1; | |
832 | } | |
833 | ||
834 | int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock) | |
835 | { | |
3240427a | 836 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) |
d5e742de MC |
837 | if (__atomic_is_lock_free(sizeof(*val), val)) { |
838 | __atomic_load(val, ret, __ATOMIC_ACQUIRE); | |
839 | return 1; | |
840 | } | |
841 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
842 | /* This will work for all future Solaris versions. */ | |
843 | if (ret != NULL) { | |
844 | *ret = atomic_or_64_nv(val, 0); | |
845 | return 1; | |
846 | } | |
847 | # endif | |
848 | if (lock == NULL || !CRYPTO_THREAD_read_lock(lock)) | |
849 | return 0; | |
850 | *ret = *val; | |
851 | if (!CRYPTO_THREAD_unlock(lock)) | |
852 | return 0; | |
853 | ||
854 | return 1; | |
855 | } | |
629b408c | 856 | |
7e45ac68 NH |
857 | int CRYPTO_atomic_store(uint64_t *dst, uint64_t val, CRYPTO_RWLOCK *lock) |
858 | { | |
3240427a | 859 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) |
7e45ac68 NH |
860 | if (__atomic_is_lock_free(sizeof(*dst), dst)) { |
861 | __atomic_store(dst, &val, __ATOMIC_RELEASE); | |
862 | return 1; | |
863 | } | |
864 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
865 | /* This will work for all future Solaris versions. */ | |
4c04a198 | 866 | if (dst != NULL) { |
7e45ac68 NH |
867 | atomic_swap_64(dst, val); |
868 | return 1; | |
869 | } | |
870 | # endif | |
3190f5c0 | 871 | if (lock == NULL || !CRYPTO_THREAD_write_lock(lock)) |
7e45ac68 NH |
872 | return 0; |
873 | *dst = val; | |
874 | if (!CRYPTO_THREAD_unlock(lock)) | |
875 | return 0; | |
876 | ||
877 | return 1; | |
878 | } | |
879 | ||
629b408c HL |
880 | int CRYPTO_atomic_load_int(int *val, int *ret, CRYPTO_RWLOCK *lock) |
881 | { | |
3240427a | 882 | # if defined(__GNUC__) && defined(__ATOMIC_ACQ_REL) && !defined(BROKEN_CLANG_ATOMICS) |
629b408c HL |
883 | if (__atomic_is_lock_free(sizeof(*val), val)) { |
884 | __atomic_load(val, ret, __ATOMIC_ACQUIRE); | |
885 | return 1; | |
886 | } | |
887 | # elif defined(__sun) && (defined(__SunOS_5_10) || defined(__SunOS_5_11)) | |
888 | /* This will work for all future Solaris versions. */ | |
889 | if (ret != NULL) { | |
ce6b2f98 | 890 | *ret = (int)atomic_or_uint_nv((unsigned int *)val, 0); |
629b408c HL |
891 | return 1; |
892 | } | |
893 | # endif | |
894 | if (lock == NULL || !CRYPTO_THREAD_read_lock(lock)) | |
895 | return 0; | |
896 | *ret = *val; | |
897 | if (!CRYPTO_THREAD_unlock(lock)) | |
898 | return 0; | |
899 | ||
900 | return 1; | |
901 | } | |
902 | ||
f844f9eb | 903 | # ifndef FIPS_MODULE |
2915fe19 RS |
904 | int openssl_init_fork_handlers(void) |
905 | { | |
59795962 | 906 | return 1; |
2915fe19 | 907 | } |
f844f9eb | 908 | # endif /* FIPS_MODULE */ |
84952925 DMSP |
909 | |
910 | int openssl_get_fork_id(void) | |
911 | { | |
912 | return getpid(); | |
913 | } | |
71a04cfc | 914 | #endif |