Coherent4.2.10/coh.386/seg.c

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

/* $Header: /ker/coh.386/RCS/seg.c,v 2.6 93/10/29 00:55:34 nigel Exp Locker: nigel $ */
/* (lgl-
 *	The information contained herein is a trade secret of Mark Williams
 *	Company, and  is confidential information.  It is provided  under a
 *	license agreement,  and may be  copied or disclosed  only under the
 *	terms of  that agreement.  Any  reproduction or disclosure  of this
 *	material without the express written authorization of Mark Williams
 *	Company or persuant to the license agreement is unlawful.
 *
 *	COHERENT Version 2.3.37
 *	Copyright (c) 1982, 1983, 1984.
 *	An unpublished work by Mark Williams Company, Chicago.
 *	All rights reserved.
 -lgl) */
/*
 * Coherent.
 * Segment manipulation.
 *
 * $Log:	seg.c,v $
 * Revision 2.6  93/10/29  00:55:34  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.5  93/09/02  18:08:01  nigel
 * Prepare for DDI/DKI merge
 * 
 * Revision 2.4  93/08/19  03:26:45  nigel
 * Nigel's r83 (Stylistic cleanup)
 */

#include <sys/errno.h>
#include <stddef.h>

#define	_KERNEL		1

#include <kernel/alloc.h>
#include <kernel/trace.h>
#include <kernel/reg.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/buf.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/seg.h>
#include <a.out.h>


/*
 * NIGEL: Wrap up the garbage before we take it out.
 */
 
__DUMB_GATE	__seglink = __GATE_DECLARE ("segment list");

#define	__LOCK_SEGMENT_LIST(where) \
		(__GATE_LOCK (__seglink, "lock : seglink " where))
#define	__UNLOCK_SEGMENT_LIST() \
		(__GATE_UNLOCK (__seglink))

SEG	segmq;				/* seg.h */

#define	min(a, b)	((a) < (b) ? (a) : (b))

/*
 * Initialisation code.
 */

void
seginit()
{
	/*
	 * Create empty circular-list of memory segments.
	 */

	segmq.s_forw = & segmq;
	segmq.s_back = & segmq;
}


/*
 * Given an inode, `ip', and flags, `ff', describing a segment associated
 * with the inode, see if the segment already exists and if so, return a
 * copy.  If the segment does not exist, allocate the segment having size
 * `ss', and read the segment using the inode at seek offset `dq' with a
 * size of `ds'.
 */

SEG *
ssalloc(ip, ff, ss)
INODE *ip;
int ff;
int ss;
{
	SEG *sp;
	int f;

	__LOCK_SEGMENT_LIST ("ssalloc ()");

	f = ff & (SFSHRX | SFTEXT);

	/*
	 * Look for the segment in the memory queue.
	 */

	for (sp = segmq.s_forw ; sp != & segmq ; sp = sp->s_forw) {

		if (sp->s_ip == ip &&
		    (sp->s_flags & (SFSHRX | SFTEXT)) == f) {

			__UNLOCK_SEGMENT_LIST ();
			return segdupl (sp);
		}
	}
	__UNLOCK_SEGMENT_LIST ();

	/*
	 * Allocate and create the segment.
	 */

	return salloc (__ROUND_UP_TO_MULTIPLE (ss, NBPC), ff);
}


/*
 * Free the given segment pointer.
 */

void
sfree (sp)
SEG *sp;
{
	INODE *ip;

	if (sp->s_urefc != 1) {
		sp->s_urefc --;
		sp->s_lrefc --;
		return;
	}

	__LOCK_SEGMENT_LIST ("sfree ()");

	-- sp->s_lrefc;

	sp->s_back->s_forw = sp->s_forw;
	sp->s_forw->s_back = sp->s_back;

	c_free (sp->s_vmem, btocru (sp->s_size));

	__UNLOCK_SEGMENT_LIST ();

	if (sp->s_lrefc)
		panic ("Bad segment count");

	/*
	 * Check if inode is ilocked, in order to allow the process
	 * to exec itself (file with the same inode as parent). Vlad.
	 */

	if ((ip = sp->s_ip) != NULL && ! ilocked (ip))
		ldetach (ip);

	kfree (sp);
}


/*
 * Given a pointer to a newly created process, copy all of our segments
 * into the given process.
 *
 * Return nonzero if successful.
 */

int
segadup (cpp)
PROC *cpp;
{
	SEG * sp;
	int n;

	cpp->p_flags |= PFSWIO;

	for (n = 0 ; n < NUSEG ; n ++) {
		cpp->p_segl [n] = SELF->p_segl [n];

		if ((sp = SELF->p_segl [n].sr_segp) == NULL)
			continue;

		if ((sp = segdupl (sp)) == NULL)
			break;

		cpp->p_segl [n].sr_segp = sp;
		if ((sp->s_flags & SFCORE) == 0)
			cpp->p_flags &= ~ PFCORE;
	}

	/*
	 * One of the calls to segdupl() failed.
	 * Undo any that succeeded.
	 */

	if (n < NUSEG) {


		/*
		 * If segdupl () fails on first segment, sr_segp needs
		 * to be cleared or relproc () would cause parent to
		 * nuke itself.
		 */
		if (n == 0)
			cpp->p_segl [0].sr_segp = NULL;
			
		while (n > 0) {
			if ((sp = cpp->p_segl [-- n].sr_segp) != NULL) {
				cpp->p_segl [n].sr_segp = NULL;
				sfree (sp);
			}
		}
	}

	cpp->p_flags &= ~ PFSWIO;
	return n;
}


/*
 * Duplicate a segment.
 */

SEG *
segdupl(sp)
SEG *sp;
{
	SEG *sp1;

	if (sp->s_flags & SFSHRX) {
		sp->s_urefc ++;
		sp->s_lrefc ++;
		return sp;
	}
	if ((sp->s_flags & SFCORE) == 0)
		panic("Cannot duplicate non shared swapped segment");

	if ((sp1 = salloc (sp->s_size,
			   sp->s_flags | SFNSWP | SFNCLR)) == NULL)
		return NULL;

	sp1->s_flags = sp->s_flags;
	dmacopy (btocru (sp->s_size), sp->s_vmem, sp1->s_vmem);

	return sp1;
}


/*
 * Allocate a segment `bytes_wanted' bytes long.
 * `flags' contains some pseudo flags.
 */

SEG *
salloc (bytes_wanted, flags)
int bytes_wanted, flags;
{
	SEG *sp;

	if ((sp = smalloc (bytes_wanted)) == NULL)
		return NULL;

	sp->s_flags = (flags & (SFSYST | SFTEXT | SFSHRX | SFDOWN)) | SFCORE;

	if ((flags & SFNCLR) == 0)
		dmaclear (sp->s_size, MAPIO (sp->s_vmem, 0));

	return sp;
}


/*
 * Make the segment descriptor pointed to by `sp1' have the attributes
 * of `sp2' including it's position in the segment queue and release
 * `sp2'.  `seglink' must be locked when this routine is called.
 */

void
satcopy(sp1, sp2)
SEG *sp1;
SEG *sp2;
{
	sp1->s_back->s_forw = sp1->s_forw;
	sp1->s_forw->s_back = sp1->s_back;
	sp2->s_back->s_forw = sp1;
	sp1->s_back = sp2->s_back;
	sp2->s_forw->s_back = sp1;
	sp1->s_forw  = sp2->s_forw;
	sp1->s_daddr = sp2->s_daddr;
	sp1->s_size = sp2->s_size;
	sp1->s_vmem = sp2->s_vmem;
	kfree (sp2);
}


/*
 * Grow or shrink the segment `sp' so that it has size `new_bytes' bytes.
 * Return 1 on success, 0 on failure.
 * 
 * WARNING: Downward growing segments (like user stack) not done yet!
 */

int
seggrow(sp, new_bytes)
SEG *sp;
unsigned int new_bytes;
{
	SEG *sp1;
	int dowflag;
	unsigned int	old_bytes, common_pages;

	dowflag = sp->s_flags & SFDOWN;
	old_bytes = sp->s_size;

	/* Get rid of degenerate case. */
	if (new_bytes == old_bytes)
		return 1;

	/*
	 * If we want a larger segment AND c_grow() succeeds
	 *	boost segment size to new_bytes
	 */

	if (new_bytes >= old_bytes && c_grow (sp, new_bytes) == 0) {

		T_HAL(0x100, printf("c_grow(%d) ", new_bytes));

		sp->s_size = new_bytes;
		dmaclear (new_bytes - old_bytes,
			  MAPIO (sp->s_vmem, old_bytes));
		return 1;
	}

	if ((sp1 = salloc (new_bytes,
			   sp->s_flags | SFNSWP | SFNCLR)) != NULL) {

		T_HAL(0x100, printf("salloc(%d) ", new_bytes));
		if (dowflag == 0) {
			common_pages = btocru (min (new_bytes, old_bytes));
			dmacopy (common_pages, sp->s_vmem, sp1->s_vmem);
			if (new_bytes > old_bytes)
				dmaclear (new_bytes - old_bytes,
					  MAPIO (sp1->s_vmem, old_bytes));
		} else
			panic ("downflag");

		__LOCK_SEGMENT_LIST ("seggrow ()");

		c_free (sp->s_vmem, btocru (old_bytes));
		satcopy (sp, sp1);

		__UNLOCK_SEGMENT_LIST ();

		return 1;
	}

	return 0;
}


/*
 * Given a segment pointer, `sp' and a segment size, grow the given segment
 * to the given size.
 */

void
segsize(sp, s2)
SEG *sp;
caddr_t s2;
{
	caddr_t s1;

	s1 = (caddr_t) sp->s_size;
	if (s2 == 0 || seggrow (sp, (off_t) s2) == 0) {
		SET_U_ERROR (ENOMEM, "can not grow segment");
		return;
	}

	if (sproto (0) == 0) {
		if (seggrow (sp, (off_t) s1) == 0 || sproto (0) == 0) {

			T_PIGGY (0x2000000, printf("auto SEGV\n"));
			sendsig (SIGSEGV, SELF);
		}
	}
	segload ();
}


/*
 * Allocate a segment in memory that is `bytes_wanted' bytes long.
 * The `seglink' gate should be locked before this routine is called.
 *
 * if successful, return allocated SEG * else, return 0
 *
 * NIGEL: This routine is actually only called from salloc (), whose callers
 * expect a completely initialized structure (or so it seems). Let's do that
 * initialization rather than expecting kalloc () to have accidentally done
 * the job. Furthermore, this routine is specially set up to only work for the
 * _I386 version of the data structures.
 */

SEG *
smalloc (bytes_wanted)
off_t bytes_wanted;
{
	SEG *sp1;
	SEG *new_seg;
	unsigned	pages_wanted;

	pages_wanted = btocru (bytes_wanted);

	/*
	 * Estimate space needed for new segment and its overhead.
	 * Fail if not enough free RAM available.
	 */

	if (countsize (pages_wanted) > allocno ())
		return 0;

	/*
	 * Allocate a new SEG struct to keep track of the segment, if possible.
	 */

	if ((new_seg = kalloc (sizeof (SEG))) == NULL)
		return 0;

	if ((new_seg->s_vmem = c_alloc (pages_wanted)) == NULL) {
		kfree (new_seg);
		return 0;
	}

	/* link new_seg in at start of segmq */

	__LOCK_SEGMENT_LIST ("smalloc ()");

	sp1 = segmq.s_forw;
	sp1->s_back->s_forw = new_seg;
	new_seg->s_back = sp1->s_back;
	sp1->s_back = new_seg;
	new_seg->s_forw = sp1;

	new_seg->s_urefc = 1;
	new_seg->s_lrefc = 1;
	new_seg->s_size  = bytes_wanted;

	new_seg->s_ip = NULL;
	new_seg->s_daddr = 0;

	__UNLOCK_SEGMENT_LIST ();

	return new_seg;
}


/*
 * Set up `SR' structure in user area from segments descriptors in
 * process structure.  Also set up the user segmentation registers.
 */

int
sproto (xhp)
struct xechdr *xhp;
{
	int n;
	SEG *sp;

	for (n = 0 ; n < NUSEG ; n ++) {
		SELF->p_segl [n].sr_flag = SELF->p_segl [n].sr_size = 0;
		if ((sp = SELF->p_segl [n].sr_segp) == NULL)
			continue;

		if (n == SIUSERP)
			SELF->p_segl [n].sr_base = (caddr_t) & u;
		else {
			if (xhp)
				SELF->p_segl [n].sr_base = (caddr_t) xhp->segs [n].mbase;
			SELF->p_segl [n].sr_flag |= SRFPMAP;
		}
		if (n != SIUSERP && n != SISTEXT)
			SELF->p_segl [n].sr_flag |= (SRFDATA | SRFDUMP);
		SELF->p_segl [n].sr_size = sp->s_size;
	}
	return mproto ();
}


/*
 * Search for a busy text inode.
 */

int
sbusy(ip)
struct inode *ip;
{
	SEG *sp;

	__LOCK_SEGMENT_LIST ("sbusy ()");

	/*
	 * Look for the segment in the memory queue.
	 */

	for (sp = segmq.s_forw ; sp != & segmq ; sp = sp->s_forw) {
		if (sp->s_ip == ip &&
		    (sp->s_flags & (SFSHRX | SFTEXT)) == (SFSHRX | SFTEXT)) {
			__UNLOCK_SEGMENT_LIST ();
			return 1;
		}
	}

	__UNLOCK_SEGMENT_LIST ();
	return 0;
}