Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1304 lines
29 KiB
C

static char sccsid[] = "@(#)19 1.23.1.7 src/bos/kernel/ipc/sem.c, sysipc, bos411, 9431A411a 7/22/94 10:14:10";
/*
* COMPONENT_NAME: (SYSIPC) IPC semaphore services
*
* FUNCTIONS: semexit, semget, semop, atomic, order, semsleep, semundo
* semunrm sem_lock_init
*
* ORIGINS: 27, 3, 83
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1987, 1994
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
*/
/*
* LEVEL 1,5 Years Bull Confidential Information
*/
#include <sys/user.h>
#include <sys/sem.h>
#ifndef _POWER_MP
#include <sys/lockl.h>
#endif
#include <sys/errno.h>
#include <sys/sysinfo.h>
#include <sys/malloc.h>
#include <sys/syspest.h>
#include <sys/sleep.h>
#include <sys/trchkid.h>
#include <sys/audit.h>
#include <sys/id.h>
#include "ipcspace.h"
#ifdef _POWER_MP
#include <sys/atomic_op.h>
#include <sys/lock_def.h>
#include <sys/lock_alloc.h>
#include <sys/lockname.h>
#endif
extern struct semid_ds sema[]; /* semaphore data structures */
extern struct seminfo seminfo; /* param information structure */
struct sem_undo *semunp = NULL; /* ptr to head of undo chain */
#ifndef _POWER_MP
lock_t sem_lock = LOCK_AVAIL; /* semaphore services lock */
#else
/*
* The follwing value is a tunable value. The current value (16) might be small.
*/
#define NB_SEM_LOCKS 16 /* Number of locks for SEMMNI sets */
Simple_lock lock_sem_creat; /* Lock to create a non private set */
Simple_lock lock_sem_qid[NB_SEM_LOCKS];
/* Locks to access semaphore sets */
Simple_lock lock_sem_undo; /* Lock to access any undo structure */
/* or the chained list (semunp) */
#endif
uint_t sem_mark = 0; /* high water mark for descriptors */
extern time_t time; /* system idea of date */
struct semid_ds *ipcget(),
*semconv();
void atomic(),
order(),
semundo(),
semunrm(),
semexit();
/*
* NAME: sem_lock_init
*
* FUNCTION: This function is called in ipc_lock_init() in ipc.c.
* ipc_lock_init() is called during system initialization when
* _POWER_MP is defined.
*
* EXECUTION ENVIRONMENT: Process
*
* DATA STRUCTURES: lock_sem_creat, lock_sem_undo, lock_sem_qid[]
*
* RETURNS: None.
*/
#ifdef _POWER_MP
void
sem_lock_init()
{
int i;
/* initialize the semaphore locks */
lock_alloc(&lock_sem_creat,LOCK_ALLOC_PAGED,
SEM_LOCK_CLASS, 32767);
simple_lock_init(&lock_sem_creat);
lock_alloc(&lock_sem_undo,LOCK_ALLOC_PAGED,
SEM_LOCK_CLASS, 32766);
simple_lock_init(&lock_sem_undo);
for (i=0;i < NB_SEM_LOCKS;i++) {
lock_alloc(&lock_sem_qid[i],LOCK_ALLOC_PAGED,
SEM_LOCK_CLASS, i);
simple_lock_init(&lock_sem_qid[i]);
}
}
#endif /* _POWER_MP */
/*
* NAME: semaoe
*
* FUNCTION: Create or update adjust on exit entry.
*
* EXECUTION ENVIRONMENT:
* called by semaphore system calls
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*
* MP MODIFICATION:
* Need the lock_sem_undo_lock to modify any undo structure or the
* chained list of undo structures. Means take the lock all the time.
*/
int
semaoe(val, id, num)
short val, /* operation value to be adjusted on exit */
num; /* semaphore # */
int id; /* semid */
{
register struct undo *uup, /* ptr to entry to update */
*uup2; /* ptr to move entry */
register struct sem_undo *up, /* ptr to process undo struct */
*up2; /* ptr to undo list */
register int i, /* loop control */
found; /* matching entry found flag */
if(val == 0)
return(0);
if(val > seminfo.semaem || val < -seminfo.semaem) {
u.u_error = ERANGE;
return(-1);
}
#ifdef _POWER_MP
simple_lock(&lock_sem_undo);
#endif
if((up = (struct sem_undo *)U.U_semundo) == NULL) {
if( (up = xmalloc( seminfo.semusz, 2, kernel_heap)) == NULL ){
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
u.u_error = ENOMEM;
return(-1);
}
up->un_np = NULL;
up->un_cnt = 0;
U.U_semundo = (void *)up;
}
for(uup = up->un_ent, found = i = 0;i < up->un_cnt;i++) {
if(uup->un_id < id || (uup->un_id == id && uup->un_num < num)) {
uup++;
continue;
}
if(uup->un_id == id && uup->un_num == num)
found = 1;
break;
}
if(!found) {
if(up->un_cnt >= seminfo.semume) {
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
u.u_error = EINVAL;
return(1);
}
if(up->un_cnt == 0) {
up->un_np = semunp;
semunp = up;
}
uup2 = &up->un_ent[up->un_cnt++];
while(uup2-- > uup)
*(uup2 + 1) = *uup2;
uup->un_id = id;
uup->un_num = num;
uup->un_aoe = -val;
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
return(0);
}
uup->un_aoe -= val;
if(uup->un_aoe > seminfo.semaem || uup->un_aoe < -seminfo.semaem) {
uup->un_aoe += val;
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
u.u_error = ERANGE;
return(1);
}
if(uup->un_aoe == 0) {
uup2 = &up->un_ent[--(up->un_cnt)];
while(uup++ < uup2)
*(uup - 1) = *uup;
if(up->un_cnt == 0) {
/* Remove process from undo list. */
if(semunp == up)
semunp = up->un_np;
else
for(up2 = semunp;up2 != NULL;up2 = up2->un_np)
if(up2->un_np == up) {
up2->un_np = up->un_np;
break;
}
up->un_np = NULL;
}
}
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
return(0);
}
/*
* NAME: semconv
*
* FUNCTION: Convert user supplied semid into a ptr to the associated
* semaphore header.
*
* EXECUTION ENVIRONMENT:
* Executed during system call
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS:
* pointer to semaphore header if successful
* NULL if not successful
*
* MP MODIFICATION:
* Upon success, hold the lock protecting the access to the semaphore set.
*/
struct semid_ds *
semconv(s)
uint_t s; /* semid */
{
register struct semid_ds *sp; /* ptr to associated header */
uint_t index; /* descriptor index */
uint_t seq; /* sequence number */
seq = s / seminfo.semmni;
index = s % seminfo.semmni;
if ( index >= sem_mark ) {
u.u_error = EINVAL;
return( NULL );
}
sp = &sema[index]; /* low 16 bits of semid=index*/
#ifdef _POWER_MP
simple_lock(&lock_sem_qid[s % NB_SEM_LOCKS]);
#endif
if ( (seq != sp->sem_perm.seq) ||
!(sp->sem_perm.mode & IPC_ALLOC) ) {
#ifdef _POWER_MP
simple_unlock(&lock_sem_qid[s % NB_SEM_LOCKS]);
#endif
u.u_error = EINVAL;
return(NULL);
}
return(sp);
}
/*
* NAME: semctl
*
* FUNCTION: semctl system call
*
* EXECUTION ENVIRONMENT:
* System call
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*
* MP MODIFICATION:
* Reset the key to IPC_PRIVATE while removing a semaphore set.
*/
semctl( int semid, int semnum, int cmd, int arg)
{
register struct semid_ds *sp; /* ptr to semaphore header */
register struct sem *p; /* ptr to semaphore */
register int i; /* loop control */
register int rval; /* return value */
struct semid_ds semds; /* copy of header */
short *array; /* copy of array parameter */
short *tp;
int bytes;
int rv;
static int svcnumD = 0;
static int svcnumO = 0;
static int svcnumM = 0;
uid_t current_uid;
gid_t current_gid;
current_uid = getuidx(ID_EFFECTIVE);
current_gid = getgidx(ID_EFFECTIVE);
TRCHKGT_SYSC(SEMCTL, semid, semnum, cmd, arg, NULL);
#ifndef _POWER_MP
sysinfo.sema++; /* update sysinfo semaphore counter */
cpuinfo[CPUID].sema++;
#else
fetch_and_add((atomic_p) &sysinfo.sema,1);
fetch_and_add((atomic_p) &cpuinfo[CPUID].sema,1);
#endif
#ifndef _POWER_MP
rv = lockl(&sem_lock,LOCK_SHORT);
ASSERT( LOCK_SUCC == rv );
#endif
/*
* MP mode : On successful completion,
* semconv() holds the lock protecting the semaphore set.
*/
if((sp = semconv(semid)) == NULL)
goto outxnr;
rval = 0;
switch(cmd) {
/* Remove semaphore set. */
case IPC_RMID:
if(audit_flag && audit_svcstart("SEM_Delete", &svcnumD, 1, semid)){
audit_svcfinis();
}
if(current_uid != sp->sem_perm.uid && current_uid != sp->sem_perm.cuid
&& !priv_req(BYPASS_DAC_WRITE)) {
u.u_error = EPERM;
break;
}
semunrm(semid, 0, sp->sem_nsems);
for(i = sp->sem_nsems, p = sp->sem_base;i--;p++) {
p->semval = p->sempid = 0;
if(p->semncnt) {
e_wakeup(&p->semnwait);
p->semncnt = 0;
}
if(p->semzcnt) {
e_wakeup(&p->semzwait);
p->semzcnt = 0;
}
}
rv = xmfree( sp->sem_base , kernel_heap );
ASSERT( rv == 0 );
sp->sem_perm.seq++;
#ifdef _POWER_MP
/*
* MP mode : Avoid a concurrent semget() finding a released set.
*/
sp->sem_perm.key = IPC_PRIVATE;
#endif
sp->sem_perm.mode = 0;
break;
/* Set ownership and permissions. */
case IPC_SET:
if(current_uid != sp->sem_perm.uid && current_uid != sp->sem_perm.cuid
&& !priv_req(SET_OBJ_DAC)) {
u.u_error = EPERM;
break;
}
if(copyin(arg, &semds, sizeof(semds))) {
u.u_error = EFAULT;
break;
}
if(audit_flag && audit_svcstart("SEM_Owner", &svcnumO, 3,
semid, semds.sem_perm.uid, semds.sem_perm.gid)){
audit_svcfinis();
}
sp->sem_perm.uid = semds.sem_perm.uid;
sp->sem_perm.gid = semds.sem_perm.gid;
sp->sem_perm.mode = (semds.sem_perm.mode & 0777) | IPC_ALLOC;
sp->sem_ctime = time;
break;
/* Get semaphore data structure. */
case IPC_STAT:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
if(audit_flag && audit_svcstart("SEM_Mode", &svcnumM, 2,
semid, semds.sem_perm.mode)){
audit_svcfinis();
}
if(copyout(sp, arg, sizeof(*sp))) {
u.u_error = EFAULT;
break;
}
break;
/* Get # of processes sleeping for greater semval. */
case GETNCNT:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
if(semnum >= sp->sem_nsems) {
u.u_error = EINVAL;
break;
}
rval = (sp->sem_base + semnum)->semncnt;
break;
/* Get pid of last process to operate on semaphore. */
case GETPID:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
if(semnum >= sp->sem_nsems) {
u.u_error = EINVAL;
break;
}
rval = (sp->sem_base + semnum)->sempid;
break;
/* Get semval of one semaphore. */
case GETVAL:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
if(semnum >= sp->sem_nsems) {
u.u_error = EINVAL;
break;
}
rval = (sp->sem_base + semnum)->semval;
break;
/* Get all semvals in set. */
case GETALL:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
bytes = sp->sem_nsems * sizeof(short);
if( (array = xmalloc(bytes, 2, kernel_heap)) == NULL ) {
u.u_error = ENOMEM;
break;
}
for( i = sp->sem_nsems, tp = array, p = sp->sem_base;
i ; p++,tp++,i-- )
*tp = p->semval;
if( copyout(array, arg, bytes) )
u.u_error = EFAULT;
rv = xmfree(array, kernel_heap);
ASSERT( rv == 0 );
break;
/* Get # of processes sleeping for semval to become zero. */
case GETZCNT:
if(ipcaccess(&sp->sem_perm, SEM_R))
break;
if(semnum >= sp->sem_nsems) {
u.u_error = EINVAL;
break;
}
rval = (sp->sem_base + semnum)->semzcnt;
break;
/* Set semval of one semaphore. */
case SETVAL:
if(ipcaccess(&sp->sem_perm, SEM_A))
break;
if(semnum >= sp->sem_nsems) {
u.u_error = EINVAL;
break;
}
if((unsigned)arg > (unsigned)seminfo.semvmx) {
u.u_error = ERANGE;
break;
}
if((p = sp->sem_base + semnum)->semval = arg) {
if(p->semncnt) {
p->semncnt = 0;
e_wakeupx(&p->semnwait, E_WKX_NO_PREEMPT);
}
} else
if(p->semzcnt) {
p->semzcnt = 0;
e_wakeupx(&p->semzwait, E_WKX_NO_PREEMPT);
}
p->sempid = U.U_procp->p_pid;
semunrm(semid, semnum, semnum);
break;
/* Set semvals of all semaphores in set. */
case SETALL:
if(ipcaccess(&sp->sem_perm, SEM_A))
break;
bytes = sizeof(short) * sp->sem_nsems;
if( (array = xmalloc(bytes, 2, kernel_heap)) == NULL ){
BUGPR(("xmalloc failed\n"));
u.u_error = ENOMEM;
break;
}
if( copyin(arg , array, bytes) ) {
xmfree(array, kernel_heap);
u.u_error = EFAULT;
break;
}
for(i = 0;i < sp->sem_nsems;)
if((unsigned)array[i++] > (unsigned)seminfo.semvmx) {
xmfree(array, kernel_heap);
u.u_error = ERANGE;
goto outxnr_0;
}
semunrm(semid, 0, sp->sem_nsems);
for(i = 0, p = sp->sem_base;i < sp->sem_nsems;
(p++)->sempid = U.U_procp->p_pid) {
if(p->semval = array[i++]) {
if(p->semncnt) {
p->semncnt = 0;
e_wakeupx(&p->semnwait,
E_WKX_NO_PREEMPT);
}
} else
if(p->semzcnt) {
p->semzcnt = 0;
e_wakeupx(&p->semzwait,
E_WKX_NO_PREEMPT);
}
}
xmfree(array, kernel_heap);
break;
default:
u.u_error = EINVAL;
break;
}
outxnr_0:
#ifdef _POWER_MP
simple_unlock(&lock_sem_qid[semid % NB_SEM_LOCKS]);
#endif
outxnr:
#ifndef _POWER_MP
unlockl(&sem_lock);
#endif
return u.u_error ? -1 : rval;
}
/*
* NAME: semexit
*
* FUNCTION: Cleans up semaphore resources on exit
*
* EXECUTION ENVIRONMENT:
* Called by exit to clean up on process exit.
*
* RETURNS: NONE
*
* MP MODIFICATION:
* There's only one thread remaining in the process. So we don't need to
* protect concurrent accesses to the proc's undo structure.
* We remove the undo structure from the chained list ASAP. Then we can
* work on our own without the lock_sem_undo lock.
* We just need the lock_sem_undo lock to protect the removal of the undo
* structure from the chained list.
*/
void
semexit()
{
register struct sem_undo *up, /* process undo struct ptr */
*p; /* undo struct ptr */
register struct semid_ds *sp; /* semid being undone ptr */
register int i; /* loop control */
register long v; /* adjusted value */
register struct sem *semp; /* semaphore ptr */
if((up = (struct sem_undo *)U.U_semundo) == NULL)
return;
#ifndef _POWER_MP
i = lockl( &sem_lock, LOCK_SHORT );
ASSERT( i == LOCK_SUCC );
#endif
if(up->un_cnt == 0)
goto cleanup;
#ifdef _POWER_MP
simple_lock(&lock_sem_undo);
#endif
if(semunp == up)
semunp = up->un_np;
else
for(p = semunp;p != NULL;p = p->un_np)
if(p->un_np == up) {
p->un_np = up->un_np;
break;
}
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
for(i = up->un_cnt;i--;) {
/*
* MP mode : On successful completion,
* semconv() holds the lock protecting the semaphore set.
*/
if((sp = semconv(up->un_ent[i].un_id)) == NULL)
continue;
v = (long)(semp = sp->sem_base + up->un_ent[i].un_num)->semval +
up->un_ent[i].un_aoe;
if(v < 0 || v > seminfo.semvmx) {
#ifdef _POWER_MP
simple_unlock(&lock_sem_qid
[up->un_ent[i].un_id % NB_SEM_LOCKS]);
#endif
continue;
}
semp->semval = v;
if(v == 0 && semp->semzcnt) {
semp->semzcnt = 0;
e_wakeupx(&semp->semzwait, E_WKX_NO_PREEMPT);
}
if(up->un_ent[i].un_aoe > 0 && semp->semncnt) {
semp->semncnt = 0;
e_wakeupx(&semp->semnwait, E_WKX_NO_PREEMPT);
}
#ifdef _POWER_MP
simple_unlock(&lock_sem_qid
[up->un_ent[i].un_id % NB_SEM_LOCKS]);
#endif
}
up->un_cnt = 0;
cleanup:
i = xmfree( up, kernel_heap );
ASSERT( i == 0 );
U.U_semundo = NULL;
#ifndef _POWER_MP
unlockl( &sem_lock );
#endif
}
/*
* NAME: semget
*
* FUNCTION: semget system call
*
* EXECUTION ENVIRONMENT:
* system call
*
* DATA STRUCTURES: set u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*
* MP MODIFICATION:
* Take the lock_sem_creat lock to create a non private semaphore set.
* Set the value of the key as the last initialisation operation.
*/
int
semget( key_t key, int nsems, int semflg)
{
register struct semid_ds * sp; /* semaphore header ptr */
register int i; /* temp */
int s; /* ipcget status return */
struct ipc_id * ip;
uint_t rval;
int rv;
struct sem *semp; /* allocated semaphore */
int svcrc = 0;
static int svcnum = 0;
if (audit_flag)
svcrc = audit_svcstart("SEM_Create",&svcnum,0);
TRCHKGT_SYSC(SEMGET, key, nsems, semflg, NULL, NULL);
#ifndef _POWER_MP
sysinfo.sema++; /* update sysinfo semaphore counter */
cpuinfo[CPUID].sema++;
#else
fetch_and_add((atomic_p) &sysinfo.sema,1);
fetch_and_add((atomic_p) &cpuinfo[CPUID].sema,1);
#endif
#ifndef _POWER_MP
rv = lockl(&sem_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
#else
if ((key != IPC_PRIVATE) && (semflg & IPC_CREAT))
simple_lock(&lock_sem_creat);
#endif
if((sp = ipcget(key, semflg, sema, seminfo.semmni, sizeof(*sp), &s,
&sem_mark)) == NULL)
goto outx;
if(s) {
/* This is a new semaphore set. Finish initialization. */
if(nsems <= 0 || nsems > seminfo.semmsl) {
u.u_error = EINVAL;
sp->sem_perm.mode = 0;
goto outx;
}
if( (sp->sem_base = xmalloc(sizeof(struct sem)*nsems, 2,
kernel_heap)) == NULL ) {
BUGPR(("xmalloc failed\n" ));
u.u_error = ENOMEM;
sp->sem_perm.mode = 0;
goto outx;
}
/*
* initialize semaphores
*/
bzero( sp->sem_base, sizeof(struct sem) * nsems);
semp = sp->sem_base;
for( i = 0 ; i < nsems ; i++ ) {
semp->semnwait = EVENT_NULL;
semp->semzwait = EVENT_NULL;
semp++;
}
sp->sem_nsems = nsems;
sp->sem_ctime = time;
sp->sem_otime = 0;
#ifdef _POWER_MP
sp->sem_perm.key = key;
#endif
} else
if(nsems && sp->sem_nsems < nsems) {
u.u_error = EINVAL;
goto outx;
}
/* set semid value to sem_perm.seq */
/* || index into sema array */
rval = (sp->sem_perm.seq * seminfo.semmni) + (sp - sema);
outx:
if(svcrc){
if(u.u_error){
int bval = -1;
audit_svcbcopy(&bval, sizeof(int));
audit_svcfinis();
}
else {
audit_svcbcopy(&rval, sizeof(uint_t));
audit_svcfinis();
}
}
#ifndef _POWER_MP
unlockl(&sem_lock);
#else
if ((key != IPC_PRIVATE) && (semflg & IPC_CREAT))
simple_unlock(&lock_sem_creat);
#endif
return u.u_error ? -1 : rval;
}
/*
* NAME: semop
*
* FUNCTION: semop system call
*
* EXECUTION ENVIRONMENT:
* system call
*
* DATA STRUCTURES:
* changes u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*/
int
semop(int semid, struct sembuf *sops, size_t nsops)
{
register struct semid_ds *sp; /* ptr to associated header */
register struct sembuf *op;
ushort perm_req;
int i;
int rv;
static int svcnum = 0;
struct sembuf *ksops;
struct sembuf sopbuf[32];
int free, lock;
if(audit_flag && audit_svcstart("SEM_Op", &svcnum, 1, semid)){
audit_svcfinis();
}
TRCHKGT_SYSC(SEMOP, semid, sops, nsops, NULL, NULL);
#ifndef _POWER_MP
sysinfo.sema++; /* update sysinfo semaphore counter */
cpuinfo[CPUID].sema++;
#else
fetch_and_add((atomic_p) &sysinfo.sema,1);
fetch_and_add((atomic_p) &cpuinfo[CPUID].sema,1);
#endif
/* free must be initialized early, because of jumps to out_op
*/
free = 0;
lock = 0;
if (nsops == 0)
goto out_op;
if(nsops > seminfo.semopm)
{
u.u_error = E2BIG;
goto out_op;
}
/* xmallocs are expensive, so it the number of semops is small copy
* them onto stack. This covers the majority of semop calls. Use
* xmalloc on large values
*/
if (nsops <= (sizeof(sopbuf)/sizeof(sopbuf[0])))
{
ksops = sopbuf;
}
else
{
ksops = (struct sembuf *)xmalloc(nsops*sizeof(struct sembuf),
2, kernel_heap);
if (ksops == NULL)
{
u.u_error = ENOMEM;
goto out_op;
}
free = 1;
}
/* do copy in before getting the lock, to avoid page fault while
* holding the semaphore lock
*/
if ( copyin((caddr_t)sops, (caddr_t)ksops,(int)(nsops*sizeof(*sops))))
{
u.u_error = EFAULT;
goto out_op;
}
#ifndef _POWER_MP
rv = lockl(&sem_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
lock = 1;
#endif
/*
* Check for valid semid, number of operations within limit,
* valid addresses, and semnums within range of semaphore
* array -- includes copy from user space to kernel space.
*/
/*
* MP mode : On successful completion,
* semconv() holds the lock protecting the semaphore set.
*/
if((sp = semconv(semid)) == NULL)
goto out_op;
#ifdef _POWER_MP
lock = 1;
#endif
/* # operations = 0; don't update sem_otime */
/* or sem_pid */
/* Verify that sem #s are in range and */
/* permissions are granted. */
perm_req = 0;
for(i = 0, op = (ksops);i++ < nsops;op++)
{
perm_req = perm_req | op->sem_op;
if(op->sem_num >= sp->sem_nsems)
{
u.u_error = EFBIG;
goto out_op;
}
}
if (ipcaccess(&sp->sem_perm, perm_req ? SEM_A : SEM_R))
goto out_op;
/* process semaphore operations in array */
if (ksops[0].sem_flg & SEM_ORDER)
order (sp,semid,ksops,sops,nsops);
else
atomic(sp,semid,ksops,nsops);
/* set time in semaphore if all ops o.k. */
if (u.u_error == 0)
sp->sem_otime = time;
out_op:
if (lock)
#ifndef _POWER_MP
unlockl(&sem_lock);
#else
simple_unlock(&lock_sem_qid[semid % NB_SEM_LOCKS]);
#endif
if (free)
xmfree(ksops, kernel_heap);
return u.u_error ? -1 : 0;
}
/*
* NAME: atomic
*
* FUNCTION: atomic semaphore operations
*
* EXECUTION ENVIRONMENT:
* Called once by the semop sytem call
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS: NONE
*/
void
atomic(sp,semid,sops,nsops)
register struct semid_ds * sp; /* ptr to semaphore data structure */
int semid;
struct sembuf * sops;
size_t nsops;
{
register struct sem *semp; /* ptr to semaphore */
register struct sembuf *op; /* ptr to semaphore operation */
register int i;
/*
* Loop waiting for the operations to be satisfied atomically.
* Actually, do the operations and undo them if a wait is needed
* or an error is detected.
*/
loopagain:
for(i = 0, op = (sops);i < nsops;i++, op++)
{
semp = sp->sem_base + op->sem_num;
if(op->sem_op > 0)
{
if(op->sem_op + (long)semp->semval > seminfo.semvmx ||
(op->sem_flg & SEM_UNDO &&
semaoe(op->sem_op, semid, op->sem_num)))
{
if(u.u_error == 0)
u.u_error = ERANGE;
if(i)
semundo((sops), i, semid, sp);
return ;
}
semp->semval += op->sem_op;
if(semp->semncnt) {
semp->semncnt = 0;
e_wakeupx(&semp->semnwait, E_WKX_NO_PREEMPT);
}
continue;
}
if(op->sem_op < 0)
{
if(semp->semval >= -op->sem_op)
{
if(-op->sem_op > seminfo.semvmx ||
(op->sem_flg & SEM_UNDO &&
semaoe(op->sem_op, semid, op->sem_num)))
{
if (u.u_error == 0)
u.u_error = ERANGE;
if(i)
semundo((sops), i, semid, sp);
return ;
}
semp->semval += op->sem_op;
if(semp->semval == 0 && semp->semzcnt)
{
semp->semzcnt = 0;
e_wakeupx(&semp->semzwait,
E_WKX_NO_PREEMPT);
}
continue;
}
if(i)
semundo((sops), i, semid, sp);
if(op->sem_flg & IPC_NOWAIT)
{
u.u_error = EAGAIN;
return ;
}
if( u.u_error = semsleep(&semp->semncnt,
&semp->semnwait, sp) )
return;
else goto loopagain;
}
if(semp->semval)
{ if(i)
semundo((sops), i, semid, sp);
if(op->sem_flg & IPC_NOWAIT)
{ u.u_error = EAGAIN;
return ;
}
if( u.u_error = semsleep(&semp->semzcnt,
&semp->semzwait, sp) )
return;
else goto loopagain;
}
}
/*
* All operations succeeded. Update sempid for accessed semaphores.
*/
for(i = 0, op = (sops);i++ < nsops;
(sp->sem_base + (op++)->sem_num)->sempid = U.U_procp->p_pid);
}
/*
* NAME: order
*
* FUNCTION: does semaphore operations in order
*
* EXECUTION ENVIRONMENT:
* Called by semops system call
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS: NONE
*/
void
order(sp,semid,sops,usops,nsops)
register struct semid_ds * sp; /* ptr to semaphore header */
int semid;
struct sembuf * sops;
struct sembuf * usops; /* user copy of semops */
size_t nsops;
{
register struct sem *semp; /* ptr to semaphore */
register struct sembuf *op; /* ptr to semaphore operation*/
register int i;
/* LOOP THROUGH SEMAPHORE OPERATIONS DOING THEM IN */
/* ORDER UNTIL DONE OR AN ERROR CAUSES RETURN */
for(i=0, op = (sops); i < nsops; i++, op++)
{ semp = sp->sem_base + op->sem_num;
op->sem_flg &= ~SEM_ERR; /* reset SEM_ERR bit */
if (op->sem_op == 0) /* BEGIN ZERO SEM_OP */
{ if (semp->semval != 0)
if (op->sem_flg & IPC_NOWAIT)
{ u.u_error = EAGAIN;
goto out_order;
}
else do
if (u.u_error = semsleep(
&semp->semzcnt,
&semp->semzwait, sp))
goto out_order;
while (semp->semval != 0);
semp->sempid = U.U_procp->p_pid;
} /* END OF ZERO SEM_OP */
else if (op->sem_op > 0) /* BEGIN POS SEM_OP */
{ if (op->sem_op + (long)semp->semval > seminfo.semvmx)
{ u.u_error = ERANGE;
goto out_order;
}
if (op->sem_flg & SEM_UNDO)
if(semaoe(op->sem_op, semid, op->sem_num))
goto out_order;
semp->semval += op->sem_op;
if(semp->semncnt)
{ semp->semncnt = 0;
e_wakeupx(&semp->semnwait, E_WKX_NO_PREEMPT);
}
semp->sempid = U.U_procp->p_pid;
} /* END OF POS SEM_OP */
else /* BEGIN NEG SEM_OP */
{ if (-op->sem_op > seminfo.semvmx)
{ u.u_error = ERANGE;
goto out_order;
}
if (semp->semval < -op->sem_op)
{ if (op->sem_flg & IPC_NOWAIT)
{ u.u_error = EAGAIN;
goto out_order;
}
else do
if(u.u_error = semsleep(
&semp->semncnt,
&semp->semnwait,sp))
goto out_order;
while (semp->semval < -op->sem_op);
}
if (op->sem_flg & SEM_UNDO)
if(semaoe(op->sem_op, semid, op->sem_num))
goto out_order;
semp->semval += op->sem_op;
if (semp->semval == 0 && semp->semzcnt)
{ semp->semzcnt = 0;
e_wakeupx(&semp->semzwait, E_WKX_NO_PREEMPT);
}
semp->sempid = U.U_procp->p_pid;
} /* END OF NEG SEM_OP */
}
return;
out_order:
(sops+i)->sem_flg |= SEM_ERR;
if ( copyout(&( (sops+i)->sem_flg ), &( (usops+i)->sem_flg),
sizeof(usops->sem_flg)) )
u.u_error = EFAULT;
}
/*
* NAME: semsleep
*
* FUNCTION: sleep on semaphore event
*
* RETURNS:
* 0 - if successful
* EINTR - if signal received
* EIDRM - if sem id was removed while sleeping
*
* MP MODIFICATION:
* Replace the call to e_sleepl by a call to e_sleep_thread, with the
* appropriate lock. Modify the test of the return code, too.
*/
int
semsleep(count, wait_list, sp)
ushort *count; /* number of procs on list */
int *wait_list; /* wait list to be added to */
struct semid_ds *sp; /* pointer to semaphore header */
{
int sleeprtn; /* return value from e_sleep */
int seq; /* sequence number when released */
#ifdef _POWER_MP
int semid;
#endif
(*count)++;
seq = sp->sem_perm.seq;
#ifndef _POWER_MP
sleeprtn = e_sleepl(&sem_lock, wait_list, EVENT_SIGRET);
if (sleeprtn == EVENT_SIG) {
#else
semid = (seq * seminfo.semmni) + (sp - sema);
sleeprtn = e_sleep_thread(wait_list,
&lock_sem_qid[semid % NB_SEM_LOCKS],
LOCK_SIMPLE|INTERRUPTIBLE);
if (sleeprtn == THREAD_INTERRUPTED) {
#endif
if ( (sp->sem_perm.mode & IPC_ALLOC) &&
(sp->sem_perm.seq == seq) ) {
if ( *count <= 1 ) {
*count = 0;
e_wakeupx( wait_list, E_WKX_NO_PREEMPT );
} else {
(*count)--;
}
}
return(EINTR);
} else {
if ( ((sp->sem_perm.mode & IPC_ALLOC) == 0) ||
(sp->sem_perm.seq != seq) )
return (EIDRM);
return (0);
}
}
/*
* NAME: semundo
*
* FUNCTION: Undo work done up to finding an operation that can't be done.
*
* EXECUTION ENVIRONMENT:
* Called during semops system call
*
* RETURNS: NONE
*/
void
semundo(op, n, id, sp)
register struct sembuf *op; /* first operation that was done ptr */
register int n, /* # of operations that were done */
id; /* semaphore id */
register struct semid_ds *sp; /* semaphore data structure ptr */
{
register struct sem *semp; /* semaphore ptr */
for(op += n - 1;n--;op--) {
if(op->sem_op == 0)
continue;
semp = sp->sem_base + op->sem_num;
semp->semval -= op->sem_op;
if(op->sem_flg & SEM_UNDO)
semaoe(-op->sem_op, id, op->sem_num);
}
}
/*
* NAME: semunrm
*
* FUNCTION: Undo entry remover.
*
* EXECUTION ENVIRONMENT:
* Called by semctl system call
*
* NOTES:
* This routine is called to clear all undo entries for a set of
* semaphores that are being removed from the system or are being
* reset by SETVAL or SETVALS commands to semctl.
*
* RETURNS: NONE
*
* MP MODIFICATION:
* Need the lock_sem_undo_lock to modify any undo structure or the
* chained list of undo structures. Means take the lock all the time.
*/
void
semunrm(id, low, high)
int id; /* semid */
ushort low, /* lowest semaphore being changed */
high; /* highest semaphore being changed */
{
register struct sem_undo *pp, /* ptr to predecessor to p */
*p; /* ptr to current entry */
register struct undo *up; /* ptr to undo entry */
register int i, /* loop control */
j; /* loop control */
pp = NULL;
#ifdef _POWER_MP
simple_lock(&lock_sem_undo);
#endif
p = semunp;
while(p != NULL) {
/* Search through current structure for matching entries. */
for(up = p->un_ent, i = 0;i < p->un_cnt;) {
if(id < up->un_id)
break;
if(id > up->un_id || low > up->un_num) {
up++;
i++;
continue;
}
if(high < up->un_num)
break;
for(j = i;++j < p->un_cnt;
p->un_ent[j - 1] = p->un_ent[j]);
p->un_cnt--;
}
/* Reset pointers for next round. */
if(p->un_cnt == 0)
/* Remove from linked list. */
if(pp == NULL) {
semunp = p->un_np;
p->un_np = NULL;
p = semunp;
} else {
pp->un_np = p->un_np;
p->un_np = NULL;
p = pp->un_np;
}
else {
pp = p;
p = p->un_np;
}
}
#ifdef _POWER_MP
simple_unlock(&lock_sem_undo);
#endif
}