diff options
Diffstat (limited to 'fs/nfs/inode.c')
| -rw-r--r-- | fs/nfs/inode.c | 76 | 
1 files changed, 42 insertions, 34 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index b77b328a06d7..326d9e10d833 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -442,8 +442,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st  			nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR);  		if (fattr->valid & NFS_ATTR_FATTR_CHANGE)  			inode->i_version = fattr->change_attr; -		else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR)) -			nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); +		else +			nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR +				| NFS_INO_REVAL_PAGECACHE);  		if (fattr->valid & NFS_ATTR_FATTR_SIZE)  			inode->i_size = nfs_size_to_loff_t(fattr->size);  		else @@ -503,7 +504,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)  {  	struct inode *inode = d_inode(dentry);  	struct nfs_fattr *fattr; -	int error = -ENOMEM; +	int error = 0;  	nfs_inc_stats(inode, NFSIOS_VFSSETATTR); @@ -512,15 +513,14 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)  		attr->ia_valid &= ~ATTR_MODE;  	if (attr->ia_valid & ATTR_SIZE) { -		loff_t i_size; -  		BUG_ON(!S_ISREG(inode->i_mode)); -		i_size = i_size_read(inode); -		if (attr->ia_size == i_size) +		error = inode_newsize_ok(inode, attr->ia_size); +		if (error) +			return error; + +		if (attr->ia_size == i_size_read(inode))  			attr->ia_valid &= ~ATTR_SIZE; -		else if (attr->ia_size < i_size && IS_SWAPFILE(inode)) -			return -ETXTBSY;  	}  	/* Optimization: if the end result is no change, don't RPC */ @@ -535,8 +535,11 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)  		nfs_sync_inode(inode);  	fattr = nfs_alloc_fattr(); -	if (fattr == NULL) +	if (fattr == NULL) { +		error = -ENOMEM;  		goto out; +	} +  	/*  	 * Return any delegations if we're going to change ACLs  	 */ @@ -758,11 +761,13 @@ EXPORT_SYMBOL_GPL(nfs_put_lock_context);   * @ctx: pointer to context   * @is_sync: is this a synchronous close   * - * always ensure that the attributes are up to date if we're mounted - * with close-to-open semantics + * Ensure that the attributes are up to date if we're mounted + * with close-to-open semantics and we have cached data that will + * need to be revalidated on open.   */  void nfs_close_context(struct nfs_open_context *ctx, int is_sync)  { +	struct nfs_inode *nfsi;  	struct inode *inode;  	struct nfs_server *server; @@ -771,7 +776,12 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)  	if (!is_sync)  		return;  	inode = d_inode(ctx->dentry); -	if (!list_empty(&NFS_I(inode)->open_files)) +	nfsi = NFS_I(inode); +	if (inode->i_mapping->nrpages == 0) +		return; +	if (nfsi->cache_validity & NFS_INO_INVALID_DATA) +		return; +	if (!list_empty(&nfsi->open_files))  		return;  	server = NFS_SERVER(inode);  	if (server->flags & NFS_MOUNT_NOCTO) @@ -843,6 +853,11 @@ void put_nfs_open_context(struct nfs_open_context *ctx)  }  EXPORT_SYMBOL_GPL(put_nfs_open_context); +static void put_nfs_open_context_sync(struct nfs_open_context *ctx) +{ +	__put_nfs_open_context(ctx, 1); +} +  /*   * Ensure that mmap has a recent RPC credential for use when writing out   * shared pages @@ -887,7 +902,7 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c  	return ctx;  } -static void nfs_file_clear_open_context(struct file *filp) +void nfs_file_clear_open_context(struct file *filp)  {  	struct nfs_open_context *ctx = nfs_file_open_context(filp); @@ -898,7 +913,7 @@ static void nfs_file_clear_open_context(struct file *filp)  		spin_lock(&inode->i_lock);  		list_move_tail(&ctx->list, &NFS_I(inode)->open_files);  		spin_unlock(&inode->i_lock); -		__put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1); +		put_nfs_open_context_sync(ctx);  	}  } @@ -918,12 +933,6 @@ int nfs_open(struct inode *inode, struct file *filp)  	return 0;  } -int nfs_release(struct inode *inode, struct file *filp) -{ -	nfs_file_clear_open_context(filp); -	return 0; -} -  /*   * This function is called whenever some part of NFS notices that   * the cached attributes have to be refreshed. @@ -1244,9 +1253,11 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat  	if (fattr->valid & NFS_ATTR_FATTR_SIZE) {  		cur_size = i_size_read(inode);  		new_isize = nfs_size_to_loff_t(fattr->size); -		if (cur_size != new_isize && nfsi->nrequests == 0) +		if (cur_size != new_isize)  			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;  	} +	if (nfsi->nrequests != 0) +		invalid &= ~NFS_INO_REVAL_PAGECACHE;  	/* Have any file permissions changed? */  	if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) @@ -1270,13 +1281,6 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat  	return 0;  } -static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr) -{ -	if (!(fattr->valid & NFS_ATTR_FATTR_CTIME)) -		return 0; -	return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0; -} -  static atomic_long_t nfs_attr_generation_counter;  static unsigned long nfs_read_attr_generation_counter(void) @@ -1425,7 +1429,6 @@ static int nfs_inode_attrs_need_update(const struct inode *inode, const struct n  	const struct nfs_inode *nfsi = NFS_I(inode);  	return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 || -		nfs_ctime_need_update(inode, fattr) ||  		((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0);  } @@ -1488,6 +1491,13 @@ static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr  {  	unsigned long invalid = NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; +	/* +	 * Don't revalidate the pagecache if we hold a delegation, but do +	 * force an attribute update +	 */ +	if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) +		invalid = NFS_INO_INVALID_ATTR|NFS_INO_REVAL_FORCED; +  	if (S_ISDIR(inode->i_mode))  		invalid |= NFS_INO_INVALID_DATA;  	nfs_set_cache_invalid(inode, invalid); @@ -1684,13 +1694,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  			invalid |= NFS_INO_INVALID_ATTR  				| NFS_INO_INVALID_DATA  				| NFS_INO_INVALID_ACCESS -				| NFS_INO_INVALID_ACL -				| NFS_INO_REVAL_PAGECACHE; +				| NFS_INO_INVALID_ACL;  			if (S_ISDIR(inode->i_mode))  				nfs_force_lookup_revalidate(inode);  			inode->i_version = fattr->change_attr;  		} -	} else if (server->caps & NFS_CAP_CHANGE_ATTR) +	} else  		nfsi->cache_validity |= save_cache_validity;  	if (fattr->valid & NFS_ATTR_FATTR_MTIME) { @@ -1717,7 +1726,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)  			if ((nfsi->nrequests == 0) || new_isize > cur_isize) {  				i_size_write(inode, new_isize);  				invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; -				invalid &= ~NFS_INO_REVAL_PAGECACHE;  			}  			dprintk("NFS: isize change on server for file %s/%ld "  					"(%Ld to %Ld)\n",  |