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);
|