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