269 lines
6.9 KiB
C
269 lines
6.9 KiB
C
/*
|
|
* MPTCP implementation - WEIGHTED VEGAS
|
|
*
|
|
* Algorithm design:
|
|
* Yu Cao <cyAnalyst@126.com>
|
|
* Mingwei Xu <xmw@csnet1.cs.tsinghua.edu.cn>
|
|
* Xiaoming Fu <fu@cs.uni-goettinggen.de>
|
|
*
|
|
* Implementation:
|
|
* Yu Cao <cyAnalyst@126.com>
|
|
* Enhuan Dong <deh13@mails.tsinghua.edu.cn>
|
|
*
|
|
* Ported to the official MPTCP-kernel:
|
|
* Christoph Paasch <christoph.paasch@uclouvain.be>
|
|
*
|
|
* 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 <linux/skbuff.h>
|
|
#include <net/tcp.h>
|
|
#include <net/mptcp.h>
|
|
#include <linux/module.h>
|
|
#include <linux/tcp.h>
|
|
|
|
static int initial_alpha = 2;
|
|
static int total_alpha = 10;
|
|
static int gamma = 1;
|
|
|
|
module_param(initial_alpha, int, 0644);
|
|
MODULE_PARM_DESC(initial_alpha, "initial alpha for all subflows");
|
|
module_param(total_alpha, int, 0644);
|
|
MODULE_PARM_DESC(total_alpha, "total alpha for all subflows");
|
|
module_param(gamma, int, 0644);
|
|
MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)");
|
|
|
|
#define MPTCP_WVEGAS_SCALE 16
|
|
|
|
/* wVegas variables */
|
|
struct wvegas {
|
|
u32 beg_snd_nxt; /* right edge during last RTT */
|
|
u8 doing_wvegas_now;/* if true, do wvegas for this RTT */
|
|
|
|
u16 cnt_rtt; /* # of RTTs measured within last RTT */
|
|
u32 sampled_rtt; /* cumulative RTTs measured within last RTT (in usec) */
|
|
u32 base_rtt; /* the min of all wVegas RTT measurements seen (in usec) */
|
|
|
|
u64 instant_rate; /* cwnd / srtt_us, unit: pkts/us * 2^16 */
|
|
u64 weight; /* the ratio of subflow's rate to the total rate, * 2^16 */
|
|
int alpha; /* alpha for each subflows */
|
|
|
|
u32 queue_delay; /* queue delay*/
|
|
};
|
|
|
|
|
|
static inline u64 mptcp_wvegas_scale(u32 val, int scale)
|
|
{
|
|
return (u64) val << scale;
|
|
}
|
|
|
|
static void wvegas_enable(const struct sock *sk)
|
|
{
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
|
|
wvegas->doing_wvegas_now = 1;
|
|
|
|
wvegas->beg_snd_nxt = tp->snd_nxt;
|
|
|
|
wvegas->cnt_rtt = 0;
|
|
wvegas->sampled_rtt = 0;
|
|
|
|
wvegas->instant_rate = 0;
|
|
wvegas->alpha = initial_alpha;
|
|
wvegas->weight = mptcp_wvegas_scale(1, MPTCP_WVEGAS_SCALE);
|
|
|
|
wvegas->queue_delay = 0;
|
|
}
|
|
|
|
static inline void wvegas_disable(const struct sock *sk)
|
|
{
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
|
|
wvegas->doing_wvegas_now = 0;
|
|
}
|
|
|
|
static void mptcp_wvegas_init(struct sock *sk)
|
|
{
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
|
|
wvegas->base_rtt = 0x7fffffff;
|
|
wvegas_enable(sk);
|
|
}
|
|
|
|
static inline u64 mptcp_wvegas_rate(u32 cwnd, u32 rtt_us)
|
|
{
|
|
return div_u64(mptcp_wvegas_scale(cwnd, MPTCP_WVEGAS_SCALE), rtt_us);
|
|
}
|
|
|
|
static void mptcp_wvegas_pkts_acked(struct sock *sk,
|
|
const struct ack_sample *sample)
|
|
{
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
u32 vrtt;
|
|
|
|
if (sample->rtt_us < 0)
|
|
return;
|
|
|
|
vrtt = sample->rtt_us + 1;
|
|
|
|
if (vrtt < wvegas->base_rtt)
|
|
wvegas->base_rtt = vrtt;
|
|
|
|
wvegas->sampled_rtt += vrtt;
|
|
wvegas->cnt_rtt++;
|
|
}
|
|
|
|
static void mptcp_wvegas_state(struct sock *sk, u8 ca_state)
|
|
{
|
|
if (ca_state == TCP_CA_Open)
|
|
wvegas_enable(sk);
|
|
else
|
|
wvegas_disable(sk);
|
|
}
|
|
|
|
static void mptcp_wvegas_cwnd_event(struct sock *sk, enum tcp_ca_event event)
|
|
{
|
|
if (event == CA_EVENT_CWND_RESTART) {
|
|
mptcp_wvegas_init(sk);
|
|
} else if (event == CA_EVENT_LOSS) {
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
wvegas->instant_rate = 0;
|
|
}
|
|
}
|
|
|
|
static inline u32 mptcp_wvegas_ssthresh(const struct tcp_sock *tp)
|
|
{
|
|
return min(tp->snd_ssthresh, tp->snd_cwnd);
|
|
}
|
|
|
|
static u64 mptcp_wvegas_weight(const struct mptcp_cb *mpcb, const struct sock *sk)
|
|
{
|
|
u64 total_rate = 0;
|
|
struct sock *sub_sk;
|
|
const struct wvegas *wvegas = inet_csk_ca(sk);
|
|
|
|
if (!mpcb)
|
|
return wvegas->weight;
|
|
|
|
|
|
mptcp_for_each_sk(mpcb, sub_sk) {
|
|
struct wvegas *sub_wvegas = inet_csk_ca(sub_sk);
|
|
|
|
/* sampled_rtt is initialized by 0 */
|
|
if (mptcp_sk_can_send(sub_sk) && (sub_wvegas->sampled_rtt > 0))
|
|
total_rate += sub_wvegas->instant_rate;
|
|
}
|
|
|
|
if (total_rate && wvegas->instant_rate)
|
|
return div64_u64(mptcp_wvegas_scale(wvegas->instant_rate, MPTCP_WVEGAS_SCALE), total_rate);
|
|
else
|
|
return wvegas->weight;
|
|
}
|
|
|
|
static void mptcp_wvegas_cong_avoid(struct sock *sk, u32 ack, u32 acked)
|
|
{
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
struct wvegas *wvegas = inet_csk_ca(sk);
|
|
|
|
if (!wvegas->doing_wvegas_now) {
|
|
tcp_reno_cong_avoid(sk, ack, acked);
|
|
return;
|
|
}
|
|
|
|
if (after(ack, wvegas->beg_snd_nxt)) {
|
|
wvegas->beg_snd_nxt = tp->snd_nxt;
|
|
|
|
if (wvegas->cnt_rtt <= 2) {
|
|
tcp_reno_cong_avoid(sk, ack, acked);
|
|
} else {
|
|
u32 rtt, diff, q_delay;
|
|
u64 target_cwnd;
|
|
|
|
rtt = wvegas->sampled_rtt / wvegas->cnt_rtt;
|
|
target_cwnd = div_u64(((u64)tp->snd_cwnd * wvegas->base_rtt), rtt);
|
|
|
|
diff = div_u64((u64)tp->snd_cwnd * (rtt - wvegas->base_rtt), rtt);
|
|
|
|
if (diff > gamma && tcp_in_slow_start(tp)) {
|
|
tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1);
|
|
tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp);
|
|
|
|
} else if (tcp_in_slow_start(tp)) {
|
|
tcp_slow_start(tp, acked);
|
|
} else {
|
|
if (diff >= wvegas->alpha) {
|
|
wvegas->instant_rate = mptcp_wvegas_rate(tp->snd_cwnd, rtt);
|
|
wvegas->weight = mptcp_wvegas_weight(tp->mpcb, sk);
|
|
wvegas->alpha = max(2U, (u32)((wvegas->weight * total_alpha) >> MPTCP_WVEGAS_SCALE));
|
|
}
|
|
if (diff > wvegas->alpha) {
|
|
tp->snd_cwnd--;
|
|
tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp);
|
|
} else if (diff < wvegas->alpha) {
|
|
tp->snd_cwnd++;
|
|
}
|
|
|
|
/* Try to drain link queue if needed*/
|
|
q_delay = rtt - wvegas->base_rtt;
|
|
if ((wvegas->queue_delay == 0) || (wvegas->queue_delay > q_delay))
|
|
wvegas->queue_delay = q_delay;
|
|
|
|
if (q_delay >= 2 * wvegas->queue_delay) {
|
|
u32 backoff_factor = div_u64(mptcp_wvegas_scale(wvegas->base_rtt, MPTCP_WVEGAS_SCALE), 2 * rtt);
|
|
tp->snd_cwnd = ((u64)tp->snd_cwnd * backoff_factor) >> MPTCP_WVEGAS_SCALE;
|
|
wvegas->queue_delay = 0;
|
|
}
|
|
}
|
|
|
|
if (tp->snd_cwnd < 2)
|
|
tp->snd_cwnd = 2;
|
|
else if (tp->snd_cwnd > tp->snd_cwnd_clamp)
|
|
tp->snd_cwnd = tp->snd_cwnd_clamp;
|
|
|
|
tp->snd_ssthresh = tcp_current_ssthresh(sk);
|
|
}
|
|
|
|
wvegas->cnt_rtt = 0;
|
|
wvegas->sampled_rtt = 0;
|
|
}
|
|
/* Use normal slow start */
|
|
else if (tcp_in_slow_start(tp))
|
|
tcp_slow_start(tp, acked);
|
|
}
|
|
|
|
|
|
static struct tcp_congestion_ops mptcp_wvegas __read_mostly = {
|
|
.init = mptcp_wvegas_init,
|
|
.ssthresh = tcp_reno_ssthresh,
|
|
.cong_avoid = mptcp_wvegas_cong_avoid,
|
|
.pkts_acked = mptcp_wvegas_pkts_acked,
|
|
.set_state = mptcp_wvegas_state,
|
|
.cwnd_event = mptcp_wvegas_cwnd_event,
|
|
|
|
.owner = THIS_MODULE,
|
|
.name = "wvegas",
|
|
};
|
|
|
|
static int __init mptcp_wvegas_register(void)
|
|
{
|
|
BUILD_BUG_ON(sizeof(struct wvegas) > ICSK_CA_PRIV_SIZE);
|
|
tcp_register_congestion_control(&mptcp_wvegas);
|
|
return 0;
|
|
}
|
|
|
|
static void __exit mptcp_wvegas_unregister(void)
|
|
{
|
|
tcp_unregister_congestion_control(&mptcp_wvegas);
|
|
}
|
|
|
|
module_init(mptcp_wvegas_register);
|
|
module_exit(mptcp_wvegas_unregister);
|
|
|
|
MODULE_AUTHOR("Yu Cao, Enhuan Dong");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("MPTCP wVegas");
|
|
MODULE_VERSION("0.1");
|