$Id: linux-2.6_freezetcp.patch 1061 2009-06-05 08:49:48Z omehani $
Freeze-TCP patch for Linux 2.6 (Git tree d9244b5).

Olivier Mehani <olivier.mehani@nicta.com.au>
====
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 9d5078b..006f187 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -96,6 +96,7 @@ enum {
 #define TCP_QUICKACK		12	/* Block/reenable quick acks */
 #define TCP_CONGESTION		13	/* Congestion control algorithm */
 #define TCP_MD5SIG		14	/* TCP MD5 Signature (RFC2385) */
+#define TCP_FREEZE		15	/* Freeze-TCP */
 
 #define TCPI_OPT_TIMESTAMPS	1
 #define TCPI_OPT_SACK		2
@@ -408,6 +409,10 @@ struct tcp_sock {
 #endif
 
 	int			linger2;
+
+#ifdef CONFIG_TCP_FREEZE
+	int			frozen;
+#endif
 };
 
 static inline struct tcp_sock *tcp_sk(const struct sock *sk)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 646dbe3..514fca3 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -531,6 +531,10 @@ static inline void tcp_fast_path_check(struct sock *sk)
 		tcp_fast_path_on(tp);
 }
 
+/* tcp_freeze.c */
+extern int tcp_set_freeze(struct tcp_sock *sk, int val);
+extern int tcp_get_freeze(struct tcp_sock *sk);
+
 /* Compute the actual rto_min value */
 static inline u32 tcp_rto_min(struct sock *sk)
 {
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 5b919f7..bc15667 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -428,6 +428,29 @@ config INET_TCP_DIAG
 	depends on INET_DIAG
 	def_tristate INET_DIAG
 
+config TCP_FREEZE
+	bool "TCP: Freeze-TCP"
+	depends on EXPERIMENTAL
+	default n
+	help
+	  Support for Freeze-TCP [0] to temporarily freeze the remote sender.
+	  The receiver can send a Zero-Window Advertizement to suspend the
+	  remote peer's sender. This can be used e.g. when the receiver is
+	  mobile and predicts a disconnection to avoid losing packets.
+
+	  [0] T. Goff, J. Moronski, D. S. Phatak, and V. Gupta, "Freeze-TCP: a
+	  True End-to-end TCP Enhancement Mechanism for Mobile Environments,"
+	  In Proceedings of IEEE INFOCOM 2000.
+
+	  If unsure, say N.
+
+config TCP_FREEZE_DEBUG
+	bool "TCP: Freeze-TCP debug messages"
+	depends on TCP_FREEZE
+	default n
+	help
+	  Support for debug messages for Freeze-TCP.
+
 menuconfig TCP_CONG_ADVANCED
 	bool "TCP: advanced congestion control"
 	---help---
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 80ff87c..956d30e 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
 obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
 obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
 obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
+obj-$(CONFIG_TCP_FREEZE) += tcp_freeze.o
 obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 7a0f0b2..ffeda89 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2208,6 +2208,12 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 		break;
 #endif
 
+#ifdef CONFIG_TCP_FREEZE
+	case TCP_FREEZE:
+		err = tcp_set_freeze(tp, val);
+		break;
+#endif
+
 	default:
 		err = -ENOPROTOOPT;
 		break;
@@ -2385,6 +2391,13 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
 		if (copy_to_user(optval, icsk->icsk_ca_ops->name, len))
 			return -EFAULT;
 		return 0;
+
+#ifdef CONFIG_TCP_FREEZE
+	case TCP_FREEZE:
+		val = tcp_get_freeze(tp);
+		break;
+#endif
+
 	default:
 		return -ENOPROTOOPT;
 	}
@@ -2923,6 +2936,9 @@ void __init tcp_init(void)
 	printk(KERN_INFO "TCP: Hash tables configured "
 	       "(established %d bind %d)\n",
 	       tcp_hashinfo.ehash_size, tcp_hashinfo.bhash_size);
+#ifdef CONFIG_TCP_FREEZE
+	printk(KERN_INFO "TCP: support for freezing using ZWA\n");
+#endif
 
 	tcp_register_congestion_control(&tcp_reno);
 }
diff --git a/net/ipv4/tcp_freeze.c b/net/ipv4/tcp_freeze.c
new file mode 100644
index 0000000..c3e4ae5
--- /dev/null
+++ b/net/ipv4/tcp_freeze.c
@@ -0,0 +1,65 @@
+/*
+ * Freeze-TCP support for Linux
+ * Copyright (C) 2009 Nicta, Olivier Mehani <olivier.mehani@nicta.com.au>
+ * 
+ * net/ipv4/tcp_freeze.c
+ *
+ * Helper functions to manipulate the frozen flag of the TCP socket structure
+ * and send the appropriate messages.
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <net/tcp.h>
+#include <linux/tcp.h>
+
+
+#ifdef CONFIG_TCP_FREEZE_DEBUG
+# define DEBUG(...) printk(KERN_DEBUG __VA_ARGS__)
+#else
+# define DEBUG(...) /* debug statement */
+#endif
+
+int tcp_set_freeze(struct tcp_sock *tp, int val)
+{
+	DEBUG("TCP: %s(%p, %d)\n", __FUNCTION__, tp, val);
+	if (!val && tcp_get_freeze(tp)) {
+		DEBUG("TCP: Unfreezing socket...\n");
+		tp->frozen = 0;
+		/* Send ZWA */
+		tcp_send_ack((struct sock *) tp);
+	} else if (val == 1 && !tcp_get_freeze(tp)) {
+		DEBUG("TCP: Freezing socket...\n");
+		tp->frozen = 1;
+		/* Send dupacks 
+		 * TODO: configurable number of AVK via sysctl */
+		tcp_send_ack((struct sock *) tp);
+		tcp_send_ack((struct sock *) tp);
+	} else {
+		DEBUG("TCP: Invalid command\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int tcp_get_freeze(struct tcp_sock *tp)
+{
+	/* DEBUG("TCP: %s(%p), %d\n", __FUNCTION__, tp, tp->frozen); */
+	return tp->frozen;
+}
+
+EXPORT_SYMBOL(tcp_set_freeze);
+EXPORT_SYMBOL(tcp_get_freeze);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index eec3e6f..eee1b4c 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3397,6 +3397,13 @@ static int tcp_ack_update_window(struct sock *sk, struct sk_buff *skb, u32 ack,
 	int flag = 0;
 	u32 nwin = ntohs(tcp_hdr(skb)->window);
 
+#ifdef CONFIG_TCP_FREEZE
+	/* Sender side fix for implementation of Freeze TCP
+	 * (originally from Francis Chai's patch */
+	if (nwin < 0)
+		tp->snd_wnd = 0;
+#endif 
+
 	if (likely(!tcp_hdr(skb)->syn))
 		nwin <<= tp->rx_opt.snd_wscale;
 
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 43bbba7..0d7fbef 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -475,6 +475,9 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
 		if (newtp->af_specific->md5_lookup(sk, newsk))
 			newtp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
 #endif
+#ifdef CONFIG_TCP_FREEZE
+		newtp->frozen = 0;
+#endif
 		if (skb->len >= TCP_MIN_RCVMSS+newtp->tcp_header_len)
 			newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
 		newtp->rx_opt.mss_clamp = req->mss;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 59aec60..c5f135a 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -244,6 +244,9 @@ static u16 tcp_select_window(struct sock *sk)
 	u32 cur_win = tcp_receive_window(tp);
 	u32 new_win = __tcp_select_window(sk);
 
+	if (unlikely(tp->frozen == 1))
+		cur_win = new_win = 0;
+
 	/* Never shrink the offered window */
 	if (new_win < cur_win) {
 		/* Danger Will Robinson!

