
/*
 *         PVM version 3.3:  Parallel Virtual Machine System
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM version 3 was funded in part by the U.S. Department of Energy,
 * the National Science Foundation and the State of Tennessee.
 */

/*
 * pvmdshmem.c
 *
 * Shared-memory MPP interface.
 *
$Log: pvmdshmem.c,v $
 * Revision 1.3  1994/11/08  15:35:07  manchek
 * shared memory damage control
 *
 */


#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <fcntl.h>
#ifdef IMA_SYMM
#include <sys/file.h>		/* XXX for open(); change to fcntl.h in ptx? */
#include <parallel/parallel.h>
#endif
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
/*
#include <signal.h>
*/

#include "global.h"
#include "ddpro.h"
#include "tdpro.h"
#include "protoglarp.h"
#include "pvmalloc.h"
#include "pvmdabuf.h"
#include "pkt.h"
#include "task.h"
#include "listmac.h"
#include "pvmshmem.h"
#include "bfunc.h"

#ifndef max
#define max(a,b)    ((a)>(b)?(a):(b))
#endif

#ifndef min
#define min(a,b)    ((a)<(b)?(a):(b))
#endif

char *getenv();


extern int debugmask;			/* from pvmd.c */
extern int pvm_useruid;			/* from pvmd.c */
extern int mytid;				/* from pvmd.c */
extern int myndf;				/* from pvmd.c */
extern int myhostpart;			/* from pvmd.c */
extern int tidhmask;			/* from pvmd.c */
extern int ourudpmtu;			/* from pvmd.c */
extern int myunixpid;			/* from pvmd.c */
extern struct task *locltasks;	/* from task.c */
extern struct peer *peers;		/* from pvmshmem.c */


/***************
 **  Globals  **
 **           **
 ***************/

char *outmsgbuf = 0;			/* outgoing message buffer */
int outbufsz = 0;				/* size of outgoing msg buffer */
int pgsz = 0;					/* page size */
int pvmpgsz = 0;				/* PVM virtual page size */
char *infopage = 0;				/* proto, NDF, pid-tid table */
struct pidtid *pidtids = 0;		/* pid -> tid table */
int maxpidtid = 0;				/* size of pid-tid table */
int shmbufsiz = 0;				/* shared-memory buffer size */
#ifdef IMA_POWER4
struct shmpghdr *globinfo = 0;	/* global task information */
#endif


/***************
 **  Private  **
 **           **
 ***************/

static char rcsid[] = "$Id: pvmdshmem.c,v 1.3 1994/11/08 15:35:07 manchek Exp $";
static char pvmtxt[512];		/* scratch for error log */
static char *inbox = 0;			/* incoming message header buffer */
static int inboxsz = 0;			/* size of incoming message buffer */ 
static struct pkt *ovfpkts = 0;	/* packets waiting to be delivered */
static int mybufid = -1;		/* shared-memory msg buffer ID */
static int globid = -1;			/* ID of global shared segment */


/* 
 *			Buffer layout
 *		 ____________________
 *		|       inbox        | 1
 *		|____________________|
 *		|   pid->tid table   | 2
 *		|____________________|
 *		|                    | 3
 *		|      outmsgbuf     |
 *      |          .         | .
 *		|          .         | .
 *		|          .         | .
 *
 *
 * Note: tasks don't keep a pid-tid table, so their outgoing message
 * buffer starts at the second page.
 *
 * To send messages to one another, the sender puts the message header,
 * which contains its own task ID and the location of the message body
 * (expressed as an offset from the head of the segment), into the inbox
 * of the intended receiver. When the addressee is ready to accept the
 * message, it reads the message header and locates the message fragments 
 * in the sender's outgoing message buffer. Access to buffers are guarded
 * locks. Tasks can read the same data simultaneously, but one must obtain 
 * exclusive access to the page before it can modify the data.
 *
 * Messages to tasks on other hosts are routed by pvmd.
 */

/*
void
wakesig(sig)
	int sig;
{
}
*/

/* XXX mpp_init() is a mess - it just returns if anything goes wrong */

void
mpp_init(argc, argv)
	int *argc;
	char **argv;
{
	struct pidtidhdr *pvminfo;
	char *p;

#ifdef LOG
char fname[32];
FILE *logfp;
sprintf(fname, "/tmp/pvmt.%d", pvm_useruid);
logfp = fopen(fname, "w");
fclose(logfp);
#endif

	pgsz = sysconf(_SC_PAGESIZE);
	pvmpgsz = FRAGPAGE*pgsz;
	ourudpmtu = pgsz - PVMPAGEHDR;
	inboxsz = 
		(INBOXPAGE*pgsz - sizeof(struct msgboxhdr))/sizeof(struct shmpkhdr);
	if (!(p = getenv("PVMBUFSIZE")) || !(shmbufsiz = strtol(p, (char**)0, 0)))
		shmbufsiz = SHMBUFSIZE;

	if ((mybufid = shmget((key_t)(TIDPVMD + pvm_useruid), shmbufsiz, 
	IPC_CREAT|PERMS)) == -1) {
		pvmlogperror("mpp_init() can't create msg buffer");
		return;
	}
	if ((inbox = (char *)shmat(mybufid, 0, 0)) == (char *)-1L) {
		pvmlogperror("mpp_init() can't attach msg buffer");
		return;
	}

#ifdef IMA_POWER4
	if ((globid = 
	shmget((key_t)pvm_useruid, pgsz, IPC_POWER4|IPC_CREAT|IPC_EXCL|PERMS)) 
	== -1) {
		if (errno == EEXIST 
		&& ((globid = shmget((key_t)pvm_useruid, pgsz, IPC_POWER4)) == -1
		|| (globinfo = (struct shmpghdr *)shmat(globid, 0, 0)) 
		== (struct shmpghdr *)-1))
			pvmlogperror("mpp_init() can't attach existing global table");
		else
			pvmlogperror("mpp_init() can't create global table");
	} else {
		if ((globinfo = (struct shmpghdr *)shmat(globid, 0, 0)) 
		== (struct shmpghdr *)-1)
			pvmlogperror("mpp_init() can't attach global table");
		BZERO((char*)globinfo, pgsz);
		PAGEINITLOCK(&globinfo->pg_lock);
	}
#endif

	infopage = inbox + INBOXPAGE*pgsz;
	outmsgbuf = infopage + pgsz;
	if (!(outbufsz = (shmbufsiz - INBOXPAGE*pgsz - pgsz)/pvmpgsz)) {
		pvmlogerror("mpp_init() SHMBUFSIZE too small!");
		return;
	}

	msgbufinit(inbox);
#ifndef IMA_KSR1
	PAGEINITLOCK(&((struct shmpghdr *)infopage)->pg_lock);
#endif
	((struct shmpghdr *)infopage)->pg_ref = 0;
	pvminfo = (struct pidtidhdr *)(infopage + PVMPAGEHDR);
	pvminfo->i_proto = TDPROTOCOL;
	pvminfo->i_ndf  = myndf;
	pvminfo->i_next = 0;
	pvminfo->i_bufsiz = shmbufsiz;
	pvminfo->i_dpid = myunixpid;
	pidtids = (struct pidtid *)(pvminfo + 1);
	maxpidtid = (pgsz - sizeof(struct pidtidhdr))/sizeof(struct pidtid);

	peer_init();

    ovfpkts = TALLOC(1, struct pkt, "ofpkts");
    BZERO((char*)ovfpkts, sizeof(struct pkt));
    ovfpkts->pk_link = ovfpkts->pk_rlink = ovfpkts;

/*
#if defined(SUN4SOL2)
	sprintf(pvmtxt, "%ld CPUs online\n", sysconf(_SC_NPROCESSORS_ONLN));
	sprintf(pvmtxt, "%ld CPUs online\n", sysconf(15));
	pvmlogerror(pvmtxt);
#endif
*/
}


/*	mpp_free()
*
*	Remove shared memory segment of tid from peers list.  If we were
*	the only reference to it, delete it as well.
*/

void
mpp_free(tid)
	int tid;
{
	struct peer *pp;

	if (debugmask & (PDMTASK|PDMNODE)) {
		sprintf(pvmtxt, "mpp_free() t%x\n", tid);
		pvmlogerror(pvmtxt);
	}

	for (pp = peers->p_link; pp != peers; pp = pp->p_link) {
		if (pp->p_tid == tid) {
			peer_detach(pp);
			LISTDELETE(pp, p_link, p_rlink);
			PVM_FREE(pp);
			break;
		}
	}
}
	

/* fill in pid->tid table */
void
mpp_conn(tp)
	struct task *tp;
{
	int idx;
	struct pidtidhdr *pvminfo = (struct pidtidhdr *)(infopage + PVMPAGEHDR);
#ifdef IMA_POWER4
	struct gtask *globtasks;
#endif

	PAGELOCK(&((struct shmpghdr *)infopage)->pg_lock);
	if ((idx = pvminfo->i_next++) == maxpidtid) {
		idx = 0;
		pvminfo->i_next = 1;
	}
	if (((struct shmpghdr *)infopage)->pg_ref < maxpidtid)
		((struct shmpghdr *)infopage)->pg_ref++;
	pidtids[idx].pt_tid = tp->t_tid;
	pidtids[idx].pt_ptid = tp->t_ptid;
	pidtids[idx].pt_stat = ST_NOTREADY;
	pidtids[idx].pt_pid = tp->t_pid;
	PAGEUNLOCK(&((struct shmpghdr *)infopage)->pg_lock);
#ifdef IMA_POWER4
	/* update global table */
	PAGELOCK(&globinfo->pg_lock);
	globtasks = (struct gtask *)(globinfo + 1);
	if ((idx = globinfo->pg_ref++) == (pgsz - PVMPAGEHDR)/sizeof(struct gtask)){
		idx = 0;
		globinfo->pg_ref = 1;
	}
	globtasks[idx].gt_tid = tp->t_tid;
	globtasks[idx].gt_stat = ST_NOTREADY;
	PAGEUNLOCK(&globinfo->pg_lock);
#endif
	if (debugmask & PDMTASK) {
		sprintf(pvmtxt, "mpp_conn() new task t%x\n", tp->t_tid);
		pvmlogerror(pvmtxt);
	}
}
						
/* XXX lazy cleanup sucks */

static void
removeshm(tid)
int tid;
{
	int shmid, semid;

	if (!tid)
		tid = tid_new() + 1;

#ifdef IMA_POWER4
	while ((shmid = shmget((key_t)tid, shmbufsiz,
				IPC_POWER4|IPC_CREAT|IPC_EXCL|PERMS))
	== -1)
#else
	while ((shmid = shmget((key_t)tid, shmbufsiz, IPC_CREAT|IPC_EXCL|PERMS))
	== -1
#if defined(IMA_SGIMP) || defined(IMA_SGIMP64) || defined(IMA_ALPHAMP)
	|| (semid = semget((key_t)tid, 1, IPC_CREAT|IPC_EXCL|PERMS)) == -1
#endif
	)
#endif
	{
		if (task_find(tid)) {
			tid = tid_new() + 1;		/* skip */
			continue;
		}
		/* if ((shmid = shmget((key_t)tid, shmbufsiz, PERMS)) == -1  */
#ifdef IMA_POWER4
		if ((shmid = shmget((key_t)tid, 0, IPC_POWER4|PERMS)) == -1 
		|| shmctl(shmid, IPC_RMID, (struct shmid_ds *)0) == -1)
#else
		if (shmid == -1 && ((shmid = shmget((key_t)tid, 0, PERMS)) == -1 
		|| shmctl(shmid, IPC_RMID, (struct shmid_ds *)0) == -1))
#endif
		{
			sprintf(pvmtxt, "removeshm: shm key = %x", tid);
			pvmlogperror(pvmtxt);
			tid = tid_new() + 1;
		} 
#if defined(IMA_SGIMP) || defined(IMA_SGIMP64) || defined(IMA_ALPHAMP)
		if (semid == -1 && ((semid = semget((key_t)tid, 0, PERMS)) == -1
		|| semctl(semid, 0, IPC_RMID) == -1)) {
			sprintf(pvmtxt, "removeshm: sem key = %x", tid);
			pvmlogperror(pvmtxt);
			tid = tid_new() + 1;
		}
		semid = 0;
#endif
	}
	shmctl(shmid, IPC_RMID, (struct shmid_ds *)0);
	semctl(semid, 0, IPC_RMID);
}

/* fork and exec new tasks */
mpp_load(flags, name, av, count, tids, ptid)
	int flags;				/* exec options */
	char *name;				/* executable */
	char **av;				/* arg list (argv[-1] must be there) */
	int count;				/* how many */
	int tids[];				/* array to store new tids */
	int ptid;				/* parent task ID */
{
	int i;
	int err = 0;
	struct task *tp;
	int cc;

	(void)removeshm(0);
	for (i = 0; i < count; i++) {
		if (err) {
			tids[i] = err;
		} else {
			if (i)
				(void)removeshm(tids[i-1] + 1);

			if (err = forkexec(flags, av[0], av, 0, (char **)0, &tp)) {
				tids[i] = err;
			} else {
				tp->t_ptid = ptid;
				mpp_conn(tp);
				tids[i] = tp->t_tid;
			}
		}
	}
	peer_conn(0);	/* destroy old shared-memory buffers */
}


void
mpp_input()
{
	int next;
	struct pkt *pp;
	struct peer *pe;
	struct task *tp;
	int tid;
	int src;
	int dst;
	int len;
	int ff;
	char *cp, *buf;
	struct msgboxhdr *inbp;
	struct shmpkhdr *inmsgs;

	inbp = (struct msgboxhdr *)inbox;
	inmsgs = (struct shmpkhdr *)(inbp + 1);
/*
#ifndef TEST_ADD
	PAGELOCK(&inbp->mb_lock);
#endif
*/
	do {
/*
#ifndef TEST_ADD
		PAGEUNLOCK(&inbp->mb_lock);
#endif
*/
		next = (inbp->mb_read + 1) % inboxsz;
		src = inmsgs[next].ph_src;

		if (inmsgs[next].ph_dat < 0) {		/* new task */
			int ipid = inmsgs[next].ph_dst;

			if (!ipid)
				ipid = src;
			if (!(tp = task_findpid(ipid))) {
				/* not spawned by us */
				if ((tid = tid_new()) < 0) {
					pvmlogerror("mpp_input() out of tids?\n");
/*
#ifndef TEST_ADD
					PAGELOCK(&inbp->mb_lock);
#endif
*/
					continue;
				}
				(void)removeshm(tid);
				tp = task_new(tid);
				task_setpid(tp, src);
				mpp_conn(tp);

			} else if (tp->t_pid != src)
				task_setpid(tp, src);
			tp->t_flag |= TF_CONN;
/*
#ifndef TEST_ADD
			PAGELOCK(&inbp->mb_lock);
#endif
*/
			continue;
		}

		if (!(pe = peer_conn(src)) || pe == (struct peer *)-1L) {
			sprintf(pvmtxt, "mpp_input() can't connect to src t%x\n", src);
			pvmlogerror(pvmtxt);
			return;
		}
		cp = pe->p_buf + INBOXPAGE*pgsz + inmsgs[next].ph_dat;
		buf = cp - (inmsgs[next].ph_dat & (pgsz-1)) + PVMPAGEHDR;
		dst = inmsgs[next].ph_dst;
		ff = inmsgs[next].ph_flag;
		/* dst = pvmget32(cp); */
		len = pvmget32(cp + 8) + TDFRAGHDR;

		if ((dst & ~tidhmask) != TIDPVMD) {
			pp = pk_new(len);
			BCOPY(cp, pp->pk_dat, len);
			da_unref(buf);
			cp = pp->pk_dat;
			pvmput32(cp, dst);
			pvmput8(cp + 12, ff);

		} else {
			pp = pk_new(0);
			pp->pk_dat = cp;
			pp->pk_buf = buf;
			pp->pk_max = ourudpmtu;
		}
		pp->pk_len = len;

		if (tp = task_find(src)) {
			loclinpkt(tp, pp);
			if ((tp->t_flag & TF_CLOSE) && !(tp->t_flag & TF_FORKD)) {
				task_cleanup(tp);
				task_free(tp);
			}

		} else {
			sprintf(pvmtxt, "mpp_input() from unknown task t%x\n", src);
			pvmlogerror(pvmtxt);
			pk_free(pp);
		}

	} while ((inbp->mb_read = next) != inbp->mb_last);
/*
	PAGEUNLOCK(&inbp->mb_lock);
#endif
*/
}


void
mpp_output(tp, pp)
	struct task *tp;
	struct pkt *pp;
{
	struct peer *pe;
	int dst;
	struct shmpkhdr *dmsgs = 0;
	struct pkt *pp1, *pp2;
	struct msgboxhdr *dboxp;
	char *cp;
	int loc;
	int next;
	int onemsg = 1;

	dst = pp->pk_dst;
	if ((loc = pp->pk_dat - outmsgbuf) > outbufsz*pvmpgsz || loc < 0) {
		cp = 0;
/* XXX this loop wants a shared page.  gosh, won't it hang if two
   XXX tasks are trying to send and both get here.  i think so. -b */
		do {
			if (onemsg) {
				onemsg = 0;
				if (debugmask & PDMPACKET)
					pvmlogerror("mpp_output() outgoing buffer full?\n");
			}
			if (cp)
				da_unref(cp);
			cp = da_new(max(DDFRAGHDR,TDFRAGHDR) + pp->pk_len);
		} while ((loc = cp - outmsgbuf) > outbufsz*pvmpgsz || loc < 0);

		BCOPY(pp->pk_dat, cp + max(DDFRAGHDR,TDFRAGHDR), pp->pk_len);
		pp->pk_dat = cp + max(DDFRAGHDR,TDFRAGHDR);
/*
		BCOPY(pp->pk_buf - PVMPAGEHDR, cp - PVMPAGEHDR,
			pp->pk_len + PVMPAGEHDR + pp->pk_dat - cp);
*/
		da_unref(pp->pk_buf);
		pp->pk_buf = cp;
		/* BCOPY(pp->pk_dat, pp2->pk_dat, pp->pk_len); */
		/* pk_free(pp); */
	}

	if ((pe = peer_conn(dst)) && pe != (struct peer *)-1L) {
		dboxp = (struct msgboxhdr *)pe->p_buf;
		dmsgs = (struct shmpkhdr *)(dboxp + 1);
		PAGELOCK(&dboxp->mb_lock);
/*
#ifdef TEST_ADD
		if ((next = (dboxp->mb_last + 1) % inboxsz) 
		!= TEST_ADD(&dboxp->mb_read,0))
#else
*/
		if ((next = (dboxp->mb_last + 1) % inboxsz) != dboxp->mb_read) {
/* #endif	*/
			if (debugmask & PDMPACKET) {
				sprintf(pvmtxt,
					"mpp_output() src t%x dst t%x ff %x len %d\n",
					pp->pk_src, pp->pk_dst, pp->pk_flag & (FFSOM|FFEOM),
					pp->pk_len);
				pvmlogerror(pvmtxt);
			}
			cp = (pp->pk_dat -= TDFRAGHDR);
			/* XXX multicast: this should only be done once */
			/* pvmput32(cp, dst); */
			pvmput32(cp + 4, pp->pk_src);
			pvmput32(cp + 8, pp->pk_len);
			/* pvmput8(cp + 12, pp->pk_flag & (FFSOM|FFEOM)); */

			dmsgs[next].ph_src = mytid;
			dmsgs[next].ph_dst = dst;
			dmsgs[next].ph_flag = pp->pk_flag & (FFSOM|FFEOM);
			dmsgs[next].ph_dat = loc - TDFRAGHDR;

			/* if (dboxp->mb_last == dboxp->mb_read) */
			if (dboxp->mb_sleep)
#if defined(IMA_SUNMP)
				cond_signal(&dboxp->mb_cond);
#else
				peer_wake(pe);
#endif
			da_ref(pp->pk_buf);
			dboxp->mb_last = next;
			PAGEUNLOCK(&dboxp->mb_lock);
			goto done;
		} else 
			LISTPUTAFTER(ovfpkts, pp, pk_link, pk_rlink);
		PAGEUNLOCK(&dboxp->mb_lock);
	} else
		LISTPUTAFTER(ovfpkts, pp, pk_link, pk_rlink);
	return;
		
done:
	pk_free(pp);
}


int
mpp_mcast(pp, tids, ntask)
	struct pkt *pp; /* packet to send */
	int tids[];     /* target tasks */
	int ntask;      /* how many */
{
	int i;
	struct task *tp;
	struct pkt *pp2, *pp3 = 0;
	int loc = pp->pk_dat - outmsgbuf;
	
	if (loc > outbufsz*pvmpgsz || loc < 0) {
		/* copy into my buffer */
		pp3 = pk_new(max(DDFRAGHDR,TDFRAGHDR) + pp->pk_len);
		pp3->pk_dat += max(DDFRAGHDR,TDFRAGHDR);
		BCOPY(pp->pk_dat, pp3->pk_dat, pp->pk_len);
	}
	/* for (i = 0; i < ntask; i++) */
	for (i = ntask - 1; i >= 0; i--)
		if (tp = task_find(tids[i])) {
			pp2 = pk_new(0);
			pp2->pk_src = pp->pk_src;
			pp2->pk_dst = tids[i];
			pp2->pk_flag = pp->pk_flag;
			pp2->pk_max = pp->pk_max;
			pp2->pk_len = pp->pk_len;
			if (pp3) {
				pp2->pk_buf = pp3->pk_buf;
				pp2->pk_dat = pp3->pk_dat;
			} else {
				pp2->pk_buf = pp->pk_buf;
				pp2->pk_dat = pp->pk_dat;
			}
			da_ref(pp2->pk_buf);
			mpp_output(tp, pp2);
		}
	if (pp3)
		pk_free(pp3);
	return 0;
}


int
mpp_probe()
{
	struct pkt *pp, *pp2;
	struct task *tp;
	int hasmsg;
	struct msgboxhdr *inbp = (struct msgboxhdr *)inbox;
	struct pidtidhdr *pvminfo = (struct pidtidhdr *)(infopage + PVMPAGEHDR);
#ifdef IMA_POWER4
	struct gtask *globtasks = (struct gtask *)(globinfo + 1);
#endif
	int ntids, i;

	/* try to send buffered packets (if any) again */
	for (pp = ovfpkts->pk_link; pp != ovfpkts; ) {
		pp2 = pp;
		pp = pp->pk_link;
		if (tp = task_find(pp2->pk_dst)) {
			LISTDELETE(pp2, pk_link, pk_rlink);
			if (tp->t_sock < 0)
				mpp_output(tp, pp2);
			else
				LISTPUTBEFORE(tp->t_txq, pp2, pk_link, pk_rlink);
		}
	}
	
#ifdef IMA_POWER4
	for (i = 0; globtasks[i].gt_tid; i++)
		if (globtasks[i].gt_stat == ST_NOTREADY 
		&& (tp = task_find(globtasks[i].gt_tid)) && tp->t_sock >= 0)
			globtasks[i].gt_stat = ST_SOCKET;
#else
	ntids = min(maxpidtid, ((struct shmpghdr *)infopage)->pg_ref);
	for (i = 0; i < ntids; i++) 
		if (pidtids[i].pt_stat == ST_NOTREADY 
		&& (tp = task_find(pidtids[i].pt_tid)) && tp->t_sock >= 0)
			pidtids[i].pt_stat = ST_SOCKET;
#endif
			
/*
#ifdef TEST_ADD
	return (inbp->mb_read != TEST_ADD(&inbp->mb_last,0));
#else
	PAGELOCK(&inbp->mb_lock);
*/
	hasmsg = (inbp->mb_read != inbp->mb_last);
/*	PAGEUNLOCK(&inbp->mb_lock);		*/
	return hasmsg;
/* #endif	*/
}


void
mpp_cleanup()
{
	struct peer *pp;
	struct shmid_ds shmds;

	if (inbox && shmdt(inbox) == -1)
		pvmlogperror("mpp_cleanup() shmdt inbox");

	if (mybufid != -1 && shmctl(mybufid, IPC_RMID, (struct shmid_ds *)0) == -1)
		pvmlogperror("mpp_cleanup() shmctl IPC_RMID mybuf");

#ifdef IMA_POWER4
	if (globinfo) {
		shmdt((char*)globinfo);
		if (globid != -1) {
			if (shmctl(globid, IPC_STAT, &shmds) == -1)
				pvmlogperror("mpp_cleanup() shmctl IPC_STAT globinfo\n");
			else
				if (shmds.shm_nattch == 0
				&& shmctl(globid, IPC_RMID, (struct shmid_ds *)0) == -1)
					pvmlogperror("mpp_cleanup() shmctl IPC_RMID globinfo\n");
		}
	}
#endif

	if (peers) {
		for (pp = peers->p_link; pp != peers; pp = pp->p_link) {
			shmdt(pp->p_buf);
			if (pp->p_tid != -1 &&
			shmctl(pp->p_shmid, IPC_RMID, (struct shmid_ds *)0) == -1) {
				sprintf(pvmtxt, "shmctl: key = %x", pp->p_tid);
				pvmlogperror(pvmtxt);
			}
		}
	}
}


