cregit-Linux how code gets into the kernel

Release 4.8 net/sunrpc/svcsock.c

Directory: net/sunrpc
 * linux/net/sunrpc/svcsock.c
 * These are the RPC server socket internals.
 * The server scheduling algorithm does not always distribute the load
 * evenly when servicing a single client. May need to modify the
 * svc_xprt_enqueue procedure...
 * TCP support is largely untested and may be a little slow. The problem
 * is that we currently do two separate recvfrom's, one for the 4-byte
 * record length, and the second for the actual record. This could possibly
 * be improved by always reading a minimum size of around 100 bytes and
 * tucking any superfluous bytes away in a temporary store. Still, that
 * leaves write requests out in the rain. An alternative may be to peek at
 * the first skb in the queue, and if it matches the next TCP sequence
 * number, to extract the record marker. Yuck.
 * Copyright (C) 1995, 1996 Olaf Kirch <>

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/file.h>
#include <linux/freezer.h>
#include <net/sock.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/tcp.h>
#include <net/tcp_states.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <trace/events/skb.h>

#include <linux/sunrpc/types.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/xprt.h>

#include "sunrpc.h"


static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
					 int flags);
static int		svc_udp_recvfrom(struct svc_rqst *);
static int		svc_udp_sendto(struct svc_rqst *);
static void		svc_sock_detach(struct svc_xprt *);
static void		svc_tcp_sock_detach(struct svc_xprt *);
static void		svc_sock_free(struct svc_xprt *);

static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
					  struct net *, struct sockaddr *,
					  int, int);
static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int,
					     struct net *, struct sockaddr *,
					     int, int);
static void svc_bc_sock_free(struct svc_xprt *xprt);


static struct lock_class_key svc_key[2];

static struct lock_class_key svc_slock_key[2];

static void svc_reclassify_socket(struct socket *sock) { struct sock *sk = sock->sk; if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) return; switch (sk->sk_family) { case AF_INET: sock_lock_init_class_and_name(sk, "slock-AF_INET-NFSD", &svc_slock_key[0], "sk_xprt.xpt_lock-AF_INET-NFSD", &svc_key[0]); break; case AF_INET6: sock_lock_init_class_and_name(sk, "slock-AF_INET6-NFSD", &svc_slock_key[1], "sk_xprt.xpt_lock-AF_INET6-NFSD", &svc_key[1]); break; default: BUG(); } }


peter zijlstrapeter zijlstra8388.30%120.00%
hannes frederic sowahannes frederic sowa44.26%120.00%
weston andros adamsonweston andros adamson33.19%120.00%
tom tuckertom tucker22.13%120.00%
john heffnerjohn heffner22.13%120.00%

static void svc_reclassify_socket(struct socket *sock) { }


peter zijlstrapeter zijlstra10100.00%1100.00%

#endif /* * Release an skbuff after use */
static void svc_release_skb(struct svc_rqst *rqstp) { struct sk_buff *skb = rqstp->rq_xprt_ctxt; if (skb) { struct svc_sock *svsk = container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); rqstp->rq_xprt_ctxt = NULL; dprintk("svc: service %p, releasing skb %p\n", rqstp, skb); skb_free_datagram_locked(svsk->sk_sk, skb); } }


tom tuckertom tucker4262.69%133.33%
chuck leverchuck lever2435.82%133.33%
eric dumazeteric dumazet11.49%133.33%

union svc_pktinfo_u { struct in_pktinfo pkti; struct in6_pktinfo pkti6; }; #define SVC_PKTINFO_SPACE \ CMSG_SPACE(sizeof(union svc_pktinfo_u))
static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) { struct svc_sock *svsk = container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); switch (svsk->sk_sk->sk_family) { case AF_INET: { struct in_pktinfo *pki = CMSG_DATA(cmh); cmh->cmsg_level = SOL_IP; cmh->cmsg_type = IP_PKTINFO; pki->ipi_ifindex = 0; pki->ipi_spec_dst.s_addr = svc_daddr_in(rqstp)->sin_addr.s_addr; cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; case AF_INET6: { struct in6_pktinfo *pki = CMSG_DATA(cmh); struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp); cmh->cmsg_level = SOL_IPV6; cmh->cmsg_type = IPV6_PKTINFO; pki->ipi6_ifindex = daddr->sin6_scope_id; pki->ipi6_addr = daddr->sin6_addr; cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; } }


tom tuckertom tucker12874.42%120.00%
mi jinlongmi jinlong1911.05%120.00%
neil brownneil brown42.33%120.00%
alexey dobriyanalexey dobriyan10.58%120.00%

/* * send routine intended to be shared by the fore- and back-channel */
int svc_send_common(struct socket *sock, struct xdr_buf *xdr, struct page *headpage, unsigned long headoffset, struct page *tailpage, unsigned long tailoffset) { int result; int size; struct page **ppage = xdr->pages; size_t base = xdr->page_base; unsigned int pglen = xdr->page_len; unsigned int flags = MSG_MORE | MSG_SENDPAGE_NOTLAST; int slen; int len = 0; slen = xdr->len; /* send head */ if (slen == xdr->head[0].iov_len) flags = 0; len = kernel_sendpage(sock, headpage, headoffset, xdr->head[0].iov_len, flags); if (len != xdr->head[0].iov_len) goto out; slen -= xdr->head[0].iov_len; if (slen == 0) goto out; /* send page data */ size = PAGE_SIZE - base < pglen ? PAGE_SIZE - base : pglen; while (pglen > 0) { if (slen == size) flags = 0; result = kernel_sendpage(sock, *ppage, base, size, flags); if (result > 0) len += result; if (result != size) goto out; slen -= size; pglen -= size; size = PAGE_SIZE < pglen ? PAGE_SIZE : pglen; base = 0; ppage++; } /* send tail */ if (xdr->tail[0].iov_len) { result = kernel_sendpage(sock, tailpage, tailoffset, xdr->tail[0].iov_len, 0); if (result > 0) len += result; } out: return len; }


tom tuckertom tucker21670.59%214.29%
rahul iyerrahul iyer309.80%17.14%
neil brownneil brown154.90%535.71%
greg banksgreg banks82.61%321.43%
trond myklebusttrond myklebust20.65%17.14%

/* * Generic sendto routine */
static int svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) { struct svc_sock *svsk = container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); struct socket *sock = svsk->sk_sock; union { struct cmsghdr hdr; long all[SVC_PKTINFO_SPACE / sizeof(long)]; } buffer; struct cmsghdr *cmh = &buffer.hdr; int len = 0; unsigned long tailoff; unsigned long headoff; RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); if (rqstp->rq_prot == IPPROTO_UDP) { struct msghdr msg = { .msg_name = &rqstp->rq_addr, .msg_namelen = rqstp->rq_addrlen, .msg_control = cmh, .msg_controllen = sizeof(buffer), .msg_flags = MSG_MORE, }; svc_set_cmsg_data(rqstp, cmh); if (sock_sendmsg(sock, &msg) < 0) goto out; } tailoff = ((unsigned long)xdr->tail[0].iov_base) & (PAGE_SIZE-1); headoff = 0; len = svc_send_common(sock, xdr, rqstp->rq_respages[0], headoff, rqstp->rq_respages[0], tailoff); out: dprintk("svc: socket %p sendto([%p %Zu... ], %d) = %d (addr %s)\n", svsk, xdr->head[0].iov_base, xdr->head[0].iov_len, xdr->len, len, svc_print_addr(rqstp, buf, sizeof(buf))); return len; }


rahul iyerrahul iyer25898.10%133.33%
tom tuckertom tucker10.38%133.33%

/* * Report socket names for nfsdfs */
static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining) { const struct sock *sk = svsk->sk_sk; const char *proto_name = sk->sk_protocol == IPPROTO_UDP ? "udp" : "tcp"; int len; switch (sk->sk_family) { case PF_INET: len = snprintf(buf, remaining, "ipv4 %s %pI4 %d\n", proto_name, &inet_sk(sk)->inet_rcv_saddr, inet_sk(sk)->inet_num); break; #if IS_ENABLED(CONFIG_IPV6) case PF_INET6: len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n", proto_name, &sk->sk_v6_rcv_saddr, inet_sk(sk)->inet_num); break; #endif default: len = snprintf(buf, remaining, "*unknown-%d*\n", sk->sk_family); } if (len >= remaining) { *buf = '\0'; return -ENAMETOOLONG; } return len; }


chuck leverchuck lever7748.43%323.08%
tom tuckertom tucker3522.01%17.69%
eric dumazeteric dumazet127.55%323.08%
rahul iyerrahul iyer85.03%17.69%
neil brownneil brown74.40%17.69%
harvey harrisonharvey harrison21.26%17.69%
greg banksgreg banks10.63%17.69%

/* * Generic recvfrom routine. */
static int svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen) { struct svc_sock *svsk = container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); struct msghdr msg = { .msg_flags = MSG_DONTWAIT, }; int len; rqstp->rq_xprt_hlen = 0; clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); len = kernel_recvmsg(svsk->sk_sock, &msg, iov, nr, buflen, msg.msg_flags); /* If we read a full record, then assume there may be more * data to read (stream based sockets only!) */ if (len == buflen) set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n", svsk, iov[0].iov_base, iov[0].iov_len, len); return len; }


tom tuckertom tucker7453.62%333.33%
trond myklebusttrond myklebust3122.46%111.11%
neil brownneil brown1813.04%444.44%

static int svc_partial_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen, unsigned int base) { size_t save_iovlen; void *save_iovbase; unsigned int i; int ret; if (base == 0) return svc_recvfrom(rqstp, iov, nr, buflen); for (i = 0; i < nr; i++) { if (iov[i].iov_len > base) break; base -= iov[i].iov_len; } save_iovlen = iov[i].iov_len; save_iovbase = iov[i].iov_base; iov[i].iov_len -= base; iov[i].iov_base += base; ret = svc_recvfrom(rqstp, &iov[i], nr - i, buflen); iov[i].iov_len = save_iovlen; iov[i].iov_base = save_iovbase; return ret; }


j. bruce fieldsj. bruce fields170100.00%1100.00%

/* * Set socket snd and rcv buffer lengths */
static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, unsigned int rcv) { #if 0 mm_segment_t oldfs; oldfs = get_fs(); set_fs(KERNEL_DS); sock_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&snd, sizeof(snd)); sock_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, sizeof(rcv)); #else /* sock_setsockopt limits use to sysctl_?mem_max, * which isn't acceptable. Until that is made conditional * on not having CAP_SYS_RESOURCE or similar, we go direct... * DaveM said I could! */ lock_sock(sock->sk); sock->sk->sk_sndbuf = snd * 2; sock->sk->sk_rcvbuf = rcv * 2; sock->sk->sk_write_space(sock->sk); release_sock(sock->sk); #endif }


tom tuckertom tucker4257.53%114.29%
trond myklebusttrond myklebust79.59%114.29%
j. bruce fieldsj. bruce fields45.48%114.29%
greg banksgreg banks45.48%114.29%
neil brownneil brown22.74%114.29%

static int svc_sock_secure_port(struct svc_rqst *rqstp) { return svc_port_is_privileged(svc_addr(rqstp)); }


chuck leverchuck lever20100.00%1100.00%

/* * INET callback when data has been received on the socket. */
static void svc_data_ready(struct sock *sk) { struct svc_sock *svsk = (struct svc_sock *)sk->sk_user_data; if (svsk) { dprintk("svc: socket %p(inet %p), busy=%d\n", svsk, sk, test_bit(XPT_BUSY, &svsk->sk_xprt.xpt_flags)); svsk->sk_odata(sk); if (!test_and_set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags)) svc_xprt_enqueue(&svsk->sk_xprt); } }


tom tuckertom tucker5162.20%233.33%
chuck leverchuck lever1720.73%116.67%
trond myklebusttrond myklebust1315.85%233.33%
david s. millerdavid s. miller11.22%116.67%

/* * INET callback when space is newly available on the socket. */
static void svc_write_space(struct sock *sk) { struct svc_sock *svsk = (struct svc_sock *)(sk->sk_user_data); if (svsk) { dprintk("svc: socket %p(inet %p), write_space busy=%d\n", svsk, sk, test_bit(XPT_BUSY, &svsk->sk_xprt.xpt_flags)); svsk->sk_owspace(sk); svc_xprt_enqueue(&svsk->sk_xprt); } }


tom tuckertom tucker4362.32%133.33%
chuck leverchuck lever1927.54%133.33%
trond myklebusttrond myklebust710.14%133.33%

static int svc_tcp_has_wspace(struct svc_xprt *xprt) { struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) return 1; return !test_bit(SOCK_NOSPACE, &svsk->sk_sock->flags); }


trond myklebusttrond myklebust55100.00%4100.00%

/* * See net/ipv6/ip_sockglue.c : ip_cmsg_recv_pktinfo */
static int svc_udp_get_dest_address4(struct svc_rqst *rqstp, struct cmsghdr *cmh) { struct in_pktinfo *pki = CMSG_DATA(cmh); struct sockaddr_in *daddr = svc_daddr_in(rqstp); if (cmh->cmsg_type != IP_PKTINFO) return 0; daddr->sin_family = AF_INET; daddr->sin_addr.s_addr = pki->ipi_spec_dst.s_addr; return 1; }


tom tuckertom tucker2232.35%125.00%
mi jinlongmi jinlong1826.47%125.00%
chuck leverchuck lever1623.53%125.00%

/* * See net/ipv6/datagram.c : ip6_datagram_recv_ctl */
static int svc_udp_get_dest_address6(struct svc_rqst *rqstp, struct cmsghdr *cmh) { struct in6_pktinfo *pki = CMSG_DATA(cmh); struct sockaddr_in6 *daddr = svc_daddr_in6(rqstp); if (cmh->cmsg_type != IPV6_PKTINFO) return 0; daddr->sin6_family = AF_INET6; daddr->sin6_addr = pki->ipi6_addr; daddr->sin6_scope_id = pki->ipi6_ifindex; return 1; }


chuck leverchuck lever2838.89%125.00%
mi jinlongmi jinlong2636.11%125.00%
tom tuckertom tucker1723.61%125.00%
alexey dobriyanalexey dobriyan11.39%125.00%

/* * Copy the UDP datagram's destination address to the rqstp structure. * The 'destination' address in this case is the address to which the * peer sent the datagram, i.e. our local address. For multihomed * hosts, this can change from msg to msg. Note that only the IP * address changes, the port number should remain the same. */
static int svc_udp_get_dest_address(struct svc_rqst *rqstp, struct cmsghdr *cmh) { switch (cmh->cmsg_level) { case SOL_IP: return svc_udp_get_dest_address4(rqstp, cmh); case SOL_IPV6: return svc_udp_get_dest_address6(rqstp, cmh); } return 0; }


chuck leverchuck lever4795.92%150.00%
tom tuckertom tucker24.08%150.00%

/* * Receive a datagram from a UDP socket. */
static int svc_udp_recvfrom(struct svc_rqst *rqstp) { struct svc_sock *svsk = container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); struct svc_serv *serv = svsk->sk_xprt.xpt_server; struct sk_buff *skb; union { struct cmsghdr hdr; long all[SVC_PKTINFO_SPACE / sizeof(long)]; } buffer; struct cmsghdr *cmh = &buffer.hdr; struct msghdr msg = { .msg_name = svc_addr(rqstp), .msg_control = cmh, .msg_controllen = sizeof(buffer), .msg_flags = MSG_DONTWAIT, }; size_t len; int err; if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags)) /* udp sockets need large rcvbuf as all pending * requests are still in that buffer. sndbuf must * also be large enough that there is enough space * for one reply per thread. We count all threads * rather than threads in a particular pool, which * provides an upper bound on the number of threads * which will access the socket. */ svc_sock_setbufsize(svsk->sk_sock, (serv->sv_nrthreads+3) * serv->sv_max_mesg, (serv->sv_nrthreads+3) * serv->sv_max_mesg); clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); skb = NULL; err = kernel_recvmsg(svsk->sk_sock, &msg, NULL, 0, 0, MSG_PEEK | MSG_DONTWAIT); if (err >= 0) skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err); if (skb == NULL) { if (err != -EAGAIN) { /* possibly an icmp error */ dprintk("svc: recvfrom returned error %d\n", -err); set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); } return 0; } len = svc_addr_len(svc_addr(rqstp)); rqstp->rq_addrlen = len; if (skb->tstamp.tv64 == 0) { skb->tstamp = ktime_get_real(); /* Don't enable netstamp, sunrpc doesn't need that much accuracy */ } svsk->sk_sk->sk_stamp = skb->tstamp; set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be more data... */ len = skb->len; rqstp->rq_arg.len = len; rqstp->rq_prot = IPPROTO_UDP; if (!svc_udp_get_dest_address(rqstp, cmh)) { net_warn_ratelimited("svc: received unknown control message %d/%d; dropping RPC reply datagram\n", cmh->cmsg_level, cmh->cmsg_type); goto out_free; } rqstp->rq_daddrlen = svc_addr_len(svc_daddr(rqstp)); if (skb_is_nonlinear(skb)) { /* we have to copy */ local_bh_disable(); if (csum_partial_copy_to_xdr(&rqstp->rq_arg, skb)) { local_bh_enable(); /* checksum error */ goto out_free; } local_bh_enable(); skb_free_datagram_locked(svsk->sk_sk, skb); } else { /* we can use it in-place */ rqstp->rq_arg.head[0].iov_base = skb->data; rqstp->rq_arg.head[0].iov_len = len; if (skb_checksum_complete(skb)) goto out_free; rqstp->rq_xprt_ctxt = skb; } rqstp->rq_arg.page_base = 0; if (len <= rqstp->rq_arg.head[0].iov_len) { rqstp->rq_arg.head[0].iov_len = len; rqstp->rq_arg.page_len = 0; rqstp->rq_respages = rqstp->rq_pages+1; } else { rqstp->rq_arg.page_len = len - rqstp->rq_arg.head[0].iov_len; rqstp->rq_respages = rqstp->rq_pages + 1 + DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE); } rqstp->rq_next_page = rqstp->rq_respages+1; if (serv->sv_stats) serv->sv_stats->netudpcnt++; return len; out_free: trace_kfree_skb(skb, svc_udp_recvfrom); skb_free_datagram_locked(svsk->sk_sk, skb); return 0; }


tom tuckertom tucker39363.59%313.64%
neil brownneil brown12019.42%627.27%
j. bruce fieldsj. bruce fields345.50%313.64%
chuck leverchuck lever193.07%313.64%
david s. millerdavid s. miller142.27%14.55%
mi jinlongmi jinlong121.94%14.55%
eric dumazeteric dumazet81.29%29.09%
joe perchesjoe perches20.32%14.55%

static int svc_udp_sendto(struct svc_rqst *rqstp) { int error; error = svc_sendto(rqstp, &rqstp->rq_res); if (error == -ECONNREFUSED) /* ICMP error on earlier request. */ error = svc_sendto(rqstp, &rqstp->rq_res); return error; }


tom tuckertom tucker3061.22%150.00%

static void svc_udp_prep_reply_hdr(struct svc_rqst *rqstp) { }


tom tuckertom tucker440.00%150.00%

static int svc_udp_has_wspace(struct svc_xprt *xprt) { struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); struct svc_serv