xref: /netbsd-src/sys/kern/uipc_sem.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: uipc_sem.c,v 1.40 2013/03/29 01:08:17 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Mindaugas Rasiukevicius.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 /*
59  * Implementation of POSIX semaphore.
60  */
61 
62 #include <sys/cdefs.h>
63 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.40 2013/03/29 01:08:17 christos Exp $");
64 
65 #include <sys/param.h>
66 #include <sys/kernel.h>
67 
68 #include <sys/atomic.h>
69 #include <sys/proc.h>
70 #include <sys/ksem.h>
71 #include <sys/syscall.h>
72 #include <sys/stat.h>
73 #include <sys/kmem.h>
74 #include <sys/fcntl.h>
75 #include <sys/file.h>
76 #include <sys/filedesc.h>
77 #include <sys/kauth.h>
78 #include <sys/module.h>
79 #include <sys/mount.h>
80 #include <sys/syscall.h>
81 #include <sys/syscallargs.h>
82 #include <sys/syscallvar.h>
83 
84 MODULE(MODULE_CLASS_MISC, ksem, NULL);
85 
86 #define	SEM_MAX_NAMELEN		14
87 #define	SEM_VALUE_MAX		(~0U)
88 
89 #define	KS_UNLINKED		0x01
90 
91 static kmutex_t		ksem_lock	__cacheline_aligned;
92 static LIST_HEAD(,ksem)	ksem_head	__cacheline_aligned;
93 static u_int		nsems_total	__cacheline_aligned;
94 static u_int		nsems		__cacheline_aligned;
95 
96 static kauth_listener_t	ksem_listener;
97 
98 static int		ksem_sysinit(void);
99 static int		ksem_sysfini(bool);
100 static int		ksem_modcmd(modcmd_t, void *);
101 static int		ksem_close_fop(file_t *);
102 static int		ksem_stat_fop(file_t *, struct stat *);
103 static int		ksem_read_fop(file_t *, off_t *, struct uio *,
104     kauth_cred_t, int);
105 
106 static const struct fileops semops = {
107 	.fo_read = ksem_read_fop,
108 	.fo_write = fbadop_write,
109 	.fo_ioctl = fbadop_ioctl,
110 	.fo_fcntl = fnullop_fcntl,
111 	.fo_poll = fnullop_poll,
112 	.fo_stat = ksem_stat_fop,
113 	.fo_close = ksem_close_fop,
114 	.fo_kqfilter = fnullop_kqfilter,
115 	.fo_restart = fnullop_restart,
116 };
117 
118 static const struct syscall_package ksem_syscalls[] = {
119 	{ SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init },
120 	{ SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open },
121 	{ SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink },
122 	{ SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close },
123 	{ SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post },
124 	{ SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait },
125 	{ SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait },
126 	{ SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue },
127 	{ SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy },
128 	{ SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait },
129 	{ 0, 0, NULL },
130 };
131 
132 static int
133 ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
134     void *arg0, void *arg1, void *arg2, void *arg3)
135 {
136 	ksem_t *ks;
137 	mode_t mode;
138 
139 	if (action != KAUTH_SYSTEM_SEMAPHORE)
140 		return KAUTH_RESULT_DEFER;
141 
142 	ks = arg1;
143 	mode = ks->ks_mode;
144 
145 	if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) ||
146 	    (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) ||
147 	    (mode & S_IWOTH) != 0)
148 		return KAUTH_RESULT_ALLOW;
149 
150 	return KAUTH_RESULT_DEFER;
151 }
152 
153 static int
154 ksem_sysinit(void)
155 {
156 	int error;
157 
158 	mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE);
159 	LIST_INIT(&ksem_head);
160 	nsems_total = 0;
161 	nsems = 0;
162 
163 	error = syscall_establish(NULL, ksem_syscalls);
164 	if (error) {
165 		(void)ksem_sysfini(false);
166 	}
167 
168 	ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
169 	    ksem_listener_cb, NULL);
170 
171 	return error;
172 }
173 
174 static int
175 ksem_sysfini(bool interface)
176 {
177 	int error;
178 
179 	if (interface) {
180 		error = syscall_disestablish(NULL, ksem_syscalls);
181 		if (error != 0) {
182 			return error;
183 		}
184 		/*
185 		 * Make sure that no semaphores are in use.  Note: semops
186 		 * must be unused at this point.
187 		 */
188 		if (nsems_total) {
189 			error = syscall_establish(NULL, ksem_syscalls);
190 			KASSERT(error == 0);
191 			return EBUSY;
192 		}
193 	}
194 	kauth_unlisten_scope(ksem_listener);
195 	mutex_destroy(&ksem_lock);
196 	return 0;
197 }
198 
199 static int
200 ksem_modcmd(modcmd_t cmd, void *arg)
201 {
202 
203 	switch (cmd) {
204 	case MODULE_CMD_INIT:
205 		return ksem_sysinit();
206 
207 	case MODULE_CMD_FINI:
208 		return ksem_sysfini(true);
209 
210 	default:
211 		return ENOTTY;
212 	}
213 }
214 
215 static ksem_t *
216 ksem_lookup(const char *name)
217 {
218 	ksem_t *ks;
219 
220 	KASSERT(mutex_owned(&ksem_lock));
221 
222 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
223 		if (strcmp(ks->ks_name, name) == 0) {
224 			mutex_enter(&ks->ks_lock);
225 			return ks;
226 		}
227 	}
228 	return NULL;
229 }
230 
231 static int
232 ksem_perm(lwp_t *l, ksem_t *ks)
233 {
234 	kauth_cred_t uc = l->l_cred;
235 
236 	KASSERT(mutex_owned(&ks->ks_lock));
237 
238 	if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0)
239 		return EACCES;
240 
241 	return 0;
242 }
243 
244 /*
245  * ksem_get: get the semaphore from the descriptor.
246  *
247  * => locks the semaphore, if found.
248  * => holds a reference on the file descriptor.
249  */
250 static int
251 ksem_get(int fd, ksem_t **ksret)
252 {
253 	ksem_t *ks;
254 	file_t *fp;
255 
256 	fp = fd_getfile(fd);
257 	if (__predict_false(fp == NULL))
258 		return EINVAL;
259 	if (__predict_false(fp->f_type != DTYPE_SEM)) {
260 		fd_putfile(fd);
261 		return EINVAL;
262 	}
263 	ks = fp->f_data;
264 	mutex_enter(&ks->ks_lock);
265 
266 	*ksret = ks;
267 	return 0;
268 }
269 
270 /*
271  * ksem_create: allocate and setup a new semaphore structure.
272  */
273 static int
274 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val)
275 {
276 	ksem_t *ks;
277 	kauth_cred_t uc;
278 	char *kname;
279 	size_t len;
280 
281 	/* Pre-check for the limit. */
282 	if (nsems >= ksem_max) {
283 		return ENFILE;
284 	}
285 
286 	if (val > SEM_VALUE_MAX) {
287 		return EINVAL;
288 	}
289 
290 	if (name != NULL) {
291 		len = strlen(name);
292 		if (len > SEM_MAX_NAMELEN) {
293 			return ENAMETOOLONG;
294 		}
295 		/* Name must start with a '/' but not contain one. */
296 		if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
297 			return EINVAL;
298 		}
299 		kname = kmem_alloc(++len, KM_SLEEP);
300 		strlcpy(kname, name, len);
301 	} else {
302 		kname = NULL;
303 		len = 0;
304 	}
305 
306 	ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP);
307 	mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE);
308 	cv_init(&ks->ks_cv, "psem");
309 	ks->ks_name = kname;
310 	ks->ks_namelen = len;
311 	ks->ks_mode = mode;
312 	ks->ks_value = val;
313 	ks->ks_ref = 1;
314 
315 	uc = l->l_cred;
316 	ks->ks_uid = kauth_cred_geteuid(uc);
317 	ks->ks_gid = kauth_cred_getegid(uc);
318 
319 	atomic_inc_uint(&nsems_total);
320 	*ksret = ks;
321 	return 0;
322 }
323 
324 static void
325 ksem_free(ksem_t *ks)
326 {
327 
328 	KASSERT(!cv_has_waiters(&ks->ks_cv));
329 
330 	if (ks->ks_name) {
331 		KASSERT(ks->ks_namelen > 0);
332 		kmem_free(ks->ks_name, ks->ks_namelen);
333 	}
334 	mutex_destroy(&ks->ks_lock);
335 	cv_destroy(&ks->ks_cv);
336 	kmem_free(ks, sizeof(ksem_t));
337 
338 	atomic_dec_uint(&nsems_total);
339 }
340 
341 int
342 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
343     register_t *retval)
344 {
345 	/* {
346 		unsigned int value;
347 		intptr_t *idp;
348 	} */
349 
350 	return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
351 }
352 
353 int
354 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
355 {
356 	proc_t *p = l->l_proc;
357 	ksem_t *ks;
358 	file_t *fp;
359 	intptr_t id;
360 	int fd, error;
361 
362 	error = fd_allocfile(&fp, &fd);
363 	if (error) {
364 		return error;
365 	}
366 	fp->f_type = DTYPE_SEM;
367 	fp->f_flag = FREAD | FWRITE;
368 	fp->f_ops = &semops;
369 
370 	id = (intptr_t)fd;
371 	error = (*docopyout)(&id, idp, sizeof(*idp));
372 	if (error) {
373 		fd_abort(p, fp, fd);
374 		return error;
375 	}
376 
377 	/* Note the mode does not matter for anonymous semaphores. */
378 	error = ksem_create(l, NULL, &ks, 0, val);
379 	if (error) {
380 		fd_abort(p, fp, fd);
381 		return error;
382 	}
383 	fp->f_data = ks;
384 	fd_affix(p, fp, fd);
385 	return error;
386 }
387 
388 int
389 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap,
390     register_t *retval)
391 {
392 	/* {
393 		const char *name;
394 		int oflag;
395 		mode_t mode;
396 		unsigned int value;
397 		intptr_t *idp;
398 	} */
399 
400 	return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
401 	    SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
402 }
403 
404 int
405 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
406      unsigned int value, intptr_t *idp, copyout_t docopyout)
407 {
408 	char name[SEM_MAX_NAMELEN + 1];
409 	proc_t *p = l->l_proc;
410 	ksem_t *ksnew = NULL, *ks;
411 	file_t *fp;
412 	intptr_t id;
413 	int fd, error;
414 
415 	error = copyinstr(semname, name, sizeof(name), NULL);
416 	if (error) {
417 		return error;
418 	}
419 	error = fd_allocfile(&fp, &fd);
420 	if (error) {
421 		return error;
422 	}
423 	fp->f_type = DTYPE_SEM;
424 	fp->f_flag = FREAD | FWRITE;
425 	fp->f_ops = &semops;
426 
427 	/*
428 	 * The ID (file descriptor number) can be stored early.
429 	 * Note that zero is a special value for libpthread.
430 	 */
431 	id = (intptr_t)fd;
432 	error = (*docopyout)(&id, idp, sizeof(*idp));
433 	if (error) {
434 		goto err;
435 	}
436 
437 	if (oflag & O_CREAT) {
438 		/* Create a new semaphore. */
439 		error = ksem_create(l, name, &ksnew, mode, value);
440 		if (error) {
441 			goto err;
442 		}
443 		KASSERT(ksnew != NULL);
444 	}
445 
446 	/* Lookup for a semaphore with such name. */
447 	mutex_enter(&ksem_lock);
448 	ks = ksem_lookup(name);
449 	if (ks) {
450 		KASSERT(mutex_owned(&ks->ks_lock));
451 		mutex_exit(&ksem_lock);
452 
453 		/* Check for exclusive create. */
454 		if (oflag & O_EXCL) {
455 			mutex_exit(&ks->ks_lock);
456 			error = EEXIST;
457 			goto err;
458 		}
459 		/*
460 		 * Verify permissions.  If we can access it,
461 		 * add the reference of this thread.
462 		 */
463 		error = ksem_perm(l, ks);
464 		if (error == 0) {
465 			ks->ks_ref++;
466 		}
467 		mutex_exit(&ks->ks_lock);
468 		if (error) {
469 			goto err;
470 		}
471 	} else {
472 		/* Fail if not found and not creating. */
473 		if ((oflag & O_CREAT) == 0) {
474 			mutex_exit(&ksem_lock);
475 			KASSERT(ksnew == NULL);
476 			error = ENOENT;
477 			goto err;
478 		}
479 
480 		/* Check for the limit locked. */
481 		if (nsems >= ksem_max) {
482 			mutex_exit(&ksem_lock);
483 			error = ENFILE;
484 			goto err;
485 		}
486 
487 		/*
488 		 * Finally, insert semaphore into the list.
489 		 * Note: it already has the initial reference.
490 		 */
491 		ks = ksnew;
492 		LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
493 		nsems++;
494 		mutex_exit(&ksem_lock);
495 
496 		ksnew = NULL;
497 	}
498 	KASSERT(ks != NULL);
499 	fp->f_data = ks;
500 	fd_affix(p, fp, fd);
501 err:
502 	if (error) {
503 		fd_abort(p, fp, fd);
504 	}
505 	if (ksnew) {
506 		ksem_free(ksnew);
507 	}
508 	return error;
509 }
510 
511 int
512 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
513     register_t *retval)
514 {
515 	/* {
516 		intptr_t id;
517 	} */
518 	int fd = (int)SCARG(uap, id);
519 
520 	if (fd_getfile(fd) == NULL) {
521 		return EBADF;
522 	}
523 	return fd_close(fd);
524 }
525 
526 static int
527 ksem_read_fop(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
528     int flags)
529 {
530 	size_t len;
531 	char *name;
532 	ksem_t *ks = fp->f_data;
533 
534 	mutex_enter(&ks->ks_lock);
535 	len = ks->ks_namelen;
536 	name = ks->ks_name;
537 	mutex_exit(&ks->ks_lock);
538 	if (name == NULL || len == 0)
539 		return 0;
540 	return uiomove(name, len, uio);
541 }
542 
543 static int
544 ksem_stat_fop(file_t *fp, struct stat *ub)
545 {
546 	ksem_t *ks = fp->f_data;
547 
548 	mutex_enter(&ks->ks_lock);
549 
550 	memset(ub, 0, sizeof(*ub));
551 
552 	ub->st_mode = ks->ks_mode | ((ks->ks_name && ks->ks_namelen)
553 	    ? _S_IFLNK : _S_IFREG);
554 	ub->st_uid = ks->ks_uid;
555 	ub->st_gid = ks->ks_gid;
556 	ub->st_size = ks->ks_value;
557 	ub->st_blocks = (ub->st_size) ? 1 : 0;
558 	ub->st_nlink = ks->ks_ref;
559 	ub->st_blksize = 4096;
560 
561 	nanotime(&ub->st_atimespec);
562 	ub->st_mtimespec = ub->st_ctimespec = ub->st_birthtimespec =
563 	    ub->st_atimespec;
564 
565 	/*
566 	 * Left as 0: st_dev, st_ino, st_rdev, st_flags, st_gen.
567 	 * XXX (st_dev, st_ino) should be unique.
568 	 */
569 	mutex_exit(&ks->ks_lock);
570 	return 0;
571 }
572 
573 static int
574 ksem_close_fop(file_t *fp)
575 {
576 	ksem_t *ks = fp->f_data;
577 	bool destroy = false;
578 
579 	mutex_enter(&ks->ks_lock);
580 	KASSERT(ks->ks_ref > 0);
581 	if (--ks->ks_ref == 0) {
582 		/*
583 		 * Destroy if the last reference and semaphore is unnamed,
584 		 * or unlinked (for named semaphore).
585 		 */
586 		destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
587 	}
588 	mutex_exit(&ks->ks_lock);
589 
590 	if (destroy) {
591 		ksem_free(ks);
592 	}
593 	return 0;
594 }
595 
596 int
597 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
598     register_t *retval)
599 {
600 	/* {
601 		const char *name;
602 	} */
603 	char name[SEM_MAX_NAMELEN + 1];
604 	ksem_t *ks;
605 	u_int refcnt;
606 	int error;
607 
608 	error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL);
609 	if (error)
610 		return error;
611 
612 	mutex_enter(&ksem_lock);
613 	ks = ksem_lookup(name);
614 	if (ks == NULL) {
615 		mutex_exit(&ksem_lock);
616 		return ENOENT;
617 	}
618 	KASSERT(mutex_owned(&ks->ks_lock));
619 
620 	/* Verify permissions. */
621 	error = ksem_perm(l, ks);
622 	if (error) {
623 		mutex_exit(&ks->ks_lock);
624 		mutex_exit(&ksem_lock);
625 		return error;
626 	}
627 
628 	/* Remove from the global list. */
629 	LIST_REMOVE(ks, ks_entry);
630 	nsems--;
631 	mutex_exit(&ksem_lock);
632 
633 	refcnt = ks->ks_ref;
634 	if (refcnt) {
635 		/* Mark as unlinked, if there are references. */
636 		ks->ks_flags |= KS_UNLINKED;
637 	}
638 	mutex_exit(&ks->ks_lock);
639 
640 	if (refcnt == 0) {
641 		ksem_free(ks);
642 	}
643 	return 0;
644 }
645 
646 int
647 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
648     register_t *retval)
649 {
650 	/* {
651 		intptr_t id;
652 	} */
653 	int fd = (int)SCARG(uap, id), error;
654 	ksem_t *ks;
655 
656 	error = ksem_get(fd, &ks);
657 	if (error) {
658 		return error;
659 	}
660 	KASSERT(mutex_owned(&ks->ks_lock));
661 	if (ks->ks_value == SEM_VALUE_MAX) {
662 		error = EOVERFLOW;
663 		goto out;
664 	}
665 	ks->ks_value++;
666 	if (ks->ks_waiters) {
667 		cv_broadcast(&ks->ks_cv);
668 	}
669 out:
670 	mutex_exit(&ks->ks_lock);
671 	fd_putfile(fd);
672 	return error;
673 }
674 
675 int
676 do_ksem_wait(lwp_t *l, intptr_t id, bool try, struct timespec *abstime)
677 {
678 	int fd = (int)id, error, timeo;
679 	ksem_t *ks;
680 
681 	error = ksem_get(fd, &ks);
682 	if (error) {
683 		return error;
684 	}
685 	KASSERT(mutex_owned(&ks->ks_lock));
686 	while (ks->ks_value == 0) {
687 		ks->ks_waiters++;
688 		if (!try && abstime != NULL) {
689 			error = ts2timo(CLOCK_REALTIME, TIMER_ABSTIME, abstime,
690 			    &timeo, NULL);
691 			if (error != 0)
692 				goto out;
693 		} else {
694 			timeo = 0;
695 		}
696 		error = try ? EAGAIN : cv_timedwait_sig(&ks->ks_cv,
697 		    &ks->ks_lock, timeo);
698 		ks->ks_waiters--;
699 		if (error)
700 			goto out;
701 	}
702 	ks->ks_value--;
703 out:
704 	mutex_exit(&ks->ks_lock);
705 	fd_putfile(fd);
706 	return error;
707 }
708 
709 int
710 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
711     register_t *retval)
712 {
713 	/* {
714 		intptr_t id;
715 	} */
716 
717 	return do_ksem_wait(l, SCARG(uap, id), false, NULL);
718 }
719 
720 int
721 sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap,
722     register_t *retval)
723 {
724 	/* {
725 		intptr_t id;
726 		const struct timespec *abstime;
727 	} */
728 	struct timespec ts;
729 	int error;
730 
731 	error = copyin(SCARG(uap, abstime), &ts, sizeof(ts));
732 	if (error != 0)
733 		return error;
734 
735 	if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000)
736 		return EINVAL;
737 
738 	error = do_ksem_wait(l, SCARG(uap, id), false, &ts);
739 	if (error == EWOULDBLOCK)
740 		error = ETIMEDOUT;
741 	return error;
742 }
743 
744 int
745 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
746     register_t *retval)
747 {
748 	/* {
749 		intptr_t id;
750 	} */
751 
752 	return do_ksem_wait(l, SCARG(uap, id), true, NULL);
753 }
754 
755 int
756 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
757     register_t *retval)
758 {
759 	/* {
760 		intptr_t id;
761 		unsigned int *value;
762 	} */
763 	int fd = (int)SCARG(uap, id), error;
764 	ksem_t *ks;
765 	unsigned int val;
766 
767 	error = ksem_get(fd, &ks);
768 	if (error) {
769 		return error;
770 	}
771 	KASSERT(mutex_owned(&ks->ks_lock));
772 	val = ks->ks_value;
773 	mutex_exit(&ks->ks_lock);
774 	fd_putfile(fd);
775 
776 	return copyout(&val, SCARG(uap, value), sizeof(val));
777 }
778 
779 int
780 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
781     register_t *retval)
782 {
783 	/* {
784 		intptr_t id;
785 	} */
786 	int fd = (int)SCARG(uap, id), error;
787 	ksem_t *ks;
788 
789 	error = ksem_get(fd, &ks);
790 	if (error) {
791 		return error;
792 	}
793 	KASSERT(mutex_owned(&ks->ks_lock));
794 
795 	/* Operation is only for unnamed semaphores. */
796 	if (ks->ks_name != NULL) {
797 		error = EINVAL;
798 		goto out;
799 	}
800 	/* Cannot destroy if there are waiters. */
801 	if (ks->ks_waiters) {
802 		error = EBUSY;
803 		goto out;
804 	}
805 out:
806 	mutex_exit(&ks->ks_lock);
807 	if (error) {
808 		fd_putfile(fd);
809 		return error;
810 	}
811 	return fd_close(fd);
812 }
813