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