Commit c3a8d947 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

tcp: use dctcp if enabled on the route to the initiator

Currently, the following case doesn't use DCTCP, even if it should:
A responder has f.e. Cubic as system wide default, but for a specific
route to the initiating host, DCTCP is being set in RTAX_CC_ALGO. The
initiating host then uses DCTCP as congestion control, but since the
initiator sets ECT(0), tcp_ecn_create_request() doesn't set ecn_ok,
and we have to fall back to Reno after 3WHS completes.

We were thinking on how to solve this in a minimal, non-intrusive
way without bloating tcp_ecn_create_request() needlessly: lets cache
the CA ecn option flag in RTAX_FEATURES. In other words, when ECT(0)
is set on the SYN packet, set ecn_ok=1 iff route RTAX_FEATURES
contains the unexposed (internal-only) DST_FEATURE_ECN_CA. This allows
to only do a single metric feature lookup inside tcp_ecn_create_request().

Joint work with Florian Westphal.
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b8d3e416
...@@ -207,6 +207,12 @@ static inline void dst_metric_set(struct dst_entry *dst, int metric, u32 val) ...@@ -207,6 +207,12 @@ static inline void dst_metric_set(struct dst_entry *dst, int metric, u32 val)
p[metric-1] = val; p[metric-1] = val;
} }
/* Kernel-internal feature bits that are unallocated in user space. */
#define DST_FEATURE_ECN_CA (1 << 31)
#define DST_FEATURE_MASK (DST_FEATURE_ECN_CA)
#define DST_FEATURE_ECN_MASK (DST_FEATURE_ECN_CA | RTAX_FEATURE_ECN)
static inline u32 static inline u32
dst_feature(const struct dst_entry *dst, u32 feature) dst_feature(const struct dst_entry *dst, u32 feature)
{ {
......
...@@ -888,7 +888,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked); ...@@ -888,7 +888,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
extern struct tcp_congestion_ops tcp_reno; extern struct tcp_congestion_ops tcp_reno;
struct tcp_congestion_ops *tcp_ca_find_key(u32 key); struct tcp_congestion_ops *tcp_ca_find_key(u32 key);
u32 tcp_ca_get_key_by_name(const char *name); u32 tcp_ca_get_key_by_name(const char *name, bool *ecn_ca);
#ifdef CONFIG_INET #ifdef CONFIG_INET
char *tcp_ca_get_name_by_key(u32 key, char *buffer); char *tcp_ca_get_name_by_key(u32 key, char *buffer);
#else #else
......
...@@ -678,6 +678,12 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics) ...@@ -678,6 +678,12 @@ int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
continue; continue;
if (nla_put_string(skb, i + 1, name)) if (nla_put_string(skb, i + 1, name))
goto nla_put_failure; goto nla_put_failure;
} else if (i == RTAX_FEATURES - 1) {
u32 user_features = metrics[i] & RTAX_FEATURE_MASK;
BUILD_BUG_ON(RTAX_FEATURE_MASK & DST_FEATURE_MASK);
if (nla_put_u32(skb, i + 1, user_features))
goto nla_put_failure;
} else { } else {
if (nla_put_u32(skb, i + 1, metrics[i])) if (nla_put_u32(skb, i + 1, metrics[i]))
goto nla_put_failure; goto nla_put_failure;
......
...@@ -879,6 +879,7 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) ...@@ -879,6 +879,7 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc)
static int static int
fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg) fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
{ {
bool ecn_ca = false;
struct nlattr *nla; struct nlattr *nla;
int remaining; int remaining;
...@@ -898,7 +899,7 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg) ...@@ -898,7 +899,7 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
char tmp[TCP_CA_NAME_MAX]; char tmp[TCP_CA_NAME_MAX];
nla_strlcpy(tmp, nla, sizeof(tmp)); nla_strlcpy(tmp, nla, sizeof(tmp));
val = tcp_ca_get_key_by_name(tmp); val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
if (val == TCP_CA_UNSPEC) if (val == TCP_CA_UNSPEC)
return -EINVAL; return -EINVAL;
} else { } else {
...@@ -913,6 +914,9 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg) ...@@ -913,6 +914,9 @@ fib_convert_metrics(struct fib_info *fi, const struct fib_config *cfg)
fi->fib_metrics[type - 1] = val; fi->fib_metrics[type - 1] = val;
} }
if (ecn_ca)
fi->fib_metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
return 0; return 0;
} }
......
...@@ -114,16 +114,19 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca) ...@@ -114,16 +114,19 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca)
} }
EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control); EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control);
u32 tcp_ca_get_key_by_name(const char *name) u32 tcp_ca_get_key_by_name(const char *name, bool *ecn_ca)
{ {
const struct tcp_congestion_ops *ca; const struct tcp_congestion_ops *ca;
u32 key; u32 key = TCP_CA_UNSPEC;
might_sleep(); might_sleep();
rcu_read_lock(); rcu_read_lock();
ca = __tcp_ca_find_autoload(name); ca = __tcp_ca_find_autoload(name);
key = ca ? ca->key : TCP_CA_UNSPEC; if (ca) {
key = ca->key;
*ecn_ca = ca->flags & TCP_CONG_NEEDS_ECN;
}
rcu_read_unlock(); rcu_read_unlock();
return key; return key;
......
...@@ -6003,14 +6003,17 @@ static void tcp_ecn_create_request(struct request_sock *req, ...@@ -6003,14 +6003,17 @@ static void tcp_ecn_create_request(struct request_sock *req,
const struct net *net = sock_net(listen_sk); const struct net *net = sock_net(listen_sk);
bool th_ecn = th->ece && th->cwr; bool th_ecn = th->ece && th->cwr;
bool ect, ecn_ok; bool ect, ecn_ok;
u32 ecn_ok_dst;
if (!th_ecn) if (!th_ecn)
return; return;
ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield); ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield);
ecn_ok = net->ipv4.sysctl_tcp_ecn || dst_feature(dst, RTAX_FEATURE_ECN); ecn_ok_dst = dst_feature(dst, DST_FEATURE_ECN_MASK);
ecn_ok = net->ipv4.sysctl_tcp_ecn || ecn_ok_dst;
if ((!ect && ecn_ok) || tcp_ca_needs_ecn(listen_sk)) if ((!ect && ecn_ok) || tcp_ca_needs_ecn(listen_sk) ||
(ecn_ok_dst & DST_FEATURE_ECN_CA))
inet_rsk(req)->ecn_ok = 1; inet_rsk(req)->ecn_ok = 1;
} }
......
...@@ -1698,6 +1698,7 @@ static int ip6_dst_gc(struct dst_ops *ops) ...@@ -1698,6 +1698,7 @@ static int ip6_dst_gc(struct dst_ops *ops)
static int ip6_convert_metrics(struct mx6_config *mxc, static int ip6_convert_metrics(struct mx6_config *mxc,
const struct fib6_config *cfg) const struct fib6_config *cfg)
{ {
bool ecn_ca = false;
struct nlattr *nla; struct nlattr *nla;
int remaining; int remaining;
u32 *mp; u32 *mp;
...@@ -1722,7 +1723,7 @@ static int ip6_convert_metrics(struct mx6_config *mxc, ...@@ -1722,7 +1723,7 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
char tmp[TCP_CA_NAME_MAX]; char tmp[TCP_CA_NAME_MAX];
nla_strlcpy(tmp, nla, sizeof(tmp)); nla_strlcpy(tmp, nla, sizeof(tmp));
val = tcp_ca_get_key_by_name(tmp); val = tcp_ca_get_key_by_name(tmp, &ecn_ca);
if (val == TCP_CA_UNSPEC) if (val == TCP_CA_UNSPEC)
goto err; goto err;
} else { } else {
...@@ -1735,8 +1736,12 @@ static int ip6_convert_metrics(struct mx6_config *mxc, ...@@ -1735,8 +1736,12 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
__set_bit(type - 1, mxc->mx_valid); __set_bit(type - 1, mxc->mx_valid);
} }
mxc->mx = mp; if (ecn_ca) {
__set_bit(RTAX_FEATURES - 1, mxc->mx_valid);
mp[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA;
}
mxc->mx = mp;
return 0; return 0;
err: err:
kfree(mp); kfree(mp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment