uipc_sem.c revision 1.12.6.2 1 /* $NetBSD: uipc_sem.c,v 1.12.6.2 2006/06/01 22:38:09 kardel 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.12.6.2 2006/06/01 22:38:09 kardel 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 struct simplelock 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 struct lock 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 struct simplelock ksem_slock;
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(simple_lock_held(&ks->ks_interlock));
152 /*
153 * If the ksem is anonymous (or has been unlinked), then
154 * this is the end if its life.
155 */
156 if (ks->ks_name == NULL) {
157 simple_unlock(&ks->ks_interlock);
158
159 simple_lock(&ksem_slock);
160 nsems--;
161 LIST_REMOVE(ks, ks_hash);
162 simple_unlock(&ksem_slock);
163
164 free(ks, M_SEM);
165 return;
166 }
167 simple_unlock(&ks->ks_interlock);
168 }
169
170 static inline void
171 ksem_addref(struct ksem *ks)
172 {
173
174 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
175 ks->ks_ref++;
176 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
177 }
178
179 static inline void
180 ksem_delref(struct ksem *ks)
181 {
182
183 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
184 KASSERT(ks->ks_ref != 0); /* XXX KDASSERT */
185 if (--ks->ks_ref == 0) {
186 ksem_free(ks);
187 return;
188 }
189 simple_unlock(&ks->ks_interlock);
190 }
191
192 static struct ksem_proc *
193 ksem_proc_alloc(void)
194 {
195 struct ksem_proc *kp;
196
197 kp = malloc(sizeof(*kp), M_SEM, M_WAITOK);
198 lockinit(&kp->kp_lock, PWAIT, "ksproc", 0, 0);
199 LIST_INIT(&kp->kp_ksems);
200
201 return (kp);
202 }
203
204 static void
205 ksem_add_proc(struct proc *p, struct ksem *ks)
206 {
207 struct ksem_proc *kp;
208 struct ksem_ref *ksr;
209
210 if (p->p_ksems == NULL) {
211 kp = ksem_proc_alloc();
212 p->p_ksems = kp;
213 } else
214 kp = p->p_ksems;
215
216 ksr = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
217 ksr->ksr_ksem = ks;
218
219 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
220 LIST_INSERT_HEAD(&kp->kp_ksems, ksr, ksr_list);
221 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
222 }
223
224 /* We MUST have a write lock on the ksem_proc list! */
225 static struct ksem_ref *
226 ksem_drop_proc(struct ksem_proc *kp, struct ksem *ks)
227 {
228 struct ksem_ref *ksr;
229
230 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
231 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
232 if (ksr->ksr_ksem == ks) {
233 ksem_delref(ks);
234 LIST_REMOVE(ksr, ksr_list);
235 return (ksr);
236 }
237 }
238 #ifdef DIAGNOSTIC
239 panic("ksem_drop_proc: ksem_proc %p ksem %p", kp, ks);
240 #endif
241 return (NULL);
242 }
243
244 static int
245 ksem_perm(struct proc *p, struct ksem *ks)
246 {
247 kauth_cred_t uc;
248
249 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
250 uc = p->p_cred;
251 if ((kauth_cred_geteuid(uc) == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
252 (kauth_cred_getegid(uc) == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
253 (ks->ks_mode & S_IWOTH) != 0 ||
254 kauth_authorize_generic(uc, KAUTH_GENERIC_ISSUSER, &p->p_acflag) == 0)
255 return (0);
256 return (EPERM);
257 }
258
259 static struct ksem *
260 ksem_lookup_byid(semid_t id)
261 {
262 struct ksem *ks;
263
264 LOCK_ASSERT(simple_lock_held(&ksem_slock));
265 LIST_FOREACH(ks, &ksem_hash[SEM_HASH(id)], ks_hash) {
266 if (ks->ks_id == id)
267 return ks;
268 }
269 return NULL;
270 }
271
272 static struct ksem *
273 ksem_lookup_byname(const char *name)
274 {
275 struct ksem *ks;
276
277 LOCK_ASSERT(simple_lock_held(&ksem_slock));
278 LIST_FOREACH(ks, &ksem_head, ks_entry) {
279 if (strcmp(ks->ks_name, name) == 0) {
280 simple_lock(&ks->ks_interlock);
281 return (ks);
282 }
283 }
284 return (NULL);
285 }
286
287 static int
288 ksem_create(struct proc *p, const char *name, struct ksem **ksret,
289 mode_t mode, unsigned int value)
290 {
291 struct ksem *ret;
292 kauth_cred_t uc;
293 size_t len;
294
295 uc = p->p_cred;
296 if (value > SEM_VALUE_MAX)
297 return (EINVAL);
298 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
299 if (name != NULL) {
300 len = strlen(name);
301 if (len > SEM_MAX_NAMELEN) {
302 free(ret, M_SEM);
303 return (ENAMETOOLONG);
304 }
305 /* name must start with a '/' but not contain one. */
306 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
307 free(ret, M_SEM);
308 return (EINVAL);
309 }
310 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
311 strlcpy(ret->ks_name, name, len + 1);
312 } else
313 ret->ks_name = NULL;
314 ret->ks_mode = mode;
315 ret->ks_value = value;
316 ret->ks_ref = 1;
317 ret->ks_waiters = 0;
318 ret->ks_uid = kauth_cred_geteuid(uc);
319 ret->ks_gid = kauth_cred_getegid(uc);
320 simple_lock_init(&ret->ks_interlock);
321
322 simple_lock(&ksem_slock);
323 if (nsems >= SEM_MAX) {
324 simple_unlock(&ksem_slock);
325 if (ret->ks_name != NULL)
326 free(ret->ks_name, M_SEM);
327 free(ret, M_SEM);
328 return (ENFILE);
329 }
330 nsems++;
331 while (ksem_lookup_byid(ksem_counter) != NULL) {
332 ksem_counter++;
333 /* 0 is a special value for libpthread */
334 if (ksem_counter == 0)
335 ksem_counter++;
336 }
337 ret->ks_id = ksem_counter;
338 LIST_INSERT_HEAD(&ksem_hash[SEM_HASH(ret->ks_id)], ret, ks_hash);
339 simple_unlock(&ksem_slock);
340
341 *ksret = ret;
342 return (0);
343 }
344
345 int
346 sys__ksem_init(struct lwp *l, void *v, register_t *retval)
347 {
348 struct sys__ksem_init_args /* {
349 unsigned int value;
350 semid_t *idp;
351 } */ *uap = v;
352
353 return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
354 }
355
356 int
357 do_ksem_init(struct lwp *l, unsigned int value, semid_t *idp,
358 copyout_t docopyout)
359 {
360 struct ksem *ks;
361 semid_t id;
362 int error;
363
364 /* Note the mode does not matter for anonymous semaphores. */
365 error = ksem_create(l->l_proc, NULL, &ks, 0, value);
366 if (error)
367 return (error);
368 id = SEM_TO_ID(ks);
369 error = (*docopyout)(&id, idp, sizeof(id));
370 if (error) {
371 simple_lock(&ks->ks_interlock);
372 ksem_delref(ks);
373 return (error);
374 }
375
376 ksem_add_proc(l->l_proc, ks);
377
378 return (0);
379 }
380
381 int
382 sys__ksem_open(struct lwp *l, void *v, register_t *retval)
383 {
384 struct sys__ksem_open_args /* {
385 const char *name;
386 int oflag;
387 mode_t mode;
388 unsigned int value;
389 semid_t *idp;
390 } */ *uap = v;
391
392 return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
393 SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
394 }
395
396 int
397 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
398 unsigned int value, semid_t *idp, copyout_t docopyout)
399 {
400 char name[SEM_MAX_NAMELEN + 1];
401 size_t done;
402 int error;
403 struct ksem *ksnew, *ks;
404 semid_t id;
405
406 error = copyinstr(semname, name, sizeof(name), &done);
407 if (error)
408 return (error);
409
410 ksnew = NULL;
411 simple_lock(&ksem_slock);
412 ks = ksem_lookup_byname(name);
413
414 /* Found one? */
415 if (ks != NULL) {
416 /* Check for exclusive create. */
417 if (oflag & O_EXCL) {
418 simple_unlock(&ks->ks_interlock);
419 simple_unlock(&ksem_slock);
420 return (EEXIST);
421 }
422 found_one:
423 /*
424 * Verify permissions. If we can access it, add
425 * this process's reference.
426 */
427 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
428 error = ksem_perm(l->l_proc, ks);
429 if (error == 0)
430 ksem_addref(ks);
431 simple_unlock(&ks->ks_interlock);
432 simple_unlock(&ksem_slock);
433 if (error)
434 return (error);
435
436 id = SEM_TO_ID(ks);
437 error = (*docopyout)(&id, idp, sizeof(id));
438 if (error) {
439 simple_lock(&ks->ks_interlock);
440 ksem_delref(ks);
441 return (error);
442 }
443
444 ksem_add_proc(l->l_proc, ks);
445
446 return (0);
447 }
448
449 /*
450 * didn't ask for creation? error.
451 */
452 if ((oflag & O_CREAT) == 0) {
453 simple_unlock(&ksem_slock);
454 return (ENOENT);
455 }
456
457 /*
458 * We may block during creation, so drop the lock.
459 */
460 simple_unlock(&ksem_slock);
461 error = ksem_create(l->l_proc, name, &ksnew, mode, value);
462 if (error != 0)
463 return (error);
464
465 id = SEM_TO_ID(ksnew);
466 error = (*docopyout)(&id, idp, sizeof(id));
467 if (error) {
468 free(ksnew->ks_name, M_SEM);
469 ksnew->ks_name = NULL;
470
471 simple_lock(&ksnew->ks_interlock);
472 ksem_delref(ksnew);
473 return (error);
474 }
475
476 /*
477 * We need to make sure we haven't lost a race while
478 * allocating during creation.
479 */
480 simple_lock(&ksem_slock);
481 if ((ks = ksem_lookup_byname(name)) != NULL) {
482 if (oflag & O_EXCL) {
483 simple_unlock(&ks->ks_interlock);
484 simple_unlock(&ksem_slock);
485
486 free(ksnew->ks_name, M_SEM);
487 ksnew->ks_name = NULL;
488
489 simple_lock(&ksnew->ks_interlock);
490 ksem_delref(ksnew);
491 return (EEXIST);
492 }
493 goto found_one;
494 } else {
495 /* ksnew already has its initial reference. */
496 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
497 simple_unlock(&ksem_slock);
498
499 ksem_add_proc(l->l_proc, ksnew);
500 }
501 return (error);
502 }
503
504 /* We must have a read lock on the ksem_proc list! */
505 static struct ksem *
506 ksem_lookup_proc(struct ksem_proc *kp, semid_t id)
507 {
508 struct ksem_ref *ksr;
509
510 LIST_FOREACH(ksr, &kp->kp_ksems, ksr_list) {
511 if (id == SEM_TO_ID(ksr->ksr_ksem)) {
512 simple_lock(&ksr->ksr_ksem->ks_interlock);
513 return (ksr->ksr_ksem);
514 }
515 }
516
517 return (NULL);
518 }
519
520 int
521 sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
522 {
523 struct sys__ksem_unlink_args /* {
524 const char *name;
525 } */ *uap = v;
526 char name[SEM_MAX_NAMELEN + 1], *cp;
527 size_t done;
528 struct ksem *ks;
529 int error;
530
531 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
532 if (error)
533 return error;
534
535 simple_lock(&ksem_slock);
536 ks = ksem_lookup_byname(name);
537 if (ks == NULL) {
538 simple_unlock(&ksem_slock);
539 return (ENOENT);
540 }
541
542 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
543
544 LIST_REMOVE(ks, ks_entry);
545 cp = ks->ks_name;
546 ks->ks_name = NULL;
547
548 simple_unlock(&ksem_slock);
549
550 if (ks->ks_ref == 0)
551 ksem_free(ks);
552 else
553 simple_unlock(&ks->ks_interlock);
554
555 free(cp, M_SEM);
556
557 return (0);
558 }
559
560 int
561 sys__ksem_close(struct lwp *l, void *v, register_t *retval)
562 {
563 struct sys__ksem_close_args /* {
564 semid_t id;
565 } */ *uap = v;
566 struct ksem_proc *kp;
567 struct ksem_ref *ksr;
568 struct ksem *ks;
569
570 if ((kp = l->l_proc->p_ksems) == NULL)
571 return (EINVAL);
572
573 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
574
575 ks = ksem_lookup_proc(kp, SCARG(uap, id));
576 if (ks == NULL) {
577 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
578 return (EINVAL);
579 }
580
581 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
582 if (ks->ks_name == NULL) {
583 simple_unlock(&ks->ks_interlock);
584 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
585 return (EINVAL);
586 }
587
588 ksr = ksem_drop_proc(kp, ks);
589 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
590 free(ksr, M_SEM);
591
592 return (0);
593 }
594
595 int
596 sys__ksem_post(struct lwp *l, void *v, register_t *retval)
597 {
598 struct sys__ksem_post_args /* {
599 semid_t id;
600 } */ *uap = v;
601 struct ksem_proc *kp;
602 struct ksem *ks;
603 int error;
604
605 if ((kp = l->l_proc->p_ksems) == NULL)
606 return (EINVAL);
607
608 lockmgr(&kp->kp_lock, LK_SHARED, NULL);
609 ks = ksem_lookup_proc(kp, SCARG(uap, id));
610 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
611 if (ks == NULL)
612 return (EINVAL);
613
614 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
615 if (ks->ks_value == SEM_VALUE_MAX) {
616 error = EOVERFLOW;
617 goto out;
618 }
619 ++ks->ks_value;
620 if (ks->ks_waiters)
621 wakeup(ks);
622 error = 0;
623 out:
624 simple_unlock(&ks->ks_interlock);
625 return (error);
626 }
627
628 static int
629 ksem_wait(struct lwp *l, semid_t id, int tryflag)
630 {
631 struct ksem_proc *kp;
632 struct ksem *ks;
633 int error;
634
635 if ((kp = l->l_proc->p_ksems) == NULL)
636 return (EINVAL);
637
638 lockmgr(&kp->kp_lock, LK_SHARED, NULL);
639 ks = ksem_lookup_proc(kp, id);
640 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
641 if (ks == NULL)
642 return (EINVAL);
643
644 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
645 ksem_addref(ks);
646 while (ks->ks_value == 0) {
647 ks->ks_waiters++;
648 error = tryflag ? EAGAIN : ltsleep(ks, PCATCH, "psem", 0,
649 &ks->ks_interlock);
650 ks->ks_waiters--;
651 if (error)
652 goto out;
653 }
654 ks->ks_value--;
655 error = 0;
656 out:
657 ksem_delref(ks);
658 return (error);
659 }
660
661 int
662 sys__ksem_wait(struct lwp *l, void *v, register_t *retval)
663 {
664 struct sys__ksem_wait_args /* {
665 semid_t id;
666 } */ *uap = v;
667
668 return ksem_wait(l, SCARG(uap, id), 0);
669 }
670
671 int
672 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval)
673 {
674 struct sys__ksem_trywait_args /* {
675 semid_t id;
676 } */ *uap = v;
677
678 return ksem_wait(l, SCARG(uap, id), 1);
679 }
680
681 int
682 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval)
683 {
684 struct sys__ksem_getvalue_args /* {
685 semid_t id;
686 unsigned int *value;
687 } */ *uap = v;
688 struct ksem_proc *kp;
689 struct ksem *ks;
690 unsigned int val;
691
692 if ((kp = l->l_proc->p_ksems) == NULL)
693 return (EINVAL);
694
695 lockmgr(&kp->kp_lock, LK_SHARED, NULL);
696 ks = ksem_lookup_proc(kp, SCARG(uap, id));
697 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
698 if (ks == NULL)
699 return (EINVAL);
700
701 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
702 val = ks->ks_value;
703 simple_unlock(&ks->ks_interlock);
704
705 return (copyout(&val, SCARG(uap, value), sizeof(val)));
706 }
707
708 int
709 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
710 {
711 struct sys__ksem_destroy_args /*{
712 semid_t id;
713 } */ *uap = v;
714 struct ksem_proc *kp;
715 struct ksem_ref *ksr;
716 struct ksem *ks;
717
718 if ((kp = l->l_proc->p_ksems) == NULL)
719 return (EINVAL);
720
721 lockmgr(&kp->kp_lock, LK_EXCLUSIVE, NULL);
722
723 ks = ksem_lookup_proc(kp, SCARG(uap, id));
724 if (ks == NULL) {
725 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
726 return (EINVAL);
727 }
728
729 LOCK_ASSERT(simple_lock_held(&ks->ks_interlock));
730
731 /*
732 * XXX This misses named semaphores which have been unlink'd,
733 * XXX but since behavior of destroying a named semaphore is
734 * XXX undefined, this is technically allowed.
735 */
736 if (ks->ks_name != NULL) {
737 simple_unlock(&ks->ks_interlock);
738 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
739 return (EINVAL);
740 }
741
742 if (ks->ks_waiters) {
743 simple_unlock(&ks->ks_interlock);
744 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
745 return (EBUSY);
746 }
747
748 ksr = ksem_drop_proc(kp, ks);
749 lockmgr(&kp->kp_lock, LK_RELEASE, NULL);
750 free(ksr, M_SEM);
751
752 return (0);
753 }
754
755 static void
756 ksem_forkhook(struct proc *p2, struct proc *p1)
757 {
758 struct ksem_proc *kp1, *kp2;
759 struct ksem_ref *ksr, *ksr1;
760
761 if ((kp1 = p1->p_ksems) == NULL) {
762 p2->p_ksems = NULL;
763 return;
764 }
765
766 p2->p_ksems = kp2 = ksem_proc_alloc();
767
768 lockmgr(&kp1->kp_lock, LK_SHARED, NULL);
769
770 if (!LIST_EMPTY(&kp1->kp_ksems)) {
771 LIST_FOREACH(ksr, &kp1->kp_ksems, ksr_list) {
772 ksr1 = malloc(sizeof(*ksr), M_SEM, M_WAITOK);
773 ksr1->ksr_ksem = ksr->ksr_ksem;
774 simple_lock(&ksr->ksr_ksem->ks_interlock);
775 ksem_addref(ksr->ksr_ksem);
776 simple_unlock(&ksr->ksr_ksem->ks_interlock);
777 LIST_INSERT_HEAD(&kp2->kp_ksems, ksr1, ksr_list);
778 }
779 }
780
781 lockmgr(&kp1->kp_lock, LK_RELEASE, NULL);
782 }
783
784 static void
785 ksem_exithook(struct proc *p, void *arg)
786 {
787 struct ksem_proc *kp;
788 struct ksem_ref *ksr;
789
790 if ((kp = p->p_ksems) == NULL)
791 return;
792
793 /* Don't bother locking; process is dying. */
794
795 while ((ksr = LIST_FIRST(&kp->kp_ksems)) != NULL) {
796 LIST_REMOVE(ksr, ksr_list);
797 simple_lock(&ksr->ksr_ksem->ks_interlock);
798 ksem_delref(ksr->ksr_ksem);
799 free(ksr, M_SEM);
800 }
801 }
802
803 void
804 ksem_init(void)
805 {
806 int i;
807
808 simple_lock_init(&ksem_slock);
809 exithook_establish(ksem_exithook, NULL);
810 exechook_establish(ksem_exithook, NULL);
811 forkhook_establish(ksem_forkhook);
812
813 for (i = 0; i < SEM_HASHTBL_SIZE; i++)
814 LIST_INIT(&ksem_hash[i]);
815 }
816