/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: sys_vm.c 1.11 89/09/01 $"

/*
 * sys_vm.c
 *	VM Related system-calls.
 */

/* $Log:	sys_vm.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/signal.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/vm.h"
#include "sys/lock.h"

#include "sys/intctl.h"

vm_ctl()
{
	register struct a {
		int	funct;
		caddr_t	stuff;
	} *uap = (struct a *)u.u_ap;
	register struct proc *p = u.u_procp;
	register long	wantRS;
	register spl_t	s;
	struct	vm_tune	vm_arg;
	extern	bool_t	root_vm_setrs;
	extern	bool_t	root_vm_swapable;
	extern	bool_t	root_vm_pffable;

	switch(uap->funct) {

	case VM_GETPARAM:
		u.u_error = copyout((caddr_t)&vmtune, uap->stuff,
							sizeof(struct vm_tune));
		break;

	case VM_SETPARAM:
		u.u_error = copyin(uap->stuff, (caddr_t)&vm_arg,
							sizeof(struct vm_tune));
		if (u.u_error || !suser())
			break;
		
		/*
		 * Some basic sanity checks on the data.
		 * These insure no kernel panics and basic sanity; it
		 * possible to completely mess up the system with
		 * inappropriate combinations of values here.
		 *
		 * Insisting minRS,maxRS >=2 insures a RAW IO can make progress.
		 *
		 * minRS is coerced <= maxforkRS to insure forks can
		 * proceed.
		 */

#define	BOUND(a,op,b) if (!(a op b)) a = b

		BOUND(vm_arg.vt_maxRS, <=, maxRS);
		BOUND(vm_arg.vt_maxRS, >=, 4);

		BOUND(vm_arg.vt_minRS, <=, vm_arg.vt_maxRS);
		BOUND(vm_arg.vt_minRS, <=, maxforkRS);
		BOUND(vm_arg.vt_minRS, >=, 4);

		BOUND(vm_arg.vt_RSexecmult, >=, 1);
		BOUND(vm_arg.vt_RSexecdiv, >=, 1);

		BOUND(vm_arg.vt_PFFvtime, >=, 0);
		BOUND(vm_arg.vt_PFFhigh, >=, 0);
		BOUND(vm_arg.vt_PFFlow, >=, 0);
		BOUND(vm_arg.vt_PFFlow, <=, vm_arg.vt_PFFhigh);
		BOUND(vm_arg.vt_PFFincr, >=, 0);
		BOUND(vm_arg.vt_PFFdecr, >=, 0);

		/*
		 * All is at least sane; assign the real values...
		 */

		vmtune = vm_arg;
		break;

	case VM_GETRS:
		if (suword(uap->stuff, (int)p->p_rscurr) < 0)
			u.u_error = EFAULT;
		break;

	case VM_SETRS:
		/*
		 * Get and verify legality of new Rset size.
		 */

		if ((wantRS = fuword(uap->stuff)) == -1) {
			u.u_error = EFAULT;
			break;
		}

		if (wantRS < vmtune.vt_minRS
		||  wantRS > vmtune.vt_maxRS
		||  wantRS > p->p_maxrss) {
			u.u_error = EINVAL;
			break;
		}

		/*
		 * Is legal size; check for root and adjust self Rset size.
		 * vsetRS() can set a different value if this races with
		 * a concurrent vm_ctl(VM_SETPARAM), but don't worry about it.
		 */

		if (!root_vm_setrs || suser())
			vsetRS(wantRS);
		break;

	case VM_SWAPABLE:
		/*
		 * Declare self swap-able or not.
		 * If making self non-swap-able, might just need to be root...
		 */

		s = p_lock(&p->p_state, SPLHI);

		if (uap->stuff)				/* want swappable */
			p->p_flag &= ~SNOSWAP;
		else if (!root_vm_swapable || suser())	/* non-swappable */
			p->p_flag |= SNOSWAP;

		v_lock(&p->p_state, s);
		break;

	case VM_PFFABLE:
		/*
		 * Declare self PFF-able or not.
		 * If making self non-PFF-able, might just need to be root...
		 */

		s = p_lock(&p->p_state, SPLHI);

		if (uap->stuff)				/* want PFF-able */
			p->p_flag &= ~SNOPFF;
		else if (!root_vm_pffable || suser())	/* No PFF adjustment */
			p->p_flag |= SNOPFF;

		v_lock(&p->p_state, s);
		break;

	default:
		u.u_error = EINVAL;
		break;
	}
}

lock()
{
	struct a {
		long oper;
	} *uap = (struct a *)u.u_ap;

	if (!suser())
		return;

	switch (uap->oper) {
	case TXTLOCK:
		if ((u.u_lock & (PROCLOCK|TXTLOCK)) || (textlock() == 0))
			goto bad;
		break;

	case PROCLOCK:
		if ((u.u_lock&(PROCLOCK|TXTLOCK|DATLOCK)) || (textlock() == 0))
			goto bad;
		if (datalock() == 0) {
			(void)tunlock();
			goto bad;
		}
		proclock();
		break;

	case DATLOCK:
		if ((u.u_lock&(PROCLOCK|DATLOCK))  ||  datalock() == 0)
			goto bad;
		break;

	case UNLOCK:
		if (punlock() == 0)
			goto bad;
		break;

	default:
bad:
		if (u.u_error == 0)
			u.u_error = EINVAL;
	}
}

textlock()
{
	u.u_lock |= TXTLOCK;
	u.u_procp->p_flag |= SNOSWAP;
	return(1);
}
		
tunlock()
{
	u.u_lock &= ~TXTLOCK;
	if (u.u_lock == 0)
		u.u_procp->p_flag &= ~SNOSWAP;
	return(1);
}

datalock()
{
	u.u_lock |= DATLOCK;
	u.u_procp->p_flag |= SNOSWAP;
	return(1);
}
		
dunlock()
{
	u.u_lock &= ~DATLOCK;
	if (u.u_lock == 0)
		u.u_procp->p_flag &= ~SNOSWAP;
	return(1);
}

proclock()
{
	u.u_procp->p_flag |= SNOSWAP;
	u.u_lock |= PROCLOCK;
}

punlock()
{
	if ((u.u_lock&(PROCLOCK|TXTLOCK|DATLOCK)) == 0)
		return(0);
	u.u_procp->p_flag &= ~SNOSWAP;
	u.u_lock &= ~PROCLOCK;
	if (u.u_lock & TXTLOCK)
		(void)tunlock();
	if (u.u_lock & DATLOCK)
		(void)dunlock();
	return(1);
}
