add tcp-syncnt URL param to adjust TCP_SYNCNT sockopt
authorPeter Lieven <pl@kamp.de>
Mon, 23 Dec 2013 12:12:28 +0000 (13:12 +0100)
committerPeter Lieven <pl@kamp.de>
Mon, 23 Dec 2013 13:03:18 +0000 (14:03 +0100)
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>
configure.ac
include/libnfs-private.h
lib/init.c
lib/libnfs.c
lib/socket.c

index 8877a5b86b6225684c9823d3c33db1deb560e7ed..cca169934899e927c2b9322148813051a50d5b22 100644 (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])
index 328d79dc62720d45f2205bb61f9d1c3081fa0b30..6b64033c6af13c1046ca90f45374e152680687cc 100644 (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);
 
index 2a451a16d51f4282b76b40f0d32ee416aae72ef0..bb2fe17bd00ef48306cb716df5b2f37d09580a81 100644 (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;
 }
index ec11ba5558ca30d592eea1069ee3e322e08864e2..cd15fb01eabe0bcc4fa722f33d322d6ad719ba38 100644 (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));
+                       }
                }
        }
 
index 762dbfac2fc5d5ff092b5ade6a205d0900f6933e..1676084f21dfad95b764c74cef4997af46cc303d 100644 (file)
 #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);