/* $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: vm_drum.c 1.17 90/04/24 $"

/*
 * vm_drum.c
 *	Manage swap space.
 *
 * Supports optional dynamic allocation of swap space (dynamic_swap_space).
 * Note that if dynamic_swap_space were not configurable (ie, hard wired
 * always), u_c{d,s}map could go away, and fork() and exec() streamlined a bit.
 */

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

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/signal.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/vm.h"
#include "sys/user.h"
#include "sys/vnode.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/buf.h"
#include "sys/map.h"
#include "sys/cmap.h"
#include "sys/cmn_err.h"

#include "sys/pte.h"
#include "sys/plocal.h"
#include "sys/intctl.h"

struct dmapext *vsallocext();

int	dmmin = DMMIN;			/* min dmap chunk allocation size */
int	dmmax = DMMAX;			/* max dmap chunk allocation size */
int	dmmax_sw = DMMAX_SW;		/* max swap space chunk size */

struct	dmap	zdmap;			/* for clearing map structs */

int		maxdmap;		/* max size of swap alloc per segment */
static	int	dm_nemap;		/* number elements in dmapext array */
static	int	sizeof_dmapext;		/* size (bytes) of dmapext object */
static	long	sizeof_dmapext_disk;	/* size (sectors) of dmapext object */

extern	int	dynamic_swap_space;	/* dynamic allocation?? */

static	long	dmap_can_hold[NDMAP];	/* [i] == blocks held in [i..NDMAP-1] */

#define	NEED_DMAP_EXT(i, nblks)	((i) >= NDMAP || (nblks) > dmap_can_hold[i])

/*
 * init_dmap()
 *	Initialize dmap related values.
 */

init_dmap()
{
	register int	blk;
	register uint	vaddr;
	register int	i;
	swblk_t		sum;

	/* 
	 * Determine number of entries in dmapext array (dm_nemap),
	 * max swap size we can represent per segment (maxdmap),
	 * and size of dmapext object (sizeof_dmapext).
	 */

	for (blk = dmmin, vaddr = 0; vaddr < MAXADDR; ) {
		++dm_nemap;
		maxdmap += blk;
		vaddr += ctob(dtoc(blk));
		if (blk < dmmax)
			blk *= 2;
	}

	/*
	 * Compute dmap_can_hold[].  dmap_can_hold[i] is maximum # blocks that
	 * can be represented starting at [i] before needing an extension
	 * object.  Streamlines vsalloc() for dynamicly allocated swap space
	 * by putting the loop here (once).
	 */

	for (sum = 0, blk = dmmin, i = 0; i < NDMAP; i++) {
		sum += blk;
		if (blk < dmmax)
			blk *= 2;
	}
	for (blk = dmmin, i = 0; i < NDMAP; i++) {
		dmap_can_hold[i] = sum;
		sum -= blk;
		if (blk < dmmax)
			blk *= 2;
	}

#ifdef	i386
	/*
	 * Kludge to get sizeof_dmapext to not overflow into next page by
	 * only a few bytes -- assuming dmmin == 16k, dmmax == 256k, need
	 * to compensate for 6 longs (2 from struct dmapext and 4 from
	 * the short blocks).  In practice, this drops about 1.5Meg from
	 * the max size segment, and since stack consumes at least 4Meg
	 * of virtual space (due to virtual hole, data can't grow into
	 * top 4Meg of 256Meg), this is a don't care.
	 *
	 * Not an issue on ns32000, since 16Meg address space and 256k max
	 * chunk doesn't fill a HW page.
	 */
	dm_nemap -= 6;
	maxdmap -= 6 * dmmax;
#endif	/* i386  */

	sizeof_dmapext = sizeof(struct dmapext) + sizeof(swblk_t)*(dm_nemap-1);
	sizeof_dmapext_disk = ctod(clrnd(btoc(sizeof_dmapext)));
}

/*
 * vsalloc()
 *	Insure range of pages is covered by pieces of swap space.
 *
 * Returns true for success, false for fail.  If fail, undoes any allocation
 * done prior to failure.
 *
 * Assumes caller has exclusive access to dmap for changes;
 * can race with pageout (no harm).
 *
 * Note that if always configure with dynamic_swap_space set, could simplify
 * this procedure.
 */

vsalloc(dmp, vsbase, npages)
	struct	dmap	*dmp;
	size_t		vsbase;			/* 1st page needing space */
	size_t		npages;			/* # pages needing space */
{
	register int	npg;
	register swblk_t *ip;
	register long	blk;
	struct	dmapext	*dme;
	long	blk0;
	swblk_t	*ip0;
	swblk_t	*ipmax;
	swblk_t	*mapmax;

	if (dynamic_swap_space)
		return vsalloc_dynamic(dmp, vsbase, npages);

	vsbase = ctod(vsbase);
	npg = ctod(npages);

	if (dme = dmp->dm_ext) {
		ip = dme->dme_map;
		mapmax = &ip[dm_nemap];
	} else {
		ip = dmp->dm_map;
		mapmax = &ip[NDMAP];
	}

	/*
	 * Find starting chunk.
	 */

	blk = dmmin;
	while (vsbase >= blk) {
		++ip;
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		else {				/* reached constant size blks */
			ip += (vsbase / blk);
			vsbase %= blk;
			break;
		}
	}
	ip0 = ip;
	blk0 = blk;
	npg += vsbase;				/* for start of block */

	/*
	 * Allocate space where needed.  Allocate extension if needed.
	 * Each allocated block number is stored negative to allow
	 * error unwind.  This has to be inverted before returning.
	 */

	while (npg > 0) {
		if (ip >= mapmax) {
			/*
			 * Allocate extension and copy current dmap to it.
			 * Write dmp->dm_ext only after it's all set up to
			 * avoid nasty races with pageout.
			 */
			ASSERT(dme == NULL, "vsalloc: dm_ext");
			/*
			 *+ When allocating swap space for a process, 
			 *+ the kernel overflowed the dmap array that holds
			 *+ swap addresses for this process, had to
			 *+ allocate a dmap extension object, and found that
			 *+ one had already been allocated.
			 */
			if ((dme = vsallocext()) == NULL)
				goto nospace;
			dme->dme_nent = NDMAP;
			bcopy((caddr_t) dmp->dm_map, (caddr_t) dme->dme_map,
						NDMAP * sizeof(swblk_t));
			dmp->dm_ext = dme;
			ip0 = &dme->dme_map[ip0 - dmp->dm_map];
			ip = &dme->dme_map[ip - dmp->dm_map];
			mapmax = &dme->dme_map[dm_nemap];
		}
		if (*ip == 0) {
			if ((*ip = -swapalloc(blk)) == 0)
				goto nospace;
		}
		npg -= blk;
		if (blk < dmmax)
			blk *= 2;
		++ip;
	}

	/*
	 * Got all requested space.  Un-negate block #'s to make them "real",
	 * and update # entries in extension.
	 */

	blk = blk0;
	ipmax = ip;
	for (ip = ip0; ip < ipmax; ip++) {
		if (*ip < 0)
			*ip = -*ip;
		if (blk < dmmax)
			blk *= 2;
	}
	if (dme && ip > &dme->dme_map[dme->dme_nent])
		dme->dme_nent = (ip - dme->dme_map);

	return (1);

	/*
	 * Didn't get space -- release any that we did get and return failure.
	 */
nospace:
	blk = blk0;
	ipmax = ip;
	for (ip = ip0; ip < ipmax; ip++) {
		if (*ip < 0) {
			swapfree(blk, -*ip);
			*ip = 0;
		}
		if (blk < dmmax)
			blk *= 2;
	}
	return (0);
}

/*
 * vsalloc_dynamic()
 *	Insure dmap object has room to represent the swap space.
 *
 * Allocate dmap extension if needed.  No actual allocation of swap space;
 * this is done in vstodb().
 *
 * Returns true for success, false for fail.
 *
 * Split form vsalloc() to keep code more clearly broken out.
 *
 * Assumes caller has exclusive access to dmap for changes;
 * can race with pageout (no harm).
 */

static
vsalloc_dynamic(dmp, vsbase, npages)
	struct	dmap	*dmp;
	register size_t	vsbase;			/* 1st page needing space */
	size_t		npages;			/* # pages needing space */
{
	register long	blk;
	register int	i;

	/*
	 * If already has an extension, nothing to do.
	 */

	if (dmp->dm_ext)
		return 1;

	/*
	 * Find starting chunk.  When done, i == 1st index that needs new
	 * space and vsbase is starting offset in that chunk.
	 */

	vsbase = ctod(vsbase);

	for (i = 0, blk = dmmin; vsbase >= blk; ) {
		++i;
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		else {				/* reached constant size blks */
			i += (vsbase / blk);
			vsbase %= blk;
			break;
		}
	}

	/*
	 * Insure object is big enough to represent the space:
	 * allocate extension if needed.
	 */

	if (NEED_DMAP_EXT(i, ctod(npages) + vsbase)) {
		struct dmapext *dme;
		ASSERT_DEBUG(dmp->dm_ext == NULL, "vsalloc: dm_ext");
		if ((dme = vsallocext()) == NULL)
			return 0;
		dme->dme_nent = NDMAP;
		bcopy(	(caddr_t) dmp->dm_map, (caddr_t) dme->dme_map,
				NDMAP * sizeof(swblk_t));
		dmp->dm_ext = dme;
	}

	return (1);
}

/*
 * fix_dmap()
 *	Ensure dmap array has no contiguous swap blocks in adjacent
 *	entries.
 *
 * Need to do this since the "striping" swap driver doesn't allow an
 * i/o request to cross a device boundary (ie the entire request must
 * be in the same partition) and it's possible for the swapout/pageout
*  routines in the mapper to cluster adjacent pages into a single i/o
 * operation and that could potentially cross a device boundary.
 *
 * Called by shm_bmap. This should be removed when the swap driver
 * becomes a true striping driver.
 */

fix_dmap(dmp, npages)
	struct dmap *dmp;
	register int npages;
{
	register swblk_t *ip;
	register long blk;

	if (dmp->dm_ext)
		ip = dmp->dm_ext->dme_map;
	else
		ip = dmp->dm_map;

	for (blk = dmmin, npages = ctod(npages); npages > blk; ++ip) {
		if (*ip) {
			/*
			 * If this entry + size matches the next
			 * entry, alloc a new block and free this
			 * one.
			 */
			if ((*ip) + blk == *(ip+1)) {
				swblk_t base = *ip;
				*ip = swapalloc(blk);
				if (*ip == 0) {
					*ip = base;
					return(0);
				}
				swapfree(blk, base);
			}
		}
		npages -= blk;
		if (blk < dmmax)
			blk *= 2;
	}
	return(1);
}

/*
 * vsfree()
 *	Deallocate any swap chunks totally covered by range of pages.
 */

vsfree(dmp, vsbase, npages)
	register struct dmap *dmp;
	register size_t vsbase;			/* 1st page not needing space */
	size_t		npages;			/* # pages not needing space */
{
	register int	npg;
	register swblk_t *ip;
	register long	blk;
	swblk_t	*mapmax;

	vsbase = ctod(vsbase);
	npg = ctod(npages);

	if (dmp->dm_ext) {
		ip = dmp->dm_ext->dme_map;
		mapmax = &ip[dmp->dm_ext->dme_nent];
	} else {
		ip = dmp->dm_map;
		mapmax = &ip[NDMAP];
	}

	/*
	 * Find starting chunk.
	 */

	blk = dmmin;
	while (vsbase >= blk && ip < mapmax) {
		++ip;
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		else {				/* reached constant size blks */
			/*
			 * Before advancing 'ip', check for possible pointer
			 * overflow that would otherwise satisfy the check:
			 *	while (ip < mapmax) ....
			 */
			if (vsbase / blk >= mapmax - ip)
				return;			/* no space to free */
			ip += (vsbase / blk);
			vsbase %= blk;
			break;
		}
	}

	/*
	 * If page not at start of chunk, adjust to start of next.
	 */

	if (vsbase) {
		npg -= (blk - vsbase);
		if (blk < dmmax)
			blk *= 2;
		++ip;
	}

	/*
	 * Free space as long as totally overlap and map exists.
	 * Caller has arranged that npg covers remainder of swap space
	 * if want to free it all beyond a given point.
	 */

	while (npg >= blk && ip < mapmax) {
		if (*ip) {
			swapfree(blk, *ip);
			*ip = 0;
		}
		npg -= blk;
		if (blk < dmmax)
			blk *= 2;
		++ip;
	}
}

/*
 * vsclone()
 *	Clone one dmap into another (for fork).
 *
 * Returns true for success, else false.
 */

vsclone(sdmp, tdmp)
	struct	dmap	*sdmp;				/* source dmap */
	struct	dmap	*tdmp;				/* target dmap */
{
	register swblk_t *sip;
	register swblk_t *tip;
	register swblk_t *lastip;
	register long	blk;

	if (sdmp->dm_ext) {
		if ((tdmp->dm_ext = vsallocext()) == NULL)
			return (0);
		if (dynamic_swap_space) {	/* zeroed in vsallocext() */
			tdmp->dm_ext->dme_nent = 0;
			return (1);
		}
		tdmp->dm_ext->dme_nent = sdmp->dm_ext->dme_nent;
		sip = sdmp->dm_ext->dme_map;
		tip = tdmp->dm_ext->dme_map;
		lastip = &tdmp->dm_ext->dme_map[tdmp->dm_ext->dme_nent];
	} else {
		tdmp->dm_ext = NULL;
		if (dynamic_swap_space) {
			bzero((caddr_t) tdmp->dm_map, sizeof(tdmp->dm_map));
			return (1);
		}
		sip = sdmp->dm_map;
		tip = tdmp->dm_map;
		lastip = &tdmp->dm_map[NDMAP];
	}

	blk = dmmin;
	for (; tip < lastip; sip++, tip++) {
		if (*sip) {
			if ((*tip = swapalloc(blk)) == 0) {
				while (++tip < lastip)
					*tip = 0;
				vsrele(tdmp);
				return (0);
			}
		} else
			*tip = 0;
		if (blk < dmmax)
			blk *= 2;
	}

	return (1);
}

/*
 * vsallocext()
 *	Allocate a dmap extension and swap space.
 *
 * Returns NULL if fail, else address of new struct dmapext.
 */


static struct dmapext *
vsallocext()
{
	register struct dmapext *dme;
	register swblk_t dme_swap;

	dme_swap = swapalloc(sizeof_dmapext_disk);
	if (dme_swap == 0)
		return (NULL);
	dme = (struct dmapext *) wmemall(sizeof_dmapext, 1);
	bzero((caddr_t) dme, (uint) sizeof_dmapext);
	dme->dme_daddr = dme_swap;
	return (dme);
}

/*
 * vsrele()
 *	Release all resources associated with a dmap.
 */

vsrele(dmp)
	struct dmap *dmp;
{
	register struct dmapext *dme;

	vsfree(dmp, 0, (size_t)dtoc(maxdmap));
	if (dme = dmp->dm_ext) {		/* "vsdeallocext()" */
		dmp->dm_ext = 0;
		swapfree(sizeof_dmapext_disk, (long) dme->dme_daddr);
		wmemfree((caddr_t) dme, sizeof_dmapext);
	}
}

/*
 * vsswapout()
 *	If necessary, swap out swap-space representation (dmap).
 */

vsswapout(dmp)
	register struct dmap *dmp;
{
	register struct dmapext *dme;

	if (dme = dmp->dm_ext) {
		swap(u.u_procp, dme->dme_daddr,
				&Usrptmap[btokmx((struct pte *)dme)],
				(int) dtob(sizeof_dmapext_disk),
				B_WRITE|B_UAREA, swapdev_vp);
		dmp->dm_daddr = dme->dme_daddr;
		wmemfree((caddr_t) dme, sizeof_dmapext);
	}
}

/*
 * vsswapin()
 *	If necessary, swap in swap-space representation (dmap).
 *
 * Returns 1 for success, 0 for fail.
 */

vsswapin(dmp)
	register struct dmap *dmp;
{
	register struct dmapext *dme;
	register swblk_t dme_swap;

	if ((dme_swap = dmp->dm_daddr) == 0)
		return (1);				/* nothing to do */
	if ((dme = (struct dmapext *)wmemall(sizeof_dmapext, 0)) == NULL)
		return (0);
	swap(u.u_procp, dme_swap, &Usrptmap[btokmx((struct pte *)dme)],
			(int) dtob(sizeof_dmapext_disk),
			B_READ|B_UAREA, swapdev_vp);
	dmp->dm_ext = dme;
	ASSERT(dme->dme_daddr == dme_swap, "vsswapin: skew");
	/*
	 *+ The kernel discovered an inconsistency in the swap space
	 *+ representation maps.  When it swapped in a dmap extension object,
	 *+ the swap address the object was read from didn't match the
	 *+ address stored in the object itself.
	 */
	return (1);
}

/*
 * vsswap()
 *	Swap a segment of virtual memory to disk.
 *
 * Done by locating the contiguous dirty pte's (in Rset and on
 * dirty-list) and calling vschunk with each chunk.
 *
 * Algorithm is awkward, but handles the cases.
 *
 * Returns number of private pages freed from resident-set, and bumps
 * counters of # resident-set pages freed from each possible mapped object.
 */

size_t
vsswap(p, pte, type, vsbase, vscount, dmp, map_used)
	struct	proc	*p;
	register struct pte *pte;
	int		type;
	register int	vsbase;
	int		vscount;
	struct	dmap	*dmp;
	size_t		*map_used;
{
	register struct cmap *c;
	register int size = 0;
	register int skip = 0;
	size_t	priv_used = 0;
	int	incr;

	ASSERT((vscount % CLSIZE) == 0, "vsswap: vscount");
	/*
	 *+ The swapping function was asked to swap a segment of
	 *+ virtual memory to disk, but the number of pages to be
	 *+ written out was not a multiple of the page cluster size.
	 */

	incr = (type == CSTACK) ? -CLSIZE : CLSIZE;

	/*
	 * Look at each page and dispose of it appropriately.
	 * For contiguous sets of dirty pages, reclaim (if necessary)
	 * into process page-table (but not into Rset), then call
	 * vschunk() to write and free these pages.
	 */

	for (; vscount != 0; vscount -= CLSIZE, pte += incr) {

		/*
		 * A 'skipped' page may be end of a contiguous set
		 * of dirty pages.
		 */

		if (skip) {
			if (size) {
				vschunk(p, vsbase, size, type, dmp);
				vsbase += size;
				size = 0;
			}
			vsbase += skip;
			skip = 0;
		}

		/*
		 * FOD page is skipped.
 		 * PG_INVAL page is skipped (implicitly).
		 * Mapped pte is freed, no detach (==> no free u_mmap[]).
		 *	If we do this alot, is more efficient to expand
		 *	vmmapfree() in-line here.
		 * Already paged-out page is skipped.
		 * (order of tests is important!)
		 */

		if (pte->pg_fod) {
			ASSERT_DEBUG(*(int *)pte & PG_FZERO, "vsswap: !ZFOD");
			skip += CLSIZE;
			continue;
		}
		if (PTEMAPPED(*pte)) {
			size_t	map_freed = vmmapfree(p, pte, 0);
			p->p_rssize -= map_freed;
			map_used[PTETOMAPX(*pte)] += map_freed;
			skip += CLSIZE;
			continue;
		}
		if (PTEPF(*pte) == 0) {
			skip += CLSIZE;
			continue;
		}

		/*
		 * If valid and dirty, must write it (decr p_rssize here,
		 * although vschunk() will actually free it).
		 * Valid and non-dirty ==> just free (and skip).
		 */

		if (pte->pg_v) {
			if (anycl(pte, pg_m))
				size += CLSIZE;
			else {
				memfree(pte, CLSIZE, 1);
				skip += CLSIZE;
			}
			--p->p_rssize;
			++priv_used;
			continue;
		}

		/*
		 * Is reclaimable.  Lock memory list and check again.
		 * If still reclaimable...  if dirty, reclaim into
		 * page table ('alloc' so vschunk() can write and free);
		 * otherwise (not dirty) just set 'gone' (could move to
		 * front of free-list).
		 */

		LOCK_MEM;

		if (PTEPF(*pte) == 0) {			/* lost race! */
			UNLOCK_MEM;
			skip += CLSIZE;
			continue;
		}
		c = PTETOCMAP(*pte);

		ASSERT_DEBUG(c->c_refcnt == 0, "vsswap2");
		ASSERT_DEBUG(type == c->c_type, "vsswap3");
		ASSERT_DEBUG(!c->c_holdfod, "vsswap: holdfod");

		if (!c->c_dirty) {
			zap_pfnum(pte);
			c->c_gone = 1;
			skip += CLSIZE;
		} else {
			/*
			 * Can't have concurrent pageout since
			 * pageout is our caller.
			 */
			ASSERT_DEBUG(c->c_pageout == 0, "vsswap4");
			CM_UNLINK_DIRTY(c);
			++c->c_refcnt;
			c->c_dirty = 0;
			size += CLSIZE;
		}
		UNLOCK_MEM;
	}

	/*
	 * If any left, write & free 'em.
	 */

	if (size)
		vschunk(p, vsbase, size, type, dmp);

	return(priv_used);
}

/*
 * vschunk()
 *	Write a (virtually) contiguous chunk of a process segment
 *	to swap space.
 *
 * Caller insures pages are not reallocatable during this.
 */

static
vschunk(p, base, size, type, dmp)
	register struct proc *p;
	register int base;
	register int size;
	int	type;
	struct dmap *dmp;
{
	register struct pte *pte;
	struct dblock db;

	base = ctod(base);
	size = ctod(size);

	do {
		/*
		 * Figure max contiguous blocks that can write.
		 * vstodb() can't race with process releasing space since
		 * process is guaranteed not executing.
		 */
		vstodb(base, size, dmp, &db, type == CSTACK);
		if (type == CSTACK)
			pte = sptopte(p, dtoc(base+db.db_size)-1);
		else
			pte = dptopte(p, dtoc(base));
		/*
		 * If failed (dynamic allocation and no space), kill the
		 * process (hard to do anything else here), free the memory,
		 * and re-fill with ZFOD's (to insure process won't try to
		 * page in from it).  In this case could use PG_INVAL, but
		 * should be consistent (see pageout()).
		 *
		 * Otherwise (typical), write the pages and free them.
		 */
		if (db.db_base == 0) {
			memfree(pte, (int)dtoc(db.db_size), 1);
			ptefill(pte, PG_ZFOD, (int)dtoc(db.db_size));
			no_space_swkill(p);
		} else {
			swap(p, db.db_base, pte, (int)dtob(db.db_size),
							B_WRITE, swapdev_vp);
			memfree(pte, (int)dtoc(db.db_size), 1);
		}
		base += db.db_size;
		size -= db.db_size;
	} while (size != 0);
}

/*
 * vstodb()
 *	Given a base/size pair in virtual swap area,
 *	return a physical base/size pair which is the
 *	(largest) initial, physically contiguous block.
 *
 * Called in this module and by pageout().
 *
 * If dynamic swap space allocation is enabled (dynamic_swap_space), allocate
 * space if not already there.  Return dbp->db_base == 0 if this fails, but
 * always return the size it should have been (some callers need this; eg,
 * vschunk()).
 *
 * The interface of this procedure is poor.  It should return the sector
 * number and (optionally) the size.  Most callers aren't interested in
 * the size number.
 *
 * A CLONE OF THIS PROCEDURE IS USED IN ps, gcore, w, AND (maybe)
 * OTHER UTILITIES.  IF IT CHANGES, CHECK THEIR USAGE.
 */

vstodb(vsbase, vssize, dmp, dbp, rev)
	register int	vsbase;
	register int	vssize;
	struct dmap	*dmp;
	register struct dblock *dbp;
	int		rev;
{
	register swblk_t *ip;
	register long blk;

	ASSERT_DEBUG(vsbase >= 0 && vssize >= 0, "vstodb");

	if (dmp->dm_ext)
		ip = dmp->dm_ext->dme_map;
	else
		ip = dmp->dm_map;

	blk = dmmin;
	while (vsbase >= blk) {
		ip++;
		vsbase -= blk;
		if (blk < dmmax)
			blk *= 2;
		else {				/* reached constant size blks */
			ip += (vsbase / blk);
			vsbase %= blk;
			break;
		}
	}

	/*
	 * Determine size of actual data.  Even if fail, some callers want
	 * to know this (eg, vschunk()).
	 */

	dbp->db_size = imin(vssize, (int)blk - vsbase);

	/*
	 * If no space allocated, attempt to allocate.  Update number
	 * of entries in extension object if now extended.  See comments
	 * in pageout() about why this can't race with process exit.
	 */

	if (*ip == 0) {
		struct dmapext *dme;
		ASSERT(dynamic_swap_space, "vstodb: !dynamic_swap_space");
		/*
		 *+ When getting the swap location for a piece of virtual
		 *+ memory, the kernel found an unallocated swap block in a
		 *+ process's dmap structure.
		 *+ Dynamic allocation of swap blocks is not enabled.
		 */
		*ip = swapalloc(blk);
		if (*ip == 0) {
			/*
			 * Fail.  db_size set above, return db_base == 0
			 * to indicate error.
			 */
			dbp->db_base = 0;
			return;
		}
		if ((dme = dmp->dm_ext) && ip >= &dme->dme_map[dme->dme_nent])
			dme->dme_nent = (ip - dme->dme_map) + 1;
	}

	ASSERT(*ip + blk <= nswap, "vstodb: *ip");
	/*
	 *+ When getting the swap location for a piece of virtual
	 *+ memory, the kernel found an invalid swap block address 
	 *+ in the process's dmap structure.
	 */

	/*
	 * Determine 'base' sector, depending on whether space is reversed
	 * (stack) or not.
	 */

	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
	ASSERT_DEBUG((dbp->db_size&((NBPG/DEV_BSIZE)-1))==0, "vstodb: db_size");
	ASSERT_DEBUG((dbp->db_base&((NBPG/DEV_BSIZE)-1))==0, "vstodb: db_base");
}

/*
 * no_space_swkill()
 *	swkill() a process due to lack of dynamic swap space.
 */

no_space_swkill(p)
	struct	proc	*p;
{
	swkill(p, "lack of dynamic swap space");
}
