diff options
author | mckusick <mckusick@FreeBSD.org> | 2002-01-22 06:17:22 +0000 |
---|---|---|
committer | mckusick <mckusick@FreeBSD.org> | 2002-01-22 06:17:22 +0000 |
commit | 4f050b1765cbe6b5989b5ccee6a3d46a479bfd1a (patch) | |
tree | 977b2b5cd36d61bc3124ea18d905cfc36e0f690d | |
parent | ed13300966ae65f0dbd7f5e77b4883370c8a415d (diff) | |
download | FreeBSD-src-4f050b1765cbe6b5989b5ccee6a3d46a479bfd1a.zip FreeBSD-src-4f050b1765cbe6b5989b5ccee6a3d46a479bfd1a.tar.gz |
This patch fixes a long standing complaint with soft updates in
which small and/or nearly full filesystems would fail with `file
system full' messages when trying to replace a number of existing
files (for example during a system installation). When the allocation
routines are about to fail with a file system full condition, they
make a call to softdep_request_cleanup() which attempts to accelerate
the flushing of pending deletion requests in an effort to free up
space. In the face of filesystem I/O requests that exceed the
available disk transfer capacity, the cleanup request could take
an unbounded amount of time. Thus, the softdep_request_cleanup()
routine will only try for tickdelay seconds (default 2 seconds)
before giving up and returning a filesystem full error. Under typical
conditions, the softdep_request_cleanup() routine is able to free
up space in under fifty milliseconds.
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 28 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_extern.h | 1 | ||||
-rw-r--r-- | sys/ufs/ffs/ffs_softdep.c | 45 |
3 files changed, 64 insertions, 10 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 321bf5a..aa6f31c 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -107,7 +107,7 @@ ffs_alloc(ip, lbn, bpref, size, cred, bnp) { register struct fs *fs; ufs_daddr_t bno; - int cg; + int cg, reclaimed; #ifdef QUOTA int error; #endif @@ -124,6 +124,8 @@ ffs_alloc(ip, lbn, bpref, size, cred, bnp) if (cred == NOCRED) panic("ffs_alloc: missing credential"); #endif /* DIAGNOSTIC */ + reclaimed = 0; +retry: if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) goto nospace; if (suser_xxx(cred, NULL, PRISON_ROOT) && @@ -155,6 +157,11 @@ ffs_alloc(ip, lbn, bpref, size, cred, bnp) (void) chkdq(ip, (long)-btodb(size), cred, FORCE); #endif nospace: + if (fs->fs_pendingblocks > 0 && reclaimed == 0) { + reclaimed = 1; + softdep_request_cleanup(fs, ITOV(ip)); + goto retry; + } ffs_fserr(fs, cred->cr_uid, "file system full"); uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); return (ENOSPC); @@ -177,15 +184,17 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp) struct ucred *cred; struct buf **bpp; { - register struct fs *fs; + struct vnode *vp; + struct fs *fs; struct buf *bp; - int cg, request, error; + int cg, request, error, reclaimed; ufs_daddr_t bprev, bno; *bpp = 0; + vp = ITOV(ip); fs = ip->i_fs; #ifdef DIAGNOSTIC - if (ITOV(ip)->v_mount->mnt_kern_flag & MNTK_SUSPENDED) + if (vp->v_mount->mnt_kern_flag & MNTK_SUSPENDED) panic("ffs_realloccg: allocation on suspended filesystem"); if ((u_int)osize > fs->fs_bsize || fragoff(fs, osize) != 0 || (u_int)nsize > fs->fs_bsize || fragoff(fs, nsize) != 0) { @@ -198,6 +207,8 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp) if (cred == NOCRED) panic("ffs_realloccg: missing credential"); #endif /* DIAGNOSTIC */ + reclaimed = 0; +retry: if (suser_xxx(cred, NULL, PRISON_ROOT) && freespace(fs, fs->fs_minfree) - numfrags(fs, nsize - osize) < 0) goto nospace; @@ -210,7 +221,7 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp) /* * Allocate the extra space in the buffer. */ - error = bread(ITOV(ip), lbprev, osize, NOCRED, &bp); + error = bread(vp, lbprev, osize, NOCRED, &bp); if (error) { brelse(bp); return (error); @@ -297,7 +308,7 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp) ffs_alloccg); if (bno > 0) { bp->b_blkno = fsbtodb(fs, bno); - if (!DOINGSOFTDEP(ITOV(ip))) + if (!DOINGSOFTDEP(vp)) ffs_blkfree(ip, bprev, (long)osize); if (nsize < request) ffs_blkfree(ip, bno + numfrags(fs, nsize), @@ -321,6 +332,11 @@ nospace: /* * no space available */ + if (fs->fs_pendingblocks > 0 && reclaimed == 0) { + reclaimed = 1; + softdep_request_cleanup(fs, vp); + goto retry; + } ffs_fserr(fs, cred->cr_uid, "file system full"); uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); return (ENOSPC); diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index 49f0279..b70f7f7 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -109,6 +109,7 @@ int softdep_flushfiles __P((struct mount *, int, struct thread *)); void softdep_update_inodeblock __P((struct inode *, struct buf *, int)); void softdep_load_inodeblock __P((struct inode *)); void softdep_freefile __P((struct vnode *, ino_t, int)); +int softdep_request_cleanup __P((struct fs *, struct vnode *)); void softdep_setup_freeblocks __P((struct inode *, off_t)); void softdep_setup_inomapdep __P((struct buf *, struct inode *, ino_t)); void softdep_setup_blkmapdep __P((struct buf *, struct fs *, ufs_daddr_t)); diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 8316254..913d5a1 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -503,9 +503,10 @@ static int *stat_countp; /* statistic to count in proc_waiting timeout */ static struct callout_handle handle; /* handle on posted proc_waiting timeout */ static struct thread *filesys_syncer; /* proc of filesystem syncer process */ static int req_clear_inodedeps; /* syncer process flush some inodedeps */ -#define FLUSH_INODES 1 +#define FLUSH_INODES 1 static int req_clear_remove; /* syncer process flush some freeblks */ -#define FLUSH_REMOVE 2 +#define FLUSH_REMOVE 2 +#define FLUSH_REMOVE_WAIT 3 /* * runtime statistics */ @@ -695,7 +696,7 @@ process_worklist_item(matchmnt, flags) } if (wk == 0) { FREE_LOCK(&lk); - return (0); + return (-1); } WORKLIST_REMOVE(wk); num_on_worklist -= 1; @@ -4825,6 +4826,41 @@ softdep_slowdown(vp) } /* + * Called by the allocation routines when they are about to fail + * in the hope that we can free up some disk space. + * + * First check to see if the work list has anything on it. If it has, + * clean up entries until we successfully free some space. Because this + * process holds inodes locked, we cannot handle any remove requests + * that might block on a locked inode as that could lead to deadlock. + * If the worklist yields no free space, encourage the syncer daemon + * to help us. In no event will we try for longer than tickdelay seconds. + */ +int +softdep_request_cleanup(fs, vp) + struct fs *fs; + struct vnode *vp; +{ + long starttime, needed; + + needed = fs->fs_cstotal.cs_nbfree + fs->fs_contigsumsize; + starttime = time_second + tickdelay; + if (UFS_UPDATE(vp, 1) != 0) + return (0); + while (fs->fs_pendingblocks > 0 && fs->fs_cstotal.cs_nbfree <= needed) { + if (time_second > starttime) + return (0); + if (num_on_worklist > 0 && + process_worklist_item(NULL, LK_NOWAIT) != -1) { + stat_worklist_push += 1; + continue; + } + request_cleanup(FLUSH_REMOVE_WAIT, 0); + } + return (1); +} + +/* * If memory utilization has gotten too high, deliberately slow things * down and speed up the I/O processing. */ @@ -4861,7 +4897,7 @@ request_cleanup(resource, islocked) * Next, we attempt to speed up the syncer process. If that * is successful, then we allow the process to continue. */ - if (speedup_syncer()) + if (speedup_syncer() && resource != FLUSH_REMOVE_WAIT) return(0); /* * If we are resource constrained on inode dependencies, try @@ -4882,6 +4918,7 @@ request_cleanup(resource, islocked) break; case FLUSH_REMOVE: + case FLUSH_REMOVE_WAIT: stat_blk_limit_push += 1; req_clear_remove += 1; stat_countp = &stat_blk_limit_hit; |