diff options
author | David Shaw <dshaw@jabberwocky.com> | 2009-03-05 20:16:14 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-03-18 17:38:40 -0400 |
commit | 31dec2538e45e9fff2007ea1f4c6bae9f78db724 (patch) | |
tree | c2b42679c73c1c6f31312f38a1b1d049e918d635 /fs/nfsd/vfs.c | |
parent | 1e685ec270cb97680be4eb8cf6b615f5f7f1403a (diff) |
Short write in nfsd becomes a full write to the client
If a filesystem being written to via NFS returns a short write count
(as opposed to an error) to nfsd, nfsd treats that as a success for
the entire write, rather than the short count that actually succeeded.
For example, given a 8192 byte write, if the underlying filesystem
only writes 4096 bytes, nfsd will ack back to the nfs client that all
8192 bytes were written. The nfs client does have retry logic for
short writes, but this is never called as the client is told the
complete write succeeded.
There are probably other ways it could happen, but in my case it
happened with a fuse (filesystem in userspace) filesystem which can
rather easily have a partial write.
Here is a patch to properly return the short write count to the
client.
Signed-off-by: David Shaw <dshaw@jabberwocky.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 13 |
1 files changed, 7 insertions, 6 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 0c076293155d..54404d730809 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -960,7 +960,7 @@ static void kill_suid(struct dentry *dentry) | |||
960 | static __be32 | 960 | static __be32 |
961 | nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | 961 | nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, |
962 | loff_t offset, struct kvec *vec, int vlen, | 962 | loff_t offset, struct kvec *vec, int vlen, |
963 | unsigned long cnt, int *stablep) | 963 | unsigned long *cnt, int *stablep) |
964 | { | 964 | { |
965 | struct svc_export *exp; | 965 | struct svc_export *exp; |
966 | struct dentry *dentry; | 966 | struct dentry *dentry; |
@@ -974,7 +974,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
974 | err = nfserr_perm; | 974 | err = nfserr_perm; |
975 | 975 | ||
976 | if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && | 976 | if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && |
977 | (!lock_may_write(file->f_path.dentry->d_inode, offset, cnt))) | 977 | (!lock_may_write(file->f_path.dentry->d_inode, offset, *cnt))) |
978 | goto out; | 978 | goto out; |
979 | #endif | 979 | #endif |
980 | 980 | ||
@@ -1006,7 +1006,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1006 | host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); | 1006 | host_err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset); |
1007 | set_fs(oldfs); | 1007 | set_fs(oldfs); |
1008 | if (host_err >= 0) { | 1008 | if (host_err >= 0) { |
1009 | nfsdstats.io_write += cnt; | 1009 | nfsdstats.io_write += host_err; |
1010 | fsnotify_modify(file->f_path.dentry); | 1010 | fsnotify_modify(file->f_path.dentry); |
1011 | } | 1011 | } |
1012 | 1012 | ||
@@ -1051,9 +1051,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1051 | } | 1051 | } |
1052 | 1052 | ||
1053 | dprintk("nfsd: write complete host_err=%d\n", host_err); | 1053 | dprintk("nfsd: write complete host_err=%d\n", host_err); |
1054 | if (host_err >= 0) | 1054 | if (host_err >= 0) { |
1055 | err = 0; | 1055 | err = 0; |
1056 | else | 1056 | *cnt = host_err; |
1057 | } else | ||
1057 | err = nfserrno(host_err); | 1058 | err = nfserrno(host_err); |
1058 | out: | 1059 | out: |
1059 | return err; | 1060 | return err; |
@@ -1095,7 +1096,7 @@ out: | |||
1095 | */ | 1096 | */ |
1096 | __be32 | 1097 | __be32 |
1097 | nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | 1098 | nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, |
1098 | loff_t offset, struct kvec *vec, int vlen, unsigned long cnt, | 1099 | loff_t offset, struct kvec *vec, int vlen, unsigned long *cnt, |
1099 | int *stablep) | 1100 | int *stablep) |
1100 | { | 1101 | { |
1101 | __be32 err = 0; | 1102 | __be32 err = 0; |