Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * linux/net/sunrpc/socklib.c 4 : : * 5 : : * Common socket helper routines for RPC client and server 6 : : * 7 : : * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 8 : : */ 9 : : 10 : : #include <linux/compiler.h> 11 : : #include <linux/netdevice.h> 12 : : #include <linux/gfp.h> 13 : : #include <linux/skbuff.h> 14 : : #include <linux/types.h> 15 : : #include <linux/pagemap.h> 16 : : #include <linux/udp.h> 17 : : #include <linux/sunrpc/xdr.h> 18 : : #include <linux/export.h> 19 : : 20 : : 21 : : /** 22 : : * xdr_skb_read_bits - copy some data bits from skb to internal buffer 23 : : * @desc: sk_buff copy helper 24 : : * @to: copy destination 25 : : * @len: number of bytes to copy 26 : : * 27 : : * Possibly called several times to iterate over an sk_buff and copy 28 : : * data out of it. 29 : : */ 30 : : static size_t 31 : 0 : xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 32 : : { 33 : 0 : if (len > desc->count) 34 : : len = desc->count; 35 : 0 : if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 36 : : return 0; 37 : 0 : desc->count -= len; 38 : 0 : desc->offset += len; 39 : 0 : return len; 40 : : } 41 : : 42 : : /** 43 : : * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 44 : : * @desc: sk_buff copy helper 45 : : * @to: copy destination 46 : : * @len: number of bytes to copy 47 : : * 48 : : * Same as skb_read_bits, but calculate a checksum at the same time. 49 : : */ 50 : 0 : static size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 51 : : { 52 : : unsigned int pos; 53 : : __wsum csum2; 54 : : 55 : 0 : if (len > desc->count) 56 : : len = desc->count; 57 : 0 : pos = desc->offset; 58 : 0 : csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); 59 : 0 : desc->csum = csum_block_add(desc->csum, csum2, pos); 60 : 0 : desc->count -= len; 61 : 0 : desc->offset += len; 62 : 0 : return len; 63 : : } 64 : : 65 : : /** 66 : : * xdr_partial_copy_from_skb - copy data out of an skb 67 : : * @xdr: target XDR buffer 68 : : * @base: starting offset 69 : : * @desc: sk_buff copy helper 70 : : * @copy_actor: virtual method for copying data 71 : : * 72 : : */ 73 : : static ssize_t 74 : 0 : xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) 75 : : { 76 : 0 : struct page **ppage = xdr->pages; 77 : 0 : unsigned int len, pglen = xdr->page_len; 78 : : ssize_t copied = 0; 79 : : size_t ret; 80 : : 81 : 0 : len = xdr->head[0].iov_len; 82 : 0 : if (base < len) { 83 : 0 : len -= base; 84 : 0 : ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 85 : 0 : copied += ret; 86 : 0 : if (ret != len || !desc->count) 87 : : goto out; 88 : : base = 0; 89 : : } else 90 : 0 : base -= len; 91 : : 92 : 0 : if (unlikely(pglen == 0)) 93 : : goto copy_tail; 94 : 0 : if (unlikely(base >= pglen)) { 95 : 0 : base -= pglen; 96 : 0 : goto copy_tail; 97 : : } 98 : 0 : if (base || xdr->page_base) { 99 : 0 : pglen -= base; 100 : 0 : base += xdr->page_base; 101 : 0 : ppage += base >> PAGE_SHIFT; 102 : 0 : base &= ~PAGE_MASK; 103 : : } 104 : : do { 105 : : char *kaddr; 106 : : 107 : : /* ACL likes to be lazy in allocating pages - ACLs 108 : : * are small by default but can get huge. */ 109 : 0 : if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) { 110 : 0 : *ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN); 111 : 0 : if (unlikely(*ppage == NULL)) { 112 : 0 : if (copied == 0) 113 : : copied = -ENOMEM; 114 : : goto out; 115 : : } 116 : : } 117 : : 118 : : len = PAGE_SIZE; 119 : 0 : kaddr = kmap_atomic(*ppage); 120 : 0 : if (base) { 121 : 0 : len -= base; 122 : 0 : if (pglen < len) 123 : : len = pglen; 124 : 0 : ret = copy_actor(desc, kaddr + base, len); 125 : : base = 0; 126 : : } else { 127 : 0 : if (pglen < len) 128 : : len = pglen; 129 : 0 : ret = copy_actor(desc, kaddr, len); 130 : : } 131 : 0 : flush_dcache_page(*ppage); 132 : : kunmap_atomic(kaddr); 133 : 0 : copied += ret; 134 : 0 : if (ret != len || !desc->count) 135 : : goto out; 136 : 0 : ppage++; 137 : 0 : } while ((pglen -= len) != 0); 138 : : copy_tail: 139 : 0 : len = xdr->tail[0].iov_len; 140 : 0 : if (base < len) 141 : 0 : copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 142 : : out: 143 : 0 : return copied; 144 : : } 145 : : 146 : : /** 147 : : * csum_partial_copy_to_xdr - checksum and copy data 148 : : * @xdr: target XDR buffer 149 : : * @skb: source skb 150 : : * 151 : : * We have set things up such that we perform the checksum of the UDP 152 : : * packet in parallel with the copies into the RPC client iovec. -DaveM 153 : : */ 154 : 0 : int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 155 : : { 156 : : struct xdr_skb_reader desc; 157 : : 158 : 0 : desc.skb = skb; 159 : 0 : desc.offset = 0; 160 : 0 : desc.count = skb->len - desc.offset; 161 : : 162 : 0 : if (skb_csum_unnecessary(skb)) 163 : : goto no_checksum; 164 : : 165 : 0 : desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 166 : 0 : if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 167 : : return -1; 168 : 0 : if (desc.offset != skb->len) { 169 : : __wsum csum2; 170 : 0 : csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 171 : 0 : desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 172 : : } 173 : 0 : if (desc.count) 174 : : return -1; 175 : 0 : if (csum_fold(desc.csum)) 176 : : return -1; 177 : 0 : if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 178 : 0 : !skb->csum_complete_sw) 179 : 0 : netdev_rx_csum_fault(skb->dev, skb); 180 : : return 0; 181 : : no_checksum: 182 : 0 : if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 183 : : return -1; 184 : 0 : if (desc.count) 185 : : return -1; 186 : 0 : return 0; 187 : : } 188 : : EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr);