1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2094bb20bSChuck Lever /* 3094bb20bSChuck Lever * linux/net/sunrpc/socklib.c 4094bb20bSChuck Lever * 5094bb20bSChuck Lever * Common socket helper routines for RPC client and server 6094bb20bSChuck Lever * 7094bb20bSChuck Lever * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 8094bb20bSChuck Lever */ 9094bb20bSChuck Lever 10fb286bb2SHerbert Xu #include <linux/compiler.h> 11fb286bb2SHerbert Xu #include <linux/netdevice.h> 125a0e3ad6STejun Heo #include <linux/gfp.h> 13fb286bb2SHerbert Xu #include <linux/skbuff.h> 14094bb20bSChuck Lever #include <linux/types.h> 15094bb20bSChuck Lever #include <linux/pagemap.h> 16094bb20bSChuck Lever #include <linux/udp.h> 17094bb20bSChuck Lever #include <linux/sunrpc/xdr.h> 18bc3b2d7fSPaul Gortmaker #include <linux/export.h> 19094bb20bSChuck Lever 20094bb20bSChuck Lever 21094bb20bSChuck Lever /** 229d292316SChuck Lever * xdr_skb_read_bits - copy some data bits from skb to internal buffer 23094bb20bSChuck Lever * @desc: sk_buff copy helper 24094bb20bSChuck Lever * @to: copy destination 25094bb20bSChuck Lever * @len: number of bytes to copy 26094bb20bSChuck Lever * 27094bb20bSChuck Lever * Possibly called several times to iterate over an sk_buff and copy 28094bb20bSChuck Lever * data out of it. 29094bb20bSChuck Lever */ 30550aebfeSTrond Myklebust static size_t 31550aebfeSTrond Myklebust xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 32094bb20bSChuck Lever { 33094bb20bSChuck Lever if (len > desc->count) 34094bb20bSChuck Lever len = desc->count; 359d292316SChuck Lever if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 36094bb20bSChuck Lever return 0; 37094bb20bSChuck Lever desc->count -= len; 38094bb20bSChuck Lever desc->offset += len; 39094bb20bSChuck Lever return len; 40094bb20bSChuck Lever } 41094bb20bSChuck Lever 42094bb20bSChuck Lever /** 439d292316SChuck Lever * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 44094bb20bSChuck Lever * @desc: sk_buff copy helper 45094bb20bSChuck Lever * @to: copy destination 46094bb20bSChuck Lever * @len: number of bytes to copy 47094bb20bSChuck Lever * 48094bb20bSChuck Lever * Same as skb_read_bits, but calculate a checksum at the same time. 49094bb20bSChuck Lever */ 50dd456471SChuck Lever static size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 51094bb20bSChuck Lever { 525f92a738SAl Viro unsigned int pos; 535f92a738SAl Viro __wsum csum2; 54094bb20bSChuck Lever 55094bb20bSChuck Lever if (len > desc->count) 56094bb20bSChuck Lever len = desc->count; 57094bb20bSChuck Lever pos = desc->offset; 58094bb20bSChuck Lever csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); 59094bb20bSChuck Lever desc->csum = csum_block_add(desc->csum, csum2, pos); 60094bb20bSChuck Lever desc->count -= len; 61094bb20bSChuck Lever desc->offset += len; 62094bb20bSChuck Lever return len; 63094bb20bSChuck Lever } 64094bb20bSChuck Lever 65094bb20bSChuck Lever /** 66094bb20bSChuck Lever * xdr_partial_copy_from_skb - copy data out of an skb 67094bb20bSChuck Lever * @xdr: target XDR buffer 68094bb20bSChuck Lever * @base: starting offset 69094bb20bSChuck Lever * @desc: sk_buff copy helper 70094bb20bSChuck Lever * @copy_actor: virtual method for copying data 71094bb20bSChuck Lever * 72094bb20bSChuck Lever */ 73ec846469STrond Myklebust static ssize_t 74ec846469STrond Myklebust xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) 75094bb20bSChuck Lever { 76094bb20bSChuck Lever struct page **ppage = xdr->pages; 77094bb20bSChuck Lever unsigned int len, pglen = xdr->page_len; 78094bb20bSChuck Lever ssize_t copied = 0; 79322e2efeSChuck Lever size_t ret; 80094bb20bSChuck Lever 81094bb20bSChuck Lever len = xdr->head[0].iov_len; 82094bb20bSChuck Lever if (base < len) { 83094bb20bSChuck Lever len -= base; 84094bb20bSChuck Lever ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 85094bb20bSChuck Lever copied += ret; 86094bb20bSChuck Lever if (ret != len || !desc->count) 87094bb20bSChuck Lever goto out; 88094bb20bSChuck Lever base = 0; 89094bb20bSChuck Lever } else 90094bb20bSChuck Lever base -= len; 91094bb20bSChuck Lever 92094bb20bSChuck Lever if (unlikely(pglen == 0)) 93094bb20bSChuck Lever goto copy_tail; 94094bb20bSChuck Lever if (unlikely(base >= pglen)) { 95094bb20bSChuck Lever base -= pglen; 96094bb20bSChuck Lever goto copy_tail; 97094bb20bSChuck Lever } 98094bb20bSChuck Lever if (base || xdr->page_base) { 99094bb20bSChuck Lever pglen -= base; 100094bb20bSChuck Lever base += xdr->page_base; 10109cbfeafSKirill A. Shutemov ppage += base >> PAGE_SHIFT; 10209cbfeafSKirill A. Shutemov base &= ~PAGE_MASK; 103094bb20bSChuck Lever } 104094bb20bSChuck Lever do { 105094bb20bSChuck Lever char *kaddr; 106094bb20bSChuck Lever 107094bb20bSChuck Lever /* ACL likes to be lazy in allocating pages - ACLs 108094bb20bSChuck Lever * are small by default but can get huge. */ 109431f6eb3STrond Myklebust if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) { 11052db6f9aSChuck Lever *ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN); 111094bb20bSChuck Lever if (unlikely(*ppage == NULL)) { 112094bb20bSChuck Lever if (copied == 0) 113094bb20bSChuck Lever copied = -ENOMEM; 114094bb20bSChuck Lever goto out; 115094bb20bSChuck Lever } 116094bb20bSChuck Lever } 117094bb20bSChuck Lever 11809cbfeafSKirill A. Shutemov len = PAGE_SIZE; 119b8541786SCong Wang kaddr = kmap_atomic(*ppage); 120094bb20bSChuck Lever if (base) { 121094bb20bSChuck Lever len -= base; 122094bb20bSChuck Lever if (pglen < len) 123094bb20bSChuck Lever len = pglen; 124094bb20bSChuck Lever ret = copy_actor(desc, kaddr + base, len); 125094bb20bSChuck Lever base = 0; 126094bb20bSChuck Lever } else { 127094bb20bSChuck Lever if (pglen < len) 128094bb20bSChuck Lever len = pglen; 129094bb20bSChuck Lever ret = copy_actor(desc, kaddr, len); 130094bb20bSChuck Lever } 131094bb20bSChuck Lever flush_dcache_page(*ppage); 132b8541786SCong Wang kunmap_atomic(kaddr); 133094bb20bSChuck Lever copied += ret; 134094bb20bSChuck Lever if (ret != len || !desc->count) 135094bb20bSChuck Lever goto out; 136094bb20bSChuck Lever ppage++; 137094bb20bSChuck Lever } while ((pglen -= len) != 0); 138094bb20bSChuck Lever copy_tail: 139094bb20bSChuck Lever len = xdr->tail[0].iov_len; 140094bb20bSChuck Lever if (base < len) 141094bb20bSChuck Lever copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 142094bb20bSChuck Lever out: 143094bb20bSChuck Lever return copied; 144094bb20bSChuck Lever } 145094bb20bSChuck Lever 146094bb20bSChuck Lever /** 147094bb20bSChuck Lever * csum_partial_copy_to_xdr - checksum and copy data 148094bb20bSChuck Lever * @xdr: target XDR buffer 149094bb20bSChuck Lever * @skb: source skb 150094bb20bSChuck Lever * 151094bb20bSChuck Lever * We have set things up such that we perform the checksum of the UDP 152094bb20bSChuck Lever * packet in parallel with the copies into the RPC client iovec. -DaveM 153094bb20bSChuck Lever */ 154094bb20bSChuck Lever int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 155094bb20bSChuck Lever { 156dd456471SChuck Lever struct xdr_skb_reader desc; 157094bb20bSChuck Lever 158094bb20bSChuck Lever desc.skb = skb; 1591da8c681SWillem de Bruijn desc.offset = 0; 160094bb20bSChuck Lever desc.count = skb->len - desc.offset; 161094bb20bSChuck Lever 16260476372SHerbert Xu if (skb_csum_unnecessary(skb)) 163094bb20bSChuck Lever goto no_checksum; 164094bb20bSChuck Lever 165094bb20bSChuck Lever desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 1669d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 167094bb20bSChuck Lever return -1; 168094bb20bSChuck Lever if (desc.offset != skb->len) { 1695f92a738SAl Viro __wsum csum2; 170094bb20bSChuck Lever csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 171094bb20bSChuck Lever desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 172094bb20bSChuck Lever } 173094bb20bSChuck Lever if (desc.count) 174094bb20bSChuck Lever return -1; 175d3bc23e7SAl Viro if (csum_fold(desc.csum)) 176094bb20bSChuck Lever return -1; 1777e3cead5STom Herbert if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 1787e3cead5STom Herbert !skb->csum_complete_sw) 1797fe50ac8SCong Wang netdev_rx_csum_fault(skb->dev, skb); 180094bb20bSChuck Lever return 0; 181094bb20bSChuck Lever no_checksum: 1829d292316SChuck Lever if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 183094bb20bSChuck Lever return -1; 184094bb20bSChuck Lever if (desc.count) 185094bb20bSChuck Lever return -1; 186094bb20bSChuck Lever return 0; 187094bb20bSChuck Lever } 18812444809S\"Talpey, Thomas\ EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr); 189