summaryrefslogtreecommitdiffstats
path: root/private/ntos/boot/bootcode/hpfs/i386/buf.inc
blob: fe2eb83778e3b06c79ed7d63fbbf7266453352f3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
;static char *SCCSID = "@(#)buf.h	12.1 88/11/21";
;**	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
;	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 B_SUM 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
;

BUFNODE 	struc

  B_LRU	db  (size DCHDR) dup (?)			 ; LRU chain, if not BF_IORQ
  B_LRUH dd ?		 ; 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
  B_SEC dd ? 		 ; VSector #
  B_ADDR dd ?		 ; address of data
  B_DCHN	db  (size DCHDR) dup (?)		 ; doubly linked list of dirty buffers
  B_dirt db ?		 ; 1 bit per dirty sector
				 ;   used to optimize the write.  Non dirty
				 ;   marked sectors may still be rewritten
  B_iop db ? 		 ; disk driver I/O operation
  B_type db ?		 ; type of info
  B_FLAG db ?		 ; flags
  B_HCNT dw ?		 ; hold count
  B_LCNT db ?		 ; lock count (really a flag, only 0 or 1
  B_BADSEC db ?		 ; 1 bit per defective sector
  B_next dd ?		 ; advisory address of next buffer content
				 ;   always a buffer header addr, never 0
  B_DADR dd ?		 ; address of routine to call when I/O is done

				 ;  The following two fields are redefined
				 ;  if BF_IORQ is set.
  B_HASH	db  (size DCHDR) dup (?)		     ; sector hash chain
  B_HTA dd ? 		     ; address of entry in hash table

  B_WAIT dd ?		 ; head of wait chain
ifdef DEBUG
  B_SUM dw 4 dup (?)		 ; holds checksum of buffer contents
else
  B_XTRA db 8 dup (?)
endif

; 64 byte boundary

  B_LDTIME dd ?		 ; time (in ms/512) when buffer was last dirtied
  B_FDTIME dd ?		 ; time (in ms/512) when buffer got first dirtied
  B_LWWAIT dd ?		 ; head of lazy write wait chain
  B_XTRA2 db 64-16 dup (?)
BUFNODE	ends

;typedef struct BUFNODE  BUFNODE;
;typedef struct BUFNODE *PBUFNODE;

BUFHDRSHIFT	equ	7

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

;     Following are alternative offsets if BF_IORQ is set

B_XFER	equ	(DWORD PTR B_HTA)    ; holds transfer sec cnt
B_NADR	equ	(DWORD PTR B_HASH)   ; addr of client's NOTEREC
B_NMSK	equ	(DWORD PTR B_HASH+4) ; notification mask
ifdef MASM
	 .errnz   (size DCHDR)-8  ; enough room for double map
endif

;	B_FLAG bits	

BFL_LWLOCK 	equ	00000001h	    ; buffer is being lazy written in a block


;	B_type values

BF_FREE	equ	0		    ; buffer is free (not in LRU list)
BF_LRU	equ	1		    ; buffer in LRU list


;	Priority values for LRU placement

BP_KEEP	equ	0		    ; Buffer contains future-usable data
BP_NOOPINION	equ	1		    ; Buffer contains marginally useful data
BP_TOSS	equ	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.
;

NOTEREC 	struc
  NTR_FLD dd ?		    ; the mask bit field
  NTR_BLC dd ?		    ; head of the block chain
  NTR_MSK dd ?		    ; next bit to set in NTR_FLD
NOTEREC	ends


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