sctp: Update SWS avaoidance receiver side algorithm

We currently send window update SACKs every time we free up 1 PMTU
worth of data.  That a lot more SACKs then necessary.  Instead, we'll
now send back the actuall window every time we send a sack, and do
window-update SACKs when a fraction of the receive buffer has been
opened.  The fraction is controlled with a sysctl.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h
index 58f714a..6390884 100644
--- a/include/net/sctp/constants.h
+++ b/include/net/sctp/constants.h
@@ -308,6 +308,10 @@
 
 #define SCTP_DEFAULT_MINWINDOW	1500	/* default minimum rwnd size */
 #define SCTP_DEFAULT_MAXWINDOW	65535	/* default rwnd size */
+#define SCTP_DEFAULT_RWND_SHIFT  4	/* by default, update on 1/16 of
+					 * rcvbuf, which is 1/8 of initial
+					 * window
+					 */
 #define SCTP_DEFAULT_MAXSEGMENT 1500	/* MTU size, this is the limit
                                          * to which we will raise the P-MTU.
 					 */
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index cd2e187..90876b6 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -231,6 +231,11 @@
 	/* Flag to indicate whether computing and verifying checksum
 	 * is disabled. */
         int checksum_disable;
+
+	/* Threshold for rwnd update SACKS.  Receive buffer shifted this many
+	 * bits is an indicator of when to send and window update SACK.
+	 */
+	int rwnd_update_shift;
 } sctp_globals;
 
 #define sctp_rto_initial		(sctp_globals.rto_initial)
@@ -267,6 +272,7 @@
 #define sctp_prsctp_enable		(sctp_globals.prsctp_enable)
 #define sctp_auth_enable		(sctp_globals.auth_enable)
 #define sctp_checksum_disable		(sctp_globals.checksum_disable)
+#define sctp_rwnd_upd_shift		(sctp_globals.rwnd_update_shift)
 
 /* SCTP Socket type: UDP or TCP style. */
 typedef enum {
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 8e755eb..37e9825 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1383,8 +1383,9 @@
 	case SCTP_STATE_SHUTDOWN_RECEIVED:
 	case SCTP_STATE_SHUTDOWN_SENT:
 		if ((asoc->rwnd > asoc->a_rwnd) &&
-		    ((asoc->rwnd - asoc->a_rwnd) >=
-		     min_t(__u32, (asoc->base.sk->sk_rcvbuf >> 1), asoc->pathmtu)))
+		    ((asoc->rwnd - asoc->a_rwnd) >= max_t(__u32,
+			   (asoc->base.sk->sk_rcvbuf >> sctp_rwnd_upd_shift),
+			   asoc->pathmtu)))
 			return 1;
 		break;
 	default:
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 08ef203..a3c8988 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1258,6 +1258,9 @@
 	/* Set SCOPE policy to enabled */
 	sctp_scope_policy = SCTP_SCOPE_POLICY_ENABLE;
 
+	/* Set the default rwnd update threshold */
+	sctp_rwnd_upd_shift		= SCTP_DEFAULT_RWND_SHIFT;
+
 	sctp_sysctl_register();
 
 	INIT_LIST_HEAD(&sctp_address_families);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index eda4fe7..8ae6709 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -217,8 +217,7 @@
 		sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
 				SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
 	} else {
-		if (asoc->a_rwnd > asoc->rwnd)
-			asoc->a_rwnd = asoc->rwnd;
+		asoc->a_rwnd = asoc->rwnd;
 		sack = sctp_make_sack(asoc);
 		if (!sack)
 			goto nomem;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index ab7151d..ae03ded 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -52,6 +52,7 @@
 static int sack_timer_min = 1;
 static int sack_timer_max = 500;
 static int addr_scope_max = 3; /* check sctp_scope_policy_t in include/net/sctp/constants.h for max entries */
+static int rwnd_scale_max = 16;
 
 extern int sysctl_sctp_mem[3];
 extern int sysctl_sctp_rmem[3];
@@ -284,6 +285,18 @@
 		.extra1		= &zero,
 		.extra2		= &addr_scope_max,
 	},
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "rwnd_update_shift",
+		.data		= &sctp_rwnd_upd_shift,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &one,
+		.extra2		= &rwnd_scale_max,
+	},
+
 	{ .ctl_name = 0 }
 };