0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 #include <linux/module.h>
0038 #include <net/tcp.h>
0039
0040
0041 #define LP_RESOL TCP_TS_HZ
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053 enum tcp_lp_state {
0054 LP_VALID_RHZ = (1 << 0),
0055 LP_VALID_OWD = (1 << 1),
0056 LP_WITHIN_THR = (1 << 3),
0057 LP_WITHIN_INF = (1 << 4),
0058 };
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 struct lp {
0078 u32 flag;
0079 u32 sowd;
0080 u32 owd_min;
0081 u32 owd_max;
0082 u32 owd_max_rsv;
0083 u32 remote_hz;
0084 u32 remote_ref_time;
0085 u32 local_ref_time;
0086 u32 last_drop;
0087 u32 inference;
0088 };
0089
0090
0091
0092
0093
0094
0095
0096
0097 static void tcp_lp_init(struct sock *sk)
0098 {
0099 struct lp *lp = inet_csk_ca(sk);
0100
0101 lp->flag = 0;
0102 lp->sowd = 0;
0103 lp->owd_min = 0xffffffff;
0104 lp->owd_max = 0;
0105 lp->owd_max_rsv = 0;
0106 lp->remote_hz = 0;
0107 lp->remote_ref_time = 0;
0108 lp->local_ref_time = 0;
0109 lp->last_drop = 0;
0110 lp->inference = 0;
0111 }
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121 static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
0122 {
0123 struct lp *lp = inet_csk_ca(sk);
0124
0125 if (!(lp->flag & LP_WITHIN_INF))
0126 tcp_reno_cong_avoid(sk, ack, acked);
0127 }
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137 static u32 tcp_lp_remote_hz_estimator(struct sock *sk)
0138 {
0139 struct tcp_sock *tp = tcp_sk(sk);
0140 struct lp *lp = inet_csk_ca(sk);
0141 s64 rhz = lp->remote_hz << 6;
0142 s64 m = 0;
0143
0144
0145
0146 if (lp->remote_ref_time == 0 || lp->local_ref_time == 0)
0147 goto out;
0148
0149
0150 if (tp->rx_opt.rcv_tsval == lp->remote_ref_time ||
0151 tp->rx_opt.rcv_tsecr == lp->local_ref_time)
0152 goto out;
0153
0154 m = TCP_TS_HZ *
0155 (tp->rx_opt.rcv_tsval - lp->remote_ref_time) /
0156 (tp->rx_opt.rcv_tsecr - lp->local_ref_time);
0157 if (m < 0)
0158 m = -m;
0159
0160 if (rhz > 0) {
0161 m -= rhz >> 6;
0162 rhz += m;
0163 } else
0164 rhz = m << 6;
0165
0166 out:
0167
0168 if ((rhz >> 6) > 0)
0169 lp->flag |= LP_VALID_RHZ;
0170 else
0171 lp->flag &= ~LP_VALID_RHZ;
0172
0173
0174 lp->remote_ref_time = tp->rx_opt.rcv_tsval;
0175 lp->local_ref_time = tp->rx_opt.rcv_tsecr;
0176
0177 return rhz >> 6;
0178 }
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191 static u32 tcp_lp_owd_calculator(struct sock *sk)
0192 {
0193 struct tcp_sock *tp = tcp_sk(sk);
0194 struct lp *lp = inet_csk_ca(sk);
0195 s64 owd = 0;
0196
0197 lp->remote_hz = tcp_lp_remote_hz_estimator(sk);
0198
0199 if (lp->flag & LP_VALID_RHZ) {
0200 owd =
0201 tp->rx_opt.rcv_tsval * (LP_RESOL / lp->remote_hz) -
0202 tp->rx_opt.rcv_tsecr * (LP_RESOL / TCP_TS_HZ);
0203 if (owd < 0)
0204 owd = -owd;
0205 }
0206
0207 if (owd > 0)
0208 lp->flag |= LP_VALID_OWD;
0209 else
0210 lp->flag &= ~LP_VALID_OWD;
0211
0212 return owd;
0213 }
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227 static void tcp_lp_rtt_sample(struct sock *sk, u32 rtt)
0228 {
0229 struct lp *lp = inet_csk_ca(sk);
0230 s64 mowd = tcp_lp_owd_calculator(sk);
0231
0232
0233 if (!(lp->flag & LP_VALID_RHZ) || !(lp->flag & LP_VALID_OWD))
0234 return;
0235
0236
0237 if (mowd < lp->owd_min)
0238 lp->owd_min = mowd;
0239
0240
0241
0242 if (mowd > lp->owd_max) {
0243 if (mowd > lp->owd_max_rsv) {
0244 if (lp->owd_max_rsv == 0)
0245 lp->owd_max = mowd;
0246 else
0247 lp->owd_max = lp->owd_max_rsv;
0248 lp->owd_max_rsv = mowd;
0249 } else
0250 lp->owd_max = mowd;
0251 }
0252
0253
0254 if (lp->sowd != 0) {
0255 mowd -= lp->sowd >> 3;
0256 lp->sowd += mowd;
0257 } else
0258 lp->sowd = mowd << 3;
0259 }
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271 static void tcp_lp_pkts_acked(struct sock *sk, const struct ack_sample *sample)
0272 {
0273 struct tcp_sock *tp = tcp_sk(sk);
0274 struct lp *lp = inet_csk_ca(sk);
0275 u32 now = tcp_time_stamp(tp);
0276 u32 delta;
0277
0278 if (sample->rtt_us > 0)
0279 tcp_lp_rtt_sample(sk, sample->rtt_us);
0280
0281
0282 delta = now - tp->rx_opt.rcv_tsecr;
0283 if ((s32)delta > 0)
0284 lp->inference = 3 * delta;
0285
0286
0287 if (lp->last_drop && (now - lp->last_drop < lp->inference))
0288 lp->flag |= LP_WITHIN_INF;
0289 else
0290 lp->flag &= ~LP_WITHIN_INF;
0291
0292
0293 if (lp->sowd >> 3 <
0294 lp->owd_min + 15 * (lp->owd_max - lp->owd_min) / 100)
0295 lp->flag |= LP_WITHIN_THR;
0296 else
0297 lp->flag &= ~LP_WITHIN_THR;
0298
0299 pr_debug("TCP-LP: %05o|%5u|%5u|%15u|%15u|%15u\n", lp->flag,
0300 tcp_snd_cwnd(tp), lp->remote_hz, lp->owd_min, lp->owd_max,
0301 lp->sowd >> 3);
0302
0303 if (lp->flag & LP_WITHIN_THR)
0304 return;
0305
0306
0307
0308
0309 lp->owd_min = lp->sowd >> 3;
0310 lp->owd_max = lp->sowd >> 2;
0311 lp->owd_max_rsv = lp->sowd >> 2;
0312
0313
0314
0315 if (lp->flag & LP_WITHIN_INF)
0316 tcp_snd_cwnd_set(tp, 1U);
0317
0318
0319
0320 else
0321 tcp_snd_cwnd_set(tp, max(tcp_snd_cwnd(tp) >> 1U, 1U));
0322
0323
0324 lp->last_drop = now;
0325 }
0326
0327 static struct tcp_congestion_ops tcp_lp __read_mostly = {
0328 .init = tcp_lp_init,
0329 .ssthresh = tcp_reno_ssthresh,
0330 .undo_cwnd = tcp_reno_undo_cwnd,
0331 .cong_avoid = tcp_lp_cong_avoid,
0332 .pkts_acked = tcp_lp_pkts_acked,
0333
0334 .owner = THIS_MODULE,
0335 .name = "lp"
0336 };
0337
0338 static int __init tcp_lp_register(void)
0339 {
0340 BUILD_BUG_ON(sizeof(struct lp) > ICSK_CA_PRIV_SIZE);
0341 return tcp_register_congestion_control(&tcp_lp);
0342 }
0343
0344 static void __exit tcp_lp_unregister(void)
0345 {
0346 tcp_unregister_congestion_control(&tcp_lp);
0347 }
0348
0349 module_init(tcp_lp_register);
0350 module_exit(tcp_lp_unregister);
0351
0352 MODULE_AUTHOR("Wong Hoi Sing Edison, Hung Hing Lun Mike");
0353 MODULE_LICENSE("GPL");
0354 MODULE_DESCRIPTION("TCP Low Priority");