#define SCCSID  "@(#)smsft.c    6.13 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

#include <ostypes.h>
#include <osconst.h>
#define FNO_NUM FNO_SMSFT_C
#include <fnomem.h>
#include <osmac.h>
#include <mi.h>
#define INCL_DOSERRORS
#include <bseerr.h>
#define INCL_TYPES
#include <basedef.h>
#include <basemac.h>
#include <strat2.h>
#include <vmowner.h>
#include <pageio.h>
#include <smexport.h>
#include <pgexport.h>
#include <vmexport.h>
#include <tkexport.h>
#include <pglocal.h>                    /* for PF definition */
#include <syiexp.h>                     /* sysinit error messages */
#include <basemid.h>                    /* for MSG_xxxx */
#include <curdir.h>
#include <string.h>
#include <ioexp.h>                      /* must follow tkexport.h */
#include <smlocal.h>
#include <devcmd.h>
#include <semxport.h>
#include <infoseg.h>
#include <vmkh.h>            // must be after tkexport.h for KSEM definition
#include <kmode.h>           // defect 98488

void SMFreeCompDFs();     // defect 98488
ulong_t *SMpFreeCompDFs;  // defect 98488

extern KSEMMTX pgCommitSem;

extern KSEMMTX  SMSwapperSem;
#define SM_SWAPPER_PAD_SHRINK   0x80
extern struct infoseg_s Sis2;           /* this should be in infoseg.h */
extern void TKYieldNonIdle();           /* Let non-idle threads run B733890 */

#if (PIS_VADDR - PGIO_VADDR)
    #error "PIS_VADDR and PGIO_VADDR don't match"
#endif
#if (PIS_PADDR - PGIO_PADDR)
    #error "PIS_PADDR and PGIO_PADDR don't match"
#endif

/***LD+ data local to swapper SFT routines
 *
 *
 *      smSFNarr is an array of hSFNs (system file numbers) used for I/O
 *      on the swap file.  It needs to be initialized to NULL so that
 *      file system shutdown code won't try to dereference it if we crash
 *      while booting.
 */
hSFN            *smSFNarr = NULL;
/*end*/

/***LD+ smSFTmap - swapper SFT allocation bitmap
 *
 *      smSFTmap is a very small bitmap, used to manage the swapper's
 *      pool of SFTs and TCB variable save buffers.  A set bit indicates
 *      a free SFT, and a zero bit a taken one.
 */
ulong_t         smSFTmap = (1 << MAXSIMULSWAP) - 1;
/*end*/


/***LD+ smSwapDisk - drive number of the drive containing the swap file
 *
 *      smSwapDisk is the logical drive number (A=1, B=2, etc.) of
 *      the drive that the swap file is located on.
 */
// Moved to sdata.asm for System Anchor Block - f122427
extern unsigned char   smSwapDisk;
/*end*/

/***ED  SMMaxPageCmds - max number of page cmds
 *
 *      SMMaxPageCmds is the max number of commands in FSD page i/o list
 */
uchar_t         SMMaxPageCmds = 0;

/***LD  smPCarr - array of SMPageCmdList pointers
 *
 *      smPCarr is an array of SMPageCmdList pointers used for
 *      group swap outs
 */
struct SMPageCmdList_s *smPCarr[MAXSIMULSWAP];


struct fsinfo_s {                       /* QFsInfo buffer - level 1 */
    dword       fsid;                   /* this should really be defined */
    dword       sectors_per_AU;         /* in some standard include file */
    dword       number_AU;
    dword       avail_AU;
    word        bytes_per_sector;
};


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


/***LP  smSIOSwapIn     - swap a page in
 *
 *      This routine is the SFT based file system version of SIOSwapIn.
 *      As such it is called by the Swap Manager with a page to be
 *      swapped in, the swap frame to swap it in from, and the address
 *      it should be swapped in to.  To do this it calls primitive
 *      kernel SFT routines, after doing the necessary translations.
 *
 *      - translate swap id into a swap file offset
 *      - grab an SFT out of the pool
 *      - seek the SFT to the correct swap file offset
 *      - do the read
 *      - set up the return code
 *
 *      ENTRY   SwapId  - swap id of first page
 *              addr    - transfer address of page to swap in (either
 *                        physical or virtual address, depending on DD's whim)
 *              SFTIndex - SFT/PageCmdList index
 *      EXIT    error code
 *
 *
 *      translate swap id to byte offset in file
 *      allocate an SFT from the swapper pool
 *      seek the SFT to the calculated byte offset
 *      if (no error in seek)
 *              perform the read
 *      free the SFT
 *      return error code
 */
unsigned smSIOSwapIn(SwapId, addr, SFTIndex)
swapid_t SwapId;
ulong_t addr;
ulong_t SFTIndex;
{
    register unsigned   err;
    register SMPageCmd_t *pchOp;

    pchOp=smPCarr[SFTIndex];

    pchOp->Hdr.pgcmdh_InFlags     = 0;
    pchOp->Hdr.pgcmdh_OutFlags    = 0;
    pchOp->Hdr.pgcmdh_OpCount     = 1;
    pchOp->Cmd[0].pgcmd_cmd       = PB_READ_X;
    pchOp->Cmd[0].pgcmd_priority  = PRIO_PAGER_HIGH;
    pchOp->Cmd[0].pgcmd_status    = 0;
    pchOp->Cmd[0].pgcmd_addr      = addr;
    pchOp->Cmd[0].pgcmd_fileOffset = SwapIdToByte(SwapId);

    err = FSD_DoPageIO(smSFNarr[SFTIndex], (PageCmdHeader *)pchOp);
//  smAssert(err == NO_ERROR);

    /*
     * If an error occurred, grab the error code out of the packet
     * before returning the packet to the pool.
     */
    if ((pchOp->Cmd[0].pgcmd_status & PGIO_ATTEMPTED) == 0 ||
        (pchOp->Cmd[0].pgcmd_status & PGIO_FAILED) != 0) {
        err = pchOp->Cmd[0].pgcmd_error;
    }
    return(err);
}



/***LP  smSIOResize     - resize the swap file
 *
 *
 *      This routine is the SFT based file system version of SIOResize.
 *      As such it is called by the Swap Manager with the new size for
 *      the swap file.  Note that we do not do any free space checking,
 *      as that was supposed to already have been done by the Swap Manager.
 *      All smSIOResize does is translate parameters and call a kernel
 *      SFT routine.
 *
 *      - translate size from pages into bytes
 *      - issue the resize call
 *      - translate any error code
 *
 *      ENTRY   nPages  - new size of the swap file, in pages
 *      EXIT    error code
 *
 *      allocate an SFT from the swapper pool
 *      issue the resize call
 *      free the SFT
 *      return the error code
 *
 */
unsigned smSIOResize(nPages)
swapid_t nPages;
{
    register unsigned   err;
    int                 SFTindex;
    ulong_t             ulWantContig=PAGESIZE;

    err = smAllocSFT(SSToDS(&SFTindex));
    if (err)
        return(err);

retry:
    err = FSD_AllocatePageSpace(smSFNarr[SFTindex], PagesToBytes(nPages),
        ulWantContig);
    if (err && ulWantContig==PAGESIZE)  {
        ulWantContig=0x400;    /* BUGBUG : should be sector size, using 1k */
        goto retry;
    }

    smFreeSFT(SFTindex);
    return(err);
}



/***LP  smSIOSpaceQuery - determine free space on the swap volume
 *
 *      This routine is the SFT based file system version of SIOSpaceQuery.
 *      Estimating total swap space is a tricky thing when swapping
 *      through the file system, but we ignore all this and simply
 *      return the current amount of free space plus the swapfile size.
 *
 *      ENTRY   pPageCnt = pointer to location to fill in with result
 *      EXIT    returns error code
 *              pPageCnt updated
 *
 *      - query amount of free space on swap disk
 *      - add that to current size of swap file
 *
 */
unsigned smSIOSpaceQuery(swapid_t *pPageCnt)
{
        register unsigned long  bytecnt;
        register unsigned       err;
        struct fsinfo_s         fsinfo;

        err = IOGetDiskSpaceInfo(smSwapDisk, SSToDS(&fsinfo), sizeof(fsinfo));

        bytecnt = fsinfo.avail_AU * fsinfo.sectors_per_AU *
                (dword)fsinfo.bytes_per_sector;
        *pPageCnt = (bytecnt >> PAGESHIFT) + smFileSize;

        return(err);
}


/***LP  smGrpSwpErrHandler - error handler for group swapping
 *
 *      smGrpSwpErrHandler - starting from the last cmd in the list, process
 *          each cmd to determine what happened to it and act accordingly.
 *          Note: since only the first cmd must be swapped out, only retry
 *          that cmd if it encountered an error.
 *
 *      ENTRY   pSMPgCList     - pointer to a SM PageCmdList
 *              SFTindex       - index of the SFT array
 *      EXIT    0 on success
 *              else error code
 *
 *      rc = 0
 *      ncmds = pSMPgCList->Hdr.pgcmdh_OpCount
 *      for (i=(ncmds-1); i>=0; i--)
 *          if ((pSMPgCList->Cmd[i].pgcmd_status & PGIO_ATTEMPTED) == 0)
 *              if (i)
 *                  PGFreeBuf(pSMPgCList->Swp[i].laddr)
 *                  pgClearSemPF(pSMPgCList->Swp[i].pPF)
 *                  SMFreeDF(ByteToSwapId(pSMPgCList->Cmd[i].pgcmd_fileOffset))
 *              else
 *                  pSMPgCList->Swp[i].pSF->sf_df =
 *                             ByteToSwapId(pSMPgCList->Cmd[i].pgcmd_fileOffset)
 *                  rc = smRetrySwapOut(pSMPgCList,SFTindex)
 *          else
 *              if ((pSMPgCList->Cmd[i].pgcmd_status & PGIO_FAILED) == 0)
 *                  SMSetpf(pSMPgCList,i)
 *                  PGFreeBuf(pSMPgCList->Swp[i].laddr)
 *                  if (i)
 *                      SMIdleSF(pSMPgCList->Swp[i].pSF)
 *                      pgClearSemPF(pSMPgCList->Swp[i].pPF)
 *              else
 *                  smcBrokenDF++
 *                  SMSwapSpace--
 *                  if (i)
 *                      PGFreeBuf(pSMPgCList->Swp[i].laddr)
 *                      pgClearSemPF(pSMPgCList->Swp[i].pPF)
 *                  else
 *                      pSMPgCList->Swp[0].pSF->sf_df = smAllocDF()
 *                      if (pSMPgCList->Swp[0].pSF->sf_df == (ulong_t)-1)
 *                          pSMPgCList->Swp[0].pSF->sf_df = 0
 *                          pSMPgCList->Swp[0].pSF->sf_flags &= ~SF_DFVALID
 *                          PGFreeBuf(pSMPgCList->Swp[0].laddr)
 *                          rc = ERROR_SWAP_FILE_FULL
 *                      else
 *                          rc = smRetrySwapOut(pSMPgCList,SFTindex)
 *      return(rc)
 *
 */

int smGrpSwpErrHandler(pSMPgCList,SFTindex)
struct SMPageCmdList_s *pSMPgCList;
int SFTindex;
{
    uchar_t ncmds;
    int i;
    int rc;

    /*  test status char for error
     *  if low nibble 0 then never attempted else low nibble = 1,
     *  check high nibble if high nibble = 0 then operation successful
     *  else error
     */
    rc = 0;
    ncmds = pSMPgCList->Hdr.pgcmdh_OpCount;

    /*  since we will only retry the first command in the group, process
     *  the others first
     */
    for (i=(ncmds-1); i>=0; i--) {
        smAssert((pSMPgCList->Swp[i].pVP->vp_flags & (VP_BUSY|VP_DF|VP_PF)) ==
                 (VP_BUSY|VP_DF|VP_PF));
        if ((pSMPgCList->Cmd[i].pgcmd_status & PGIO_ATTEMPTED) == 0)  {
            /*
             * Operation was never attempted
             */
            if (i) {
                /*
                 * if not first page in the list, free the buffer, clear
                 * the pf and free the df
                 */
                PGFreeBuf(pSMPgCList->Swp[i].laddr);
                pgClearSemVP(pSMPgCList->Swp[i].pVP);
                SMFreeDF(pSMPgCList->Swp[i].pPF->pf_block);
                pSMPgCList->Swp[i].pVP->vp_df = 0;
#ifdef SMSTRICT
                pSMPgCList->Swp[i].pPF->pf_block = -1;
#endif
            } else {
                /*
                 * This was the first page in the list, so retry it.  The
                 * df field in the SF is already set.
                 */
                rc = smRetrySwapOut(pSMPgCList,SFTindex);
            }
        } else {          /* operation was tried */
            if ((pSMPgCList->Cmd[i].pgcmd_status & PGIO_FAILED) == 0)  {
                /*
                 * this operation was successful
                 */
                pSMPgCList->Swp[i].pVP->vp_dirty = 0;
                PGFreeBuf(pSMPgCList->Swp[i].laddr);

                if (i)  {  /* if not first cmd */
                    pgClearSemVP(pSMPgCList->Swp[i].pVP);
                }
            } else {
                /*
                 * this operation failed
                 */
#ifdef SMSTRICT
                pSMPgCList->Swp[i].pPF->pf_block = -1;
#endif
                pSMPgCList->Swp[i].pVP->vp_df = 0;
                smcBrokenDF++;
                SMSwapSpace--;

                /*
                 * if failure and not first command, don't retry
                 * just free buffer and clear PF sem
                 */
                if (i) {
                    PGFreeBuf(pSMPgCList->Swp[i].laddr);
                    pgClearSemVP(pSMPgCList->Swp[i].pVP);
#ifdef SMSTRICT
                    pSMPgCList->Swp[i].pPF->pf_block = -4;
#endif
                } else  {
                    /*
                     * if the first cmd failed: we must retry so
                     * get another DF and retry this cmd
                     */
                    if ((pSMPgCList->Swp[0].pPF->pf_block = smAllocDF(FALSE)) ==-1){
                        /*
                         * if we're out of DFs give up, clean up and return
                         */
                        PGFreeBuf(pSMPgCList->Swp[0].laddr);
                        rc = ERROR_SWAP_FILE_FULL;
                    } else {
                        /*
                         * we found new DF so try again
                         */
                        pSMPgCList->Swp[0].pVP->vp_df = 1;
                        rc = smRetrySwapOut(pSMPgCList,SFTindex);
                    }
                }
            }
        }
    }
    return(rc);
}


/***LP  smRetrySwapOut - single swapout after error on group swap out
 *
 *      smRetrySwapOut takes a page from physical memory and swaps it out
 *      to disk.
 *
 *      note: this routine is only called if an error was returned by
 *            FSD_DoPageIO and the first command of a SMPageCmdList was
 *            not completed successfull
 *
 *      ENTRY   pSMPgCList - pointer to a SMPageCmdList
 *              SFTindex   - index of SFT currently being used
 *
 *      EXIT    0 for no error else return code
 *
 */
int smRetrySwapOut(pSMPgCList, SFTindex)
struct SMPageCmdList_s *pSMPgCList;
int SFTindex;
{
    int err;

    smSetCmd1(pSMPgCList);
    err = FSD_DoPageIO(smSFNarr[SFTindex], (VOID *)pSMPgCList);
    smAssert(err==0);

    while (pSMPgCList->Hdr.pgcmdh_OutFlags != PGIO_FO_DONE)  {
        smcBrokenDF++;
        SMSwapSpace--;

        if ((pSMPgCList->Swp[0].pPF->pf_block = smAllocDF(FALSE)) == -1) {
            pSMPgCList->Swp[0].pVP->vp_df = 0;
#ifdef SMSTRICT
            pSMPgCList->Swp[0].pPF->pf_block = -5;
#endif
            err = ERROR_SWAP_FILE_FULL;
            goto bailout;
        }
        smSetCmd1(pSMPgCList);
        err = FSD_DoPageIO(smSFNarr[SFTindex], (VOID *)pSMPgCList);
        smAssert(err==0);
    }

    pSMPgCList->Swp[0].pVP->vp_dirty = 0;
bailout:
    PGFreeBuf(pSMPgCList->Swp[0].laddr);
    return(err);
}


/***LP  smSetCmd1 - set header and first cmd fields for a SMPageCmdList
 *
 *      note: this routine is only called if an error was returned by
 *            FSD_DoPageIO and the first command of a SMPageCmdList was
 *            not completed successfull
 *
 *      ENTRY   pPCList - pointer to a SMPageCmdList
 *
 *      EXIT    none
 *
 */
void smSetCmd1(pPCList)
struct SMPageCmdList_s *pPCList;
{
    pPCList->Hdr.pgcmdh_InFlags    = 0;
    pPCList->Hdr.pgcmdh_OutFlags   = 0;
    pPCList->Hdr.pgcmdh_OpCount    = 1;

    pPCList->Cmd[0].pgcmd_cmd      = PB_WRITE_X;
    pPCList->Cmd[0].pgcmd_priority = 0;
    pPCList->Cmd[0].pgcmd_status   = 0;
    pPCList->Cmd[0].pgcmd_fileOffset=SwapIdToByte(pPCList->Swp[0].pPF->pf_block);
}


/***EP  SMCompact - Attempt to compact the swap file
 *
 *      SMCompact is called when the system is very idle, in the
 *      context of the TSD daemon, to try to compact the swap file.
 *
 *      SMCompact returns when either the swap file is as small as we
 *      can get it or something more important than shrinking the swap
 *      file has come up, as indicated by TKNeedTSDDaemon.
 *
 *      ENTRY   none
 *
 *      EXIT    none
 */
void SMCompact(void)
{
    unsigned err;
    laddr_t laddr;
    ulong_t saddr;
    ulong_t iSFT;
    SMPageCmd_t *pchOp;
    ulong_t DFTmp;
    register struct vp_s *pvp;
    ulong_t iDF;

    if ((SMSwapSpace-SMcDFInuse) > SM_SWAPPER_PAD_SHRINK) {
        if (smInitialized > 1) {           /* BUGBUG is this needed */
// overcommit
            if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
//              if ((err = KSEMRequestMutex((HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem),
//                 0)) == NO_ERROR) {
                if ((err = VMGetSem(smHobheap, VMGS_TAKESEM|VMGS_ERRBLOCK,
                      NA)) == NO_ERROR)  {
                   if ( (KSEMRequestMutex((HKSEM)&pgCommitSem, 0)) == NO_ERROR) {
                        SMSetSwapSpace(SMcDFInuse, 0);
                        KSEMReleaseMutex((HKSEM)&pgCommitSem);
                   }
//                 KSEMReleaseMutex((HKSEM)&(apkh[smHeap].kh_pkrh->krh_ksem));
                   VMClearSem(smHobheap);
                }
            } else {
                if ( (KSEMRequestMutex((HKSEM)&SMSwapperSem, 0)) == NO_ERROR) {
                     (void)SMSetSwapSpace2();
                     KSEMReleaseMutex((HKSEM)&SMSwapperSem);
                }
            }
// end overcommit
        }
    }



top:
    if (!smPendingShrink || IOShutdownState) {
        /*
         * If there's no shrinking to do or the system is shutting down,
         * get out of here.
         */
        goto bailout;
    }

    TKYieldNonIdle();                   /* Yield to non-idle threads B733890 */
    if (TKNeedTSDDaemon()) {            /* Important work to do? */
        goto bailout;
    }

    if (smDFMac < SMSwapSpace) {
        goto try_shrink;
    }

    /*
     * Find the VP that is connected to the last in-use DF.
     */
    if (SMcInMemFile > smDFMac) {   // don't shrink in memory swapfile
        goto bailout;
    }
    pvp = pgFindDF(smDFMac);
    if (pvp == NULL) {
        smAssert(smcBrokenDF);
        goto bailout;
    }
    iDF = smDFMac;

    if (TKNeedTSDDaemon()) {
        goto bailout;
    }

    /*
     * Before we get the semaphore, but before we get carried away, let's make
     * sure that we've got a place close up in the file to move the page to.
     * Note that this test may well fail, as some pages have both PF and DFs
     * and we don't want to steal a clean disk image just for the sake of
     * compaction.
     * also don't shrink the in memory swapfile
     */
    DFTmp = smAllocDF(TRUE);
    if (DFTmp == -1 || DFTmp >= iDF  || SMcInMemFile > DFTmp) {
        if (DFTmp != -1)
            SMFreeDF(DFTmp);
        goto bailout;
    }

    /*
     * OK, pvp points to the offending vp, so let's make sure that we can
     * get its semaphore.
     */
    if (pvp->vp_busy) {
        /*
         * The semaphore is taken, so we can't do anything with
         * the page.  Sleep and try again.
         */
        SMFreeDF(DFTmp);
        goto top;
    }

    /*
     *  If the page has an image in memory as well as on disk, just free
     *  the disk image and we are done.
     */
    if (pvp->vp_pf) {
        SMFreeDF(iDF);
        SMFreeDF(DFTmp);
        pvp->vp_df = 0;
#ifdef SMSTRICT
        pgFrameToPF(pvp->vp_frame)->pf_block = -1;
#endif
        goto try_shrink;
    }

    /*
     * If we've gotten to here, there is no PF with this VP, so let's
     * take the VP semaphore
     */
    pgSetSem(pvp);

    /*
     * To avoid priority inversions, declare the daemon as owner of an
     * invertable semaphore.
     */
    TKEnterInversion(pTCBCur);
    SMPageSemHeld = (ulong_t)pvp;

    /*
     * We now own the semaphore, so allocate a buffer to read the page into.
     */
    if ( PGAllocBuf(1, PG_SWAPPABLE | PG_NOBLOCK, -1, SSToDS(&laddr),
        SSToDS(&saddr), 0) ) {
        SMFreeDF(DFTmp);
        goto allocerror;
    }
    if (pgSwapAddrType & PIS_PADDR) {
        PGPageToPhys(laddr >> PAGESHIFT, 0, SSToDS(&saddr));
    }

    /*
     * Mark this thread as swapping and get a swapper SFT
     */
    smSetSwapFlags(SM_TCB_SWAPPING);
    if ( smAllocSFT(SSToDS(&iSFT)) ) {
        PGFreeBuf(laddr);
        SMFreeDF(DFTmp);
        goto sfterror;
    }
    pchOp=smPCarr[iSFT];

    /*
     * Fill in a command structure to read in one page.  Note that most
     * of the structure is re-used unaltered for the write out below.
     */
    pchOp->Hdr.pgcmdh_InFlags     = 0;
    pchOp->Hdr.pgcmdh_OutFlags    = 0;
    pchOp->Hdr.pgcmdh_OpCount     = 1;
    pchOp->Cmd[0].pgcmd_cmd       = PB_READ_X;
    pchOp->Cmd[0].pgcmd_priority  = PRIO_PAGER_READ_AHEAD;
    pchOp->Cmd[0].pgcmd_status    = 0;
    pchOp->Cmd[0].pgcmd_addr      = saddr;
    pchOp->Cmd[0].pgcmd_fileOffset = SwapIdToByte(iDF);

    err = FSD_DoPageIO(smSFNarr[iSFT], (PageCmdHeader *)pchOp);

    /*
     * If either the TSD daemon needs his thread back or the I/O failed,
     * clean up and leave now.
     */
    if (TKNeedTSDDaemon() ||
        (pchOp->Cmd[0].pgcmd_status & PGIO_ATTEMPTED) == 0 ||
        (pchOp->Cmd[0].pgcmd_status & PGIO_FAILED) != 0  || err != 0) {
        PGFreeBuf(laddr);
        SMFreeDF(DFTmp);
        goto clearsem;
    }

    /*
     * Write the page out to the new DF.
     */
    pchOp->Cmd[0].pgcmd_cmd  = PB_WRITE_X;
    pchOp->Cmd[0].pgcmd_fileOffset = SwapIdToByte(DFTmp);

    err = FSD_DoPageIO(smSFNarr[iSFT], (PageCmdHeader *)pchOp);

    if ((pchOp->Cmd[0].pgcmd_status & PGIO_ATTEMPTED) == 0 ||
        (pchOp->Cmd[0].pgcmd_status & PGIO_FAILED) != 0  || err != 0) {
        PGFreeBuf(laddr);
        SMFreeDF(DFTmp);
        goto clearsem;
    }

    /*
     * Update the DF field in the VP or PF.
     */
    SMFreeDF(iDF);
    if (pvp->vp_pf) {
        pgFrameToPF(pvp->vp_frame)->pf_block = DFTmp;
    } else {
        pvp->vp_block = DFTmp;
    }
    PGFreeBuf(laddr);

clearsem:
    smFreeSFT(iSFT);
sfterror:
    smClearSwapFlags(SM_TCB_SWAPPING);
allocerror:
    SMPageSemHeld = 0;
    pgClearSemVP(pvp);
    TKExitInversion();

try_shrink:
    if (!TKNeedTSDDaemon()) {
        /*
         * We need to take the commit semaphore to prevent anyone else
         * from trying to simultaneously resize the swap file.  To avoid
         * excessive TSD daemon latencies we don't want to block to do
         * this, so take the semaphore no block, and if there are any
         * errors just hop back up to the top of the routine and yield.
         */
// overcommit
        if (PGGlobalFlags & PG_DO_OVERCOMMIT) {
            if((err = VMGetSem(smHobheap, VMGS_TAKESEM|VMGS_ERRBLOCK,
                  NA)) == NO_ERROR) {
                if ( (KSEMRequestMutex((HKSEM)&pgCommitSem, 0)) == NO_ERROR) {
                     smPerformShrink();
                     KSEMReleaseMutex((HKSEM)&pgCommitSem);
                }
                VMClearSem(smHobheap);
            }
        } else {
            if ( (KSEMRequestMutex((HKSEM)&SMSwapperSem, 0)) == NO_ERROR) {
                 smPerformShrink();
                 KSEMReleaseMutex((HKSEM)&SMSwapperSem);
            }
        }
// end overcommit

        goto top;
    }

bailout:
    return;
}


void SMFreeCompDFs()
{
   ulong_t *pKRHBToFree;
   ulong_t *pKRHBToFreeNext;

   smAssert(SMpFreeCompDFs != 0);

   pKRHBToFree = SMpFreeCompDFs;

   flGlobalActions &= ~KM_FF_FREECOMPDFS;
   SMpFreeCompDFs = 0;  //clear the head pointer and start freeing the blocks

   while (pKRHBToFree != 0) {
      pKRHBToFreeNext = *pKRHBToFree;
      VMFreeKHB(VM_HKH_PUB_RESRW,pKRHBToFree);  // can block
      pKRHBToFree = pKRHBToFreeNext;
   }
   return;
}


#pragma code_seg("CINIT_TEXT", "CODE")
#pragma data_seg("CINIT_DATA", "CINITDATA")
/***EP  smSIOInit       - initialize SFT swap I/O package
 *
 *      description
 *
 *      ENTRY   pszSwapPath = string containing swap path
 *              pInitSize = pointer to ulong containing required minimum
 *                              inital size of swap file (in pages)
 *              pFlags  = pointer to flags word
 *
 *      EXIT    returns error code
 *              pInitSize filled in
 *              pFlags updated
 *
 *      USES    SMMinFree
 *
 *      if (szSwapPath does not start with a drive specifier)
 *              szSwapPath = BootDisk + ':' + szSwapPath
 *
 *      szSwapFile = szSwapPath + "\SWAPPER.DAT"
 *
 *      ; Make sure that the drive specified is not a floppy and that
 *      ; it has a reasonable amount of space left on it.
 *      if (DriveType == floppy)
 *              ErrorPrint("Swap: swap device cannot be a floppy")
 *              PageInitSwap(-, NoSwapping, -)
 *              ErrorReturn(BadParameter)
 *      delete(szSwapFile);
 *      if (free space on drive < swapMinFree + initial size of swap file)
 *              ErrorPrint("Swap: insufficient space on swap device")
 *              PageInitSwap(-, NoSwapping, -)
 *              ErrorReturn(BadParameter)
 *
 *      ; Allocate the pool of SFTs and initialize them to point
 *      ; to the swap file.
 *      smSFNarr = alloc(MAXSIMULSWAP * sizeof(smSFNarr[0]))
 *      for (i=0; i<MAXSIMULSWAP; i++)
 *              SFTOpen(&smSFNarr[i], szSwapFile)
 *
 *      SFTresize(smSFNarr(0], initial size)
 *
 *      ; fix up indirect calls
 *      SIOSwapOut = smSIOSwapOut
 *      SIOSwapIn = smSIOSwapIn
 *      SIOResize = smSIOResize
 *      SIOSpaceQuery = smSIOSpaceQuery
 *
 *      initialize SFT allocation map so that all entries are free
 */
swapid_t smSIOInit (pszSwapPath, pInitSize, pFlags)
char *pszSwapPath;
swapid_t *pInitSize;
ulong_t *pFlags;
{
    unsigned    FreeSpace;
    char        achSwapFile[MAXPATHLEN]; /* buffer for building file name */
    char        *pszSwapFile;           /* pointer to swap file name string */
    int         i,j;
    unsigned    err = NO_ERROR;
    ulong_t     ulOpenFlags;
    ulong_t     ulMediaFlags;
    ulong_t     ulReqMax;
    ulong_t     pcaddr = NULL;

    *pFlags = PIS_VADDR;
    /*
     * If the swap path does not contain a drive specifier, add
     * the specifier of the boot disk.
     */
    if ((pszSwapPath != NULL && pszSwapPath[1] != ':') || pszSwapPath==NULL) {
        achSwapFile[0] = 'c';   /* BUGBUG - is C ok? */
        achSwapFile[1] = ':';
        achSwapFile[2] = '\0';
    }
    else
        achSwapFile[0]= '\0';

    if (pszSwapPath != NULL)
        strcat(SSToDS(achSwapFile), pszSwapPath);
    i = strlen(SSToDS(achSwapFile)) - 1;
    if (achSwapFile[i] == '\\' || achSwapFile[i] == '/')
        achSwapFile[i] = '\0';
    strcat(SSToDS(achSwapFile), "\\swapper.dat");

    /*
     * Allocate the pool of hSFNs and initialize them to point
     * to the swap file.
     */
    i = -1;                             /* for use by error recovery code */
// 56884 start
//  err = VMAllocKHB(VM_HKH_PUB_RESRW, MAXSIMULSWAP * sizeof(*smSFNarr),
//      SMSFNOWNER, NA, &smSFNarr);
//  if (err)
//      goto bailout;

    //113275 ************************WARNING*****************************
    //113275 *  smpHeap calculations below are duplicated in SMReinit   *
    //113275 *  in smalloc.c.  If changes are made below, please update *
    //113275 *  SMReinit appropriately.                                 *
    //113275 ************************************************************

    smSFNarr =  smpHeap;
    smpHeap += (MAXSIMULSWAP * sizeof(*smSFNarr));
// 56884 end

    /*
     * First try opening the swapper file where the user wanted.  If that
     * fails then try /os2/system on the boot drive, and then the root of
     * the boot drive.
     */
    pszSwapFile = SSToDS(&achSwapFile[0]);
    smSwapDisk = (unsigned char)((pszSwapFile[0]>='a' && pszSwapFile[0]<='z') ?
        (pszSwapFile[0] - 'a' + 1) : (pszSwapFile[0] - 'A' + 1));
    ulOpenFlags = PGIO_FIRSTOPEN;
    err = FSD_OpenPageFile(SSToDS(&ulOpenFlags), pszSwapFile, &smSFNarr[0],
        SSToDS(&ulReqMax));
    if (err == NO_ERROR) {
        err = smSIOSpaceQuery(SSToDS(&FreeSpace));
        if (!err && (FreeSpace > (SM_MIN_SWAPINIT_SIZE+SM_MIN_MINFREE_SIZE))) {
            goto openrest;
        }
        /* BUGBUG: FSD_ClosePageFile(smSFNarr[0]); */
    }

    smSwapDisk = (unsigned char) ((struct InfoSegGDT *)(Sis2.is_va))->SIS_BootDrv;
    pszSwapFile = "c:\\os2\\system\\swapper.dat";
    *pszSwapFile = (char) ((unsigned short)'a' + smSwapDisk - 1);
    err = FSD_OpenPageFile(SSToDS(&ulOpenFlags), pszSwapFile, &smSFNarr[0],
        SSToDS(&ulReqMax));
    if (err == NO_ERROR) {
        err = smSIOSpaceQuery(SSToDS(&FreeSpace));
        if (!err && (FreeSpace > (SM_MIN_SWAPINIT_SIZE+SM_MIN_MINFREE_SIZE))) {
            goto openrest;
        }
        /* BUGBUG: FSD_ClosePageFile(smSFNarr[0]); */
    }

    pszSwapFile = "c:\\swapper.dat";
    *pszSwapFile = (char) ((unsigned short)'a' + smSwapDisk - 1);
    /* smSwapDisk is unchanged from the last attempt */
    err = FSD_OpenPageFile(SSToDS(&ulOpenFlags), pszSwapFile, &smSFNarr[0],
        SSToDS(&ulReqMax));
    if (err != NO_ERROR)
        goto bailout;
    err = smSIOSpaceQuery(SSToDS(&FreeSpace));
    if (err || (FreeSpace <= (SM_MIN_SWAPINIT_SIZE+SM_MIN_MINFREE_SIZE))) {
        err = ERROR_NOT_ENOUGH_MEMORY;
        goto bailout;
    }

openrest:
    err = FSD_GetPageMediaInfo(smSFNarr[0],SSToDS(&ulOpenFlags));

    ulMediaFlags = ulOpenFlags;       /* save flags for PGInitHighMem call */

    i = 0;                              /* for use by error recovery code */
    /*
     * BUGBUG: Need to make sure that the drive specified is not a floppy.
     * We could test for MEDIA_NON_REMOVABLE from the GetMediaInfo, above,
     * but at last check it was returning "removable" for removable hard
     * disks, and that's not what we want.  At any rate, swapping to
     * media that can be removed while the system is running is not
     * supported.
     */
    if (pszSwapFile != SSToDS(&achSwapFile[0])) {
        SYISaveMessage(MSG_SWAP_INVALID_PATH, 1, pszSwapPath);
    }
    *pFlags = ulOpenFlags & PIS_ADDRMASK;
    ulOpenFlags = 0;

    for (i=1; i<MAXSIMULSWAP; i++) {
        err = FSD_OpenPageFile(SSToDS(&ulOpenFlags),pszSwapFile, &smSFNarr[i],
            SSToDS(&ulReqMax));
        if (err)
            goto bailout;
    }

    --i;                                /* for error recovery code */

    /*
     *   set SMMaxPageCmd to max of ulReqMax and MAXSWAPGROUPSIZE
     */
    if (ulReqMax < MAXSWAPGROUPSIZE)
        SMMaxPageCmds = (uchar_t)ulReqMax;
    else
        SMMaxPageCmds = MAXSWAPGROUPSIZE;
    /*
     *   allocate the SMPageCmdList pool
     */
// 56884 - start
//    err=VMAllocKHB(VM_HKH_PUB_RESRW, MAXSIMULSWAP*sizeof(SMPageCmd_t),
//        SMSFNOWNER, NA, SSToDS(&pcaddr));
//    if (err)
//        goto bailout;
    pcaddr   =  smpHeap;
    smpHeap += (MAXSIMULSWAP * sizeof(SMPageCmd_t));
// 56884 - end
    /*
     *  initialize the pointers to the SMPageCmdList pool
     */
    for (j=0; j<MAXSIMULSWAP; j++)  {
        smPCarr[j] = (struct SMPageCmdList_s *)
                         (pcaddr + (ulong_t)(sizeof(SMPageCmd_t)*j));
    }

    if (*pInitSize+SMMinFree <= FreeSpace) {
        err = FSD_AllocatePageSpace(smSFNarr[0], PagesToBytes(*pInitSize),
           PAGESIZE);
    } else {
        err=1;
    }

    if (err)  {
         *pInitSize = SM_MIN_SWAPINIT_SIZE;
         SMMinFree  = SM_MIN_MINFREE_SIZE;
         err = FSD_AllocatePageSpace(smSFNarr[0],
                            PagesToBytes(SM_MIN_SWAPINIT_SIZE), PAGESIZE);
         if (err)  {
             // BUGBUG - hack for HPFS when it can't find contiguous
             //  space of PAGESIZE or more
             err = FSD_AllocatePageSpace(smSFNarr[0],
                     PagesToBytes(SM_MIN_SWAPINIT_SIZE), 0x512);
             if (err)  {
                  goto bailout;
             }
         }
     }
    // do this last since HighMemory will always be enabled is swapping is off

    PGInitHighMem(ulMediaFlags & MEDIA_16M);

    return(err);


bailout:
    /*
     * This is the common error handling code, which should
     * free up all the dynamic structures allocated and exit.
     */
    /*
     * close swapper file, if opened
     */
//    for ( ; i>=0; i--) {              /* i is still set from above */
//      FSD_ClosePageFile(smSFNarr[i]);
//    }

    if (smSFNarr) {
// 56884
//        VMFreeKHB(VM_HKH_PUB_RESRW,smSFNarr);
// 56884 end
        smSFNarr = NULL;                /* for shutdown code */
    }

// 56884
//    if (pcaddr) {
//        VMFreeKHB(VM_HKH_PUB_RESRW,(void *)pcaddr);
//    }
// 56884 end

    return(err);
}
#pragma code_seg()
#pragma data_seg()
