Changes imported from XFS for FreeBSD project:

- add fields to struct buf (needed by XFS)
    - 3 private fields: b_fsprivate1, b_fsprivate2, b_fsprivate3
    - b_pin_count, count of pinned buffer

- add new B_MANAGED flag
- add breada() function to initiate asynchronous I/O on read-ahead blocks.
- add bufdone_finish(), bpin(), bunpin_wait() functions

Patches provided by:	kan
Reviewed by:		phk
Silence on:		arch@
This commit is contained in:
rodrigc 2005-12-07 03:39:08 +00:00
parent 03a6ffac1f
commit 5a03a98174
3 changed files with 156 additions and 34 deletions

View File

@ -236,6 +236,11 @@ static struct mtx nblock;
static struct mtx bdonelock;
/*
* Lock that protects against bwait()/bdone()/B_DONE races.
*/
static struct mtx bpinlock;
/*
* Definitions for the buffer free lists.
*/
@ -523,6 +528,7 @@ bufinit(void)
mtx_init(&nblock, "needsbuffer lock", NULL, MTX_DEF);
mtx_init(&bdlock, "buffer daemon lock", NULL, MTX_DEF);
mtx_init(&bdonelock, "bdone lock", NULL, MTX_DEF);
mtx_init(&bpinlock, "bpin lock", NULL, MTX_DEF);
/* next, make a null set of free lists */
for (i = 0; i < BUFFER_QUEUES; i++)
@ -720,37 +726,16 @@ bread(struct vnode * vp, daddr_t blkno, int size, struct ucred * cred,
}
/*
* Operates like bread, but also starts asynchronous I/O on
* read-ahead blocks. We must clear BIO_ERROR and B_INVAL prior
* to initiating I/O . If B_CACHE is set, the buffer is valid
* and we do not have to do anything.
* Attempt to initiate asynchronous I/O on read-ahead blocks. We must
* clear BIO_ERROR and B_INVAL prior to initiating I/O . If B_CACHE is set,
* the buffer is valid and we do not have to do anything.
*/
int
breadn(struct vnode * vp, daddr_t blkno, int size,
daddr_t * rablkno, int *rabsize,
int cnt, struct ucred * cred, struct buf **bpp)
void
breada(struct vnode * vp, daddr_t * rablkno, int * rabsize,
int cnt, struct ucred * cred)
{
struct buf *bp, *rabp;
struct buf *rabp;
int i;
int rv = 0, readwait = 0;
CTR3(KTR_BUF, "breadn(%p, %jd, %d)", vp, blkno, size);
*bpp = bp = getblk(vp, blkno, size, 0, 0, 0);
/* if not found in cache, do some I/O */
if ((bp->b_flags & B_CACHE) == 0) {
if (curthread != PCPU_GET(idlethread))
curthread->td_proc->p_stats->p_ru.ru_inblock++;
bp->b_iocmd = BIO_READ;
bp->b_flags &= ~B_INVAL;
bp->b_ioflags &= ~BIO_ERROR;
if (bp->b_rcred == NOCRED && cred != NOCRED)
bp->b_rcred = crhold(cred);
vfs_busy_pages(bp, 0);
bp->b_iooffset = dbtob(bp->b_blkno);
bstrategy(bp);
++readwait;
}
for (i = 0; i < cnt; i++, rablkno++, rabsize++) {
if (inmem(vp, *rablkno))
@ -774,6 +759,39 @@ breadn(struct vnode * vp, daddr_t blkno, int size,
brelse(rabp);
}
}
}
/*
* Operates like bread, but also starts asynchronous I/O on
* read-ahead blocks.
*/
int
breadn(struct vnode * vp, daddr_t blkno, int size,
daddr_t * rablkno, int *rabsize,
int cnt, struct ucred * cred, struct buf **bpp)
{
struct buf *bp;
int rv = 0, readwait = 0;
CTR3(KTR_BUF, "breadn(%p, %jd, %d)", vp, blkno, size);
*bpp = bp = getblk(vp, blkno, size, 0, 0, 0);
/* if not found in cache, do some I/O */
if ((bp->b_flags & B_CACHE) == 0) {
if (curthread != PCPU_GET(idlethread))
curthread->td_proc->p_stats->p_ru.ru_inblock++;
bp->b_iocmd = BIO_READ;
bp->b_flags &= ~B_INVAL;
bp->b_ioflags &= ~BIO_ERROR;
if (bp->b_rcred == NOCRED && cred != NOCRED)
bp->b_rcred = crhold(cred);
vfs_busy_pages(bp, 0);
bp->b_iooffset = dbtob(bp->b_blkno);
bstrategy(bp);
++readwait;
}
breada(vp, rablkno, rabsize, cnt, cred);
if (readwait) {
rv = bufwait(bp);
@ -807,6 +825,10 @@ bufwrite(struct buf *bp)
if (BUF_REFCNT(bp) == 0)
panic("bufwrite: buffer is not busy???");
if (bp->b_pin_count > 0)
bunpin_wait(bp);
KASSERT(!(bp->b_vflags & BV_BKGRDINPROG),
("FFS background buffer should not get here %p", bp));
@ -1117,6 +1139,11 @@ brelse(struct buf *bp)
KASSERT(!(bp->b_flags & (B_CLUSTER|B_PAGING)),
("brelse: inappropriate B_PAGING or B_CLUSTER bp %p", bp));
if (bp->b_flags & B_MANAGED) {
bqrelse(bp);
return;
}
if (bp->b_iocmd == BIO_WRITE &&
(bp->b_ioflags & BIO_ERROR) &&
!(bp->b_flags & B_INVAL)) {
@ -1394,6 +1421,18 @@ bqrelse(struct buf *bp)
BUF_UNLOCK(bp);
return;
}
if (bp->b_flags & B_MANAGED) {
if (bp->b_flags & B_REMFREE) {
mtx_lock(&bqlock);
bremfreel(bp);
mtx_unlock(&bqlock);
}
bp->b_flags &= ~(B_ASYNC | B_NOCACHE | B_AGE | B_RELBUF);
BUF_UNLOCK(bp);
return;
}
mtx_lock(&bqlock);
/* Handle delayed bremfree() processing. */
if (bp->b_flags & B_REMFREE)
@ -1821,6 +1860,10 @@ getnewbuf(int slpflag, int slptimeo, int size, int maxsize)
bp->b_npages = 0;
bp->b_dirtyoff = bp->b_dirtyend = 0;
bp->b_bufobj = NULL;
bp->b_pin_count = 0;
bp->b_fsprivate1 = NULL;
bp->b_fsprivate2 = NULL;
bp->b_fsprivate3 = NULL;
LIST_INIT(&bp->b_dep);
@ -2059,6 +2102,10 @@ flushbufqueues(int flushdeps)
if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0)
continue;
if (bp->b_pin_count > 0) {
BUF_UNLOCK(bp);
continue;
}
BO_LOCK(bp->b_bufobj);
if ((bp->b_vflags & BV_BKGRDINPROG) != 0 ||
(bp->b_flags & B_DELWRI) == 0) {
@ -2393,6 +2440,19 @@ getblk(struct vnode * vp, daddr_t blkno, int size, int slpflag, int slptimeo,
if ((bp->b_flags & B_VMIO) == 0 ||
(size > bp->b_kvasize)) {
if (bp->b_flags & B_DELWRI) {
/*
* If buffer is pinned and caller does
* not want sleep waiting for it to be
* unpinned, bail out
* */
if (bp->b_pin_count > 0) {
if (flags & GB_LOCK_NOWAIT) {
bqrelse(bp);
return (NULL);
} else {
bunpin_wait(bp);
}
}
bp->b_flags |= B_NOCACHE;
bwrite(bp);
} else {
@ -3033,11 +3093,11 @@ bufdone(struct buf *bp)
struct bufobj *dropobj;
void (*biodone)(struct buf *);
CTR3(KTR_BUF, "bufdone(%p) vp %p flags %X", bp, bp->b_vp, bp->b_flags);
dropobj = NULL;
KASSERT(BUF_REFCNT(bp) > 0, ("biodone: bp %p not busy %d", bp, BUF_REFCNT(bp)));
KASSERT(BUF_REFCNT(bp) > 0, ("biodone: bp %p not busy %d", bp,
BUF_REFCNT(bp)));
KASSERT(!(bp->b_flags & B_DONE), ("biodone: bp %p already done", bp));
runningbufwakeup(bp);
@ -3052,6 +3112,19 @@ bufdone(struct buf *bp)
bufobj_wdrop(dropobj);
return;
}
bufdone_finish(bp);
if (dropobj)
bufobj_wdrop(dropobj);
}
void
bufdone_finish(struct buf *bp)
{
KASSERT(BUF_REFCNT(bp) > 0, ("biodone: bp %p not busy %d", bp,
BUF_REFCNT(bp)));
if (LIST_FIRST(&bp->b_dep) != NULL)
buf_complete(bp);
@ -3117,7 +3190,8 @@ bufdone(struct buf *bp)
if (m == NULL)
panic("biodone: page disappeared!");
bp->b_pages[i] = m;
pmap_qenter(trunc_page((vm_offset_t)bp->b_data), bp->b_pages, bp->b_npages);
pmap_qenter(trunc_page((vm_offset_t)bp->b_data),
bp->b_pages, bp->b_npages);
}
#if defined(VFS_BIO_DEBUG)
if (OFF_TO_IDX(foff) != m->pindex) {
@ -3184,8 +3258,6 @@ bufdone(struct buf *bp)
bqrelse(bp);
} else
bdone(bp);
if (dropobj)
bufobj_wdrop(dropobj);
}
/*
@ -3741,6 +3813,32 @@ bufobj_wwait(struct bufobj *bo, int slpflag, int timeo)
return (error);
}
void
bpin(struct buf *bp)
{
mtx_lock(&bpinlock);
bp->b_pin_count++;
mtx_unlock(&bpinlock);
}
void
bunpin(struct buf *bp)
{
mtx_lock(&bpinlock);
if (--bp->b_pin_count == 0)
wakeup(bp);
mtx_unlock(&bpinlock);
}
void
bunpin_wait(struct buf *bp)
{
mtx_lock(&bpinlock);
while (bp->b_pin_count > 0)
msleep(bp, &bpinlock, PRIBIO, "bwunpin", 0);
mtx_unlock(&bpinlock);
}
#include "opt_ddb.h"
#ifdef DDB
#include <ddb/ddb.h>

View File

@ -770,6 +770,12 @@ cluster_wbuild(vp, size, start_lbn, len)
--len;
continue;
}
if (tbp->b_pin_count > 0) {
BUF_UNLOCK(tbp);
++start_lbn;
--len;
continue;
}
bremfree(tbp);
tbp->b_flags &= ~B_DONE;
@ -873,6 +879,15 @@ cluster_wbuild(vp, size, start_lbn, len)
BUF_UNLOCK(tbp);
break;
}
/*
* Do not pull in pinned buffers.
*/
if (tbp->b_pin_count > 0) {
BUF_UNLOCK(tbp);
break;
}
/*
* Ok, it's passed all the tests,
* so remove it from the free list

View File

@ -135,6 +135,10 @@ struct buf {
struct vm_page *b_pages[btoc(MAXPHYS)];
int b_npages;
struct workhead b_dep; /* (D) List of filesystem dependencies. */
void *b_fsprivate1;
void *b_fsprivate2;
void *b_fsprivate3;
int b_pin_count;
};
#define b_object b_bufobj->bo_object
@ -214,7 +218,7 @@ struct buf {
#define B_01000000 0x01000000 /* Available flag. */
#define B_02000000 0x02000000 /* Available flag. */
#define B_PAGING 0x04000000 /* volatile paging I/O -- bypass VMIO */
#define B_08000000 0x08000000 /* Available flag. */
#define B_MANAGED 0x08000000 /* Managed by FS. */
#define B_RAM 0x10000000 /* Read ahead mark (flag) */
#define B_VMIO 0x20000000 /* VMIO flag */
#define B_CLUSTER 0x40000000 /* pagein op, so swap() can count it */
@ -486,6 +490,7 @@ int buf_dirty_count_severe(void);
void bremfree(struct buf *);
void bremfreef(struct buf *); /* XXX Force bremfree, only for nfs. */
int bread(struct vnode *, daddr_t, int, struct ucred *, struct buf **);
void breada(struct vnode *, daddr_t *, int *, int, struct ucred *);
int breadn(struct vnode *, daddr_t, int, daddr_t *, int *, int,
struct ucred *, struct buf **);
void bdwrite(struct buf *);
@ -504,6 +509,7 @@ struct buf *geteblk(int);
int bufwait(struct buf *);
int bufwrite(struct buf *);
void bufdone(struct buf *);
void bufdone_finish(struct buf *);
int cluster_read(struct vnode *, u_quad_t, daddr_t, long,
struct ucred *, long, int, struct buf **);
@ -527,6 +533,9 @@ void reassignbuf(struct buf *);
struct buf *trypbuf(int *);
void bwait(struct buf *, u_char, const char *);
void bdone(struct buf *);
void bpin(struct buf *);
void bunpin(struct buf *);
void bunpin_wait(struct buf *);
#endif /* _KERNEL */