/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:osi.c 12.7$ */
/* $ACIS:osi.c 12.7$ */
/* $Source: /ibm/acis/usr/sys/afs/RCS/osi.c,v $ */

#ifndef lint
static char *rcsid = "$Header:osi.c 12.7$";
#endif

#include "../h/types.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/time.h"
#include "../h/kernel.h"
#include "../h/cmap.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/protosw.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/uio.h"
#include "../h/buf.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../h/vmsystm.h"
#include "../ufs/inode.h"
#include "../netinet/in.h"
#include "../h/mbuf.h"
#include "../h/proc.h"
#include "../rpc/types.h"
#include "../afs/osi.h"
#include "../afs/lock.h"

long osi_memUsage = 0;

static int dumping = 0;
#ifdef AFSDEBMEM
static struct osimem *memlist;
#endif

struct osimem {
    struct osimem *next;
#ifdef AFSDEBMEM
    long size;
    long addr;
#endif AFSDEBMEM
};

static int afs_purges = 0;

osi_cleanup() {
    osi_memUsage = 0;
    dumping = 0;
#ifdef AFSDEBMEM
    memlist = 0;
#endif
    afs_purges = 0;
}

/* flush text cache */
osi_FlushText(vp)
    register struct vnode *vp; {
    afs_purges++;
    mpurge(vp);
    if (vp->v_flag & VTEXT) xrele(vp);	/* this this, anyway */
}

/* procedure for making our processes as invisible as we can */
osi_Invisible() {
	/* called once per "kernel" lwp to make it invisible */
	register struct proc *q, *p = u.u_procp;
	register int i;

	float_exit(p);
	untimeout(realitexpire, (caddr_t)p);
	vrelvm();	/* ASSUMES we were not vforked */
	for (i = 0; i <= u.u_lastfile; i++) {
		struct file *f = u.u_ofile[i];
		if (f) {
			(void) vno_lockrelease(f);
			closef(f);
			u.u_ofile[i] = NULL;
		}
		u.u_pofile[i] = 0;
	}
	VN_RELE(u.u_cdir);
	if (u.u_rdir)
		VN_RELE(u.u_rdir);
	/*
	 * Run away from our parent.
	 */
	if (q = p->p_ysptr)
		q->p_osptr = p->p_osptr;
	if (q = p->p_osptr)
		q->p_ysptr = p->p_ysptr;
	if ((q = p->p_pptr)->p_cptr == p)
		q->p_cptr = p->p_osptr;
	/*
	 * Get adopted by process 0.
	 */
	p->p_pptr = &proc[0];
	p->p_ppid = 0;
	p->p_osptr = proc[0].p_cptr;
	if (proc[0].p_cptr)
		proc[0].p_cptr->p_ysptr = p;
	p->p_ysptr = NULL;
	proc[0].p_cptr = p;

	p->p_flag |= SSYS;
}

/* procedure to destroy our lwp when unmounting */
osi_exit()
{
	register int i;
	register struct proc *p, *q;
	register int x;
	struct mbuf *m = m_getclr(M_WAIT, MT_ZOMBIE);

#ifdef PGINPROF
	vmsizmon();
#endif
	p = u.u_procp;
	p->p_flag &= ~(STRC|SULOCK|SSYS);
	p->p_flag |= SWEXIT;
	p->p_sigignore = ~0;
	p->p_cpticks = 0;
	p->p_pctcpu = 0;
	for (i = 0; i < NSIG; i++)
		u.u_signal[i] = SIG_IGN;
	u.u_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
	acct();
	crfree(u.u_cred);
	/*
	 * Freeing the user structure and kernel stack
	 * for the current process: have to run a bit longer
	 * using the pages which are about to be freed...
	 * vrelu will block memory allocation by raising ipl.
	 */
	vrelu(u.u_procp, 0);
	vrelpt(u.u_procp);
	if (*p->p_prev = p->p_nxt)		/* off allproc queue */
		p->p_nxt->p_prev = p->p_prev;
	if (p->p_nxt = zombproc)		/* onto zombproc */
		p->p_nxt->p_prev = &p->p_nxt;
	p->p_prev = &zombproc;
	zombproc = p;
	multprog--;
	p->p_stat = SZOMB;
	noproc = 1;
	i = PIDHASH(p->p_pid);
	x = p - proc;
	if (pidhash[i] == x)
		pidhash[i] = p->p_idhash;
	else {
		for (i = pidhash[i]; i != 0; i = proc[i].p_idhash)
			if (proc[i].p_idhash == x) {
				proc[i].p_idhash = p->p_idhash;
				goto done;
			}
		panic("osi_exit");
	}
done:
	p->p_xstat = 0;	/* we exitted without error ... */
	p->p_ru = mtod(m, struct rusage *);
	*p->p_ru = u.u_ru;
	ruadd(p->p_ru, &u.u_cru);
	p->p_cptr = NULL;

	/*
	 * Process 0 never waits for us, Get adopted by process 1.
	 */
	if (q = p->p_ysptr)
		q->p_osptr = p->p_osptr;
	if (q = p->p_osptr)
		q->p_ysptr = p->p_ysptr;
	if ((q = p->p_pptr)->p_cptr == p)
		q->p_cptr = p->p_osptr;
	/*
	 * Get adopted by process 1.
	 */
	p->p_pptr = &proc[1];
	p->p_ppid = 1;
	p->p_osptr = proc[1].p_cptr;
	if (proc[1].p_cptr)
		proc[1].p_cptr->p_ysptr = p;
	p->p_ysptr = NULL;
	proc[1].p_cptr = p;


	/* have init clean us up */
	psignal(p->p_pptr, SIGCHLD);
	wakeup((caddr_t)p->p_pptr);
	swtch();
}

/* call procedure aproc with arock as an argument, in ams milliseconds */
int osi_CallProc(aproc, arock, ams)
    register int (*aproc);
    register char *arock;
    long ams; {
    /* hz is in cycles/second, and timeout's 3rd parm is in cycles(barf!) */
    return timeout(aproc, arock, (ams * hz)/1000 + 1);
}

/* cancel a timeout, whether or not it has already occurred */
int osi_CancelProc(aproc, arock)
    register int (*aproc);
    register char *arock; {
    return untimeout(aproc, arock);
}

/* get the time in seconds */
long osi_Time() {
    struct timeval tv;
    osi_GetTime(&tv);
    return tv.tv_sec;
}

/* set the real time */
osi_SetTime(atv)
    register struct timeval *atv; {
    register int s;

    /* stolen from kern_time.c */
    boottime.tv_sec += atv->tv_sec - time.tv_sec;
    s = splclock(); time = *atv; (void) splx(s);
    resettodr();
    return 0;
}

char *osi_Alloc(x)
    long x; {
    register struct osimem *tm;
#ifdef ibm032
    x += 4;     /* rt strlen overruns end of string */
#endif
    osi_memUsage += x;
#ifdef AFSDEBMEM
    tm = (struct osimem *) kmem_alloc(x+12);
#else
    tm = (struct osimem *) kmem_alloc(x);
#endif AFSDEBMEM
#ifdef AFSDEBMEM
    tm->next = memlist;
    memlist = tm;
    tm->size = x;
    tm->addr = *((long *)((char *)&x - 4));     /* try to find return address */
    return (char *) ((long)tm + 12);
#else
    return (char *) tm;
#endif AFSDEBMEM
}

osi_Free(x,asize)
    register char *x;
    register long asize; {
    register struct osimem *tm, **lm, *um;

#ifdef ibm032
    asize += 4;     /* rt strlen overruns end of strings */
#endif
    osi_memUsage -= asize;
#ifdef AFSDEBMEM
    while(dumping) osi_Sleep(&dumping);     /* not bulletproof, but good enough
*/
    tm = (struct osimem *) ((long)x - 12);
    lm = &memlist;
    for(um=memlist; um; lm = &um->next, um = *lm) {
        if (um == tm) break;
    }
    if (um) *lm = tm->next;
    kmem_free(tm,asize+12);
#else
    kmem_free((struct osimem *)x, asize);
#endif
}

osi_Dump(){
    register struct osimem *tm;
    register long *val;
    dumping = 1;
#ifdef AFSDEBMEM
    for(tm=memlist; tm; tm=tm->next) {
        val = (long *) ((long)tm + 12);
        printf("block at %x size %d addr %x: ",
               tm, tm->size, tm->addr);
        if (tm->size >= 4) printf("%x ", val[0]);
        if (tm->size >= 8) printf("%x ", val[1]);
        if (tm->size >= 12) printf("%x ", val[2]);
        if (tm->size >= 16) printf("%x ", val[3]);
        if (tm->size >= 20) printf("%x ", val[4]);
        printf("\n");
    }
#endif AFSDEBMEM
    dumping = 0;
    osi_Wakeup(&dumping);
}
