1094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever/* 2094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * linux/net/sunrpc/socklib.c 3094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 4094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * Common socket helper routines for RPC client and server 5094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 6094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 7094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever */ 8094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 9fb286bb2990a107009dbf25f6ffebeb7df77f9beHerbert Xu#include <linux/compiler.h> 10fb286bb2990a107009dbf25f6ffebeb7df77f9beHerbert Xu#include <linux/netdevice.h> 115a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h> 12fb286bb2990a107009dbf25f6ffebeb7df77f9beHerbert Xu#include <linux/skbuff.h> 13094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever#include <linux/types.h> 14094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever#include <linux/pagemap.h> 15094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever#include <linux/udp.h> 16094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever#include <linux/sunrpc/xdr.h> 17bc3b2d7fb9b014d75ebb79ba371a763dbab5e8cfPaul Gortmaker#include <linux/export.h> 18094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 19094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 20094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever/** 219d29231690925915015c21c1fff73c7118099843Chuck Lever * xdr_skb_read_bits - copy some data bits from skb to internal buffer 22094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @desc: sk_buff copy helper 23094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @to: copy destination 24094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @len: number of bytes to copy 25094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 26094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * Possibly called several times to iterate over an sk_buff and copy 27094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * data out of it. 28094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever */ 29dd4564715eae2c4136f278da9ae1c3bb5af3e509Chuck Leversize_t xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 30094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever{ 31094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (len > desc->count) 32094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = desc->count; 339d29231690925915015c21c1fff73c7118099843Chuck Lever if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 34094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return 0; 35094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc->count -= len; 36094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc->offset += len; 37094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return len; 38094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever} 391244480976d357447aeddd3f44977586bfa0462b\"Talpey, Thomas\EXPORT_SYMBOL_GPL(xdr_skb_read_bits); 40094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 41094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever/** 429d29231690925915015c21c1fff73c7118099843Chuck Lever * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 43094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @desc: sk_buff copy helper 44094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @to: copy destination 45094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @len: number of bytes to copy 46094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 47094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * Same as skb_read_bits, but calculate a checksum at the same time. 48094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever */ 49dd4564715eae2c4136f278da9ae1c3bb5af3e509Chuck Leverstatic size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 50094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever{ 515f92a7388a29594d6c365b23a48d4bb8299a3ea7Al Viro unsigned int pos; 525f92a7388a29594d6c365b23a48d4bb8299a3ea7Al Viro __wsum csum2; 53094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 54094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (len > desc->count) 55094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = desc->count; 56094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever pos = desc->offset; 57094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); 58094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc->csum = csum_block_add(desc->csum, csum2, pos); 59094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc->count -= len; 60094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc->offset += len; 61094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return len; 62094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever} 63094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 64094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever/** 65094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * xdr_partial_copy_from_skb - copy data out of an skb 66094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @xdr: target XDR buffer 67094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @base: starting offset 68094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @desc: sk_buff copy helper 69094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @copy_actor: virtual method for copying data 70094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 71094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever */ 72dd4564715eae2c4136f278da9ae1c3bb5af3e509Chuck Leverssize_t xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) 73094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever{ 74094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever struct page **ppage = xdr->pages; 75094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever unsigned int len, pglen = xdr->page_len; 76094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ssize_t copied = 0; 77322e2efe6224be5de2852a7fddfac5cf11317af3Chuck Lever size_t ret; 78094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 79094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = xdr->head[0].iov_len; 80094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (base < len) { 81094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len -= base; 82094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 83094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever copied += ret; 84094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (ret != len || !desc->count) 85094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto out; 86094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base = 0; 87094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } else 88094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base -= len; 89094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 90094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (unlikely(pglen == 0)) 91094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto copy_tail; 92094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (unlikely(base >= pglen)) { 93094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base -= pglen; 94094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto copy_tail; 95094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 96094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (base || xdr->page_base) { 97094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever pglen -= base; 98094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base += xdr->page_base; 99094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ppage += base >> PAGE_CACHE_SHIFT; 100094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base &= ~PAGE_CACHE_MASK; 101094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 102094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever do { 103094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever char *kaddr; 104094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 105094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever /* ACL likes to be lazy in allocating pages - ACLs 106094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * are small by default but can get huge. */ 107094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (unlikely(*ppage == NULL)) { 108094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever *ppage = alloc_page(GFP_ATOMIC); 109094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (unlikely(*ppage == NULL)) { 110094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (copied == 0) 111094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever copied = -ENOMEM; 112094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto out; 113094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 114094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 115094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 116094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = PAGE_CACHE_SIZE; 117b85417860172ff693dc115d7999805fc240cec1cCong Wang kaddr = kmap_atomic(*ppage); 118094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (base) { 119094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len -= base; 120094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (pglen < len) 121094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = pglen; 122094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ret = copy_actor(desc, kaddr + base, len); 123094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever base = 0; 124094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } else { 125094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (pglen < len) 126094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = pglen; 127094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ret = copy_actor(desc, kaddr, len); 128094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 129094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever flush_dcache_page(*ppage); 130b85417860172ff693dc115d7999805fc240cec1cCong Wang kunmap_atomic(kaddr); 131094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever copied += ret; 132094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (ret != len || !desc->count) 133094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto out; 134094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever ppage++; 135094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } while ((pglen -= len) != 0); 136094bb20b9fcab3a1652a77741caba6b78097d622Chuck Levercopy_tail: 137094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever len = xdr->tail[0].iov_len; 138094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (base < len) 139094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 140094bb20b9fcab3a1652a77741caba6b78097d622Chuck Leverout: 141094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return copied; 142094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever} 1431244480976d357447aeddd3f44977586bfa0462b\"Talpey, Thomas\EXPORT_SYMBOL_GPL(xdr_partial_copy_from_skb); 144094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 145094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever/** 146094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * csum_partial_copy_to_xdr - checksum and copy data 147094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @xdr: target XDR buffer 148094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * @skb: source skb 149094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * 150094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * We have set things up such that we perform the checksum of the UDP 151094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever * packet in parallel with the copies into the RPC client iovec. -DaveM 152094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever */ 153094bb20b9fcab3a1652a77741caba6b78097d622Chuck Leverint csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 154094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever{ 155dd4564715eae2c4136f278da9ae1c3bb5af3e509Chuck Lever struct xdr_skb_reader desc; 156094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 157094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc.skb = skb; 158094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc.offset = sizeof(struct udphdr); 159094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc.count = skb->len - desc.offset; 160094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 161604763722c655c7e3f31ecf6f7b4dafcd26a7a15Herbert Xu if (skb_csum_unnecessary(skb)) 162094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever goto no_checksum; 163094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever 164094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 1659d29231690925915015c21c1fff73c7118099843Chuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 166094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return -1; 167094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (desc.offset != skb->len) { 1685f92a7388a29594d6c365b23a48d4bb8299a3ea7Al Viro __wsum csum2; 169094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 170094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 171094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever } 172094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (desc.count) 173094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return -1; 174d3bc23e7ee9db8023dff5a86bb3b0069ed018789Al Viro if (csum_fold(desc.csum)) 175094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return -1; 1767e3cead5172927732f51fde77fef6f521e22f209Tom Herbert if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 1777e3cead5172927732f51fde77fef6f521e22f209Tom Herbert !skb->csum_complete_sw) 178fb286bb2990a107009dbf25f6ffebeb7df77f9beHerbert Xu netdev_rx_csum_fault(skb->dev); 179094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return 0; 180094bb20b9fcab3a1652a77741caba6b78097d622Chuck Leverno_checksum: 1819d29231690925915015c21c1fff73c7118099843Chuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 182094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return -1; 183094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever if (desc.count) 184094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return -1; 185094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever return 0; 186094bb20b9fcab3a1652a77741caba6b78097d622Chuck Lever} 1871244480976d357447aeddd3f44977586bfa0462b\"Talpey, Thomas\EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr); 188