/*static char *SCCSID = "@(#)buf.h	6.3 92/01/31";*/
/*
 **	Buffer Nodes
 *
 *	HISTORY
 *	    14-Sep-1988 11:18 by Leif Pederson [leifp] re-synch w/gordon
 *	    8-Nov-1988 by Thomas Fenwick [thomasf] pickup volume changes
 */

/* XLATOFF */


/**	SECTOR - One sector's worth of storage.
 *
 */

typedef struct SECTOR { /* SECTOR */
	BYTE ab [SECSIZE];
} SECTOR;

typedef SECTOR far *PSECTOR;

/* XLATON */


/**	BLOCK - The quantity of data that is pointed to by a bufnode.
 *
 */

typedef SECTOR BLOCK [SPB];
typedef BLOCK far *PBLOCK;


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

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


/*
 *	When the comments talk about "buffer address" they're always
 *	talking about the buffer header address.  If we're talking
 *	about the address of the data field of the buffer, we say
 *	"buffer data".
 *
 *	These buffer headers are also used as I/O request blocks for
 *	the data in the buffer.  The bIOp field contains the
 *	function.  Sometimes we need an I/O request block which
 *	isn't assocated with a buffer - for example, when we read
 *	directly into an application's memory area.  For this, we keep
 *	structures, but they aren't associated with a buffer and the
 *	B_ADDR field is used to hold the I/O target address.
 *	These BUFNODEs have BF_IORQ in the flags field to distinguish
 *	them from regular buffer headers.
 *
 *	Note that we use the cksBuf field for debugging purposes.  This is an
 *	array of SPB words, each of which holds the CAS (high and low
 *	16 bits xored togehter) of one of the sectors.  We can then check
 *	this value to make sure that we're setting the dirty bits
 *	correctly.
 *
 *	NOTE - see the discussion on holding and locking at the end of
 *	this file
 */

typedef struct BUFNODE { /* BUFNODE */
////test stuff
//	ULONG sig1;		/* PIC signature for assertion checking */
////end test stuff
	DCHNB	dcbpLru;		/* LRU chain */
	LSN	lsnSec;			/* Vsector # */
////test stuff
//	ULONG sig2;		/* PIC signature for assertion checking */
////end test stuff
	PBLOCK	pblkData;		/* address of data block */
	ULONG	blkDataPhysAddr;	/* locked physical address of the 2k */
					/* buffer - used by lwasync to pass  */
					/* qdiskop for S/G list creation     */
	DCHNB	dcbpDirt;		/* dirty list */

	USHORT flDMask;		/* B_DIRTMASK
      					 * mask of bits indicating which
		       			 * files have dirty sectors in this
                      * buffer.
			      		 */

	BYTE	bDirtMask;	 /* B_DIRT
         				  * 1 bit per dirty sector
			         	  * used to optimize the write.
         				  * Non dirty marked sectors may
		         		  * still be rewritten
					        */

	BYTE	 fFlag;		 /* flags, BFL_xxx */
	USHORT  cusHolds;	 /* hold count */

	USHORT	cLocked;	/* B_LCNT   Lock count. A flag except in write_direct
//	BYTE  cLocked;	/* B_LCNT   Lock count. A flag except in write_direct
                      * case when it used as a count in conjunction with
                      * BFL_EMPTY flag.
                      */
//   USHORT locker1;
//   USHORT locker2;
//   USHORT locker3;
//   USHORT waiter1;
//   USHORT waiter2;
//   USHORT waiter3;
//   USHORT unlocker1;
//   USHORT unlocker2;
//   USHORT unlocker3;
////test stuff
//	ULONG sig2;		/* PIC signature for assertion checking */
////end test stuff
	BYTE	bBadMask;		/* B_BADSEC 1 bit per defective sect */
	struct BUFNODE *pbufNextHint;	/* advisory address of next buffer
					                   * content; always a buffer header
					                   * addr, never 0
					                   */

	DCHNB	dcbpHash;		/* sector hash chain */
	struct BUFNODE * *ppbufHTA;	/* address of entry in hash table */
	PVOID	pWaitChain;		/* head of wait chain */

#ifdef DEBUG
	USHORT	ausCheckSum [4];	/* holds checksum of buffer contents */
#endif

	ULONG	msLastDirtied;		/* ms since boot when buffer
	             	 			 * was last dirtied
	 	 			             */

	PVOID	pLzRdBitMap;		/* ptr to lazy read bitmap, 1==unused,
				                * 0==used
		               		 */
	ULONG	msFirstDirtied;		/* ms since boot when buffer
					                * was first dirtied
			               		 */
	PVOID	pLWHead;		/* head of lazy write wait chain */

} BUFNODE;

typedef BUFNODE *PBUFNODE;


/*	NOTEREC - Notification Record
 *
 *	Some callers may post several disk requests in parallel and
 *	want to keep track of when they're *all* complete.  I/O
 *	request blocks (buffer headers w/o buffers) have fields to
 *	allow this.  The caller stores the address of his NOTEREC,
 *	and when each request completes it clears it's associated bit.
 *	When all of the bits clear the block chain in the NOTEREC
 *	is woken.
 *
 *	Note that there can be only one thread blocked on a NOTEREC
 *	because the first guy to wakeup will return the NOTEREC to the
 *	heap or whatever.  This occurs naturally; unlike I/O to the
 *	buffer cache, NOTERECs are used for direct I/O.  If someone
 *	else wants to do I/O to the same location and if record and file
 *	locking allows that, then they'll get their own NOTEREC or
 *	cache I/O request and have a horse race.  NOTERECs are only used
 *	to do file data I/O, all "filesystem" structures are manipulated
 *	via the cache.
 *
 *	Error & Mask are byte fields because they will never exceed 8,
 *	since HPFS only queues 4 I/Os at a time.
 *
 *	NxtBit is a ushort because it can be 0x10 (if we allow 8 I/O requests
 *	at a time).
 */


typedef struct sExtentBuf
  {
   PBUFNODE aExtentBuf;
  } sExtentBuf;

typedef BYTE FAR *PBYTE;		

typedef struct sExtentElement
  {
   ULONG ulSecFrs;  /* first sector of extent */
   USHORT usSecCnt; /* number of sectors  */
	PBYTE pbyClntBuf;
  } sExtentElement;

#define MAXNOTERECIO 10 /* ((4 + 1) * 2) are the max cache blocks */
                        /* required to support 4 concurrent I/Os */

typedef struct NOTEREC { /* NOTEREC */
//test only
#ifdef DEBUG
	BYTE sig [4];		/* PIC signature for assertion checking */
#endif
//test
//	ULONG sig;		/* PIC signature for assertion checking */
//	PVOID pioh1;		/* NTR_REQ - chain of IO Handles */
//   PVOID IOListHead1;
//	PVOID pioh2;		/* NTR_REQ - chain of IO Handles */
//   PVOID IOListHead2;
//	PVOID pioh4;		/* NTR_REQ - chain of IO Handles */
//   PVOID IOListHead4;
//	PVOID pioh8;		/* NTR_REQ - chain of IO Handles */
//   PVOID IOListHead8;
	BYTE errmsk;		/* NTR_FLD - error mask - accumulative */
	BYTE mask;		/*  "   "  - the Mask Bit field */
	USHORT nxtbit;		/* NTR_MSK - next bit to set in field */
	ULONG block;		/* NTR_BLC - the block chain */
	PVOID pioh;		/* NTR_REQ - chain of IO Handles */
   sExtentElement aExtentList[4];
	BYTE byCreclOn;
	BYTE byELIdx;
	BYTE aExtentBufIndex;
   sExtentBuf pExtentBuf[MAXNOTERECIO];

} NOTEREC;     /* NOTEREC */

typedef NOTEREC far *PNOTEREC;

#define MAXNOTEBIT 8		/* max bit for 4 concurrent I/Os (in nxtbit) */

#ifdef DEBUG
#define SIGNR "NOTR"
#endif


/* XLATOFF */

/**	add a buffer to the front of a list
 */
#define DCADDF(pbuf,field,bufHead)							\
	{   register PBUFNODE pbufTmp = (pbuf);						\
	    register PBUFNODE 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 PBUFNODE pbufTmp = (pbuf);						\
	    register PBUFNODE 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 PBUFNODE pbufFwd = (pbuf)->field.pbufFwd;	\
	    register PBUFNODE pbufBak = (pbuf)->field.pbufBak;	\
								\
	    pbufFwd->field.pbufBak = pbufBak;			\
	    pbufBak->field.pbufFwd = pbufFwd;			\
	}

#ifdef DEBUG
#define CBUFDIRTY_CK(pbuf) {						\
	    if (cbufDirty == 0) {					\
		dprintf (__FILE__": cbufDirty about to underflow\n");	\
		Int3 ();						\
	    }								\
	    if ((pbuf) == &bufLWHead) {					\
		dprintf (__FILE__": pbuf == bufLWHead\n");		\
		Int3 ();						\
	    }								\
	}
#define PBUFLINK_CK(pbuf) {						\
	    if (((pbuf)->dcbpDirt.pbufBak == NULL) ||			\
		((pbuf)->dcbpDirt.pbufFwd == NULL)) {			\
		dprintf (__FILE__": pbuf (%x) has null dcpDirt ptr\n",	\
			 pbuf);						\
		Int3 ();						\
	    }								\
	}
#else
#define CBUFDIRTY_CK(pbuf)
#define PBUFLINK_CK(pbuf)
#endif


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


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

				/* some folks just zero this field, so */
				/* check references to fFlag when adding */
#define BFL_LWLOCK  0x00000001	/* buffer is being lazy written in a block */
//#define BFL_BITMAP  0x00000010	/* is a bitmap cache block */
#define BFL_LZREAD  0x00000002	/* is a lazy read block */
#define BFL_EMPTY   0x00000080	/* is a null cache block */
#if defined(PERFVIEW) && defined(RAHEAD)
#define BFL_PVWRA    0x00000020	 /* Cache block read by ReadAhead */
#define BFL_PVWRAUSE 0x00000040	 /* Cache block read by ReadAhead and used */
#endif


/* BUFNODE bType values (PIC does not have this field) */

#define BF_FREE		0	/* buffer is free (not in LRU list) */
#define BF_LRU		1	/* buffer is in LRU list */


/*	Priority values for LRU placement   */

#define BP_KEEP		0	    /* Buffer contains future-usable data */
#define BP_NOOPINION	1	    /* Buffer contains marginally useful data */
#define BP_TOSS		2	    /* Buffer is unlikely to be used */


/**	SECPTR - Sector Pointer Structure
 *
 *	Structures which contain a sector number usually use the
 *	SECPTR structure, which contains an advisory pointer.  The
 *	pointer points to a buffer header, which is *probably* the
 *	header for the sector named in SECPTR, but the user must check.
 */

typedef struct SECPTR { /* SECPTR */
    LSN lsn;			/* Vsector number */
    PBUFNODE pbufHint;		/* address of buf header, 0 if none */
} SECPTR;  /* SECPTR */

typedef SECPTR far *PSECPTR;


/**	Holding and Locking
 *
 *  LOCK means that the buffer contents are inconsistant/incorrect.  No body
 *	 is allowed to look at the contents of the buffer.  This is done
 *	 when we're reading in from the disk; we'll mark the buffer
 *	 with the Vsector # (so that any other folks that want that sector
 *	 won't issue their own reads in parallel) but we mark it LOCKED
 *	 so that nobody looks at it's contents which aren't correct yet.
 *
 *	 LOCKed is pretty rare because most folks which are mucking
 *	 with a buffer have it back in a consistant state before they
 *	 allow a context switch.
 *
 *	 An important exception to this is directory manipulation -
 *	 directory splitting, etc.  In this case, a flag has been set
 *	 on the directory itself (in SBDIR) so that no one will try to
 *	 look at the directory contents.  The cache block which has the
 *	 inconsistant DIRBLK might also have sectors belonging to someone
 *	 else and those sectors can be accessed by other folks because
 *	 the buffer isn't locked.  (We don't lock the directory and not
 *	 the block for this reason, it's a fallout.  We lock the directory
 *	 because it's too mucky for people to "back out" if they're
 *	 searching down into a directory and find out that they've
 *	 reached an area which is being rebuilt.  The rebuilding might
 *	 propigate up and change the unlocked higher DIRBLKs that this
 *	 other guy has already accessed...  So we lock the whole directory,
 *	 and thus needn't bother locking the cache blocks themselves.
 *
 *   HOLD means that the contents are valid, but the cache block must continue
 *	 to hold that data.  Folks use this when they need to access
 *	 two different sectors at the same time.  They HOLD one when
 *	 they read the other so that there's no chance that by the
 *	 time the 2nd read finishes the first one has been evicted.
 *	 This is much cheaper than remembering the first one's Vsector
 *	 # and calling RDBUF N times as you transfer N words of info
 *	 between the two sectors.
 *
 *	 By definition only one guy can lock a buffer (the lock count should
 *	 go to a flag; it's already a flag on directorys) but the hold
 *	 value is a count, since multiple people can hold.  (Like the
 *	 electrician's safety plate which allows multiple electricians
 *	 to lock a breaker OPEN so that it can't be closed until ALL are
 *	 done).  (SBDIRs have a hold count for the same reason - folks
 *	 may yield while in a directory and don't want it to change out
 *	 from under them)
 *
 *	 We also use hold when we set dirty bits.  The concern is that
 *	 if we're going to write something to a buffer, yield the CPU,
 *	 then write something else, we don't want to have to make two
 *	 calls to SetDirt, one after each write.  This costs time, and
 *	 also it would be a waste if the lazywriter were to write this
 *	 guy anyhow, since he's going to get dirty again ASAP and
 *	 writing him out doesn't free the cache block anyway, since it's
 *	 held.
 *
 *	 So my current algorithm is that I won't lazywrite anybody who
 *	 is held, and therefore won't mark them clean.  This means,
 *	 in effect, that there is no ordering constraint on dirtying
 *	 a buffer or marking it dirty so long as it is held the entire
 *	 time.  I think that the code now always marks it dirty before
 *	 a yield, even if held, because the debug code is a bit hard
 *	 assed about it, but this could be relaxed under the current
 *	 lazywrite rules I've just described.
 *
 *   Both in the case of directorys and cache blocks, the theory is that
 *   since these buffers are MRU, it's extremely rare that we'd actually try
 *   to reclaim their buffer slots, and in general it's rare that there's
 *   a conflict in their use.  So in actual execution, these locks are
 *   very rarely encountered.  They're cheap - INC to set and DEC to clear,
 *   so almost always all we're doing is INCing and DECing a location
 *   and it's just two wasted instructions.  Once in a while, though,
 *   it's a big bacon save, as they say.
 */
