uipc_sem.c revision 1.42 1 /* $NetBSD: uipc_sem.c,v 1.42 2014/09/05 09:20:59 matt 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 (at) 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.42 2014/09/05 09:20:59 matt 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_ksem;
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_ksem = 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_ksem = 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_ksem;
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_ksem;
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_ksem;
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_p, 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_p && 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_p ? 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