uipc_sem.c revision 1.36 1 /* $NetBSD: uipc_sem.c,v 1.36 2012/03/08 21:59:30 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.36 2012/03/08 21:59:30 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 EBADF;
246 }
247 if (__predict_false(fp->f_type != DTYPE_SEM)) {
248 fd_putfile(fd);
249 return EBADF;
250 }
251 ks = fp->f_data;
252 mutex_enter(&ks->ks_lock);
253
254 *ksret = ks;
255 return 0;
256 }
257
258 /*
259 * ksem_create: allocate and setup a new semaphore structure.
260 */
261 static int
262 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val)
263 {
264 ksem_t *ks;
265 kauth_cred_t uc;
266 char *kname;
267 size_t len;
268
269 /* Pre-check for the limit. */
270 if (nsems >= ksem_max) {
271 return ENFILE;
272 }
273
274 if (val > SEM_VALUE_MAX) {
275 return EINVAL;
276 }
277
278 if (name != NULL) {
279 len = strlen(name);
280 if (len > SEM_MAX_NAMELEN) {
281 return ENAMETOOLONG;
282 }
283 /* Name must start with a '/' but not contain one. */
284 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
285 return EINVAL;
286 }
287 kname = kmem_alloc(++len, KM_SLEEP);
288 strlcpy(kname, name, len);
289 } else {
290 kname = NULL;
291 len = 0;
292 }
293
294 ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP);
295 mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE);
296 cv_init(&ks->ks_cv, "psem");
297 ks->ks_name = kname;
298 ks->ks_namelen = len;
299 ks->ks_mode = mode;
300 ks->ks_value = val;
301 ks->ks_ref = 1;
302
303 uc = l->l_cred;
304 ks->ks_uid = kauth_cred_geteuid(uc);
305 ks->ks_gid = kauth_cred_getegid(uc);
306
307 atomic_inc_uint(&nsems_total);
308 *ksret = ks;
309 return 0;
310 }
311
312 static void
313 ksem_free(ksem_t *ks)
314 {
315
316 KASSERT(!cv_has_waiters(&ks->ks_cv));
317
318 if (ks->ks_name) {
319 KASSERT(ks->ks_namelen > 0);
320 kmem_free(ks->ks_name, ks->ks_namelen);
321 }
322 mutex_destroy(&ks->ks_lock);
323 cv_destroy(&ks->ks_cv);
324 kmem_free(ks, sizeof(ksem_t));
325
326 atomic_dec_uint(&nsems_total);
327 }
328
329 int
330 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
331 register_t *retval)
332 {
333 /* {
334 unsigned int value;
335 intptr_t *idp;
336 } */
337
338 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
339 }
340
341 int
342 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
343 {
344 proc_t *p = l->l_proc;
345 ksem_t *ks;
346 file_t *fp;
347 intptr_t id;
348 int fd, error;
349
350 error = fd_allocfile(&fp, &fd);
351 if (error) {
352 return error;
353 }
354 fp->f_type = DTYPE_SEM;
355 fp->f_flag = FREAD | FWRITE;
356 fp->f_ops = &semops;
357
358 id = (intptr_t)fd;
359 error = (*docopyout)(&id, idp, sizeof(*idp));
360 if (error) {
361 fd_abort(p, fp, fd);
362 return error;
363 }
364
365 /* Note the mode does not matter for anonymous semaphores. */
366 error = ksem_create(l, NULL, &ks, 0, val);
367 if (error) {
368 fd_abort(p, fp, fd);
369 return error;
370 }
371 fp->f_data = ks;
372 fd_affix(p, fp, fd);
373 return error;
374 }
375
376 int
377 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap,
378 register_t *retval)
379 {
380 /* {
381 const char *name;
382 int oflag;
383 mode_t mode;
384 unsigned int value;
385 intptr_t *idp;
386 } */
387
388 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
389 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
390 }
391
392 int
393 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
394 unsigned int value, intptr_t *idp, copyout_t docopyout)
395 {
396 char name[SEM_MAX_NAMELEN + 1];
397 proc_t *p = l->l_proc;
398 ksem_t *ksnew = NULL, *ks;
399 file_t *fp;
400 intptr_t id;
401 int fd, error;
402
403 error = copyinstr(semname, name, sizeof(name), NULL);
404 if (error) {
405 return error;
406 }
407 error = fd_allocfile(&fp, &fd);
408 if (error) {
409 return error;
410 }
411 fp->f_type = DTYPE_SEM;
412 fp->f_flag = FREAD | FWRITE;
413 fp->f_ops = &semops;
414
415 /*
416 * The ID (file descriptor number) can be stored early.
417 * Note that zero is a special value for libpthread.
418 */
419 id = (intptr_t)fd;
420 error = (*docopyout)(&id, idp, sizeof(*idp));
421 if (error) {
422 goto err;
423 }
424
425 if (oflag & O_CREAT) {
426 /* Create a new semaphore. */
427 error = ksem_create(l, name, &ksnew, mode, value);
428 if (error) {
429 goto err;
430 }
431 KASSERT(ksnew != NULL);
432 }
433
434 /* Lookup for a semaphore with such name. */
435 mutex_enter(&ksem_lock);
436 ks = ksem_lookup(name);
437 if (ks) {
438 KASSERT(mutex_owned(&ks->ks_lock));
439 mutex_exit(&ksem_lock);
440
441 /* Check for exclusive create. */
442 if (oflag & O_EXCL) {
443 mutex_exit(&ks->ks_lock);
444 error = EEXIST;
445 goto err;
446 }
447 /*
448 * Verify permissions. If we can access it,
449 * add the reference of this thread.
450 */
451 error = ksem_perm(l, ks);
452 if (error == 0) {
453 ks->ks_ref++;
454 }
455 mutex_exit(&ks->ks_lock);
456 if (error) {
457 goto err;
458 }
459 } else {
460 /* Fail if not found and not creating. */
461 if ((oflag & O_CREAT) == 0) {
462 mutex_exit(&ksem_lock);
463 KASSERT(ksnew == NULL);
464 error = ENOENT;
465 goto err;
466 }
467
468 /* Check for the limit locked. */
469 if (nsems >= ksem_max) {
470 mutex_exit(&ksem_lock);
471 error = ENFILE;
472 goto err;
473 }
474
475 /*
476 * Finally, insert semaphore into the list.
477 * Note: it already has the initial reference.
478 */
479 ks = ksnew;
480 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
481 nsems++;
482 mutex_exit(&ksem_lock);
483
484 ksnew = NULL;
485 }
486 KASSERT(ks != NULL);
487 fp->f_data = ks;
488 fd_affix(p, fp, fd);
489 err:
490 if (error) {
491 fd_abort(p, fp, fd);
492 }
493 if (ksnew) {
494 ksem_free(ksnew);
495 }
496 return error;
497 }
498
499 int
500 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
501 register_t *retval)
502 {
503 /* {
504 intptr_t id;
505 } */
506 int fd = (int)SCARG(uap, id);
507
508 if (fd_getfile(fd) == NULL) {
509 return EBADF;
510 }
511 return fd_close(fd);
512 }
513
514 static int
515 ksem_close_fop(file_t *fp)
516 {
517 ksem_t *ks = fp->f_data;
518 bool destroy = false;
519
520 mutex_enter(&ks->ks_lock);
521 KASSERT(ks->ks_ref > 0);
522 if (--ks->ks_ref == 0) {
523 /*
524 * Destroy if the last reference and semaphore is unnamed,
525 * or unlinked (for named semaphore).
526 */
527 destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
528 }
529 mutex_exit(&ks->ks_lock);
530
531 if (destroy) {
532 ksem_free(ks);
533 }
534 return 0;
535 }
536
537 int
538 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
539 register_t *retval)
540 {
541 /* {
542 const char *name;
543 } */
544 char name[SEM_MAX_NAMELEN + 1];
545 ksem_t *ks;
546 u_int refcnt;
547 int error;
548
549 error = copyinstr(SCARG(uap, name), name, sizeof(name), NULL);
550 if (error)
551 return error;
552
553 mutex_enter(&ksem_lock);
554 ks = ksem_lookup(name);
555 if (ks == NULL) {
556 mutex_exit(&ksem_lock);
557 return ENOENT;
558 }
559 KASSERT(mutex_owned(&ks->ks_lock));
560
561 /* Verify permissions. */
562 error = ksem_perm(l, ks);
563 if (error) {
564 mutex_exit(&ks->ks_lock);
565 mutex_exit(&ksem_lock);
566 return error;
567 }
568
569 /* Remove from the global list. */
570 LIST_REMOVE(ks, ks_entry);
571 nsems--;
572 mutex_exit(&ksem_lock);
573
574 refcnt = ks->ks_ref;
575 if (refcnt) {
576 /* Mark as unlinked, if there are references. */
577 ks->ks_flags |= KS_UNLINKED;
578 }
579 mutex_exit(&ks->ks_lock);
580
581 if (refcnt == 0) {
582 ksem_free(ks);
583 }
584 return 0;
585 }
586
587 int
588 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
589 register_t *retval)
590 {
591 /* {
592 intptr_t id;
593 } */
594 int fd = (int)SCARG(uap, id), error;
595 ksem_t *ks;
596
597 error = ksem_get(fd, &ks);
598 if (error) {
599 return error;
600 }
601 KASSERT(mutex_owned(&ks->ks_lock));
602 if (ks->ks_value == SEM_VALUE_MAX) {
603 error = EOVERFLOW;
604 goto out;
605 }
606 ks->ks_value++;
607 if (ks->ks_waiters) {
608 cv_broadcast(&ks->ks_cv);
609 }
610 out:
611 mutex_exit(&ks->ks_lock);
612 fd_putfile(fd);
613 return error;
614 }
615
616 int
617 do_ksem_wait(lwp_t *l, intptr_t id, bool try, struct timespec *abstime)
618 {
619 int fd = (int)id, error, timeo;
620 ksem_t *ks;
621
622 error = ksem_get(fd, &ks);
623 if (error) {
624 return error;
625 }
626 KASSERT(mutex_owned(&ks->ks_lock));
627 while (ks->ks_value == 0) {
628 ks->ks_waiters++;
629 if (!try && abstime != NULL) {
630 error = abstimeout2timo(abstime, &timeo);
631 if (error != 0)
632 goto out;
633 } else {
634 timeo = 0;
635 }
636 error = try ? EAGAIN : cv_timedwait_sig(&ks->ks_cv,
637 &ks->ks_lock, timeo);
638 ks->ks_waiters--;
639 if (error)
640 goto out;
641 }
642 ks->ks_value--;
643 out:
644 mutex_exit(&ks->ks_lock);
645 fd_putfile(fd);
646 return error;
647 }
648
649 int
650 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
651 register_t *retval)
652 {
653 /* {
654 intptr_t id;
655 } */
656
657 return do_ksem_wait(l, SCARG(uap, id), false, NULL);
658 }
659
660 int
661 sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap,
662 register_t *retval)
663 {
664 /* {
665 intptr_t id;
666 const struct timespec *abstime;
667 } */
668 struct timespec ts;
669 int error;
670
671 error = copyin(SCARG(uap, abstime), &ts, sizeof(ts));
672 if (error != 0)
673 return error;
674
675 if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000)
676 return EINVAL;
677
678 error = do_ksem_wait(l, SCARG(uap, id), false, &ts);
679 if (error == EWOULDBLOCK)
680 error = ETIMEDOUT;
681 return error;
682 }
683
684 int
685 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
686 register_t *retval)
687 {
688 /* {
689 intptr_t id;
690 } */
691
692 return do_ksem_wait(l, SCARG(uap, id), true, NULL);
693 }
694
695 int
696 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
697 register_t *retval)
698 {
699 /* {
700 intptr_t id;
701 unsigned int *value;
702 } */
703 int fd = (int)SCARG(uap, id), error;
704 ksem_t *ks;
705 unsigned int val;
706
707 error = ksem_get(fd, &ks);
708 if (error) {
709 return error;
710 }
711 KASSERT(mutex_owned(&ks->ks_lock));
712 val = ks->ks_value;
713 mutex_exit(&ks->ks_lock);
714 fd_putfile(fd);
715
716 return copyout(&val, SCARG(uap, value), sizeof(val));
717 }
718
719 int
720 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
721 register_t *retval)
722 {
723 /* {
724 intptr_t id;
725 } */
726 int fd = (int)SCARG(uap, id), error;
727 ksem_t *ks;
728
729 error = ksem_get(fd, &ks);
730 if (error) {
731 return error;
732 }
733 KASSERT(mutex_owned(&ks->ks_lock));
734
735 /* Operation is only for unnamed semaphores. */
736 if (ks->ks_name != NULL) {
737 error = EINVAL;
738 goto out;
739 }
740 /* Cannot destroy if there are waiters. */
741 if (ks->ks_waiters) {
742 error = EBUSY;
743 goto out;
744 }
745 out:
746 mutex_exit(&ks->ks_lock);
747 if (error) {
748 fd_putfile(fd);
749 return error;
750 }
751 return fd_close(fd);
752 }
753