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 <pl@kamp.de>
libnfs-4.0.0-vitalif
Peter Lieven 2013-12-23 13:12:28 +01:00
parent d2ec73c75a
commit 1c8b4547ce
5 changed files with 67 additions and 17 deletions

View File

@ -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])

View File

@ -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);

View File

@ -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;
}

View File

@ -280,6 +280,9 @@ flags:
if (strp2) {
*strp2 = 0;
strp2++;
if (!strncmp(strp, "tcp-syncnt", 10)) {
rpc_set_tcp_syncnt(nfs->rpc, atoi(strp2));
}
}
}

View File

@ -46,6 +46,10 @@
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#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);