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