From: Andreas Gruenbacher Allow to allocate pages in the receive buffer lazily. Used for the GETACL RPC, which has a big maximum reply size, but a small average reply size. Signed-off-by: Olaf Kirch Signed-off-by: Andreas Gruenbacher Signed-off-by: Andrew Morton --- 25-akpm/include/linux/sunrpc/xdr.h | 2 +- 25-akpm/net/sunrpc/xdr.c | 15 ++++++++++++--- 25-akpm/net/sunrpc/xprt.c | 26 ++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 8 deletions(-) diff -puN include/linux/sunrpc/xdr.h~nfsacl-lazy-rpc-receive-buffer-allocation include/linux/sunrpc/xdr.h --- 25/include/linux/sunrpc/xdr.h~nfsacl-lazy-rpc-receive-buffer-allocation 2005-01-23 01:27:53.115231584 -0800 +++ 25-akpm/include/linux/sunrpc/xdr.h 2005-01-23 01:27:53.122230520 -0800 @@ -160,7 +160,7 @@ typedef struct { typedef size_t (*skb_read_actor_t)(skb_reader_t *desc, void *to, size_t len); -extern void xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, +extern int xdr_partial_copy_from_skb(struct xdr_buf *, unsigned int, skb_reader_t *, skb_read_actor_t); struct socket; diff -puN net/sunrpc/xdr.c~nfsacl-lazy-rpc-receive-buffer-allocation net/sunrpc/xdr.c --- 25/net/sunrpc/xdr.c~nfsacl-lazy-rpc-receive-buffer-allocation 2005-01-23 01:27:53.116231432 -0800 +++ 25-akpm/net/sunrpc/xdr.c 2005-01-23 01:27:53.123230368 -0800 @@ -176,7 +176,7 @@ xdr_inline_pages(struct xdr_buf *xdr, un xdr->buflen += len; } -void +int xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, skb_reader_t *desc, skb_read_actor_t copy_actor) @@ -190,7 +190,7 @@ xdr_partial_copy_from_skb(struct xdr_buf len -= base; ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); if (ret != len || !desc->count) - return; + return 0; base = 0; } else base -= len; @@ -210,6 +210,13 @@ xdr_partial_copy_from_skb(struct xdr_buf do { char *kaddr; + /* ACL likes to be lazy in allocating pages - ACLs + * are small by default but can get huge. */ + if (unlikely(*ppage == NULL)) { + if (!(*ppage = alloc_page(GFP_ATOMIC))) + return -ENOMEM; + } + len = PAGE_CACHE_SIZE; kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); if (base) { @@ -226,13 +233,15 @@ xdr_partial_copy_from_skb(struct xdr_buf flush_dcache_page(*ppage); kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); if (ret != len || !desc->count) - return; + return 0; ppage++; } while ((pglen -= len) != 0); copy_tail: len = xdr->tail[0].iov_len; if (base < len) copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); + + return 0; } diff -puN net/sunrpc/xprt.c~nfsacl-lazy-rpc-receive-buffer-allocation net/sunrpc/xprt.c --- 25/net/sunrpc/xprt.c~nfsacl-lazy-rpc-receive-buffer-allocation 2005-01-23 01:27:53.118231128 -0800 +++ 25-akpm/net/sunrpc/xprt.c 2005-01-23 01:27:53.124230216 -0800 @@ -725,7 +725,8 @@ csum_partial_copy_to_xdr(struct xdr_buf goto no_checksum; desc.csum = csum_partial(skb->data, desc.offset, skb->csum); - xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits); + if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_and_csum_bits) < 0) + return -1; if (desc.offset != skb->len) { unsigned int csum2; csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); @@ -737,7 +738,8 @@ csum_partial_copy_to_xdr(struct xdr_buf return -1; return 0; no_checksum: - xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits); + if (xdr_partial_copy_from_skb(xdr, 0, &desc, skb_read_bits) < 0) + return -1; if (desc.count) return -1; return 0; @@ -907,6 +909,7 @@ tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req; struct xdr_buf *rcvbuf; size_t len; + int r; /* Find and lock the request corresponding to this xid */ spin_lock(&xprt->sock_lock); @@ -927,16 +930,30 @@ tcp_read_request(struct rpc_xprt *xprt, len = xprt->tcp_reclen - xprt->tcp_offset; memcpy(&my_desc, desc, sizeof(my_desc)); my_desc.count = len; - xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, + r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, &my_desc, tcp_copy_data); desc->count -= len; desc->offset += len; } else - xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, + r = xdr_partial_copy_from_skb(rcvbuf, xprt->tcp_copied, desc, tcp_copy_data); xprt->tcp_copied += len; xprt->tcp_offset += len; + if (r < 0) { + /* Error when copying to the receive buffer, + * usually because we weren't able to allocate + * additional buffer pages. All we can do now + * is turn off XPRT_COPY_DATA, so the request + * will not receive any additional updates, + * and time out. + * Any remaining data from this record will + * be discarded. + */ + xprt->tcp_flags &= ~XPRT_COPY_DATA; + goto out; + } + if (xprt->tcp_copied == req->rq_private_buf.buflen) xprt->tcp_flags &= ~XPRT_COPY_DATA; else if (xprt->tcp_offset == xprt->tcp_reclen) { @@ -949,6 +966,7 @@ tcp_read_request(struct rpc_xprt *xprt, req->rq_task->tk_pid); xprt_complete_rqst(xprt, req, xprt->tcp_copied); } +out: spin_unlock(&xprt->sock_lock); tcp_check_recm(xprt); } _