268 lines
6.1 KiB
C
268 lines
6.1 KiB
C
/*
|
|
* MPTCP implementation - Balia Congestion Control
|
|
* (Balanced Linked Adaptation Algorithm)
|
|
*
|
|
* Analysis, Design and Implementation:
|
|
* Qiuyu Peng <qpeng@caltech.edu>
|
|
* Anwar Walid <anwar@research.bell-labs.com>
|
|
* Jaehyun Hwang <jhyun.hwang@samsung.com>
|
|
* Steven H. Low <slow@caltech.edu>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <net/tcp.h>
|
|
#include <net/mptcp.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
/* The variable 'rate' (i.e., x_r) will be scaled
|
|
* e.g., from B/s to KB/s, MB/s, or GB/s
|
|
* if max_rate > 2^rate_scale_limit
|
|
*/
|
|
|
|
static int rate_scale_limit = 25;
|
|
static int alpha_scale = 10;
|
|
static int scale_num = 5;
|
|
|
|
struct mptcp_balia {
|
|
u64 ai;
|
|
u64 md;
|
|
bool forced_update;
|
|
};
|
|
|
|
static inline int mptcp_balia_sk_can_send(const struct sock *sk)
|
|
{
|
|
return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt_us;
|
|
}
|
|
|
|
static inline u64 mptcp_get_ai(const struct sock *meta_sk)
|
|
{
|
|
return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->ai;
|
|
}
|
|
|
|
static inline void mptcp_set_ai(const struct sock *meta_sk, u64 ai)
|
|
{
|
|
((struct mptcp_balia *)inet_csk_ca(meta_sk))->ai = ai;
|
|
}
|
|
|
|
static inline u64 mptcp_get_md(const struct sock *meta_sk)
|
|
{
|
|
return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->md;
|
|
}
|
|
|
|
static inline void mptcp_set_md(const struct sock *meta_sk, u64 md)
|
|
{
|
|
((struct mptcp_balia *)inet_csk_ca(meta_sk))->md = md;
|
|
}
|
|
|
|
static inline u64 mptcp_balia_scale(u64 val, int scale)
|
|
{
|
|
return (u64) val << scale;
|
|
}
|
|
|
|
static inline bool mptcp_get_forced(const struct sock *meta_sk)
|
|
{
|
|
return ((struct mptcp_balia *)inet_csk_ca(meta_sk))->forced_update;
|
|
}
|
|
|
|
static inline void mptcp_set_forced(const struct sock *meta_sk, bool force)
|
|
{
|
|
((struct mptcp_balia *)inet_csk_ca(meta_sk))->forced_update = force;
|
|
}
|
|
|
|
static void mptcp_balia_recalc_ai(const struct sock *sk)
|
|
{
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
|
const struct mptcp_cb *mpcb = tp->mpcb;
|
|
const struct sock *sub_sk;
|
|
u64 max_rate = 0, rate = 0, sum_rate = 0;
|
|
u64 alpha, ai = tp->snd_cwnd, md = (tp->snd_cwnd >> 1);
|
|
int num_scale_down = 0;
|
|
|
|
if (!mpcb)
|
|
return;
|
|
|
|
/* Only one subflow left - fall back to normal reno-behavior */
|
|
if (mpcb->cnt_established <= 1)
|
|
goto exit;
|
|
|
|
/* Find max_rate first */
|
|
mptcp_for_each_sk(mpcb, sub_sk) {
|
|
struct tcp_sock *sub_tp = tcp_sk(sub_sk);
|
|
u64 tmp;
|
|
|
|
if (!mptcp_balia_sk_can_send(sub_sk))
|
|
continue;
|
|
|
|
tmp = div_u64((u64)tp->mss_cache * sub_tp->snd_cwnd
|
|
* (USEC_PER_SEC << 3), sub_tp->srtt_us);
|
|
sum_rate += tmp;
|
|
|
|
if (tp == sub_tp)
|
|
rate = tmp;
|
|
|
|
if (tmp >= max_rate)
|
|
max_rate = tmp;
|
|
}
|
|
|
|
/* At least, the current subflow should be able to send */
|
|
if (unlikely(!rate))
|
|
goto exit;
|
|
|
|
alpha = div64_u64(max_rate, rate);
|
|
|
|
/* Scale down max_rate if it is too high (e.g., >2^25) */
|
|
while (max_rate > mptcp_balia_scale(1, rate_scale_limit)) {
|
|
max_rate >>= scale_num;
|
|
num_scale_down++;
|
|
}
|
|
|
|
if (num_scale_down) {
|
|
sum_rate = 0;
|
|
mptcp_for_each_sk(mpcb, sub_sk) {
|
|
struct tcp_sock *sub_tp = tcp_sk(sub_sk);
|
|
u64 tmp;
|
|
|
|
if (!mptcp_balia_sk_can_send(sub_sk))
|
|
continue;
|
|
|
|
tmp = div_u64((u64)tp->mss_cache * sub_tp->snd_cwnd
|
|
* (USEC_PER_SEC << 3), sub_tp->srtt_us);
|
|
tmp >>= (scale_num * num_scale_down);
|
|
|
|
sum_rate += tmp;
|
|
}
|
|
rate >>= (scale_num * num_scale_down);
|
|
}
|
|
|
|
/* (sum_rate)^2 * 10 * w_r
|
|
* ai = ------------------------------------
|
|
* (x_r + max_rate) * (4x_r + max_rate)
|
|
*/
|
|
sum_rate *= sum_rate;
|
|
|
|
ai = div64_u64(sum_rate * 10, rate + max_rate);
|
|
ai = div64_u64(ai * tp->snd_cwnd, (rate << 2) + max_rate);
|
|
|
|
if (unlikely(!ai))
|
|
ai = tp->snd_cwnd;
|
|
|
|
md = ((tp->snd_cwnd >> 1) * min(mptcp_balia_scale(alpha, alpha_scale),
|
|
mptcp_balia_scale(3, alpha_scale) >> 1))
|
|
>> alpha_scale;
|
|
|
|
exit:
|
|
mptcp_set_ai(sk, ai);
|
|
mptcp_set_md(sk, md);
|
|
}
|
|
|
|
static void mptcp_balia_init(struct sock *sk)
|
|
{
|
|
if (mptcp(tcp_sk(sk))) {
|
|
mptcp_set_forced(sk, 0);
|
|
mptcp_set_ai(sk, 0);
|
|
mptcp_set_md(sk, 0);
|
|
}
|
|
}
|
|
|
|
static void mptcp_balia_cwnd_event(struct sock *sk, enum tcp_ca_event event)
|
|
{
|
|
if (event == CA_EVENT_COMPLETE_CWR || event == CA_EVENT_LOSS)
|
|
mptcp_balia_recalc_ai(sk);
|
|
}
|
|
|
|
static void mptcp_balia_set_state(struct sock *sk, u8 ca_state)
|
|
{
|
|
if (!mptcp(tcp_sk(sk)))
|
|
return;
|
|
|
|
mptcp_set_forced(sk, 1);
|
|
}
|
|
|
|
static void mptcp_balia_cong_avoid(struct sock *sk, u32 ack, u32 acked)
|
|
{
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
const struct mptcp_cb *mpcb = tp->mpcb;
|
|
int snd_cwnd;
|
|
|
|
if (!mptcp(tp)) {
|
|
tcp_reno_cong_avoid(sk, ack, acked);
|
|
return;
|
|
}
|
|
|
|
if (!tcp_is_cwnd_limited(sk))
|
|
return;
|
|
|
|
if (tp->snd_cwnd <= tp->snd_ssthresh) {
|
|
/* In "safe" area, increase. */
|
|
tcp_slow_start(tp, acked);
|
|
mptcp_balia_recalc_ai(sk);
|
|
return;
|
|
}
|
|
|
|
if (mptcp_get_forced(mptcp_meta_sk(sk))) {
|
|
mptcp_balia_recalc_ai(sk);
|
|
mptcp_set_forced(sk, 0);
|
|
}
|
|
|
|
if (mpcb->cnt_established > 1)
|
|
snd_cwnd = (int) mptcp_get_ai(sk);
|
|
else
|
|
snd_cwnd = tp->snd_cwnd;
|
|
|
|
if (tp->snd_cwnd_cnt >= snd_cwnd) {
|
|
if (tp->snd_cwnd < tp->snd_cwnd_clamp) {
|
|
tp->snd_cwnd++;
|
|
mptcp_balia_recalc_ai(sk);
|
|
}
|
|
|
|
tp->snd_cwnd_cnt = 0;
|
|
} else {
|
|
tp->snd_cwnd_cnt++;
|
|
}
|
|
}
|
|
|
|
static u32 mptcp_balia_ssthresh(struct sock *sk)
|
|
{
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
|
const struct mptcp_cb *mpcb = tp->mpcb;
|
|
|
|
if (unlikely(!mptcp(tp) || mpcb->cnt_established <= 1))
|
|
return tcp_reno_ssthresh(sk);
|
|
else
|
|
return max((u32)(tp->snd_cwnd - mptcp_get_md(sk)), 1U);
|
|
}
|
|
|
|
static struct tcp_congestion_ops mptcp_balia = {
|
|
.init = mptcp_balia_init,
|
|
.ssthresh = mptcp_balia_ssthresh,
|
|
.cong_avoid = mptcp_balia_cong_avoid,
|
|
.cwnd_event = mptcp_balia_cwnd_event,
|
|
.set_state = mptcp_balia_set_state,
|
|
.owner = THIS_MODULE,
|
|
.name = "balia",
|
|
};
|
|
|
|
static int __init mptcp_balia_register(void)
|
|
{
|
|
BUILD_BUG_ON(sizeof(struct mptcp_balia) > ICSK_CA_PRIV_SIZE);
|
|
return tcp_register_congestion_control(&mptcp_balia);
|
|
}
|
|
|
|
static void __exit mptcp_balia_unregister(void)
|
|
{
|
|
tcp_unregister_congestion_control(&mptcp_balia);
|
|
}
|
|
|
|
module_init(mptcp_balia_register);
|
|
module_exit(mptcp_balia_unregister);
|
|
|
|
MODULE_AUTHOR("Jaehyun Hwang, Anwar Walid, Qiuyu Peng, Steven H. Low");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("MPTCP BALIA CONGESTION CONTROL ALGORITHM");
|
|
MODULE_VERSION("0.1");
|