#define SCCSID  "@(#)pgalloc.c  6.12 92/03/16"

/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 */

// Change History
// Defect  date        Author              Abstract
// ======================================================================
// 51579   09/09/92    Carlos Romero       Replace QResident by QCTResident
//                                         and adjust QCTResident recording
// 63923   03/22/93    Glenn Brew          Large resident allocations causing
//                                         IPE in pgStrict (line 2256)
// 70987   06/24/93    Charlie Schmitt     Change pgAllocPFHighMem to not
//                                         affect PF linked list. (line 2416)
// 76699   12/15/93    Ron Cadima          Make Perfview compile option with
//                                         flag PERFVIEW
// 102346  04/20/95    Scott Cottrill      Added check for busy semaphores
//                                         before call to worker routine if
//                                         worker routine is pgwFree.

#define FNO_NUM  FNO_PGALLOC_C
#include <fnomem.h>

#define INCL_DOSERRORS
#define INCL_OS2STD
#include <os2.h>

#define INCL_DOSP
#include <os2p.h>

#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <smexport.h>
#include <pglocal.h>
#include <kutil.h>
#include <vmowner.h>
#include <stdlib.h>
#include <string.h>

#ifdef PERFVIEW
#include <pvwxport.h>
#include <perfvw.h>
#endif

#include <vmkh.h>            // must be after tkexport.h for KSEM definition
#include <vmlocal.h>         // needed for vmCrossesPgBdry in pgAllocContext

#define  PG_RES_PAGE_PAD(n)   (n/0x100)   // see pgCommit for details

KSEMMTX pgCommitSem;
extern KSEMMTX SMSwapperSem;
extern VMHOB smHobheap;
#pragma code_seg("SWAP32_13","CODE")

/***EP  PGAllocContext - initialize a page table context.
 *
 *      PGAllocContext initializes the per-process page manager data
 *      for a new task.
 *
 *      This routine is swappable.
 *
 *          - allocate a page table context
 *          - initialize pgdata structure
 *
 *      ENTRY   pptda - pointer to ptda to be initialized
 *      EXIT    0 on success
 *                  ptda paging data filled in
 *              else error code
 *
 *      ppd = &pptda->ptda_pgdata
 *      ppc = address of ptda page manager arrays
 *      memset(ppd, 0, sizeof(*ppd))
 *      memset(ppc, 0, sizeof(*ppc))
 *
 *      if ((ppd->pd_ptcontext = pgAllocBit(pgbmContext, TKCPTDAMAX)) == 0)
 *          return(ERROR_NOT_ENOUGH_MEMORY)
 *
 *      ppd->pd_ppte = page tables for this process
 *      ppd->pd_ppde = &ppc->pp_pde
 *      ppd->pd_pcalloc = &ppc->pp_calloc
 *      ppd->pd_pcpresent = &ppc->pp_cpresent
 *      ppd->pd_base = MINCRPAGE
 *      ppd->pd_maxpde = MAXCRPDE
 */

int
PGAllocContext(pptda)
struct ptda_s *pptda;
{
    register struct pgdata_s *ppd;
    register struct pgpc_s *ppc;

    TKMayBlock();       /* swappable code! */

    pgAssert(pptda->ptda_signature[0] == 'T' && pptda->ptda_signature[1]=='D');

    ppd = (struct pgdata_s *) &pptda->ptda_pgdata;
    memset((void *) ppd, 0, sizeof(*ppd));
    ppc = (struct pgpc_s *) &pptda->ptda_pgpc;
    memset((void *) ppc, 0,  sizeof(*ppc));

skip_context:
    if ((ppd->pd_ptcontext = (ushort_t)pgAllocBit(pgbmContext,TKCPTDAMAX))==0){
        pgAssert(FALSE);
    }

    /*
     *  Setup per-process page manager data
     */
    ppd->pd_ppte = &pgpCRPageTables[(ppd->pd_ptcontext - 1) *
                                    (MAXCR_SIZE / PAGESIZE)];   // GSR
// GSR                              (MAXCRSIZE / PAGESIZE)];
    /*
     *  Use the ptes of this guy's page tables for his page directory entries
     */
    ppd->pd_ppde = pgPagePdTopPte((ulong_t)ppd->pd_ppte >> PAGESHIFT, &pgData);

    /* d78501 - the page manager is dependent upon all pde's being on the
     * same page.  (ie pgReleasePt runs thru the pde's looking for the next
     * highest pde trying to set pd_cpdehigh, but never tests to see if
     * the pde's are on a valid page, it assumes that if one pde is in use
     * they all must be valid, this was true when the size of the CR
     * was 0x80 pages, but the GSR made it 0x50 and broke this assumption
     */
    if ( vmCrossesPgBdry( ppd->pd_ppde, ( (sizeof(struct pte_s)) * MAXCR_PDE)))
        goto skip_context;


    ppd->pd_pcalloc = &ppc->pp_calloc[0];
    ppd->pd_pcpresent = &ppc->pp_cpresent[0];
    ppd->pd_pcresident = &ppc->pp_cresident[0];
    ppd->pd_base = MINCRPAGE;
    ppd->pd_maxpde = MAXCR_PDE;  //GSR
// GSR    ppd->pd_maxpde = MAXCRPDE;

    return(0);
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***EP  PGInitVDMContext - page manager re-initialization for VDM processes
 *
 *      This routine allocates a page directory alias for a VDM that is
 *      in the process of initializitation.  The aliases are allocated by
 *      calling VMAllocMem and requesting 4meg of address space on a 4meg
 *      boundary.
 *
 *      To identify the page table as an alias, the pt's allocated count
 *      is set to VDMALIASSIG.  The pt's present count shifted left by 16
 *      plus the pt's resident count gives a pointer to the ptda of the vdm
 *      that owns the alias.
 *
 *      The pgdata_s field pd_pvdmalias is set to the base of the alias
 *      so it can be freed by PGFreeContext.
 *
 *      This routine is swappable.
 *
 *      ENTRY:  pptda - pointer to new process' ptda
 *              pladdrvdm - pointer to unitialized vdm alias linear address
 *      EXIT:   0 if success and
 *                      *pladdrvdm = vdm page directory alias linear address
 *              else error
 *
 *      allocate 4meg alias from VM
 *      allocate first page table in process
 *      point aliasing pde to allocated page table
 *      store alias address in pgdata_s and return area
 *      set count of alias pt to special values
 */
int
PGInitVDMContext(pptda, pladdrvdm)
struct ptda_s *pptda;
laddr_t *pladdrvdm;
{
    struct pgdata_s *retppd;
    register struct pgdata_s *ppd;
    register ulong_t pdialias;
    int err;
    VMAC ac;

    TKMayBlock();       /* swappable code! */

    /*
     *  Allocate 4meg of space from VM for the alias
     */
    pgAssert(pptda->ptda_signature[0] == 'T' && pptda->ptda_signature[1]== 'D');
    ppd = (struct pgdata_s *) &pptda->ptda_pgdata;
    if (err = VMAllocMem(PAGEDIRSIZE, 0, VMAC_ARENASYS | VMAC_ALIGNDIR,
                         HOBNULL, VDMALIASOWNER,
                         0, 0, 0, SSToDS(&ac))) {
        return(err);
    }

    /*
     *  Allocate the aliased pt and point both pdes to it.  Don't unpin
     *  so it stays present until PGFreeContext.  Also put
     *  special values (described in header) into the alias' pte counts.
     */
    if (err = pgPinPt(0, pptda, 1, PG_ALLOC | PG_RESIDENT, SSToDS(&retppd))) {
        return(err);
    }
    pdialias = (ac.ac_va - MINSYSLADDR) >> PAGEDIRSHIFT;
    pgpPageDir[ac.ac_va >> PAGEDIRSHIFT] = pgData.pd_ppde[pdialias] =
                                ppd->pd_ppde[0];
    pgcPteAllocated[pdialias] = (ushort_t) VDMALIASSIG;
    pgcPtePresent[pdialias] = (ushort_t) (((ulong_t) pptda) >> 16);
    pgcPteResident[pdialias] = (ushort_t) pptda;
    ppd->pd_pvdmalias = *pladdrvdm = ac.ac_va;
    return(0);
}
#pragma code_seg()
#pragma code_seg ("SWAP32_27","CODE")
/***EP  PGFreeContext - release a page table context.
 *
 *      PGFreeContext releases a page table context, if on behalf of
 *      a dying VDM process, the page directory alias is also freed.
 *
 *      This routine is swappable.
 *
 *      ENTRY   pptda - pointer to ptda to be freed
 *      EXIT    NONE
 *
 *      ppd = &pptda->ptda_pgdata
 *      if (ppd->pd_pvdmalias)
 *          free the alias
 *      if (ppd->pd_ptcontext == 0)
 *          panic()
 *      pgFreeBit(pgbmContext, (int) ppd->pd_ptcontext)
 */

void
PGFreeContext(pptda)
struct ptda_s *pptda;
{
    register ulong_t pdialias;
    register struct pgdata_s *ppd;
    int i;

    TKMayBlock();       /* swappable code! */

    ppd = (struct pgdata_s *) &pptda->ptda_pgdata;

    pgAssert(pptda->ptda_signature[0] == 'T' && pptda->ptda_signature[1]== 'D');
    pgAssert(ppd->pd_ptcontext != 0);

    /*
     *  If vdm process, free pde alias
     */
    if (ppd->pd_pvdmalias) {
        pdialias = (ppd->pd_pvdmalias - MINSYSLADDR) >> PAGEDIRSHIFT;
        pgAssert(pgcPteAllocated[pdialias] == (ushort_t) VDMALIASSIG);

        pgData.pd_ppde[pdialias].pt_long = 0;
        pgpPageDir[ppd->pd_pvdmalias >> PAGEDIRSHIFT].pt_long = 0;
        pgcPteAllocated[pdialias] = 0;
        pgcPtePresent[pdialias] = 0;
        pgcPteResident[pdialias] = 0;

        if (VMFreeMem(ppd->pd_pvdmalias, HOBNULL, 0)) {
            pgAssert(FALSE);
        }
        pgUnpinPt(0, ppd, 1, PG_RESIDENT);
        pgAssert(ppd->pd_pcalloc[0] == 0); /* vdm alias should be free */
    }
    pgFreeBit(pgbmContext, (int) ppd->pd_ptcontext);

#ifdef PGSTRICT
    /*
     *  Verify everything was freed (and counts aren't screw up)
     */
// GSR   for (i = 0 ; i < MAXCRPDE; i++) {
    for (i = 0 ; i < MAXCR_PDE; i++) {   // GSR
        if ((pptda == pPTDACur && pgpPageDir[i].pt_long) ||
            ppd->pd_pcalloc[i] ||
            ppd->pd_pcpresent[i] || ppd->pd_pcresident[i]) {
            panic(("PGFreeContext: not all free"));
        }
    }
#endif
}
#pragma code_seg()
#pragma code_seg("SWAP32_3","CODE")
/***EP  PGAlloc - alloc and init page table entries; account for overcommit.
 *
 *      PGAlloc allocates and initializes page table entries, and
 *      performs overcommit accounting.
 *
 *      This routine is swappable.
 *
 *      ENTRY   page - base virtual page number
 *              pptda - pointer to ptda
 *              npages - number of pages requested
 *              blockno - base block number (for initially discarded pages)
 *                        base paddr (for UVIRT allocations) or -1 if invalid
 *              hob - handle to object record
 *              obpg - object-relative page number
 *              flags - allocation flags (see pgexport.h)
 *      EXIT    0 on success, else error code
 *
 *      compute prototype pte permission flags
 *      stuff parameters into pginfo structure
 *      if (uvirt allocation)
 *          set resincr to 1 if system space allocation, else 0
 *          put base physical address in prototype pte
 *          call pgDriver with pgwAllocPte worker
 *      else if (discardable zero-fill)
 *          add  npages to the static zero-fill VP's ref count
 *          put static VP index in prototype pte
 *          call pgDriver with pgwAllocPte worker
 *      else
 *          build image of prototype VP
 *          set frameincr to 1 if discardable allocation
 *          request overcommit
 *          call pgDriver with pgwAlloc worker
 *          if (resident memory request)
 *              call pgAllocPF
 */

int
PGAlloc(page, pptda, npages, blockno, hob, obpg, flags)
page_t page;
struct ptda_s *pptda;
int npages;
ulong_t blockno;
ushort_t hob;
ulong_t obpg;
int flags;
{
    struct pginfo_s pgi;
    struct vp_s vp;
    int err;

    TKMayBlock();       /* swappable code! */

    /*
     *  Setup prototype pte flags and other simple pginfo fields
     */
    pgi.pi_data3 = flags & (PG_W | PG_U | PG_RESIDENT | PG_UVIRT | PG_GUARD);
    pgi.pi_page = page;
    pgi.pi_pptda = pptda;
    pgi.pi_flags = (flags & (PG_STATE | PG_MODIFIER | PG_PERM)) | PG_ALLOC;
    pgi.pi_data1 = 0;           /* resident count increment */
    pgi.pi_data2 = 0;           /* frame field increment */

    /*
     *  UVirt allocation.
     */
    if (flags & PG_UVIRT) {
        pgAssert(((flags & PG_STATE) & ~PG_RESIDENT) == PG_UVIRT); //OEMINT13
        if (flags & PG_GUARD) {
            return(ERROR_ACCESS_DENIED);
        }
        pgi.pi_data3 |= blockno & PT_FRAME;
        if ((page >= MINSYSPAGE) || (flags & PG_RESIDENT)) {        //OEMINT13
            pgi.pi_data3 |= PT_RESIDENT|PT_PRESENT;
            pgi.pi_flags |= PG_RESIDENT;
            pgi.pi_data1 = 1;
        }
        if ((flags & PG_NOINCR) == 0 && blockno != -1) {
            pgi.pi_data2 = PT_FRAMEINCR;
        }
        pgi.pi_pvp = 0;
        err = pgDriver(SSToDS(&pgi), npages, &pgwAllocPte, &pgwFree);

    /*
     *  Discardable zero-fill allocation
     */
    } else if ((flags & (PG_ZEROFILL | PG_DISCARDABLE)) ==
                         (PG_ZEROFILL | PG_DISCARDABLE)) {

        pgAssert((flags & PG_STATE) == PG_DISCARDABLE);
        pgi.pi_data3 = pgZeroFillPte;
        pgi.pi_pvp = pgpZeroFillVP;
        err = pgDriver(SSToDS(&pgi), npages, &pgwAllocPte, &pgwFree);

    /*
     *  Any other sort of allocation.  Setup prototype vp before calling
     *  worker.
     */
    } else {
        pgi.pi_pvp = SSToDS(&vp);
        vp.vp_refcount = 1;
        vp.vp_hob = hob;
        vp.vp_obpg = (ushort_t)obpg;
        if (flags & (PG_DISCARDABLE | PG_SWAPONWRITE)) {
            pgAssert((flags & PG_STATE) == PG_DISCARDABLE ||
                     (flags & PG_STATE) == PG_SWAPONWRITE);
            vp.vp_flags = (VP_DISCARDABLE | (flags & PG_SWAPONWRITE));
            vp.vp_frame = blockno;
            pgi.pi_data2 = 1;           /* frame field increment */

            /*
             *  If the page is swap-on-load, mark it dirty now so we won't
             *  discard it like SOWs.
             */
            pgAssert(PG_SWAPONLOAD == (PG_CONTIG | PG_SWAPONWRITE));
            if (flags & PG_CONTIG) {
                vp.vp_flags |= VP_DIRTY;
            }
        /*
         *  Mark resident pages as busy so no one can touch them between
         *  pte and vp allocation and PF allocation.
         */
        } else if (flags & PG_RESIDENT) {
            pgAssert((flags & PG_STATE) == PG_RESIDENT);
            pgi.pi_data1 = 1;
            pgi.pi_data3 &= ~PG_GUARD;   /* no resident guard pages */
            vp.vp_flags = VP_BUSY | VP_RESIDENT;
#ifdef PGSTRICT
            vp.vp_semowner = pTCBCur->TCBNumber;
            vp.vp_block = -1;
#endif
        /*
         *  must be swappable
         */
        } else {
            pgAssert((flags & PG_STATE) == PG_SWAPPABLE);
            vp.vp_flags = 0;
            vp.vp_frame = VPB_AOD;
            if (flags & PG_ZEROFILL) {
                vp.vp_frame = VPB_ZEROFILL;
            }
        }
        if ((err = pgCommit(npages, flags)) == 0) {
            if ((err=pgDriver(SSToDS(&pgi), npages, &pgwAlloc, &pgwFree))==0){
                pgAssert((flags & PG_RESIDENT) == 0 || blockno == 0 ||
                    blockno == PGB_64KALIGN || blockno == PGB_128KALIGN);
                if ((flags & PG_RESIDENT) &&
                    (err = pgAllocPF(pgPagePdTopPte(page, pgi.pi_ppd),
                                     page, npages, flags, blockno))) {
                    pgCommitFree(npages, flags);
                }
            } else {
                pgCommitFree(npages, flags);
            }
        }
    }
    return(err);
}
#pragma code_seg()
#pragma code_seg("SWAP32_3","CODE")


/***LP  pgwAlloc - init a new pte and vp.  pgDriver worker.
 *
 *      This routine allocates and initializes a page table entry and
 *      VPs for a single page.  This is the worker for all page allocations
 *      except for uvirt and discardable zerofill pages.
 *
 *      This routine is swappable.
 *
 *      ENTRY:  pgi - pginfo structure with the following peculiarities:
 *                      pi_pvp - prototype vp
 *                      pi_data1 - amount to increment resident pt count
 *                      pi_data2 - amount to increment VP frame field
 *                      pi_data3 - prototype pte
 *      EXIT:   0 if success, else error code
 *
 *      pgAllocVP
 *      copy prototype VP to new VP
 *      add frameincr to prototype VP's frame field
 *      add 1 to prototype VP's page field
 *      add resincr to page table resident count
 *      setup new pte
 */
int
pgwAlloc(ppgi)
register struct pginfo_s *ppgi;
{
    struct vp_s *pvp;

    pgAssert(ppgi->pi_ppte->pt_long == 0);      /* pgDriver should catch */

    pvp = pgAllocVP();
    memcpy(pvp, ppgi->pi_pvp, sizeof(struct vp_s));/*copy prototype vp to new*/
    ppgi->pi_pvp->vp_frame += ppgi->pi_data2;   /* advance vp frame field */
    ppgi->pi_pvp->vp_obpg += 1;                 /* inc vp ob-relative pg # */
    ppgi->pi_ppd->pd_pcresident[ppgi->pi_pdi] += ppgi->pi_data1;/* res cnt */
    ppgi->pi_ppd->pd_pcpresent[ppgi->pi_pdi] += ppgi->pi_data1; /* pres cnt */
    ppgi->pi_ppd->pd_pcalloc[ppgi->pi_pdi] += 1;                /* alloc cnt */
    ppgi->pi_ppte->pt_long = ppgi->pi_data3 |
                             (pgpVPToVPI(pvp) << PT_FRAMESHIFT); /* setup pte*/
    pgAssert(ppgi->pi_pvp->vp_refcount == 1);
    return(0);
}
#pragma code_seg()
#pragma code_seg("SWAP32_13","CODE")

/***LP  pgwAllocPte - init a new pte.  pgDriver worker.
 *
 *      This routine allocates and initializes a page table entry
 *      for a single page.  It is used to allocate uvirt pages and
 *      discardable zerofill pages.
 *
 *      This routine is swappable.
 *
 *      ENTRY:  pgi - pginfo structure with the following peculiarities:
 *                      pi_pvp - if non-zero, increment this vp's ref count
 *                      pi_data1 - amount to increment resident pt count
 *                      pi_data2 - add this to prototype pte for each page
 *                      pi_data3 - prototype pte
 *      EXIT:   0 if success, else error code
 *
 *      copy prototype pte to new pte
 *      add frameincr to prototype pte's frame field
 *      add resincr to page table resident count
 *      if (vp passed in)
 *          inc vp ref count
 */
int
pgwAllocPte(ppgi)
register struct pginfo_s *ppgi;
{
    pgAssert(ppgi->pi_ppte->pt_long == 0);      /* pgDriver should catch */

    ppgi->pi_ppd->pd_pcresident[ppgi->pi_pdi] += ppgi->pi_data1;/* res cnt */
    ppgi->pi_ppd->pd_pcpresent[ppgi->pi_pdi] += ppgi->pi_data1; /* pres cnt */
    ppgi->pi_ppd->pd_pcalloc[ppgi->pi_pdi] += 1;                /* alloc cnt */
    ppgi->pi_ppte->pt_long = ppgi->pi_data3;                    /* setup pte */
    ppgi->pi_data3 += ppgi->pi_data2;

    /*
     *  If a static vp was passed in (for discardable zerofill pages),
     *  inc vp ref count.
     */
    if (ppgi->pi_pvp != 0) {
        ppgi->pi_pvp->vp_refcount++;
    }
    return(0);
}

#pragma code_seg()

#pragma code_seg("SWAP32_27", "CODE")

/***EP  PGRemapUvirt - free a range of pages and map it to a physical range
 *
 *      The new mapping will have RWXU permissions.
 *
 *      ENTRY:  page - target starting page number, must be below 512meg
 *              pptda - target context
 *              npages - number of pages to remap
 *              physaddr - starting physical address (will be rounded down
 *            "            to page boundary)
 *              flags - PG_NOINCR
 *      EXIT:   0 if success, else error code
 */
int
PGRemapUvirt(page_t page, struct ptda_s *pptda, ulong_t npages,
             ulong_t physaddr, ulong_t flags)
{
    struct pginfo_s pgi;

    pgAssert(page < MAXCRPAGE);

    TKMayBlock();       /* swappable code! */

    /*
     *  Setup prototype pte flags and other simple pginfo fields
     */
    pgi.pi_data3 = (physaddr & PT_FRAME) | PG_W | PG_U | PG_UVIRT | PT_PRESENT;
    pgi.pi_page = page;
    pgi.pi_pptda = pptda;
    pgi.pi_flags = 0;
    pgi.pi_data2 = (flags & PG_NOINCR) ? 0 : PT_FRAMEINCR;
    return(pgDriver(SSToDS(&pgi), npages, &pgwRemapUvirt, &pgwFree));
}

/***LP  pgwRemapUvirt - pgDriver worker for PGRemapUvirt
 *
 *      ENTRY:  ppgi - pointer to standard pginfo structure with:
 *                      pi_data3 = new pte image
 *                      pi_data2 = amount to increment above each iteration
 *      EXIT:   0 if success, else error (currently none)
 */
int
pgwRemapUvirt(struct pginfo_s *ppgi)
{
    /*
     *  First free the page
     */
#ifdef PGSTRICT
    int err;

    err =
#endif
          pgwFree(ppgi);
    pgAssert(err == 0);

    pgAssert((ppgi->pi_data3 & (PT_PRESENT | PT_UVIRT | PT_RESIDENT)) ==
             (PT_PRESENT | PT_UVIRT));
    /*
     *  Now fill in the new pte contents
     */
    ppgi->pi_ppd->pd_pcpresent[ppgi->pi_pdi]++;                 /* pres cnt */
    ppgi->pi_ppd->pd_pcalloc[ppgi->pi_pdi]++;                   /* alloc cnt */
    ppgi->pi_ppte->pt_long = ppgi->pi_data3;                    /* setup pte */
    ppgi->pi_data3 += ppgi->pi_data2;
    return(0);
}

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")

/***EP  PGAllocSizeQuery - return number of allocatable pages.
 *
 *      PGAllocSizeQuery returns the number of pages the page manager
 *      believes could be allocated as swappable.  The returned value
 *      approximates a snapshot of reality and should only be used as
 *      a guide; its accuracy is not to be relied upon.
 *
 *      This routine is swappable.
 *
 *          - serialize overcommit variable access
 *          - query swap overcommit
 *
 *      ENTRY   pnpages - pointer to unitialized integer
 *      EXIT    0 on success
 *                  *pnpages = number of pages believed to be allocatable
 *              else error code
 *
 *      if (swapping is enabled)
 *          get overcommit semaphore
 *          query swap manager for maximum swap file size
 *          return max size less already committed memory
 *      else
 *          return uncommitted physical pages
 */
int
PGAllocSizeQuery(pnpages)
int *pnpages;
{
    int npages;
    int swapfilesize;
    int i;

    TKMayBlock();       /* swappable code! */

    /*
     *  If swapping is enabled, call the swapper for maximum swap file
     *  size and return that number less any memory already commited.
     */
    if (PGSwapEnabled) {
// overcommit
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
            if (i = KSEMRequestMutex( (HKSEM)&pgCommitSem, -1)) {
                return(i);
            }
            i = SMQuerySwapSpace(SSToDS(&npages));
            swapfilesize = pgSwappablePages -
                               max(0, (int) (pgPhysPages -
                                             pgResidentPages -
                    (max (PG_MIN_DISC_IN_MEM,pgDiscardableInmem)) ));
 //                                          pgDiscardableInmem));

            *pnpages = npages - swapfilesize;
            /*
             *  The above formula should be the same as the official overcommit
             *  formula, except negative numbers are allowed.
             */
            pgAssert(pgSwapFileSize() == max(0,swapfilesize));
            KSEMReleaseMutex((HKSEM)&pgCommitSem);
// end overcommit
        } else {
            if (i = KSEMRequestMutex( (HKSEM)&SMSwapperSem, -1)) {
                return(i);
            }
            i = SMQuerySwapSpace(SSToDS(&npages));
            npages = max( SMSwapSpace , (npages+SMcInMemFile)-SMMinFree);
            pgAssert(npages >= SMcDFInuse);
            *pnpages = npages+pgListCount - SMcDFInuse;
            KSEMReleaseMutex((HKSEM)&SMSwapperSem);
        }
    /*
     *  If swapping is not enabled, return the number of uncommited physical
     *  pages.
     */
    } else {
// defect 68302 - QuerySysInfo giving bad info for mem avail
//      *pnpages = pgPhysPages - (pgResidentPages + PADPAGES);

        *pnpages = pgPhysPages - (pgResidentPages + pgSwappablePages +
                                  pgDiscardablePages + PADPAGES);
        if(*pnpages > pgPhysPages) {
            *pnpages = 0;
        }
// end defect 68302
        i = 0;
    }
    return(i);
}

#pragma code_seg()
#pragma code_seg("SWAP32_4","CODE")

/***EP  PGFree - Free page table entries and associated memory resources.
 *
 *      PGFree frees page table entries and associated memory resources,
 *      and performs overcommit accounting using the current types of each
 *      page.
 *
 *      If PF_SKIPFREE is specified, then pages which are already free are
 *      ignored.
 *
 *      This routine is swappable.
 *
 *      ENTRY   page - base virtual page number
 *              pptda - pointer to ptda
 *              npages - number of pages to be freed
 *              flags - PG_SKIPFREE  ignore already free pages
 *      EXIT    0 or error code
 *
 *      setup pginfo structure, including temporary overcommit counts
 *      call pgDriver with pgwFree worker
 *      if (overcommit was freed)
 *          free it
 *      flush TLB
 */

int
PGFree(page, pptda, npages, flags)
page_t page;
struct ptda_s *pptda;
int npages;
int flags;
{
    char not_overcommitted;     //car01  - check for overcommited pages
    struct pginfo_s pgi;
    ulong_t ccommit[3];         /* counts of commit freed.
                                 * Index by vp_flags >> PG_COMMITSHIFT */
    int err;

    not_overcommitted = TRUE;  //car01 assumed there is not overcommitment

    TKMayBlock();       /* swappable code! */

    do {
        ccommit[0] = ccommit[1] = ccommit[2] = 0;
        pgi.pi_page = page;
        pgi.pi_pptda = pptda;
        pgi.pi_flags = flags | PG_IGNOREINTS | PG_WAITSEM;
        pgi.pi_data1 = (ulong_t) SSToDS(&ccommit);
    }while((err = pgDriver(SSToDS(&pgi), npages, &pgwFree, 0)) == ERROR_INTERRUPT);

    /*
     *  call pgCommitFree for any overcommit that was freed up
     */
    if (ccommit[VP_RESIDENT >> PG_COMMITSHIFT]) {

        not_overcommitted = FALSE;  //car01 set this FALSE so we don't
                                    //car01 substract twice from QCTResident

        pgCommitFree(ccommit[VP_RESIDENT >> PG_COMMITSHIFT], VP_RESIDENT);
    } else if (ccommit[VP_DISCARDABLE >> PG_COMMITSHIFT]) {
        pgCommitFree(ccommit[VP_DISCARDABLE >> PG_COMMITSHIFT],VP_DISCARDABLE);
    } else if (ccommit[0]) {
        pgCommitFree(ccommit[0], 0);
    }

#ifdef PERFVIEW
    if ((flags & PG_RESIDENT) && (not_overcommitted)) {     //@@car01
/***PV - Perfview measurement hook                          //@@car01
 *                                                          //@@car01
 *      TRACE MINOR = <PVWEI_LQ_mem_QCTResident_0>,         //@@car01
 *      TP    = @,                                          //@@car01
 *      TYPE    = (PVW),                                    //@@car01
 *      GROUP   = NOSTRICT                                  //@@car01
 */                                                         //@@car01
       PvwLeaveQ(mem,QCTResident,0);                        //@@car01
    }                                                       //@@car01
#endif


    pgFlushTLB();
    return(err);
}

#pragma code_seg()


/***LP  pgwFree - free pages worker for pgDriver
 *
 *      This routine frees page table entries and associated memory resources,
 *      and counts overcommit that should be freed.  It also frees vps and
 *      pfs if necessary.
 *
 *      The page semaphore must be clear when this routine is called.
 *
 *      ENTRY:  ppgi - pginfo structure with the following special field:
 *                      pi_data1 - pointer to array indexed by
 *                                 vp_flags >> PG_COMMITSHIFT of overcommit
 *                                 to be freed up.
 *                      pi_flags - PG_ALLOC - don't free overcommit
 *      EXIT:   0
 *
 *      dec pt allocated count
 *      zero the pte
 *      if (page is present)
 *          dec pt present count
 *          if (pte is resident)
 *              dec pt resident count
 *          if (not uvirt)
 *              if (page is dirty)
 *                  propogate dirty bit to vp, trasform sows to swappable,
 *                  check for illegal writes
 *              dec vp ref count
 *              if (0)
 *                  goto freevp
 *              dec pf ref count
 *              if (0)
 *                  idle pf
 *      else if (not uvirt) ; not present
 *          dec vp ref count
 *          if (0)
 *              if (PF_VALID)
 *                  if (pf idle)
 *                     reclaim pf
 *                freevp:
 *                  free pf
 *              if (not PG_ALLOC)
 *                  use vp state bits to dec appropriate commit counts
 *              free the vp
 */

int
pgwFree(ppgi)
register struct pginfo_s *ppgi;
{
    struct pf_s *ppf;
    ulong_t frame;
    ulong_t i;

    /*
     *  make sure everything is OK
     */
    pgStrict(ppgi->pi_ppte, ppgi->pi_pvp);

    /*
     *  Common code for all frees.  Dec pt allocated count and zero pte
     */
    ppgi->pi_ppd->pd_pcalloc[ppgi->pi_pdi]--;   /* dec pt allocated cnt*/
    ppgi->pi_ppte->pt_long = 0;

    /*
     *  if the page is present
     */
    if (ppgi->pi_pte.pt_present) {
        ppgi->pi_ppd->pd_pcpresent[ppgi->pi_pdi]--;     /* dec pt present cnt*/
        if (ppgi->pi_pte.pt_resident) {
            /*
             *  If we free the last resident page on the page table, reduce the
             *  page table's overcommit.
             */
            if (--(ppgi->pi_ppd->pd_pcresident[ppgi->pi_pdi]) == 0) {
                pgReleasePt(ppgi->pi_ppd, ppgi->pi_pdi, PG_RESIDENT);
            }
        }

        /*
         *  If not uvirt
      /  */
        if (ppgi->pi_pte.pt_uvirt == 0) {

            /*
             *  If page is dirty, propogate dirty bit to vp and check for
             *  illegal writes.
             */
            if (ppgi->pi_pte.pt_dirty) {
                if (ppgi->pi_pte.pt_ring3write == 0) {
                    pgIllegalWrite(ppgi->pi_pvp);
                }
                ppgi->pi_pvp->vp_flags |= (VP_DIRTY | VP_SHDIRTY);
                if (ppgi->pi_pvp->vp_df) {
                    /*
                     *  Don't keep dfs if they have no valid data
                     */
                    ppgi->pi_pvp->vp_df = 0;
                    SMFreeDF(ppgi->pi_ppf->pf_block);
#ifdef PGSTRICT
                    ppgi->pi_ppf->pf_block = -1;
#endif
                }
            }

            /*
             *  Various sanity checks
             */
            pgAssert(ppgi->pi_pte.pt_frame == ppgi->pi_pvp->vp_frame);
            pgAssert(ppgi->pi_ppf == pgFrameToPF(ppgi->pi_pte.pt_frame));
            pgAssert((ppgi->pi_pvp->vp_flags & (VP_PF | VP_PFIDLE)) == VP_PF);

            if (--(ppgi->pi_pvp->vp_refcount) == 0) {
                ppf = ppgi->pi_ppf;
                pgAssert(LOCKS(ppf) == 0); /* don't free a locked page */
                frame = ppgi->pi_pte.pt_frame;
                goto freevp;
            }
            if (--(ppgi->pi_ppf->pf_refcount) == 0) {
                ppgi->pi_pvp->vp_pfidle = 1;
                pgIdlePF(ppgi->pi_ppf, ppgi->pi_pte.pt_frame);
            }
        }

    /*
     *  If the page was not present and not uvirt
     */
    } else if (ppgi->pi_pte.pt_uvirt == 0) {
        /*
         *  If the vp ref count hits 0, free the vp and its overcommit
         */
        if (--(ppgi->pi_pvp->vp_refcount) == 0) {

            /*
             *  If a pf is attached, free it
             */
            if (ppgi->pi_pvp->vp_pf) {
                frame = ppgi->pi_pvp->vp_frame;
                ppf = pgFrameToPF(frame);

                /*
                 *  If the pf is idle, reclaim it before freeing it
                 */
                if (ppgi->pi_pvp->vp_pfidle) {
                    pgDeletePF(ppf);
                }
              freevp:
                pgAssert(ppgi->pi_pvp->vp_pf);

                if ((ppgi->pi_pvp->vp_flags & VP_DISCARDABLE) &&
                    ((ppgi->pi_pvp->vp_flags & VP_SOW) == 0) ) /* fix for exit */
                      pgDiscardableInmem--;

                ppgi->pi_pvp->vp_flags &= ~(VP_PF | VP_PFIDLE);
                ppgi->pi_pvp->vp_block = ppf->pf_block;
                pgFreePF(ppf, frame);
            }

            /*
             *  Increase appropriate value is the array of overcommit
             *  changes that PGFree will take care of when we are done.
             *  Assert that the page isn't discardable zerofill, since
             *  their vp ref count should never hit zero.
             *  Free the overcommit of SOWs as swappable.
             */
            if ((ppgi->pi_flags & PG_ALLOC) == 0) {
                i = ppgi->pi_pvp->vp_flags >> PG_COMMITSHIFT;
                pgAssert(i != (PG_RESIDENT|PG_DISCARDABLE) >> PG_COMMITSHIFT);
                if (ppgi->pi_pvp->vp_sow) {
                    i = PG_SWAPPABLE >> PG_COMMITSHIFT;
                }
                ((ulong_t *)ppgi->pi_data1)[i]++;
            } else if (ppgi->pi_pvp->vp_wanted) {
                pgClearSemVP(ppgi->pi_pvp);
            }
            pgFreeVP(ppgi->pi_pvp);
        }
    }
    return(0);
}


/***LP  pgCommit - Commit to provide storage of the specified type.
 *
 *      pgCommit performs overcommit calculations to ensure that
 *      enough virtual and/or physical memory exists to commit to
 *      allocating physical memory of the specifed type when needed.
 *
 *      If swapping is disabled, all overcommit will come from the
 *      resident pool.  However, the swappable and discardable counts will be
 *      maintained in case swapping is later turned on.
 *
 *      ENTRY   npages - page count
 *              flags -
 *                  PG_SWAPPABLE: swappable pages (0 is also swappable)
 *                  PG_RESIDENT: resident pages (fixed, locked or resident)
 *                  PG_DISCARDABLE
 *                  PG_SWAPONWRITE: same as PG_SWAPPABLE
 *      EXIT    0 on success, else error code
 *
 *      take overcommit semaphore
 *      modifiy overcommit count
 *      if (swapping is enabled)
 *          make sure we don't eat into pageable pad
 *          if (not recursively entered)
 *              allocate vps
 *              allocate swap space
 *      else ;swapping off
 *          make sure everything fits in physical memory
 *      clear overcommit semaphore
 */

int
pgCommit(npages, flags)
ulong_t npages;
int flags;
{
    int err;
    ulong_t newtotalcommit;
    int dpg;
// overcommit
    int pgCHeapSemTaken = 0;
// end overcommit

    pgAssert((flags & (PG_UVIRT | PG_NOTIFYBLOCK)) == 0);

pgCRestart:
    if (err = KSEMRequestMutex((HKSEM)&pgCommitSem, (flags & PG_NOBLOCK) ? 0 : -1)) {
        if (err == ERROR_TIMEOUT) {
            err = ERROR_NOBLOCK;
        }
        return(err);
    }

    /*
     * swap on write pages are committed for as swappable.
     */
    if (flags & PG_SWAPONWRITE) {
        flags = PG_SWAPPABLE;
    }

    /*
     *  Add the requested type of memory to the appropriate commit
     *  count.  Assume success.  The overcommit counts are effectively in
     *  an array indexed by state >> PG_COMMITSHIFT.
     */
    (&pgSwappablePages)[(flags & (PG_DISCARDABLE|PG_RESIDENT)) >>
                        PG_COMMITSHIFT] += npages;

#ifdef PERFVIEW
    if (flags & PG_RESIDENT) {                         //@@car01
/***PV - Perfview measurement hook                     //@@car01
 *                                                     //@@car01
 *      TRACE MINOR = <PVWEI_EQ_mem_QCTResident_0>,    //@@car01
 *      TP    = @,                                     //@@car01
 *      TYPE    = (PVW),                               //@@car01
 *      GROUP   = NOSTRICT                             //@@car01
 */                                                    //@@car01
    PvwEnterQ(mem,QCTResident,0);                      //@@car01 bulk(npages)
    }                                                  //@@car01
#endif

    pgAssert((&pgDiscardablePages - &pgResidentPages) == 1 &&
             (&pgResidentPages - &pgSwappablePages) == 1 &&
             (PG_SWAPPABLE >> PG_COMMITSHIFT) == 0 &&
             (PG_RESIDENT >> PG_COMMITSHIFT) == 1 &&
             (PG_DISCARDABLE >> PG_COMMITSHIFT) == 2);

    /*
     *  newtotalcommit is the amount of total overcommit we will need
     *  i.e., the number of VPs the allocate.
     */
    newtotalcommit = pgResidentPages+pgDiscardablePages+pgSwappablePages+
                     1 + (npages / (PAGESIZE / sizeof(struct vp_s)));

    /*  dpg is the number of pages we need to add to the VP array.
     *  Add in 3 pages, 1 for vp array if we grow the vp array
     *  1 for page table if vp array grows past 4 meg boundary
     *  and 1 to leave in the free list (it can't be empty - see pgAllocVP)
     */
    dpg = ((ulong_t)(pgpVPBase + newtotalcommit + 3) >> PAGESHIFT) - pgVPEndPg;

    if (PGSwapEnabled) {
        /*
         *  If we don't eat into out pageable pad...
         */
// d63923 - if pgdriver is in the process of allocating resident pages and the
//    second call to pgPinOnePt fails (because of the inability to commit
//    a resident page for a page table) then the call to pgwfree will cause a
//    panic in pgStrict on the first vp.  The reason is that the vp's all have
//    their vp_resident bit and vp_pf bit set but do not have a pf associated
//    with the vp. This causes the panic in pgStrict. Note: the pfs are
//    attached in pgAllocPF after the call to pgdriver returns to PGAlloc.
//    To prevent this situation w/o rewriting most of the resident allocation
//    code, add a resident pad onto any resident allocation that exceeds
//    256 pages.
//      if (pgResidentPages <= pgMaxResPages) {

        if ( (!(flags & PG_RESIDENT)) ||
             (pgResidentPages+PG_RES_PAGE_PAD(npages)) <= pgMaxResPages) {
// end d63923
            /*
             *  If we are haven't re-entered our selves (from the vp or swap
             *  manager) allocate vps and swap space.
             */
            if (pgCommitSem.ksm_cusNest == 1) {
// overcommit
               if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
                    if (!(flags & PG_NOBLOCK) && !pgCHeapSemTaken) {
                        /*
                         * b722570
                        * Avoid a deadlock. If SMSetSwapSpace() has to increase
                         * the swap space, it will need the semaphore for the
                         * swap manager's heap. If at the same time, the heap
                         * manager is reallocating this heap, then the heap
                         * manager will have the heap semaphore and could need the
                         * page manager's commit semaphore. If the heap semaphore
                         * is not available, release the commit semaphore, wait for
                         * the heap semaphore, and then restart.
                         */
//                      if ((err = KSEMRequestMutex(
//                          (HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem),
//                          0)) != NO_ERROR) {
                        if ((err = VMGetSem(smHobheap,
                             VMGS_TAKESEM|VMGS_ERRBLOCK,
                             NA)) != NO_ERROR)  {
                            /*
                             *  Put commit counts back to where they were before
                             *  taking the commit semaphore.
                             */
                            (&pgSwappablePages)[(flags & (PG_DISCARDABLE|PG_RESIDENT)) >>
                                                PG_COMMITSHIFT] -= npages;
                            KSEMReleaseMutex((HKSEM)&pgCommitSem);

                            if ( (err != ERROR_TIMEOUT) ||
                                 ((err = VMGetSem(smHobheap, VMGS_TAKESEM,
                                         NA)) != NO_ERROR)) {
                                return(err);
                            }
                            pgCHeapSemTaken++;
                            goto pgCRestart;
                        }
                        pgCHeapSemTaken++;
                    }

                    err = SMSetSwapSpace(pgSwapFileSize(), flags & PG_NOBLOCK);
                    if (err)
                       goto errorexit;
                }   // end do_overcommit
//end overcommit
                if (dpg > 0) {
                    /*
                     *  Here we grow the vp array.  There is no way to do
                     *  that without blocking, so return error if PG_NOBLOCK
                     */
                    err = ERROR_NOBLOCK;
                    if ((flags & PG_NOBLOCK) || (err = pgResizeVP(dpg))) {
                        /*
                         *  Don't bother undoing the swap space.  The next call
                         *  to commit will set it back.
                         */
                        goto errorexit;
                    }
                }

            /*
             *  If this is a recursive overcommit call, the vp code and
             *  the swapper has seen to it that there is enough swap space
             *  and vps to hold any overflow caused by this new
             *  memory.  Therefore, we can return success now
             *  in that case.
             */
            } else {
                pgAssert(pgCommitSem.ksm_cusNest == 2); /* only recurse once */
            }

        /*
         *  This request will leave us with too little pageable memory.
         */
        } else {
            err = ERROR_NOT_ENOUGH_MEMORY;
            goto errorexit;
        }

    /*
     *  If swapping is off, the sum of the resident, swappable and discardable
     *  pages must all fit in core at the same time.  If not, error out.
     */
//  } else if (newtotalcommit <= pgPhysPages) {   d63923
    } else if ( (newtotalcommit+PG_RES_PAGE_PAD(npages)) <= pgPhysPages) {
        if (pgCommitSem.ksm_cusNest == 1 && dpg > 0) {
            err = ERROR_NOBLOCK;
            if ((flags & PG_NOBLOCK) || (err = pgResizeVP(dpg))) {
                goto errorexit;
            }
        }
    } else {
        err = ERROR_NOT_ENOUGH_MEMORY;
        goto errorexit;
    }

  exit:
    KSEMReleaseMutex((HKSEM)&pgCommitSem);
// overcommit
    if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
        if (pgCHeapSemTaken)
//          KSEMReleaseMutex((HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem));
            VMClearSem(smHobheap);
    }
// overcommit
    return(err);

  errorexit:
    /*
     *  On error, put commit counts back to where they should be.
     */
    (&pgSwappablePages)[(flags & (PG_DISCARDABLE|PG_RESIDENT)) >>
                        PG_COMMITSHIFT] -= npages;
    goto exit;
}


/***LP  pgCommitFree - Release prior commitment to provide storage
 *
 *      pgCommitFree undoes overcommit calculations to release
 *      a previous commitment to allocate physical memory.
 *
 *      ENTRY   npages - page count
 *              type - type of commitment
 *                  PG_SWAPPABLE: swappable pages (also 0)
 *                  PG_RESIDENT: resident pages (fixed, locked or resident)
 *                  PG_DISCARDABLE
 *      EXIT    NONE
 *
 *      take commit sem
 *      reduce appropriate commit count
 *      if (not entered recursively)
 *          reduce number of vps
 *          reduce swap file space
 *      clear pgCommitSem
 */

void
pgCommitFree(npages, flags)
ulong_t npages;
int flags;
{
    int dpg;
// overcommit
    int err;
    int pgCHeapSemTaken = 0;
// end overcommit

    TKMayBlock();       /* swappable code */

pgCFRestart:
    while(KSEMRequestMutex((HKSEM)&pgCommitSem, -1));

    /*
     *  Decrement the appropriate commit count
     */
    (&pgSwappablePages)[(flags & (PG_DISCARDABLE|PG_RESIDENT)) >>
                        PG_COMMITSHIFT] -= npages;

#ifdef PERFVIEW
    if (flags & PG_RESIDENT) {                       //@@car01
/***PV - Perfview measurement hook                   //@@car01
 *                                                   //@@car01
 *      TRACE MINOR = <PVWEI_LQ_mem_QCTResident_0>,  //@@car01
 *      TP    = @,                                   //@@car01
 *      TYPE    = (PVW),                             //@@car01
 *      GROUP   = NOSTRICT                           //@@car01
 */                                                  //@@car01
       PvwLeaveQ(mem,QCTResident,0);                 //@@car01
    }                                                //@@car01
#endif

    /*
     *  If we are not re-entering the commit code, free vps and swap space
     */
    if (pgCommitSem.ksm_cusNest == 1) {
// overcommit
       if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
           if ( (!pgCHeapSemTaken) && PGSwapEnabled) {
                /*
                 * b722570
                 * Avoid a deadlock. If SMSetSwapSpace() has to increase
                 * the swap space, it will need the semaphore for the
                 * swap manager's heap. If at the same time, the heap
                 * manager is reallocating this heap, then the heap
                 * manager will have the heap semaphore and could need the
                 * page manager's commit semaphore. If the heap semaphore
                 * is not available, release the commit semaphore, wait for
                 * the heap semaphore, and then restart.
                 */
//              if ((err = KSEMRequestMutex(
//                 (HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem),
//                 0)) != NO_ERROR) {
                if((err = VMGetSem(smHobheap,
                     VMGS_TAKESEM|VMGS_ERRBLOCK,
                     NA)) != NO_ERROR) {
                    /*
                     *  Put commit counts back to where they were before
                     *  taking the commit semaphore.
                     */
                    (&pgSwappablePages)[(flags & (PG_DISCARDABLE|PG_RESIDENT)) >>
                                        PG_COMMITSHIFT] += npages;
                    KSEMReleaseMutex((HKSEM)&pgCommitSem);

                    if ((err != ERROR_TIMEOUT) ||
                        ((err = VMGetSem(smHobheap, VMGS_TAKESEM,
                                NA)) != NO_ERROR)) {
                            return;
                    }
                    pgCHeapSemTaken++;
                    goto pgCFRestart;
                }
                pgCHeapSemTaken++;
            }
        } // end do_overcommit
// end overcommit

        dpg = ((ulong_t)(pgpVPEnd - 3) - (ulong_t)pgpVPEnd->vp_blink) >>
              PAGESHIFT;
        dpg = (int)min(dpg,(pgVPEndPg- ((ulong_t)(pgpVPBase+ pgSwappablePages+
                    pgResidentPages+pgDiscardablePages + 3) >> PAGESHIFT)));
        pgAssert(dpg >= 0);
        dpg = -dpg;
        if (dpg < 0) {
            pgResizeVP(dpg);
        }
// overcommit
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
            if (PGSwapEnabled) {
               SMSetSwapSpace(pgSwapFileSize(), 0);
            }
        }
// end overcommit
    }

    /*
     *  If we are freeing resident memory, wakeup anyone waiting for
     *  short-term lock overcommit since less resident means more short locks
     */
    if ((flags & PG_RESIDENT) && pgShortLockWanted) {
        pgShortLockWanted = 0;
        TKWakeup(&pgShortLockWanted, 0, 0);
    }

    KSEMReleaseMutex((HKSEM)&pgCommitSem);
// overcommit
    if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
        if (pgCHeapSemTaken)
//          KSEMReleaseMutex((HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem));
            VMClearSem(smHobheap);
    }
// end overcommit
}


/***LP  pgShortLockCommit - Request short-term lock overcommit
 *
 *      Unless devices are holding their locks for longer than they
 *      should, this routine can never fail for reasonably sized requests
 *      (reasonable depends on the amount of memory in the system, but
 *      is always at least 64k).
 *
 *      ENTRY:  npages - number of pages requested
 *              flags - PG_NOBLOCK
 *      EXIT:   0 if success, else error code
 *
 *      if (we don't have enough space exists to satisfy request)
 *          if (it might help to block)
 *              block and restart
 *      else
 *          increase pgShortLockPages
 */
int
pgShortLockCommit(npages, flags)
ulong_t npages;
int flags;
{
    int err;

    /*
     *  For sort-term lock commit we don't have to take the overcommit
     *  semaphore because 1. we don't block (except to wait for others to free
     *  commit and we want to allow them through unimpeded by the sem) and
     *  2. the resident commit code increments its counts before blocking.
     */
  restart:
    if (pgResidentPages+pgShortLockPages+npages+PADPAGES <= pgPhysPages) {

        /*
         *  If we have space, just increment the count and return
         */
        pgShortLockPages += npages;
        return(0);

    } else {
        /*
         * Request is for a short term lock that we cannot immediately
         * satisfy.  If a decrease in the number of short-term locked
         * pages would allow the lock to succeed, block and wait for
         * this to occur.
         */
        if (PGSwapEnabled &&
            pgResidentPages+npages+PADPAGES <= pgPhysPages) {

            if (flags & PG_NOBLOCK) {
                return(ERROR_NOBLOCK);
            }

            pgShortLockWanted = 1;
            if (err = TKSleep(&pgShortLockWanted,TIMEOUT_SHORTLOCK,0,0)) {
                return(err==ERROR_TIMEOUT ? ERROR_NOT_ENOUGH_MEMORY : err);
            }
            goto restart;

        /*
         *  Blocking would not allow this request to be satisfied,
         *  just return error.
         */
        } else {
            return(ERROR_NOT_ENOUGH_MEMORY);
        }
    }
}


/***LP  pgShortLockCommitFree - free short-term lock overcommit
 *
 *      This routine cannot fail or block.
 *
 *      ENTRY:  npages - number of pages of overcommit to free
 *      EXIT:   none
 *
 *      decrease overcommit count
 *      if (someone is waiting for commit to be freed)
 *          wake him up
 */
void
pgShortLockCommitFree(npages)
ulong_t npages;
{
    /*
     *  Since we can't block freeing short term commit, don't bother
     *  taking the commit sem.
     */
    pgShortLockPages -= npages;
    pgAssert((int) pgShortLockPages >= 0);
    if (pgShortLockWanted) {
        pgShortLockWanted = 0;
        TKWakeup(&pgShortLockWanted, 0, 0);
    }
    return;
}


/***LP  pgPinPt - Pin page tables into core.
 *
 *      pgPinPt locks page tables into core, possibly requiring page table
 *      allocation, reclaim, or swapin.  The page tables are forced to remain
 *      resident by marking their pte resident and incrementing
 *      their pde present and allocated counts.
 *
 *      Page tables are given swappable overcommit thoughout their lives.
 *      If a page table's resident/locked count is greater than 0, the
 *      page table must have resident overcommit also.
 *      The ptes pointer to pages containing page tables are always
 *      marked PT_RESIDENT even when the associated page table
 *      does not have resident overcommit.  This will prevent the page from
 *      being idled through a mechanism other than unpin.
 *      When swapping is off, page tables only have swappable overcommit.
 *
 *      ENTRY   page - base virtual page number
 *              pptda - pointer to ptda
 *              npages - page count
 *              flags - PG_RESIDENT, PG_NOBLOCK, PG_NOTIFYBLOCK
 *              pppd - pointer to pgdata return structure
 *      EXIT    0 on success, else error code
 *
 *      for each page table in range:
 *          if the pde is not valid or needs resident overcommit
 *              call pgPinForcePresent
 *          increase present and alloc'ed pt counts
 */

int
pgPinPt(page, pptda, npages, flags, pppd)
page_t page;
struct ptda_s *pptda;
int npages;
int flags;
struct pgdata_s **pppd;
{
    struct pgdata_s *ppd;
    register int pdi;
    ulong_t npt;
    int err;

    if (pgPageToPd(page, pptda, SSToDS(&ppd))) {
        return(ERROR_INVALID_ADDRESS);
    }
    pdi = pgPagePdToPDI(page, ppd);
    npt = pgNPagesToNPT(page, npages);
    *pppd = ppd;

    /*
     *  Loop through all the page tables in the range and force them
     *  present.  Keep them that way by incrementing their allocated and
     *  present pte counts.
     */
    for (; npt--; pdi++) {
        pgPinOnePt(pdi,ppd,flags,err, 0);
        if (err) {
            goto undo;
        }
    }
    return(0);

undo:
    /*
     *  If we error during a pin, unpin any page tables we have already hit
     */
    npt = pdi - pgPagePdToPDI(page, ppd);
    for (;npt; --npt) {
        pgUnpinPt(page, ppd, 1, flags);
        page += NPTEPPT;
    }
    return(err);
}


/***EP  pgPinForcePresent - forces present a not-present page table
 *
 *      This routine is called by pgPinPt to force present a page table
 *      and/or give it resident overcommit.  If the pt is invalid, a new
 *      a new one will be allocated.  If the pt is swapped out, it will
 *      be swapped in.  If the page is idle, it will be reclaimed.  If the
 *      page is present, the page's state will be set to resident.
 *      If the PG_RESIDENT flag is specified, the page table will be
 *      given resident overcommit in addition to its normal swappable
 *      commit.
 *
 *      The routine will also make sure the page table mapping the page table
 *      in question is present and/or has resident overcommit.
 *
 *      ENTRY:  pdi - page directory index of target page table
 *              ppd - pointer to pgdata structure describing target arena
 *              flags - PG_RESIDENT - give page table resident commit
 *                      PG_NOBLOCK - return error rather than yield cpu
 *                      PG_NOTIFYBLOCK - return ERROR_BLOCKED if we blocked
 *                      PG_IGNOREINTS - spin on ERROR_INTERRUPT
 *      EXIT:   0 or error code
 *r
 *      pin the page table's page table
 *      if (the page table isn't allocated)
 *          allocate it
 *      else
 *          if (pt needs resident overcommit)
 *              request resident overcommit
 *          if (page table is not present)
 *              swap it in
 *      if (needed)
 *          update pde high and low counts
 *      clear page table's pte semphore
 *      unpin pt's pt
 */
int
pgPinForcePresent(pdi, ppd, flags)
register ulong_t pdi;
register struct pgdata_s *ppd;
ulong_t flags;
{
    int err = 0;
    struct pf_s *ppf;
    struct vp_s *pvp;
    page_t pagept;
    register struct pte_s *pptept;
    struct pgdata_s *ppdpt;
    ulong_t pdipt;
    int highindex, lowindex;
    ulong_t hibound, lobound;
    int fnewres = 0;    /* set if getting resident commit for first time */
    int fupdpde = 0;    /* set if are making a currently 0 pde valid */

    pgAssert(ppd != &pgShrData || pdi < MAXGLOBALSHRPDE);  // GSR

    /*
     *  pagept = virtual page number of the page the pt sits on
     *  Pin that page and take its page semaphore.
     */
    pagept = (((page_t) ppd->pd_ppte) >> PAGESHIFT) + pdi;
    if (err = pgPinPt(pagept, 0, 1, flags, SSToDS(&ppdpt))) {
        return(err);
    }
    pptept = &ppd->pd_ppde[pdi];
    pgAssert(pptept == pgPagePdTopPte(pagept, &pgData));
    pdipt = pgPagePdToPDI(pagept, &pgData);

    /*
     *  If the page table is unallocated, allocate it here
     */
    if (pptept->pt_long == 0) {
  allocpt:
        if ((flags & PG_ALLOC) == 0) {
            err = ERROR_ACCESS_DENIED;
            goto unpin;
        }
        if (flags & PG_NOBLOCK) {
            err = ERROR_NOBLOCK;
            goto unpin;
        }
        pgAssert((flags & PG_NOTIFYBLOCK) == 0);
        if (err = pgCommit(1, PG_SWAPPABLE)) {
            goto unpin;
        }
        /*
         *  We might have blocked in the commit.  Make sure that no one
         *  else allocated the page table while asleep.  If so, free the
         *  commit and fall through to the normal path.
         */
        if (pptept->pt_long != 0) {
            pgCommitFree(1, PG_SWAPPABLE);
            goto validpt;
        }

        /*
         *  We still need to allocate the thing.
         *  Get resident commit for the new page table if it needs it
         */
        if (flags & PG_RESIDENT) {
            if (PGSwapEnabled) {
                if (err = pgCommit(1, PG_RESIDENT)) {
                    pgCommitFree(1, PG_SWAPPABLE);
                    goto unpin;
                }
                /*
                 *  We might have blocked in the commit.  Make sure that no one
                 *  else allocated the page table while asleep.  If so, free the
                 *  commit and fall through jump to the normal path.
                 */
                if (pptept->pt_long != 0) {
                    pgCommitFree(1, PG_RESIDENT);
                    pgCommitFree(1, PG_SWAPPABLE);
                    goto validpt;
                }
            } else {
                pgResPT++;
            }
            fnewres = 1;
            pgcPteResident[pdipt]++;
        }

        /*
         *  Allocate a vp and attach it to the pte.
         */
        pvp = pgAllocVP();
        pgAssert(pvp->vp_refcount == 1);
        pvp->vp_flags = VP_PF;
        if ((pvp->vp_flags & VP_DISCARDABLE) &&
            ((pvp->vp_flags & VP_SOW) == 0) )
           pgDiscardableInmem++;
#ifdef PGSTRICT
        pvp->vp_block = -2;
#endif
        pgSetSem(pvp);
        if (ppd != &pgData) {
// GSR start
            if (ppd != &pgShrData) {
                pvp->vp_hob = pgCRPtHOB;
                pvp->vp_obpg = (ushort_t)
                        (pagept - ((ulong_t)pgpCRPageTables >> PAGESHIFT));
            } else {
                pvp->vp_hob = pgShrCRPtHOB;
                pvp->vp_obpg = (ushort_t)
                        (pagept - ((ulong_t)pgpShrCRPageTables >> PAGESHIFT));

            }
// GSR end
        } else {
            pvp->vp_hob = pgSysPtHOB;
            pvp->vp_obpg = (ushort_t)
                         (pagept - ((ulong_t)pgpSysPageTables >> PAGESHIFT));
        }
        pvp->vp_resident = 0;
        pvp->vp_flags |= flags & PG_RESIDENT;

        pptept->pt_long = PT_RING3ACCESS | PT_RING3WRITE | PT_RESIDENT |
                          (pgpVPToVPI(pvp) << PT_FRAMESHIFT);

        /*
         *  The page table is now setup like a normal not present
         *  page table, with it's semaphore set.
         *  Now allocate the PF for it.
         */
        if (err = pgAllocPF(pptept, pagept, 1, flags | PG_ZEROFILL, 0)) {
            pgCommitFree(1, PG_SWAPPABLE);
            goto resundo;
        }
        pgcPteAllocated[pdipt]++;
        pgcPtePresent[pdipt]++;
        fupdpde = 1;

    /*
     *  The page table is already allocated
     */
    } else {
  validpt:
        if (err=pgTakeSem(pptept, 1,
                          flags & (PG_NOBLOCK|PG_NOTIFYBLOCK|PG_IGNOREINTS))){
            /*
             *  if the page table was freed while we waited, jump back up to
             *  allocate it.  Otherwise, it's a real error and we have to go home.
             */
            if (err == ERROR_ACCESS_DENIED) {
                goto allocpt;
            } else {
                goto unpin;
            }
        }

        /*
         *  If the caller requested resident commit and we don't already have
         *  it, get it here.
         *  Don't double commit the page table if swapping is off.
         */
        if ((flags & PG_RESIDENT) && ppd->pd_pcresident[pdi] == 0) {
            pgAssert((flags & (PG_NOTIFYBLOCK)) == 0);
            if (PGSwapEnabled) {
                if (err = pgCommit(1, (flags & PG_NOBLOCK) | PG_RESIDENT)) {
                    goto clearsem;
                }
            } else {
                ++pgResPT;
            }
            fnewres = 1;
            pgcPteResident[pdipt]++;
        }

        /*
         *  If the page table is not currently pinned, bring it in.
         */
        if (ppd->pd_pcpresent[pdi] == 0) {

            /*
             *  If the page is swapped out, bring it in.
             */
            if (pptept->pt_present == 0) {
                pvp = pgFrameToVP(pptept->pt_frame);
                lobound = hibound = pgPageablePAI.pai_end.pb_frame;
                if (flags & PG_RESIDENT) {
                    hibound = pgHiResBoundfr;
                    lobound = pgLoResBoundfr;
                }
                if (err = pgForcePresent(pptept, pagept, 0, pvp,flags,
                                         lobound, hibound)){
                    pgAssert(err != ERROR_GUARDPAGE);
                    goto resundo;
                }
            } else {
                pvp = pgFrameToPF(pptept->pt_frame)->pf_pvp;
            }
            pgAssert(pvp->vp_resident == 0);
            pvp->vp_flags |= flags & PG_RESIDENT;
            pgAssert(pptept->pt_resident == 0);
            pptept->pt_resident = 1;
            fupdpde = 1;

        } else if (fnewres) {

            /*
             *  If a present page table is being upgraded to resident
             *  overcommit it might have to be moved into the resident area.
             *  We know this is not needed if we just swapped the thing in or
             *  allocated it, since that code will use the PG_RESIDENT flag
             *  that was passed in to get it right.
             */
            ppf = pgFrameToPF(pptept->pt_frame);
            pgAssert(ppf->pf_pvp->vp_resident == 0);
            ppf->pf_pvp->vp_resident = 1;

            /*
             *  If the page is not in the right place already...
             */
            if (!pgIsResident(ppf)) {
                err = ERROR_NOBLOCK;
                if ((flags & PG_NOBLOCK) ||
                (err = pgMoveFrame(pptept, pagept,ppf->pf_pvp,PG_RESIDENT,0))){
                    ppf->pf_pvp->vp_resident = 0;    //b723155
                    goto resundo;
                }
            }
        }
    }

    /*
     *  If a page table was just promoted from invalid to present, update
     *  the pde and the high and low pde counts (if needed).
     */
    if (fupdpde) {
        /*
         * Update the real page directory if we are in system space
         */
        if (ppd == &pgData) {
            pgpPageDir[pdi+(MINSYSLADDR >> PAGEDIRSHIFT)].pt_long =
                                                               pptept->pt_long;

        /*
         *  If the page table is not within system memory,
         *  update the high and low pde counts.  If this is the
         *  current task, also update the real page directory.
         */
        } else {
// GSR start
            // also updata the real page directory if were in the
            //    global shared region
            if (ppd == &pgShrData) {
                pgpPageDir[pdi+(MINGLOBALSHRLADDR >> PAGEDIRSHIFT)].pt_long =
                                                          pptept->pt_long;
            } else {
// GSR end
                lowindex = (int) ppd->pd_cpdelow - 1;
                highindex = (int) ppd->pd_maxpde - (int) ppd->pd_cpdehigh;
                /*
                 *  Only update counts if between the two bounds
                 */
                if ((int) pdi > lowindex && (int) pdi < highindex) {
                    /*
                     *  Update the count closest to the pdi (ties go high)
                     */
                    if ((int) pdi - lowindex < highindex - (int) pdi) {
                        ppd->pd_cpdelow = (ushort_t) (pdi + 1);
                    } else {
                        ppd->pd_cpdehigh = ppd->pd_maxpde - (ushort_t) pdi;
                    }
                }
                /*
                 *  If this page table is within the non-system memory of
                 *  the active process, update the real page directory.
                 */
                if (ppd == (struct pgdata_s *) &(pPTDACur->ptda_pgdata)) {
                    pgAssert(ppd->pd_base == 0);  /* for Yawl add pd_base below */
                    pgpPageDir[pdi].pt_long = pptept->pt_long;
                }
            }
        }
    }

  clearsem:
    pgClearSem(pptept, 1);
  unpin:
    pgUnpinPt(pagept, &pgData, 1, flags);
    return(err);

  resundo:
    /*
     *  Error clean-up after we have possibly gotten resident commit.
     */
    if (fnewres) {
        pgcPteResident[pdipt]--;
        if (PGSwapEnabled) {
            pgCommitFree(1, PG_RESIDENT);
        }
    }
    goto clearsem;
}


/***LP  pgUnpinPt - Unpin page tables
 *
 *      pgUnpinPt unlocks page tables, possibly freeing or setting up page
 *      tables as reclaimable.
 *
 *      This call can yield if all of the pages in the page table
 *      are not present.
 *
 *      ENTRY   page - base virtual page number
 *              ppd - pointer to pgdata structure
 *              npages - page count
 *              flags - PG_RESIDENT also decrement resident/locked counts
 *      EXIT    NONE
 *r
 *      for (each page table)
 *          if (PG_RESIDENT flag specified)
 *              if (--resident count == 0)
 *                  call pgReleasePt to free overcommit
 *          if (--alloccount == 0)
 *              if (--presentcount != 0) panic
 *              call pgReleasePt to free the page table
 *          else if (--presentcount == 0)
 *              call pgReleasePt to idle the page table
 */

void
pgUnpinPt(page, ppd, npages, flags)
page_t page;
register struct pgdata_s *ppd;
int npages;
int flags;
{
    register ulong_t pdi;
    ulong_t npt;

    /*
     *  For all the page tables in the range, decrement their allocated
     *  and present counts, freeing or idling as needed.
     */
    pdi = pgPagePdToPDI(page, ppd);
    for (npt = pgNPagesToNPT(page, npages); npt--; pdi++) {
        pgUnpinOnePt(pdi,ppd,flags,1);
    }
}


/***LP  pgReleasePt - idles or frees a page table
 *
 *      pgReleasePt is called when the present, allocated or resident count
 *      of a page table reaches zero.  If the present count hit zero, the
 *      page table is made pageable so the ager can put it on the idle list.
 *      If the allocated count hit zero, the page table is freed.
 *      In both cases, the counts of present page tables on the high and
 *      low ends of the arena are updated as needed.
 *      If the resident/locked count hit zero the page table's resident
 *      overcommit is freed.
 *
 *      This call can yield if all of the pages in the page table
 *      are not present.
 *
 *      ENTRY:  ppd - pointer to pgdata structure describing page tables
 *              pdi - index of individual page table to modify
 *              flags - PG_RESIDENT free the page table's resident overcommit
 *                                  but don't invalidate pde or idle the pt
 *                      PG_NOBLOCK - only honored when when the present count
 *                                   hits zero when the allocated count is
 *                                   non-zero and resident count was already 0
 *      EXIT:   0 if success, else error (ignore if PG_NOBLOCK isn't set)
 *
 *      if (not PG_RESIDENT request)
 *          zero pde
 *          update high and low pde counts
 *      pin pt's pt
 *      take pt's pte sem
 *      if (PG_RESIDENT && res count == 0)
 *          free resident overcommit
 *          goto clearsem
 *      if (page table wasn't validated during blocking)
 *          make pt pageable
 *          if (allocated count is zero)
 *              free page table
 *      clear sem
 *      unpin
 */
int
pgReleasePt(ppd, pdi, flags)
register struct pgdata_s *ppd;
register int pdi;
int flags;
{
    page_t pagept;
    struct pgdata_s *ppdpt;
    register struct pte_s *pptept;
    struct pte_s *pptetmp;
    int pditmp;
    int err;
    int active;
    struct pginfo_s pgi;
    struct vp_s *pvp;
    struct pf_s *ppf;

    pagept = (((page_t) ppd->pd_ppte) >> PAGESHIFT) + pdi;
    pptept = &ppd->pd_ppde[pdi];
    pgAssert(pptept == pgPagePdTopPte(pagept, ((struct pgdata_s *) &pgData)));

    /*
     *  Pin the page table's page table and take the pte semaphore for the
     *  page table
     */
    if (err = pgPinPt(pagept, 0, 1, flags | PG_IGNOREINTS,
                SSToDS(&ppdpt))) {
        return(err);
    }
    if (err = pgTakeSem(pptept, 1, flags | PG_IGNOREINTS)) {
        goto unpin;
    }

    /*
     *  If resident count hits zero, free this pt's resident overcommit
     *  and decrease the page table's page table's resident count.
     *  Then jump to the return path.
     */
    if (flags & PG_RESIDENT) {
        /*
         *  Someone may have incremented the resident count while blocked, so
         *  make sure we still have to do this.
         */
        if (ppd->pd_pcresident[pdi] == 0) {

            if (PGSwapEnabled) {
                pgCommitFree(1, PG_RESIDENT);
            } else {
                --pgResPT;
                pgAssert(pgResPT >= 0);
            }
            pgFrameToPF(pptept->pt_frame)->pf_pvp->vp_resident = 0;
            pgcPteResident[(pagept-MINSYSPAGE) >> (PAGEDIRSHIFT-PAGESHIFT)]--;
        }

    /*
     *  If the page table table is still invalid after we blocked above,
     *  actually do the free or idle.
     */
    } else if (ppd->pd_pcpresent[pdi] == 0) {
        pgAssert(ppd->pd_pcresident[pdi] == 0 &&
                 pptept->pt_long != 0);   /* caught by takesem */

        if (ppd != &pgData) {
// GSR start
            if (ppd == &pgShrData) {
//             printf("Freeing shared pdi = %x\n",pdi);
               pgpPageDir[(MINGLOBALSHRLADDR >> PAGEDIRSHIFT) + pdi].pt_long = 0;
            } else {
// GSR end
                pgAssert(ppd->pd_base == 0);        /* for Yawl add pd_base */
                active = FALSE;

                /*
                 *  If this page table is in the active process, zero the real page
                 *  directory entry.
                 */
                if (ppd == (struct pgdata_s *) &(pPTDACur->ptda_pgdata)) {
                    pgpPageDir[pdi].pt_long = 0;
                    active = TRUE;
                }

                /*
                 *  Update counts of high and low pdes if the pde that used to be
                 *  either at the top of the low end or bottom of the high end
                 *  was freed.
                 */
                pptetmp = pptept;
                pditmp = pdi;
                if (pdi == ppd->pd_cpdelow - 1) {
                    while (--pdi >= 0 && (--pptept)->pt_resident == 0) {
                        if (active) {
                            pgpPageDir[pdi].pt_long = 0;
                        }
                    }
                    ppd->pd_cpdelow = (ushort_t) (pdi + 1);
                } else if (pdi == (int)(ppd->pd_maxpde - ppd->pd_cpdehigh)) {
                    while (++pdi <= ppd->pd_maxpde - 1 &&
                        (++pptept)->pt_resident == 0) {
                        if (active) {
                            pgpPageDir[pdi].pt_long = 0;
                        }
                    }
                    ppd->pd_cpdehigh = ppd->pd_maxpde - (ushort_t) pdi;
                }
                pdi = pditmp;
                pptept = pptetmp;
            }
        } else {
            /*
             *  In system space there are no high and low pt counts,
             *  but we do have to zero the real page directory entry.
             */
            pgpPageDir[(MINSYSLADDR >> PAGEDIRSHIFT) + pdi].pt_long = 0;
        }
        pgFlushTLB();

        if (pptept->pt_present) {
            ppf = pgFrameToPF(pptept->pt_frame);
            pvp = ppf->pf_pvp;
        } else {
            pvp = pgFrameToVP(pptept->pt_frame);
        }
        pptept->pt_resident = 0;

        /*
         *  If allocated count hits zero, free the page table.
         */
        if (ppd->pd_pcalloc[pdi] == 0) {
            pgi.pi_page = pagept;
            pgi.pi_ppte = pptept;
            pgi.pi_pte.pt_long = pptept->pt_long;
            pgi.pi_ppd = &pgData;
            pgi.pi_pvp = pvp;
            pgi.pi_ppf = ppf;
            pgi.pi_pdi = pgPagePdToPDI(pagept, &pgData);
            pgi.pi_flags = PG_ALLOC;    /* don't muck with commit */
            pgwFree(SSToDS(&pgi));
            pgCommitFree(1, PG_SWAPPABLE);
            goto unpin;                     /* skip extra clearsem */
        }
    }

    pgClearSem(pptept, 1);
  unpin:
    pgUnpinPt(pagept, &pgData, 1, flags & PG_RESIDENT);
    return(err);
}


/***LP  pgDriver - preparation for many page manager routines
 *
 *      This routine loops over the pages in a range, pinning page
 *      tables and taking semaphores, and calls a worker function.
 *      The worker routine must return 0 if success and an error code
 *      otherwise.  The error code is returned to the caller.
 *
 *      ENTRY - ppgi - partially filled in pginfo structure
 *                  interesting flags:
 *                      PG_ALLOC - allocate page tables, don't take sems,
 *                                 must be free
 *                      PG_WAITSEM - wait for sems, but don't take
 *                      PG_SKIPFREE - don't error or call worker on free pages
 *                      PG_IGNOREINTS
 *
 *              npages - number of pages in range
 *              pfnworker - pointer to worker function
 *              pfnerror - pointer to error worker function
 *                         0 if no clean-up
 *
 *      for (each page table in the range)
 *          pin the page table
 *          for (each page in the range on the page table)
 *              if (page is allocated)
 *                  if (PG_ALLOC)
 *                      goto error
 *                  take page semaphore
 *              build worker argument packet
 *              call worker
 *              clear page semaphore
 *          unpin page table
 */

int
pgDriver(ppgi, npages, pfnworker, pfnerror)
register struct pginfo_s *ppgi;
ulong_t npages;
pgpfnDriver_t pfnworker;
pgpfnDriver_t pfnerror;
{
    struct pgdata_s *ppd;
    register ulong_t pdi;
    ulong_t npagessub;
    ulong_t i;
    int err;
    ulong_t pagebase;

    pagebase = ppgi->pi_page;
    if (err = pgPageToPd(ppgi->pi_page, ppgi->pi_pptda, SSToDS(&ppd))) {
        return(err);
    }
    ppgi->pi_ppd = ppd;
    pdi = pgPagePdToPDI(ppgi->pi_page, ppd);
    ppgi->pi_ppte = pgPagePdTopPte(ppgi->pi_page, ppd);
    npagessub = min((NPTEPPT - (ppgi->pi_page % NPTEPPT)), npages);

    /*
     *  Main worker loop.  For each page table in the range:
     *      pin the page table (in-line if possible)
     *      take the semaphores for the pages of interest on the page table
     *      call the worker on the pages of interest on the page table
     *      clear the semaphores
     *      unpin the page table
     *
     *  Values of variables inside the main loop:
     *      npagessub = number of page in the current subrange -- always
     *          NPTEPPT except for possibly partial first or last
     *          page tables.
     *      i = count of pages with npagessub
     *      page = start of current subrange
     *      pdi = page directory index of page table in current subrange
     *      ppte = pointer to pte of first page in current subrange
     *      npages = total pages remaining to work on
     */
    for(;;) {
        pgPinOnePt(pdi, ppd, ppgi->pi_flags, err, 0);  /* pin the current pt */
        if (err) {
            /*
             *  If the page table is unallocated and the skipfree flag
             *  was specified, just go on to the next pt.  Otherwise,
             *  return error.
             */
            if (err == ERROR_ACCESS_DENIED && (ppgi->pi_flags & PG_SKIPFREE)) {
                ppgi->pi_page += npagessub;
                ppgi->pi_ppte += npagessub;
                goto nextpt;
            }
            goto undo;
        }

        /*
         *  Setup per-page table info in pginfo structure
         */
        ppgi->pi_pdi = pdi;

        /*
         *  For each page in the page table...
         */
        for (i = npagessub; i > 0; --i, ppgi->pi_ppte++, ppgi->pi_page++) {

            /*
             *  If page is already allocated, take its semaphore
             */
        retrysem:
            if (ppgi->pi_ppte->pt_long) {
                /*
                 *  If this is not an allocation request, take the sem
                 */
                ppgi->pi_pte.pt_long = ppgi->pi_ppte->pt_long;
                if ((ppgi->pi_flags & PG_ALLOC) == 0) {
                    if (ppgi->pi_pte.pt_uvirt == 0) {
                        if (ppgi->pi_pte.pt_present) {
                            ppgi->pi_ppf = pgFrameToPF(ppgi->pi_pte.pt_frame);
                            ppgi->pi_pvp = ppgi->pi_ppf->pf_pvp;
                        } else {
                            ppgi->pi_pvp = pgFrameToVP(ppgi->pi_pte.pt_frame);
                        }

                        /*
                         *  If the semaphore is free, take it, else block
                         */
                        if (ppgi->pi_pvp->vp_busy == 0) {
                            if ((ppgi->pi_flags & PG_WAITSEM) == 0) {
                                pgSetSem(ppgi->pi_pvp);
                            }
                        } else {
                            ppgi->pi_pvp->vp_wanted = 1;
                            if (err=TKSleep(ppgi->pi_pvp, -1,
                                           (ppgi->pi_flags&PG_IGNOREINTS)?
                                           1 : 0,
                                           0)) {
                                goto unpin;
                            }
                            goto retrysem;
                        }
                    }
                /*
                 *  If this is an allocation request, error out since the
                 *  page is already allocated, unless the worker is pgwFree
                 *  since we are on an error clean-up path.
                 */
                } else if (pfnworker == &pgwFree) {
                    ppgi->pi_pvp = pgFrameToVP(ppgi->pi_pte.pt_frame);
                } else {
                    err = ERROR_ACCESS_DENIED;
                    goto unpin;
                }

            /*
             *  If the page is unallocated, PG_SKIPFREE or PG_ALLOC
             *  better be specified
             */
            } else if (ppgi->pi_flags & PG_SKIPFREE) {
                continue;
            } else if ((ppgi->pi_flags & PG_ALLOC) == 0) {
                err = ERROR_ACCESS_DENIED;
                goto unpin;
            }

            /*
             *  call worker and clear semaphore if we took it
             */
//  102346 - A check was added against the worker routine.  If the worker
//           routine is pgwFree and the page that we are trying to free is not
//           a uvirt page, then we need to check the semaphore(ie. busy flag)
//           to ensure that nobody else is expecting to use this page.  This
//           should fix various page faults experienced in the kernel.  Note
//           that the check against the uvirt page was added because a sleep
//           call waiting for a uvirt page will cause an indefinite sleep.
//
            if ((pfnworker == &pgwFree) &&                                //102346
               (ppgi->pi_pte.pt_uvirt == 0)) {                            //102346
               if (ppgi->pi_pte.pt_present) {                             //102346
                   ppgi->pi_ppf = pgFrameToPF(ppgi->pi_pte.pt_frame);     //102346
                   ppgi->pi_pvp = ppgi->pi_ppf->pf_pvp;                   //102346
               } else {                                                   //102346
                   ppgi->pi_pvp = pgFrameToVP(ppgi->pi_pte.pt_frame);     //102346
               }                                                          //102346
#ifndef PGSTRICT                                                          //102346
freewait:      if (ppgi->pi_pvp->vp_busy == 1){                           //102346
#else                                                                     //102346
freewait:      if ((ppgi->pi_pvp->vp_busy == 1) &&                        //102346
                  (pTCBCur->TCBNumber != ppgi->pi_pvp->vp_semowner)){     //102346
                     printf("\nsleeping\t");                              //102346
                     printf("tcb number = %d\t",pTCBCur->TCBNumber);      //102346
                     printf("semowner = %d\t",ppgi->pi_pvp->vp_semowner); //102346
#endif                                                                    //102346
                     ppgi->pi_pvp->vp_wanted = 1;                         //102346
#ifdef PGSTRICT                                                           //102346
                     printf("pvp = %d\t",ppgi->pi_pvp);                   //102346
#endif                                                                    //102346
                     err=TKSleep(ppgi->pi_pvp, -1, 0, 0);                 //102346
                     if (err) {                                           //102346
                        goto unpin;                                       //102346
                     }                                                    //102346
                     else{                                                //102346
                       goto freewait;                                     //102346
                     }                                                    //102346
               }                                                          //102346
            }                                                             //102346
// 102346 - end of code insertion                                         //102346

            err=(*pfnworker)(ppgi);
            if ((ppgi->pi_flags & (PG_WAITSEM | PG_ALLOC)) == 0 &&
                 ppgi->pi_pte.pt_uvirt == 0 ) {
                pgClearSemVP(ppgi->pi_pvp);
            }
            if (err) {
                break;
            }
        }
  unpin:
        pgUnpinOnePt(pdi, ppd, ppgi->pi_flags,1);/*unpin the current page table*/

        /*
         *  If the worker returned error, goto undo code
         */
        if (err) {
            goto undo;
        }

        /*
         *  If that was the last sub-range, return success.
         */
  nextpt:
        if ((npages -= npagessub) == 0) {
            return(0);
        }

        /*
         *  Setup values for next sub-range
         */
        pdi++;
        npagessub = min(NPTEPPT, npages);
    }


undo:
    /*
     *  If we get an error somewhere along the line, undo whatever
     *  work we have done so far, assuming we have done some and
     *  an error worker was specified.
     *  Any operations on the current page are assumed to be
     *  already cleaned-up.
     */
    if (pfnerror) {
        npages = ppgi->pi_page - pagebase;
        ppgi->pi_page = pagebase;
        ppgi->pi_flags |= PG_SKIPFREE | PG_IGNOREINTS;
        if (npages && pgDriver(ppgi, npages, pfnerror, 0)) {
            pgAssert(FALSE);
        }
    }
    pgFlushTLB();
    return(err);
}


#ifdef  PGSTRICT
void
pgStrict(ppte, pvp)
register struct pte_s *ppte;
register struct vp_s *pvp;
{
    register struct pf_s *ppf;

    if (ppte) {
        if (ppte->pt_uvirt) {
            return;
        }

        if (ppte->pt_present) {
            pvp = pgFrameToPF(ppte->pt_frame)->pf_pvp;
        } else {
            pvp = pgVPITopVP(ppte->pt_frame);
        }
    }

    pgAssert(pvp > pgpVPBase && pvp < pgpVPEnd - 1 &&
             ((ulong_t)pvp - (ulong_t)pgpVPBase) % sizeof(*pvp) == 0);
    pgAssert(pvp->vp_refcount > 0);
#ifdef PGVERYSTRICT
    pgAssert(pvp->vp_hob == DISCARDOWNER || pvp->vp_hob == INITOWNER ||
             VMHandleVerify(pvp->vp_hob, 0, 0) == NO_ERROR);
#endif
    if (pvp->vp_sow) {
        pgAssert(pvp->vp_discardable);
        pgAssert((pvp->vp_flags & (VP_RESIDENT|VP_CACHE|VP_DF)) == 0);
    } else if (pvp->vp_discardable) {
        pgAssert((pvp->vp_flags & (VP_CACHE|VP_DF)) == 0);
    } else if (pvp->vp_resident) {
        pgAssert((pvp->vp_flags & (VP_CACHE|VP_DF)) == 0);
        pgAssert(pvp->vp_pf);
    }

    if (pvp->vp_flags & (VP_PF|VP_PFIDLE)) {
        ppf = pgFrameToPF(pvp->vp_frame);
        pgAssert((ppf->pf_flags & (PF_FREE|PF_RES)) == 0);
        pgAssert(ppf->pf_pvp == pvp);
        if (pvp->vp_pfidle) {
            pgAssert(BLINK(ppfFLINK(ppf)) == pvp->vp_frame);
            pgAssert(FLINK(ppfBLINK(ppf)) == pvp->vp_frame);
        } else {
            pgAssert(ppf->pf_refcount > 0);
        }
    } else {
        if (pvp->vp_df) {
            pgAssert((pvp->vp_flags & (VP_DISCARDABLE|VP_SOW)) == 0);
        } else {
            pgAssert((pvp->vp_flags & (VP_DISCARDABLE|VP_SOW)) ||
                pvp->vp_block == VPB_ZEROFILL || pvp->vp_block == VPB_AOD ||
                pvp->vp_block == VPB_DFBROKEN);
        }
    }
}
#endif

#pragma code_seg("CINIT_TEXT", "CODE")

int
PGAllocHighMem(page, pptda, npages, blockno, hob, obpg, flags)
page_t page;
struct ptda_s *pptda;
int npages;
ulong_t blockno;
ushort_t hob;
ulong_t obpg;
int flags;
{
    struct pginfo_s pgi;
    struct vp_s vp;
    int err;

    TKMayBlock();       /* swappable code! */

   /*  If no high frames exist or the caller wants contiguous memory and
    *    there is not enough high frames to satisfy the request call PGAlloc
    */
    if ((pgHighPages == 0) ||
        ((flags & PG_CONTIG) && (pgHighPages < npages))) {
        return(PGAlloc(page, pptda, npages, blockno, hob, obpg, flags));
    }

    if (npages > pgHighPages) {
        pgAssert((flags & PG_CONTIG) == 0);
        err=PGAlloc(page, pptda, npages-pgHighPages, blockno,hob,obpg,flags);
        if (err != NO_ERROR)  {
            return(err);
        }
        pgAssert(obpg==0);
        obpg    = (npages-pgHighPages);
        page   += (npages-pgHighPages);
        npages  = pgHighPages;
    }

    /*
     *  Setup prototype pte flags and other simple pginfo fields
     */
    pgi.pi_data3 = flags & (PG_W | PG_U | PG_RESIDENT | PG_UVIRT | PG_GUARD);
    pgi.pi_page = page;
    pgi.pi_pptda = pptda;
    pgi.pi_flags = (flags & (PG_STATE | PG_MODIFIER | PG_PERM)) | PG_ALLOC;
    pgi.pi_data1 = 0;           /* resident count increment */
    pgi.pi_data2 = 0;           /* frame field increment */

    pgi.pi_pvp = SSToDS(&vp);
    vp.vp_refcount = 1;
    vp.vp_hob = hob;
    vp.vp_obpg = (ushort_t)obpg;

    pgAssert((flags & PG_STATE) == PG_RESIDENT);
    pgi.pi_data1 = 1;
    pgi.pi_data3 &= ~PG_GUARD;   /* no resident guard pages */
    vp.vp_flags = VP_BUSY | VP_RESIDENT;
#ifdef PGSTRICT
    vp.vp_semowner = pTCBCur->TCBNumber;
    vp.vp_block = -1;
#endif
    /*
     *  lie to pgCommit this will be fixed in pgInitHighMem
     *  if we let the resident flag be passed in we might fail the commit
     *  request due to too many resident pages ( > pgMaxResPages)
     *  also boost pgPhysPages so pgCommit does fail because swapping is
     *  not initialized and there isn't enough physical storage to satisify
     *  this request, this will also be fixed in pgInitHighMem
     */
    pgPhysPages += npages;
    if (err = pgCommit(npages, PG_DISCARDABLE)) {
       pgPhysPages -= npages;
       return(err);
    }
    if ((err=pgDriver(SSToDS(&pgi), npages, &pgwAlloc, &pgwFree))==0)
    {
        pgAssert((flags & PG_RESIDENT) == 0 || blockno == 0 ||
                    blockno == PGB_64KALIGN || blockno == PGB_128KALIGN);

        err = pgAllocPFHighMem(pgPagePdTopPte(page, pgi.pi_ppd),
                              page, npages, flags, blockno);
        pgAssert(err == 0);
    }
    return(err);
}



int
pgAllocPFHighMem(ppte, page, npages, flags, boundary)
register struct pte_s *ppte;
page_t page;
int npages;
int flags;
page_t boundary;
{
    register struct vp_s *pvp;
    register struct pf_s *ppf;
    struct pte_s *pptebase = ppte;
    struct pf_s *ppfT = 0;                      /* temp pf */
    frame_t frame;
    int i;

    /*
     * Loop over the range attaching frames to the pages.
     */
    for (i = npages; i > 0; --i, ++ppte, ++page, ++ppf, ++frame) {
        pgAssert(((PGGlobalFlags & PG_HIGH_MEM_INITED)==0) && (pgHighPages > 0));
        ppf = pgHighMemHeadppf;
        pgHighPagesUsed++;   // MUST BE before the pgFrameToPF call
                             // used in BLINK macro
        if (pgHighPages == 1) {
           frame = pgHighMemHeadframe;
        } else {
           frame = BLINK(ppfFLINK(ppf));
// 70987 - Don't mess with the links here. High storage is properly addressed
//         via the global head / tail pointers.
//           BLINK(ppfFLINK(ppf)) = BLINK(ppf);
//           FLINK(ppfBLINK(ppf)) = FLINK(ppf);
//           BLINK(ppf) = frame;
        }
        pgHighMemHeadframe++;
        pgHighMemHeadppf++;
        pgHighPages--;
        pgAssert(pgFrameToPF(frame) == ppf);
        LOCKS(ppf) = 0;
        ppf->pf_free = 0;
        /*
         * Zero initialize frame if required.
         */
        if (flags & PG_ZEROFILL) {
            memset((void *) pgAliasTemp(frame, 0), 0, PAGESIZE);
        }
        /*
         * Connect the vp, pf, and pte.
         */
        pvp = pgVPITopVP(ppte->pt_frame);
        ppf->pf_pvp = pvp;
#ifdef PGSTRICT
        ppf->pf_block = pvp->vp_block;
#endif
        pvp->vp_frame = frame;
        ppf->pf_refcount = 1;
        pvp->vp_pf = 1;
        pgAssert(pvp->vp_pfidle == 0);
        ppte->pt_frame = frame;
        ppte->pt_present = 1;
        pgClearSemVP(pvp);
    }
    return(0);
}

#pragma code_seg()

