/*static char *SCCSID = "@(#)cachswrt.c 6.6 92/02/13";*/
/*static char *SCCSID = "@(#)cachswrt.c 6.6 92/02/13";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include <pvwxport.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 "pbmacros.h"
#include "assert.h"
#include "pvw.h"

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

/*
 ***    CachSWrit - Cache Sector Write
 *
 *      CachSWrit is a slave of WriDir - 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.
 *
 *      We don't know that the physical sector is at the start of
 *      the cache block, though.
 *
 *      We know that the requested sector is in the supplied cache
 *      buffer.  However, if the cache buffer is locked we'll
 *      block, and it's possible that the sector will be gone
 *      when we wakeup.  In this case we return, so it's possible
 *      that the data is in a cache block, but we didn't transfer
 *      it.  The caller must be able to handle this situation, although
 *      it's super rare.
 *
 *      ENTRY   plsn = pointer to physical sector # (may be vlsn)
 *              pculRunLen = pointer to run length (sectors)
 *              pbuf = cache header address (pointer to cache header)
 *              ppbSource = pointer to transfer address (pointer to pointer
 *                      to destination - doubly indirect because we want to
 *                      change the pointer on exit)
 *              pcusXfer = ptr to remaining transfer count
 *              fWrtThru = write through flag
 *
 *      RETURNS NO_ERROR if successful, else error code.
 *              *plsn updated
 *              *pculRunLen updated
 *              *ppbSource updated
 *              *pcusXfer updated
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          21-Sep-1988 15:05:54 by Bill McJohn [t-billm]
 *                              brought up to date with 9/15/88 assembly code
 */

USHORT
CachSWrit (plsn, pculRunLen, pbuf, ppbSource, pcusXfer, fWrtThru, pfrm)
PLSN            plsn;           /*  ptr to first lsn to xfer    */
PULONG          pculRunLen;     /*  ptr to # of sectors in run  */
PBUFNODE        pbuf;           /*  ptr to buffer with first lsn        */
PBYTE far       *ppbSource;     /*  Source of data for transfer */
PUSHORT         pcusXfer;       /*  ptr to remaining xfer count */
BOOL            fWrtThru;       /*  write through flag          */
PFRAME          pfrm;
{
        USHORT cusSectsToXfer;
        USHORT usRet;
        PBUFNODE pbufPrev;   /* Previous pbuf */

#ifdef PERFVIEW
        BOOL        fHintUsed = FALSE; /* True after a hint is used */
#endif

   if (pbuf == NULL)
     return (NO_ERROR);

        do {

            ASSERT_BUF(pbuf);

            while (pbuf->cLocked != 0) {

                if (WBU (pbuf, pfrm) == WBU_NEW)
                    /*  when we got the buffer back, it had
                     *  a different data block in it.  Return
                     *  to sender, no forwarding address.
                     */
                    return (NO_ERROR);
            }
#ifdef PERFVIEW
            if (fHintUsed)
                PVWINC(Ca,NHits); /* The hint is good */
#endif

            /*  We'll transfer the lesser of the remaining run length
             *  or the sectors left in the block starting from *plsn.
             *
             */
            cusSectsToXfer = (USHORT)(MIN (*pculRunLen, SPB - *plsn % SPB));

            /*  Verify that the user's data address is valid and
             *  in memory;  if this can't be done, propagate the
             *  error on up.  Note that ProbeAddress may block,
             *  so we hold the buffer over the call.
             */

#ifndef MINIFSD
            if (pbuf->fFlag & BFL_LZREAD)
              pbuf->cLocked++;
#endif

            IncrHoldsBUF(pbuf);  // @76193

            pfrm->fRecheckAddr = TRUE;
            usRet = ProbeAddress (PB_OPREAD, *ppbSource,
                                   cusSectsToXfer * SECSIZE, pfrm);

            DecrHoldsBUF(pbuf);  // @76193

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

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

            BYTE_COPY ( (*ppbSource),
                        ((PBYTE)(pbuf->pblkData) + (*plsn % SPB) * SECSIZE),
                        cusSectsToXfer * SECSIZE );

            pbuf->flDMask |= pfrm->flDMask;

#ifndef MINIFSD
//#114986
            if (pbuf->fFlag & BFL_LZREAD) {
              ClrLzRdBit ((USHORT)(*plsn % SPB) * SECSIZE,
                          (USHORT)(cusSectsToXfer * SECSIZE), pbuf, pfrm);
            }
#endif

       /* Setting the dirty bit is necessary only if the lazy writer
        * is going to do the I/O. If write-through then QWrite will
        * will do the work.
        */

       if (!(fWrtThru)) {

         pbuf->bDirtMask |=
                 abDirtOrd[(*plsn % SPB) * SPB
                  + (*plsn+cusSectsToXfer-1) % SPB];

#ifdef MINIFSD
        BufWrit(pbuf,pfrm);
#else
        AddLW(pbuf,pfrm);

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

       }

            /*  Update next device sector number to be transferred
             *  and the remaining run length, and the transfer
             *  source address.
             *
             */

            *plsn += cusSectsToXfer;
            *pculRunLen -= cusSectsToXfer;
            *ppbSource += cusSectsToXfer * SECSIZE;
            *pcusXfer -= cusSectsToXfer * SECSIZE;

            /*  Move to the next hint pointer, in the hopes
             *  that it's right.
             *
             */
            pbufPrev = pbuf;
            pbuf = pbuf->pbufNextHint;

#ifdef PERFVIEW
            /* if pculRunLen is nonzero, we will try to use the hint,
             * otherwise we won't.
             */
            if (*pculRunLen) {
                PVWINC(Ca,NHints); /* One more hint used */
                fHintUsed = TRUE;
                if (pbufPrev==pbuf) PVWINC(Ca,NSelf);
            }
#endif


        } while (*pculRunLen && (pbuf != NULL) && (pbuf != pbufPrev) &&
                 (pbuf->lsnSec == (*plsn | pfrm->pvol->ulVolBits)));
}
