// SPDX-License-Identifier: GPL-2.0-only
/*
* linux / net / sunrpc / socklib . c
*
* Common socket helper routines for RPC client and server
*
* Copyright ( C ) 1995 , 1996 Olaf Kirch < okir @ monad . swb . de >
*/
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/gfp.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/pagemap.h>
#include <linux/udp.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/xdr.h>
#include <linux/export.h>
#include "socklib.h"
/*
* Helper structure for copying from an sk_buff .
*/
struct xdr_skb_reader {
struct sk_buff *skb;
unsigned int offset;
bool need_checksum;
size_t count;
__wsum csum;
};
/**
* xdr_skb_read_bits - copy some data bits from skb to internal buffer
* @ desc : sk_buff copy helper
* @ to : copy destination
* @ len : number of bytes to copy
*
* Possibly called several times to iterate over an sk_buff and copy data out of
* it .
*/
static size_t
xdr_skb_read_bits(struct xdr_skb_reader *desc, void *to, size_t len)
{
len = min(len, desc->count);
if (desc->need_checksum) {
__wsum csum;
csum = skb_copy_and_csum_bits(desc->skb, desc->offset, to, len);
desc->csum = csum_block_add(desc->csum, csum, desc->offset);
} else {
if (unlikely(skb_copy_bits(desc->skb, desc->offset, to, len)))
return 0 ;
}
desc->count -= len;
desc->offset += len;
return len;
}
static ssize_t
xdr_partial_copy_from_skb(struct xdr_buf *xdr, struct xdr_skb_reader *desc)
{
struct page **ppage = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
unsigned int poff = xdr->page_base & ~PAGE_MASK;
unsigned int pglen = xdr->page_len;
ssize_t copied = 0 ;
size_t ret;
if (xdr->head[0 ].iov_len == 0 )
return 0 ;
ret = xdr_skb_read_bits(desc, xdr->head[0 ].iov_base,
xdr->head[0 ].iov_len);
if (ret != xdr->head[0 ].iov_len || !desc->count)
return ret;
copied += ret;
while (pglen) {
unsigned int len = min(PAGE_SIZE - poff, pglen);
char *kaddr;
/* ACL likes to be lazy in allocating pages - ACLs
* are small by default but can get huge. */
if ((xdr->flags & XDRBUF_SPARSE_PAGES) && *ppage == NULL) {
*ppage = alloc_page(GFP_NOWAIT | __GFP_NOWARN);
if (unlikely(*ppage == NULL)) {
if (copied == 0 )
return -ENOMEM;
return copied;
}
}
kaddr = kmap_atomic(*ppage);
ret = xdr_skb_read_bits(desc, kaddr + poff, len);
flush_dcache_page(*ppage);
kunmap_atomic(kaddr);
copied += ret;
if (ret != len || !desc->count)
return copied;
ppage++;
pglen -= len;
poff = 0 ;
}
if (xdr->tail[0 ].iov_len) {
copied += xdr_skb_read_bits(desc, xdr->tail[0 ].iov_base,
xdr->tail[0 ].iov_len);
}
return copied;
}
/**
* csum_partial_copy_to_xdr - checksum and copy data
* @ xdr : target XDR buffer
* @ skb : source skb
*
* We have set things up such that we perform the checksum of the UDP
* packet in parallel with the copies into the RPC client iovec . - DaveM
*/
int csum_partial_copy_to_xdr(struct xdr_buf *xdr, struct sk_buff *skb)
{
struct xdr_skb_reader desc = {
.skb = skb,
.count = skb->len - desc.offset,
};
if (skb_csum_unnecessary(skb)) {
if (xdr_partial_copy_from_skb(xdr, &desc) < 0 )
return -1 ;
if (desc.count)
return -1 ;
return 0 ;
}
desc.need_checksum = true ;
desc.csum = csum_partial(skb->data, desc.offset, skb->csum);
if (xdr_partial_copy_from_skb(xdr, &desc) < 0 )
return -1 ;
if (desc.offset != skb->len) {
__wsum csum2;
csum2 = skb_checksum(skb, desc.offset, skb->len - desc.offset, 0 );
desc.csum = csum_block_add(desc.csum, csum2, desc.offset);
}
if (desc.count)
return -1 ;
if (csum_fold(desc.csum))
return -1 ;
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE) &&
!skb->csum_complete_sw)
netdev_rx_csum_fault(skb->dev, skb);
return 0 ;
}
static inline int xprt_sendmsg(struct socket *sock, struct msghdr *msg,
size_t seek)
{
if (seek)
iov_iter_advance(&msg->msg_iter, seek);
return sock_sendmsg(sock, msg);
}
static int xprt_send_kvec(struct socket *sock, struct msghdr *msg,
struct kvec *vec, size_t seek)
{
iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, vec, 1 , vec->iov_len);
return xprt_sendmsg(sock, msg, seek);
}
static int xprt_send_pagedata(struct socket *sock, struct msghdr *msg,
struct xdr_buf *xdr, size_t base)
{
iov_iter_bvec(&msg->msg_iter, ITER_SOURCE, xdr->bvec, xdr_buf_pagecount(xdr),
xdr->page_len + xdr->page_base);
return xprt_sendmsg(sock, msg, base + xdr->page_base);
}
/* Common case:
* - stream transport
* - sending from byte 0 of the message
* - the message is wholly contained in @ xdr ' s head iovec
*/
static int xprt_send_rm_and_kvec(struct socket *sock, struct msghdr *msg,
rpc_fraghdr marker, struct kvec *vec,
size_t base)
{
struct kvec iov[2 ] = {
[0 ] = {
.iov_base = &marker,
.iov_len = sizeof (marker)
},
[1 ] = *vec,
};
size_t len = iov[0 ].iov_len + iov[1 ].iov_len;
iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, iov, 2 , len);
return xprt_sendmsg(sock, msg, base);
}
/**
* xprt_sock_sendmsg - write an xdr_buf directly to a socket
* @ sock : open socket to send on
* @ msg : socket message metadata
* @ xdr : xdr_buf containing this request
* @ base : starting position in the buffer
* @ marker : stream record marker field
* @ sent_p : return the total number of bytes successfully queued for sending
*
* Return values :
* On success , returns zero and fills in @ sent_p .
* % - ENOTSOCK if @ sock is not a struct socket .
*/
int xprt_sock_sendmsg(struct socket *sock, struct msghdr *msg,
struct xdr_buf *xdr, unsigned int base,
rpc_fraghdr marker, unsigned int *sent_p)
{
unsigned int rmsize = marker ? sizeof (marker) : 0 ;
unsigned int remainder = rmsize + xdr->len - base;
unsigned int want;
int err = 0 ;
*sent_p = 0 ;
if (unlikely(!sock))
return -ENOTSOCK;
msg->msg_flags |= MSG_MORE;
want = xdr->head[0 ].iov_len + rmsize;
if (base < want) {
unsigned int len = want - base;
remainder -= len;
if (remainder == 0 )
msg->msg_flags &= ~MSG_MORE;
if (rmsize)
err = xprt_send_rm_and_kvec(sock, msg, marker,
&xdr->head[0 ], base);
else
err = xprt_send_kvec(sock, msg, &xdr->head[0 ], base);
if (remainder == 0 || err != len)
goto out;
*sent_p += err;
base = 0 ;
} else {
base -= want;
}
if (base < xdr->page_len) {
unsigned int len = xdr->page_len - base;
remainder -= len;
if (remainder == 0 )
msg->msg_flags &= ~MSG_MORE;
err = xprt_send_pagedata(sock, msg, xdr, base);
if (remainder == 0 || err != len)
goto out;
*sent_p += err;
base = 0 ;
} else {
base -= xdr->page_len;
}
if (base >= xdr->tail[0 ].iov_len)
return 0 ;
msg->msg_flags &= ~MSG_MORE;
err = xprt_send_kvec(sock, msg, &xdr->tail[0 ], base);
out:
if (err > 0 ) {
*sent_p += err;
err = 0 ;
}
return err;
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland