1094bb20bSChuck Lever /* 2094bb20bSChuck Lever * linux/net/sunrpc/socklib.c 3094bb20bSChuck Lever * 4094bb20bSChuck Lever * Common socket helper routines for RPC client and server 5094bb20bSChuck Lever * 6094bb20bSChuck Lever * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 7094bb20bSChuck Lever */ 8094bb20bSChuck Lever 9fb286bb2SHerbert Xu #include <linux/compiler.h> 10fb286bb2SHerbert Xu #include <linux/netdevice.h> 11fb286bb2SHerbert Xu #include <linux/skbuff.h> 12094bb20bSChuck Lever #include <linux/types.h> 13094bb20bSChuck Lever #include <linux/pagemap.h> 14094bb20bSChuck Lever #include <linux/udp.h> 15094bb20bSChuck Lever #include <linux/sunrpc/xdr.h> 16094bb20bSChuck Lever 17094bb20bSChuck Lever 18094bb20bSChuck Lever /** 199d292316SChuck Lever * xdr_skb_read_bits - copy some data bits from skb to internal buffer 20094bb20bSChuck Lever * @desc: sk_buff copy helper 21094bb20bSChuck Lever * @to: copy destination 22094bb20bSChuck Lever * @len: number of bytes to copy 23094bb20bSChuck Lever * 24094bb20bSChuck Lever * Possibly called several times to iterate over an sk_buff and copy 25094bb20bSChuck Lever * data out of it. 26094bb20bSChuck Lever */ 27dd456471SChuck Lever size_t xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 28094bb20bSChuck Lever { 29094bb20bSChuck Lever if (len > desc->count) 30094bb20bSChuck Lever len = desc->count; 319d292316SChuck Lever if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 32094bb20bSChuck Lever return 0; 33094bb20bSChuck Lever desc->count -= len; 34094bb20bSChuck Lever desc->offset += len; 35094bb20bSChuck Lever return len; 36094bb20bSChuck Lever } 3712444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xdr_skb_read_bits); 38094bb20bSChuck Lever 39094bb20bSChuck Lever /** 409d292316SChuck Lever * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 41094bb20bSChuck Lever * @desc: sk_buff copy helper 42094bb20bSChuck Lever * @to: copy destination 43094bb20bSChuck Lever * @len: number of bytes to copy 44094bb20bSChuck Lever * 45094bb20bSChuck Lever * Same as skb_read_bits, but calculate a checksum at the same time. 46094bb20bSChuck Lever */ 47dd456471SChuck Lever static size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 48094bb20bSChuck Lever { 495f92a738SAl Viro unsigned int pos; 505f92a738SAl Viro __wsum csum2; 51094bb20bSChuck Lever 52094bb20bSChuck Lever if (len > desc->count) 53094bb20bSChuck Lever len = desc->count; 54094bb20bSChuck Lever pos = desc->offset; 55094bb20bSChuck Lever csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); 56094bb20bSChuck Lever desc->csum = csum_block_add(desc->csum, csum2, pos); 57094bb20bSChuck Lever desc->count -= len; 58094bb20bSChuck Lever desc->offset += len; 59094bb20bSChuck Lever return len; 60094bb20bSChuck Lever } 61094bb20bSChuck Lever 62094bb20bSChuck Lever /** 63094bb20bSChuck Lever * xdr_partial_copy_from_skb - copy data out of an skb 64094bb20bSChuck Lever * @xdr: target XDR buffer 65094bb20bSChuck Lever * @base: starting offset 66094bb20bSChuck Lever * @desc: sk_buff copy helper 67094bb20bSChuck Lever * @copy_actor: virtual method for copying data 68094bb20bSChuck Lever * 69094bb20bSChuck Lever */ 70dd456471SChuck Lever ssize_t xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) 71094bb20bSChuck Lever { 72094bb20bSChuck Lever struct page **ppage = xdr->pages; 73094bb20bSChuck Lever unsigned int len, pglen = xdr->page_len; 74094bb20bSChuck Lever ssize_t copied = 0; 75094bb20bSChuck Lever int ret; 76094bb20bSChuck Lever 77094bb20bSChuck Lever len = xdr->head[0].iov_len; 78094bb20bSChuck Lever if (base < len) { 79094bb20bSChuck Lever len -= base; 80094bb20bSChuck Lever ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 81094bb20bSChuck Lever copied += ret; 82094bb20bSChuck Lever if (ret != len || !desc->count) 83094bb20bSChuck Lever goto out; 84094bb20bSChuck Lever base = 0; 85094bb20bSChuck Lever } else 86094bb20bSChuck Lever base -= len; 87094bb20bSChuck Lever 88094bb20bSChuck Lever if (unlikely(pglen == 0)) 89094bb20bSChuck Lever goto copy_tail; 90094bb20bSChuck Lever if (unlikely(base >= pglen)) { 91094bb20bSChuck Lever base -= pglen; 92094bb20bSChuck Lever goto copy_tail; 93094bb20bSChuck Lever } 94094bb20bSChuck Lever if (base || xdr->page_base) { 95094bb20bSChuck Lever pglen -= base; 96094bb20bSChuck Lever base += xdr->page_base; 97094bb20bSChuck Lever ppage += base >> PAGE_CACHE_SHIFT; 98094bb20bSChuck Lever base &= ~PAGE_CACHE_MASK; 99094bb20bSChuck Lever } 100094bb20bSChuck Lever do { 101094bb20bSChuck Lever char *kaddr; 102094bb20bSChuck Lever 103094bb20bSChuck Lever /* ACL likes to be lazy in allocating pages - ACLs 104094bb20bSChuck Lever * are small by default but can get huge. */ 105094bb20bSChuck Lever if (unlikely(*ppage == NULL)) { 106094bb20bSChuck Lever *ppage = alloc_page(GFP_ATOMIC); 107094bb20bSChuck Lever if (unlikely(*ppage == NULL)) { 108094bb20bSChuck Lever if (copied == 0) 109094bb20bSChuck Lever copied = -ENOMEM; 110094bb20bSChuck Lever goto out; 111094bb20bSChuck Lever } 112094bb20bSChuck Lever } 113094bb20bSChuck Lever 114094bb20bSChuck Lever len = PAGE_CACHE_SIZE; 115094bb20bSChuck Lever kaddr = kmap_atomic(*ppage, KM_SKB_SUNRPC_DATA); 116094bb20bSChuck Lever if (base) { 117094bb20bSChuck Lever len -= base; 118094bb20bSChuck Lever if (pglen < len) 119094bb20bSChuck Lever len = pglen; 120094bb20bSChuck Lever ret = copy_actor(desc, kaddr + base, len); 121094bb20bSChuck Lever base = 0; 122094bb20bSChuck Lever } else { 123094bb20bSChuck Lever if (pglen < len) 124094bb20bSChuck Lever len = pglen; 125094bb20bSChuck Lever ret = copy_actor(desc, kaddr, len); 126094bb20bSChuck Lever } 127094bb20bSChuck Lever flush_dcache_page(*ppage); 128094bb20bSChuck Lever kunmap_atomic(kaddr, KM_SKB_SUNRPC_DATA); 129094bb20bSChuck Lever copied += ret; 130094bb20bSChuck Lever if (ret != len || !desc->count) 131094bb20bSChuck Lever goto out; 132094bb20bSChuck Lever ppage++; 133094bb20bSChuck Lever } while ((pglen -= len) != 0); 134094bb20bSChuck Lever copy_tail: 135094bb20bSChuck Lever len = xdr->tail[0].iov_len; 136094bb20bSChuck Lever if (base < len) 137094bb20bSChuck Lever copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 138094bb20bSChuck Lever out: 139094bb20bSChuck Lever return copied; 140094bb20bSChuck Lever } 14112444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xdr_partial_copy_from_skb); 142094bb20bSChuck Lever 143094bb20bSChuck Lever /** 144094bb20bSChuck Lever * csum_partial_copy_to_xdr - checksum and copy data 145094bb20bSChuck Lever * @xdr: target XDR buffer 146094bb20bSChuck Lever * @skb: source skb 147094bb20bSChuck Lever * 148094bb20bSChuck Lever * We have set things up such that we perform the checksum of the UDP 149094bb20bSChuck Lever * packet in parallel with the copies into the RPC client iovec. -DaveM 150094bb20bSChuck Lever */ 151094bb20bSChuck Lever int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 152094bb20bSChuck Lever { 153dd456471SChuck Lever struct xdr_skb_reader desc; 154094bb20bSChuck Lever 155094bb20bSChuck Lever desc.skb = skb; 156094bb20bSChuck Lever desc.offset = sizeof(struct udphdr); 157094bb20bSChuck Lever desc.count = skb->len - desc.offset; 158094bb20bSChuck Lever 15960476372SHerbert Xu if (skb_csum_unnecessary(skb)) 160094bb20bSChuck Lever goto no_checksum; 161094bb20bSChuck Lever 162094bb20bSChuck Lever desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 1639d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 164094bb20bSChuck Lever return -1; 165094bb20bSChuck Lever if (desc.offset != skb->len) { 1665f92a738SAl Viro __wsum csum2; 167094bb20bSChuck Lever csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 168094bb20bSChuck Lever desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 169094bb20bSChuck Lever } 170094bb20bSChuck Lever if (desc.count) 171094bb20bSChuck Lever return -1; 172d3bc23e7SAl Viro if (csum_fold(desc.csum)) 173094bb20bSChuck Lever return -1; 17484fa7933SPatrick McHardy if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) 175fb286bb2SHerbert Xu netdev_rx_csum_fault(skb->dev); 176094bb20bSChuck Lever return 0; 177094bb20bSChuck Lever no_checksum: 1789d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 179094bb20bSChuck Lever return -1; 180094bb20bSChuck Lever if (desc.count) 181094bb20bSChuck Lever return -1; 182094bb20bSChuck Lever return 0; 183094bb20bSChuck Lever } 18412444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr); 185