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