1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 5 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice(s), this list of conditions and the following disclaimer as 13 * the first lines of this file unmodified other than the possible 14 * addition of one or more copyright notices. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice(s), this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * $FreeBSD$ 33 */ 34 35 /* 36 * Some notes about this implementation. 37 * 38 * This is mostly a simple implementation of POSIX semaphores that 39 * does not need threading. Any semaphore created is a kernel-based 40 * semaphore regardless of the pshared attribute. This is necessary 41 * because libc's stub for pthread_cond_wait() doesn't really wait, 42 * and it is not worth the effort impose this behavior on libc. 43 * 44 * All functions here are designed to be thread-safe so that a 45 * threads library need not provide wrappers except to make 46 * sem_wait() and sem_timedwait() cancellation points or to 47 * provide a faster userland implementation for non-pshared 48 * semaphores. 49 * 50 * Also, this implementation of semaphores cannot really support 51 * real pshared semaphores. The sem_t is an allocated object 52 * and can't be seen by other processes when placed in shared 53 * memory. It should work across forks as long as the semaphore 54 * is created before any forks. 55 * 56 * The function sem_init() should be overridden by a threads 57 * library if it wants to provide a different userland version 58 * of semaphores. The functions sem_wait() and sem_timedwait() 59 * need to be wrapped to provide cancellation points. The function 60 * sem_post() may need to be wrapped to be signal-safe. 61 */ 62 #include "namespace.h" 63 #include <sys/types.h> 64 #include <sys/queue.h> 65 #include <machine/atomic.h> 66 #include <errno.h> 67 #include <sys/umtx.h> 68 #include <sys/_semaphore.h> 69 #include <limits.h> 70 #include <fcntl.h> 71 #include <pthread.h> 72 #include <stdarg.h> 73 #include <stdlib.h> 74 #include <time.h> 75 #include "un-namespace.h" 76 #include "libc_private.h" 77 78 /* 79 * Old semaphore definitions. 80 */ 81 struct sem { 82 #define SEM_MAGIC ((u_int32_t) 0x09fa4012) 83 u_int32_t magic; 84 pthread_mutex_t lock; 85 pthread_cond_t gtzero; 86 u_int32_t count; 87 u_int32_t nwaiters; 88 #define SEM_USER (NULL) 89 semid_t semid; /* semaphore id if kernel (shared) semaphore */ 90 int syssem; /* 1 if kernel (shared) semaphore */ 91 LIST_ENTRY(sem) entry; 92 struct sem **backpointer; 93 }; 94 95 typedef struct sem* sem_t; 96 97 #define SEM_FAILED ((sem_t *)0) 98 #define SEM_VALUE_MAX __INT_MAX 99 100 #define SYM_FB10(sym) __CONCAT(sym, _fb10) 101 #define WEAK_REF(sym, alias) __weak_reference(sym, alias) 102 #define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) 103 104 #define FB10_COMPAT(func, sym) \ 105 WEAK_REF(func, SYM_FB10(sym)); \ 106 SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) 107 108 static sem_t sem_alloc(unsigned int value, semid_t semid, int system_sem); 109 static void sem_free(sem_t sem); 110 111 static LIST_HEAD(, sem) named_sems = LIST_HEAD_INITIALIZER(named_sems); 112 static pthread_mutex_t named_sems_mtx = PTHREAD_MUTEX_INITIALIZER; 113 114 FB10_COMPAT(_libc_sem_init_compat, sem_init); 115 FB10_COMPAT(_libc_sem_destroy_compat, sem_destroy); 116 FB10_COMPAT(_libc_sem_open_compat, sem_open); 117 FB10_COMPAT(_libc_sem_close_compat, sem_close); 118 FB10_COMPAT(_libc_sem_unlink_compat, sem_unlink); 119 FB10_COMPAT(_libc_sem_wait_compat, sem_wait); 120 FB10_COMPAT(_libc_sem_trywait_compat, sem_trywait); 121 FB10_COMPAT(_libc_sem_timedwait_compat, sem_timedwait); 122 FB10_COMPAT(_libc_sem_post_compat, sem_post); 123 FB10_COMPAT(_libc_sem_getvalue_compat, sem_getvalue); 124 125 static inline int 126 sem_check_validity(sem_t *sem) 127 { 128 129 if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) 130 return (0); 131 else { 132 errno = EINVAL; 133 return (-1); 134 } 135 } 136 137 static void 138 sem_free(sem_t sem) 139 { 140 141 sem->magic = 0; 142 free(sem); 143 } 144 145 static sem_t 146 sem_alloc(unsigned int value, semid_t semid, int system_sem) 147 { 148 sem_t sem; 149 150 if (value > SEM_VALUE_MAX) { 151 errno = EINVAL; 152 return (NULL); 153 } 154 155 sem = (sem_t)malloc(sizeof(struct sem)); 156 if (sem == NULL) { 157 errno = ENOSPC; 158 return (NULL); 159 } 160 161 sem->count = (u_int32_t)value; 162 sem->nwaiters = 0; 163 sem->magic = SEM_MAGIC; 164 sem->semid = semid; 165 sem->syssem = system_sem; 166 return (sem); 167 } 168 169 int 170 _libc_sem_init_compat(sem_t *sem, int pshared, unsigned int value) 171 { 172 semid_t semid; 173 174 /* 175 * We always have to create the kernel semaphore if the 176 * threads library isn't present since libc's version of 177 * pthread_cond_wait() is just a stub that doesn't really 178 * wait. 179 */ 180 semid = (semid_t)SEM_USER; 181 if ((pshared != 0) && ksem_init(&semid, value) != 0) 182 return (-1); 183 184 *sem = sem_alloc(value, semid, pshared); 185 if ((*sem) == NULL) { 186 if (pshared != 0) 187 ksem_destroy(semid); 188 return (-1); 189 } 190 return (0); 191 } 192 193 int 194 _libc_sem_destroy_compat(sem_t *sem) 195 { 196 int retval; 197 198 if (sem_check_validity(sem) != 0) 199 return (-1); 200 201 /* 202 * If this is a system semaphore let the kernel track it otherwise 203 * make sure there are no waiters. 204 */ 205 if ((*sem)->syssem != 0) 206 retval = ksem_destroy((*sem)->semid); 207 else if ((*sem)->nwaiters > 0) { 208 errno = EBUSY; 209 retval = -1; 210 } 211 else { 212 retval = 0; 213 (*sem)->magic = 0; 214 } 215 216 if (retval == 0) 217 sem_free(*sem); 218 return (retval); 219 } 220 221 sem_t * 222 _libc_sem_open_compat(const char *name, int oflag, ...) 223 { 224 sem_t *sem; 225 sem_t s; 226 semid_t semid; 227 mode_t mode; 228 unsigned int value; 229 230 mode = 0; 231 value = 0; 232 233 if ((oflag & O_CREAT) != 0) { 234 va_list ap; 235 236 va_start(ap, oflag); 237 mode = va_arg(ap, int); 238 value = va_arg(ap, unsigned int); 239 va_end(ap); 240 } 241 /* 242 * we can be lazy and let the kernel handle the "oflag", 243 * we'll just merge duplicate IDs into our list. 244 */ 245 if (ksem_open(&semid, name, oflag, mode, value) == -1) 246 return (SEM_FAILED); 247 /* 248 * search for a duplicate ID, we must return the same sem_t * 249 * if we locate one. 250 */ 251 _pthread_mutex_lock(&named_sems_mtx); 252 LIST_FOREACH(s, &named_sems, entry) { 253 if (s->semid == semid) { 254 sem = s->backpointer; 255 _pthread_mutex_unlock(&named_sems_mtx); 256 return (sem); 257 } 258 } 259 sem = (sem_t *)malloc(sizeof(*sem)); 260 if (sem == NULL) 261 goto err; 262 *sem = sem_alloc(value, semid, 1); 263 if ((*sem) == NULL) 264 goto err; 265 LIST_INSERT_HEAD(&named_sems, *sem, entry); 266 (*sem)->backpointer = sem; 267 _pthread_mutex_unlock(&named_sems_mtx); 268 return (sem); 269 err: 270 _pthread_mutex_unlock(&named_sems_mtx); 271 ksem_close(semid); 272 if (sem != NULL) { 273 if (*sem != NULL) 274 sem_free(*sem); 275 else 276 errno = ENOSPC; 277 free(sem); 278 } else { 279 errno = ENOSPC; 280 } 281 return (SEM_FAILED); 282 } 283 284 int 285 _libc_sem_close_compat(sem_t *sem) 286 { 287 288 if (sem_check_validity(sem) != 0) 289 return (-1); 290 291 if ((*sem)->syssem == 0) { 292 errno = EINVAL; 293 return (-1); 294 } 295 296 _pthread_mutex_lock(&named_sems_mtx); 297 if (ksem_close((*sem)->semid) != 0) { 298 _pthread_mutex_unlock(&named_sems_mtx); 299 return (-1); 300 } 301 LIST_REMOVE((*sem), entry); 302 _pthread_mutex_unlock(&named_sems_mtx); 303 sem_free(*sem); 304 *sem = NULL; 305 free(sem); 306 return (0); 307 } 308 309 int 310 _libc_sem_unlink_compat(const char *name) 311 { 312 313 return (ksem_unlink(name)); 314 } 315 316 static int 317 _umtx_wait_uint(volatile unsigned *mtx, unsigned id, const struct timespec *abstime) 318 { 319 struct _umtx_time *tm_p, timeout; 320 size_t tm_size; 321 322 if (abstime == NULL) { 323 tm_p = NULL; 324 tm_size = 0; 325 } else { 326 timeout._clockid = CLOCK_REALTIME; 327 timeout._flags = UMTX_ABSTIME; 328 timeout._timeout = *abstime; 329 tm_p = &timeout; 330 tm_size = sizeof(timeout); 331 } 332 return _umtx_op(__DEVOLATILE(void *, mtx), 333 UMTX_OP_WAIT_UINT_PRIVATE, id, 334 (void *)tm_size, __DECONST(void*, tm_p)); 335 } 336 337 static int 338 _umtx_wake(volatile void *mtx) 339 { 340 return _umtx_op(__DEVOLATILE(void *, mtx), UMTX_OP_WAKE_PRIVATE, 341 1, NULL, NULL); 342 } 343 344 #define TIMESPEC_SUB(dst, src, val) \ 345 do { \ 346 (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ 347 (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ 348 if ((dst)->tv_nsec < 0) { \ 349 (dst)->tv_sec--; \ 350 (dst)->tv_nsec += 1000000000; \ 351 } \ 352 } while (0) 353 354 355 static void 356 sem_cancel_handler(void *arg) 357 { 358 sem_t *sem = arg; 359 360 atomic_add_int(&(*sem)->nwaiters, -1); 361 if ((*sem)->nwaiters && (*sem)->count) 362 _umtx_wake(&(*sem)->count); 363 } 364 365 int 366 _libc_sem_timedwait_compat(sem_t * __restrict sem, 367 const struct timespec * __restrict abstime) 368 { 369 int val, retval; 370 371 if (sem_check_validity(sem) != 0) 372 return (-1); 373 374 if ((*sem)->syssem != 0) { 375 _pthread_cancel_enter(1); 376 retval = ksem_wait((*sem)->semid); /* XXX no timeout */ 377 _pthread_cancel_leave(retval == -1); 378 return (retval); 379 } 380 381 retval = 0; 382 _pthread_testcancel(); 383 for (;;) { 384 while ((val = (*sem)->count) > 0) { 385 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 386 return (0); 387 } 388 if (retval) { 389 _pthread_testcancel(); 390 break; 391 } 392 if (abstime) { 393 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 394 errno = EINVAL; 395 return (-1); 396 } 397 } 398 atomic_add_int(&(*sem)->nwaiters, 1); 399 pthread_cleanup_push(sem_cancel_handler, sem); 400 _pthread_cancel_enter(1); 401 retval = _umtx_wait_uint(&(*sem)->count, 0, abstime); 402 _pthread_cancel_leave(0); 403 pthread_cleanup_pop(0); 404 atomic_add_int(&(*sem)->nwaiters, -1); 405 } 406 return (retval); 407 } 408 409 int 410 _libc_sem_wait_compat(sem_t *sem) 411 { 412 return _libc_sem_timedwait_compat(sem, NULL); 413 } 414 415 int 416 _libc_sem_trywait_compat(sem_t *sem) 417 { 418 int val; 419 420 if (sem_check_validity(sem) != 0) 421 return (-1); 422 423 if ((*sem)->syssem != 0) 424 return ksem_trywait((*sem)->semid); 425 426 while ((val = (*sem)->count) > 0) { 427 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) 428 return (0); 429 } 430 errno = EAGAIN; 431 return (-1); 432 } 433 434 int 435 _libc_sem_post_compat(sem_t *sem) 436 { 437 438 if (sem_check_validity(sem) != 0) 439 return (-1); 440 441 if ((*sem)->syssem != 0) 442 return ksem_post((*sem)->semid); 443 444 atomic_add_rel_int(&(*sem)->count, 1); 445 rmb(); 446 if ((*sem)->nwaiters) 447 return _umtx_wake(&(*sem)->count); 448 return (0); 449 } 450 451 int 452 _libc_sem_getvalue_compat(sem_t * __restrict sem, int * __restrict sval) 453 { 454 int retval; 455 456 if (sem_check_validity(sem) != 0) 457 return (-1); 458 459 if ((*sem)->syssem != 0) 460 retval = ksem_getvalue((*sem)->semid, sval); 461 else { 462 *sval = (int)(*sem)->count; 463 retval = 0; 464 } 465 return (retval); 466 } 467