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> 115a0e3ad6STejun Heo #include <linux/gfp.h> 12fb286bb2SHerbert Xu #include <linux/skbuff.h> 13094bb20bSChuck Lever #include <linux/types.h> 14094bb20bSChuck Lever #include <linux/pagemap.h> 15094bb20bSChuck Lever #include <linux/udp.h> 16094bb20bSChuck Lever #include <linux/sunrpc/xdr.h> 17bc3b2d7fSPaul Gortmaker #include <linux/export.h> 18094bb20bSChuck Lever 19094bb20bSChuck Lever 20094bb20bSChuck Lever /** 219d292316SChuck Lever * xdr_skb_read_bits - copy some data bits from skb to internal buffer 22094bb20bSChuck Lever * @desc: sk_buff copy helper 23094bb20bSChuck Lever * @to: copy destination 24094bb20bSChuck Lever * @len: number of bytes to copy 25094bb20bSChuck Lever * 26094bb20bSChuck Lever * Possibly called several times to iterate over an sk_buff and copy 27094bb20bSChuck Lever * data out of it. 28094bb20bSChuck Lever */ 29dd456471SChuck Lever size_t xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 30094bb20bSChuck Lever { 31094bb20bSChuck Lever if (len > desc->count) 32094bb20bSChuck Lever len = desc->count; 339d292316SChuck Lever if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 34094bb20bSChuck Lever return 0; 35094bb20bSChuck Lever desc->count -= len; 36094bb20bSChuck Lever desc->offset += len; 37094bb20bSChuck Lever return len; 38094bb20bSChuck Lever } 3912444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xdr_skb_read_bits); 40094bb20bSChuck Lever 41094bb20bSChuck Lever /** 429d292316SChuck Lever * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 43094bb20bSChuck Lever * @desc: sk_buff copy helper 44094bb20bSChuck Lever * @to: copy destination 45094bb20bSChuck Lever * @len: number of bytes to copy 46094bb20bSChuck Lever * 47094bb20bSChuck Lever * Same as skb_read_bits, but calculate a checksum at the same time. 48094bb20bSChuck Lever */ 49dd456471SChuck Lever static size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 50094bb20bSChuck Lever { 515f92a738SAl Viro unsigned int pos; 525f92a738SAl Viro __wsum csum2; 53094bb20bSChuck Lever 54094bb20bSChuck Lever if (len > desc->count) 55094bb20bSChuck Lever len = desc->count; 56094bb20bSChuck Lever pos = desc->offset; 57094bb20bSChuck Lever csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); 58094bb20bSChuck Lever desc->csum = csum_block_add(desc->csum, csum2, pos); 59094bb20bSChuck Lever desc->count -= len; 60094bb20bSChuck Lever desc->offset += len; 61094bb20bSChuck Lever return len; 62094bb20bSChuck Lever } 63094bb20bSChuck Lever 64094bb20bSChuck Lever /** 65094bb20bSChuck Lever * xdr_partial_copy_from_skb - copy data out of an skb 66094bb20bSChuck Lever * @xdr: target XDR buffer 67094bb20bSChuck Lever * @base: starting offset 68094bb20bSChuck Lever * @desc: sk_buff copy helper 69094bb20bSChuck Lever * @copy_actor: virtual method for copying data 70094bb20bSChuck Lever * 71094bb20bSChuck Lever */ 72dd456471SChuck 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) 73094bb20bSChuck Lever { 74094bb20bSChuck Lever struct page **ppage = xdr->pages; 75094bb20bSChuck Lever unsigned int len, pglen = xdr->page_len; 76094bb20bSChuck Lever ssize_t copied = 0; 77322e2efeSChuck Lever size_t ret; 78094bb20bSChuck Lever 79094bb20bSChuck Lever len = xdr->head[0].iov_len; 80094bb20bSChuck Lever if (base < len) { 81094bb20bSChuck Lever len -= base; 82094bb20bSChuck Lever ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 83094bb20bSChuck Lever copied += ret; 84094bb20bSChuck Lever if (ret != len || !desc->count) 85094bb20bSChuck Lever goto out; 86094bb20bSChuck Lever base = 0; 87094bb20bSChuck Lever } else 88094bb20bSChuck Lever base -= len; 89094bb20bSChuck Lever 90094bb20bSChuck Lever if (unlikely(pglen == 0)) 91094bb20bSChuck Lever goto copy_tail; 92094bb20bSChuck Lever if (unlikely(base >= pglen)) { 93094bb20bSChuck Lever base -= pglen; 94094bb20bSChuck Lever goto copy_tail; 95094bb20bSChuck Lever } 96094bb20bSChuck Lever if (base || xdr->page_base) { 97094bb20bSChuck Lever pglen -= base; 98094bb20bSChuck Lever base += xdr->page_base; 9909cbfeafSKirill A. Shutemov ppage += base >> PAGE_SHIFT; 10009cbfeafSKirill A. Shutemov base &= ~PAGE_MASK; 101094bb20bSChuck Lever } 102094bb20bSChuck Lever do { 103094bb20bSChuck Lever char *kaddr; 104094bb20bSChuck Lever 105094bb20bSChuck Lever /* ACL likes to be lazy in allocating pages - ACLs 106094bb20bSChuck Lever * are small by default but can get huge. */ 107094bb20bSChuck Lever if (unlikely(*ppage == NULL)) { 108094bb20bSChuck Lever *ppage = alloc_page(GFP_ATOMIC); 109094bb20bSChuck Lever if (unlikely(*ppage == NULL)) { 110094bb20bSChuck Lever if (copied == 0) 111094bb20bSChuck Lever copied = -ENOMEM; 112094bb20bSChuck Lever goto out; 113094bb20bSChuck Lever } 114094bb20bSChuck Lever } 115094bb20bSChuck Lever 11609cbfeafSKirill A. Shutemov len = PAGE_SIZE; 117b8541786SCong Wang kaddr = kmap_atomic(*ppage); 118094bb20bSChuck Lever if (base) { 119094bb20bSChuck Lever len -= base; 120094bb20bSChuck Lever if (pglen < len) 121094bb20bSChuck Lever len = pglen; 122094bb20bSChuck Lever ret = copy_actor(desc, kaddr + base, len); 123094bb20bSChuck Lever base = 0; 124094bb20bSChuck Lever } else { 125094bb20bSChuck Lever if (pglen < len) 126094bb20bSChuck Lever len = pglen; 127094bb20bSChuck Lever ret = copy_actor(desc, kaddr, len); 128094bb20bSChuck Lever } 129094bb20bSChuck Lever flush_dcache_page(*ppage); 130b8541786SCong Wang kunmap_atomic(kaddr); 131094bb20bSChuck Lever copied += ret; 132094bb20bSChuck Lever if (ret != len || !desc->count) 133094bb20bSChuck Lever goto out; 134094bb20bSChuck Lever ppage++; 135094bb20bSChuck Lever } while ((pglen -= len) != 0); 136094bb20bSChuck Lever copy_tail: 137094bb20bSChuck Lever len = xdr->tail[0].iov_len; 138094bb20bSChuck Lever if (base < len) 139094bb20bSChuck Lever copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 140094bb20bSChuck Lever out: 141094bb20bSChuck Lever return copied; 142094bb20bSChuck Lever } 14312444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(xdr_partial_copy_from_skb); 144094bb20bSChuck Lever 145094bb20bSChuck Lever /** 146094bb20bSChuck Lever * csum_partial_copy_to_xdr - checksum and copy data 147094bb20bSChuck Lever * @xdr: target XDR buffer 148094bb20bSChuck Lever * @skb: source skb 149094bb20bSChuck Lever * 150094bb20bSChuck Lever * We have set things up such that we perform the checksum of the UDP 151094bb20bSChuck Lever * packet in parallel with the copies into the RPC client iovec. -DaveM 152094bb20bSChuck Lever */ 153094bb20bSChuck Lever int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 154094bb20bSChuck Lever { 155dd456471SChuck Lever struct xdr_skb_reader desc; 156094bb20bSChuck Lever 157094bb20bSChuck Lever desc.skb = skb; 158094bb20bSChuck Lever desc.offset = sizeof(struct udphdr); 159094bb20bSChuck Lever desc.count = skb->len - desc.offset; 160094bb20bSChuck Lever 16160476372SHerbert Xu if (skb_csum_unnecessary(skb)) 162094bb20bSChuck Lever goto no_checksum; 163094bb20bSChuck Lever 164094bb20bSChuck Lever desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 1659d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 166094bb20bSChuck Lever return -1; 167094bb20bSChuck Lever if (desc.offset != skb->len) { 1685f92a738SAl Viro __wsum csum2; 169094bb20bSChuck Lever csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 170094bb20bSChuck Lever desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 171094bb20bSChuck Lever } 172094bb20bSChuck Lever if (desc.count) 173094bb20bSChuck Lever return -1; 174d3bc23e7SAl Viro if (csum_fold(desc.csum)) 175094bb20bSChuck Lever return -1; 1767e3cead5STom Herbert if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 1777e3cead5STom Herbert !skb->csum_complete_sw) 178fb286bb2SHerbert Xu netdev_rx_csum_fault(skb->dev); 179094bb20bSChuck Lever return 0; 180094bb20bSChuck Lever no_checksum: 1819d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 182094bb20bSChuck Lever return -1; 183094bb20bSChuck Lever if (desc.count) 184094bb20bSChuck Lever return -1; 185094bb20bSChuck Lever return 0; 186094bb20bSChuck Lever } 18712444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr); 188