/* vfs_cluster.c 4.33 82/06/07 */
* Read in (if necessary) the block and return a buffer pointer.
bp
= getblk(dev
, blkno
, size
);
if (bp
->b_flags
&B_DONE
) {
trace(TR_BREADHIT
, dev
, blkno
);
(*bdevsw
[major(dev
)].d_strategy
)(bp
);
trace(TR_BREADMISS
, dev
, blkno
);
u
.u_vm
.vm_inblk
++; /* pay for read */
* Read in the block, like bread, but also start I/O on the
* read-ahead block (which is not allocated to the caller)
breada(dev
, blkno
, size
, rablkno
, rasize
)
daddr_t rablkno
; int rasize
;
register struct buf
*bp
, *rabp
;
* If the block isn't in core, then allocate
* a buffer and initiate i/o (getblk checks
if (!incore(dev
, blkno
)) {
bp
= getblk(dev
, blkno
, size
);
if ((bp
->b_flags
&B_DONE
) == 0) {
(*bdevsw
[major(dev
)].d_strategy
)(bp
);
trace(TR_BREADMISS
, dev
, blkno
);
u
.u_vm
.vm_inblk
++; /* pay for read */
trace(TR_BREADHIT
, dev
, blkno
);
* If there's a read-ahead block, start i/o
if (rablkno
&& !incore(dev
, rablkno
)) {
rabp
= getblk(dev
, rablkno
, rasize
);
if (rabp
->b_flags
& B_DONE
) {
trace(TR_BREADHITRA
, dev
, blkno
);
rabp
->b_flags
|= B_READ
|B_ASYNC
;
(*bdevsw
[major(dev
)].d_strategy
)(rabp
);
trace(TR_BREADMISSRA
, dev
, rablock
);
u
.u_vm
.vm_inblk
++; /* pay in advance */
* If block was in core, let bread get it.
* If block wasn't in core, then the read was started
* above, and just wait for it.
return (bread(dev
, blkno
, size
));
* Write the buffer, waiting for completion.
* Then release the buffer.
bp
->b_flags
&= ~(B_READ
| B_DONE
| B_ERROR
| B_DELWRI
| B_AGE
);
if ((flag
&B_DELWRI
) == 0)
u
.u_vm
.vm_oublk
++; /* noone paid yet */
trace(TR_BWRITE
, bp
->b_dev
, bp
->b_blkno
);
(*bdevsw
[major(bp
->b_dev
)].d_strategy
)(bp
);
* If the write was synchronous, then await i/o completion.
* If the write was "delayed", then we put the buffer on
* the q of blocks awaiting i/o completion status.
* Otherwise, the i/o must be finished and we check for
if ((flag
&B_ASYNC
) == 0) {
} else if (flag
& B_DELWRI
)
* Release the buffer, marking it so that if it is grabbed
* for another purpose it will be written out before being
* given up (e.g. when writing a partial block where it is
* assumed that another write for the same block will soon follow).
* This can't be done for magtape, since writes must be done
* in the same order as requested.
if ((bp
->b_flags
&B_DELWRI
) == 0)
u
.u_vm
.vm_oublk
++; /* noone paid yet */
flags
= bdevsw
[major(bp
->b_dev
)].d_flags
;
bp
->b_flags
|= B_DELWRI
| B_DONE
;
* Release the buffer, start I/O on it, but don't wait for completion.
* Release the buffer, with no I/O implied.
register struct buf
*flist
;
* If someone's waiting for the buffer, or
* is waiting for a buffer wake 'em up.
if (bp
->b_flags
&B_WANTED
)
if (bfreelist
[0].b_flags
&B_WANTED
) {
bfreelist
[0].b_flags
&= ~B_WANTED
;
wakeup((caddr_t
)bfreelist
);
if (bp
->b_flags
& B_LOCKED
)
bp
->b_flags
&= ~B_ERROR
; /* try again later */
bp
->b_dev
= NODEV
; /* no assoc */
* Stick the buffer back on a free list.
if (bp
->b_flags
& (B_ERROR
|B_INVAL
)) {
/* block has no info ... put at front of most free list */
flist
= &bfreelist
[BQUEUES
-1];
if (bp
->b_flags
& B_LOCKED
)
flist
= &bfreelist
[BQ_LOCKED
];
else if (bp
->b_flags
& B_AGE
)
flist
= &bfreelist
[BQ_AGE
];
flist
= &bfreelist
[BQ_LRU
];
bp
->b_flags
&= ~(B_WANTED
|B_BUSY
|B_ASYNC
|B_AGE
);
* See if the block is associated with some buffer
* (mainly to avoid getting hung up on a wait in breada)
dp
= BUFHASH(dev
, blkno
);
for (bp
= dp
->b_forw
; bp
!= dp
; bp
= bp
->b_forw
)
if (bp
->b_blkno
== blkno
&& bp
->b_dev
== dev
&&
(bp
->b_flags
& B_INVAL
) == 0)
return (bread(dev
, blkno
, size
));
* Assign a buffer for the given block. If the appropriate
* block is already associated, return it; otherwise search
* for the oldest non-busy buffer and reassign it.
* We use splx here because this routine may be called
* on the interrupt stack during a dump, and we don't
* want to lower the ipl back to 0.
register struct buf
*bp
, *dp
, *ep
;
if ((unsigned)blkno
>= 1 << (sizeof(int)*NBBY
-PGSHIFT
))
blkno
= 1 << ((sizeof(int)*NBBY
-PGSHIFT
) + 1);
* Search the cache for the block. If we hit, but
* the buffer is in use for i/o, then we wait until
dp
= BUFHASH(dev
, blkno
);
for (bp
= dp
->b_forw
; bp
!= dp
; bp
= bp
->b_forw
) {
if (bp
->b_blkno
!= blkno
|| bp
->b_dev
!= dev
||
if (bp
->b_flags
&B_BUSY
) {
sleep((caddr_t
)bp
, PRIBIO
+1);
if (major(dev
) >= nblkdev
)
* Not found in the cache, select something from
* a free list. Preference is to LRU list, then AGE list.
for (ep
= &bfreelist
[BQUEUES
-1]; ep
> bfreelist
; ep
--)
if (ep
== bfreelist
) { /* no free blocks at all */
sleep((caddr_t
)ep
, PRIBIO
+1);
if (bp
->b_flags
& B_DELWRI
) {
trace(TR_BRELSE
, bp
->b_dev
, bp
->b_blkno
);
* not assigned to any particular device
register struct buf
*bp
, *dp
;
for (dp
= &bfreelist
[BQUEUES
-1]; dp
> bfreelist
; dp
--)
if (dp
== bfreelist
) { /* no free blocks */
sleep((caddr_t
)dp
, PRIBIO
+1);
if (bp
->b_flags
& B_DELWRI
) {
trace(TR_BRELSE
, bp
->b_dev
, bp
->b_blkno
);
bp
->b_flags
= B_BUSY
|B_INVAL
;
bp
->b_dev
= (dev_t
)NODEV
;
* Allocate space associated with a buffer.
* First need to make sure that all overlaping previous I/O
if (size
== bp
->b_bcount
)
if (size
< bp
->b_bcount
|| bp
->b_dev
== NODEV
)
start
= bp
->b_blkno
+ (bp
->b_bcount
/ DEV_BSIZE
);
last
= bp
->b_blkno
+ (size
/ DEV_BSIZE
) - 1;
dp
= BUFHASH(bp
->b_dev
, bp
->b_blkno
);
for (ep
= dp
->b_forw
; ep
!= dp
; ep
= ep
->b_forw
) {
if (ep
->b_blkno
< start
|| ep
->b_blkno
> last
||
ep
->b_dev
!= bp
->b_dev
|| ep
->b_flags
&B_INVAL
)
if (ep
->b_flags
&B_BUSY
) {
sleep((caddr_t
)ep
, PRIBIO
+1);
* What we would really like to do is kill this
* I/O since it is now useless. We cannot do that
* so we force it to complete, so that it cannot
* over-write our useful data later.
if (ep
->b_flags
& B_DELWRI
) {
* Here the buffer is already available, so all we
* need to do is set the size. Someday a better memory
* management scheme will be implemented.
* Release space associated with a buffer.
* Here the buffer does not change, so all we
* need to do is set the size. Someday a better memory
* management scheme will be implemented.
* Wait for I/O completion on the buffer; return errors
while ((bp
->b_flags
&B_DONE
)==0)
sleep((caddr_t
)bp
, PRIBIO
);
* Mark I/O complete on a buffer. If the header
* indicates a dirty page push completion, the
* header is inserted into the ``cleaned'' list
* to be processed by the pageout daemon. Otherwise
* release it if I/O is asynchronous, and wake
* up anyone waiting for it.
if (bp
->b_flags
& B_DONE
)
if (bp
->b_flags
& B_DIRTY
) {
if (bp
->b_flags
& B_ERROR
)
bp
->b_bcount
= swsize
[bp
- swbuf
];
bp
->b_pfcent
= swpf
[bp
- swbuf
];
cnt
.v_pgpgout
+= bp
->b_bcount
/ NBPG
;
if (bswlist
.b_flags
& B_WANTED
)
wakeup((caddr_t
)&proc
[2]);
bp
->b_flags
&= ~B_WANTED
;
* make sure all write-behind blocks
* on dev (or NODEV for all)
* (from umount and update)
* (and temporarily pagein)
register struct buf
*flist
;
for (flist
= bfreelist
; flist
< &bfreelist
[BQUEUES
]; flist
++)
for (bp
= flist
->av_forw
; bp
!= flist
; bp
= bp
->av_forw
) {
if ((bp
->b_flags
& B_DELWRI
) == 0)
if (dev
== NODEV
|| dev
== bp
->b_dev
) {
* Pick up the device's error number and pass it to the user;
* if there is an error but the number is 0 set a generalized
* code. Actually the latter is always true because devices
* don't yet return specific errors.
if ((u
.u_error
= bp
->b_error
)==0)
* Invalidate in core blocks belonging to closed or umounted filesystem
* This is not nicely done at all - the buffer ought to be removed from the
* hash chains & have its dev/blkno fields clobbered, but unfortunately we
* can't do that here, as it is quite possible that the block is still
* being used for i/o. Eventually, all disc drivers should be forced to
* have a close routine, which ought ensure that the queue is empty, then
* properly flush the queues. Until that happy day, this suffices for
register struct bufhd
*hp
;
#define dp ((struct buf *)hp)
for (hp
= bufhash
; hp
< &bufhash
[BUFHSZ
]; hp
++)
for (bp
= dp
->b_forw
; bp
!= dp
; bp
= bp
->b_forw
)