diff options
Diffstat (limited to 'fs/nfsd/vfs.c')
| -rw-r--r-- | fs/nfsd/vfs.c | 122 | 
1 files changed, 65 insertions, 57 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index c99857689e2c..99c2b9dfbb10 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -40,6 +40,7 @@  #include "../internal.h"  #include "acl.h"  #include "idmap.h" +#include "xdr4.h"  #endif /* CONFIG_NFSD_V4 */  #include "nfsd.h" @@ -517,15 +518,23 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,  }  #endif -__be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos, -		struct nfsd_file *nf_dst, u64 dst_pos, u64 count, bool sync) +static struct nfsd4_compound_state *nfsd4_get_cstate(struct svc_rqst *rqstp) +{ +	return &((struct nfsd4_compoundres *)rqstp->rq_resp)->cstate; +} + +__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp, +		struct nfsd_file *nf_src, u64 src_pos, +		struct nfsd_file *nf_dst, u64 dst_pos, +		u64 count, bool sync)  {  	struct file *src = nf_src->nf_file;  	struct file *dst = nf_dst->nf_file; +	errseq_t since;  	loff_t cloned;  	__be32 ret = 0; -	down_write(&nf_dst->nf_rwsem); +	since = READ_ONCE(dst->f_wb_err);  	cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count, 0);  	if (cloned < 0) {  		ret = nfserrno(cloned); @@ -540,15 +549,25 @@ __be32 nfsd4_clone_file_range(struct nfsd_file *nf_src, u64 src_pos,  		int status = vfs_fsync_range(dst, dst_pos, dst_end, 0);  		if (!status) +			status = filemap_check_wb_err(dst->f_mapping, since); +		if (!status)  			status = commit_inode_metadata(file_inode(src));  		if (status < 0) { -			nfsd_reset_boot_verifier(net_generic(nf_dst->nf_net, -						 nfsd_net_id)); +			struct nfsd_net *nn = net_generic(nf_dst->nf_net, +							  nfsd_net_id); + +			trace_nfsd_clone_file_range_err(rqstp, +					&nfsd4_get_cstate(rqstp)->save_fh, +					src_pos, +					&nfsd4_get_cstate(rqstp)->current_fh, +					dst_pos, +					count, status); +			nfsd_reset_write_verifier(nn); +			trace_nfsd_writeverf_reset(nn, rqstp, status);  			ret = nfserrno(status);  		}  	}  out_err: -	up_write(&nf_dst->nf_rwsem);  	return ret;  } @@ -777,6 +796,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,  		int may_flags, struct file **filp)  {  	__be32 err; +	bool retried = false;  	validate_process_creds();  	/* @@ -792,9 +812,16 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,  	 */  	if (type == S_IFREG)  		may_flags |= NFSD_MAY_OWNER_OVERRIDE; +retry:  	err = fh_verify(rqstp, fhp, type, may_flags); -	if (!err) +	if (!err) {  		err = __nfsd_open(rqstp, fhp, type, may_flags, filp); +		if (err == nfserr_stale && !retried) { +			retried = true; +			fh_put(fhp); +			goto retry; +		} +	}  	validate_process_creds();  	return err;  } @@ -944,10 +971,12 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,  				unsigned long *cnt, int stable,  				__be32 *verf)  { +	struct nfsd_net		*nn = net_generic(SVC_NET(rqstp), nfsd_net_id);  	struct file		*file = nf->nf_file;  	struct super_block	*sb = file_inode(file)->i_sb;  	struct svc_export	*exp;  	struct iov_iter		iter; +	errseq_t		since;  	__be32			nfserr;  	int			host_err;  	int			use_wgather; @@ -985,36 +1014,28 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,  		flags |= RWF_SYNC;  	iov_iter_kvec(&iter, WRITE, vec, vlen, *cnt); -	if (flags & RWF_SYNC) { -		down_write(&nf->nf_rwsem); -		host_err = vfs_iter_write(file, &iter, &pos, flags); -		if (host_err < 0) -			nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp), -						 nfsd_net_id)); -		up_write(&nf->nf_rwsem); -	} else { -		down_read(&nf->nf_rwsem); -		if (verf) -			nfsd_copy_boot_verifier(verf, -					net_generic(SVC_NET(rqstp), -					nfsd_net_id)); -		host_err = vfs_iter_write(file, &iter, &pos, flags); -		up_read(&nf->nf_rwsem); -	} +	since = READ_ONCE(file->f_wb_err); +	if (verf) +		nfsd_copy_write_verifier(verf, nn); +	host_err = vfs_iter_write(file, &iter, &pos, flags);  	if (host_err < 0) { -		nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp), -					 nfsd_net_id)); +		nfsd_reset_write_verifier(nn); +		trace_nfsd_writeverf_reset(nn, rqstp, host_err);  		goto out_nfserr;  	}  	*cnt = host_err;  	nfsd_stats_io_write_add(exp, *cnt);  	fsnotify_modify(file); +	host_err = filemap_check_wb_err(file->f_mapping, since); +	if (host_err < 0) +		goto out_nfserr;  	if (stable && use_wgather) {  		host_err = wait_for_concurrent_writes(file); -		if (host_err < 0) -			nfsd_reset_boot_verifier(net_generic(SVC_NET(rqstp), -						 nfsd_net_id)); +		if (host_err < 0) { +			nfsd_reset_write_verifier(nn); +			trace_nfsd_writeverf_reset(nn, rqstp, host_err); +		}  	}  out_nfserr: @@ -1089,19 +1110,6 @@ out:  }  #ifdef CONFIG_NFSD_V3 -static int -nfsd_filemap_write_and_wait_range(struct nfsd_file *nf, loff_t offset, -				  loff_t end) -{ -	struct address_space *mapping = nf->nf_file->f_mapping; -	int ret = filemap_fdatawrite_range(mapping, offset, end); - -	if (ret) -		return ret; -	filemap_fdatawait_range_keep_errors(mapping, offset, end); -	return 0; -} -  /*   * Commit all pending writes to stable storage.   * @@ -1115,6 +1123,7 @@ __be32  nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,                 loff_t offset, unsigned long count, __be32 *verf)  { +	struct nfsd_net		*nn;  	struct nfsd_file	*nf;  	loff_t			end = LLONG_MAX;  	__be32			err = nfserr_inval; @@ -1131,29 +1140,28 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,  			NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &nf);  	if (err)  		goto out; +	nn = net_generic(nf->nf_net, nfsd_net_id);  	if (EX_ISSYNC(fhp->fh_export)) { -		int err2 = nfsd_filemap_write_and_wait_range(nf, offset, end); +		errseq_t since = READ_ONCE(nf->nf_file->f_wb_err); +		int err2; -		down_write(&nf->nf_rwsem); -		if (!err2) -			err2 = vfs_fsync_range(nf->nf_file, offset, end, 0); +		err2 = vfs_fsync_range(nf->nf_file, offset, end, 0);  		switch (err2) {  		case 0: -			nfsd_copy_boot_verifier(verf, net_generic(nf->nf_net, -						nfsd_net_id)); +			nfsd_copy_write_verifier(verf, nn); +			err2 = filemap_check_wb_err(nf->nf_file->f_mapping, +						    since);  			break;  		case -EINVAL:  			err = nfserr_notsupp;  			break;  		default: -			err = nfserrno(err2); -			nfsd_reset_boot_verifier(net_generic(nf->nf_net, -						 nfsd_net_id)); +			nfsd_reset_write_verifier(nn); +			trace_nfsd_writeverf_reset(nn, rqstp, err2);  		} -		up_write(&nf->nf_rwsem); +		err = nfserrno(err2);  	} else -		nfsd_copy_boot_verifier(verf, net_generic(nf->nf_net, -					nfsd_net_id)); +		nfsd_copy_write_verifier(verf, nn);  	nfsd_file_put(nf);  out: @@ -1747,8 +1755,8 @@ retry:  	 * so do it by hand */  	trap = lock_rename(tdentry, fdentry);  	ffhp->fh_locked = tfhp->fh_locked = true; -	fill_pre_wcc(ffhp); -	fill_pre_wcc(tfhp); +	fh_fill_pre_attrs(ffhp); +	fh_fill_pre_attrs(tfhp);  	odentry = lookup_one_len(fname, fdentry, flen);  	host_err = PTR_ERR(odentry); @@ -1808,8 +1816,8 @@ retry:  	 * were the same, so again we do it by hand.  	 */  	if (!close_cached) { -		fill_post_wcc(ffhp); -		fill_post_wcc(tfhp); +		fh_fill_post_attrs(ffhp); +		fh_fill_post_attrs(tfhp);  	}  	unlock_rename(tdentry, fdentry);  	ffhp->fh_locked = tfhp->fh_locked = false;  |