diff options
-rw-r--r-- | include/linux/sunrpc/xdr.h | 2 | ||||
-rw-r--r-- | net/sunrpc/xdr.c | 16 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 26 |
3 files changed, 36 insertions, 8 deletions
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 541dcf838abf..0f5b7a5a7432 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h | |||
@@ -160,7 +160,7 @@ typedef struct { | |||
160 | 160 | ||
161 | typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); | 161 | typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); |
162 | 162 | ||
163 | extern void xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, | 163 | extern int xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, |
164 | skb_reader_t *, skb_read_actor_t); | 164 | skb_reader_t *, skb_read_actor_t); |
165 | 165 | ||
166 | struct socket; | 166 | struct socket; |
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index f86d1baa6302..65b268d39782 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -176,7 +176,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, | |||
176 | xdr->buflen += len; | 176 | xdr->buflen += len; |
177 | } | 177 | } |
178 | 178 | ||
179 | void | 179 | int |
180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | 180 | xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, |
181 | skb_reader_t *desc, | 181 | skb_reader_t *desc, |
182 | skb_read_actor_t copy_actor) | 182 | skb_read_actor_t copy_actor) |
@@ -190,7 +190,7 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
190 | len -= base; | 190 | len -= base; |
191 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); | 191 | ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); |
192 | if (ret != len || !desc->count) | 192 | if (ret != len || !desc->count) |
193 | return; | 193 | return 0; |
194 | base = 0; | 194 | base = 0; |
195 | } else | 195 | } else |
196 | base -= len; | 196 | base -= len; |
@@ -210,6 +210,14 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
210 | do { | 210 | do { |
211 | char *kaddr; | 211 | char *kaddr; |
212 | 212 | ||
213 | /* ACL likes to be lazy in allocating pages - ACLs | ||
214 | * are small by default but can get huge. */ | ||
215 | if (unlikely(*ppage == NULL)) { | ||
216 | *ppage = alloc_page(GFP_ATOMIC); | ||
217 | if (unlikely(*ppage == NULL)) | ||
218 | return -ENOMEM; | ||
219 | } | ||
220 | |||
213 | len = PAGE_CACHE_SIZE; | 221 | len = PAGE_CACHE_SIZE; |
214 | kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); | 222 | kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); |
215 | if (base) { | 223 | if (base) { |
@@ -226,13 +234,15 @@ xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, | |||
226 | flush_dcache_page(*ppage); | 234 | flush_dcache_page(*ppage); |
227 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); | 235 | kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); |
228 | if (ret != len || !desc->count) | 236 | if (ret != len || !desc->count) |
229 | return; | 237 | return 0; |
230 | ppage++; | 238 | ppage++; |
231 | } while ((pglen -= len) != 0); | 239 | } while ((pglen -= len) != 0); |
232 | copy_tail: | 240 | copy_tail: |
233 | len = xdr->tail[0].iov_len; | 241 | len = xdr->tail[0].iov_len; |
234 | if (base < len) | 242 | if (base < len) |
235 | copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); | 243 | copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); |
244 | |||
245 | return 0; | ||
236 | } | 246 | } |
237 | 247 | ||
238 | 248 | ||
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index c74a6bb94074..a180ed4952d6 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -725,7 +725,8 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) | |||
725 | goto no_checksum; | 725 | goto no_checksum; |
726 | 726 | ||
727 | desc.csum = csum_partial(skb->data, desc.offset, skb->csum); | 727 | desc.csum = csum_partial(skb->data, desc.offset, skb->csum); |
728 | xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits); | 728 | if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits) < 0) |
729 | return -1; | ||
729 | if (desc.offset != skb->len) { | 730 | if (desc.offset != skb->len) { |
730 | unsigned int csum2; | 731 | unsigned int csum2; |
731 | csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); | 732 | csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); |
@@ -737,7 +738,8 @@ csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) | |||
737 | return -1; | 738 | return -1; |
738 | return 0; | 739 | return 0; |
739 | no_checksum: | 740 | no_checksum: |
740 | xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits); | 741 | if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits) < 0) |
742 | return -1; | ||
741 | if (desc.count) | 743 | if (desc.count) |
742 | return -1; | 744 | return -1; |
743 | return 0; | 745 | return 0; |
@@ -907,6 +909,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
907 | struct rpc_rqst *req; | 909 | struct rpc_rqst *req; |
908 | struct xdr_buf *rcvbuf; | 910 | struct xdr_buf *rcvbuf; |
909 | size_t len; | 911 | size_t len; |
912 | int r; | ||
910 | 913 | ||
911 | /* Find and lock the request corresponding to this xid */ | 914 | /* Find and lock the request corresponding to this xid */ |
912 | spin_lock(&xprt->sock_lock); | 915 | spin_lock(&xprt->sock_lock); |
@@ -927,16 +930,30 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
927 | len = xprt->tcp_reclen - xprt->tcp_offset; | 930 | len = xprt->tcp_reclen - xprt->tcp_offset; |
928 | memcpy(&my_desc, desc, sizeof(my_desc)); | 931 | memcpy(&my_desc, desc, sizeof(my_desc)); |
929 | my_desc.count = len; | 932 | my_desc.count = len; |
930 | xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, | 933 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, |
931 | &my_desc, tcp_copy_data); | 934 | &my_desc, tcp_copy_data); |
932 | desc->count -= len; | 935 | desc->count -= len; |
933 | desc->offset += len; | 936 | desc->offset += len; |
934 | } else | 937 | } else |
935 | xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, | 938 | r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, |
936 | desc, tcp_copy_data); | 939 | desc, tcp_copy_data); |
937 | xprt->tcp_copied += len; | 940 | xprt->tcp_copied += len; |
938 | xprt->tcp_offset += len; | 941 | xprt->tcp_offset += len; |
939 | 942 | ||
943 | if (r < 0) { | ||
944 | /* Error when copying to the receive buffer, | ||
945 | * usually because we weren't able to allocate | ||
946 | * additional buffer pages. All we can do now | ||
947 | * is turn off XPRT_COPY_DATA, so the request | ||
948 | * will not receive any additional updates, | ||
949 | * and time out. | ||
950 | * Any remaining data from this record will | ||
951 | * be discarded. | ||
952 | */ | ||
953 | xprt->tcp_flags &= ~XPRT_COPY_DATA; | ||
954 | goto out; | ||
955 | } | ||
956 | |||
940 | if (xprt->tcp_copied == req->rq_private_buf.buflen) | 957 | if (xprt->tcp_copied == req->rq_private_buf.buflen) |
941 | xprt->tcp_flags &= ~XPRT_COPY_DATA; | 958 | xprt->tcp_flags &= ~XPRT_COPY_DATA; |
942 | else if (xprt->tcp_offset == xprt->tcp_reclen) { | 959 | else if (xprt->tcp_offset == xprt->tcp_reclen) { |
@@ -949,6 +966,7 @@ tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) | |||
949 | req->rq_task->tk_pid); | 966 | req->rq_task->tk_pid); |
950 | xprt_complete_rqst(xprt, req, xprt->tcp_copied); | 967 | xprt_complete_rqst(xprt, req, xprt->tcp_copied); |
951 | } | 968 | } |
969 | out: | ||
952 | spin_unlock(&xprt->sock_lock); | 970 | spin_unlock(&xprt->sock_lock); |
953 | tcp_check_recm(xprt); | 971 | tcp_check_recm(xprt); |
954 | } | 972 | } |