2005-06-24 03:19:55 +08:00
|
|
|
/*
|
2014-11-05 03:25:38 +08:00
|
|
|
* Pluggable TCP congestion control support and newReno
|
2005-06-24 03:19:55 +08:00
|
|
|
* congestion control.
|
2012-08-22 18:11:26 +08:00
|
|
|
* Based on ideas from I/O scheduler support and Web100.
|
2005-06-24 03:19:55 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Stephen Hemminger <shemminger@osdl.org>
|
|
|
|
*/
|
|
|
|
|
2012-03-12 15:03:32 +08:00
|
|
|
#define pr_fmt(fmt) "TCP: " fmt
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/list.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/gfp.h>
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
#include <linux/jhash.h>
|
2005-06-24 03:19:55 +08:00
|
|
|
#include <net/tcp.h>
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(tcp_cong_list_lock);
|
|
|
|
static LIST_HEAD(tcp_cong_list);
|
|
|
|
|
|
|
|
/* Simple linear search, don't expect many entries! */
|
|
|
|
static struct tcp_congestion_ops *tcp_ca_find(const char *name)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *e;
|
|
|
|
|
2005-06-24 11:37:36 +08:00
|
|
|
list_for_each_entry_rcu(e, &tcp_cong_list, list) {
|
2005-06-24 03:19:55 +08:00
|
|
|
if (strcmp(e->name, name) == 0)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
/* Must be called with rcu lock held */
|
|
|
|
static const struct tcp_congestion_ops *__tcp_ca_find_autoload(const char *name)
|
|
|
|
{
|
|
|
|
const struct tcp_congestion_ops *ca = tcp_ca_find(name);
|
|
|
|
#ifdef CONFIG_MODULES
|
|
|
|
if (!ca && capable(CAP_NET_ADMIN)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
request_module("tcp_%s", name);
|
|
|
|
rcu_read_lock();
|
|
|
|
ca = tcp_ca_find(name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return ca;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Simple linear search, not much in here. */
|
|
|
|
struct tcp_congestion_ops *tcp_ca_find_key(u32 key)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *e;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(e, &tcp_cong_list, list) {
|
|
|
|
if (e->key == key)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
/*
|
2007-02-18 02:07:33 +08:00
|
|
|
* Attach new congestion control algorithm to the list
|
2005-06-24 03:19:55 +08:00
|
|
|
* of available options.
|
|
|
|
*/
|
|
|
|
int tcp_register_congestion_control(struct tcp_congestion_ops *ca)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2016-11-21 21:18:38 +08:00
|
|
|
/* all algorithms must implement these */
|
|
|
|
if (!ca->ssthresh || !ca->undo_cwnd ||
|
|
|
|
!(ca->cong_avoid || ca->cong_control)) {
|
2012-03-12 15:03:32 +08:00
|
|
|
pr_err("%s does not implement required ops\n", ca->name);
|
2005-06-24 03:19:55 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
ca->key = jhash(ca->name, sizeof(ca->name), strlen(ca->name));
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
spin_lock(&tcp_cong_list_lock);
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
if (ca->key == TCP_CA_UNSPEC || tcp_ca_find_key(ca->key)) {
|
|
|
|
pr_notice("%s already registered or non-unique key\n",
|
|
|
|
ca->name);
|
2005-06-24 03:19:55 +08:00
|
|
|
ret = -EEXIST;
|
|
|
|
} else {
|
2006-09-25 11:11:58 +08:00
|
|
|
list_add_tail_rcu(&ca->list, &tcp_cong_list);
|
2015-02-16 22:38:13 +08:00
|
|
|
pr_debug("%s registered\n", ca->name);
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
|
|
|
spin_unlock(&tcp_cong_list_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_register_congestion_control);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove congestion control algorithm, called from
|
|
|
|
* the module's remove function. Module ref counts are used
|
|
|
|
* to ensure that this can't be done till all sockets using
|
|
|
|
* that method are closed.
|
|
|
|
*/
|
|
|
|
void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca)
|
|
|
|
{
|
|
|
|
spin_lock(&tcp_cong_list_lock);
|
|
|
|
list_del_rcu(&ca->list);
|
|
|
|
spin_unlock(&tcp_cong_list_lock);
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
|
|
|
|
/* Wait for outstanding readers to complete before the
|
|
|
|
* module gets removed entirely.
|
|
|
|
*
|
|
|
|
* A try_module_get() should fail by now as our module is
|
|
|
|
* in "going" state since no refs are held anymore and
|
|
|
|
* module_exit() handler being called.
|
|
|
|
*/
|
|
|
|
synchronize_rcu();
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control);
|
|
|
|
|
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: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-08-31 21:58:47 +08:00
|
|
|
u32 tcp_ca_get_key_by_name(const char *name, bool *ecn_ca)
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
{
|
|
|
|
const struct tcp_congestion_ops *ca;
|
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: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-08-31 21:58:47 +08:00
|
|
|
u32 key = TCP_CA_UNSPEC;
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ca = __tcp_ca_find_autoload(name);
|
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: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-08-31 21:58:47 +08:00
|
|
|
if (ca) {
|
|
|
|
key = ca->key;
|
|
|
|
*ecn_ca = ca->flags & TCP_CONG_NEEDS_ECN;
|
|
|
|
}
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_ca_get_key_by_name);
|
|
|
|
|
|
|
|
char *tcp_ca_get_name_by_key(u32 key, char *buffer)
|
|
|
|
{
|
|
|
|
const struct tcp_congestion_ops *ca;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ca = tcp_ca_find_key(key);
|
|
|
|
if (ca)
|
|
|
|
ret = strncpy(buffer, ca->name,
|
|
|
|
TCP_CA_NAME_MAX);
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_ca_get_name_by_key);
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
/* Assign choice of congestion control. */
|
2014-09-27 04:37:32 +08:00
|
|
|
void tcp_assign_congestion_control(struct sock *sk)
|
2005-06-24 03:19:55 +08:00
|
|
|
{
|
2005-08-10 15:03:31 +08:00
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
2005-06-24 03:19:55 +08:00
|
|
|
struct tcp_congestion_ops *ca;
|
|
|
|
|
2014-09-27 04:37:32 +08:00
|
|
|
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;
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
2014-09-27 04:37:32 +08:00
|
|
|
/* Fallback to next available. The last really
|
|
|
|
* guaranteed fallback is Reno from this list.
|
|
|
|
*/
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
2014-09-27 04:37:32 +08:00
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
2017-04-26 08:38:02 +08:00
|
|
|
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
|
2014-09-27 04:37:32 +08:00
|
|
|
|
2015-09-25 22:39:18 +08:00
|
|
|
if (ca->flags & TCP_CONG_NEEDS_ECN)
|
|
|
|
INET_ECN_xmit(sk);
|
|
|
|
else
|
|
|
|
INET_ECN_dontxmit(sk);
|
2014-09-27 04:37:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void tcp_init_congestion_control(struct sock *sk)
|
|
|
|
{
|
|
|
|
const struct inet_connection_sock *icsk = inet_csk(sk);
|
2005-06-24 03:19:55 +08:00
|
|
|
|
2017-06-01 02:21:27 +08:00
|
|
|
tcp_sk(sk)->prior_ssthresh = 0;
|
2005-08-10 15:03:31 +08:00
|
|
|
if (icsk->icsk_ca_ops->init)
|
|
|
|
icsk->icsk_ca_ops->init(sk);
|
2015-09-25 22:39:18 +08:00
|
|
|
if (tcp_ca_needs_ecn(sk))
|
|
|
|
INET_ECN_xmit(sk);
|
|
|
|
else
|
|
|
|
INET_ECN_dontxmit(sk);
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
|
|
|
|
2017-08-25 19:10:12 +08:00
|
|
|
static void tcp_reinit_congestion_control(struct sock *sk,
|
|
|
|
const struct tcp_congestion_ops *ca)
|
2015-01-06 06:57:45 +08:00
|
|
|
{
|
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
|
|
|
|
|
|
|
tcp_cleanup_congestion_control(sk);
|
|
|
|
icsk->icsk_ca_ops = ca;
|
2015-05-30 01:47:07 +08:00
|
|
|
icsk->icsk_ca_setsockopt = 1;
|
2017-04-26 08:38:02 +08:00
|
|
|
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
|
2015-01-06 06:57:45 +08:00
|
|
|
|
2017-04-26 08:38:02 +08:00
|
|
|
if (sk->sk_state != TCP_CLOSE)
|
2015-09-25 22:39:18 +08:00
|
|
|
tcp_init_congestion_control(sk);
|
2015-01-06 06:57:45 +08:00
|
|
|
}
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
/* Manage refcounts on socket close. */
|
2005-08-10 15:03:31 +08:00
|
|
|
void tcp_cleanup_congestion_control(struct sock *sk)
|
2005-06-24 03:19:55 +08:00
|
|
|
{
|
2005-08-10 15:03:31 +08:00
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
|
|
|
|
|
|
|
if (icsk->icsk_ca_ops->release)
|
|
|
|
icsk->icsk_ca_ops->release(sk);
|
|
|
|
module_put(icsk->icsk_ca_ops->owner);
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Used by sysctl to change default congestion control */
|
|
|
|
int tcp_set_default_congestion_control(const char *name)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *ca;
|
|
|
|
int ret = -ENOENT;
|
|
|
|
|
|
|
|
spin_lock(&tcp_cong_list_lock);
|
|
|
|
ca = tcp_ca_find(name);
|
2008-10-17 06:24:51 +08:00
|
|
|
#ifdef CONFIG_MODULES
|
2009-08-13 21:44:51 +08:00
|
|
|
if (!ca && capable(CAP_NET_ADMIN)) {
|
2005-06-24 03:19:55 +08:00
|
|
|
spin_unlock(&tcp_cong_list_lock);
|
|
|
|
|
|
|
|
request_module("tcp_%s", name);
|
|
|
|
spin_lock(&tcp_cong_list_lock);
|
|
|
|
ca = tcp_ca_find(name);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ca) {
|
2007-04-24 13:26:16 +08:00
|
|
|
ca->flags |= TCP_CONG_NON_RESTRICTED; /* default is always allowed */
|
2005-06-24 03:19:55 +08:00
|
|
|
list_move(&ca->list, &tcp_cong_list);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
spin_unlock(&tcp_cong_list_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2006-11-01 09:31:33 +08:00
|
|
|
/* Set default value from kernel configuration at bootup */
|
|
|
|
static int __init tcp_congestion_default(void)
|
|
|
|
{
|
|
|
|
return tcp_set_default_congestion_control(CONFIG_DEFAULT_TCP_CONG);
|
|
|
|
}
|
|
|
|
late_initcall(tcp_congestion_default);
|
|
|
|
|
2006-11-10 08:32:06 +08:00
|
|
|
/* Build string with list of available congestion control values */
|
|
|
|
void tcp_get_available_congestion_control(char *buf, size_t maxlen)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *ca;
|
|
|
|
size_t offs = 0;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
|
|
|
|
offs += snprintf(buf + offs, maxlen - offs,
|
|
|
|
"%s%s",
|
|
|
|
offs == 0 ? "" : " ", ca->name);
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
/* Get current default congestion control */
|
|
|
|
void tcp_get_default_congestion_control(char *name)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *ca;
|
|
|
|
/* We will always have reno... */
|
|
|
|
BUG_ON(list_empty(&tcp_cong_list));
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ca = list_entry(tcp_cong_list.next, struct tcp_congestion_ops, list);
|
|
|
|
strncpy(name, ca->name, TCP_CA_NAME_MAX);
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2006-11-10 08:35:15 +08:00
|
|
|
/* Built list of non-restricted congestion control values */
|
|
|
|
void tcp_get_allowed_congestion_control(char *buf, size_t maxlen)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *ca;
|
|
|
|
size_t offs = 0;
|
|
|
|
|
|
|
|
*buf = '\0';
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
|
2007-04-24 13:26:16 +08:00
|
|
|
if (!(ca->flags & TCP_CONG_NON_RESTRICTED))
|
2006-11-10 08:35:15 +08:00
|
|
|
continue;
|
|
|
|
offs += snprintf(buf + offs, maxlen - offs,
|
|
|
|
"%s%s",
|
|
|
|
offs == 0 ? "" : " ", ca->name);
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Change list of non-restricted congestion control */
|
|
|
|
int tcp_set_allowed_congestion_control(char *val)
|
|
|
|
{
|
|
|
|
struct tcp_congestion_ops *ca;
|
2010-08-28 10:31:56 +08:00
|
|
|
char *saved_clone, *clone, *name;
|
2006-11-10 08:35:15 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2010-08-28 10:31:56 +08:00
|
|
|
saved_clone = clone = kstrdup(val, GFP_USER);
|
2006-11-10 08:35:15 +08:00
|
|
|
if (!clone)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock(&tcp_cong_list_lock);
|
|
|
|
/* pass 1 check for bad entries */
|
|
|
|
while ((name = strsep(&clone, " ")) && *name) {
|
|
|
|
ca = tcp_ca_find(name);
|
|
|
|
if (!ca) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-24 13:26:16 +08:00
|
|
|
/* pass 2 clear old values */
|
2006-11-10 08:35:15 +08:00
|
|
|
list_for_each_entry_rcu(ca, &tcp_cong_list, list)
|
2007-04-24 13:26:16 +08:00
|
|
|
ca->flags &= ~TCP_CONG_NON_RESTRICTED;
|
2006-11-10 08:35:15 +08:00
|
|
|
|
|
|
|
/* pass 3 mark as allowed */
|
|
|
|
while ((name = strsep(&val, " ")) && *name) {
|
|
|
|
ca = tcp_ca_find(name);
|
|
|
|
WARN_ON(!ca);
|
|
|
|
if (ca)
|
2007-04-24 13:26:16 +08:00
|
|
|
ca->flags |= TCP_CONG_NON_RESTRICTED;
|
2006-11-10 08:35:15 +08:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
spin_unlock(&tcp_cong_list_lock);
|
2010-08-28 10:31:56 +08:00
|
|
|
kfree(saved_clone);
|
2006-11-10 08:35:15 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-01 11:02:49 +08:00
|
|
|
/* Change congestion control for socket. If load is false, then it is the
|
|
|
|
* responsibility of the caller to call tcp_init_congestion_control or
|
|
|
|
* tcp_reinit_congestion_control (if the current congestion control was
|
|
|
|
* already initialized.
|
|
|
|
*/
|
2017-08-25 19:10:12 +08:00
|
|
|
int tcp_set_congestion_control(struct sock *sk, const char *name, bool load, bool reinit)
|
2005-06-24 11:37:36 +08:00
|
|
|
{
|
2005-08-10 15:03:31 +08:00
|
|
|
struct inet_connection_sock *icsk = inet_csk(sk);
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
const struct tcp_congestion_ops *ca;
|
2005-06-24 11:37:36 +08:00
|
|
|
int err = 0;
|
|
|
|
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
if (icsk->icsk_ca_dst_locked)
|
|
|
|
return -EPERM;
|
2007-04-24 13:32:11 +08:00
|
|
|
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
rcu_read_lock();
|
2017-07-01 11:02:49 +08:00
|
|
|
if (!load)
|
|
|
|
ca = tcp_ca_find(name);
|
|
|
|
else
|
|
|
|
ca = __tcp_ca_find_autoload(name);
|
net: tcp: add key management to congestion control
This patch adds necessary infrastructure to the congestion control
framework for later per route congestion control support.
For a per route congestion control possibility, our aim is to store
a unique u32 key identifier into dst metrics, which can then be
mapped into a tcp_congestion_ops struct. We argue that having a
RTAX key entry is the most simple, generic and easy way to manage,
and also keeps the memory footprint of dst entries lower on 64 bit
than with storing a pointer directly, for example. Having a unique
key id also allows for decoupling actual TCP congestion control
module management from the FIB layer, i.e. we don't have to care
about expensive module refcounting inside the FIB at this point.
We first thought of using an IDR store for the realization, which
takes over dynamic assignment of unused key space and also performs
the key to pointer mapping in RCU. While doing so, we stumbled upon
the issue that due to the nature of dynamic key distribution, it
just so happens, arguably in very rare occasions, that excessive
module loads and unloads can lead to a possible reuse of previously
used key space. Thus, previously stale keys in the dst metric are
now being reassigned to a different congestion control algorithm,
which might lead to unexpected behaviour. One way to resolve this
would have been to walk FIBs on the actually rare occasion of a
module unload and reset the metric keys for each FIB in each netns,
but that's just very costly.
Therefore, we argue a better solution is to reuse the unique
congestion control algorithm name member and map that into u32 key
space through jhash. For that, we split the flags attribute (as it
currently uses 2 bits only anyway) into two u32 attributes, flags
and key, so that we can keep the cacheline boundary of 2 cachelines
on x86_64 and cache the precalculated key at registration time for
the fast path. On average we might expect 2 - 4 modules being loaded
worst case perhaps 15, so a key collision possibility is extremely
low, and guaranteed collision-free on LE/BE for all in-tree modules.
Overall this results in much simpler code, and all without the
overhead of an IDR. Due to the deterministic nature, modules can
now be unloaded, the congestion control algorithm for a specific
but unloaded key will fall back to the default one, and on module
reload time it will switch back to the expected algorithm
transparently.
Joint work with Florian Westphal.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-01-06 06:57:46 +08:00
|
|
|
/* No change asking for existing value */
|
2015-05-30 01:47:07 +08:00
|
|
|
if (ca == icsk->icsk_ca_ops) {
|
|
|
|
icsk->icsk_ca_setsockopt = 1;
|
2005-06-24 11:37:36 +08:00
|
|
|
goto out;
|
2015-05-30 01:47:07 +08:00
|
|
|
}
|
2017-07-01 11:02:49 +08:00
|
|
|
if (!ca) {
|
2005-06-24 11:37:36 +08:00
|
|
|
err = -ENOENT;
|
2017-07-01 11:02:49 +08:00
|
|
|
} else if (!load) {
|
2017-08-25 19:10:12 +08:00
|
|
|
const struct tcp_congestion_ops *old_ca = icsk->icsk_ca_ops;
|
|
|
|
|
|
|
|
if (try_module_get(ca->owner)) {
|
|
|
|
if (reinit) {
|
|
|
|
tcp_reinit_congestion_control(sk, ca);
|
|
|
|
} else {
|
|
|
|
icsk->icsk_ca_ops = ca;
|
|
|
|
module_put(old_ca->owner);
|
|
|
|
}
|
|
|
|
} else {
|
2017-07-01 11:02:49 +08:00
|
|
|
err = -EBUSY;
|
2017-08-25 19:10:12 +08:00
|
|
|
}
|
2017-07-01 11:02:49 +08:00
|
|
|
} else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) ||
|
|
|
|
ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))) {
|
2006-11-10 08:35:15 +08:00
|
|
|
err = -EPERM;
|
2017-07-01 11:02:49 +08:00
|
|
|
} else if (!try_module_get(ca->owner)) {
|
2005-06-24 11:37:36 +08:00
|
|
|
err = -EBUSY;
|
2017-07-01 11:02:49 +08:00
|
|
|
} else {
|
2015-01-06 06:57:45 +08:00
|
|
|
tcp_reinit_congestion_control(sk, ca);
|
2017-07-01 11:02:49 +08:00
|
|
|
}
|
2005-06-24 11:37:36 +08:00
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-11-01 02:07:31 +08:00
|
|
|
/* Slow start is used when congestion window is no greater than the slow start
|
|
|
|
* threshold. We base on RFC2581 and also handle stretch ACKs properly.
|
|
|
|
* We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but
|
|
|
|
* something better;) a packet is only considered (s)acked in its entirety to
|
|
|
|
* defend the ACK attacks described in the RFC. Slow start processes a stretch
|
|
|
|
* ACK of degree N as if N acks of degree 1 are received back to back except
|
|
|
|
* ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and
|
|
|
|
* returns the leftover acks to adjust cwnd in congestion avoidance mode.
|
2006-01-04 08:03:49 +08:00
|
|
|
*/
|
2015-01-29 09:01:35 +08:00
|
|
|
u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)
|
2006-01-04 08:03:49 +08:00
|
|
|
{
|
2015-07-10 04:16:30 +08:00
|
|
|
u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh);
|
2007-05-17 15:04:18 +08:00
|
|
|
|
2015-01-29 09:01:35 +08:00
|
|
|
acked -= cwnd - tp->snd_cwnd;
|
2013-11-01 02:07:31 +08:00
|
|
|
tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);
|
2015-01-29 09:01:35 +08:00
|
|
|
|
|
|
|
return acked;
|
2006-01-04 08:03:49 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_slow_start);
|
|
|
|
|
2015-01-29 09:01:36 +08:00
|
|
|
/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd (or alternative w),
|
|
|
|
* for every packet that was ACKed.
|
|
|
|
*/
|
2015-01-29 09:01:35 +08:00
|
|
|
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked)
|
2009-02-28 12:44:37 +08:00
|
|
|
{
|
2015-03-11 05:17:03 +08:00
|
|
|
/* If credits accumulated at a higher w, apply them gently now. */
|
|
|
|
if (tp->snd_cwnd_cnt >= w) {
|
|
|
|
tp->snd_cwnd_cnt = 0;
|
|
|
|
tp->snd_cwnd++;
|
|
|
|
}
|
|
|
|
|
2015-01-29 09:01:36 +08:00
|
|
|
tp->snd_cwnd_cnt += acked;
|
2009-02-28 12:44:37 +08:00
|
|
|
if (tp->snd_cwnd_cnt >= w) {
|
2015-01-29 09:01:36 +08:00
|
|
|
u32 delta = tp->snd_cwnd_cnt / w;
|
|
|
|
|
|
|
|
tp->snd_cwnd_cnt -= delta * w;
|
|
|
|
tp->snd_cwnd += delta;
|
2009-02-28 12:44:37 +08:00
|
|
|
}
|
2015-01-29 09:01:36 +08:00
|
|
|
tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_cwnd_clamp);
|
2009-02-28 12:44:37 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai);
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
/*
|
|
|
|
* TCP Reno congestion control
|
|
|
|
* This is special case used for fallback as well.
|
|
|
|
*/
|
|
|
|
/* This is Jacobson's slow start and congestion avoidance.
|
|
|
|
* SIGCOMM '88, p. 328.
|
|
|
|
*/
|
2014-05-03 12:18:05 +08:00
|
|
|
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
|
2005-06-24 03:19:55 +08:00
|
|
|
{
|
2005-08-10 15:03:31 +08:00
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
|
2014-05-03 12:18:05 +08:00
|
|
|
if (!tcp_is_cwnd_limited(sk))
|
2005-06-24 03:19:55 +08:00
|
|
|
return;
|
|
|
|
|
2005-11-11 09:07:24 +08:00
|
|
|
/* In "safe" area, increase. */
|
2015-07-10 04:16:29 +08:00
|
|
|
if (tcp_in_slow_start(tp)) {
|
2015-01-29 09:01:37 +08:00
|
|
|
acked = tcp_slow_start(tp, acked);
|
|
|
|
if (!acked)
|
|
|
|
return;
|
|
|
|
}
|
2007-02-09 22:24:47 +08:00
|
|
|
/* In dangerous area, increase slowly. */
|
2015-01-29 09:01:37 +08:00
|
|
|
tcp_cong_avoid_ai(tp, tp->snd_cwnd, acked);
|
2005-06-24 03:19:55 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid);
|
|
|
|
|
|
|
|
/* Slow start threshold is half the congestion window (min 2) */
|
2005-08-10 15:03:31 +08:00
|
|
|
u32 tcp_reno_ssthresh(struct sock *sk)
|
2005-06-24 03:19:55 +08:00
|
|
|
{
|
2005-08-10 15:03:31 +08:00
|
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
2014-08-30 14:32:05 +08:00
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
return max(tp->snd_cwnd >> 1U, 2U);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_reno_ssthresh);
|
|
|
|
|
2016-11-21 21:18:38 +08:00
|
|
|
u32 tcp_reno_undo_cwnd(struct sock *sk)
|
|
|
|
{
|
|
|
|
const struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
|
2017-08-04 11:38:51 +08:00
|
|
|
return max(tp->snd_cwnd, tp->prior_cwnd);
|
2016-11-21 21:18:38 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tcp_reno_undo_cwnd);
|
|
|
|
|
2005-06-24 03:19:55 +08:00
|
|
|
struct tcp_congestion_ops tcp_reno = {
|
2007-04-24 13:26:16 +08:00
|
|
|
.flags = TCP_CONG_NON_RESTRICTED,
|
2005-06-24 03:19:55 +08:00
|
|
|
.name = "reno",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.ssthresh = tcp_reno_ssthresh,
|
|
|
|
.cong_avoid = tcp_reno_cong_avoid,
|
2016-11-21 21:18:38 +08:00
|
|
|
.undo_cwnd = tcp_reno_undo_cwnd,
|
2005-06-24 03:19:55 +08:00
|
|
|
};
|