1 /* $NetBSD: uipc_sem.c,v 1.14 2006/05/14 21:15:12 elad 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.14 2006/05/14 21:15:12 elad 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 147 static void 148 ksem_free(struct ksem *ks) 149 { 150 151 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 152 /* 153 * If the ksem is anonymous (or has been unlinked), then 154 * this is the end if its life. 155 */ 156 if (ks->ks_name == NULL) { 157 simple_unlock(&ks->ks_interlock); 158 159 simple_lock(&ksem_slock); 160 nsems--; 161 LIST_REMOVE(ks, ks_hash); 162 simple_unlock(&ksem_slock); 163 164 free(ks, M_SEM); 165 return; 166 } 167 simple_unlock(&ks->ks_interlock); 168 } 169 170 static inline void 171 ksem_addref(struct ksem *ks) 172 { 173 174 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 175 ks->ks_ref++; 176 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */ 177 } 178 179 static inline void 180 ksem_delref(struct ksem *ks) 181 { 182 183 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 184 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */ 185 if (--ks->ks_ref == 0) { 186 ksem_free(ks); 187 return; 188 } 189 simple_unlock(&ks->ks_interlock); 190 } 191 192 static struct ksem_proc * 193 ksem_proc_alloc(void) 194 { 195 struct ksem_proc *kp; 196 197 kp = malloc(sizeof(*kp), M_SEM, M_WAITOK); 198 lockinit(&kp->kp_lock, PWAIT, "ksproc", 0, 0); 199 LIST_INIT(&kp->kp_ksems); 200 201 return (kp); 202 } 203 204 static void 205 ksem_add_proc(struct proc *p, struct ksem *ks) 206 { 207 struct ksem_proc *kp; 208 struct ksem_ref *ksr; 209 210 if (p->p_ksems == NULL) { 211 kp = ksem_proc_alloc(); 212 p->p_ksems = kp; 213 } else 214 kp = p->p_ksems; 215 216 ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK); 217 ksr->ksr_ksem = ks; 218 219 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 220 LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list); 221 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 222 } 223 224 /* We MUST have a write lock on the ksem_proc list! */ 225 static struct ksem_ref * 226 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks) 227 { 228 struct ksem_ref *ksr; 229 230 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 231 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 232 if (ksr->ksr_ksem == ks) { 233 ksem_delref(ks); 234 LIST_REMOVE(ksr, ksr_list); 235 return (ksr); 236 } 237 } 238 #ifdef DIAGNOSTIC 239 panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks); 240 #endif 241 return (NULL); 242 } 243 244 static int 245 ksem_perm(struct proc *p, struct ksem *ks) 246 { 247 kauth_cred_t uc; 248 249 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 250 uc = p->p_cred; 251 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) || 252 (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) || 253 (ks->ks_mode & S_IWOTH) != 0 || 254 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, &p->p_acflag) == 0) 255 return (0); 256 return (EPERM); 257 } 258 259 static struct ksem * 260 ksem_lookup_byid(semid_t id) 261 { 262 struct ksem *ks; 263 264 LOCK_ASSERT(simple_lock_held(&ksem_slock)); 265 LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) { 266 if (ks->ks_id == id) 267 return ks; 268 } 269 return NULL; 270 } 271 272 static struct ksem * 273 ksem_lookup_byname(const char *name) 274 { 275 struct ksem *ks; 276 277 LOCK_ASSERT(simple_lock_held(&ksem_slock)); 278 LIST_FOREACH(ks, &ksem_head, ks_entry) { 279 if (strcmp(ks->ks_name, name) == 0) { 280 simple_lock(&ks->ks_interlock); 281 return (ks); 282 } 283 } 284 return (NULL); 285 } 286 287 static int 288 ksem_create(struct proc *p, const char *name, struct ksem **ksret, 289 mode_t mode, unsigned int value) 290 { 291 struct ksem *ret; 292 kauth_cred_t uc; 293 size_t len; 294 295 uc = p->p_cred; 296 if (value > SEM_VALUE_MAX) 297 return (EINVAL); 298 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO); 299 if (name != NULL) { 300 len = strlen(name); 301 if (len > SEM_MAX_NAMELEN) { 302 free(ret, M_SEM); 303 return (ENAMETOOLONG); 304 } 305 /* name must start with a '/' but not contain one. */ 306 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) { 307 free(ret, M_SEM); 308 return (EINVAL); 309 } 310 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK); 311 strlcpy(ret->ks_name, name, len + 1); 312 } else 313 ret->ks_name = NULL; 314 ret->ks_mode = mode; 315 ret->ks_value = value; 316 ret->ks_ref = 1; 317 ret->ks_waiters = 0; 318 ret->ks_uid = kauth_cred_geteuid(uc); 319 ret->ks_gid = kauth_cred_getegid(uc); 320 simple_lock_init(&ret->ks_interlock); 321 322 simple_lock(&ksem_slock); 323 if (nsems >= SEM_MAX) { 324 simple_unlock(&ksem_slock); 325 if (ret->ks_name != NULL) 326 free(ret->ks_name, M_SEM); 327 free(ret, M_SEM); 328 return (ENFILE); 329 } 330 nsems++; 331 while (ksem_lookup_byid(ksem_counter) != NULL) { 332 ksem_counter++; 333 /* 0 is a special value for libpthread */ 334 if (ksem_counter == 0) 335 ksem_counter++; 336 } 337 ret->ks_id = ksem_counter; 338 LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash); 339 simple_unlock(&ksem_slock); 340 341 *ksret = ret; 342 return (0); 343 } 344 345 int 346 sys__ksem_init(struct lwp *l, void *v, register_t *retval) 347 { 348 struct sys__ksem_init_args /* { 349 unsigned int value; 350 semid_t *idp; 351 } */ *uap = v; 352 353 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout); 354 } 355 356 int 357 do_ksem_init(struct lwp *l, unsigned int value, semid_t *idp, 358 copyout_t docopyout) 359 { 360 struct ksem *ks; 361 semid_t id; 362 int error; 363 364 /* Note the mode does not matter for anonymous semaphores. */ 365 error = ksem_create(l->l_proc, NULL, &ks, 0, value); 366 if (error) 367 return (error); 368 id = SEM_TO_ID(ks); 369 error = (*docopyout)(&id, idp, sizeof(id)); 370 if (error) { 371 simple_lock(&ks->ks_interlock); 372 ksem_delref(ks); 373 return (error); 374 } 375 376 ksem_add_proc(l->l_proc, ks); 377 378 return (0); 379 } 380 381 int 382 sys__ksem_open(struct lwp *l, void *v, register_t *retval) 383 { 384 struct sys__ksem_open_args /* { 385 const char *name; 386 int oflag; 387 mode_t mode; 388 unsigned int value; 389 semid_t *idp; 390 } */ *uap = v; 391 392 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag), 393 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout); 394 } 395 396 int 397 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode, 398 unsigned int value, semid_t *idp, copyout_t docopyout) 399 { 400 char name[SEM_MAX_NAMELEN + 1]; 401 size_t done; 402 int error; 403 struct ksem *ksnew, *ks; 404 semid_t id; 405 406 error = copyinstr(semname, name, sizeof(name), &done); 407 if (error) 408 return (error); 409 410 ksnew = NULL; 411 simple_lock(&ksem_slock); 412 ks = ksem_lookup_byname(name); 413 414 /* Found one? */ 415 if (ks != NULL) { 416 /* Check for exclusive create. */ 417 if (oflag & O_EXCL) { 418 simple_unlock(&ks->ks_interlock); 419 simple_unlock(&ksem_slock); 420 return (EEXIST); 421 } 422 found_one: 423 /* 424 * Verify permissions. If we can access it, add 425 * this process's reference. 426 */ 427 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 428 error = ksem_perm(l->l_proc, ks); 429 if (error == 0) 430 ksem_addref(ks); 431 simple_unlock(&ks->ks_interlock); 432 simple_unlock(&ksem_slock); 433 if (error) 434 return (error); 435 436 id = SEM_TO_ID(ks); 437 error = (*docopyout)(&id, idp, sizeof(id)); 438 if (error) { 439 simple_lock(&ks->ks_interlock); 440 ksem_delref(ks); 441 return (error); 442 } 443 444 ksem_add_proc(l->l_proc, ks); 445 446 return (0); 447 } 448 449 /* 450 * didn't ask for creation? error. 451 */ 452 if ((oflag & O_CREAT) == 0) { 453 simple_unlock(&ksem_slock); 454 return (ENOENT); 455 } 456 457 /* 458 * We may block during creation, so drop the lock. 459 */ 460 simple_unlock(&ksem_slock); 461 error = ksem_create(l->l_proc, name, &ksnew, mode, value); 462 if (error != 0) 463 return (error); 464 465 id = SEM_TO_ID(ksnew); 466 error = (*docopyout)(&id, idp, sizeof(id)); 467 if (error) { 468 free(ksnew->ks_name, M_SEM); 469 ksnew->ks_name = NULL; 470 471 simple_lock(&ksnew->ks_interlock); 472 ksem_delref(ksnew); 473 return (error); 474 } 475 476 /* 477 * We need to make sure we haven't lost a race while 478 * allocating during creation. 479 */ 480 simple_lock(&ksem_slock); 481 if ((ks = ksem_lookup_byname(name)) != NULL) { 482 if (oflag & O_EXCL) { 483 simple_unlock(&ks->ks_interlock); 484 simple_unlock(&ksem_slock); 485 486 free(ksnew->ks_name, M_SEM); 487 ksnew->ks_name = NULL; 488 489 simple_lock(&ksnew->ks_interlock); 490 ksem_delref(ksnew); 491 return (EEXIST); 492 } 493 goto found_one; 494 } else { 495 /* ksnew already has its initial reference. */ 496 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 497 simple_unlock(&ksem_slock); 498 499 ksem_add_proc(l->l_proc, ksnew); 500 } 501 return (error); 502 } 503 504 /* We must have a read lock on the ksem_proc list! */ 505 static struct ksem * 506 ksem_lookup_proc(struct ksem_proc *kp, semid_t id) 507 { 508 struct ksem_ref *ksr; 509 510 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) { 511 if (id == SEM_TO_ID(ksr->ksr_ksem)) { 512 simple_lock(&ksr->ksr_ksem->ks_interlock); 513 return (ksr->ksr_ksem); 514 } 515 } 516 517 return (NULL); 518 } 519 520 int 521 sys__ksem_unlink(struct lwp *l, void *v, register_t *retval) 522 { 523 struct sys__ksem_unlink_args /* { 524 const char *name; 525 } */ *uap = v; 526 char name[SEM_MAX_NAMELEN + 1], *cp; 527 size_t done; 528 struct ksem *ks; 529 int error; 530 531 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done); 532 if (error) 533 return error; 534 535 simple_lock(&ksem_slock); 536 ks = ksem_lookup_byname(name); 537 if (ks == NULL) { 538 simple_unlock(&ksem_slock); 539 return (ENOENT); 540 } 541 542 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 543 544 LIST_REMOVE(ks, ks_entry); 545 cp = ks->ks_name; 546 ks->ks_name = NULL; 547 548 simple_unlock(&ksem_slock); 549 550 if (ks->ks_ref == 0) 551 ksem_free(ks); 552 else 553 simple_unlock(&ks->ks_interlock); 554 555 free(cp, M_SEM); 556 557 return (0); 558 } 559 560 int 561 sys__ksem_close(struct lwp *l, void *v, register_t *retval) 562 { 563 struct sys__ksem_close_args /* { 564 semid_t id; 565 } */ *uap = v; 566 struct ksem_proc *kp; 567 struct ksem_ref *ksr; 568 struct ksem *ks; 569 570 if ((kp = l->l_proc->p_ksems) == NULL) 571 return (EINVAL); 572 573 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 574 575 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 576 if (ks == NULL) { 577 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 578 return (EINVAL); 579 } 580 581 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 582 if (ks->ks_name == NULL) { 583 simple_unlock(&ks->ks_interlock); 584 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 585 return (EINVAL); 586 } 587 588 ksr = ksem_drop_proc(kp, ks); 589 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 590 free(ksr, M_SEM); 591 592 return (0); 593 } 594 595 int 596 sys__ksem_post(struct lwp *l, void *v, register_t *retval) 597 { 598 struct sys__ksem_post_args /* { 599 semid_t id; 600 } */ *uap = v; 601 struct ksem_proc *kp; 602 struct ksem *ks; 603 int error; 604 605 if ((kp = l->l_proc->p_ksems) == NULL) 606 return (EINVAL); 607 608 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 609 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 610 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 611 if (ks == NULL) 612 return (EINVAL); 613 614 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 615 if (ks->ks_value == SEM_VALUE_MAX) { 616 error = EOVERFLOW; 617 goto out; 618 } 619 ++ks->ks_value; 620 if (ks->ks_waiters) 621 wakeup(ks); 622 error = 0; 623 out: 624 simple_unlock(&ks->ks_interlock); 625 return (error); 626 } 627 628 static int 629 ksem_wait(struct lwp *l, semid_t id, int tryflag) 630 { 631 struct ksem_proc *kp; 632 struct ksem *ks; 633 int error; 634 635 if ((kp = l->l_proc->p_ksems) == NULL) 636 return (EINVAL); 637 638 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 639 ks = ksem_lookup_proc(kp, id); 640 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 641 if (ks == NULL) 642 return (EINVAL); 643 644 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 645 ksem_addref(ks); 646 while (ks->ks_value == 0) { 647 ks->ks_waiters++; 648 error = tryflag ? EAGAIN : ltsleep(ks, PCATCH, "psem", 0, 649 &ks->ks_interlock); 650 ks->ks_waiters--; 651 if (error) 652 goto out; 653 } 654 ks->ks_value--; 655 error = 0; 656 out: 657 ksem_delref(ks); 658 return (error); 659 } 660 661 int 662 sys__ksem_wait(struct lwp *l, void *v, register_t *retval) 663 { 664 struct sys__ksem_wait_args /* { 665 semid_t id; 666 } */ *uap = v; 667 668 return ksem_wait(l, SCARG(uap, id), 0); 669 } 670 671 int 672 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval) 673 { 674 struct sys__ksem_trywait_args /* { 675 semid_t id; 676 } */ *uap = v; 677 678 return ksem_wait(l, SCARG(uap, id), 1); 679 } 680 681 int 682 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval) 683 { 684 struct sys__ksem_getvalue_args /* { 685 semid_t id; 686 unsigned int *value; 687 } */ *uap = v; 688 struct ksem_proc *kp; 689 struct ksem *ks; 690 unsigned int val; 691 692 if ((kp = l->l_proc->p_ksems) == NULL) 693 return (EINVAL); 694 695 lockmgr(&kp->kp_lock, LK_SHARED, NULL); 696 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 697 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 698 if (ks == NULL) 699 return (EINVAL); 700 701 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 702 val = ks->ks_value; 703 simple_unlock(&ks->ks_interlock); 704 705 return (copyout(&val, SCARG(uap, value), sizeof(val))); 706 } 707 708 int 709 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval) 710 { 711 struct sys__ksem_destroy_args /*{ 712 semid_t id; 713 } */ *uap = v; 714 struct ksem_proc *kp; 715 struct ksem_ref *ksr; 716 struct ksem *ks; 717 718 if ((kp = l->l_proc->p_ksems) == NULL) 719 return (EINVAL); 720 721 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL); 722 723 ks = ksem_lookup_proc(kp, SCARG(uap, id)); 724 if (ks == NULL) { 725 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 726 return (EINVAL); 727 } 728 729 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock)); 730 731 /* 732 * XXX This misses named semaphores which have been unlink'd, 733 * XXX but since behavior of destroying a named semaphore is 734 * XXX undefined, this is technically allowed. 735 */ 736 if (ks->ks_name != NULL) { 737 simple_unlock(&ks->ks_interlock); 738 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 739 return (EINVAL); 740 } 741 742 if (ks->ks_waiters) { 743 simple_unlock(&ks->ks_interlock); 744 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 745 return (EBUSY); 746 } 747 748 ksr = ksem_drop_proc(kp, ks); 749 lockmgr(&kp->kp_lock, LK_RELEASE, NULL); 750 free(ksr, M_SEM); 751 752 return (0); 753 } 754 755 static void 756 ksem_forkhook(struct proc *p2, struct proc *p1) 757 { 758 struct ksem_proc *kp1, *kp2; 759 struct ksem_ref *ksr, *ksr1; 760 761 if ((kp1 = p1->p_ksems) == NULL) { 762 p2->p_ksems = NULL; 763 return; 764 } 765 766 p2->p_ksems = kp2 = ksem_proc_alloc(); 767 768 lockmgr(&kp1->kp_lock, LK_SHARED, NULL); 769 770 if (!LIST_EMPTY(&kp1->kp_ksems)) { 771 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) { 772 ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK); 773 ksr1->ksr_ksem = ksr->ksr_ksem; 774 simple_lock(&ksr->ksr_ksem->ks_interlock); 775 ksem_addref(ksr->ksr_ksem); 776 simple_unlock(&ksr->ksr_ksem->ks_interlock); 777 LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list); 778 } 779 } 780 781 lockmgr(&kp1->kp_lock, LK_RELEASE, NULL); 782 } 783 784 static void 785 ksem_exithook(struct proc *p, void *arg) 786 { 787 struct ksem_proc *kp; 788 struct ksem_ref *ksr; 789 790 if ((kp = p->p_ksems) == NULL) 791 return; 792 793 /* Don't bother locking; process is dying. */ 794 795 while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) { 796 LIST_REMOVE(ksr, ksr_list); 797 simple_lock(&ksr->ksr_ksem->ks_interlock); 798 ksem_delref(ksr->ksr_ksem); 799 free(ksr, M_SEM); 800 } 801 } 802 803 void 804 ksem_init(void) 805 { 806 int i; 807 808 simple_lock_init(&ksem_slock); 809 exithook_establish(ksem_exithook, NULL); 810 exechook_establish(ksem_exithook, NULL); 811 forkhook_establish(ksem_forkhook); 812 813 for (i = 0; i < SEM_HASHTBL_SIZE; i++) 814 LIST_INIT(&ksem_hash[i]); 815 } 816