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.
This commit is contained in:
parent
ed13300966
commit
4f050b1765
@ -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 @@ ffs_realloccg(ip, lbprev, bpref, osize, nsize, cred, bpp)
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
@ -4824,6 +4825,41 @@ softdep_slowdown(vp)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
Loading…
Reference in New Issue
Block a user