/*static char *SCCSID = "@(#)cache.h	6.12 92/03/11";*/


#define BUFPAGESIZE 2048
#define SECPERBUF (BUFPAGESIZE / SECSIZE)
#define HASHCNT 4096
#define READASTART 2
#define SECSIZE 512
#define BYTEBITSPERSEC (SECSIZE / 8)
#define csecBUFTHRESHOLD 8
#define cbBUFTHRESHOLD csecBUFTHRESHOLD * SECSIZE
#define DMASK_INIT 0x100
#define SPBBITS 0x0f
#define WRITETHRESHOLD 16*1024
#define MSDISKWRITEIDLE 500
#define SPBMASK 3
#define RAHEAD_MIN 8
#define RAHEAD_MAX 16
#define RAHEAD_WINDOW 16*1024

/**     BDCHN - doubly linked chain of buffers
 */

typedef struct DCHNB { /* DCHNB */
        struct BUFHDR *pbufFwd;
        struct BUFHDR *pbufBak;
} DCHNB;

typedef struct BUFHDR {
        DCHNB    BufLru;                // LRU chain
        ulong_t  BufRBA;                // RBA #
        ulong_t  BufhVPB;               // hVPB
        PBYTE    BufLinAddr;            // Linear Address of data block
        PBYTE    BufPhysAddr;           // Physical Address of Page
        ulong_t  BufFlag;               // flags
        DCHNB    BufHashLru;            // hash LRU chain
        struct   BUFHDR * *ppBufHTA;    // address of entry in hash table
        struct   BUFHDR *BufReadLink;   // Async Read Link
        struct   BUFHDR *BufWriteLink;  // Lazy Write Link
        ulong_t  BufRefCnt;             // Reference Count
        DCHNB    BufDirtyLru;           // Dirty LRU chain
        DCHNB    BufFreeLru;            // Free LRU chain
        ulong_t  BufFileDirtyMsk;       // File Dirty Mask
        BYTE     BufDirtyMsk;           // Bits to determine dirty sectors
        ulong_t  BufmsLastDirtied;      // Last dirtied time in ms
        ulong_t  BufmsFirstDirtied;     // First dirtied time ms
        ushort_t BufOwner;              // Thread who has buffer locked
        BYTE     BufDirtyBits[BYTEBITSPERSEC * SECPERBUF];
                                        // Bits to determine which bytes have
                                        // been dirtied.
} BUFHDR;

typedef BUFHDR *PBUFHDR;

#define BUF_READING     0x0001
#define BUF_LWLOCK      0x0002
#define BUF_LWWANTED    0x0004
#define BUF_LAZYREAD    0x0008
#define BUF_LOCK        0x0010
#define BUF_WAITING     0x0020

typedef struct BufXferRec {
        ulong_t BufXfer_UserOffset;
        ulong_t BufXfer_cbCount;
        ulong_t BufXfer_rba;
        ulong_t BufXfer_SecBytPos;
        PBUFHDR BufXfer_pBuf;
        int     BufXfer_Type;
} BufXferRec;

typedef BufXferRec *PBUFXFERREC;

#define BUFXFER_BUF         1
#define BUFXFER_DUP         2

#define RA_HI               4
#define RA_LOW              2   /* Must be > 0                */
#define RA_BUFSIZE          4   /* Buffer size (Cache blocks) */

/*
 * PlaceBuf interface definitions.
 */
#define PLCBUF_MRU             0x0001
#define PLCBUF_LRU             0x0002

/*
 * BufFind interface definitions.
 */
#define BFND_NULL              0x0000
#define BFND_READ              0x0001
#define BFND_WRITE             0x0002

/*
 * LazyRead interface definitions.
 */
#define LREAD_LWRITE           0x0001

/*
 * fBufWritable interface definitions.
 */
#define BWRITABLE_GAPS         0x0001
#define BWRITABLE_PARTIALSEC   0x0002

/*
 * GFB interface definitions.
 */
#define GFB_BLOCK              0x0001
#define GFB_NOBLOCK            0x0002

typedef struct LWPARMS { /* LWPARMS */
        ULONG   msIdle;         /* ms of no disk I/O to declare idle  */
        ULONG   msDataAge;      /* max number of ms dirty data in cache */
        ULONG   msIdleBuf;      /* ms since last buf dirty before idle flush */
        ULONG   cbWriteCache;   /* threshold below which all writes cached */
        ULONG   cbufDirtyMax;   /* max # dirty before AddLw wakes up LW */
        ULONG   cbuf;           /* total number of buffers */
} LWPARMS;   /* LWPARMS */
typedef struct _BUFA_LOOKUP {
        ULONG offus;
        ULONG cSecToWrite;
} BUFA_LOOKUP[];

/*
 * Gets the min of two numbers (these are also in stdlib.h!)
 */
#define MIN(x,y)        ((x)<(y)?(x):(y))

/*
 * Gets the max of two numbers (these are also in stdlib.h!)
 */
#define MAX(x,y)        ((x)>(y)?(x):(y))

#define ROUNDRBA(rba)      (ULONG)(rba - (rba % SECPERBUF))
#define REMAINDERRBA(rba)  (rba % SECPERBUF)
#define BUFOFFSET(rba)     (REMAINDERRBA(rba) * SECSIZE)
#define ROUND_TO_SECSIZE(a)             (((a)+SECSIZE-1) & (~(SECSIZE-1)))

/****    BufIncRef - Increment Buffer Reference Count
 *
 *      BufIncRef - Increment Buffer Reference Count
 *
 *      ENTRY   pBuf = Buffer header address
 *
 *      EXIT
 *
 *      HISTORY
 */


#define BufIncRef(pBuf) \
      ++pBuf->BufRefCnt; \
      if (pBuf->BufRefCnt == 0) \
         ++pBuf->BufRefCnt;

/*
 ***    BufDecRef - Decrement Buffer Reference Count
 *
 *      BufDecRef - Decrement Buffer Reference Count
 *
 *      ENTRY   pBuf = buffer header address
 *
 *      EXIT
 *
 *      HISTORY
 */


#define BufDecRef(pBuf) \
      --pBuf->BufRefCnt;

/**     HASHVAL macro evaluates to the hash function value of its argument.
 *
 *      Since the hash val is a USHORT, and since HASHCNT is small enough
 *      we ignore the high word of lsn!
 */
#define HASHVAL(lsn) ((LOUSHORT(lsn) & ((HASHCNT - 1) << 2)) >> 2)

/*
 **     fFlag bits
 */

#define DCADDF(pbuf,field,bufHead)                                                      \
        {   register PBUFHDR pbufTmp = (pbuf);                                         \
            register PBUFHDR pbufHead = & (bufHead);                                   \
                                                                                       \
            (pbufTmp->field.pbufFwd = pbufHead->field.pbufFwd)->field.pbufBak = pbufTmp;\
            (pbufTmp->field.pbufBak = pbufHead)->field.pbufFwd = pbufTmp;               \
        }

/**     add a buffer to the end of a list
 */
#define DCADDB(pbuf,field,bufHead)                                                      \
        {   register PBUFHDR pbufTmp = (pbuf);                                         \
            register PBUFHDR pbufHead = & (bufHead);                                   \
                                                                                        \
            (pbufTmp->field.pbufBak = pbufHead->field.pbufBak)->field.pbufFwd = pbufTmp;\
            (pbufTmp->field.pbufFwd = pbufHead)->field.pbufBak = pbufTmp;               \
        }

/**     remove a buffer altogether.  Don't link him to himself
 */
#define DCREM(pbuf,field)                                       \
        {   register PBUFHDR pbufFwd = (pbuf)->field.pbufFwd;  \
            register PBUFHDR pbufBak = (pbuf)->field.pbufBak;  \
                                                               \
            pbufFwd->field.pbufBak = pbufBak;                   \
            pbufBak->field.pbufFwd = pbufFwd;                   \
        }
#ifdef  FSSTRICT
#define CBUFDIRTY_CK(pbuf) {                                            \
            if (cbufDirty == 0) {                                       \
                panic ("cbufDirty about to underflow");                 \
            }                                                           \
            if ((pbuf) == &BufDirtyHead) {                            \
                panic ("pbuf == BufDirtyHead");                       \
            }                                                           \
        }
#else
#define CBUFDIRTY_CK(pbuf)
#endif

#ifdef  FSSTRICT
#define PBUFLINK_CK(pbuf) {                                             \
            if (((pbuf)->BufDirtyLru.pbufBak == NULL) ||                   \
                ((pbuf)->BufDirtyLru.pbufFwd == NULL)) {                   \
                panic ("Removing Buffer that has NULL links");           \
            }                                                           \
        }
#else
#define PBUFLINK_CK(pbuf)
#endif

/**     remove a buffer from the dirty chain and decrement the count.
 */
#define DBCREM(pbuf)                                                           \
                                                                               \
        PBUFLINK_CK (pbuf);                                                    \
        (pbuf)->BufDirtyLru.pbufBak->BufDirtyLru.pbufFwd = (pbuf)->BufDirtyLru.pbufFwd; \
        (pbuf)->BufDirtyLru.pbufFwd->BufDirtyLru.pbufBak = (pbuf)->BufDirtyLru.pbufBak; \
        (pbuf)->BufDirtyLru.pbufFwd = NULL;                                       \
        (pbuf)->BufDirtyLru.pbufBak = NULL;                                       \
        CBUFDIRTY_CK (pbuf);                                                   \
        cbufDirty--;                                                           \

/***LP  BufAssert - cause internal error if assertion fails
 *
 *      Given a boolean, this procedure causes an internal error if
 *      the boolean is false.  Typically, the value of this boolean is
 *      derived from some test expression.
 *
 *      BufAssert (f)
 *
 *      ENTRY   f               - boolean value (int)
 *      RETURN  NONE
 *
 *      CALLS   _assert
 *
 *      WARNING This procedure is implemented as a macro.
 *
 *      EFFECTS NONE
 */
#ifdef  FSSTRICT
#  define BufAssert(e) _assert(e)
#else
#  define BufAssert(e)
#endif

extern void PlaceBuf(PBUFHDR pBuf,ulong_t priBuf);
extern PBUFHDR HashFind(ulong_t rba,ulong_t hvpb);
extern PBUFHDR CCH(ulong_t rba,ulong_t hvpb);
extern int AddHash(PBUFHDR pBuf,ulong_t hVPB);
extern PBUFHDR GFB(ulong_t flags);
extern void BufUnLock(PBUFHDR pBuf);
extern void BufLock(PBUFHDR pBuf);
extern void BufWaitUnLock(PBUFHDR pBuf);
extern void BufLRUnLock(PBUFHDR pBuf);
extern void BufLRLock(PBUFHDR pBuf);
extern ulong_t FigRec (ulong_t ClusNum,ulong_t SecClusPos,ulong_t hVPB);
extern int ReqListAdd_REQ(ulong_t rba,ulong_t cnt,ulong_t cmd,
                           ulong_t RLSel,ulong_t RLOff,ulong_t priority,
                           ulong_t hVPB);
extern void ReqListAdd_SG(ulong_t bytcnt,ulong_t RLSel,ulong_t RLOff,PBYTE sgaddr);
extern int  ReqListAdd_Extent(ulong_t rba,ulong_t cExtent,ulong_t cmd,
                              ulong_t RLSel,ulong_t RLOff,ulong_t xferaddr);
extern int  ReqListExecute(ulong_t RLSel,ulong_t RLOff);
extern int  ReqListAlloc(ulong_t *ReqListSel,ulong_t *ReqListOff);
extern int  ReqListInit(ulong_t ReqListSel,ulong_t ReqListOff,ulong_t hVPB,
                        ulong_t fFree);
extern void ReqList_GetNextRead(ulong_t RLSel,ulong_t RLOff,
ulong_t *ReqListSel,ulong_t *ReqListOff);
extern void   ReqList_SetNextRead(ulong_t ReadTailSel,ulong_t ReadTailOff,
ulong_t NewReqListSel,ulong_t NewReqListOff);
extern PBUFHDR ReqList_GetReadAheadData (ulong_t RLSel,ulong_t RLOff,
                                         PBUFHDR *pFirstBuf);
extern int NextSec(int fDirect,ulong_t hVPB);

extern int SetupPhysIO(ulong_t *pcnt,ulong_t *pnclus,ulong_t secCnt,
                       ulong_t nclus,ulong_t *prba,ulong_t *ppMiddleSectors,
                       ulong_t *pxferaddr,ulong_t SecClusPos,
                       int fDirect,ulong_t hVPB);
extern int IsEOF(ulong_t nextcluster,ulong_t hVPB);
extern void BufFree(PBUFHDR pBuf);
extern int SuBuff386(ulong_t count,ulong_t uaddr,PBYTE destaddr);
extern int FuBuff386(ulong_t count,ulong_t uaddr,PBYTE destaddr);
extern void ReqListLnkPtr(PBUFHDR pBuf,void * pnext,ulong_t Reqsel,
                          ulong_t Reqoff);
extern int DRead(ulong_t Rrba,ulong_t count,PBYTE destaddr,ulong_t hVPB);
extern int DWrite(ulong_t Rrba,ulong_t count,PBYTE destaddr,ulong_t hVPB,
                  ulong_t ReqListSel);
extern int IsRBABad(ulong_t,ulong_t);
extern int IsRBAInSysArea(ulong_t,ulong_t);
extern int IsRBAForFAT(ulong_t,ulong_t);
extern int BufWrt (PBUFHDR pBuf,ulong_t ReqListSel);
extern int BufPreWrt (PBUFHDR pBuf,int *pfWrite);
extern int BufPreWrt2 (PBUFHDR pBuf,int *pfWrite);
extern PBUFHDR BufAlloc(ulong_t rba,ulong_t hVPB);
extern PBUFHDR BufFind(ulong_t rba,ulong_t flags,ulong_t hVPB);
extern void FLW(void);
extern void AddLW (PBUFHDR pBuf);
extern void SetRBA_Count(ulong_t *,ulong_t,ulong_t *,ulong_t);
extern void printbuf(PBUFHDR pBuf);
extern void FreeDosBuffer(ulong_t rba,ulong_t count,ulong_t hVPB);
extern void WriteDosBuffer(ulong_t count,ulong_t hVPB);
extern int LazyRead(PBUFHDR pbuf,ulong_t fLazyWrite,int *pfMustRead);
extern int FindNonChar(void *pString,BYTE findchar,ulong_t count);
extern void FreeReqList(ulong_t sel,ulong_t off);
extern void SaveFatTCBInfo(void *pTCBBuf);
extern void RestoreFatTCBInfo(void *pTCBBuf);
extern void LWForceWakeUp(void);
extern void BufUnlink(PBUFHDR pBuf);
extern int IsDriveLocked(ulong_t hVPB);
extern int fBufWritable(PBUFHDR pBuf,ulong_t flags);
extern ulong_t msNow(void);
extern int SetupForIO(ulong_t);
extern int FATReadInit(ulong_t);
extern void StartReadAhead (sf_entry *pSFT,sfdFATFS *pSFTFAT,ulong_t pos,
                            ulong_t hVPB);
extern void GFBWait (void);
extern void RemoveHash(PBUFHDR pBuf);
extern void QueueAsyncRead (ulong_t ReqListSel,ulong_t ReqListOff);
extern void LazyReadIODone(PBUFHDR pFirstBuf,PBYTE LinBuf);
extern ulong_t GetVIDSector(ulong_t hVPB);
extern void UpdateVID(ulong_t hVPB,ushort_t BufSel,ushort_t BufOff,ulong_t rba,
                      ulong_t count);
extern void BufFillFromBitMap(void *pBitMap,PBYTE pSrc,PBYTE pDest);
extern int AllocateClus(ulong_t hVPB,ulong_t *pCluster);
extern int FindClusInfo(ulong_t ClusterNum,ulong_t *pPrevClusterNum,
                        ulong_t *pNextClusterNum,ulong_t hVPB);
extern int ReLinkClus(ulong_t NewClusterNum,ulong_t PrevClusterNum,
                      ulong_t NextClusterNum,ulong_t ClusterNum,ulong_t hVPB);
extern ulong_t RBAToClus(ulong_t rba,ulong_t hVPB,ulong_t *pFirstRBA,
                         ulong_t *pSecsPerClus);
extern void HotFixHardError(ulong_t hVPB);
extern void FreeCluster(ulong_t hVPB,ulong_t Cluster);
extern ulong_t GetBuf(ulong_t rba,ulong_t hVPB,ulong_t *pBufSel,ulong_t *pBufOff);
extern void ReleaseBuf(void);
extern char GetDriveLetterFromhVPB(ulong_t hVPB);
extern ulong_t GetClusterSize(ulong_t hVPB);
extern int fReqListFree(void);
extern BUFHDR BufLRUHead;              // Head of LRU chain
extern BUFHDR BufFreeList;

extern PBUFHDR BufHashTable [];          // hash table
extern int GFB_Sem;
extern ulong_t FreeBufs;
extern BUFHDR BufDirtyHead;
extern ulong_t FileDirtyMsk;
/*      Dirty bits if entire block changed starting at sector 'i' */
extern BYTE abDirtBits[];


/*      Clearing mask for DirtMask value if we don't finish block  */
extern BYTE abDirtBits2[];

extern BYTE ByteBitMasks[];

/*
 ***  Dirty Bit Ordinal table
 *
 */

extern BYTE abDirtOrd[];


/*
 *** Number of buffers to allocate.
 */

extern ulong_t cBufs;

extern  struct  InfoSegGDT SISData;

/*  Lazy write variables
 */
extern int fLWDaemon;           /*  TRUE => lazy write daemon present */
extern LWPARMS LWData;          /*  Lazy write parameters             */
extern BUFA_LOOKUP aBufa;
extern ulong_t msLastIO;        /*  ms count for last non-lazy I/O    */
extern ulong_t semLW;           /*  semaphore for lazy daemon block   */
extern ulong_t semLR;           /* semaphore for lazy read block      */
extern ulong_t semLRWait;       /* semaphore for lazy read block      */
extern ulong_t pLinLRBuf;       /* linear address of lazy write Lazy Read buffer */
extern ulong_t pPhysLRBuf;      /* physical address of lazy write Lazy Read buffer */
extern int fLWIO;               /*  flag indicating that there is lw io */
extern ulong_t cBufs;
extern ulong_t cbufDirty;
extern ulong_t msLastDiskWrite;
extern ulong_t cbufLWLock;
extern ulong_t LWBufWanted;
extern int fGFBActive;
extern ulong_t pGFBWaitChain;
extern ulong_t S2_RW_RQPKT_SIZE;
extern int fAsyncReadRunning;
extern int AsyncReadSem;
extern ulong_t AsyncReadHeadSel;
extern ulong_t AsyncReadHeadOff;
extern ulong_t AsyncReadTailSel;
extern ulong_t AsyncReadTailOff;
extern ulong_t DiskThresh;
extern ulong_t FLWReqListSel;
extern KSEMMTX HotFixKsem;
extern KSEMMTX FLWKsem;
extern ulong_t HotFixBufferSize;
extern int fHotFixing;

#include <infoseg.h>
#if defined(MMIOPH)
#include <perfhook.h>

extern unsigned char *          RMT;                /* Ptr to ras_mec_table */

#define MMPHPreAddHash()        \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3204)  \
   WRITE_DWORD_VALUE(pbuf->BufRBA) \
   }

#define MMPHPreAddLW()          \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3205)  \
   WRITE_DWORD_VALUE(pBuf->BufRBA) \
   }

#define MMPHPreCCH()         \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3206)  \
   WRITE_DWORD_VALUE(rba)       \
   }

#define MMPHPreLazyWriter()        \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3207)  \
   WRITE_WORD_VALUE((ushort_t)cbufDirty)  \
   }

#define MMPHPreLazyRead()       \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3208)  \
   WRITE_DWORD_VALUE(pbuf->BufRBA)  \
   }

#define MMPHPreSDW(a)        \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x320A)  \
   WRITE_DWORD_VALUE(rba)  \
   WRITE_WORD_VALUE(a)    \
   }

#define MMPHPreFLW()         \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x320B)  \
   }

#define MMPHPreLWWrite()        \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x320C)  \
   WRITE_DWORD_VALUE(pBuf)      \
   WRITE_DWORD_VALUE(pBuf->BufRBA) \
   }

#define MMPHPreSDR()         \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3213)  \
   WRITE_DWORD_VALUE(rba)  \
   WRITE_WORD_VALUE((ushort_t)cRun)    \
   }

#define MMPHPostAddHash()       \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3284)  \
   }

#define MMPHPostAddLW()         \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3285)  \
   }

#define MMPHPostCCH()           \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3286)  \
   WRITE_DWORD_VALUE(pbuf)      \
   }

#define MMPHPostLazyWriter()       \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3287)  \
   }

#define MMPHPostLazyRead()      \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3288)  \
   }

#define MMPHPostSDW()           \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x328A)  \
   }

#define MMPHPostFLW()           \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x328B)  \
   }

#define MMPHPostLWWrite()       \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x328C)  \
   }

#define MMPHPostSDR()           \
   if (TEST_TRACING(RMT,0x32)){ \
   SET_DEKKO_ADDRESS(SISData)  \
   WRITE_MAJMNR_CODES (0x3293)  \
   }


#else                    /* define the null macros for */
                      /*   non-performance builds   */
#define MMPHPreAddHash()
#define MMPHPostAddHash()

#define MMPHPreAddLW()
#define MMPHPostAddLW()

#define MMPHPreCCH()
#define MMPHPostCCH()

#define MMPHPreLazyWriter()
#define MMPHPostLazyWriter()

#define MMPHPreLazyRead()
#define MMPHPostLazyRead()

#define MMPHPreSDW(a)
#define MMPHPostSDW()

#define MMPHPreFLW()
#define MMPHPostFLW()

#define MMPHPreLWWrite()
#define MMPHPostLWWrite()

#define MMPHPreSDR()
#define MMPHPostSDR()

#endif
