/*static char *SCCSID = "@(#)vmbmp32.c  6.2 91/07/20";*/
#define SCCSID  "@(#)vmbmp32.c  6.2 91/07/20"

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

#define INCL_DOSP
#include <os2p.h>

#define FNO_NUM         FNO_VMBMP32_C
#include <fnomem.h>
#include <osmac.h>
#include <vmowner.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmlocal.h>

#define BMP_DONATE_SIZE              0xc00
#define BMP_DONATE_SIZE_REMAINING    (PAGESIZE - BMP_DONATE_SIZE)

/*
 *  BUGBUG IDB )1/17/90 - replace filler in VMBH structure
 */

#define bh_cBusyBlocks bh_filler

/***LP  bmpUpdateLastBusy - update the last busy block pointer
 *
 *      Given a pointer to a BMP header, this function
 *      starts at the block before the current last busy block
 *      and searches backwards until it finds a busy block.
 *      This block becomes the new last busy block, and a
 *      pointer to the block immediately following it (i.e.,
 *      the first free block after the last busy block) is
 *      returned.
 *
 *      bmpUpdateLastBusy(pbh)
 *
 *      ENTRY   pbh             - pointer to BMP header (VMBH *)
 *      RETURN  char *          - pointer to first free after last busy
 *
 *      CALLS   Busy block identifier specified in BMP header
 *
 *      WARNING It is assumed that the busy block identifier
 *              function for a resident BMP object will not
 *              yield.
 *
 *      EFFECTS BMP header
 */

char                    *bmpUpdateLastBusy(pbh)
register VMBH           *pbh;           /* Pointer to BMP header */
{
    register char       *pb;    /* Pointer to block just freed */

    pb = pbh->bh_pbLastBusy;            /* Point to old last busy block */
    while(pb >= (char *)(pbh + 1))      /* While blocks remain */
      {
        if((*pbh->bh_pfnisbusy)(pb))
          {                             /* If busy block found */
            pbh->bh_pbLastBusy = pb;    /* Set pointer */
            return(pb + pbh->bh_cbPerBlock);
                                        /* Return pointer to free block */
          }
        pb -= pbh->bh_cbPerBlock;       /* Back up to previous record */
      }
    pbh->bh_pbLastBusy = NULL;          /* All blocks are free */
    return((char *)(pbh + 1));          /* Return pointer to first block */
}


/***LP  vmRebuildBMPFreeList - rebuild the free list
 *
 *      This procedure rebuilds the free list, removing any blocks
 *      that are beyond the end of the allocated portion of the object.
 *
 *      vmRebuildBMPFreeList(VMBH *pbh)
 *
 *      ENTRY           pbh             - Pointer to BMP header
 *      RETURN          NONE
 *
 *      Walk the free list and link together all free blocks
 *      below the end of the allocated portion of the object.
 *      Set the new free list pointer in the BMP header.
 *
 *      CALLS           NONE
 *
 *      WARNING         Since this function touches blocks in the BMP
 *                      object, it may cause page faults if the the object's
 *                      pages are swappable.  Therefore, the caller must take
 *                      the semaphore for the object before calling this
 *                      function.
 *
 *      EFFECTS         Changes free list pointer in BMP header; changes
 *                      contents of free list.
 */

void                    vmRebuildBMPFreeList(pbh)
VMBH                    *pbh;           /* Pointer to BMP header */
{
  register char *pb;                    /* Block pointer */
  register char *pbFree;                /* Free list pointer */
  register char *pbNext;                /* Next block pointer */

  pbFree = NULL;                        /* Assume no free blocks */
  pb = pbh->bh_pbFreeHead;              /* Point to first free block */
  while(pb != NULL)                     /* While not at end of list */
    {
      pbNext = *((char **) pb);         /* Point at next free block */
      if(pb < pbh->bh_pbEndBlocks)      /* If below the end */
        {
          *((char **) pb) = pbFree;     /* Link block onto new free list */
          pbFree = pb;
        }
      pb = pbNext;                      /* Try next block */
    }
  pbh->bh_pbFreeHead = pbFree;          /* Set new free list */
}


/***LP  bmpShrink - shrink a BMP object
 *
 *      Given a pointer to a BMP header, this procedure recomputes
 *      the last busy block in the object, and decides whether there
 *      is excess free space at the end of the object that should be
 *      released.  If there is, then this procedure releases the space.
 *
 *      bmpShrink (pbh)
 *
 *      ENTRY   pbh             - pointer to BMP header
 *      RETURN  NONE
 *
 *      The amount of free space at the end of the object should be
 *      enough for the number of objects that will fit in half a
 *      page or BMPMINFREE, whichever is greater.  This amount is
 *      added to pblkfree and the result is rounded up to the nearest
 *      page boundary.  If this address is less than the current end
 *      of valid memory for the object, then all the pages between
 *      it and the end are released.
 *
 *      CALLS   vmPageCeil, vmRebuildBMPFreeList, VMSetMem
 *
 *      WARNING
 *
 *      EFFECTS Changes BMP header and storage commitment for BMP
 *              object.
 */

#define BMPMINFREE      4               /* Minimum desired free list size */

void                    bmpShrink(pbh)
register VMBH           *pbh;           /* Pointer to BMP header */
{
    register unsigned long  c;          /* Generic count */
    unsigned long       cblkmin;        /* Minimum free blocks at end */
    char                *pblkfree;      /* Pointer to free block */
    register char       *ppg;           /* Pointer to page boundary */
    int                 rc;             /* Return code */

    rc = VMGetSem(pbh->bh_hob,VMGS_TAKESEM+VMGS_IGNINT,NULL);
                                        /* Take ownership of the semaphore */
    vmAssert(rc == NO_ERROR);           /* Must not fail */
    cblkmin = (PAGESIZE/2)/pbh->bh_cbPerBlock;
                                        /* No. of blocks in half a page */
    if(cblkmin < BMPMINFREE) cblkmin = BMPMINFREE;
                                        /* Must have at least BMPMINFREE free */
    pblkfree = bmpUpdateLastBusy(pbh);  /* Point past new last busy block */
    ppg = (char *) vmPageCeil(pblkfree);/* Point to page boundary */
    for(;;)                             /* Find page where enough blocks free */
      {
        c = (unsigned long)(ppg - pblkfree)/pbh->bh_cbPerBlock;
                                        /* Compute no. of blocks up to end */
        if(c >= cblkmin) break;         /* Break if enough */
        ppg += PAGESIZE;                /* Add another page */
      }
    ppg = pblkfree + (c*pbh->bh_cbPerBlock);
                                        /* Compute desired end of table */
    if(ppg < pbh->bh_pbEndBlocks)       /* If there is space to release */
      {
        c = vmPageCeil(pbh->bh_pbEndBlocks);
                                        /* Top end */
        pbh->bh_pbEndBlocks = ppg;      /* Remember new end */
        ppg = (char *) vmPageCeil(ppg); /* Round up */
        c -= (unsigned long) ppg;       /* Compute size */
        vmAssert(c >= PAGESIZE && (c & (PAGESIZE - 1)) == 0);
                                        /* Must be non-zero page multiple */
        vmRebuildBMPFreeList(pbh);      /* Rebuild the free list */
        VMSetMem((ulong_t) ppg, c, HOBNULL, VMS_DECOMMIT, NA);
        vmAssert(rc == NO_ERROR);       /* Must not fail */
      }
    VMClearSem(pbh->bh_hob);            /* Release the semaphore */
}


/***EP  BMPDestroy - Destroy a BMP object.
 *
 *      BMPDestroy releases all memory resources associated with a BMP object.
 *
 *      BMPDestroy (pbh)
 *
 *      ENTRY   pbh             - address of 1st byte after BMP header (VMBH *)
 *      EXIT    NONE
 *
 *
 *      CALLS   VMFreeMem
 *
 *      EFFECTS Destroys BMP object.
 */

void                    BMPDestroy(pbh)
register VMBH           *pbh;           /* Pointer just past BMP header */
{
    int                 rc;             /* Return code from called fns */

    --pbh;                              /* Point at BMP header */

    rc = VMFreeMem((ulong_t)pbh,HOBNULL,NA);
    vmAssert(rc == NO_ERROR);           /* Must not fail */
}


/***EP  BMPFree - free a block
 *
 *      BMPFree returns a block to the free list.
 *
 *      BMPFree (pbh, pb)
 *
 *      ENTRY   pbh             - address of 1st byte after BMP header (VMBH *)
 *              pb              - block address (void *)
 *      EXIT    NONE
 *
 *      Place block on the free list.  If block was the last busy
 *      block, then, starting from newly freed block, use the
 *      client-provided busy block identifier function (if any)
 *      to find the new last busy block.  If there is a sufficient
 *      amount of free space at the end of the object, then
 *      free some space.  It is necessary to rebuild the free list
 *      so that it includes no blocks in the area that is about to
 *      be released.
 *
 *      CALLS   VMGetSem, bmpShrink, VMClearSem
 *
 *      WARNING This procedure takes the semaphore if the object
 *              is swappable in order to prevent yielding in the
 *              middle of updating the free list.
 *
 *      EFFECTS List of free blocks.  See also bmpShrink.
 */

void                    BMPFree(pbh,pb)
register VMBH           *pbh;           /* Pointer just past BMP header */
void                    *pb;    /* Address of block to free */
{
    int                 rc;             /* Return code from called fns */

    --pbh;                              /* Point at BMP header */

    /*
     *  Verify that the free list head pointer is valid.
     */

    vmAssert(((pbh->bh_pbFreeHead == NULL &&
               pbh->bh_cBusyBlocks >= ((char *) pbh->bh_pbEndBlocks -
                                      (char *)(pbh + 1))/pbh->bh_cbPerBlock) ||
           (pbh->bh_pbFreeHead >= (char *)(pbh + 1) &&
            pbh->bh_pbFreeHead < pbh->bh_pbEndBlocks)) &&
           pbh->bh_pbEndBlocks <= pbh->bh_pbEndVirt);

    /*
     *  The object's memory may have been decommitted as a result of a BMPFree
     *  on another block occurring at any yield between the time it was marked
     *  as free by the BMP32 client, and the time we reach here.  In this
     *  case, the last busy pointer points below the specified block and we
     *  take no action.
     */

    if(pbh->bh_pbLastBusy < pb)
      {
        pbh->bh_cBusyBlocks--;
        return;
      }

    if((pbh->bh_flpgtype & PG_RESIDENT) == 0)
      {                                 /* If the object is swappable */
        rc = VMGetSem(pbh->bh_hob,VMGS_TAKESEM+VMGS_IGNINT,NULL);
                                        /* Take ownership of the semaphore */
        vmAssert(rc == NO_ERROR);       /* Must not fail */
      }

    *((void **) pb) = pbh->bh_pbFreeHead;/* Insert at front of free list */
    pbh->bh_pbFreeHead = pb;
    pbh->bh_cBusyBlocks--;

    if(pb == pbh->bh_pbLastBusy)        /* If we freed the last busy block */
      {
        bmpShrink(pbh);                 /* Attempt to shrink the object */
      }
    if((pbh->bh_flpgtype & PG_RESIDENT) == 0)
      VMClearSem(pbh->bh_hob);          /* Release the semaphore if needed */

    /*
     *  Verify that the free list head pointer is valid.
     */

    vmAssert((pbh->bh_pbFreeHead == NULL ||
           (pbh->bh_pbFreeHead >= (char *)(pbh + 1) &&
            pbh->bh_pbFreeHead < pbh->bh_pbEndBlocks)) &&
           pbh->bh_pbEndBlocks <= pbh->bh_pbEndVirt);
}


/***EP  BMPGet - get a free block
 *
 *      BMPGet gets a free block from the managed object.  A block is
 *      taken from the free list.  If none are available, the object
 *      is grown to create some.  If the object cannot be grown, then
 *      an error is returned.
 *
 *      BMPGet (pbh, ppb)
 *
 *      ENTRY   pbh             - address of 1st byte after BMP header (VMBH *)
 *              ppb             - place to put block address (void **)
 *      EXIT    int             - return code (NO_ERROR if successful)
 *
 *      - If the BMP object is swappable, then take the object's
 *        semaphore.
 *      - If the list of free blocks is not empty, then remove
 *        the first block in the list, and return a pointer to it.
 *      - If the free list is empty, then try to take the object's
 *        semaphore without blocking.
 *      - If we blocked, then check the free list again, because
 *        some other thread may have grown the object.
 *      - If we did not block, then we know the free list is still
 *        empty.  If there is sufficient virtual space available,
 *        then commit additional physical storage at the tail of
 *        the object.
 *      - Release the semaphore.
 *      - If the commit succeeded, then carve the new space up into
 *        one or more blocks and place them on the free list.  Then,
 *        take the first block off the free list, and return a pointer
 *        to it.
 *      - If the block being allocated is above the highest previously
 *        known busy block, then make this newly allocated block the
 *        highest busy block.
 *      - If the BMP object is swappable, then release its semaphore.
 *
 *      CALLS   VMGetSem, VMSetMem, VMClearSem, vmPageCeil
 *
 *      WARNING This function may block.
 *
 *      EFFECTS BMP object's free list, other header info, physical size.
 */

int                     BMPGet(pbh,ppb)
register VMBH           *pbh;           /* Pointer just past BMP header */
void                    **ppb;          /* Place to put block's address */
{
  unsigned long cbCommit;               /* Number of new bytes needed */
  register char         *pb;            /* Pointer to block */
  register char         *ppg;           /* Page pointer */
  int                   rc;             /* Return code */

  --pbh;                                /* Point at BMP header */

  /*
   *  If the object is swappable, then we need to own the
   *  semaphore to allocate a block, because we could take
   *  a page fault and yield while trying to delete the first
   *  block from the free list.
   */

  if((pbh->bh_flpgtype & PG_RESIDENT) == 0)
    {
      if((rc = VMGetSem(pbh->bh_hob,VMGS_TAKESEM,NULL)) != NO_ERROR)
        {
          return(rc);
        }
    }
  rc = NO_ERROR;

  /*
   *  Loop to grow BMP object until free list non-empty or object is full.
   */

  while((pb = pbh->bh_pbFreeHead) == NULL)
    {
      /*
       *  If object is full, set error code and break out of loop.
       */

      vmAssert(pbh->bh_cBusyBlocks >=
      ((char *) pbh->bh_pbEndBlocks - (char *)(pbh + 1))/pbh->bh_cbPerBlock);

      if(pbh->bh_pbEndBlocks >= pbh->bh_pbEndVirt)
        {
          vmAssert(pbh->bh_pbEndBlocks == pbh->bh_pbEndVirt);
          rc = ERROR_NOT_ENOUGH_MEMORY;
          break;
        }

      /*
       *  Try to grow the pool.  Try to take the semaphore without blocking.
       *  If we block, restart, since another thread may have already grown
       *  the pool.  If we were successful, then we know that the free list
       *  is still empty.  If any error other than ERROR_BLOCKED, break out of
       *  loop.
       */

      ppg = (char *) vmPageCeil(pbh->bh_pbEndBlocks);
      if((rc = VMGetSem(pbh->bh_hob,VMGS_TAKESEM+VMGS_ERRBLOCK,NULL)) ==
          ERROR_BLOCKED)
        {
          rc = NO_ERROR;
          continue;
        }
      if(rc != NO_ERROR)
        {
          break;
        }

      /*
       *  Compute the additional number of bytes to commit.  This is
       *  equal to (blocksize - bytes free on last page) rounded up to
       *  a page multiple.
       */

      cbCommit = (ulong_t)(pbh->bh_cbPerBlock) -
                ((ulong_t)(ppg) - (ulong_t)(pbh->bh_pbEndBlocks));
      cbCommit = vmPageCeil(cbCommit);

      /*
       *  Commit the additional memory.  Release semaphore (sem count
       *  reduces to one if swappable BMP object).
       */

      rc = VMSetMem((ulong_t) ppg, cbCommit, HOBNULL, pbh->bh_flpgtype, NA);
      if(rc == NO_ERROR)
        {
          /*
           *  Build new free listwhile retaining the BMP32 object's
           *  semaphore (NOTE:  Allows yields to be added to the build
           *  function, or enables it to become swappable.
           */

          vmBuildBMPFreeList(pbh, pbh->bh_pbEndBlocks, ppg + cbCommit);
        }
      VMClearSem(pbh->bh_hob);
      if(rc != NO_ERROR)
        {
          break;
        }
    }

  /*
   *  If successful, remove free block found from the free list.  Clear
   *  semaphore if swappable BMP object and return.
   */

  if(rc == NO_ERROR)
    {
      vmBmpGet(pbh, ppb);
    }
  if((pbh->bh_flpgtype & PG_RESIDENT) == 0)
    {
      VMClearSem(pbh->bh_hob);
    }

  return(rc);
}


/***EP  BMPGet2 - get a free block with or without blocking
 *
 *      BMPGet gets a free block from the managed object.  A block is
 *      taken from the free list.  If none are available and NOBLOCK flag
 *      is specified, ERROR_NOBLOCK is returned, otherwise the object
 *      is grown to create some.  If the object cannot be grown, then
 *      an error is returned. NOBLOCK request cannot be made for swappable
 *      BMP records.
 *
 *      BMPGet2 (pbh, ppb, fl)
 *
 *      ENTRY   pbh             - address of 1st byte after BMP header (VMBH *)
 *              ppb     -         place to put block address (void **)
 *              fl              - flags, VMBMP_NOBLOCK or NULL
 *      EXIT    int             - return code (NO_ERROR if successful)
 */
int                     BMPGet2(pbh,ppb,fl)
register VMBH           *pbh;           /* Pointer just past BMP header */
void                    **ppb;  /* Place to put block's address */
ulong_t                 fl;             /* flags, VMBMP_NOBLOCK or NULL */
{

    if (fl & VMBMP_NOBLOCK)
      {
        /* No blocking requested */
        --pbh;                          /* Point at BMP header */

        vmAssert(pbh->bh_flpgtype & PG_RESIDENT);
        /* Can't have NOBLOCK on swappable BMP */

        if (pbh->bh_pbFreeHead != NULL)
          {
            vmBmpGet(pbh, ppb);
            return(NO_ERROR);
          }
        else
            return(ERROR_NOBLOCK);
      };
    return(BMPGet(pbh, ppb));
}


/***LP  vmBmpGet - Get the next record on BMP free llist, updating
 *              highest busy block if necessary.
 *
 *      vmBmpGet (pbh, ppb)
 *
 *      ENTRY   pbh             - address of BMP header (VMBH *)
 *              ppb             - place to put block address (void **)
 *      EXIT    NONE  (Panic if free list empty)
 */
void                    vmBmpGet(pbh, ppb)
register VMBH           *pbh;           /* Pointer to BMP header */
void                    **ppb;  /* Place to put block's address */
{
  register char *pb = pbh->bh_pbFreeHead;

  /*
   *  Verify that the free list head pointer is valid.  Make sure free list
   *  is not empty.
   */

  vmAssert(pbh->bh_pbFreeHead >= (char *)(pbh + 1) &&
           pbh->bh_pbFreeHead < pbh->bh_pbEndBlocks &&
           pbh->bh_pbEndBlocks <= pbh->bh_pbEndVirt);
  vmAssert(pb != NULL);

  /*
   *  Remove block from list and return pointer to it.  Update highest
   *  busy block address if removed block has a higher address.
   */

  pbh->bh_pbFreeHead = *((void **) pb);
  *ppb = pb;
  if(pb > pbh->bh_pbLastBusy)
    {
      pbh->bh_pbLastBusy = pb;
    }

  pbh->bh_cBusyBlocks++;

  /*
   *  Verify that the free list head pointer is valid.
   */

  vmAssert((pbh->bh_pbFreeHead == NULL ||
           (pbh->bh_pbFreeHead >= (char *)(pbh + 1) &&
            pbh->bh_pbFreeHead < pbh->bh_pbEndBlocks)) &&
           pbh->bh_pbEndBlocks <= pbh->bh_pbEndVirt);
}


/***LP  bmpIsBusy - dummy busy block identifier
 *
 *      This function always returns true.  Its purpose is to
 *      serve as the busy block identifier function for those
 *      BMP objects that do not have such functions specified.
 *      It will always respond that the specified block is busy.
 *
 *      bmpIsBusy (pb)
 *
 *      ENTRY   pb              - dummy pointer argument (void *)
 *      RETURN  1               - true
 *
 *      CALLS   NONE
 *
 *      WARNING NONE
 *
 *      EFFECTS NONE
 */

int                     bmpIsBusy(pb)
void                    *pb;            /* Dummy block pointer */
{
  return(1);                            /* Return true always */
}



/***LP  vmBuildBMPFreeList - Build freelist in BMP object
 *
 *      This function creates a new free list for a BMP object.  It is called
 *      when the object is created and whenever it is grown.  The caller
 *      must hold the heap semaphore and must have already committed the
 *      heap pages involved.
 *
 *      void vmBuildBMPFreeList(VMBH *pbh, void **pbStartFree, void *pbEndCom)
 *
 *      ENTRY           pbh             - Pointer to BMP header
 *                      pbStartFree     - Pointer to start of free region
 *                      pbEndCom        - End of committed region of heap
 *      RETURN          void            - Any error is an internal error
 *
 *      CALLS           NONE
 *
 *      EFFECTS         Updates BMP header.
 *
 *      WARNING         Function may block if BMP object is swappable.
 */

void vmBuildBMPFreeList(pbh, pbStartFree, pbEndCom)
VMBH                    *pbh;           /* Pointer to BMP object's header */
void                    *pbStartFree;   /* Pointer to start of free list */
register char           *pbEndCom;      /* Pointer to end of committed part */
{
  register char         *pb;            /* Pointer to block */
  register char         *pbNext;        /* Pointer to next block */
  ulong_t               cb = pbh->bh_cbPerBlock;
                                        /* Block size in bytes */
  register char *pbLimStart = pbEndCom + 1 - cb;
                                        /* 1 + max limit of new block start */
  /*
   *  The upper bound pbEndCom specified for the free list is typically the
   *  boundary of newly committed memory.  Since the BMP32 object should
   *  contain no more than the exact number of blocks specified on the
   *  BMPInit call, there is a possibility that more blocks could fit on the
   *  final page of the BMP32 object.  Therefore, we must clamp the pbEndCom
   *  value to the end of the usable virtual address space.
   */

  if(pbEndCom > (char *) pbh->bh_pbEndVirt)
    {
      pbLimStart = (char *) pbh->bh_pbEndVirt + 1 - cb;
    }

  /*
   *  Create linked list of free blocks as large as possible within the
   *  committed portion of the heap.
   */

  pbh->bh_pbEndBlocks = pbStartFree;

  /*
   *  While there is room for two or more blocks, initialize forward
   *  pointer of block to reference next block.
   */

  pb = pbStartFree;
  pbNext = pb + cb;
  while(pbNext < pbLimStart)            /* While pb has succeeding block */
    {
      *((char **) pb) = pbNext;
      pb = pbNext;
      pbNext += cb;
    }

  /*
   *  If new free list section is non-empty, prepend it to any existing
   *  free list.
   */

  if(pbStartFree < pbLimStart)
    {
      pbh->bh_pbEndBlocks = pbNext;
      *((char **) pb) = pbh->bh_pbFreeHead;
      pbh->bh_pbFreeHead = pbStartFree;
    }

  return;
}




/***EP  BMPHandleMap - Verify and map a BMP 32 handle to a record pointer
 *
 *      This function verifies that a given BMP32 handle (1-based BMP32
 *      record number references a valid BMP record and if so, returns the
 *      virtual address of the associated record.
 *
 *      BMPHandleMap (pbh, h, ppb)
 *
 *      ENTRY   pbh             - Pointer to just beyond BMP32 header
 *              h               - Contains the handle
 *              ppb             - Pointer to location into which the
 *                                address of the BMP record will be returned
 *
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      Check that the handle is within the range of the BMP32 object and
 *      call the busy function for the object.  If these checks pass,
 *      calculate the virtual address by the formula:
 *
 *      <virtual address> = address of 1st object + (handle-1) * recordsize
 *
 *      CALLS   NONE
 *
 *      WARNING NONE
 *
 *      EFFECTS NONE
 */

int BMPHandleMap(pbh, h, ppb)
register VMBH *pbh;
ulong_t h;
void **ppb;
{
  register char *pb;
  pb = vmBMPHandleMap(pbh, h);          /* Calculate pointer */
  pbh--;                                /* Point at BMP32 header */
  if(pb < (char *) pbh || pb > (char *) pbh->bh_pbLastBusy ||
    !((*pbh->bh_pfnisbusy)(pb)))
    {
      return(ERROR_INVALID_HANDLE);
    }
  *ppb = pb;                            /* Return pointer to block */
  return(NO_ERROR);                     /* Indicate no error */
}

/***LP  vmInitBMPObject - Initialize BMP object header and free list
 *
 *      vmInitBMPObject initializes the BMP header and free list
 *      for a BMP object for which a specified number of pages
 *      have been committed.
 *
 *      ENTRY           pbh             - Pointer to BMP header
 *                      hob             - BMP object handle
 *                      cbPerBlock      - Size of block in bytes
 *                      fl              - Flags (see BMPInit)
 *                      pfnisbusy       - Busy block identifier function
 *                      ppbFirst        - Place to put pointer to first block
 *                      cbObject        - Size of object in bytes
 *                      cpgCommit       - Number of already committed pages
 */

void vmInitBMPObject(pbh,hob,cbPerBlock,fl,pfnisbusy,ppbFirst,cbObject,
                     cbCommit)
register VMBH           *pbh;           /* Start of BMP header */
VMHOB                   hob;            /* Handle of object record */
ulong_t                 cbPerBlock;     /* Size of a block in bytes */
ulong_t                 fl;             /* flags */
int                     (*pfnisbusy)(void *);/* Busy block identifier fn */
void                    **ppbFirst;     /* Place to return pointer to 1st blk */
ulong_t                 cbObject;       /* Unrounded size of object in bytes */
ulong_t                 cbCommit;       /* Size in bytes committed */
{
  /*
   *  Use default BMP busy block function if none provided.
   */

  if(pfnisbusy == NULL)
    {
      pfnisbusy = bmpIsBusy;
    }

  /*
   *  Initialize various fields in the BMP object's header.
   */

  pbh->bh_pfnisbusy = pfnisbusy;        /* Save busy identifier fn pointer */
  pbh->bh_hob = hob;                    /* Save object record handle */
  pbh->bh_cbPerBlock = (ushort_t) cbPerBlock; /* Save block size */
  pbh->bh_flpgtype = fl | VMS_COMMIT;   /* Save page type flags */
  pbh->bh_pbLastBusy = NULL;            /* No busy blocks yet */
  pbh->bh_pbEndVirt = (char *) pbh + cbObject;
                                        /* Compute virtual end of object */
  /*
   *  Initialize the free block list.
   */

  pbh->bh_pbEndBlocks = pbh+1;          /* Mark start of free list */
  pbh->bh_cBusyBlocks = 0;              /* Set count of busy blocks to NIL */
  vmBuildBMPFreeList(pbh, pbh->bh_pbEndBlocks, (char *) pbh + cbCommit);

  /*
   *  Return pointer to start of block table.  This pointer points just after
   *  the BMP object's header.
   */

  *ppbFirst = pbh + 1;
}


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

/***EP  BMPInit - initialize BMP object
 *
 *      BMPInit initializes a BMP object.  It allocates a sparse
 *      object in the system arena of the specified type (resident
 *      or swappable), initializes the BMP header, builds the
 *      initial free list, and returns the address of the first
 *      block in the pool (the base of the table of blocks).
 *
 *      BMPInit (cblocks, cbblock, fl, hobowner, pfnisbusy, ppb)
 *
 *      ENTRY   cblocks         - maximum number of blocks (unsigned long)
 *              cbblock         - size of a block in bytes (unsigned long)
 *              fl              - flags
 *              hobowner        - owner
 *              pfnisbusy       - free block identifier fn (int (*)(void *))
 *              ppb             - place to put pointer to first block
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      Compute the size of the needed object.  Allocate it as a sparse
 *      object.  Commit the first page of storage to it.  Initialize the
 *      header and the free block list.  Return a pointer to the start
 *      of the block table.
 *
 *      CALLS   VMAllocMem
 *
 *      WARNING NONE
 *
 *      EFFECTS Creates new object.  Initializes BMP header.
 */

int                     BMPInit(cblocks,cbblock,fl,hobowner,pfnisbusy,ppb)
ulong_t                 cblocks;        /* Maximum number of blocks */
ulong_t                 cbblock;        /* Size of a block in bytes */
ulong_t                 fl;             /* flags */
VMHOB                   hobowner;       /* owner */
int                     (*pfnisbusy)(void *);   /* Busy block identifier fn */
void                    **ppb;  /* Place to return pointer */
{
  register ulong_t      cbReserve;      /* Virtual Size of object in bytes */
  VMBH                  *pbh;           /* Pointer to BMP header */
  int                   rc;             /* Return code */
  ulong_t               eflags;     // perf - extended flags
  VMAC                  ac;             /* Alloc Info buffer */

  /*
   *  Validate parameters.  Blocksize must be > pointer size and must
   *  fit in 16 bits.  Only flags allowed are PG_RESIDENT or PG_SWAPPABLE.
   */

  // defect 98488 added VMBMP_PAGEDIRALIGN bit

  eflags = fl &   (VMBMP_DONATEMEM | VMBMP_PAGEDIRALIGN); // save donate bit
  fl     = fl &  ~(VMBMP_DONATEMEM | VMBMP_PAGEDIRALIGN);
                                       // remove donate bit from fl variable

  vmAssert((cbblock >= sizeof(void **) &&
            cbblock < 65536L &&
            (fl == PG_RESIDENT || fl == (fl & (PG_U|PG_SWAPPABLE)))));

  fl |= vm_BMP_CREATE;

  /*
   *  Calculate virtual size of object.
   */

  cbReserve = cblocks*cbblock + sizeof(VMBH);

  /*
   *  Allocate the BMP object, reserving the maximum virtual size
   *  needed and committing enough pages for the initial free list.
   */

  if((rc = VMAllocMem(cbReserve,        /* Virtual size of object */
                      PAGESIZE,         /* Commit one page */
                      fl,               /* Allocation flags */
                      HOBNULL,          /* PTDA not applicable */
                      hobowner,         /* BMP32 owner */
                      HOBNULL,          /* Not an MTE object */
                      NA,               /* No Selector Mgr flags */
                      NA,               /* No block number */
                      SSToDS(&ac)))     /* Return Buffer */
    != NO_ERROR)
  {
      return(rc);                       /* Return error */
  }

  // hob = ac.ac_hob;
  if (eflags & VMBMP_DONATEMEM)  {
     rc =  VMDonateKHB((char *)ac.ac_va, 0xe00, PG_W);
     pbh = (VMBH *) (ac.ac_va+0xe00);              /* Set pointer to BMP header */
     cbReserve -= 0xe00;
  } else {
     pbh = (VMBH *) ac.ac_va;              /* Set pointer to BMP header */
  }


  /*
   *  Now we touch the object for the first time.  We may page fault
   *  and block, but there is no need to get the semaphore, since the
   *  object is newly allocated and no one else knows its name yet.
   */

  vmInitBMPObject(pbh,ac.ac_hob,cbblock,
                    (eflags & VMBMP_PAGEDIRALIGN) ? (fl | VMAC_ALIGNDIR) : fl,
                    pfnisbusy,ppb,cbReserve,
                    (eflags & VMBMP_DONATEMEM) ? 0x200 : PAGESIZE);

  return(NO_ERROR);                     /* Success */
}

#pragma code_seg()
