]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/uipc_sem.c
Keep proper track of nsegs counter: sem_free is called for all
[FreeBSD/FreeBSD.git] / sys / kern / uipc_sem.c
1 /*-
2  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3  * Copyright (c) 2003-2005 SPARTA, Inc.
4  * Copyright (c) 2005 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * This software was developed for the FreeBSD Project in part by Network
8  * Associates Laboratories, the Security Research Division of Network
9  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
10  * as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include "opt_mac.h"
38 #include "opt_posix.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/sysproto.h>
43 #include <sys/eventhandler.h>
44 #include <sys/kernel.h>
45 #include <sys/ksem.h>
46 #include <sys/priv.h>
47 #include <sys/proc.h>
48 #include <sys/posix4.h>
49 #include <sys/lock.h>
50 #include <sys/mutex.h>
51 #include <sys/module.h>
52 #include <sys/condvar.h>
53 #include <sys/sem.h>
54 #include <sys/uio.h>
55 #include <sys/semaphore.h>
56 #include <sys/syscall.h>
57 #include <sys/stat.h>
58 #include <sys/sysent.h>
59 #include <sys/sysctl.h>
60 #include <sys/time.h>
61 #include <sys/malloc.h>
62 #include <sys/fcntl.h>
63 #include <sys/_semaphore.h>
64
65 #include <security/mac/mac_framework.h>
66
67 static int sem_count_proc(struct proc *p);
68 static struct ksem *sem_lookup_byname(const char *name);
69 static int sem_create(struct thread *td, const char *name,
70     struct ksem **ksret, mode_t mode, unsigned int value);
71 static void sem_free(struct ksem *ksnew);
72 static int sem_perm(struct thread *td, struct ksem *ks);
73 static void sem_enter(struct proc *p, struct ksem *ks);
74 static int sem_leave(struct proc *p, struct ksem *ks);
75 static void sem_exechook(void *arg, struct proc *p,
76     struct image_params *imgp);
77 static void sem_exithook(void *arg, struct proc *p);
78 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
79     int flags);
80 static int sem_hasopen(struct thread *td, struct ksem *ks);
81
82 static int kern_sem_close(struct thread *td, semid_t id);
83 static int kern_sem_post(struct thread *td, semid_t id);
84 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
85     struct timespec *abstime);
86 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
87     semid_t *idp);
88 static int kern_sem_open(struct thread *td, int dir, const char *name,
89     int oflag, mode_t mode, unsigned int value, semid_t *idp);
90 static int kern_sem_unlink(struct thread *td, const char *name);
91
92 #ifndef SEM_MAX
93 #define SEM_MAX 30
94 #endif
95
96 #define SEM_MAX_NAMELEN 14
97
98 #define SEM_TO_ID(x)    ((intptr_t)(x))
99 #define ID_TO_SEM(x)    id_to_sem(x)
100
101 /*
102  * Available semaphores go here, this includes sem_init and any semaphores
103  * created via sem_open that have not yet been unlinked.
104  */
105 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
106
107 /*
108  * Semaphores still in use but have been sem_unlink()'d go here.
109  */
110 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
111
112 static struct mtx sem_lock;
113 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
114
115 static int nsems = 0;
116 SYSCTL_DECL(_p1003_1b);
117 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
118
119 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
120
121 #ifdef SEM_DEBUG
122 #define DP(x)   printf x
123 #else
124 #define DP(x)
125 #endif
126
127 static __inline void
128 sem_ref(struct ksem *ks)
129 {
130
131         mtx_assert(&sem_lock, MA_OWNED);
132         ks->ks_ref++;
133         DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
134 }
135
136 static __inline void
137 sem_rel(struct ksem *ks)
138 {
139
140         mtx_assert(&sem_lock, MA_OWNED);
141         DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
142         if (--ks->ks_ref == 0)
143                 sem_free(ks);
144 }
145
146 static __inline
147 struct ksem *
148 id_to_sem(semid_t id)
149 {
150         struct ksem *ks;
151
152         mtx_assert(&sem_lock, MA_OWNED);
153         DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
154         LIST_FOREACH(ks, &ksem_head, ks_entry) {
155                 DP(("id_to_sem: ks = %p\n", ks));
156                 if (ks == (struct ksem *)id)
157                         return (ks);
158         }
159         return (NULL);
160 }
161
162 static struct ksem *
163 sem_lookup_byname(const char *name)
164 {
165         struct ksem *ks;
166
167         mtx_assert(&sem_lock, MA_OWNED);
168         LIST_FOREACH(ks, &ksem_head, ks_entry)
169                 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
170                         return (ks);
171         return (NULL);
172 }
173
174 static int
175 sem_create(struct thread *td, const char *name, struct ksem **ksret,
176     mode_t mode, unsigned int value)
177 {
178         struct ksem *ret;
179         struct proc *p;
180         struct ucred *uc;
181         size_t len;
182         int error;
183
184         DP(("sem_create\n"));
185         p = td->td_proc;
186         uc = td->td_ucred;
187         if (value > SEM_VALUE_MAX)
188                 return (EINVAL);
189         ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
190         if (name != NULL) {
191                 len = strlen(name);
192                 if (len > SEM_MAX_NAMELEN) {
193                         free(ret, M_SEM);
194                         return (ENAMETOOLONG);
195                 }
196
197                 /* Name must start with a '/' but not contain one. */
198                 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
199                         free(ret, M_SEM);
200                         return (EINVAL);
201                 }
202                 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
203                 strcpy(ret->ks_name, name);
204         } else {
205                 ret->ks_name = NULL;
206         }
207         ret->ks_mode = mode;
208         ret->ks_value = value;
209         ret->ks_ref = 1;
210         ret->ks_waiters = 0;
211         ret->ks_uid = uc->cr_uid;
212         ret->ks_gid = uc->cr_gid;
213         ret->ks_onlist = 0;
214         cv_init(&ret->ks_cv, "sem");
215         LIST_INIT(&ret->ks_users);
216 #ifdef MAC
217         mac_posixsem_init(ret);
218         mac_posixsem_create(uc, ret);
219 #endif
220         if (name != NULL)
221                 sem_enter(td->td_proc, ret);
222         *ksret = ret;
223         mtx_lock(&sem_lock);
224         nsems++;
225         if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
226                 sem_leave(td->td_proc, ret);
227                 sem_free(ret);
228                 error = ENFILE;
229         } else
230                 error = 0;
231         mtx_unlock(&sem_lock);
232         return (error);
233 }
234
235 #ifndef _SYS_SYSPROTO_H_
236 struct ksem_init_args {
237         unsigned int value;
238         semid_t *idp;
239 };
240 int ksem_init(struct thread *td, struct ksem_init_args *uap);
241 #endif
242 int
243 ksem_init(struct thread *td, struct ksem_init_args *uap)
244 {
245
246         return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
247 }
248
249 static int
250 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
251 {
252         struct ksem *ks;
253         semid_t id;
254         int error;
255
256         error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
257         if (error)
258                 return (error);
259         id = SEM_TO_ID(ks);
260         if (dir == UIO_USERSPACE) {
261                 error = copyout(&id, idp, sizeof(id));
262                 if (error) {
263                         mtx_lock(&sem_lock);
264                         sem_rel(ks);
265                         mtx_unlock(&sem_lock);
266                         return (error);
267                 }
268         } else {
269                 *idp = id;
270         }
271         mtx_lock(&sem_lock);
272         LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
273         ks->ks_onlist = 1;
274         mtx_unlock(&sem_lock);
275         return (error);
276 }
277
278 #ifndef _SYS_SYSPROTO_H_
279 struct ksem_open_args {
280         char *name;
281         int oflag;
282         mode_t mode;
283         unsigned int value;
284         semid_t *idp;   
285 };
286 int ksem_open(struct thread *td, struct ksem_open_args *uap);
287 #endif
288 int
289 ksem_open(struct thread *td, struct ksem_open_args *uap)
290 {
291         char name[SEM_MAX_NAMELEN + 1];
292         size_t done;
293         int error;
294
295         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
296         if (error)
297                 return (error);
298         DP((">>> sem_open start\n"));
299         error = kern_sem_open(td, UIO_USERSPACE,
300             name, uap->oflag, uap->mode, uap->value, uap->idp);
301         DP(("<<< sem_open end\n"));
302         return (error);
303 }
304
305 static int
306 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
307     mode_t mode, unsigned int value, semid_t *idp)
308 {
309         struct ksem *ksnew, *ks;
310         int error;
311         semid_t id;
312
313         ksnew = NULL;
314         mtx_lock(&sem_lock);
315         ks = sem_lookup_byname(name);
316
317         /*
318          * If we found it but O_EXCL is set, error.
319          */
320         if (ks != NULL && (oflag & O_EXCL) != 0) {
321                 mtx_unlock(&sem_lock);
322                 return (EEXIST);
323         }
324
325         /*
326          * If we didn't find it...
327          */
328         if (ks == NULL) {
329                 /*
330                  * didn't ask for creation? error.
331                  */
332                 if ((oflag & O_CREAT) == 0) {
333                         mtx_unlock(&sem_lock);
334                         return (ENOENT);
335                 }
336
337                 /*
338                  * We may block during creation, so drop the lock.
339                  */
340                 mtx_unlock(&sem_lock);
341                 error = sem_create(td, name, &ksnew, mode, value);
342                 if (error != 0)
343                         return (error);
344                 id = SEM_TO_ID(ksnew);
345                 if (dir == UIO_USERSPACE) {
346                         DP(("about to copyout! %d to %p\n", id, idp));
347                         error = copyout(&id, idp, sizeof(id));
348                         if (error) {
349                                 mtx_lock(&sem_lock);
350                                 sem_leave(td->td_proc, ksnew);
351                                 sem_rel(ksnew);
352                                 mtx_unlock(&sem_lock);
353                                 return (error);
354                         }
355                 } else {
356                         DP(("about to set! %d to %p\n", id, idp));
357                         *idp = id;
358                 }
359
360                 /*
361                  * We need to make sure we haven't lost a race while
362                  * allocating during creation.
363                  */
364                 mtx_lock(&sem_lock);
365                 ks = sem_lookup_byname(name);
366                 if (ks != NULL) {
367                         /* we lost... */
368                         sem_leave(td->td_proc, ksnew);
369                         sem_rel(ksnew);
370                         /* we lost and we can't loose... */
371                         if ((oflag & O_EXCL) != 0) {
372                                 mtx_unlock(&sem_lock);
373                                 return (EEXIST);
374                         }
375                 } else {
376                         DP(("sem_create: about to add to list...\n"));
377                         LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 
378                         DP(("sem_create: setting list bit...\n"));
379                         ksnew->ks_onlist = 1;
380                         DP(("sem_create: done, about to unlock...\n"));
381                 }
382         } else {
383 #ifdef MAC
384                 error = mac_posixsem_check_open(td->td_ucred, ks);
385                 if (error)
386                         goto err_open;
387 #endif
388                 /*
389                  * if we aren't the creator, then enforce permissions.
390                  */
391                 error = sem_perm(td, ks);
392                 if (error)
393                         goto err_open;
394                 sem_ref(ks);
395                 mtx_unlock(&sem_lock);
396                 id = SEM_TO_ID(ks);
397                 if (dir == UIO_USERSPACE) {
398                         error = copyout(&id, idp, sizeof(id));
399                         if (error) {
400                                 mtx_lock(&sem_lock);
401                                 sem_rel(ks);
402                                 mtx_unlock(&sem_lock);
403                                 return (error);
404                         }
405                 } else {
406                         *idp = id;
407                 }
408                 sem_enter(td->td_proc, ks);
409                 mtx_lock(&sem_lock);
410                 sem_rel(ks);
411         }
412 err_open:
413         mtx_unlock(&sem_lock);
414         return (error);
415 }
416
417 static int
418 sem_perm(struct thread *td, struct ksem *ks)
419 {
420         struct ucred *uc;
421
422         /*
423          * XXXRW: This permission routine appears to be incorrect.  If the
424          * user matches, we shouldn't go on to the group if the user
425          * permissions don't allow the action?  Not changed for now.  To fix,
426          * change from a series of if (); if (); to if () else if () else...
427          */
428         uc = td->td_ucred;
429         DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
430             uc->cr_uid, uc->cr_gid,
431              ks->ks_uid, ks->ks_gid, ks->ks_mode));
432         if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
433                 return (0);
434         if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
435                 return (0);
436         if ((ks->ks_mode & S_IWOTH) != 0)
437                 return (0);
438         return (priv_check(td, PRIV_SEM_WRITE));
439 }
440
441 static void
442 sem_free(struct ksem *ks)
443 {
444
445 #ifdef MAC
446         mac_posixsem_destroy(ks);
447 #endif
448         nsems--;
449         if (ks->ks_onlist)
450                 LIST_REMOVE(ks, ks_entry);
451         if (ks->ks_name != NULL)
452                 free(ks->ks_name, M_SEM);
453         cv_destroy(&ks->ks_cv);
454         free(ks, M_SEM);
455 }
456
457 static __inline struct kuser *
458 sem_getuser(struct proc *p, struct ksem *ks)
459 {
460         struct kuser *k;
461
462         LIST_FOREACH(k, &ks->ks_users, ku_next)
463                 if (k->ku_pid == p->p_pid)
464                         return (k);
465         return (NULL);
466 }
467
468 static int
469 sem_hasopen(struct thread *td, struct ksem *ks)
470 {
471         
472         return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
473             || sem_getuser(td->td_proc, ks) != NULL);
474 }
475
476 static int
477 sem_leave(struct proc *p, struct ksem *ks)
478 {
479         struct kuser *k;
480
481         DP(("sem_leave: ks = %p\n", ks));
482         k = sem_getuser(p, ks);
483         DP(("sem_leave: ks = %p, k = %p\n", ks, k));
484         if (k != NULL) {
485                 LIST_REMOVE(k, ku_next);
486                 sem_rel(ks);
487                 DP(("sem_leave: about to free k\n"));
488                 free(k, M_SEM);
489                 DP(("sem_leave: returning\n"));
490                 return (0);
491         }
492         return (EINVAL);
493 }
494
495 static void
496 sem_enter(struct proc *p, struct ksem *ks)
497 {
498         struct kuser *ku, *k;
499
500         ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
501         ku->ku_pid = p->p_pid;
502         mtx_lock(&sem_lock);
503         k = sem_getuser(p, ks);
504         if (k != NULL) {
505                 mtx_unlock(&sem_lock);
506                 free(ku, M_TEMP);
507                 return;
508         }
509         LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
510         sem_ref(ks);
511         mtx_unlock(&sem_lock);
512 }
513
514 #ifndef _SYS_SYSPROTO_H_
515 struct ksem_unlink_args {
516         char *name;
517 };
518 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
519 #endif
520 int
521 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
522 {
523         char name[SEM_MAX_NAMELEN + 1];
524         size_t done;
525         int error;
526
527         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
528         return (error ? error :
529             kern_sem_unlink(td, name));
530 }
531
532 static int
533 kern_sem_unlink(struct thread *td, const char *name)
534 {
535         struct ksem *ks;
536         int error;
537
538         mtx_lock(&sem_lock);
539         ks = sem_lookup_byname(name);
540         if (ks != NULL) {
541 #ifdef MAC
542                 error = mac_posixsem_check_unlink(td->td_ucred, ks);
543                 if (error) {
544                         mtx_unlock(&sem_lock);
545                         return (error);
546                 }
547 #endif
548                 error = sem_perm(td, ks);
549         } else
550                 error = ENOENT;
551         DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
552         if (error == 0) {
553                 LIST_REMOVE(ks, ks_entry);
554                 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 
555                 sem_rel(ks);
556         }
557         mtx_unlock(&sem_lock);
558         return (error);
559 }
560
561 #ifndef _SYS_SYSPROTO_H_
562 struct ksem_close_args {
563         semid_t id;
564 };
565 int ksem_close(struct thread *td, struct ksem_close_args *uap);
566 #endif
567 int
568 ksem_close(struct thread *td, struct ksem_close_args *uap)
569 {
570
571         return (kern_sem_close(td, uap->id));
572 }
573
574 static int
575 kern_sem_close(struct thread *td, semid_t id)
576 {
577         struct ksem *ks;
578         int error;
579
580         error = EINVAL;
581         mtx_lock(&sem_lock);
582         ks = ID_TO_SEM(id);
583
584         /*
585          * This is not a valid operation for unnamed sems.
586          */
587         if (ks != NULL && ks->ks_name != NULL)
588                 error = sem_leave(td->td_proc, ks);
589         mtx_unlock(&sem_lock);
590         return (error);
591 }
592
593 #ifndef _SYS_SYSPROTO_H_
594 struct ksem_post_args {
595         semid_t id;
596 };
597 int ksem_post(struct thread *td, struct ksem_post_args *uap);
598 #endif
599 int
600 ksem_post(struct thread *td, struct ksem_post_args *uap)
601 {
602
603         return (kern_sem_post(td, uap->id));
604 }
605
606 static int
607 kern_sem_post(struct thread *td, semid_t id)
608 {
609         struct ksem *ks;
610         int error;
611
612         mtx_lock(&sem_lock);
613         ks = ID_TO_SEM(id);
614         if (ks == NULL || !sem_hasopen(td, ks)) {
615                 error = EINVAL;
616                 goto err;
617         }
618 #ifdef MAC
619         error = mac_posixsem_check_post(td->td_ucred, ks);
620         if (error)
621                 goto err;
622 #endif
623         if (ks->ks_value == SEM_VALUE_MAX) {
624                 error = EOVERFLOW;
625                 goto err;
626         }
627         ++ks->ks_value;
628         if (ks->ks_waiters > 0)
629                 cv_signal(&ks->ks_cv);
630         error = 0;
631 err:
632         mtx_unlock(&sem_lock);
633         return (error);
634 }
635
636 #ifndef _SYS_SYSPROTO_H_
637 struct ksem_wait_args {
638         semid_t id;
639 };
640 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
641 #endif
642 int
643 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
644 {
645
646         return (kern_sem_wait(td, uap->id, 0, NULL));
647 }
648
649 #ifndef _SYS_SYSPROTO_H_
650 struct ksem_timedwait_args {
651         semid_t id;
652         const struct timespec *abstime;
653 };
654 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
655 #endif
656 int
657 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
658 {
659         struct timespec abstime;
660         struct timespec *ts;
661         int error;
662
663         /*
664          * We allow a null timespec (wait forever).
665          */
666         if (uap->abstime == NULL)
667                 ts = NULL;
668         else {
669                 error = copyin(uap->abstime, &abstime, sizeof(abstime));
670                 if (error != 0)
671                         return (error);
672                 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
673                         return (EINVAL);
674                 ts = &abstime;
675         }
676         return (kern_sem_wait(td, uap->id, 0, ts));
677 }
678
679 #ifndef _SYS_SYSPROTO_H_
680 struct ksem_trywait_args {
681         semid_t id;
682 };
683 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
684 #endif
685 int
686 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
687 {
688
689         return (kern_sem_wait(td, uap->id, 1, NULL));
690 }
691
692 static int
693 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
694     struct timespec *abstime)
695 {
696         struct timespec ts1, ts2;
697         struct timeval tv;
698         struct ksem *ks;
699         int error;
700
701         DP((">>> kern_sem_wait entered!\n"));
702         mtx_lock(&sem_lock);
703         ks = ID_TO_SEM(id);
704         if (ks == NULL) {
705                 DP(("kern_sem_wait ks == NULL\n"));
706                 error = EINVAL;
707                 goto err;
708         }
709         sem_ref(ks);
710         if (!sem_hasopen(td, ks)) {
711                 DP(("kern_sem_wait hasopen failed\n"));
712                 error = EINVAL;
713                 goto err;
714         }
715 #ifdef MAC
716         error = mac_posixsem_check_wait(td->td_ucred, ks);
717         if (error) {
718                 DP(("kern_sem_wait mac failed\n"));
719                 goto err;
720         }
721 #endif
722         DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
723         if (ks->ks_value == 0) {
724                 ks->ks_waiters++;
725                 if (tryflag != 0)
726                         error = EAGAIN;
727                 else if (abstime == NULL)
728                         error = cv_wait_sig(&ks->ks_cv, &sem_lock);
729                 else {
730                         for (;;) {
731                                 ts1 = *abstime;
732                                 getnanotime(&ts2);
733                                 timespecsub(&ts1, &ts2);
734                                 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
735                                 if (tv.tv_sec < 0) {
736                                         error = ETIMEDOUT;
737                                         break;
738                                 }
739                                 error = cv_timedwait_sig(&ks->ks_cv,
740                                     &sem_lock, tvtohz(&tv));
741                                 if (error != EWOULDBLOCK)
742                                         break;
743                         }
744                 }
745                 ks->ks_waiters--;
746                 if (error)
747                         goto err;
748         }
749         ks->ks_value--;
750         error = 0;
751 err:
752         if (ks != NULL)
753                 sem_rel(ks);
754         mtx_unlock(&sem_lock);
755         DP(("<<< kern_sem_wait leaving, error = %d\n", error));
756         return (error);
757 }
758
759 #ifndef _SYS_SYSPROTO_H_
760 struct ksem_getvalue_args {
761         semid_t id;
762         int *val;
763 };
764 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
765 #endif
766 int
767 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
768 {
769         struct ksem *ks;
770         int error, val;
771
772         mtx_lock(&sem_lock);
773         ks = ID_TO_SEM(uap->id);
774         if (ks == NULL || !sem_hasopen(td, ks)) {
775                 mtx_unlock(&sem_lock);
776                 return (EINVAL);
777         }
778 #ifdef MAC
779         error = mac_posixsem_check_getvalue(td->td_ucred, ks);
780         if (error) {
781                 mtx_unlock(&sem_lock);
782                 return (error);
783         }
784 #endif
785         val = ks->ks_value;
786         mtx_unlock(&sem_lock);
787         error = copyout(&val, uap->val, sizeof(val));
788         return (error);
789 }
790
791 #ifndef _SYS_SYSPROTO_H_
792 struct ksem_destroy_args {
793         semid_t id;
794 };
795 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
796 #endif
797 int
798 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
799 {
800         struct ksem *ks;
801         int error;
802
803         mtx_lock(&sem_lock);
804         ks = ID_TO_SEM(uap->id);
805         if (ks == NULL || !sem_hasopen(td, ks) ||
806             ks->ks_name != NULL) {
807                 error = EINVAL;
808                 goto err;
809         }
810 #ifdef MAC
811         error = mac_posixsem_check_destroy(td->td_ucred, ks);
812         if (error)
813                 goto err;
814 #endif
815         if (ks->ks_waiters != 0) {
816                 error = EBUSY;
817                 goto err;
818         }
819         sem_rel(ks);
820         error = 0;
821 err:
822         mtx_unlock(&sem_lock);
823         return (error);
824 }
825
826 /*
827  * Count the number of kusers associated with a proc, so as to guess at how
828  * many to allocate when forking.
829  */
830 static int
831 sem_count_proc(struct proc *p)
832 {
833         struct ksem *ks;
834         struct kuser *ku;
835         int count;
836
837         mtx_assert(&sem_lock, MA_OWNED);
838
839         count = 0;
840         LIST_FOREACH(ks, &ksem_head, ks_entry) {
841                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
842                         if (ku->ku_pid == p->p_pid)
843                                 count++;
844                 }
845         }
846         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
847                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
848                         if (ku->ku_pid == p->p_pid)
849                                 count++;
850                 }
851         }
852         return (count);
853 }
854
855 /*
856  * When a process forks, the child process must gain a reference to each open
857  * semaphore in the parent process, whether it is unlinked or not.  This
858  * requires allocating a kuser structure for each semaphore reference in the
859  * new process.  Because the set of semaphores in the parent can change while
860  * the fork is in progress, we have to handle races -- first we attempt to
861  * allocate enough storage to acquire references to each of the semaphores,
862  * then we enter the semaphores and release the temporary references.
863  */
864 static void
865 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
866 {
867         struct ksem *ks, **sem_array;
868         int count, i, new_count;
869         struct kuser *ku;
870
871         mtx_lock(&sem_lock);
872         count = sem_count_proc(p1);
873         if (count == 0) {
874                 mtx_unlock(&sem_lock);
875                 return;
876         }
877 race_lost:
878         mtx_assert(&sem_lock, MA_OWNED);
879         mtx_unlock(&sem_lock);
880         sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
881         mtx_lock(&sem_lock);
882         new_count = sem_count_proc(p1);
883         if (count < new_count) {
884                 /* Lost race, repeat and allocate more storage. */
885                 free(sem_array, M_TEMP);
886                 count = new_count;
887                 goto race_lost;
888         }
889
890         /*
891          * Given an array capable of storing an adequate number of semaphore
892          * references, now walk the list of semaphores and acquire a new
893          * reference for any semaphore opened by p1.
894          */
895         count = new_count;
896         i = 0;
897         LIST_FOREACH(ks, &ksem_head, ks_entry) {
898                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
899                         if (ku->ku_pid == p1->p_pid) {
900                                 sem_ref(ks);
901                                 sem_array[i] = ks;
902                                 i++;
903                                 break;
904                         }
905                 }
906         }
907         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
908                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
909                         if (ku->ku_pid == p1->p_pid) {
910                                 sem_ref(ks);
911                                 sem_array[i] = ks;
912                                 i++;
913                                 break;
914                         }
915                 }
916         }
917         mtx_unlock(&sem_lock);
918         KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
919
920         /*
921          * Now cause p2 to enter each of the referenced semaphores, then
922          * release our temporary reference.  This is pretty inefficient.
923          * Finally, free our temporary array.
924          */
925         for (i = 0; i < count; i++) {
926                 sem_enter(p2, sem_array[i]);
927                 mtx_lock(&sem_lock);
928                 sem_rel(sem_array[i]);
929                 mtx_unlock(&sem_lock);
930         }
931         free(sem_array, M_TEMP);
932 }
933
934 static void
935 sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
936 {
937         sem_exithook(arg, p);           
938 }
939
940 static void
941 sem_exithook(void *arg, struct proc *p)
942 {
943         struct ksem *ks, *ksnext;
944
945         mtx_lock(&sem_lock);
946         ks = LIST_FIRST(&ksem_head);
947         while (ks != NULL) {
948                 ksnext = LIST_NEXT(ks, ks_entry);
949                 sem_leave(p, ks);
950                 ks = ksnext;
951         }
952         ks = LIST_FIRST(&ksem_deadhead);
953         while (ks != NULL) {
954                 ksnext = LIST_NEXT(ks, ks_entry);
955                 sem_leave(p, ks);
956                 ks = ksnext;
957         }
958         mtx_unlock(&sem_lock);
959 }
960
961 static int
962 sem_modload(struct module *module, int cmd, void *arg)
963 {
964         int error = 0;
965
966         switch (cmd) {
967         case MOD_LOAD:
968                 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
969                 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
970                 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
971                 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
972                     sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
973                 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
974                     sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
975                 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
976                     sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
977                 break;
978
979         case MOD_UNLOAD:
980                 if (nsems != 0) {
981                         error = EOPNOTSUPP;
982                         break;
983                 }
984                 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
985                 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
986                 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
987                 mtx_destroy(&sem_lock);
988                 break;
989
990         case MOD_SHUTDOWN:
991                 break;
992         default:
993                 error = EINVAL;
994                 break;
995         }
996         return (error);
997 }
998
999 static moduledata_t sem_mod = {
1000         "sem",
1001         &sem_modload,
1002         NULL
1003 };
1004
1005 SYSCALL_MODULE_HELPER(ksem_init);
1006 SYSCALL_MODULE_HELPER(ksem_open);
1007 SYSCALL_MODULE_HELPER(ksem_unlink);
1008 SYSCALL_MODULE_HELPER(ksem_close);
1009 SYSCALL_MODULE_HELPER(ksem_post);
1010 SYSCALL_MODULE_HELPER(ksem_wait);
1011 SYSCALL_MODULE_HELPER(ksem_timedwait);
1012 SYSCALL_MODULE_HELPER(ksem_trywait);
1013 SYSCALL_MODULE_HELPER(ksem_getvalue);
1014 SYSCALL_MODULE_HELPER(ksem_destroy);
1015
1016 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1017 MODULE_VERSION(sem, 1);