2 * Copyright (c) 2000 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>
47 /*#include <sys/sysproto.h>*/
48 /*#include <sys/sysent.h>*/
50 /* Uncomment this line to see the debugging output */
51 /* #define SEM_DEBUG */
53 /* Macros to deal with the semaphore subsystem lock. The lock currently uses
54 * the semlock_holder static variable as a mutex. NULL means no lock, any
55 * value other than NULL means locked. semlock_holder is used because it was
56 * present in the code before the Darwin port, and for no other reason.
57 * When the time comes to relax the funnel requirements of the kernel only
58 * these macros should need to be changed. A spin lock would work well.
61 #define SUBSYSTEM_LOCK_AQUIRE(p) { sysv_sem_aquiring_threads++; \
62 while (semlock_holder != NULL) \
63 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "sysvsem", 0); \
65 sysv_sem_aquiring_threads--; }
67 /* Release the lock */
68 #define SUBSYSTEM_LOCK_RELEASE { semlock_holder = NULL; wakeup((caddr_t)&semlock_holder); }
70 /* Release the lock and return a value */
71 #define UNLOCK_AND_RETURN(ret) { SUBSYSTEM_LOCK_RELEASE; return(ret); }
73 #define M_SYSVSEM M_SUBPROC
76 static void seminit
__P((void *));
77 SYSINIT(sysv_sem
, SI_SUB_SYSV_SEM
, SI_ORDER_FIRST
, seminit
, NULL
)
80 /* Hard system limits to avoid resource starvation / DOS attacks.
81 * These are not needed if we can make the semaphore pages swappable.
83 static struct seminfo limitseminfo
= {
84 SEMMAP
, /* # of entries in semaphore map */
85 SEMMNI
, /* # of semaphore identifiers */
86 SEMMNS
, /* # of semaphores in system */
87 SEMMNU
, /* # of undo structures in system */
88 SEMMSL
, /* max # of semaphores per id */
89 SEMOPM
, /* max # of operations per semop call */
90 SEMUME
, /* max # of undo entries per process */
91 SEMUSZ
, /* size in bytes of undo structure */
92 SEMVMX
, /* semaphore maximum value */
93 SEMAEM
/* adjust on exit max value */
96 /* Current system allocations. We use this structure to track how many
97 * resources we have allocated so far. This way we can set large hard limits
98 * and not allocate the memory for them up front.
100 struct seminfo seminfo
= {
101 SEMMAP
, /* Unused, # of entries in semaphore map */
102 0, /* # of semaphore identifiers */
103 0, /* # of semaphores in system */
104 0, /* # of undo entries in system */
105 SEMMSL
, /* max # of semaphores per id */
106 SEMOPM
, /* max # of operations per semop call */
107 SEMUME
, /* max # of undo entries per process */
108 SEMUSZ
, /* size in bytes of undo structure */
109 SEMVMX
, /* semaphore maximum value */
110 SEMAEM
/* adjust on exit max value */
113 /* A counter so the module unload code knows when there are no more processes using
114 * the sysv_sem code */
115 static long sysv_sem_sleeping_threads
= 0;
116 static long sysv_sem_aquiring_threads
= 0;
119 int semctl
__P((struct proc
*p
, struct semctl_args
*uap
, int *));
121 int semget
__P((struct proc
*p
, struct semget_args
*uap
, int *));
123 int semop
__P((struct proc
*p
, struct semop_args
*uap
, int *));
124 struct semconfig_args
;
125 int semconfig
__P((struct proc
*p
, struct semconfig_args
*uap
, int *));
128 static struct sem_undo
*semu_alloc
__P((struct proc
*p
));
129 static int semundo_adjust
__P((struct proc
*p
, struct sem_undo
**supptr
,
130 int semid
, int semnum
, int adjval
));
131 static void semundo_clear
__P((int semid
, int semnum
));
133 typedef int sy_call_t
__P((struct proc
*, void *, int *));
135 /* XXX casting to (sy_call_t *) is bogus, as usual. */
136 static sy_call_t
*semcalls
[] = {
137 (sy_call_t
*)semctl
, (sy_call_t
*)semget
,
138 (sy_call_t
*)semop
, (sy_call_t
*)semconfig
141 static int semtot
= 0; /* # of used semaphores */
142 struct semid_ds
*sema
= NULL
; /* semaphore id pool */
143 struct sem
*sem
= NULL
; /* semaphore pool */
144 static struct sem_undo
*semu_list
= NULL
; /* list of active undo structures */
145 struct sem_undo
*semu
= NULL
; /* semaphore undo pool */
147 static struct proc
*semlock_holder
= NULL
;
149 /* seminit no longer needed. The data structures are grown dynamically */
156 * Entry point for all SEM calls
158 * In Darwin this is no longer the entry point. It will be removed after
159 * the code has been tested better.
169 semsys(p
, uap
, retval
)
171 /* XXX actually varargs. */
172 struct semsys_args
*uap
;
176 /* The individual calls handling the locking now */
177 /*while (semlock_holder != NULL && semlock_holder != p)
178 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
181 if (uap
->which
>= sizeof(semcalls
)/sizeof(semcalls
[0]))
183 return ((*semcalls
[uap
->which
])(p
, &uap
->a2
, retval
));
187 * Lock or unlock the entire semaphore facility.
189 * This will probably eventually evolve into a general purpose semaphore
190 * facility status enquiry mechanism (I don't like the "read /dev/kmem"
191 * approach currently taken by ipcs and the amount of info that we want
192 * to be able to extract for ipcs is probably beyond what the capability
193 * of the getkerninfo facility.
195 * At the time that the current version of semconfig was written, ipcs is
196 * the only user of the semconfig facility. It uses it to ensure that the
197 * semaphore facility data structures remain static while it fishes around
201 #ifndef _SYS_SYSPROTO_H_
202 struct semconfig_args
{
203 semconfig_ctl_t flag
;
208 semconfig(p
, uap
, retval
)
210 struct semconfig_args
*uap
;
216 case SEM_CONFIG_FREEZE
:
217 SUBSYSTEM_LOCK_AQUIRE(p
);
220 case SEM_CONFIG_THAW
:
221 SUBSYSTEM_LOCK_RELEASE
;
225 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
235 /* Expand the semu array to the given capacity. If the expansion fails
236 * return 0, otherwise return 1.
238 * Assumes we already have the subsystem lock.
241 grow_semu_array(newSize
)
245 register struct sem_undo
*newSemu
;
246 if (newSize
<= seminfo
.semmnu
)
248 if (newSize
> limitseminfo
.semmnu
) /* enforce hard limit */
251 printf("undo structure hard limit of %d reached, requested %d\n",
252 limitseminfo
.semmnu
, newSize
);
256 newSize
= (newSize
/SEMMNU_INC
+ 1) * SEMMNU_INC
;
257 newSize
= newSize
> limitseminfo
.semmnu
? limitseminfo
.semmnu
: newSize
;
260 printf("growing semu[] from %d to %d\n", seminfo
.semmnu
, newSize
);
262 MALLOC(newSemu
, struct sem_undo
*, sizeof(struct sem_undo
)*newSize
,
263 M_SYSVSEM
, M_WAITOK
);
267 printf("allocation failed. no changes made.\n");
272 /* Initialize our structure. */
273 for (i
= 0; i
< seminfo
.semmnu
; i
++)
275 newSemu
[i
] = semu
[i
];
276 for(j
= 0; j
< SEMUME
; j
++) /* Is this really needed? */
277 newSemu
[i
].un_ent
[j
] = semu
[i
].un_ent
[j
];
279 for (i
= seminfo
.semmnu
; i
< newSize
; i
++)
281 newSemu
[i
].un_proc
= NULL
;
284 /* Clean up the old array */
286 FREE(semu
, M_SYSVSEM
);
289 seminfo
.semmnu
= newSize
;
291 printf("expansion successful\n");
297 * Expand the sema array to the given capacity. If the expansion fails
298 * we return 0, otherwise we return 1.
300 * Assumes we already have the subsystem lock.
303 grow_sema_array(newSize
)
306 register struct semid_ds
*newSema
;
309 if (newSize
<= seminfo
.semmni
)
311 if (newSize
> limitseminfo
.semmni
) /* enforce hard limit */
314 printf("identifier hard limit of %d reached, requested %d\n",
315 limitseminfo
.semmni
, newSize
);
319 newSize
= (newSize
/SEMMNI_INC
+ 1) * SEMMNI_INC
;
320 newSize
= newSize
> limitseminfo
.semmni
? limitseminfo
.semmni
: newSize
;
323 printf("growing sema[] from %d to %d\n", seminfo
.semmni
, newSize
);
325 MALLOC(newSema
, struct semid_ds
*, sizeof(struct semid_ds
)*newSize
,
326 M_SYSVSEM
, M_WAITOK
);
330 printf("allocation failed. no changes made.\n");
335 /* Initialize our new ids, and copy over the old ones */
336 for (i
= 0; i
< seminfo
.semmni
; i
++)
338 newSema
[i
] = sema
[i
];
339 /* This is a hack. What we really want to be able to
340 * do is change the value a process is waiting on
341 * without waking it up, but I don't know how to do
342 * this with the existing code, so we wake up the
343 * process and let it do a lot of work to determine the
344 * semaphore set is really not available yet, and then
345 * sleep on the correct, reallocated semid_ds pointer.
347 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
)
348 wakeup((caddr_t
)&sema
[i
]);
351 for (i
= seminfo
.semmni
; i
< newSize
; i
++)
353 newSema
[i
].sem_base
= 0;
354 newSema
[i
].sem_perm
.mode
= 0;
357 /* Clean up the old array */
359 FREE(sema
, M_SYSVSEM
);
362 seminfo
.semmni
= newSize
;
364 printf("expansion successful\n");
370 * Expand the sem array to the given capacity. If the expansion fails
371 * we return 0 (fail), otherwise we return 1 (success).
373 * Assumes we already hold the subsystem lock.
376 grow_sem_array(newSize
)
379 register struct sem
*newSem
= NULL
;
382 if (newSize
< semtot
)
384 if (newSize
> limitseminfo
.semmns
) /* enforce hard limit */
387 printf("semaphore hard limit of %d reached, requested %d\n",
388 limitseminfo
.semmns
, newSize
);
392 newSize
= (newSize
/SEMMNS_INC
+ 1) * SEMMNS_INC
;
393 newSize
= newSize
> limitseminfo
.semmns
? limitseminfo
.semmns
: newSize
;
396 printf("growing sem array from %d to %d\n", seminfo
.semmns
, newSize
);
398 MALLOC(newSem
, struct sem
*, sizeof(struct sem
)*newSize
,
399 M_SYSVSEM
, M_WAITOK
);
403 printf("allocation failed. no changes made.\n");
408 /* We have our new memory, now copy the old contents over */
410 for(i
= 0; i
< seminfo
.semmns
; i
++)
413 /* Update our id structures to point to the new semaphores */
414 for(i
= 0; i
< seminfo
.semmni
; i
++)
415 if (sema
[i
].sem_perm
.mode
& SEM_ALLOC
) /* ID in use */
418 sema
[i
].sem_base
+= newSem
- sem
;
420 sema
[i
].sem_base
-= sem
- newSem
;
423 /* clean up the old array */
425 FREE(sem
, M_SYSVSEM
);
428 seminfo
.semmns
= newSize
;
430 printf("expansion complete\n");
436 * Allocate a new sem_undo structure for a process
437 * (returns ptr to structure or NULL if no more room)
439 * Assumes we already hold the subsystem lock.
442 static struct sem_undo
*
447 register struct sem_undo
*suptr
;
448 register struct sem_undo
**supptr
;
452 * Try twice to allocate something.
453 * (we'll purge any empty structures after the first pass so
454 * two passes are always enough)
457 for (attempt
= 0; attempt
< 2; attempt
++) {
459 * Look for a free structure.
460 * Fill it in and return it if we find one.
463 for (i
= 0; i
< seminfo
.semmnu
; i
++) {
465 if (suptr
->un_proc
== NULL
) {
466 suptr
->un_next
= semu_list
;
475 * We didn't find a free one, if this is the first attempt
476 * then try to free some structures.
480 /* All the structures are in use - try to free some */
481 int did_something
= 0;
484 while ((suptr
= *supptr
) != NULL
) {
485 if (suptr
->un_cnt
== 0) {
486 suptr
->un_proc
= NULL
;
487 *supptr
= suptr
->un_next
;
490 supptr
= &(suptr
->un_next
);
493 /* If we didn't free anything. Try expanding
494 * the semu[] array. If that doesn't work
495 * then fail. We expand last to get the
496 * most reuse out of existing resources.
499 if (!grow_semu_array(seminfo
.semmnu
+ 1))
503 * The second pass failed even though we freed
504 * something after the first pass!
505 * This is IMPOSSIBLE!
507 panic("semu_alloc - second attempt failed");
514 * Adjust a particular entry for a particular proc
516 * Assumes we already hold the subsystem lock.
520 semundo_adjust(p
, supptr
, semid
, semnum
, adjval
)
521 register struct proc
*p
;
522 struct sem_undo
**supptr
;
526 register struct sem_undo
*suptr
;
527 register struct undo
*sunptr
;
530 /* Look for and remember the sem_undo if the caller doesn't provide
535 for (suptr
= semu_list
; suptr
!= NULL
;
536 suptr
= suptr
->un_next
) {
537 if (suptr
->un_proc
== p
) {
545 suptr
= semu_alloc(p
);
553 * Look for the requested entry and adjust it (delete if adjval becomes
556 sunptr
= &suptr
->un_ent
[0];
557 for (i
= 0; i
< suptr
->un_cnt
; i
++, sunptr
++) {
558 if (sunptr
->un_id
!= semid
|| sunptr
->un_num
!= semnum
)
561 sunptr
->un_adjval
= 0;
563 sunptr
->un_adjval
+= adjval
;
564 if (sunptr
->un_adjval
== 0) {
566 if (i
< suptr
->un_cnt
)
568 suptr
->un_ent
[suptr
->un_cnt
];
573 /* Didn't find the right entry - create it */
576 if (suptr
->un_cnt
!= seminfo
.semume
) {
577 sunptr
= &suptr
->un_ent
[suptr
->un_cnt
];
579 sunptr
->un_adjval
= adjval
;
580 sunptr
->un_id
= semid
; sunptr
->un_num
= semnum
;
586 /* Assumes we already hold the subsystem lock.
589 semundo_clear(semid
, semnum
)
592 register struct sem_undo
*suptr
;
594 for (suptr
= semu_list
; suptr
!= NULL
; suptr
= suptr
->un_next
) {
595 register struct undo
*sunptr
= &suptr
->un_ent
[0];
598 while (i
< suptr
->un_cnt
) {
599 if (sunptr
->un_id
== semid
) {
600 if (semnum
== -1 || sunptr
->un_num
== semnum
) {
602 if (i
< suptr
->un_cnt
) {
604 suptr
->un_ent
[suptr
->un_cnt
];
617 * Note that the user-mode half of this passes a union, not a pointer
619 #ifndef _SYS_SYSPROTO_H_
629 semctl(p
, uap
, retval
)
631 register struct semctl_args
*uap
;
634 int semid
= uap
->semid
;
635 int semnum
= uap
->semnum
;
637 union semun arg
= uap
->arg
;
638 union semun real_arg
;
639 struct ucred
*cred
= p
->p_ucred
;
641 struct semid_ds sbuf
;
642 register struct semid_ds
*semaptr
;
644 SUBSYSTEM_LOCK_AQUIRE(p
);
646 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid
, semnum
, cmd
, arg
);
649 semid
= IPCID_TO_IX(semid
);
650 if (semid
< 0 || semid
>= seminfo
.semmsl
)
653 printf("Invalid semid\n");
655 UNLOCK_AND_RETURN(EINVAL
);
658 semaptr
= &sema
[semid
];
659 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
660 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
))
661 UNLOCK_AND_RETURN(EINVAL
);
668 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
669 UNLOCK_AND_RETURN(eval
);
670 semaptr
->sem_perm
.cuid
= cred
->cr_uid
;
671 semaptr
->sem_perm
.uid
= cred
->cr_uid
;
672 semtot
-= semaptr
->sem_nsems
;
673 for (i
= semaptr
->sem_base
- sem
; i
< semtot
; i
++)
674 sem
[i
] = sem
[i
+ semaptr
->sem_nsems
];
675 for (i
= 0; i
< seminfo
.semmni
; i
++) {
676 if ((sema
[i
].sem_perm
.mode
& SEM_ALLOC
) &&
677 sema
[i
].sem_base
> semaptr
->sem_base
)
678 sema
[i
].sem_base
-= semaptr
->sem_nsems
;
680 semaptr
->sem_perm
.mode
= 0;
681 semundo_clear(semid
, -1);
682 wakeup((caddr_t
)semaptr
);
686 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_M
)))
687 UNLOCK_AND_RETURN(eval
);
688 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
689 UNLOCK_AND_RETURN(eval);*/
690 if ((eval
= copyin(arg
.buf
, (caddr_t
)&sbuf
,
692 UNLOCK_AND_RETURN(eval
);
693 semaptr
->sem_perm
.uid
= sbuf
.sem_perm
.uid
;
694 semaptr
->sem_perm
.gid
= sbuf
.sem_perm
.gid
;
695 semaptr
->sem_perm
.mode
= (semaptr
->sem_perm
.mode
& ~0777) |
696 (sbuf
.sem_perm
.mode
& 0777);
697 semaptr
->sem_ctime
= time_second
;
701 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
702 UNLOCK_AND_RETURN(eval
);
703 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
704 UNLOCK_AND_RETURN(eval);*/
705 eval
= copyout((caddr_t
)semaptr
, arg
.buf
,
706 sizeof(struct semid_ds
));
710 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
711 UNLOCK_AND_RETURN(eval
);
712 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
713 UNLOCK_AND_RETURN(EINVAL
);
714 rval
= semaptr
->sem_base
[semnum
].semncnt
;
718 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
719 UNLOCK_AND_RETURN(eval
);
720 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
721 UNLOCK_AND_RETURN(EINVAL
);
722 rval
= semaptr
->sem_base
[semnum
].sempid
;
726 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
727 UNLOCK_AND_RETURN(eval
);
728 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
729 UNLOCK_AND_RETURN(EINVAL
);
730 rval
= semaptr
->sem_base
[semnum
].semval
;
734 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
735 UNLOCK_AND_RETURN(eval
);
736 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
737 UNLOCK_AND_RETURN(eval);*/
738 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
739 eval
= copyout((caddr_t
)&semaptr
->sem_base
[i
].semval
,
740 &arg
.array
[i
], sizeof(arg
.array
[0]));
747 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_R
)))
748 UNLOCK_AND_RETURN(eval
);
749 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
750 UNLOCK_AND_RETURN(EINVAL
);
751 rval
= semaptr
->sem_base
[semnum
].semzcnt
;
755 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
758 printf("Invalid credentials for write\n");
760 UNLOCK_AND_RETURN(eval
);
762 if (semnum
< 0 || semnum
>= semaptr
->sem_nsems
)
765 printf("Invalid number out of range for set\n");
767 UNLOCK_AND_RETURN(EINVAL
);
769 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
772 printf("Error during value copyin\n");
774 UNLOCK_AND_RETURN(eval);
776 semaptr
->sem_base
[semnum
].semval
= arg
.val
;
777 semundo_clear(semid
, semnum
);
778 wakeup((caddr_t
)semaptr
);
782 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
)))
783 UNLOCK_AND_RETURN(eval
);
784 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
785 UNLOCK_AND_RETURN(eval);*/
786 for (i
= 0; i
< semaptr
->sem_nsems
; i
++) {
787 eval
= copyin(&arg
.array
[i
],
788 (caddr_t
)&semaptr
->sem_base
[i
].semval
,
789 sizeof(arg
.array
[0]));
793 semundo_clear(semid
, -1);
794 wakeup((caddr_t
)semaptr
);
798 UNLOCK_AND_RETURN(EINVAL
);
803 UNLOCK_AND_RETURN(eval
);
806 #ifndef _SYS_SYSPROTO_H_
815 semget(p
, uap
, retval
)
817 register struct semget_args
*uap
;
822 int nsems
= uap
->nsems
;
823 int semflg
= uap
->semflg
;
824 struct ucred
*cred
= p
->p_ucred
;
826 SUBSYSTEM_LOCK_AQUIRE(p
);
828 if (key
!= IPC_PRIVATE
)
829 printf("semget(0x%x, %d, 0%o)\n", key
, nsems
, semflg
);
831 printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems
, semflg
);
834 if (key
!= IPC_PRIVATE
) {
835 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
836 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) &&
837 sema
[semid
].sem_perm
.key
== key
)
840 if (semid
< seminfo
.semmni
) {
842 printf("found public key\n");
844 if ((eval
= ipcperm(cred
, &sema
[semid
].sem_perm
,
846 UNLOCK_AND_RETURN(eval
);
847 if (nsems
> 0 && sema
[semid
].sem_nsems
< nsems
) {
849 printf("too small\n");
851 UNLOCK_AND_RETURN(EINVAL
);
853 if ((semflg
& IPC_CREAT
) && (semflg
& IPC_EXCL
)) {
855 printf("not exclusive\n");
857 UNLOCK_AND_RETURN(EEXIST
);
864 printf("need to allocate an id for the request\n");
866 if (key
== IPC_PRIVATE
|| (semflg
& IPC_CREAT
)) {
867 if (nsems
<= 0 || nsems
> seminfo
.semmsl
) {
869 printf("nsems out of range (0<%d<=%d)\n", nsems
,
872 UNLOCK_AND_RETURN(EINVAL
);
874 if (nsems
> seminfo
.semmns
- semtot
) {
876 printf("not enough semaphores left (need %d, got %d)\n",
877 nsems
, seminfo
.semmns
- semtot
);
879 if (!grow_sem_array(semtot
+ nsems
))
882 printf("failed to grow the sem array\n");
884 UNLOCK_AND_RETURN(ENOSPC
);
887 for (semid
= 0; semid
< seminfo
.semmni
; semid
++) {
888 if ((sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) == 0)
891 if (semid
== seminfo
.semmni
) {
893 printf("no more id's available\n");
895 if (!grow_sema_array(seminfo
.semmni
+ 1))
898 printf("failed to grow sema array\n");
900 UNLOCK_AND_RETURN(ENOSPC
);
904 printf("semid %d is available\n", semid
);
906 sema
[semid
].sem_perm
.key
= key
;
907 sema
[semid
].sem_perm
.cuid
= cred
->cr_uid
;
908 sema
[semid
].sem_perm
.uid
= cred
->cr_uid
;
909 sema
[semid
].sem_perm
.cgid
= cred
->cr_gid
;
910 sema
[semid
].sem_perm
.gid
= cred
->cr_gid
;
911 sema
[semid
].sem_perm
.mode
= (semflg
& 0777) | SEM_ALLOC
;
912 sema
[semid
].sem_perm
.seq
=
913 (sema
[semid
].sem_perm
.seq
+ 1) & 0x7fff;
914 sema
[semid
].sem_nsems
= nsems
;
915 sema
[semid
].sem_otime
= 0;
916 sema
[semid
].sem_ctime
= time_second
;
917 sema
[semid
].sem_base
= &sem
[semtot
];
919 bzero(sema
[semid
].sem_base
,
920 sizeof(sema
[semid
].sem_base
[0])*nsems
);
922 printf("sembase = 0x%x, next = 0x%x\n", sema
[semid
].sem_base
,
927 printf("didn't find it and wasn't asked to create it\n");
929 UNLOCK_AND_RETURN(ENOENT
);
933 *retval
= IXSEQ_TO_IPCID(semid
, sema
[semid
].sem_perm
);
935 printf("semget is done, returning %d\n", *retval
);
937 SUBSYSTEM_LOCK_RELEASE
;
941 #ifndef _SYS_SYSPROTO_H_
950 semop(p
, uap
, retval
)
952 register struct semop_args
*uap
;
955 int semid
= uap
->semid
;
956 int nsops
= uap
->nsops
;
957 struct sembuf sops
[MAX_SOPS
];
958 register struct semid_ds
*semaptr
;
959 register struct sembuf
*sopptr
;
960 register struct sem
*semptr
;
961 struct sem_undo
*suptr
= NULL
;
962 struct ucred
*cred
= p
->p_ucred
;
964 int do_wakeup
, do_undos
;
966 SUBSYSTEM_LOCK_AQUIRE(p
);
968 printf("call to semop(%d, 0x%x, %d)\n", semid
, sops
, nsops
);
971 semid
= IPCID_TO_IX(semid
); /* Convert back to zero origin */
973 if (semid
< 0 || semid
>= seminfo
.semmsl
)
974 UNLOCK_AND_RETURN(EINVAL
);
976 semaptr
= &sema
[semid
];
977 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0)
978 UNLOCK_AND_RETURN(EINVAL
);
979 if (semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
))
980 UNLOCK_AND_RETURN(EINVAL
);
982 if ((eval
= ipcperm(cred
, &semaptr
->sem_perm
, IPC_W
))) {
984 printf("eval = %d from ipaccess\n", eval
);
986 UNLOCK_AND_RETURN(eval
);
989 if (nsops
> MAX_SOPS
) {
991 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS
, nsops
);
993 UNLOCK_AND_RETURN(E2BIG
);
996 if ((eval
= copyin(uap
->sops
, &sops
, nsops
* sizeof(sops
[0]))) != 0) {
998 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval
,
999 uap
->sops
, &sops
, nsops
* sizeof(sops
[0]));
1001 UNLOCK_AND_RETURN(eval
);
1005 * Loop trying to satisfy the vector of requests.
1006 * If we reach a point where we must wait, any requests already
1007 * performed are rolled back and we go to sleep until some other
1008 * process wakes us up. At this point, we start all over again.
1010 * This ensures that from the perspective of other tasks, a set
1011 * of requests is atomic (never partially satisfied).
1018 for (i
= 0; i
< nsops
; i
++) {
1021 if (sopptr
->sem_num
>= semaptr
->sem_nsems
)
1022 UNLOCK_AND_RETURN(EFBIG
);
1024 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1027 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1028 semaptr
, semaptr
->sem_base
, semptr
,
1029 sopptr
->sem_num
, semptr
->semval
, sopptr
->sem_op
,
1030 (sopptr
->sem_flg
& IPC_NOWAIT
) ? "nowait" : "wait");
1033 if (sopptr
->sem_op
< 0) {
1034 if (semptr
->semval
+ sopptr
->sem_op
< 0) {
1036 printf("semop: can't do it now\n");
1040 semptr
->semval
+= sopptr
->sem_op
;
1041 if (semptr
->semval
== 0 &&
1042 semptr
->semzcnt
> 0)
1045 if (sopptr
->sem_flg
& SEM_UNDO
)
1047 } else if (sopptr
->sem_op
== 0) {
1048 if (semptr
->semval
> 0) {
1050 printf("semop: not zero now\n");
1055 if (semptr
->semncnt
> 0)
1057 semptr
->semval
+= sopptr
->sem_op
;
1058 if (sopptr
->sem_flg
& SEM_UNDO
)
1064 * Did we get through the entire vector?
1070 * No ... rollback anything that we've already done
1073 printf("semop: rollback 0 through %d\n", i
-1);
1075 for (j
= 0; j
< i
; j
++)
1076 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1080 * If the request that we couldn't satisfy has the
1081 * NOWAIT flag set then return with EAGAIN.
1083 if (sopptr
->sem_flg
& IPC_NOWAIT
)
1084 UNLOCK_AND_RETURN(EAGAIN
);
1086 if (sopptr
->sem_op
== 0)
1092 printf("semop: good night!\n");
1094 /* Release our lock on the semaphore subsystem so
1095 * another thread can get at the semaphore we are
1096 * waiting for. We will get the lock back after we
1099 SUBSYSTEM_LOCK_RELEASE
;
1100 sysv_sem_sleeping_threads
++;
1101 eval
= tsleep((caddr_t
)semaptr
, (PZERO
- 4) | PCATCH
,
1103 sysv_sem_sleeping_threads
--;
1106 printf("semop: good morning (eval=%d)!\n", eval
);
1108 /* There is no need to get the lock if we are just
1109 * going to return without performing more semaphore
1115 SUBSYSTEM_LOCK_AQUIRE(p
); /* Get it back */
1116 suptr
= NULL
; /* sem_undo may have been reallocated */
1117 semaptr
= &sema
[semid
]; /* sema may have been reallocated */
1121 printf("semop: good morning!\n");
1125 * Make sure that the semaphore still exists
1127 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ||
1128 semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
)) {
1129 /* The man page says to return EIDRM. */
1130 /* Unfortunately, BSD doesn't define that code! */
1132 UNLOCK_AND_RETURN(EIDRM
);
1134 UNLOCK_AND_RETURN(EINVAL
);
1139 * The semaphore is still alive. Readjust the count of
1140 * waiting processes. semptr needs to be recomputed
1141 * because the sem[] may have been reallocated while
1142 * we were sleeping, updating our sem_base pointer.
1144 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1145 if (sopptr
->sem_op
== 0)
1153 * Process any SEM_UNDO requests.
1156 for (i
= 0; i
< nsops
; i
++) {
1158 * We only need to deal with SEM_UNDO's for non-zero
1163 if ((sops
[i
].sem_flg
& SEM_UNDO
) == 0)
1165 adjval
= sops
[i
].sem_op
;
1168 eval
= semundo_adjust(p
, &suptr
, semid
,
1169 sops
[i
].sem_num
, -adjval
);
1174 * Oh-Oh! We ran out of either sem_undo's or undo's.
1175 * Rollback the adjustments to this point and then
1176 * rollback the semaphore ups and down so we can return
1177 * with an error with all structures restored. We
1178 * rollback the undo's in the exact reverse order that
1179 * we applied them. This guarantees that we won't run
1180 * out of space as we roll things back out.
1182 for (j
= i
- 1; j
>= 0; j
--) {
1183 if ((sops
[j
].sem_flg
& SEM_UNDO
) == 0)
1185 adjval
= sops
[j
].sem_op
;
1188 if (semundo_adjust(p
, &suptr
, semid
,
1189 sops
[j
].sem_num
, adjval
) != 0)
1190 panic("semop - can't undo undos");
1193 for (j
= 0; j
< nsops
; j
++)
1194 semaptr
->sem_base
[sops
[j
].sem_num
].semval
-=
1198 printf("eval = %d from semundo_adjust\n", eval
);
1200 UNLOCK_AND_RETURN(eval
);
1201 } /* loop through the sops */
1202 } /* if (do_undos) */
1204 /* We're definitely done - set the sempid's */
1205 for (i
= 0; i
< nsops
; i
++) {
1207 semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
1208 semptr
->sempid
= p
->p_pid
;
1211 /* Do a wakeup if any semaphore was up'd.
1212 * we will release our lock on the semaphore subsystem before
1213 * we wakeup other processes to prevent a little thrashing.
1214 * Note that this is fine because we are done using the
1215 * semaphore structures at this point in time. We only use
1216 * a local variable pointer value, and the retval
1218 * Note 2: Future use of sem_wakeup may reqiure the lock.
1220 SUBSYSTEM_LOCK_RELEASE
;
1223 printf("semop: doing wakeup\n");
1225 sem_wakeup((caddr_t
)semaptr
);
1227 wakeup((caddr_t
)semaptr
);
1229 printf("semop: back from wakeup\n");
1231 wakeup((caddr_t
)semaptr
);
1235 printf("semop: done\n");
1242 * Go through the undo structures for this process and apply the adjustments to
1249 register struct sem_undo
*suptr
;
1250 register struct sem_undo
**supptr
;
1253 /* If we have not allocated our semaphores yet there can't be
1254 * anything to undo, but we need the lock to prevent
1255 * dynamic memory race conditions.
1257 SUBSYSTEM_LOCK_AQUIRE(p
);
1260 SUBSYSTEM_LOCK_RELEASE
;
1266 * Go through the chain of undo vectors looking for one
1267 * associated with this process.
1270 for (supptr
= &semu_list
; (suptr
= *supptr
) != NULL
;
1271 supptr
= &suptr
->un_next
) {
1272 if (suptr
->un_proc
== p
)
1280 printf("proc @%08x has undo structure with %d entries\n", p
,
1285 * If there are any active undo elements then process them.
1287 if (suptr
->un_cnt
> 0) {
1290 for (ix
= 0; ix
< suptr
->un_cnt
; ix
++) {
1291 int semid
= suptr
->un_ent
[ix
].un_id
;
1292 int semnum
= suptr
->un_ent
[ix
].un_num
;
1293 int adjval
= suptr
->un_ent
[ix
].un_adjval
;
1294 struct semid_ds
*semaptr
;
1296 semaptr
= &sema
[semid
];
1297 if ((semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0)
1298 panic("semexit - semid not allocated");
1299 if (semnum
>= semaptr
->sem_nsems
)
1300 panic("semexit - semnum out of range");
1303 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1304 suptr
->un_proc
, suptr
->un_ent
[ix
].un_id
,
1305 suptr
->un_ent
[ix
].un_num
,
1306 suptr
->un_ent
[ix
].un_adjval
,
1307 semaptr
->sem_base
[semnum
].semval
);
1311 if (semaptr
->sem_base
[semnum
].semval
< -adjval
)
1312 semaptr
->sem_base
[semnum
].semval
= 0;
1314 semaptr
->sem_base
[semnum
].semval
+=
1317 semaptr
->sem_base
[semnum
].semval
+= adjval
;
1319 /* Maybe we should build a list of semaptr's to wake
1320 * up, finish all access to data structures, release the
1321 * subsystem lock, and wake all the processes. Something
1322 * to think about. It wouldn't buy us anything unless
1323 * wakeup had the potential to block, or the syscall
1324 * funnel state was changed to allow multiple threads
1325 * in the BSD code at once.
1328 sem_wakeup((caddr_t
)semaptr
);
1330 wakeup((caddr_t
)semaptr
);
1333 printf("semexit: back from wakeup\n");
1339 * Deallocate the undo vector.
1342 printf("removing vector\n");
1344 suptr
->un_proc
= NULL
;
1345 *supptr
= suptr
->un_next
;
1349 * There is a semaphore leak (i.e. memory leak) in this code.
1350 * We should be deleting the IPC_PRIVATE semaphores when they are
1351 * no longer needed, and we dont. We would have to track which processes
1352 * know about which IPC_PRIVATE semaphores, updating the list after
1353 * every fork. We can't just delete them semaphore when the process
1354 * that created it dies, because that process may well have forked
1355 * some children. So we need to wait until all of it's children have
1356 * died, and so on. Maybe we should tag each IPC_PRIVATE sempahore
1357 * with the creating group ID, count the number of processes left in
1358 * that group, and delete the semaphore when the group is gone.
1359 * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1360 * There is an upper bound on the size of our semaphore array, so
1361 * leaking the semaphores should not work as a DOS attack.
1363 * Please note that the original BSD code this file is based on had the
1364 * same leaky semaphore problem.
1367 SUBSYSTEM_LOCK_RELEASE
;