Home | History | Annotate | Line # | Download | only in kern
uipc_sem.c revision 1.8.2.1
      1 /*	$NetBSD: uipc_sem.c,v 1.8.2.1 2003/07/02 15:26:44 darrenr 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 (at) 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 "opt_posix.h"
     66 
     67 #include <sys/param.h>
     68 #include <sys/systm.h>
     69 #include <sys/kernel.h>
     70 #include <sys/proc.h>
     71 #include <sys/lock.h>
     72 #include <sys/ksem.h>
     73 #include <sys/sa.h>
     74 #include <sys/syscall.h>
     75 #include <sys/stat.h>
     76 #include <sys/malloc.h>
     77 #include <sys/fcntl.h>
     78 
     79 #include <sys/mount.h>
     80 
     81 #include <sys/syscallargs.h>
     82 
     83 #ifndef SEM_MAX
     84 #define SEM_MAX	30
     85 #endif
     86 
     87 #define SEM_MAX_NAMELEN	14
     88 #define SEM_VALUE_MAX (~0U)
     89 
     90 #define SEM_TO_ID(x)	((intptr_t)(x))
     91 
     92 MALLOC_DEFINE(M_SEM, "p1003_1b_sem", "p1003_1b semaphores");
     93 
     94 /*
     95  * Note: to read the ks_name member, you need either the ks_interlock
     96  * or the ksem_slock.  To write the ks_name member, you need both.  Make
     97  * sure the order is ksem_slock -> ks_interlock.
     98  */
     99 struct ksem {
    100 	LIST_ENTRY(ksem) ks_entry;	/* global list entry */
    101 	struct simplelock ks_interlock;	/* lock on this ksem */
    102 	char *ks_name;			/* if named, this is the name */
    103 	unsigned int ks_ref;		/* number of references */
    104 	mode_t ks_mode;			/* protection bits */
    105 	uid_t ks_uid;			/* creator uid */
    106 	gid_t ks_gid;			/* creator gid */
    107 	unsigned int ks_value;		/* current value */
    108 	unsigned int ks_waiters;	/* number of waiters */
    109 };
    110 
    111 struct ksem_ref {
    112 	LIST_ENTRY(ksem_ref) ksr_list;
    113 	struct ksem *ksr_ksem;
    114 };
    115 
    116 struct ksem_proc {
    117 	struct lock kp_lock;
    118 	LIST_HEAD(, ksem_ref) kp_ksems;
    119 };
    120 
    121 /*
    122  * ksem_slock protects ksem_head and nsems.  Only named semaphores go
    123  * onto ksem_head.
    124  */
    125 static struct simplelock ksem_slock;
    126 static LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
    127 static int nsems = 0;
    128 
    129 static void
    130 ksem_free(struct ksem *ks)
    131 {
    132 
    133 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    134 	/*
    135 	 * If the ksem is anonymous (or has been unlinked), then
    136 	 * this is the end if its life.
    137 	 */
    138 	if (ks->ks_name == NULL) {
    139 		simple_unlock(&ks->ks_interlock);
    140 		free(ks, M_SEM);
    141 
    142 		simple_lock(&ksem_slock);
    143 		nsems--;
    144 		simple_unlock(&ksem_slock);
    145 		return;
    146 	}
    147 	simple_unlock(&ks->ks_interlock);
    148 }
    149 
    150 static __inline void
    151 ksem_addref(struct ksem *ks)
    152 {
    153 
    154 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    155 	ks->ks_ref++;
    156 	KASSERT(ks->ks_ref != 0);	/* XXX KDASSERT */
    157 }
    158 
    159 static __inline void
    160 ksem_delref(struct ksem *ks)
    161 {
    162 
    163 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    164 	KASSERT(ks->ks_ref != 0);	/* XXX KDASSERT */
    165 	if (--ks->ks_ref == 0) {
    166 		ksem_free(ks);
    167 		return;
    168 	}
    169 	simple_unlock(&ks->ks_interlock);
    170 }
    171 
    172 static struct ksem_proc *
    173 ksem_proc_alloc(void)
    174 {
    175 	struct ksem_proc *kp;
    176 
    177 	kp = malloc(sizeof(*kp), M_SEM, M_WAITOK);
    178 	lockinit(&kp->kp_lock, PWAIT, "ksproc", 0, 0);
    179 	LIST_INIT(&kp->kp_ksems);
    180 
    181 	return (kp);
    182 }
    183 
    184 static void
    185 ksem_add_proc(struct proc *p, struct ksem *ks)
    186 {
    187 	struct ksem_proc *kp;
    188 	struct ksem_ref *ksr;
    189 
    190 	if (p->p_ksems == NULL) {
    191 		kp = ksem_proc_alloc();
    192 		p->p_ksems = kp;
    193 	} else
    194 		kp = p->p_ksems;
    195 
    196 	ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
    197 	ksr->ksr_ksem = ks;
    198 
    199 	lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
    200 	LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list);
    201 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    202 }
    203 
    204 /* We MUST have a write lock on the ksem_proc list! */
    205 static struct ksem_ref *
    206 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks)
    207 {
    208 	struct ksem_ref *ksr;
    209 
    210 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    211 	LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
    212 		if (ksr->ksr_ksem == ks) {
    213 			ksem_delref(ks);
    214 			LIST_REMOVE(ksr, ksr_list);
    215 			return (ksr);
    216 		}
    217 	}
    218 #ifdef DIAGNOSTIC
    219 	panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks);
    220 #endif
    221 	return (NULL);
    222 }
    223 
    224 static int
    225 ksem_perm(struct proc *p, struct ksem *ks)
    226 {
    227 	struct ucred *uc;
    228 
    229 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    230 	uc = p->p_ucred;
    231 	if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
    232 	    (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
    233 	    (ks->ks_mode & S_IWOTH) != 0 || suser(uc, &p->p_acflag) == 0)
    234 		return (0);
    235 	return (EPERM);
    236 }
    237 
    238 static struct ksem *
    239 ksem_lookup_byname(const char *name)
    240 {
    241 	struct ksem *ks;
    242 
    243 	LOCK_ASSERT(simple_lock_held(&ksem_slock));
    244 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
    245 		if (strcmp(ks->ks_name, name) == 0) {
    246 			simple_lock(&ks->ks_interlock);
    247 			return (ks);
    248 		}
    249 	}
    250 	return (NULL);
    251 }
    252 
    253 static int
    254 ksem_create(struct proc *p, const char *name, struct ksem **ksret,
    255     mode_t mode, unsigned int value)
    256 {
    257 	struct ksem *ret;
    258 	struct ucred *uc;
    259 	size_t len;
    260 
    261 	uc = p->p_ucred;
    262 	if (value > SEM_VALUE_MAX)
    263 		return (EINVAL);
    264 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
    265 	if (name != NULL) {
    266 		len = strlen(name);
    267 		if (len > SEM_MAX_NAMELEN) {
    268 			free(ret, M_SEM);
    269 			return (ENAMETOOLONG);
    270 		}
    271 		/* name must start with a '/' but not contain one. */
    272 		if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
    273 			free(ret, M_SEM);
    274 			return (EINVAL);
    275 		}
    276 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
    277 		strlcpy(ret->ks_name, name, len + 1);
    278 	} else
    279 		ret->ks_name = NULL;
    280 	ret->ks_mode = mode;
    281 	ret->ks_value = value;
    282 	ret->ks_ref = 1;
    283 	ret->ks_waiters = 0;
    284 	ret->ks_uid = uc->cr_uid;
    285 	ret->ks_gid = uc->cr_gid;
    286 	simple_lock_init(&ret->ks_interlock);
    287 
    288 	simple_lock(&ksem_slock);
    289 	if (nsems >= SEM_MAX) {
    290 		simple_unlock(&ksem_slock);
    291 		if (ret->ks_name != NULL)
    292 			free(ret->ks_name, M_SEM);
    293 		free(ret, M_SEM);
    294 		return (ENFILE);
    295 	}
    296 	nsems++;
    297 	simple_unlock(&ksem_slock);
    298 
    299 	*ksret = ret;
    300 	return (0);
    301 }
    302 
    303 int
    304 sys__ksem_init(struct lwp *l, void *v, register_t *retval)
    305 {
    306 	struct sys__ksem_init_args /* {
    307 		unsigned int value;
    308 		semid_t *idp;
    309 	} */ *uap = v;
    310 	struct ksem *ks;
    311 	semid_t id;
    312 	int error;
    313 
    314 	/* Note the mode does not matter for anonymous semaphores. */
    315 	error = ksem_create(l->l_proc, NULL, &ks, 0, SCARG(uap, value));
    316 	if (error)
    317 		return (error);
    318 	id = SEM_TO_ID(ks);
    319 	error = copyout(&id, SCARG(uap, idp), sizeof(id));
    320 	if (error) {
    321 		simple_lock(&ks->ks_interlock);
    322 		ksem_delref(ks);
    323 		return (error);
    324 	}
    325 
    326 	ksem_add_proc(l->l_proc, ks);
    327 
    328 	return (0);
    329 }
    330 
    331 int
    332 sys__ksem_open(struct lwp *l, void *v, register_t *retval)
    333 {
    334 	struct sys__ksem_open_args /* {
    335 		const char *name;
    336 		int oflag;
    337 		mode_t mode;
    338 		unsigned int value;
    339 		semid_t *idp;
    340 	} */ *uap = v;
    341 	char name[SEM_MAX_NAMELEN + 1];
    342 	size_t done;
    343 	int error;
    344 	struct ksem *ksnew, *ks;
    345 	semid_t id;
    346 
    347 	error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
    348 	if (error)
    349 		return (error);
    350 
    351 	ksnew = NULL;
    352 	simple_lock(&ksem_slock);
    353 	ks = ksem_lookup_byname(name);
    354 
    355 	/* Found one? */
    356 	if (ks != NULL) {
    357 		/* Check for exclusive create. */
    358 		if (SCARG(uap, oflag) & O_EXCL) {
    359 			simple_unlock(&ks->ks_interlock);
    360 			simple_unlock(&ksem_slock);
    361 			return (EEXIST);
    362 		}
    363  found_one:
    364 		/*
    365 		 * Verify permissions.  If we can access it, add
    366 		 * this process's reference.
    367 		 */
    368 		LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    369 		error = ksem_perm(l->l_proc, ks);
    370 		if (error == 0)
    371 			ksem_addref(ks);
    372 		simple_unlock(&ks->ks_interlock);
    373 		simple_unlock(&ksem_slock);
    374 		if (error)
    375 			return (error);
    376 
    377 		id = SEM_TO_ID(ks);
    378 		error = copyout(&id, SCARG(uap, idp), sizeof(id));
    379 		if (error) {
    380 			simple_lock(&ks->ks_interlock);
    381 			ksem_delref(ks);
    382 			return (error);
    383 		}
    384 
    385 		ksem_add_proc(l->l_proc, ks);
    386 
    387 		return (0);
    388 	}
    389 
    390 	/*
    391 	 * didn't ask for creation? error.
    392 	 */
    393 	if ((SCARG(uap, oflag) & O_CREAT) == 0) {
    394 		simple_unlock(&ksem_slock);
    395 		return (ENOENT);
    396 	}
    397 
    398 	/*
    399 	 * We may block during creation, so drop the lock.
    400 	 */
    401 	simple_unlock(&ksem_slock);
    402 	error = ksem_create(l->l_proc, name, &ksnew, SCARG(uap, mode),
    403 	    SCARG(uap, value));
    404 	if (error != 0)
    405 		return (error);
    406 
    407 	id = SEM_TO_ID(ksnew);
    408 	error = copyout(&id, SCARG(uap, idp), sizeof(id));
    409 	if (error) {
    410 		free(ksnew->ks_name, M_SEM);
    411 		ksnew->ks_name = NULL;
    412 
    413 		simple_lock(&ksnew->ks_interlock);
    414 		ksem_delref(ksnew);
    415 		return (error);
    416 	}
    417 
    418 	/*
    419 	 * We need to make sure we haven't lost a race while
    420 	 * allocating during creation.
    421 	 */
    422 	simple_lock(&ksem_slock);
    423 	if ((ks = ksem_lookup_byname(name)) != NULL) {
    424 		if (SCARG(uap, oflag) & O_EXCL) {
    425 			simple_unlock(&ks->ks_interlock);
    426 			simple_unlock(&ksem_slock);
    427 
    428 			free(ksnew->ks_name, M_SEM);
    429 			ksnew->ks_name = NULL;
    430 
    431 			simple_lock(&ksnew->ks_interlock);
    432 			ksem_delref(ksnew);
    433 			return (EEXIST);
    434 		}
    435 		goto found_one;
    436 	} else {
    437 		/* ksnew already has its initial reference. */
    438 		LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
    439 		simple_unlock(&ksem_slock);
    440 
    441 		ksem_add_proc(l->l_proc, ksnew);
    442 	}
    443 	return (error);
    444 }
    445 
    446 /* We must have a read lock on the ksem_proc list! */
    447 static struct ksem *
    448 ksem_lookup_proc(struct ksem_proc *kp, semid_t id)
    449 {
    450 	struct ksem_ref *ksr;
    451 
    452 	LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
    453 		if (id == (semid_t) ksr->ksr_ksem) {
    454 			simple_lock(&ksr->ksr_ksem->ks_interlock);
    455 			return (ksr->ksr_ksem);
    456 		}
    457 	}
    458 
    459 	return (NULL);
    460 }
    461 
    462 int
    463 sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
    464 {
    465 	struct sys__ksem_unlink_args /* {
    466 		const char *name;
    467 	} */ *uap = v;
    468 	char name[SEM_MAX_NAMELEN + 1], *cp;
    469 	size_t done;
    470 	struct ksem *ks;
    471 	int error;
    472 
    473 	error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
    474 	if (error)
    475 		return error;
    476 
    477 	simple_lock(&ksem_slock);
    478 	ks = ksem_lookup_byname(name);
    479 	if (ks == NULL) {
    480 		simple_unlock(&ksem_slock);
    481 		return (ENOENT);
    482 	}
    483 
    484 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    485 
    486 	LIST_REMOVE(ks, ks_entry);
    487 	cp = ks->ks_name;
    488 	ks->ks_name = NULL;
    489 
    490 	simple_unlock(&ksem_slock);
    491 
    492 	if (ks->ks_ref == 0)
    493 		ksem_free(ks);
    494 	else
    495 		simple_unlock(&ks->ks_interlock);
    496 
    497 	free(cp, M_SEM);
    498 
    499 	return (0);
    500 }
    501 
    502 int
    503 sys__ksem_close(struct lwp *l, void *v, register_t *retval)
    504 {
    505 	struct sys__ksem_close_args /* {
    506 		semid_t id;
    507 	} */ *uap = v;
    508 	struct ksem_proc *kp;
    509 	struct ksem_ref *ksr;
    510 	struct ksem *ks;
    511 
    512 	if ((kp = l->l_proc->p_ksems) == NULL)
    513 		return (EINVAL);
    514 
    515 	lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
    516 
    517 	ks = ksem_lookup_proc(kp, SCARG(uap, id));
    518 	if (ks == NULL) {
    519 		lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    520 		return (EINVAL);
    521 	}
    522 
    523 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    524 	if (ks->ks_name == NULL) {
    525 		simple_unlock(&ks->ks_interlock);
    526 		lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    527 		return (EINVAL);
    528 	}
    529 
    530 	ksr = ksem_drop_proc(kp, ks);
    531 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    532 	free(ksr, M_SEM);
    533 
    534 	return (0);
    535 }
    536 
    537 int
    538 sys__ksem_post(struct lwp *l, void *v, register_t *retval)
    539 {
    540 	struct sys__ksem_post_args /* {
    541 		semid_t id;
    542 	} */ *uap = v;
    543 	struct ksem_proc *kp;
    544 	struct ksem *ks;
    545 	int error;
    546 
    547 	if ((kp = l->l_proc->p_ksems) == NULL)
    548 		return (EINVAL);
    549 
    550 	lockmgr(&kp->kp_lock, LK_SHARED, NULL);
    551 	ks = ksem_lookup_proc(kp, SCARG(uap, id));
    552 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    553 	if (ks == NULL)
    554 		return (EINVAL);
    555 
    556 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    557 	if (ks->ks_value == SEM_VALUE_MAX) {
    558 		error = EOVERFLOW;
    559 		goto out;
    560 	}
    561 	++ks->ks_value;
    562 	if (ks->ks_waiters)
    563 		wakeup(ks);
    564 	error = 0;
    565  out:
    566 	simple_unlock(&ks->ks_interlock);
    567 	return (error);
    568 }
    569 
    570 static int
    571 ksem_wait(struct lwp *l, semid_t id, int tryflag)
    572 {
    573 	struct ksem_proc *kp;
    574 	struct ksem *ks;
    575 	int error;
    576 
    577 	if ((kp = l->l_proc->p_ksems) == NULL)
    578 		return (EINVAL);
    579 
    580 	lockmgr(&kp->kp_lock, LK_SHARED, NULL);
    581 	ks = ksem_lookup_proc(kp, id);
    582 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    583 	if (ks == NULL)
    584 		return (EINVAL);
    585 
    586 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    587 	ksem_addref(ks);
    588 	while (ks->ks_value == 0) {
    589 		ks->ks_waiters++;
    590 		error = tryflag ? EAGAIN : ltsleep(ks, PCATCH, "psem", 0,
    591 		    &ks->ks_interlock);
    592 		ks->ks_waiters--;
    593 		if (error)
    594 			goto out;
    595 	}
    596 	ks->ks_value--;
    597 	error = 0;
    598  out:
    599 	ksem_delref(ks);
    600 	return (error);
    601 }
    602 
    603 int
    604 sys__ksem_wait(struct lwp *l, void *v, register_t *retval)
    605 {
    606 	struct sys__ksem_wait_args /* {
    607 		semid_t id;
    608 	} */ *uap = v;
    609 
    610 	return ksem_wait(l, SCARG(uap, id), 0);
    611 }
    612 
    613 int
    614 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval)
    615 {
    616 	struct sys__ksem_trywait_args /* {
    617 		semid_t id;
    618 	} */ *uap = v;
    619 
    620 	return ksem_wait(l, SCARG(uap, id), 1);
    621 }
    622 
    623 int
    624 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval)
    625 {
    626 	struct sys__ksem_getvalue_args /* {
    627 		semid_t id;
    628 		unsigned int *value;
    629 	} */ *uap = v;
    630 	struct ksem_proc *kp;
    631 	struct ksem *ks;
    632 	unsigned int val;
    633 
    634 	if ((kp = l->l_proc->p_ksems) == NULL)
    635 		return (EINVAL);
    636 
    637 	lockmgr(&kp->kp_lock, LK_SHARED, NULL);
    638 	ks = ksem_lookup_proc(kp, SCARG(uap, id));
    639 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    640 	if (ks == NULL)
    641 		return (EINVAL);
    642 
    643 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    644 	val = ks->ks_value;
    645 	simple_unlock(&ks->ks_interlock);
    646 
    647 	return (copyout(&val, SCARG(uap, value), sizeof(val)));
    648 }
    649 
    650 int
    651 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
    652 {
    653 	struct sys__ksem_destroy_args /*{
    654 		semid_t id;
    655 	} */ *uap = v;
    656 	struct ksem_proc *kp;
    657 	struct ksem_ref *ksr;
    658 	struct ksem *ks;
    659 
    660 	if ((kp = l->l_proc->p_ksems) == NULL)
    661 		return (EINVAL);
    662 
    663 	lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
    664 
    665 	ks = ksem_lookup_proc(kp, SCARG(uap, id));
    666 	if (ks == NULL) {
    667 		lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    668 		return (EINVAL);
    669 	}
    670 
    671 	LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
    672 
    673 	/*
    674 	 * XXX This misses named semaphores which have been unlink'd,
    675 	 * XXX but since behavior of destroying a named semaphore is
    676 	 * XXX undefined, this is technically allowed.
    677 	 */
    678 	if (ks->ks_name != NULL) {
    679 		simple_unlock(&ks->ks_interlock);
    680 		lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    681 		return (EINVAL);
    682 	}
    683 
    684 	if (ks->ks_waiters) {
    685 		simple_unlock(&ks->ks_interlock);
    686 		lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    687 		return (EBUSY);
    688 	}
    689 
    690 	ksr = ksem_drop_proc(kp, ks);
    691 	lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
    692 	free(ksr, M_SEM);
    693 
    694 	return (0);
    695 }
    696 
    697 static void
    698 ksem_forkhook(struct proc *p2, struct proc *p1)
    699 {
    700 	struct ksem_proc *kp1, *kp2;
    701 	struct ksem_ref *ksr, *ksr1;
    702 
    703 	if ((kp1 = p1->p_ksems) == NULL) {
    704 		p2->p_ksems = NULL;
    705 		return;
    706 	}
    707 
    708 	p2->p_ksems = kp2 = ksem_proc_alloc();
    709 
    710 	lockmgr(&kp1->kp_lock, LK_SHARED, NULL);
    711 
    712 	if (!LIST_EMPTY(&kp1->kp_ksems)) {
    713 		LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) {
    714 			ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
    715 			ksr1->ksr_ksem = ksr->ksr_ksem;
    716 			simple_lock(&ksr->ksr_ksem->ks_interlock);
    717 			ksem_addref(ksr->ksr_ksem);
    718 			simple_unlock(&ksr->ksr_ksem->ks_interlock);
    719 			LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list);
    720 		}
    721 	}
    722 
    723 	lockmgr(&kp1->kp_lock, LK_RELEASE, NULL);
    724 }
    725 
    726 static void
    727 ksem_exithook(struct lwp *l, void *arg)
    728 {
    729 	struct ksem_proc *kp;
    730 	struct ksem_ref *ksr;
    731 
    732 	if ((kp = l->l_proc->p_ksems) == NULL)
    733 		return;
    734 
    735 	/* Don't bother locking; process is dying. */
    736 
    737 	while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) {
    738 		LIST_REMOVE(ksr, ksr_list);
    739 		simple_lock(&ksr->ksr_ksem->ks_interlock);
    740 		ksem_delref(ksr->ksr_ksem);
    741 		free(ksr, M_SEM);
    742 	}
    743 }
    744 
    745 void
    746 ksem_init(void)
    747 {
    748 
    749 	simple_lock_init(&ksem_slock);
    750 	exithook_establish(ksem_exithook, NULL);
    751 	exechook_establish(ksem_exithook, NULL);
    752 	forkhook_establish(ksem_forkhook);
    753 }
    754