1 /* 2 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include "opt_posix.h" 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/sysproto.h> 35 #include <sys/eventhandler.h> 36 #include <sys/kernel.h> 37 #include <sys/proc.h> 38 #include <sys/lock.h> 39 #include <sys/mutex.h> 40 #include <sys/condvar.h> 41 #include <sys/sem.h> 42 #include <sys/uio.h> 43 #include <sys/syscall.h> 44 #include <sys/stat.h> 45 #include <sys/sysent.h> 46 #include <sys/sysctl.h> 47 #include <sys/time.h> 48 #include <sys/malloc.h> 49 #include <sys/fcntl.h> 50 51 #include <posix4/posix4.h> 52 #include <posix4/semaphore.h> 53 #include <posix4/_semaphore.h> 54 55 static struct ksem *sem_lookup_byname(const char *name); 56 static int sem_create(struct thread *td, const char *name, 57 struct ksem **ksret, mode_t mode, unsigned int value); 58 static void sem_free(struct ksem *ksnew); 59 static int sem_perm(struct thread *td, struct ksem *ks); 60 static void sem_enter(struct proc *p, struct ksem *ks); 61 static int sem_leave(struct proc *p, struct ksem *ks); 62 static void sem_exithook(void *arg, struct proc *p); 63 static int sem_hasopen(struct thread *td, struct ksem *ks); 64 65 static int kern_sem_close(struct thread *td, semid_t id); 66 static int kern_sem_post(struct thread *td, semid_t id); 67 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag, 68 struct timespec *abstime); 69 static int kern_sem_init(struct thread *td, int dir, unsigned int value, 70 semid_t *idp); 71 static int kern_sem_open(struct thread *td, int dir, const char *name, 72 int oflag, mode_t mode, unsigned int value, semid_t *idp); 73 static int kern_sem_unlink(struct thread *td, const char *name); 74 75 #ifndef SEM_MAX 76 #define SEM_MAX 30 77 #endif 78 79 #define SEM_MAX_NAMELEN 14 80 81 #define SEM_TO_ID(x) ((intptr_t)(x)) 82 #define ID_TO_SEM(x) id_to_sem(x) 83 84 struct kuser { 85 pid_t ku_pid; 86 LIST_ENTRY(kuser) ku_next; 87 }; 88 89 struct ksem { 90 LIST_ENTRY(ksem) ks_entry; /* global list entry */ 91 int ks_onlist; /* boolean if on a list (ks_entry) */ 92 char *ks_name; /* if named, this is the name */ 93 int ks_ref; /* number of references */ 94 mode_t ks_mode; /* protection bits */ 95 uid_t ks_uid; /* creator uid */ 96 gid_t ks_gid; /* creator gid */ 97 unsigned int ks_value; /* current value */ 98 struct cv ks_cv; /* waiters sleep here */ 99 int ks_waiters; /* number of waiters */ 100 LIST_HEAD(, kuser) ks_users; /* pids using this sem */ 101 }; 102 103 /* 104 * available semaphores go here, this includes sem_init and any semaphores 105 * created via sem_open that have not yet been unlinked. 106 */ 107 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head); 108 /* 109 * semaphores still in use but have been sem_unlink()'d go here. 110 */ 111 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead); 112 113 static struct mtx sem_lock; 114 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data"); 115 116 static int nsems = 0; 117 SYSCTL_DECL(_p1003_1b); 118 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, ""); 119 120 static eventhandler_tag sem_exit_tag, sem_exec_tag; 121 122 #ifdef SEM_DEBUG 123 #define DP(x) printf x 124 #else 125 #define DP(x) 126 #endif 127 128 static __inline 129 void 130 sem_ref(struct ksem *ks) 131 { 132 133 ks->ks_ref++; 134 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref)); 135 } 136 137 static __inline 138 void 139 sem_rel(struct ksem *ks) 140 { 141 142 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1)); 143 if (--ks->ks_ref == 0) 144 sem_free(ks); 145 } 146 147 static __inline struct ksem *id_to_sem(semid_t id); 148 149 static __inline 150 struct ksem * 151 id_to_sem(id) 152 semid_t id; 153 { 154 struct ksem *ks; 155 156 DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id)); 157 LIST_FOREACH(ks, &ksem_head, ks_entry) { 158 DP(("id_to_sem: ks = %p\n", ks)); 159 if (ks == (struct ksem *)id) 160 return (ks); 161 } 162 return (NULL); 163 } 164 165 static struct ksem * 166 sem_lookup_byname(name) 167 const char *name; 168 { 169 struct ksem *ks; 170 171 LIST_FOREACH(ks, &ksem_head, ks_entry) 172 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0) 173 return (ks); 174 return (NULL); 175 } 176 177 static int 178 sem_create(td, name, ksret, mode, value) 179 struct thread *td; 180 const char *name; 181 struct ksem **ksret; 182 mode_t mode; 183 unsigned int value; 184 { 185 struct ksem *ret; 186 struct proc *p; 187 struct ucred *uc; 188 size_t len; 189 int error; 190 191 DP(("sem_create\n")); 192 p = td->td_proc; 193 uc = td->td_ucred; 194 if (value > SEM_VALUE_MAX) 195 return (EINVAL); 196 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO); 197 if (name != NULL) { 198 len = strlen(name); 199 if (len > SEM_MAX_NAMELEN) { 200 free(ret, M_SEM); 201 return (ENAMETOOLONG); 202 } 203 /* name must start with a '/' but not contain one. */ 204 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) { 205 free(ret, M_SEM); 206 return (EINVAL); 207 } 208 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK); 209 strcpy(ret->ks_name, name); 210 } else { 211 ret->ks_name = NULL; 212 } 213 ret->ks_mode = mode; 214 ret->ks_value = value; 215 ret->ks_ref = 1; 216 ret->ks_waiters = 0; 217 ret->ks_uid = uc->cr_uid; 218 ret->ks_gid = uc->cr_gid; 219 ret->ks_onlist = 0; 220 cv_init(&ret->ks_cv, "sem"); 221 LIST_INIT(&ret->ks_users); 222 if (name != NULL) 223 sem_enter(td->td_proc, ret); 224 *ksret = ret; 225 mtx_lock(&sem_lock); 226 if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) { 227 sem_leave(td->td_proc, ret); 228 sem_free(ret); 229 error = ENFILE; 230 } else { 231 nsems++; 232 error = 0; 233 } 234 mtx_unlock(&sem_lock); 235 return (error); 236 } 237 238 #ifndef _SYS_SYSPROTO_H_ 239 struct ksem_init_args { 240 unsigned int value; 241 semid_t *idp; 242 }; 243 int ksem_init(struct thread *td, struct ksem_init_args *uap); 244 #endif 245 int 246 ksem_init(td, uap) 247 struct thread *td; 248 struct ksem_init_args *uap; 249 { 250 int error; 251 252 error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp); 253 return (error); 254 } 255 256 static int 257 kern_sem_init(td, dir, value, idp) 258 struct thread *td; 259 int dir; 260 unsigned int value; 261 semid_t *idp; 262 { 263 struct ksem *ks; 264 semid_t id; 265 int error; 266 267 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value); 268 if (error) 269 return (error); 270 id = SEM_TO_ID(ks); 271 if (dir == UIO_USERSPACE) { 272 error = copyout(&id, idp, sizeof(id)); 273 if (error) { 274 mtx_lock(&sem_lock); 275 sem_rel(ks); 276 mtx_unlock(&sem_lock); 277 return (error); 278 } 279 } else { 280 *idp = id; 281 } 282 mtx_lock(&sem_lock); 283 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry); 284 ks->ks_onlist = 1; 285 mtx_unlock(&sem_lock); 286 return (error); 287 } 288 289 #ifndef _SYS_SYSPROTO_H_ 290 struct ksem_open_args { 291 char *name; 292 int oflag; 293 mode_t mode; 294 unsigned int value; 295 semid_t *idp; 296 }; 297 int ksem_open(struct thread *td, struct ksem_open_args *uap); 298 #endif 299 int 300 ksem_open(td, uap) 301 struct thread *td; 302 struct ksem_open_args *uap; 303 { 304 char name[SEM_MAX_NAMELEN + 1]; 305 size_t done; 306 int error; 307 308 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done); 309 if (error) 310 return (error); 311 DP((">>> sem_open start\n")); 312 error = kern_sem_open(td, UIO_USERSPACE, 313 name, uap->oflag, uap->mode, uap->value, uap->idp); 314 DP(("<<< sem_open end\n")); 315 return (error); 316 } 317 318 static int 319 kern_sem_open(td, dir, name, oflag, mode, value, idp) 320 struct thread *td; 321 int dir; 322 const char *name; 323 int oflag; 324 mode_t mode; 325 unsigned int value; 326 semid_t *idp; 327 { 328 struct ksem *ksnew, *ks; 329 int error; 330 semid_t id; 331 332 ksnew = NULL; 333 mtx_lock(&sem_lock); 334 ks = sem_lookup_byname(name); 335 /* 336 * If we found it but O_EXCL is set, error. 337 */ 338 if (ks != NULL && (oflag & O_EXCL) != 0) { 339 mtx_unlock(&sem_lock); 340 return (EEXIST); 341 } 342 /* 343 * If we didn't find it... 344 */ 345 if (ks == NULL) { 346 /* 347 * didn't ask for creation? error. 348 */ 349 if ((oflag & O_CREAT) == 0) { 350 mtx_unlock(&sem_lock); 351 return (ENOENT); 352 } 353 /* 354 * We may block during creation, so drop the lock. 355 */ 356 mtx_unlock(&sem_lock); 357 error = sem_create(td, name, &ksnew, mode, value); 358 if (error != 0) 359 return (error); 360 id = SEM_TO_ID(ksnew); 361 if (dir == UIO_USERSPACE) { 362 DP(("about to copyout! %d to %p\n", id, idp)); 363 error = copyout(&id, idp, sizeof(id)); 364 if (error) { 365 mtx_lock(&sem_lock); 366 sem_leave(td->td_proc, ksnew); 367 sem_rel(ksnew); 368 mtx_unlock(&sem_lock); 369 return (error); 370 } 371 } else { 372 DP(("about to set! %d to %p\n", id, idp)); 373 *idp = id; 374 } 375 /* 376 * We need to make sure we haven't lost a race while 377 * allocating during creation. 378 */ 379 mtx_lock(&sem_lock); 380 ks = sem_lookup_byname(name); 381 if (ks != NULL) { 382 /* we lost... */ 383 sem_leave(td->td_proc, ksnew); 384 sem_rel(ksnew); 385 /* we lost and we can't loose... */ 386 if ((oflag & O_EXCL) != 0) { 387 mtx_unlock(&sem_lock); 388 return (EEXIST); 389 } 390 } else { 391 DP(("sem_create: about to add to list...\n")); 392 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 393 DP(("sem_create: setting list bit...\n")); 394 ksnew->ks_onlist = 1; 395 DP(("sem_create: done, about to unlock...\n")); 396 } 397 mtx_unlock(&sem_lock); 398 } else { 399 /* 400 * if we aren't the creator, then enforce permissions. 401 */ 402 error = sem_perm(td, ks); 403 if (!error) 404 sem_ref(ks); 405 mtx_unlock(&sem_lock); 406 if (error) 407 return (error); 408 id = SEM_TO_ID(ks); 409 if (dir == UIO_USERSPACE) { 410 error = copyout(&id, idp, sizeof(id)); 411 if (error) { 412 mtx_lock(&sem_lock); 413 sem_rel(ks); 414 mtx_unlock(&sem_lock); 415 return (error); 416 } 417 } else { 418 *idp = id; 419 } 420 sem_enter(td->td_proc, ks); 421 mtx_lock(&sem_lock); 422 sem_rel(ks); 423 mtx_unlock(&sem_lock); 424 } 425 return (error); 426 } 427 428 static int 429 sem_perm(td, ks) 430 struct thread *td; 431 struct ksem *ks; 432 { 433 struct ucred *uc; 434 435 uc = td->td_ucred; 436 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n", 437 uc->cr_uid, uc->cr_gid, 438 ks->ks_uid, ks->ks_gid, ks->ks_mode)); 439 if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) || 440 (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) || 441 (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0) 442 return (0); 443 return (EPERM); 444 } 445 446 static void 447 sem_free(struct ksem *ks) 448 { 449 450 nsems--; 451 if (ks->ks_onlist) 452 LIST_REMOVE(ks, ks_entry); 453 if (ks->ks_name != NULL) 454 free(ks->ks_name, M_SEM); 455 cv_destroy(&ks->ks_cv); 456 free(ks, M_SEM); 457 } 458 459 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks); 460 461 static __inline struct kuser * 462 sem_getuser(p, ks) 463 struct proc *p; 464 struct ksem *ks; 465 { 466 struct kuser *k; 467 468 LIST_FOREACH(k, &ks->ks_users, ku_next) 469 if (k->ku_pid == p->p_pid) 470 return (k); 471 return (NULL); 472 } 473 474 static int 475 sem_hasopen(td, ks) 476 struct thread *td; 477 struct ksem *ks; 478 { 479 480 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0) 481 || sem_getuser(td->td_proc, ks) != NULL); 482 } 483 484 static int 485 sem_leave(p, ks) 486 struct proc *p; 487 struct ksem *ks; 488 { 489 struct kuser *k; 490 491 DP(("sem_leave: ks = %p\n", ks)); 492 k = sem_getuser(p, ks); 493 DP(("sem_leave: ks = %p, k = %p\n", ks, k)); 494 if (k != NULL) { 495 LIST_REMOVE(k, ku_next); 496 sem_rel(ks); 497 DP(("sem_leave: about to free k\n")); 498 free(k, M_SEM); 499 DP(("sem_leave: returning\n")); 500 return (0); 501 } 502 return (EINVAL); 503 } 504 505 static void 506 sem_enter(p, ks) 507 struct proc *p; 508 struct ksem *ks; 509 { 510 struct kuser *ku, *k; 511 512 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK); 513 ku->ku_pid = p->p_pid; 514 mtx_lock(&sem_lock); 515 k = sem_getuser(p, ks); 516 if (k != NULL) { 517 mtx_unlock(&sem_lock); 518 free(ku, M_TEMP); 519 return; 520 } 521 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next); 522 sem_ref(ks); 523 mtx_unlock(&sem_lock); 524 } 525 526 #ifndef _SYS_SYSPROTO_H_ 527 struct ksem_unlink_args { 528 char *name; 529 }; 530 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap); 531 #endif 532 533 int 534 ksem_unlink(td, uap) 535 struct thread *td; 536 struct ksem_unlink_args *uap; 537 { 538 char name[SEM_MAX_NAMELEN + 1]; 539 size_t done; 540 int error; 541 542 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done); 543 return (error ? error : 544 kern_sem_unlink(td, name)); 545 } 546 547 static int 548 kern_sem_unlink(td, name) 549 struct thread *td; 550 const char *name; 551 { 552 struct ksem *ks; 553 int error; 554 555 mtx_lock(&sem_lock); 556 ks = sem_lookup_byname(name); 557 if (ks == NULL) 558 error = ENOENT; 559 else 560 error = sem_perm(td, ks); 561 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error)); 562 if (error == 0) { 563 LIST_REMOVE(ks, ks_entry); 564 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 565 sem_rel(ks); 566 } 567 mtx_unlock(&sem_lock); 568 return (error); 569 } 570 571 #ifndef _SYS_SYSPROTO_H_ 572 struct ksem_close_args { 573 semid_t id; 574 }; 575 int ksem_close(struct thread *td, struct ksem_close_args *uap); 576 #endif 577 578 int 579 ksem_close(struct thread *td, struct ksem_close_args *uap) 580 { 581 582 return (kern_sem_close(td, uap->id)); 583 } 584 585 static int 586 kern_sem_close(td, id) 587 struct thread *td; 588 semid_t id; 589 { 590 struct ksem *ks; 591 int error; 592 593 error = EINVAL; 594 mtx_lock(&sem_lock); 595 ks = ID_TO_SEM(id); 596 /* this is not a valid operation for unnamed sems */ 597 if (ks != NULL && ks->ks_name != NULL) 598 error = sem_leave(td->td_proc, ks); 599 mtx_unlock(&sem_lock); 600 return (error); 601 } 602 603 #ifndef _SYS_SYSPROTO_H_ 604 struct ksem_post_args { 605 semid_t id; 606 }; 607 int ksem_post(struct thread *td, struct ksem_post_args *uap); 608 #endif 609 int 610 ksem_post(td, uap) 611 struct thread *td; 612 struct ksem_post_args *uap; 613 { 614 615 return (kern_sem_post(td, uap->id)); 616 } 617 618 static int 619 kern_sem_post(td, id) 620 struct thread *td; 621 semid_t id; 622 { 623 struct ksem *ks; 624 int error; 625 626 mtx_lock(&sem_lock); 627 ks = ID_TO_SEM(id); 628 if (ks == NULL || !sem_hasopen(td, ks)) { 629 error = EINVAL; 630 goto err; 631 } 632 if (ks->ks_value == SEM_VALUE_MAX) { 633 error = EOVERFLOW; 634 goto err; 635 } 636 ++ks->ks_value; 637 if (ks->ks_waiters > 0) 638 cv_signal(&ks->ks_cv); 639 error = 0; 640 err: 641 mtx_unlock(&sem_lock); 642 return (error); 643 } 644 645 #ifndef _SYS_SYSPROTO_H_ 646 struct ksem_wait_args { 647 semid_t id; 648 }; 649 int ksem_wait(struct thread *td, struct ksem_wait_args *uap); 650 #endif 651 652 int 653 ksem_wait(td, uap) 654 struct thread *td; 655 struct ksem_wait_args *uap; 656 { 657 658 return (kern_sem_wait(td, uap->id, 0, NULL)); 659 } 660 661 #ifndef _SYS_SYSPROTO_H_ 662 struct ksem_timedwait_args { 663 semid_t id; 664 struct timespec *abstime; 665 }; 666 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap); 667 #endif 668 int 669 ksem_timedwait(td, uap) 670 struct thread *td; 671 struct ksem_timedwait_args *uap; 672 { 673 struct timespec abstime; 674 struct timespec *ts; 675 int error; 676 677 /* We allow a null timespec (wait forever). */ 678 if (uap->abstime == NULL) 679 ts = NULL; 680 else { 681 error = copyin(uap->abstime, &abstime, sizeof(abstime)); 682 if (error != 0) 683 return (error); 684 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0) 685 return (EINVAL); 686 ts = &abstime; 687 } 688 return (kern_sem_wait(td, uap->id, 0, ts)); 689 } 690 691 #ifndef _SYS_SYSPROTO_H_ 692 struct ksem_trywait_args { 693 semid_t id; 694 }; 695 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap); 696 #endif 697 int 698 ksem_trywait(td, uap) 699 struct thread *td; 700 struct ksem_trywait_args *uap; 701 { 702 703 return (kern_sem_wait(td, uap->id, 1, NULL)); 704 } 705 706 static int 707 kern_sem_wait(td, id, tryflag, abstime) 708 struct thread *td; 709 semid_t id; 710 int tryflag; 711 struct timespec *abstime; 712 { 713 struct timespec ts1, ts2; 714 struct timeval tv; 715 struct ksem *ks; 716 int error; 717 718 DP((">>> kern_sem_wait entered!\n")); 719 mtx_lock(&sem_lock); 720 ks = ID_TO_SEM(id); 721 if (ks == NULL) { 722 DP(("kern_sem_wait ks == NULL\n")); 723 error = EINVAL; 724 goto err; 725 } 726 sem_ref(ks); 727 if (!sem_hasopen(td, ks)) { 728 DP(("kern_sem_wait hasopen failed\n")); 729 error = EINVAL; 730 goto err; 731 } 732 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag)); 733 if (ks->ks_value == 0) { 734 ks->ks_waiters++; 735 if (tryflag != 0) 736 error = EAGAIN; 737 else if (abstime == NULL) 738 error = cv_wait_sig(&ks->ks_cv, &sem_lock); 739 else { 740 for (;;) { 741 ts1 = *abstime; 742 getnanotime(&ts2); 743 timespecsub(&ts1, &ts2); 744 TIMESPEC_TO_TIMEVAL(&tv, &ts1); 745 if (tv.tv_sec < 0) { 746 error = ETIMEDOUT; 747 break; 748 } 749 error = cv_timedwait_sig(&ks->ks_cv, 750 &sem_lock, tvtohz(&tv)); 751 if (error != EWOULDBLOCK) 752 break; 753 } 754 } 755 ks->ks_waiters--; 756 if (error) 757 goto err; 758 } 759 ks->ks_value--; 760 error = 0; 761 err: 762 if (ks != NULL) 763 sem_rel(ks); 764 mtx_unlock(&sem_lock); 765 DP(("<<< kern_sem_wait leaving, error = %d\n", error)); 766 return (error); 767 } 768 769 #ifndef _SYS_SYSPROTO_H_ 770 struct ksem_getvalue_args { 771 semid_t id; 772 int *val; 773 }; 774 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap); 775 #endif 776 int 777 ksem_getvalue(td, uap) 778 struct thread *td; 779 struct ksem_getvalue_args *uap; 780 { 781 struct ksem *ks; 782 int error, val; 783 784 mtx_lock(&sem_lock); 785 ks = ID_TO_SEM(uap->id); 786 if (ks == NULL || !sem_hasopen(td, ks)) { 787 mtx_unlock(&sem_lock); 788 return (EINVAL); 789 } 790 val = ks->ks_value; 791 mtx_unlock(&sem_lock); 792 error = copyout(&val, uap->val, sizeof(val)); 793 return (error); 794 } 795 796 #ifndef _SYS_SYSPROTO_H_ 797 struct ksem_destroy_args { 798 semid_t id; 799 }; 800 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap); 801 #endif 802 int 803 ksem_destroy(td, uap) 804 struct thread *td; 805 struct ksem_destroy_args *uap; 806 { 807 struct ksem *ks; 808 int error; 809 810 mtx_lock(&sem_lock); 811 ks = ID_TO_SEM(uap->id); 812 if (ks == NULL || !sem_hasopen(td, ks) || 813 ks->ks_name != NULL) { 814 error = EINVAL; 815 goto err; 816 } 817 if (ks->ks_waiters != 0) { 818 error = EBUSY; 819 goto err; 820 } 821 sem_rel(ks); 822 error = 0; 823 err: 824 mtx_unlock(&sem_lock); 825 return (error); 826 } 827 828 static void 829 sem_exithook(arg, p) 830 void *arg; 831 struct proc *p; 832 { 833 struct ksem *ks, *ksnext; 834 835 mtx_lock(&sem_lock); 836 ks = LIST_FIRST(&ksem_head); 837 while (ks != NULL) { 838 ksnext = LIST_NEXT(ks, ks_entry); 839 sem_leave(p, ks); 840 ks = ksnext; 841 } 842 ks = LIST_FIRST(&ksem_deadhead); 843 while (ks != NULL) { 844 ksnext = LIST_NEXT(ks, ks_entry); 845 sem_leave(p, ks); 846 ks = ksnext; 847 } 848 mtx_unlock(&sem_lock); 849 } 850 851 static int 852 sem_modload(struct module *module, int cmd, void *arg) 853 { 854 int error = 0; 855 856 switch (cmd) { 857 case MOD_LOAD: 858 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF); 859 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX); 860 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX); 861 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook, 862 NULL, EVENTHANDLER_PRI_ANY); 863 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook, 864 NULL, EVENTHANDLER_PRI_ANY); 865 break; 866 case MOD_UNLOAD: 867 if (nsems != 0) { 868 error = EOPNOTSUPP; 869 break; 870 } 871 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag); 872 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag); 873 mtx_destroy(&sem_lock); 874 break; 875 case MOD_SHUTDOWN: 876 break; 877 default: 878 error = EINVAL; 879 break; 880 } 881 return (error); 882 } 883 884 static moduledata_t sem_mod = { 885 "sem", 886 &sem_modload, 887 NULL 888 }; 889 890 SYSCALL_MODULE_HELPER(ksem_init); 891 SYSCALL_MODULE_HELPER(ksem_open); 892 SYSCALL_MODULE_HELPER(ksem_unlink); 893 SYSCALL_MODULE_HELPER(ksem_close); 894 SYSCALL_MODULE_HELPER(ksem_post); 895 SYSCALL_MODULE_HELPER(ksem_wait); 896 SYSCALL_MODULE_HELPER(ksem_timedwait); 897 SYSCALL_MODULE_HELPER(ksem_trywait); 898 SYSCALL_MODULE_HELPER(ksem_getvalue); 899 SYSCALL_MODULE_HELPER(ksem_destroy); 900 901 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST); 902 MODULE_VERSION(sem, 1); 903