Skip to content

Commit

Permalink
Merge branch 'dctcp'
Browse files Browse the repository at this point in the history
Daniel Borkmann says:

====================
net: tcp: DCTCP congestion control algorithm

This patch series adds support for the DataCenter TCP (DCTCP) congestion
control algorithm. Please see individual patches for the details.

The last patch adds DCTCP as a congestion control module, and previous
ones add needed infrastructure to extend the congestion control framework.

Joint work between Florian Westphal, Daniel Borkmann and Glenn Judd.

v3 -> v2:
 - No changes anywhere, just a resend as requested by Dave
 - Added Stephen's ACK
v1 -> v2:
 - Rebased to latest net-next
 - Addressed Eric's feedback, thanks!
  - Update stale comment wrt. DCTCP ECN usage
  - Don't call INET_ECN_xmit for every packet
 - Add dctcp ss/inetdiag support to expose internal stats to userspace
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 29, 2014
2 parents 53dfd50 + e3118e8 commit a11238e
Show file tree
Hide file tree
Showing 12 changed files with 574 additions and 78 deletions.
43 changes: 43 additions & 0 deletions Documentation/networking/dctcp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
DCTCP (DataCenter TCP)
----------------------

DCTCP is an enhancement to the TCP congestion control algorithm for data
center networks and leverages Explicit Congestion Notification (ECN) in
the data center network to provide multi-bit feedback to the end hosts.

To enable it on end hosts:

sysctl -w net.ipv4.tcp_congestion_control=dctcp

All switches in the data center network running DCTCP must support ECN
marking and be configured for marking when reaching defined switch buffer
thresholds. The default ECN marking threshold heuristic for DCTCP on
switches is 20 packets (30KB) at 1Gbps, and 65 packets (~100KB) at 10Gbps,
but might need further careful tweaking.

For more details, see below documents:

Paper:

The algorithm is further described in detail in the following two
SIGCOMM/SIGMETRICS papers:

i) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
"Data Center TCP (DCTCP)", Data Center Networks session
Proc. ACM SIGCOMM, New Delhi, 2010.
http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
http://www.sigcomm.org/ccr/papers/2010/October/1851275.1851192

ii) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
"Analysis of DCTCP: Stability, Convergence, and Fairness"
Proc. ACM SIGMETRICS, San Jose, 2011.
http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf

IETF informational draft:

http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00

DCTCP site:

http://simula.stanford.edu/~alizade/Site/DCTCP.html
78 changes: 58 additions & 20 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,23 +733,6 @@ struct tcp_skb_cb {

#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))

/* RFC3168 : 6.1.1 SYN packets must not have ECT/ECN bits set
*
* If we receive a SYN packet with these bits set, it means a network is
* playing bad games with TOS bits. In order to avoid possible false congestion
* notifications, we disable TCP ECN negociation.
*/
static inline void
TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb,
struct net *net)
{
const struct tcphdr *th = tcp_hdr(skb);

if (net->ipv4.sysctl_tcp_ecn && th->ece && th->cwr &&
INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield))
inet_rsk(req)->ecn_ok = 1;
}

/* Due to TSO, an SKB can be composed of multiple actual
* packets. To keep these tracked properly, we use this.
*/
Expand Down Expand Up @@ -780,8 +763,17 @@ enum tcp_ca_event {
CA_EVENT_CWND_RESTART, /* congestion window restart */
CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */
CA_EVENT_LOSS, /* loss timeout */
CA_EVENT_FAST_ACK, /* in sequence ack */
CA_EVENT_SLOW_ACK, /* other ack */
CA_EVENT_ECN_NO_CE, /* ECT set, but not CE marked */
CA_EVENT_ECN_IS_CE, /* received CE marked IP packet */
CA_EVENT_DELAYED_ACK, /* Delayed ack is sent */
CA_EVENT_NON_DELAYED_ACK,
};

/* Information about inbound ACK, passed to cong_ops->in_ack_event() */
enum tcp_ca_ack_event_flags {
CA_ACK_SLOWPATH = (1 << 0), /* In slow path processing */
CA_ACK_WIN_UPDATE = (1 << 1), /* ACK updated window */
CA_ACK_ECE = (1 << 2), /* ECE bit is set on ack */
};

/*
Expand All @@ -791,7 +783,10 @@ enum tcp_ca_event {
#define TCP_CA_MAX 128
#define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX)

/* Algorithm can be set on socket without CAP_NET_ADMIN privileges */
#define TCP_CONG_NON_RESTRICTED 0x1
/* Requires ECN/ECT set on all packets */
#define TCP_CONG_NEEDS_ECN 0x2

struct tcp_congestion_ops {
struct list_head list;
Expand All @@ -810,6 +805,8 @@ struct tcp_congestion_ops {
void (*set_state)(struct sock *sk, u8 new_state);
/* call when cwnd event occurs (optional) */
void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
/* call when ack arrives (optional) */
void (*in_ack_event)(struct sock *sk, u32 flags);
/* new value of cwnd after loss (optional) */
u32 (*undo_cwnd)(struct sock *sk);
/* hook for packet ack accounting (optional) */
Expand All @@ -824,6 +821,7 @@ struct tcp_congestion_ops {
int tcp_register_congestion_control(struct tcp_congestion_ops *type);
void tcp_unregister_congestion_control(struct tcp_congestion_ops *type);

void tcp_assign_congestion_control(struct sock *sk);
void tcp_init_congestion_control(struct sock *sk);
void tcp_cleanup_congestion_control(struct sock *sk);
int tcp_set_default_congestion_control(const char *name);
Expand All @@ -835,11 +833,17 @@ int tcp_set_congestion_control(struct sock *sk, const char *name);
int tcp_slow_start(struct tcp_sock *tp, u32 acked);
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);

extern struct tcp_congestion_ops tcp_init_congestion_ops;
u32 tcp_reno_ssthresh(struct sock *sk);
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
extern struct tcp_congestion_ops tcp_reno;

static inline bool tcp_ca_needs_ecn(const struct sock *sk)
{
const struct inet_connection_sock *icsk = inet_csk(sk);

return icsk->icsk_ca_ops->flags & TCP_CONG_NEEDS_ECN;
}

static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
{
struct inet_connection_sock *icsk = inet_csk(sk);
Expand All @@ -857,6 +861,40 @@ static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event)
icsk->icsk_ca_ops->cwnd_event(sk, event);
}

/* RFC3168 : 6.1.1 SYN packets must not have ECT/ECN bits set
*
* If we receive a SYN packet with these bits set, it means a
* network is playing bad games with TOS bits. In order to
* avoid possible false congestion notifications, we disable
* TCP ECN negociation.
*
* Exception: tcp_ca wants ECN. This is required for DCTCP
* congestion control; it requires setting ECT on all packets,
* including SYN. We inverse the test in this case: If our
* local socket wants ECN, but peer only set ece/cwr (but not
* ECT in IP header) its probably a non-DCTCP aware sender.
*/
static inline void
TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb,
const struct sock *listen_sk)
{
const struct tcphdr *th = tcp_hdr(skb);
const struct net *net = sock_net(listen_sk);
bool th_ecn = th->ece && th->cwr;
bool ect, need_ecn;

if (!th_ecn)
return;

ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield);
need_ecn = tcp_ca_needs_ecn(listen_sk);

if (!ect && !need_ecn && net->ipv4.sysctl_tcp_ecn)
inet_rsk(req)->ecn_ok = 1;
else if (ect && need_ecn)
inet_rsk(req)->ecn_ok = 1;
}

/* These functions determine how the current flow behaves in respect of SACK
* handling. SACK is negotiated with the peer, and therefore it can vary
* between different flows.
Expand Down
13 changes: 11 additions & 2 deletions include/uapi/linux/inet_diag.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ enum {
INET_DIAG_TCLASS,
INET_DIAG_SKMEMINFO,
INET_DIAG_SHUTDOWN,
INET_DIAG_DCTCPINFO,
};

#define INET_DIAG_MAX INET_DIAG_SHUTDOWN

#define INET_DIAG_MAX INET_DIAG_DCTCPINFO

/* INET_DIAG_MEM */

Expand All @@ -133,5 +133,14 @@ struct tcpvegas_info {
__u32 tcpv_minrtt;
};

/* INET_DIAG_DCTCPINFO */

struct tcp_dctcp_info {
__u16 dctcp_enabled;
__u16 dctcp_ce_state;
__u32 dctcp_alpha;
__u32 dctcp_ab_ecn;
__u32 dctcp_ab_tot;
};

#endif /* _UAPI_INET_DIAG_H_ */
26 changes: 25 additions & 1 deletion net/ipv4/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,27 @@ config TCP_CONG_ILLINOIS
For further details see:
http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html

config TCP_CONG_DCTCP
tristate "DataCenter TCP (DCTCP)"
default n
---help---
DCTCP leverages Explicit Congestion Notification (ECN) in the network to
provide multi-bit feedback to the end hosts. It is designed to provide:

- High burst tolerance (incast due to partition/aggregate),
- Low latency (short flows, queries),
- High throughput (continuous data updates, large file transfers) with
commodity, shallow-buffered switches.

All switches in the data center network running DCTCP must support
ECN marking and be configured for marking when reaching defined switch
buffer thresholds. The default ECN marking threshold heuristic for
DCTCP on switches is 20 packets (30KB) at 1Gbps, and 65 packets
(~100KB) at 10Gbps, but might need further careful tweaking.

For further details see:
http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf

choice
prompt "Default TCP congestion control"
default DEFAULT_CUBIC
Expand Down Expand Up @@ -598,9 +619,11 @@ choice
config DEFAULT_WESTWOOD
bool "Westwood" if TCP_CONG_WESTWOOD=y

config DEFAULT_DCTCP
bool "DCTCP" if TCP_CONG_DCTCP=y

config DEFAULT_RENO
bool "Reno"

endchoice

endif
Expand All @@ -620,6 +643,7 @@ config DEFAULT_TCP_CONG
default "westwood" if DEFAULT_WESTWOOD
default "veno" if DEFAULT_VENO
default "reno" if DEFAULT_RENO
default "dctcp" if DEFAULT_DCTCP
default "cubic"

config TCP_MD5SIG
Expand Down
1 change: 1 addition & 0 deletions net/ipv4/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o
obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
obj-$(CONFIG_TCP_CONG_DCTCP) += tcp_dctcp.o
obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o
obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o
Expand Down
6 changes: 2 additions & 4 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ void tcp_init_sock(struct sock *sk)

tp->reordering = sysctl_tcp_reordering;
tcp_enable_early_retrans(tp);
icsk->icsk_ca_ops = &tcp_init_congestion_ops;
tcp_assign_congestion_control(sk);

tp->tsoffset = 0;

Expand Down Expand Up @@ -3258,8 +3258,6 @@ void __init tcp_init(void)
tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);

tcp_metrics_init();

tcp_register_congestion_control(&tcp_reno);

BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0);
tcp_tasklet_init();
}
46 changes: 22 additions & 24 deletions net/ipv4/tcp_cong.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,34 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca)
EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control);

/* Assign choice of congestion control. */
void tcp_init_congestion_control(struct sock *sk)
void tcp_assign_congestion_control(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_congestion_ops *ca;

/* if no choice made yet assign the current value set as default */
if (icsk->icsk_ca_ops == &tcp_init_congestion_ops) {
rcu_read_lock();
list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
if (try_module_get(ca->owner)) {
icsk->icsk_ca_ops = ca;
break;
}

/* fallback to next available */
rcu_read_lock();
list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
if (likely(try_module_get(ca->owner))) {
icsk->icsk_ca_ops = ca;
goto out;
}
rcu_read_unlock();
/* Fallback to next available. The last really
* guaranteed fallback is Reno from this list.
*/
}
out:
rcu_read_unlock();

/* Clear out private data before diag gets it and
* the ca has not been initialized.
*/
if (ca->get_info)
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
}

void tcp_init_congestion_control(struct sock *sk)
{
const struct inet_connection_sock *icsk = inet_csk(sk);

if (icsk->icsk_ca_ops->init)
icsk->icsk_ca_ops->init(sk);
Expand Down Expand Up @@ -345,15 +355,3 @@ struct tcp_congestion_ops tcp_reno = {
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
};

/* Initial congestion control used (until SYN)
* really reno under another name so we can tell difference
* during tcp_set_default_congestion_control
*/
struct tcp_congestion_ops tcp_init_congestion_ops = {
.name = "",
.owner = THIS_MODULE,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
};
EXPORT_SYMBOL_GPL(tcp_init_congestion_ops);
Loading

0 comments on commit a11238e

Please sign in to comment.