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