提交 173a8024 编写于 作者: Y YangYuxi 提交者: Caspar Zhang

TencentOS-kernel: ipvs: avoid drop first packet by reusing conntrack

fix #29256237

commit a01a9445c00eca3e37523eb6b0d87f494eceeb4b TencentOS-kernel

Since 'commit f719e375 ("ipvs: drop first packet to
redirect conntrack")', when a new TCP connection meet
the conditions that need reschedule, the first syn packet
is dropped, this cause one second latency for the new
connection, more discussion about this problem can easy
search from google, such as:

1)One second connection delay in masque
https://marc.info/?t=151683118100004&r=1&w=2

2)IPVS low throughput #70747
https://github.com/kubernetes/kubernetes/issues/70747

3)Apache Bench can fill up ipvs service proxy in seconds #544
https://github.com/cloudnativelabs/kube-router/issues/544

4)Additional 1s latency in `host -> service IP -> pod`
https://github.com/kubernetes/kubernetes/issues/90854

5)kube-proxy ipvs conn_reuse_mode setting causes errors
with high load from single client
https://github.com/kubernetes/kubernetes/issues/81775

The root cause is when the old session is expired, the
conntrack related to the session is dropped by
ip_vs_conn_drop_conntrack. The code is as follows:
```
static void ip_vs_conn_expire(struct timer_list *t)
{
...

     if ((cp->flags & IP_VS_CONN_F_NFCT) &&
         !(cp->flags & IP_VS_CONN_F_ONE_PACKET)) {
             /* Do not access conntracks during subsys cleanup
              * because nf_conntrack_find_get can not be used after
              * conntrack cleanup for the net.
              */
             smp_rmb();
             if (ipvs->enable)
                     ip_vs_conn_drop_conntrack(cp);
     }
...
}
```
As shown in the code, only when condition (cp->flags & IP_VS_CONN_F_NFCT)
is true, the function ip_vs_conn_drop_conntrack will be called.

So we optimize this by following steps (Administrators
can choose the following optimization by setting
net.ipv4.vs.conn_reuse_old_conntrack=1):
1) erase the IP_VS_CONN_F_NFCT flag (it is safely because
   no packets will use the old session)
2) call ip_vs_conn_expire_now to release the old session,
   then the related conntrack will not be dropped
3) then ipvs unnecessary to drop the first syn packet, it
   just continue to pass the syn packet to the next process,
   create a new ipvs session, and the new session will related
   to the old conntrack(which is reopened by conntrack as a new
   one), the next whole things is just as normal as that the old
   session isn't used to exist.

The above processing has no problems except for passive FTP,
for passive FTP situation, ipvs can judging from
condition (atomic_read(&cp->n_control)) and condition (cp->control).
So, for other conditions(means not FTP), ipvs should give users
the right to choose,they can choose a high performance one processing
logical by setting net.ipv4.vs.conn_reuse_old_conntrack=1. It is necessary
because most business scenarios (such as kubernetes) are very sensitive
to TCP short connection latency.

This patch has been verified on our thousands of kubernets
node servers on Tencent Inc.
Signed-off-by: NYangYuxi <yx.atom1@gmail.com>
[Tony: add the missing sysctl knob and disable it by default]
Signed-off-by: NTony Lu <tonylu@linux.alibaba.com>
Acked-by: NDust Li <dust.li@linux.alibaba.com>
上级 26f3eab7
......@@ -43,6 +43,29 @@ conn_reuse_mode - INTEGER
balancer in Direct Routing mode. This bit helps on adding new
real servers to a very busy cluster.
conn_reuse_old_conntrack - BOOLEAN
- 0 - disabled (default)
- not 0 - enabled
If set, when a new TCP syn packet hits an old ipvs connection
table and need reschedule to a new dest: if
1) the packet uses conntrack
2) the old ipvs connection table is not a master control
connection (E.g. the command connection of passive FTP)
3) the old ipvs connection table hasn't been controlled by any
connection (E.g. the data connection of passive FTP)
ipvs will not release the old conntrack, just let the conntrack
reopen the old session as it is a new one. This is an optimization
option selectable by the system administrator.
If not set, when a new TCP syn packet hits an old ipvs connection
table and need reschedule to a new dest: if
1) the packet uses conntrack
ipvs just drops this syn packet, expires the old connection by timer.
This will cause the client to retransmit TCP SYN.
Only has effect when conn_reuse_mode not 0.
conntrack - BOOLEAN
0 - disabled (default)
not 0 - enabled
......
......@@ -924,6 +924,7 @@ struct netns_ipvs {
int sysctl_pmtu_disc;
int sysctl_backup_only;
int sysctl_conn_reuse_mode;
int sysctl_conn_reuse_old_conntrack;
int sysctl_schedule_icmp;
int sysctl_ignore_tunneled;
......@@ -1045,6 +1046,11 @@ static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs)
return ipvs->sysctl_conn_reuse_mode;
}
static inline int sysctl_conn_reuse_old_conntrack(struct netns_ipvs *ipvs)
{
return ipvs->sysctl_conn_reuse_old_conntrack;
}
static inline int sysctl_schedule_icmp(struct netns_ipvs *ipvs)
{
return ipvs->sysctl_schedule_icmp;
......@@ -1132,6 +1138,11 @@ static inline int sysctl_conn_reuse_mode(struct netns_ipvs *ipvs)
return 1;
}
static inline int sysctl_conn_reuse_old_conntrack(struct netns_ipvs *ipvs)
{
return 0;
}
static inline int sysctl_schedule_icmp(struct netns_ipvs *ipvs)
{
return 0;
......
......@@ -1928,7 +1928,7 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
conn_reuse_mode = sysctl_conn_reuse_mode(ipvs);
if (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) {
bool uses_ct = false, resched = false;
bool uses_ct = false, resched = false, drop = false;
if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest &&
unlikely(!atomic_read(&cp->dest->weight))) {
......@@ -1948,10 +1948,17 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
}
if (resched) {
if (uses_ct) {
if (likely(!atomic_read(&cp->n_control) && !cp->control) &&
likely(sysctl_conn_reuse_old_conntrack(ipvs)))
cp->flags &= ~IP_VS_CONN_F_NFCT;
else
drop = true;
}
if (!atomic_read(&cp->n_control))
ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp);
if (uses_ct)
if (drop)
return NF_DROP;
cp = NULL;
}
......
......@@ -1891,6 +1891,12 @@ static struct ctl_table vs_vars[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "conn_reuse_old_conntrack",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "schedule_icmp",
.maxlen = sizeof(int),
......@@ -3951,7 +3957,9 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
tbl[idx++].data = &ipvs->sysctl_pmtu_disc;
tbl[idx++].data = &ipvs->sysctl_backup_only;
ipvs->sysctl_conn_reuse_mode = 1;
ipvs->sysctl_conn_reuse_old_conntrack = 0;
tbl[idx++].data = &ipvs->sysctl_conn_reuse_mode;
tbl[idx++].data = &ipvs->sysctl_conn_reuse_old_conntrack;
tbl[idx++].data = &ipvs->sysctl_schedule_icmp;
tbl[idx++].data = &ipvs->sysctl_ignore_tunneled;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册