1 /* $NetBSD: uipc_sem.c,v 1.29 2008/11/14 15:49:21 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2003, 2007, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of Wasabi Systems, Inc, and by Andrew Doran. 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 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.29 2008/11/14 15:49:21 ad Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/systm.h> 63 #include <sys/kernel.h> 64 #include <sys/proc.h> 65 #include <sys/ksem.h> 66 #include <sys/syscall.h> 67 #include <sys/stat.h> 68 #include <sys/kmem.h> 69 #include <sys/fcntl.h> 70 #include <sys/kauth.h> 71 #include <sys/module.h> 72 #include <sys/mount.h> 73 #include <sys/syscall.h> 74 #include <sys/syscallargs.h> 75 #include <sys/syscallvar.h> 76 77 #define SEM_MAX_NAMELEN 14 78 #define SEM_VALUE_MAX (~0U) 79 #define SEM_HASHTBL_SIZE 13 80 81 #define SEM_TO_ID(x) (((x)->ks_id)) 82 #define SEM_HASH(id) ((id) % SEM_HASHTBL_SIZE) 83 84 MODULE(MODULE_CLASS_MISC, ksem, NULL); 85 86 static const struct syscall_package ksem_syscalls[] = { 87 { SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init }, 88 { SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open }, 89 { SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink }, 90 { SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close }, 91 { SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post }, 92 { SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait }, 93 { SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait }, 94 { SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue }, 95 { SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy }, 96 { 0, 0, NULL }, 97 }; 98 99 /* 100 * Note: to read the ks_name member, you need either the ks_interlock 101 * or the ksem_mutex. To write the ks_name member, you need both. Make 102 * sure the order is ksem_mutex -> ks_interlock. 103 */ 104 struct ksem { 105 LIST_ENTRY(ksem) ks_entry; /* global list entry */ 106 LIST_ENTRY(ksem) ks_hash; /* hash list entry */ 107 kmutex_t ks_interlock; /* lock on this ksem */ 108 kcondvar_t ks_cv; /* condition variable */ 109 unsigned int ks_ref; /* number of references */ 110 char *ks_name; /* if named, this is the name */ 111 size_t ks_namelen; /* length of name */ 112 mode_t ks_mode; /* protection bits */ 113 uid_t ks_uid; /* creator uid */ 114 gid_t ks_gid; /* creator gid */ 115 unsigned int ks_value; /* current value */ 116 unsigned int ks_waiters; /* number of waiters */ 117 intptr_t ks_id; /* unique identifier */ 118 }; 119 120 struct ksem_ref { 121 LIST_ENTRY(ksem_ref) ksr_list; 122 struct ksem *ksr_ksem; 123 }; 124 125 struct ksem_proc { 126 krwlock_t kp_lock; 127 LIST_HEAD(, ksem_ref) kp_ksems; 128 }; 129 130 LIST_HEAD(ksem_list, ksem); 131 132 /* 133 * ksem_mutex protects ksem_head and nsems. Only named semaphores go 134 * onto ksem_head. 135 */ 136 static kmutex_t ksem_mutex; 137 static struct ksem_list ksem_head = LIST_HEAD_INITIALIZER(&ksem_head); 138 static struct ksem_list ksem_hash[SEM_HASHTBL_SIZE]; 139 static int nsems = 0; 140 141 /* 142 * ksem_counter is the last assigned intptr_t. It needs to be COMPAT_NETBSD32 143 * friendly, even though intptr_t itself is defined as uintptr_t. 144 */ 145 static uint32_t ksem_counter = 1; 146 147 static specificdata_key_t ksem_specificdata_key; 148 static void *ksem_ehook; 149 static void *ksem_fhook; 150 151 static void 152 ksem_free(struct ksem *ks) 153 { 154 155 KASSERT(mutex_owned(&ks->ks_interlock)); 156 157 /* 158 * If the ksem is anonymous (or has been unlinked), then 159 * this is the end if its life. 160 */ 161 if (ks->ks_name == NULL) { 162 mutex_exit(&ks->ks_interlock); 163 mutex_destroy(&ks->ks_interlock); 164 cv_destroy(&ks->ks_cv); 165 166 mutex_enter(&ksem_mutex); 167 nsems--; 168 LIST_REMOVE(ks, ks_hash); 169 mutex_exit(&ksem_mutex); 170 171 kmem_free(ks, sizeof(*ks)); 172 return; 173 } 174 mutex_exit(&ks->ks_interlock); 175 } 176 177 static inline void 178 ksem_addref(struct ksem *ks) 179 { 180 181 KASSERT(mutex_owned(&ks->ks_interlock)); 182 ks->ks_ref++; 183 KASSERT(ks->ks_ref != 0); 184 } 185 186 static inline void 187 ksem_delref(struct ksem *ks) 188 { 189 190 KASSERT(mutex_owned(&ks->ks_interlock)); 191 KASSERT(ks->ks_ref != 0); 192 if (--ks->ks_ref == 0) { 193 ksem_free(ks); 194 return; 195 } 196 mutex_exit(&ks->ks_interlock); 197 } 198 199 static struct ksem_proc * 200 ksem_proc_alloc(void) 201 { 202 struct ksem_proc *kp; 203 204 kp = kmem_alloc(sizeof(*kp), KM_SLEEP); 205 rw_init(&kp->kp_lock); 206 LIST_INIT(&kp->kp_ksems); 207 208 return (kp); 209 } 210 211 static void 212 ksem_proc_dtor(void *arg) 213 { 214 struct ksem_proc *kp = arg; 215 struct ksem_ref *ksr; 216 217 rw_enter(&kp->kp_lock, RW_WRITER); 218 219 while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) { 220 LIST_REMOVE(ksr, ksr_list); 221 mutex_enter(&ksr->ksr_ksem->ks_interlock); 222 ksem_delref(ksr->ksr_ksem); 223 kmem_free(ksr, sizeof(*ksr)); 224 } 225 226 rw_exit(&kp->kp_lock); 227 rw_destroy(&kp->kp_lock); 228 kmem_free(kp, sizeof(*kp)); 229 } 230 231 static void 232 ksem_add_proc(struct proc *p, struct ksem *ks) 233 { 234 struct ksem_proc *kp; 235 struct ksem_ref *ksr; 236 237 kp = proc_getspecific(p, ksem_specificdata_key); 238 if (kp == NULL) { 239 kp = ksem_proc_alloc(); 240 proc_setspecific(p, ksem_specificdata_key, kp); 241 } 242 243 ksr = kmem_alloc(sizeof(*ksr), KM_SLEEP); 244 ksr->ksr_ksem = ks; 245 246 rw_enter(&kp->kp_lock, RW_WRITER); 247 LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list); 248 rw_exit(&kp->kp_lock); 249 } 250 251 /* We MUST have a write lock on the ksem_proc list! */ 252 static struct ksem_ref * 253 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks) 254 { 255 struct ksem_ref *ksr; 256 257 KASSERT(mutex_owned(&ks->ks_interlock)); 258 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 259 if (ksr->ksr_ksem == ks) { 260 ksem_delref(ks); 261 LIST_REMOVE(ksr, ksr_list); 262 return (ksr); 263 } 264 } 265 #ifdef DIAGNOSTIC 266 panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks); 267 #endif 268 return (NULL); 269 } 270 271 static int 272 ksem_perm(struct lwp *l, struct ksem *ks) 273 { 274 kauth_cred_t uc; 275 276 KASSERT(mutex_owned(&ks->ks_interlock)); 277 uc = l->l_cred; 278 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) || 279 (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) || 280 (ks->ks_mode & S_IWOTH) != 0 || 281 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, NULL) == 0) 282 return (0); 283 return (EPERM); 284 } 285 286 static struct ksem * 287 ksem_lookup_byid(intptr_t id) 288 { 289 struct ksem *ks; 290 291 KASSERT(mutex_owned(&ksem_mutex)); 292 LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) { 293 if (ks->ks_id == id) 294 return ks; 295 } 296 return NULL; 297 } 298 299 static struct ksem * 300 ksem_lookup_byname(const char *name) 301 { 302 struct ksem *ks; 303 304 KASSERT(mutex_owned(&ksem_mutex)); 305 LIST_FOREACH(ks, &ksem_head, ks_entry) { 306 if (strcmp(ks->ks_name, name) == 0) { 307 mutex_enter(&ks->ks_interlock); 308 return (ks); 309 } 310 } 311 return (NULL); 312 } 313 314 static int 315 ksem_create(struct lwp *l, const char *name, struct ksem **ksret, 316 mode_t mode, unsigned int value) 317 { 318 struct ksem *ret; 319 kauth_cred_t uc; 320 size_t len; 321 322 uc = l->l_cred; 323 if (value > SEM_VALUE_MAX) 324 return (EINVAL); 325 ret = kmem_zalloc(sizeof(*ret), KM_SLEEP); 326 if (name != NULL) { 327 len = strlen(name); 328 if (len > SEM_MAX_NAMELEN) { 329 kmem_free(ret, sizeof(*ret)); 330 return (ENAMETOOLONG); 331 } 332 /* name must start with a '/' but not contain one. */ 333 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 334 kmem_free(ret, sizeof(*ret)); 335 return (EINVAL); 336 } 337 ret->ks_namelen = len + 1; 338 ret->ks_name = kmem_alloc(ret->ks_namelen, KM_SLEEP); 339 strlcpy(ret->ks_name, name, len + 1); 340 } else 341 ret->ks_name = NULL; 342 ret->ks_mode = mode; 343 ret->ks_value = value; 344 ret->ks_ref = 1; 345 ret->ks_waiters = 0; 346 ret->ks_uid = kauth_cred_geteuid(uc); 347 ret->ks_gid = kauth_cred_getegid(uc); 348 mutex_init(&ret->ks_interlock, MUTEX_DEFAULT, IPL_NONE); 349 cv_init(&ret->ks_cv, "psem"); 350 351 mutex_enter(&ksem_mutex); 352 if (nsems >= ksem_max) { 353 mutex_exit(&ksem_mutex); 354 if (ret->ks_name != NULL) 355 kmem_free(ret->ks_name, ret->ks_namelen); 356 kmem_free(ret, sizeof(*ret)); 357 return (ENFILE); 358 } 359 nsems++; 360 while (ksem_lookup_byid(ksem_counter) != NULL) { 361 ksem_counter++; 362 /* 0 is a special value for libpthread */ 363 if (ksem_counter == 0) 364 ksem_counter++; 365 } 366 ret->ks_id = ksem_counter; 367 LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash); 368 mutex_exit(&ksem_mutex); 369 370 *ksret = ret; 371 return (0); 372 } 373 374 int 375 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap, register_t *retval) 376 { 377 /* { 378 unsigned int value; 379 intptr_t *idp; 380 } */ 381 382 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); 383 } 384 385 int 386 do_ksem_init(struct lwp *l, unsigned int value, intptr_t *idp, 387 copyout_t docopyout) 388 { 389 struct ksem *ks; 390 intptr_t id; 391 int error; 392 393 /* Note the mode does not matter for anonymous semaphores. */ 394 error = ksem_create(l, NULL, &ks, 0, value); 395 if (error) 396 return (error); 397 id = SEM_TO_ID(ks); 398 error = (*docopyout)(&id, idp, sizeof(id)); 399 if (error) { 400 mutex_enter(&ks->ks_interlock); 401 ksem_delref(ks); 402 return (error); 403 } 404 405 ksem_add_proc(l->l_proc, ks); 406 407 return (0); 408 } 409 410 int 411 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap, register_t *retval) 412 { 413 /* { 414 const char *name; 415 int oflag; 416 mode_t mode; 417 unsigned int value; 418 intptr_t *idp; 419 } */ 420 421 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 422 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 423 } 424 425 int 426 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 427 unsigned int value, intptr_t *idp, copyout_t docopyout) 428 { 429 char name[SEM_MAX_NAMELEN + 1]; 430 size_t done; 431 int error; 432 struct ksem *ksnew, *ks; 433 intptr_t id; 434 435 error = copyinstr(semname, name, sizeof(name), &done); 436 if (error) 437 return (error); 438 439 ksnew = NULL; 440 mutex_enter(&ksem_mutex); 441 ks = ksem_lookup_byname(name); 442 443 /* Found one? */ 444 if (ks != NULL) { 445 /* Check for exclusive create. */ 446 if (oflag & O_EXCL) { 447 mutex_exit(&ks->ks_interlock); 448 mutex_exit(&ksem_mutex); 449 return (EEXIST); 450 } 451 found_one: 452 /* 453 * Verify permissions. If we can access it, add 454 * this process's reference. 455 */ 456 KASSERT(mutex_owned(&ks->ks_interlock)); 457 error = ksem_perm(l, ks); 458 if (error == 0) 459 ksem_addref(ks); 460 mutex_exit(&ks->ks_interlock); 461 mutex_exit(&ksem_mutex); 462 if (error) 463 return (error); 464 465 id = SEM_TO_ID(ks); 466 error = (*docopyout)(&id, idp, sizeof(id)); 467 if (error) { 468 mutex_enter(&ks->ks_interlock); 469 ksem_delref(ks); 470 return (error); 471 } 472 473 ksem_add_proc(l->l_proc, ks); 474 475 return (0); 476 } 477 478 /* 479 * didn't ask for creation? error. 480 */ 481 if ((oflag & O_CREAT) == 0) { 482 mutex_exit(&ksem_mutex); 483 return (ENOENT); 484 } 485 486 /* 487 * We may block during creation, so drop the lock. 488 */ 489 mutex_exit(&ksem_mutex); 490 error = ksem_create(l, name, &ksnew, mode, value); 491 if (error != 0) 492 return (error); 493 494 id = SEM_TO_ID(ksnew); 495 error = (*docopyout)(&id, idp, sizeof(id)); 496 if (error) { 497 kmem_free(ksnew->ks_name, ksnew->ks_namelen); 498 ksnew->ks_name = NULL; 499 500 mutex_enter(&ksnew->ks_interlock); 501 ksem_delref(ksnew); 502 return (error); 503 } 504 505 /* 506 * We need to make sure we haven't lost a race while 507 * allocating during creation. 508 */ 509 mutex_enter(&ksem_mutex); 510 if ((ks = ksem_lookup_byname(name)) != NULL) { 511 if (oflag & O_EXCL) { 512 mutex_exit(&ks->ks_interlock); 513 mutex_exit(&ksem_mutex); 514 515 kmem_free(ksnew->ks_name, ksnew->ks_namelen); 516 ksnew->ks_name = NULL; 517 518 mutex_enter(&ksnew->ks_interlock); 519 ksem_delref(ksnew); 520 return (EEXIST); 521 } 522 goto found_one; 523 } else { 524 /* ksnew already has its initial reference. */ 525 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 526 mutex_exit(&ksem_mutex); 527 528 ksem_add_proc(l->l_proc, ksnew); 529 } 530 return (error); 531 } 532 533 /* We must have a read lock on the ksem_proc list! */ 534 static struct ksem * 535 ksem_lookup_proc(struct ksem_proc *kp, intptr_t id) 536 { 537 struct ksem_ref *ksr; 538 539 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 540 if (id == SEM_TO_ID(ksr->ksr_ksem)) { 541 mutex_enter(&ksr->ksr_ksem->ks_interlock); 542 return (ksr->ksr_ksem); 543 } 544 } 545 546 return (NULL); 547 } 548 549 int 550 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap, register_t *retval) 551 { 552 /* { 553 const char *name; 554 } */ 555 char name[SEM_MAX_NAMELEN + 1], *cp; 556 size_t done, len; 557 struct ksem *ks; 558 int error; 559 560 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done); 561 if (error) 562 return error; 563 564 mutex_enter(&ksem_mutex); 565 ks = ksem_lookup_byname(name); 566 if (ks == NULL) { 567 mutex_exit(&ksem_mutex); 568 return (ENOENT); 569 } 570 571 KASSERT(mutex_owned(&ks->ks_interlock)); 572 573 LIST_REMOVE(ks, ks_entry); 574 cp = ks->ks_name; 575 len = ks->ks_namelen; 576 ks->ks_name = NULL; 577 578 mutex_exit(&ksem_mutex); 579 580 if (ks->ks_ref == 0) 581 ksem_free(ks); 582 else 583 mutex_exit(&ks->ks_interlock); 584 585 kmem_free(cp, len); 586 587 return (0); 588 } 589 590 int 591 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap, register_t *retval) 592 { 593 /* { 594 intptr_t id; 595 } */ 596 struct ksem_proc *kp; 597 struct ksem_ref *ksr; 598 struct ksem *ks; 599 600 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 601 if (kp == NULL) 602 return (EINVAL); 603 604 rw_enter(&kp->kp_lock, RW_WRITER); 605 606 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 607 if (ks == NULL) { 608 rw_exit(&kp->kp_lock); 609 return (EINVAL); 610 } 611 612 KASSERT(mutex_owned(&ks->ks_interlock)); 613 if (ks->ks_name == NULL) { 614 mutex_exit(&ks->ks_interlock); 615 rw_exit(&kp->kp_lock); 616 return (EINVAL); 617 } 618 619 ksr = ksem_drop_proc(kp, ks); 620 rw_exit(&kp->kp_lock); 621 kmem_free(ksr, sizeof(*ksr)); 622 623 return (0); 624 } 625 626 int 627 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap, register_t *retval) 628 { 629 /* { 630 intptr_t id; 631 } */ 632 struct ksem_proc *kp; 633 struct ksem *ks; 634 int error; 635 636 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 637 if (kp == NULL) 638 return (EINVAL); 639 640 rw_enter(&kp->kp_lock, RW_READER); 641 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 642 rw_exit(&kp->kp_lock); 643 if (ks == NULL) 644 return (EINVAL); 645 646 KASSERT(mutex_owned(&ks->ks_interlock)); 647 if (ks->ks_value == SEM_VALUE_MAX) { 648 error = EOVERFLOW; 649 goto out; 650 } 651 ++ks->ks_value; 652 if (ks->ks_waiters) 653 cv_broadcast(&ks->ks_cv); 654 error = 0; 655 out: 656 mutex_exit(&ks->ks_interlock); 657 return (error); 658 } 659 660 static int 661 ksem_wait(struct lwp *l, intptr_t id, int tryflag) 662 { 663 struct ksem_proc *kp; 664 struct ksem *ks; 665 int error; 666 667 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 668 if (kp == NULL) 669 return (EINVAL); 670 671 rw_enter(&kp->kp_lock, RW_READER); 672 ks = ksem_lookup_proc(kp, id); 673 rw_exit(&kp->kp_lock); 674 if (ks == NULL) 675 return (EINVAL); 676 677 KASSERT(mutex_owned(&ks->ks_interlock)); 678 ksem_addref(ks); 679 while (ks->ks_value == 0) { 680 ks->ks_waiters++; 681 if (tryflag) 682 error = EAGAIN; 683 else 684 error = cv_wait_sig(&ks->ks_cv, &ks->ks_interlock); 685 ks->ks_waiters--; 686 if (error) 687 goto out; 688 } 689 ks->ks_value--; 690 error = 0; 691 out: 692 ksem_delref(ks); 693 return (error); 694 } 695 696 int 697 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap, register_t *retval) 698 { 699 /* { 700 intptr_t id; 701 } */ 702 703 return ksem_wait(l, SCARG(uap, id), 0); 704 } 705 706 int 707 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap, register_t *retval) 708 { 709 /* { 710 intptr_t id; 711 } */ 712 713 return ksem_wait(l, SCARG(uap, id), 1); 714 } 715 716 int 717 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap, register_t *retval) 718 { 719 /* { 720 intptr_t id; 721 unsigned int *value; 722 } */ 723 struct ksem_proc *kp; 724 struct ksem *ks; 725 unsigned int val; 726 727 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 728 if (kp == NULL) 729 return (EINVAL); 730 731 rw_enter(&kp->kp_lock, RW_READER); 732 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 733 rw_exit(&kp->kp_lock); 734 if (ks == NULL) 735 return (EINVAL); 736 737 KASSERT(mutex_owned(&ks->ks_interlock)); 738 val = ks->ks_value; 739 mutex_exit(&ks->ks_interlock); 740 741 return (copyout(&val, SCARG(uap, value), sizeof(val))); 742 } 743 744 int 745 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap, register_t *retval) 746 { 747 /* { 748 intptr_t id; 749 } */ 750 struct ksem_proc *kp; 751 struct ksem_ref *ksr; 752 struct ksem *ks; 753 754 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 755 if (kp == NULL) 756 return (EINVAL); 757 758 rw_enter(&kp->kp_lock, RW_WRITER); 759 760 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 761 if (ks == NULL) { 762 rw_exit(&kp->kp_lock); 763 return (EINVAL); 764 } 765 766 KASSERT(mutex_owned(&ks->ks_interlock)); 767 768 /* 769 * XXX This misses named semaphores which have been unlink'd, 770 * XXX but since behavior of destroying a named semaphore is 771 * XXX undefined, this is technically allowed. 772 */ 773 if (ks->ks_name != NULL) { 774 mutex_exit(&ks->ks_interlock); 775 rw_exit(&kp->kp_lock); 776 return (EINVAL); 777 } 778 779 if (ks->ks_waiters) { 780 mutex_exit(&ks->ks_interlock); 781 rw_exit(&kp->kp_lock); 782 return (EBUSY); 783 } 784 785 ksr = ksem_drop_proc(kp, ks); 786 rw_exit(&kp->kp_lock); 787 kmem_free(ksr, sizeof(*ksr)); 788 789 return (0); 790 } 791 792 static void 793 ksem_forkhook(struct proc *p2, struct proc *p1) 794 { 795 struct ksem_proc *kp1, *kp2; 796 struct ksem_ref *ksr, *ksr1; 797 798 kp1 = proc_getspecific(p1, ksem_specificdata_key); 799 if (kp1 == NULL) 800 return; 801 802 kp2 = ksem_proc_alloc(); 803 804 rw_enter(&kp1->kp_lock, RW_READER); 805 806 if (!LIST_EMPTY(&kp1->kp_ksems)) { 807 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) { 808 ksr1 = kmem_alloc(sizeof(*ksr), KM_SLEEP); 809 ksr1->ksr_ksem = ksr->ksr_ksem; 810 mutex_enter(&ksr->ksr_ksem->ks_interlock); 811 ksem_addref(ksr->ksr_ksem); 812 mutex_exit(&ksr->ksr_ksem->ks_interlock); 813 LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list); 814 } 815 } 816 817 rw_exit(&kp1->kp_lock); 818 proc_setspecific(p2, ksem_specificdata_key, kp2); 819 } 820 821 static void 822 ksem_exechook(struct proc *p, void *arg) 823 { 824 struct ksem_proc *kp; 825 826 kp = proc_getspecific(p, ksem_specificdata_key); 827 if (kp != NULL) { 828 proc_setspecific(p, ksem_specificdata_key, NULL); 829 ksem_proc_dtor(kp); 830 } 831 } 832 833 static int 834 ksem_fini(bool interface) 835 { 836 int error; 837 838 if (interface) { 839 error = syscall_disestablish(NULL, ksem_syscalls); 840 if (error != 0) { 841 return error; 842 } 843 if (nsems != 0) { 844 error = syscall_establish(NULL, ksem_syscalls); 845 KASSERT(error == 0); 846 return EBUSY; 847 } 848 } 849 exechook_disestablish(ksem_ehook); 850 forkhook_disestablish(ksem_fhook); 851 proc_specific_key_delete(ksem_specificdata_key); 852 mutex_destroy(&ksem_mutex); 853 return 0; 854 } 855 856 static int 857 ksem_init(void) 858 { 859 int error, i; 860 861 mutex_init(&ksem_mutex, MUTEX_DEFAULT, IPL_NONE); 862 for (i = 0; i < SEM_HASHTBL_SIZE; i++) 863 LIST_INIT(&ksem_hash[i]); 864 error = proc_specific_key_create(&ksem_specificdata_key, 865 ksem_proc_dtor); 866 if (error != 0) { 867 mutex_destroy(&ksem_mutex); 868 return error; 869 } 870 ksem_ehook = exechook_establish(ksem_exechook, NULL); 871 ksem_fhook = forkhook_establish(ksem_forkhook); 872 error = syscall_establish(NULL, ksem_syscalls); 873 if (error != 0) { 874 (void)ksem_fini(false); 875 } 876 return error; 877 } 878 879 static int 880 ksem_modcmd(modcmd_t cmd, void *arg) 881 { 882 883 switch (cmd) { 884 case MODULE_CMD_INIT: 885 return ksem_init(); 886 887 case MODULE_CMD_FINI: 888 return ksem_fini(true); 889 890 default: 891 return ENOTTY; 892 } 893 } 894