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