/*static char *SCCSID = "@(#)rnm.c	6.9 92/02/12";*/
#define	 INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include <strat2.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 "frame.h"
#include "lw.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"
#include "lprintf.h"
#include "pr_cache.h"
#include "asyncio.h"
#include "assert.h"
#include "pr_dskio.h"
#include "pr_misc.h"

#ifdef	 DEBUG
   #include "pr_debug.h"
#endif


void CheckMoveToCache(PNOTEREC pNoteRec, PFRAME pfrm);

void MoveToCache(unsigned long ulSecFrs,USHORT usSecCnt,
		 PBYTE pbyClntBuf, PFRAME pfrm);


/** RNM - Reset Notification Masks & cleanup PIOH chains
  *
  * SDR & SDW use a notification mask so that when a I/O request
  * is done a bit can be cleared in a byte.  This allows
  * SDR & SDW to have up to 4 requests outstanding and then wait
  * for them all.
  *
  * If the transfer request is so cut up that 4 requests
  * aren't enough (we're talking REALLY cut up), RNM is
  * called to wait for the current I/O's to complete and to
  * reset the mask bit.  We don't do fancy stuff like look
  * to see which bits are done and recycle just them because
  * this happens so rarely.
  *
  * We also don't check the error bits, which are or'ed into
  * errmsk, where they accumulate.
  *
  * RNM also traverses the PIOH chain in the NoteRec.  For each PIOH
  * the pages are unlocked, write errors are HotFixed, and the PIOH
  * is released.
  *
  * ENTRY   pNoteRec = pointer to NoteRec
  *	fWrite = boolean meaning write - need to HotFix errors
  *
  * EXIT    none
  *
  * Yields CPU
  *
  * HISTORY
  *	16-Feb-90 by JR (John Rogers, JohnRo@MICROSOFT)
  *	- ADDED SOME DEBUG OUTPUT AND ASSERTION CHECKS, PER MARK HITCH.
***/

VOID RNM(PNOTEREC pNoteRec, BOOL fWrite,  PFRAME pfrm)
{
   USHORT erc;
   ASSERT_NOTEREC(pNoteRec);
      {

      /*
       * I/O may still be pending - wait for it.  FSH_SEMWAIT on the	
       * semaphore in *pNoteRec						
       */

  do {

     erc = NO_ERROR;                                           //#82290 Start
	  if (pNoteRec->mask) {
                                                    //#114986
#ifndef MINIFSD
	    MAYBLOCK(erc = FSH_SEMWAIT(&(pNoteRec->block), ulMaxIOWait));
#else
	    MAYBLOCK(erc = FSH_SEMWAIT(&(pNoteRec->block), TO_INFINITE));
#endif
     }
                                                               //#82290 End
#ifdef	 DEBUG
	 if (erc == ERROR_PROTECTION_VIOLATION)
	    {
	    INTERR(__FILE__": gp fault on semaphore");
	    }
#endif
                                                    //#114986
#ifndef MINIFSD
	 if (erc == ERROR_SEM_TIMEOUT) {
      pNoteRec->errmsk = ERROR_SEM_TIMEOUT;
      SETHARDERR (ERROR_GEN_FAILURE);     //#
      break;
    }
#endif

	  }  while (erc != NO_ERROR);
      }

#if	 defined       (LOGGING) || defined (DEBUG) || defined (LOG_PRINT)
   lprintf(LMOD_PROC, "RNM wakeup; pNR %p", pNoteRec);
#endif

   /*************************************************************************/
   /* reset NxtBit to start with 1 again				                         */
   /*************************************************************************/

   pNoteRec->nxtbit = 1;

      {
      PIOH pioh;
      BYTE i;
      PBUFNODE	 pbuf;		
      BYTE BufIndex = 0;

   if (pNoteRec->aExtentBufIndex) {

    if ( pNoteRec->aExtentBufIndex > (MAXNOTERECIO - 2) ) {
      BufIndex = pNoteRec->aExtentBufIndex;
      pNoteRec->aExtentBufIndex = MAXNOTERECIO - 2;
    }

    for (i=0; i<pNoteRec->aExtentBufIndex; i++) {

	     pbuf = pNoteRec->pExtentBuf[i].aExtentBuf;

        UnlockBUF (pbuf); // @114986

        if (pbuf->cLocked == 0) {

         pbuf->lsnSec = LSN_BOGUS;
         pbuf->fFlag &= ~BFL_EMPTY;

         PlaceBuf (pbuf,BP_TOSS);

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

        }

    }

      if (BufIndex > (MAXNOTERECIO - 2) ) {

        pNoteRec->aExtentBufIndex = BufIndex - (MAXNOTERECIO - 2);

	     pNoteRec->pExtentBuf[0].aExtentBuf =
	      pNoteRec->pExtentBuf[MAXNOTERECIO-2].aExtentBuf;

        pNoteRec->pExtentBuf[1].aExtentBuf =
	      pNoteRec->pExtentBuf[MAXNOTERECIO-1].aExtentBuf;

      } else
         pNoteRec->aExtentBufIndex = 0;

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

   }

                                                 //#114986
	 if (erc == NO_ERROR)
      CheckMoveToCache(pNoteRec,pfrm);

      /**********************************************************************/
      /* clean up the pioh linked list					                         */
      /**********************************************************************/

      while (pioh = (PIOH)pNoteRec->pioh)
	 {

	 ASSERT_IOH(pioh);

#ifdef	 DEBUG_ASYNC
	 dprintf(__FILE__": pNoteRec=%p, pioh=%p\n", pNoteRec, pioh);
	 dprintf(__FILE__": pioh->pNoteRec=%p, pioh->piohNext=%p\n",
	    pioh->pNoteRec, pioh->piohNext);
#endif
	 if (pioh->piohNext != NULL)
	    {
	    ASSERT_IOH(pioh->piohNext);
	    }
	 pNoteRec->pioh = pioh->piohNext;
	 UnlockIOHPages(pioh,pfrm);

	 /*
	  * if doing writes, we must check for errors			
	  */

                                                  //#114986
	 if (fWrite && erc != ERROR_SEM_TIMEOUT) {
	    PEXECLIST pxlh;
	    pxlh = GetIOListHead(pioh);

	    /*
	     * see if we have a write error				
	     */

	    if (pxlh->ELHdr.Lst_Status & RLH_Unrec_Error) {
	       PLISTENT ple;
	       ple = FirstIOReq(pxlh);
	       WriteHF(ple->RWReq.Start_Block|pioh->pvol->ulVolBits,
		            (USHORT)ple->RWReq.Block_Count, (PVOID)pioh->pVirtData,pfrm);
	    }		       /* if error - end of scope for ple   */
	 }			       /* if write - end of scope for pxlh  */

#ifdef	 DEBUG

	 /*******************************************************************/
	 /* we could check the piohd chain to be sure each ioh is there	    */
	 /*******************************************************************/

#endif
	    RelIOHand(pioh);
	 }			       /* while				    */
      }				       /* end of scope - pioh		    */

#ifdef	 DEBUG

   /*************************************************************************/
   /* we should assure the piohd chain is empty				    */
   /*************************************************************************/

#endif

	pNoteRec->block = 0L;
	pNoteRec->pioh = NULL;

}

void CheckMoveToCache(pNoteRec,pfrm)
PNOTEREC pNoteRec;
PFRAME	 pfrm;
{
   USHORT i;

   if (pNoteRec->byELIdx)
      {
      for (i=0;i<pNoteRec->byELIdx;i++)
	 {
	 if (pNoteRec->aExtentList[i].usSecCnt)
	    {
	    MoveToCache(
			pNoteRec->aExtentList[i].ulSecFrs,
			pNoteRec->aExtentList[i].usSecCnt,
			pNoteRec->aExtentList[i].pbyClntBuf,
			pfrm);
	    }
	 }

      /* Reset NoteRec */
      pNoteRec->byELIdx = 0;

      }
}


/**********************************************************************
 *
 * For every sector just read in by QRead(), we attempt		
 * to read that sector into the cache.  This is done by getting	
 * a buffer from the cache and setting it up using getinbuf().	
 * Memory is then copied from the client buffer to the cache buffer.
 *								
 * ENTRY:							
 *  ulSecFrs: First sector in run, should be a block aligned sector
 *  ulSecCnt: Number of sectors transferred			
 *  pbyClntBuf: Holds data to be moved into cache		
 *	
 **********************************************************************/

void MoveToCache(ulSecFrs,usSecCnt,pbyClntBuf,pfrm)

unsigned long ulSecFrs;		    /* Sector First */
USHORT	      usSecCnt;		    /* Sector Count */
PBYTE	      pbyClntBuf;	    /* Pointer to Client Buffer */
PFRAME	      pfrm;
{
   PBUFNODE	 pbuf;		     /* Pointer to buffer */
   PBUFNODE	 pbuf1;		     /* Pointer to buffer */
   USHORT	 usSecCpyCnt;	     /* Scratch */
   USHORT	 usCpyCnt;	     /* Scratch */
   USHORT	 usOddSecCnt;	     /* Scratch */
   ULONG     ulSecCur;	     /* Current Sector dealing with */
   PBYTE	 pbyClntBufIdx;	     /* BYTE far *pbyClntBufIdx; */
   PSECTOR psec;             /*  Address of sector data in cache buffer   */
   LSN lsnFirstInBlock;

   pbyClntBufIdx = pbyClntBuf;
   ulSecCur = ulSecFrs;

   /* IF sector number is not block aligned... */
   if (usOddSecCnt=(USHORT)(ulSecFrs%SPB))
      {

      /* Move odd blocks into cache  */
      /* Use RdBuf to fill a cache block */
      psec = RdBuf(ulSecFrs, BP_KEEP, &pbuf, pfrm);

	   if (pbuf->bBadMask != 0) {  // @78489
	     SETHARDERR (ERROR_READ_FAULT);
        return;
   	}

#ifndef MINIFSD
        if (pbuf->fFlag & BFL_LZREAD) {
          if (LazyRead (pbuf, pfrm))
	         return(GetHardErr(ERROR_READ_FAULT));
        }
#endif

      /* Make sure usSecCnt is not exceeded */

      /* Update parameters  */
      ulSecCur += (SPB - usOddSecCnt);

      pbyClntBufIdx += (SPB - usOddSecCnt) * SECSIZE;

      }

   for (
       /* From the first sector of the run just read in by QRead() */
       ;
       /* To the last sector of the run just read in by QRead()	 */
       ulSecCur < (usSecCnt+ulSecFrs);
       /* Continue to read in sectors */
       ulSecCur += SPB, pbyClntBufIdx += usCpyCnt
       )

      {
     /*
      * IF not a cache hit...
      *	  THEN continue to process this BLOCK
      *	  ELSE move to next block
      */

     if (!(CCH(ulSecCur,pfrm))) {	 /* not a cache hit */
	/* ...Continue to process this block */

	usSecCpyCnt = (unsigned short)((((ulSecCur-ulSecFrs)+SPB) > usSecCnt)?
	   ((usSecCnt+ulSecFrs)-ulSecCur):(SPB));

	/* Check for small I/O.  All small I/O's will be the last iteration */
      /* of "for" loop */

      if (usSecCpyCnt<SPB) {
	     /* Use RdBuf to fill a cache block */
	     psec = RdBuf(ulSecCur, BP_KEEP, &pbuf, pfrm);

	     if (pbuf->bBadMask != 0) {  // @78489
	       SETHARDERR (ERROR_READ_FAULT);
          return;
      }
                                           //#114986
#ifndef MINIFSD
      if (pbuf->fFlag & BFL_LZREAD) {
        if (LazyRead (pbuf, pfrm))
	       return(GetHardErr(ERROR_READ_FAULT));
      }
#endif

      } else
	
	if ((pbuf = GFB(pfrm)) != NULL) {
	 /* got a free cache buffer */

     if ((pbuf1 = HashFind(ulSecCur)) == NULL) { /* Sectors didn't sneak  */
                                                 /* into cache during GFB */
         pbuf->lsnSec = ulSecCur;
      	pbuf->bBadMask = 0;
         CreateBUF(pbuf);  //@114986
         LockBUF (pbuf); // @114986
	      AddHash(pbuf);

   /*  User's data address is valid and locked in memory */

   /* Write from memory to cache */
	usCpyCnt = SPB*SECSIZE;

	BYTE_COPY(pbyClntBufIdx,(PBYTE)(pbuf->pblkData), SPB*SECSIZE);

   ClrLockBUF (pbuf); // @114986
	PlaceBuf(pbuf, BP_KEEP);

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

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

     } /* end not in cache after GFB */
     else { /* in the cache after GFB */
      pbuf->lsnSec = LSN_BOGUS;
      AddHash(pbuf);
      PlaceBuf(pbuf, BP_TOSS);
      usCpyCnt = SPB*SECSIZE;
      continue;
     }
   } /* end got a free cache buffer */

	 else {	 /* no free cache buffers available */
		/*
	    * Just simply break out,
	    * Hopefully, we will put the data block in cache next time.
	    * Also, that which has made it to cache should stay a while
	    *
	  */
	    break;
	 } /* end no free buffers available */
     } /* end not a cache hit */
     else {  /* was a cache hit */
	/* ELSE update variable and continue loop */
	usCpyCnt = SPB*SECSIZE;
     }

      } /* END for loop */

}
