|
|
BSD 4.3reno
/*
* 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_proc.c 7.11 (Berkeley) 6/30/90
*/
#include "param.h"
#include "systm.h"
#include "user.h"
#include "proc.h"
#include "map.h"
#include "cmap.h"
#include "text.h"
#include "vm.h"
#include "machine/pte.h"
#include "machine/mtpr.h"
#include "machine/cpu.h"
/*
* Get virtual memory resources for a new process.
* Called after page tables are allocated, but before they
* are initialized, we initialize the memory management registers,
* and then expand the page tables for the data and stack segments
* creating zero fill pte's there. Text pte's are set up elsewhere.
*
* SHOULD FREE EXTRA PAGE TABLE PAGES HERE OR SOMEWHERE.
*/
vgetvm(ts, ds, ss)
segsz_t ts, ds, ss;
{
#if defined(vax)
u.u_pcb.pcb_p0lr = AST_NONE;
setp0lr(ts);
setp1lr(P1PAGES - HIGHPAGES);
#endif
#if defined(tahoe)
setp0lr(ts);
setp1lr(0);
setp2lr(P2PAGES - HIGHPAGES);
#endif
#if defined(hp300) || defined(i386)
setp0lr(LOWPAGES + ts);
setp1lr(P1PAGES - HIGHPAGES);
#endif
u.u_procp->p_tsize = ts;
u.u_tsize = ts;
expand((int)ss, 1);
expand((int)ds, 0);
}
/*
* Release the virtual memory resources (memory
* pages, and swap area) associated with the current process.
* Caller must not be swappable. Used at exit or execl.
*/
vrelvm()
{
register struct proc *p = u.u_procp;
/*
* Release memory; text first, then data and stack pages.
*/
xfree();
p->p_rssize -= vmemfree(dptopte(p, 0), (int)p->p_dsize);
p->p_rssize -= vmemfree(sptopte(p, p->p_ssize - 1), (int)p->p_ssize);
if (p->p_rssize != 0)
panic("vrelvm rss");
/*
* Wait for all page outs to complete, then
* release swap space.
*/
p->p_swrss = 0;
while (p->p_poip)
sleep((caddr_t)&p->p_poip, PSWP+1);
(void) vsexpand((segsz_t)0, &u.u_dmap, 1);
(void) vsexpand((segsz_t)0, &u.u_smap, 1);
p->p_tsize = 0;
p->p_dsize = 0;
p->p_ssize = 0;
u.u_tsize = 0;
u.u_dsize = 0;
u.u_ssize = 0;
}
/*
* Pass virtual memory resources from p to q.
* P's u. area is up, q's is uq. Used internally
* when starting/ending a vfork().
*/
vpassvm(p, q, up, uq, umap)
register struct proc *p, *q;
register struct user *up, *uq;
struct pte *umap;
{
/*
* Pass fields related to vm sizes.
*/
uq->u_tsize = q->p_tsize = p->p_tsize; up->u_tsize = p->p_tsize = 0;
uq->u_dsize = q->p_dsize = p->p_dsize; up->u_dsize = p->p_dsize = 0;
uq->u_ssize = q->p_ssize = p->p_ssize; up->u_ssize = p->p_ssize = 0;
q->p_mmsize = p->p_mmsize; p->p_mmsize = 0;
/*
* Pass proc table paging statistics.
*/
q->p_swrss = p->p_swrss; p->p_swrss = 0;
q->p_rssize = p->p_rssize; p->p_rssize = 0;
q->p_poip = p->p_poip; p->p_poip = 0;
/*
* Relink text segment.
*/
q->p_textp = p->p_textp;
xrepl(p, q);
p->p_textp = 0;
/*
* Pass swap space maps.
*/
uq->u_dmap = up->u_dmap; up->u_dmap = zdmap;
uq->u_smap = up->u_smap; up->u_smap = zdmap;
/*
* Pass u. paging statistics.
*/
uq->u_outime = up->u_outime; up->u_outime = 0;
uq->u_ru = up->u_ru;
bzero((caddr_t)&up->u_ru, sizeof (struct rusage));
uq->u_cru = up->u_cru;
bzero((caddr_t)&up->u_cru, sizeof (struct rusage));
/*
* And finally, pass the page tables themselves.
* On return we are running on the other set of
* page tables, but still with the same u. area.
*/
vpasspt(p, q, up, uq, umap);
#ifdef MAPMEM
/*
* Exchange mapped memory resources.
*/
if (up->u_mmap)
mmvfork(up, uq);
#endif
}
/*
* Change the size of the data+stack regions of the process.
* If the size is shrinking, it's easy-- just release virtual memory.
* If it's growing, initalize new page table entries as
* 'zero fill on demand' pages.
*/
expand(change, region)
int change, region;
{
register struct proc *p;
#if defined(vax)
register struct pte *base, *p0, *p1;
int p0lr, p1lr;
#endif
#if defined(tahoe)
register struct pte *base, *p0, *p2;
int p0lr, p2lr;
#endif
#if defined(hp300) || defined(i386)
register struct pte *base;
int p0lr, p1lr;
#endif
struct pte proto;
struct pte *base0;
segsz_t ods, oss;
int size;
u_int v;
p = u.u_procp;
if (change == 0)
return;
if (change % CLSIZE)
panic("expand");
#ifdef PGINPROF
vmsizmon();
#endif
/*
* Update the sizes to reflect the change. Note that we may
* swap as a result of a ptexpand, but this will work, because
* the routines which swap out will get the current text and data
* sizes from the arguments they are passed, and when the process
* resumes the lengths in the proc structure are used to
* build the new page tables.
*/
ods = u.u_dsize;
oss = u.u_ssize;
if (region == 0) {
v = dptov(p, change > 0 ? p->p_dsize : p->p_dsize+change);
p->p_dsize += change;
u.u_dsize += change;
} else {
v = sptov(p, change > 0 ? p->p_ssize-1+change : p->p_ssize-1);
p->p_ssize += change;
u.u_ssize += change;
}
/*
* Compute the end of the text+data regions and the beginning
* of the stack region in the page tables,
* and expand the page tables if necessary.
*/
#if defined(vax)
p0 = u.u_pcb.pcb_p0br + (u.u_pcb.pcb_p0lr&~AST_CLR);
p1 = u.u_pcb.pcb_p1br + (u.u_pcb.pcb_p1lr&~PME_CLR);
if (change > p1 - p0)
ptexpand(clrnd(ctopt(change - (p1 - p0))), ods, p->p_mmsize, oss);
#endif
#if defined(tahoe)
p0 = u.u_pcb.pcb_p0br + u.u_pcb.pcb_p0lr;
p2 = u.u_pcb.pcb_p2br + u.u_pcb.pcb_p2lr;
if (change > p2 - p0)
ptexpand(clrnd(ctopt(change - (p2 - p0))), ods, p->p_mmsize, oss);
#endif
#if defined(hp300) || defined(i386)
size = ptsize(p) - u.u_pcb.pcb_szpt;
if (size > 0)
ptexpand(size, ods, p->p_mmsize, oss);
#endif
/* PTEXPAND SHOULD GIVE BACK EXCESS PAGE TABLE PAGES */
/*
* Compute the base of the allocated/freed region.
*/
#if defined(vax)
p0lr = u.u_pcb.pcb_p0lr&~AST_CLR;
p1lr = u.u_pcb.pcb_p1lr&~PME_CLR;
#endif
#if defined(tahoe)
p0lr = u.u_pcb.pcb_p0lr;
p2lr = u.u_pcb.pcb_p2lr;
#endif
#if defined(hp300) || defined(i386)
p0lr = u.u_pcb.pcb_p0lr;
p1lr = u.u_pcb.pcb_p1lr;
#endif
if (region == 0) {
#ifdef MAPMEM
size = dptov(u.u_procp, ods);
#else
size = p0lr;
#endif
base = u.u_pcb.pcb_p0br + size + (change > 0 ? 0 : change);
} else {
#if defined(vax) || defined(hp300) || defined(i386)
size = p1lr;
base = u.u_pcb.pcb_p1br + size - (change > 0 ? change : 0);
#endif
#if defined(tahoe)
size = p2lr;
base = u.u_pcb.pcb_p2br + size - (change > 0 ? change : 0);
#endif
}
/*
* If we shrank, give back the virtual memory.
*/
if (change < 0)
p->p_rssize -= vmemfree(base, -change);
/*
* Update the processor length registers and copies in the pcb.
*/
if (region == 0) {
if (u.u_procp->p_mmsize == 0)
setp0lr(size + change);
} else
#if defined(vax) || defined(hp300) || defined(i386)
setp1lr(size - change);
#endif
#if defined(tahoe)
setp2lr(size - change);
#endif
/*
* If shrinking, clear pte's, otherwise
* initialize zero fill on demand pte's.
*/
*(int *)&proto = PG_UW;
if (change < 0)
change = -change;
else {
proto.pg_fod = 1;
((struct fpte *)&proto)->pg_fileno = PG_FZERO;
cnt.v_nzfod += change;
}
base0 = base;
size = change;
while (--change >= 0)
*base++ = proto;
/*
* We changed mapping for the current process,
* so must update the hardware translation
*/
newptes(base0, v, size);
}
/*
* Create a duplicate copy of the current process
* in process slot p, which has been partially initialized
* by newproc().
*
* Could deadlock here if two large proc's get page tables
* and then each gets part of his UPAGES if they then have
* consumed all the available memory. This can only happen when
* USRPTSIZE + UPAGES * NPROC > maxmem
* which is impossible except on systems with tiny real memories,
* when large procs stupidly fork() instead of vfork().
*/
procdup(p, isvfork)
register struct proc *p;
{
/*
* Allocate page tables for new process, waiting
* for memory to be free.
*/
while (vgetpt(p, vmemall) == 0) {
kmapwnt++;
sleep((caddr_t)kernelmap, PSWP+4);
}
/*
* Snapshot the current u. area pcb and get a u.
* for the new process, a copy of our u.
*/
resume(pcbb(u.u_procp));
(void) vgetu(p, vmemall, Forkmap, &forkutl, &u);
#if defined(tahoe)
/*
* Define new cache data key.
*/
ckey_cnt[forkutl.u_procp->p_ckey]++;
forkutl.u_procp->p_dkey = getdatakey();
forkutl.u_pcb.pcb_savacc.faddr = (float *)NULL;
#endif
/*
* Arrange for a non-local goto when the new process
* is started, to resume here, returning nonzero from setjmp.
*/
forkutl.u_pcb.pcb_sswap = (int *)&u.u_ssave;
if (savectx(&forkutl.u_ssave)) {
#ifdef MAPMEM
if ((u.u_procp->p_flag & SVFORK) == 0 && u.u_mmap)
(void) mmfork((struct user *)0, &u);
#endif
/*
* Return 1 in child.
*/
return (1);
}
/*
* If the new process is being created in vfork(), then
* exchange vm resources with it. We want to end up with
* just a u. area and an empty p0 region, so initialize the
* prototypes in the other area before the exchange.
*/
if (isvfork) {
#if defined(vax)
forkutl.u_pcb.pcb_p0lr = u.u_pcb.pcb_p0lr & AST_CLR;
forkutl.u_pcb.pcb_p1lr = P1PAGES - HIGHPAGES;
#endif
#if defined(tahoe)
forkutl.u_pcb.pcb_p0lr = u.u_pcb.pcb_p0lr;
forkutl.u_pcb.pcb_p1lr = 0;
forkutl.u_pcb.pcb_p2lr = P2PAGES - HIGHPAGES;
#endif
#if defined(hp300) || defined(i386)
forkutl.u_pcb.pcb_p0lr = 0;
forkutl.u_pcb.pcb_p1lr = P1PAGES - HIGHPAGES;
#endif
vpassvm(u.u_procp, p, &u, &forkutl, Forkmap);
/*
* Return 0 in parent.
*/
return (0);
}
/*
* A real fork; clear vm statistics of new process
* and link into the new text segment.
* Equivalent things happen during vfork() in vpassvm().
*/
bzero((caddr_t)&forkutl.u_ru, sizeof (struct rusage));
bzero((caddr_t)&forkutl.u_cru, sizeof (struct rusage));
forkutl.u_dmap = u.u_cdmap;
forkutl.u_smap = u.u_csmap;
forkutl.u_outime = 0;
/*
* Attach to the text segment.
*/
if (p->p_textp) {
p->p_textp->x_count++;
xlink(p);
}
#ifdef MAPMEM
/*
* We do this before duplicating the address space since vmdup()
* might block causing Forkmap/forkutl to become invalid
*/
if (u.u_mmap)
(void) mmfork(&u, &forkutl);
#endif
/*
* Duplicate data and stack space of current process
* in the new process.
*/
vmdup(p, dptopte(p, 0), dptov(p, 0), p->p_dsize, CDATA);
vmdup(p, sptopte(p, p->p_ssize - 1), sptov(p, p->p_ssize - 1), p->p_ssize, CSTACK);
/*
* Return 0 in parent.
*/
return (0);
}
vmdup(p, pte, v, count, type)
struct proc *p;
register struct pte *pte;
register unsigned v;
register segsz_t count;
int type;
{
register struct pte *opte = vtopte(u.u_procp, v);
register int i;
register struct cmap *c;
while (count != 0) {
count -= CLSIZE;
if (opte->pg_fod) {
v += CLSIZE;
for (i = 0; i < CLSIZE; i++)
*(int *)pte++ = *(int *)opte++;
continue;
}
opte += CLSIZE;
(void) vmemall(pte, CLSIZE, p, type);
p->p_rssize += CLSIZE;
for (i = 0; i < CLSIZE; i++) {
copyseg((caddr_t)ctob(v+i), (pte+i)->pg_pfnum);
*(int *)(pte+i) |= (PG_V|PG_M) + PG_UW;
}
v += CLSIZE;
c = &cmap[pgtocm(pte->pg_pfnum)];
MUNLOCK(c);
pte += CLSIZE;
}
p->p_flag |= SPTECHG;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.