4.3BSD-Reno/src/sys/kern/vm_text.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution is only permitted until one year after the first shipment
 * of 4.4BSD by the Regents.  Otherwise, redistribution and use in source and
 * binary forms are permitted provided that: (1) source distributions retain
 * this entire copyright notice and comment, and (2) distributions including
 * binaries display the following acknowledgement:  This product includes
 * software developed by the University of California, Berkeley and its
 * contributors'' in the documentation or other materials provided with the
 * distribution and in all advertising materials mentioning features or use
 * of this software.  Neither the name of the University nor the names of
 * its contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *	@(#)vm_text.c	7.9 (Berkeley) 6/28/90
 */

#include "param.h"
#include "systm.h"
#include "user.h"
#include "proc.h"
#include "text.h"
#include "vnode.h"
#include "buf.h"
#include "seg.h"
#include "cmap.h"
#include "uio.h"
#include "exec.h"
#include "vm.h"

#include "machine/pte.h"
#include "machine/cpu.h"

#define X_LOCK(xp) { \
	while ((xp)->x_flag & XLOCK) { \
		(xp)->x_flag |= XWANT; \
		sleep((caddr_t)(xp), PSWP); \
	} \
	(xp)->x_flag |= XLOCK; \
}
#define	XUNLOCK(xp) { \
	if ((xp)->x_flag & XWANT) \
		wakeup((caddr_t)(xp)); \
	(xp)->x_flag &= ~(XLOCK|XWANT); \
}
#define FREE_AT_HEAD(xp) { \
	(xp)->x_forw = xhead; \
	xhead = (xp); \
	(xp)->x_back = &xhead; \
	if (xtail == &xhead) \
		xtail = &(xp)->x_forw; \
	else \
		(xp)->x_forw->x_back = &(xp)->x_forw; \
}
#define FREE_AT_TAIL(xp) { \
	(xp)->x_back = xtail; \
	*xtail = (xp); \
	xtail = &(xp)->x_forw; \
	/* x_forw is NULL */ \
}
#define	ALLOC(xp) { \
	*((xp)->x_back) = (xp)->x_forw; \
	if ((xp)->x_forw) \
		(xp)->x_forw->x_back = (xp)->x_back; \
	else \
		xtail = (xp)->x_back; \
	(xp)->x_forw = NULL; \
	(xp)->x_back = NULL; \
}

/*
 * The text-cache:
 *
 * We place up to ``maxtextcache'' free text table entries on a free list
 * to form an LRU cache.  This causes the swap (but not RAM) resources to
 * be saved.  These text images are treated as "sticky", and are placed on
 * the free list when unused.  They may be reclaimed from the free list
 * until reused.  The cache changes to MRU once the maximum limit is
 * reached since we just cease caching new texts rather than replacing
 * the LRU one (should be fixed).  All cached text resources may be
 * reclaimed by calling xpurge().  Currently, swpexpand() and xalloc() do
 * this if an attempted swap allocation fails.
 *
 * Note that although true "sticky" texts are handling in the same way,
 * they are not considered part of the cache; i.e. they are not subject
 * to the maximum limit nor are they purged with xpurge().  They are in
 * a sense "locked down" cache entries.
 */
struct	text *xhead, **xtail;		/* text table free list */
int	xcache;				/* number of "sticky" texts retained */
int	maxtextcache = -1;		/* maximum number of "sticky" texts */
struct	xstats xstats;			/* cache statistics */

/*
 * initialize text table
 */
xinit()
{
	register struct text *xp;

	xtail = &xhead;
	for (xp = text; xp < textNTEXT; xp++)
		FREE_AT_TAIL(xp);
	if (maxtextcache == -1)
		maxtextcache = ntext;
}

/*
 * relinquish use of the shared text segment
 * of a process.
 */
xfree()
{
	register struct text *xp;
	register struct vnode *vp;
	struct vattr vattr;

	if ((xp = u.u_procp->p_textp) == NULL)
		return;
	xstats.free++;
	X_LOCK(xp);
	vp = xp->x_vptr;
	if (--xp->x_count == 0 &&
	    (VOP_GETATTR(vp, &vattr, u.u_cred) != 0 ||
	    (vattr.va_mode & VSVTX) == 0)) {
		if (xcache >= maxtextcache || xp->x_flag & XTRC ||
		    vattr.va_nlink == 0) {			/* XXX */
			xp->x_rssize -= vmemfree(tptopte(u.u_procp, 0),
				(int)u.u_tsize);
			if (xp->x_rssize != 0)
				panic("xfree rssize");
			while (xp->x_poip)
				sleep((caddr_t)&xp->x_poip, PSWP+1);
			xp->x_flag &= ~XLOCK;
			xuntext(xp);
			FREE_AT_HEAD(xp);
		} else {
			if (xp->x_flag & XWRIT) {
				xstats.free_cacheswap++;
				xp->x_flag |= XUNUSED;
			}
			xcache++;
			xstats.free_cache++;
			xp->x_flag |= XCACHED;
			xccdec(xp, u.u_procp);
#if defined(tahoe)	
			xp->x_ckey = 0;
#endif
			FREE_AT_TAIL(xp);
		}
	} else {
#if defined(tahoe)
		if (xp->x_count == 0)
			xp->x_ckey = 0;
#endif
		xccdec(xp, u.u_procp);
		xstats.free_inuse++;
	}
	xunlink(u.u_procp);
	XUNLOCK(xp);
	u.u_procp->p_textp = NULL;
}

/*
 * Attach to a shared text segment.
 * If there is no shared text, just return.
 * If there is, hook up to it:
 * if it is not currently being used, it has to be read
 * in from the vnode (vp); the written bit is set to force it
 * to be written out as appropriate.
 * If it is being used, but is not currently in core,
 * a swap has to be done to get it back.
 */
xalloc(vp, ep, toff, cred)
	register struct vnode *vp;
	struct exec *ep;
	off_t toff;
	struct ucred *cred;
{
	register struct text *xp;
	register struct proc *p;

	if (ep->a_text == 0)
		return;
	xstats.alloc++;
	p = u.u_procp;
	while ((xp = vp->v_text) != NULL) {
		if (xp->x_flag&XLOCK) {
			/*
			 * Wait for text to be unlocked,
			 * then start over (may have changed state).
			 */
			xwait(xp);
			continue;
		}
		X_LOCK(xp);
		if (xp->x_flag & XCACHED) {
			xstats.alloc_cachehit++;
			ALLOC(xp);
			xp->x_flag &= ~(XCACHED|XUNUSED);
			xcache--;
		} else
			xstats.alloc_inuse++;
		xp->x_count++;
		p->p_textp = xp;
		xlink(p);
		XUNLOCK(xp);
#if defined(tahoe)
		ckeyrelease(p->p_ckey);
		if (ckey_cnt[xp->x_ckey])
			ckey_cnt[xp->x_ckey]++;
		else		/* dead key */
			xp->x_ckey = getcodekey();
		p->p_ckey = xp->x_ckey;
#endif
		return;
	}
	xp = xhead;
	if (xp == NULL) {
		tablefull("text");
		psignal(p, SIGKILL);
		return;
	}
	ALLOC(xp);
	if (xp->x_vptr)
		xuntext(xp);
	xp->x_flag = XLOAD|XLOCK;
	if (p->p_flag & SPAGV)
		xp->x_flag |= XPAGV;
	xp->x_size = clrnd(btoc(ep->a_text));
	if (vsxalloc(xp) == NULL) {
		/* flush text cache and try again */
		if (xpurge() == 0 || vsxalloc(xp) == NULL) {
			swkill(p, "xalloc: no swap space");
			return;
		}
	}
	xp->x_count = 1;
	xp->x_ccount = 0;
	xp->x_rssize = 0;
	xp->x_mtime = 0;
	xp->x_vptr = vp;
	vp->v_flag |= VTEXT;
	vp->v_text = xp;
	VREF(vp);
	p->p_textp = xp;
	xlink(p);
	if ((p->p_flag & SPAGV) == 0) {
		settprot(RW);
		p->p_flag |= SKEEP;
		(void) vn_rdwr(UIO_READ, vp,
			(caddr_t)ctob(tptov(p, 0)),
			(int)ep->a_text, toff,
			UIO_USERSPACE, (IO_UNIT|IO_NODELOCKED), cred, (int *)0);
		p->p_flag &= ~SKEEP;
	}
	settprot(RO);
#if defined(tahoe)
	ckeyrelease(p->p_ckey);
	xp->x_ckey = getcodekey();
	p->p_ckey = xp->x_ckey;
#endif
	xp->x_flag |= XWRIT;
	xp->x_flag &= ~XLOAD;
	XUNLOCK(xp);
}

/*
 * Lock and unlock a text segment from swapping
 */
xlock(xp)
	register struct text *xp;
{

	X_LOCK(xp);
}

/*
 * Wait for xp to be unlocked if it is currently locked.
 */
xwait(xp)
	register struct text *xp;
{

	X_LOCK(xp);
	XUNLOCK(xp);
}

xunlock(xp)
	register struct text *xp;
{

	XUNLOCK(xp);
}

/*
 * Decrement the in-core usage count of a shared text segment,
 * which must be locked.  When the count drops to zero,
 * free the core space.
 */
xccdec(xp, p)
	register struct text *xp;
	register struct proc *p;
{

	if (--xp->x_ccount == 0) {
		if (xp->x_flag & XWRIT) {
			vsswap(p, tptopte(p, 0), CTEXT, 0, (int)xp->x_size,
			    (struct dmap *)0);
			if (xp->x_flag & XPAGV)
				(void) swap(p, xp->x_ptdaddr,
				    (caddr_t)tptopte(p, 0),
				    (int)xp->x_size * sizeof (struct pte),
				    B_WRITE, B_PAGET, swapdev_vp, 0);
			xp->x_flag &= ~XWRIT;
		} else
			xp->x_rssize -= vmemfree(tptopte(p, 0),
			    (int)xp->x_size);
		if (xp->x_rssize != 0)
			panic("text rssize");
	}
}

/*
 * Detach a process from the in-core text.
 * External interface to xccdec, used when swapping out a process.
 */
xdetach(xp, p)
	register struct text *xp;
	struct proc *p;
{

	if (xp && xp->x_ccount != 0) {
		X_LOCK(xp);
		xccdec(xp, p);
		xunlink(p);
		XUNLOCK(xp);
	}
}

/*
 * Free the swap image of all unused saved-text text segments
 * which are from file system mp (used by umount system call).
 */
xumount(mp)
	struct mount *mp;
{
	register struct text *xp;

	for (xp = text; xp < textNTEXT; xp++) 
		if (xp->x_vptr != NULL &&
		    (mp == NULL || (xp->x_vptr->v_mount == mp)) &&
		    (xp->x_flag & XLOCK) == 0)
			xuntext(xp);
	mpurgemp(mp);
}

/*
 * Flush all cached text segments to reclaim swap space.
 * Used during swap allocation when out of swap space.
 */
xpurge()
{
	register struct text *xp;
	int found = 0;

	xstats.purge++;
	for (xp = text; xp < textNTEXT; xp++)
		if (xp->x_vptr && (xp->x_flag & (XLOCK|XCACHED)) == XCACHED) {
			xuntext(xp);
			/* really gone? */
			if (xp->x_vptr == NULL)
				found++;
		}
	return(found);
}

/*
 * remove a shared text segment from the text table, if possible.
 */
xrele(vp)
	register struct vnode *vp;
{

	if (vp->v_flag & VTEXT)
		xuntext(vp->v_text);
}

/*
 * remove text image from the text table.
 * the use count must be zero.
 */
xuntext(xp)
	register struct text *xp;
{
	register struct vnode *vp;

	X_LOCK(xp);
	if (xp->x_count == 0) {
		vp = xp->x_vptr;
		xp->x_vptr = NULL;
		vsxfree(xp, (long)xp->x_size);
		vp->v_flag &= ~VTEXT;
		vp->v_text = NULL;
		mpurge(vp);
		vrele(vp);
		/*
		 * Take care of text cache statistics
		 */
		if (xp->x_flag & XCACHED) {
			if (xp->x_flag & XUNUSED)
				xstats.alloc_unused++;
			xp->x_flag &= ~(XCACHED|XUNUSED);
			xstats.alloc_cacheflush++;
			xcache--;
		}
	}
	XUNLOCK(xp);
}

/*
 * Add a process to those sharing a text segment by
 * getting the page tables and then linking to x_caddr.
 */
xlink(p)
	register struct proc *p;
{
	register struct text *xp = p->p_textp;

	if (xp == 0)
		return;
	vinitpt(p);
	p->p_xlink = xp->x_caddr;
	xp->x_caddr = p;
	xp->x_ccount++;
}

xunlink(p)
	register struct proc *p;
{
	register struct text *xp = p->p_textp;
	register struct proc *q;

	if (xp == 0)
		return;
	if (xp->x_caddr == p) {
		xp->x_caddr = p->p_xlink;
		p->p_xlink = 0;
		return;
	}
	for (q = xp->x_caddr; q->p_xlink; q = q->p_xlink)
		if (q->p_xlink == p) {
			q->p_xlink = p->p_xlink;
			p->p_xlink = 0;
			return;
		}
	panic("lost text");
}

/*
 * Replace p by q in a text incore linked list.
 * Used by vfork(), internally.
 */
xrepl(p, q)
	struct proc *p, *q;
{
	register struct text *xp = q->p_textp;

	if (xp == 0)
		return;
	xunlink(p);
	q->p_xlink = xp->x_caddr;
	xp->x_caddr = q;
}

int xkillcnt = 0;

/*
 * Invalidate the text associated with vp.
 * Purge in core cache of pages associated with vp and kill all active
 * processes.
 */
xinval(vp)
	struct vnode *vp;
{
	register struct text *xp;
	register struct proc *p;
	int found = 0;

	mpurge(vp);
	xp = vp->v_text;
	if (xp->x_flag & XPAGV) {
		for (p = xp->x_caddr; p; p = p->p_xlink) {
			/*
			 * swkill without uprintf
			 */
			printf("pid %d killed due to text modification\n",
				p->p_pid);
			psignal(p, SIGKILL);
			p->p_flag |= SULOCK;
			xkillcnt++;
			found++;
		}
		/*
		 * Take care of the text cache.
		 * If there was a process still using the text just mark
		 * the text as XTRC so it won't be cached.  If no one was
		 * using it then it is in the cache and we need to flush
		 * it with xuntext.
		 */
		if (found)
			xp->x_flag |= XTRC;
		else
			xuntext(xp);
	}
}