18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/net/sunrpc/socklib.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Common socket helper routines for RPC client and server 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/compiler.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/gfp.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 168c2ecf20Sopenharmony_ci#include <linux/udp.h> 178c2ecf20Sopenharmony_ci#include <linux/sunrpc/msg_prot.h> 188c2ecf20Sopenharmony_ci#include <linux/sunrpc/xdr.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "socklib.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Helper structure for copying from an sk_buff. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cistruct xdr_skb_reader { 278c2ecf20Sopenharmony_ci struct sk_buff *skb; 288c2ecf20Sopenharmony_ci unsigned int offset; 298c2ecf20Sopenharmony_ci size_t count; 308c2ecf20Sopenharmony_ci __wsum csum; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_citypedef size_t (*xdr_skb_read_actor)(struct xdr_skb_reader *desc, void *to, 348c2ecf20Sopenharmony_ci size_t len); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/** 378c2ecf20Sopenharmony_ci * xdr_skb_read_bits - copy some data bits from skb to internal buffer 388c2ecf20Sopenharmony_ci * @desc: sk_buff copy helper 398c2ecf20Sopenharmony_ci * @to: copy destination 408c2ecf20Sopenharmony_ci * @len: number of bytes to copy 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * Possibly called several times to iterate over an sk_buff and copy 438c2ecf20Sopenharmony_ci * data out of it. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic size_t 468c2ecf20Sopenharmony_cixdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci if (len > desc->count) 498c2ecf20Sopenharmony_ci len = desc->count; 508c2ecf20Sopenharmony_ci if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len))) 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci desc->count -= len; 538c2ecf20Sopenharmony_ci desc->offset += len; 548c2ecf20Sopenharmony_ci return len; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/** 588c2ecf20Sopenharmony_ci * xdr_skb_read_and_csum_bits - copy and checksum from skb to buffer 598c2ecf20Sopenharmony_ci * @desc: sk_buff copy helper 608c2ecf20Sopenharmony_ci * @to: copy destination 618c2ecf20Sopenharmony_ci * @len: number of bytes to copy 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Same as skb_read_bits, but calculate a checksum at the same time. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic size_t xdr_skb_read_and_csum_bits(struct xdr_skb_reader *desc, void *to, size_t len) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned int pos; 688c2ecf20Sopenharmony_ci __wsum csum2; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (len > desc->count) 718c2ecf20Sopenharmony_ci len = desc->count; 728c2ecf20Sopenharmony_ci pos = desc->offset; 738c2ecf20Sopenharmony_ci csum2 = skb_copy_and_csum_bits(desc->skb, pos, to, len); 748c2ecf20Sopenharmony_ci desc->csum = csum_block_add(desc->csum, csum2, pos); 758c2ecf20Sopenharmony_ci desc->count -= len; 768c2ecf20Sopenharmony_ci desc->offset += len; 778c2ecf20Sopenharmony_ci return len; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * xdr_partial_copy_from_skb - copy data out of an skb 828c2ecf20Sopenharmony_ci * @xdr: target XDR buffer 838c2ecf20Sopenharmony_ci * @base: starting offset 848c2ecf20Sopenharmony_ci * @desc: sk_buff copy helper 858c2ecf20Sopenharmony_ci * @copy_actor: virtual method for copying data 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistatic ssize_t 898c2ecf20Sopenharmony_cixdr_partial_copy_from_skb(struct xdr_buf *xdr, unsigned int base, struct xdr_skb_reader *desc, xdr_skb_read_actor copy_actor) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct page **ppage = xdr->pages; 928c2ecf20Sopenharmony_ci unsigned int len, pglen = xdr->page_len; 938c2ecf20Sopenharmony_ci ssize_t copied = 0; 948c2ecf20Sopenharmony_ci size_t ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci len = xdr->head[0].iov_len; 978c2ecf20Sopenharmony_ci if (base < len) { 988c2ecf20Sopenharmony_ci len -= base; 998c2ecf20Sopenharmony_ci ret = copy_actor(desc, (char *)xdr->head[0].iov_base + base, len); 1008c2ecf20Sopenharmony_ci copied += ret; 1018c2ecf20Sopenharmony_ci if (ret != len || !desc->count) 1028c2ecf20Sopenharmony_ci goto out; 1038c2ecf20Sopenharmony_ci base = 0; 1048c2ecf20Sopenharmony_ci } else 1058c2ecf20Sopenharmony_ci base -= len; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (unlikely(pglen == 0)) 1088c2ecf20Sopenharmony_ci goto copy_tail; 1098c2ecf20Sopenharmony_ci if (unlikely(base >= pglen)) { 1108c2ecf20Sopenharmony_ci base -= pglen; 1118c2ecf20Sopenharmony_ci goto copy_tail; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci if (base || xdr->page_base) { 1148c2ecf20Sopenharmony_ci pglen -= base; 1158c2ecf20Sopenharmony_ci base += xdr->page_base; 1168c2ecf20Sopenharmony_ci ppage += base >> PAGE_SHIFT; 1178c2ecf20Sopenharmony_ci base &= ~PAGE_MASK; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci do { 1208c2ecf20Sopenharmony_ci char *kaddr; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* ACL likes to be lazy in allocating pages - ACLs 1238c2ecf20Sopenharmony_ci * are small by default but can get huge. */ 1248c2ecf20Sopenharmony_ci if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) { 1258c2ecf20Sopenharmony_ci *ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN); 1268c2ecf20Sopenharmony_ci if (unlikely(*ppage == NULL)) { 1278c2ecf20Sopenharmony_ci if (copied == 0) 1288c2ecf20Sopenharmony_ci copied = -ENOMEM; 1298c2ecf20Sopenharmony_ci goto out; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci len = PAGE_SIZE; 1348c2ecf20Sopenharmony_ci kaddr = kmap_atomic(*ppage); 1358c2ecf20Sopenharmony_ci if (base) { 1368c2ecf20Sopenharmony_ci len -= base; 1378c2ecf20Sopenharmony_ci if (pglen < len) 1388c2ecf20Sopenharmony_ci len = pglen; 1398c2ecf20Sopenharmony_ci ret = copy_actor(desc, kaddr + base, len); 1408c2ecf20Sopenharmony_ci base = 0; 1418c2ecf20Sopenharmony_ci } else { 1428c2ecf20Sopenharmony_ci if (pglen < len) 1438c2ecf20Sopenharmony_ci len = pglen; 1448c2ecf20Sopenharmony_ci ret = copy_actor(desc, kaddr, len); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci flush_dcache_page(*ppage); 1478c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 1488c2ecf20Sopenharmony_ci copied += ret; 1498c2ecf20Sopenharmony_ci if (ret != len || !desc->count) 1508c2ecf20Sopenharmony_ci goto out; 1518c2ecf20Sopenharmony_ci ppage++; 1528c2ecf20Sopenharmony_ci } while ((pglen -= len) != 0); 1538c2ecf20Sopenharmony_cicopy_tail: 1548c2ecf20Sopenharmony_ci len = xdr->tail[0].iov_len; 1558c2ecf20Sopenharmony_ci if (base < len) 1568c2ecf20Sopenharmony_ci copied += copy_actor(desc, (char *)xdr->tail[0].iov_base + base, len - base); 1578c2ecf20Sopenharmony_ciout: 1588c2ecf20Sopenharmony_ci return copied; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * csum_partial_copy_to_xdr - checksum and copy data 1638c2ecf20Sopenharmony_ci * @xdr: target XDR buffer 1648c2ecf20Sopenharmony_ci * @skb: source skb 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * We have set things up such that we perform the checksum of the UDP 1678c2ecf20Sopenharmony_ci * packet in parallel with the copies into the RPC client iovec. -DaveM 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ciint csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct xdr_skb_reader desc; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci desc.skb = skb; 1748c2ecf20Sopenharmony_ci desc.offset = 0; 1758c2ecf20Sopenharmony_ci desc.count = skb->len - desc.offset; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (skb_csum_unnecessary(skb)) 1788c2ecf20Sopenharmony_ci goto no_checksum; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci desc.csum = csum_partial(skb->data, desc.offset, skb->csum); 1818c2ecf20Sopenharmony_ci if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_and_csum_bits) < 0) 1828c2ecf20Sopenharmony_ci return -1; 1838c2ecf20Sopenharmony_ci if (desc.offset != skb->len) { 1848c2ecf20Sopenharmony_ci __wsum csum2; 1858c2ecf20Sopenharmony_ci csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0); 1868c2ecf20Sopenharmony_ci desc.csum = csum_block_add(desc.csum, csum2, desc.offset); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci if (desc.count) 1898c2ecf20Sopenharmony_ci return -1; 1908c2ecf20Sopenharmony_ci if (csum_fold(desc.csum)) 1918c2ecf20Sopenharmony_ci return -1; 1928c2ecf20Sopenharmony_ci if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) && 1938c2ecf20Sopenharmony_ci !skb->csum_complete_sw) 1948c2ecf20Sopenharmony_ci netdev_rx_csum_fault(skb->dev, skb); 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_cino_checksum: 1978c2ecf20Sopenharmony_ci if (xdr_partial_copy_from_skb(xdr, 0, &desc, xdr_skb_read_bits) < 0) 1988c2ecf20Sopenharmony_ci return -1; 1998c2ecf20Sopenharmony_ci if (desc.count) 2008c2ecf20Sopenharmony_ci return -1; 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(csum_partial_copy_to_xdr); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic inline int xprt_sendmsg(struct socket *sock, struct msghdr *msg, 2068c2ecf20Sopenharmony_ci size_t seek) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (seek) 2098c2ecf20Sopenharmony_ci iov_iter_advance(&msg->msg_iter, seek); 2108c2ecf20Sopenharmony_ci return sock_sendmsg(sock, msg); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int xprt_send_kvec(struct socket *sock, struct msghdr *msg, 2148c2ecf20Sopenharmony_ci struct kvec *vec, size_t seek) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci iov_iter_kvec(&msg->msg_iter, WRITE, vec, 1, vec->iov_len); 2178c2ecf20Sopenharmony_ci return xprt_sendmsg(sock, msg, seek); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int xprt_send_pagedata(struct socket *sock, struct msghdr *msg, 2218c2ecf20Sopenharmony_ci struct xdr_buf *xdr, size_t base) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int err; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci err = xdr_alloc_bvec(xdr, GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (err < 0) 2278c2ecf20Sopenharmony_ci return err; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci iov_iter_bvec(&msg->msg_iter, WRITE, xdr->bvec, xdr_buf_pagecount(xdr), 2308c2ecf20Sopenharmony_ci xdr->page_len + xdr->page_base); 2318c2ecf20Sopenharmony_ci return xprt_sendmsg(sock, msg, base + xdr->page_base); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* Common case: 2358c2ecf20Sopenharmony_ci * - stream transport 2368c2ecf20Sopenharmony_ci * - sending from byte 0 of the message 2378c2ecf20Sopenharmony_ci * - the message is wholly contained in @xdr's head iovec 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic int xprt_send_rm_and_kvec(struct socket *sock, struct msghdr *msg, 2408c2ecf20Sopenharmony_ci rpc_fraghdr marker, struct kvec *vec, 2418c2ecf20Sopenharmony_ci size_t base) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct kvec iov[2] = { 2448c2ecf20Sopenharmony_ci [0] = { 2458c2ecf20Sopenharmony_ci .iov_base = &marker, 2468c2ecf20Sopenharmony_ci .iov_len = sizeof(marker) 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci [1] = *vec, 2498c2ecf20Sopenharmony_ci }; 2508c2ecf20Sopenharmony_ci size_t len = iov[0].iov_len + iov[1].iov_len; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci iov_iter_kvec(&msg->msg_iter, WRITE, iov, 2, len); 2538c2ecf20Sopenharmony_ci return xprt_sendmsg(sock, msg, base); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * xprt_sock_sendmsg - write an xdr_buf directly to a socket 2588c2ecf20Sopenharmony_ci * @sock: open socket to send on 2598c2ecf20Sopenharmony_ci * @msg: socket message metadata 2608c2ecf20Sopenharmony_ci * @xdr: xdr_buf containing this request 2618c2ecf20Sopenharmony_ci * @base: starting position in the buffer 2628c2ecf20Sopenharmony_ci * @marker: stream record marker field 2638c2ecf20Sopenharmony_ci * @sent_p: return the total number of bytes successfully queued for sending 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * Return values: 2668c2ecf20Sopenharmony_ci * On success, returns zero and fills in @sent_p. 2678c2ecf20Sopenharmony_ci * %-ENOTSOCK if @sock is not a struct socket. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ciint xprt_sock_sendmsg(struct socket *sock, struct msghdr *msg, 2708c2ecf20Sopenharmony_ci struct xdr_buf *xdr, unsigned int base, 2718c2ecf20Sopenharmony_ci rpc_fraghdr marker, unsigned int *sent_p) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci unsigned int rmsize = marker ? sizeof(marker) : 0; 2748c2ecf20Sopenharmony_ci unsigned int remainder = rmsize + xdr->len - base; 2758c2ecf20Sopenharmony_ci unsigned int want; 2768c2ecf20Sopenharmony_ci int err = 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci *sent_p = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (unlikely(!sock)) 2818c2ecf20Sopenharmony_ci return -ENOTSOCK; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_MORE; 2848c2ecf20Sopenharmony_ci want = xdr->head[0].iov_len + rmsize; 2858c2ecf20Sopenharmony_ci if (base < want) { 2868c2ecf20Sopenharmony_ci unsigned int len = want - base; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci remainder -= len; 2898c2ecf20Sopenharmony_ci if (remainder == 0) 2908c2ecf20Sopenharmony_ci msg->msg_flags &= ~MSG_MORE; 2918c2ecf20Sopenharmony_ci if (rmsize) 2928c2ecf20Sopenharmony_ci err = xprt_send_rm_and_kvec(sock, msg, marker, 2938c2ecf20Sopenharmony_ci &xdr->head[0], base); 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci err = xprt_send_kvec(sock, msg, &xdr->head[0], base); 2968c2ecf20Sopenharmony_ci if (remainder == 0 || err != len) 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci *sent_p += err; 2998c2ecf20Sopenharmony_ci base = 0; 3008c2ecf20Sopenharmony_ci } else { 3018c2ecf20Sopenharmony_ci base -= want; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (base < xdr->page_len) { 3058c2ecf20Sopenharmony_ci unsigned int len = xdr->page_len - base; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci remainder -= len; 3088c2ecf20Sopenharmony_ci if (remainder == 0) 3098c2ecf20Sopenharmony_ci msg->msg_flags &= ~MSG_MORE; 3108c2ecf20Sopenharmony_ci err = xprt_send_pagedata(sock, msg, xdr, base); 3118c2ecf20Sopenharmony_ci if (remainder == 0 || err != len) 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci *sent_p += err; 3148c2ecf20Sopenharmony_ci base = 0; 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci base -= xdr->page_len; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (base >= xdr->tail[0].iov_len) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci msg->msg_flags &= ~MSG_MORE; 3228c2ecf20Sopenharmony_ci err = xprt_send_kvec(sock, msg, &xdr->tail[0], base); 3238c2ecf20Sopenharmony_ciout: 3248c2ecf20Sopenharmony_ci if (err > 0) { 3258c2ecf20Sopenharmony_ci *sent_p += err; 3268c2ecf20Sopenharmony_ci err = 0; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci return err; 3298c2ecf20Sopenharmony_ci} 330