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