#if defined(CONFIG_BCM_MPTCP) && defined(CONFIG_BCM_KF_MPTCP) /* * MPTCP implementation - Balia Congestion Control * (Balanced Linked Adaptation Algorithm) * * Analysis, Design and Implementation: * Qiuyu Peng * Anwar Walid * Jaehyun Hwang * Steven H. Low * * 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 #include #include /* 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"); #endif