2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Implementation of SVID semaphores
28 * Author: Daniel Boulet
30 * This software is provided ``AS IS'' without any warranties of any kind.
33 * John Bellardo modified the implementation for Darwin. 12/2000
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <mach/mach_types.h>
44 #include <sys/filedesc.h>
46 #include <sys/kern_audit.h>
47 #include <sys/sysctl.h>
49 /*#include <sys/sysproto.h>*/
50 /*#include <sys/sysent.h>*/
52 /* Uncomment this line to see the debugging output */
53 /* #define SEM_DEBUG */
55 /* Macros to deal with the semaphore subsystem lock. The lock currently uses
56 * the semlock_holder static variable as a mutex. NULL means no lock, any
57 * value other than NULL means locked. semlock_holder is used because it was
58 * present in the code before the Darwin port, and for no other reason.
59 * When the time comes to relax the funnel requirements of the kernel only
60 * these macros should need to be changed. A spin lock would work well.
63 #define SUBSYSTEM_LOCK_AQUIRE(p) { sysv_sem_aquiring_threads++; \
64 while (semlock_holder != NULL) \
65 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "sysvsem", 0); \
67 sysv_sem_aquiring_threads--; }
69 /* Release the lock */
70 #define SUBSYSTEM_LOCK_RELEASE { semlock_holder = NULL; wakeup((caddr_t)&semlock_holder); }
72 /* Release the lock and return a value */
73 #define UNLOCK_AND_RETURN(ret) { SUBSYSTEM_LOCK_RELEASE; return(ret); }
75 #define M_SYSVSEM M_SUBPROC
78 static void seminit
__P((void *));
79 SYSINIT(sysv_sem
, SI_SUB_SYSV_SEM
, SI_ORDER_FIRST
, seminit
, NULL
)
82 /* Hard system limits to avoid resource starvation / DOS attacks.
83 * These are not needed if we can make the semaphore pages swappable.
85 static struct seminfo limitseminfo
= {
86 SEMMAP
, /* # of entries in semaphore map */
87 SEMMNI
, /* # of semaphore identifiers */
88 SEMMNS
, /* # of semaphores in system */
89 SEMMNU
, /* # of undo structures in system */
90 SEMMSL
, /* max # of semaphores per id */
91 SEMOPM
, /* max # of operations per semop call */
92 SEMUME
, /* max # of undo entries per process */
93 SEMUSZ
, /* size in bytes of undo structure */
94 SEMVMX
, /* semaphore maximum value */
95 SEMAEM
/* adjust on exit max value */
98 /* Current system allocations. We use this structure to track how many
99 * resources we have allocated so far. This way we can set large hard limits
100 * and not allocate the memory for them up front.
102 struct seminfo seminfo
= {
103 SEMMAP
, /* Unused, # of entries in semaphore map */
104 0, /* # of semaphore identifiers */
105 0, /* # of semaphores in system */
106 0, /* # of undo entries in system */
107 SEMMSL
, /* max # of semaphores per id */
108 SEMOPM
, /* max # of operations per semop call */
109 SEMUME
, /* max # of undo entries per process */
110 SEMUSZ
, /* size in bytes of undo structure */
111 SEMVMX
, /* semaphore maximum value */
112 SEMAEM
/* adjust on exit max value */
115 /* A counter so the module unload code knows when there are no more processes using
116 * the sysv_sem code */
117 static long sysv_sem_sleeping_threads
= 0;
118 static long sysv_sem_aquiring_threads
= 0;
121 int semctl
__P((struct proc
*p
, struct semctl_args
*uap
, int *));
123 int semget
__P((struct proc
*p
, struct semget_args
*uap
, int *));
125 int semop
__P((struct proc
*p
, struct semop_args
*uap
, int *));
126 struct semconfig_args
;
127 int semconfig
__P((struct proc
*p
, struct semconfig_args
*uap
, int *));
130 static struct sem_undo
*semu_alloc
__P((struct proc
*p
));
131 static int semundo_adjust
__P((struct proc
*p
, struct sem_undo
**supptr
,
132 int semid
, int semnum
, int adjval
));
133 static void semundo_clear
__P((int semid
, int semnum
));
135 typedef int sy_call_t
__P((struct proc
*, void *, int *));
137 /* XXX casting to (sy_call_t *) is bogus, as usual. */
138 static sy_call_t
*semcalls
[] = {
139 (sy_call_t
*)semctl
, (sy_call_t
*)semget
,
140 (sy_call_t
*)semop
, (sy_call_t
*)semconfig
143 static int semtot
= 0; /* # of used semaphores */
144 struct semid_ds
*sema
= NULL
; /* semaphore id pool */
145 struct sem
*sem
= NULL
; /* semaphore pool */
146 static struct sem_undo
*semu_list
= NULL
; /* list of active undo structures */
147 struct sem_undo
*semu
= NULL
; /* semaphore undo pool */
149 static struct proc
*semlock_holder
= NULL
;
151 /* seminit no longer needed. The data structures are grown dynamically */
158 * Entry point for all SEM calls
160 * In Darwin this is no longer the entry point. It will be removed after
161 * the code has been tested better.
171 semsys(p
, uap
, retval
)
173 /* XXX actually varargs. */
174 struct semsys_args
*uap
;
178 /* The individual calls handling the locking now */
179 /*while (semlock_holder != NULL && semlock_holder != p)
180 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
183 if (uap
->which
>= sizeof(semcalls
)/sizeof(semcalls
[0]))
185 return ((*semcalls
[uap
->which
])(p
, &uap
->a2
, retval
));
189 * Lock or unlock the entire semaphore facility.
191 * This will probably eventually evolve into a general purpose semaphore
192 * facility status enquiry mechanism (I don't like the "read /dev/kmem"
193 * approach currently taken by ipcs and the amount of info that we want
194 * to be able to extract for ipcs is probably beyond what the capability
195 * of the getkerninfo facility.
197 * At the time that the current version of semconfig was written, ipcs is
198 * the only user of the semconfig facility. It uses it to ensure that the
199 * semaphore facility data structures remain static while it fishes around
203 #ifndef _SYS_SYSPROTO_H_
204 struct semconfig_args
{
205 semconfig_ctl_t flag
;
210 semconfig(p
, uap
, retval
)
212 struct semconfig_args
*uap
;
218 case SEM_CONFIG_FREEZE
:
219 SUBSYSTEM_LOCK_AQUIRE(p
);
222 case SEM_CONFIG_THAW
:
223 SUBSYSTEM_LOCK_RELEASE
;
227 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
237 /* Expand the semu array to the given capacity. If the expansion fails
238 * return 0, otherwise return 1.
240 * Assumes we already have the subsystem lock.
243 grow_semu_array(newSize
)
247 register struct sem_undo
*newSemu
;
248 if (newSize
<= seminfo
.semmnu
)
250 if (newSize
> limitseminfo
.semmnu
) /* enforce hard limit */
253 printf("undo structure hard limit of %d reached, requested %d\n",
254 limitseminfo
.semmnu
, newSize
);
258 newSize
= (newSize
/SEMMNU_INC
+ 1) * SEMMNU_INC
;
259 newSize
= newSize
> limitseminfo
.semmnu
? limitseminfo
.semmnu
: newSize
;
262 printf("growing semu[] from %d to %d\n", seminfo
.semmnu
, newSize
);
264 MALLOC(newSemu
, struct sem_undo
*, sizeof(struct sem_undo
)*newSize
,
265 M_SYSVSEM
, M_WAITOK
);
269 printf("allocation failed. no changes made.\n");
274 /* Initialize our structure. */
275 for (i
= 0; i
< seminfo
.semmnu
; i
++)
277 newSemu
[i
] = semu
[i
];
278 for(j
= 0; j
< SEMUME
; j
++) /* Is this really needed? */
279 newSemu
[i
].un_ent
[j
] = semu
[i
].un_ent
[j
];
281 for (i
= seminfo
.semmnu
; i
< newSize
; i
++)
283 newSemu
[i
].un_proc
= NULL
;
286 /* Clean up the old array */
288 FREE(semu
, M_SYSVSEM
);
291 seminfo
.semmnu
= newSize
;
293 printf("expansion successful\n");
299 * Expand the sema array to the given capacity. If the expansion fails
300 * we return 0, otherwise we return 1.
302 * Assumes we already have the subsystem lock.
305 grow_sema_array(newSize
)
308 register struct semid_ds
*newSema
;
311 if (newSize
<= seminfo
.semmni
)
313 if (newSize
> limitseminfo
.semmni
) /* enforce hard limit */
316 printf("identifier hard limit of %d reached, requested %d\n",
317 limitseminfo
.semmni
, newSize
);
321 newSize
= (newSize
/SEMMNI_INC
+ 1) * SEMMNI_INC
;
322 newSize
= newSize
> limitseminfo
.semmni
? limitseminfo
.semmni
: newSize
;
325 printf("growing sema[] from %d to %d\n", seminfo
.semmni
, newSize
);
327 MALLOC(newSema
, struct semid_ds
*, sizeof(struct semid_ds
)*newSize
,
328 M_SYSVSEM
, M_WAITOK
);
332 printf("allocation failed. no changes made.\n");
337 /* Initialize our new ids, and copy over the old ones */
338 for (i
= 0; i
< seminfo
.semmni
; i
++)
340 newSema
[i
] = sema
[i
];
341 /* This is a hack. What we really want to be able to
342 * do is change the value a process is waiting on
343 * without waking it up, but I don't know how to do
344 * this with the existing code, so we wake up the
345 * process and let it do a lot of work to determine the
346 * semaphore set is really not available yet, and then
347 * sleep on the correct, reallocated semid_ds pointer.
349 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
)
350 wakeup((caddr_t
)&sema
[i
]);
353 for (i
= seminfo
.semmni
; i
< newSize
; i
++)
355 newSema
[i
].sem_base
= 0;
356 newSema
[i
].sem_perm
.mode
= 0;
359 /* Clean up the old array */
361 FREE(sema
, M_SYSVSEM
);
364 seminfo
.semmni
= newSize
;
366 printf("expansion successful\n");
372 * Expand the sem array to the given capacity. If the expansion fails
373 * we return 0 (fail), otherwise we return 1 (success).
375 * Assumes we already hold the subsystem lock.
378 grow_sem_array(newSize
)
381 register struct sem
*newSem
= NULL
;
384 if (newSize
< semtot
)
386 if (newSize
> limitseminfo
.semmns
) /* enforce hard limit */
389 printf("semaphore hard limit of %d reached, requested %d\n",
390 limitseminfo
.semmns
, newSize
);
394 newSize
= (newSize
/SEMMNS_INC
+ 1) * SEMMNS_INC
;
395 newSize
= newSize
> limitseminfo
.semmns
? limitseminfo
.semmns
: newSize
;
398 printf("growing sem array from %d to %d\n", seminfo
.semmns
, newSize
);
400 MALLOC(newSem
, struct sem
*, sizeof(struct sem
)*newSize
,
401 M_SYSVSEM
, M_WAITOK
);
405 printf("allocation failed. no changes made.\n");
410 /* We have our new memory, now copy the old contents over */
412 for(i
= 0; i
< seminfo
.semmns
; i
++)
415 /* Update our id structures to point to the new semaphores */
416 for(i
= 0; i
< seminfo
.semmni
; i
++)
417 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
) /* ID in use */
420 sema
[i
].sem_base
+= newSem
- sem
;
422 sema
[i
].sem_base
-= sem
- newSem
;
425 /* clean up the old array */
427 FREE(sem
, M_SYSVSEM
);
430 seminfo
.semmns
= newSize
;
432 printf("expansion complete\n");
438 * Allocate a new sem_undo structure for a process
439 * (returns ptr to structure or NULL if no more room)
441 * Assumes we already hold the subsystem lock.
444 static struct sem_undo
*
449 register struct sem_undo
*suptr
;
450 register struct sem_undo
**supptr
;
454 * Try twice to allocate something.
455 * (we'll purge any empty structures after the first pass so
456 * two passes are always enough)
459 for (attempt
= 0; attempt
< 2; attempt
++) {
461 * Look for a free structure.
462 * Fill it in and return it if we find one.
465 for (i
= 0; i
< seminfo
.semmnu
; i
++) {
467 if (suptr
->un_proc
== NULL
) {
468 suptr
->un_next
= semu_list
;
477 * We didn't find a free one, if this is the first attempt
478 * then try to free some structures.
482 /* All the structures are in use - try to free some */
483 int did_something
= 0;
486 while ((suptr
= *supptr
) != NULL
) {
487 if (suptr
->un_cnt
== 0) {
488 suptr
->un_proc
= NULL
;
489 *supptr
= suptr
->un_next
;
492 supptr
= &(suptr
->un_next
);
495 /* If we didn't free anything. Try expanding
496 * the semu[] array. If that doesn't work
497 * then fail. We expand last to get the
498 * most reuse out of existing resources.
501 if (!grow_semu_array(seminfo
.semmnu
+ 1))
505 * The second pass failed even though we freed
506 * something after the first pass!
507 * This is IMPOSSIBLE!
509 panic("semu_alloc - second attempt failed");
516 * Adjust a particular entry for a particular proc
518 * Assumes we already hold the subsystem lock.
522 semundo_adjust(p
, supptr
, semid
, semnum
, adjval
)
523 register struct proc
*p
;
524 struct sem_undo
**supptr
;
528 register struct sem_undo
*suptr
;
529 register struct undo
*sunptr
;
532 /* Look for and remember the sem_undo if the caller doesn't provide
537 for (suptr
= semu_list
; suptr
!= NULL
;
538 suptr
= suptr
->un_next
) {
539 if (suptr
->un_proc
== p
) {
547 suptr
= semu_alloc(p
);
555 * Look for the requested entry and adjust it (delete if adjval becomes
558 sunptr
= &suptr
->un_ent
[0];
559 for (i
= 0; i
< suptr
->un_cnt
; i
++, sunptr
++) {
560 if (sunptr
->un_id
!= semid
|| sunptr
->un_num
!= semnum
)
563 sunptr
->un_adjval
= 0;
565 sunptr
->un_adjval
+= adjval
;
566 if (sunptr
->un_adjval
== 0) {
568 if (i
< suptr
->un_cnt
)
570 suptr
->un_ent
[suptr
->un_cnt
];
575 /* Didn't find the right entry - create it */
578 if (suptr
->un_cnt
!= limitseminfo
.semume
) {
579 sunptr
= &suptr
->un_ent
[suptr
->un_cnt
];
581 sunptr
->un_adjval
= adjval
;
582 sunptr
->un_id
= semid
; sunptr
->un_num
= semnum
;
588 /* Assumes we already hold the subsystem lock.
591 semundo_clear(semid
, semnum
)
594 register struct sem_undo
*suptr
;
596 for (suptr
= semu_list
; suptr
!= NULL
; suptr
= suptr
->un_next
) {
597 register struct undo
*sunptr
= &suptr
->un_ent
[0];
600 while (i
< suptr
->un_cnt
) {
601 if (sunptr
->un_id
== semid
) {
602 if (semnum
== -1 || sunptr
->un_num
== semnum
) {
604 if (i
< suptr
->un_cnt
) {
606 suptr
->un_ent
[suptr
->un_cnt
];
619 * Note that the user-mode half of this passes a union, not a pointer
621 #ifndef _SYS_SYSPROTO_H_
631 semctl(p
, uap
, retval
)
633 register struct semctl_args
*uap
;
636 int semid
= uap
->semid
;
637 int semnum
= uap
->semnum
;
639 union semun arg
= uap
->arg
;
640 union semun real_arg
;
641 struct ucred
*cred
= p
->p_ucred
;
643 struct semid_ds sbuf
;
644 register struct semid_ds
*semaptr
;
646 AUDIT_ARG(svipc_cmd
, cmd
);
647 AUDIT_ARG(svipc_id
, semid
);
648 SUBSYSTEM_LOCK_AQUIRE(p
);
650 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid
, semnum
, cmd
, arg
);
653 semid
= IPCID_TO_IX(semid
);
654 if (semid
< 0 || semid
>= seminfo
.semmni
)
657 printf("Invalid semid\n");
659 UNLOCK_AND_RETURN(EINVAL
);
662 semaptr
= &sema
[semid
];
663 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
664 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
))
665 UNLOCK_AND_RETURN(EINVAL
);
672 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
673 UNLOCK_AND_RETURN(eval
);
674 semaptr
->sem_perm
.cuid
= cred
->cr_uid
;
675 semaptr
->sem_perm
.uid
= cred
->cr_uid
;
676 semtot
-= semaptr
->sem_nsems
;
677 for (i
= semaptr
->sem_base
- sem
; i
< semtot
; i
++)
678 sem
[i
] = sem
[i
+ semaptr
->sem_nsems
];
679 for (i
= 0; i
< seminfo
.semmni
; i
++) {
680 if ((sema
[i
].sem_perm
.mode
& SEM_ALLOC
) &&
681 sema
[i
].sem_base
> semaptr
->sem_base
)
682 sema
[i
].sem_base
-= semaptr
->sem_nsems
;
684 semaptr
->sem_perm
.mode
= 0;
685 semundo_clear(semid
, -1);
686 wakeup((caddr_t
)semaptr
);
690 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
691 UNLOCK_AND_RETURN(eval
);
692 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
693 UNLOCK_AND_RETURN(eval);*/
694 if ((eval
= copyin(arg
.buf
, (caddr_t
)&sbuf
,
696 UNLOCK_AND_RETURN(eval
);
697 semaptr
->sem_perm
.uid
= sbuf
.sem_perm
.uid
;
698 semaptr
->sem_perm
.gid
= sbuf
.sem_perm
.gid
;
699 semaptr
->sem_perm
.mode
= (semaptr
->sem_perm
.mode
& ~0777) |
700 (sbuf
.sem_perm
.mode
& 0777);
701 semaptr
->sem_ctime
= time_second
;
705 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
706 UNLOCK_AND_RETURN(eval
);
707 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
708 UNLOCK_AND_RETURN(eval);*/
709 eval
= copyout((caddr_t
)semaptr
, arg
.buf
,
710 sizeof(struct semid_ds
));
714 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
715 UNLOCK_AND_RETURN(eval
);
716 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
717 UNLOCK_AND_RETURN(EINVAL
);
718 rval
= semaptr
->sem_base
[semnum
].semncnt
;
722 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
723 UNLOCK_AND_RETURN(eval
);
724 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
725 UNLOCK_AND_RETURN(EINVAL
);
726 rval
= semaptr
->sem_base
[semnum
].sempid
;
730 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
731 UNLOCK_AND_RETURN(eval
);
732 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
733 UNLOCK_AND_RETURN(EINVAL
);
734 rval
= semaptr
->sem_base
[semnum
].semval
;
738 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
739 UNLOCK_AND_RETURN(eval
);
740 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
741 UNLOCK_AND_RETURN(eval);*/
742 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
743 eval
= copyout((caddr_t
)&semaptr
->sem_base
[i
].semval
,
744 &arg
.array
[i
], sizeof(arg
.array
[0]));
751 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
752 UNLOCK_AND_RETURN(eval
);
753 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
754 UNLOCK_AND_RETURN(EINVAL
);
755 rval
= semaptr
->sem_base
[semnum
].semzcnt
;
759 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
762 printf("Invalid credentials for write\n");
764 UNLOCK_AND_RETURN(eval
);
766 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
769 printf("Invalid number out of range for set\n");
771 UNLOCK_AND_RETURN(EINVAL
);
773 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
776 printf("Error during value copyin\n");
778 UNLOCK_AND_RETURN(eval);
780 semaptr
->sem_base
[semnum
].semval
= arg
.val
;
781 semundo_clear(semid
, semnum
);
782 wakeup((caddr_t
)semaptr
);
786 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
787 UNLOCK_AND_RETURN(eval
);
788 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
789 UNLOCK_AND_RETURN(eval);*/
790 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
791 eval
= copyin(&arg
.array
[i
],
792 (caddr_t
)&semaptr
->sem_base
[i
].semval
,
793 sizeof(arg
.array
[0]));
797 semundo_clear(semid
, -1);
798 wakeup((caddr_t
)semaptr
);
802 UNLOCK_AND_RETURN(EINVAL
);
807 UNLOCK_AND_RETURN(eval
);
810 #ifndef _SYS_SYSPROTO_H_
819 semget(p
, uap
, retval
)
821 register struct semget_args
*uap
;
826 int nsems
= uap
->nsems
;
827 int semflg
= uap
->semflg
;
828 struct ucred
*cred
= p
->p_ucred
;
830 SUBSYSTEM_LOCK_AQUIRE(p
);
832 if (key
!= IPC_PRIVATE
)
833 printf("semget(0x%x, %d, 0%o)\n", key
, nsems
, semflg
);
835 printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems
, semflg
);
838 if (key
!= IPC_PRIVATE
) {
839 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
840 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) &&
841 sema
[semid
].sem_perm
.key
== key
)
844 if (semid
< seminfo
.semmni
) {
846 printf("found public key\n");
848 if ((eval
= ipcperm(cred
, &sema
[semid
].sem_perm
,
850 UNLOCK_AND_RETURN(eval
);
851 if (nsems
> 0 && sema
[semid
].sem_nsems
< nsems
) {
853 printf("too small\n");
855 UNLOCK_AND_RETURN(EINVAL
);
857 if ((semflg
& IPC_CREAT
) && (semflg
& IPC_EXCL
)) {
859 printf("not exclusive\n");
861 UNLOCK_AND_RETURN(EEXIST
);
868 printf("need to allocate an id for the request\n");
870 if (key
== IPC_PRIVATE
|| (semflg
& IPC_CREAT
)) {
871 if (nsems
<= 0 || nsems
> limitseminfo
.semmsl
) {
873 printf("nsems out of range (0<%d<=%d)\n", nsems
,
876 UNLOCK_AND_RETURN(EINVAL
);
878 if (nsems
> seminfo
.semmns
- semtot
) {
880 printf("not enough semaphores left (need %d, got %d)\n",
881 nsems
, seminfo
.semmns
- semtot
);
883 if (!grow_sem_array(semtot
+ nsems
))
886 printf("failed to grow the sem array\n");
888 UNLOCK_AND_RETURN(ENOSPC
);
891 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
892 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) == 0)
895 if (semid
== seminfo
.semmni
) {
897 printf("no more id's available\n");
899 if (!grow_sema_array(seminfo
.semmni
+ 1))
902 printf("failed to grow sema array\n");
904 UNLOCK_AND_RETURN(ENOSPC
);
908 printf("semid %d is available\n", semid
);
910 sema
[semid
].sem_perm
.key
= key
;
911 sema
[semid
].sem_perm
.cuid
= cred
->cr_uid
;
912 sema
[semid
].sem_perm
.uid
= cred
->cr_uid
;
913 sema
[semid
].sem_perm
.cgid
= cred
->cr_gid
;
914 sema
[semid
].sem_perm
.gid
= cred
->cr_gid
;
915 sema
[semid
].sem_perm
.mode
= (semflg
& 0777) | SEM_ALLOC
;
916 sema
[semid
].sem_perm
.seq
=
917 (sema
[semid
].sem_perm
.seq
+ 1) & 0x7fff;
918 sema
[semid
].sem_nsems
= nsems
;
919 sema
[semid
].sem_otime
= 0;
920 sema
[semid
].sem_ctime
= time_second
;
921 sema
[semid
].sem_base
= &sem
[semtot
];
923 bzero(sema
[semid
].sem_base
,
924 sizeof(sema
[semid
].sem_base
[0])*nsems
);
926 printf("sembase = 0x%x, next = 0x%x\n", sema
[semid
].sem_base
,
931 printf("didn't find it and wasn't asked to create it\n");
933 UNLOCK_AND_RETURN(ENOENT
);
937 *retval
= IXSEQ_TO_IPCID(semid
, sema
[semid
].sem_perm
);
938 AUDIT_ARG(svipc_id
, *retval
);
940 printf("semget is done, returning %d\n", *retval
);
942 SUBSYSTEM_LOCK_RELEASE
;
946 #ifndef _SYS_SYSPROTO_H_
955 semop(p
, uap
, retval
)
957 register struct semop_args
*uap
;
960 int semid
= uap
->semid
;
961 int nsops
= uap
->nsops
;
962 struct sembuf sops
[MAX_SOPS
];
963 register struct semid_ds
*semaptr
;
964 register struct sembuf
*sopptr
;
965 register struct sem
*semptr
;
966 struct sem_undo
*suptr
= NULL
;
967 struct ucred
*cred
= p
->p_ucred
;
969 int do_wakeup
, do_undos
;
971 AUDIT_ARG(svipc_id
, uap
->semid
);
972 SUBSYSTEM_LOCK_AQUIRE(p
);
974 printf("call to semop(%d, 0x%x, %d)\n", semid
, sops
, nsops
);
977 semid
= IPCID_TO_IX(semid
); /* Convert back to zero origin */
979 if (semid
< 0 || semid
>= seminfo
.semmni
)
980 UNLOCK_AND_RETURN(EINVAL
);
982 semaptr
= &sema
[semid
];
983 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0)
984 UNLOCK_AND_RETURN(EINVAL
);
985 if (semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
))
986 UNLOCK_AND_RETURN(EINVAL
);
988 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
))) {
990 printf("eval = %d from ipaccess\n", eval
);
992 UNLOCK_AND_RETURN(eval
);
995 if (nsops
> MAX_SOPS
) {
997 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS
, nsops
);
999 UNLOCK_AND_RETURN(E2BIG
);
1002 if ((eval
= copyin(uap
->sops
, &sops
, nsops
* sizeof(sops
[0]))) != 0) {
1004 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval
,
1005 uap
->sops
, &sops
, nsops
* sizeof(sops
[0]));
1007 UNLOCK_AND_RETURN(eval
);
1011 * Loop trying to satisfy the vector of requests.
1012 * If we reach a point where we must wait, any requests already
1013 * performed are rolled back and we go to sleep until some other
1014 * process wakes us up. At this point, we start all over again.
1016 * This ensures that from the perspective of other tasks, a set
1017 * of requests is atomic (never partially satisfied).
1024 for (i
= 0; i
< nsops
; i
++) {
1027 if (sopptr
->sem_num
>= semaptr
->sem_nsems
)
1028 UNLOCK_AND_RETURN(EFBIG
);
1030 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1033 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1034 semaptr
, semaptr
->sem_base
, semptr
,
1035 sopptr
->sem_num
, semptr
->semval
, sopptr
->sem_op
,
1036 (sopptr
->sem_flg
& IPC_NOWAIT
) ? "nowait" : "wait");
1039 if (sopptr
->sem_op
< 0) {
1040 if (semptr
->semval
+ sopptr
->sem_op
< 0) {
1042 printf("semop: can't do it now\n");
1046 semptr
->semval
+= sopptr
->sem_op
;
1047 if (semptr
->semval
== 0 &&
1048 semptr
->semzcnt
> 0)
1051 if (sopptr
->sem_flg
& SEM_UNDO
)
1053 } else if (sopptr
->sem_op
== 0) {
1054 if (semptr
->semval
> 0) {
1056 printf("semop: not zero now\n");
1061 if (semptr
->semncnt
> 0)
1063 semptr
->semval
+= sopptr
->sem_op
;
1064 if (sopptr
->sem_flg
& SEM_UNDO
)
1070 * Did we get through the entire vector?
1076 * No ... rollback anything that we've already done
1079 printf("semop: rollback 0 through %d\n", i
-1);
1081 for (j
= 0; j
< i
; j
++)
1082 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1086 * If the request that we couldn't satisfy has the
1087 * NOWAIT flag set then return with EAGAIN.
1089 if (sopptr
->sem_flg
& IPC_NOWAIT
)
1090 UNLOCK_AND_RETURN(EAGAIN
);
1092 if (sopptr
->sem_op
== 0)
1098 printf("semop: good night!\n");
1100 /* Release our lock on the semaphore subsystem so
1101 * another thread can get at the semaphore we are
1102 * waiting for. We will get the lock back after we
1105 SUBSYSTEM_LOCK_RELEASE
;
1106 sysv_sem_sleeping_threads
++;
1107 eval
= tsleep((caddr_t
)semaptr
, (PZERO
- 4) | PCATCH
,
1109 sysv_sem_sleeping_threads
--;
1112 printf("semop: good morning (eval=%d)!\n", eval
);
1114 /* There is no need to get the lock if we are just
1115 * going to return without performing more semaphore
1121 SUBSYSTEM_LOCK_AQUIRE(p
); /* Get it back */
1122 suptr
= NULL
; /* sem_undo may have been reallocated */
1123 semaptr
= &sema
[semid
]; /* sema may have been reallocated */
1127 printf("semop: good morning!\n");
1131 * Make sure that the semaphore still exists
1133 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
1134 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1135 /* The man page says to return EIDRM. */
1136 /* Unfortunately, BSD doesn't define that code! */
1138 UNLOCK_AND_RETURN(EIDRM
);
1140 UNLOCK_AND_RETURN(EINVAL
);
1145 * The semaphore is still alive. Readjust the count of
1146 * waiting processes. semptr needs to be recomputed
1147 * because the sem[] may have been reallocated while
1148 * we were sleeping, updating our sem_base pointer.
1150 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1151 if (sopptr
->sem_op
== 0)
1159 * Process any SEM_UNDO requests.
1162 for (i
= 0; i
< nsops
; i
++) {
1164 * We only need to deal with SEM_UNDO's for non-zero
1169 if ((sops
[i
].sem_flg
& SEM_UNDO
) == 0)
1171 adjval
= sops
[i
].sem_op
;
1174 eval
= semundo_adjust(p
, &suptr
, semid
,
1175 sops
[i
].sem_num
, -adjval
);
1180 * Oh-Oh! We ran out of either sem_undo's or undo's.
1181 * Rollback the adjustments to this point and then
1182 * rollback the semaphore ups and down so we can return
1183 * with an error with all structures restored. We
1184 * rollback the undo's in the exact reverse order that
1185 * we applied them. This guarantees that we won't run
1186 * out of space as we roll things back out.
1188 for (j
= i
- 1; j
>= 0; j
--) {
1189 if ((sops
[j
].sem_flg
& SEM_UNDO
) == 0)
1191 adjval
= sops
[j
].sem_op
;
1194 if (semundo_adjust(p
, &suptr
, semid
,
1195 sops
[j
].sem_num
, adjval
) != 0)
1196 panic("semop - can't undo undos");
1199 for (j
= 0; j
< nsops
; j
++)
1200 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1204 printf("eval = %d from semundo_adjust\n", eval
);
1206 UNLOCK_AND_RETURN(eval
);
1207 } /* loop through the sops */
1208 } /* if (do_undos) */
1210 /* We're definitely done - set the sempid's */
1211 for (i
= 0; i
< nsops
; i
++) {
1213 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1214 semptr
->sempid
= p
->p_pid
;
1217 /* Do a wakeup if any semaphore was up'd.
1218 * we will release our lock on the semaphore subsystem before
1219 * we wakeup other processes to prevent a little thrashing.
1220 * Note that this is fine because we are done using the
1221 * semaphore structures at this point in time. We only use
1222 * a local variable pointer value, and the retval
1224 * Note 2: Future use of sem_wakeup may reqiure the lock.
1226 SUBSYSTEM_LOCK_RELEASE
;
1229 printf("semop: doing wakeup\n");
1231 sem_wakeup((caddr_t
)semaptr
);
1233 wakeup((caddr_t
)semaptr
);
1235 printf("semop: back from wakeup\n");
1237 wakeup((caddr_t
)semaptr
);
1241 printf("semop: done\n");
1248 * Go through the undo structures for this process and apply the adjustments to
1255 register struct sem_undo
*suptr
;
1256 register struct sem_undo
**supptr
;
1259 /* If we have not allocated our semaphores yet there can't be
1260 * anything to undo, but we need the lock to prevent
1261 * dynamic memory race conditions.
1263 SUBSYSTEM_LOCK_AQUIRE(p
);
1266 SUBSYSTEM_LOCK_RELEASE
;
1272 * Go through the chain of undo vectors looking for one
1273 * associated with this process.
1276 for (supptr
= &semu_list
; (suptr
= *supptr
) != NULL
;
1277 supptr
= &suptr
->un_next
) {
1278 if (suptr
->un_proc
== p
)
1286 printf("proc @%08x has undo structure with %d entries\n", p
,
1291 * If there are any active undo elements then process them.
1293 if (suptr
->un_cnt
> 0) {
1296 for (ix
= 0; ix
< suptr
->un_cnt
; ix
++) {
1297 int semid
= suptr
->un_ent
[ix
].un_id
;
1298 int semnum
= suptr
->un_ent
[ix
].un_num
;
1299 int adjval
= suptr
->un_ent
[ix
].un_adjval
;
1300 struct semid_ds
*semaptr
;
1302 semaptr
= &sema
[semid
];
1303 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0)
1304 panic("semexit - semid not allocated");
1305 if (semnum
>= semaptr
->sem_nsems
)
1306 panic("semexit - semnum out of range");
1309 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1310 suptr
->un_proc
, suptr
->un_ent
[ix
].un_id
,
1311 suptr
->un_ent
[ix
].un_num
,
1312 suptr
->un_ent
[ix
].un_adjval
,
1313 semaptr
->sem_base
[semnum
].semval
);
1317 if (semaptr
->sem_base
[semnum
].semval
< -adjval
)
1318 semaptr
->sem_base
[semnum
].semval
= 0;
1320 semaptr
->sem_base
[semnum
].semval
+=
1323 semaptr
->sem_base
[semnum
].semval
+= adjval
;
1325 /* Maybe we should build a list of semaptr's to wake
1326 * up, finish all access to data structures, release the
1327 * subsystem lock, and wake all the processes. Something
1328 * to think about. It wouldn't buy us anything unless
1329 * wakeup had the potential to block, or the syscall
1330 * funnel state was changed to allow multiple threads
1331 * in the BSD code at once.
1334 sem_wakeup((caddr_t
)semaptr
);
1336 wakeup((caddr_t
)semaptr
);
1339 printf("semexit: back from wakeup\n");
1345 * Deallocate the undo vector.
1348 printf("removing vector\n");
1350 suptr
->un_proc
= NULL
;
1351 *supptr
= suptr
->un_next
;
1355 * There is a semaphore leak (i.e. memory leak) in this code.
1356 * We should be deleting the IPC_PRIVATE semaphores when they are
1357 * no longer needed, and we dont. We would have to track which processes
1358 * know about which IPC_PRIVATE semaphores, updating the list after
1359 * every fork. We can't just delete them semaphore when the process
1360 * that created it dies, because that process may well have forked
1361 * some children. So we need to wait until all of it's children have
1362 * died, and so on. Maybe we should tag each IPC_PRIVATE sempahore
1363 * with the creating group ID, count the number of processes left in
1364 * that group, and delete the semaphore when the group is gone.
1365 * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1366 * There is an upper bound on the size of our semaphore array, so
1367 * leaking the semaphores should not work as a DOS attack.
1369 * Please note that the original BSD code this file is based on had the
1370 * same leaky semaphore problem.
1373 SUBSYSTEM_LOCK_RELEASE
;
1375 /* (struct sysctl_oid *oidp, void *arg1, int arg2, \
1376 struct sysctl_req *req) */
1378 sysctl_seminfo SYSCTL_HANDLER_ARGS
1382 error
= SYSCTL_OUT(req
, arg1
, sizeof(int));
1383 if (error
|| !req
->newptr
)
1386 SUBSYSTEM_LOCK_AQUIRE(current_proc());
1387 /* Set the values only if shared memory is not initialised */
1388 if ((sem
== (struct sem
*) 0) &&
1389 (sema
== (struct semid_ds
*) 0) &&
1390 (semu
== (struct semid_ds
*) 0) &&
1391 (semu_list
== (struct sem_undo
*) 0)) {
1392 if (error
= SYSCTL_IN(req
, arg1
, sizeof(int))) {
1398 SUBSYSTEM_LOCK_RELEASE
;
1403 /* SYSCTL_NODE(_kern, KERN_SYSV, sysv, CTLFLAG_RW, 0, "SYSV"); */
1404 extern struct sysctl_oid_list sysctl__kern_sysv_children
;
1405 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNI
, semmni
, CTLTYPE_INT
| CTLFLAG_RW
,
1406 &limitseminfo
.semmni
, 0, &sysctl_seminfo
,"I","semmni");
1408 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNS
, semmns
, CTLTYPE_INT
| CTLFLAG_RW
,
1409 &limitseminfo
.semmns
, 0, &sysctl_seminfo
,"I","semmns");
1411 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMNU
, semmnu
, CTLTYPE_INT
| CTLFLAG_RW
,
1412 &limitseminfo
.semmnu
, 0, &sysctl_seminfo
,"I","semmnu");
1414 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMMSL
, semmsl
, CTLTYPE_INT
| CTLFLAG_RW
,
1415 &limitseminfo
.semmsl
, 0, &sysctl_seminfo
,"I","semmsl");
1417 SYSCTL_PROC(_kern_sysv
, KSYSV_SEMUNE
, semume
, CTLTYPE_INT
| CTLFLAG_RW
,
1418 &limitseminfo
.semume
, 0, &sysctl_seminfo
,"I","semume");