xref: /netbsd-src/sys/kern/uipc_sem.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1 /*	$NetBSD: uipc_sem.c,v 1.38 2012/03/13 18:40:55 elad 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.38 2012/03/13 18:40:55 elad 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 typedef struct ksem {
92 	LIST_ENTRY(ksem)	ks_entry;	/* global list entry */
93 	kmutex_t		ks_lock;	/* lock on this ksem */
94 	kcondvar_t		ks_cv;		/* condition variable */
95 	u_int			ks_ref;		/* number of references */
96 	u_int			ks_value;	/* current value */
97 	u_int			ks_waiters;	/* number of waiters */
98 	char *			ks_name;	/* name, if named */
99 	size_t			ks_namelen;	/* length of name */
100 	int			ks_flags;	/* for KS_UNLINKED */
101 	mode_t			ks_mode;	/* protection bits */
102 	uid_t			ks_uid;		/* creator uid */
103 	gid_t			ks_gid;		/* creator gid */
104 } ksem_t;
105 
106 static kmutex_t		ksem_lock	__cacheline_aligned;
107 static LIST_HEAD(,ksem)	ksem_head	__cacheline_aligned;
108 static u_int		nsems_total	__cacheline_aligned;
109 static u_int		nsems		__cacheline_aligned;
110 
111 static kauth_listener_t	ksem_listener;
112 
113 static int		ksem_sysinit(void);
114 static int		ksem_sysfini(bool);
115 static int		ksem_modcmd(modcmd_t, void *);
116 static int		ksem_close_fop(file_t *);
117 
118 static const struct fileops semops = {
119 	.fo_read = fbadop_read,
120 	.fo_write = fbadop_write,
121 	.fo_ioctl = fbadop_ioctl,
122 	.fo_fcntl = fnullop_fcntl,
123 	.fo_poll = fnullop_poll,
124 	.fo_stat = fbadop_stat,
125 	.fo_close = ksem_close_fop,
126 	.fo_kqfilter = fnullop_kqfilter,
127 	.fo_restart = fnullop_restart,
128 };
129 
130 static const struct syscall_package ksem_syscalls[] = {
131 	{ SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init },
132 	{ SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open },
133 	{ SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink },
134 	{ SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close },
135 	{ SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post },
136 	{ SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait },
137 	{ SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait },
138 	{ SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue },
139 	{ SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy },
140 	{ SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait },
141 	{ 0, 0, NULL },
142 };
143 
144 static int
145 ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
146     void *arg0, void *arg1, void *arg2, void *arg3)
147 {
148 	ksem_t *ks;
149 	mode_t mode;
150 
151 	if (action != KAUTH_SYSTEM_SEMAPHORE)
152 		return KAUTH_RESULT_DEFER;
153 
154 	ks = arg1;
155 	mode = ks->ks_mode;
156 
157 	if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) ||
158 	    (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) ||
159 	    (mode & S_IWOTH) != 0)
160 		return KAUTH_RESULT_ALLOW;
161 
162 	return KAUTH_RESULT_DEFER;
163 }
164 
165 static int
166 ksem_sysinit(void)
167 {
168 	int error;
169 
170 	mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE);
171 	LIST_INIT(&ksem_head);
172 	nsems_total = 0;
173 	nsems = 0;
174 
175 	error = syscall_establish(NULL, ksem_syscalls);
176 	if (error) {
177 		(void)ksem_sysfini(false);
178 	}
179 
180 	ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
181 	    ksem_listener_cb, NULL);
182 
183 	return error;
184 }
185 
186 static int
187 ksem_sysfini(bool interface)
188 {
189 	int error;
190 
191 	if (interface) {
192 		error = syscall_disestablish(NULL, ksem_syscalls);
193 		if (error != 0) {
194 			return error;
195 		}
196 		/*
197 		 * Make sure that no semaphores are in use.  Note: semops
198 		 * must be unused at this point.
199 		 */
200 		if (nsems_total) {
201 			error = syscall_establish(NULL, ksem_syscalls);
202 			KASSERT(error == 0);
203 			return EBUSY;
204 		}
205 	}
206 	kauth_unlisten_scope(ksem_listener);
207 	mutex_destroy(&ksem_lock);
208 	return 0;
209 }
210 
211 static int
212 ksem_modcmd(modcmd_t cmd, void *arg)
213 {
214 
215 	switch (cmd) {
216 	case MODULE_CMD_INIT:
217 		return ksem_sysinit();
218 
219 	case MODULE_CMD_FINI:
220 		return ksem_sysfini(true);
221 
222 	default:
223 		return ENOTTY;
224 	}
225 }
226 
227 static ksem_t *
228 ksem_lookup(const char *name)
229 {
230 	ksem_t *ks;
231 
232 	KASSERT(mutex_owned(&ksem_lock));
233 
234 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
235 		if (strcmp(ks->ks_name, name) == 0) {
236 			mutex_enter(&ks->ks_lock);
237 			return ks;
238 		}
239 	}
240 	return NULL;
241 }
242 
243 static int
244 ksem_perm(lwp_t *l, ksem_t *ks)
245 {
246 	kauth_cred_t uc = l->l_cred;
247 
248 	KASSERT(mutex_owned(&ks->ks_lock));
249 
250 	if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0)
251 		return EACCES;
252 
253 	return 0;
254 }
255 
256 /*
257  * ksem_get: get the semaphore from the descriptor.
258  *
259  * => locks the semaphore, if found.
260  * => holds a reference on the file descriptor.
261  */
262 static int
263 ksem_get(int fd, ksem_t **ksret)
264 {
265 	ksem_t *ks;
266 	file_t *fp;
267 
268 	fp = fd_getfile(fd);
269 	if (__predict_false(fp == NULL))
270 		return EINVAL;
271 	if (__predict_false(fp->f_type != DTYPE_SEM)) {
272 		fd_putfile(fd);
273 		return EINVAL;
274 	}
275 	ks = fp->f_data;
276 	mutex_enter(&ks->ks_lock);
277 
278 	*ksret = ks;
279 	return 0;
280 }
281 
282 /*
283  * ksem_create: allocate and setup a new semaphore structure.
284  */
285 static int
286 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val)
287 {
288 	ksem_t *ks;
289 	kauth_cred_t uc;
290 	char *kname;
291 	size_t len;
292 
293 	/* Pre-check for the limit. */
294 	if (nsems >= ksem_max) {
295 		return ENFILE;
296 	}
297 
298 	if (val > SEM_VALUE_MAX) {
299 		return EINVAL;
300 	}
301 
302 	if (name != NULL) {
303 		len = strlen(name);
304 		if (len > SEM_MAX_NAMELEN) {
305 			return ENAMETOOLONG;
306 		}
307 		/* Name must start with a '/' but not contain one. */
308 		if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
309 			return EINVAL;
310 		}
311 		kname = kmem_alloc(++len, KM_SLEEP);
312 		strlcpy(kname, name, len);
313 	} else {
314 		kname = NULL;
315 		len = 0;
316 	}
317 
318 	ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP);
319 	mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE);
320 	cv_init(&ks->ks_cv, "psem");
321 	ks->ks_name = kname;
322 	ks->ks_namelen = len;
323 	ks->ks_mode = mode;
324 	ks->ks_value = val;
325 	ks->ks_ref = 1;
326 
327 	uc = l->l_cred;
328 	ks->ks_uid = kauth_cred_geteuid(uc);
329 	ks->ks_gid = kauth_cred_getegid(uc);
330 
331 	atomic_inc_uint(&nsems_total);
332 	*ksret = ks;
333 	return 0;
334 }
335 
336 static void
337 ksem_free(ksem_t *ks)
338 {
339 
340 	KASSERT(!cv_has_waiters(&ks->ks_cv));
341 
342 	if (ks->ks_name) {
343 		KASSERT(ks->ks_namelen > 0);
344 		kmem_free(ks->ks_name, ks->ks_namelen);
345 	}
346 	mutex_destroy(&ks->ks_lock);
347 	cv_destroy(&ks->ks_cv);
348 	kmem_free(ks, sizeof(ksem_t));
349 
350 	atomic_dec_uint(&nsems_total);
351 }
352 
353 int
354 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
355     register_t *retval)
356 {
357 	/* {
358 		unsigned int value;
359 		intptr_t *idp;
360 	} */
361 
362 	return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
363 }
364 
365 int
366 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
367 {
368 	proc_t *p = l->l_proc;
369 	ksem_t *ks;
370 	file_t *fp;
371 	intptr_t id;
372 	int fd, error;
373 
374 	error = fd_allocfile(&fp, &fd);
375 	if (error) {
376 		return error;
377 	}
378 	fp->f_type = DTYPE_SEM;
379 	fp->f_flag = FREAD | FWRITE;
380 	fp->f_ops = &semops;
381 
382 	id = (intptr_t)fd;
383 	error = (*docopyout)(&id, idp, sizeof(*idp));
384 	if (error) {
385 		fd_abort(p, fp, fd);
386 		return error;
387 	}
388 
389 	/* Note the mode does not matter for anonymous semaphores. */
390 	error = ksem_create(l, NULL, &ks, 0, val);
391 	if (error) {
392 		fd_abort(p, fp, fd);
393 		return error;
394 	}
395 	fp->f_data = ks;
396 	fd_affix(p, fp, fd);
397 	return error;
398 }
399 
400 int
401 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap,
402     register_t *retval)
403 {
404 	/* {
405 		const char *name;
406 		int oflag;
407 		mode_t mode;
408 		unsigned int value;
409 		intptr_t *idp;
410 	} */
411 
412 	return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
413 	    SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
414 }
415 
416 int
417 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
418      unsigned int value, intptr_t *idp, copyout_t docopyout)
419 {
420 	char name[SEM_MAX_NAMELEN + 1];
421 	proc_t *p = l->l_proc;
422 	ksem_t *ksnew = NULL, *ks;
423 	file_t *fp;
424 	intptr_t id;
425 	int fd, error;
426 
427 	error = copyinstr(semname, name, sizeof(name), NULL);
428 	if (error) {
429 		return error;
430 	}
431 	error = fd_allocfile(&fp, &fd);
432 	if (error) {
433 		return error;
434 	}
435 	fp->f_type = DTYPE_SEM;
436 	fp->f_flag = FREAD | FWRITE;
437 	fp->f_ops = &semops;
438 
439 	/*
440 	 * The ID (file descriptor number) can be stored early.
441 	 * Note that zero is a special value for libpthread.
442 	 */
443 	id = (intptr_t)fd;
444 	error = (*docopyout)(&id, idp, sizeof(*idp));
445 	if (error) {
446 		goto err;
447 	}
448 
449 	if (oflag & O_CREAT) {
450 		/* Create a new semaphore. */
451 		error = ksem_create(l, name, &ksnew, mode, value);
452 		if (error) {
453 			goto err;
454 		}
455 		KASSERT(ksnew != NULL);
456 	}
457 
458 	/* Lookup for a semaphore with such name. */
459 	mutex_enter(&ksem_lock);
460 	ks = ksem_lookup(name);
461 	if (ks) {
462 		KASSERT(mutex_owned(&ks->ks_lock));
463 		mutex_exit(&ksem_lock);
464 
465 		/* Check for exclusive create. */
466 		if (oflag & O_EXCL) {
467 			mutex_exit(&ks->ks_lock);
468 			error = EEXIST;
469 			goto err;
470 		}
471 		/*
472 		 * Verify permissions.  If we can access it,
473 		 * add the reference of this thread.
474 		 */
475 		error = ksem_perm(l, ks);
476 		if (error == 0) {
477 			ks->ks_ref++;
478 		}
479 		mutex_exit(&ks->ks_lock);
480 		if (error) {
481 			goto err;
482 		}
483 	} else {
484 		/* Fail if not found and not creating. */
485 		if ((oflag & O_CREAT) == 0) {
486 			mutex_exit(&ksem_lock);
487 			KASSERT(ksnew == NULL);
488 			error = ENOENT;
489 			goto err;
490 		}
491 
492 		/* Check for the limit locked. */
493 		if (nsems >= ksem_max) {
494 			mutex_exit(&ksem_lock);
495 			error = ENFILE;
496 			goto err;
497 		}
498 
499 		/*
500 		 * Finally, insert semaphore into the list.
501 		 * Note: it already has the initial reference.
502 		 */
503 		ks = ksnew;
504 		LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
505 		nsems++;
506 		mutex_exit(&ksem_lock);
507 
508 		ksnew = NULL;
509 	}
510 	KASSERT(ks != NULL);
511 	fp->f_data = ks;
512 	fd_affix(p, fp, fd);
513 err:
514 	if (error) {
515 		fd_abort(p, fp, fd);
516 	}
517 	if (ksnew) {
518 		ksem_free(ksnew);
519 	}
520 	return error;
521 }
522 
523 int
524 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
525     register_t *retval)
526 {
527 	/* {
528 		intptr_t id;
529 	} */
530 	int fd = (int)SCARG(uap, id);
531 
532 	if (fd_getfile(fd) == NULL) {
533 		return EBADF;
534 	}
535 	return fd_close(fd);
536 }
537 
538 static int
539 ksem_close_fop(file_t *fp)
540 {
541 	ksem_t *ks = fp->f_data;
542 	bool destroy = false;
543 
544 	mutex_enter(&ks->ks_lock);
545 	KASSERT(ks->ks_ref > 0);
546 	if (--ks->ks_ref == 0) {
547 		/*
548 		 * Destroy if the last reference and semaphore is unnamed,
549 		 * or unlinked (for named semaphore).
550 		 */
551 		destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
552 	}
553 	mutex_exit(&ks->ks_lock);
554 
555 	if (destroy) {
556 		ksem_free(ks);
557 	}
558 	return 0;
559 }
560 
561 int
562 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
563     register_t *retval)
564 {
565 	/* {
566 		const char *name;
567 	} */
568 	char name[SEM_MAX_NAMELEN + 1];
569 	ksem_t *ks;
570 	u_int refcnt;
571 	int error;
572 
573 	error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL);
574 	if (error)
575 		return error;
576 
577 	mutex_enter(&ksem_lock);
578 	ks = ksem_lookup(name);
579 	if (ks == NULL) {
580 		mutex_exit(&ksem_lock);
581 		return ENOENT;
582 	}
583 	KASSERT(mutex_owned(&ks->ks_lock));
584 
585 	/* Verify permissions. */
586 	error = ksem_perm(l, ks);
587 	if (error) {
588 		mutex_exit(&ks->ks_lock);
589 		mutex_exit(&ksem_lock);
590 		return error;
591 	}
592 
593 	/* Remove from the global list. */
594 	LIST_REMOVE(ks, ks_entry);
595 	nsems--;
596 	mutex_exit(&ksem_lock);
597 
598 	refcnt = ks->ks_ref;
599 	if (refcnt) {
600 		/* Mark as unlinked, if there are references. */
601 		ks->ks_flags |= KS_UNLINKED;
602 	}
603 	mutex_exit(&ks->ks_lock);
604 
605 	if (refcnt == 0) {
606 		ksem_free(ks);
607 	}
608 	return 0;
609 }
610 
611 int
612 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
613     register_t *retval)
614 {
615 	/* {
616 		intptr_t id;
617 	} */
618 	int fd = (int)SCARG(uap, id), error;
619 	ksem_t *ks;
620 
621 	error = ksem_get(fd, &ks);
622 	if (error) {
623 		return error;
624 	}
625 	KASSERT(mutex_owned(&ks->ks_lock));
626 	if (ks->ks_value == SEM_VALUE_MAX) {
627 		error = EOVERFLOW;
628 		goto out;
629 	}
630 	ks->ks_value++;
631 	if (ks->ks_waiters) {
632 		cv_broadcast(&ks->ks_cv);
633 	}
634 out:
635 	mutex_exit(&ks->ks_lock);
636 	fd_putfile(fd);
637 	return error;
638 }
639 
640 int
641 do_ksem_wait(lwp_t *l, intptr_t id, bool try, struct timespec *abstime)
642 {
643 	int fd = (int)id, error, timeo;
644 	ksem_t *ks;
645 
646 	error = ksem_get(fd, &ks);
647 	if (error) {
648 		return error;
649 	}
650 	KASSERT(mutex_owned(&ks->ks_lock));
651 	while (ks->ks_value == 0) {
652 		ks->ks_waiters++;
653 		if (!try && abstime != NULL) {
654 			error = abstimeout2timo(abstime, &timeo);
655 			if (error != 0)
656 				goto out;
657 		} else {
658 			timeo = 0;
659 		}
660 		error = try ? EAGAIN : cv_timedwait_sig(&ks->ks_cv,
661 		    &ks->ks_lock, timeo);
662 		ks->ks_waiters--;
663 		if (error)
664 			goto out;
665 	}
666 	ks->ks_value--;
667 out:
668 	mutex_exit(&ks->ks_lock);
669 	fd_putfile(fd);
670 	return error;
671 }
672 
673 int
674 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
675     register_t *retval)
676 {
677 	/* {
678 		intptr_t id;
679 	} */
680 
681 	return do_ksem_wait(l, SCARG(uap, id), false, NULL);
682 }
683 
684 int
685 sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap,
686     register_t *retval)
687 {
688 	/* {
689 		intptr_t id;
690 		const struct timespec *abstime;
691 	} */
692 	struct timespec ts;
693 	int error;
694 
695 	error = copyin(SCARG(uap, abstime), &ts, sizeof(ts));
696 	if (error != 0)
697 		return error;
698 
699 	if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000)
700 		return EINVAL;
701 
702 	error = do_ksem_wait(l, SCARG(uap, id), false, &ts);
703 	if (error == EWOULDBLOCK)
704 		error = ETIMEDOUT;
705 	return error;
706 }
707 
708 int
709 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
710     register_t *retval)
711 {
712 	/* {
713 		intptr_t id;
714 	} */
715 
716 	return do_ksem_wait(l, SCARG(uap, id), true, NULL);
717 }
718 
719 int
720 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
721     register_t *retval)
722 {
723 	/* {
724 		intptr_t id;
725 		unsigned int *value;
726 	} */
727 	int fd = (int)SCARG(uap, id), error;
728 	ksem_t *ks;
729 	unsigned int val;
730 
731 	error = ksem_get(fd, &ks);
732 	if (error) {
733 		return error;
734 	}
735 	KASSERT(mutex_owned(&ks->ks_lock));
736 	val = ks->ks_value;
737 	mutex_exit(&ks->ks_lock);
738 	fd_putfile(fd);
739 
740 	return copyout(&val, SCARG(uap, value), sizeof(val));
741 }
742 
743 int
744 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
745     register_t *retval)
746 {
747 	/* {
748 		intptr_t id;
749 	} */
750 	int fd = (int)SCARG(uap, id), error;
751 	ksem_t *ks;
752 
753 	error = ksem_get(fd, &ks);
754 	if (error) {
755 		return error;
756 	}
757 	KASSERT(mutex_owned(&ks->ks_lock));
758 
759 	/* Operation is only for unnamed semaphores. */
760 	if (ks->ks_name != NULL) {
761 		error = EINVAL;
762 		goto out;
763 	}
764 	/* Cannot destroy if there are waiters. */
765 	if (ks->ks_waiters) {
766 		error = EBUSY;
767 		goto out;
768 	}
769 out:
770 	mutex_exit(&ks->ks_lock);
771 	if (error) {
772 		fd_putfile(fd);
773 		return error;
774 	}
775 	return fd_close(fd);
776 }
777