| /* |
| * MPTCP implementation - Linked Increase congestion control Algorithm (LIA) |
| * |
| * Initial Design & Implementation: |
| * Sébastien Barré <sebastien.barre@uclouvain.be> |
| * |
| * Current Maintainer & Author: |
| * Christoph Paasch <christoph.paasch@uclouvain.be> |
| * |
| * Additional authors: |
| * Jaakko Korkeaniemi <jaakko.korkeaniemi@aalto.fi> |
| * Gregory Detal <gregory.detal@uclouvain.be> |
| * Fabien Duchêne <fabien.duchene@uclouvain.be> |
| * Andreas Seelinger <Andreas.Seelinger@rwth-aachen.de> |
| * Lavkesh Lahngir <lavkesh51@gmail.com> |
| * Andreas Ripke <ripke@neclab.eu> |
| * Vlad Dogaru <vlad.dogaru@intel.com> |
| * Octavian Purdila <octavian.purdila@intel.com> |
| * John Ronan <jronan@tssg.org> |
| * Catalin Nicutar <catalin.nicutar@gmail.com> |
| * Brandon Heller <brandonh@stanford.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> |
| |
| /* Scaling is done in the numerator with alpha_scale_num and in the denominator |
| * with alpha_scale_den. |
| * |
| * To downscale, we just need to use alpha_scale. |
| * |
| * We have: alpha_scale = alpha_scale_num / (alpha_scale_den ^ 2) |
| */ |
| static int alpha_scale_den = 10; |
| static int alpha_scale_num = 32; |
| static int alpha_scale = 12; |
| |
| struct mptcp_ccc { |
| u64 alpha; |
| bool forced_update; |
| }; |
| |
| static inline int mptcp_ccc_sk_can_send(const struct sock *sk) |
| { |
| return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt_us; |
| } |
| |
| static inline u64 mptcp_get_alpha(const struct sock *meta_sk) |
| { |
| return ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->alpha; |
| } |
| |
| static inline void mptcp_set_alpha(const struct sock *meta_sk, u64 alpha) |
| { |
| ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->alpha = alpha; |
| } |
| |
| static inline u64 mptcp_ccc_scale(u32 val, int scale) |
| { |
| return (u64) val << scale; |
| } |
| |
| static inline bool mptcp_get_forced(const struct sock *meta_sk) |
| { |
| return ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->forced_update; |
| } |
| |
| static inline void mptcp_set_forced(const struct sock *meta_sk, bool force) |
| { |
| ((struct mptcp_ccc *)inet_csk_ca(meta_sk))->forced_update = force; |
| } |
| |
| static void mptcp_ccc_recalc_alpha(const struct sock *sk) |
| { |
| const struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; |
| const struct sock *sub_sk; |
| int best_cwnd = 0, best_rtt = 0, can_send = 0; |
| u64 max_numerator = 0, sum_denominator = 0, alpha = 1; |
| |
| if (!mpcb) |
| return; |
| |
| /* Only one subflow left - fall back to normal reno-behavior |
| * (set alpha to 1) |
| */ |
| if (mpcb->cnt_established <= 1) |
| goto exit; |
| |
| /* Do regular alpha-calculation for multiple subflows */ |
| |
| /* Find the max numerator of the alpha-calculation */ |
| mptcp_for_each_sk(mpcb, sub_sk) { |
| struct tcp_sock *sub_tp = tcp_sk(sub_sk); |
| u64 tmp; |
| |
| if (!mptcp_ccc_sk_can_send(sub_sk)) |
| continue; |
| |
| can_send++; |
| |
| /* We need to look for the path, that provides the max-value. |
| * Integer-overflow is not possible here, because |
| * tmp will be in u64. |
| */ |
| tmp = div64_u64(mptcp_ccc_scale(sub_tp->snd_cwnd, |
| alpha_scale_num), (u64)sub_tp->srtt_us * sub_tp->srtt_us); |
| |
| if (tmp >= max_numerator) { |
| max_numerator = tmp; |
| best_cwnd = sub_tp->snd_cwnd; |
| best_rtt = sub_tp->srtt_us; |
| } |
| } |
| |
| /* No subflow is able to send - we don't care anymore */ |
| if (unlikely(!can_send)) |
| goto exit; |
| |
| /* Calculate the denominator */ |
| mptcp_for_each_sk(mpcb, sub_sk) { |
| struct tcp_sock *sub_tp = tcp_sk(sub_sk); |
| |
| if (!mptcp_ccc_sk_can_send(sub_sk)) |
| continue; |
| |
| sum_denominator += div_u64( |
| mptcp_ccc_scale(sub_tp->snd_cwnd, |
| alpha_scale_den) * best_rtt, |
| sub_tp->srtt_us); |
| } |
| sum_denominator *= sum_denominator; |
| if (unlikely(!sum_denominator)) { |
| pr_err("%s: sum_denominator == 0, cnt_established:%d\n", |
| __func__, mpcb->cnt_established); |
| mptcp_for_each_sk(mpcb, sub_sk) { |
| struct tcp_sock *sub_tp = tcp_sk(sub_sk); |
| pr_err("%s: pi:%d, state:%d\n, rtt:%u, cwnd: %u", |
| __func__, sub_tp->mptcp->path_index, |
| sub_sk->sk_state, sub_tp->srtt_us, |
| sub_tp->snd_cwnd); |
| } |
| } |
| |
| alpha = div64_u64(mptcp_ccc_scale(best_cwnd, alpha_scale_num), sum_denominator); |
| |
| if (unlikely(!alpha)) |
| alpha = 1; |
| |
| exit: |
| mptcp_set_alpha(mptcp_meta_sk(sk), alpha); |
| } |
| |
| static void mptcp_ccc_init(struct sock *sk) |
| { |
| if (mptcp(tcp_sk(sk))) { |
| mptcp_set_forced(mptcp_meta_sk(sk), 0); |
| mptcp_set_alpha(mptcp_meta_sk(sk), 1); |
| } |
| /* If we do not mptcp, behave like reno: return */ |
| } |
| |
| static void mptcp_ccc_cwnd_event(struct sock *sk, enum tcp_ca_event event) |
| { |
| if (event == CA_EVENT_LOSS) |
| mptcp_ccc_recalc_alpha(sk); |
| } |
| |
| static void mptcp_ccc_set_state(struct sock *sk, u8 ca_state) |
| { |
| if (!mptcp(tcp_sk(sk))) |
| return; |
| |
| mptcp_set_forced(mptcp_meta_sk(sk), 1); |
| } |
| |
| static void mptcp_ccc_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 (tcp_in_slow_start(tp)) { |
| /* In "safe" area, increase. */ |
| tcp_slow_start(tp, acked); |
| mptcp_ccc_recalc_alpha(sk); |
| return; |
| } |
| |
| if (mptcp_get_forced(mptcp_meta_sk(sk))) { |
| mptcp_ccc_recalc_alpha(sk); |
| mptcp_set_forced(mptcp_meta_sk(sk), 0); |
| } |
| |
| if (mpcb->cnt_established > 1) { |
| u64 alpha = mptcp_get_alpha(mptcp_meta_sk(sk)); |
| |
| /* This may happen, if at the initialization, the mpcb |
| * was not yet attached to the sock, and thus |
| * initializing alpha failed. |
| */ |
| if (unlikely(!alpha)) |
| alpha = 1; |
| |
| snd_cwnd = (int) div_u64 ((u64) mptcp_ccc_scale(1, alpha_scale), |
| alpha); |
| |
| /* snd_cwnd_cnt >= max (scale * tot_cwnd / alpha, cwnd) |
| * Thus, we select here the max value. |
| */ |
| if (snd_cwnd < tp->snd_cwnd) |
| snd_cwnd = tp->snd_cwnd; |
| } 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_ccc_recalc_alpha(sk); |
| } |
| |
| tp->snd_cwnd_cnt = 0; |
| } else { |
| tp->snd_cwnd_cnt++; |
| } |
| } |
| |
| static struct tcp_congestion_ops mptcp_ccc = { |
| .init = mptcp_ccc_init, |
| .ssthresh = tcp_reno_ssthresh, |
| .cong_avoid = mptcp_ccc_cong_avoid, |
| .undo_cwnd = tcp_reno_undo_cwnd, |
| .cwnd_event = mptcp_ccc_cwnd_event, |
| .set_state = mptcp_ccc_set_state, |
| .owner = THIS_MODULE, |
| .name = "lia", |
| }; |
| |
| static int __init mptcp_ccc_register(void) |
| { |
| BUILD_BUG_ON(sizeof(struct mptcp_ccc) > ICSK_CA_PRIV_SIZE); |
| return tcp_register_congestion_control(&mptcp_ccc); |
| } |
| |
| static void __exit mptcp_ccc_unregister(void) |
| { |
| tcp_unregister_congestion_control(&mptcp_ccc); |
| } |
| |
| module_init(mptcp_ccc_register); |
| module_exit(mptcp_ccc_unregister); |
| |
| MODULE_AUTHOR("Christoph Paasch, Sébastien Barré"); |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("MPTCP LINKED INCREASE CONGESTION CONTROL ALGORITHM"); |
| MODULE_VERSION("0.1"); |