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