aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2012-11-15 14:52:19 -0500
committerJ. Bruce Fields <bfields@redhat.com>2012-11-26 09:08:15 -0500
commitffe1137ba743cdf1c2414d5a89690aec1daa6bba (patch)
treeb774cb101e03779db6dd569a28b3e5d7d4bf06b1 /fs/nfsd
parent70cc7f75b1ee4161dfdea1012223db25712ab1a5 (diff)
nfsd4: delay filling in write iovec array till after xdr decoding
Our server rejects compounds containing more than one write operation. It's unclear whether this is really permitted by the spec; with 4.0, it's possibly OK, with 4.1 (which has clearer limits on compound parameters), it's probably not OK. No client that we're aware of has ever done this, but in theory it could be useful. The source of the limitation: we need an array of iovecs to pass to the write operation. In the worst case that array of iovecs could have hundreds of elements (the maximum rwsize divided by the page size), so it's too big to put on the stack, or in each compound op. So we instead keep a single such array in the compound argument. We fill in that array at the time we decode the xdr operation. But we decode every op in the compound before executing any of them. So once we've used that array we can't decode another write. If we instead delay filling in that array till the time we actually perform the write, we can reuse it. Another option might be to switch to decoding compound ops one at a time. I considered doing that, but it has a number of other side effects, and I'd rather fix just this one problem for now. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4proc.c24
-rw-r--r--fs/nfsd/nfs4xdr.c20
-rw-r--r--fs/nfsd/xdr4.h1
3 files changed, 23 insertions, 22 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 1d2396b79574..87d24e5f3ca4 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -881,6 +881,24 @@ out:
881 return status; 881 return status;
882} 882}
883 883
884static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write)
885{
886 int i = 1;
887 int buflen = write->wr_buflen;
888
889 vec[0].iov_base = write->wr_head.iov_base;
890 vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len);
891 buflen -= vec[0].iov_len;
892
893 while (buflen) {
894 vec[i].iov_base = page_address(write->wr_pagelist[i - 1]);
895 vec[i].iov_len = min_t(int, PAGE_SIZE, buflen);
896 buflen -= vec[i].iov_len;
897 i++;
898 }
899 return i;
900}
901
884static __be32 902static __be32
885nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 903nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
886 struct nfsd4_write *write) 904 struct nfsd4_write *write)
@@ -889,6 +907,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
889 struct file *filp = NULL; 907 struct file *filp = NULL;
890 __be32 status = nfs_ok; 908 __be32 status = nfs_ok;
891 unsigned long cnt; 909 unsigned long cnt;
910 int nvecs;
892 911
893 /* no need to check permission - this will be done in nfsd_write() */ 912 /* no need to check permission - this will be done in nfsd_write() */
894 913
@@ -911,8 +930,11 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
911 write->wr_how_written = write->wr_stable_how; 930 write->wr_how_written = write->wr_stable_how;
912 gen_boot_verifier(&write->wr_verifier); 931 gen_boot_verifier(&write->wr_verifier);
913 932
933 nvecs = fill_in_write_vector(rqstp->rq_vec, write);
934 WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec));
935
914 status = nfsd_write(rqstp, &cstate->current_fh, filp, 936 status = nfsd_write(rqstp, &cstate->current_fh, filp,
915 write->wr_offset, rqstp->rq_vec, write->wr_vlen, 937 write->wr_offset, rqstp->rq_vec, nvecs,
916 &cnt, &write->wr_how_written); 938 &cnt, &write->wr_how_written);
917 if (filp) 939 if (filp)
918 fput(filp); 940 fput(filp);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index cb9f9017af8f..09204f590355 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1139,24 +1139,6 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
1139 DECODE_TAIL; 1139 DECODE_TAIL;
1140} 1140}
1141 1141
1142static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write)
1143{
1144 int i = 1;
1145 int buflen = write->wr_buflen;
1146
1147 vec[0].iov_base = write->wr_head.iov_base;
1148 vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len);
1149 buflen -= vec[0].iov_len;
1150
1151 while (buflen) {
1152 vec[i].iov_base = page_address(write->wr_pagelist[i - 1]);
1153 vec[i].iov_len = min_t(int, PAGE_SIZE, buflen);
1154 buflen -= vec[i].iov_len;
1155 i++;
1156 }
1157 return i;
1158}
1159
1160static __be32 1142static __be32
1161nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) 1143nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
1162{ 1144{
@@ -1204,8 +1186,6 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
1204 argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); 1186 argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
1205 } 1187 }
1206 argp->p += XDR_QUADLEN(len); 1188 argp->p += XDR_QUADLEN(len);
1207 write->wr_vlen = fill_in_write_vector(argp->rqstp->rq_vec, write);
1208 WARN_ON_ONCE(write->wr_vlen > ARRAY_SIZE(argp->rqstp->rq_vec));
1209 1189
1210 DECODE_TAIL; 1190 DECODE_TAIL;
1211} 1191}
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 152867b8125d..331f8a3277ab 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -385,7 +385,6 @@ struct nfsd4_write {
385 u64 wr_offset; /* request */ 385 u64 wr_offset; /* request */
386 u32 wr_stable_how; /* request */ 386 u32 wr_stable_how; /* request */
387 u32 wr_buflen; /* request */ 387 u32 wr_buflen; /* request */
388 int wr_vlen;
389 struct kvec wr_head; 388 struct kvec wr_head;
390 struct page ** wr_pagelist; /* request */ 389 struct page ** wr_pagelist; /* request */
391 390