0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/module.h>
0015 #include <net/tcp.h>
0016
0017
0018 struct hybla {
0019 bool hybla_en;
0020 u32 snd_cwnd_cents;
0021 u32 rho;
0022 u32 rho2;
0023 u32 rho_3ls;
0024 u32 rho2_7ls;
0025 u32 minrtt_us;
0026 };
0027
0028
0029 static int rtt0 = 25;
0030 module_param(rtt0, int, 0644);
0031 MODULE_PARM_DESC(rtt0, "reference rout trip time (ms)");
0032
0033
0034 static inline void hybla_recalc_param (struct sock *sk)
0035 {
0036 struct hybla *ca = inet_csk_ca(sk);
0037
0038 ca->rho_3ls = max_t(u32,
0039 tcp_sk(sk)->srtt_us / (rtt0 * USEC_PER_MSEC),
0040 8U);
0041 ca->rho = ca->rho_3ls >> 3;
0042 ca->rho2_7ls = (ca->rho_3ls * ca->rho_3ls) << 1;
0043 ca->rho2 = ca->rho2_7ls >> 7;
0044 }
0045
0046 static void hybla_init(struct sock *sk)
0047 {
0048 struct tcp_sock *tp = tcp_sk(sk);
0049 struct hybla *ca = inet_csk_ca(sk);
0050
0051 ca->rho = 0;
0052 ca->rho2 = 0;
0053 ca->rho_3ls = 0;
0054 ca->rho2_7ls = 0;
0055 ca->snd_cwnd_cents = 0;
0056 ca->hybla_en = true;
0057 tcp_snd_cwnd_set(tp, 2);
0058 tp->snd_cwnd_clamp = 65535;
0059
0060
0061 hybla_recalc_param(sk);
0062
0063
0064 ca->minrtt_us = tp->srtt_us;
0065 tcp_snd_cwnd_set(tp, ca->rho);
0066 }
0067
0068 static void hybla_state(struct sock *sk, u8 ca_state)
0069 {
0070 struct hybla *ca = inet_csk_ca(sk);
0071
0072 ca->hybla_en = (ca_state == TCP_CA_Open);
0073 }
0074
0075 static inline u32 hybla_fraction(u32 odds)
0076 {
0077 static const u32 fractions[] = {
0078 128, 139, 152, 165, 181, 197, 215, 234,
0079 };
0080
0081 return (odds < ARRAY_SIZE(fractions)) ? fractions[odds] : 128;
0082 }
0083
0084
0085
0086
0087
0088
0089
0090 static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked)
0091 {
0092 struct tcp_sock *tp = tcp_sk(sk);
0093 struct hybla *ca = inet_csk_ca(sk);
0094 u32 increment, odd, rho_fractions;
0095 int is_slowstart = 0;
0096
0097
0098 if (tp->srtt_us < ca->minrtt_us) {
0099 hybla_recalc_param(sk);
0100 ca->minrtt_us = tp->srtt_us;
0101 }
0102
0103 if (!tcp_is_cwnd_limited(sk))
0104 return;
0105
0106 if (!ca->hybla_en) {
0107 tcp_reno_cong_avoid(sk, ack, acked);
0108 return;
0109 }
0110
0111 if (ca->rho == 0)
0112 hybla_recalc_param(sk);
0113
0114 rho_fractions = ca->rho_3ls - (ca->rho << 3);
0115
0116 if (tcp_in_slow_start(tp)) {
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130 is_slowstart = 1;
0131 increment = ((1 << min(ca->rho, 16U)) *
0132 hybla_fraction(rho_fractions)) - 128;
0133 } else {
0134
0135
0136
0137
0138
0139
0140 increment = ca->rho2_7ls / tcp_snd_cwnd(tp);
0141 if (increment < 128)
0142 tp->snd_cwnd_cnt++;
0143 }
0144
0145 odd = increment % 128;
0146 tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) + (increment >> 7));
0147 ca->snd_cwnd_cents += odd;
0148
0149
0150 while (ca->snd_cwnd_cents >= 128) {
0151 tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) + 1);
0152 ca->snd_cwnd_cents -= 128;
0153 tp->snd_cwnd_cnt = 0;
0154 }
0155
0156 if (increment == 0 && odd == 0 && tp->snd_cwnd_cnt >= tcp_snd_cwnd(tp)) {
0157 tcp_snd_cwnd_set(tp, tcp_snd_cwnd(tp) + 1);
0158 tp->snd_cwnd_cnt = 0;
0159 }
0160
0161 if (is_slowstart)
0162 tcp_snd_cwnd_set(tp, min(tcp_snd_cwnd(tp), tp->snd_ssthresh));
0163
0164 tcp_snd_cwnd_set(tp, min(tcp_snd_cwnd(tp), tp->snd_cwnd_clamp));
0165 }
0166
0167 static struct tcp_congestion_ops tcp_hybla __read_mostly = {
0168 .init = hybla_init,
0169 .ssthresh = tcp_reno_ssthresh,
0170 .undo_cwnd = tcp_reno_undo_cwnd,
0171 .cong_avoid = hybla_cong_avoid,
0172 .set_state = hybla_state,
0173
0174 .owner = THIS_MODULE,
0175 .name = "hybla"
0176 };
0177
0178 static int __init hybla_register(void)
0179 {
0180 BUILD_BUG_ON(sizeof(struct hybla) > ICSK_CA_PRIV_SIZE);
0181 return tcp_register_congestion_control(&tcp_hybla);
0182 }
0183
0184 static void __exit hybla_unregister(void)
0185 {
0186 tcp_unregister_congestion_control(&tcp_hybla);
0187 }
0188
0189 module_init(hybla_register);
0190 module_exit(hybla_unregister);
0191
0192 MODULE_AUTHOR("Daniele Lacamera");
0193 MODULE_LICENSE("GPL");
0194 MODULE_DESCRIPTION("TCP Hybla");