[PATCH] NFS: Fix races in nfs_revalidate_mapping()
Prevent the call to invalidate_inode_pages2() from racing with file writes by taking the inode->i_mutex across the page cache flush and invalidate. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									bde8f00ce6
								
							
						
					
					
						commit
						717d44e849
					
				| @ -532,7 +532,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||||
| 
 | ||||
| 	lock_kernel(); | ||||
| 
 | ||||
| 	res = nfs_revalidate_mapping(inode, filp->f_mapping); | ||||
| 	res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping); | ||||
| 	if (res < 0) { | ||||
| 		unlock_kernel(); | ||||
| 		return res; | ||||
|  | ||||
| @ -665,49 +665,86 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) | ||||
| 	return __nfs_revalidate_inode(server, inode); | ||||
| } | ||||
| 
 | ||||
| static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) | ||||
| { | ||||
| 	struct nfs_inode *nfsi = NFS_I(inode); | ||||
| 	 | ||||
| 	if (mapping->nrpages != 0) { | ||||
| 		int ret = invalidate_inode_pages2(mapping); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	spin_lock(&inode->i_lock); | ||||
| 	nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | ||||
| 	if (S_ISDIR(inode->i_mode)) { | ||||
| 		memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | ||||
| 		/* This ensures we revalidate child dentries */ | ||||
| 		nfsi->cache_change_attribute = jiffies; | ||||
| 	} | ||||
| 	spin_unlock(&inode->i_lock); | ||||
| 	nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); | ||||
| 	dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", | ||||
| 			inode->i_sb->s_id, (long long)NFS_FILEID(inode)); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	mutex_lock(&inode->i_mutex); | ||||
| 	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_DATA) { | ||||
| 		ret = nfs_sync_mapping(mapping); | ||||
| 		if (ret == 0) | ||||
| 			ret = nfs_invalidate_mapping_nolock(inode, mapping); | ||||
| 	} | ||||
| 	mutex_unlock(&inode->i_mutex); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * nfs_revalidate_mapping_nolock - Revalidate the pagecache | ||||
|  * @inode - pointer to host inode | ||||
|  * @mapping - pointer to mapping | ||||
|  */ | ||||
| int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping) | ||||
| { | ||||
| 	struct nfs_inode *nfsi = NFS_I(inode); | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) | ||||
| 			|| nfs_attribute_timeout(inode) || NFS_STALE(inode)) { | ||||
| 		ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); | ||||
| 		if (ret < 0) | ||||
| 			goto out; | ||||
| 	} | ||||
| 	if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||||
| 		ret = nfs_invalidate_mapping_nolock(inode, mapping); | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * nfs_revalidate_mapping - Revalidate the pagecache | ||||
|  * @inode - pointer to host inode | ||||
|  * @mapping - pointer to mapping | ||||
|  * | ||||
|  * This version of the function will take the inode->i_mutex and attempt to | ||||
|  * flush out all dirty data if it needs to invalidate the page cache. | ||||
|  */ | ||||
| int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) | ||||
| { | ||||
| 	struct nfs_inode *nfsi = NFS_I(inode); | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (NFS_STALE(inode)) | ||||
| 		ret = -ESTALE; | ||||
| 	if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) | ||||
| 			|| nfs_attribute_timeout(inode)) | ||||
| 			|| nfs_attribute_timeout(inode) || NFS_STALE(inode)) { | ||||
| 		ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { | ||||
| 		if (mapping->nrpages != 0) { | ||||
| 			if (S_ISREG(inode->i_mode)) { | ||||
| 				ret = nfs_sync_mapping(mapping); | ||||
| 				if (ret < 0) | ||||
| 					goto out; | ||||
| 			} | ||||
| 			ret = invalidate_inode_pages2(mapping); | ||||
| 			if (ret < 0) | ||||
| 				goto out; | ||||
| 		} | ||||
| 		spin_lock(&inode->i_lock); | ||||
| 		nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; | ||||
| 		if (S_ISDIR(inode->i_mode)) { | ||||
| 			memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); | ||||
| 			/* This ensures we revalidate child dentries */ | ||||
| 			nfsi->cache_change_attribute = jiffies; | ||||
| 		} | ||||
| 		spin_unlock(&inode->i_lock); | ||||
| 
 | ||||
| 		nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); | ||||
| 		dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", | ||||
| 				inode->i_sb->s_id, | ||||
| 				(long long)NFS_FILEID(inode)); | ||||
| 		if (ret < 0) | ||||
| 			goto out; | ||||
| 	} | ||||
| 	if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||||
| 		ret = nfs_invalidate_mapping(inode, mapping); | ||||
| out: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @ -50,7 +50,9 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
| 	struct page *page; | ||||
| 	void *err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); | ||||
| 	void *err; | ||||
| 
 | ||||
| 	err = ERR_PTR(nfs_revalidate_mapping_nolock(inode, inode->i_mapping)); | ||||
| 	if (err) | ||||
| 		goto read_failed; | ||||
| 	page = read_cache_page(&inode->i_data, 0, | ||||
|  | ||||
| @ -308,6 +308,7 @@ extern int nfs_attribute_timeout(struct inode *inode); | ||||
| extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode); | ||||
| extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); | ||||
| extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); | ||||
| extern int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping); | ||||
| extern int nfs_setattr(struct dentry *, struct iattr *); | ||||
| extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); | ||||
| extern void nfs_begin_attr_update(struct inode *); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user