Skip to content

Commit

Permalink
NFS: Fix a spinlock recursion inside nfs_update_inode()
Browse files Browse the repository at this point in the history
 In cases where the server has gone insane, nfs_update_inode() may end
 up calling nfs_invalidate_inode(), which again calls stuff that takes
 the inode->i_lock that we're already holding.

 In addition, given the sort of things we have in NFS these days that
 need to be cleaned up on inode release, I'm not sure we should ever
 be calling make_bad_inode().

 Fix up spinlock recursion, and limit nfs_invalidate_inode() to clearing
 the caches, and marking the inode as being stale.

 Thanks to Steve Dickson <[email protected]> for spotting this.

 Signed-off-by: Trond Myklebust <[email protected]>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Nov 25, 2005
1 parent ff60406 commit b37b03b
Showing 1 changed file with 12 additions and 14 deletions.
26 changes: 12 additions & 14 deletions fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,14 +643,11 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
/*
* Invalidate the local caches
*/
void
nfs_zap_caches(struct inode *inode)
static void nfs_zap_caches_locked(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
int mode = inode->i_mode;

spin_lock(&inode->i_lock);

NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies;

Expand All @@ -659,7 +656,12 @@ nfs_zap_caches(struct inode *inode)
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
else
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
}

void nfs_zap_caches(struct inode *inode)
{
spin_lock(&inode->i_lock);
nfs_zap_caches_locked(inode);
spin_unlock(&inode->i_lock);
}

Expand All @@ -676,16 +678,13 @@ static void nfs_zap_acl_cache(struct inode *inode)
}

/*
* Invalidate, but do not unhash, the inode
* Invalidate, but do not unhash, the inode.
* NB: must be called with inode->i_lock held!
*/
static void
nfs_invalidate_inode(struct inode *inode)
static void nfs_invalidate_inode(struct inode *inode)
{
umode_t save_mode = inode->i_mode;

make_bad_inode(inode);
inode->i_mode = save_mode;
nfs_zap_caches(inode);
set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
nfs_zap_caches_locked(inode);
}

struct nfs_find_desc {
Expand Down Expand Up @@ -1528,14 +1527,13 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
__FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
#endif
out_err:
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
* (But we fall through to invalidate the caches.)
*/
nfs_invalidate_inode(inode);
out_err:
set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
return -ESTALE;
}

Expand Down

0 comments on commit b37b03b

Please sign in to comment.