diff options
Diffstat (limited to 'fs/nfs/inode.c')
| -rw-r--r-- | fs/nfs/inode.c | 119 | 
1 files changed, 86 insertions, 33 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 18ab2da4eeb6..360114ae8b82 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -164,17 +164,16 @@ static void nfs_zap_caches_locked(struct inode *inode)  	if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {  		nfs_fscache_invalidate(inode);  		nfsi->cache_validity |= NFS_INO_INVALID_ATTR -					| NFS_INO_INVALID_LABEL  					| 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_LABEL  					| NFS_INO_INVALID_ACCESS  					| NFS_INO_INVALID_ACL  					| NFS_INO_REVAL_PAGECACHE; +	nfs_zap_label_cache_locked(nfsi);  }  void nfs_zap_caches(struct inode *inode) @@ -266,6 +265,13 @@ nfs_init_locked(struct inode *inode, void *opaque)  }  #ifdef CONFIG_NFS_V4_SECURITY_LABEL +static void nfs_clear_label_invalid(struct inode *inode) +{ +	spin_lock(&inode->i_lock); +	NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_LABEL; +	spin_unlock(&inode->i_lock); +} +  void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,  					struct nfs4_label *label)  { @@ -283,6 +289,7 @@ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,  					__func__,  					(char *)label->label,  					label->len, error); +		nfs_clear_label_invalid(inode);  	}  } @@ -312,7 +319,7 @@ struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)  }  EXPORT_SYMBOL_GPL(nfs4_label_alloc);  #else -void inline nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, +void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,  					struct nfs4_label *label)  {  } @@ -458,9 +465,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st  		unlock_new_inode(inode);  	} else  		nfs_refresh_inode(inode, fattr); -	dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n", +	dprintk("NFS: nfs_fhget(%s/%Lu fh_crc=0x%08x ct=%d)\n",  		inode->i_sb->s_id, -		(long long)NFS_FILEID(inode), +		(unsigned long long)NFS_FILEID(inode),  		nfs_display_fhandle_hash(fh),  		atomic_read(&inode->i_count)); @@ -870,8 +877,8 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  	struct nfs_fattr *fattr = NULL;  	struct nfs_inode *nfsi = NFS_I(inode); -	dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", -		inode->i_sb->s_id, (long long)NFS_FILEID(inode)); +	dfprintk(PAGECACHE, "NFS: revalidating (%s/%Lu)\n", +		inode->i_sb->s_id, (unsigned long long)NFS_FILEID(inode));  	trace_nfs_revalidate_inode_enter(inode); @@ -895,9 +902,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  	status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label);  	if (status != 0) { -		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", +		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) getattr failed, error=%d\n",  			 inode->i_sb->s_id, -			 (long long)NFS_FILEID(inode), status); +			 (unsigned long long)NFS_FILEID(inode), status);  		if (status == -ESTALE) {  			nfs_zap_caches(inode);  			if (!S_ISDIR(inode->i_mode)) @@ -908,9 +915,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  	status = nfs_refresh_inode(inode, fattr);  	if (status) { -		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", +		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Lu) refresh failed, error=%d\n",  			 inode->i_sb->s_id, -			 (long long)NFS_FILEID(inode), status); +			 (unsigned long long)NFS_FILEID(inode), status);  		goto err_out;  	} @@ -919,9 +926,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)  	nfs_setsecurity(inode, fattr, label); -	dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", +	dfprintk(PAGECACHE, "NFS: (%s/%Lu) revalidation complete\n",  		inode->i_sb->s_id, -		(long long)NFS_FILEID(inode)); +		(unsigned long long)NFS_FILEID(inode));  err_out:  	nfs4_label_free(label); @@ -977,16 +984,17 @@ static int nfs_invalidate_mapping(struct inode *inode, struct address_space *map  		if (ret < 0)  			return ret;  	} -	spin_lock(&inode->i_lock); -	nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; -	if (S_ISDIR(inode->i_mode)) +	if (S_ISDIR(inode->i_mode)) { +		spin_lock(&inode->i_lock);  		memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); -	spin_unlock(&inode->i_lock); +		spin_unlock(&inode->i_lock); +	}  	nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);  	nfs_fscache_wait_on_invalidate(inode); -	dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", -			inode->i_sb->s_id, (long long)NFS_FILEID(inode)); +	dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n", +			inode->i_sb->s_id, +			(unsigned long long)NFS_FILEID(inode));  	return 0;  } @@ -1007,6 +1015,7 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode)  int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)  {  	struct nfs_inode *nfsi = NFS_I(inode); +	unsigned long *bitlock = &nfsi->flags;  	int ret = 0;  	/* swapfiles are not supposed to be shared. */ @@ -1018,12 +1027,46 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)  		if (ret < 0)  			goto out;  	} -	if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { -		trace_nfs_invalidate_mapping_enter(inode); -		ret = nfs_invalidate_mapping(inode, mapping); -		trace_nfs_invalidate_mapping_exit(inode, ret); + +	/* +	 * We must clear NFS_INO_INVALID_DATA first to ensure that +	 * invalidations that come in while we're shooting down the mappings +	 * are respected. But, that leaves a race window where one revalidator +	 * can clear the flag, and then another checks it before the mapping +	 * gets invalidated. Fix that by serializing access to this part of +	 * the function. +	 * +	 * At the same time, we need to allow other tasks to see whether we +	 * might be in the middle of invalidating the pages, so we only set +	 * the bit lock here if it looks like we're going to be doing that. +	 */ +	for (;;) { +		ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING, +				  nfs_wait_bit_killable, TASK_KILLABLE); +		if (ret) +			goto out; +		spin_lock(&inode->i_lock); +		if (test_bit(NFS_INO_INVALIDATING, bitlock)) { +			spin_unlock(&inode->i_lock); +			continue; +		} +		if (nfsi->cache_validity & NFS_INO_INVALID_DATA) +			break; +		spin_unlock(&inode->i_lock); +		goto out;  	} +	set_bit(NFS_INO_INVALIDATING, bitlock); +	smp_wmb(); +	nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; +	spin_unlock(&inode->i_lock); +	trace_nfs_invalidate_mapping_enter(inode); +	ret = nfs_invalidate_mapping(inode, mapping); +	trace_nfs_invalidate_mapping_exit(inode, ret); + +	clear_bit_unlock(NFS_INO_INVALIDATING, bitlock); +	smp_mb__after_clear_bit(); +	wake_up_bit(bitlock, NFS_INO_INVALIDATING);  out:  	return ret;  } @@ -1282,12 +1325,28 @@ static int nfs_inode_attrs_need_update(const struct inode *inode, const struct n  		((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0);  } +/* + * Don't trust the change_attribute, mtime, ctime or size if + * a pnfs LAYOUTCOMMIT is outstanding + */ +static void nfs_inode_attrs_handle_layoutcommit(struct inode *inode, +		struct nfs_fattr *fattr) +{ +	if (pnfs_layoutcommit_outstanding(inode)) +		fattr->valid &= ~(NFS_ATTR_FATTR_CHANGE | +				NFS_ATTR_FATTR_MTIME | +				NFS_ATTR_FATTR_CTIME | +				NFS_ATTR_FATTR_SIZE); +} +  static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)  {  	int ret;  	trace_nfs_refresh_inode_enter(inode); +	nfs_inode_attrs_handle_layoutcommit(inode, fattr); +  	if (nfs_inode_attrs_need_update(inode, fattr))  		ret = nfs_update_inode(inode, fattr);  	else @@ -1434,7 +1493,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  	unsigned long now = jiffies;  	unsigned long save_cache_validity; -	dfprintk(VFS, "NFS: %s(%s/%ld fh_crc=0x%08x ct=%d info=0x%x)\n", +	dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",  			__func__, inode->i_sb->s_id, inode->i_ino,  			nfs_display_fhandle_hash(NFS_FH(inode)),  			atomic_read(&inode->i_count), fattr->valid); @@ -1455,7 +1514,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  		/*  		* Big trouble! The inode has become a different object.  		*/ -		printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n", +		printk(KERN_DEBUG "NFS: %s: inode %lu mode changed, %07o to %07o\n",  				__func__, inode->i_ino, inode->i_mode, fattr->mode);  		goto out_err;  	} @@ -1517,8 +1576,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  		if (new_isize != cur_isize) {  			/* Do we perhaps have any outstanding writes, or has  			 * the file grown beyond our last write? */ -			if ((nfsi->npages == 0 && !test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) || -			     new_isize > cur_isize) { +			if ((nfsi->npages == 0) || new_isize > cur_isize) {  				i_size_write(inode, new_isize);  				invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;  			} @@ -1597,7 +1655,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  		inode->i_blocks = fattr->du.nfs2.blocks;  	/* Update attrtimeo value if we're out of the unstable period */ -	if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) { +	if (invalid & NFS_INO_INVALID_ATTR) {  		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);  		nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);  		nfsi->attrtimeo_timestamp = now; @@ -1610,7 +1668,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  		}  	}  	invalid &= ~NFS_INO_INVALID_ATTR; -	invalid &= ~NFS_INO_INVALID_LABEL;  	/* Don't invalidate the data if we were to blame */  	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)  				|| S_ISLNK(inode->i_mode))) @@ -1641,10 +1698,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)  		return NULL;  	nfsi->flags = 0UL;  	nfsi->cache_validity = 0UL; -#ifdef CONFIG_NFS_V3_ACL -	nfsi->acl_access = ERR_PTR(-EAGAIN); -	nfsi->acl_default = ERR_PTR(-EAGAIN); -#endif  #if IS_ENABLED(CONFIG_NFS_V4)  	nfsi->nfs4_acl = NULL;  #endif /* CONFIG_NFS_V4 */  | 
