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