* Implementation of SVID semaphores
* This software is provided ``AS IS'' without any warranties of any kind.
static int semctl(), semget(), semop(), semconfig();
int (*semcalls
[])() = { semctl
, semget
, semop
, semconfig
};
static struct proc
*semlock_holder
= NULL
;
vm_offset_t whocares1
, whocares2
;
for ( i
= 0; i
< seminfo
.semmni
; i
+= 1 ) {
sema
[i
].sem_perm
.mode
= 0;
for ( i
= 0; i
< seminfo
.semmnu
; i
+= 1 ) {
register struct sem_undo
*suptr
= SEMU(i
);
TEXT_SET(pseudo_set
, seminit
);
* Entry point for all SEM calls
while ( semlock_holder
!= NULL
&& semlock_holder
!= p
) {
/* printf("semaphore facility locked - sleeping ...\n"); */
tsleep( (caddr_t
)&semlock_holder
, (PZERO
- 4), "semsys", 0 );
if (uap
->which
>= sizeof(semcalls
)/sizeof(semcalls
[0]))
return ((*semcalls
[uap
->which
])(p
, &uap
[1], retval
));
* Lock or unlock the entire semaphore facility.
* This will probably eventually evolve into a general purpose semaphore
* facility status enquiry mechanism (I don't like the "read /dev/kmem"
* approach currently taken by ipcs and the amount of info that we want
* to be able to extract for ipcs is probably beyond what the capability
* of the getkerninfo facility.
* At the time that the current version of semconfig was written, ipcs is
* the only user of the semconfig facility. It uses it to ensure that the
* semaphore facility data structures remain static while it fishes around
semconfig(p
, uap
, retval
)
struct semconfig_args
*uap
;
wakeup( (caddr_t
)&semlock_holder
);
printf("semconfig: unknown flag parameter value (%d) - ignored\n",uap
->flag
);
* Allocate a new sem_undo structure for a process
* (returns ptr to structure or NULL if no more room)
semu_alloc(struct proc
*p
)
register struct sem_undo
*suptr
;
register struct sem_undo
**supptr
;
* Try twice to allocate something.
* (we'll purge any empty structures after the first pass so
* two passes are always enough)
for ( attempt
= 0; attempt
< 2; attempt
+= 1 ) {
* Look for a free structure.
* Fill it in and return it if we find one.
for ( i
= 0; i
< seminfo
.semmnu
; i
+= 1 ) {
if ( suptr
->un_proc
== NULL
) {
suptr
->un_next
= semu_list
;
* We didn't find a free one, if this is the first attempt
* then try to free some structures.
/* All the structures are in use - try to free some */
while ( (suptr
= *supptr
) != NULL
) {
if ( suptr
->un_cnt
== 0 ) {
*supptr
= suptr
->un_next
;
supptr
= &(suptr
->un_next
);
/* If we didn't free anything then just give-up */
* The second pass failed even though we freed
* something after the first pass!
panic("semu_alloc - second attempt failed");
* Adjust a particular entry for a particular proc
semundo_adjust(register struct proc
*p
,struct sem_undo
**supptr
,int semid
,int semnum
,int adjval
)
register struct sem_undo
*suptr
;
register struct undo
*sunptr
;
/* Look for and remember the sem_undo if the caller doesn't provide it */
/* printf("adjust: need to find suptr\n"); */
for ( suptr
= semu_list
; suptr
!= NULL
; suptr
= suptr
->un_next
) {
if ( suptr
->un_proc
== p
) {
/* printf("adjust: found suptr @%08x\n",suptr); */
return(0); /* Don't create it if it doesn't exist */
/* printf("adjust: allocated suptr @%08x\n",suptr); */
/* Look for the requested entry and adjust it (delete if adjval becomes 0) */
sunptr
= &(suptr
->un_ent
[0]);
for ( i
= 0; i
< suptr
->un_cnt
; i
+= 1, sunptr
+= 1 ) {
if ( sunptr
->un_id
== semid
&& sunptr
->un_num
== semnum
) {
/* Found the right entry - adjust it */
/* printf("adjust: %08x %d:%d(%d) += %d\n",suptr->un_proc,semid,semnum,sunptr->un_adjval,adjval); */
sunptr
->un_adjval
+= adjval
;
if ( sunptr
->un_adjval
== 0 ) {
/* printf("adjust: %08x deleting entry %d:%d\n",suptr->un_proc,semid,semnum); */
if ( i
< suptr
->un_cnt
) {
suptr
->un_ent
[i
] = suptr
->un_ent
[suptr
->un_cnt
];
/* Didn't find the right entry - create it */
if ( suptr
->un_cnt
== SEMUME
) {
/* printf("adjust: %08x allocating entry %d as %d:%d(%d)\n",suptr->un_proc,suptr->un_cnt,semid,semnum,adjval); */
sunptr
= &(suptr
->un_ent
[suptr
->un_cnt
]);
sunptr
->un_adjval
= adjval
;
sunptr
->un_id
= semid
; sunptr
->un_num
= semnum
;
semundo_clear(int semid
,int semnum
)
register struct sem_undo
*suptr
;
for ( suptr
= semu_list
; suptr
!= NULL
; suptr
= suptr
->un_next
) {
register struct undo
*sunptr
= &(suptr
->un_ent
[0]);
while ( i
< suptr
->un_cnt
) {
if ( sunptr
->un_id
== semid
) {
if ( semnum
== -1 || sunptr
->un_num
== semnum
) {
/* printf("clear: %08x %d:%d(%d)\n",suptr->un_proc,semid,sunptr->un_num,sunptr->un_adjval); */
if ( i
< suptr
->un_cnt
) {
suptr
->un_ent
[i
] = suptr
->un_ent
[suptr
->un_cnt
];
register struct semctl_args
*uap
;
int semnum
= uap
->semnum
;
union semun
*arg
= uap
->arg
;
struct ucred
*cred
= p
->p_ucred
;
register struct semid_ds
*semaptr
;
printf("call to semctl(%d,%d,%d,0x%x)\n",semid
,semnum
,cmd
,arg
);
semid
= IPCID_TO_IX(semid
);
if ( semid
< 0 || semid
>= seminfo
.semmsl
) {
/* printf("semid out of range (0<=%d<%d)\n",semid,seminfo.semmsl); */
if ( semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
) ) {
/* printf("invalid sequence number\n"); */
if ( (semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ) {
/* printf("no such semaphore id\n"); */
&& semaptr
->sem_perm
.cuid
!= cred
->cr_uid
&& semaptr
->sem_perm
.uid
!= cred
->cr_uid
) {
semaptr
->sem_perm
.cuid
= cred
->cr_uid
;
semaptr
->sem_perm
.uid
= cred
->cr_uid
;
semtot
-= semaptr
->sem_nsems
;
for ( i
= semaptr
->sem_base
- sem
; i
< semtot
; i
+= 1 ) {
/* printf("0x%x = 0x%x; ",&sem[i],&sem[i + semaptr->sem_nsems]); */
sem
[i
] = sem
[i
+ semaptr
->sem_nsems
];
for ( i
= 0; i
< seminfo
.semmni
; i
+= 1 ) {
if ( (sema
[i
].sem_perm
.mode
& SEM_ALLOC
)
&& sema
[i
].sem_base
> semaptr
->sem_base
) {
/* printf("sema[%d].sem_base was 0x%x",i,sema[i].sem_base); */
sema
[i
].sem_base
-= semaptr
->sem_nsems
;
/* printf(", now 0x%x\n",sema[i].sem_base); */
semaptr
->sem_perm
.mode
= 0;
/* Delete any undo entries for this semid */
/* Make sure that anybody who is waiting notices the deletion */
wakeup( (caddr_t
)semaptr
);
/* printf("IPC_SET\n"); */
&& semaptr
->sem_perm
.cuid
!= cred
->cr_uid
&& semaptr
->sem_perm
.uid
!= cred
->cr_uid
) {
if ( (eval
= copyin(arg
, &real_arg
, sizeof(real_arg
))) != 0 ) {
if ( (eval
= copyin(real_arg
.buf
, (caddr_t
)&sbuf
, sizeof(sbuf
)) ) != 0 ) {
semaptr
->sem_perm
.uid
= sbuf
.sem_perm
.uid
;
semaptr
->sem_perm
.gid
= sbuf
.sem_perm
.gid
;
semaptr
->sem_perm
.mode
= (semaptr
->sem_perm
.mode
& ~0777)
| (sbuf
.sem_perm
.mode
& 0777);
semaptr
->sem_ctime
= time
.tv_sec
;
/* printf("IPC_STAT\n"); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
if ( (eval
= copyin(arg
, &real_arg
, sizeof(real_arg
))) != 0 ) {
eval
= copyout((caddr_t
)semaptr
, real_arg
.buf
, sizeof(struct semid_ds
)) ;
/* printf("GETNCNT(%d)\n",semnum); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
if ( semnum
< 0 || semnum
>= semaptr
->sem_nsems
) return(EINVAL
);
rval
= semaptr
->sem_base
[semnum
].semncnt
;
/* printf("GETPID(%d)\n",semnum); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
if ( semnum
< 0 || semnum
>= semaptr
->sem_nsems
) return(EINVAL
);
rval
= semaptr
->sem_base
[semnum
].sempid
;
/* printf("GETVAL(%d)\n",semnum); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
if ( semnum
< 0 || semnum
>= semaptr
->sem_nsems
) return(EINVAL
);
rval
= semaptr
->sem_base
[semnum
].semval
;
/* printf("GETALL\n"); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
if ( (eval
= copyin(arg
, &real_arg
, sizeof(real_arg
))) != 0 ) {
/* printf("initial copyin failed (addr=0x%x)\n",arg); */
/* printf("%d semaphores\n",semaptr->sem_nsems); */
for ( i
= 0; i
< semaptr
->sem_nsems
; i
+= 1 ) {
/* printf("copyout to 0x%x\n",&real_arg.array[i]); */
copyout((caddr_t
)&semaptr
->sem_base
[i
].semval
,
sizeof(real_arg
.array
[0]));
/* printf("copyout to 0x%x failed\n",&real_arg.array[i]); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_R
, cred
)) ) {
/* printf("GETZCNT(%d)\n",semnum); */
if ( semnum
< 0 || semnum
>= semaptr
->sem_nsems
) return(EINVAL
);
rval
= semaptr
->sem_base
[semnum
].semzcnt
;
printf("SETVAL(%d)\n",semnum
);
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_W
, cred
)) ) {
if ( semnum
< 0 || semnum
>= semaptr
->sem_nsems
) return(EINVAL
);
if ( (eval
= copyin(arg
, &real_arg
, sizeof(real_arg
))) != 0 ) {
printf("semaptr=%x, sem_base=%x, semptr=%x, oldval=%d, ",
semaptr
,semaptr
->sem_base
,&semaptr
->sem_base
[semnum
],semaptr
->sem_base
[semnum
].semval
);
semaptr
->sem_base
[semnum
].semval
= real_arg
.val
;
printf(" newval=%d\n", semaptr
->sem_base
[semnum
].semval
);
semundo_clear(semid
,semnum
);
wakeup( (caddr_t
)semaptr
); /* somebody else might care */
/* printf("SETALL\n"); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_W
, cred
)) ) {
if ( (eval
= copyin(arg
, &real_arg
, sizeof(real_arg
))) != 0 ) {
for ( i
= 0; i
< semaptr
->sem_nsems
; i
+= 1 ) {
copyin(&real_arg
.array
[i
],
(caddr_t
)&semaptr
->sem_base
[i
].semval
,
sizeof(real_arg
.array
[0]));
wakeup( (caddr_t
)semaptr
); /* somebody else might care */
/* printf("invalid command %d\n",cmd); */
register struct semget_args
*uap
;
int semflg
= uap
->semflg
;
struct ucred
*cred
= p
->p_ucred
;
printf("semget(0x%x,%d,0%o)\n",key
,nsems
,semflg
);
if ( key
== IPC_PRIVATE
) {
for ( semid
= 0; semid
< seminfo
.semmni
; semid
+= 1 ) {
if ( (sema
[semid
].sem_perm
.mode
& SEM_ALLOC
)
&& sema
[semid
].sem_perm
.key
== key
) {
if ( semid
< seminfo
.semmni
) {
printf("found public key\n");
if ( (eval
= ipcaccess(&sema
[semid
].sem_perm
, semflg
& 0700, cred
)) ) {
if ( nsems
> 0 && sema
[semid
].sem_nsems
< nsems
) {
if ( (semflg
& IPC_CREAT
) && (semflg
& IPC_EXCL
) ) {
printf("not exclusive\n");
printf("didn't find public key\n");
if ( semid
== seminfo
.semmni
) {
printf("need to allocate the semid_ds\n");
if ( key
== IPC_PRIVATE
|| (semflg
& IPC_CREAT
) ) {
if ( nsems
<= 0 || nsems
> seminfo
.semmsl
) {
printf("nsems out of range (0<%d<=%d)\n",nsems
,seminfo
.semmsl
);
if ( nsems
> seminfo
.semmns
- semtot
) {
printf("not enough semaphores left (need %d, got %d)\n",
nsems
,seminfo
.semmns
- semtot
);
for ( semid
= 0; semid
< seminfo
.semmni
; semid
+= 1 ) {
if ( (sema
[semid
].sem_perm
.mode
& SEM_ALLOC
) == 0 ) {
if ( semid
== seminfo
.semmni
) {
printf("no more semid_ds's available\n");
printf("semid %d is available\n",semid
);
sema
[semid
].sem_perm
.key
= key
;
sema
[semid
].sem_perm
.cuid
= cred
->cr_uid
;
sema
[semid
].sem_perm
.uid
= cred
->cr_uid
;
sema
[semid
].sem_perm
.cgid
= cred
->cr_gid
;
sema
[semid
].sem_perm
.gid
= cred
->cr_gid
;
sema
[semid
].sem_perm
.mode
= (semflg
& 0777) | SEM_ALLOC
;
sema
[semid
].sem_perm
.seq
= (sema
[semid
].sem_perm
.seq
+ 1) & 0x7fff; /* avoid semid overflows */
sema
[semid
].sem_nsems
= nsems
;
sema
[semid
].sem_otime
= 0;
sema
[semid
].sem_ctime
= time
.tv_sec
;
sema
[semid
].sem_base
= &sem
[semtot
];
bzero(sema
[semid
].sem_base
,sizeof(sema
[semid
].sem_base
[0])*nsems
);
printf("sembase = 0x%x, next = 0x%x\n",sema
[semid
].sem_base
,&sem
[semtot
]);
printf("didn't find it and wasn't asked to create it\n");
*retval
= IXSEQ_TO_IPCID(semid
,sema
[semid
].sem_perm
); /* Convert to one origin */
register struct semop_args
*uap
;
struct sembuf sops
[MAX_SOPS
];
register struct semid_ds
*semaptr
;
register struct sembuf
*sopptr
;
register struct sem
*semptr
;
struct sem_undo
*suptr
= NULL
;
struct ucred
*cred
= p
->p_ucred
;
int all_ok
, do_wakeup
, do_undos
;
printf("call to semop(%d,0x%x,%d)\n",semid
,sops
,nsops
);
semid
= IPCID_TO_IX(semid
); /* Convert back to zero origin */
if ( semid
< 0 || semid
>= seminfo
.semmsl
) {
/* printf("semid out of range (0<=%d<%d)\n",semid,seminfo.semmsl); */
if ( (semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ) {
/* printf("no such semaphore id\n"); */
if ( semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
) ) {
/* printf("invalid sequence number\n"); */
if ( (eval
= ipcaccess(&semaptr
->sem_perm
, IPC_W
, cred
)) ) {
printf("eval = %d from ipaccess\n",eval
);
if ( nsops
> MAX_SOPS
) {
printf("too many sops (max=%d, nsops=%d)\n",MAX_SOPS
,nsops
);
if ( (eval
= copyin(uap
->sops
, &sops
, nsops
* sizeof(sops
[0]))) != 0 ) {
printf("eval = %d from copyin(%08x, %08x, %d)\n",eval
,uap
->sops
,&sops
,nsops
* sizeof(sops
[0]));
* Loop trying to satisfy the vector of requests.
* If we reach a point where we must wait, any requests already
* performed are rolled back and we go to sleep until some other
* process wakes us up. At this point, we start all over again.
* This ensures that from the perspective of other tasks, a set
* of requests is atomic (never partially satisfied).
for ( i
= 0; i
< nsops
; i
+= 1 ) {
if ( sopptr
->sem_num
>= semaptr
->sem_nsems
) {
semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
semaptr
,semaptr
->sem_base
,semptr
,
sopptr
->sem_num
,semptr
->semval
,sopptr
->sem_op
,
(sopptr
->sem_flg
& IPC_NOWAIT
) ? "nowait" : "wait");
if ( sopptr
->sem_op
< 0 ) {
if ( semptr
->semval
+ sopptr
->sem_op
< 0 ) {
printf("semop: can't do it now\n");
semptr
->semval
+= sopptr
->sem_op
;
if ( semptr
->semval
== 0 && semptr
->semzcnt
> 0 ) {
if ( sopptr
->sem_flg
& SEM_UNDO
) {
} else if ( sopptr
->sem_op
== 0 ) {
if ( semptr
->semval
> 0 ) {
printf("semop: not zero now\n");
if ( semptr
->semncnt
> 0 ) {
semptr
->semval
+= sopptr
->sem_op
;
if ( sopptr
->sem_flg
& SEM_UNDO
) {
* Did we get through the entire vector?
* No ... rollback anything that we've already done
printf("semop: rollback 0 through %d\n",i
-1);
for ( j
= 0; j
< i
; j
+= 1 ) {
semaptr
->sem_base
[sops
[j
].sem_num
].semval
-= sops
[j
].sem_op
;
* If the request that we couldn't satisfy has the NOWAIT
* flag set then return with EAGAIN.
if ( sopptr
->sem_flg
& IPC_NOWAIT
) {
if ( sopptr
->sem_op
== 0 ) {
printf("semop: good night!\n");
eval
= tsleep( (caddr_t
)semaptr
, (PZERO
- 4) | PCATCH
, "sem wait", 0 );
printf("semop: good morning (eval=%d)!\n",eval
);
suptr
= NULL
; /* The sem_undo may have been reallocated */
/* printf("semop: interrupted system call\n"); */
printf("semop: good morning!\n");
* Make sure that the semaphore still exists
if ( (semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0
|| semaptr
->sem_perm
.seq
!= IPCID_TO_SEQ(uap
->semid
) ) {
/* printf("semaphore id deleted\n"); */
/* The man page says to return EIDRM. */
/* Unfortunately, BSD doesn't define that code! */
* The semaphore is still alive. Readjust the count of
if ( sopptr
->sem_op
== 0 ) {
* Process any SEM_UNDO requests.
for ( i
= 0; i
< nsops
; i
+= 1 ) {
/* We only need to deal with SEM_UNDO's for non-zero op's */
if ( (sops
[i
].sem_flg
& SEM_UNDO
) != 0 && (adjval
= sops
[i
].sem_op
) != 0 ) {
eval
= semundo_adjust(p
,&suptr
,semid
,sops
[i
].sem_num
,-adjval
);
* Oh-Oh! We ran out of either sem_undo's or undo's.
* Rollback the adjustments to this point and then
* rollback the semaphore ups and down so we can
* return with an error with all structures restored.
* We rollback the undo's in the exact reverse order that
* we applied them. This guarantees that we won't run
* out of space as we roll things back out.
for ( j
= i
- 1; j
>= 0; j
-= 1 ) {
if ( (sops
[i
].sem_flg
& SEM_UNDO
) != 0 && (adjval
= sops
[i
].sem_op
) != 0 ) {
if ( semundo_adjust(p
,&suptr
,semid
,sops
[j
].sem_num
,adjval
) != 0 ) {
/* This is impossible! */
panic("semop - can't undo undos");
} /* loop backwards through sops */
for ( j
= 0; j
< nsops
; j
+= 1 ) {
semaptr
->sem_base
[sops
[j
].sem_num
].semval
-= sops
[j
].sem_op
;
printf("eval = %d from semundo_adjust\n",eval
);
} /* semundo_adjust failed */
} /* if ( SEM_UNDO && adjval != 0 ) */
} /* loop through the sops */
/* We're definitely done - set the sempid's */
for ( i
= 0; i
< nsops
; i
+= 1 ) {
semptr
= &semaptr
->sem_base
[sopptr
->sem_num
];
semptr
->sempid
= p
->p_pid
;
/* Do a wakeup if any semaphore was up'd. */
printf("semop: doing wakeup\n");
sem_wakeup( (caddr_t
)semaptr
);
wakeup( (caddr_t
)semaptr
);
printf("semop: back from wakeup\n");
wakeup( (caddr_t
)semaptr
);
panic("semop: how did we get here???");
* Go through the undo structures for this process and apply the
* adjustments to semaphores.
register struct sem_undo
*suptr
;
register struct sem_undo
**supptr
;
* If somebody else is holding the global semaphore facility lock
* then sleep until it is released.
while ( semlock_holder
!= NULL
&& semlock_holder
!= p
) {
printf("semaphore facility locked - sleeping ...\n");
tsleep( (caddr_t
)&semlock_holder
, (PZERO
- 4), "semexit", 0 );
* Go through the chain of undo vectors looking for one
* associated with this process.
for ( supptr
= &semu_list
;
(suptr
= *supptr
) != NULL
;
supptr
= &(suptr
->un_next
)
if ( suptr
->un_proc
== p
) {
printf("proc @%08x has undo structure with %d entries\n",p
,suptr
->un_cnt
);
* If there are any active undo elements then process them.
if ( suptr
->un_cnt
> 0 ) {
for ( ix
= 0; ix
< suptr
->un_cnt
; ix
+= 1 ) {
int semid
= suptr
->un_ent
[ix
].un_id
;
int semnum
= suptr
->un_ent
[ix
].un_num
;
int adjval
= suptr
->un_ent
[ix
].un_adjval
;
struct semid_ds
*semaptr
;
if ( (semaptr
->sem_perm
.mode
& SEM_ALLOC
) == 0 ) {
panic("semexit - semid not allocated");
if ( semnum
>= semaptr
->sem_nsems
) {
panic("semexit - semnum out of range");
printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",suptr
->un_proc
,
suptr
->un_ent
[ix
].un_id
,suptr
->un_ent
[ix
].un_num
,suptr
->un_ent
[ix
].un_adjval
,
semaptr
->sem_base
[semnum
].semval
);
if ( semaptr
->sem_base
[semnum
].semval
< -adjval
) {
semaptr
->sem_base
[semnum
].semval
= 0;
semaptr
->sem_base
[semnum
].semval
+= adjval
;
semaptr
->sem_base
[semnum
].semval
+= adjval
;
/* printf("semval now %d\n",semaptr->sem_base[semnum].semval); */
sem_wakeup((caddr_t
)semaptr
); /* A little sloppy (we should KNOW if anybody is waiting). */
wakeup((caddr_t
)semaptr
); /* A little sloppy (we should KNOW if anybody is waiting). */
printf("semexit: back from wakeup\n");
* Deallocate the undo vector.
printf("removing vector\n");
*supptr
= suptr
->un_next
;
* If the exiting process is holding the global semaphore facility
if ( semlock_holder
== p
) {
wakeup( (caddr_t
)&semlock_holder
);