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