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