From 1c8b4547cee99df587ed6b5d0112ab3f48d6534d Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Mon, 23 Dec 2013 13:12:28 +0100 Subject: [PATCH] add tcp-syncnt URL param to adjust TCP_SYNCNT sockopt This allows indirect support for a configurable connect timeout. Linux uses a exponential backoff for SYN retries starting with 1 second. This means for a value n for TCP_SYNCNT, the connect will effectively timeout after 2^(n+1)-1 seconds. Example: examples/nfs-ls nfs://10.0.0.1/export?tcp-syncnt=1 Signed-off-by: Peter Lieven --- configure.ac | 4 ++++ include/libnfs-private.h | 40 +++++++++++++++++++++++----------------- lib/init.c | 1 + lib/libnfs.c | 3 +++ lib/socket.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 8877a5b..cca1699 100644 --- a/configure.ac +++ b/configure.ac @@ -121,6 +121,10 @@ AC_CHECK_HEADERS([sys/statvfs.h]) dnl Check for sys/socket.h AC_CHECK_HEADERS([sys/socket.h]) +# check for netinet/tcp.h +dnl Check for netinet/tcp.h +AC_CHECK_HEADERS([netinet/tcp.h]) + # check for netinet/in.h dnl Check for netinet/in.h AC_CHECK_HEADERS([netinet/in.h]) diff --git a/include/libnfs-private.h b/include/libnfs-private.h index 328d79d..6b64033 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -56,9 +56,10 @@ struct rpc_fragment { }; #define RPC_CONTEXT_MAGIC 0xc6e46435 +#define RPC_PARAM_UNDEFINED -1 struct rpc_context { - uint32_t magic; + uint32_t magic; int fd; int is_connected; @@ -70,29 +71,32 @@ struct rpc_context { struct AUTH *auth; uint32_t xid; - /* buffer used for encoding RPC PDU */ - char *encodebuf; - int encodebuflen; + /* buffer used for encoding RPC PDU */ + char *encodebuf; + int encodebuflen; - struct rpc_pdu *outqueue; - struct sockaddr_storage udp_src; - struct rpc_pdu *waitpdu; + struct rpc_pdu *outqueue; + struct sockaddr_storage udp_src; + struct rpc_pdu *waitpdu; - uint32_t inpos; - uint32_t insize; - char *inbuf; + uint32_t inpos; + uint32_t insize; + char *inbuf; - /* special fields for UDP, which can sometimes be BROADCASTed */ - int is_udp; - struct sockaddr *udp_dest; - int is_broadcast; + /* special fields for UDP, which can sometimes be BROADCASTed */ + int is_udp; + struct sockaddr *udp_dest; + int is_broadcast; - /* track the address we connect to so we can auto-reconnect on session failure */ - struct sockaddr_storage s; - int auto_reconnect; + /* track the address we connect to so we can auto-reconnect on session failure */ + struct sockaddr_storage s; + int auto_reconnect; /* fragment reassembly */ struct rpc_fragment *fragments; + + /* parameters passable via URL */ + int tcp_syncnt; }; struct rpc_pdu { @@ -135,6 +139,8 @@ struct sockaddr *rpc_get_recv_sockaddr(struct rpc_context *rpc); void rpc_set_autoreconnect(struct rpc_context *rpc); void rpc_unset_autoreconnect(struct rpc_context *rpc); +void rpc_set_tcp_syncnt(struct rpc_context *rpc, int v); + int rpc_add_fragment(struct rpc_context *rpc, char *data, uint64_t size); void rpc_free_all_fragments(struct rpc_context *rpc); diff --git a/lib/init.c b/lib/init.c index 2a451a1..bb2fe17 100644 --- a/lib/init.c +++ b/lib/init.c @@ -73,6 +73,7 @@ struct rpc_context *rpc_init_context(void) rpc->xid = salt + time(NULL) + getpid() << 16; salt += 0x01000000; rpc->fd = -1; + rpc->tcp_syncnt = RPC_PARAM_UNDEFINED; return rpc; } diff --git a/lib/libnfs.c b/lib/libnfs.c index ec11ba5..cd15fb0 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -280,6 +280,9 @@ flags: if (strp2) { *strp2 = 0; strp2++; + if (!strncmp(strp, "tcp-syncnt", 10)) { + rpc_set_tcp_syncnt(nfs->rpc, atoi(strp2)); + } } } diff --git a/lib/socket.c b/lib/socket.c index 762dbfa..1676084 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -46,6 +46,10 @@ #include #endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif + #ifdef HAVE_NETDB_H #include #endif @@ -92,6 +96,26 @@ static void set_nonblocking(int fd) #endif //FIXME } +#ifdef HAVE_NETINET_TCP_H +int set_tcp_sockopt(int sockfd, int optname, int value) +{ + int level; + + #if defined(__FreeBSD__) || defined(__sun) || (defined(__APPLE__) && defined(__MACH__)) + struct protoent *buf; + + if ((buf = getprotobyname("tcp")) != NULL) + level = buf->p_proto; + else + return -1; + #else + level = SOL_TCP; + #endif + + return setsockopt(sockfd, level, optname, (char *)&value, sizeof(value)); +} +#endif + int rpc_get_fd(struct rpc_context *rpc) { assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -368,6 +392,13 @@ void rpc_unset_autoreconnect(struct rpc_context *rpc) rpc->auto_reconnect = 0; } +void rpc_set_tcp_syncnt(struct rpc_context *rpc, int v) +{ + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + rpc->tcp_syncnt = v; +} + static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_storage *s) { int socksize; @@ -378,6 +409,11 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s case AF_INET: socksize = sizeof(struct sockaddr_in); rpc->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +#ifdef HAVE_NETINET_TCP_H + if (rpc->tcp_syncnt != RPC_PARAM_UNDEFINED) { + set_tcp_sockopt(rpc->fd, TCP_SYNCNT, rpc->tcp_syncnt); + } +#endif break; default: rpc_set_error(rpc, "Can not handle AF_FAMILY:%d", s->ss_family); -- 2.34.1