#define SCCSID  "@(#)smalloc.c  6.22 92/04/30"

/*
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 */
// Change History
// Defect  date        Author              Abstract
// ======================================================================
// 56884  11/9/92      GEB         move swapper's bitmap from the resident
//                                 heap smHeap (that uses KRH functions) to
//                                 it own memory object
// 114099  03/06/95  Change Team   Modified logic to check for minimum swapper
//                                 size when it calls smDecreaseSpace.
// 113275  03/10/95  Change Team   commit swapper's bitmap based upon initial
//                                 swap file size request
// 138343  09/29/95  Change Team   smIncreaseBitmaoForInmemFile()
//                                 Added above function to Resize swapper
//                                 bitmap - @V3.0RV01

#define INCL_DOSERRORS
#define INCL_OS2STD
#include <os2.h>
#define INCL_DOSP
#include <os2p.h>
#include <basemid.h>                    /* for MSG_xxxx */
#define FNO_NUM FNO_SMALLOC_C
#include <fnomem.h>
#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <infoseg.h>
#include <pageio.h>
#include <smexport.h>
#include <pgexport.h>                   /* for PgInitSwap */
#include <tkexport.h>                   /* to get TCB definition */
#include <tom.h>
#include <vmexport.h>                   /* for VMxxx calls */
#include <smlocal.h>
#include <vmowner.h>                    /* for SMSFOWNER */
#include <syiexp.h>                     /* for sys init error display */
#include <kutil.h>
#include <sigdef.h>                     /* for tkptda.h */
#include <except.h>                     /* for tkptda.h */
#include <curdir.h>                     /* for tkptda.h */
#include <dirent.h>                     /* for tkptda.h */
#include <find.h>                       /* for tkptda.h */
#include <screen.h>                     /* for tkptda.h */
#include <sem.h>                        /* for tkptda.h */
#include <tkptda.h>
#include <pglocal.h>                    /* for pf declaration */
#include <stdlib.h>
#include <string.h>
#include <mmph.h>

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

#include <sched.h>                      /* dynamic timeslice */
#include <vmkh.h>            // must be after tkexport.h for KSEM definition

// compression variables
uchar_t *smpHeapComp;
uchar_t smbmComp[ SM_MAX_COMPRESSED_ENTRIES / 8];
ulong_t SMCompNextIndex =-1;
ulong_t *SMpCompTbl;  // geb
uchar_t *SMpCompBase;


// end compression

ulong_t smcLastErrorFence = 0xffffffffl;
uchar_t smfBackedOffFence = 0;

extern void tkTSDDaemon(void);

extern void SMPrintFatalMsg(void);
#define PG_DISC_ONLY_AGING 0x1
#define PG_SWAP_FILE_FULL 0x2

void SMTestSwapFile(void);
ulong_t SMInHardError=0;
#define SM_SWAPPER_PAD  0xc0

#define BM_SHIFT        (PAGESHIFT + 3)
#define BM_MASK         ((1 << (BM_SHIFT)) - 1)
#define SM_LAST_CHANCE_TIMEOUT  0x1400

/***ED  smHobheap
 *
 *      handle of object that contains the swapper's disk frame bitmap
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern VMHOB smHobheap;

/***LD  smpHeap
 *
 *      pointer to start of swappers disk frame bit map  : 56884
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern uchar_t *smpHeap;

/***LD  smpHeap
 *
 *      pointer to end of swappers disk frame bit map, will value will
 *      always be on a page boundary : 56884
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern uchar_t *smpHeapEnd;

/*
 * Swap file shrinking is controlled by a pair of constants: HYSTERESIS
 * and SHRINK_DELAY, the meanings of which are explained below.  We want
 * to keep the swap file as small as possible to avoid wasting resources,
 * but also want to avoid excessive "bounce" in the file size, since the
 * process of resizing is not cheap.  To this end we maintain a variable,
 * smSizeHistory, which moves upward immediately when requests increase
 * and behaves as an exponential moving average when the requests decrease.
 * The equation used was going to be:
 *
 *      h (new) = ((h << n) + c - h + 2^(n-1)) >> n
 * Where:
 *      h = current history value
 *      c = count of pages in new request
 *      n = hysteresis value
 *
 * This was simplified to:
 *
 *      h (new) = h - ((h - c) >> n)
 *
 * which is both much easier to compute and has better rounding properties.
 * The net effect of this is that when the value of n is small, the history
 * value tends to track the new requests very closely (when n is 0 they are
 * identical), and when n is large it tends to change very slowly.  Since we
 * are manipulating page counts in 32-bit registers, there is a practical
 * upper bound of 9 on HYSTERESIS before truncation errors occur.
 *
 * The second part of the swap file shrinking scheme is the time delay.
 * Earlier attempts waited until a certain number of memory requests had
 * occurred before starting the shrink process, but this was unsatisfactory
 * as the requests tend to be bursty, and thus we often ended up shrinking
 * the swap file during a time of peak activity, such as application
 * termination.  To avoid this a TOM hook was added, and now whenever we
 * determine that the swap file can be shrunk we immediately arm the TOM
 * event.  When the TOM event goes off our TOM hook routine is called (at
 * interrupt time!), the pending shrink bit is set, and the compaction daemon
 * is woken up.  This causes the couple seconds of work we do to shrink the
 * swap file to be delayed until after the burst of memory manager requests
 * has passed, to a time when things are (hopefully) calmer.  The constant
 * SHRINK_DELAY is the time (in milliseconds) that we delay after deciding
 * to shrink the swap file before actually taking any action toward doing so.
 * As a rule of thumb it should be set long enough so that if a user shuts
 * down one application and immediately starts another the swap file is not
 * shrunk, but if he steps away for a cup of coffee, the file will be shrunk
 * by the time he returns.
 *
 * The PADDING constant is the amount of slop we keep in the swap file
 * over and above the amount requested by the page manager.  The
 * MAXSIMULSWAP*SWAPGROUPSIZE term accounts for the disk frames that can
 * be temporarily consumed by swapping operations, and the 4 extra are to
 * allow some room for disk I/O errors, and to cover up any off-by-one
 * errors in our overcommit algorithm.
 */

#define HYSTERESIS      7               /* used in size history computation */
#define SHRINK_DELAY    30000L          /* ms to delay before shrinking file*/
#define PADDING         ((MAXSIMULSWAP*MAXSWAPGROUPSIZE) + 4)
/*end*/


/*
 * segment end labels, used by init code
 */
extern char StartSMLOCK_TEXT;
extern char EndSMLOCK_TEXT;

ulong_t smTcbResizing = 0;

/***ED  smHardErrPID
 *
 *      smHardErrPID pid number of hard error deamon, used by the swapper
 *      to void calling SMHardError function under the harderr' s context
 */
ushort_t smHardErrPID=0;

/***ED  SMSwapperSem
 *
 *      SMSwapperSem protects the swapper from multiple people trying
 *      to grow (shrink) it at the same time
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern KSEMMTX SMSwapperSem;

/***LD  smMinSwapFile
 *
 *      smMinSwapFile is the initial size set by the user in config.sys or
 *      the default value assigned if an init error occurred
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t  smMinSwapFile;

/***LD  smcGrowFails
 *
 *      smcGrowFails - counter of number of consecutive bad error codes from
 *      smSIOResize.
 */
ulong_t  smcGrowFails=0;

#define SM_MAX_FAILS_BEFORE_MSG  0x20  // max num of smSIOResize failures
                                       // before displaying hard error msg

/***ED  SMcDFInuse - number of allocated DFs.
 *
 *      SMcDFInuse is the number of DFs that are currently in use.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t         SMcDFInuse;

/***ED  SMcInMemFile
 *
 *      SMcInMemFile is a special variable used only on machines that have
 *      greater than 16 meg of memory which can not be supported by
 *      there disk device driver.  It specifies the number of pages that
 *      are contained with in the in memory swapfile.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t  SMcInMemFile;

/***ED  SMpInMemFile
 *
 *      SMpInMemFile is a special variable used only on machines that have
 *      greater than 16 meg of memory which can not be supported by
 *      there disk device driver.  It points to the linear address that maps
 *      the in memory swapfile. See PGINIT.C (pgInitHighMem) for more details
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern uchar_t *SMpInMemFile;

/***ED  SMSwapSpace - current amount of swap space (in pages)
 *
 *      The dword variable SMSwapSpace contains the total amount of
 *      swap space currently available, in units of pages (4k blocks).
 *      The value of SMSwapSpace may change in response to calls to
 *      SMSetSwapSpace.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t         SMSwapSpace;

/*
 *  NOTE: smdfbm must by followed by smfilesize, this allows the sas table to
 *  gain access to the df bitmap and its size (smdfbm is NOT constant)
 */

/***LD  smbmDF
 *
 *      smbmDF is a pointer to the base of a bit map used to map
 *      swap file space allocation.
 */
// Moved to sdata.asm for System Anchor Block - f122427              //xxxxx
extern char            *smbmDF;                                      //AA04663

/***LD  smFileSize
 *
 *      smFileSize is the true current size of the swap file, in pages.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern swapid_t        smFileSize;

/***LD  smSizeHistory - history value used for tracking swap file size
 *
 *      smSizeHistory contains a time averaged value corresponding
 *      to the swap file size.  The idea is to put a little hysteresis
 *      into swap file resizes to avoid unnecessary grows.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t         smSizeHistory;

/***LD  smDFMac - highest allocated DF number
 *
 *      smDFMac is the number of the highest allocated (in use) DF.
 *      It is used to determine when the tail of the swap file is empty.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern ulong_t         smDFMac;

/***LD  smcBrokenDF
 *
 *      smcBrokenDF is a count of the total number of disk frames that
 *      have gone bad.  Initially zero.
 *      Note that the "allocatable region" consists of the first
 *      (SMSwapSpace + smcBrokenDF) frames. Initially smcBrokenDF is 0
 *      and SMSwapSpace by itself contains the size of the allocatable region.
 *      When a frame breaks, SMSwapSpace is decremented and smcBrokenDF
 *      is incremented, so they must be summed to get the total.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern unsigned        smcBrokenDF;

/***LD  SMMinFree
 *
 *      SMMinFree is the minimum amount of free space that must remain
 *      on the backing store, in pages.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern unsigned        SMMinFree;

/***LD  smPendingShrink
 *
 *      smPendingShrink is a boolean variable that, if set, indicates
 *      that we are trying to shrink the swap file, and are just waiting
 *      for a convenient moment to do so.  When this occurs the file will
 *      be shrunk and smPendingShrink cleared.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern char            smPendingShrink;


/***LD  smInitialized - has swapper been initialized?
 *
 *      smInitialized is set to 1 when the swapper is first initialized,
 *      to 2 when it is reinitialized, and is used to catch the cheeseheads
 *      who keep trying to call swapper routines too early.
 */
char    smInitialized = 0;


/***LD  smHeap - handle for the swapper heap
 */
// VMHKH   smHeap;  56884

/***LP  smParanoid - enable super-strict checking
 */
char    smParanoid = FALSE;


/***LD  smToe - used for timed swap file shrinking
 */
toe_t   smToe = {&smToe, &smToe, 0};


/***ED  SMPageSemHeld - page semaphore owned by compaction daemon
 *
 *      If this variable is non-zero then it contains the ppte of a
 *      page semaphore owned by the compaction daemon.  Normally we
 *      can get away without priority inversion protection on page
 *      semaphores because they are only taken underneath a number of
 *      protected semaphores, and there are rarely any conflicts.
 *      Unfortunately the compaction daemon is special, as it takes
 *      random page semaphores without taking any other higher level
 *      sems.  Worse yet, it does so at idle priority, which lets it
 *      block forever without starving.  To avoid this we hand roll
 *      inversion protection for the one page semaphore at a time
 *      owned by the compaction daemon.
 */
ulong_t SMPageSemHeld = 0;

/***ED  SMCompactSlot - slot number of compaction daemon
 *
 *      TKDeclareInversion takes a slot number, and since it's too
 *      much trouble to figure it out each time we can figure it out
 *      once at init time and then just fetch it from here.
 */
ushort_t SMCompactSlot;


#pragma code_seg()
/***LP  smSetSwapFlags  - sets TCB swapper variable
 *
 *      This routine is used to check for nested swap operations.
 *      When it is called, we expect to find the TCB swapper variable
 *      clear, and proceed to set it.  If the variable is not clear,
 *      an internal error occurs.
 *
 *      This routine also locks down the TSD of the swapping thread
 *      so it will not be thrown away causing deadlock when we try to
 *      bring it back.
 *
 *      ENTRY   flag to set
 *      EXIT    none
 *
 *      if (TCB swapper variable)
 *              panic()
 *      TCB swapper variable = TRUE
 */
void
smSetSwapFlags(uchar_t ucFlag)
{
        if ((pTCBCur->TCBfSwapping) & ucFlag)
                panic("smSetSwapFlags: nested swapping");
        /*
         *  Lock the TSD if it is not already resident (the TSDs of all
         *  the internal threads hung off the sysint process, pid 1, are
         *  all known to be resident).
         *  In tksleep.c we are asserting that the TSD daemon is in pid 1.
         */
        if (pPTDACur->ptda_Pid != 1) {
            pgLockTSD();
        }
        pTCBCur->TCBfSwapping |= ucFlag;

        //  Support for dynamic timeslicing
        pTCBCur->TCBSchFlags |= SCH_PAGE_FAULT;
}


/***LP  smClearSwapFlags        - clears TCB swapper variable
 *
 *      This routine is used to check for nested swap operations.
 *      When it is called, we expect to find the TCB swapper variable
 *      set, and proceed to clear it.  If the variable is not clear,
 *      an internal error occurs.
 *
 *      This routine also unlocks the current TSD to undo what smSetSwapFlags
 *      did.
 *
 *      ENTRY   flag to clear
 *      EXIT    none
 *
 *      if (TCB swapper variable has flag set)
 *              panic()
 *      turn flag off
 */
void
smClearSwapFlags(uchar_t ucFlag)
{
        if ((pTCBCur->TCBfSwapping & ucFlag) == 0)
                panic("smClearSwapFlags: nested swapping");
        pTCBCur->TCBfSwapping &= ~ucFlag;
        if (pPTDACur->ptda_Pid != 1) {
           pgUnlockTSD();
        }
}
#pragma code_seg()


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


/***EP  SMSwapOut - Swap Managers group swap out entry point
 *
 *      SMSwapOut - takes a single pf that is tobe swapped out and collects
 *                MAXSWAPGROUPSIZE-1 other pfs and swaps them all as an
 *                unrelated group
 *
 *      ENTRY   ppf - pointer to pf that is to be swapped out.  The page's
 *                    semaphore should be taken.
 *      EXIT    0 on success
 *                  ppf swapped out successfully
 *              else error code
 *
 *      if ((err = smAllocSFT())==0)
 *          pSMPageCmdList =SMPCarr[SFTindex]
 *          if ((err=PGBuildGrpIOList())==0)
 *              if ((err=smGrpAllocDF()) == 0)
 *                  set the PageCmdLIst header flags
 *                  set TCB swapping flag
 *                  err = FSD_DoPageIO
 *                  clear TCB swapping flag
 *                  if (!err)
 *                      for (each Cmd)
 *                          SMSetpf
 *                          PGFreeBuf
 *                          if (not first pf)
 *                              SMIdleSF
 *                              pgClearSemPF
 *                  else
 *                      err = SMGrpSwpErrHandler()
 *                      goto bailout
 *              if (err)
 *                  for (i=0; i<ncmds; i++)
 *                      PGFreeBuf(pSMPageCmdList->Swp[i].laddr);
 *                      pgClearSemPF(pSMPageCmdList->Swp[i].pPF);
 *    bailout:
 *          free SFT
 *      return (err)
 *
 */
unsigned SMSwapOut(ppf)
struct pf_s *ppf;
{
    register struct SMPageCmdList_s *pSMPageCmdList;
    uchar_t ncmds;
    int SFTindex;
    int err;
    int i;
    ulong_t iDF;


    MMPHPreSMSwapOut();                 /* Performance Hook */

    if (SMcInMemFile != 0)
    {
        iDF = AllocBit((ulong_t *)smbmDF, SMcInMemFile);
        if (iDF != (ulong_t)-1) {
            memcpy( SMpInMemFile+(PAGESIZE * iDF),
                   (void *)pgAliasTemp(pgPFToFrame(ppf), 0), PAGESIZE);
            smAssert(ppf->pf_pvp->vp_df == 0);
            ppf->pf_pvp->vp_dirty = 0;
            ppf->pf_pvp->vp_df    = 1;
            ppf->pf_block         = iDF;
            SMcDFInuse++;
            smAssert(iDF < smFileSize);

            if (iDF > smDFMac)  {
                smDFMac = iDF;
            }
            return(0);
        }
    }
    smSetSwapFlags(SM_TCB_SWAPPING); /* flag thread as swapping */
    /* get a SFT and PageCmd list for the I/O from the swapper pool */

    if ((err = smAllocSFT(SSToDS(&SFTindex))) == NO_ERROR)  {
        pSMPageCmdList =smPCarr[SFTindex];
        if ((err = PGBuildGrpIOList(ppf,pSMPageCmdList)) == NO_ERROR)   {
            ncmds = pSMPageCmdList->Hdr.pgcmdh_OpCount;
            if ((err = smGRPAllocDF(ncmds, pSMPageCmdList)) == NO_ERROR)  {
                pSMPageCmdList->Hdr.pgcmdh_InFlags  = 0;
                pSMPageCmdList->Hdr.pgcmdh_OutFlags = 0;


                err = FSD_DoPageIO(smSFNarr[SFTindex],(void *)pSMPageCmdList);
//              smAssert(err == NO_ERROR);
                if (pSMPageCmdList->Hdr.pgcmdh_OutFlags == PGIO_FO_DONE) {

/***PV - Perfview measurement hook
 *
 *      ncmds contains the count of pages swapped out.
 */

#ifdef PERFVIEW
                    PvwCnt(mem,SwapOut,0);
#endif

                    /*
                     * process first command separately then do
                     * the other commands
                     */
                    pSMPageCmdList->Swp[0].pVP->vp_dirty = 0;
                    PGFreeBuf(pSMPageCmdList->Swp[0].laddr);

                    /*
                     * We are finished with all the pages other than
                     * the first one.
                     */
                    for (i=1; i<ncmds; i++) {
                        pSMPageCmdList->Swp[i].pVP->vp_dirty = 0;
                        PGFreeBuf(pSMPageCmdList->Swp[i].laddr);
                        pgClearSemVP(pSMPageCmdList->Swp[i].pVP);
                    }
                } else { /* error condition */
                    err = smGrpSwpErrHandler(pSMPageCmdList,SFTindex);
                }
            }
            else if (err != NO_ERROR) {   /* from GrpAllocDF */
                PGFreeBuf(pSMPageCmdList->Swp[0].laddr);
                for (i = 1; i < ncmds; i++)  {
                    PGFreeBuf(pSMPageCmdList->Swp[i].laddr);
                    pgClearSemVP(pSMPageCmdList->Swp[i].pVP);
                }
            }
        }
        smFreeSFT(SFTindex);
    }
    smClearSwapFlags(SM_TCB_SWAPPING);
    MMPHPostSMSwapOut();                /* Performance Hook */
    return(err);
}


/***EP  SMSwapIn        - swap a page in from disk to memory
 *
 *
 *      SMSwapIn reads a single page from the backing store into
 *      physical memory.  The address type (virtual or physical) is
 *      determined at initialization time and is the same as is
 *      used for SMSwapOut.
 *
 *      If an error occurs while swapping in the page, the swap in
 *      will be retried.  If both attempts fail the Page Manager will be
 *      told that the page is lost.  If the second attempt succeeds, the
 *      Page Mgr will be given a special error code that says that the
 *      page was swapped in successfully, but that the disk image can
 *      no longer be trusted.
 *
 *      - validate swapid
 *      - call SIOSwapIn
 *      - idle the SF
 *
 *      ENTRY   iDF - disk frame to swap in from
 *              frame - physical address to swap into
 *      EXIT    error code
 *
 *      WARNING: blocks
 *
 *      err = NO_ERROR
 *
 *      assert(swap frame pointed to by pSF is valid and not free)
 *
 *      ; Perform the actual swap in operation
 *      err = SIOSwapIn(pSF->sf_df, VPAddr)
 *
 *      if (err != NO_ERROR AND err != ERROR_INTERRUPT)
 *              if (the failed swap frame is not already marked as broken)
 *                      smcBrokenDF++
 *                      SMSwapSpace--
 *                      failed_swap_frame.flags |= SF_BROKEN
 *                      use ELF to log swap frame failure
 *                      write panicy message to screen
 *              endif
 *              err = SIOSwapIn(pSF->sf_df, VPAddr);
 *              if (err == NO_ERROR)
 *                      err = ERROR_SWAP_IO_PROBLEMS
 *      endif
 *
 *      return(err)
 *
 */
unsigned SMSwapIn(iDF, frame)
ulong_t iDF;
ulong_t frame;
{
    register unsigned err = NO_ERROR;
    ulong_t SFTIndex;
    laddr_t bufladdr;
    ulong_t transaddr;
    uchar_t *p;               // for compression

    smAssert(SMswapping);

    // compression
    if (iDF & SM_COMPRESSION_FLAG) {
       p = (uchar_t *)pgAliasTemp(frame, 0);

       smDecompress( *(SMpCompTbl+(iDF & SM_COMPRESSION_FLAG_MSK)), p);
//       smDecompress( smpHeapComp+
//                    (SM_MAX_COMPRESSED_SIZE * (iDF & SM_COMPRESSION_FLAG_MSK)),
//                     p );
       return(0);
    }
    // end compression

    smAssert(iDF <= smFileSize);

    if (SMcInMemFile != 0)
    {
        if (iDF < SMcInMemFile) {
            memcpy((void *)pgAliasTemp(frame, 0),
                                    SMpInMemFile+(PAGESIZE * iDF), PAGESIZE);
            return(0);
        }
    }

#ifdef PERFVIEW
/***PV - Perfview measurement hook
 *
 *      TRACE MINOR = <PVWEI_Ct_mem_SwapIn_0>,
 *      TP    = @,
 *      TYPE    = (PVW),
 *      GROUP   = NOSTRICT
 */
    PvwCnt(mem,SwapIn,0);
#endif

    MMPHPreSMSwapIn();                  /* Performance Hook */
    TKMayBlock();

    smSetSwapFlags(SM_TCB_SWAPPING);

    /*
     *  Allocate a transfer buffer and SFT/Page command list.
     *  Set transaddr to the transfer address of the buffer.  Use the physical
     *  address or the segmented address, depending on what the DD wants.
     *  Since we get the SFT before the AllocBuf, the AllocBuf will always
     *  succeed.
     */
    err = smAllocSFT(SSToDS(&SFTIndex)); /* BLOCKS */
    if (err)
        goto exit;

    err = PGAllocBuf(1, PG_SWAPPABLE, frame,
                     SSToDS(&bufladdr), SSToDS(&transaddr), 0);
    smAssert(err == 0);
    if (pgSwapAddrType & PIS_PADDR) {
        transaddr =  frame << PAGESHIFT;
    }

    /*
     * Perform the actual swap in operation
     */
    err = smSIOSwapIn(iDF, transaddr, SFTIndex);/* BLOCKS */
    MemStatIncr(ms_swapin);

    if (err && err != ERROR_INTERRUPT) {
        smcBrokenDF++;
        SMSwapSpace--;
        err = smSIOSwapIn(iDF, transaddr, SFTIndex); /* BLOCKS */
        if (err == NO_ERROR) {
            /*
             * We had an error with this DF, so the page manager will
             * disconnect it from VP.
             * Don't free the DF, so that it won't be reused.
             * Set the error code so the page manager knows what happened.
             */
            err = ERROR_SWAP_IO_PROBLEMS;
        }
    }

    PGFreeBuf(bufladdr);
    smFreeSFT(SFTIndex);
  exit:
    smClearSwapFlags(SM_TCB_SWAPPING);
    MMPHPostSMSwapIn();                 /* Performance Hook */
    return(err);
}

#pragma code_seg("SMLOCK_TEXT", "CODE")
/***LP  smAllocDF - obtain a disk frame
 *
 *      smAllocDF grabs a free disk frame from one of two places.  It
 *      first attempts to find a DF that is not currently in use (i.e.,
 *      one that is marked free in the bitmap).  If there are no such
 *      frames then it will walk the PF array and look for pages that
 *      have images in both physical memory and the swap file.  If there are
 *      no DFs to be had in either location then overcommit is broken or
 *      we are not freeing DFs when we should.  In this case we return
 *      the distinguished value -1, and our caller is responsible for
 *      noticing the problem.
 *
 *      ENTRY   fNoSteal - if TRUE, don't steal in-use DFs or block for busy
 *                         ones
 *      EXIT    returns DF number, or -1 if no DFs are available.
 *
 *      try to allocate an unused DF from the bitmap
 *
 *      if (there were no unused DFs)
 *              for (each pf)
 *                      steal a DF from an attached VP
 *              endif
 *      endif
 *
 *      adjust "last DF in use" variable
 */
ulong_t smAllocDF(fNoSteal)
int fNoSteal;
{
    ulong_t     iDF;
    struct pagerange_s *ppr;
    register struct pf_s *ppf;
    int         nranges;
    struct pf_s *ppfend;

    if (SMcInMemFile != 0) {
        /* 8 = number of bits in a byte */
        iDF = AllocBit((ulong_t *)(smbmDF+(SMcInMemFile/8)),
                                          smFileSize-SMcInMemFile);
        if (iDF != (ulong_t)-1) {
            iDF+=SMcInMemFile;
        }
    } else {
        iDF = AllocBit((ulong_t*)smbmDF, smFileSize);
    }
    if (iDF == (ulong_t)-1) {
        if (fNoSteal) {
           goto errorexit;
        }
        PGGlobalFlags |= PG_DISC_ONLY_AGING;

        /*
         * There were no DFs left in the bit map, so let's steal one
         * from a VP with an image in both memory and on the disk.
         * Since there are many more VPs than PFs, we will walk the pf
         * array looking for VPs with DFs.
         */
        ppr = pgPageablePAI.pai_pprt;           /* page range table */
        nranges = pgPageablePAI.pai_nranges;    /* number of ranges in table */

        /*
         *  For each range of pfs...
         */
        for (; nranges > 0; nranges--, ppr++) {
            /* the following 2 if statements are for machines that have > 16 */
            /*  of memory but that memory was disabled at init time */
            if (ppr->pr_firstframe > pgPhysMax)
                break;
            if (ppr->pr_lastframe > pgPhysMax)
                ppfend = pgFrameToPF(pgPhysMax);
            else
                ppfend = pgFrameToPF(ppr->pr_lastframe);  /* last pf in range */

            ppf = pgFrameToPF(ppr->pr_firstframe);      /* first pf in range */

            /*
             *  For each non-free pf in the range, look for a VP with
             *  a DF whose semaphore isn't taken.  If we find one, steal it
             *  and exit.
             */
            for (; ppf <= ppfend; ppf++) {
                if ((ppf->pf_flags & (PF_FREE | PF_BUSY)) == 0 &&
                    ppf->pf_pvp->vp_df    &&
                    (ppf->pf_block & SM_COMPRESSION_FLAG) == 0  ) {  // d77068

                    if (ppf->pf_pvp->vp_busy == 0) {
                        smAssert(ppf->pf_pvp->vp_dirty == 0);
                        iDF = ppf->pf_block;            /* found one! */

                        if (SMcInMemFile > 0 && iDF < SMcInMemFile) {
                           continue;
                        }
                        ppf->pf_pvp->vp_df = 0;
#ifdef SMSTRICT
                        ppf->pf_block = -1;
#endif
                        goto found;

                    /*
                     *  This guy has a DF that could have been stolen,
                     *  but his semaphore is busy.  Note this fact so we
                     *  can block and try again if we find nothing else
                     */
                    }
                }
            }
        }

        /*
         *  If we fall through to here, we went through all the PFs and
         *  didn't find anything we could use.  So, if there are still
         *  enough in-memory-discardable pages, just return else kill
         *  the system with the Swapper Out of Swapper Space message
         */
        if (pgDiscardableInmem < PG_MIN_DISC_IN_MEM)  {
            SMPrintFatalMsg();
        }
        return(-1);
    }

    /*
     *  If we make it to here, we found DF by some means.  Return it to the
     *  caller.
     */
    SMcDFInuse++;              // : NOTE found: used by df stealer
  found:
#ifdef SMSTRICT
    if (smParanoid) {
        smAssert(pgFindDF(iDF) == NULL);        /* don't give same DF twice */
    }
#endif
    smAssert(iDF < smFileSize);
    smAssert(((smbmDF[(iDF)/(8*sizeof(*smbmDF))] >>
        ((iDF) % (8*sizeof(*smbmDF)))) & 1) != 0);

    if (iDF > smDFMac)
        smDFMac = iDF;

  errorexit:
    return(iDF);
}


/***LP  SMFreeDF - free a disk frame
 *
 *      SMFreeDF undoes the work of smAllocDF and places a disk frame
 *      back into the bitmap, marking it free for future allocations.
 *
 *      ENTRY   iDF DF number
 *      EXIT    none
 *
 *      validate the DF
 *
 *      mark the DF as free in the bitmap
 *
 *      adjust "last DF in use" variable
 */
void SMFreeDF(iDF)
ulong_t iDF;
{
    // compression
    if (iDF & SM_COMPRESSION_FLAG) {

       if (SMCompNextIndex == -1) {
           SMCompNextIndex = iDF & SM_COMPRESSION_FLAG_MSK;
       } else {
           FreeBit((ulong_t*)&smbmComp[0],
                       (int)(iDF & SM_COMPRESSION_FLAG_MSK));
       }
       VMFreeKHB(VM_HKH_PUB_RESRW,
                *(SMpCompTbl+(iDF & SM_COMPRESSION_FLAG_MSK)) );
#ifdef SMSTRICT
       *(SMpCompTbl+(iDF & SM_COMPRESSION_FLAG_MSK)) = 0;
#endif
       return;
    }
    // end compression
    /*
     * Check that the bit for this DF is currently marked as allocated
     */
    smAssert(((smbmDF[(iDF)/(8*sizeof(*smbmDF))] >>
        ((iDF) % (8*sizeof(*smbmDF)))) & 1) != 0);
    smAssert(iDF <= smDFMac);
    FreeBit((ulong_t*)smbmDF, (int)iDF);
    SMcDFInuse--;

    /*
     * If this DF was the highest numbered allocated DF, then scan
     * backwards to find the next highest one.
     */
    if (iDF == smDFMac) {
        smDFMac = HighestBit((ulong_t*)smbmDF, smDFMac);
        if (smDFMac == (ulong_t)-1) {
            smDFMac = 0;
        } else {
            smAssert(((smbmDF[(smDFMac)/(8*sizeof(*smbmDF))] >>
                ((smDFMac) % (8*sizeof(*smbmDF)))) & 1) != 0);
#ifdef SMSTRICT
            if (smParanoid) {
                smAssert(pgFindDF(smDFMac) || smcBrokenDF);
            }
#endif
        }
    }
}



/***LP  smGRPAllocDF - allocate a group of Disk Frames
 *
 *      smGRPAllocDF - given a count of number of needed dfs, try to find
 *                     that number of contiguous dfs in the swapfile, if
 *                     unable to find contiguous frames get single frames
 *                     until the count is satisfied
 *
 *      ENTRY   nDFCnt     - number of dfs requested
 *              pSMPgCList - pointer to a SMPageCmdList
 *      EXIT    0 on success
 *              else error code
 *
 *      iDF = SMAllocGRP((ulong_t *)smbmDF, smFileSize, nDFCnt)
 *      if (iDF != -1L)
 *          for (cnt=0; cnt<nDFCnt; cnt++, iDF++)
 *              pSMPgCList->Cmd[cnt].pgcmd_fileOffset = SwapIdToByte(iDF)
 *          iDF--
 *          smAssert(iDF < smFileSize);
 *          if (iDF > smDFMac)
 *              smDFMac = iDF;
 *      else
 *          for (cnt=0; cnt<nDFCnt; cnt++)
 *              iDF = smAllocDF()
 *              if (iDF == -1L)
 *                  for (i=0; i<cnt ;i++ )
 *                      SMFreeDF(ByteToSwapId(pSMPgCList->Cmd[i].pgcmd_fileOffset));
 *                  return(ERROR_SWAP_FILE_FULL);
 *              smAssert(iDF < smFileSize);
 *              pSMPgCList->Cmd[cnt].pgcmd_fileOffset = SwapIdToByte(iDF);
 *      return (err)
 */

int smGRPAllocDF(nDFCnt, pSMPgCList)
ulong_t nDFCnt;
struct SMPageCmdList_s *pSMPgCList;
{
    register ulong_t     iDF;
    ulong_t     cnt;
    ulong_t     i;

    /* search the free DF map for nDFCnt contiguous disk frames */

    if (SMcInMemFile != 0) {
        /* 8 = number of bits in a byte */
        iDF = AllocBitGroup((ulong_t *)(smbmDF+(SMcInMemFile/8) ),
                              smFileSize-SMcInMemFile, nDFCnt);
        if (iDF != (ulong_t)-1) {
            iDF+=SMcInMemFile;
        }
    } else {
        iDF = AllocBitGroup((ulong_t*)smbmDF, smFileSize, nDFCnt);
    }

    if (iDF != -1L)  {       /* if we found a group */
        for (cnt=0; cnt<nDFCnt; cnt++,iDF++)  {
            smAssert(pSMPgCList->Swp[cnt].pVP->vp_df == 0);

            pSMPgCList->Cmd[cnt].pgcmd_fileOffset = SwapIdToByte(iDF);
            pSMPgCList->Swp[cnt].pVP->vp_df = 1;
            pSMPgCList->Swp[cnt].pPF->pf_block = iDF;
        }

        iDF--;                            /* adjust last DF in use variable */
        SMcDFInuse += nDFCnt;
        smAssert(iDF < smFileSize);

        if (iDF > smDFMac)  {
            smDFMac = iDF;
        }
    } else {                              /* no group was found */
        for (cnt=0; cnt<nDFCnt; cnt++) {     /* for each item in the array */
            smAssert(pSMPgCList->Swp[cnt].pVP->vp_df == 0);
            iDF = smAllocDF(FALSE);             /* call the old DF stealer */
            if (iDF == -1L)  {                  /* if error back out and */
                for (i=0; i<cnt ;i++ ) {
                    smAssert(pSMPgCList->Swp[i].pVP->vp_df == 1);
                    SMFreeDF(pSMPgCList->Swp[i].pPF->pf_block);
                    pSMPgCList->Swp[i].pVP->vp_df = 0;
#ifdef SMSTRICT
                    pSMPgCList->Swp[i].pPF->pf_block = -1;
#endif

                }
                return(ERROR_SWAP_FILE_FULL);
            }
            smAssert(iDF < smFileSize);

            pSMPgCList->Cmd[cnt].pgcmd_fileOffset = SwapIdToByte(iDF);
            smAssert(pSMPgCList->Swp[cnt].pVP->vp_df == 0);
            pSMPgCList->Swp[cnt].pVP->vp_df = 1;
            pSMPgCList->Swp[cnt].pPF->pf_block = iDF;
        }
    }
    return(0);
}


/***EP  SMSetSwapSpace - request a change in swap file size
 *
 *      SMSetSwapSpace requests the swapper to guarantee that at least
 *      a certain amount of swap space has been reserved on the disk.
 *      If the backing store is not resizable (e.g., a swapping
 *      partition), then a SMSetSwapSpace for more than the
 *      current (fixed) value of SMSwapSpace will always fail.
 *
 *      An attempt is made to have a little hysteresis in the swap
 *      file size, so requests to shrink the swap space may not be
 *      acted on immediately.
 *
 *      Note that calls to SMSetSwapSpace must be serialized, but
 *      that no serialization is done.  It is therefore up to the caller(s)
 *      to serialize the calls.  Failure to do this will lead to disaster.
 *
 *      Increase:
 *      - cancel any pending shrink
 *      - if necessary, call SIOResize to grow file
 *      - adjust SMSwapSpace and smFileSize
 *
 *      Decrease:
 *      - adjust SMSwapSpace
 *      - set pending shrink variable
 *
 *      If pending shrink is set:
 *      - the tail of the file is empty, shrink the file and
 *        adjust smFileSize
 *
 *      ENTRY   nRequested - total swap space required (in pages)
 *              fNoBlock - if set, return error rather than block
 *      EXIT    returns error code
 *
 *      WARNING can block
 *              caller(s) must serialize calls
 *
 *      EFFECTS changes SMSwapSpace to reflect new total swap space
 *
 */
unsigned SMSetSwapSpace(nRequested, fNoBlock)
swapid_t nRequested;
ulong_t fNoBlock;
{
    register unsigned err;

    smAssert(SMswapping);
    if (smInitialized <= 1) {
        SMSwapSpace = nRequested;
        return(NO_ERROR);
    }
    if (!nRequested) return(0);

    TKMayBlock();
#if defined(SMSTRICT)
    if (smTcbResizing) {
        panic("SMSetSwapSpace: reentered");
    }
#endif
    smTcbResizing = (ulong_t)pTCBCur;

    /*
     * Adjust the request by the number of broken frames and the amount
     * of overhead required for the bitmap
     */
    nRequested += PADDING + smcBrokenDF + ((nRequested+BM_MASK) >> BM_SHIFT);

    /*
     * First handle the easy case of resizing not being supported.
     */
//    if (smNoResize) {                      BUGBUG ?????
//        if (nRequested <= SMSwapSpace) {
//            err = NO_ERROR;
//            goto bailout;
//        }
//        else {
//            err = ERROR_NOT_ENOUGH_MEMORY;
//            goto bailout;
//        }
//    }

    if (nRequested > SMSwapSpace) {     /* increase requested */
        if (fNoBlock) {
            err = ERROR_NOBLOCK;
        }
        else {
            err = smIncreaseSpace(nRequested);
        }
    }
    else {                              /* decrease in size requested */
    //114099 --- begins   err = smDecreaseSpace(nRequested); ---
        if (SMSwapSpace > smMinSwapFile) {
            if (nRequested > smMinSwapFile) {
                err = smDecreaseSpace(nRequested);
            } else {
                err = smDecreaseSpace(smMinSwapFile);
            }
        }
        else {
             err = 0;
        }
    //114099 --- ends
    }

    if (err == NO_ERROR && smPendingShrink) {
        smPerformShrink();
    }

bailout:
#if defined(SMSTRICT)
    if (smTcbResizing != (ulong_t)pTCBCur) {
        panic("SMSetSwapSpace: corrupted");
    }
#endif
    smTcbResizing = 0;
    return(err);
}




int SMSetSwapSpace2()
{
    register unsigned err;
    register unsigned nRequested=SMcDFInuse;

    smAssert(SMswapping);

//  if (smInitialized <= 1) {             done in pgfault.c
//      SMSwapSpace = nRequested;
//      return(NO_ERROR);
//  }

    TKMayBlock();
    if (smTcbResizing) {   // its ok tobe re-entered, just return NO_ERROR
                           // since the swapfile is already trying to grow
        return(NO_ERROR);
    }
    smTcbResizing = (ulong_t)pTCBCur;

    /*
     * Adjust the request by the number of broken frames and the amount
     * of overhead required for the bitmap
     */
//  nRequested += PADDING + smcBrokenDF + ((nRequested+BM_MASK) >> BM_SHIFT);
    nRequested += SM_SWAPPER_PAD+smcBrokenDF;

    if (nRequested > SMSwapSpace) {     /* increase requested */
         err = smIncreaseSpace(nRequested);
    }
    else {                              /* decrease in size requested */
        if (SMSwapSpace > smMinSwapFile) {
            if (nRequested > smMinSwapFile) {
                err = smDecreaseSpace(nRequested);
            } else {
                err = smDecreaseSpace(smMinSwapFile);
            }
        }
        else {
             err = 0;
        }
    }

    if (err == NO_ERROR && smPendingShrink) {
        smPerformShrink();
    }

bailout:
#if defined(SMSTRICT)
    if (smTcbResizing != (ulong_t)pTCBCur) {
        panic("SMSetSwapSpace: corrupted");
    }
#endif
    smTcbResizing = 0;
    return(err);
}


/***LP  smIncreaseSpace - handle requests for increases in swap space
 *
 *      smIncreaseSpace is called by SMSetSwapSpace to handle requests
 *      for increased amounts of swap space.  It first attempts to satisfy
 *      these requests by cancelling or scaling back any pending shrinks,
 *      and it that is insufficient it will attempt to actually grow the
 *      swap file.
 *
 *      ENTRY   nRequested      - total swap space requested (in pages)
 *      EXIT    error code
 *                      NO_ERROR
 *                      ERROR_NOT_ENOUGH_MEMORY
 *                      error code from file operations
 *      EFFECTS can grow swap file
 *
 *      ; If the request represents an increase in size, the
 *      ; first thing to do is cancel any planned decrease in
 *      ; swap file size.
 *
 *      TOMCancel(shrink)
 *
 *      if (nRequested <= smFileSize)
 *
 *              ; We can get the space we need by cancelling or
 *              ; scaling back a pending shrink.
 *
 *              SMSwapSpace = new size
 *              if (shrink was only scaled back, not cancelled)
 *                      smPendingShrink = TRUE
 *              endif
 *
 *      else we actually have to grow the file.
 *
 *              FreeSpace = SIOSpaceQuery() - SMMinFree
 *              if (interrupted)
 *                      go to cleanup code and return error
 *              if (nRequested+smcBrokenDF > (smFileSize + FreeSpace))
 *                      err = ERROR_NOT_ENOUGH_MEMORY
 *              else
 *                      NewSize = (nRequested + number of pages wasted
 *                          in broken frames) rounded up to the
 *                          nearest round number that will leave
 *                          sufficient free space on the disk.
 *                      err = SIOResize(NewSize)
 *                      if (no error occurred)
 *                          allocate a new bitmap for the swap file
 *                          transfer the contents of the old bitmap
 *                          SMSwapSpace = NewSize - broken frames
 *                      endif
 *              endif
 *      endif
 */
unsigned smIncreaseSpace(nRequested)
register swapid_t nRequested;
{
    register swapid_t   NewSize;
    register unsigned   err = NO_ERROR;
    unsigned            FreeSpace;
    ulong_t             CommitSize;

    /*
     * If the request represents in increase is size, the
     * first thing to do is cancel any planned decrease in
     * swap file size.
     */
    if (TOMIsSet(&smToe)) {
        TOMCancel(&smToe);
    }
    smPendingShrink = FALSE;
    NewSize = RoundUpPages(nRequested);

    if (nRequested <= smFileSize) {
        /*
         * We can get the space we need by cancelling or
         * scaling back a pending shrink.
         */
        if (NewSize >= smFileSize)
            /*
             * Completely cancel the pending shrink
             */
            SMSwapSpace = smFileSize - smcBrokenDF;
        else {
            /*
             * Shrink should be scaled back rather than canceled.
             * We just canceled our Tom event, so we don't need to
             * check to avoid setting it twice here.
             */
            SMSwapSpace = NewSize - smcBrokenDF;
            TOMSet(&smToe, SHRINK_DELAY, TOM_W_SWAPSHRINK);
        }
    }
    else {
        /*
         * We actually have to grow the file.
         */
        smSetSwapFlags(SM_TCB_SWAPPING);
        err = smSIOSpaceQuery((swapid_t *) SSToDS(&FreeSpace)); /* BLOCKS */
        smClearSwapFlags(SM_TCB_SWAPPING);
        if (err) {
            if (err == ERROR_INTERRUPT)
                goto bailout;
            else
                panic("smIncreaseSpace: space query failed");
        }

        /*
         * WARNING - this math is all being done with unsigned numbers, so
         * be sure to avoid overflow!  (1 - 2) > 0
         *
         * Is there already less then MinFree space left on the disk?
         * If so, fail the request.
         */
// overcommit
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
            if (FreeSpace - smFileSize <= SMMinFree) {
                err = ERROR_NOT_ENOUGH_MEMORY;
                goto bailout;
            }
        }
// end overcommit

        /*
         * If growing the swap file to a nice round size would cause
         * too little space to be left on the disk then plan to grow it
         * as little as possible.  If growing it only this much would
         * leave us with too little free space then fail the request.
         */
// overcommit
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
            if (FreeSpace <= SMMinFree)  {
                err = ERROR_NOT_ENOUGH_MEMORY;
                goto bailout;
            }
            FreeSpace -= SMMinFree;
            if (NewSize > FreeSpace) {
                NewSize = (nRequested + 7) & (~7);
            }
            if (NewSize > FreeSpace) {
                err = ERROR_NOT_ENOUGH_MEMORY;
                goto bailout;
            }
        } else {
            if ( (NewSize-SMcInMemFile) > FreeSpace) {
                if (FreeSpace > 0x18+(smFileSize-SMcInMemFile)) {
                   NewSize = ((FreeSpace-0x10) + 7) & (~7);
                   NewSize += SMcInMemFile;
                } else {
                   err = ERROR_NOT_ENOUGH_MEMORY;
                   goto bailout;
                }
            }
        }
// end overcommit
        /*
         * Ok, we can grow the file to NewSize, so do so.
         */
        smSetSwapFlags(SM_TCB_SWAPPING);
        err = smSIOResize(NewSize-SMcInMemFile);     /* BLOCKS */
        smClearSwapFlags(SM_TCB_SWAPPING);
        if (err != NO_ERROR) {
            if (err == ERROR_DISK_FULL)
                err = ERROR_NOT_ENOUGH_MEMORY;
            goto bailout;
        }
        smcGrowFails=0;

        /*
         * Allocate a new bitmap and transfer the contents of the old one.
         */
        smAssert((NewSize & 7) == 0);
// 56884 start
//      smSetSwapFlags(SM_TCB_RESIZING);
//      err = VMAllocKHB(smHeap, NewSize/8, SMDFHOWNER, 0,
//                       (char **) SSToDS(&pMapT));
//      smClearSwapFlags(SM_TCB_RESIZING);
         if ( (smbmDF + (NewSize/8) ) > smpHeapEnd) {
            if (NewSize <= SWAP_MAX_SIZE) {
               smSetSwapFlags(SM_TCB_RESIZING);
// compiler bug : must split following line due to compiler error
//             CommitSize = (smbmDF+(NewSize/8)+(PAGESIZE-1)) & ~(PAGESIZE-1);
               CommitSize = smbmDF+(NewSize/8)+(PAGESIZE-1);
               CommitSize &= ~(PAGESIZE-1);
               CommitSize -= smpHeapEnd;
// end bug
// start #d71580
// the new page must be zero filled so the bitmap is empty at the start
               err = VMSetMem((ulong_t)smpHeapEnd, CommitSize,
                        HOBNULL,
                     VMS_COMMIT | PG_ZEROFILL | PG_R | PG_W | PG_RESIDENT, NA);
// end #d71580
               smpHeapEnd += CommitSize;           // assume success
               smClearSwapFlags(SM_TCB_RESIZING);
            } else {
               err = ERROR_NOT_ENOUGH_MEMORY;
            }
         }
// 56884 end
        if (err) {
            smpHeapEnd -= CommitSize;          // fix end heap pointer
            smSetSwapFlags(SM_TCB_SWAPPING);
            smSIOResize(smFileSize-SMcInMemFile);    /* BLOCKS */
            smClearSwapFlags(SM_TCB_SWAPPING);
            goto bailout;
        }

// 56884 - memory zero'ed at allocation time
//        TKBeginNoBlock();
//        memcpy(pMapT, smbmDF, smFileSize/8);
//        memset(pMapT+(smFileSize/8), 0, (NewSize - smFileSize)/8);
//        pMapOld = smbmDF;
//        smbmDF = pMapT;
// 56884 end

        smFileSize = NewSize;
        SMSwapSpace = NewSize - smcBrokenDF;

// 56884 start
//        TKEndNoBlock();
//        smSetSwapFlags(SM_TCB_RESIZING);
//        VMFreeKHB(smHeap, pMapOld);                  /* BLOCKS */
//        smClearSwapFlags(SM_TCB_RESIZING);
// 56884 end

       /*
        * Since we just grew the swap file, turn off discard only aging
        */
        PGGlobalFlags &= ~PG_DISC_ONLY_AGING;

        if ((PGGlobalFlags & PG_DO_OVERCOMMIT) == 0) {
            if ( (FreeSpace - (NewSize-SMcInMemFile)) <= SMMinFree)  {
                return(0xffff);
            }
        }
    }

bailout:;

    if (err == NO_ERROR)  {
        smSizeHistory = nRequested;
    } else {
        if ((PGGlobalFlags & PG_DO_OVERCOMMIT) == 0) {
            if (err == ERROR_NOT_ENOUGH_MEMORY) {
                if (smcGrowFails++ == 0) {   // smcGrowFails MUST be incremented
                                             //   before calling smHardError for
                                             //   the call in smAllocDF to work
                    err=0xffff;
                }
                if (smcGrowFails == SM_MAX_FAILS_BEFORE_MSG) {
                    smcGrowFails = 0;
                }
            }
        }
    }
    return(err);
}



/***LP smDecreaseSpace
 *
 *      smDecreaseSpace is called by SMSetSwapSpace to handle requests
 *      for decrease amounts of swap space.  It lets a number of shrink
 *      requests come in before actually shrinking the file, in an attempt
 *      to provide a little hysteresis and avoid having the swap file
 *      size bounce unnecessarily.
 *
 *      ENTRY   nRequested      - amount of swap space requested (in pages)
 *      EXIT    returns error code
 *                      NO_ERROR
 *                      error code from file operations
 *      EFFECTS may trigger a shrink
 *
 *
 *      RoundedRequest = (nRequested + HYSTERESIS) rounded up to
 *          the nearest multiple of FileSizeQuantum
 *      RoundedCurrent = SMSwapSpace rounded up to the nearest
 *          multiple of FileSizeQuantum
 *      if (RoundedRequest < RoundedCurrent)
 *
 *              ; This request was a substantial decrease, and
 *              ; we've been shrinking for a while now, so
 *              ; let's actually shrink the swap file.  (We don't
 *              ; want to be too eager about shrinking the file,
 *              ; since it may grow again soon.)
 *
 *              SMSwapSpace = RoundedRequest
 *      else
 *              ; This was a shrink, but the trend toward shrinking
 *              ; is not yet dramatic or consistent enough to
 *              ; convince us to actually shrink the file.  We'll
 *              ; just record the fact that this was a shrink
 *              ; and return success.
 *
 *      endif
 */
unsigned smDecreaseSpace(nRequested)
register swapid_t nRequested;
{
    register swapid_t   RoundedRequest;
    swapid_t            RoundedCurrent;

    if (smfBackedOffFence == 1 && SMcDFInuse+0x100 < smcLastErrorFence) {
        smfBackedOffFence=2;
    }
    RoundedRequest = RoundUpPages(nRequested);
    RoundedCurrent = RoundUpPages(SMSwapSpace);
    if (RoundedRequest < RoundedCurrent) {
        /*
         * This request was a substantial decrease, but
         * we don't want to be too eager about shrinking the
         * file, so we'll just set our TOM event and wait a while
         * before actually shrinking.
         */

       /*
        * Since there's now some free space in the swap file
        *   turn off disard only aging
        */
        PGGlobalFlags &= ~PG_DISC_ONLY_AGING;
//      if (RoundedRequest > smDFMac) {                    // b736191
        if ((RoundedRequest+0x80) < SMSwapSpace) {         // b736191
            SMSwapSpace = RoundedRequest - smcBrokenDF;
            if (!TOMIsSet(&smToe)) {
                TOMSet(&smToe, SHRINK_DELAY, TOM_W_SWAPSHRINK);
            }
        }
    }
    return(0);
}


/***LP  SMTomHook - shrink hook called by timer
 *
 *      This tiny routine is called at interrupt time by a timer hook.
 *      All it does is set the pending shrink flag and wake up the
 *      TSD/compaction daemon.
 */
void SMTomHook(toe_t *ptoe)
{
    smPendingShrink = TRUE;
    TKWakeup((void *) tkTSDDaemon, 0, NULL);

}



/***LP  smPerformShrink - perform a pending shrink, if possible
 *
 *      smPerformShrink checks to see if it is possible to complete
 *      a pending shrink, and if so, attempts to complete it.  If the
 *      attempt is interrupted, exit with no error and we'll try again
 *      later.
 *
 *      Note that alloc() and free() can cause swapping, so
 *      SM_TCB_SWAPPING must NOT be set around calls to those
 *      routines.
 *
 *      if (smPendingShrink AND the tail of the swap file is empty)
 *              err = SIOResize(new size)
 *              if error
 *                      return
 *              allocate a smaller bitmap
 *              copy over the relevant portion from the old bitmap
 *              smPendingShrink = FALSE
 *      endif
 *      return err
 */
unsigned smPerformShrink()
{
    register unsigned   err = NO_ERROR;
    register swapid_t   NewSize;
    ulong_t ulSizeOld;
// 56884 start
//  #ifdef SMSTRICT
//     char *pHeapTest;
//  #endif
// 56884 end

    NewSize = SMSwapSpace + smcBrokenDF;

    if (smPendingShrink && ((smDFMac < NewSize) ||
        (RoundUpPages(smDFMac+smcBrokenDF+1) < smFileSize))) {
        if (smDFMac >= NewSize) {
            /*
             * We can't shrink the whole way, but we can go far enough
             * to make it worth while.  Figure out the new size for the
             * file and leave the pending shrink flag set.
             */
            NewSize = RoundUpPages(smDFMac+smcBrokenDF+1);
        }
        else {
            /*
             * We're going to shrink the swap file all we need to, so
             * clear the flag to prevent and further shrinkage.
             */
            TOMCancel(&smToe);
            smPendingShrink = FALSE;
        }
        smAssert(smDFMac < NewSize);

        /*
         * BUGBUG donh 2-1-90: the flags should be ORed together and set
         * in a single call, but this screws up the TSD lock in SetSwapFlags.
         * The TSD lock can be eliminated if we can eliminate MFT
         * serialization for the swapper MFT.
         */
        smSetSwapFlags(SM_TCB_RESIZING);
        smSetSwapFlags(SM_TCB_SWAPPING);

        ulSizeOld = smFileSize;
        smFileSize = NewSize;
        err = smSIOResize(NewSize-SMcInMemFile);     /* BLOCKS */
        if (err) {                      /* shrinks fail during shutdown */
            smClearSwapFlags(SM_TCB_RESIZING);
            smClearSwapFlags(SM_TCB_SWAPPING);
            smFileSize = ulSizeOld;
            goto bailout;
        }

        smcGrowFails =0;
        /*
         *  if we shrink below the swapper's error fence, clear the fence
         *   value and flag
         */
        if (NewSize < smcLastErrorFence) {
            smcLastErrorFence = 0xffffffffl;
            smfBackedOffFence = 0;
        }

        smClearSwapFlags(SM_TCB_SWAPPING);

        /*
         * reallocate the bit map
         */
// 56884 start
//
//        /*
//         * reallocate the bit map
//         */
//        smAssert((NewSize & 7) == 0);
//#ifdef SMSTRICT
//        pHeapTest = smbmDF;
//        err = VMReallocKHB(smHeap, &smbmDF, NewSize/8, 0); /* BLOCKS */
//        smAssert(pHeapTest == smbmDF);
//#else
//        err = VMReallocKHB(smHeap, &smbmDF, NewSize/8, 0); /* BLOCKS */
//#endif
//#ifdef SHRINKS_CAN_FAIL
//        if (err) {
//            smSetSwapFlags(SM_TCB_SWAPPING);
//            smSIOResize(smFileSize);
//            smClearSwapFlags(SM_TCB_RESIZING);
//            smClearSwapFlags(SM_TCB_SWAPPING);
//            smFileSize = ulSizeOld;
//            goto bailout;
//        }
//#else
//        smAssert(err == NO_ERROR);
//#endif
//
// 56884 end
        smClearSwapFlags(SM_TCB_RESIZING);
    }
bailout:
    return(err == ERROR_INTERRUPT ? NO_ERROR : err);
}

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









void SMTestSwapFile()
{
   int rc;
   swapid_t PageCnt;

   smAssert(smInitialized > 1);
   if ( (SMSwapSpace-SMcDFInuse) < SM_SWAPPER_PAD) {
      if ( (pPTDACur->ptda_Ppid > 1 &&
            pPTDACur->ptda_Pid != smHardErrPID) ||
            ((SMSwapSpace-SMcDFInuse) < 0x20) ) {

           if ( (KSEMRequestMutex((HKSEM)&SMSwapperSem, 0)) == NO_ERROR) {
               rc = SMSetSwapSpace2();
               KSEMReleaseMutex((HKSEM)&SMSwapperSem);
               if (rc == 0xffff) {
                   smcLastErrorFence = SMcDFInuse;
callharderr:;
                   smfBackedOffFence = 1;
                   if (pPTDACur->ptda_Ppid > 1 &&
                       pPTDACur->ptda_Pid != smHardErrPID) {
                       smHardError();
                   }
               }
           }
       }
   } else {   // test if user backoff and is now growing back into
              // the MinFree zone
       if (smfBackedOffFence == 2  && SMcDFInuse > smcLastErrorFence) {

           if ( (KSEMRequestMutex((HKSEM)&SMSwapperSem, -1)) == NO_ERROR) {
               smSetSwapFlags(SM_TCB_SWAPPING);
               rc = smSIOSpaceQuery(SSToDS(&PageCnt));
               smClearSwapFlags(SM_TCB_SWAPPING);
               KSEMReleaseMutex((HKSEM)&SMSwapperSem);
               if ( (PageCnt - SMcDFInuse) >
                    (SMMinFree+SMcInMemFile+SM_SWAPPER_PAD) ) {
                  // if we got to here then the user has deleted some
                  //  files from the hard file
                   smcLastErrorFence = 0xffffffffl;
                   smfBackedOffFence = 0;
               } else {
                  // else the user has grown back into the MinFree region
                  goto callharderr;
               }
           }
       }
   }
   return;
}






/***EP  SMQuerySwapSpace - estimate maximum possible swap space
 *
 *
 *      SMQuerySwapSpace returns an estimate of the maximum possible
 *      swap space.  For a non-resizable backing store, SMSwapSpace
 *      is returned.  For a resizable backing store, such as file
 *      system swap file, the maximum possible size of the store is
 *      estimated and this value is returned.  Note that there is
 *      no guarantee that this much space will be available at any
 *      point in the future.
 *
 *      - call the Swap I/O package SIOSpaceQuery function
 *
 *      ENTRY   none
 *      EXIT    returns estimate of mamximum possible swap space (in pages)
 *
 */
unsigned SMQuerySwapSpace(swapid_t *pPageCnt)
{
    register unsigned err;

    TKMayBlock();
    smAssert(SMswapping);
    smSetSwapFlags(SM_TCB_SWAPPING);
    err = smSIOSpaceQuery(pPageCnt); /* BLOCKS */

    if (*pPageCnt > SMMinFree)    // must not go negative
        *pPageCnt -= SMMinFree;
    else
        *pPageCnt =0;

    smClearSwapFlags(SM_TCB_SWAPPING);
    return(err);
}


ulong_t smCompress(psrc,ptgt)
register uchar_t *psrc;
register uchar_t *ptgt;
{
   int c;
   uchar_t *pend;
   uchar_t *ptgt_end;
   uchar_t *ptgt_start;


//   if ( (*(ulong_t *)(psrc+0xffc) != 0L) ||  in  pgage.c
//         *(ulong_t *)(psrc+0x80) != 0L  ) {
//      return(0);
//   }

   ptgt_start = ptgt;
   ptgt_end = ptgt + SM_MAX_COMPRESSED_SIZE;

   pend = psrc+0x1000;

   for ( ; psrc < pend ; ) {
      if ( *psrc == 0) {
         // if (ptgt+1 >= ptgt_end) return(0);
         // if (ptgt+3 >= ptgt_end) return(0);  already checked
         // is there room for  0 0 <size>\
         //  note : we cna make this check here since the last dword
         //         is guaranteed tobe zero
         if (ptgt+4 >= ptgt_end) return(0);
         *ptgt = 0;
         ptgt++;
         psrc++;

         for (c=0 ; psrc < pend ; c++ , psrc++) {
            if ( *psrc != 0) goto non_zero;
         }


// if we're here, p points at the end of the page, so don't reference it!

         if (c == 0) {   // this should never happen since we
                         // check the last dword for zeros
            smAssert(0);
            *ptgt = *psrc;
         }

         if (c > 0) {  // if we found any zeros
         // if (ptgt+3 >= ptgt_end) return(0);  already checked
            *ptgt = 0;
            ptgt++;
            *(unsigned short *)ptgt = c;
            ptgt+=2;
         }

goto end_compress;

non_zero:;

//       if (c == 0) {      // this is done at the end of the loop anyways
//          *ptgt = *psrc;
//          psrc++;
//          ptgt++;
//       }

         if (c > 0) {  // if we found any zeros
         // were going to add 3 so if ptgt+3 >= ptgt_end, return(0)
         // if (ptgt+3 >= ptgt_end) return(0);  already checked
            *ptgt = 0;
            ptgt++;
            *(unsigned short *)ptgt = c;
            ptgt+=2;
         }

         if (ptgt+1 >= ptgt_end) return(0);
         *ptgt = *psrc;  // save char, this will be the non-zero that broke the loop
         ptgt++;
         psrc++;

      } else {   // *psrc != 0
         if (ptgt+1 >= ptgt_end) return(0);
         *ptgt = *psrc;    // save char
         ptgt++;
         psrc++;
      }
   }  // end for loop

end_compress:;
// if ( (ptgt-ptgt_start) == 4)
//    printf("Z %x\n", (ptgt-ptgt_start) );
// else
//    printf("B %x\n", (ptgt-ptgt_start) );
   return((ulong_t) (ptgt-ptgt_start) );
}

void smDecompress(psrc,ptgt)
register uchar_t *psrc;
register uchar_t *ptgt;
{
   uchar_t *pend_page;
   uchar_t *pend;

   pend_page = ptgt+0x1000;

   for (; ptgt < pend_page; psrc++, ptgt++) {

      if ( *psrc == 0) {


         *ptgt = 0;

         ptgt++;
         psrc++;

         if (ptgt == pend_page) return;

         if (*psrc == 0)  {  // found entry to expand
            psrc++;

            pend = ptgt + *(unsigned short *)psrc;

            psrc++;  // add only one since the for loop will incr also

            for ( ; ptgt < pend ;  ptgt++) {
               *ptgt = 0;
            }
            ptgt--;  // sub one since the for loop will incr it too


         } else {   // only one zero found therefor not entry to expand
            *ptgt = *psrc;
         }
      } else {  // non-zero digit found
         *ptgt = *psrc;
      }
   }
   return;
}


#pragma code_seg()

#pragma code_seg("CINIT_TEXT", "CODE")
/***EP  SMReinit - finish up the ugly work of initializing the swapper
 *
 *      SMReinit finishes swapper initialization, specifically opening
 *      the swap file, allocating the bitmap, and notifying the page
 *      manager of our success.
 *
 *      ENTRY   pszSwapPath - pointer to SWAPPATH= string as extracted from
 *                      config.sys by sysinit.
 *      EXIT    returns no error
 *
 *      if (swapping is selected in config.sys)
 *              call swap I/O initialization routine
 *              allocate and initialize DF bitmap
 *              call page manager
 *      endif
 *
 */

#define SM_DONATE_SIZE 0x8b0

void SMReinit(pszSwapPath)
char *pszSwapPath;
{
    ulong_t     flags;
    swapid_t    initsize;
    unsigned err;
    ulong_t     bmpSize;  //113275
    VMAC        ac;
    uchar_t     *pdonate;

    if (SMswapping) {
        if (SMCommit) {
            PGGlobalFlags |= PG_DO_OVERCOMMIT;
        }
//      smAssert((&smFileSize - &smbmDF) == 1);

        initsize      = (SMCFGSwapSize+3) / 4;
        initsize      = RoundUpPages(initsize);
        if (initsize < SM_MIN_SWAPINIT_SIZE) initsize = SM_MIN_SWAPINIT_SIZE;

        SMMinFree     = (SMCFGMinFree+3) / 4;
        if (SMMinFree < SM_MIN_MINFREE_SIZE)  SMMinFree = SM_MIN_MINFREE_SIZE;

        /*
         * allocate and initialize bit map.
         * None of these allocatations should fail since the only errors other
         * than bad parameters would be out of memory and the tcb flag should
         * prevent that.
         */
        smSetSwapFlags(SM_TCB_RESIZING);
// 56884 - start
//        err = VMCreateKH((SWAP_MAX_SIZE / 8),
//                         PG_RESIDENT | PG_R | PG_W,
//                         SMDFHOWNER,
//                         &smHeap,
//                         SSToDS(&ac));

    //113275 ************************WARNING*************************
    //113275 *  bmpSize is dependent upon the calculations inside   *
    //113275 *  smSIOInit in smsft.c.  Please update both areas     *
    //113275 *  appropriately.                                      *
    //113275 ********************************************************

    bmpSize = (initsize + (MAXSIMULSWAP * sizeof(hSFN)) +              //113275
                          (MAXSIMULSWAP * sizeof(SMPageCmd_t))) / 8;   //113275
    bmpSize += (PAGESIZE-1);                                           //113275
    bmpSize &= ~(PAGESIZE-1);                                          //113275

    /*
     * Allocate swapper memory committing the first page
     */
        err = VMAllocMem((SWAP_MAX_SIZE / 8)+PAGESIZE,/* Virtual size of obj */
//113275                  PAGESIZE,         /* Commit one page */
                          bmpSize,          /* 113275 */
                          PG_RESIDENT | PG_R | PG_W, /* allocation flags */
                          HOBNULL,          /* PTDA not applicable */
                          SMDFHOWNER,       /* owner */
                          HOBNULL,          /* Not an MTE object */
                          NA,               /* No Selector Mgr flags */
                          NA,               /* No block number */
                          SSToDS(&ac));     /* Return Buffer */
        smAssert(err == NO_ERROR);
        smpHeap = (uchar_t *) ac.ac_va;
        smHobheap = ac.ac_hob;
//113275 memset(smpHeap, 0, PAGESIZE);
//113275 smpHeapEnd = smpHeap + PAGESIZE;
        memset(smpHeap, 0, bmpSize);     //113275
        smpHeapEnd = smpHeap + bmpSize;  //113275
// 56884 end

        if (PGGlobalFlags & PG_SYS_CONSTRAINED) {    // perf enhancement
            pdonate=smpHeap;
            smpHeap += SM_DONATE_SIZE;
        }

        err = smSIOInit(pszSwapPath, SSToDS(&initsize),SSToDS(&flags));

        if (err == NO_ERROR)  {

            if (PGGlobalFlags & PG_SYS_CONSTRAINED) {
                err=  VMDonateKHB(pdonate, SM_DONATE_SIZE, PG_W);
            }

            if (SMcInMemFile != 0) {
                 initsize += SMcInMemFile;
            }
            SMSwapSpace = smFileSize = smMinSwapFile = initsize;
// 56884 - start
//          err = VMAllocKHB(smHeap, initsize/8, SMDFHOWNER, 0, &smbmDF);
//          if (err == NO_ERROR)  {
//              memset(smbmDF, 0, initsize/8);
//          }
            smbmDF =  smpHeap;
// 56884 - end
        }
        smClearSwapFlags(SM_TCB_RESIZING);

        if (err != NO_ERROR) {
            SMswapping = FALSE;
            SYISaveMessageID(MSG_SWAP_CANT_INIT);
            goto noswap;
        }

        /*
         *  Initialize the swapper semaphore.
         */
        KSEMInit((HKSEM)&SMSwapperSem, KRNL_MUTEXSEM, 0);

        // compression
        err = VMAllocKHB(VM_HKH_PUB_RESRW,
                         SM_MAX_COMPRESSED_ENTRIES * sizeof(ulong_t *),
                         SMDFHOWNER,       /* owner */
                         0,
                         &SMpCompTbl);

        smAssert(err == NO_ERROR);

        memset(SMpCompTbl, 0,
                         SM_MAX_COMPRESSED_ENTRIES * sizeof(ulong_t));

        memset(smbmComp, 0, SM_MAX_COMPRESSED_ENTRIES / 8 );

        SMCompNextIndex = AllocBit((ulong_t *)&smbmComp[0],
                                               SM_MAX_COMPRESSED_ENTRIES);

        err = VMAllocKHB(VM_HKH_PUB_RESRW,
                         SM_MAX_COMPRESSED_SIZE,
                         SMDFHOWNER,       /* owner */
                         0,
                         &SMpCompBase);

        smAssert(err == NO_ERROR);

       // end compression

        /*
         * Inform the page manager that swapping is underway.
         */
        smInitialized = 2;
        PGInitSwap(flags);
    }
    else {
        /*
         * Either swapping was not selected in config.sys, or we were
         * not able to open the swap file.  In either case we want to
         * free unneeded code and inform the page manager.
         */
    noswap:
       (void)VMFreeMem( ((ulong_t)(smpHeap) & ~(PAGESIZE - 1)), NA, NA);
        PGInitHighMem(FALSE);              /* no swapping, so enable high mem */
// D60999 start
        /*
         *  Since swapping is not enabled we should perform over commit
         *  accounting, otherwise a single application could allocate
         *  and use all available memory thus preventing other apps (ie shell)
         *  from running
         */
        PGGlobalFlags |= PG_DO_OVERCOMMIT;
// D60999 end
        SMSwapSpace = 0;
        VMDonateKHB(&StartSMLOCK_TEXT,&EndSMLOCK_TEXT-&StartSMLOCK_TEXT,PG_R);
        PGInitSwap(PIS_NOSWAP);
    }
}

/*
** Function added to allow Swapper Bitmap to be properly committed
** for in-mem swappers. - Defect # 138343 - @V3.0RV01
*/
extern void smIncreaseBitmapForInmemFile(void);

void smIncreaseBitmapForInmemFile(void)
{
  int rc;
  ulong_t CommitSize;

  CommitSize =  smbmDF+(SMcInMemFile/8)+(PAGESIZE-1);
  CommitSize &= ~(PAGESIZE-1);

  rc = VMSetMem((ulong_t)smpHeapEnd,
       CommitSize,
       HOBNULL,
       VMS_COMMIT | PG_ZEROFILL | PG_R | PG_W | PG_RESIDENT,
       NA);

  smAssert(rc == NO_ERROR);            // IPE if VMSetMem returns an error
  smpHeapEnd  += CommitSize;           // Adjust swapper heap

} /* smIncreaseBitmapForInmemFile */


#pragma code_seg()
