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