.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/net/sunrpc/socklib.c |
---|
3 | 4 | * |
---|
.. | .. |
---|
13 | 14 | #include <linux/types.h> |
---|
14 | 15 | #include <linux/pagemap.h> |
---|
15 | 16 | #include <linux/udp.h> |
---|
| 17 | +#include <linux/sunrpc/msg_prot.h> |
---|
16 | 18 | #include <linux/sunrpc/xdr.h> |
---|
17 | 19 | #include <linux/export.h> |
---|
18 | 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); |
---|
19 | 35 | |
---|
20 | 36 | /** |
---|
21 | 37 | * xdr_skb_read_bits - copy some data bits from skb to internal buffer |
---|
.. | .. |
---|
26 | 42 | * Possibly called several times to iterate over an sk_buff and copy |
---|
27 | 43 | * data out of it. |
---|
28 | 44 | */ |
---|
29 | | -size_t xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) |
---|
| 45 | +static size_t |
---|
| 46 | +xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) |
---|
30 | 47 | { |
---|
31 | 48 | if (len > desc->count) |
---|
32 | 49 | len = desc->count; |
---|
.. | .. |
---|
36 | 53 | desc->offset += len; |
---|
37 | 54 | return len; |
---|
38 | 55 | } |
---|
39 | | -EXPORT_SYMBOL_GPL(xdr_skb_read_bits); |
---|
40 | 56 | |
---|
41 | 57 | /** |
---|
42 | 58 | * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer |
---|
.. | .. |
---|
54 | 70 | if (len > desc->count) |
---|
55 | 71 | len = desc->count; |
---|
56 | 72 | pos = desc->offset; |
---|
57 | | - csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len, 0); |
---|
| 73 | + csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len); |
---|
58 | 74 | desc->csum = csum_block_add(desc->csum, csum2, pos); |
---|
59 | 75 | desc->count -= len; |
---|
60 | 76 | desc->offset += len; |
---|
.. | .. |
---|
69 | 85 | * @copy_actor: virtual method for copying data |
---|
70 | 86 | * |
---|
71 | 87 | */ |
---|
72 | | -ssize_t xdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) |
---|
| 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) |
---|
73 | 90 | { |
---|
74 | 91 | struct page **ppage = xdr->pages; |
---|
75 | 92 | unsigned int len, pglen = xdr->page_len; |
---|
.. | .. |
---|
104 | 121 | |
---|
105 | 122 | /* ACL likes to be lazy in allocating pages - ACLs |
---|
106 | 123 | * are small by default but can get huge. */ |
---|
107 | | - if (unlikely(*ppage == NULL)) { |
---|
108 | | - *ppage = alloc_page(GFP_ATOMIC); |
---|
| 124 | + if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) { |
---|
| 125 | + *ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN); |
---|
109 | 126 | if (unlikely(*ppage == NULL)) { |
---|
110 | 127 | if (copied == 0) |
---|
111 | 128 | copied = -ENOMEM; |
---|
.. | .. |
---|
140 | 157 | out: |
---|
141 | 158 | return copied; |
---|
142 | 159 | } |
---|
143 | | -EXPORT_SYMBOL_GPL(xdr_partial_copy_from_skb); |
---|
144 | 160 | |
---|
145 | 161 | /** |
---|
146 | 162 | * csum_partial_copy_to_xdr - checksum and copy data |
---|
.. | .. |
---|
175 | 191 | return -1; |
---|
176 | 192 | if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && |
---|
177 | 193 | !skb->csum_complete_sw) |
---|
178 | | - netdev_rx_csum_fault(skb->dev); |
---|
| 194 | + netdev_rx_csum_fault(skb->dev, skb); |
---|
179 | 195 | return 0; |
---|
180 | 196 | no_checksum: |
---|
181 | 197 | if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) |
---|
.. | .. |
---|
185 | 201 | return 0; |
---|
186 | 202 | } |
---|
187 | 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 | +} |
---|