Merge tag 'xtensa-20210902' of git://github.com/jcmvbkbc/linux-xtensa
[linux-2.6-microblaze.git] / net / sunrpc / socklib.c
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/msg_prot.h>
18 #include <linux/sunrpc/xdr.h>
19 #include <linux/export.h>
20
21 #include "socklib.h"
22
23 /*
24  * Helper structure for copying from an sk_buff.
25  */
26 struct xdr_skb_reader {
27         struct sk_buff  *skb;
28         unsigned int    offset;
29         size_t          count;
30         __wsum          csum;
31 };
32
33 typedef size_t (*xdr_skb_read_actor)(struct xdr_skb_reader *desc, void *to,
34                                      size_t len);
35
36 /**
37  * xdr_skb_read_bits - copy some data bits from skb to internal buffer
38  * @desc: sk_buff copy helper
39  * @to: copy destination
40  * @len: number of bytes to copy
41  *
42  * Possibly called several times to iterate over an sk_buff and copy
43  * data out of it.
44  */
45 static size_t
46 xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len)
47 {
48         if (len > desc->count)
49                 len = desc->count;
50         if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len)))
51                 return 0;
52         desc->count -= len;
53         desc->offset += len;
54         return len;
55 }
56
57 /**
58  * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer
59  * @desc: sk_buff copy helper
60  * @to: copy destination
61  * @len: number of bytes to copy
62  *
63  * Same as skb_read_bits, but calculate a checksum at the same time.
64  */
65 static size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len)
66 {
67         unsigned int pos;
68         __wsum csum2;
69
70         if (len > desc->count)
71                 len = desc->count;
72         pos = desc->offset;
73         csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len);
74         desc->csum = csum_block_add(desc->csum, csum2, pos);
75         desc->count -= len;
76         desc->offset += len;
77         return len;
78 }
79
80 /**
81  * xdr_partial_copy_from_skb - copy data out of an skb
82  * @xdr: target XDR buffer
83  * @base: starting offset
84  * @desc: sk_buff copy helper
85  * @copy_actor: virtual method for copying data
86  *
87  */
88 static ssize_t
89 xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor)
90 {
91         struct page     **ppage = xdr->pages;
92         unsigned int    len, pglen = xdr->page_len;
93         ssize_t         copied = 0;
94         size_t          ret;
95
96         len = xdr->head[0].iov_len;
97         if (base < len) {
98                 len -= base;
99                 ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len);
100                 copied += ret;
101                 if (ret != len || !desc->count)
102                         goto out;
103                 base = 0;
104         } else
105                 base -= len;
106
107         if (unlikely(pglen == 0))
108                 goto copy_tail;
109         if (unlikely(base >= pglen)) {
110                 base -= pglen;
111                 goto copy_tail;
112         }
113         if (base || xdr->page_base) {
114                 pglen -= base;
115                 base += xdr->page_base;
116                 ppage += base >> PAGE_SHIFT;
117                 base &= ~PAGE_MASK;
118         }
119         do {
120                 char *kaddr;
121
122                 /* ACL likes to be lazy in allocating pages - ACLs
123                  * are small by default but can get huge. */
124                 if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) {
125                         *ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
126                         if (unlikely(*ppage == NULL)) {
127                                 if (copied == 0)
128                                         copied = -ENOMEM;
129                                 goto out;
130                         }
131                 }
132
133                 len = PAGE_SIZE;
134                 kaddr = kmap_atomic(*ppage);
135                 if (base) {
136                         len -= base;
137                         if (pglen < len)
138                                 len = pglen;
139                         ret = copy_actor(desc, kaddr + base, len);
140                         base = 0;
141                 } else {
142                         if (pglen < len)
143                                 len = pglen;
144                         ret = copy_actor(desc, kaddr, len);
145                 }
146                 flush_dcache_page(*ppage);
147                 kunmap_atomic(kaddr);
148                 copied += ret;
149                 if (ret != len || !desc->count)
150                         goto out;
151                 ppage++;
152         } while ((pglen -= len) != 0);
153 copy_tail:
154         len = xdr->tail[0].iov_len;
155         if (base < len)
156                 copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base);
157 out:
158         return copied;
159 }
160
161 /**
162  * csum_partial_copy_to_xdr - checksum and copy data
163  * @xdr: target XDR buffer
164  * @skb: source skb
165  *
166  * We have set things up such that we perform the checksum of the UDP
167  * packet in parallel with the copies into the RPC client iovec.  -DaveM
168  */
169 int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
170 {
171         struct xdr_skb_reader   desc;
172
173         desc.skb = skb;
174         desc.offset = 0;
175         desc.count = skb->len - desc.offset;
176
177         if (skb_csum_unnecessary(skb))
178                 goto no_checksum;
179
180         desc.csum = csum_partial(skb->data, desc.offset, skb->csum);
181         if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0)
182                 return -1;
183         if (desc.offset != skb->len) {
184                 __wsum csum2;
185                 csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0);
186                 desc.csum = csum_block_add(desc.csum, csum2, desc.offset);
187         }
188         if (desc.count)
189                 return -1;
190         if (csum_fold(desc.csum))
191                 return -1;
192         if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
193             !skb->csum_complete_sw)
194                 netdev_rx_csum_fault(skb->dev, skb);
195         return 0;
196 no_checksum:
197         if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0)
198                 return -1;
199         if (desc.count)
200                 return -1;
201         return 0;
202 }
203 EXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr);
204
205 static inline int xprt_sendmsg(struct socket *sock, struct msghdr *msg,
206                                size_t seek)
207 {
208         if (seek)
209                 iov_iter_advance(&msg->msg_iter, seek);
210         return sock_sendmsg(sock, msg);
211 }
212
213 static int xprt_send_kvec(struct socket *sock, struct msghdr *msg,
214                           struct kvec *vec, size_t seek)
215 {
216         iov_iter_kvec(&msg->msg_iter, WRITE, vec, 1, vec->iov_len);
217         return xprt_sendmsg(sock, msg, seek);
218 }
219
220 static int xprt_send_pagedata(struct socket *sock, struct msghdr *msg,
221                               struct xdr_buf *xdr, size_t base)
222 {
223         int err;
224
225         err = xdr_alloc_bvec(xdr, GFP_KERNEL);
226         if (err < 0)
227                 return err;
228
229         iov_iter_bvec(&msg->msg_iter, WRITE, xdr->bvec, xdr_buf_pagecount(xdr),
230                       xdr->page_len + xdr->page_base);
231         return xprt_sendmsg(sock, msg, base + xdr->page_base);
232 }
233
234 /* Common case:
235  *  - stream transport
236  *  - sending from byte 0 of the message
237  *  - the message is wholly contained in @xdr's head iovec
238  */
239 static int xprt_send_rm_and_kvec(struct socket *sock, struct msghdr *msg,
240                                  rpc_fraghdr marker, struct kvec *vec,
241                                  size_t base)
242 {
243         struct kvec iov[2] = {
244                 [0] = {
245                         .iov_base       = &marker,
246                         .iov_len        = sizeof(marker)
247                 },
248                 [1] = *vec,
249         };
250         size_t len = iov[0].iov_len + iov[1].iov_len;
251
252         iov_iter_kvec(&msg->msg_iter, WRITE, iov, 2, len);
253         return xprt_sendmsg(sock, msg, base);
254 }
255
256 /**
257  * xprt_sock_sendmsg - write an xdr_buf directly to a socket
258  * @sock: open socket to send on
259  * @msg: socket message metadata
260  * @xdr: xdr_buf containing this request
261  * @base: starting position in the buffer
262  * @marker: stream record marker field
263  * @sent_p: return the total number of bytes successfully queued for sending
264  *
265  * Return values:
266  *   On success, returns zero and fills in @sent_p.
267  *   %-ENOTSOCK if  @sock is not a struct socket.
268  */
269 int xprt_sock_sendmsg(struct socket *sock, struct msghdr *msg,
270                       struct xdr_buf *xdr, unsigned int base,
271                       rpc_fraghdr marker, unsigned int *sent_p)
272 {
273         unsigned int rmsize = marker ? sizeof(marker) : 0;
274         unsigned int remainder = rmsize + xdr->len - base;
275         unsigned int want;
276         int err = 0;
277
278         *sent_p = 0;
279
280         if (unlikely(!sock))
281                 return -ENOTSOCK;
282
283         msg->msg_flags |= MSG_MORE;
284         want = xdr->head[0].iov_len + rmsize;
285         if (base < want) {
286                 unsigned int len = want - base;
287
288                 remainder -= len;
289                 if (remainder == 0)
290                         msg->msg_flags &= ~MSG_MORE;
291                 if (rmsize)
292                         err = xprt_send_rm_and_kvec(sock, msg, marker,
293                                                     &xdr->head[0], base);
294                 else
295                         err = xprt_send_kvec(sock, msg, &xdr->head[0], base);
296                 if (remainder == 0 || err != len)
297                         goto out;
298                 *sent_p += err;
299                 base = 0;
300         } else {
301                 base -= want;
302         }
303
304         if (base < xdr->page_len) {
305                 unsigned int len = xdr->page_len - base;
306
307                 remainder -= len;
308                 if (remainder == 0)
309                         msg->msg_flags &= ~MSG_MORE;
310                 err = xprt_send_pagedata(sock, msg, xdr, base);
311                 if (remainder == 0 || err != len)
312                         goto out;
313                 *sent_p += err;
314                 base = 0;
315         } else {
316                 base -= xdr->page_len;
317         }
318
319         if (base >= xdr->tail[0].iov_len)
320                 return 0;
321         msg->msg_flags &= ~MSG_MORE;
322         err = xprt_send_kvec(sock, msg, &xdr->tail[0], base);
323 out:
324         if (err > 0) {
325                 *sent_p += err;
326                 err = 0;
327         }
328         return err;
329 }