mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
4388ce05fa
An "abtract" address for an AF_UNIX socket start with a nul and can contain any bytes for the given length, but traditionally doesn't contain other nuls. When reported, the leading nul is replaced by '@'. sunrpc currently rejects connections to these addresses and reports them as an empty string. To provide support for future use of these addresses, allow them for outgoing connections and report them more usefully. Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
3260 lines
84 KiB
C
3260 lines
84 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* linux/net/sunrpc/xprtsock.c
|
|
*
|
|
* Client-side transport implementation for sockets.
|
|
*
|
|
* TCP callback races fixes (C) 1998 Red Hat
|
|
* TCP send fixes (C) 1998 Red Hat
|
|
* TCP NFS related read + write fixes
|
|
* (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
|
|
*
|
|
* Rewrite of larges part of the code in order to stabilize TCP stuff.
|
|
* Fix behaviour when socket buffer is full.
|
|
* (C) 1999 Trond Myklebust <trond.myklebust@fys.uio.no>
|
|
*
|
|
* IP socket transport implementation, (C) 2005 Chuck Lever <cel@netapp.com>
|
|
*
|
|
* IPv6 support contributed by Gilles Quillard, Bull Open Source, 2005.
|
|
* <gilles.quillard@bull.net>
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/in.h>
|
|
#include <linux/net.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/un.h>
|
|
#include <linux/udp.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/sunrpc/clnt.h>
|
|
#include <linux/sunrpc/addr.h>
|
|
#include <linux/sunrpc/sched.h>
|
|
#include <linux/sunrpc/svcsock.h>
|
|
#include <linux/sunrpc/xprtsock.h>
|
|
#include <linux/file.h>
|
|
#ifdef CONFIG_SUNRPC_BACKCHANNEL
|
|
#include <linux/sunrpc/bc_xprt.h>
|
|
#endif
|
|
|
|
#include <net/sock.h>
|
|
#include <net/checksum.h>
|
|
#include <net/udp.h>
|
|
#include <net/tcp.h>
|
|
#include <linux/bvec.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/sched/mm.h>
|
|
|
|
#include <trace/events/sock.h>
|
|
#include <trace/events/sunrpc.h>
|
|
|
|
#include "socklib.h"
|
|
#include "sunrpc.h"
|
|
|
|
static void xs_close(struct rpc_xprt *xprt);
|
|
static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock);
|
|
static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
|
|
struct socket *sock);
|
|
|
|
/*
|
|
* xprtsock tunables
|
|
*/
|
|
static unsigned int xprt_udp_slot_table_entries = RPC_DEF_SLOT_TABLE;
|
|
static unsigned int xprt_tcp_slot_table_entries = RPC_MIN_SLOT_TABLE;
|
|
static unsigned int xprt_max_tcp_slot_table_entries = RPC_MAX_SLOT_TABLE;
|
|
|
|
static unsigned int xprt_min_resvport = RPC_DEF_MIN_RESVPORT;
|
|
static unsigned int xprt_max_resvport = RPC_DEF_MAX_RESVPORT;
|
|
|
|
#define XS_TCP_LINGER_TO (15U * HZ)
|
|
static unsigned int xs_tcp_fin_timeout __read_mostly = XS_TCP_LINGER_TO;
|
|
|
|
/*
|
|
* We can register our own files under /proc/sys/sunrpc by
|
|
* calling register_sysctl() again. The files in that
|
|
* directory become the union of all files registered there.
|
|
*
|
|
* We simply need to make sure that we don't collide with
|
|
* someone else's file names!
|
|
*/
|
|
|
|
static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
|
|
static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
|
|
static unsigned int max_tcp_slot_table_limit = RPC_MAX_SLOT_TABLE_LIMIT;
|
|
static unsigned int xprt_min_resvport_limit = RPC_MIN_RESVPORT;
|
|
static unsigned int xprt_max_resvport_limit = RPC_MAX_RESVPORT;
|
|
|
|
static struct ctl_table_header *sunrpc_table_header;
|
|
|
|
static struct xprt_class xs_local_transport;
|
|
static struct xprt_class xs_udp_transport;
|
|
static struct xprt_class xs_tcp_transport;
|
|
static struct xprt_class xs_bc_tcp_transport;
|
|
|
|
/*
|
|
* FIXME: changing the UDP slot table size should also resize the UDP
|
|
* socket buffers for existing UDP transports
|
|
*/
|
|
static struct ctl_table xs_tunables_table[] = {
|
|
{
|
|
.procname = "udp_slot_table_entries",
|
|
.data = &xprt_udp_slot_table_entries,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = &min_slot_table_size,
|
|
.extra2 = &max_slot_table_size
|
|
},
|
|
{
|
|
.procname = "tcp_slot_table_entries",
|
|
.data = &xprt_tcp_slot_table_entries,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = &min_slot_table_size,
|
|
.extra2 = &max_slot_table_size
|
|
},
|
|
{
|
|
.procname = "tcp_max_slot_table_entries",
|
|
.data = &xprt_max_tcp_slot_table_entries,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = &min_slot_table_size,
|
|
.extra2 = &max_tcp_slot_table_limit
|
|
},
|
|
{
|
|
.procname = "min_resvport",
|
|
.data = &xprt_min_resvport,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = &xprt_min_resvport_limit,
|
|
.extra2 = &xprt_max_resvport_limit
|
|
},
|
|
{
|
|
.procname = "max_resvport",
|
|
.data = &xprt_max_resvport,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_minmax,
|
|
.extra1 = &xprt_min_resvport_limit,
|
|
.extra2 = &xprt_max_resvport_limit
|
|
},
|
|
{
|
|
.procname = "tcp_fin_timeout",
|
|
.data = &xs_tcp_fin_timeout,
|
|
.maxlen = sizeof(xs_tcp_fin_timeout),
|
|
.mode = 0644,
|
|
.proc_handler = proc_dointvec_jiffies,
|
|
},
|
|
{ },
|
|
};
|
|
|
|
/*
|
|
* Wait duration for a reply from the RPC portmapper.
|
|
*/
|
|
#define XS_BIND_TO (60U * HZ)
|
|
|
|
/*
|
|
* Delay if a UDP socket connect error occurs. This is most likely some
|
|
* kind of resource problem on the local host.
|
|
*/
|
|
#define XS_UDP_REEST_TO (2U * HZ)
|
|
|
|
/*
|
|
* The reestablish timeout allows clients to delay for a bit before attempting
|
|
* to reconnect to a server that just dropped our connection.
|
|
*
|
|
* We implement an exponential backoff when trying to reestablish a TCP
|
|
* transport connection with the server. Some servers like to drop a TCP
|
|
* connection when they are overworked, so we start with a short timeout and
|
|
* increase over time if the server is down or not responding.
|
|
*/
|
|
#define XS_TCP_INIT_REEST_TO (3U * HZ)
|
|
|
|
/*
|
|
* TCP idle timeout; client drops the transport socket if it is idle
|
|
* for this long. Note that we also timeout UDP sockets to prevent
|
|
* holding port numbers when there is no RPC traffic.
|
|
*/
|
|
#define XS_IDLE_DISC_TO (5U * 60 * HZ)
|
|
|
|
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
|
# undef RPC_DEBUG_DATA
|
|
# define RPCDBG_FACILITY RPCDBG_TRANS
|
|
#endif
|
|
|
|
#ifdef RPC_DEBUG_DATA
|
|
static void xs_pktdump(char *msg, u32 *packet, unsigned int count)
|
|
{
|
|
u8 *buf = (u8 *) packet;
|
|
int j;
|
|
|
|
dprintk("RPC: %s\n", msg);
|
|
for (j = 0; j < count && j < 128; j += 4) {
|
|
if (!(j & 31)) {
|
|
if (j)
|
|
dprintk("\n");
|
|
dprintk("0x%04x ", j);
|
|
}
|
|
dprintk("%02x%02x%02x%02x ",
|
|
buf[j], buf[j+1], buf[j+2], buf[j+3]);
|
|
}
|
|
dprintk("\n");
|
|
}
|
|
#else
|
|
static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count)
|
|
{
|
|
/* NOP */
|
|
}
|
|
#endif
|
|
|
|
static inline struct rpc_xprt *xprt_from_sock(struct sock *sk)
|
|
{
|
|
return (struct rpc_xprt *) sk->sk_user_data;
|
|
}
|
|
|
|
static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt)
|
|
{
|
|
return (struct sockaddr *) &xprt->addr;
|
|
}
|
|
|
|
static inline struct sockaddr_un *xs_addr_un(struct rpc_xprt *xprt)
|
|
{
|
|
return (struct sockaddr_un *) &xprt->addr;
|
|
}
|
|
|
|
static inline struct sockaddr_in *xs_addr_in(struct rpc_xprt *xprt)
|
|
{
|
|
return (struct sockaddr_in *) &xprt->addr;
|
|
}
|
|
|
|
static inline struct sockaddr_in6 *xs_addr_in6(struct rpc_xprt *xprt)
|
|
{
|
|
return (struct sockaddr_in6 *) &xprt->addr;
|
|
}
|
|
|
|
static void xs_format_common_peer_addresses(struct rpc_xprt *xprt)
|
|
{
|
|
struct sockaddr *sap = xs_addr(xprt);
|
|
struct sockaddr_in6 *sin6;
|
|
struct sockaddr_in *sin;
|
|
struct sockaddr_un *sun;
|
|
char buf[128];
|
|
|
|
switch (sap->sa_family) {
|
|
case AF_LOCAL:
|
|
sun = xs_addr_un(xprt);
|
|
if (sun->sun_path[0]) {
|
|
strscpy(buf, sun->sun_path, sizeof(buf));
|
|
} else {
|
|
buf[0] = '@';
|
|
strscpy(buf+1, sun->sun_path+1, sizeof(buf)-1);
|
|
}
|
|
xprt->address_strings[RPC_DISPLAY_ADDR] =
|
|
kstrdup(buf, GFP_KERNEL);
|
|
break;
|
|
case AF_INET:
|
|
(void)rpc_ntop(sap, buf, sizeof(buf));
|
|
xprt->address_strings[RPC_DISPLAY_ADDR] =
|
|
kstrdup(buf, GFP_KERNEL);
|
|
sin = xs_addr_in(xprt);
|
|
snprintf(buf, sizeof(buf), "%08x", ntohl(sin->sin_addr.s_addr));
|
|
break;
|
|
case AF_INET6:
|
|
(void)rpc_ntop(sap, buf, sizeof(buf));
|
|
xprt->address_strings[RPC_DISPLAY_ADDR] =
|
|
kstrdup(buf, GFP_KERNEL);
|
|
sin6 = xs_addr_in6(xprt);
|
|
snprintf(buf, sizeof(buf), "%pi6", &sin6->sin6_addr);
|
|
break;
|
|
default:
|
|
BUG();
|
|
}
|
|
|
|
xprt->address_strings[RPC_DISPLAY_HEX_ADDR] = kstrdup(buf, GFP_KERNEL);
|
|
}
|
|
|
|
static void xs_format_common_peer_ports(struct rpc_xprt *xprt)
|
|
{
|
|
struct sockaddr *sap = xs_addr(xprt);
|
|
char buf[128];
|
|
|
|
snprintf(buf, sizeof(buf), "%u", rpc_get_port(sap));
|
|
xprt->address_strings[RPC_DISPLAY_PORT] = kstrdup(buf, GFP_KERNEL);
|
|
|
|
snprintf(buf, sizeof(buf), "%4hx", rpc_get_port(sap));
|
|
xprt->address_strings[RPC_DISPLAY_HEX_PORT] = kstrdup(buf, GFP_KERNEL);
|
|
}
|
|
|
|
static void xs_format_peer_addresses(struct rpc_xprt *xprt,
|
|
const char *protocol,
|
|
const char *netid)
|
|
{
|
|
xprt->address_strings[RPC_DISPLAY_PROTO] = protocol;
|
|
xprt->address_strings[RPC_DISPLAY_NETID] = netid;
|
|
xs_format_common_peer_addresses(xprt);
|
|
xs_format_common_peer_ports(xprt);
|
|
}
|
|
|
|
static void xs_update_peer_port(struct rpc_xprt *xprt)
|
|
{
|
|
kfree(xprt->address_strings[RPC_DISPLAY_HEX_PORT]);
|
|
kfree(xprt->address_strings[RPC_DISPLAY_PORT]);
|
|
|
|
xs_format_common_peer_ports(xprt);
|
|
}
|
|
|
|
static void xs_free_peer_addresses(struct rpc_xprt *xprt)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < RPC_DISPLAY_MAX; i++)
|
|
switch (i) {
|
|
case RPC_DISPLAY_PROTO:
|
|
case RPC_DISPLAY_NETID:
|
|
continue;
|
|
default:
|
|
kfree(xprt->address_strings[i]);
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
xs_alloc_sparse_pages(struct xdr_buf *buf, size_t want, gfp_t gfp)
|
|
{
|
|
size_t i,n;
|
|
|
|
if (!want || !(buf->flags & XDRBUF_SPARSE_PAGES))
|
|
return want;
|
|
n = (buf->page_base + want + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
for (i = 0; i < n; i++) {
|
|
if (buf->pages[i])
|
|
continue;
|
|
buf->bvec[i].bv_page = buf->pages[i] = alloc_page(gfp);
|
|
if (!buf->pages[i]) {
|
|
i *= PAGE_SIZE;
|
|
return i > buf->page_base ? i - buf->page_base : 0;
|
|
}
|
|
}
|
|
return want;
|
|
}
|
|
|
|
static ssize_t
|
|
xs_sock_recvmsg(struct socket *sock, struct msghdr *msg, int flags, size_t seek)
|
|
{
|
|
ssize_t ret;
|
|
if (seek != 0)
|
|
iov_iter_advance(&msg->msg_iter, seek);
|
|
ret = sock_recvmsg(sock, msg, flags);
|
|
return ret > 0 ? ret + seek : ret;
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_kvec(struct socket *sock, struct msghdr *msg, int flags,
|
|
struct kvec *kvec, size_t count, size_t seek)
|
|
{
|
|
iov_iter_kvec(&msg->msg_iter, ITER_DEST, kvec, 1, count);
|
|
return xs_sock_recvmsg(sock, msg, flags, seek);
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_bvec(struct socket *sock, struct msghdr *msg, int flags,
|
|
struct bio_vec *bvec, unsigned long nr, size_t count,
|
|
size_t seek)
|
|
{
|
|
iov_iter_bvec(&msg->msg_iter, ITER_DEST, bvec, nr, count);
|
|
return xs_sock_recvmsg(sock, msg, flags, seek);
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_discard(struct socket *sock, struct msghdr *msg, int flags,
|
|
size_t count)
|
|
{
|
|
iov_iter_discard(&msg->msg_iter, ITER_DEST, count);
|
|
return sock_recvmsg(sock, msg, flags);
|
|
}
|
|
|
|
#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
|
|
static void
|
|
xs_flush_bvec(const struct bio_vec *bvec, size_t count, size_t seek)
|
|
{
|
|
struct bvec_iter bi = {
|
|
.bi_size = count,
|
|
};
|
|
struct bio_vec bv;
|
|
|
|
bvec_iter_advance(bvec, &bi, seek & PAGE_MASK);
|
|
for_each_bvec(bv, bvec, bi, bi)
|
|
flush_dcache_page(bv.bv_page);
|
|
}
|
|
#else
|
|
static inline void
|
|
xs_flush_bvec(const struct bio_vec *bvec, size_t count, size_t seek)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static ssize_t
|
|
xs_read_xdr_buf(struct socket *sock, struct msghdr *msg, int flags,
|
|
struct xdr_buf *buf, size_t count, size_t seek, size_t *read)
|
|
{
|
|
size_t want, seek_init = seek, offset = 0;
|
|
ssize_t ret;
|
|
|
|
want = min_t(size_t, count, buf->head[0].iov_len);
|
|
if (seek < want) {
|
|
ret = xs_read_kvec(sock, msg, flags, &buf->head[0], want, seek);
|
|
if (ret <= 0)
|
|
goto sock_err;
|
|
offset += ret;
|
|
if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
|
|
goto out;
|
|
if (ret != want)
|
|
goto out;
|
|
seek = 0;
|
|
} else {
|
|
seek -= want;
|
|
offset += want;
|
|
}
|
|
|
|
want = xs_alloc_sparse_pages(
|
|
buf, min_t(size_t, count - offset, buf->page_len),
|
|
GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
|
|
if (seek < want) {
|
|
ret = xs_read_bvec(sock, msg, flags, buf->bvec,
|
|
xdr_buf_pagecount(buf),
|
|
want + buf->page_base,
|
|
seek + buf->page_base);
|
|
if (ret <= 0)
|
|
goto sock_err;
|
|
xs_flush_bvec(buf->bvec, ret, seek + buf->page_base);
|
|
ret -= buf->page_base;
|
|
offset += ret;
|
|
if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
|
|
goto out;
|
|
if (ret != want)
|
|
goto out;
|
|
seek = 0;
|
|
} else {
|
|
seek -= want;
|
|
offset += want;
|
|
}
|
|
|
|
want = min_t(size_t, count - offset, buf->tail[0].iov_len);
|
|
if (seek < want) {
|
|
ret = xs_read_kvec(sock, msg, flags, &buf->tail[0], want, seek);
|
|
if (ret <= 0)
|
|
goto sock_err;
|
|
offset += ret;
|
|
if (offset == count || msg->msg_flags & (MSG_EOR|MSG_TRUNC))
|
|
goto out;
|
|
if (ret != want)
|
|
goto out;
|
|
} else if (offset < seek_init)
|
|
offset = seek_init;
|
|
ret = -EMSGSIZE;
|
|
out:
|
|
*read = offset - seek_init;
|
|
return ret;
|
|
sock_err:
|
|
offset += seek;
|
|
goto out;
|
|
}
|
|
|
|
static void
|
|
xs_read_header(struct sock_xprt *transport, struct xdr_buf *buf)
|
|
{
|
|
if (!transport->recv.copied) {
|
|
if (buf->head[0].iov_len >= transport->recv.offset)
|
|
memcpy(buf->head[0].iov_base,
|
|
&transport->recv.xid,
|
|
transport->recv.offset);
|
|
transport->recv.copied = transport->recv.offset;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
xs_read_stream_request_done(struct sock_xprt *transport)
|
|
{
|
|
return transport->recv.fraghdr & cpu_to_be32(RPC_LAST_STREAM_FRAGMENT);
|
|
}
|
|
|
|
static void
|
|
xs_read_stream_check_eor(struct sock_xprt *transport,
|
|
struct msghdr *msg)
|
|
{
|
|
if (xs_read_stream_request_done(transport))
|
|
msg->msg_flags |= MSG_EOR;
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_stream_request(struct sock_xprt *transport, struct msghdr *msg,
|
|
int flags, struct rpc_rqst *req)
|
|
{
|
|
struct xdr_buf *buf = &req->rq_private_buf;
|
|
size_t want, read;
|
|
ssize_t ret;
|
|
|
|
xs_read_header(transport, buf);
|
|
|
|
want = transport->recv.len - transport->recv.offset;
|
|
if (want != 0) {
|
|
ret = xs_read_xdr_buf(transport->sock, msg, flags, buf,
|
|
transport->recv.copied + want,
|
|
transport->recv.copied,
|
|
&read);
|
|
transport->recv.offset += read;
|
|
transport->recv.copied += read;
|
|
}
|
|
|
|
if (transport->recv.offset == transport->recv.len)
|
|
xs_read_stream_check_eor(transport, msg);
|
|
|
|
if (want == 0)
|
|
return 0;
|
|
|
|
switch (ret) {
|
|
default:
|
|
break;
|
|
case -EFAULT:
|
|
case -EMSGSIZE:
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
return read;
|
|
case 0:
|
|
return -ESHUTDOWN;
|
|
}
|
|
return ret < 0 ? ret : read;
|
|
}
|
|
|
|
static size_t
|
|
xs_read_stream_headersize(bool isfrag)
|
|
{
|
|
if (isfrag)
|
|
return sizeof(__be32);
|
|
return 3 * sizeof(__be32);
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_stream_header(struct sock_xprt *transport, struct msghdr *msg,
|
|
int flags, size_t want, size_t seek)
|
|
{
|
|
struct kvec kvec = {
|
|
.iov_base = &transport->recv.fraghdr,
|
|
.iov_len = want,
|
|
};
|
|
return xs_read_kvec(transport->sock, msg, flags, &kvec, want, seek);
|
|
}
|
|
|
|
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
|
|
static ssize_t
|
|
xs_read_stream_call(struct sock_xprt *transport, struct msghdr *msg, int flags)
|
|
{
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct rpc_rqst *req;
|
|
ssize_t ret;
|
|
|
|
/* Is this transport associated with the backchannel? */
|
|
if (!xprt->bc_serv)
|
|
return -ESHUTDOWN;
|
|
|
|
/* Look up and lock the request corresponding to the given XID */
|
|
req = xprt_lookup_bc_request(xprt, transport->recv.xid);
|
|
if (!req) {
|
|
printk(KERN_WARNING "Callback slot table overflowed\n");
|
|
return -ESHUTDOWN;
|
|
}
|
|
if (transport->recv.copied && !req->rq_private_buf.len)
|
|
return -ESHUTDOWN;
|
|
|
|
ret = xs_read_stream_request(transport, msg, flags, req);
|
|
if (msg->msg_flags & (MSG_EOR|MSG_TRUNC))
|
|
xprt_complete_bc_request(req, transport->recv.copied);
|
|
else
|
|
req->rq_private_buf.len = transport->recv.copied;
|
|
|
|
return ret;
|
|
}
|
|
#else /* CONFIG_SUNRPC_BACKCHANNEL */
|
|
static ssize_t
|
|
xs_read_stream_call(struct sock_xprt *transport, struct msghdr *msg, int flags)
|
|
{
|
|
return -ESHUTDOWN;
|
|
}
|
|
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
|
|
|
|
static ssize_t
|
|
xs_read_stream_reply(struct sock_xprt *transport, struct msghdr *msg, int flags)
|
|
{
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct rpc_rqst *req;
|
|
ssize_t ret = 0;
|
|
|
|
/* Look up and lock the request corresponding to the given XID */
|
|
spin_lock(&xprt->queue_lock);
|
|
req = xprt_lookup_rqst(xprt, transport->recv.xid);
|
|
if (!req || (transport->recv.copied && !req->rq_private_buf.len)) {
|
|
msg->msg_flags |= MSG_TRUNC;
|
|
goto out;
|
|
}
|
|
xprt_pin_rqst(req);
|
|
spin_unlock(&xprt->queue_lock);
|
|
|
|
ret = xs_read_stream_request(transport, msg, flags, req);
|
|
|
|
spin_lock(&xprt->queue_lock);
|
|
if (msg->msg_flags & (MSG_EOR|MSG_TRUNC))
|
|
xprt_complete_rqst(req->rq_task, transport->recv.copied);
|
|
else
|
|
req->rq_private_buf.len = transport->recv.copied;
|
|
xprt_unpin_rqst(req);
|
|
out:
|
|
spin_unlock(&xprt->queue_lock);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t
|
|
xs_read_stream(struct sock_xprt *transport, int flags)
|
|
{
|
|
struct msghdr msg = { 0 };
|
|
size_t want, read = 0;
|
|
ssize_t ret = 0;
|
|
|
|
if (transport->recv.len == 0) {
|
|
want = xs_read_stream_headersize(transport->recv.copied != 0);
|
|
ret = xs_read_stream_header(transport, &msg, flags, want,
|
|
transport->recv.offset);
|
|
if (ret <= 0)
|
|
goto out_err;
|
|
transport->recv.offset = ret;
|
|
if (transport->recv.offset != want)
|
|
return transport->recv.offset;
|
|
transport->recv.len = be32_to_cpu(transport->recv.fraghdr) &
|
|
RPC_FRAGMENT_SIZE_MASK;
|
|
transport->recv.offset -= sizeof(transport->recv.fraghdr);
|
|
read = ret;
|
|
}
|
|
|
|
switch (be32_to_cpu(transport->recv.calldir)) {
|
|
default:
|
|
msg.msg_flags |= MSG_TRUNC;
|
|
break;
|
|
case RPC_CALL:
|
|
ret = xs_read_stream_call(transport, &msg, flags);
|
|
break;
|
|
case RPC_REPLY:
|
|
ret = xs_read_stream_reply(transport, &msg, flags);
|
|
}
|
|
if (msg.msg_flags & MSG_TRUNC) {
|
|
transport->recv.calldir = cpu_to_be32(-1);
|
|
transport->recv.copied = -1;
|
|
}
|
|
if (ret < 0)
|
|
goto out_err;
|
|
read += ret;
|
|
if (transport->recv.offset < transport->recv.len) {
|
|
if (!(msg.msg_flags & MSG_TRUNC))
|
|
return read;
|
|
msg.msg_flags = 0;
|
|
ret = xs_read_discard(transport->sock, &msg, flags,
|
|
transport->recv.len - transport->recv.offset);
|
|
if (ret <= 0)
|
|
goto out_err;
|
|
transport->recv.offset += ret;
|
|
read += ret;
|
|
if (transport->recv.offset != transport->recv.len)
|
|
return read;
|
|
}
|
|
if (xs_read_stream_request_done(transport)) {
|
|
trace_xs_stream_read_request(transport);
|
|
transport->recv.copied = 0;
|
|
}
|
|
transport->recv.offset = 0;
|
|
transport->recv.len = 0;
|
|
return read;
|
|
out_err:
|
|
return ret != 0 ? ret : -ESHUTDOWN;
|
|
}
|
|
|
|
static __poll_t xs_poll_socket(struct sock_xprt *transport)
|
|
{
|
|
return transport->sock->ops->poll(transport->file, transport->sock,
|
|
NULL);
|
|
}
|
|
|
|
static bool xs_poll_socket_readable(struct sock_xprt *transport)
|
|
{
|
|
__poll_t events = xs_poll_socket(transport);
|
|
|
|
return (events & (EPOLLIN | EPOLLRDNORM)) && !(events & EPOLLRDHUP);
|
|
}
|
|
|
|
static void xs_poll_check_readable(struct sock_xprt *transport)
|
|
{
|
|
|
|
clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
|
|
if (!xs_poll_socket_readable(transport))
|
|
return;
|
|
if (!test_and_set_bit(XPRT_SOCK_DATA_READY, &transport->sock_state))
|
|
queue_work(xprtiod_workqueue, &transport->recv_worker);
|
|
}
|
|
|
|
static void xs_stream_data_receive(struct sock_xprt *transport)
|
|
{
|
|
size_t read = 0;
|
|
ssize_t ret = 0;
|
|
|
|
mutex_lock(&transport->recv_mutex);
|
|
if (transport->sock == NULL)
|
|
goto out;
|
|
for (;;) {
|
|
ret = xs_read_stream(transport, MSG_DONTWAIT);
|
|
if (ret < 0)
|
|
break;
|
|
read += ret;
|
|
cond_resched();
|
|
}
|
|
if (ret == -ESHUTDOWN)
|
|
kernel_sock_shutdown(transport->sock, SHUT_RDWR);
|
|
else
|
|
xs_poll_check_readable(transport);
|
|
out:
|
|
mutex_unlock(&transport->recv_mutex);
|
|
trace_xs_stream_read_data(&transport->xprt, ret, read);
|
|
}
|
|
|
|
static void xs_stream_data_receive_workfn(struct work_struct *work)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(work, struct sock_xprt, recv_worker);
|
|
unsigned int pflags = memalloc_nofs_save();
|
|
|
|
xs_stream_data_receive(transport);
|
|
memalloc_nofs_restore(pflags);
|
|
}
|
|
|
|
static void
|
|
xs_stream_reset_connect(struct sock_xprt *transport)
|
|
{
|
|
transport->recv.offset = 0;
|
|
transport->recv.len = 0;
|
|
transport->recv.copied = 0;
|
|
transport->xmit.offset = 0;
|
|
}
|
|
|
|
static void
|
|
xs_stream_start_connect(struct sock_xprt *transport)
|
|
{
|
|
transport->xprt.stat.connect_count++;
|
|
transport->xprt.stat.connect_start = jiffies;
|
|
}
|
|
|
|
#define XS_SENDMSG_FLAGS (MSG_DONTWAIT | MSG_NOSIGNAL)
|
|
|
|
/**
|
|
* xs_nospace - handle transmit was incomplete
|
|
* @req: pointer to RPC request
|
|
* @transport: pointer to struct sock_xprt
|
|
*
|
|
*/
|
|
static int xs_nospace(struct rpc_rqst *req, struct sock_xprt *transport)
|
|
{
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct sock *sk = transport->inet;
|
|
int ret = -EAGAIN;
|
|
|
|
trace_rpc_socket_nospace(req, transport);
|
|
|
|
/* Protect against races with write_space */
|
|
spin_lock(&xprt->transport_lock);
|
|
|
|
/* Don't race with disconnect */
|
|
if (xprt_connected(xprt)) {
|
|
/* wait for more buffer space */
|
|
set_bit(XPRT_SOCK_NOSPACE, &transport->sock_state);
|
|
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
|
|
sk->sk_write_pending++;
|
|
xprt_wait_for_buffer_space(xprt);
|
|
} else
|
|
ret = -ENOTCONN;
|
|
|
|
spin_unlock(&xprt->transport_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int xs_sock_nospace(struct rpc_rqst *req)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(req->rq_xprt, struct sock_xprt, xprt);
|
|
struct sock *sk = transport->inet;
|
|
int ret = -EAGAIN;
|
|
|
|
lock_sock(sk);
|
|
if (!sock_writeable(sk))
|
|
ret = xs_nospace(req, transport);
|
|
release_sock(sk);
|
|
return ret;
|
|
}
|
|
|
|
static int xs_stream_nospace(struct rpc_rqst *req, bool vm_wait)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(req->rq_xprt, struct sock_xprt, xprt);
|
|
struct sock *sk = transport->inet;
|
|
int ret = -EAGAIN;
|
|
|
|
if (vm_wait)
|
|
return -ENOBUFS;
|
|
lock_sock(sk);
|
|
if (!sk_stream_memory_free(sk))
|
|
ret = xs_nospace(req, transport);
|
|
release_sock(sk);
|
|
return ret;
|
|
}
|
|
|
|
static int xs_stream_prepare_request(struct rpc_rqst *req, struct xdr_buf *buf)
|
|
{
|
|
return xdr_alloc_bvec(buf, rpc_task_gfp_mask());
|
|
}
|
|
|
|
/*
|
|
* Determine if the previous message in the stream was aborted before it
|
|
* could complete transmission.
|
|
*/
|
|
static bool
|
|
xs_send_request_was_aborted(struct sock_xprt *transport, struct rpc_rqst *req)
|
|
{
|
|
return transport->xmit.offset != 0 && req->rq_bytes_sent == 0;
|
|
}
|
|
|
|
/*
|
|
* Return the stream record marker field for a record of length < 2^31-1
|
|
*/
|
|
static rpc_fraghdr
|
|
xs_stream_record_marker(struct xdr_buf *xdr)
|
|
{
|
|
if (!xdr->len)
|
|
return 0;
|
|
return cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | (u32)xdr->len);
|
|
}
|
|
|
|
/**
|
|
* xs_local_send_request - write an RPC request to an AF_LOCAL socket
|
|
* @req: pointer to RPC request
|
|
*
|
|
* Return values:
|
|
* 0: The request has been sent
|
|
* EAGAIN: The socket was blocked, please call again later to
|
|
* complete the request
|
|
* ENOTCONN: Caller needs to invoke connect logic then call again
|
|
* other: Some other error occurred, the request was not sent
|
|
*/
|
|
static int xs_local_send_request(struct rpc_rqst *req)
|
|
{
|
|
struct rpc_xprt *xprt = req->rq_xprt;
|
|
struct sock_xprt *transport =
|
|
container_of(xprt, struct sock_xprt, xprt);
|
|
struct xdr_buf *xdr = &req->rq_snd_buf;
|
|
rpc_fraghdr rm = xs_stream_record_marker(xdr);
|
|
unsigned int msglen = rm ? req->rq_slen + sizeof(rm) : req->rq_slen;
|
|
struct msghdr msg = {
|
|
.msg_flags = XS_SENDMSG_FLAGS,
|
|
};
|
|
bool vm_wait;
|
|
unsigned int sent;
|
|
int status;
|
|
|
|
/* Close the stream if the previous transmission was incomplete */
|
|
if (xs_send_request_was_aborted(transport, req)) {
|
|
xprt_force_disconnect(xprt);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
xs_pktdump("packet data:",
|
|
req->rq_svec->iov_base, req->rq_svec->iov_len);
|
|
|
|
vm_wait = sk_stream_is_writeable(transport->inet) ? true : false;
|
|
|
|
req->rq_xtime = ktime_get();
|
|
status = xprt_sock_sendmsg(transport->sock, &msg, xdr,
|
|
transport->xmit.offset, rm, &sent);
|
|
dprintk("RPC: %s(%u) = %d\n",
|
|
__func__, xdr->len - transport->xmit.offset, status);
|
|
|
|
if (likely(sent > 0) || status == 0) {
|
|
transport->xmit.offset += sent;
|
|
req->rq_bytes_sent = transport->xmit.offset;
|
|
if (likely(req->rq_bytes_sent >= msglen)) {
|
|
req->rq_xmit_bytes_sent += transport->xmit.offset;
|
|
transport->xmit.offset = 0;
|
|
return 0;
|
|
}
|
|
status = -EAGAIN;
|
|
vm_wait = false;
|
|
}
|
|
|
|
switch (status) {
|
|
case -EAGAIN:
|
|
status = xs_stream_nospace(req, vm_wait);
|
|
break;
|
|
default:
|
|
dprintk("RPC: sendmsg returned unrecognized error %d\n",
|
|
-status);
|
|
fallthrough;
|
|
case -EPIPE:
|
|
xprt_force_disconnect(xprt);
|
|
status = -ENOTCONN;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* xs_udp_send_request - write an RPC request to a UDP socket
|
|
* @req: pointer to RPC request
|
|
*
|
|
* Return values:
|
|
* 0: The request has been sent
|
|
* EAGAIN: The socket was blocked, please call again later to
|
|
* complete the request
|
|
* ENOTCONN: Caller needs to invoke connect logic then call again
|
|
* other: Some other error occurred, the request was not sent
|
|
*/
|
|
static int xs_udp_send_request(struct rpc_rqst *req)
|
|
{
|
|
struct rpc_xprt *xprt = req->rq_xprt;
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
struct xdr_buf *xdr = &req->rq_snd_buf;
|
|
struct msghdr msg = {
|
|
.msg_name = xs_addr(xprt),
|
|
.msg_namelen = xprt->addrlen,
|
|
.msg_flags = XS_SENDMSG_FLAGS,
|
|
};
|
|
unsigned int sent;
|
|
int status;
|
|
|
|
xs_pktdump("packet data:",
|
|
req->rq_svec->iov_base,
|
|
req->rq_svec->iov_len);
|
|
|
|
if (!xprt_bound(xprt))
|
|
return -ENOTCONN;
|
|
|
|
if (!xprt_request_get_cong(xprt, req))
|
|
return -EBADSLT;
|
|
|
|
status = xdr_alloc_bvec(xdr, rpc_task_gfp_mask());
|
|
if (status < 0)
|
|
return status;
|
|
req->rq_xtime = ktime_get();
|
|
status = xprt_sock_sendmsg(transport->sock, &msg, xdr, 0, 0, &sent);
|
|
|
|
dprintk("RPC: xs_udp_send_request(%u) = %d\n",
|
|
xdr->len, status);
|
|
|
|
/* firewall is blocking us, don't return -EAGAIN or we end up looping */
|
|
if (status == -EPERM)
|
|
goto process_status;
|
|
|
|
if (status == -EAGAIN && sock_writeable(transport->inet))
|
|
status = -ENOBUFS;
|
|
|
|
if (sent > 0 || status == 0) {
|
|
req->rq_xmit_bytes_sent += sent;
|
|
if (sent >= req->rq_slen)
|
|
return 0;
|
|
/* Still some bytes left; set up for a retry later. */
|
|
status = -EAGAIN;
|
|
}
|
|
|
|
process_status:
|
|
switch (status) {
|
|
case -ENOTSOCK:
|
|
status = -ENOTCONN;
|
|
/* Should we call xs_close() here? */
|
|
break;
|
|
case -EAGAIN:
|
|
status = xs_sock_nospace(req);
|
|
break;
|
|
case -ENETUNREACH:
|
|
case -ENOBUFS:
|
|
case -EPIPE:
|
|
case -ECONNREFUSED:
|
|
case -EPERM:
|
|
/* When the server has died, an ICMP port unreachable message
|
|
* prompts ECONNREFUSED. */
|
|
break;
|
|
default:
|
|
dprintk("RPC: sendmsg returned unrecognized error %d\n",
|
|
-status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_send_request - write an RPC request to a TCP socket
|
|
* @req: pointer to RPC request
|
|
*
|
|
* Return values:
|
|
* 0: The request has been sent
|
|
* EAGAIN: The socket was blocked, please call again later to
|
|
* complete the request
|
|
* ENOTCONN: Caller needs to invoke connect logic then call again
|
|
* other: Some other error occurred, the request was not sent
|
|
*
|
|
* XXX: In the case of soft timeouts, should we eventually give up
|
|
* if sendmsg is not able to make progress?
|
|
*/
|
|
static int xs_tcp_send_request(struct rpc_rqst *req)
|
|
{
|
|
struct rpc_xprt *xprt = req->rq_xprt;
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
struct xdr_buf *xdr = &req->rq_snd_buf;
|
|
rpc_fraghdr rm = xs_stream_record_marker(xdr);
|
|
unsigned int msglen = rm ? req->rq_slen + sizeof(rm) : req->rq_slen;
|
|
struct msghdr msg = {
|
|
.msg_flags = XS_SENDMSG_FLAGS,
|
|
};
|
|
bool vm_wait;
|
|
unsigned int sent;
|
|
int status;
|
|
|
|
/* Close the stream if the previous transmission was incomplete */
|
|
if (xs_send_request_was_aborted(transport, req)) {
|
|
if (transport->sock != NULL)
|
|
kernel_sock_shutdown(transport->sock, SHUT_RDWR);
|
|
return -ENOTCONN;
|
|
}
|
|
if (!transport->inet)
|
|
return -ENOTCONN;
|
|
|
|
xs_pktdump("packet data:",
|
|
req->rq_svec->iov_base,
|
|
req->rq_svec->iov_len);
|
|
|
|
if (test_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state))
|
|
xs_tcp_set_socket_timeouts(xprt, transport->sock);
|
|
|
|
xs_set_srcport(transport, transport->sock);
|
|
|
|
/* Continue transmitting the packet/record. We must be careful
|
|
* to cope with writespace callbacks arriving _after_ we have
|
|
* called sendmsg(). */
|
|
req->rq_xtime = ktime_get();
|
|
tcp_sock_set_cork(transport->inet, true);
|
|
|
|
vm_wait = sk_stream_is_writeable(transport->inet) ? true : false;
|
|
|
|
do {
|
|
status = xprt_sock_sendmsg(transport->sock, &msg, xdr,
|
|
transport->xmit.offset, rm, &sent);
|
|
|
|
dprintk("RPC: xs_tcp_send_request(%u) = %d\n",
|
|
xdr->len - transport->xmit.offset, status);
|
|
|
|
/* If we've sent the entire packet, immediately
|
|
* reset the count of bytes sent. */
|
|
transport->xmit.offset += sent;
|
|
req->rq_bytes_sent = transport->xmit.offset;
|
|
if (likely(req->rq_bytes_sent >= msglen)) {
|
|
req->rq_xmit_bytes_sent += transport->xmit.offset;
|
|
transport->xmit.offset = 0;
|
|
if (atomic_long_read(&xprt->xmit_queuelen) == 1)
|
|
tcp_sock_set_cork(transport->inet, false);
|
|
return 0;
|
|
}
|
|
|
|
WARN_ON_ONCE(sent == 0 && status == 0);
|
|
|
|
if (sent > 0)
|
|
vm_wait = false;
|
|
|
|
} while (status == 0);
|
|
|
|
switch (status) {
|
|
case -ENOTSOCK:
|
|
status = -ENOTCONN;
|
|
/* Should we call xs_close() here? */
|
|
break;
|
|
case -EAGAIN:
|
|
status = xs_stream_nospace(req, vm_wait);
|
|
break;
|
|
case -ECONNRESET:
|
|
case -ECONNREFUSED:
|
|
case -ENOTCONN:
|
|
case -EADDRINUSE:
|
|
case -ENOBUFS:
|
|
case -EPIPE:
|
|
break;
|
|
default:
|
|
dprintk("RPC: sendmsg returned unrecognized error %d\n",
|
|
-status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk)
|
|
{
|
|
transport->old_data_ready = sk->sk_data_ready;
|
|
transport->old_state_change = sk->sk_state_change;
|
|
transport->old_write_space = sk->sk_write_space;
|
|
transport->old_error_report = sk->sk_error_report;
|
|
}
|
|
|
|
static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *sk)
|
|
{
|
|
sk->sk_data_ready = transport->old_data_ready;
|
|
sk->sk_state_change = transport->old_state_change;
|
|
sk->sk_write_space = transport->old_write_space;
|
|
sk->sk_error_report = transport->old_error_report;
|
|
}
|
|
|
|
static void xs_sock_reset_state_flags(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
clear_bit(XPRT_SOCK_DATA_READY, &transport->sock_state);
|
|
clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state);
|
|
clear_bit(XPRT_SOCK_WAKE_WRITE, &transport->sock_state);
|
|
clear_bit(XPRT_SOCK_WAKE_DISCONNECT, &transport->sock_state);
|
|
clear_bit(XPRT_SOCK_NOSPACE, &transport->sock_state);
|
|
}
|
|
|
|
static void xs_run_error_worker(struct sock_xprt *transport, unsigned int nr)
|
|
{
|
|
set_bit(nr, &transport->sock_state);
|
|
queue_work(xprtiod_workqueue, &transport->error_worker);
|
|
}
|
|
|
|
static void xs_sock_reset_connection_flags(struct rpc_xprt *xprt)
|
|
{
|
|
xprt->connect_cookie++;
|
|
smp_mb__before_atomic();
|
|
clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
|
|
clear_bit(XPRT_CLOSING, &xprt->state);
|
|
xs_sock_reset_state_flags(xprt);
|
|
smp_mb__after_atomic();
|
|
}
|
|
|
|
/**
|
|
* xs_error_report - callback to handle TCP socket state errors
|
|
* @sk: socket
|
|
*
|
|
* Note: we don't call sock_error() since there may be a rpc_task
|
|
* using the socket, and so we don't want to clear sk->sk_err.
|
|
*/
|
|
static void xs_error_report(struct sock *sk)
|
|
{
|
|
struct sock_xprt *transport;
|
|
struct rpc_xprt *xprt;
|
|
|
|
if (!(xprt = xprt_from_sock(sk)))
|
|
return;
|
|
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
transport->xprt_err = -sk->sk_err;
|
|
if (transport->xprt_err == 0)
|
|
return;
|
|
dprintk("RPC: xs_error_report client %p, error=%d...\n",
|
|
xprt, -transport->xprt_err);
|
|
trace_rpc_socket_error(xprt, sk->sk_socket, transport->xprt_err);
|
|
|
|
/* barrier ensures xprt_err is set before XPRT_SOCK_WAKE_ERROR */
|
|
smp_mb__before_atomic();
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_ERROR);
|
|
}
|
|
|
|
static void xs_reset_transport(struct sock_xprt *transport)
|
|
{
|
|
struct socket *sock = transport->sock;
|
|
struct sock *sk = transport->inet;
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct file *filp = transport->file;
|
|
|
|
if (sk == NULL)
|
|
return;
|
|
/*
|
|
* Make sure we're calling this in a context from which it is safe
|
|
* to call __fput_sync(). In practice that means rpciod and the
|
|
* system workqueue.
|
|
*/
|
|
if (!(current->flags & PF_WQ_WORKER)) {
|
|
WARN_ON_ONCE(1);
|
|
set_bit(XPRT_CLOSE_WAIT, &xprt->state);
|
|
return;
|
|
}
|
|
|
|
if (atomic_read(&transport->xprt.swapper))
|
|
sk_clear_memalloc(sk);
|
|
|
|
kernel_sock_shutdown(sock, SHUT_RDWR);
|
|
|
|
mutex_lock(&transport->recv_mutex);
|
|
lock_sock(sk);
|
|
transport->inet = NULL;
|
|
transport->sock = NULL;
|
|
transport->file = NULL;
|
|
|
|
sk->sk_user_data = NULL;
|
|
|
|
xs_restore_old_callbacks(transport, sk);
|
|
xprt_clear_connected(xprt);
|
|
xs_sock_reset_connection_flags(xprt);
|
|
/* Reset stream record info */
|
|
xs_stream_reset_connect(transport);
|
|
release_sock(sk);
|
|
mutex_unlock(&transport->recv_mutex);
|
|
|
|
trace_rpc_socket_close(xprt, sock);
|
|
__fput_sync(filp);
|
|
|
|
xprt_disconnect_done(xprt);
|
|
}
|
|
|
|
/**
|
|
* xs_close - close a socket
|
|
* @xprt: transport
|
|
*
|
|
* This is used when all requests are complete; ie, no DRC state remains
|
|
* on the server we want to save.
|
|
*
|
|
* The caller _must_ be holding XPRT_LOCKED in order to avoid issues with
|
|
* xs_reset_transport() zeroing the socket from underneath a writer.
|
|
*/
|
|
static void xs_close(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
dprintk("RPC: xs_close xprt %p\n", xprt);
|
|
|
|
xs_reset_transport(transport);
|
|
xprt->reestablish_timeout = 0;
|
|
}
|
|
|
|
static void xs_inject_disconnect(struct rpc_xprt *xprt)
|
|
{
|
|
dprintk("RPC: injecting transport disconnect on xprt=%p\n",
|
|
xprt);
|
|
xprt_disconnect_done(xprt);
|
|
}
|
|
|
|
static void xs_xprt_free(struct rpc_xprt *xprt)
|
|
{
|
|
xs_free_peer_addresses(xprt);
|
|
xprt_free(xprt);
|
|
}
|
|
|
|
/**
|
|
* xs_destroy - prepare to shutdown a transport
|
|
* @xprt: doomed transport
|
|
*
|
|
*/
|
|
static void xs_destroy(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt,
|
|
struct sock_xprt, xprt);
|
|
dprintk("RPC: xs_destroy xprt %p\n", xprt);
|
|
|
|
cancel_delayed_work_sync(&transport->connect_worker);
|
|
xs_close(xprt);
|
|
cancel_work_sync(&transport->recv_worker);
|
|
cancel_work_sync(&transport->error_worker);
|
|
xs_xprt_free(xprt);
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
/**
|
|
* xs_udp_data_read_skb - receive callback for UDP sockets
|
|
* @xprt: transport
|
|
* @sk: socket
|
|
* @skb: skbuff
|
|
*
|
|
*/
|
|
static void xs_udp_data_read_skb(struct rpc_xprt *xprt,
|
|
struct sock *sk,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct rpc_task *task;
|
|
struct rpc_rqst *rovr;
|
|
int repsize, copied;
|
|
u32 _xid;
|
|
__be32 *xp;
|
|
|
|
repsize = skb->len;
|
|
if (repsize < 4) {
|
|
dprintk("RPC: impossible RPC reply size %d!\n", repsize);
|
|
return;
|
|
}
|
|
|
|
/* Copy the XID from the skb... */
|
|
xp = skb_header_pointer(skb, 0, sizeof(_xid), &_xid);
|
|
if (xp == NULL)
|
|
return;
|
|
|
|
/* Look up and lock the request corresponding to the given XID */
|
|
spin_lock(&xprt->queue_lock);
|
|
rovr = xprt_lookup_rqst(xprt, *xp);
|
|
if (!rovr)
|
|
goto out_unlock;
|
|
xprt_pin_rqst(rovr);
|
|
xprt_update_rtt(rovr->rq_task);
|
|
spin_unlock(&xprt->queue_lock);
|
|
task = rovr->rq_task;
|
|
|
|
if ((copied = rovr->rq_private_buf.buflen) > repsize)
|
|
copied = repsize;
|
|
|
|
/* Suck it into the iovec, verify checksum if not done by hw. */
|
|
if (csum_partial_copy_to_xdr(&rovr->rq_private_buf, skb)) {
|
|
spin_lock(&xprt->queue_lock);
|
|
__UDPX_INC_STATS(sk, UDP_MIB_INERRORS);
|
|
goto out_unpin;
|
|
}
|
|
|
|
|
|
spin_lock(&xprt->transport_lock);
|
|
xprt_adjust_cwnd(xprt, task, copied);
|
|
spin_unlock(&xprt->transport_lock);
|
|
spin_lock(&xprt->queue_lock);
|
|
xprt_complete_rqst(task, copied);
|
|
__UDPX_INC_STATS(sk, UDP_MIB_INDATAGRAMS);
|
|
out_unpin:
|
|
xprt_unpin_rqst(rovr);
|
|
out_unlock:
|
|
spin_unlock(&xprt->queue_lock);
|
|
}
|
|
|
|
static void xs_udp_data_receive(struct sock_xprt *transport)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct sock *sk;
|
|
int err;
|
|
|
|
mutex_lock(&transport->recv_mutex);
|
|
sk = transport->inet;
|
|
if (sk == NULL)
|
|
goto out;
|
|
for (;;) {
|
|
skb = skb_recv_udp(sk, MSG_DONTWAIT, &err);
|
|
if (skb == NULL)
|
|
break;
|
|
xs_udp_data_read_skb(&transport->xprt, sk, skb);
|
|
consume_skb(skb);
|
|
cond_resched();
|
|
}
|
|
xs_poll_check_readable(transport);
|
|
out:
|
|
mutex_unlock(&transport->recv_mutex);
|
|
}
|
|
|
|
static void xs_udp_data_receive_workfn(struct work_struct *work)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(work, struct sock_xprt, recv_worker);
|
|
unsigned int pflags = memalloc_nofs_save();
|
|
|
|
xs_udp_data_receive(transport);
|
|
memalloc_nofs_restore(pflags);
|
|
}
|
|
|
|
/**
|
|
* xs_data_ready - "data ready" callback for sockets
|
|
* @sk: socket with data to read
|
|
*
|
|
*/
|
|
static void xs_data_ready(struct sock *sk)
|
|
{
|
|
struct rpc_xprt *xprt;
|
|
|
|
trace_sk_data_ready(sk);
|
|
|
|
xprt = xprt_from_sock(sk);
|
|
if (xprt != NULL) {
|
|
struct sock_xprt *transport = container_of(xprt,
|
|
struct sock_xprt, xprt);
|
|
|
|
trace_xs_data_ready(xprt);
|
|
|
|
transport->old_data_ready(sk);
|
|
/* Any data means we had a useful conversation, so
|
|
* then we don't need to delay the next reconnect
|
|
*/
|
|
if (xprt->reestablish_timeout)
|
|
xprt->reestablish_timeout = 0;
|
|
if (!test_and_set_bit(XPRT_SOCK_DATA_READY, &transport->sock_state))
|
|
queue_work(xprtiod_workqueue, &transport->recv_worker);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function to force a TCP close if the server is sending
|
|
* junk and/or it has put us in CLOSE_WAIT
|
|
*/
|
|
static void xs_tcp_force_close(struct rpc_xprt *xprt)
|
|
{
|
|
xprt_force_disconnect(xprt);
|
|
}
|
|
|
|
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
|
|
static size_t xs_tcp_bc_maxpayload(struct rpc_xprt *xprt)
|
|
{
|
|
return PAGE_SIZE;
|
|
}
|
|
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
|
|
|
|
/**
|
|
* xs_local_state_change - callback to handle AF_LOCAL socket state changes
|
|
* @sk: socket whose state has changed
|
|
*
|
|
*/
|
|
static void xs_local_state_change(struct sock *sk)
|
|
{
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *transport;
|
|
|
|
if (!(xprt = xprt_from_sock(sk)))
|
|
return;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
if (sk->sk_shutdown & SHUTDOWN_MASK) {
|
|
clear_bit(XPRT_CONNECTED, &xprt->state);
|
|
/* Trigger the socket release */
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_DISCONNECT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_state_change - callback to handle TCP socket state changes
|
|
* @sk: socket whose state has changed
|
|
*
|
|
*/
|
|
static void xs_tcp_state_change(struct sock *sk)
|
|
{
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *transport;
|
|
|
|
if (!(xprt = xprt_from_sock(sk)))
|
|
return;
|
|
dprintk("RPC: xs_tcp_state_change client %p...\n", xprt);
|
|
dprintk("RPC: state %x conn %d dead %d zapped %d sk_shutdown %d\n",
|
|
sk->sk_state, xprt_connected(xprt),
|
|
sock_flag(sk, SOCK_DEAD),
|
|
sock_flag(sk, SOCK_ZAPPED),
|
|
sk->sk_shutdown);
|
|
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
trace_rpc_socket_state_change(xprt, sk->sk_socket);
|
|
switch (sk->sk_state) {
|
|
case TCP_ESTABLISHED:
|
|
if (!xprt_test_and_set_connected(xprt)) {
|
|
xprt->connect_cookie++;
|
|
clear_bit(XPRT_SOCK_CONNECTING, &transport->sock_state);
|
|
xprt_clear_connecting(xprt);
|
|
|
|
xprt->stat.connect_count++;
|
|
xprt->stat.connect_time += (long)jiffies -
|
|
xprt->stat.connect_start;
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_PENDING);
|
|
}
|
|
break;
|
|
case TCP_FIN_WAIT1:
|
|
/* The client initiated a shutdown of the socket */
|
|
xprt->connect_cookie++;
|
|
xprt->reestablish_timeout = 0;
|
|
set_bit(XPRT_CLOSING, &xprt->state);
|
|
smp_mb__before_atomic();
|
|
clear_bit(XPRT_CONNECTED, &xprt->state);
|
|
clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
|
|
smp_mb__after_atomic();
|
|
break;
|
|
case TCP_CLOSE_WAIT:
|
|
/* The server initiated a shutdown of the socket */
|
|
xprt->connect_cookie++;
|
|
clear_bit(XPRT_CONNECTED, &xprt->state);
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_DISCONNECT);
|
|
fallthrough;
|
|
case TCP_CLOSING:
|
|
/*
|
|
* If the server closed down the connection, make sure that
|
|
* we back off before reconnecting
|
|
*/
|
|
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
|
|
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
|
break;
|
|
case TCP_LAST_ACK:
|
|
set_bit(XPRT_CLOSING, &xprt->state);
|
|
smp_mb__before_atomic();
|
|
clear_bit(XPRT_CONNECTED, &xprt->state);
|
|
smp_mb__after_atomic();
|
|
break;
|
|
case TCP_CLOSE:
|
|
if (test_and_clear_bit(XPRT_SOCK_CONNECTING,
|
|
&transport->sock_state))
|
|
xprt_clear_connecting(xprt);
|
|
clear_bit(XPRT_CLOSING, &xprt->state);
|
|
/* Trigger the socket release */
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_DISCONNECT);
|
|
}
|
|
}
|
|
|
|
static void xs_write_space(struct sock *sk)
|
|
{
|
|
struct sock_xprt *transport;
|
|
struct rpc_xprt *xprt;
|
|
|
|
if (!sk->sk_socket)
|
|
return;
|
|
clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
|
|
|
|
if (unlikely(!(xprt = xprt_from_sock(sk))))
|
|
return;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
if (!test_and_clear_bit(XPRT_SOCK_NOSPACE, &transport->sock_state))
|
|
return;
|
|
xs_run_error_worker(transport, XPRT_SOCK_WAKE_WRITE);
|
|
sk->sk_write_pending--;
|
|
}
|
|
|
|
/**
|
|
* xs_udp_write_space - callback invoked when socket buffer space
|
|
* becomes available
|
|
* @sk: socket whose state has changed
|
|
*
|
|
* Called when more output buffer space is available for this socket.
|
|
* We try not to wake our writers until they can make "significant"
|
|
* progress, otherwise we'll waste resources thrashing kernel_sendmsg
|
|
* with a bunch of small requests.
|
|
*/
|
|
static void xs_udp_write_space(struct sock *sk)
|
|
{
|
|
/* from net/core/sock.c:sock_def_write_space */
|
|
if (sock_writeable(sk))
|
|
xs_write_space(sk);
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_write_space - callback invoked when socket buffer space
|
|
* becomes available
|
|
* @sk: socket whose state has changed
|
|
*
|
|
* Called when more output buffer space is available for this socket.
|
|
* We try not to wake our writers until they can make "significant"
|
|
* progress, otherwise we'll waste resources thrashing kernel_sendmsg
|
|
* with a bunch of small requests.
|
|
*/
|
|
static void xs_tcp_write_space(struct sock *sk)
|
|
{
|
|
/* from net/core/stream.c:sk_stream_write_space */
|
|
if (sk_stream_is_writeable(sk))
|
|
xs_write_space(sk);
|
|
}
|
|
|
|
static void xs_udp_do_set_buffer_size(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
struct sock *sk = transport->inet;
|
|
|
|
if (transport->rcvsize) {
|
|
sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
|
|
sk->sk_rcvbuf = transport->rcvsize * xprt->max_reqs * 2;
|
|
}
|
|
if (transport->sndsize) {
|
|
sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
|
|
sk->sk_sndbuf = transport->sndsize * xprt->max_reqs * 2;
|
|
sk->sk_write_space(sk);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xs_udp_set_buffer_size - set send and receive limits
|
|
* @xprt: generic transport
|
|
* @sndsize: requested size of send buffer, in bytes
|
|
* @rcvsize: requested size of receive buffer, in bytes
|
|
*
|
|
* Set socket send and receive buffer size limits.
|
|
*/
|
|
static void xs_udp_set_buffer_size(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
transport->sndsize = 0;
|
|
if (sndsize)
|
|
transport->sndsize = sndsize + 1024;
|
|
transport->rcvsize = 0;
|
|
if (rcvsize)
|
|
transport->rcvsize = rcvsize + 1024;
|
|
|
|
xs_udp_do_set_buffer_size(xprt);
|
|
}
|
|
|
|
/**
|
|
* xs_udp_timer - called when a retransmit timeout occurs on a UDP transport
|
|
* @xprt: controlling transport
|
|
* @task: task that timed out
|
|
*
|
|
* Adjust the congestion window after a retransmit timeout has occurred.
|
|
*/
|
|
static void xs_udp_timer(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
{
|
|
spin_lock(&xprt->transport_lock);
|
|
xprt_adjust_cwnd(xprt, task, -ETIMEDOUT);
|
|
spin_unlock(&xprt->transport_lock);
|
|
}
|
|
|
|
static int xs_get_random_port(void)
|
|
{
|
|
unsigned short min = xprt_min_resvport, max = xprt_max_resvport;
|
|
unsigned short range;
|
|
unsigned short rand;
|
|
|
|
if (max < min)
|
|
return -EADDRINUSE;
|
|
range = max - min + 1;
|
|
rand = get_random_u32_below(range);
|
|
return rand + min;
|
|
}
|
|
|
|
static unsigned short xs_sock_getport(struct socket *sock)
|
|
{
|
|
struct sockaddr_storage buf;
|
|
unsigned short port = 0;
|
|
|
|
if (kernel_getsockname(sock, (struct sockaddr *)&buf) < 0)
|
|
goto out;
|
|
switch (buf.ss_family) {
|
|
case AF_INET6:
|
|
port = ntohs(((struct sockaddr_in6 *)&buf)->sin6_port);
|
|
break;
|
|
case AF_INET:
|
|
port = ntohs(((struct sockaddr_in *)&buf)->sin_port);
|
|
}
|
|
out:
|
|
return port;
|
|
}
|
|
|
|
/**
|
|
* xs_set_port - reset the port number in the remote endpoint address
|
|
* @xprt: generic transport
|
|
* @port: new port number
|
|
*
|
|
*/
|
|
static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
|
|
{
|
|
dprintk("RPC: setting port for xprt %p to %u\n", xprt, port);
|
|
|
|
rpc_set_port(xs_addr(xprt), port);
|
|
xs_update_peer_port(xprt);
|
|
}
|
|
|
|
static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock)
|
|
{
|
|
if (transport->srcport == 0 && transport->xprt.reuseport)
|
|
transport->srcport = xs_sock_getport(sock);
|
|
}
|
|
|
|
static int xs_get_srcport(struct sock_xprt *transport)
|
|
{
|
|
int port = transport->srcport;
|
|
|
|
if (port == 0 && transport->xprt.resvport)
|
|
port = xs_get_random_port();
|
|
return port;
|
|
}
|
|
|
|
static unsigned short xs_sock_srcport(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt);
|
|
unsigned short ret = 0;
|
|
mutex_lock(&sock->recv_mutex);
|
|
if (sock->sock)
|
|
ret = xs_sock_getport(sock->sock);
|
|
mutex_unlock(&sock->recv_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int xs_sock_srcaddr(struct rpc_xprt *xprt, char *buf, size_t buflen)
|
|
{
|
|
struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt);
|
|
union {
|
|
struct sockaddr sa;
|
|
struct sockaddr_storage st;
|
|
} saddr;
|
|
int ret = -ENOTCONN;
|
|
|
|
mutex_lock(&sock->recv_mutex);
|
|
if (sock->sock) {
|
|
ret = kernel_getsockname(sock->sock, &saddr.sa);
|
|
if (ret >= 0)
|
|
ret = snprintf(buf, buflen, "%pISc", &saddr.sa);
|
|
}
|
|
mutex_unlock(&sock->recv_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static unsigned short xs_next_srcport(struct sock_xprt *transport, unsigned short port)
|
|
{
|
|
if (transport->srcport != 0)
|
|
transport->srcport = 0;
|
|
if (!transport->xprt.resvport)
|
|
return 0;
|
|
if (port <= xprt_min_resvport || port > xprt_max_resvport)
|
|
return xprt_max_resvport;
|
|
return --port;
|
|
}
|
|
static int xs_bind(struct sock_xprt *transport, struct socket *sock)
|
|
{
|
|
struct sockaddr_storage myaddr;
|
|
int err, nloop = 0;
|
|
int port = xs_get_srcport(transport);
|
|
unsigned short last;
|
|
|
|
/*
|
|
* If we are asking for any ephemeral port (i.e. port == 0 &&
|
|
* transport->xprt.resvport == 0), don't bind. Let the local
|
|
* port selection happen implicitly when the socket is used
|
|
* (for example at connect time).
|
|
*
|
|
* This ensures that we can continue to establish TCP
|
|
* connections even when all local ephemeral ports are already
|
|
* a part of some TCP connection. This makes no difference
|
|
* for UDP sockets, but also doesn't harm them.
|
|
*
|
|
* If we're asking for any reserved port (i.e. port == 0 &&
|
|
* transport->xprt.resvport == 1) xs_get_srcport above will
|
|
* ensure that port is non-zero and we will bind as needed.
|
|
*/
|
|
if (port <= 0)
|
|
return port;
|
|
|
|
memcpy(&myaddr, &transport->srcaddr, transport->xprt.addrlen);
|
|
do {
|
|
rpc_set_port((struct sockaddr *)&myaddr, port);
|
|
err = kernel_bind(sock, (struct sockaddr *)&myaddr,
|
|
transport->xprt.addrlen);
|
|
if (err == 0) {
|
|
if (transport->xprt.reuseport)
|
|
transport->srcport = port;
|
|
break;
|
|
}
|
|
last = port;
|
|
port = xs_next_srcport(transport, port);
|
|
if (port > last)
|
|
nloop++;
|
|
} while (err == -EADDRINUSE && nloop != 2);
|
|
|
|
if (myaddr.ss_family == AF_INET)
|
|
dprintk("RPC: %s %pI4:%u: %s (%d)\n", __func__,
|
|
&((struct sockaddr_in *)&myaddr)->sin_addr,
|
|
port, err ? "failed" : "ok", err);
|
|
else
|
|
dprintk("RPC: %s %pI6:%u: %s (%d)\n", __func__,
|
|
&((struct sockaddr_in6 *)&myaddr)->sin6_addr,
|
|
port, err ? "failed" : "ok", err);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* We don't support autobind on AF_LOCAL sockets
|
|
*/
|
|
static void xs_local_rpcbind(struct rpc_task *task)
|
|
{
|
|
xprt_set_bound(task->tk_xprt);
|
|
}
|
|
|
|
static void xs_local_set_port(struct rpc_xprt *xprt, unsigned short port)
|
|
{
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
static struct lock_class_key xs_key[3];
|
|
static struct lock_class_key xs_slock_key[3];
|
|
|
|
static inline void xs_reclassify_socketu(struct socket *sock)
|
|
{
|
|
struct sock *sk = sock->sk;
|
|
|
|
sock_lock_init_class_and_name(sk, "slock-AF_LOCAL-RPC",
|
|
&xs_slock_key[0], "sk_lock-AF_LOCAL-RPC", &xs_key[0]);
|
|
}
|
|
|
|
static inline void xs_reclassify_socket4(struct socket *sock)
|
|
{
|
|
struct sock *sk = sock->sk;
|
|
|
|
sock_lock_init_class_and_name(sk, "slock-AF_INET-RPC",
|
|
&xs_slock_key[1], "sk_lock-AF_INET-RPC", &xs_key[1]);
|
|
}
|
|
|
|
static inline void xs_reclassify_socket6(struct socket *sock)
|
|
{
|
|
struct sock *sk = sock->sk;
|
|
|
|
sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
|
|
&xs_slock_key[2], "sk_lock-AF_INET6-RPC", &xs_key[2]);
|
|
}
|
|
|
|
static inline void xs_reclassify_socket(int family, struct socket *sock)
|
|
{
|
|
if (WARN_ON_ONCE(!sock_allow_reclassification(sock->sk)))
|
|
return;
|
|
|
|
switch (family) {
|
|
case AF_LOCAL:
|
|
xs_reclassify_socketu(sock);
|
|
break;
|
|
case AF_INET:
|
|
xs_reclassify_socket4(sock);
|
|
break;
|
|
case AF_INET6:
|
|
xs_reclassify_socket6(sock);
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
static inline void xs_reclassify_socket(int family, struct socket *sock)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void xs_dummy_setup_socket(struct work_struct *work)
|
|
{
|
|
}
|
|
|
|
static struct socket *xs_create_sock(struct rpc_xprt *xprt,
|
|
struct sock_xprt *transport, int family, int type,
|
|
int protocol, bool reuseport)
|
|
{
|
|
struct file *filp;
|
|
struct socket *sock;
|
|
int err;
|
|
|
|
err = __sock_create(xprt->xprt_net, family, type, protocol, &sock, 1);
|
|
if (err < 0) {
|
|
dprintk("RPC: can't create %d transport socket (%d).\n",
|
|
protocol, -err);
|
|
goto out;
|
|
}
|
|
xs_reclassify_socket(family, sock);
|
|
|
|
if (reuseport)
|
|
sock_set_reuseport(sock->sk);
|
|
|
|
err = xs_bind(transport, sock);
|
|
if (err) {
|
|
sock_release(sock);
|
|
goto out;
|
|
}
|
|
|
|
filp = sock_alloc_file(sock, O_NONBLOCK, NULL);
|
|
if (IS_ERR(filp))
|
|
return ERR_CAST(filp);
|
|
transport->file = filp;
|
|
|
|
return sock;
|
|
out:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static int xs_local_finish_connecting(struct rpc_xprt *xprt,
|
|
struct socket *sock)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
|
|
xprt);
|
|
|
|
if (!transport->inet) {
|
|
struct sock *sk = sock->sk;
|
|
|
|
lock_sock(sk);
|
|
|
|
xs_save_old_callbacks(transport, sk);
|
|
|
|
sk->sk_user_data = xprt;
|
|
sk->sk_data_ready = xs_data_ready;
|
|
sk->sk_write_space = xs_udp_write_space;
|
|
sk->sk_state_change = xs_local_state_change;
|
|
sk->sk_error_report = xs_error_report;
|
|
sk->sk_use_task_frag = false;
|
|
|
|
xprt_clear_connected(xprt);
|
|
|
|
/* Reset to new socket */
|
|
transport->sock = sock;
|
|
transport->inet = sk;
|
|
|
|
release_sock(sk);
|
|
}
|
|
|
|
xs_stream_start_connect(transport);
|
|
|
|
return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, 0);
|
|
}
|
|
|
|
/**
|
|
* xs_local_setup_socket - create AF_LOCAL socket, connect to a local endpoint
|
|
* @transport: socket transport to connect
|
|
*/
|
|
static int xs_local_setup_socket(struct sock_xprt *transport)
|
|
{
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct file *filp;
|
|
struct socket *sock;
|
|
int status;
|
|
|
|
status = __sock_create(xprt->xprt_net, AF_LOCAL,
|
|
SOCK_STREAM, 0, &sock, 1);
|
|
if (status < 0) {
|
|
dprintk("RPC: can't create AF_LOCAL "
|
|
"transport socket (%d).\n", -status);
|
|
goto out;
|
|
}
|
|
xs_reclassify_socket(AF_LOCAL, sock);
|
|
|
|
filp = sock_alloc_file(sock, O_NONBLOCK, NULL);
|
|
if (IS_ERR(filp)) {
|
|
status = PTR_ERR(filp);
|
|
goto out;
|
|
}
|
|
transport->file = filp;
|
|
|
|
dprintk("RPC: worker connecting xprt %p via AF_LOCAL to %s\n",
|
|
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
|
|
status = xs_local_finish_connecting(xprt, sock);
|
|
trace_rpc_socket_connect(xprt, sock, status);
|
|
switch (status) {
|
|
case 0:
|
|
dprintk("RPC: xprt %p connected to %s\n",
|
|
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
xprt->stat.connect_count++;
|
|
xprt->stat.connect_time += (long)jiffies -
|
|
xprt->stat.connect_start;
|
|
xprt_set_connected(xprt);
|
|
break;
|
|
case -ENOBUFS:
|
|
break;
|
|
case -ENOENT:
|
|
dprintk("RPC: xprt %p: socket %s does not exist\n",
|
|
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
break;
|
|
case -ECONNREFUSED:
|
|
dprintk("RPC: xprt %p: connection refused for %s\n",
|
|
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "%s: unhandled error (%d) connecting to %s\n",
|
|
__func__, -status,
|
|
xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
}
|
|
|
|
out:
|
|
xprt_clear_connecting(xprt);
|
|
xprt_wake_pending_tasks(xprt, status);
|
|
return status;
|
|
}
|
|
|
|
static void xs_local_connect(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
int ret;
|
|
|
|
if (transport->file)
|
|
goto force_disconnect;
|
|
|
|
if (RPC_IS_ASYNC(task)) {
|
|
/*
|
|
* We want the AF_LOCAL connect to be resolved in the
|
|
* filesystem namespace of the process making the rpc
|
|
* call. Thus we connect synchronously.
|
|
*
|
|
* If we want to support asynchronous AF_LOCAL calls,
|
|
* we'll need to figure out how to pass a namespace to
|
|
* connect.
|
|
*/
|
|
rpc_task_set_rpc_status(task, -ENOTCONN);
|
|
goto out_wake;
|
|
}
|
|
ret = xs_local_setup_socket(transport);
|
|
if (ret && !RPC_IS_SOFTCONN(task))
|
|
msleep_interruptible(15000);
|
|
return;
|
|
force_disconnect:
|
|
xprt_force_disconnect(xprt);
|
|
out_wake:
|
|
xprt_clear_connecting(xprt);
|
|
xprt_wake_pending_tasks(xprt, -ENOTCONN);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SUNRPC_SWAP)
|
|
/*
|
|
* Note that this should be called with XPRT_LOCKED held, or recv_mutex
|
|
* held, or when we otherwise know that we have exclusive access to the
|
|
* socket, to guard against races with xs_reset_transport.
|
|
*/
|
|
static void xs_set_memalloc(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
|
|
xprt);
|
|
|
|
/*
|
|
* If there's no sock, then we have nothing to set. The
|
|
* reconnecting process will get it for us.
|
|
*/
|
|
if (!transport->inet)
|
|
return;
|
|
if (atomic_read(&xprt->swapper))
|
|
sk_set_memalloc(transport->inet);
|
|
}
|
|
|
|
/**
|
|
* xs_enable_swap - Tag this transport as being used for swap.
|
|
* @xprt: transport to tag
|
|
*
|
|
* Take a reference to this transport on behalf of the rpc_clnt, and
|
|
* optionally mark it for swapping if it wasn't already.
|
|
*/
|
|
static int
|
|
xs_enable_swap(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *xs = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
mutex_lock(&xs->recv_mutex);
|
|
if (atomic_inc_return(&xprt->swapper) == 1 &&
|
|
xs->inet)
|
|
sk_set_memalloc(xs->inet);
|
|
mutex_unlock(&xs->recv_mutex);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xs_disable_swap - Untag this transport as being used for swap.
|
|
* @xprt: transport to tag
|
|
*
|
|
* Drop a "swapper" reference to this xprt on behalf of the rpc_clnt. If the
|
|
* swapper refcount goes to 0, untag the socket as a memalloc socket.
|
|
*/
|
|
static void
|
|
xs_disable_swap(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *xs = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
mutex_lock(&xs->recv_mutex);
|
|
if (atomic_dec_and_test(&xprt->swapper) &&
|
|
xs->inet)
|
|
sk_clear_memalloc(xs->inet);
|
|
mutex_unlock(&xs->recv_mutex);
|
|
}
|
|
#else
|
|
static void xs_set_memalloc(struct rpc_xprt *xprt)
|
|
{
|
|
}
|
|
|
|
static int
|
|
xs_enable_swap(struct rpc_xprt *xprt)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void
|
|
xs_disable_swap(struct rpc_xprt *xprt)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
if (!transport->inet) {
|
|
struct sock *sk = sock->sk;
|
|
|
|
lock_sock(sk);
|
|
|
|
xs_save_old_callbacks(transport, sk);
|
|
|
|
sk->sk_user_data = xprt;
|
|
sk->sk_data_ready = xs_data_ready;
|
|
sk->sk_write_space = xs_udp_write_space;
|
|
sk->sk_use_task_frag = false;
|
|
|
|
xprt_set_connected(xprt);
|
|
|
|
/* Reset to new socket */
|
|
transport->sock = sock;
|
|
transport->inet = sk;
|
|
|
|
xs_set_memalloc(xprt);
|
|
|
|
release_sock(sk);
|
|
}
|
|
xs_udp_do_set_buffer_size(xprt);
|
|
|
|
xprt->stat.connect_start = jiffies;
|
|
}
|
|
|
|
static void xs_udp_setup_socket(struct work_struct *work)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(work, struct sock_xprt, connect_worker.work);
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
struct socket *sock;
|
|
int status = -EIO;
|
|
unsigned int pflags = current->flags;
|
|
|
|
if (atomic_read(&xprt->swapper))
|
|
current->flags |= PF_MEMALLOC;
|
|
sock = xs_create_sock(xprt, transport,
|
|
xs_addr(xprt)->sa_family, SOCK_DGRAM,
|
|
IPPROTO_UDP, false);
|
|
if (IS_ERR(sock))
|
|
goto out;
|
|
|
|
dprintk("RPC: worker connecting xprt %p via %s to "
|
|
"%s (port %s)\n", xprt,
|
|
xprt->address_strings[RPC_DISPLAY_PROTO],
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PORT]);
|
|
|
|
xs_udp_finish_connecting(xprt, sock);
|
|
trace_rpc_socket_connect(xprt, sock, 0);
|
|
status = 0;
|
|
out:
|
|
xprt_clear_connecting(xprt);
|
|
xprt_unlock_connect(xprt, transport);
|
|
xprt_wake_pending_tasks(xprt, status);
|
|
current_restore_flags(pflags, PF_MEMALLOC);
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_shutdown - gracefully shut down a TCP socket
|
|
* @xprt: transport
|
|
*
|
|
* Initiates a graceful shutdown of the TCP socket by calling the
|
|
* equivalent of shutdown(SHUT_RDWR);
|
|
*/
|
|
static void xs_tcp_shutdown(struct rpc_xprt *xprt)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
struct socket *sock = transport->sock;
|
|
int skst = transport->inet ? transport->inet->sk_state : TCP_CLOSE;
|
|
|
|
if (sock == NULL)
|
|
return;
|
|
if (!xprt->reuseport) {
|
|
xs_close(xprt);
|
|
return;
|
|
}
|
|
switch (skst) {
|
|
case TCP_FIN_WAIT1:
|
|
case TCP_FIN_WAIT2:
|
|
case TCP_LAST_ACK:
|
|
break;
|
|
case TCP_ESTABLISHED:
|
|
case TCP_CLOSE_WAIT:
|
|
kernel_sock_shutdown(sock, SHUT_RDWR);
|
|
trace_rpc_socket_shutdown(xprt, sock);
|
|
break;
|
|
default:
|
|
xs_reset_transport(transport);
|
|
}
|
|
}
|
|
|
|
static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
|
|
struct socket *sock)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
unsigned int keepidle;
|
|
unsigned int keepcnt;
|
|
unsigned int timeo;
|
|
|
|
spin_lock(&xprt->transport_lock);
|
|
keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ);
|
|
keepcnt = xprt->timeout->to_retries + 1;
|
|
timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
|
|
(xprt->timeout->to_retries + 1);
|
|
clear_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
|
|
spin_unlock(&xprt->transport_lock);
|
|
|
|
/* TCP Keepalive options */
|
|
sock_set_keepalive(sock->sk);
|
|
tcp_sock_set_keepidle(sock->sk, keepidle);
|
|
tcp_sock_set_keepintvl(sock->sk, keepidle);
|
|
tcp_sock_set_keepcnt(sock->sk, keepcnt);
|
|
|
|
/* TCP user timeout (see RFC5482) */
|
|
tcp_sock_set_user_timeout(sock->sk, timeo);
|
|
}
|
|
|
|
static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt,
|
|
unsigned long connect_timeout,
|
|
unsigned long reconnect_timeout)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
struct rpc_timeout to;
|
|
unsigned long initval;
|
|
|
|
spin_lock(&xprt->transport_lock);
|
|
if (reconnect_timeout < xprt->max_reconnect_timeout)
|
|
xprt->max_reconnect_timeout = reconnect_timeout;
|
|
if (connect_timeout < xprt->connect_timeout) {
|
|
memcpy(&to, xprt->timeout, sizeof(to));
|
|
initval = DIV_ROUND_UP(connect_timeout, to.to_retries + 1);
|
|
/* Arbitrary lower limit */
|
|
if (initval < XS_TCP_INIT_REEST_TO << 1)
|
|
initval = XS_TCP_INIT_REEST_TO << 1;
|
|
to.to_initval = initval;
|
|
to.to_maxval = initval;
|
|
memcpy(&transport->tcp_timeout, &to,
|
|
sizeof(transport->tcp_timeout));
|
|
xprt->timeout = &transport->tcp_timeout;
|
|
xprt->connect_timeout = connect_timeout;
|
|
}
|
|
set_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
|
|
spin_unlock(&xprt->transport_lock);
|
|
}
|
|
|
|
static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
if (!transport->inet) {
|
|
struct sock *sk = sock->sk;
|
|
|
|
/* Avoid temporary address, they are bad for long-lived
|
|
* connections such as NFS mounts.
|
|
* RFC4941, section 3.6 suggests that:
|
|
* Individual applications, which have specific
|
|
* knowledge about the normal duration of connections,
|
|
* MAY override this as appropriate.
|
|
*/
|
|
if (xs_addr(xprt)->sa_family == PF_INET6) {
|
|
ip6_sock_set_addr_preferences(sk,
|
|
IPV6_PREFER_SRC_PUBLIC);
|
|
}
|
|
|
|
xs_tcp_set_socket_timeouts(xprt, sock);
|
|
tcp_sock_set_nodelay(sk);
|
|
|
|
lock_sock(sk);
|
|
|
|
xs_save_old_callbacks(transport, sk);
|
|
|
|
sk->sk_user_data = xprt;
|
|
sk->sk_data_ready = xs_data_ready;
|
|
sk->sk_state_change = xs_tcp_state_change;
|
|
sk->sk_write_space = xs_tcp_write_space;
|
|
sk->sk_error_report = xs_error_report;
|
|
sk->sk_use_task_frag = false;
|
|
|
|
/* socket options */
|
|
sock_reset_flag(sk, SOCK_LINGER);
|
|
|
|
xprt_clear_connected(xprt);
|
|
|
|
/* Reset to new socket */
|
|
transport->sock = sock;
|
|
transport->inet = sk;
|
|
|
|
release_sock(sk);
|
|
}
|
|
|
|
if (!xprt_bound(xprt))
|
|
return -ENOTCONN;
|
|
|
|
xs_set_memalloc(xprt);
|
|
|
|
xs_stream_start_connect(transport);
|
|
|
|
/* Tell the socket layer to start connecting... */
|
|
set_bit(XPRT_SOCK_CONNECTING, &transport->sock_state);
|
|
return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK);
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_setup_socket - create a TCP socket and connect to a remote endpoint
|
|
* @work: queued work item
|
|
*
|
|
* Invoked by a work queue tasklet.
|
|
*/
|
|
static void xs_tcp_setup_socket(struct work_struct *work)
|
|
{
|
|
struct sock_xprt *transport =
|
|
container_of(work, struct sock_xprt, connect_worker.work);
|
|
struct socket *sock = transport->sock;
|
|
struct rpc_xprt *xprt = &transport->xprt;
|
|
int status;
|
|
unsigned int pflags = current->flags;
|
|
|
|
if (atomic_read(&xprt->swapper))
|
|
current->flags |= PF_MEMALLOC;
|
|
|
|
if (xprt_connected(xprt))
|
|
goto out;
|
|
if (test_and_clear_bit(XPRT_SOCK_CONNECT_SENT,
|
|
&transport->sock_state) ||
|
|
!sock) {
|
|
xs_reset_transport(transport);
|
|
sock = xs_create_sock(xprt, transport, xs_addr(xprt)->sa_family,
|
|
SOCK_STREAM, IPPROTO_TCP, true);
|
|
if (IS_ERR(sock)) {
|
|
xprt_wake_pending_tasks(xprt, PTR_ERR(sock));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
dprintk("RPC: worker connecting xprt %p via %s to "
|
|
"%s (port %s)\n", xprt,
|
|
xprt->address_strings[RPC_DISPLAY_PROTO],
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PORT]);
|
|
|
|
status = xs_tcp_finish_connecting(xprt, sock);
|
|
trace_rpc_socket_connect(xprt, sock, status);
|
|
dprintk("RPC: %p connect status %d connected %d sock state %d\n",
|
|
xprt, -status, xprt_connected(xprt),
|
|
sock->sk->sk_state);
|
|
switch (status) {
|
|
case 0:
|
|
case -EINPROGRESS:
|
|
/* SYN_SENT! */
|
|
set_bit(XPRT_SOCK_CONNECT_SENT, &transport->sock_state);
|
|
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
|
|
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
|
fallthrough;
|
|
case -EALREADY:
|
|
goto out_unlock;
|
|
case -EADDRNOTAVAIL:
|
|
/* Source port number is unavailable. Try a new one! */
|
|
transport->srcport = 0;
|
|
status = -EAGAIN;
|
|
break;
|
|
case -EINVAL:
|
|
/* Happens, for instance, if the user specified a link
|
|
* local IPv6 address without a scope-id.
|
|
*/
|
|
case -ECONNREFUSED:
|
|
case -ECONNRESET:
|
|
case -ENETDOWN:
|
|
case -ENETUNREACH:
|
|
case -EHOSTUNREACH:
|
|
case -EADDRINUSE:
|
|
case -ENOBUFS:
|
|
break;
|
|
default:
|
|
printk("%s: connect returned unhandled error %d\n",
|
|
__func__, status);
|
|
status = -EAGAIN;
|
|
}
|
|
|
|
/* xs_tcp_force_close() wakes tasks with a fixed error code.
|
|
* We need to wake them first to ensure the correct error code.
|
|
*/
|
|
xprt_wake_pending_tasks(xprt, status);
|
|
xs_tcp_force_close(xprt);
|
|
out:
|
|
xprt_clear_connecting(xprt);
|
|
out_unlock:
|
|
xprt_unlock_connect(xprt, transport);
|
|
current_restore_flags(pflags, PF_MEMALLOC);
|
|
}
|
|
|
|
/**
|
|
* xs_connect - connect a socket to a remote endpoint
|
|
* @xprt: pointer to transport structure
|
|
* @task: address of RPC task that manages state of connect request
|
|
*
|
|
* TCP: If the remote end dropped the connection, delay reconnecting.
|
|
*
|
|
* UDP socket connects are synchronous, but we use a work queue anyway
|
|
* to guarantee that even unprivileged user processes can set up a
|
|
* socket on a privileged port.
|
|
*
|
|
* If a UDP socket connect fails, the delay behavior here prevents
|
|
* retry floods (hard mounts).
|
|
*/
|
|
static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
unsigned long delay = 0;
|
|
|
|
WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport));
|
|
|
|
if (transport->sock != NULL) {
|
|
dprintk("RPC: xs_connect delayed xprt %p for %lu "
|
|
"seconds\n", xprt, xprt->reestablish_timeout / HZ);
|
|
|
|
delay = xprt_reconnect_delay(xprt);
|
|
xprt_reconnect_backoff(xprt, XS_TCP_INIT_REEST_TO);
|
|
|
|
} else
|
|
dprintk("RPC: xs_connect scheduled xprt %p\n", xprt);
|
|
|
|
queue_delayed_work(xprtiod_workqueue,
|
|
&transport->connect_worker,
|
|
delay);
|
|
}
|
|
|
|
static void xs_wake_disconnect(struct sock_xprt *transport)
|
|
{
|
|
if (test_and_clear_bit(XPRT_SOCK_WAKE_DISCONNECT, &transport->sock_state))
|
|
xs_tcp_force_close(&transport->xprt);
|
|
}
|
|
|
|
static void xs_wake_write(struct sock_xprt *transport)
|
|
{
|
|
if (test_and_clear_bit(XPRT_SOCK_WAKE_WRITE, &transport->sock_state))
|
|
xprt_write_space(&transport->xprt);
|
|
}
|
|
|
|
static void xs_wake_error(struct sock_xprt *transport)
|
|
{
|
|
int sockerr;
|
|
|
|
if (!test_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
|
|
return;
|
|
mutex_lock(&transport->recv_mutex);
|
|
if (transport->sock == NULL)
|
|
goto out;
|
|
if (!test_and_clear_bit(XPRT_SOCK_WAKE_ERROR, &transport->sock_state))
|
|
goto out;
|
|
sockerr = xchg(&transport->xprt_err, 0);
|
|
if (sockerr < 0)
|
|
xprt_wake_pending_tasks(&transport->xprt, sockerr);
|
|
out:
|
|
mutex_unlock(&transport->recv_mutex);
|
|
}
|
|
|
|
static void xs_wake_pending(struct sock_xprt *transport)
|
|
{
|
|
if (test_and_clear_bit(XPRT_SOCK_WAKE_PENDING, &transport->sock_state))
|
|
xprt_wake_pending_tasks(&transport->xprt, -EAGAIN);
|
|
}
|
|
|
|
static void xs_error_handle(struct work_struct *work)
|
|
{
|
|
struct sock_xprt *transport = container_of(work,
|
|
struct sock_xprt, error_worker);
|
|
|
|
xs_wake_disconnect(transport);
|
|
xs_wake_write(transport);
|
|
xs_wake_error(transport);
|
|
xs_wake_pending(transport);
|
|
}
|
|
|
|
/**
|
|
* xs_local_print_stats - display AF_LOCAL socket-specific stats
|
|
* @xprt: rpc_xprt struct containing statistics
|
|
* @seq: output file
|
|
*
|
|
*/
|
|
static void xs_local_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
|
|
{
|
|
long idle_time = 0;
|
|
|
|
if (xprt_connected(xprt))
|
|
idle_time = (long)(jiffies - xprt->last_used) / HZ;
|
|
|
|
seq_printf(seq, "\txprt:\tlocal %lu %lu %lu %ld %lu %lu %lu "
|
|
"%llu %llu %lu %llu %llu\n",
|
|
xprt->stat.bind_count,
|
|
xprt->stat.connect_count,
|
|
xprt->stat.connect_time / HZ,
|
|
idle_time,
|
|
xprt->stat.sends,
|
|
xprt->stat.recvs,
|
|
xprt->stat.bad_xids,
|
|
xprt->stat.req_u,
|
|
xprt->stat.bklog_u,
|
|
xprt->stat.max_slots,
|
|
xprt->stat.sending_u,
|
|
xprt->stat.pending_u);
|
|
}
|
|
|
|
/**
|
|
* xs_udp_print_stats - display UDP socket-specific stats
|
|
* @xprt: rpc_xprt struct containing statistics
|
|
* @seq: output file
|
|
*
|
|
*/
|
|
static void xs_udp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
seq_printf(seq, "\txprt:\tudp %u %lu %lu %lu %lu %llu %llu "
|
|
"%lu %llu %llu\n",
|
|
transport->srcport,
|
|
xprt->stat.bind_count,
|
|
xprt->stat.sends,
|
|
xprt->stat.recvs,
|
|
xprt->stat.bad_xids,
|
|
xprt->stat.req_u,
|
|
xprt->stat.bklog_u,
|
|
xprt->stat.max_slots,
|
|
xprt->stat.sending_u,
|
|
xprt->stat.pending_u);
|
|
}
|
|
|
|
/**
|
|
* xs_tcp_print_stats - display TCP socket-specific stats
|
|
* @xprt: rpc_xprt struct containing statistics
|
|
* @seq: output file
|
|
*
|
|
*/
|
|
static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
|
|
{
|
|
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
|
long idle_time = 0;
|
|
|
|
if (xprt_connected(xprt))
|
|
idle_time = (long)(jiffies - xprt->last_used) / HZ;
|
|
|
|
seq_printf(seq, "\txprt:\ttcp %u %lu %lu %lu %ld %lu %lu %lu "
|
|
"%llu %llu %lu %llu %llu\n",
|
|
transport->srcport,
|
|
xprt->stat.bind_count,
|
|
xprt->stat.connect_count,
|
|
xprt->stat.connect_time / HZ,
|
|
idle_time,
|
|
xprt->stat.sends,
|
|
xprt->stat.recvs,
|
|
xprt->stat.bad_xids,
|
|
xprt->stat.req_u,
|
|
xprt->stat.bklog_u,
|
|
xprt->stat.max_slots,
|
|
xprt->stat.sending_u,
|
|
xprt->stat.pending_u);
|
|
}
|
|
|
|
/*
|
|
* Allocate a bunch of pages for a scratch buffer for the rpc code. The reason
|
|
* we allocate pages instead doing a kmalloc like rpc_malloc is because we want
|
|
* to use the server side send routines.
|
|
*/
|
|
static int bc_malloc(struct rpc_task *task)
|
|
{
|
|
struct rpc_rqst *rqst = task->tk_rqstp;
|
|
size_t size = rqst->rq_callsize;
|
|
struct page *page;
|
|
struct rpc_buffer *buf;
|
|
|
|
if (size > PAGE_SIZE - sizeof(struct rpc_buffer)) {
|
|
WARN_ONCE(1, "xprtsock: large bc buffer request (size %zu)\n",
|
|
size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
page = alloc_page(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN);
|
|
if (!page)
|
|
return -ENOMEM;
|
|
|
|
buf = page_address(page);
|
|
buf->len = PAGE_SIZE;
|
|
|
|
rqst->rq_buffer = buf->data;
|
|
rqst->rq_rbuffer = (char *)rqst->rq_buffer + rqst->rq_callsize;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Free the space allocated in the bc_alloc routine
|
|
*/
|
|
static void bc_free(struct rpc_task *task)
|
|
{
|
|
void *buffer = task->tk_rqstp->rq_buffer;
|
|
struct rpc_buffer *buf;
|
|
|
|
buf = container_of(buffer, struct rpc_buffer, data);
|
|
free_page((unsigned long)buf);
|
|
}
|
|
|
|
static int bc_sendto(struct rpc_rqst *req)
|
|
{
|
|
struct xdr_buf *xdr = &req->rq_snd_buf;
|
|
struct sock_xprt *transport =
|
|
container_of(req->rq_xprt, struct sock_xprt, xprt);
|
|
struct msghdr msg = {
|
|
.msg_flags = 0,
|
|
};
|
|
rpc_fraghdr marker = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT |
|
|
(u32)xdr->len);
|
|
unsigned int sent = 0;
|
|
int err;
|
|
|
|
req->rq_xtime = ktime_get();
|
|
err = xdr_alloc_bvec(xdr, rpc_task_gfp_mask());
|
|
if (err < 0)
|
|
return err;
|
|
err = xprt_sock_sendmsg(transport->sock, &msg, xdr, 0, marker, &sent);
|
|
xdr_free_bvec(xdr);
|
|
if (err < 0 || sent != (xdr->len + sizeof(marker)))
|
|
return -EAGAIN;
|
|
return sent;
|
|
}
|
|
|
|
/**
|
|
* bc_send_request - Send a backchannel Call on a TCP socket
|
|
* @req: rpc_rqst containing Call message to be sent
|
|
*
|
|
* xpt_mutex ensures @rqstp's whole message is written to the socket
|
|
* without interruption.
|
|
*
|
|
* Return values:
|
|
* %0 if the message was sent successfully
|
|
* %ENOTCONN if the message was not sent
|
|
*/
|
|
static int bc_send_request(struct rpc_rqst *req)
|
|
{
|
|
struct svc_xprt *xprt;
|
|
int len;
|
|
|
|
/*
|
|
* Get the server socket associated with this callback xprt
|
|
*/
|
|
xprt = req->rq_xprt->bc_xprt;
|
|
|
|
/*
|
|
* Grab the mutex to serialize data as the connection is shared
|
|
* with the fore channel
|
|
*/
|
|
mutex_lock(&xprt->xpt_mutex);
|
|
if (test_bit(XPT_DEAD, &xprt->xpt_flags))
|
|
len = -ENOTCONN;
|
|
else
|
|
len = bc_sendto(req);
|
|
mutex_unlock(&xprt->xpt_mutex);
|
|
|
|
if (len > 0)
|
|
len = 0;
|
|
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* The close routine. Since this is client initiated, we do nothing
|
|
*/
|
|
|
|
static void bc_close(struct rpc_xprt *xprt)
|
|
{
|
|
xprt_disconnect_done(xprt);
|
|
}
|
|
|
|
/*
|
|
* The xprt destroy routine. Again, because this connection is client
|
|
* initiated, we do nothing
|
|
*/
|
|
|
|
static void bc_destroy(struct rpc_xprt *xprt)
|
|
{
|
|
dprintk("RPC: bc_destroy xprt %p\n", xprt);
|
|
|
|
xs_xprt_free(xprt);
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
static const struct rpc_xprt_ops xs_local_ops = {
|
|
.reserve_xprt = xprt_reserve_xprt,
|
|
.release_xprt = xprt_release_xprt,
|
|
.alloc_slot = xprt_alloc_slot,
|
|
.free_slot = xprt_free_slot,
|
|
.rpcbind = xs_local_rpcbind,
|
|
.set_port = xs_local_set_port,
|
|
.connect = xs_local_connect,
|
|
.buf_alloc = rpc_malloc,
|
|
.buf_free = rpc_free,
|
|
.prepare_request = xs_stream_prepare_request,
|
|
.send_request = xs_local_send_request,
|
|
.wait_for_reply_request = xprt_wait_for_reply_request_def,
|
|
.close = xs_close,
|
|
.destroy = xs_destroy,
|
|
.print_stats = xs_local_print_stats,
|
|
.enable_swap = xs_enable_swap,
|
|
.disable_swap = xs_disable_swap,
|
|
};
|
|
|
|
static const struct rpc_xprt_ops xs_udp_ops = {
|
|
.set_buffer_size = xs_udp_set_buffer_size,
|
|
.reserve_xprt = xprt_reserve_xprt_cong,
|
|
.release_xprt = xprt_release_xprt_cong,
|
|
.alloc_slot = xprt_alloc_slot,
|
|
.free_slot = xprt_free_slot,
|
|
.rpcbind = rpcb_getport_async,
|
|
.set_port = xs_set_port,
|
|
.connect = xs_connect,
|
|
.get_srcaddr = xs_sock_srcaddr,
|
|
.get_srcport = xs_sock_srcport,
|
|
.buf_alloc = rpc_malloc,
|
|
.buf_free = rpc_free,
|
|
.send_request = xs_udp_send_request,
|
|
.wait_for_reply_request = xprt_wait_for_reply_request_rtt,
|
|
.timer = xs_udp_timer,
|
|
.release_request = xprt_release_rqst_cong,
|
|
.close = xs_close,
|
|
.destroy = xs_destroy,
|
|
.print_stats = xs_udp_print_stats,
|
|
.enable_swap = xs_enable_swap,
|
|
.disable_swap = xs_disable_swap,
|
|
.inject_disconnect = xs_inject_disconnect,
|
|
};
|
|
|
|
static const struct rpc_xprt_ops xs_tcp_ops = {
|
|
.reserve_xprt = xprt_reserve_xprt,
|
|
.release_xprt = xprt_release_xprt,
|
|
.alloc_slot = xprt_alloc_slot,
|
|
.free_slot = xprt_free_slot,
|
|
.rpcbind = rpcb_getport_async,
|
|
.set_port = xs_set_port,
|
|
.connect = xs_connect,
|
|
.get_srcaddr = xs_sock_srcaddr,
|
|
.get_srcport = xs_sock_srcport,
|
|
.buf_alloc = rpc_malloc,
|
|
.buf_free = rpc_free,
|
|
.prepare_request = xs_stream_prepare_request,
|
|
.send_request = xs_tcp_send_request,
|
|
.wait_for_reply_request = xprt_wait_for_reply_request_def,
|
|
.close = xs_tcp_shutdown,
|
|
.destroy = xs_destroy,
|
|
.set_connect_timeout = xs_tcp_set_connect_timeout,
|
|
.print_stats = xs_tcp_print_stats,
|
|
.enable_swap = xs_enable_swap,
|
|
.disable_swap = xs_disable_swap,
|
|
.inject_disconnect = xs_inject_disconnect,
|
|
#ifdef CONFIG_SUNRPC_BACKCHANNEL
|
|
.bc_setup = xprt_setup_bc,
|
|
.bc_maxpayload = xs_tcp_bc_maxpayload,
|
|
.bc_num_slots = xprt_bc_max_slots,
|
|
.bc_free_rqst = xprt_free_bc_rqst,
|
|
.bc_destroy = xprt_destroy_bc,
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* The rpc_xprt_ops for the server backchannel
|
|
*/
|
|
|
|
static const struct rpc_xprt_ops bc_tcp_ops = {
|
|
.reserve_xprt = xprt_reserve_xprt,
|
|
.release_xprt = xprt_release_xprt,
|
|
.alloc_slot = xprt_alloc_slot,
|
|
.free_slot = xprt_free_slot,
|
|
.buf_alloc = bc_malloc,
|
|
.buf_free = bc_free,
|
|
.send_request = bc_send_request,
|
|
.wait_for_reply_request = xprt_wait_for_reply_request_def,
|
|
.close = bc_close,
|
|
.destroy = bc_destroy,
|
|
.print_stats = xs_tcp_print_stats,
|
|
.enable_swap = xs_enable_swap,
|
|
.disable_swap = xs_disable_swap,
|
|
.inject_disconnect = xs_inject_disconnect,
|
|
};
|
|
|
|
static int xs_init_anyaddr(const int family, struct sockaddr *sap)
|
|
{
|
|
static const struct sockaddr_in sin = {
|
|
.sin_family = AF_INET,
|
|
.sin_addr.s_addr = htonl(INADDR_ANY),
|
|
};
|
|
static const struct sockaddr_in6 sin6 = {
|
|
.sin6_family = AF_INET6,
|
|
.sin6_addr = IN6ADDR_ANY_INIT,
|
|
};
|
|
|
|
switch (family) {
|
|
case AF_LOCAL:
|
|
break;
|
|
case AF_INET:
|
|
memcpy(sap, &sin, sizeof(sin));
|
|
break;
|
|
case AF_INET6:
|
|
memcpy(sap, &sin6, sizeof(sin6));
|
|
break;
|
|
default:
|
|
dprintk("RPC: %s: Bad address family\n", __func__);
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
|
|
unsigned int slot_table_size,
|
|
unsigned int max_slot_table_size)
|
|
{
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *new;
|
|
|
|
if (args->addrlen > sizeof(xprt->addr)) {
|
|
dprintk("RPC: xs_setup_xprt: address too large\n");
|
|
return ERR_PTR(-EBADF);
|
|
}
|
|
|
|
xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size,
|
|
max_slot_table_size);
|
|
if (xprt == NULL) {
|
|
dprintk("RPC: xs_setup_xprt: couldn't allocate "
|
|
"rpc_xprt\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
new = container_of(xprt, struct sock_xprt, xprt);
|
|
mutex_init(&new->recv_mutex);
|
|
memcpy(&xprt->addr, args->dstaddr, args->addrlen);
|
|
xprt->addrlen = args->addrlen;
|
|
if (args->srcaddr)
|
|
memcpy(&new->srcaddr, args->srcaddr, args->addrlen);
|
|
else {
|
|
int err;
|
|
err = xs_init_anyaddr(args->dstaddr->sa_family,
|
|
(struct sockaddr *)&new->srcaddr);
|
|
if (err != 0) {
|
|
xprt_free(xprt);
|
|
return ERR_PTR(err);
|
|
}
|
|
}
|
|
|
|
return xprt;
|
|
}
|
|
|
|
static const struct rpc_timeout xs_local_default_timeout = {
|
|
.to_initval = 10 * HZ,
|
|
.to_maxval = 10 * HZ,
|
|
.to_retries = 2,
|
|
};
|
|
|
|
/**
|
|
* xs_setup_local - Set up transport to use an AF_LOCAL socket
|
|
* @args: rpc transport creation arguments
|
|
*
|
|
* AF_LOCAL is a "tpi_cots_ord" transport, just like TCP
|
|
*/
|
|
static struct rpc_xprt *xs_setup_local(struct xprt_create *args)
|
|
{
|
|
struct sockaddr_un *sun = (struct sockaddr_un *)args->dstaddr;
|
|
struct sock_xprt *transport;
|
|
struct rpc_xprt *xprt;
|
|
struct rpc_xprt *ret;
|
|
|
|
xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
|
|
xprt_max_tcp_slot_table_entries);
|
|
if (IS_ERR(xprt))
|
|
return xprt;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
xprt->prot = 0;
|
|
xprt->xprt_class = &xs_local_transport;
|
|
xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
|
|
|
|
xprt->bind_timeout = XS_BIND_TO;
|
|
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
|
xprt->idle_timeout = XS_IDLE_DISC_TO;
|
|
|
|
xprt->ops = &xs_local_ops;
|
|
xprt->timeout = &xs_local_default_timeout;
|
|
|
|
INIT_WORK(&transport->recv_worker, xs_stream_data_receive_workfn);
|
|
INIT_WORK(&transport->error_worker, xs_error_handle);
|
|
INIT_DELAYED_WORK(&transport->connect_worker, xs_dummy_setup_socket);
|
|
|
|
switch (sun->sun_family) {
|
|
case AF_LOCAL:
|
|
if (sun->sun_path[0] != '/' && sun->sun_path[0] != '\0') {
|
|
dprintk("RPC: bad AF_LOCAL address: %s\n",
|
|
sun->sun_path);
|
|
ret = ERR_PTR(-EINVAL);
|
|
goto out_err;
|
|
}
|
|
xprt_set_bound(xprt);
|
|
xs_format_peer_addresses(xprt, "local", RPCBIND_NETID_LOCAL);
|
|
break;
|
|
default:
|
|
ret = ERR_PTR(-EAFNOSUPPORT);
|
|
goto out_err;
|
|
}
|
|
|
|
dprintk("RPC: set up xprt to %s via AF_LOCAL\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR]);
|
|
|
|
if (try_module_get(THIS_MODULE))
|
|
return xprt;
|
|
ret = ERR_PTR(-EINVAL);
|
|
out_err:
|
|
xs_xprt_free(xprt);
|
|
return ret;
|
|
}
|
|
|
|
static const struct rpc_timeout xs_udp_default_timeout = {
|
|
.to_initval = 5 * HZ,
|
|
.to_maxval = 30 * HZ,
|
|
.to_increment = 5 * HZ,
|
|
.to_retries = 5,
|
|
};
|
|
|
|
/**
|
|
* xs_setup_udp - Set up transport to use a UDP socket
|
|
* @args: rpc transport creation arguments
|
|
*
|
|
*/
|
|
static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
|
|
{
|
|
struct sockaddr *addr = args->dstaddr;
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *transport;
|
|
struct rpc_xprt *ret;
|
|
|
|
xprt = xs_setup_xprt(args, xprt_udp_slot_table_entries,
|
|
xprt_udp_slot_table_entries);
|
|
if (IS_ERR(xprt))
|
|
return xprt;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
xprt->prot = IPPROTO_UDP;
|
|
xprt->xprt_class = &xs_udp_transport;
|
|
/* XXX: header size can vary due to auth type, IPv6, etc. */
|
|
xprt->max_payload = (1U << 16) - (MAX_HEADER << 3);
|
|
|
|
xprt->bind_timeout = XS_BIND_TO;
|
|
xprt->reestablish_timeout = XS_UDP_REEST_TO;
|
|
xprt->idle_timeout = XS_IDLE_DISC_TO;
|
|
|
|
xprt->ops = &xs_udp_ops;
|
|
|
|
xprt->timeout = &xs_udp_default_timeout;
|
|
|
|
INIT_WORK(&transport->recv_worker, xs_udp_data_receive_workfn);
|
|
INIT_WORK(&transport->error_worker, xs_error_handle);
|
|
INIT_DELAYED_WORK(&transport->connect_worker, xs_udp_setup_socket);
|
|
|
|
switch (addr->sa_family) {
|
|
case AF_INET:
|
|
if (((struct sockaddr_in *)addr)->sin_port != htons(0))
|
|
xprt_set_bound(xprt);
|
|
|
|
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP);
|
|
break;
|
|
case AF_INET6:
|
|
if (((struct sockaddr_in6 *)addr)->sin6_port != htons(0))
|
|
xprt_set_bound(xprt);
|
|
|
|
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP6);
|
|
break;
|
|
default:
|
|
ret = ERR_PTR(-EAFNOSUPPORT);
|
|
goto out_err;
|
|
}
|
|
|
|
if (xprt_bound(xprt))
|
|
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PORT],
|
|
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
|
else
|
|
dprintk("RPC: set up xprt to %s (autobind) via %s\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
|
|
|
if (try_module_get(THIS_MODULE))
|
|
return xprt;
|
|
ret = ERR_PTR(-EINVAL);
|
|
out_err:
|
|
xs_xprt_free(xprt);
|
|
return ret;
|
|
}
|
|
|
|
static const struct rpc_timeout xs_tcp_default_timeout = {
|
|
.to_initval = 60 * HZ,
|
|
.to_maxval = 60 * HZ,
|
|
.to_retries = 2,
|
|
};
|
|
|
|
/**
|
|
* xs_setup_tcp - Set up transport to use a TCP socket
|
|
* @args: rpc transport creation arguments
|
|
*
|
|
*/
|
|
static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
|
{
|
|
struct sockaddr *addr = args->dstaddr;
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *transport;
|
|
struct rpc_xprt *ret;
|
|
unsigned int max_slot_table_size = xprt_max_tcp_slot_table_entries;
|
|
|
|
if (args->flags & XPRT_CREATE_INFINITE_SLOTS)
|
|
max_slot_table_size = RPC_MAX_SLOT_TABLE_LIMIT;
|
|
|
|
xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
|
|
max_slot_table_size);
|
|
if (IS_ERR(xprt))
|
|
return xprt;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
xprt->prot = IPPROTO_TCP;
|
|
xprt->xprt_class = &xs_tcp_transport;
|
|
xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
|
|
|
|
xprt->bind_timeout = XS_BIND_TO;
|
|
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
|
xprt->idle_timeout = XS_IDLE_DISC_TO;
|
|
|
|
xprt->ops = &xs_tcp_ops;
|
|
xprt->timeout = &xs_tcp_default_timeout;
|
|
|
|
xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
|
|
xprt->connect_timeout = xprt->timeout->to_initval *
|
|
(xprt->timeout->to_retries + 1);
|
|
|
|
INIT_WORK(&transport->recv_worker, xs_stream_data_receive_workfn);
|
|
INIT_WORK(&transport->error_worker, xs_error_handle);
|
|
INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket);
|
|
|
|
switch (addr->sa_family) {
|
|
case AF_INET:
|
|
if (((struct sockaddr_in *)addr)->sin_port != htons(0))
|
|
xprt_set_bound(xprt);
|
|
|
|
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
|
|
break;
|
|
case AF_INET6:
|
|
if (((struct sockaddr_in6 *)addr)->sin6_port != htons(0))
|
|
xprt_set_bound(xprt);
|
|
|
|
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
|
|
break;
|
|
default:
|
|
ret = ERR_PTR(-EAFNOSUPPORT);
|
|
goto out_err;
|
|
}
|
|
|
|
if (xprt_bound(xprt))
|
|
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PORT],
|
|
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
|
else
|
|
dprintk("RPC: set up xprt to %s (autobind) via %s\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
|
|
|
if (try_module_get(THIS_MODULE))
|
|
return xprt;
|
|
ret = ERR_PTR(-EINVAL);
|
|
out_err:
|
|
xs_xprt_free(xprt);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* xs_setup_bc_tcp - Set up transport to use a TCP backchannel socket
|
|
* @args: rpc transport creation arguments
|
|
*
|
|
*/
|
|
static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
|
|
{
|
|
struct sockaddr *addr = args->dstaddr;
|
|
struct rpc_xprt *xprt;
|
|
struct sock_xprt *transport;
|
|
struct svc_sock *bc_sock;
|
|
struct rpc_xprt *ret;
|
|
|
|
xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
|
|
xprt_tcp_slot_table_entries);
|
|
if (IS_ERR(xprt))
|
|
return xprt;
|
|
transport = container_of(xprt, struct sock_xprt, xprt);
|
|
|
|
xprt->prot = IPPROTO_TCP;
|
|
xprt->xprt_class = &xs_bc_tcp_transport;
|
|
xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
|
|
xprt->timeout = &xs_tcp_default_timeout;
|
|
|
|
/* backchannel */
|
|
xprt_set_bound(xprt);
|
|
xprt->bind_timeout = 0;
|
|
xprt->reestablish_timeout = 0;
|
|
xprt->idle_timeout = 0;
|
|
|
|
xprt->ops = &bc_tcp_ops;
|
|
|
|
switch (addr->sa_family) {
|
|
case AF_INET:
|
|
xs_format_peer_addresses(xprt, "tcp",
|
|
RPCBIND_NETID_TCP);
|
|
break;
|
|
case AF_INET6:
|
|
xs_format_peer_addresses(xprt, "tcp",
|
|
RPCBIND_NETID_TCP6);
|
|
break;
|
|
default:
|
|
ret = ERR_PTR(-EAFNOSUPPORT);
|
|
goto out_err;
|
|
}
|
|
|
|
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
|
|
xprt->address_strings[RPC_DISPLAY_ADDR],
|
|
xprt->address_strings[RPC_DISPLAY_PORT],
|
|
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
|
|
|
/*
|
|
* Once we've associated a backchannel xprt with a connection,
|
|
* we want to keep it around as long as the connection lasts,
|
|
* in case we need to start using it for a backchannel again;
|
|
* this reference won't be dropped until bc_xprt is destroyed.
|
|
*/
|
|
xprt_get(xprt);
|
|
args->bc_xprt->xpt_bc_xprt = xprt;
|
|
xprt->bc_xprt = args->bc_xprt;
|
|
bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt);
|
|
transport->sock = bc_sock->sk_sock;
|
|
transport->inet = bc_sock->sk_sk;
|
|
|
|
/*
|
|
* Since we don't want connections for the backchannel, we set
|
|
* the xprt status to connected
|
|
*/
|
|
xprt_set_connected(xprt);
|
|
|
|
if (try_module_get(THIS_MODULE))
|
|
return xprt;
|
|
|
|
args->bc_xprt->xpt_bc_xprt = NULL;
|
|
args->bc_xprt->xpt_bc_xps = NULL;
|
|
xprt_put(xprt);
|
|
ret = ERR_PTR(-EINVAL);
|
|
out_err:
|
|
xs_xprt_free(xprt);
|
|
return ret;
|
|
}
|
|
|
|
static struct xprt_class xs_local_transport = {
|
|
.list = LIST_HEAD_INIT(xs_local_transport.list),
|
|
.name = "named UNIX socket",
|
|
.owner = THIS_MODULE,
|
|
.ident = XPRT_TRANSPORT_LOCAL,
|
|
.setup = xs_setup_local,
|
|
.netid = { "" },
|
|
};
|
|
|
|
static struct xprt_class xs_udp_transport = {
|
|
.list = LIST_HEAD_INIT(xs_udp_transport.list),
|
|
.name = "udp",
|
|
.owner = THIS_MODULE,
|
|
.ident = XPRT_TRANSPORT_UDP,
|
|
.setup = xs_setup_udp,
|
|
.netid = { "udp", "udp6", "" },
|
|
};
|
|
|
|
static struct xprt_class xs_tcp_transport = {
|
|
.list = LIST_HEAD_INIT(xs_tcp_transport.list),
|
|
.name = "tcp",
|
|
.owner = THIS_MODULE,
|
|
.ident = XPRT_TRANSPORT_TCP,
|
|
.setup = xs_setup_tcp,
|
|
.netid = { "tcp", "tcp6", "" },
|
|
};
|
|
|
|
static struct xprt_class xs_bc_tcp_transport = {
|
|
.list = LIST_HEAD_INIT(xs_bc_tcp_transport.list),
|
|
.name = "tcp NFSv4.1 backchannel",
|
|
.owner = THIS_MODULE,
|
|
.ident = XPRT_TRANSPORT_BC_TCP,
|
|
.setup = xs_setup_bc_tcp,
|
|
.netid = { "" },
|
|
};
|
|
|
|
/**
|
|
* init_socket_xprt - set up xprtsock's sysctls, register with RPC client
|
|
*
|
|
*/
|
|
int init_socket_xprt(void)
|
|
{
|
|
if (!sunrpc_table_header)
|
|
sunrpc_table_header = register_sysctl("sunrpc", xs_tunables_table);
|
|
|
|
xprt_register_transport(&xs_local_transport);
|
|
xprt_register_transport(&xs_udp_transport);
|
|
xprt_register_transport(&xs_tcp_transport);
|
|
xprt_register_transport(&xs_bc_tcp_transport);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* cleanup_socket_xprt - remove xprtsock's sysctls, unregister
|
|
*
|
|
*/
|
|
void cleanup_socket_xprt(void)
|
|
{
|
|
if (sunrpc_table_header) {
|
|
unregister_sysctl_table(sunrpc_table_header);
|
|
sunrpc_table_header = NULL;
|
|
}
|
|
|
|
xprt_unregister_transport(&xs_local_transport);
|
|
xprt_unregister_transport(&xs_udp_transport);
|
|
xprt_unregister_transport(&xs_tcp_transport);
|
|
xprt_unregister_transport(&xs_bc_tcp_transport);
|
|
}
|
|
|
|
static int param_set_portnr(const char *val, const struct kernel_param *kp)
|
|
{
|
|
return param_set_uint_minmax(val, kp,
|
|
RPC_MIN_RESVPORT,
|
|
RPC_MAX_RESVPORT);
|
|
}
|
|
|
|
static const struct kernel_param_ops param_ops_portnr = {
|
|
.set = param_set_portnr,
|
|
.get = param_get_uint,
|
|
};
|
|
|
|
#define param_check_portnr(name, p) \
|
|
__param_check(name, p, unsigned int);
|
|
|
|
module_param_named(min_resvport, xprt_min_resvport, portnr, 0644);
|
|
module_param_named(max_resvport, xprt_max_resvport, portnr, 0644);
|
|
|
|
static int param_set_slot_table_size(const char *val,
|
|
const struct kernel_param *kp)
|
|
{
|
|
return param_set_uint_minmax(val, kp,
|
|
RPC_MIN_SLOT_TABLE,
|
|
RPC_MAX_SLOT_TABLE);
|
|
}
|
|
|
|
static const struct kernel_param_ops param_ops_slot_table_size = {
|
|
.set = param_set_slot_table_size,
|
|
.get = param_get_uint,
|
|
};
|
|
|
|
#define param_check_slot_table_size(name, p) \
|
|
__param_check(name, p, unsigned int);
|
|
|
|
static int param_set_max_slot_table_size(const char *val,
|
|
const struct kernel_param *kp)
|
|
{
|
|
return param_set_uint_minmax(val, kp,
|
|
RPC_MIN_SLOT_TABLE,
|
|
RPC_MAX_SLOT_TABLE_LIMIT);
|
|
}
|
|
|
|
static const struct kernel_param_ops param_ops_max_slot_table_size = {
|
|
.set = param_set_max_slot_table_size,
|
|
.get = param_get_uint,
|
|
};
|
|
|
|
#define param_check_max_slot_table_size(name, p) \
|
|
__param_check(name, p, unsigned int);
|
|
|
|
module_param_named(tcp_slot_table_entries, xprt_tcp_slot_table_entries,
|
|
slot_table_size, 0644);
|
|
module_param_named(tcp_max_slot_table_entries, xprt_max_tcp_slot_table_entries,
|
|
max_slot_table_size, 0644);
|
|
module_param_named(udp_slot_table_entries, xprt_udp_slot_table_entries,
|
|
slot_table_size, 0644);
|