From 2a037d7fbb8202fa3f26856cbf2a6f6c6f536423 Mon Sep 17 00:00:00 2001 From: CSRG Date: Mon, 26 Sep 1983 07:06:30 -0800 Subject: [PATCH] BSD 4_2 development Work on file usr/src/sys/sys/vm_page.c Synthesized-from: CSRG/cd1/4.2 --- usr/src/sys/sys/vm_page.c | 1058 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1058 insertions(+) create mode 100644 usr/src/sys/sys/vm_page.c diff --git a/usr/src/sys/sys/vm_page.c b/usr/src/sys/sys/vm_page.c new file mode 100644 index 0000000000..d7c6d9be25 --- /dev/null +++ b/usr/src/sys/sys/vm_page.c @@ -0,0 +1,1058 @@ +/* vm_page.c 6.3 83/09/26 */ + +#include "../machine/reg.h" +#include "../machine/pte.h" + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/inode.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/proc.h" +#include "../h/buf.h" +#include "../h/text.h" +#include "../h/cmap.h" +#include "../h/vm.h" +#include "../h/file.h" +#include "../h/trace.h" + +int nohash = 0; +/* + * Handle a page fault. + * + * Basic outline + * If page is allocated, but just not valid: + * Wait if intransit, else just revalidate + * Done + * Compute from which page operation would take place + * If page is text page, and filling from file system or swap space: + * If in free list cache, reattach it and then done + * Allocate memory for page in + * If block here, restart because we could have swapped, etc. + * Lock process from swapping for duration + * Update pte's to reflect that page is intransit. + * If page is zero fill on demand: + * Clear pages and flush free list cache of stale cacheing + * for this swap page (e.g. before initializing again due + * to 407/410 exec). + * If page is fill from file and in buffer cache: + * Copy the page from the buffer cache. + * If not a fill on demand: + * Determine swap address and cluster to page in + * Do the swap to bring the page in + * Instrument the pagein + * After swap validate the required new page + * Leave prepaged pages reclaimable (not valid) + * Update shared copies of text page tables + * Complete bookkeeping on pages brought in: + * No longer intransit + * Hash text pages into core hash structure + * Unlock pages (modulo raw i/o requirements) + * Flush translation buffer + * Process pagein is done + */ +#ifdef TRACE +#define pgtrace(e) trace(e,v,u.u_procp->p_pid) +#else +#define pgtrace(e) +#endif + +int preptofree = 1; /* send pre-paged pages to free list */ + +pagein(virtaddr, dlyu) + unsigned virtaddr; + int dlyu; +{ + register struct proc *p; + register struct pte *pte; + register struct inode *ip; + register u_int v; + unsigned pf; + int type, fileno; + struct pte opte; + dev_t dev; + register int i; + int klsize; + unsigned vsave; + struct cmap *c; + int j; + daddr_t bn, bncache, bnswap; + int si; +#ifdef PGINPROF +#include "../vax/mtpr.h" + int otime, olbolt, oicr, a, s; + + s = spl6(); + otime = time, olbolt = lbolt, oicr = mfpr(ICR); +#endif + cnt.v_faults++; + /* + * Classify faulted page into a segment and get a pte + * for the faulted page. + */ + vsave = v = clbase(btop(virtaddr)); + p = u.u_procp; + if (isatsv(p, v)) { + type = CTEXT; + pte = tptopte(p, vtotp(p, v)); + } else if (isadsv(p, v)) { + type = CDATA; + pte = dptopte(p, vtodp(p, v)); + } else { + type = CSTACK; + pte = sptopte(p, vtosp(p, v)); + } + if (pte->pg_v) + panic("pagein"); + + /* + * If page is reclaimable, reclaim it. + * If page is text and intransit, sleep while it is intransit, + * If it is valid after the sleep, we are done. + * Otherwise we have to start checking again, since page could + * even be reclaimable now (we may have swapped for a long time). + */ +restart: + if (pte->pg_fod == 0 && pte->pg_pfnum) { + if (type == CTEXT && cmap[pgtocm(pte->pg_pfnum)].c_intrans) { + pgtrace(TR_INTRANS); + sleep((caddr_t)p->p_textp, PSWP+1); + pgtrace(TR_EINTRANS); + pte = vtopte(p, v); + if (pte->pg_v) { +valid: + if (dlyu) { + c = &cmap[pgtocm(pte->pg_pfnum)]; + if (c->c_lock) { + c->c_want = 1; + sleep((caddr_t)c, PSWP+1); + goto restart; + } + c->c_lock = 1; + } + newptes(pte, v, CLSIZE); + cnt.v_intrans++; + return; + } + goto restart; + } + /* + * If page is in the free list, then take + * it back into the resident set, updating + * the size recorded for the resident set. + */ + si = splimp(); + if (cmap[pgtocm(pte->pg_pfnum)].c_free) { + pgtrace(TR_FRECLAIM); + munlink(pte->pg_pfnum); + cnt.v_pgfrec++; + if (type == CTEXT) + p->p_textp->x_rssize += CLSIZE; + else + p->p_rssize += CLSIZE; + } else + pgtrace(TR_RECLAIM); + splx(si); + pte->pg_v = 1; + if (anycl(pte, pg_m)) + pte->pg_m = 1; + distcl(pte); + if (type == CTEXT) + distpte(p->p_textp, vtotp(p, v), pte); + u.u_ru.ru_minflt++; + cnt.v_pgrec++; + if (dlyu) { + c = &cmap[pgtocm(pte->pg_pfnum)]; + if (c->c_lock) { + c->c_want = 1; + sleep((caddr_t)c, PSWP+1); + goto restart; + } + c->c_lock = 1; + } + newptes(pte, v, CLSIZE); +#ifdef PGINPROF + a = vmtime(otime, olbolt, oicr); + rectime += a; + if (a >= 0) + vmfltmon(rmon, a, rmonmin, rres, NRMON); + splx(s); +#endif + return; + } +#ifdef PGINPROF + splx(s); +#endif + /* + * is where data comes from/goes to. + * is where data is cached from/to. + * is where data will eventually go. + */ + if (pte->pg_fod == 0) { + fileno = -1; + bnswap = bncache = bn = vtod(p, v, &u.u_dmap, &u.u_smap); + dev = swapdev; + } else { + fileno = ((struct fpte *)pte)->pg_fileno; + bn = ((struct fpte *)pte)->pg_blkno; + bnswap = vtod(p, v, &u.u_dmap, &u.u_smap); + if (fileno > PG_FMAX) + panic("pagein pg_fileno"); + if (fileno == PG_FTEXT) { + if (p->p_textp == 0) + panic("pagein PG_FTEXT"); + dev = p->p_textp->x_iptr->i_dev; + bncache = bn; + } else if (fileno == PG_FZERO) { + dev = swapdev; + bncache = bnswap; + } else { + register struct file *fp = u.u_ofile[fileno]; + + if (fp == NULL || fp->f_type != DTYPE_INODE) + panic("pagein u.u_ofile"); + ip = (struct inode *)fp->f_data; + dev = ip->i_dev; + } + } + klsize = 1; + opte = *pte; + + /* + * Check for text detached but in free list. + * This can happen only if the page is filling + * from a inode or from the swap device, (e.g. not when reading + * in 407/410 execs to a zero fill page.) + */ + if (type == CTEXT && fileno != PG_FZERO && !nohash) { + si = splimp(); + while ((c = mfind(dev, bncache)) != 0) { + pf = cmtopg(c - cmap); + if (c->c_lock == 0) + break; + mlock(pf); + munlock(pf); + } + if (c) { + if (c->c_type != CTEXT || c->c_gone == 0 || + c->c_free == 0) + panic("pagein mfind"); + p->p_textp->x_rssize += CLSIZE; + /* + * Following code mimics memall(). + */ + munlink(pf); + for (j = 0; j < CLSIZE; j++) { + *(int *)pte = pf++; + pte->pg_prot = opte.pg_prot; + pte++; + } + pte -= CLSIZE; + c->c_free = 0; + c->c_gone = 0; + if (c->c_intrans || c->c_want) + panic("pagein intrans|want"); + c->c_lock = 1; + if (c->c_page != vtotp(p, v)) + panic("pagein c_page chgd"); + c->c_ndx = p->p_textp - &text[0]; + if (dev == swapdev) { + cnt.v_xsfrec++; + pgtrace(TR_XSFREC); + } else { + cnt.v_xifrec++; + pgtrace(TR_XIFREC); + } + cnt.v_pgrec++; + u.u_ru.ru_minflt++; + if (dev != swapdev) { + c = mfind(swapdev, bnswap); + if (c) + munhash(swapdev, bnswap); + pte->pg_m = 1; + } + splx(si); + goto skipswap; + } + splx(si); + } + + /* + * Wasn't reclaimable or reattachable. + * Have to prepare to bring the page in. + * We allocate the page before locking so we will + * be swappable if there is no free memory. + * If we block we have to start over, since anything + * could have happened. + */ + if (freemem < CLSIZE * KLMAX) { + pgtrace(TR_WAITMEM); + while (freemem < CLSIZE * KLMAX) + sleep((caddr_t)&freemem, PSWP+2); + pgtrace(TR_EWAITMEM); + pte = vtopte(p, v); + if (pte->pg_v) + goto valid; + goto restart; + } + + /* + * Now can get memory and committed to bringing in the page. + * Lock this process, get a page, + * construct the new pte, and increment + * the (process or text) resident set size. + */ + p->p_flag |= SPAGE; + (void) memall(pte, CLSIZE, p, type); + pte->pg_prot = opte.pg_prot; + pf = pte->pg_pfnum; + cmap[pgtocm(pf)].c_intrans = 1; + distcl(pte); + if (type == CTEXT) { + p->p_textp->x_rssize += CLSIZE; + distpte(p->p_textp, vtotp(p, v), pte); + } else + p->p_rssize += CLSIZE; + + /* + * Two cases: either fill on demand (zero, or from file or text) + * or from swap space. + */ + if (opte.pg_fod) { + pte->pg_m = 1; + if (fileno == PG_FZERO || fileno == PG_FTEXT) { + /* + * Flush any previous text page use of this + * swap device block. + */ + si = splimp(); + if (type == CTEXT) { + c = mfind(swapdev, bnswap); + if (c) + munhash(swapdev, bnswap); + } + splx(si); + /* + * If zero fill, short-circuit hard work + * by just clearing pages. + */ + if (fileno == PG_FZERO) { + pgtrace(TR_ZFOD); + for (i = 0; i < CLSIZE; i++) + clearseg(pf+i); + if (type != CTEXT) + cnt.v_zfod += CLSIZE; + goto skipswap; + } + pgtrace(TR_EXFOD); + cnt.v_exfod += CLSIZE; + } else + panic("pagein vread"); + /* + * Blocks of an executable may still be in the buffer + * cache, so we explicitly flush them out to disk + * so that the proper data will be paged in. + */ + blkflush(dev, bn, (long)CLSIZE*NBPG); +#ifdef TRACE + if (type != CTEXT) + trace(TR_XFODMISS, dev, bn); +#endif + } else { + if (opte.pg_pfnum) + panic("pagein pfnum"); + pgtrace(TR_SWAPIN); + /* + * Fill from swap area. Try to find adjacent + * pages to bring in also. + */ + v = kluster(p, v, pte, B_READ, &klsize, + (type == CTEXT) ? kltxt : + ((p->p_flag & SSEQL) ? klseql : klin), bn); + /* THIS COULD BE COMPUTED INCREMENTALLY... */ + bncache = bn = vtod(p, v, &u.u_dmap, &u.u_smap); + } + + distcl(pte); + swap(p, bn, ptob(v), klsize * ctob(CLSIZE), + B_READ, B_PGIN, dev, 0); +#ifdef TRACE + trace(TR_PGINDONE, vsave, u.u_procp->p_pid); +#endif + + /* + * Instrumentation. + */ + u.u_ru.ru_majflt++; + cnt.v_pgin++; + cnt.v_pgpgin += klsize * CLSIZE; +#ifdef PGINPROF + a = vmtime(otime, olbolt, oicr) / 100; + pgintime += a; + if (a >= 0) + vmfltmon(pmon, a, pmonmin, pres, NPMON); +#endif + +skipswap: + /* + * Fix page table entries. + * + * Only page requested in is validated, and rest of pages + * can be ``reclaimed''. This allows system to reclaim prepaged pages + * quickly if they are not used and memory is tight. + */ + pte = vtopte(p, vsave); + pte->pg_v = 1; + distcl(pte); + if (type == CTEXT) { + distpte(p->p_textp, vtotp(p, vsave), pte); + if (opte.pg_fod) + p->p_textp->x_flag |= XWRIT; + wakeup((caddr_t)p->p_textp); + } + + /* + * Memall returned page(s) locked. Unlock all + * pages in cluster. If locking pages for raw i/o + * leave the page which was required to be paged in locked, + * but still unlock others. + * If text pages, hash into the cmap situation table. + */ + pte = vtopte(p, v); + for (i = 0; i < klsize; i++) { + c = &cmap[pgtocm(pte->pg_pfnum)]; + c->c_intrans = 0; + if (type == CTEXT && c->c_blkno == 0 && bncache && !nohash) { + mhash(c, dev, bncache); + bncache += CLBYTES / DEV_BSIZE; + } + if (v != vsave || !dlyu) + munlock(pte->pg_pfnum); + if (v != vsave && type != CTEXT && preptofree) { + /* + * Throw pre-paged data/stack pages at the + * bottom of the free list. + */ + p->p_rssize -= CLSIZE; + memfree(pte, CLSIZE, 0); + } + newptes(pte, v, CLSIZE); + v += CLSIZE; + pte += CLSIZE; + } + + /* + * All done. + */ + p->p_flag &= ~SPAGE; + + /* + * If process is declared fifo, memory is tight, + * and this was a data page-in, free memory + * klsdist pagein clusters away from the current fault. + */ + if ((p->p_flag&SSEQL) && freemem < lotsfree && type == CDATA) { + int k = (vtodp(p, vsave) / CLSIZE) / klseql; +#ifdef notdef + if (vsave > u.u_vsave) + k -= klsdist; + else + k += klsdist; + dpageout(p, k * klseql * CLSIZE, klout*CLSIZE); + u.u_vsave = vsave; +#else + dpageout(p, (k - klsdist) * klseql * CLSIZE, klout*CLSIZE); + dpageout(p, (k + klsdist) * klseql * CLSIZE, klout*CLSIZE); +#endif + } +} + +#if defined(BERT) +int dmod = 1000000; +int dcnt; +#endif +/* + * Take away n pages of data space + * starting at data page dp. + * Used to take pages away from sequential processes. + * Mimics pieces of code in pageout() below. + */ +dpageout(p, dp, n) + struct proc *p; + int dp, n; +{ + register struct cmap *c; + int i, klsize; + register struct pte *pte; + unsigned v; + daddr_t daddr; + + if (dp < 0) { + n += dp; + dp = 0; + } + if (dp + n > p->p_dsize) + n = p->p_dsize - dp; +#if defined(BERT) + if (++dcnt % dmod == 0) + printf("dp %d, n %d\n", dp, n); +#endif + for (i = 0; i < n; i += CLSIZE, dp += CLSIZE) { + pte = dptopte(p, dp); + if (pte->pg_fod || pte->pg_pfnum == 0) + continue; + c = &cmap[pgtocm(pte->pg_pfnum)]; + if (c->c_lock || c->c_free) + continue; + if (pte->pg_v) { + pte->pg_v = 0; + if (anycl(pte, pg_m)) + pte->pg_m = 1; + distcl(pte); + } + if (dirtycl(pte)) { + if (bswlist.av_forw == NULL) + continue; + mlock(pte->pg_pfnum); + pte->pg_m = 0; + distcl(pte); + p->p_poip++; + v = kluster(p, dptov(p, dp), pte, B_WRITE, + &klsize, klout, (daddr_t)0); + /* THIS ASSUMES THAT p == u.u_procp */ + daddr = vtod(p, v, &u.u_dmap, &u.u_smap); + swap(p, daddr, ptob(v), klsize * ctob(CLSIZE), + B_WRITE, B_DIRTY, swapdev, pte->pg_pfnum); + } else { + if (c->c_gone == 0) + p->p_rssize -= CLSIZE; + memfree(pte, CLSIZE, 0); + cnt.v_seqfree += CLSIZE; + } + } +} + +int fifo = 0; +/* + * The page out daemon, which runs as process 2. + * + * As long as there are at least lotsfree pages, + * this process is not run. When the number of free + * pages stays in the range desfree to lotsfree, + * this daemon runs through the pages in the loop + * at a rate determined in vmsched(), simulating the missing + * hardware reference bit, and cleaning pages and transferring + * them to the free list. + */ +pageout() +{ + register struct proc *rp; + register struct text *xp; + register struct cmap *c; + register struct pte *pte; + int count, pushes; + swblk_t daddr; + unsigned v; + int maxhand = pgtocm(maxfree); + int klsize; + +loop: + /* + * Before sleeping, look to see if there are any swap I/O headers + * in the ``cleaned'' list that correspond to dirty + * pages that have been pushed asynchronously. If so, + * empty the list by calling cleanup(). + * + * N.B.: We guarantee never to block while the cleaned list is nonempty. + */ + (void) spl6(); + if (bclnlist != NULL) { + (void) spl0(); + cleanup(); + goto loop; + } + sleep((caddr_t)&proc[2], PSWP+1); + (void) spl0(); + count = 0; + pushes = 0; + while (nscan < desscan && freemem < lotsfree) { +top: + /* + * An iteration of the clock pointer (hand) around the loop. + * Look at the page at hand. If it is a + * locked (for physical i/o e.g.), system (u., page table) + * or free, then leave it alone. + * Otherwise, find a process and text pointer for the + * page, and a virtual page number in either the + * process or the text image. + */ + c = &cmap[hand]; + if (c->c_lock || c->c_free) + goto skip; + switch (c->c_type) { + + case CSYS: + goto skip; + + case CTEXT: + xp = &text[c->c_ndx]; + rp = xp->x_caddr; + v = tptov(rp, c->c_page); + pte = tptopte(rp, c->c_page); + break; + + case CDATA: + case CSTACK: + rp = &proc[c->c_ndx]; + while (rp->p_flag & SNOVM) + rp = rp->p_xlink; + xp = rp->p_textp; + if (c->c_type == CDATA) { + v = dptov(rp, c->c_page); + pte = dptopte(rp, c->c_page); + } else { + v = sptov(rp, c->c_page); + pte = sptopte(rp, c->c_page); + } + break; + } + + if (pte->pg_pfnum != cmtopg(hand)) + panic("bad c_page"); + + /* + * If page is valid; make invalid but reclaimable. + * If this pte is not valid, then it must be reclaimable + * and we can add it to the free list. + */ + if (pte->pg_v) { + pte->pg_v = 0; + if (anycl(pte, pg_m)) + pte->pg_m = 1; + distcl(pte); + if (c->c_type == CTEXT) + distpte(xp, vtotp(rp, v), pte); + if ((rp->p_flag & (SSEQL|SUANOM)) || fifo || + rp->p_rssize > rp->p_maxrss) + goto take; + } else { +take: + if (c->c_type != CTEXT) { + /* + * Guarantee a minimal investment in data + * space for jobs in balance set. + */ + if (rp->p_rssize < saferss - rp->p_slptime) + goto skip; + } + + /* + * If the page is currently dirty, we + * have to arrange to have it cleaned before it + * can be freed. We mark it clean immediately. + * If it is reclaimed while being pushed, then modified + * again, we are assured of the correct order of + * writes because we lock the page during the write. + * This guarantees that a swap() of this process (and + * thus this page), initiated in parallel, will, + * in fact, push the page after us. + * + * The most general worst case here would be for + * a reclaim, a modify and a swapout to occur + * all before the single page transfer completes. + */ + if (dirtycl(pte)) { + /* + * Limit pushes to avoid saturating + * pageout device. + * + * MAGIC 4 BECAUSE WE RUN EVERY 1/4 SEC (clock) + */ + if (pushes > maxpgio / 4) + goto skip; + pushes++; + /* + * If the process is being swapped out + * or about to exit, do not bother with its + * dirty pages + */ + if (rp->p_flag & (SLOCK|SWEXIT)) + goto skip; + + /* + * Now carefully make sure that there will + * be a header available for the push so that + * we will not block waiting for a header in + * swap(). The reason this is important is + * that we (proc[2]) are the one who cleans + * dirty swap headers and we could otherwise + * deadlock waiting for ourselves to clean + * swap headers. The sleep here on &proc[2] + * is actually (effectively) a sleep on both + * ourselves and &bswlist, and this is known + * to swdone and swap in vm_swp.c. That is, + * &proc[2] will be awakened both when dirty + * headers show up and also to get the pageout + * daemon moving. + */ + loop2: + (void) spl6(); + if (bclnlist != NULL) { + (void) spl0(); + cleanup(); + goto loop2; + } + if (bswlist.av_forw == NULL) { + bswlist.b_flags |= B_WANTED; + sleep((caddr_t)&proc[2], PSWP+2); + (void) spl0(); + /* + * Page disposition may have changed + * since process may have exec'ed, + * forked, exited or just about + * anything else... try this page + * frame again, from the top. + */ + goto top; + } + (void) spl0(); + + mlock((unsigned)cmtopg(hand)); + uaccess(rp, Pushmap, &pushutl); + /* + * Now committed to pushing the page... + */ + pte->pg_m = 0; + distcl(pte); + if (c->c_type == CTEXT) { + xp->x_poip++; + distpte(xp, vtotp(rp, v), pte); + } else + rp->p_poip++; + v = kluster(rp, v, pte, B_WRITE, &klsize, klout, (daddr_t)0); + if (klsize == 0) + panic("pageout klsize"); + daddr = vtod(rp, v, &pushutl.u_dmap, &pushutl.u_smap); + swap(rp, daddr, ptob(v), klsize * ctob(CLSIZE), + B_WRITE, B_DIRTY, swapdev, pte->pg_pfnum); + /* + * The cleaning of this page will be + * completed later, in cleanup() called + * (synchronously) by us (proc[2]). In + * the meantime, the page frame is locked + * so no havoc can result. + */ + goto skip; + + } + /* + * Decrement the resident set size of the current + * text object/process, and put the page in the + * free list. Note that we don't give memfree the + * pte as its argument, since we don't want to destroy + * the pte. If it hasn't already been discarded + * it may yet have a chance to be reclaimed from + * the free list. + */ + if (c->c_gone == 0) + if (c->c_type == CTEXT) + xp->x_rssize -= CLSIZE; + else + rp->p_rssize -= CLSIZE; + memfree(pte, CLSIZE, 0); + cnt.v_dfree += CLSIZE; + + /* + * We managed to add a page to the free list, + * so we give ourselves another couple of trips + * around the loop. + */ + count = 0; + } +skip: + cnt.v_scan++; + nscan++; + if (++hand >= maxhand) { + hand = 0; + cnt.v_rev++; + if (count > 2) { + /* + * Extremely unlikely, but we went around + * the loop twice and didn't get anywhere. + * Don't cycle, stop till the next clock tick. + */ + goto loop; + } + count++; + } + } + goto loop; +} + +/* + * Process the ``cleaned'' list. + * + * Scan through the linked list of swap I/O headers + * and free the corresponding pages that have been + * cleaned by being written back to the paging area. + * If the page has been reclaimed during this time, + * we do not free the page. As they are processed, + * the swap I/O headers are removed from the cleaned + * list and inserted into the free list. + */ +cleanup() +{ + register struct buf *bp; + register struct proc *rp; + register struct text *xp; + register struct cmap *c; + register struct pte *pte; + struct pte *upte; + unsigned pf; + register int i; + int s, center; + + for (;;) { + s = spl6(); + if ((bp = bclnlist) == 0) + break; + bclnlist = bp->av_forw; + splx(s); + pte = vtopte(&proc[2], btop(bp->b_un.b_addr)); + center = 0; + for (i = 0; i < bp->b_bcount; i += CLSIZE * NBPG) { + pf = pte->pg_pfnum; + munlock(pf); + c = &cmap[pgtocm(pf)]; + if (pf != bp->b_pfcent) { + if (c->c_gone) { + memfree(pte, CLSIZE, 0); + cnt.v_dfree += CLSIZE; + } + goto skip; + } + center++; + switch (c->c_type) { + + case CSYS: + panic("cleanup CSYS"); + + case CTEXT: + xp = &text[c->c_ndx]; + xp->x_poip--; + if (xp->x_poip == 0) + wakeup((caddr_t)&xp->x_poip); + break; + + case CDATA: + case CSTACK: + rp = &proc[c->c_ndx]; + while (rp->p_flag & SNOVM) + rp = rp->p_xlink; + rp->p_poip--; + if (rp->p_poip == 0) + wakeup((caddr_t)&rp->p_poip); + break; + } + if (c->c_gone == 0) { + switch (c->c_type) { + + case CTEXT: + upte = tptopte(xp->x_caddr, c->c_page); + break; + + case CDATA: + upte = dptopte(rp, c->c_page); + break; + + case CSTACK: + upte = sptopte(rp, c->c_page); + break; + } + if (upte->pg_v) + goto skip; + if (c->c_type == CTEXT) + xp->x_rssize -= CLSIZE; + else + rp->p_rssize -= CLSIZE; + } + memfree(pte, CLSIZE, 0); + cnt.v_dfree += CLSIZE; +skip: + pte += CLSIZE; + } + if (center != 1) + panic("cleanup center"); + bp->b_flags = 0; + bp->av_forw = bswlist.av_forw; + bswlist.av_forw = bp; + if (bswlist.b_flags & B_WANTED) { + bswlist.b_flags &= ~B_WANTED; + wakeup((caddr_t)&bswlist); + } + } + splx(s); +} + +/* + * Kluster locates pages adjacent to the argument pages + * that are immediately available to include in the pagein/pageout, + * and given the availability of memory includes them. + * It knows that the process image is contiguous in chunks; + * an assumption here is that CLSIZE * KLMAX is a divisor of dmmin, + * so that by looking at KLMAX chunks of pages, all such will + * necessarily be mapped swap contiguous. + */ +int noklust; +int klicnt[KLMAX]; +int klocnt[KLMAX]; + +kluster(p, v, pte0, rw, pkl, klsize, bn0) + register struct proc *p; + unsigned v; + struct pte *pte0; + int rw, *pkl, klsize; + daddr_t bn0; +{ + int type, cl, clmax; + int kloff, k, klmax; + register struct pte *pte; + int klback, klforw; + register int i; + unsigned v0; + daddr_t bn; + + if (rw == B_READ) + klicnt[0]++; + else + klocnt[0]++; + *pkl = 1; + if (noklust || klsize <= 1 || klsize > KLMAX || (klsize & (klsize - 1))) + return (v); + if (rw == B_READ && freemem < CLSIZE * KLMAX) + return (v); + if (isassv(p, v)) { + type = CSTACK; + cl = vtosp(p, v) / CLSIZE; + clmax = p->p_ssize / CLSIZE; + } else if (isadsv(p, v)) { + type = CDATA; + cl = vtodp(p, v) / CLSIZE; + clmax = p->p_dsize / CLSIZE; + } else { + type = CTEXT; + cl = vtotp(p, v) / CLSIZE; + clmax = p->p_textp->x_size / CLSIZE; + } + kloff = cl & (klsize - 1); + pte = pte0; + bn = bn0; + for (k = kloff; --k >= 0;) { + if (type == CSTACK) + pte += CLSIZE; + else + pte -= CLSIZE; + if (type == CTEXT && rw == B_READ && bn) { + bn -= CLBYTES / DEV_BSIZE; + if (mfind(swapdev, bn)) + break; + } + if (!klok(pte, rw)) + break; + } + klback = (kloff - k) - 1; + pte = pte0; + if ((cl - kloff) + klsize > clmax) + klmax = clmax - (cl - kloff); + else + klmax = klsize; + bn = bn0; + for (k = kloff; ++k < klmax;) { + if (type == CSTACK) + pte -= CLSIZE; + else + pte += CLSIZE; + if (type == CTEXT && rw == B_READ && bn) { + bn += (CLBYTES / DEV_BSIZE); + if (mfind(swapdev, bn)) + break; + } + if (!klok(pte, rw)) + break; + } + klforw = (k - kloff) - 1; + if (klforw + klback == 0) + return (v); + pte = pte0; + if (type == CSTACK) { + pte -= klforw * CLSIZE; + v -= klforw * CLSIZE; + } else { + pte -= klback * CLSIZE; + v -= klback * CLSIZE; + } + *pkl = klforw + klback + 1; + if (rw == B_READ) + klicnt[0]--, klicnt[*pkl - 1]++; + else + klocnt[0]--, klocnt[*pkl - 1]++; + v0 = v; + for (i = 0; i < *pkl; i++) { + if (pte == pte0) + goto cont; + if (rw == B_WRITE) { + mlock(pte->pg_pfnum); + pte->pg_m = 0; + distcl(pte); + if (type == CTEXT) + distpte(p->p_textp, vtotp(p, v), pte); + } else { + struct pte opte; + int pf; + + opte = *pte; + if (memall(pte, CLSIZE, p, type) == 0) + panic("kluster"); + pte->pg_prot = opte.pg_prot; + pf = pte->pg_pfnum; + cmap[pgtocm(pf)].c_intrans = 1; + distcl(pte); + if (type == CTEXT) { + p->p_textp->x_rssize += CLSIZE; + distpte(p->p_textp, vtotp(p, v), pte); + } else + p->p_rssize += CLSIZE; + distcl(pte); + } +cont: + pte += CLSIZE; + v += CLSIZE; + } + return (v0); +} + +klok(pte, rw) + register struct pte *pte; + int rw; +{ + register struct cmap *c; + + if (rw == B_WRITE) { + if (pte->pg_fod) + return (0); + if (pte->pg_pfnum == 0) + return (0); + c = &cmap[pgtocm(pte->pg_pfnum)]; + if (c->c_lock || c->c_intrans) + return (0); + if (!dirtycl(pte)) + return (0); + return (1); + } else { + if (pte->pg_fod) + return (0); + if (pte->pg_pfnum) + return (0); + return (1); + } +} -- 2.20.1