2021-10-11 22:19:34 -03:00

857 lines
18 KiB
C

static char sccsid[] = "@(#)20 1.33.1.16 src/bos/kernel/ipc/shm.c, sysipc, bos411, 9439A411b 9/26/94 11:40:07";
/*
* COMPONENT_NAME: (SYSIPC) IPC shared memory services
*
* FUNCTIONS: shmget, shmat, shmdt, shmctl, shmconv, ishmdt, shmex
* shmfork, shmfree, shm_getseg, shmwait, psrmshm, shm_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/shm.h>
#include <sys/lockl.h>
#include <sys/errno.h>
#include <sys/vmuser.h>
#include <sys/adspace.h>
#include <sys/malloc.h>
#include <sys/syspest.h>
#include <sys/trchkid.h>
#include <sys/priv.h>
#include <sys/audit.h>
#include <sys/id.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 shmid_ds shmem[]; /* shared memory headers */
extern struct shminfo shminfo; /* sys-wide shared memory info structure */
extern time_t time; /* system idea of date */
#ifdef _POWER_MP
Simple_lock lock_shm;
#define LOCK_SHM -1
#else
lock_t shm_lock = LOCK_AVAIL; /* lock for shared memory services */
#endif
uint_t shm_mark = 0; /* high water mark for descriptors */
BUGVDEF(shm_debug, 0);
BUGVDEF(shmm_debug, 0);
struct shmid_ds *ipcget(),
*shmconv();
void shmfree(),
ishmdt(),
rmshseg();
#ifdef _POWER_MP
#define SHM_GET_LOCK(locked) { \
if (!(locked)) { simple_lock(&lock_shm); (locked) = TRUE;}}
#else
#define SHM_GET_LOCK(locked) { \
if (!(locked)) {(locked) = lockl(&shm_lock,LOCK_SHORT);\
ASSERT( (locked) == LOCK_SUCC ); (locked) = TRUE;}}
#endif
#ifdef _POWER_MP
#define SHM_REL_LOCK(locked) {if ((locked)) simple_unlock(&lock_shm);}
#else
#define SHM_REL_LOCK(locked) {if ((locked)) unlockl(&shm_lock);}
#endif
#ifdef _POWER_MP
/*
* NAME: shm_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:
*
* DATA STRUCTURES: lock_shm
*
* RETURNS: None.
*/
void
shm_lock_init()
{
/* initialize the shared memory lock */
lock_alloc(&lock_shm,LOCK_ALLOC_PAGED,SHM_LOCK_CLASS,LOCK_SHM);
simple_lock_init(&lock_shm);
}
#endif /* _POWER_MP */
/*
* NAME: shmget
*
* FUNCTION: Get an identifier for a shared memory segment associated
* with the key given, perhaps creating it along the way.
*
* EXECUTION ENVIRONMENT:
*
* system call
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS: -1 if not successful identifier if successful
*/
int
shmget( key_t key, int size, int shmflg)
{
register struct shmid_ds *sp; /* shared memory header ptr */
int s; /* ipcget status */
uint_t rv; /* returned shm identifier */
int svcrc = 0;
static int svcnum = 0;
if (audit_flag)
svcrc = audit_svcstart("SHM_Create",&svcnum,0);
TRCHKGT_SYSC(SHMGET, key, size, shmflg, NULL, NULL);
BUGLPR(shm_debug, BUGNTA, ("shmget: key=%d size=%d shmflag=%d\n",
key, size, shmflg) );
#ifdef _POWER_MP
simple_lock(&lock_shm);
#else
rv = lockl(&shm_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
#endif
if((sp = ipcget(key, shmflg, shmem, shminfo.shmmni,
sizeof(*sp), &s, &shm_mark)) == NULL){
BUGLPR(shm_debug, BUGGID, ("shmget: ipcget failed\n"));
goto outx;
}
if(s)
{
/* This is a new shared memory segment. Allocate memory and
finish initialization. */
if(size < shminfo.shmmin || size > shminfo.shmmax) {
u.u_error = EINVAL;
sp->shm_perm.mode = 0;
BUGLPR(shm_debug, BUGGID, ("shmget: bad seg size\n"));
goto outx;
}
sp->shm_segsz = size;
if ( shm_getseg(sp) ) {
u.u_error = ENOMEM;
sp->shm_perm.mode = 0;
BUGLPR(shm_debug, BUGGID, \
("shmget: seg create failed\n"));
goto outx;
}
sp->shm_nattch = sp->shm_cnattch = 0;
sp->shm_atime = sp->shm_dtime = 0;
sp->shm_ctime = time;
sp->shm_lpid = 0;
sp->shm_cpid = U.U_procp->p_pid;
#ifdef _POWER_MP
sp->shm_perm.key = key;
#endif
} else /** it is not a new segment **/
if(size && sp->shm_segsz < size) {
u.u_error = EINVAL;
BUGLPR(shm_debug, BUGGID, \
("shmget: seg size error\n"));
goto outx;
}
rv = (sp->shm_perm.seq * shminfo.shmmni) + (sp - shmem);
outx:
if(svcrc){
if(u.u_error){
int bval = -1;
audit_svcbcopy(&bval, sizeof(int));
audit_svcfinis();
}
else {
audit_svcbcopy(&rv, sizeof(uint_t));
audit_svcfinis();
}
}
#ifdef _POWER_MP
simple_unlock(&lock_shm);
#else
unlockl(&shm_lock);
#endif
BUGLPR(shm_debug, BUGNTF, ("shmget: rv = %d errno = %d\n", \
u.u_error ? -1:rv, u.u_error) );
return u.u_error ? -1 : rv;
}
/*
* NAME: shmat
*
* FUNCTION: shmat system call
*
* EXECUTION ENVIRONMENT:
* system call
*
* DATA STRUCTURES: Changes u.u_error
*
* RETURNS:
* start address of shared memory if successful
* -1 if not successful
*
*/
void * shmat(int shmid, const void *iaddr, int flag)
{
struct shmid_ds *sp; /* shared memory header ptr */
struct segstate *segstate; /* pointer users segment state */
int rv;
int seg_num; /* user segment being shmated */
static int svcnum = 0;
char * addr = iaddr;
int mthread, waslocked;
/*
* Acquire address space lock if multi-threaded.
*/
mthread = MTHREADT(curthread);
if (mthread)
{
waslocked = base_vmm_lock(&U.U_adspace_lock);
ASSERT(!waslocked);
}
if(audit_flag && audit_svcstart("SHM_Open", &svcnum, 1, shmid)){
audit_svcfinis();
}
TRCHKGT_SYSC(SHMAT, shmid, iaddr, flag, NULL, NULL);
BUGLPR(shm_debug, BUGNTF, ("shmat shmid= %X addr= %X flag= %X\n", \
shmid, addr, flag ) );
#ifdef _POWER_MP
simple_lock(&lock_shm);
#else
rv = lockl(&shm_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
#endif
if(flag & SHM_RND)
addr = (char *)((uint)addr & ~(SHMLBA - 1));
if (flag & SHM_MAP) {
(void)mapfile(shmid, &addr, flag);
goto outx;
}
if (flag & SHM_FMAP) {
(void)fmapfile(shmid, &addr, flag);
goto outx;
}
if((sp = shmconv(shmid, SHM_DEST)) == NULL)
goto outx;
if(ipcaccess(&sp->shm_perm, SHM_R)){
BUGLPR(shm_debug, BUGGID, ("shmat: ipcaccess failed\n"));
goto outx;
}
if((flag & SHM_RDONLY) == 0)
if(ipcaccess(&sp->shm_perm, SHM_W)) {
BUGLPR(shm_debug, BUGGID, \
("shmat: ipcaccess failed\n"));
goto outx;
}
if( (seg_num = shm_findspace(&addr, 1)) == -1 ) {
BUGLPR(shm_debug, BUGGID, ("shmat: shm_findspace failed\n"));
goto outx;
}
/*
* Give process addressability to the section of memory.
* Then set segment state in u block
*/
shm_ldseg(addr, sp->shm_handle, flag, 1);
segstate = &U.U_segst[ seg_num ];
ASSERT(segstate->segflag == SEG_AVAIL);
segstate->segflag = SEG_SHARED;
segstate->num_segs = 1;
segstate->shm_ptr = sp;
/*
* Don't need to clear segment on first attach; it is created as 0's
*/
sp->shm_nattch++;
sp->shm_cnattch++;
sp->shm_atime = time;
sp->shm_lpid = U.U_procp->p_pid;
outx:
#ifdef _POWER_MP
simple_unlock(&lock_shm);
#else
unlockl(&shm_lock);
#endif
if (mthread)
base_vmm_unlock(&U.U_adspace_lock);
BUGLPR(shm_debug, BUGNTF, ("shmat: rv=%X errno=%d\n", \
u.u_error ? -1:(int)addr, u.u_error));
return u.u_error ? (void *)-1 : (void *)addr;
}
/*
* NAME:shmdt
*
* FUNCTION: shmdt system call
*
* EXECUTION ENVIRONMENT:
* system call
*
* DATA STRUCTURES: Sets u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*/
int
shmdt(const void *addr)
{
int rv;
int mthread, waslocked;
/*
* Acquire address space lock if multi-threaded.
*/
mthread = MTHREADT(curthread);
if (mthread)
{
waslocked = base_vmm_lock(&U.U_adspace_lock);
ASSERT(!waslocked);
}
TRCHKLT_SYSC(SHMDT, addr);
BUGLPR(shm_debug, BUGNTF, ("shmdt addr= 0x%X\n", addr));
#ifdef _POWER_MP
simple_lock(&lock_shm);
#else
rv = lockl(&shm_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
#endif
ishmdt(addr);
#ifdef _POWER_MP
simple_unlock(&lock_shm);
#else
unlockl(&shm_lock);
#endif
if (mthread)
base_vmm_unlock(&U.U_adspace_lock);
BUGLPR(shm_debug, BUGNTF, ("shmdt rv = %d errno = %d\n", \
u.u_error ? -1 : 0, u.u_error) );
return u.u_error ? -1 : 0;
}
/*
* NAME: shmctl
*
* FUNCTION: shmctl system call
*
* EXECUTION ENVIRONMENT:
* system call
*
* DATA STRUCTURES: changes u.u_error
*
* RETURNS: 0 if successful -1 if not successful
*/
int
shmctl(int shmid, int cmd, struct shmid_ds *arg)
/* int shmid; */
/* int cmd; */
/* struct shmid_ds * arg; */
{
register struct shmid_ds *sp; /* shared memory header ptr */
struct shmid_ds ds; /* hold area for IPC_SET */
/* int i, segno; */
int rv;
static int svcnumC = 0;
static int svcnumO = 0;
static int svcnumM = 0;
uid_t current_uid;
TRCHKGT_SYSC(SHMCTL, shmid, cmd, arg, NULL, NULL);
BUGLPR(shm_debug, BUGNTF, ("shmctl shmid=%X cmd=%X arg=%X\n" , \
shmid, cmd, arg) );
#ifdef _POWER_MP
simple_lock(&lock_shm);
#else
rv = lockl(&shm_lock,LOCK_SHORT);
ASSERT( rv == LOCK_SUCC );
#endif
if((sp = shmconv(shmid, (cmd == IPC_STAT) ? 0 : SHM_DEST))
== NULL)
goto outx;
current_uid = getuidx(ID_EFFECTIVE);
switch(cmd) {
/* Remove shared memory identifier. */
case IPC_RMID:
if(current_uid != sp->shm_perm.uid && current_uid != sp->shm_perm.cuid
&& !priv_req(BYPASS_DAC_WRITE)) {
u.u_error = EPERM;
goto outx;
}
sp->shm_ctime = time;
sp->shm_perm.mode |= SHM_DEST;
if(audit_flag && audit_svcstart("SHM_Close", &svcnumC, 1, shmid)){
audit_svcfinis();
}
/* Change key to private so old key can be reused without
waiting for last detach. Only allowed accesses to
this segment now are shmdt() and shmctl(IPC_STAT).
All others will give bad shmid. */
sp->shm_perm.key = IPC_PRIVATE;
/* Adjust counts to counter shmfree decrements. */
sp->shm_nattch++;
sp->shm_cnattch++;
shmfree(sp);
goto outx;
/* Set ownership and permissions. */
case IPC_SET:
if(current_uid != sp->shm_perm.uid && current_uid != sp->shm_perm.cuid
&& !priv_req(SET_OBJ_DAC)) {
u.u_error = EPERM;
goto outx;
}
if(copyin(arg, &ds, SHMID_SIZE)) {
u.u_error = EFAULT;
goto outx;
}
if(audit_flag && audit_svcstart("SHM_Owner", &svcnumO, 3,
shmid, ds.shm_perm.uid, ds.shm_perm.gid)){
audit_svcfinis();
}
sp->shm_perm.uid = ds.shm_perm.uid;
sp->shm_perm.gid = ds.shm_perm.gid;
sp->shm_perm.mode = (ds.shm_perm.mode & 0777) |
(sp->shm_perm.mode & ~0777);
sp->shm_ctime = time;
goto outx;
/* Get shared memory data structure. */
case IPC_STAT:
if(ipcaccess(&sp->shm_perm, SHM_R))
goto outx;
if(audit_flag && audit_svcstart("SHM_Mode", &svcnumM, 2,
shmid, ds.shm_perm.mode)){
audit_svcfinis();
}
if(copyout(sp, arg, SHMID_SIZE))
u.u_error = EFAULT;
goto outx;
case SHM_SIZE:
if(current_uid != sp->shm_perm.uid && current_uid != sp->shm_perm.cuid
&& !priv_req(SET_OBJ_STAT)) {
u.u_error = EPERM;
goto outx;
}
if(copyin(arg, &ds, SHMID_SIZE)) {
u.u_error = EFAULT;
goto outx;
}
if(ds.shm_segsz > shminfo.shmmax) {
u.u_error = EINVAL;
goto outx;
}
if(shm_growseg(sp, ds.shm_segsz)) {
u.u_error = ENOMEM;
goto outx;
}
sp->shm_ctime = time;
sp->shm_segsz = ds.shm_segsz;
break; /* goto outx; */
default:
u.u_error = EINVAL;
goto outx;
}
outx:
#ifdef _POWER_MP
simple_unlock(&lock_shm);
#else
unlockl(&shm_lock);
#endif
BUGLPR(shm_debug, BUGNTF, ("shmctl: rv = %d errno = %d\n" , \
u.u_error ? -1 : 0, u.u_error) );
return u.u_error ? -1 : 0;
}
/*
* NAME ishmdt
*
* FUNCTION: internal shared memory detach routine
*
* EXECUTION ENVIRONMENT:
* called by shmdt system call
*
* DATA STRUCTURES: changes u.u_error
*
* RETURNS: NONE
*/
void
ishmdt(addr)
register char *addr;
{
register struct shmid_ds *sp;
register int i;
register struct segstate *segstate;
BUGLPR(shm_debug, BUGNTA, ("ishmdt addr=%X\n", addr));
/*
* Check for segment alignment and segment being shared
*/
i = (unsigned int)addr >> SEGSHIFT;
if ( (((int)addr & (SHMLBA-1)) == 0) &&
(i >= SHMLOSEG) && (i <= SHMHISEG) ){
segstate = &U.U_segst[i];
if (segstate->segflag & SEG_MAPPED) {
rmmseg(i);
return;
}
if (segstate->segflag & SEG_SHARED) {
rmshseg(segstate, i);
return;
}
}
BUGLPR(shm_debug, BUGGID, ("bad address =%X\n" , addr));
u.u_error = EINVAL;
return;
}
/*
* NAME: shmconv
*
* FUNCTION: Convert user supplied shmid into a ptr to the associated
* shared memory header.
*
* EXECUTION ENVIRONMENT:
* called by shared memory system calls
*
* DATA STRUCTURES: sets u.u_error
*
* RETURNS:
* pointer to shared memory descriptor if successful
* NULL if not successful
*/
struct shmid_ds *
shmconv(s, flg)
uint_t s; /* shmid */
int flg; /* error if matching bits are set in mode */
{
register struct shmid_ds *sp; /* ptr to associated header */
uint_t index; /* descriptor index */
uint_t seq; /* sequence number */
seq = s / shminfo.shmmni; /* get sequence number and index */
index = s % shminfo.shmmni;
if ( index >= shm_mark ) {
u.u_error = EINVAL;
return( NULL );
}
sp = &shmem[index]; /* low 16 bits of shmid=index */
if ((seq != sp->shm_perm.seq) /* high 16 bits = usage cnt */
|| (sp->shm_perm.mode & flg)
|| !(sp->shm_perm.mode & IPC_ALLOC) ) {
u.u_error = EINVAL;
BUGLPR(shm_debug, BUGGID, ("shmconv: bad index=%X\n" , s));
return(NULL);
}
return(sp);
}
/*
* NAME:shmex
*
* FUNCTION: Called by exit and exec to handle shared memory processing.
*
* EXECUTION ENVIRONMENT:
* caller must be a process
*
* RETURNS: NONE
*
* NOTES:
* This function was previously called shmexec. shmexit used to
* call shmexec.
*
* Same processing for exec and exit. At exit, no thread locking
* is required because the process is single threaded.
*/
shmex()
{
struct shmid_ds *sp;
struct segstate *seg_state;
int seg;
int locked = 0;
int num_segs;
int avail;
BUGLPR(shm_debug, BUGNTF, ("shmex\n"));
/* Detach any attached segments. */
seg = SHMLOSEG;
do{
seg_state = &U.U_segst[seg];
num_segs = seg_state->num_segs;
avail = seg_state->segflag == SEG_AVAIL;
if ( seg_state->segflag & SEG_MAPPED) {
SHM_GET_LOCK(locked);
rmmseg(seg);
}
else if (seg_state->segflag & SEG_MMAP){
SHM_GET_LOCK(locked);
rmmapseg(seg);
}
else if (seg_state->segflag & SEG_SHARED){
SHM_GET_LOCK(locked);
rmshseg(seg_state, seg);}
seg += (avail) ? 1 : num_segs;
}while(seg <= SHMHISEG);
ASSERT(seg == SHMHISEG + 1);
SHM_REL_LOCK(locked);
}
/*
* NAME: shmfork
*
* FUNCTION: handles shared memory fork processing
*
* EXECUTION ENVIRONMENT:
* Called by kfork while running under parent process
*
* RETURNS: NONE
*
* NOTE:
* Caller must be holding the process user address space lock.
*/
void
shmfork(uchild)
struct user *uchild; /* pointer to child's user area */
{
struct segstate *ssptr;
struct shmid_ds *shmptr;
int seg;
int locked=0;
int mthread;
BUGLPR(shm_debug, 1, ("shmfork\n"));
/*
* If this process is multithreaded, copy the
* adspace allocation under the adspace lock.
* XXX - Note that all the allocation bits are
* copied here. This assumes that the same
* segment registers are always allocated in
* the parent and the child.
*/
mthread = MTHREADT(curthread);
if (mthread)
uchild->U_adspace.alloc = U.U_adspace.alloc;
/*
* update counts on any attached segments.
*/
seg = SHMLOSEG;
do {
ssptr = &U.U_segst[seg];
/*
* If this process is multithreaded, copy the
* adspace and segstate structure here under the
* adspace lock. Big data segments have already
* been handled.
*/
if (mthread && !(ssptr->segflag & SEG_WORKING))
{
uchild->U_segst[seg] = *ssptr;
uchild->U_adspace.srval[seg] =
U.U_adspace.srval[seg];
}
if (ssptr->segflag & SEG_SHARED){
SHM_GET_LOCK(locked);
shmptr = ssptr->shm_ptr;
shmptr->shm_nattch++;
shmptr->shm_cnattch++;
} else if (ssptr->segflag & SEG_MAPPED) {
SHM_GET_LOCK(locked);
mapfork( seg );}
else if (ssptr->segflag & SEG_MMAP){
SHM_GET_LOCK(locked);
mmapfork(uchild, seg);}
seg += (ssptr->segflag == SEG_AVAIL) ? 1 : ssptr->num_segs;
} while(seg <= SHMHISEG);
ASSERT(seg == SHMHISEG + 1);
SHM_REL_LOCK(locked);
}
/*
* NAME:shmfree
*
* FUNCTION: Decrement counts. Free segment if indicated.
*
* EXECUTION ENVIRONMENT:
* called by shmctl
*
* RETURNS: NONE
*/
void
shmfree(sp)
register struct shmid_ds *sp; /* shared memory header ptr */
{
ASSERT( sp != NULL );
sp->shm_nattch--;
if (--(sp->shm_cnattch) == 0 && sp->shm_perm.mode & SHM_DEST) {
sp->shm_perm.mode = 0;
sp->shm_perm.seq++;
shm_dseg(sp);
}
}
/*
* NAME: rmshseg
*
* FUNCTION: remove a memory segment
*
* EXECUTION ENVIRONMENT:
* called by shm_exit and ishmdt
*
* RETURNS: NONE
*/
void
rmshseg(segst,addr)
struct segstate *segst;
uint_t addr;
{
struct shmid_ds *sp;
sp = segst->shm_ptr;
shm_freespace(addr << SEGSHIFT, 1);
segst->shm_ptr = (struct shmid_ds *)0;
segst->segflag = SEG_AVAIL;
segst->num_segs = 0;
/* segment cannot be freed until SID shootdown has occurred */
shmfree(sp);
sp->shm_dtime = time;
sp->shm_lpid = U.U_procp->p_pid;
return;
}
/*
* NAME: psrmshm
*
* FUNCTION: remove all the shared memory segments attached to a process
*
* EXECUTION ENVIRONMENT:
* process
*
* NOTE:
* this is only used by processes killed by the paging space monitor
*
* RETURNS: NONE
*/
void
psrmshm(p)
struct proc *p;
{
struct shmid_ds *sp; /* shared memory header ptr */
struct segstate *segst;
struct segstate *endsegst;
int shmid;
segst = U.U_segst;
endsegst = U.U_segst + NSEGS;
while (segst < endsegst) {
if ((segst->segflag & (SEG_AVAIL | SEG_SHARED)) == SEG_SHARED) {
shmid = (segst->shm_ptr - shmem) +
segst->shm_ptr->shm_perm.seq * shminfo.shmmni;
/* if there is only one process (this one) attached to
this segment, remove the id so the segment will
go away when this process exits */
if (((sp = shmconv(shmid, SHM_DEST)) != NULL) &&
(sp->shm_cnattch == 1))
shmctl (shmid, IPC_RMID, NULL);
}
++segst;
}
return;
}