BSD 4 development
authorBill Joy <wnj@ucbvax.Berkeley.EDU>
Sun, 9 Nov 1980 16:07:06 +0000 (08:07 -0800)
committerBill Joy <wnj@ucbvax.Berkeley.EDU>
Sun, 9 Nov 1980 16:07:06 +0000 (08:07 -0800)
Work on file usr/src/sys/sys/vmproc.c

Synthesized-from: CSRG//cd1/4.0

usr/src/sys/sys/vmproc.c [new file with mode: 0644]

diff --git a/usr/src/sys/sys/vmproc.c b/usr/src/sys/sys/vmproc.c
new file mode 100644 (file)
index 0000000..8532545
--- /dev/null
@@ -0,0 +1,374 @@
+/*     vmproc.c        4.2     11/9/80 */
+
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/dir.h"
+#include "../h/user.h"
+#include "../h/proc.h"
+#include "../h/mtpr.h"
+#include "../h/pte.h"
+#include "../h/map.h"
+#include "../h/cmap.h"
+#include "../h/text.h"
+#include "../h/vm.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)
+       size_t ts, ds, ss;
+{
+
+       mtpr(P0LR, ts);
+       u.u_pcb.pcb_p0lr = ts | AST;
+       mtpr(P1LR, P1TOP - UPAGES);
+       u.u_pcb.pcb_p1lr = P1TOP - UPAGES;
+       u.u_procp->p_tsize = ts;
+       u.u_tsize = ts;
+       expand((int)ss, P1BR);
+       expand((int)ds, P0BR);
+}
+
+/*
+ * 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;
+       register int n;
+       
+       /*
+        * Release memory; text first, then data and stack pages.
+        */
+       xfree();
+       p->p_rssize -= vmemfree(dptopte(p, 0), p->p_dsize);
+       p->p_rssize -= vmemfree(sptopte(p, p->p_ssize - 1), 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((size_t)0, &u.u_dmap, 1);
+       (void) vsexpand((size_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;
+       /*
+        * Consistency check that there are no mapped pages left.
+        */
+       for (n = 0; n < NOFILE; n++)
+               if (u.u_vrpages[n])
+                       panic("vrelvm vrpages");
+}
+
+/*
+ * 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;
+
+       /*
+        * 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_vm = up->u_vm; up->u_vm = zvms;
+       uq->u_cvm = up->u_cvm; up->u_cvm = zvms;
+
+       /*
+        * 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);
+}
+
+/*
+ * 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)
+{
+       register struct proc *p;
+       register struct pte *base, *p0, *p1;
+       struct pte proto;
+       int a0, a1;
+
+       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.
+        */
+       if (region == P0BR) {
+               p->p_dsize += change;
+               u.u_dsize += change;
+       } else {
+               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.
+        */
+       p0 = (struct pte *)mfpr(P0BR) + mfpr(P0LR);
+       p1 = (struct pte *)mfpr(P1BR) + mfpr(P1LR);
+       if (change > p1 - p0)
+               ptexpand(clrnd(ctopt(change - (p1 - p0))));
+       /* PTEXPAND SHOULD GIVE BACK EXCESS PAGE TABLE PAGES */
+
+       /*
+        * Compute the base of the allocated/freed region.
+        */
+       if (region == P0BR) {
+               base = (struct pte *)mfpr(P0BR);
+               base += (a0 = mfpr(P0LR)) + (change > 0 ? 0 : change);
+       } else {
+               base = (struct pte *)mfpr(P1BR);
+               base += (a1 = mfpr(P1LR)) - (change > 0 ? change : 0);
+       }
+
+       /*
+        * If we shrunk, 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 == P0BR)  {
+               mtpr(P0LR, a0 + change);
+               u.u_pcb.pcb_p0lr = a0 + change | AST;
+       } else {
+               mtpr(P1LR, a1 - change);
+               u.u_pcb.pcb_p1lr = a1 - change;
+       }
+
+       /*
+        * 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;
+       }
+       while (--change >= 0)
+               *base++ = proto;
+
+       /*
+        * We changed mapping for the current process,
+        * so must flush the translation buffer.
+        */
+       mtpr(TBIA,0);
+}
+
+/*
+ * 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;
+{
+       register int n;
+
+       /*
+        * 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);
+
+       /*
+        * Arrange for a non-local goto when the new process
+        * is started, to resume here, returning nonzero from setjmp.
+        */
+       forkutl.u_pcb.pcb_sswap = u.u_ssav;
+       if (setjmp(forkutl.u_ssav))
+               /*
+                * 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) {
+               forkutl.u_pcb.pcb_p0lr = AST;
+               forkutl.u_pcb.pcb_p1lr = P1TOP - UPAGES;
+               vpassvm(u.u_procp, p, &u, &forkutl, Forkmap);
+               /*
+                * Add extra mapping references to files which
+                * have been vread() so that they can't be closed
+                * during the vork().
+                */
+               for (n = 0; n < NOFILE; n++)
+                       if (forkutl.u_vrpages[n] != 0)
+                               forkutl.u_vrpages[n]++;
+               /*
+                * 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().
+        */
+       forkutl.u_vm = zvms;
+       forkutl.u_cvm = zvms;
+       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);
+       }
+
+       /*
+        * 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 size_t count;
+       int type;
+{
+       register struct pte *opte = vtopte(u.u_procp, v);
+       register int i;
+
+       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;
+               munlock(pte->pg_pfnum);
+               pte += CLSIZE;
+       }
+}
+
+/*
+ * Check that a process will not be too large.
+ */
+chksize(ts, ds, ss)
+       size_t ts, ds, ss;
+{
+
+       if (ts>MAXTSIZ || ds>MAXDSIZ || ss>MAXSSIZ) {
+               u.u_error = ENOMEM;
+               return(1);
+       }
+       return (0);
+}