xref: /freebsd/sys/kern/uipc_sem.c (revision f4c5766baa461767ccb595252b1614f1ecc6f1a7)
1 /*
2  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include "opt_posix.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/sysproto.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 #include <sys/condvar.h>
40 #include <sys/sem.h>
41 #include <sys/uio.h>
42 #include <sys/syscall.h>
43 #include <sys/stat.h>
44 #include <sys/sysent.h>
45 #include <sys/sysctl.h>
46 #include <sys/malloc.h>
47 #include <sys/jail.h>
48 #include <sys/fcntl.h>
49 
50 #include <posix4/posix4.h>
51 #include <posix4/semaphore.h>
52 #include <posix4/_semaphore.h>
53 
54 static struct ksem *sem_lookup_byname(const char *name);
55 static int sem_create(struct thread *td, const char *name,
56     struct ksem **ksret, mode_t mode, unsigned int value);
57 static void sem_free(struct ksem *ksnew);
58 static int sem_perm(struct thread *td, struct ksem *ks);
59 static void sem_enter(struct proc *p, struct ksem *ks);
60 static int sem_leave(struct proc *p, struct ksem *ks);
61 static void sem_exithook(void *arg, struct proc *p);
62 static int sem_hasopen(struct thread *td, struct ksem *ks);
63 
64 static int kern_sem_close(struct thread *td, semid_t id);
65 static int kern_sem_post(struct thread *td, semid_t id);
66 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag);
67 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
68     semid_t *idp);
69 static int kern_sem_open(struct thread *td, int dir, const char *name,
70     int oflag, mode_t mode, unsigned int value, semid_t *idp);
71 static int kern_sem_unlink(struct thread *td, const char *name);
72 
73 #ifndef SEM_MAX
74 #define SEM_MAX	30
75 #endif
76 
77 #define SEM_MAX_NAMELEN	14
78 
79 #define SEM_TO_ID(x)	((intptr_t)(x))
80 #define ID_TO_SEM(x)	id_to_sem(x)
81 
82 struct kuser {
83 	pid_t ku_pid;
84 	LIST_ENTRY(kuser) ku_next;
85 };
86 
87 struct ksem {
88 	LIST_ENTRY(ksem) ks_entry;	/* global list entry */
89 	int ks_onlist;			/* boolean if on a list (ks_entry) */
90 	char *ks_name;			/* if named, this is the name */
91 	int ks_ref;			/* number of references */
92 	mode_t ks_mode;			/* protection bits */
93 	uid_t ks_uid;			/* creator uid */
94 	gid_t ks_gid;			/* creator gid */
95 	unsigned int ks_value;		/* current value */
96 	struct cv ks_cv;		/* waiters sleep here */
97 	int ks_waiters;			/* number of waiters */
98 	LIST_HEAD(, kuser) ks_users;	/* pids using this sem */
99 };
100 
101 /*
102  * available semaphores go here, this includes sem_init and any semaphores
103  * created via sem_open that have not yet been unlinked.
104  */
105 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
106 /*
107  * semaphores still in use but have been sem_unlink()'d go here.
108  */
109 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
110 
111 static struct mtx sem_lock;
112 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
113 
114 static int nsems = 0;
115 SYSCTL_DECL(_p1003_1b);
116 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
117 
118 static eventhandler_tag sem_exit_tag, sem_exec_tag;
119 
120 #ifdef SEM_DEBUG
121 #define DP(x)	printf x
122 #else
123 #define DP(x)
124 #endif
125 
126 static __inline
127 void
128 sem_ref(struct ksem *ks)
129 {
130 
131 	ks->ks_ref++;
132 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
133 }
134 
135 static __inline
136 void
137 sem_rel(struct ksem *ks)
138 {
139 
140 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
141 	if (--ks->ks_ref == 0)
142 		sem_free(ks);
143 }
144 
145 static __inline struct ksem *id_to_sem(semid_t id);
146 
147 static __inline
148 struct ksem *
149 id_to_sem(id)
150 	semid_t id;
151 {
152 	struct ksem *ks;
153 
154 	DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
155 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
156 		DP(("id_to_sem: ks = %p\n", ks));
157 		if (ks == (struct ksem *)id)
158 			return (ks);
159 	}
160 	return (NULL);
161 }
162 
163 static struct ksem *
164 sem_lookup_byname(name)
165 	const char *name;
166 {
167 	struct ksem *ks;
168 
169 	LIST_FOREACH(ks, &ksem_head, ks_entry)
170 		if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
171 			return (ks);
172 	return (NULL);
173 }
174 
175 static int
176 sem_create(td, name, ksret, mode, value)
177 	struct thread *td;
178 	const char *name;
179 	struct ksem **ksret;
180 	mode_t mode;
181 	unsigned int value;
182 {
183 	struct ksem *ret;
184 	struct proc *p;
185 	struct ucred *uc;
186 	size_t len;
187 	int error;
188 
189 	DP(("sem_create\n"));
190 	p = td->td_proc;
191 	uc = td->td_ucred;
192 	if (value > SEM_VALUE_MAX)
193 		return (EINVAL);
194 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
195 	if (name != NULL) {
196 		len = strlen(name);
197 		if (len > SEM_MAX_NAMELEN) {
198 			free(ret, M_SEM);
199 			return (ENAMETOOLONG);
200 		}
201 		/* name must start with a '/' but not contain one. */
202 		if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
203 			free(ret, M_SEM);
204 			return (EINVAL);
205 		}
206 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
207 		strcpy(ret->ks_name, name);
208 	} else {
209 		ret->ks_name = NULL;
210 	}
211 	ret->ks_mode = mode;
212 	ret->ks_value = value;
213 	ret->ks_ref = 1;
214 	ret->ks_waiters = 0;
215 	ret->ks_uid = uc->cr_uid;
216 	ret->ks_gid = uc->cr_gid;
217 	ret->ks_onlist = 0;
218 	cv_init(&ret->ks_cv, "sem");
219 	LIST_INIT(&ret->ks_users);
220 	if (name != NULL)
221 		sem_enter(td->td_proc, ret);
222 	*ksret = ret;
223 	mtx_lock(&sem_lock);
224 	if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
225 		sem_leave(td->td_proc, ret);
226 		sem_free(ret);
227 		error = ENFILE;
228 	} else {
229 		nsems++;
230 		error = 0;
231 	}
232 	mtx_unlock(&sem_lock);
233 	return (error);
234 }
235 
236 #ifndef _SYS_SYSPROTO_H_
237 struct ksem_init_args {
238 	unsigned int value;
239 	semid_t *idp;
240 };
241 int ksem_init(struct thread *td, struct ksem_init_args *uap);
242 #endif
243 int
244 ksem_init(td, uap)
245 	struct thread *td;
246 	struct ksem_init_args *uap;
247 {
248 	int error;
249 
250 	error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
251 	return (error);
252 }
253 
254 static int
255 kern_sem_init(td, dir, value, idp)
256 	struct thread *td;
257 	int dir;
258 	unsigned int value;
259 	semid_t *idp;
260 {
261 	struct ksem *ks;
262 	semid_t id;
263 	int error;
264 
265 	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
266 	if (error)
267 		return (error);
268 	id = SEM_TO_ID(ks);
269 	if (dir == UIO_USERSPACE) {
270 		error = copyout(&id, idp, sizeof(id));
271 		if (error) {
272 			mtx_lock(&sem_lock);
273 			sem_rel(ks);
274 			mtx_unlock(&sem_lock);
275 			return (error);
276 		}
277 	} else {
278 		*idp = id;
279 	}
280 	mtx_lock(&sem_lock);
281 	LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
282 	ks->ks_onlist = 1;
283 	mtx_unlock(&sem_lock);
284 	return (error);
285 }
286 
287 #ifndef _SYS_SYSPROTO_H_
288 struct ksem_open_args {
289 	char *name;
290 	int oflag;
291 	mode_t mode;
292 	unsigned int value;
293 	semid_t *idp;
294 };
295 int ksem_open(struct thread *td, struct ksem_open_args *uap);
296 #endif
297 int
298 ksem_open(td, uap)
299 	struct thread *td;
300 	struct ksem_open_args *uap;
301 {
302 	char name[SEM_MAX_NAMELEN + 1];
303 	size_t done;
304 	int error;
305 
306 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
307 	if (error)
308 		return (error);
309 	DP((">>> sem_open start\n"));
310 	error = kern_sem_open(td, UIO_USERSPACE,
311 	    name, uap->oflag, uap->mode, uap->value, uap->idp);
312 	DP(("<<< sem_open end\n"));
313 	return (error);
314 }
315 
316 static int
317 kern_sem_open(td, dir, name, oflag, mode, value, idp)
318 	struct thread *td;
319 	int dir;
320 	const char *name;
321 	int oflag;
322 	mode_t mode;
323 	unsigned int value;
324 	semid_t *idp;
325 {
326 	struct ksem *ksnew, *ks;
327 	int error;
328 	semid_t id;
329 
330 	ksnew = NULL;
331 	mtx_lock(&sem_lock);
332 	ks = sem_lookup_byname(name);
333 	/*
334 	 * If we found it but O_EXCL is set, error.
335 	 */
336 	if (ks != NULL && (oflag & O_EXCL) != 0) {
337 		mtx_unlock(&sem_lock);
338 		return (EEXIST);
339 	}
340 	/*
341 	 * If we didn't find it...
342 	 */
343 	if (ks == NULL) {
344 		/*
345 		 * didn't ask for creation? error.
346 		 */
347 		if ((oflag & O_CREAT) == 0) {
348 			mtx_unlock(&sem_lock);
349 			return (ENOENT);
350 		}
351 		/*
352 		 * We may block during creation, so drop the lock.
353 		 */
354 		mtx_unlock(&sem_lock);
355 		error = sem_create(td, name, &ksnew, mode, value);
356 		if (error != 0)
357 			return (error);
358 		id = SEM_TO_ID(ksnew);
359 		if (dir == UIO_USERSPACE) {
360 			DP(("about to copyout! %d to %p\n", id, idp));
361 			error = copyout(&id, idp, sizeof(id));
362 			if (error) {
363 				mtx_lock(&sem_lock);
364 				sem_leave(td->td_proc, ksnew);
365 				sem_rel(ksnew);
366 				mtx_unlock(&sem_lock);
367 				return (error);
368 			}
369 		} else {
370 			DP(("about to set! %d to %p\n", id, idp));
371 			*idp = id;
372 		}
373 		/*
374 		 * We need to make sure we haven't lost a race while
375 		 * allocating during creation.
376 		 */
377 		mtx_lock(&sem_lock);
378 		ks = sem_lookup_byname(name);
379 		if (ks != NULL) {
380 			/* we lost... */
381 			sem_leave(td->td_proc, ksnew);
382 			sem_rel(ksnew);
383 			/* we lost and we can't loose... */
384 			if ((oflag & O_EXCL) != 0) {
385 				mtx_unlock(&sem_lock);
386 				return (EEXIST);
387 			}
388 		} else {
389 			DP(("sem_create: about to add to list...\n"));
390 			LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
391 			DP(("sem_create: setting list bit...\n"));
392 			ksnew->ks_onlist = 1;
393 			DP(("sem_create: done, about to unlock...\n"));
394 		}
395 		mtx_unlock(&sem_lock);
396 	} else {
397 		/*
398 		 * if we aren't the creator, then enforce permissions.
399 		 */
400 		error = sem_perm(td, ks);
401 		if (!error)
402 			sem_ref(ks);
403 		mtx_unlock(&sem_lock);
404 		if (error)
405 			return (error);
406 		id = SEM_TO_ID(ks);
407 		if (dir == UIO_USERSPACE) {
408 			error = copyout(&id, idp, sizeof(id));
409 			if (error) {
410 				mtx_lock(&sem_lock);
411 				sem_rel(ks);
412 				mtx_unlock(&sem_lock);
413 				return (error);
414 			}
415 		} else {
416 			*idp = id;
417 		}
418 		sem_enter(td->td_proc, ks);
419 		mtx_lock(&sem_lock);
420 		sem_rel(ks);
421 		mtx_unlock(&sem_lock);
422 	}
423 	return (error);
424 }
425 
426 static int
427 sem_perm(td, ks)
428 	struct thread *td;
429 	struct ksem *ks;
430 {
431 	struct ucred *uc;
432 
433 	uc = td->td_ucred;
434 	DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
435 	    uc->cr_uid, uc->cr_gid,
436 	     ks->ks_uid, ks->ks_gid, ks->ks_mode));
437 	if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
438 	    (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
439 	    (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
440 		return (0);
441 	return (EPERM);
442 }
443 
444 static void
445 sem_free(struct ksem *ks)
446 {
447 
448 	nsems--;
449 	if (ks->ks_onlist)
450 		LIST_REMOVE(ks, ks_entry);
451 	if (ks->ks_name != NULL)
452 		free(ks->ks_name, M_SEM);
453 	cv_destroy(&ks->ks_cv);
454 	free(ks, M_SEM);
455 }
456 
457 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
458 
459 static __inline struct kuser *
460 sem_getuser(p, ks)
461 	struct proc *p;
462 	struct ksem *ks;
463 {
464 	struct kuser *k;
465 
466 	LIST_FOREACH(k, &ks->ks_users, ku_next)
467 		if (k->ku_pid == p->p_pid)
468 			return (k);
469 	return (NULL);
470 }
471 
472 static int
473 sem_hasopen(td, ks)
474 	struct thread *td;
475 	struct ksem *ks;
476 {
477 
478 	return ((ks->ks_name == NULL && sem_perm(td, ks))
479 	    || sem_getuser(td->td_proc, ks) != NULL);
480 }
481 
482 static int
483 sem_leave(p, ks)
484 	struct proc *p;
485 	struct ksem *ks;
486 {
487 	struct kuser *k;
488 
489 	DP(("sem_leave: ks = %p\n", ks));
490 	k = sem_getuser(p, ks);
491 	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
492 	if (k != NULL) {
493 		LIST_REMOVE(k, ku_next);
494 		sem_rel(ks);
495 		DP(("sem_leave: about to free k\n"));
496 		free(k, M_SEM);
497 		DP(("sem_leave: returning\n"));
498 		return (0);
499 	}
500 	return (EINVAL);
501 }
502 
503 static void
504 sem_enter(p, ks)
505 	struct proc *p;
506 	struct ksem *ks;
507 {
508 	struct kuser *ku, *k;
509 
510 	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
511 	ku->ku_pid = p->p_pid;
512 	mtx_lock(&sem_lock);
513 	k = sem_getuser(p, ks);
514 	if (k != NULL) {
515 		mtx_unlock(&sem_lock);
516 		free(ku, M_TEMP);
517 		return;
518 	}
519 	LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
520 	sem_ref(ks);
521 	mtx_unlock(&sem_lock);
522 }
523 
524 #ifndef _SYS_SYSPROTO_H_
525 struct ksem_unlink_args {
526 	char *name;
527 };
528 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
529 #endif
530 
531 int
532 ksem_unlink(td, uap)
533 	struct thread *td;
534 	struct ksem_unlink_args *uap;
535 {
536 	char name[SEM_MAX_NAMELEN + 1];
537 	size_t done;
538 	int error;
539 
540 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
541 	return (error ? error :
542 	    kern_sem_unlink(td, name));
543 }
544 
545 static int
546 kern_sem_unlink(td, name)
547 	struct thread *td;
548 	const char *name;
549 {
550 	struct ksem *ks;
551 	int error;
552 
553 	mtx_lock(&sem_lock);
554 	ks = sem_lookup_byname(name);
555 	if (ks == NULL)
556 		error = ENOENT;
557 	else
558 		error = sem_perm(td, ks);
559 	DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
560 	if (error == 0) {
561 		LIST_REMOVE(ks, ks_entry);
562 		LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
563 		sem_rel(ks);
564 	}
565 	mtx_unlock(&sem_lock);
566 	return (error);
567 }
568 
569 #ifndef _SYS_SYSPROTO_H_
570 struct ksem_close_args {
571 	semid_t id;
572 };
573 int ksem_close(struct thread *td, struct ksem_close_args *uap);
574 #endif
575 
576 int
577 ksem_close(struct thread *td, struct ksem_close_args *uap)
578 {
579 
580 	return (kern_sem_close(td, uap->id));
581 }
582 
583 static int
584 kern_sem_close(td, id)
585 	struct thread *td;
586 	semid_t id;
587 {
588 	struct ksem *ks;
589 	int error;
590 
591 	error = EINVAL;
592 	mtx_lock(&sem_lock);
593 	ks = ID_TO_SEM(id);
594 	/* this is not a valid operation for unnamed sems */
595 	if (ks != NULL && ks->ks_name != NULL)
596 		error = sem_leave(td->td_proc, ks);
597 	mtx_unlock(&sem_lock);
598 	return (error);
599 }
600 
601 #ifndef _SYS_SYSPROTO_H_
602 struct ksem_post_args {
603 	semid_t id;
604 };
605 int ksem_post(struct thread *td, struct ksem_post_args *uap);
606 #endif
607 int
608 ksem_post(td, uap)
609 	struct thread *td;
610 	struct ksem_post_args *uap;
611 {
612 
613 	return (kern_sem_post(td, uap->id));
614 }
615 
616 static int
617 kern_sem_post(td, id)
618 	struct thread *td;
619 	semid_t id;
620 {
621 	struct ksem *ks;
622 	int error;
623 
624 	mtx_lock(&sem_lock);
625 	ks = ID_TO_SEM(id);
626 	if (ks == NULL || !sem_hasopen(td, ks)) {
627 		error = EINVAL;
628 		goto err;
629 	}
630 	if (ks->ks_value == SEM_VALUE_MAX) {
631 		error = EOVERFLOW;
632 		goto err;
633 	}
634 	++ks->ks_value;
635 	if (ks->ks_waiters > 0)
636 		cv_signal(&ks->ks_cv);
637 	error = 0;
638 err:
639 	mtx_unlock(&sem_lock);
640 	return (error);
641 }
642 
643 #ifndef _SYS_SYSPROTO_H_
644 struct ksem_wait_args {
645 	semid_t id;
646 };
647 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
648 #endif
649 
650 int
651 ksem_wait(td, uap)
652 	struct thread *td;
653 	struct ksem_wait_args *uap;
654 {
655 
656 	return (kern_sem_wait(td, uap->id, 0));
657 }
658 
659 #ifndef _SYS_SYSPROTO_H_
660 struct ksem_trywait_args {
661 	semid_t id;
662 };
663 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
664 #endif
665 int
666 ksem_trywait(td, uap)
667 	struct thread *td;
668 	struct ksem_trywait_args *uap;
669 {
670 
671 	return (kern_sem_wait(td, uap->id, 1));
672 }
673 
674 static int
675 kern_sem_wait(td, id, tryflag)
676 	struct thread *td;
677 	semid_t id;
678 	int tryflag;
679 {
680 	struct ksem *ks;
681 	int error;
682 
683 	DP((">>> kern_sem_wait entered!\n"));
684 	mtx_lock(&sem_lock);
685 	ks = ID_TO_SEM(id);
686 	if (ks == NULL) {
687 		DP(("kern_sem_wait ks == NULL\n"));
688 		error = EINVAL;
689 		goto err;
690 	}
691 	sem_ref(ks);
692 	if (!sem_hasopen(td, ks)) {
693 		DP(("kern_sem_wait hasopen failed\n"));
694 		error = EINVAL;
695 		goto err;
696 	}
697 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
698 	if (ks->ks_value == 0) {
699 		ks->ks_waiters++;
700 		error = tryflag ? EAGAIN : cv_wait_sig(&ks->ks_cv, &sem_lock);
701 		ks->ks_waiters--;
702 		if (error)
703 			goto err;
704 	}
705 	ks->ks_value--;
706 	error = 0;
707 err:
708 	if (ks != NULL)
709 		sem_rel(ks);
710 	mtx_unlock(&sem_lock);
711 	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
712 	return (error);
713 }
714 
715 #ifndef _SYS_SYSPROTO_H_
716 struct ksem_getvalue_args {
717 	semid_t id;
718 	int *val;
719 };
720 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
721 #endif
722 int
723 ksem_getvalue(td, uap)
724 	struct thread *td;
725 	struct ksem_getvalue_args *uap;
726 {
727 	struct ksem *ks;
728 	int error, val;
729 
730 	mtx_lock(&sem_lock);
731 	ks = ID_TO_SEM(uap->id);
732 	if (ks == NULL || !sem_hasopen(td, ks)) {
733 		mtx_unlock(&sem_lock);
734 		return (EINVAL);
735 	}
736 	val = ks->ks_value;
737 	mtx_unlock(&sem_lock);
738 	error = copyout(&val, uap->val, sizeof(val));
739 	return (error);
740 }
741 
742 #ifndef _SYS_SYSPROTO_H_
743 struct ksem_destroy_args {
744 	semid_t id;
745 };
746 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
747 #endif
748 int
749 ksem_destroy(td, uap)
750 	struct thread *td;
751 	struct ksem_destroy_args *uap;
752 {
753 	struct ksem *ks;
754 	int error;
755 
756 	mtx_lock(&sem_lock);
757 	ks = ID_TO_SEM(uap->id);
758 	if (ks == NULL || !sem_hasopen(td, ks) ||
759 	    ks->ks_name != NULL) {
760 		error = EINVAL;
761 		goto err;
762 	}
763 	if (ks->ks_waiters != 0) {
764 		error = EBUSY;
765 		goto err;
766 	}
767 	sem_rel(ks);
768 	error = 0;
769 err:
770 	mtx_unlock(&sem_lock);
771 	return (error);
772 }
773 
774 static void
775 sem_exithook(arg, p)
776 	void *arg;
777 	struct proc *p;
778 {
779 	struct ksem *ks, *ksnext;
780 
781 	mtx_lock(&sem_lock);
782 	ks = LIST_FIRST(&ksem_head);
783 	while (ks != NULL) {
784 		ksnext = LIST_NEXT(ks, ks_entry);
785 		sem_leave(p, ks);
786 		ks = ksnext;
787 	}
788 	ks = LIST_FIRST(&ksem_deadhead);
789 	while (ks != NULL) {
790 		ksnext = LIST_NEXT(ks, ks_entry);
791 		sem_leave(p, ks);
792 		ks = ksnext;
793 	}
794 	mtx_unlock(&sem_lock);
795 }
796 
797 static int
798 sem_modload(struct module *module, int cmd, void *arg)
799 {
800         int error = 0;
801 
802         switch (cmd) {
803         case MOD_LOAD:
804 		mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
805 		p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
806 		p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
807 		sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
808 		    NULL, EVENTHANDLER_PRI_ANY);
809 		sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
810 		    NULL, EVENTHANDLER_PRI_ANY);
811                 break;
812         case MOD_UNLOAD:
813 		if (nsems != 0) {
814 			error = EOPNOTSUPP;
815 			break;
816 		}
817 		EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
818 		EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
819 		mtx_destroy(&sem_lock);
820                 break;
821         case MOD_SHUTDOWN:
822                 break;
823         default:
824                 error = EINVAL;
825                 break;
826         }
827         return (error);
828 }
829 
830 static moduledata_t sem_mod = {
831         "sem",
832         &sem_modload,
833         NULL
834 };
835 
836 SYSCALL_MODULE_HELPER(ksem_init);
837 SYSCALL_MODULE_HELPER(ksem_open);
838 SYSCALL_MODULE_HELPER(ksem_unlink);
839 SYSCALL_MODULE_HELPER(ksem_close);
840 SYSCALL_MODULE_HELPER(ksem_post);
841 SYSCALL_MODULE_HELPER(ksem_wait);
842 SYSCALL_MODULE_HELPER(ksem_trywait);
843 SYSCALL_MODULE_HELPER(ksem_getvalue);
844 SYSCALL_MODULE_HELPER(ksem_destroy);
845 
846 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
847 MODULE_VERSION(sem, 1);
848