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