/*static char *SCCSID = "@(#)cachxfer.c 6.4 92/02/12";*/
#define INCL_DOSERRORS
#include <os2.h>
#include <fsh.h>
#include <pvwxport.h>
#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "superb.h"
#include "pb_ifs.h"
#include "lw.h"
#include "frame.h"
#include "log.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "pvw.h"

#include "pr_cache.h"
#include "pr_debug.h"
#include "pr_read.h"
#include "pr_misc.h"

/* BIGVAL added for 75448. This is a mask for the largest value which can */
/* be multiplied by SECSIZE without causing the result to be zero due     */
/* to overflowing ulong (i.e. if this value were shifted 9 bits to the    */
/* left (2**9=512), the result would be zero.                             */
#define BIGVAL 0x00800000

/*
 ***    CachXfer - Cache Transfer
 *
 *      CachXfer is s slave of SDR - it transfers via the
 *      cache as much of the request as fits in that single
 *      cache block.  If there is a handy hint pointer then
 *      CachXfer will follow it and transfer multiple cache's
 *      worth, otherwise we'll return after one.
 *
 *      CachXfer is much like CSecXfer except that the request
 *      is not sector aligned, nor is it for a multiple of SECSIZE
 *      bytes.
 *
 *      ENTRY   plsn = pointer to physical sector # containing 1st byte to xfer
 *              pcusReq = ptr to request count (bytes)
 *              pculRunLen = pointer to run length (sectors)
 *              pulSeekPtr = pointer to seek pointer (bytes)
 *              ppbClientData = pointer to client data address
 *
 *      EXIT    returns NO_ERROR if successful, else error code
 *              *pcusReq updated
 *              *ppbClientData updated
 *              *pulSeekPtr = updated seek pointer (bytes)
 *              *ppbufLastUsed = address of buffer header holding last byte
 *                      xferred, or 0 if request count is not zero
 *              *plsn updated
 *              *pculRunLen updated
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *  75448   02-Nov-1993 Skip Nizinski  Avoid multiplication overflow
 *                              (result of zero), when multiplying
 *                              *pulRunLen by SECSIZE. This caused a zero
 *                              length to be passed to ProbeAddress, causing
 *                              a trap.
 *          21-Sep-1988 10:17:54 by Bill McJohn [t-billm]
 *                              brought up to date with 9/15/88 assembly code
 */

USHORT
CachXfer (plsn, pcusReq, pculRunLen,
          pulSeekPtr, ppbClientData, ppbufLastUsed, pfrm)

PLSN plsn;                    /*  ptr to Device LSN with 1st byte to xfer  */
PUSHORT pcusReq;              /*  Number of bytes to transfer              */
PULONG pculRunLen;            /*  pointer to run length (in sectors)       */
PULONG pulSeekPtr;            /*  pointer to seek pointer value (bytes)    */
PBYTE far *ppbClientData;     /*  target address for read                  */
PBUFNODE far *ppbufLastUsed;  /*  Last buffer used in transfer             */
PFRAME  pfrm;
{

        PBUFNODE pbuf;  /*  Address of cache buffer with *plsn  */
        PSECTOR psec;   /*  Address of sector data in cache buffer   */
        USHORT cusCur;  /*  count of bytes to transfer to current block  */
        USHORT usRet;
        USHORT uiRet = -1;
        PBUFNODE pbufPrev = NULL; /* Previous pbuf */
        USHORT usFirstToCheck;
        USHORT usBytesToRead;
        PBLOCK pbWork;
        BYTE bWork;
        BYTE bFirstToRead;
        UINT ui;

        /*  Start by transferring the first, possibly partial, block.
         *
         *  If the run length (in bytes) from the Seek Pointer is less
         *  than the amount requested, only process that run length.
         *  (Otherwise we would go on into sectors which are not
         *  allocated to the file in question.  This would be a bad thing.)
         */
        if (*pculRunLen > BIGVAL) {                         /* REGR:75448 */
          cusCur = *pcusReq;                                /* REGR:75448 */
        }
        else {                                              /* REGR:75448 */
          cusCur = (USHORT)
                   (MIN ((ULONG)*pcusReq,
                         *pculRunLen * SECSIZE - *pulSeekPtr % SECSIZE));
        }                                                   /* REGR:75448 */

#ifdef PERFVIEW2
        PVWSTART(Rd,RdBuf);
#endif

        psec = RdBuf(*plsn, BP_KEEP, &pbuf, pfrm);

#ifdef PERFVIEW2
        PVWSTOP(Rd,RdBuf);
#endif


        if ( psec == NULL ) {

                SETHARDERR (ERROR_READ_FAULT);
                return( ERROR_READ_FAULT );
        }

#if defined(PERFVIEW) && defined(RAHEAD)
        if (pfrm->u.fReadAhead) {
            if (pbuf->fFlag & BFL_PVWRA) {
                PVWINC(RA,BufHit);
                pbuf->fFlag |= BFL_PVWRAUSE;
            }
/*25106     PVWINC(RA,BufAcc);  */
            PVWADD(RA,BufAcc,1);
        }
#endif

        /*  If cusCur is less than the number of bytes in the cache block
         *  beyond *pulSeekPtr, transfer that many bytes.  Otherwise, transfer
         *  the remainder of the block.
         */
        cusCur = MIN (cusCur,
                      (USHORT)((SPB * SECSIZE) -
                                     (POINTER_SUBTRACT(psec, pbuf->pblkData)
                                             + *pulSeekPtr % SECSIZE) ) );

        if (pbuf->bBadMask != 0) {

            /*
             *  There was a read error in one of the other sectors
             *  in the block--see if we're going to spill over
             *  into that sector.
             */
            usRet = ChkError(psec, cusCur, pbuf, pfrm);
            if ( usRet != NO_ERROR ) return ( usRet );
        }

//#114986
#ifndef MINIFSD
        if (pbuf->fFlag & BFL_LZREAD) {

          uiRet = -1; /* initialize; use as flag */

          usFirstToCheck = (((*plsn) % SPB) * SECSIZE) + /* offset of first byte */
                          *pulSeekPtr % SECSIZE;

          usBytesToRead = (cusCur + 7) / 8;

          pbWork = (PBYTE) pbuf->pLzRdBitMap + (usFirstToCheck / 8);
          bWork = *pbWork;

        /* Get the start bit position within the first byte to
         * check in lazy-read map
         */

          bFirstToRead = usFirstToCheck % 8;

          bWork >>= bFirstToRead;

          for (ui=0; ui < 8-bFirstToRead ; ui++, bWork >>= 1) {

           if (bWork & 0x01 ) {
             if ((uiRet = LazyRead (pbuf,pfrm)) != NULL) {
               return (GetHardErr(ERROR_READ_FAULT));
             }
             break;
           }

          }

          if (uiRet) { /* LazyRead not done yet. Not an error condition */

            pbWork++;
            usBytesToRead--;

            for (ui=0; ui < usBytesToRead ; ui++, pbWork++ ) {

             if (*pbWork != 0x00) {

               if (LazyRead (pbuf,pfrm))
                 return (GetHardErr(ERROR_READ_FAULT));

               break;
             }

            }

          }

        }
#endif

        /*  Verify that the user's data address is valid and in memory.
         *  If this can't be done, return the error.  Note that, since
         *  ProbeAddress itself may block, we hold the buffer over the
         *  call.
         *
         */
        IncrHoldsBUF(pbuf);  // @76193
        pfrm->fRecheckAddr = TRUE;
        usRet = ProbeAddress(PB_OPWRITE, *ppbClientData,
                             cusCur, pfrm);
        DecrHoldsBUF(pbuf);  // @76193

        if (usRet) {
         return (usRet);
        }

        /*  Copy cusCur bytes  */

        BYTE_COPY((PBYTE)psec + *pulSeekPtr % SECSIZE,
                  *ppbClientData,
                  cusCur);

        /*  We've done the copy.  Update the sizes and lengths,
         *  and then see if we have any more.
         *
         */
         *ppbClientData += cusCur;
         *pcusReq -= cusCur;
         *pulSeekPtr += cusCur;


        /*  If *pcusReq is all gone, we're done.
         *  *pculRunLen and *plsn are not updated, because
         *  they don't matter anymore.
         */
        if (*pcusReq == 0) {
          *ppbufLastUsed = pbuf;
           return (NO_ERROR);
        }

        /*  We took what we could from the cache block, but it
         *  wasn't enough.  See if we've come to the end of our
         *  extent.
         *
         *  Update *pculRunLen and *plsn.  Since the first
         *  sector was partial, we need to round up to the next
         *  whole number of sectors.
         */

        *pculRunLen -= (cusCur + SECSIZE -1)/SECSIZE;
        *plsn  += (cusCur + SECSIZE -1)/SECSIZE;


        /*  OK, we're all set to continue into another cache
         *  buffer; if the buffer's next hint is accurate,
         *  we can go on.
         *  We'll write some fresh code and not loop back
         *  above, because from now on our requests start on
         *  sector boundaries, although they may not end so.
         */

        pbufPrev = pbuf;

        pbuf = pbuf->pbufNextHint;

        while (*pculRunLen) {

            if ((pbuf == NULL) || (pbuf == pbufPrev) ||
                (pbuf->lsnSec != *plsn)) {

                /*  The hint was not accurate, so we don't know
                 *  where the data might be.  Return.  Set
                 *  *ppbufLastUsed to NULL since we didn't
                 *  fulfill the request.
                 *
                 */
#ifdef PERFVIEW
                PVWINC(Ca,NHints); /* Increment counter of hints used */
                if (pbuf == pbufPrev) PVWINC(Ca,NSelf);
#endif
                *ppbufLastUsed = NULL;
                return (NO_ERROR);
            }

            /*  The next sector for this file is in the cache.
             *  Continue transferring.  (Note:  we also know that
             *  the data starts at the beginning of the block.)
             *
             */
            if (pbuf->cLocked) {
                /*  if the buffer is locked, we wait for it to unlock
                 *  and then we try again.
                 */
                WBU (pbuf,pfrm);
            }
            else {


                /*  Transfer the least of
                 *      -- the remaining run length
                 *      -- one block
                 *      -- the remaining request amount
                 */
                cusCur = (USHORT)(MIN (*pculRunLen, SPB) * SECSIZE);
                cusCur = MIN (cusCur, *pcusReq);

#if defined(PERFVIEW)
                PVWINC(Ca,NHints);  /* A hint was used */
                PVWINC(Ca,NHits);   /* and it was good */
                if (pbuf == pbufPrev) PVWINC(Ca,NSelf);
#if defined(RAHEAD)
                if (pfrm->u.fReadAhead) {
                    if (pbuf->fFlag & BFL_PVWRA) {
                        PVWINC(RA,BufHit);
                        pbuf->fFlag |= BFL_PVWRAUSE;
                    }
/*25106              PVWINC(RA,BufAcc);  */
                     PVWADD(RA,BufAcc,1);
                }
#endif
#endif

//#114986
#ifndef MINIFSD
            if (pbuf->fFlag & BFL_LZREAD) {

              usBytesToRead = (cusCur + 7) / 8;

              pbWork = (PBYTE) pbuf->pLzRdBitMap;
              bWork = *pbWork;

            /* Start checking at the first byte of the lazyread
             * data via lazy-read map
             */

                for (ui=0; ui < usBytesToRead ; ui++, pbWork++ ) {

                 if (*pbWork != 0x00) {

                   if (LazyRead (pbuf,pfrm))
                     return (GetHardErr(ERROR_READ_FAULT));

                   break;
                 }

                }

            }
#endif

                /*  Verify that the user's data address is valid
                 *  and in memory.  If this can't be done, return
                 *  the error.  Note that, since ProbeAddress itself
                 *  may block, we hold the buffer over the
                 *  call.
                 *
                 */
                IncrHoldsBUF(pbuf);  // @76193
                pfrm->fRecheckAddr = TRUE;
                usRet = ProbeAddress(PB_OPWRITE, *ppbClientData, cusCur, pfrm);
                DecrHoldsBUF(pbuf);  // @76193

                if (usRet) {
                 return (usRet);
                }

                /*  Transfer cusCur bytes  */
                BYTE_COPY( (PBYTE)(pbuf->pblkData),
                           *ppbClientData,
                           cusCur );

                /*  Update *pcusReq and the seek pointer.
                 *
                 */
                *ppbClientData += cusCur;
                *pcusReq -= cusCur;
                *pulSeekPtr += cusCur;

              /* Position buffer as MRU  */
                PlaceBuf (pbuf, BP_KEEP);

                /*  if we've reduced *pcusReq to zero, we're done.  */
                if (*pcusReq == 0) {
                  *ppbufLastUsed = pbuf;
                   return (NO_ERROR);
                }

                /*  We took what we could from the cache block, but it
                 *  wasn't enough.  See if we've come to the end of our
                 *  extent.
                 *
                 *  Update *pculRunLen and *plsn.  Since we
                 *  did not exhaust *pcusReq, we know that cusCur
                 *  is a multiple of SECSIZE.
                 *
                 */

                *pculRunLen -= cusCur/SECSIZE;
                *plsn += cusCur/SECSIZE;

                /*  if the current buffer's next-hint is accurate,
                 *  we can go on.
                 *
                 */
                pbufPrev = pbuf;
                pbuf = pbuf->pbufNextHint;
            }
        }

        /*  If we fall through to here, we have exhausted our run
         *  length without fulfilling the request.
         *  Note the return condition that *ppbufLastUsed is NULL
         *  if the request is not satisfied.
         *
         */
        *ppbufLastUsed = NULL;
        return (NO_ERROR);
}
