tcp_hybla.c 4.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * TCP HYBLA
 *
 * TCP-HYBLA Congestion control algorithm, based on:
 *   C.Caini, R.Firrincieli, "TCP-Hybla: A TCP Enhancement
 *   for Heterogeneous Networks",
 *   International Journal on satellite Communications,
 *				       September 2004
 *    Daniele Lacamera
 *    root at danielinux.net
 */

#include <linux/config.h>
#include <linux/module.h>
#include <net/tcp.h>

/* Tcp Hybla structure. */
struct hybla {
	u8    hybla_en;
	u32   snd_cwnd_cents; /* Keeps increment values when it is <1, <<7 */
	u32   rho;	      /* Rho parameter, integer part  */
	u32   rho2;	      /* Rho * Rho, integer part */
	u32   rho_3ls;	      /* Rho parameter, <<3 */
	u32   rho2_7ls;	      /* Rho^2, <<7	*/
	u32   minrtt;	      /* Minimum smoothed round trip time value seen */
};

/* Hybla reference round trip time (default= 1/40 sec = 25 ms),
   expressed in jiffies */
static int rtt0 = 25;
module_param(rtt0, int, 0644);
MODULE_PARM_DESC(rtt0, "reference rout trip time (ms)");


/* This is called to refresh values for hybla parameters */
36
static inline void hybla_recalc_param (struct sock *sk)
37
{
38
	struct hybla *ca = inet_csk_ca(sk);
39

40
	ca->rho_3ls = max_t(u32, tcp_sk(sk)->srtt / msecs_to_jiffies(rtt0), 8);
41 42 43 44 45
	ca->rho = ca->rho_3ls >> 3;
	ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1;
	ca->rho2 = ca->rho2_7ls >>7;
}

46
static void hybla_init(struct sock *sk)
47
{
48 49
	struct tcp_sock *tp = tcp_sk(sk);
	struct hybla *ca = inet_csk_ca(sk);
50 51 52 53 54 55 56 57 58 59 60

	ca->rho = 0;
	ca->rho2 = 0;
	ca->rho_3ls = 0;
	ca->rho2_7ls = 0;
	ca->snd_cwnd_cents = 0;
	ca->hybla_en = 1;
	tp->snd_cwnd = 2;
	tp->snd_cwnd_clamp = 65535;

	/* 1st Rho measurement based on initial srtt */
61
	hybla_recalc_param(sk);
62 63 64 65 66 67

	/* set minimum rtt as this is the 1st ever seen */
	ca->minrtt = tp->srtt;
	tp->snd_cwnd = ca->rho;
}

68
static void hybla_state(struct sock *sk, u8 ca_state)
69
{
70
	struct hybla *ca = inet_csk_ca(sk);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	ca->hybla_en = (ca_state == TCP_CA_Open);
}

static inline u32 hybla_fraction(u32 odds)
{
	static const u32 fractions[] = {
		128, 139, 152, 165, 181, 197, 215, 234,
	};

	return (odds < ARRAY_SIZE(fractions)) ? fractions[odds] : 128;
}

/* TCP Hybla main routine.
 * This is the algorithm behavior:
 *     o Recalc Hybla parameters if min_rtt has changed
 *     o Give cwnd a new value based on the model proposed
 *     o remember increments <1
 */
89
static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
90 91
			    u32 in_flight, int flag)
{
92 93
	struct tcp_sock *tp = tcp_sk(sk);
	struct hybla *ca = inet_csk_ca(sk);
94 95 96 97 98
	u32 increment, odd, rho_fractions;
	int is_slowstart = 0;

	/*  Recalculate rho only if this srtt is the lowest */
	if (tp->srtt < ca->minrtt){
99
		hybla_recalc_param(sk);
100 101 102
		ca->minrtt = tp->srtt;
	}

103 104 105
	if (!tcp_is_cwnd_limited(sk, in_flight))
		return;

106
	if (!ca->hybla_en)
107
		return tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag);
108 109

	if (ca->rho == 0)
110
		hybla_recalc_param(sk);
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

	rho_fractions = ca->rho_3ls - (ca->rho << 3);

	if (tp->snd_cwnd < tp->snd_ssthresh) {
		/*
		 * slow start
		 *      INC = 2^RHO - 1
		 * This is done by splitting the rho parameter
		 * into 2 parts: an integer part and a fraction part.
		 * Inrement<<7 is estimated by doing:
		 *	       [2^(int+fract)]<<7
		 * that is equal to:
		 *	       (2^int)	*  [(2^fract) <<7]
		 * 2^int is straightly computed as 1<<int,
		 * while we will use hybla_slowstart_fraction_increment() to
		 * calculate 2^fract in a <<7 value.
		 */
		is_slowstart = 1;
		increment = ((1 << ca->rho) * hybla_fraction(rho_fractions))
			- 128;
	} else {
		/*
		 * congestion avoidance
		 * INC = RHO^2 / W
		 * as long as increment is estimated as (rho<<7)/window
		 * it already is <<7 and we can easily count its fractions.
		 */
		increment = ca->rho2_7ls / tp->snd_cwnd;
		if (increment < 128)
			tp->snd_cwnd_cnt++;
	}

	odd = increment % 128;
	tp->snd_cwnd += increment >> 7;
	ca->snd_cwnd_cents += odd;

	/* check when fractions goes >=128 and increase cwnd by 1. */
	while(ca->snd_cwnd_cents >= 128) {
		tp->snd_cwnd++;
		ca->snd_cwnd_cents -= 128;
		tp->snd_cwnd_cnt = 0;
	}

	/* clamp down slowstart cwnd to ssthresh value. */
	if (is_slowstart)
		tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);

	tp->snd_cwnd = min_t(u32, tp->snd_cwnd, tp->snd_cwnd_clamp);
}

static struct tcp_congestion_ops tcp_hybla = {
	.init		= hybla_init,
	.ssthresh	= tcp_reno_ssthresh,
	.min_cwnd	= tcp_reno_min_cwnd,
	.cong_avoid	= hybla_cong_avoid,
	.set_state	= hybla_state,

	.owner		= THIS_MODULE,
	.name		= "hybla"
};

static int __init hybla_register(void)
{
174
	BUG_ON(sizeof(struct hybla) > ICSK_CA_PRIV_SIZE);
175 176 177 178 179 180 181 182 183 184 185 186 187 188
	return tcp_register_congestion_control(&tcp_hybla);
}

static void __exit hybla_unregister(void)
{
	tcp_unregister_congestion_control(&tcp_hybla);
}

module_init(hybla_register);
module_exit(hybla_unregister);

MODULE_AUTHOR("Daniele Lacamera");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TCP Hybla");