/*static char *SCCSID = "@(#)cachbwrt.c 6.4 92/01/31";*/
#define INCL_DOSERRORS
#include <os2.h>
#include <fsh.h>
#include <pvwxport.h>

#include "heap.h"
#include "filemode.h"
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pb_ifs.h"
#include "lw.h"
#include "frame.h"
#include "log.h"
#include "fsdata.h"
#include "pvw.h"

#include "pbmacros.h"
#include "pr_alloc.h"
#include "pr_cache.h"
#include "pr_debug.h"
#include "pr_misc.h"
#include "pr_HEAP.h"
#include "pr_write.h"

/*
 ***    CachBWrit - Cache Byte Write
 *
 *      CachBWrit is a slave of SDW - 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
 *      CachBWrit will follow it and transfer multiple cache's
 *      worth, otherwise we'll return after one.
 *
 *      We're called only if the request starts non-sector aligned,
 *      so we know for sure that we have to start things by reading
 *      that first modified sector into the cache.
 *
 *      CachBWrit is much like CSecWrit except that the request
 *      is not sector aligned, nor is it for a multiple of SECSIZE
 *      bytes.
 *
 *      If the write through bit is set we flush the buffers
 *      written out, one at a time.
 *
 *      ENTRY   plsn = pointer to physical sector # containing 1st
 *                      byte to xfer
 *              pcusReq = address of request count (bytes)
 *              pculRunLen = pointer to run length (sectors)
 *              pulSeekPtr = pointer to seek pointer (bytes)
 *                      (note that the seek pointer is not a memory address;
 *                       that is, it is not a C pointer into memory)
 *              ppbSource = pointer to Source Addr
 *              fWrtType = write through flag
 *
 *      EXIT    returns zero if successful, else an error code.
 *
 *              If the entire request is not transferred,
 *                      *pslnFirst is updated
 *                      *pculRunLength is updated
 *
 *              In either case,
 *                      *pcusReq is updated
 *                      *ppbSource is updated
 *                      *pulSeekPtr is updated.
 *
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          21-Sep-1988 15:04:54 by Bill McJohn [t-billm]
 *                              brought up to date with 9/15/88 assembly code
 */

USHORT
CachBWrit (plsn, pcusReq, pculRunLen, pulSeekPtr, ppbSource, fWrtType, pfrm)
PLSN            plsn;           /*  ptr to first lsn in run             */
PUSHORT         pcusReq;        /*  ptr to number of bytes to write     */
PULONG          pculRunLen;     /*  length of current run               */
PULONG          pulSeekPtr;     /*  ptr to file seek pointer            */
PBYTE far *     ppbSource;      /*  ptr to ptr to user's source         */
BOOL            fWrtType;       /*  indicates write-through             */
PFRAME          pfrm;
{
    PBUFNODE pbuf;      /*  Address of cache buffer with *plsn          */
    USHORT cusCur;      /*  Number of bytes to transfer in one swoop    */
    USHORT usRet = NO_ERROR;
    USHORT usRemaining;
    LSN lsn = *plsn;
    USHORT cusReq = *pcusReq;
    ULONG culRunLen = *pculRunLen;
    ULONG ulSeekPtr = *pulSeekPtr;
    PBUFNODE pbufPrev;  /* previous pbuf */

#ifdef DEBUG
    if (lsn & ~SECMASK) {
        dprintf ("CachBWrit: vol bits on in *plsn\n");
        Int3 ();
    }
#endif

    {
    PSECTOR psec;       /*  Address of sector data in cache buffer      */

    /*
     * 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.)
     */
    usRemaining = (USHORT) (ulSeekPtr % SECSIZE);
    cusCur = (USHORT) (MIN ((ULONG)(cusReq),
                            culRunLen * SECSIZE - usRemaining));
#ifndef MINIFSD

   if (!fWrtType) {

     if ((psec = InitLzRd (lsn, &pbuf, pfrm)) == NULL)
       psec = RdBuf(lsn, BP_KEEP, &pbuf, pfrm);

   } else
#endif
     psec = RdBuf(lsn, BP_KEEP, &pbuf, pfrm);

    if (psec == NULL) {

        usRet = ERROR_READ_FAULT;
        goto Exit;
    }

    if (pbuf->bBadMask != 0) {

/* cbw90: */

#ifdef DEBUG
        dprintf ("CachBWrt: buf read has bad sectors\n");
        Int3 ();
#endif
        usRet = ChkError(psec, cusCur, pbuf, pfrm);

        if (usRet) {
            goto Exit;
        }

    }

    /*
     * 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)
                                            + usRemaining)));

    /*  Make sure that the user data address is valid and
     *  in memory.  If this can't be done, propagate the
     *  error code on up.  Note that, since ProbeAddress
     *  may block, we hold the buffer over the call.
     */
#ifndef MINIFSD
    if (pbuf->fFlag & BFL_LZREAD)
      LockBUF (pbuf); // @114986
#endif

    IncrHoldsBUF(pbuf);  // @76193

//    usRet = ProbeAddress(PB_OPREAD, *ppbSource, cusReq, pfrm);

    pfrm->fRecheckAddr = TRUE;
    usRet = ProbeAddress(PB_OPREAD, *ppbSource, cusCur, pfrm);
    DecrHoldsBUF(pbuf);  // @76193

#ifndef MINIFSD
    if (pbuf->fFlag & BFL_LZREAD)
      UnlockBUF (pbuf); // @114986
#endif

     if (usRet) {
       if (pbuf->pWaitChain)
         WakeupMisc2 (&(pbuf->pWaitChain), LOB_BUFWAIT);

       goto Exit;
     }

     BYTE_COPY ( (*ppbSource),
                 ((PBYTE)psec + usRemaining),
                 cusCur );

#ifndef MINIFSD
     if (pbuf->fFlag & BFL_LZREAD)
	    ClrLzRdBit(((USHORT)POINTER_SUBTRACT(psec, pbuf->pblkData) + usRemaining),
	               cusCur, pbuf, pfrm);
#endif

    } /* psec scope */


    {
    LSN lsnLast;        /*  used to set dirty bits. */

    /*
     * We've done the copy.  Update the sizes and lengths,
     * and then see if we have any more.
     * Finish our tricky setting of the dirty bits, too.
     *
     * To set the dirty bits, or the buffer's dirty bit mask
     * with the appropriate entry in abDirtOrd.
     */
/* cbw30: */
    lsnLast = lsn + (usRemaining + cusCur - 1) / SECSIZE;

    pbuf->bDirtMask |= abDirtOrd[(lsn % SPB) * SPB + (lsnLast % SPB)];

    /* indicate the current file used this buffer */
    pbuf->flDMask |= pfrm->flDMask;

    cusReq -= cusCur;
    ulSeekPtr += cusCur;
    usRemaining = (USHORT) (ulSeekPtr % SECSIZE);
    *ppbSource += cusCur;

    /*
     * If the write-through flag is set, write the buffer;
     * otherwise, just send it to the lazy-write queue.
     */
    if (fWrtType)
      BufWrit(pbuf, pfrm);
     else {

      AddLW (pbuf, pfrm);

      if (pbuf->cLocked == 0)
        if (pbuf->pWaitChain != 0)
          WakeupMisc2(&(pbuf->pWaitChain),
                      LOB_BUFWAIT);
    }

    /*
     * If cusReq is all gone, return zero 'cause we've done
     * what we came for.  (*pculRunLen and *plsn are not
     * updated, because they don't matter anymore.)
     *
     */
     if (cusReq == 0) {
         usRet = NO_ERROR;
         goto Exit;
     }

    /*
     * 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.
     */

/* cdw35: */

    culRunLen -= (cusCur + SECSIZE -1)/SECSIZE;
    lsn  += (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 (culRunLen) {

#ifdef PERFVIEW
        PVWINC(Ca, NHints); /* Increment counter of hints used */
        if (pbufPrev==pbuf)    PVWINC(Ca,NSelf);
#endif

        if ((pbuf == NULL) || (pbuf == pbufPrev) ||
            (pbuf->lsnSec != (lsn | pfrm->pvol->ulVolBits))) {

            usRet = NO_ERROR;
            goto Exit;    /*  Hint not accurate.  */
        }

        /*
         * 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.)
         */
/* cbw40: */

        if (pbuf->cLocked != 0) {
            /*
             * If the buffer is locked, we wait for it to unlock
             * and then we try again.
             */

            WBU (pbuf,pfrm);
        } else {

#ifdef PERFVIEW
            PVWINC(Ca,NHits);   /* Hint was good */
#endif

            /*  Transfer the least of:
             *  -- the remaining request
             *  -- the remaining run length * SECSIZE
             *  -- the remaining sectors in this block * SECSIZE
             *
             *  Note that BYTE_COPY     does not alter its arguments.
             */
            cusCur = (USHORT)(MIN (culRunLen, SPB) * SECSIZE);
            cusCur = MIN (cusCur, cusReq);

            /*  Make sure that the user data address is valid and
             *  in memory.  If this can't be done, propagate the
             *  error code on up.  Note that ProbeAddress may
             *  block, so we hold the buffer over the call.
             */
#ifndef MINIFSD
            if (pbuf->fFlag & BFL_LZREAD)
              LockBUF (pbuf); // @114986
#endif

            IncrHoldsBUF(pbuf);  // @76193
            pfrm->fRecheckAddr = TRUE;
            usRet = ProbeAddress(PB_OPREAD, *ppbSource, cusCur, pfrm);
            DecrHoldsBUF(pbuf);  // @76193

#ifndef MINIFSD
            if (pbuf->fFlag & BFL_LZREAD) {
              if (usRet) {
                UnlockBUF (pbuf); // @114986
                if (pbuf->pWaitChain != 0)
                  WakeupMisc2(&(pbuf->pWaitChain),
                   LOB_BUFWAIT);
               goto Exit;
              }
            }
#else
            if (usRet)
              goto Exit;
#endif

            BYTE_COPY ( (*ppbSource),
                        ((PBYTE)(pbuf->pblkData)),
                        cusCur );

#ifndef MINIFSD
             if (pbuf->fFlag & BFL_LZREAD) {
               ClrLzRdBit ((USHORT) 0, cusCur, pbuf, pfrm);
               UnlockBUF (pbuf); // @114986
             }
#endif

            /*
             * Set the dirty bits and update *pcusReq and the seek
             * pointer.  (***Note that we use the old value of the
             * seek pointer to determine lsnLast.***)
             */
            lsnLast = lsn + (usRemaining + cusCur - 1)/SECSIZE;

            pbuf->bDirtMask |= abDirtOrd[lsnLast % SPB];

            cusReq -= cusCur;
            ulSeekPtr += cusCur;
            usRemaining = (USHORT) (ulSeekPtr % SECSIZE);
            *ppbSource += cusCur;

            pbuf->flDMask |= pfrm->flDMask;

            /*
             *  If we want to write-through, call BufWrit;
             *  if we just want to lazy-write, use AddLW.
             *
             */

            if (fWrtType)
              BufWrit(pbuf, pfrm);
             else {

              AddLW (pbuf, pfrm);

              if (pbuf->cLocked == 0)
                if (pbuf->pWaitChain != 0)
                  WakeupMisc2(&(pbuf->pWaitChain),
                              LOB_BUFWAIT);
            }

            /* If we've reduced *pcusReq to zero, we're done. */
            if (cusReq == 0) {
                usRet = NO_ERROR;
                goto Exit;
            }

            /*
             * 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.
             *
             */

            culRunLen -= cusCur/SECSIZE;
            lsn += cusCur/SECSIZE;

            pbufPrev = pbuf;
            pbuf = pbuf->pbufNextHint;
        }
    } /* end while loop */

    /*
     * If we fall through to here, we have exhausted our run
     * length without fulfilling the request.
     */
    usRet = NO_ERROR;

    } /* lsnLast scope */

Exit:
    /* set the caller's parameters from our local copies and return */
    *plsn = lsn;
    *pcusReq = cusReq;
    *pculRunLen = culRunLen;
    *pulSeekPtr = ulSeekPtr;
    return (usRet);
}
