uipc_sem.c revision 1.15.4.1 1 /* $NetBSD: uipc_sem.c,v 1.15.4.1 2006/09/11 00:20:01 ad Exp $ */
2
3 /*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of Wasabi Systems, Inc.
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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 2002 Alfred Perlstein <alfred (at) FreeBSD.org>
41 * All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 */
64
65 #include <sys/cdefs.h>
66 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.15.4.1 2006/09/11 00:20:01 ad Exp $");
67
68 #include "opt_posix.h"
69
70 #include <sys/param.h>
71 #include <sys/systm.h>
72 #include <sys/kernel.h>
73 #include <sys/proc.h>
74 #include <sys/lock.h>
75 #include <sys/ksem.h>
76 #include <sys/sa.h>
77 #include <sys/syscall.h>
78 #include <sys/stat.h>
79 #include <sys/malloc.h>
80 #include <sys/fcntl.h>
81 #include <sys/kauth.h>
82
83 #include <sys/mount.h>
84
85 #include <sys/syscallargs.h>
86
87 #ifndef SEM_MAX
88 #define SEM_MAX 30
89 #endif
90
91 #define SEM_MAX_NAMELEN 14
92 #define SEM_VALUE_MAX (~0U)
93 #define SEM_HASHTBL_SIZE 13
94
95 #define SEM_TO_ID(x) (((x)->ks_id))
96 #define SEM_HASH(id) ((id) % SEM_HASHTBL_SIZE)
97
98 MALLOC_DEFINE(M_SEM, "p1003_1b_sem", "p1003_1b semaphores");
99
100 /*
101 * Note: to read the ks_name member, you need either the ks_interlock
102 * or the ksem_slock. To write the ks_name member, you need both. Make
103 * sure the order is ksem_slock -> ks_interlock.
104 */
105 struct ksem {
106 LIST_ENTRY(ksem) ks_entry; /* global list entry */
107 LIST_ENTRY(ksem) ks_hash; /* hash list entry */
108 kmutex_t ks_interlock; /* lock on this ksem */
109 char *ks_name; /* if named, this is the name */
110 unsigned int ks_ref; /* number of references */
111 mode_t ks_mode; /* protection bits */
112 uid_t ks_uid; /* creator uid */
113 gid_t ks_gid; /* creator gid */
114 unsigned int ks_value; /* current value */
115 unsigned int ks_waiters; /* number of waiters */
116 semid_t ks_id; /* unique identifier */
117 };
118
119 struct ksem_ref {
120 LIST_ENTRY(ksem_ref) ksr_list;
121 struct ksem *ksr_ksem;
122 };
123
124 struct ksem_proc {
125 krwlock_t kp_lock;
126 LIST_HEAD(, ksem_ref) kp_ksems;
127 };
128
129 LIST_HEAD(ksem_list, ksem);
130
131 /*
132 * ksem_slock protects ksem_head and nsems. Only named semaphores go
133 * onto ksem_head.
134 */
135 static kmutex_t ksem_mutex;
136 static struct ksem_list ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
137 static struct ksem_list ksem_hash[SEM_HASHTBL_SIZE];
138 static int nsems = 0;
139
140 /*
141 * ksem_counter is the last assigned semid_t. It needs to be COMPAT_NETBSD32
142 * friendly, even though semid_t itself is defined as uintptr_t.
143 */
144 static uint32_t ksem_counter = 1;
145
146
147 static void
148 ksem_free(struct ksem *ks)
149 {
150
151 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
152
153 /*
154 * If the ksem is anonymous (or has been unlinked), then
155 * this is the end if its life.
156 */
157 if (ks->ks_name == NULL) {
158 mutex_exit(&ks->ks_interlock);
159
160 mutex_enter(&ksem_mutex);
161 nsems--;
162 LIST_REMOVE(ks, ks_hash);
163 mutex_exit(&ksem_mutex);
164
165 free(ks, M_SEM);
166 return;
167 }
168 mutex_exit(&ks->ks_interlock);
169 }
170
171 static inline void
172 ksem_addref(struct ksem *ks)
173 {
174
175 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
176 ks->ks_ref++;
177 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
178 }
179
180 static inline void
181 ksem_delref(struct ksem *ks)
182 {
183
184 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
185 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
186 if (--ks->ks_ref == 0) {
187 ksem_free(ks);
188 return;
189 }
190 mutex_exit(&ks->ks_interlock);
191 }
192
193 static struct ksem_proc *
194 ksem_proc_alloc(void)
195 {
196 struct ksem_proc *kp;
197
198 kp = malloc(sizeof(*kp), M_SEM, M_WAITOK);
199 rw_init(&kp->kp_lock);
200 LIST_INIT(&kp->kp_ksems);
201
202 return (kp);
203 }
204
205 static void
206 ksem_add_proc(struct proc *p, struct ksem *ks)
207 {
208 struct ksem_proc *kp;
209 struct ksem_ref *ksr;
210
211 if (p->p_ksems == NULL) {
212 kp = ksem_proc_alloc();
213 p->p_ksems = kp;
214 } else
215 kp = p->p_ksems;
216
217 ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
218 ksr->ksr_ksem = ks;
219
220 rw_enter(&kp->kp_lock, RW_WRITER);
221 LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list);
222 rw_exit(&kp->kp_lock);
223 }
224
225 /* We MUST have a write lock on the ksem_proc list! */
226 static struct ksem_ref *
227 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks)
228 {
229 struct ksem_ref *ksr;
230
231 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
232 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
233 if (ksr->ksr_ksem == ks) {
234 ksem_delref(ks);
235 LIST_REMOVE(ksr, ksr_list);
236 return (ksr);
237 }
238 }
239 #ifdef DIAGNOSTIC
240 panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks);
241 #endif
242 return (NULL);
243 }
244
245 static int
246 ksem_perm(struct lwp *l, struct ksem *ks)
247 {
248 kauth_cred_t uc;
249
250 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
251 uc = l->l_cred;
252 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
253 (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
254 (ks->ks_mode & S_IWOTH) != 0 ||
255 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, &l->l_acflag) == 0)
256 return (0);
257 return (EPERM);
258 }
259
260 static struct ksem *
261 ksem_lookup_byid(semid_t id)
262 {
263 struct ksem *ks;
264
265 LOCK_ASSERT(mutex_owned(&ksem_mutex));
266 LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) {
267 if (ks->ks_id == id)
268 return ks;
269 }
270 return NULL;
271 }
272
273 static struct ksem *
274 ksem_lookup_byname(const char *name)
275 {
276 struct ksem *ks;
277
278 LOCK_ASSERT(mutex_owned(&ksem_mutex));
279 LIST_FOREACH(ks, &ksem_head, ks_entry) {
280 if (strcmp(ks->ks_name, name) == 0) {
281 mutex_enter(&ks->ks_interlock);
282 return (ks);
283 }
284 }
285 return (NULL);
286 }
287
288 static int
289 ksem_create(struct lwp *l, const char *name, struct ksem **ksret,
290 mode_t mode, unsigned int value)
291 {
292 struct ksem *ret;
293 kauth_cred_t uc;
294 size_t len;
295
296 uc = l->l_cred;
297 if (value > SEM_VALUE_MAX)
298 return (EINVAL);
299 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
300 if (name != NULL) {
301 len = strlen(name);
302 if (len > SEM_MAX_NAMELEN) {
303 free(ret, M_SEM);
304 return (ENAMETOOLONG);
305 }
306 /* name must start with a '/' but not contain one. */
307 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
308 free(ret, M_SEM);
309 return (EINVAL);
310 }
311 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
312 strlcpy(ret->ks_name, name, len + 1);
313 } else
314 ret->ks_name = NULL;
315 ret->ks_mode = mode;
316 ret->ks_value = value;
317 ret->ks_ref = 1;
318 ret->ks_waiters = 0;
319 ret->ks_uid = kauth_cred_geteuid(uc);
320 ret->ks_gid = kauth_cred_getegid(uc);
321 mutex_init(&ret->ks_interlock, MUTEX_DEFAULT, IPL_NONE);
322
323 mutex_enter(&ksem_mutex);
324 if (nsems >= SEM_MAX) {
325 mutex_exit(&ksem_mutex);
326 if (ret->ks_name != NULL)
327 free(ret->ks_name, M_SEM);
328 free(ret, M_SEM);
329 return (ENFILE);
330 }
331 nsems++;
332 while (ksem_lookup_byid(ksem_counter) != NULL) {
333 ksem_counter++;
334 /* 0 is a special value for libpthread */
335 if (ksem_counter == 0)
336 ksem_counter++;
337 }
338 ret->ks_id = ksem_counter;
339 LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash);
340 mutex_exit(&ksem_mutex);
341
342 *ksret = ret;
343 return (0);
344 }
345
346 int
347 sys__ksem_init(struct lwp *l, void *v, register_t *retval)
348 {
349 struct sys__ksem_init_args /* {
350 unsigned int value;
351 semid_t *idp;
352 } */ *uap = v;
353
354 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
355 }
356
357 int
358 do_ksem_init(struct lwp *l, unsigned int value, semid_t *idp,
359 copyout_t docopyout)
360 {
361 struct ksem *ks;
362 semid_t id;
363 int error;
364
365 /* Note the mode does not matter for anonymous semaphores. */
366 error = ksem_create(l, NULL, &ks, 0, value);
367 if (error)
368 return (error);
369 id = SEM_TO_ID(ks);
370 error = (*docopyout)(&id, idp, sizeof(id));
371 if (error) {
372 mutex_enter(&ks->ks_interlock);
373 ksem_delref(ks);
374 return (error);
375 }
376
377 ksem_add_proc(l->l_proc, ks);
378
379 return (0);
380 }
381
382 int
383 sys__ksem_open(struct lwp *l, void *v, register_t *retval)
384 {
385 struct sys__ksem_open_args /* {
386 const char *name;
387 int oflag;
388 mode_t mode;
389 unsigned int value;
390 semid_t *idp;
391 } */ *uap = v;
392
393 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
394 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
395 }
396
397 int
398 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
399 unsigned int value, semid_t *idp, copyout_t docopyout)
400 {
401 char name[SEM_MAX_NAMELEN + 1];
402 size_t done;
403 int error;
404 struct ksem *ksnew, *ks;
405 semid_t id;
406
407 error = copyinstr(semname, name, sizeof(name), &done);
408 if (error)
409 return (error);
410
411 ksnew = NULL;
412 mutex_enter(&ksem_mutex);
413 ks = ksem_lookup_byname(name);
414
415 /* Found one? */
416 if (ks != NULL) {
417 /* Check for exclusive create. */
418 if (oflag & O_EXCL) {
419 mutex_exit(&ks->ks_interlock);
420 mutex_exit(&ksem_mutex);
421 return (EEXIST);
422 }
423 found_one:
424 /*
425 * Verify permissions. If we can access it, add
426 * this process's reference.
427 */
428 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
429 error = ksem_perm(l, ks);
430 if (error == 0)
431 ksem_addref(ks);
432 mutex_exit(&ks->ks_interlock);
433 mutex_exit(&ksem_mutex);
434 if (error)
435 return (error);
436
437 id = SEM_TO_ID(ks);
438 error = (*docopyout)(&id, idp, sizeof(id));
439 if (error) {
440 mutex_enter(&ks->ks_interlock);
441 ksem_delref(ks);
442 return (error);
443 }
444
445 ksem_add_proc(l->l_proc, ks);
446
447 return (0);
448 }
449
450 /*
451 * didn't ask for creation? error.
452 */
453 if ((oflag & O_CREAT) == 0) {
454 mutex_exit(&ksem_mutex);
455 return (ENOENT);
456 }
457
458 /*
459 * We may block during creation, so drop the lock.
460 */
461 mutex_exit(&ksem_mutex);
462 error = ksem_create(l, name, &ksnew, mode, value);
463 if (error != 0)
464 return (error);
465
466 id = SEM_TO_ID(ksnew);
467 error = (*docopyout)(&id, idp, sizeof(id));
468 if (error) {
469 free(ksnew->ks_name, M_SEM);
470 ksnew->ks_name = NULL;
471
472 mutex_enter(&ksnew->ks_interlock);
473 ksem_delref(ksnew);
474 return (error);
475 }
476
477 /*
478 * We need to make sure we haven't lost a race while
479 * allocating during creation.
480 */
481 mutex_enter(&ksem_mutex);
482 if ((ks = ksem_lookup_byname(name)) != NULL) {
483 if (oflag & O_EXCL) {
484 mutex_exit(&ks->ks_interlock);
485 mutex_exit(&ksem_mutex);
486
487 free(ksnew->ks_name, M_SEM);
488 ksnew->ks_name = NULL;
489
490 mutex_enter(&ksnew->ks_interlock);
491 ksem_delref(ksnew);
492 return (EEXIST);
493 }
494 goto found_one;
495 } else {
496 /* ksnew already has its initial reference. */
497 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
498 mutex_exit(&ksem_mutex);
499
500 ksem_add_proc(l->l_proc, ksnew);
501 }
502 return (error);
503 }
504
505 /* We must have a read lock on the ksem_proc list! */
506 static struct ksem *
507 ksem_lookup_proc(struct ksem_proc *kp, semid_t id)
508 {
509 struct ksem_ref *ksr;
510
511 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
512 if (id == SEM_TO_ID(ksr->ksr_ksem)) {
513 mutex_enter(&ksr->ksr_ksem->ks_interlock);
514 return (ksr->ksr_ksem);
515 }
516 }
517
518 return (NULL);
519 }
520
521 int
522 sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
523 {
524 struct sys__ksem_unlink_args /* {
525 const char *name;
526 } */ *uap = v;
527 char name[SEM_MAX_NAMELEN + 1], *cp;
528 size_t done;
529 struct ksem *ks;
530 int error;
531
532 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
533 if (error)
534 return error;
535
536 mutex_enter(&ksem_mutex);
537 ks = ksem_lookup_byname(name);
538 if (ks == NULL) {
539 mutex_exit(&ksem_mutex);
540 return (ENOENT);
541 }
542
543 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
544
545 LIST_REMOVE(ks, ks_entry);
546 cp = ks->ks_name;
547 ks->ks_name = NULL;
548
549 mutex_exit(&ksem_mutex);
550
551 if (ks->ks_ref == 0)
552 ksem_free(ks);
553 else
554 mutex_exit(&ks->ks_interlock);
555
556 free(cp, M_SEM);
557
558 return (0);
559 }
560
561 int
562 sys__ksem_close(struct lwp *l, void *v, register_t *retval)
563 {
564 struct sys__ksem_close_args /* {
565 semid_t id;
566 } */ *uap = v;
567 struct ksem_proc *kp;
568 struct ksem_ref *ksr;
569 struct ksem *ks;
570
571 if ((kp = l->l_proc->p_ksems) == NULL)
572 return (EINVAL);
573
574 rw_enter(&kp->kp_lock, RW_WRITER);
575
576 ks = ksem_lookup_proc(kp, SCARG(uap, id));
577 if (ks == NULL) {
578 rw_exit(&kp->kp_lock);
579 return (EINVAL);
580 }
581
582 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
583 if (ks->ks_name == NULL) {
584 mutex_exit(&ks->ks_interlock);
585 rw_exit(&kp->kp_lock);
586 return (EINVAL);
587 }
588
589 ksr = ksem_drop_proc(kp, ks);
590 rw_exit(&kp->kp_lock);
591 free(ksr, M_SEM);
592
593 return (0);
594 }
595
596 int
597 sys__ksem_post(struct lwp *l, void *v, register_t *retval)
598 {
599 struct sys__ksem_post_args /* {
600 semid_t id;
601 } */ *uap = v;
602 struct ksem_proc *kp;
603 struct ksem *ks;
604 int error;
605
606 if ((kp = l->l_proc->p_ksems) == NULL)
607 return (EINVAL);
608
609 rw_enter(&kp->kp_lock, RW_READER);
610 ks = ksem_lookup_proc(kp, SCARG(uap, id));
611 rw_exit(&kp->kp_lock);
612 if (ks == NULL)
613 return (EINVAL);
614
615 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
616 if (ks->ks_value == SEM_VALUE_MAX) {
617 error = EOVERFLOW;
618 goto out;
619 }
620 ++ks->ks_value;
621 if (ks->ks_waiters)
622 wakeup(ks);
623 error = 0;
624 out:
625 mutex_exit(&ks->ks_interlock);
626 return (error);
627 }
628
629 static int
630 ksem_wait(struct lwp *l, semid_t id, int tryflag)
631 {
632 struct ksem_proc *kp;
633 struct ksem *ks;
634 int error;
635
636 if ((kp = l->l_proc->p_ksems) == NULL)
637 return (EINVAL);
638
639 rw_enter(&kp->kp_lock, RW_READER);
640 ks = ksem_lookup_proc(kp, id);
641 rw_exit(&kp->kp_lock);
642 if (ks == NULL)
643 return (EINVAL);
644
645 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
646 ksem_addref(ks);
647 while (ks->ks_value == 0) {
648 ks->ks_waiters++;
649 error = tryflag ? EAGAIN : mtsleep(ks, PCATCH, "psem", 0,
650 &ks->ks_interlock);
651 ks->ks_waiters--;
652 if (error)
653 goto out;
654 }
655 ks->ks_value--;
656 error = 0;
657 out:
658 ksem_delref(ks);
659 return (error);
660 }
661
662 int
663 sys__ksem_wait(struct lwp *l, void *v, register_t *retval)
664 {
665 struct sys__ksem_wait_args /* {
666 semid_t id;
667 } */ *uap = v;
668
669 return ksem_wait(l, SCARG(uap, id), 0);
670 }
671
672 int
673 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval)
674 {
675 struct sys__ksem_trywait_args /* {
676 semid_t id;
677 } */ *uap = v;
678
679 return ksem_wait(l, SCARG(uap, id), 1);
680 }
681
682 int
683 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval)
684 {
685 struct sys__ksem_getvalue_args /* {
686 semid_t id;
687 unsigned int *value;
688 } */ *uap = v;
689 struct ksem_proc *kp;
690 struct ksem *ks;
691 unsigned int val;
692
693 if ((kp = l->l_proc->p_ksems) == NULL)
694 return (EINVAL);
695
696 rw_enter(&kp->kp_lock, RW_READER);
697 ks = ksem_lookup_proc(kp, SCARG(uap, id));
698 rw_exit(&kp->kp_lock);
699 if (ks == NULL)
700 return (EINVAL);
701
702 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
703 val = ks->ks_value;
704 mutex_exit(&ks->ks_interlock);
705
706 return (copyout(&val, SCARG(uap, value), sizeof(val)));
707 }
708
709 int
710 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
711 {
712 struct sys__ksem_destroy_args /*{
713 semid_t id;
714 } */ *uap = v;
715 struct ksem_proc *kp;
716 struct ksem_ref *ksr;
717 struct ksem *ks;
718
719 if ((kp = l->l_proc->p_ksems) == NULL)
720 return (EINVAL);
721
722 rw_enter(&kp->kp_lock, RW_WRITER);
723
724 ks = ksem_lookup_proc(kp, SCARG(uap, id));
725 if (ks == NULL) {
726 rw_exit(&kp->kp_lock);
727 return (EINVAL);
728 }
729
730 LOCK_ASSERT(mutex_owned(&ks->ks_interlock));
731
732 /*
733 * XXX This misses named semaphores which have been unlink'd,
734 * XXX but since behavior of destroying a named semaphore is
735 * XXX undefined, this is technically allowed.
736 */
737 if (ks->ks_name != NULL) {
738 mutex_exit(&ks->ks_interlock);
739 rw_exit(&kp->kp_lock);
740 return (EINVAL);
741 }
742
743 if (ks->ks_waiters) {
744 mutex_exit(&ks->ks_interlock);
745 rw_exit(&kp->kp_lock);
746 return (EBUSY);
747 }
748
749 ksr = ksem_drop_proc(kp, ks);
750 rw_exit(&kp->kp_lock);
751 free(ksr, M_SEM);
752
753 return (0);
754 }
755
756 static void
757 ksem_forkhook(struct proc *p2, struct proc *p1)
758 {
759 struct ksem_proc *kp1, *kp2;
760 struct ksem_ref *ksr, *ksr1;
761
762 if ((kp1 = p1->p_ksems) == NULL) {
763 p2->p_ksems = NULL;
764 return;
765 }
766
767 p2->p_ksems = kp2 = ksem_proc_alloc();
768
769 rw_enter(&kp1->kp_lock, RW_READER);
770
771 if (!LIST_EMPTY(&kp1->kp_ksems)) {
772 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) {
773 ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
774 ksr1->ksr_ksem = ksr->ksr_ksem;
775 mutex_enter(&ksr->ksr_ksem->ks_interlock);
776 ksem_addref(ksr->ksr_ksem);
777 mutex_exit(&ksr->ksr_ksem->ks_interlock);
778 LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list);
779 }
780 }
781
782 rw_exit(&kp1->kp_lock);
783 }
784
785 static void
786 ksem_exithook(struct proc *p, void *arg)
787 {
788 struct ksem_proc *kp;
789 struct ksem_ref *ksr;
790
791 if ((kp = p->p_ksems) == NULL)
792 return;
793
794 /* Don't bother locking; process is dying. */
795
796 while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) {
797 LIST_REMOVE(ksr, ksr_list);
798 mutex_enter(&ksr->ksr_ksem->ks_interlock);
799 ksem_delref(ksr->ksr_ksem);
800 free(ksr, M_SEM);
801 }
802 }
803
804 void
805 ksem_init(void)
806 {
807 int i;
808
809 mutex_init(&ksem_mutex, MUTEX_DEFAULT, IPL_NONE);
810 exithook_establish(ksem_exithook, NULL);
811 exechook_establish(ksem_exithook, NULL);
812 forkhook_establish(ksem_forkhook);
813
814 for (i = 0; i < SEM_HASHTBL_SIZE; i++)
815 LIST_INIT(&ksem_hash[i]);
816 }
817