/*static char *SCCSID = "@(#)bufwrit.c  6.4 92/01/31";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.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 "pb_ifs.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "assert.h"
#include "log.h"
#include "strat2.h"                     /* Needed by "asyncio.h". */
#include "asyncio.h"                    /* PIOH, PLISTENT, etc. */
#include "heap.h"

#include "pr_DEBUG.h"
#include "pr_CACHE.h"
#include "pr_DSKIO.h"
#include "pr_MISC.h"
#include "pr_heap.h"


/*
 ***    BufWrit - Synchronous Buffer Write
 *
 *      BufWrit writes a buffer to the disk, ASAP priority, and
 *      doesn't return until that buffer has been written.
 *
 *      The dirty bits are used to optimize the write.
 *
 *      Since we allow writers while the buffer is being written,
 *      we need to be carful about when we clear the dirty bits.
 *
 *      If a buffer is already being written by the lazy write thread
 *      a large block, the queueing vagaries of the disk drive may
 *      cause this soon-to-be-queued I/O to proceed in an order that
 *      is not correct.  Therefore, we serialize on the buffer.
 *
 *      ENTRY   pbufToWrite = pointer to buffer header
 *
 *      EXIT    none
 *
 *      Note:  BufWrit may block.
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          26-Sep-1988 12:51:54 by Bill McJohn [t-billm]
 *                              brought up to date with 9/23/88 assembly code
 */

/*  To optimize the write by dirty sectors, we use a look-up table
    indexed by pbufToWrite->bDirtMask.

        aBufa[pbufToWrite->bDirtMask].offus -- sector offset of first
                dirty block into buffer's data block (ith sector in block)
        aBufa[pbufToWrite->bDirtMask].cSecToWrite -- count of sectors to write.

*/


struct BUFA_LOOKUP {
        USHORT offus;
        USHORT cSecToWrite;
} aBufa[] = {
#if SPB == 4
        {0,0}, {0,1}, {1,1}, {0,2},
        {2,1}, {0,3}, {1,2}, {0,3},
        {3,1}, {0,4}, {1,3}, {0,4},
        {2,2}, {0,4}, {1,3}, {0,4}
#elif SPB == 8
/*  0 -  7 */       {0,0}, {0,1}, {1,1}, {0,2}, {2,1}, {0,3}, {1,2}, {0,3},
/*  8 - 15 */       {3,1}, {0,4}, {1,3}, {0,4}, {2,2}, {0,4}, {1,3}, {0,4},

/* 16 - 23 */       {4,1}, {0,5}, {1,4}, {0,5}, {2,3}, {0,5}, {1,4}, {0,5},
/* 24 - 31 */       {3,2}, {0,5}, {1,4}, {0,5}, {2,3}, {0,5}, {1,4}, {0,5},

/* 32 - 39 */       {5,1}, {0,6}, {1,5}, {0,6}, {2,4}, {0,6}, {1,5}, {0,6},
/* 40 - 47 */       {3,3}, {0,6}, {1,5}, {0,6}, {2,4}, {0,6}, {1,5}, {0,6},
/* 48 - 55 */       {4,2}, {0,6}, {1,5}, {0,6}, {2,4}, {0,6}, {1,5}, {0,6},
/* 56 - 63 */       {3,3}, {0,6}, {1,5}, {0,6}, {2,4}, {0,6}, {1,5}, {0,6},

/* 64 - 71 */       {6,1}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/* 72 - 79 */       {3,4}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/* 80 - 87 */       {4,3}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/* 88 - 95 */       {3,4}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/* 96 -103 */       {5,2}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/*104 -111 */       {3,4}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/*112 -119 */       {4,3}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},
/*120 -127 */       {3,4}, {0,7}, {1,6}, {0,7}, {2,5}, {0,7}, {1,6}, {0,7},

/*128 -135 */       {7,1}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*136 -143 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*144 -151 */       {4,4}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*152 -159 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*160 -167 */       {5,3}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*168 -175 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*176 -183 */       {4,4}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*184 -191 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*192 -199 */       {6,2}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*200 -207 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*208 -215 */       {4,4}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*216 -223 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*224 -231 */       {5,3}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*232 -239 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*240 -247 */       {4,4}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8},
/*248 -255 */       {3,5}, {0,8}, {1,7}, {0,8}, {2,6}, {0,8}, {1,7}, {0,8}
#else
#error SPB is not 4 or 8--BufWrit will behave incorrectly.
#endif
               };

void
BufWrit (pbufToWrite,pfrm)
PBUFNODE pbufToWrite;       /* pointer to buffer header to write */
PFRAME   pfrm;
{
        BYTE bOldDirt;      /*  location to save old dirty bits     */
        USHORT cSecWritten; /* # of sectors actually written */
        PIOH pioh;
        UINT RetBits;
        USHORT iVol;

        iVol = IVOLFROMLSN (pbufToWrite->lsnSec);
        pfrm->pvol = VolPtr [iVol];
/*
 *  NOTE:  these local variables are used by the DEBUG model to avoid
 *  excessively recomputing their values;  the non-debug version
 *  uses each of them once per recomputation if there is no error,
 *  otherwise twice per recomputation (for WtHF).
 */
#ifdef DEBUG
        USHORT cSec;        /* # of sectors to write, converted to USHORT  */
        PCHAR pData;        /* character pointer to source address  */
        LSN lsnStart;       /* first lsn to write                   */
        USHORT usRet;
#endif

#ifndef MINIFSD
        if ( (pbufToWrite->fFlag & BFL_LZREAD) &&
             (!(pbufToWrite->fFlag & BFL_LWLOCK)) ) {
          if (LazyRead(pbufToWrite, pfrm))
            return;                   /* error, nothing we can do but return */
        }
#endif

BufWritAgain:           /* label so we can try write again after HotFix */

        ASSERT_BUF (pbufToWrite);

#ifdef BUFZAP
        DoZap();
#endif

#ifdef DEBUG
        /*  entry into BufWrit with a clean buffer is an error
         *  condition
         */
        if (pbufToWrite->bDirtMask == 0)
            INTERR("BufWrit: Writing clean buffer.");
#endif

        /*  We keep trying until the dirty bits are all zero...  */
        while (pbufToWrite->bDirtMask) {

            /*  Increment the hold count, to prevent the buffer from
             *  being ripped out from under us.
             *
             */
            IncrHoldsBUF (pbufToWrite);  // @76193

            /*  If the buffer is being written, block on the lazy write
             *  wait chain until the buffer becomes available.
             *
             */
            while (pbufToWrite->fFlag & BFL_LWLOCK) {
                BlockMisc (&(pbufToWrite->pLWHead), LOB_BUFLW, pfrm);

                if (pbufToWrite->fFlag & BFL_LWLOCK)
                  continue;

                /* check if lazywriter wrote the buffer for us */
                if( pbufToWrite->bDirtMask == 0 ) {
                    pbufToWrite->flDMask = 0;
                    DecrHoldsBUF( pbufToWrite );  // @76193

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

                 /*  if necessary, Wakeup on pNoBufWaitChain */
                  if (pNoBufWaitChain != 0) {
                    WakeupMisc2 (&(pNoBufWaitChain), LOB_NOBUF);
                  }
                  return;
                }
            }

            /*  While we're off writing these sectors, somebody else may
             *  dirty the buffer.  Therefore, we need to clear the dirty
             *  bits before the write, so we'll notice any new dirt.
             *
             */

/* Now we are using a whole byte.
 */
#ifdef DEBUG
            if (pbufToWrite->bDirtMask & ~SPBBITS)
                INTERR("BufWrit: Illegal value for dirty bits.");
#endif

            bOldDirt = pbufToWrite->bDirtMask & (BYTE) SPBBITS;

            /* clean out dirty bits */
            pbufToWrite->bDirtMask = 0;
            pbufToWrite->flDMask = 0;

        /* if buffer on dirty list, remove it */
            if (pbufToWrite->dcbpDirt.pbufFwd != NULL) {
            /* (remove buffer from LazyWrite chain)    */
              DBCREM(pbufToWrite);
//test dcf2
#ifndef MINIFSD
             if ((VolPtr [IVOLFROMLSN (pbufToWrite->lsnSec)])->VDU)  //@76575
               cbufDirtyVDU--;
#endif
            }

#ifdef DEBUG
            cSec = aBufa[bOldDirt].cSecToWrite;
            pData = (PCHAR)(((PSECTOR)(pbufToWrite->pblkData)) +
                                                aBufa[bOldDirt].offus);
            lsnStart = pbufToWrite->lsnSec + (ULONG)(aBufa[bOldDirt].offus);
#endif

            cSecWritten = aBufa[bOldDirt].cSecToWrite;

#ifdef DEBUG
            /*  Sectors one through SPB - 1 are reserved.  Any request to
             *  write to these sectors must be refused.
             */
            if (lsnStart < SPB && !(lsnStart == 0 && cSec == 1) )
                    INTERR("BufWrit: tried to write reserved sectors.");
#endif

#ifndef MINIFSD

//test. label for forced strat1 test
// goto strat1;

   if (IsAsyncIOable(pbufToWrite->lsnSec)) {

     ULONG fIOHFlags = (pbufToWrite->lsnSec & ~SECMASK)          /* volbits */
                      | GIH_ABORTERR | GIH_PHYSADDR;  /* flag bits */

     if ((pioh = GetIOHand(fIOHFlags,1,ONESGLIST,pfrm)) == NULL) { /* may block */
       DecrHoldsBUF (pbufToWrite);  // @76193
       goto BufWritAgain;
     }

#ifdef DEBUG_ASYNC
            dprintf(__FILE__": Calling QDiskOp; pbData=%p, cSects=%d...\n",
                    pbData, cSects);
            dprintf ("  pioh = %p, pNoteRec = %p, vlsn = %lx\n",
                     pioh, pNoteRec, vlsnStart);
#endif

     if ((RetBits = QDiskOp(pioh,
                            PRIO_BACKGROUND_USER, PB_WRITE_X,
                            pbufToWrite->lsnSec + (ULONG)(aBufa[bOldDirt].offus),
                            cSecWritten,
                            (PCHAR)(((PSECTOR)(pbufToWrite->blkDataPhysAddr)) +
                                               aBufa[bOldDirt].offus),
                            NULL, pfrm)) != 0) {
#ifdef DEBUG
               dprintf("%s (%d): QDiskOp failed; %x\n",
                        __FILE__, __LINE__, RetBits);
               Int3 ();
#endif

       DecrHoldsBUF (pbufToWrite);  // @76193
       RetHeap(pioh);
       goto BufWritAgain;
     } else {

      msLastIO = msNow();  /* update system's time of most recent IO */

#ifdef DEBUG_ASYNC
               dprintf(__FILE__": QDiskOp OK, pxlh=%p\n",
                       GetIOListHead(pioh));
#endif

      if ((RetBits = ExecIOH (pioh, TRUE, pfrm)) != 0) {
#ifdef DEBUG
                  dprintf("%s (%d): ExecIOH failed; %x\n",
                          __FILE__, __LINE__, RetBits);
                  Int3 ();
#endif
//test
        if (RetBits != ERROR_SEM_TIMEOUT) {
          RetHeap(pioh);
          goto bufWritHF;
        }
      }

     }

    RetHeap(pioh);

   } else {

#endif

//test label for forced strat1 test
strat1:

#ifdef DEBUG
            if ((usRet = DoVolIO (DVIO_OPWRITE | DVIO_OPRESMEM,lsnStart,
                                  &cSecWritten, pData, pfrm)) != NO_ERROR) {
#else
            if (DoVolIO(DVIO_OPWRITE | DVIO_OPRESMEM,
                        pbufToWrite->lsnSec + (ULONG)(aBufa[bOldDirt].offus),
                        &cSecWritten,
                        (PCHAR)(((PSECTOR)(pbufToWrite->pblkData)) +
                                           aBufa[bOldDirt].offus),pfrm)
                   != NO_ERROR) {
#endif

#ifdef DEBUG
                dprintf("%s: DoVolIO error, ret=%d, lsn=0x%lx, cSec=%d, cSecWritten=%d\n",
                             __FILE__,
                             usRet,
                             lsnStart,
                             cSec,
                             cSecWritten);

                dprintf (__FILE__": Calling WriteHF\n");
#endif

bufWritHF:
                /*  We got a hard error - got to do a hot fix substitution
                 *
                 *  We have to write the individual SPB sectors to see which of them
                 *  are defective.
                 */

                WriteHF (pbufToWrite->lsnSec + (ULONG)(aBufa[bOldDirt].offus),
                         aBufa[bOldDirt].cSecToWrite,
                         (PCHAR)(((PSECTOR)(pbufToWrite->pblkData)) +
                                                aBufa[bOldDirt].offus),pfrm);

            }


#ifdef DEBUG
            RSC (pbufToWrite);
#endif

            /*  Decrement the hold count and wake up the next process
             *  (if any) waiting on the buffer and give it an IO-boost.
             *
             */

#ifndef MINIFSD
   } /* end strat1 */
#endif

          DecrHoldsBUF (pbufToWrite);  // @76193
                                                                   //#85759
          if (pfrm->pvol->cHotFixInUse >= pfrm->pvol->cHotFixMax) {
            pbufToWrite->bDirtMask = 0;
   	      break;
          }

        } /* end while loop */

//test
      /* If buffer not on LRU list, make it LRU */
	     if (pbufToWrite->dcbpLru.pbufFwd == pbufToWrite) {
          PlaceBuf (pbufToWrite, BP_KEEP);
        }

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

        /*  if necessary, Wakeup on pNoBufWaitChain */
        if (pNoBufWaitChain != 0) {
            WakeupMisc2 (&(pNoBufWaitChain), LOB_NOBUF);
        }

}

void far
BufWritFar (pbufToWrite,pfrm)
PBUFNODE pbufToWrite;       /* pointer to buffer header to write */
PFRAME   pfrm;
{
    BufWrit(pbufToWrite,pfrm);
}
