diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2011-04-12 19:45:44 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2011-04-12 19:45:44 +0000 |
commit | d52b7e958a1501af1aeeac4cce028cb8d56ab5a4 (patch) | |
tree | 839bc95da89f44cb47995c9e987578ee24b4b6a8 /sys/ufs | |
parent | 1f364a327df41161bdcc553fa06403fd38078b1c (diff) |
Avoid kvm starvation due to softdeps waiting on too many mapped buffers,
thus possibly consuming all of our available kva mapping buffers for
deps. Diff and analysis actually comes from Pedro Martelleto (thanks!)
tested by me and thib
ok thib@, art@
Diffstat (limited to 'sys/ufs')
-rw-r--r-- | sys/ufs/ffs/ffs_inode.c | 23 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_softdep.c | 10 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_vnops.c | 25 |
3 files changed, 50 insertions, 8 deletions
diff --git a/sys/ufs/ffs/ffs_inode.c b/sys/ufs/ffs/ffs_inode.c index afaaecbcd6a..57a691e6cf7 100644 --- a/sys/ufs/ffs/ffs_inode.c +++ b/sys/ufs/ffs/ffs_inode.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_inode.c,v 1.56 2009/01/15 07:58:36 grange Exp $ */ +/* $OpenBSD: ffs_inode.c,v 1.57 2011/04/12 19:45:43 beck Exp $ */ /* $NetBSD: ffs_inode.c,v 1.10 1996/05/11 18:27:19 mycroft Exp $ */ /* @@ -164,8 +164,10 @@ ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred) struct buf *bp; int offset, size, level; long count, nblocks, vflags, blocksreleased = 0; - int i, aflags, error, allerror; + int i, aflags, error, allerror, indirect = 0; off_t osize; + extern int num_indirdep; + extern int max_indirdep; if (length < 0) return (EINVAL); @@ -241,6 +243,8 @@ ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred) cred, aflags, &bp); if (error) return (error); + if (bp->b_lblkno >= NDADDR) + indirect = 1; DIP_ASSIGN(oip, size, length); uvm_vnp_setsize(ovp, length); (void) uvm_vnp_uncache(ovp); @@ -249,7 +253,20 @@ ffs_truncate(struct inode *oip, off_t length, int flags, struct ucred *cred) else bawrite(bp); oip->i_flag |= IN_CHANGE | IN_UPDATE; - return (UFS_UPDATE(oip, MNT_WAIT)); + error = UFS_UPDATE(oip, MNT_WAIT); + if (DOINGSOFTDEP(ovp) && num_indirdep > max_indirdep) + if (indirect) { + /* + * If the number of pending indirect block + * dependencies is sufficiently close to the + * maximum number of simultaneously mappable + * buffers force a sync on the vnode to prevent + * buffer cache exhaustion. + */ + VOP_FSYNC(ovp, curproc->p_ucred, MNT_WAIT, + curproc); + } + return (error); } uvm_vnp_setsize(ovp, length); diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 779d79c35aa..9aecd784704 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_softdep.c,v 1.102 2010/03/29 23:33:39 krw Exp $ */ +/* $OpenBSD: ffs_softdep.c,v 1.103 2011/04/12 19:45:43 beck Exp $ */ /* * Copyright 1998, 2000 Marshall Kirk McKusick. All Rights Reserved. @@ -545,6 +545,8 @@ workitem_free(item) STATIC struct workhead softdep_workitem_pending; STATIC struct worklist *worklist_tail; STATIC int num_on_worklist; /* number of worklist items to be processed */ +STATIC int num_indirdep; /* number of indirdep items to be processed */ +STATIC int max_indirdep; /* maximum number of indirdep items allowed */ STATIC int softdep_worklist_busy; /* 1 => trying to do unmount */ STATIC int softdep_worklist_req; /* serialized waiters */ STATIC int max_softdeps; /* maximum number of structs before slowdown */ @@ -1180,6 +1182,8 @@ top: void softdep_initialize() { + extern vsize_t bufkvm; + max_indirdep = (int)bufkvm / MAXPHYS * 80 / 100; bioops.io_start = softdep_disk_io_initiation; bioops.io_complete = softdep_disk_write_complete; @@ -1857,12 +1861,14 @@ setup_allocindir_phase2(bp, ip, aip) if (indirdep->ir_savebp != NULL) brelse(newindirdep->ir_savebp); WORKITEM_FREE(newindirdep, D_INDIRDEP); + num_indirdep--; } if (indirdep) break; newindirdep = pool_get(&indirdep_pool, PR_WAITOK); newindirdep->ir_list.wk_type = D_INDIRDEP; newindirdep->ir_state = ATTACHED; + num_indirdep++; if (ip->i_ump->um_fstype == UM_UFS1) newindirdep->ir_state |= UFS1FMT; LIST_INIT(&newindirdep->ir_deplisthd); @@ -2497,6 +2503,7 @@ indir_trunc(ip, dbn, level, lbn, countp) } WORKLIST_REMOVE(wk); WORKITEM_FREE(indirdep, D_INDIRDEP); + num_indirdep--; if (LIST_FIRST(&bp->b_dep) != NULL) { FREE_LOCK(&lk); panic("indir_trunc: dangling dep"); @@ -3338,6 +3345,7 @@ softdep_disk_io_initiation(bp) wk->wk_state &= ~ONWORKLIST; LIST_REMOVE(wk, wk_list); WORKITEM_FREE(indirdep, D_INDIRDEP); + num_indirdep--; FREE_LOCK(&lk); brelse(sbp); ACQUIRE_LOCK(&lk); diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index efdb9ae42ee..35ac0c9ffb5 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ffs_vnops.c,v 1.61 2011/04/05 14:14:07 thib Exp $ */ +/* $OpenBSD: ffs_vnops.c,v 1.62 2011/04/12 19:45:43 beck Exp $ */ /* $NetBSD: ffs_vnops.c,v 1.7 1996/05/11 18:27:24 mycroft Exp $ */ /* @@ -299,6 +299,7 @@ ffs_write(void *v) daddr64_t lbn; off_t osize; int blkoffset, error, extended, flags, ioflag, resid, size, xfersize; + extern int num_indirdep, max_indirdep; extended = 0; ioflag = ap->a_ioflag; @@ -372,7 +373,11 @@ ffs_write(void *v) if (uio->uio_offset + xfersize > DIP(ip, size)) { DIP_ASSIGN(ip, size, uio->uio_offset + xfersize); uvm_vnp_setsize(vp, DIP(ip, size)); - extended = 1; + /* Are we extending into an indirect block? */ + if (bp->b_lblkno < NDADDR) + extended = 1; + else + extended = 2; } (void)uvm_vnp_uncache(vp); @@ -416,8 +421,20 @@ ffs_write(void *v) uio->uio_offset -= resid - uio->uio_resid; uio->uio_resid = resid; } - } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) { - error = UFS_UPDATE(ip, MNT_WAIT); + } else if (resid > uio->uio_resid) { + if (ioflag & IO_SYNC) + error = UFS_UPDATE(ip, MNT_WAIT); + if (DOINGSOFTDEP(vp) && num_indirdep > max_indirdep) + if (extended > 1) { + /* + * If the number of pending indirect block + * dependencies is sufficiently close to the + * maximum number of simultaneously mappable + * buffers force a sync on the vnode to prevent + * buffer cache exhaustion. + */ + VOP_FSYNC(vp, p->p_ucred, MNT_WAIT, p); + } } return (error); } |