diff options
author | mckusick <mckusick@FreeBSD.org> | 2003-01-07 18:23:50 +0000 |
---|---|---|
committer | mckusick <mckusick@FreeBSD.org> | 2003-01-07 18:23:50 +0000 |
commit | db74e87c2dcb58da63b72f52910212ace4c8406f (patch) | |
tree | 06b118b7f3c3d10725186e047096ac48d7d7c9e8 /sys/ufs | |
parent | 09a741fae61bb411204d6d533ec4670c5f416c37 (diff) | |
download | FreeBSD-src-db74e87c2dcb58da63b72f52910212ace4c8406f.zip FreeBSD-src-db74e87c2dcb58da63b72f52910212ace4c8406f.tar.gz |
This patch fixes a problem caused by applications that rapidly and
repeatedly truncate the same file. Each time the file is truncated,
a buffer is grabbed to store the indirect block numbers that need
to be freed. Those blocks cannot be freed until the inode claiming
them is written to disk. Thus, the number of buffers being held by
soft updates explodes and in extreme cases can run the kernel out
of buffers. The problem can be avoided by doing an fsync on the
file every debug.maxindirdep truncates (currently defaulted to 50).
The fsync causes the inode to be written so that the held buffers
can be freed. The check for excessive buffers is checked as part
of the existing hook for excessive dependencies (softdep_slowdown)
in the truncate code.
Reported by: David Schultz <dschultz@uclink.Berkeley.EDU>
Sponsored by: DARPA & NAI Labs.
MFC after: 3 weeks
Diffstat (limited to 'sys/ufs')
-rw-r--r-- | sys/ufs/ffs/ffs_softdep.c | 17 | ||||
-rw-r--r-- | sys/ufs/ufs/ufsmount.h | 1 |
2 files changed, 13 insertions, 5 deletions
diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 78383d6..872433d 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -499,6 +499,7 @@ static int num_on_worklist; /* number of worklist items to be processed */ 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 */ +static int maxindirdeps = 50; /* max number of indirdeps before slowdown */ static int tickdelay = 2; /* number of ticks to pause during slowdown */ static int proc_waiting; /* tracks whether we have a timeout posted */ static int *stat_countp; /* statistic to count in proc_waiting timeout */ @@ -527,6 +528,7 @@ static int stat_dir_entry; /* bufs redirtied as dir entry cannot write */ #include <sys/sysctl.h> SYSCTL_INT(_debug, OID_AUTO, max_softdeps, CTLFLAG_RW, &max_softdeps, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tickdelay, CTLFLAG_RW, &tickdelay, 0, ""); +SYSCTL_INT(_debug, OID_AUTO, maxindirdeps, CTLFLAG_RW, &maxindirdeps, 0, ""); SYSCTL_INT(_debug, OID_AUTO, worklist_push, CTLFLAG_RW, &stat_worklist_push, 0,""); SYSCTL_INT(_debug, OID_AUTO, blk_limit_push, CTLFLAG_RW, &stat_blk_limit_push, 0,""); SYSCTL_INT(_debug, OID_AUTO, ino_limit_push, CTLFLAG_RW, &stat_ino_limit_push, 0,""); @@ -1862,8 +1864,7 @@ setup_allocindir_phase2(bp, ip, aip) handle_workitem_freefrag(freefrag); } if (newindirdep) { - if (indirdep->ir_savebp != NULL) - brelse(newindirdep->ir_savebp); + brelse(newindirdep->ir_savebp); WORKITEM_FREE((caddr_t)newindirdep, D_INDIRDEP); } if (indirdep) @@ -2125,6 +2126,7 @@ deallocate_dependencies(bp, inodedep) panic("deallocate_dependencies: already gone"); } indirdep->ir_state |= GOINGAWAY; + VFSTOUFS(bp->b_vp->v_mount)->um_numindirdeps += 1; while ((aip = LIST_FIRST(&indirdep->ir_deplisthd)) != 0) free_allocindir(aip, inodedep); if (bp->b_lblkno >= 0 || @@ -2564,6 +2566,7 @@ indir_trunc(freeblks, dbn, level, lbn, countp) FREE_LOCK(&lk); panic("indir_trunc: dangling dep"); } + VFSTOUFS(freeblks->fb_mnt)->um_numindirdeps -= 1; FREE_LOCK(&lk); } else { FREE_LOCK(&lk); @@ -3454,7 +3457,8 @@ softdep_disk_io_initiation(bp) * dependency can be freed. */ if (LIST_FIRST(&indirdep->ir_deplisthd) == NULL) { - indirdep->ir_savebp->b_flags |= B_INVAL | B_NOCACHE; + indirdep->ir_savebp->b_flags |= + B_INVAL | B_NOCACHE; brelse(indirdep->ir_savebp); /* inline expand WORKLIST_REMOVE(wk); */ wk->wk_state &= ~ONWORKLIST; @@ -5410,8 +5414,11 @@ softdep_slowdown(vp) max_softdeps_hard = max_softdeps * 11 / 10; if (num_dirrem < max_softdeps_hard / 2 && - num_inodedep < max_softdeps_hard) - return (0); + num_inodedep < max_softdeps_hard && + VFSTOUFS(vp->v_mount)->um_numindirdeps < maxindirdeps) + return (0); + if (VFSTOUFS(vp->v_mount)->um_numindirdeps >= maxindirdeps) + speedup_syncer(); stat_sync_limit_hit += 1; return (1); } diff --git a/sys/ufs/ufs/ufsmount.h b/sys/ufs/ufs/ufsmount.h index 30391fd..2b9cd44 100644 --- a/sys/ufs/ufs/ufsmount.h +++ b/sys/ufs/ufs/ufsmount.h @@ -73,6 +73,7 @@ struct ufsmount { u_long um_nindir; /* indirect ptrs per block */ u_long um_bptrtodb; /* indir ptr to disk block */ u_long um_seqinc; /* inc between seq blocks */ + long um_numindirdeps; /* indirdeps for this filesys */ time_t um_btime[MAXQUOTAS]; /* block quota time limit */ time_t um_itime[MAXQUOTAS]; /* inode quota time limit */ char um_qflags[MAXQUOTAS]; /* quota specific flags */ |