1 /* $NetBSD: uipc_sem.c,v 1.48 2017/11/30 20:25:55 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 /* 59 * Implementation of POSIX semaphore. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.48 2017/11/30 20:25:55 christos Exp $"); 64 65 #include <sys/param.h> 66 #include <sys/kernel.h> 67 68 #include <sys/atomic.h> 69 #include <sys/proc.h> 70 #include <sys/ksem.h> 71 #include <sys/syscall.h> 72 #include <sys/stat.h> 73 #include <sys/kmem.h> 74 #include <sys/fcntl.h> 75 #include <sys/file.h> 76 #include <sys/filedesc.h> 77 #include <sys/kauth.h> 78 #include <sys/module.h> 79 #include <sys/mount.h> 80 #include <sys/semaphore.h> 81 #include <sys/syscall.h> 82 #include <sys/syscallargs.h> 83 #include <sys/syscallvar.h> 84 #include <sys/sysctl.h> 85 86 MODULE(MODULE_CLASS_MISC, ksem, NULL); 87 88 #define SEM_MAX_NAMELEN 14 89 90 #define SEM_NSEMS_MAX 256 91 #define KS_UNLINKED 0x01 92 93 static kmutex_t ksem_lock __cacheline_aligned; 94 static LIST_HEAD(,ksem) ksem_head __cacheline_aligned; 95 static u_int nsems_total __cacheline_aligned; 96 static u_int nsems __cacheline_aligned; 97 98 static kauth_listener_t ksem_listener; 99 100 static int ksem_sysinit(void); 101 static int ksem_sysfini(bool); 102 static int ksem_modcmd(modcmd_t, void *); 103 static int ksem_close_fop(file_t *); 104 static int ksem_stat_fop(file_t *, struct stat *); 105 static int ksem_read_fop(file_t *, off_t *, struct uio *, 106 kauth_cred_t, int); 107 108 static const struct fileops semops = { 109 .fo_name = "sem", 110 .fo_read = ksem_read_fop, 111 .fo_write = fbadop_write, 112 .fo_ioctl = fbadop_ioctl, 113 .fo_fcntl = fnullop_fcntl, 114 .fo_poll = fnullop_poll, 115 .fo_stat = ksem_stat_fop, 116 .fo_close = ksem_close_fop, 117 .fo_kqfilter = fnullop_kqfilter, 118 .fo_restart = fnullop_restart, 119 }; 120 121 static const struct syscall_package ksem_syscalls[] = { 122 { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init }, 123 { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open }, 124 { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink }, 125 { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close }, 126 { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post }, 127 { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait }, 128 { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait }, 129 { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue }, 130 { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy }, 131 { SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait }, 132 { 0, 0, NULL }, 133 }; 134 135 struct sysctllog *ksem_clog; 136 int ksem_max; 137 138 static int 139 ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 140 void *arg0, void *arg1, void *arg2, void *arg3) 141 { 142 ksem_t *ks; 143 mode_t mode; 144 145 if (action != KAUTH_SYSTEM_SEMAPHORE) 146 return KAUTH_RESULT_DEFER; 147 148 ks = arg1; 149 mode = ks->ks_mode; 150 151 if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) || 152 (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) || 153 (mode & S_IWOTH) != 0) 154 return KAUTH_RESULT_ALLOW; 155 156 return KAUTH_RESULT_DEFER; 157 } 158 159 static int 160 ksem_sysinit(void) 161 { 162 int error; 163 const struct sysctlnode *rnode; 164 165 mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE); 166 LIST_INIT(&ksem_head); 167 nsems_total = 0; 168 nsems = 0; 169 170 error = syscall_establish(NULL, ksem_syscalls); 171 if (error) { 172 (void)ksem_sysfini(false); 173 } 174 175 ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, 176 ksem_listener_cb, NULL); 177 178 /* Define module-specific sysctl tree */ 179 180 ksem_max = KSEM_MAX; 181 ksem_clog = NULL; 182 183 sysctl_createv(&ksem_clog, 0, NULL, &rnode, 184 CTLFLAG_PERMANENT, 185 CTLTYPE_NODE, "posix", 186 SYSCTL_DESCR("POSIX options"), 187 NULL, 0, NULL, 0, 188 CTL_KERN, CTL_CREATE, CTL_EOL); 189 sysctl_createv(&ksem_clog, 0, &rnode, NULL, 190 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 191 CTLTYPE_INT, "semmax", 192 SYSCTL_DESCR("Maximal number of semaphores"), 193 NULL, 0, &ksem_max, 0, 194 CTL_CREATE, CTL_EOL); 195 sysctl_createv(&ksem_clog, 0, &rnode, NULL, 196 CTLFLAG_PERMANENT | CTLFLAG_READONLY, 197 CTLTYPE_INT, "semcnt", 198 SYSCTL_DESCR("Current number of semaphores"), 199 NULL, 0, &nsems, 0, 200 CTL_CREATE, CTL_EOL); 201 202 return error; 203 } 204 205 static int 206 ksem_sysfini(bool interface) 207 { 208 int error; 209 210 if (interface) { 211 error = syscall_disestablish(NULL, ksem_syscalls); 212 if (error != 0) { 213 return error; 214 } 215 /* 216 * Make sure that no semaphores are in use. Note: semops 217 * must be unused at this point. 218 */ 219 if (nsems_total) { 220 error = syscall_establish(NULL, ksem_syscalls); 221 KASSERT(error == 0); 222 return EBUSY; 223 } 224 } 225 kauth_unlisten_scope(ksem_listener); 226 mutex_destroy(&ksem_lock); 227 sysctl_teardown(&ksem_clog); 228 return 0; 229 } 230 231 static int 232 ksem_modcmd(modcmd_t cmd, void *arg) 233 { 234 235 switch (cmd) { 236 case MODULE_CMD_INIT: 237 return ksem_sysinit(); 238 239 case MODULE_CMD_FINI: 240 return ksem_sysfini(true); 241 242 default: 243 return ENOTTY; 244 } 245 } 246 247 static ksem_t * 248 ksem_lookup(const char *name) 249 { 250 ksem_t *ks; 251 252 KASSERT(mutex_owned(&ksem_lock)); 253 254 LIST_FOREACH(ks, &ksem_head, ks_entry) { 255 if (strcmp(ks->ks_name, name) == 0) { 256 mutex_enter(&ks->ks_lock); 257 return ks; 258 } 259 } 260 return NULL; 261 } 262 263 static int 264 ksem_perm(lwp_t *l, ksem_t *ks) 265 { 266 kauth_cred_t uc = l->l_cred; 267 268 KASSERT(mutex_owned(&ks->ks_lock)); 269 270 if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0) 271 return EACCES; 272 273 return 0; 274 } 275 276 /* 277 * ksem_get: get the semaphore from the descriptor. 278 * 279 * => locks the semaphore, if found. 280 * => holds a reference on the file descriptor. 281 */ 282 static int 283 ksem_get(int fd, ksem_t **ksret) 284 { 285 ksem_t *ks; 286 file_t *fp; 287 288 fp = fd_getfile(fd); 289 if (__predict_false(fp == NULL)) 290 return EINVAL; 291 if (__predict_false(fp->f_type != DTYPE_SEM)) { 292 fd_putfile(fd); 293 return EINVAL; 294 } 295 ks = fp->f_ksem; 296 mutex_enter(&ks->ks_lock); 297 298 *ksret = ks; 299 return 0; 300 } 301 302 /* 303 * ksem_create: allocate and setup a new semaphore structure. 304 */ 305 static int 306 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val) 307 { 308 ksem_t *ks; 309 kauth_cred_t uc; 310 char *kname; 311 size_t len; 312 313 /* Pre-check for the limit. */ 314 if (nsems >= ksem_max) { 315 return ENFILE; 316 } 317 318 if (val > SEM_VALUE_MAX) { 319 return EINVAL; 320 } 321 322 if (name != NULL) { 323 len = strlen(name); 324 if (len > SEM_MAX_NAMELEN) { 325 return ENAMETOOLONG; 326 } 327 /* Name must start with a '/' but not contain one. */ 328 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 329 return EINVAL; 330 } 331 kname = kmem_alloc(++len, KM_SLEEP); 332 strlcpy(kname, name, len); 333 } else { 334 kname = NULL; 335 len = 0; 336 } 337 338 if (atomic_inc_uint_nv(&l->l_proc->p_nsems) > SEM_NSEMS_MAX) { 339 atomic_dec_uint(&l->l_proc->p_nsems); 340 if (kname != NULL) 341 kmem_free(kname, len); 342 return -1; 343 } 344 345 ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP); 346 mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE); 347 cv_init(&ks->ks_cv, "psem"); 348 ks->ks_name = kname; 349 ks->ks_namelen = len; 350 ks->ks_mode = mode; 351 ks->ks_value = val; 352 ks->ks_ref = 1; 353 354 uc = l->l_cred; 355 ks->ks_uid = kauth_cred_geteuid(uc); 356 ks->ks_gid = kauth_cred_getegid(uc); 357 358 atomic_inc_uint(&nsems_total); 359 *ksret = ks; 360 return 0; 361 } 362 363 static void 364 ksem_free(ksem_t *ks) 365 { 366 367 KASSERT(!cv_has_waiters(&ks->ks_cv)); 368 369 if (ks->ks_name) { 370 KASSERT(ks->ks_namelen > 0); 371 kmem_free(ks->ks_name, ks->ks_namelen); 372 } 373 mutex_destroy(&ks->ks_lock); 374 cv_destroy(&ks->ks_cv); 375 kmem_free(ks, sizeof(ksem_t)); 376 377 atomic_dec_uint(&nsems_total); 378 atomic_dec_uint(&curproc->p_nsems); 379 } 380 381 int 382 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, 383 register_t *retval) 384 { 385 /* { 386 unsigned int value; 387 intptr_t *idp; 388 } */ 389 390 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); 391 } 392 393 int 394 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout) 395 { 396 proc_t *p = l->l_proc; 397 ksem_t *ks; 398 file_t *fp; 399 intptr_t id; 400 int fd, error; 401 402 error = fd_allocfile(&fp, &fd); 403 if (error) { 404 return error; 405 } 406 fp->f_type = DTYPE_SEM; 407 fp->f_flag = FREAD | FWRITE; 408 fp->f_ops = &semops; 409 410 id = (intptr_t)fd; 411 error = (*docopyout)(&id, idp, sizeof(*idp)); 412 if (error) { 413 fd_abort(p, fp, fd); 414 return error; 415 } 416 417 /* Note the mode does not matter for anonymous semaphores. */ 418 error = ksem_create(l, NULL, &ks, 0, val); 419 if (error) { 420 fd_abort(p, fp, fd); 421 return error; 422 } 423 fp->f_ksem = ks; 424 fd_affix(p, fp, fd); 425 return error; 426 } 427 428 int 429 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, 430 register_t *retval) 431 { 432 /* { 433 const char *name; 434 int oflag; 435 mode_t mode; 436 unsigned int value; 437 intptr_t *idp; 438 } */ 439 440 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 441 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 442 } 443 444 int 445 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 446 unsigned int value, intptr_t *idp, copyout_t docopyout) 447 { 448 char name[SEM_MAX_NAMELEN + 1]; 449 proc_t *p = l->l_proc; 450 ksem_t *ksnew = NULL, *ks; 451 file_t *fp; 452 intptr_t id; 453 int fd, error; 454 455 error = copyinstr(semname, name, sizeof(name), NULL); 456 if (error) { 457 return error; 458 } 459 error = fd_allocfile(&fp, &fd); 460 if (error) { 461 return error; 462 } 463 fp->f_type = DTYPE_SEM; 464 fp->f_flag = FREAD | FWRITE; 465 fp->f_ops = &semops; 466 467 /* 468 * The ID (file descriptor number) can be stored early. 469 * Note that zero is a special value for libpthread. 470 */ 471 id = (intptr_t)fd; 472 error = (*docopyout)(&id, idp, sizeof(*idp)); 473 if (error) { 474 goto err; 475 } 476 477 if (oflag & O_CREAT) { 478 /* Create a new semaphore. */ 479 error = ksem_create(l, name, &ksnew, mode, value); 480 if (error) { 481 goto err; 482 } 483 KASSERT(ksnew != NULL); 484 } 485 486 /* Lookup for a semaphore with such name. */ 487 mutex_enter(&ksem_lock); 488 ks = ksem_lookup(name); 489 if (ks) { 490 KASSERT(mutex_owned(&ks->ks_lock)); 491 mutex_exit(&ksem_lock); 492 493 /* Check for exclusive create. */ 494 if (oflag & O_EXCL) { 495 mutex_exit(&ks->ks_lock); 496 error = EEXIST; 497 goto err; 498 } 499 /* 500 * Verify permissions. If we can access it, 501 * add the reference of this thread. 502 */ 503 error = ksem_perm(l, ks); 504 if (error == 0) { 505 ks->ks_ref++; 506 } 507 mutex_exit(&ks->ks_lock); 508 if (error) { 509 goto err; 510 } 511 } else { 512 /* Fail if not found and not creating. */ 513 if ((oflag & O_CREAT) == 0) { 514 mutex_exit(&ksem_lock); 515 KASSERT(ksnew == NULL); 516 error = ENOENT; 517 goto err; 518 } 519 520 /* Check for the limit locked. */ 521 if (nsems >= ksem_max) { 522 mutex_exit(&ksem_lock); 523 error = ENFILE; 524 goto err; 525 } 526 527 /* 528 * Finally, insert semaphore into the list. 529 * Note: it already has the initial reference. 530 */ 531 ks = ksnew; 532 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); 533 nsems++; 534 mutex_exit(&ksem_lock); 535 536 ksnew = NULL; 537 } 538 KASSERT(ks != NULL); 539 fp->f_ksem = ks; 540 fd_affix(p, fp, fd); 541 err: 542 if (error) { 543 fd_abort(p, fp, fd); 544 } 545 if (ksnew) { 546 ksem_free(ksnew); 547 } 548 return error; 549 } 550 551 int 552 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, 553 register_t *retval) 554 { 555 /* { 556 intptr_t id; 557 } */ 558 int fd = (int)SCARG(uap, id); 559 560 if (fd_getfile(fd) == NULL) { 561 return EBADF; 562 } 563 return fd_close(fd); 564 } 565 566 static int 567 ksem_read_fop(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred, 568 int flags) 569 { 570 size_t len; 571 char *name; 572 ksem_t *ks = fp->f_ksem; 573 574 mutex_enter(&ks->ks_lock); 575 len = ks->ks_namelen; 576 name = ks->ks_name; 577 mutex_exit(&ks->ks_lock); 578 if (name == NULL || len == 0) 579 return 0; 580 return uiomove(name, len, uio); 581 } 582 583 static int 584 ksem_stat_fop(file_t *fp, struct stat *ub) 585 { 586 ksem_t *ks = fp->f_ksem; 587 588 mutex_enter(&ks->ks_lock); 589 590 memset(ub, 0, sizeof(*ub)); 591 592 ub->st_mode = ks->ks_mode | ((ks->ks_name && ks->ks_namelen) 593 ? _S_IFLNK : _S_IFREG); 594 ub->st_uid = ks->ks_uid; 595 ub->st_gid = ks->ks_gid; 596 ub->st_size = ks->ks_value; 597 ub->st_blocks = (ub->st_size) ? 1 : 0; 598 ub->st_nlink = ks->ks_ref; 599 ub->st_blksize = 4096; 600 601 nanotime(&ub->st_atimespec); 602 ub->st_mtimespec = ub->st_ctimespec = ub->st_birthtimespec = 603 ub->st_atimespec; 604 605 /* 606 * Left as 0: st_dev, st_ino, st_rdev, st_flags, st_gen. 607 * XXX (st_dev, st_ino) should be unique. 608 */ 609 mutex_exit(&ks->ks_lock); 610 return 0; 611 } 612 613 static int 614 ksem_close_fop(file_t *fp) 615 { 616 ksem_t *ks = fp->f_ksem; 617 bool destroy = false; 618 619 mutex_enter(&ks->ks_lock); 620 KASSERT(ks->ks_ref > 0); 621 if (--ks->ks_ref == 0) { 622 /* 623 * Destroy if the last reference and semaphore is unnamed, 624 * or unlinked (for named semaphore). 625 */ 626 destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL); 627 } 628 mutex_exit(&ks->ks_lock); 629 630 if (destroy) { 631 ksem_free(ks); 632 } 633 return 0; 634 } 635 636 int 637 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, 638 register_t *retval) 639 { 640 /* { 641 const char *name; 642 } */ 643 char name[SEM_MAX_NAMELEN + 1]; 644 ksem_t *ks; 645 u_int refcnt; 646 int error; 647 648 error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL); 649 if (error) 650 return error; 651 652 mutex_enter(&ksem_lock); 653 ks = ksem_lookup(name); 654 if (ks == NULL) { 655 mutex_exit(&ksem_lock); 656 return ENOENT; 657 } 658 KASSERT(mutex_owned(&ks->ks_lock)); 659 660 /* Verify permissions. */ 661 error = ksem_perm(l, ks); 662 if (error) { 663 mutex_exit(&ks->ks_lock); 664 mutex_exit(&ksem_lock); 665 return error; 666 } 667 668 /* Remove from the global list. */ 669 LIST_REMOVE(ks, ks_entry); 670 nsems--; 671 mutex_exit(&ksem_lock); 672 673 refcnt = ks->ks_ref; 674 if (refcnt) { 675 /* Mark as unlinked, if there are references. */ 676 ks->ks_flags |= KS_UNLINKED; 677 } 678 mutex_exit(&ks->ks_lock); 679 680 if (refcnt == 0) { 681 ksem_free(ks); 682 } 683 return 0; 684 } 685 686 int 687 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, 688 register_t *retval) 689 { 690 /* { 691 intptr_t id; 692 } */ 693 int fd = (int)SCARG(uap, id), error; 694 ksem_t *ks; 695 696 error = ksem_get(fd, &ks); 697 if (error) { 698 return error; 699 } 700 KASSERT(mutex_owned(&ks->ks_lock)); 701 if (ks->ks_value == SEM_VALUE_MAX) { 702 error = EOVERFLOW; 703 goto out; 704 } 705 ks->ks_value++; 706 if (ks->ks_waiters) { 707 cv_broadcast(&ks->ks_cv); 708 } 709 out: 710 mutex_exit(&ks->ks_lock); 711 fd_putfile(fd); 712 return error; 713 } 714 715 int 716 do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime) 717 { 718 int fd = (int)id, error, timeo; 719 ksem_t *ks; 720 721 error = ksem_get(fd, &ks); 722 if (error) { 723 return error; 724 } 725 KASSERT(mutex_owned(&ks->ks_lock)); 726 while (ks->ks_value == 0) { 727 ks->ks_waiters++; 728 if (!try_p && abstime != NULL) { 729 error = ts2timo(CLOCK_REALTIME, TIMER_ABSTIME, abstime, 730 &timeo, NULL); 731 if (error != 0) 732 goto out; 733 } else { 734 timeo = 0; 735 } 736 error = try_p ? EAGAIN : cv_timedwait_sig(&ks->ks_cv, 737 &ks->ks_lock, timeo); 738 ks->ks_waiters--; 739 if (error) 740 goto out; 741 } 742 ks->ks_value--; 743 out: 744 mutex_exit(&ks->ks_lock); 745 fd_putfile(fd); 746 return error; 747 } 748 749 int 750 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, 751 register_t *retval) 752 { 753 /* { 754 intptr_t id; 755 } */ 756 757 return do_ksem_wait(l, SCARG(uap, id), false, NULL); 758 } 759 760 int 761 sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap, 762 register_t *retval) 763 { 764 /* { 765 intptr_t id; 766 const struct timespec *abstime; 767 } */ 768 struct timespec ts; 769 int error; 770 771 error = copyin(SCARG(uap, abstime), &ts, sizeof(ts)); 772 if (error != 0) 773 return error; 774 775 if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000) 776 return EINVAL; 777 778 error = do_ksem_wait(l, SCARG(uap, id), false, &ts); 779 if (error == EWOULDBLOCK) 780 error = ETIMEDOUT; 781 return error; 782 } 783 784 int 785 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, 786 register_t *retval) 787 { 788 /* { 789 intptr_t id; 790 } */ 791 792 return do_ksem_wait(l, SCARG(uap, id), true, NULL); 793 } 794 795 int 796 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, 797 register_t *retval) 798 { 799 /* { 800 intptr_t id; 801 unsigned int *value; 802 } */ 803 int fd = (int)SCARG(uap, id), error; 804 ksem_t *ks; 805 unsigned int val; 806 807 error = ksem_get(fd, &ks); 808 if (error) { 809 return error; 810 } 811 KASSERT(mutex_owned(&ks->ks_lock)); 812 val = ks->ks_value; 813 mutex_exit(&ks->ks_lock); 814 fd_putfile(fd); 815 816 return copyout(&val, SCARG(uap, value), sizeof(val)); 817 } 818 819 int 820 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, 821 register_t *retval) 822 { 823 /* { 824 intptr_t id; 825 } */ 826 int fd = (int)SCARG(uap, id), error; 827 ksem_t *ks; 828 829 error = ksem_get(fd, &ks); 830 if (error) { 831 return error; 832 } 833 KASSERT(mutex_owned(&ks->ks_lock)); 834 835 /* Operation is only for unnamed semaphores. */ 836 if (ks->ks_name != NULL) { 837 error = EINVAL; 838 goto out; 839 } 840 /* Cannot destroy if there are waiters. */ 841 if (ks->ks_waiters) { 842 error = EBUSY; 843 goto out; 844 } 845 out: 846 mutex_exit(&ks->ks_lock); 847 if (error) { 848 fd_putfile(fd); 849 return error; 850 } 851 return fd_close(fd); 852 } 853