- Handle the truncation of an inode with an effective link count of 0 in

the context of the process that reduced the effective count.  Previously
   all truncation as a result of unlink happened in the softdep flush
   thread.  This had the effect of being impossible to rate limit properly
   with the journal code.  Now the process issuing unlinks is suspended
   when the journal files.  This has a side-effect of improving rm
   performance by allowing more concurrent work.
 - Handle two cases in inactive, one for effnlink == 0 and another when
   nlink finally reaches 0.
 - Eliminate the SPACECOUNTED related code since the truncation is no
   longer delayed.

Discussed with:	mckusick
This commit is contained in:
Jeff Roberson 2010-07-06 07:11:04 +00:00
parent 24d37c1eec
commit 9f9c8c59ae
9 changed files with 34 additions and 128 deletions

View File

@ -191,11 +191,6 @@ ffs_alloc(ip, lbn, bpref, size, flags, cred, bnp)
bno = ffs_hashalloc(ip, cg, bpref, size, size, ffs_alloccg);
if (bno > 0) {
delta = btodb(size);
if (ip->i_flag & IN_SPACECOUNTED) {
UFS_LOCK(ump);
fs->fs_pendingblocks += delta;
UFS_UNLOCK(ump);
}
DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + delta);
if (flags & IO_EXT)
ip->i_flag |= IN_CHANGE;
@ -321,11 +316,6 @@ ffs_realloccg(ip, lbprev, bprev, bpref, osize, nsize, flags, cred, bpp)
if (bp->b_blkno != fsbtodb(fs, bno))
panic("ffs_realloccg: bad blockno");
delta = btodb(nsize - osize);
if (ip->i_flag & IN_SPACECOUNTED) {
UFS_LOCK(ump);
fs->fs_pendingblocks += delta;
UFS_UNLOCK(ump);
}
DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + delta);
if (flags & IO_EXT)
ip->i_flag |= IN_CHANGE;
@ -394,11 +384,6 @@ ffs_realloccg(ip, lbprev, bprev, bpref, osize, nsize, flags, cred, bpp)
ffs_blkfree(ump, fs, ip->i_devvp, bprev, (long)osize,
ip->i_number, NULL);
delta = btodb(nsize - osize);
if (ip->i_flag & IN_SPACECOUNTED) {
UFS_LOCK(ump);
fs->fs_pendingblocks += delta;
UFS_UNLOCK(ump);
}
DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + delta);
if (flags & IO_EXT)
ip->i_flag |= IN_CHANGE;
@ -2422,11 +2407,6 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS)
if ((error = ffs_vget(mp, (ino_t)cmd.value, LK_EXCLUSIVE, &vp)))
break;
ip = VTOI(vp);
if (ip->i_flag & IN_SPACECOUNTED) {
UFS_LOCK(ump);
fs->fs_pendingblocks += cmd.size;
UFS_UNLOCK(ump);
}
DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + cmd.size);
ip->i_flag |= IN_CHANGE;
vput(vp);

View File

@ -180,6 +180,8 @@ ffs_truncate(vp, length, flags, cred, td)
*/
if ((flags & (IO_EXT | IO_NORMAL)) == 0)
flags |= IO_NORMAL;
if (!DOINGSOFTDEP(vp) && !DOINGASYNC(vp))
flags |= IO_SYNC;
/*
* If we are truncating the extended-attributes, and cannot
* do it with soft updates, then do it slowly here. If we are
@ -310,10 +312,6 @@ ffs_truncate(vp, length, flags, cred, td)
*/
if ((error = ffs_syncvnode(vp, MNT_WAIT)) != 0)
goto out;
UFS_LOCK(ump);
if (ip->i_flag & IN_SPACECOUNTED)
fs->fs_pendingblocks -= datablocks;
UFS_UNLOCK(ump);
/*
* We have to journal the truncation before we change
* any blocks so we don't leave the file partially

View File

@ -5183,16 +5183,9 @@ softdep_setup_freeblocks(ip, length, flags)
fs->fs_frag, needj);
lbn += tmpval;
}
/*
* If the file was removed, then the space being freed was
* accounted for then (see softdep_releasefile()). If the
* file is merely being truncated, then we account for it now.
*/
if ((ip->i_flag & IN_SPACECOUNTED) == 0) {
UFS_LOCK(ip->i_ump);
fs->fs_pendingblocks += datablocks;
UFS_UNLOCK(ip->i_ump);
}
UFS_LOCK(ip->i_ump);
fs->fs_pendingblocks += datablocks;
UFS_UNLOCK(ip->i_ump);
}
if ((flags & IO_EXT) != 0) {
oldextsize = ip->i_din2->di_extsize;
@ -5674,11 +5667,9 @@ softdep_freefile(pvp, ino, mode)
freefile->fx_oldinum = ino;
freefile->fx_devvp = ip->i_devvp;
LIST_INIT(&freefile->fx_jwork);
if ((ip->i_flag & IN_SPACECOUNTED) == 0) {
UFS_LOCK(ip->i_ump);
ip->i_fs->fs_pendinginodes += 1;
UFS_UNLOCK(ip->i_ump);
}
UFS_LOCK(ip->i_ump);
ip->i_fs->fs_pendinginodes += 1;
UFS_UNLOCK(ip->i_ump);
/*
* If the inodedep does not exist, then the zero'ed inode has
@ -7378,55 +7369,6 @@ softdep_change_linkcnt(ip)
FREE_LOCK(&lk);
}
/*
* Called when the effective link count and the reference count
* on an inode drops to zero. At this point there are no names
* referencing the file in the filesystem and no active file
* references. The space associated with the file will be freed
* as soon as the necessary soft dependencies are cleared.
*/
void
softdep_releasefile(ip)
struct inode *ip; /* inode with the zero effective link count */
{
struct inodedep *inodedep;
struct fs *fs;
int extblocks;
if (ip->i_effnlink > 0)
panic("softdep_releasefile: file still referenced");
/*
* We may be called several times as the on-disk link count
* drops to zero. We only want to account for the space once.
*/
if (ip->i_flag & IN_SPACECOUNTED)
return;
/*
* We have to deactivate a snapshot otherwise copyonwrites may
* add blocks and the cleanup may remove blocks after we have
* tried to account for them.
*/
if ((ip->i_flags & SF_SNAPSHOT) != 0)
ffs_snapremove(ITOV(ip));
/*
* If we are tracking an nlinkdelta, we have to also remember
* whether we accounted for the freed space yet.
*/
ACQUIRE_LOCK(&lk);
if ((inodedep_lookup(UFSTOVFS(ip->i_ump), ip->i_number, 0, &inodedep)))
inodedep->id_state |= SPACECOUNTED;
FREE_LOCK(&lk);
fs = ip->i_fs;
extblocks = 0;
if (fs->fs_magic == FS_UFS2_MAGIC)
extblocks = btodb(fragroundup(fs, ip->i_din2->di_extsize));
UFS_LOCK(ip->i_ump);
ip->i_fs->fs_pendingblocks += DIP(ip, i_blocks) - extblocks;
ip->i_fs->fs_pendinginodes += 1;
UFS_UNLOCK(ip->i_ump);
ip->i_flag |= IN_SPACECOUNTED;
}
/*
* Attach a sbdep dependency to the superblock buf so that we can keep
* track of the head of the linked list of referenced but unlinked inodes.
@ -7735,7 +7677,6 @@ handle_workitem_remove(dirrem, xp)
struct dirrem *dirrem;
struct vnode *xp;
{
struct thread *td = curthread;
struct inodedep *inodedep;
struct workhead dotdotwk;
struct worklist *wk;
@ -7808,9 +7749,8 @@ handle_workitem_remove(dirrem, xp)
/*
* Directory deletion. Decrement reference count for both the
* just deleted parent directory entry and the reference for ".".
* Next truncate the directory to length zero. When the
* truncation completes, arrange to have the reference count on
* the parent decremented to account for the loss of "..".
* Arrange to have the reference count on the parent decremented
* to account for the loss of "..".
*/
ip->i_nlink -= 2;
DIP_SET(ip, i_nlink, ip->i_nlink);
@ -7820,10 +7760,6 @@ handle_workitem_remove(dirrem, xp)
if (ip->i_nlink == 0)
unlinked_inodedep(mp, inodedep);
inodedep->id_nlinkdelta = ip->i_nlink - ip->i_effnlink;
FREE_LOCK(&lk);
if ((error = ffs_truncate(vp, (off_t)0, 0, td->td_ucred, td)) != 0)
softdep_error("handle_workitem_remove: truncate", error);
ACQUIRE_LOCK(&lk);
/*
* Rename a directory to a new parent. Since, we are both deleting
* and creating a new directory entry, the link count on the new
@ -9899,8 +9835,6 @@ softdep_load_inodeblock(ip)
return;
}
ip->i_effnlink -= inodedep->id_nlinkdelta;
if (inodedep->id_state & SPACECOUNTED)
ip->i_flag |= IN_SPACECOUNTED;
FREE_LOCK(&lk);
}

View File

@ -1036,9 +1036,6 @@ ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *ucred)
fs = ip->i_fs;
dp = ip->i_din2;
KASSERT(!(ip->i_flag & IN_SPACECOUNTED), ("inode %u: inode is dead",
ip->i_number));
#ifdef INVARIANTS
if (uio->uio_rw != UIO_WRITE || fs->fs_magic != FS_UFS2_MAGIC)
panic("ffs_extwrite: mode");

View File

@ -118,7 +118,7 @@
#define DIRCHG 0x000080 /* diradd, dirrem only */
#define GOINGAWAY 0x000100 /* indirdep, jremref only */
#define IOSTARTED 0x000200 /* inodedep, pagedep, bmsafemap only */
#define SPACECOUNTED 0x000400 /* inodedep only */
#define UNUSED400 0x000400 /* currently available. */
#define NEWBLOCK 0x000800 /* pagedep, jaddref only */
#define INPROGRESS 0x001000 /* dirrem, freeblks, freefrag, freefile only */
#define UFS1FMT 0x002000 /* indirdep only */

View File

@ -122,7 +122,6 @@ struct inode {
#define IN_MODIFIED 0x0008 /* Inode has been modified. */
#define IN_NEEDSYNC 0x0010 /* Inode requires fsync. */
#define IN_LAZYMOD 0x0040 /* Modified, but don't write yet. */
#define IN_SPACECOUNTED 0x0080 /* Blocks to be freed in free count. */
#define IN_LAZYACCESS 0x0100 /* Process IN_ACCESS after the
suspension finished */
#define IN_EA_LOCKED 0x0200

View File

@ -76,6 +76,7 @@ ufs_inactive(ap)
struct thread *td = ap->a_td;
mode_t mode;
int error = 0;
int isize;
struct mount *mp;
mp = NULL;
@ -118,18 +119,21 @@ ufs_inactive(ap)
}
}
}
if (ip->i_effnlink == 0 && DOINGSOFTDEP(vp))
softdep_releasefile(ip);
if (ip->i_nlink <= 0 && !UFS_RDONLY(ip)) {
isize = ip->i_size;
if (ip->i_ump->um_fstype == UFS2)
isize += ip->i_din2->di_extsize;
if (ip->i_effnlink <= 0 && isize && !UFS_RDONLY(ip)) {
#ifdef QUOTA
if (!getinoquota(ip))
(void)chkiq(ip, -1, NOCRED, FORCE);
#endif
error = UFS_TRUNCATE(vp, (off_t)0, IO_EXT | IO_NORMAL,
NOCRED, td);
}
if (ip->i_nlink <= 0 && ip->i_mode && !UFS_RDONLY(ip)) {
#ifdef UFS_EXTATTR
ufs_extattr_vnode_inactive(vp, td);
#endif
error = UFS_TRUNCATE(vp, (off_t)0, IO_EXT | IO_NORMAL,
NOCRED, td);
/*
* Setting the mode to zero needs to wait for the inode
* to be written just as does a change to the link count.

View File

@ -248,6 +248,8 @@ ufs_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
*vpp = NULL;
dp = VTOI(vdp);
if (dp->i_effnlink == 0)
return (ENOENT);
/*
* Create a vm object if vmiodirenable is enabled.

View File

@ -1119,7 +1119,7 @@ ufs_rename(ap)
struct direct newdir;
off_t endoff;
int doingdirectory, newparent;
int error = 0, ioflag;
int error = 0;
struct mount *mp;
ino_t ino;
@ -1443,8 +1443,8 @@ ufs_rename(ap)
}
if (doingdirectory && !DOINGSOFTDEP(tvp)) {
/*
* Truncate inode. The only stuff left in the directory
* is "." and "..". The "." reference is inconsequential
* The only stuff left in the directory is "."
* and "..". The "." reference is inconsequential
* since we are quashing it. We have removed the "."
* reference and the reference in the parent directory,
* but there may be other hard links. The soft
@ -1461,13 +1461,6 @@ ufs_rename(ap)
tip->i_nlink--;
DIP_SET(tip, i_nlink, tip->i_nlink);
tip->i_flag |= IN_CHANGE;
ioflag = IO_NORMAL;
if (!DOINGASYNC(tvp))
ioflag |= IO_SYNC;
/* Don't go to bad here as the new link exists. */
if ((error = UFS_TRUNCATE(tvp, (off_t)0, ioflag,
tcnp->cn_cred, tcnp->cn_thread)) != 0)
goto unlockout;
}
}
@ -1993,7 +1986,7 @@ ufs_rmdir(ap)
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct inode *ip, *dp;
int error, ioflag;
int error;
ip = VTOI(vp);
dp = VTOI(dvp);
@ -2049,10 +2042,10 @@ ufs_rmdir(ap)
}
cache_purge(dvp);
/*
* Truncate inode. The only stuff left in the directory is "." and
* "..". The "." reference is inconsequential since we are quashing
* it. The soft dependency code will arrange to do these operations
* after the parent directory entry has been deleted on disk, so
* The only stuff left in the directory is "." and "..". The "."
* reference is inconsequential since we are quashing it. The soft
* dependency code will arrange to do these operations after
* the parent directory entry has been deleted on disk, so
* when running with that code we avoid doing them now.
*/
if (!DOINGSOFTDEP(vp)) {
@ -2062,11 +2055,6 @@ ufs_rmdir(ap)
ip->i_nlink--;
DIP_SET(ip, i_nlink, ip->i_nlink);
ip->i_flag |= IN_CHANGE;
ioflag = IO_NORMAL;
if (!DOINGASYNC(vp))
ioflag |= IO_SYNC;
error = UFS_TRUNCATE(vp, (off_t)0, ioflag, cnp->cn_cred,
cnp->cn_thread);
}
cache_purge(vp);
#ifdef UFS_DIRHASH
@ -2137,6 +2125,7 @@ ufs_readdir(ap)
} */ *ap;
{
struct uio *uio = ap->a_uio;
struct inode *ip;
int error;
size_t count, lost;
off_t off;
@ -2147,6 +2136,9 @@ ufs_readdir(ap)
* the cookies to determine where in the block to start.
*/
uio->uio_offset &= ~(DIRBLKSIZ - 1);
ip = VTOI(ap->a_vp);
if (ip->i_effnlink == 0)
return (0);
off = uio->uio_offset;
count = uio->uio_resid;
/* Make sure we don't return partial entries. */