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>
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])
};
#define RPC_CONTEXT_MAGIC 0xc6e46435
+#define RPC_PARAM_UNDEFINED -1
struct rpc_context {
- uint32_t magic;
+ uint32_t magic;
int fd;
int is_connected;
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 {
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);
rpc->xid = salt + time(NULL) + getpid() << 16;
salt += 0x01000000;
rpc->fd = -1;
+ rpc->tcp_syncnt = RPC_PARAM_UNDEFINED;
return rpc;
}
if (strp2) {
*strp2 = 0;
strp2++;
+ if (!strncmp(strp, "tcp-syncnt", 10)) {
+ rpc_set_tcp_syncnt(nfs->rpc, atoi(strp2));
+ }
}
}
#include <sys/socket.h>
#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#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);
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;
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);