1 /* $NetBSD: uipc_sem.c,v 1.17 2006/10/12 01:32:19 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2003 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. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 41 * All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 55 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 62 * SUCH DAMAGE. 63 */ 64 65 #include <sys/cdefs.h> 66 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.17 2006/10/12 01:32:19 christos Exp $"); 67 68 #include "opt_posix.h" 69 70 #include <sys/param.h> 71 #include <sys/systm.h> 72 #include <sys/kernel.h> 73 #include <sys/proc.h> 74 #include <sys/lock.h> 75 #include <sys/ksem.h> 76 #include <sys/sa.h> 77 #include <sys/syscall.h> 78 #include <sys/stat.h> 79 #include <sys/malloc.h> 80 #include <sys/fcntl.h> 81 #include <sys/kauth.h> 82 83 #include <sys/mount.h> 84 85 #include <sys/syscallargs.h> 86 87 #ifndef SEM_MAX 88 #define SEM_MAX 30 89 #endif 90 91 #define SEM_MAX_NAMELEN 14 92 #define SEM_VALUE_MAX (~0U) 93 #define SEM_HASHTBL_SIZE 13 94 95 #define SEM_TO_ID(x) (((x)->ks_id)) 96 #define SEM_HASH(id) ((id) % SEM_HASHTBL_SIZE) 97 98 MALLOC_DEFINE(M_SEM, "p1003_1b_sem", "p1003_1b semaphores"); 99 100 /* 101 * Note: to read the ks_name member, you need either the ks_interlock 102 * or the ksem_slock. To write the ks_name member, you need both. Make 103 * sure the order is ksem_slock -> ks_interlock. 104 */ 105 struct ksem { 106 LIST_ENTRY(ksem) ks_entry; /* global list entry */ 107 LIST_ENTRY(ksem) ks_hash; /* hash list entry */ 108 struct simplelock ks_interlock; /* lock on this ksem */ 109 char *ks_name; /* if named, this is the name */ 110 unsigned int ks_ref; /* number of references */ 111 mode_t ks_mode; /* protection bits */ 112 uid_t ks_uid; /* creator uid */ 113 gid_t ks_gid; /* creator gid */ 114 unsigned int ks_value; /* current value */ 115 unsigned int ks_waiters; /* number of waiters */ 116 semid_t ks_id; /* unique identifier */ 117 }; 118 119 struct ksem_ref { 120 LIST_ENTRY(ksem_ref) ksr_list; 121 struct ksem *ksr_ksem; 122 }; 123 124 struct ksem_proc { 125 struct lock kp_lock; 126 LIST_HEAD(, ksem_ref) kp_ksems; 127 }; 128 129 LIST_HEAD(ksem_list, ksem); 130 131 /* 132 * ksem_slock protects ksem_head and nsems. Only named semaphores go 133 * onto ksem_head. 134 */ 135 static struct simplelock ksem_slock; 136 static struct ksem_list ksem_head = LIST_HEAD_INITIALIZER(&ksem_head); 137 static struct ksem_list ksem_hash[SEM_HASHTBL_SIZE]; 138 static int nsems = 0; 139 140 /* 141 * ksem_counter is the last assigned semid_t. It needs to be COMPAT_NETBSD32 142 * friendly, even though semid_t itself is defined as uintptr_t. 143 */ 144 static uint32_t ksem_counter = 1; 145 146 static specificdata_key_t ksem_specificdata_key; 147 148 static void 149 ksem_free(struct ksem *ks) 150 { 151 152 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 153 /* 154 * If the ksem is anonymous (or has been unlinked), then 155 * this is the end if its life. 156 */ 157 if (ks->ks_name == NULL) { 158 simple_unlock(&ks->ks_interlock); 159 160 simple_lock(&ksem_slock); 161 nsems--; 162 LIST_REMOVE(ks, ks_hash); 163 simple_unlock(&ksem_slock); 164 165 free(ks, M_SEM); 166 return; 167 } 168 simple_unlock(&ks->ks_interlock); 169 } 170 171 static inline void 172 ksem_addref(struct ksem *ks) 173 { 174 175 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 176 ks->ks_ref++; 177 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */ 178 } 179 180 static inline void 181 ksem_delref(struct ksem *ks) 182 { 183 184 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 185 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */ 186 if (--ks->ks_ref == 0) { 187 ksem_free(ks); 188 return; 189 } 190 simple_unlock(&ks->ks_interlock); 191 } 192 193 static struct ksem_proc * 194 ksem_proc_alloc(void) 195 { 196 struct ksem_proc *kp; 197 198 kp = malloc(sizeof(*kp), M_SEM, M_WAITOK); 199 lockinit(&kp->kp_lock, PWAIT, "ksproc", 0, 0); 200 LIST_INIT(&kp->kp_ksems); 201 202 return (kp); 203 } 204 205 static void 206 ksem_proc_dtor(void *arg) 207 { 208 struct ksem_proc *kp = arg; 209 struct ksem_ref *ksr; 210 211 lockmgr(&kp->kp_lock, LK_DRAIN, NULL); 212 213 while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) { 214 LIST_REMOVE(ksr, ksr_list); 215 simple_lock(&ksr->ksr_ksem->ks_interlock); 216 ksem_delref(ksr->ksr_ksem); 217 free(ksr, M_SEM); 218 } 219 220 free(kp, M_SEM); 221 } 222 223 static void 224 ksem_add_proc(struct proc *p, struct ksem *ks) 225 { 226 struct ksem_proc *kp; 227 struct ksem_ref *ksr; 228 229 kp = proc_getspecific(p, ksem_specificdata_key); 230 if (kp == NULL) { 231 kp = ksem_proc_alloc(); 232 proc_setspecific(p, ksem_specificdata_key, kp); 233 } 234 235 ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK); 236 ksr->ksr_ksem = ks; 237 238 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 239 LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list); 240 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 241 } 242 243 /* We MUST have a write lock on the ksem_proc list! */ 244 static struct ksem_ref * 245 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks) 246 { 247 struct ksem_ref *ksr; 248 249 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 250 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 251 if (ksr->ksr_ksem == ks) { 252 ksem_delref(ks); 253 LIST_REMOVE(ksr, ksr_list); 254 return (ksr); 255 } 256 } 257 #ifdef DIAGNOSTIC 258 panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks); 259 #endif 260 return (NULL); 261 } 262 263 static int 264 ksem_perm(struct lwp *l, struct ksem *ks) 265 { 266 kauth_cred_t uc; 267 268 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 269 uc = l->l_cred; 270 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) || 271 (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) || 272 (ks->ks_mode & S_IWOTH) != 0 || 273 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, &l->l_acflag) == 0) 274 return (0); 275 return (EPERM); 276 } 277 278 static struct ksem * 279 ksem_lookup_byid(semid_t id) 280 { 281 struct ksem *ks; 282 283 LOCK_ASSERT(simple_lock_held(&ksem_slock)); 284 LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) { 285 if (ks->ks_id == id) 286 return ks; 287 } 288 return NULL; 289 } 290 291 static struct ksem * 292 ksem_lookup_byname(const char *name) 293 { 294 struct ksem *ks; 295 296 LOCK_ASSERT(simple_lock_held(&ksem_slock)); 297 LIST_FOREACH(ks, &ksem_head, ks_entry) { 298 if (strcmp(ks->ks_name, name) == 0) { 299 simple_lock(&ks->ks_interlock); 300 return (ks); 301 } 302 } 303 return (NULL); 304 } 305 306 static int 307 ksem_create(struct lwp *l, const char *name, struct ksem **ksret, 308 mode_t mode, unsigned int value) 309 { 310 struct ksem *ret; 311 kauth_cred_t uc; 312 size_t len; 313 314 uc = l->l_cred; 315 if (value > SEM_VALUE_MAX) 316 return (EINVAL); 317 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO); 318 if (name != NULL) { 319 len = strlen(name); 320 if (len > SEM_MAX_NAMELEN) { 321 free(ret, M_SEM); 322 return (ENAMETOOLONG); 323 } 324 /* name must start with a '/' but not contain one. */ 325 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 326 free(ret, M_SEM); 327 return (EINVAL); 328 } 329 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK); 330 strlcpy(ret->ks_name, name, len + 1); 331 } else 332 ret->ks_name = NULL; 333 ret->ks_mode = mode; 334 ret->ks_value = value; 335 ret->ks_ref = 1; 336 ret->ks_waiters = 0; 337 ret->ks_uid = kauth_cred_geteuid(uc); 338 ret->ks_gid = kauth_cred_getegid(uc); 339 simple_lock_init(&ret->ks_interlock); 340 341 simple_lock(&ksem_slock); 342 if (nsems >= SEM_MAX) { 343 simple_unlock(&ksem_slock); 344 if (ret->ks_name != NULL) 345 free(ret->ks_name, M_SEM); 346 free(ret, M_SEM); 347 return (ENFILE); 348 } 349 nsems++; 350 while (ksem_lookup_byid(ksem_counter) != NULL) { 351 ksem_counter++; 352 /* 0 is a special value for libpthread */ 353 if (ksem_counter == 0) 354 ksem_counter++; 355 } 356 ret->ks_id = ksem_counter; 357 LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash); 358 simple_unlock(&ksem_slock); 359 360 *ksret = ret; 361 return (0); 362 } 363 364 int 365 sys__ksem_init(struct lwp *l, void *v, register_t *retval __unused) 366 { 367 struct sys__ksem_init_args /* { 368 unsigned int value; 369 semid_t *idp; 370 } */ *uap = v; 371 372 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); 373 } 374 375 int 376 do_ksem_init(struct lwp *l, unsigned int value, semid_t *idp, 377 copyout_t docopyout) 378 { 379 struct ksem *ks; 380 semid_t id; 381 int error; 382 383 /* Note the mode does not matter for anonymous semaphores. */ 384 error = ksem_create(l, NULL, &ks, 0, value); 385 if (error) 386 return (error); 387 id = SEM_TO_ID(ks); 388 error = (*docopyout)(&id, idp, sizeof(id)); 389 if (error) { 390 simple_lock(&ks->ks_interlock); 391 ksem_delref(ks); 392 return (error); 393 } 394 395 ksem_add_proc(l->l_proc, ks); 396 397 return (0); 398 } 399 400 int 401 sys__ksem_open(struct lwp *l, void *v, register_t *retval __unused) 402 { 403 struct sys__ksem_open_args /* { 404 const char *name; 405 int oflag; 406 mode_t mode; 407 unsigned int value; 408 semid_t *idp; 409 } */ *uap = v; 410 411 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 412 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 413 } 414 415 int 416 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 417 unsigned int value, semid_t *idp, copyout_t docopyout) 418 { 419 char name[SEM_MAX_NAMELEN + 1]; 420 size_t done; 421 int error; 422 struct ksem *ksnew, *ks; 423 semid_t id; 424 425 error = copyinstr(semname, name, sizeof(name), &done); 426 if (error) 427 return (error); 428 429 ksnew = NULL; 430 simple_lock(&ksem_slock); 431 ks = ksem_lookup_byname(name); 432 433 /* Found one? */ 434 if (ks != NULL) { 435 /* Check for exclusive create. */ 436 if (oflag & O_EXCL) { 437 simple_unlock(&ks->ks_interlock); 438 simple_unlock(&ksem_slock); 439 return (EEXIST); 440 } 441 found_one: 442 /* 443 * Verify permissions. If we can access it, add 444 * this process's reference. 445 */ 446 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 447 error = ksem_perm(l, ks); 448 if (error == 0) 449 ksem_addref(ks); 450 simple_unlock(&ks->ks_interlock); 451 simple_unlock(&ksem_slock); 452 if (error) 453 return (error); 454 455 id = SEM_TO_ID(ks); 456 error = (*docopyout)(&id, idp, sizeof(id)); 457 if (error) { 458 simple_lock(&ks->ks_interlock); 459 ksem_delref(ks); 460 return (error); 461 } 462 463 ksem_add_proc(l->l_proc, ks); 464 465 return (0); 466 } 467 468 /* 469 * didn't ask for creation? error. 470 */ 471 if ((oflag & O_CREAT) == 0) { 472 simple_unlock(&ksem_slock); 473 return (ENOENT); 474 } 475 476 /* 477 * We may block during creation, so drop the lock. 478 */ 479 simple_unlock(&ksem_slock); 480 error = ksem_create(l, name, &ksnew, mode, value); 481 if (error != 0) 482 return (error); 483 484 id = SEM_TO_ID(ksnew); 485 error = (*docopyout)(&id, idp, sizeof(id)); 486 if (error) { 487 free(ksnew->ks_name, M_SEM); 488 ksnew->ks_name = NULL; 489 490 simple_lock(&ksnew->ks_interlock); 491 ksem_delref(ksnew); 492 return (error); 493 } 494 495 /* 496 * We need to make sure we haven't lost a race while 497 * allocating during creation. 498 */ 499 simple_lock(&ksem_slock); 500 if ((ks = ksem_lookup_byname(name)) != NULL) { 501 if (oflag & O_EXCL) { 502 simple_unlock(&ks->ks_interlock); 503 simple_unlock(&ksem_slock); 504 505 free(ksnew->ks_name, M_SEM); 506 ksnew->ks_name = NULL; 507 508 simple_lock(&ksnew->ks_interlock); 509 ksem_delref(ksnew); 510 return (EEXIST); 511 } 512 goto found_one; 513 } else { 514 /* ksnew already has its initial reference. */ 515 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 516 simple_unlock(&ksem_slock); 517 518 ksem_add_proc(l->l_proc, ksnew); 519 } 520 return (error); 521 } 522 523 /* We must have a read lock on the ksem_proc list! */ 524 static struct ksem * 525 ksem_lookup_proc(struct ksem_proc *kp, semid_t id) 526 { 527 struct ksem_ref *ksr; 528 529 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 530 if (id == SEM_TO_ID(ksr->ksr_ksem)) { 531 simple_lock(&ksr->ksr_ksem->ks_interlock); 532 return (ksr->ksr_ksem); 533 } 534 } 535 536 return (NULL); 537 } 538 539 int 540 sys__ksem_unlink(struct lwp *l __unused, void *v, register_t *retval __unused) 541 { 542 struct sys__ksem_unlink_args /* { 543 const char *name; 544 } */ *uap = v; 545 char name[SEM_MAX_NAMELEN + 1], *cp; 546 size_t done; 547 struct ksem *ks; 548 int error; 549 550 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done); 551 if (error) 552 return error; 553 554 simple_lock(&ksem_slock); 555 ks = ksem_lookup_byname(name); 556 if (ks == NULL) { 557 simple_unlock(&ksem_slock); 558 return (ENOENT); 559 } 560 561 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 562 563 LIST_REMOVE(ks, ks_entry); 564 cp = ks->ks_name; 565 ks->ks_name = NULL; 566 567 simple_unlock(&ksem_slock); 568 569 if (ks->ks_ref == 0) 570 ksem_free(ks); 571 else 572 simple_unlock(&ks->ks_interlock); 573 574 free(cp, M_SEM); 575 576 return (0); 577 } 578 579 int 580 sys__ksem_close(struct lwp *l, void *v, register_t *retval __unused) 581 { 582 struct sys__ksem_close_args /* { 583 semid_t id; 584 } */ *uap = v; 585 struct ksem_proc *kp; 586 struct ksem_ref *ksr; 587 struct ksem *ks; 588 589 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 590 if (kp == NULL) 591 return (EINVAL); 592 593 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 594 595 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 596 if (ks == NULL) { 597 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 598 return (EINVAL); 599 } 600 601 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 602 if (ks->ks_name == NULL) { 603 simple_unlock(&ks->ks_interlock); 604 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 605 return (EINVAL); 606 } 607 608 ksr = ksem_drop_proc(kp, ks); 609 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 610 free(ksr, M_SEM); 611 612 return (0); 613 } 614 615 int 616 sys__ksem_post(struct lwp *l, void *v, register_t *retval __unused) 617 { 618 struct sys__ksem_post_args /* { 619 semid_t id; 620 } */ *uap = v; 621 struct ksem_proc *kp; 622 struct ksem *ks; 623 int error; 624 625 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 626 if (kp == NULL) 627 return (EINVAL); 628 629 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 630 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 631 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 632 if (ks == NULL) 633 return (EINVAL); 634 635 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 636 if (ks->ks_value == SEM_VALUE_MAX) { 637 error = EOVERFLOW; 638 goto out; 639 } 640 ++ks->ks_value; 641 if (ks->ks_waiters) 642 wakeup(ks); 643 error = 0; 644 out: 645 simple_unlock(&ks->ks_interlock); 646 return (error); 647 } 648 649 static int 650 ksem_wait(struct lwp *l, semid_t id, int tryflag) 651 { 652 struct ksem_proc *kp; 653 struct ksem *ks; 654 int error; 655 656 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 657 if (kp == NULL) 658 return (EINVAL); 659 660 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 661 ks = ksem_lookup_proc(kp, id); 662 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 663 if (ks == NULL) 664 return (EINVAL); 665 666 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 667 ksem_addref(ks); 668 while (ks->ks_value == 0) { 669 ks->ks_waiters++; 670 error = tryflag ? EAGAIN : ltsleep(ks, PCATCH, "psem", 0, 671 &ks->ks_interlock); 672 ks->ks_waiters--; 673 if (error) 674 goto out; 675 } 676 ks->ks_value--; 677 error = 0; 678 out: 679 ksem_delref(ks); 680 return (error); 681 } 682 683 int 684 sys__ksem_wait(struct lwp *l, void *v, register_t *retval __unused) 685 { 686 struct sys__ksem_wait_args /* { 687 semid_t id; 688 } */ *uap = v; 689 690 return ksem_wait(l, SCARG(uap, id), 0); 691 } 692 693 int 694 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval __unused) 695 { 696 struct sys__ksem_trywait_args /* { 697 semid_t id; 698 } */ *uap = v; 699 700 return ksem_wait(l, SCARG(uap, id), 1); 701 } 702 703 int 704 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval __unused) 705 { 706 struct sys__ksem_getvalue_args /* { 707 semid_t id; 708 unsigned int *value; 709 } */ *uap = v; 710 struct ksem_proc *kp; 711 struct ksem *ks; 712 unsigned int val; 713 714 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 715 if (kp == NULL) 716 return (EINVAL); 717 718 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 719 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 720 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 721 if (ks == NULL) 722 return (EINVAL); 723 724 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 725 val = ks->ks_value; 726 simple_unlock(&ks->ks_interlock); 727 728 return (copyout(&val, SCARG(uap, value), sizeof(val))); 729 } 730 731 int 732 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval __unused) 733 { 734 struct sys__ksem_destroy_args /*{ 735 semid_t id; 736 } */ *uap = v; 737 struct ksem_proc *kp; 738 struct ksem_ref *ksr; 739 struct ksem *ks; 740 741 kp = proc_getspecific(l->l_proc, ksem_specificdata_key); 742 if (kp == NULL) 743 return (EINVAL); 744 745 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 746 747 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 748 if (ks == NULL) { 749 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 750 return (EINVAL); 751 } 752 753 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 754 755 /* 756 * XXX This misses named semaphores which have been unlink'd, 757 * XXX but since behavior of destroying a named semaphore is 758 * XXX undefined, this is technically allowed. 759 */ 760 if (ks->ks_name != NULL) { 761 simple_unlock(&ks->ks_interlock); 762 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 763 return (EINVAL); 764 } 765 766 if (ks->ks_waiters) { 767 simple_unlock(&ks->ks_interlock); 768 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 769 return (EBUSY); 770 } 771 772 ksr = ksem_drop_proc(kp, ks); 773 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 774 free(ksr, M_SEM); 775 776 return (0); 777 } 778 779 static void 780 ksem_forkhook(struct proc *p2, struct proc *p1) 781 { 782 struct ksem_proc *kp1, *kp2; 783 struct ksem_ref *ksr, *ksr1; 784 785 kp1 = proc_getspecific(p1, ksem_specificdata_key); 786 if (kp1 == NULL) 787 return; 788 789 kp2 = ksem_proc_alloc(); 790 791 lockmgr(&kp1->kp_lock, LK_SHARED, NULL); 792 793 if (!LIST_EMPTY(&kp1->kp_ksems)) { 794 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) { 795 ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK); 796 ksr1->ksr_ksem = ksr->ksr_ksem; 797 simple_lock(&ksr->ksr_ksem->ks_interlock); 798 ksem_addref(ksr->ksr_ksem); 799 simple_unlock(&ksr->ksr_ksem->ks_interlock); 800 LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list); 801 } 802 } 803 804 lockmgr(&kp1->kp_lock, LK_RELEASE, NULL); 805 806 proc_setspecific(p2, ksem_specificdata_key, kp2); 807 } 808 809 static void 810 ksem_exechook(struct proc *p, void *arg __unused) 811 { 812 struct ksem_proc *kp; 813 814 kp = proc_getspecific(p, ksem_specificdata_key); 815 if (kp != NULL) { 816 proc_setspecific(p, ksem_specificdata_key, NULL); 817 ksem_proc_dtor(kp); 818 } 819 } 820 821 void 822 ksem_init(void) 823 { 824 int i, error; 825 826 simple_lock_init(&ksem_slock); 827 exechook_establish(ksem_exechook, NULL); 828 forkhook_establish(ksem_forkhook); 829 830 for (i = 0; i < SEM_HASHTBL_SIZE; i++) 831 LIST_INIT(&ksem_hash[i]); 832 833 error = proc_specific_key_create(&ksem_specificdata_key, 834 ksem_proc_dtor); 835 KASSERT(error == 0); 836 } 837