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