/*static char *SCCSID = "@(#)buf.h	6.1 90/11/18";*/
/*static char *SCCSID = "@(#)buf.h	13.4 90/01/26";*/
/***	BUF.H - Buffer definitions
 *
 *	HPFS Utilities
 *	Peter A. Williams
 *	Copyright 1988 Microsoft Corporation
 *
 *	Modification history:
 *	P.A. Williams	08/01/89	Added typedef BUFNODE and PBUFNODE.
 */


/**	Buffer Nodes
 *
 *	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 B_IOP 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
 *	a pool of I/O request blocks which are actually BUFNODE
 *	structures, but they aren't associated with a buffer and the
 * pblkData 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 ausCheckSum 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
 */


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

typedef struct DCHNB { /* DCHNB */
        void far *pbufFwd;
        void far *pbufBak;
}DCHNB;


typedef struct BUFNODE { /* BUFNODE */

  DCHNB dcbpLru;                  /* LRU chain, if not BF_IORQ*/
  long dcbpLruH;		 /* address of LRU chain head if not locked/held*/
				 /* if lcoked/held, we're on that chain but*/
				 /* this guy points to it's regular chain*/
  long lsnSec;                   /* VSector #*/
  long pblkData;		 /* address of data*/
  DCHNB dcbpDirt;                 /* doubly linked list of dirty buffers*/
  char bDirtMask;		 /* 1 bit per dirty sector*/
				 /*   used to optimize the write.  Non dirty*/
				 /*   marked sectors may still be rewritten*/
  char	 B_iop;			 /* disk driver I/O operation*/
  char	 B_type;		 /* type of info*/
  char fFlag;                    /* flags*/
  short cusHolds;		 /* hold count*/
  char cLocked;                  /* lock count (really a flag, only 0 or 1 */
  char bBadMask;		 /* 1 bit per defective sector */
  long pbufNextHint;		 /* advisory address of next buffer content*/
				 /*   always a buffer header addr, never 0*/
  long	 B_DADR;		 /* address of routine to call when I/O is done*/

				 /*  The following two fields are redefined*/
				 /*  if BF_IORQ is set.*/
  DCHNB dcbpHash;         /* sector hash chain*/
  long  ppbufHTA;                /* address of entry in hash table*/

  long  pWaitChain;              /* head of wait chain*/
#ifdef DEBUG
  short ausCheckSum[4];		 /* holds checksum of buffer contents*/
#else
  char	B_XTRA[8];
#endif

/* 64 byte boundary */

  long msLastDirtied;		 /* time (in ms/512) when buffer was last dirtied */
  long msFirstDirtied;		 /* time (in ms/512) when buffer got first dirtied */
  long pLWHead;                  /* head of lazy write wait chain */
  char	B_XTRA2[64-16];
} BUFNODE; /* BUFNODE */

typedef struct BUFNODE *PBUFNODE;

#define BUFHDRSHIFT    7

#ifdef MASM
	.errnz	(size BUFNODE) - (1 SHL BUFHDRSHIFT)
#endif

/*     Following are alternative offsets if BF_IORQ is set*/

#define B_XFER	(DWORD PTR ppbufHTA)	/* holds transfer sec cnt*/
#define B_NADR	(DWORD PTR dcbpHash)   /* addr of client's NOTEREC*/
#define B_NMSK	(DWORD PTR dcbpHash+4) /* notification mask*/
#ifdef MASM
	 .errnz	  (size DCHNB)-8  /* enough room for double map*/
#endif

/* fFlag bits	*/

#define BFL_LWLOCK  0x00000001	    /* buffer is being lazy written in a block */


/*	B_type values */

#define BF_FREE	    0		    /* buffer is free (not in LRU list) */
#define BF_LRU	    1		    /* buffer 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 */

/*	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.
 *
 *	The fields are DWORD, but only the low byte of the MSK and FLD
 *	records are used for normal completion.  The 3rd byte of NTR_FLD
 *	(..FF....h) is used for error posting - these bits are
 *	set if an irrecoverable error occured in the I/O.
 */

typedef struct NOTEREC { /* NOTEREC */
  long	 NTR_FLD;		    /* the mask bit field*/
  long block;		    /* head of the block chain*/
  long pioh;		    /* next bit to set in NTR_FLD*/
} NOTEREC;    /* NOTEREC */


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


/**	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 */
    long    SNUM;		/* VSector number */
    BUFNODE *SHINT;      /* hint address, 0 if none */
}SECPTR;      /* SECPTR */
