diff options
-rw-r--r-- | sys/kern/vfs_bio.c | 59 | ||||
-rw-r--r-- | sys/kern/vfs_subr.c | 6 | ||||
-rw-r--r-- | sys/sys/vnode.h | 2 |
3 files changed, 64 insertions, 3 deletions
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index d7f6d2f..2e831e8 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -124,6 +124,12 @@ SYSCTL_INT(_vfs, OID_AUTO, lorunningspace, CTLFLAG_RW, &lorunningspace, 0, static int hirunningspace; SYSCTL_INT(_vfs, OID_AUTO, hirunningspace, CTLFLAG_RW, &hirunningspace, 0, "Maximum amount of space to use for in-progress I/O"); +static int dirtybufferflushes; +SYSCTL_INT(_vfs, OID_AUTO, dirtybufferflushes, CTLFLAG_RW, &dirtybufferflushes, + 0, "Number of bdwrite to bawrite conversions to limit dirty buffers"); +static int altbufferflushes; +SYSCTL_INT(_vfs, OID_AUTO, altbufferflushes, CTLFLAG_RW, &altbufferflushes, + 0, "Number of fsync flushes to limit dirty buffers"); static int numdirtybuffers; SYSCTL_INT(_vfs, OID_AUTO, numdirtybuffers, CTLFLAG_RD, &numdirtybuffers, 0, "Number of buffers that are dirty (has unwritten changes) at the moment"); @@ -133,6 +139,9 @@ SYSCTL_INT(_vfs, OID_AUTO, lodirtybuffers, CTLFLAG_RW, &lodirtybuffers, 0, static int hidirtybuffers; SYSCTL_INT(_vfs, OID_AUTO, hidirtybuffers, CTLFLAG_RW, &hidirtybuffers, 0, "When the number of dirty buffers is considered severe"); +static int dirtybufthresh; +SYSCTL_INT(_vfs, OID_AUTO, dirtybufthresh, CTLFLAG_RW, &dirtybufthresh, + 0, "Number of bdwrite to bawrite conversions to clear dirty buffers"); static int numfreebuffers; SYSCTL_INT(_vfs, OID_AUTO, numfreebuffers, CTLFLAG_RD, &numfreebuffers, 0, "Number of free buffers"); @@ -584,6 +593,7 @@ bufinit(void) * of delayed-write dirty buffers we allow to stack up. */ hidirtybuffers = nbuf / 4 + 20; + dirtybufthresh = hidirtybuffers * 9 / 10; numdirtybuffers = 0; /* * To support extreme low-memory systems, make sure hidirtybuffers cannot @@ -993,6 +1003,10 @@ vfs_backgroundwritedone(bp) void bdwrite(struct buf * bp) { + struct thread *td = curthread; + struct vnode *vp; + struct buf *nbp; + GIANT_REQUIRED; if (BUF_REFCNT(bp) == 0) @@ -1002,9 +1016,48 @@ bdwrite(struct buf * bp) brelse(bp); return; } - bdirty(bp); /* + * If we have too many dirty buffers, don't create any more. + * If we are wildly over our limit, then force a complete + * cleanup. Otherwise, just keep the situation from getting + * out of control. + */ + vp = bp->b_vp; + VI_LOCK(vp); + if (vp != NULL && vp->v_dirtybufcnt > dirtybufthresh + 10) { + VI_UNLOCK(vp); + (void) VOP_FSYNC(vp, td->td_ucred, MNT_NOWAIT, td); + VI_LOCK(vp); + altbufferflushes++; + } else if (vp != NULL && vp->v_dirtybufcnt > dirtybufthresh) { + /* + * Try to find a buffer to flush. + */ + TAILQ_FOREACH(nbp, &vp->v_dirtyblkhd, b_vnbufs) { + if ((nbp->b_xflags & BX_BKGRDINPROG) || + buf_countdeps(nbp, 0) || + BUF_LOCK(nbp, LK_EXCLUSIVE | LK_NOWAIT)) + continue; + if (bp == nbp) + panic("bdwrite: found ourselves"); + VI_UNLOCK(vp); + if (nbp->b_flags & B_CLUSTEROK) { + BUF_UNLOCK(nbp); + vfs_bio_awrite(nbp); + } else { + bremfree(nbp); + bawrite(nbp); + } + VI_LOCK(vp); + dirtybufferflushes++; + break; + } + } + VI_UNLOCK(vp); + + bdirty(bp); + /* * Set B_CACHE, indicating that the buffer is fully valid. This is * true even of NFS now. */ @@ -1019,8 +1072,8 @@ bdwrite(struct buf * bp) * requesting a sync -- there might not be enough memory to do * the bmap then... So, this is important to do. */ - if (bp->b_vp->v_type != VCHR && bp->b_lblkno == bp->b_blkno) { - VOP_BMAP(bp->b_vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL); + if (vp->v_type != VCHR && bp->b_lblkno == bp->b_blkno) { + VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL); } /* diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 8e556ab..98dd788 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -990,7 +990,9 @@ getnewvnode(tag, mp, vops, vpp) vp->v_socket = 0; lockdestroy(vp->v_vnlock); lockinit(vp->v_vnlock, PVFS, tag, VLKTIMEOUT, LK_NOPAUSE); + KASSERT(vp->v_cleanbufcnt == 0, ("cleanbufcnt not 0")); KASSERT(vp->v_cleanblkroot == NULL, ("cleanblkroot not NULL")); + KASSERT(vp->v_dirtybufcnt == 0, ("dirtybufcnt not 0")); KASSERT(vp->v_dirtyblkroot == NULL, ("dirtyblkroot not NULL")); } else { numvnodes++; @@ -1470,6 +1472,7 @@ buf_vlist_remove(struct buf *bp) } vp->v_dirtyblkroot = root; TAILQ_REMOVE(&vp->v_dirtyblkhd, bp, b_vnbufs); + vp->v_dirtybufcnt--; } else { /* KASSERT(bp->b_xflags & BX_VNCLEAN, ("bp wasn't clean")); */ if (bp != vp->v_cleanblkroot) { @@ -1484,6 +1487,7 @@ buf_vlist_remove(struct buf *bp) } vp->v_cleanblkroot = root; TAILQ_REMOVE(&vp->v_cleanblkhd, bp, b_vnbufs); + vp->v_cleanbufcnt--; } bp->b_xflags &= ~(BX_VNDIRTY | BX_VNCLEAN); } @@ -1522,6 +1526,7 @@ buf_vlist_add(struct buf *bp, struct vnode *vp, b_xflags_t xflags) TAILQ_INSERT_AFTER(&vp->v_dirtyblkhd, root, bp, b_vnbufs); } + vp->v_dirtybufcnt++; vp->v_dirtyblkroot = bp; } else { /* KASSERT(xflags & BX_VNCLEAN, ("xflags not clean")); */ @@ -1544,6 +1549,7 @@ buf_vlist_add(struct buf *bp, struct vnode *vp, b_xflags_t xflags) TAILQ_INSERT_AFTER(&vp->v_cleanblkhd, root, bp, b_vnbufs); } + vp->v_cleanbufcnt++; vp->v_cleanblkroot = bp; } } diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 497188b..2327ca2 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -108,8 +108,10 @@ struct vnode { int v_holdcnt; /* i page & buffer references */ struct buflists v_cleanblkhd; /* i SORTED clean blocklist */ struct buf *v_cleanblkroot; /* i clean buf splay tree */ + int v_cleanbufcnt; /* i number of clean buffers */ struct buflists v_dirtyblkhd; /* i SORTED dirty blocklist */ struct buf *v_dirtyblkroot; /* i dirty buf splay tree */ + int v_dirtybufcnt; /* i number of dirty buffers */ u_long v_vflag; /* v vnode flags */ int v_writecount; /* v ref count of writers */ struct vm_object *v_object; /* v Place to store VM object */ |