uipc_sem.c revision 1.38 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 (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.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