Skip to contentSkip to conversation

Customizable TCP backoff patch

PATCH In review Ben Woodard 6 participants 17 messages Sep 27, 2006 – Oct 12, 2006

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Customizable TCP backoff patch

Here at LLNL we have a rather challenging network environment on our clusters. We basically have 1000's of gigE links attached to an oversubscribed federated network. Most of the time this network is idle but the expected workload is for regular spikes extremely heavy activity lasting a few minutes. All end-points in a highly coordinated manor, typically after exiting an MPI barrier, start pushing as much data as possible through the oversubscribed core. The result is a wave of TCP back-offs where all the TCP streams back-off in lock step. The network oscillates from highly congested for brief moments to largely idle. Given enough time TCP will settle down in to something mostly reasonable but even then it causes us a few problems:

1) It takes a long time for the network to settle in to a steady state and while it does network utilization is very poor.

2) Many of the sockets will rapidly back off to the maximum value. This can lead to application level timeouts being triggered because we also initially calibrated the timeouts with the notion that 2 minute back-offs would be the exception and not the norm.

3) Once we reach steady state there's no guarantee of fairness between TCP streams. For our workload this is particularly undesirable since the parallel job which kicked off all this activity must wait until the slowest transaction completes. This translates in to 1000's of nodes sitting idle.

By knowing this is the expected workload on this dedicated network we can safely make the back-offs more aggressive to mitigate most of these issues.

We also played around with using a random seed when selecting the back-off interval to avoid all the sockets backing-off in lock step. That worked reasonably well but was more invasive then simply adding a few more tunable.

Because these are general utility clusters we run many different programs and so trying to fix this problem in the application is not possible since there are literally hundreds if not thousands of them.

We're more than willing to consider other approaches to handling this
particular workload better. We've even considered that TCP isn't at all the right protocol but this affects several protocols including NFS and the benefits of running NFS over TCP are too great.

The original patch was prepared by Brian Behlendorf. He asked me to adapt it for current kernels keep it up to date and send upstream.

This may also help people like Andrew Athan which reported a similar problem a couple of days ago on the linux-net mailing list: http://www.uwsg.iu.edu/hypermail/linux/net/0609.3/0005.html I suspect that it is more common a case than is widely recognized.

include/linux/sysctl.h +2 -0
diff -ru linux-2.6.18/include/linux/sysctl.h linux-2.6.18.new/include/linux/sysctl.h
@@ -411,6 +411,8 @@
 	NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115,
 	NET_TCP_DMA_COPYBREAK=116,
 	NET_TCP_SLOW_START_AFTER_IDLE=117,
+	NET_TCP_RTO_MAX=118,
+	NET_TCP_RTO_INIT=119,
 };
 
 enum {

Only in linux-2.6.18.new/include/linux: sysctl.h.orig Only in linux-2.6.18.new/include/linux: sysctl.h.rej

include/linux/tcp.h +4 -0
diff -ru linux-2.6.18/include/linux/tcp.h linux-2.6.18.new/include/linux/tcp.h
@@ -94,6 +94,8 @@
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_BACKOFF_MAX         14      /* Maximum backoff value */
+#define TCP_BACKOFF_INIT        15      /* Initial backoff value */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+        __u32   rto_max;        /* Maximum backoff value                */
+        __u32   rto_init;       /* Initial backoff value                */
 
 /* RTT measurement */
 	__u32	srtt;		/* smoothed round trip time << 3	*/

Only in linux-2.6.18.new/include/linux: tcp.h.orig

include/net/tcp.h +2 -0
diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h
@@ -227,6 +227,8 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern int sysctl_tcp_rto_max;
+extern int sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;

Only in linux-2.6.18.new/include/net: tcp.h.orig
Only in linux-2.6.18.new/include/net: tcp.h.rej

net/ipv4/sysctl_net_ipv4.c +16 -0
diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c
@@ -697,6 +697,22 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+	        .ctl_name       = NET_TCP_RTO_MAX,
+		.procname       = "tcp_rto_max",
+		.data           = &sysctl_tcp_rto_max,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_dointvec
+	},
+	{
+	        .ctl_name       = NET_TCP_RTO_INIT,
+		.procname       = "tcp_rto_init",
+		.data           = &sysctl_tcp_rto_init,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_dointvec
+	},
 	{ .ctl_name = 0 }
 };
 

Only in linux-2.6.18.new/net/ipv4: sysctl_net_ipv4.c.orig

net/ipv4/tcp.c +21 -0
diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c
@@ -1939,6 +1939,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val * HZ;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val * HZ;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2125,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+        case TCP_BACKOFF_MAX:
+                val = (tp->rto_max ? : sysctl_tcp_rto_max) / HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+                val = (tp->rto_init ? : sysctl_tcp_rto_init) / HZ;
+                break;
 	default:
 		return -ENOPROTOOPT;
 	};

Only in linux-2.6.18.new/net/ipv4: tcp.c.orig

net/ipv4/tcp_timer.c +12 -6
diff -ru linux-2.6.18/net/ipv4/tcp_timer.c linux-2.6.18.new/net/ipv4/tcp_timer.c
@@ -31,6 +31,8 @@
 int sysctl_tcp_retries1 = TCP_RETR1;
 int sysctl_tcp_retries2 = TCP_RETR2;
 int sysctl_tcp_orphan_retries;
+int sysctl_tcp_rto_max  = TCP_RTO_MAX;
+int sysctl_tcp_rto_init = TCP_TIMEOUT_INIT;
 
 static void tcp_write_timer(unsigned long);
 static void tcp_delack_timer(unsigned long);
@@ -71,7 +73,8 @@
 
 	/* If peer does not open window for long time, or did not transmit 
 	 * anything for long time, penalize it. */
-	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+	if ((s32)(tcp_time_stamp - tp->lsndtime) >
+	    2 * (tp->rto_max ? : sysctl_tcp_rto_max) || !do_reset)
 		orphans <<= 1;
 
 	/* If some dubious ICMP arrived, penalize even more. */
@@ -256,8 +259,8 @@
 	max_probes = sysctl_tcp_retries2;
 
 	if (sock_flag(sk, SOCK_DEAD)) {
-		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
- 
+		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < 
+				   (tp->rto_max ? : sysctl_tcp_rto_max));
 		max_probes = tcp_orphan_retries(sk, alive);
 
 		if (tcp_out_of_resources(sk, alive || icsk->icsk_probes_out <= max_probes))
@@ -301,7 +304,8 @@
 			       inet->num, tp->snd_una, tp->snd_nxt);
 		}
 #endif
-		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+		if (tcp_time_stamp - tp->rcv_tstamp > 
+		    (tp->rto_max ? : sysctl_tcp_rto_max)) {
 			tcp_write_err(sk);
 			goto out;
 		}
@@ -373,7 +377,8 @@
 
 out_reset_timer:
 	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
-	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
+	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, 
+				  (tp->rto_max ? : sysctl_tcp_rto_max));
 	if (icsk->icsk_retransmits > sysctl_tcp_retries1)
 		__sk_dst_reset(sk);
 
@@ -428,7 +433,8 @@
 static void tcp_synack_timer(struct sock *sk)
 {
 	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
-				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+				   TCP_TIMEOUT_INIT, 
+				   ((tp->rto_init ? : sysctl_tcp_rto_init)));
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)
maintainer
#1 lore raw

From: Ben Woodard <[email protected]>
Date: Wed, 27 Sep 2006 11:52:57 -0700

Quoted 3 lines

Because these are general utility clusters we run many different programs and so trying to fix this problem in the application is not possible since there are literally hundreds if not thousands of them.

Then why add a socket option setting as your patch does? :-)

I also object to the socket option setting being allowed for any user because this can have awful effects if allowed by arbitrary users on arbitrary networks.

Quoted 8 lines

We're more than willing to consider other approaches to handling this particular workload better. We've even considered that TCP isn't at all the right protocol but this affects several protocols including NFS and the benefits of running NFS over TCP are too great.

The original patch was prepared by Brian Behlendorf. He asked me to adapt it for current kernels keep it up to date and send upstream.

Quoted 7 lines

This may also help people like Andrew Athan which reported a similar problem a couple of days ago on the linux-net mailing list:
http://www.uwsg.iu.edu/hypermail/linux/net/0609.3/0005.html I suspect that it is more common a case than is widely recognized.

Other issues:

1) 2 "u32" in the tcp_sock is a lot of space to devote to this new state. If it can fit in 2 "u16"'s or even less space, please use that.

2) the expression "(tp->foo ? : sysctl_foo)" is repeated many times in the patch, please encapsulate it into an inline function or similar

I'm still torn on the fundamental issues of this patch. I think random backoff is a better generic solution to this kind of problem. If it works for ethernet, it might just work for TCP too :-)

Thanks.

#2 lore raw
Quoted 7 lines · On Wed, 27 Sep 2006 16:16:38 -0700 (PDT) David Miller <[email protected]>…

From: Ben Woodard <[email protected]>
Date: Wed, 27 Sep 2006 11:52:57 -0700

Quoted 3 lines

Because these are general utility clusters we run many different programs and so trying to fix this problem in the application is not possible since there are literally hundreds if not thousands of them.

Quoted 5 lines

Then why add a socket option setting as your patch does? :-)

I also object to the socket option setting being allowed for any user because this can have awful effects if allowed by arbitrary users on arbitrary networks.

Setting a cwnd limit would do the same thing.

maintainer
#3 lore raw

From: Stephen Hemminger <[email protected]>
Date: Wed, 27 Sep 2006 16:00:44 -0700

Quoted 10 lines
Quoted 7 lines · On Wed, 27 Sep 2006 16:16:38 -0700 (PDT) David Miller <[email protected]>…

From: Ben Woodard <[email protected]>
Date: Wed, 27 Sep 2006 11:52:57 -0700

Quoted 3 lines

Because these are general utility clusters we run many different programs and so trying to fix this problem in the application is not possible since there are literally hundreds if not thousands of them.

Quoted 7 lines
Quoted 5 lines

Then why add a socket option setting as your patch does? :-)

I also object to the socket option setting being allowed for any user because this can have awful effects if allowed by arbitrary users on arbitrary networks.

Setting a cwnd limit would do the same thing.

Not really :-)

It would not influence the TCP retransmit timeouts and backoff, although it would influence the congestion window handling during recovery after such timeout based losses.

#4 lore raw
In reply to David Miller
Quoted 23 lines · David Miller

From: Ben Woodard <[email protected]>
Date: Wed, 27 Sep 2006 11:52:57 -0700

Quoted 1 line

Because these are general utility clusters we run many different programs and so trying to fix this problem in the application is not possible since there are literally hundreds if not thousands of them.

Then why add a socket option setting as your patch does? :-)

I also object to the socket option setting being allowed for any user because this can have awful effects if allowed by arbitrary users on arbitrary networks.

Quoted 9 lines

We're more than willing to consider other approaches to handling this
particular workload better. We've even considered that TCP isn't at all the right protocol but this affects several protocols including NFS and the benefits of running NFS over TCP are too great.

The original patch was prepared by Brian Behlendorf. He asked me to adapt it for current kernels keep it up to date and send upstream.

This may also help people like Andrew Athan which reported a similar problem a couple of days ago on the linux-net mailing list: http://www.uwsg.iu.edu/hypermail/linux/net/0609.3/0005.html I suspect that it is more common a case than is widely recognized.

Other issues:

Quoted 8 lines

1) 2 "u32" in the tcp_sock is a lot of space to devote to this new state. If it can fit in 2 "u16"'s or even less space, please use that.

2) the expression "(tp->foo ? : sysctl_foo)" is repeated many times in the patch, please encapsulate it into an inline function or similar

How does this look to you for answering your two complaints above.

Quoted 3 lines

I'm still torn on the fundamental issues of this patch. I think random backoff is a better generic solution to this kind of problem. If it works for ethernet, it might just work for TCP too :-)

I haven't taken this on in this patch. I'd have to think more about how to do that and I'm not sure that introducing randomness here will allow us to settle into a steady state faster than configuring a shorter timeout in the environments that need it.

Quoted 2 lines

Thanks.

include/linux/sysctl.h +2 -0
diff -ru linux-2.6.18/include/linux/sysctl.h linux-2.6.18.new/include/linux/sysctl.h
@@ -411,6 +411,8 @@
 	NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115,
 	NET_TCP_DMA_COPYBREAK=116,
 	NET_TCP_SLOW_START_AFTER_IDLE=117,
+	NET_TCP_RTO_MAX=118,
+	NET_TCP_RTO_INIT=119,
 };
 
 enum {

Only in linux-2.6.18.new/include/linux: sysctl.h.orig Only in linux-2.6.18.new/include/linux: sysctl.h.rej

include/linux/tcp.h +4 -0
diff -ru linux-2.6.18/include/linux/tcp.h linux-2.6.18.new/include/linux/tcp.h
@@ -94,6 +94,8 @@
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_BACKOFF_MAX         14      /* Maximum backoff value */
+#define TCP_BACKOFF_INIT        15      /* Initial backoff value */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+        __u16   rto_max;        /* Maximum backoff value                */
+        __u16   rto_init;       /* Initial backoff value                */
 
 /* RTT measurement */
 	__u32	srtt;		/* smoothed round trip time << 3	*/

Only in linux-2.6.18.new/include/linux: tcp.h.orig

include/net/tcp.h +2 -0
diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h
@@ -227,6 +227,8 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern int sysctl_tcp_rto_max;
+extern int sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;

Only in linux-2.6.18.new/include/net: tcp.h.orig Only in linux-2.6.18.new/include/net: tcp.h.rej

net/ipv4/sysctl_net_ipv4.c +16 -0
diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c
@@ -697,6 +697,22 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+	        .ctl_name       = NET_TCP_RTO_MAX,
+		.procname       = "tcp_rto_max",
+		.data           = &sysctl_tcp_rto_max,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_dointvec
+	},
+	{
+	        .ctl_name       = NET_TCP_RTO_INIT,
+		.procname       = "tcp_rto_init",
+		.data           = &sysctl_tcp_rto_init,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_dointvec
+	},
 	{ .ctl_name = 0 }
 };
 

Only in linux-2.6.18.new/net/ipv4: sysctl_net_ipv4.c.orig

net/ipv4/tcp.c +23 -0
diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val * HZ;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val * HZ;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2127,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+        case TCP_BACKOFF_MAX:
+                val = (tp->rto_max ? : sysctl_tcp_rto_max) / HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+                val = (tp->rto_init ? : sysctl_tcp_rto_init) / HZ;
+                break;
 	default:
 		return -ENOPROTOOPT;
 	};

Only in linux-2.6.18.new/net/ipv4: tcp.c.orig

net/ipv4/tcp_timer.c +18 -7
diff -ru linux-2.6.18/net/ipv4/tcp_timer.c linux-2.6.18.new/net/ipv4/tcp_timer.c
@@ -31,11 +31,21 @@
 int sysctl_tcp_retries1 = TCP_RETR1;
 int sysctl_tcp_retries2 = TCP_RETR2;
 int sysctl_tcp_orphan_retries;
+int sysctl_tcp_rto_max  = TCP_RTO_MAX;
+int sysctl_tcp_rto_init = TCP_TIMEOUT_INIT;
 
 static void tcp_write_timer(unsigned long);
 static void tcp_delack_timer(unsigned long);
 static void tcp_keepalive_timer (unsigned long data);
 
+static inline __u16 rto_max(struct tcp_sock *tp){
+        return tp->rto_max ? : sysctl_tcp_rto_max;
+}
+
+static inline __u16 rto_init(struct tcp_sock *tp){
+        return tp->rto_init ? : sysctl_tcp_rto_init;
+}
+
 void tcp_init_xmit_timers(struct sock *sk)
 {
 	inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer,
@@ -71,7 +81,7 @@
 
 	/* If peer does not open window for long time, or did not transmit 
 	 * anything for long time, penalize it. */
-	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*rto_max(tp) || !do_reset)
 		orphans <<= 1;
 
 	/* If some dubious ICMP arrived, penalize even more. */
@@ -256,8 +266,8 @@
 	max_probes = sysctl_tcp_retries2;
 
 	if (sock_flag(sk, SOCK_DEAD)) {
-		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
- 
+		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < 
+				   rto_max(tp));
 		max_probes = tcp_orphan_retries(sk, alive);
 
 		if (tcp_out_of_resources(sk, alive || icsk->icsk_probes_out <= max_probes))
@@ -301,7 +311,7 @@
 			       inet->num, tp->snd_una, tp->snd_nxt);
 		}
 #endif
-		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+		if (tcp_time_stamp - tp->rcv_tstamp > rto_max(tp)) {
 			tcp_write_err(sk);
 			goto out;
 		}
@@ -373,7 +383,8 @@
 
 out_reset_timer:
 	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
-	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
+	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, 
+				  rto_max(tp));
 	if (icsk->icsk_retransmits > sysctl_tcp_retries1)
 		__sk_dst_reset(sk);
 
@@ -427,8 +438,8 @@
 
 static void tcp_synack_timer(struct sock *sk)
 {
-	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
-				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, 
+				   TCP_TIMEOUT_INIT, rto_max(tcp_sk(sk)));
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)
maintainer
#5 lore raw

From: Ben Woodard <[email protected]>
Date: Tue, 03 Oct 2006 11:14:38 -0700

Quoted 6 lines
Quoted 6 lines

Other issues:

1) 2 "u32" in the tcp_sock is a lot of space to devote to this new state. If it can fit in 2 "u16"'s or even less space, please use that.

Quoted 6 lines
Quoted 4 lines

2) the expression "(tp->foo ? : sysctl_foo)" is repeated many times in the patch, please encapsulate it into an inline function or similar

How does this look to you for answering your two complaints above.

It looks a lot better. I'd like you to take #2 further, put the inline function into net/tcp.h and use it in the tcp.c instances too.

Also, please don't format code like this:

+static inline __u16 rto_max(struct tcp_sock *tp){
+ return tp->rto_max ? : sysctl_tcp_rto_max;
+}
+
+static inline __u16 rto_init(struct tcp_sock *tp){
+ return tp->rto_init ? : sysctl_tcp_rto_init;
+}

The openning brace of each functions belongs on a line by itself, thanks.

Finally, I'm not so sure "seconds" is the best unit for the socket option. In fact you use "seconds" as the unit for the socket option and the sysctl is raw jiffies. That's inconsistency is really problematic.

At the very least, seconds might not be fine enough granularity for some circumstances. Heck, the default RTO_MIN is 1/5 of a second. :-)

I also understand that going to milliseconds or microseconds would make the size of the in-socket struct members an issue again. These things are never easy are they? :-/

Any better ideas?

#6 lore raw
Quoted 7 lines · David Miller

At the very least, seconds might not be fine enough granularity for some circumstances. Heck, the default RTO_MIN is 1/5 of a second. :-)

I also understand that going to milliseconds or microseconds would make the size of the in-socket struct members an issue again. These things are never easy are they? :-/

Would be, if floating point values would be allowed. Single precision would be enough in this case and its just 32 bits IIRC.

I mean floating point values just for the user->kernel ABI and NOT for the internal timer representation.

Regards

Ingo Oeser

maintainer
#7 lore raw

From: Ingo Oeser <[email protected]>
Date: Wed, 4 Oct 2006 10:56:12 +0200

Quoted 9 lines
Quoted 7 lines · David Miller

At the very least, seconds might not be fine enough granularity for some circumstances. Heck, the default RTO_MIN is 1/5 of a second. :-)

I also understand that going to milliseconds or microseconds would make the size of the in-socket struct members an issue again. These things are never easy are they? :-/

Quoted 5 lines

Would be, if floating point values would be allowed. Single precision would be enough in this case and its just 32 bits IIRC.

I mean floating point values just for the user->kernel ABI and NOT for the internal timer representation.

We got the struct members down to 16-bits each, the problem is in fact that we'd need to make them 32-bits again.

#8 lore raw
In reply to David Miller
Quoted 42 lines · On Wed, 04 Oct 2006 00:07:22 -0700 (PDT) David Miller <[email protected]>…

From: Ben Woodard <[email protected]>
Date: Tue, 03 Oct 2006 11:14:38 -0700

Quoted 6 lines
Quoted 6 lines

Other issues:

1) 2 "u32" in the tcp_sock is a lot of space to devote to this new state. If it can fit in 2 "u16"'s or even less space, please use that.

Quoted 6 lines
Quoted 4 lines

2) the expression "(tp->foo ? : sysctl_foo)" is repeated many times in the patch, please encapsulate it into an inline function or similar

How does this look to you for answering your two complaints above.

It looks a lot better. I'd like you to take #2 further, put the inline function into net/tcp.h and use it in the tcp.c instances too.

Also, please don't format code like this:

net/ipv4/tcp_timer.c +7 -0
+static inline __u16 rto_max(struct tcp_sock *tp){
+        return tp->rto_max ? : sysctl_tcp_rto_max;
+}
+
+static inline __u16 rto_init(struct tcp_sock *tp){
+        return tp->rto_init ? : sysctl_tcp_rto_init;
+}

The openning brace of each functions belongs on a line by itself, thanks.

Finally, I'm not so sure "seconds" is the best unit for the socket option. In fact you use "seconds" as the unit for the socket option and the sysctl is raw jiffies. That's inconsistency is really problematic.

At the very least, seconds might not be fine enough granularity for some circumstances. Heck, the default RTO_MIN is 1/5 of a second. :-)

Quoted 5 lines

I also understand that going to milliseconds or microseconds would make the size of the in-socket struct members an issue again. These things are never easy are they? :-/

Any better ideas?

If you are willing to do a little more work, the sysctl value can be in milliseconds, and the internal socket member in some other unit like jiffies. It does require writing your own in/out translation instead of using proc_dointvec

Signature · 1 line
Stephen Hemminger <[email protected]>
#9 lore raw
In reply to David Miller
Quoted 13 lines · David Miller

From: Ben Woodard <[email protected]>
Date: Tue, 03 Oct 2006 11:14:38 -0700

Quoted 10 lines
Quoted 10 lines

Other issues:

1) 2 "u32" in the tcp_sock is a lot of space to devote to this new state. If it can fit in 2 "u16"'s or even less space, please use that.

2) the expression "(tp->foo ? : sysctl_foo)" is repeated many times in the patch, please encapsulate it into an inline function or similar

Quoted 5 lines
Quoted 1 line

How does this look to you for answering your two complaints above.

It looks a lot better. I'd like you to take #2 further, put the inline function into net/tcp.h and use it in the tcp.c instances too.

Done. I orginally didn't want to grow the header files with two more functions that weren't use many places. Anyway this is fixed.

Quoted 11 lines

Also, please don't format code like this:

(quoted diff) +7 -0
+static inline __u16 rto_max(struct tcp_sock *tp){
+        return tp->rto_max ? : sysctl_tcp_rto_max;
+}
+
+static inline __u16 rto_init(struct tcp_sock *tp){
+        return tp->rto_init ? : sysctl_tcp_rto_init;
+}
Quoted 2 lines

The opening brace of each functions belongs on a line by itself, thanks.

Fixed.

Quoted 10 lines

Finally, I'm not so sure "seconds" is the best unit for the socket option. In fact you use "seconds" as the unit for the socket option and the sysctl is raw jiffies. That's inconsistency is really problematic.

At the very least, seconds might not be fine enough granularity for some circumstances. Heck, the default RTO_MIN is 1/5 of a second. :-)

Quoted 5 lines

I also understand that going to milliseconds or microseconds would make the size of the in-socket struct members an issue again. These things are never easy are they? :-/

Any better ideas?

Here is what I've done. I store the values in the socket structure as ms so that they will fit into 16 bits and as jiffies in the global variables. Then I populate the global variables using proc_doulongvec_ms_jiffies()

How does that look to you?

-ben

include/linux/sysctl.h +2 -0
diff -ru linux-2.6.18/include/linux/sysctl.h linux-2.6.18.new/include/linux/sysctl.h
@@ -411,6 +411,8 @@
 	NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115,
 	NET_TCP_DMA_COPYBREAK=116,
 	NET_TCP_SLOW_START_AFTER_IDLE=117,
+	NET_TCP_RTO_MAX=118,
+	NET_TCP_RTO_INIT=119,
 };
 
 enum {
include/linux/tcp.h +4 -0
diff -ru linux-2.6.18/include/linux/tcp.h linux-2.6.18.new/include/linux/tcp.h
@@ -94,6 +94,8 @@
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_BACKOFF_MAX         14      /* Maximum backoff value */
+#define TCP_BACKOFF_INIT        15      /* Initial backoff value */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+        __u16   rto_max;        /* Maximum backoff value in ms          */
+        __u16   rto_init;       /* Initial backoff value in ms          */
 
 /* RTT measurement */
 	__u32	srtt;		/* smoothed round trip time << 3	*/
include/net/tcp.h +12 -0
diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h
@@ -227,11 +227,23 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern unsigned long sysctl_tcp_rto_max;
+extern unsigned long sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
 
+static inline unsigned long tcp_rto_max(struct tcp_sock *tp)
+{
+        return tp->rto_max ? tp->rto_max*HZ/1000 : sysctl_tcp_rto_max;
+}
+
+static inline unsigned long tcp_rto_init(struct tcp_sock *tp)
+{
+        return tp->rto_init ? tp->rto_init*HZ/1000 : sysctl_tcp_rto_init;
+}
+
 /*
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).
net/ipv4/sysctl_net_ipv4.c +22 -0
diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+	        .ctl_name       = NET_TCP_RTO_MAX,
+		.procname       = "tcp_rto_max",
+		.data           = &sysctl_tcp_rto_max,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
+	{
+	        .ctl_name       = NET_TCP_RTO_INIT,
+		.procname       = "tcp_rto_init",
+		.data           = &sysctl_tcp_rto_init,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };
 
net/ipv4/tcp.c +23 -0
diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2127,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+        case TCP_BACKOFF_MAX:
+	        val = tcp_rto_max(tp)*1000/HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+	        val = tcp_rto_init(tp)*1000/HZ;
+                break;
 	default:
 		return -ENOPROTOOPT;
 	};
net/ipv4/tcp_timer.c +11 -7
diff -ru linux-2.6.18/net/ipv4/tcp_timer.c linux-2.6.18.new/net/ipv4/tcp_timer.c
@@ -31,6 +31,8 @@
 int sysctl_tcp_retries1 = TCP_RETR1;
 int sysctl_tcp_retries2 = TCP_RETR2;
 int sysctl_tcp_orphan_retries;
+unsigned long sysctl_tcp_rto_max  = TCP_RTO_MAX;
+unsigned long sysctl_tcp_rto_init = TCP_TIMEOUT_INIT;
 
 static void tcp_write_timer(unsigned long);
 static void tcp_delack_timer(unsigned long);
@@ -71,7 +73,8 @@
 
 	/* If peer does not open window for long time, or did not transmit 
 	 * anything for long time, penalize it. */
-	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*tcp_rto_max(tp) || 
+	    !do_reset)
 		orphans <<= 1;
 
 	/* If some dubious ICMP arrived, penalize even more. */
@@ -256,8 +259,8 @@
 	max_probes = sysctl_tcp_retries2;
 
 	if (sock_flag(sk, SOCK_DEAD)) {
-		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
- 
+		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < 
+				   tcp_rto_max(tp));
 		max_probes = tcp_orphan_retries(sk, alive);
 
 		if (tcp_out_of_resources(sk, alive || icsk->icsk_probes_out <= max_probes))
@@ -301,7 +304,7 @@
 			       inet->num, tp->snd_una, tp->snd_nxt);
 		}
 #endif
-		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+		if (tcp_time_stamp - tp->rcv_tstamp > tcp_rto_max(tp)) {
 			tcp_write_err(sk);
 			goto out;
 		}
@@ -373,7 +376,8 @@
 
 out_reset_timer:
 	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
-	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
+	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, 
+				  tcp_rto_max(tp));
 	if (icsk->icsk_retransmits > sysctl_tcp_retries1)
 		__sk_dst_reset(sk);
 
@@ -427,8 +431,8 @@
 
 static void tcp_synack_timer(struct sock *sk)
 {
-	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
-				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, TCP_TIMEOUT_INIT, 
+				   tcp_rto_max(tcp_sk(sk)));
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)
#10 lore raw

In article <[email protected]> (at Tue, 10 Oct 2006 18:46:58 -0700), Ben Woodard <[email protected]> says:

Quoted 28 lines

diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c

net/ipv4/tcp.c +15 -0
@@ -1939,6 +1941,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2127,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;

net/ipv4/tcp.c line 2138

Quoted 10 lines
net/ipv4/tcp.c +6 -0
 		return 0;
+        case TCP_BACKOFF_MAX:
+	        val = tcp_rto_max(tp)*1000/HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+	        val = tcp_rto_init(tp)*1000/HZ;
+                break;
 	default:
 		return -ENOPROTOOPT;
 	};

They look very inconsistent. Please use tp->rto_max / tp->rto_init.

--yoshfuji

maintainer
#11 lore raw
In reply to Ben Woodard

From: Ben Woodard <[email protected]>
Date: Tue, 10 Oct 2006 18:46:58 -0700

include/linux/tcp.h line 263

Quoted 6 lines
include/linux/tcp.h +2 -0
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+        __u16   rto_max;        /* Maximum backoff value in ms          */
+        __u16   rto_init;       /* Initial backoff value in ms          */

The existing struct members are properly indented using tabs, your patch adds members which are indented using spaces. Please fix this, thanks a lot.

maintainer
#12 lore raw
In reply to Ben Woodard

Actually, your entire patch uses spaces for indentation of every new line added, not just those two new struct members I pointed out. Please use tabs for all of those cases.

Thanks a lot.

#13 lore raw
In reply to Ben Woodard

Ben Woodard wrote:
------------------------------------------------------------------------

Quoted 7 lines

diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h

include/net/tcp.h +0 -0
@@ -227,11 +227,23 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;

include/net/tcp.h line 239

Quoted 10 lines
include/net/tcp.h +5 -0
+extern unsigned long sysctl_tcp_rto_max;
+extern unsigned long sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
 
+static inline unsigned long tcp_rto_max(struct tcp_sock *tp)
+{
+        return tp->rto_max ? tp->rto_max*HZ/1000 : sysctl_tcp_rto_max;
                                ^^^^^^^^^^^^^^^^^^^

That should probably be msecs_to_jiffies(tp->rto_max)

include/net/tcp.h line 244

Quoted 5 lines
include/net/tcp.h +5 -0
+}
+
+static inline unsigned long tcp_rto_init(struct tcp_sock *tp)
+{
+        return tp->rto_init ? tp->rto_init*HZ/1000 : sysctl_tcp_rto_init;

Ditto.

Quoted 33 lines
include/net/tcp.h +2 -0
+}
+
 /*
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).

diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c

net/ipv4/sysctl_net_ipv4.c +14 -0
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+	        .ctl_name       = NET_TCP_RTO_MAX,
+		.procname       = "tcp_rto_max",
+		.data           = &sysctl_tcp_rto_max,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
+	{
+	        .ctl_name       = NET_TCP_RTO_INIT,

net/ipv4/sysctl_net_ipv4.c line 723

Quoted 10 lines
net/ipv4/sysctl_net_ipv4.c +8 -0
+		.procname       = "tcp_rto_init",
+		.data           = &sysctl_tcp_rto_init,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };

Try as I might, I can't find proc_doulongvec_ms_jiffies() anywhere. I think you meant proc_doulongvec_ms_jiffies_minmax()?

Also, this function has a bug in that it doesn't do corrections for potentially different HZ values. Look at the msecs_to_jiffies... When I've used proc_doulongvec_ms_jiffies_minmax() before, I would seen rather interesting truncation errors such that sysctl input didn't match sysctl output.

Quoted 36 lines

diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c

net/ipv4/tcp.c +17 -0
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;

net/ipv4/tcp.c line 2134

Quoted 8 lines
net/ipv4/tcp.c +5 -0
@@ -2110,6 +2127,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+        case TCP_BACKOFF_MAX:
+	        val = tcp_rto_max(tp)*1000/HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+	        val = tcp_rto_init(tp)*1000/HZ;

The two divisions above introduce truncation errors such the input doesn't match the output. Better to use jiffies_to_msecs().

Regards
-vlad

#14 lore raw

Here we go again. The changes are as follows:
1) the spaces are gone and now it is tabs.
2) used msecs_to_jiffies() and jiffies_to_msecs(). I'm much happier with
that. I didn't know those functions existed.
3) changed over to proc_doulongvec_ms_jiffies_minmax(). Last night's test compile failed because I was out of disk. I figured that the code I changed had compiled. I guess not.

I really didn't understand Yoshifuji Hideaki's comment. (sorry)

-ben

Quoted 130 lines · Vlad Yasevich

Ben Woodard wrote:
------------------------------------------------------------------------

Quoted 7 lines

diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h

include/net/tcp.h +0 -0
@@ -227,11 +227,23 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;

include/net/tcp.h line 239

Quoted 10 lines
include/net/tcp.h +5 -0
+extern unsigned long sysctl_tcp_rto_max;
+extern unsigned long sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
 
+static inline unsigned long tcp_rto_max(struct tcp_sock *tp)
+{
+        return tp->rto_max ? tp->rto_max*HZ/1000 : sysctl_tcp_rto_max;
                                ^^^^^^^^^^^^^^^^^^^

That should probably be msecs_to_jiffies(tp->rto_max)

include/net/tcp.h line 244

Quoted 5 lines
include/net/tcp.h +5 -0
+}
+
+static inline unsigned long tcp_rto_init(struct tcp_sock *tp)
+{
+        return tp->rto_init ? tp->rto_init*HZ/1000 : sysctl_tcp_rto_init;

Ditto.

Quoted 33 lines
include/net/tcp.h +2 -0
+}
+
 /*
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).

diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c

net/ipv4/sysctl_net_ipv4.c +14 -0
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+	        .ctl_name       = NET_TCP_RTO_MAX,
+		.procname       = "tcp_rto_max",
+		.data           = &sysctl_tcp_rto_max,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
+	{
+	        .ctl_name       = NET_TCP_RTO_INIT,

net/ipv4/sysctl_net_ipv4.c line 723

Quoted 10 lines
net/ipv4/sysctl_net_ipv4.c +8 -0
+		.procname       = "tcp_rto_init",
+		.data           = &sysctl_tcp_rto_init,
+		.maxlen         = sizeof(unsigned),
+		.mode           = 0644,
+		.proc_handler   = &proc_doulongvec_ms_jiffies,
+		.extra1         = &tcp_rto_min_constant,
+		.extra2         = &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };

Try as I might, I can't find proc_doulongvec_ms_jiffies() anywhere. I think you meant proc_doulongvec_ms_jiffies_minmax()?

Also, this function has a bug in that it doesn't do corrections for potentially different HZ values. Look at the msecs_to_jiffies... When I've used proc_doulongvec_ms_jiffies_minmax() before, I would seen rather interesting truncation errors such that sysctl input didn't match sysctl output.

Quoted 36 lines

diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c

net/ipv4/tcp.c +17 -0
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,21 @@
 		}
 		break;
 
+        case TCP_BACKOFF_MAX:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_max = val;
+                break;
+ 
+        case TCP_BACKOFF_INIT:
+                if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+                        err = -EINVAL;
+                else
+                        tp->rto_init = val;
+                break;
+ 
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;

net/ipv4/tcp.c line 2134

Quoted 8 lines
net/ipv4/tcp.c +5 -0
@@ -2110,6 +2127,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+        case TCP_BACKOFF_MAX:
+	        val = tcp_rto_max(tp)*1000/HZ;
+                break;
+        case TCP_BACKOFF_INIT:
+	        val = tcp_rto_init(tp)*1000/HZ;

The two divisions above introduce truncation errors such the input doesn't match the output. Better to use jiffies_to_msecs().

Quoted 2 lines

Regards
-vlad

include/linux/sysctl.h +2 -0
diff -ru linux-2.6.18/include/linux/sysctl.h linux-2.6.18.new/include/linux/sysctl.h
@@ -411,6 +411,8 @@
 	NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115,
 	NET_TCP_DMA_COPYBREAK=116,
 	NET_TCP_SLOW_START_AFTER_IDLE=117,
+	NET_TCP_RTO_MAX=118,
+	NET_TCP_RTO_INIT=119,
 };
 
 enum {
include/linux/tcp.h +4 -0
diff -ru linux-2.6.18/include/linux/tcp.h linux-2.6.18.new/include/linux/tcp.h
@@ -94,6 +94,8 @@
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_BACKOFF_MAX         14      /* Maximum backoff value */
+#define TCP_BACKOFF_INIT        15      /* Initial backoff value */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+	__u16	rto_max;	/* Maximum backoff value in ms		*/
+	__u16	rto_init;	/* Initial backoff value in ms		*/
 
 /* RTT measurement */
 	__u32	srtt;		/* smoothed round trip time << 3	*/
include/net/tcp.h +12 -0
diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h
@@ -227,11 +227,23 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern unsigned long sysctl_tcp_rto_max;
+extern unsigned long sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
 
+static inline unsigned long tcp_rto_max(struct tcp_sock *tp)
+{
+	return tp->rto_max ? msecs_to_jiffies(tp->rto_max) : sysctl_tcp_rto_max;
+}
+
+static inline unsigned long tcp_rto_init(struct tcp_sock *tp)
+{
+	return tp->rto_init ? msecs_to_jiffies(tp->rto_init) : sysctl_tcp_rto_init;
+}
+
 /*
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).

Only in linux-2.6.18.new/include/net: tcp.h~

net/ipv4/sysctl_net_ipv4.c +22 -0
diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+		.ctl_name	= NET_TCP_RTO_MAX,
+		.procname	= "tcp_rto_max",
+		.data		= &sysctl_tcp_rto_max,
+		.maxlen		= sizeof(unsigned),
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
+	{
+		.ctl_name	= NET_TCP_RTO_INIT,
+		.procname	= "tcp_rto_init",
+		.data		= &sysctl_tcp_rto_init,
+		.maxlen		= sizeof(unsigned),
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };
 
net/ipv4/tcp.c +22 -0
diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,20 @@
 		}
 		break;
 
+	case TCP_BACKOFF_MAX:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_max = val;
+		break;
+ 
+	case TCP_BACKOFF_INIT:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_init = val;
+		break;
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2126,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+	case TCP_BACKOFF_MAX:
+		val = jiffies_to_msecs(tcp_rto_max(tp));
+		break;
+	case TCP_BACKOFF_INIT:
+		val = jiffies_to_msecs(tcp_rto_init(tp));
+		break;
 	default:
 		return -ENOPROTOOPT;
 	};
net/ipv4/tcp_timer.c +11 -7
diff -ru linux-2.6.18/net/ipv4/tcp_timer.c linux-2.6.18.new/net/ipv4/tcp_timer.c
@@ -31,6 +31,8 @@
 int sysctl_tcp_retries1 = TCP_RETR1;
 int sysctl_tcp_retries2 = TCP_RETR2;
 int sysctl_tcp_orphan_retries;
+unsigned long sysctl_tcp_rto_max  = TCP_RTO_MAX;
+unsigned long sysctl_tcp_rto_init = TCP_TIMEOUT_INIT;
 
 static void tcp_write_timer(unsigned long);
 static void tcp_delack_timer(unsigned long);
@@ -71,7 +73,8 @@
 
 	/* If peer does not open window for long time, or did not transmit 
 	 * anything for long time, penalize it. */
-	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*tcp_rto_max(tp) || 
+	    !do_reset)
 		orphans <<= 1;
 
 	/* If some dubious ICMP arrived, penalize even more. */
@@ -256,8 +259,8 @@
 	max_probes = sysctl_tcp_retries2;
 
 	if (sock_flag(sk, SOCK_DEAD)) {
-		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
- 
+		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < 
+				   tcp_rto_max(tp));
 		max_probes = tcp_orphan_retries(sk, alive);
 
 		if (tcp_out_of_resources(sk, alive || icsk->icsk_probes_out <= max_probes))
@@ -301,7 +304,7 @@
 			       inet->num, tp->snd_una, tp->snd_nxt);
 		}
 #endif
-		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+		if (tcp_time_stamp - tp->rcv_tstamp > tcp_rto_max(tp)) {
 			tcp_write_err(sk);
 			goto out;
 		}
@@ -373,7 +376,8 @@
 
 out_reset_timer:
 	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
-	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
+	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, 
+				  tcp_rto_max(tp));
 	if (icsk->icsk_retransmits > sysctl_tcp_retries1)
 		__sk_dst_reset(sk);
 
@@ -427,8 +431,8 @@
 
 static void tcp_synack_timer(struct sock *sk)
 {
-	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
-				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, TCP_TIMEOUT_INIT, 
+				   tcp_rto_max(tcp_sk(sk)));
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)
#15 lore raw

In article <[email protected]> (at Wed, 11 Oct 2006 17:51:24 -0700), Ben Woodard <[email protected]> says:

Quoted 13 lines

diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c

net/ipv4/sysctl_net_ipv4.c +2 -0
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {

net/ipv4/sysctl_net_ipv4.c line 706

Quoted 8 lines
net/ipv4/sysctl_net_ipv4.c +5 -0
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+		.ctl_name	= NET_TCP_RTO_MAX,
+		.procname	= "tcp_rto_max",
+		.data		= &sysctl_tcp_rto_max,
+		.maxlen		= sizeof(unsigned),

sizeof(unsigned long)

net/ipv4/sysctl_net_ipv4.c line 716

Quoted 10 lines
net/ipv4/sysctl_net_ipv4.c +10 -0
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
+	{
+		.ctl_name	= NET_TCP_RTO_INIT,
+		.procname	= "tcp_rto_init",
+		.data		= &sysctl_tcp_rto_init,
+		.maxlen		= sizeof(unsigned),

sizeof(unsigned long)

Quoted 42 lines
net/ipv4/sysctl_net_ipv4.c +5 -0
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };
 

diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c

net/ipv4/tcp.c +16 -0
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,20 @@
 		}
 		break;
 
+	case TCP_BACKOFF_MAX:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_max = val;
+		break;
+ 
+	case TCP_BACKOFF_INIT:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_init = val;
+		break;
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;

net/ipv4/tcp.c line 2131

Quoted 6 lines
net/ipv4/tcp.c +3 -0
@@ -2110,6 +2126,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+	case TCP_BACKOFF_MAX:
+		val = jiffies_to_msecs(tcp_rto_max(tp));
+		break;

tp->rto_max

net/ipv4/tcp.c line 2135

Quoted 4 lines
net/ipv4/tcp.c +3 -0
+	case TCP_BACKOFF_INIT:
+		val = jiffies_to_msecs(tcp_rto_init(tp));
+		break;
 	default:

tp->rto_init

--yoshfuji

#16 lore raw
Quoted 4 lines · YOSHIFUJI Hideaki / 吉藤英明

Review

Quoted 2 lines
(quoted diff) +2 -0
+		.data		= &sysctl_tcp_rto_max,
+		.maxlen		= sizeof(unsigned),

sizeof(unsigned long)

Good catch. That would have corrupted things badly on some 64b platforms. With all the flux in the area I forgot to change the size of that but would have been OK on the ia32 boxes.

Quoted 13 lines

net/ipv4/tcp.c line 2131

Quoted 10 lines

diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c

net/ipv4/tcp.c +3 -0
@@ -2110,6 +2126,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+	case TCP_BACKOFF_MAX:
+		val = jiffies_to_msecs(tcp_rto_max(tp));
+		break;

tp->rto_max

Quoted 6 lines

net/ipv4/tcp.c line 2135

Quoted 4 lines
net/ipv4/tcp.c +3 -0
+	case TCP_BACKOFF_INIT:
+		val = jiffies_to_msecs(tcp_rto_init(tp));
+		break;
 	default:

tp->rto_init

OK I get it now.

Quoted 2 lines

--yoshfuji

include/linux/sysctl.h +2 -0
diff -ru linux-2.6.18/include/linux/sysctl.h linux-2.6.18.new/include/linux/sysctl.h
@@ -411,6 +411,8 @@
 	NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115,
 	NET_TCP_DMA_COPYBREAK=116,
 	NET_TCP_SLOW_START_AFTER_IDLE=117,
+	NET_TCP_RTO_MAX=118,
+	NET_TCP_RTO_INIT=119,
 };
 
 enum {
include/linux/tcp.h +4 -0
diff -ru linux-2.6.18/include/linux/tcp.h linux-2.6.18.new/include/linux/tcp.h
@@ -94,6 +94,8 @@
 #define TCP_INFO		11	/* Information about this connection. */
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
+#define TCP_BACKOFF_MAX		14	/* Maximum backoff value */
+#define TCP_BACKOFF_INIT	15	/* Initial backoff value */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -257,6 +259,8 @@
 	__u8	frto_counter;	/* Number of new acks after RTO */
 	__u8	nonagle;	/* Disable Nagle algorithm?             */
 	__u8	keepalive_probes; /* num of allowed keep alive probes	*/
+	__u16	rto_max;	/* Maximum backoff value in ms		*/
+	__u16	rto_init;	/* Initial backoff value in ms		*/
 
 /* RTT measurement */
 	__u32	srtt;		/* smoothed round trip time << 3	*/

Only in linux-2.6.18.new/include/linux: tcp.h~

include/net/tcp.h +12 -0
diff -ru linux-2.6.18/include/net/tcp.h linux-2.6.18.new/include/net/tcp.h
@@ -227,11 +227,23 @@
 extern int sysctl_tcp_base_mss;
 extern int sysctl_tcp_workaround_signed_windows;
 extern int sysctl_tcp_slow_start_after_idle;
+extern unsigned long sysctl_tcp_rto_max;
+extern unsigned long sysctl_tcp_rto_init;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
 extern int tcp_memory_pressure;
 
+static inline unsigned long tcp_rto_max(struct tcp_sock *tp)
+{
+	return tp->rto_max ? msecs_to_jiffies(tp->rto_max) : sysctl_tcp_rto_max;
+}
+
+static inline unsigned long tcp_rto_init(struct tcp_sock *tp)
+{
+	return tp->rto_init ? msecs_to_jiffies(tp->rto_init) : sysctl_tcp_rto_init;
+}
+
 /*
  * The next routines deal with comparing 32 bit unsigned ints
  * and worry about wraparound (automatic with unsigned arithmetic).

Only in linux-2.6.18.new/include/net: tcp.h~

net/ipv4/sysctl_net_ipv4.c +22 -0
diff -ru linux-2.6.18/net/ipv4/sysctl_net_ipv4.c linux-2.6.18.new/net/ipv4/sysctl_net_ipv4.c
@@ -128,6 +128,8 @@
 	return ret;
 }
 
+static unsigned long tcp_rto_min=0;
+static unsigned long tcp_rto_max=65535;
 
 ctl_table ipv4_table[] = {
         {
@@ -697,6 +699,26 @@
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+		.ctl_name	= NET_TCP_RTO_MAX,
+		.procname	= "tcp_rto_max",
+		.data		= &sysctl_tcp_rto_max,
+		.maxlen		= sizeof(unsigned long),
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
+	{
+		.ctl_name	= NET_TCP_RTO_INIT,
+		.procname	= "tcp_rto_init",
+		.data		= &sysctl_tcp_rto_init,
+		.maxlen		= sizeof(unsigned long),
+		.mode		= 0644,
+		.proc_handler	= &proc_doulongvec_ms_jiffies_minmax,
+		.extra1		= &tcp_rto_min_constant,
+		.extra2		= &tcp_rto_max_constant,
+	},
 	{ .ctl_name = 0 }
 };
 

Only in linux-2.6.18.new/net/ipv4: sysctl_net_ipv4.c~

net/ipv4/tcp.c +22 -0
diff -ru linux-2.6.18/net/ipv4/tcp.c linux-2.6.18.new/net/ipv4/tcp.c
@@ -1764,6 +1764,8 @@
 	return err;
 }
 
+#define TCP_BACKOFF_MAXVAL 65535
+
 /*
  *	Socket option code for TCP.
  */
@@ -1939,6 +1941,20 @@
 		}
 		break;
 
+	case TCP_BACKOFF_MAX:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_max = val;
+		break;
+ 
+	case TCP_BACKOFF_INIT:
+		if (val < 1 || val > TCP_BACKOFF_MAXVAL)
+			err = -EINVAL;
+		else
+			tp->rto_init = val;
+		break;
+ 
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2110,6 +2126,12 @@
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+	case TCP_BACKOFF_MAX:
+		val = tp->rto_max;
+		break;
+	case TCP_BACKOFF_INIT:
+		val = tp->rto_init;
+		break;
 	default:
 		return -ENOPROTOOPT;
 	};

Only in linux-2.6.18.new/net/ipv4: tcp.c~

net/ipv4/tcp_timer.c +11 -7
diff -ru linux-2.6.18/net/ipv4/tcp_timer.c linux-2.6.18.new/net/ipv4/tcp_timer.c
@@ -31,6 +31,8 @@
 int sysctl_tcp_retries1 = TCP_RETR1;
 int sysctl_tcp_retries2 = TCP_RETR2;
 int sysctl_tcp_orphan_retries;
+unsigned long sysctl_tcp_rto_max  = TCP_RTO_MAX;
+unsigned long sysctl_tcp_rto_init = TCP_TIMEOUT_INIT;
 
 static void tcp_write_timer(unsigned long);
 static void tcp_delack_timer(unsigned long);
@@ -71,7 +73,8 @@
 
 	/* If peer does not open window for long time, or did not transmit 
 	 * anything for long time, penalize it. */
-	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
+	if ((s32)(tcp_time_stamp - tp->lsndtime) > 2*tcp_rto_max(tp) || 
+	    !do_reset)
 		orphans <<= 1;
 
 	/* If some dubious ICMP arrived, penalize even more. */
@@ -256,8 +259,8 @@
 	max_probes = sysctl_tcp_retries2;
 
 	if (sock_flag(sk, SOCK_DEAD)) {
-		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
- 
+		const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < 
+				   tcp_rto_max(tp));
 		max_probes = tcp_orphan_retries(sk, alive);
 
 		if (tcp_out_of_resources(sk, alive || icsk->icsk_probes_out <= max_probes))
@@ -301,7 +304,7 @@
 			       inet->num, tp->snd_una, tp->snd_nxt);
 		}
 #endif
-		if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
+		if (tcp_time_stamp - tp->rcv_tstamp > tcp_rto_max(tp)) {
 			tcp_write_err(sk);
 			goto out;
 		}
@@ -373,7 +376,8 @@
 
 out_reset_timer:
 	icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
-	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
+	inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, 
+				  tcp_rto_max(tp));
 	if (icsk->icsk_retransmits > sysctl_tcp_retries1)
 		__sk_dst_reset(sk);
 
@@ -427,8 +431,8 @@
 
 static void tcp_synack_timer(struct sock *sk)
 {
-	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
-				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
+	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL, TCP_TIMEOUT_INIT, 
+				   tcp_rto_max(tcp_sk(sk)));
 }
 
 void tcp_set_keepalive(struct sock *sk, int val)