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