瀏覽代碼

NFS: Fix a spinlock recursion inside nfs_update_inode()

 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 <SteveD@redhat.com> for spotting this.

 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Trond Myklebust 19 年之前
父節點
當前提交
b37b03b705
共有 1 個文件被更改,包括 12 次插入14 次删除
  1. 12 14
      fs/nfs/inode.c

+ 12 - 14
fs/nfs/inode.c

@@ -643,14 +643,11 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
 /*
 /*
  * Invalidate the local caches
  * 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);
 	struct nfs_inode *nfsi = NFS_I(inode);
 	int mode = inode->i_mode;
 	int mode = inode->i_mode;
 
 
-	spin_lock(&inode->i_lock);
-
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 
 
@@ -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;
 		nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
 	else
 	else
 		nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
 		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);
 	spin_unlock(&inode->i_lock);
 }
 }
 
 
@@ -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 {
 struct nfs_find_desc {
@@ -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",
 	printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
 			__FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
 			__FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
 #endif
 #endif
+ out_err:
 	/*
 	/*
 	 * No need to worry about unhashing the dentry, as the
 	 * No need to worry about unhashing the dentry, as the
 	 * lookup validation will know that the inode is bad.
 	 * lookup validation will know that the inode is bad.
 	 * (But we fall through to invalidate the caches.)
 	 * (But we fall through to invalidate the caches.)
 	 */
 	 */
 	nfs_invalidate_inode(inode);
 	nfs_invalidate_inode(inode);
- out_err:
-	set_bit(NFS_INO_STALE, &NFS_FLAGS(inode));
 	return -ESTALE;
 	return -ESTALE;
 }
 }