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

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:vm_swap.c 12.0$";
#endif

/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)vm_swap.c	7.1 (Berkeley) 6/5/86
 */

#include "../machine/pte.h"
#if defined(ibm032)
#include "../machine/debug.h"
#else
#define DEBUGF(cond,stmt) 		/* do nothing if not debugging */
#endif ibm032

#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#ifdef VFS
#include "vnode.h"
#endif VFS
#include "proc.h"
#include "text.h"
#include "map.h"
#include "buf.h"
#include "cmap.h"
#include "vm.h"

/*
 * Swap a process in.
 */
swapin(p)
	register struct proc *p;
{
	register struct text *xp;
	register int i, s;

	DEBUGF (swdebug & SHOW_SWIN, printf("swapin(pid=%d)\n", p->p_pid));
	if (xp = p->p_textp)
		xlock(xp);
#ifdef ibm032
	p->p_szpt = clrnd(ctopt(p->p_ssize+p->p_dsize+p->p_tsize+HIGHPAGES));
#else
  	p->p_szpt = clrnd(ctopt(p->p_ssize+p->p_dsize+p->p_tsize+UPAGES));
#endif
	if (vgetpt(p, memall) == 0)
		goto nomem;
	if (vgetu(p, memall, Swapmap, &swaputl, (struct user *)0) == 0) {
		vrelpt(p);
		goto nomem;
	}
	DEBUGF (swdebug & SHOW_SWIN, printf("swapin: pid=%d u struct\n", p->p_pid));

	swdspt(p, &swaputl, B_READ);
	/*
	 * Make sure swdspt didn't smash u. pte's
	 */
	for (i = 0; i < UPAGES; i++) {
		if (Swapmap[i].pg_pfnum != p->p_addr[i].pg_pfnum)
			panic("swapin");
	}
	DEBUGF (swdebug & SHOW_SWIN, printf("swapin: pid=%d page tables\n", p->p_pid));
	vrelswu(p, &swaputl);
	if (xp) {
		xlink(p);
		xunlock(xp);
	}

	DEBUGF (swdebug & SHOW_SWIN, printf("swapin: pid=%d complete\n", p->p_pid));
	p->p_rssize = 0;
	s = splclock();
	if (p->p_stat == SRUN)
		setrq(p);
	p->p_flag |= SLOAD;
	if (p->p_flag & SSWAP) {
		swaputl.u_pcb.pcb_sswap = (int *)&u.u_ssave;
		p->p_flag &= ~SSWAP;
	}
	splx(s);
	p->p_time = 0;
	multprog++;
	cnt.v_swpin++;
	unalias((caddr_t) &swaputl);		/* don't need access any more */
	return (1);

nomem:
	if (xp)
		xunlock(xp);
	return (0);
}

int	xswapwant, xswaplock;
/*
 * Swap out process p.
 * ds and ss are the old data size and the stack size
 * of the process, and are supplied during page table
 * expansion swaps.
 */
swapout(p, ds, ss)
	register struct proc *p;
	size_t ds, ss;
{
	register struct pte *map;
	register struct user *utl;
	register int a;
	int s;
	int rc = 1;

	s = 1;
	map = Xswapmap;
	utl = &xswaputl;
	if (xswaplock & s)
		if ((xswaplock & 2) == 0) {
			s = 2;
			map = Xswap2map;
			utl = &xswap2utl;
		}
	a = splclock();
	while (xswaplock & s) {
		xswapwant |= s;
		sleep((caddr_t)map, PSWP);
	}
	xswaplock |= s;
	splx(a);
	DEBUGF (swdebug & SHOW_SWOUT, printf("swapout(pid=%d, ds=%d, ss=%d) via swap map %d\n",
		     p->p_pid, ds, ss, s));
	uaccess(p, map, utl);
	if (vgetswu(p, utl) == 0) {
		p->p_flag |= SLOAD;
		rc = 0;
		goto out;
	}
	utl->u_ru.ru_nswap++;
	utl->u_odsize = ds;
	utl->u_ossize = ss;
	p->p_flag |= SLOCK;
	if (p->p_textp) {
		if (p->p_textp->x_ccount == 1)
			p->p_textp->x_swrss = p->p_textp->x_rssize;
		xdetach(p->p_textp, p);
	}
	p->p_swrss = p->p_rssize;
	vsswap(p, dptopte(p, 0), CDATA, 0, (int)ds, &utl->u_dmap);
	vsswap(p, sptopte(p, CLSIZE-1), CSTACK, 0, (int)ss, &utl->u_smap);
	if (p->p_rssize != 0)
		panic("swapout rssize");
	DEBUGF (swdebug & SHOW_SWOUT, printf("swapout: pid=%d data and stack\n", p->p_pid));

	swdspt(p, utl, B_WRITE);
#ifdef ibm032
	unalias((caddr_t) utl);		/* no longer need access to utl */
#endif ibm032
	DEBUGF (swdebug & SHOW_SWOUT, printf("swapout: pid=%d page tables\n", p->p_pid));
	/*
	 * If 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 then block memory allocation by raising ipl.
	 */
	vrelu(p, 1);
	DEBUGF (swdebug & SHOW_SWOUT, printf("swapout: pid=%d u struct\n", p->p_pid));
	if ((p->p_flag & SLOAD) && (p->p_stat != SRUN || p != u.u_procp))
		panic("swapout");
	p->p_flag &= ~SLOAD;
	vrelpt(p);
	DEBUGF (swdebug & SHOW_SWOUT, printf("swapout: pid=%d complete\n", p->p_pid));
	p->p_flag &= ~SLOCK;
	p->p_time = 0;

	multprog--;
	cnt.v_swpout++;

	if (runout) {
		runout = 0;
		wakeup((caddr_t)&runout);
	}
out:
	xswaplock &= ~s;
	if (xswapwant & s) {
		xswapwant &= ~s;
		wakeup((caddr_t)map);
	}
	return (rc);
}

/*
 * Swap the data and stack page tables in or out.
 * Only hard thing is swapping out when new pt size is different than old.
 * If we are growing new pt pages, then we must spread pages with 2 swaps.
 * If we are shrinking pt pages, then we must merge stack pte's into last
 * data page so as not to lose them (and also do two swaps).
 */
swdspt(p, utl, rdwri)
	register struct proc *p;
	register struct user *utl;
{
	register int szpt, tsz, ssz;
	int tdlast, slast, tdsz;
	register struct pte *pte;
	register int i;

#ifdef ibm032
	szpt = clrnd(ctopt(p->p_tsize+p->p_dsize+p->p_ssize+HIGHPAGES));
#else
	szpt = clrnd(ctopt(p->p_tsize+p->p_dsize+p->p_ssize+UPAGES));
#endif
	tsz = p->p_tsize / NPTEPG;
	if (szpt == p->p_szpt) {
		swptstat.pteasy++;
		swpt(rdwri, p, 0, tsz,
		    (p->p_szpt - tsz) * NBPG - UPAGES * sizeof (struct pte));
		goto check;
	}
	if (szpt < p->p_szpt)
		swptstat.ptshrink++;
	else
		swptstat.ptexpand++;
#ifdef ibm032
	ssz = clrnd(ctopt(utl->u_ossize+HIGHPAGES));
	if (szpt < p->p_szpt && utl->u_odsize && (utl->u_ossize+HIGHPAGES)) {
#else
	ssz = clrnd(ctopt(utl->u_ossize+UPAGES));
	if (szpt < p->p_szpt && utl->u_odsize && (utl->u_ossize+UPAGES)) {
#endif
		/*
		 * Page tables shrinking... see if last text+data and
		 * last stack page must be merged... if so, copy
		 * stack pte's from last stack page to end of last
		 * data page, and decrease size of stack pt to be swapped.
		 */
		tdlast = (p->p_tsize + utl->u_odsize) % (NPTEPG * CLSIZE);
#ifdef ibm032
		slast = (utl->u_ossize + HIGHPAGES) % (NPTEPG * CLSIZE);
#else
		slast = (utl->u_ossize + UPAGES) % (NPTEPG * CLSIZE);
#endif
		if (tdlast && slast && tdlast + slast <= (NPTEPG * CLSIZE)) {
			swptstat.ptpack++;
			tdsz = clrnd(ctopt(p->p_tsize + utl->u_odsize));
			bcopy((caddr_t)sptopte(p, utl->u_ossize - 1),
			    (caddr_t)&p->p_p0br[tdsz * NPTEPG - slast],
			    (unsigned)(slast * sizeof (struct pte)));
			ssz -= CLSIZE;
		}
	}
	if (ssz)
		swpt(rdwri, p, szpt - ssz - tsz, p->p_szpt - ssz, ssz * NBPG);
	if (utl->u_odsize)
		swpt(rdwri, p, 0, tsz,
		    (int)(clrnd(ctopt(p->p_tsize + utl->u_odsize)) - tsz) * NBPG);
check:
	for (i = 0; i < utl->u_odsize; i++) {
		pte = dptopte(p, i);
		if (pte->pg_v || pte->pg_fod == 0 && (pte->pg_pfnum||pte->pg_m))
			panic("swdspt");
	}
	for (i = 0; i < utl->u_ossize; i++) {
		pte = sptopte(p, i);
		if (pte->pg_v || pte->pg_fod == 0 && (pte->pg_pfnum||pte->pg_m))
			panic("swdspt");
	}
}

/*
 * Swap a section of the page tables.
 * Errors are handled at a lower level (by doing a panic).
 */
swpt(rdwri, p, doff, a, n)
	int rdwri;
	struct proc *p;
	int doff, a, n;
{

	if (n <= 0)
		return;
	(void)swap(p, p->p_swaddr + ctod(UPAGES) + ctod(doff),
#ifndef VFS
	    (caddr_t)&p->p_p0br[a * NPTEPG], n, rdwri, B_PAGET, swapdev, 0);
#else !VFS
	    (caddr_t)&p->p_p0br[a * NPTEPG], n, rdwri, B_PAGET, swapdev_vp, 0);
#endif !VFS
}
