1 /* 2 * Copyright (C) 2010 David Xu <davidxu@freebsd.org>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice(s), this list of conditions and the following disclaimer as 10 * the first lines of this file unmodified other than the possible 11 * addition of one or more copyright notices. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice(s), this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include "namespace.h" 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <errno.h> 38 #include <machine/atomic.h> 39 #include <sys/umtx.h> 40 #include <limits.h> 41 #include <fcntl.h> 42 #include <pthread.h> 43 #include <stdarg.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <time.h> 47 #include <semaphore.h> 48 #include <unistd.h> 49 #include "un-namespace.h" 50 #include "libc_private.h" 51 52 __weak_reference(_sem_close, sem_close); 53 __weak_reference(_sem_destroy, sem_destroy); 54 __weak_reference(_sem_getvalue, sem_getvalue); 55 __weak_reference(_sem_init, sem_init); 56 __weak_reference(_sem_open, sem_open); 57 __weak_reference(_sem_post, sem_post); 58 __weak_reference(_sem_timedwait, sem_timedwait); 59 __weak_reference(_sem_trywait, sem_trywait); 60 __weak_reference(_sem_unlink, sem_unlink); 61 __weak_reference(_sem_wait, sem_wait); 62 63 #define SEM_PREFIX "/tmp/SEMD" 64 #define SEM_MAGIC ((u_int32_t)0x73656d32) 65 66 _Static_assert(SEM_VALUE_MAX <= USEM_MAX_COUNT, "SEM_VALUE_MAX too large"); 67 68 struct sem_nameinfo { 69 int open_count; 70 char *name; 71 dev_t dev; 72 ino_t ino; 73 sem_t *sem; 74 LIST_ENTRY(sem_nameinfo) next; 75 }; 76 77 static pthread_once_t once = PTHREAD_ONCE_INIT; 78 static pthread_mutex_t sem_llock; 79 static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); 80 81 static void 82 sem_prefork() 83 { 84 85 _pthread_mutex_lock(&sem_llock); 86 } 87 88 static void 89 sem_postfork() 90 { 91 _pthread_mutex_unlock(&sem_llock); 92 } 93 94 static void 95 sem_child_postfork() 96 { 97 _pthread_mutex_unlock(&sem_llock); 98 } 99 100 static void 101 sem_module_init(void) 102 { 103 pthread_mutexattr_t ma; 104 105 _pthread_mutexattr_init(&ma); 106 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); 107 _pthread_mutex_init(&sem_llock, &ma); 108 _pthread_mutexattr_destroy(&ma); 109 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); 110 } 111 112 static inline int 113 sem_check_validity(sem_t *sem) 114 { 115 116 if (sem->_magic == SEM_MAGIC) 117 return (0); 118 else { 119 errno = EINVAL; 120 return (-1); 121 } 122 } 123 124 int 125 _sem_init(sem_t *sem, int pshared, unsigned int value) 126 { 127 128 if (value > SEM_VALUE_MAX) { 129 errno = EINVAL; 130 return (-1); 131 } 132 133 bzero(sem, sizeof(sem_t)); 134 sem->_magic = SEM_MAGIC; 135 sem->_kern._count = (u_int32_t)value; 136 sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; 137 return (0); 138 } 139 140 sem_t * 141 _sem_open(const char *name, int flags, ...) 142 { 143 char path[PATH_MAX]; 144 145 struct stat sb; 146 va_list ap; 147 struct sem_nameinfo *ni = NULL; 148 sem_t *sem = NULL; 149 int fd = -1, mode, len, errsave; 150 int value = 0; 151 152 if (name[0] != '/') { 153 errno = EINVAL; 154 return (SEM_FAILED); 155 } 156 name++; 157 strcpy(path, SEM_PREFIX); 158 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 159 errno = ENAMETOOLONG; 160 return (SEM_FAILED); 161 } 162 if (flags & ~(O_CREAT|O_EXCL)) { 163 errno = EINVAL; 164 return (SEM_FAILED); 165 } 166 if ((flags & O_CREAT) != 0) { 167 va_start(ap, flags); 168 mode = va_arg(ap, int); 169 value = va_arg(ap, int); 170 va_end(ap); 171 } 172 fd = -1; 173 _pthread_once(&once, sem_module_init); 174 175 _pthread_mutex_lock(&sem_llock); 176 LIST_FOREACH(ni, &sem_list, next) { 177 if (ni->name != NULL && strcmp(name, ni->name) == 0) { 178 fd = _open(path, flags | O_RDWR | O_CLOEXEC | 179 O_EXLOCK, mode); 180 if (fd == -1 || _fstat(fd, &sb) == -1) { 181 ni = NULL; 182 goto error; 183 } 184 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | 185 O_EXCL) || ni->dev != sb.st_dev || 186 ni->ino != sb.st_ino) { 187 ni->name = NULL; 188 ni = NULL; 189 break; 190 } 191 ni->open_count++; 192 sem = ni->sem; 193 _pthread_mutex_unlock(&sem_llock); 194 _close(fd); 195 return (sem); 196 } 197 } 198 199 len = sizeof(*ni) + strlen(name) + 1; 200 ni = (struct sem_nameinfo *)malloc(len); 201 if (ni == NULL) { 202 errno = ENOSPC; 203 goto error; 204 } 205 206 ni->name = (char *)(ni+1); 207 strcpy(ni->name, name); 208 209 if (fd == -1) { 210 fd = _open(path, flags | O_RDWR | O_CLOEXEC | O_EXLOCK, mode); 211 if (fd == -1 || _fstat(fd, &sb) == -1) 212 goto error; 213 } 214 if (sb.st_size < sizeof(sem_t)) { 215 sem_t tmp; 216 217 tmp._magic = SEM_MAGIC; 218 tmp._kern._count = value; 219 tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; 220 if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) 221 goto error; 222 } 223 flock(fd, LOCK_UN); 224 sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, 225 MAP_SHARED|MAP_NOSYNC, fd, 0); 226 if (sem == MAP_FAILED) { 227 sem = NULL; 228 if (errno == ENOMEM) 229 errno = ENOSPC; 230 goto error; 231 } 232 if (sem->_magic != SEM_MAGIC) { 233 errno = EINVAL; 234 goto error; 235 } 236 ni->open_count = 1; 237 ni->sem = sem; 238 ni->dev = sb.st_dev; 239 ni->ino = sb.st_ino; 240 LIST_INSERT_HEAD(&sem_list, ni, next); 241 _close(fd); 242 _pthread_mutex_unlock(&sem_llock); 243 return (sem); 244 245 error: 246 errsave = errno; 247 if (fd != -1) 248 _close(fd); 249 if (sem != NULL) 250 munmap(sem, sizeof(sem_t)); 251 free(ni); 252 _pthread_mutex_unlock(&sem_llock); 253 errno = errsave; 254 return (SEM_FAILED); 255 } 256 257 int 258 _sem_close(sem_t *sem) 259 { 260 struct sem_nameinfo *ni; 261 262 if (sem_check_validity(sem) != 0) 263 return (-1); 264 265 if (!(sem->_kern._flags & SEM_NAMED)) { 266 errno = EINVAL; 267 return (-1); 268 } 269 270 _pthread_once(&once, sem_module_init); 271 272 _pthread_mutex_lock(&sem_llock); 273 LIST_FOREACH(ni, &sem_list, next) { 274 if (sem == ni->sem) { 275 if (--ni->open_count > 0) { 276 _pthread_mutex_unlock(&sem_llock); 277 return (0); 278 } 279 else 280 break; 281 } 282 } 283 284 if (ni) { 285 LIST_REMOVE(ni, next); 286 _pthread_mutex_unlock(&sem_llock); 287 munmap(sem, sizeof(*sem)); 288 free(ni); 289 return (0); 290 } 291 _pthread_mutex_unlock(&sem_llock); 292 errno = EINVAL; 293 return (-1); 294 } 295 296 int 297 _sem_unlink(const char *name) 298 { 299 char path[PATH_MAX]; 300 301 if (name[0] != '/') { 302 errno = ENOENT; 303 return -1; 304 } 305 name++; 306 strcpy(path, SEM_PREFIX); 307 if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { 308 errno = ENAMETOOLONG; 309 return (-1); 310 } 311 312 return (unlink(path)); 313 } 314 315 int 316 _sem_destroy(sem_t *sem) 317 { 318 319 if (sem_check_validity(sem) != 0) 320 return (-1); 321 322 if (sem->_kern._flags & SEM_NAMED) { 323 errno = EINVAL; 324 return (-1); 325 } 326 sem->_magic = 0; 327 return (0); 328 } 329 330 int 331 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval) 332 { 333 334 if (sem_check_validity(sem) != 0) 335 return (-1); 336 337 *sval = (int)USEM_COUNT(sem->_kern._count); 338 return (0); 339 } 340 341 static __inline int 342 usem_wake(struct _usem2 *sem) 343 { 344 return _umtx_op(sem, UMTX_OP_SEM2_WAKE, 0, NULL, NULL); 345 } 346 347 static __inline int 348 usem_wait(struct _usem2 *sem, const struct timespec *abstime) 349 { 350 struct _umtx_time *tm_p, timeout; 351 size_t tm_size; 352 353 if (abstime == NULL) { 354 tm_p = NULL; 355 tm_size = 0; 356 } else { 357 timeout._clockid = CLOCK_REALTIME; 358 timeout._flags = UMTX_ABSTIME; 359 timeout._timeout = *abstime; 360 tm_p = &timeout; 361 tm_size = sizeof(timeout); 362 } 363 return _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, 364 (void *)tm_size, __DECONST(void*, tm_p)); 365 } 366 367 int 368 _sem_trywait(sem_t *sem) 369 { 370 int val; 371 372 if (sem_check_validity(sem) != 0) 373 return (-1); 374 375 while (USEM_COUNT(val = sem->_kern._count) > 0) { 376 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 377 return (0); 378 } 379 errno = EAGAIN; 380 return (-1); 381 } 382 383 int 384 _sem_timedwait(sem_t * __restrict sem, 385 const struct timespec * __restrict abstime) 386 { 387 int val, retval; 388 389 if (sem_check_validity(sem) != 0) 390 return (-1); 391 392 retval = 0; 393 _pthread_testcancel(); 394 for (;;) { 395 while (USEM_COUNT(val = sem->_kern._count) > 0) { 396 if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) 397 return (0); 398 } 399 400 if (retval) { 401 _pthread_testcancel(); 402 break; 403 } 404 405 /* 406 * The timeout argument is only supposed to 407 * be checked if the thread would have blocked. 408 */ 409 if (abstime != NULL) { 410 if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { 411 errno = EINVAL; 412 return (-1); 413 } 414 } 415 _pthread_cancel_enter(1); 416 retval = usem_wait(&sem->_kern, abstime); 417 _pthread_cancel_leave(0); 418 } 419 return (retval); 420 } 421 422 int 423 _sem_wait(sem_t *sem) 424 { 425 return _sem_timedwait(sem, NULL); 426 } 427 428 /* 429 * POSIX: 430 * The sem_post() interface is reentrant with respect to signals and may be 431 * invoked from a signal-catching function. 432 * The implementation does not use lock, so it should be safe. 433 */ 434 int 435 _sem_post(sem_t *sem) 436 { 437 unsigned int count; 438 439 if (sem_check_validity(sem) != 0) 440 return (-1); 441 442 do { 443 count = sem->_kern._count; 444 if (USEM_COUNT(count) + 1 > SEM_VALUE_MAX) { 445 errno = EOVERFLOW; 446 return (-1); 447 } 448 } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, count + 1)); 449 if (count & USEM_HAS_WAITERS) 450 usem_wake(&sem->_kern); 451 return (0); 452 } 453