|
@@ -451,11 +451,94 @@ ip_vs_sync_buff_create_v0(struct netns_ipvs *ipvs)
|
|
|
return sb;
|
|
|
}
|
|
|
|
|
|
+/* Check if conn should be synced.
|
|
|
+ * pkts: conn packets, use sysctl_sync_threshold to avoid packet check
|
|
|
+ * - (1) sync_refresh_period: reduce sync rate. Additionally, retry
|
|
|
+ * sync_retries times with period of sync_refresh_period/8
|
|
|
+ * - (2) if both sync_refresh_period and sync_period are 0 send sync only
|
|
|
+ * for state changes or only once when pkts matches sync_threshold
|
|
|
+ * - (3) templates: rate can be reduced only with sync_refresh_period or
|
|
|
+ * with (2)
|
|
|
+ */
|
|
|
+static int ip_vs_sync_conn_needed(struct netns_ipvs *ipvs,
|
|
|
+ struct ip_vs_conn *cp, int pkts)
|
|
|
+{
|
|
|
+ unsigned long orig = ACCESS_ONCE(cp->sync_endtime);
|
|
|
+ unsigned long now = jiffies;
|
|
|
+ unsigned long n = (now + cp->timeout) & ~3UL;
|
|
|
+ unsigned int sync_refresh_period;
|
|
|
+ int sync_period;
|
|
|
+ int force;
|
|
|
+
|
|
|
+ /* Check if we sync in current state */
|
|
|
+ if (unlikely(cp->flags & IP_VS_CONN_F_TEMPLATE))
|
|
|
+ force = 0;
|
|
|
+ else if (likely(cp->protocol == IPPROTO_TCP)) {
|
|
|
+ if (!((1 << cp->state) &
|
|
|
+ ((1 << IP_VS_TCP_S_ESTABLISHED) |
|
|
|
+ (1 << IP_VS_TCP_S_FIN_WAIT) |
|
|
|
+ (1 << IP_VS_TCP_S_CLOSE) |
|
|
|
+ (1 << IP_VS_TCP_S_CLOSE_WAIT) |
|
|
|
+ (1 << IP_VS_TCP_S_TIME_WAIT))))
|
|
|
+ return 0;
|
|
|
+ force = cp->state != cp->old_state;
|
|
|
+ if (force && cp->state != IP_VS_TCP_S_ESTABLISHED)
|
|
|
+ goto set;
|
|
|
+ } else if (unlikely(cp->protocol == IPPROTO_SCTP)) {
|
|
|
+ if (!((1 << cp->state) &
|
|
|
+ ((1 << IP_VS_SCTP_S_ESTABLISHED) |
|
|
|
+ (1 << IP_VS_SCTP_S_CLOSED) |
|
|
|
+ (1 << IP_VS_SCTP_S_SHUT_ACK_CLI) |
|
|
|
+ (1 << IP_VS_SCTP_S_SHUT_ACK_SER))))
|
|
|
+ return 0;
|
|
|
+ force = cp->state != cp->old_state;
|
|
|
+ if (force && cp->state != IP_VS_SCTP_S_ESTABLISHED)
|
|
|
+ goto set;
|
|
|
+ } else {
|
|
|
+ /* UDP or another protocol with single state */
|
|
|
+ force = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ sync_refresh_period = sysctl_sync_refresh_period(ipvs);
|
|
|
+ if (sync_refresh_period > 0) {
|
|
|
+ long diff = n - orig;
|
|
|
+ long min_diff = max(cp->timeout >> 1, 10UL * HZ);
|
|
|
+
|
|
|
+ /* Avoid sync if difference is below sync_refresh_period
|
|
|
+ * and below the half timeout.
|
|
|
+ */
|
|
|
+ if (abs(diff) < min_t(long, sync_refresh_period, min_diff)) {
|
|
|
+ int retries = orig & 3;
|
|
|
+
|
|
|
+ if (retries >= sysctl_sync_retries(ipvs))
|
|
|
+ return 0;
|
|
|
+ if (time_before(now, orig - cp->timeout +
|
|
|
+ (sync_refresh_period >> 3)))
|
|
|
+ return 0;
|
|
|
+ n |= retries + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sync_period = sysctl_sync_period(ipvs);
|
|
|
+ if (sync_period > 0) {
|
|
|
+ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE) &&
|
|
|
+ pkts % sync_period != sysctl_sync_threshold(ipvs))
|
|
|
+ return 0;
|
|
|
+ } else if (sync_refresh_period <= 0 &&
|
|
|
+ pkts != sysctl_sync_threshold(ipvs))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+set:
|
|
|
+ cp->old_state = cp->state;
|
|
|
+ n = cmpxchg(&cp->sync_endtime, orig, n);
|
|
|
+ return n == orig || force;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Version 0 , could be switched in by sys_ctl.
|
|
|
* Add an ip_vs_conn information into the current sync_buff.
|
|
|
*/
|
|
|
-void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp)
|
|
|
+static void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp,
|
|
|
+ int pkts)
|
|
|
{
|
|
|
struct netns_ipvs *ipvs = net_ipvs(net);
|
|
|
struct ip_vs_sync_mesg_v0 *m;
|
|
@@ -468,6 +551,9 @@ void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp)
|
|
|
if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
|
|
|
return;
|
|
|
|
|
|
+ if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
|
|
|
+ return;
|
|
|
+
|
|
|
spin_lock(&ipvs->sync_buff_lock);
|
|
|
if (!ipvs->sync_buff) {
|
|
|
ipvs->sync_buff =
|
|
@@ -513,8 +599,14 @@ void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp)
|
|
|
spin_unlock(&ipvs->sync_buff_lock);
|
|
|
|
|
|
/* synchronize its controller if it has */
|
|
|
- if (cp->control)
|
|
|
- ip_vs_sync_conn(net, cp->control);
|
|
|
+ cp = cp->control;
|
|
|
+ if (cp) {
|
|
|
+ if (cp->flags & IP_VS_CONN_F_TEMPLATE)
|
|
|
+ pkts = atomic_add_return(1, &cp->in_pkts);
|
|
|
+ else
|
|
|
+ pkts = sysctl_sync_threshold(ipvs);
|
|
|
+ ip_vs_sync_conn(net, cp->control, pkts);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -522,7 +614,7 @@ void ip_vs_sync_conn_v0(struct net *net, struct ip_vs_conn *cp)
|
|
|
* Called by ip_vs_in.
|
|
|
* Sending Version 1 messages
|
|
|
*/
|
|
|
-void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp)
|
|
|
+void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts)
|
|
|
{
|
|
|
struct netns_ipvs *ipvs = net_ipvs(net);
|
|
|
struct ip_vs_sync_mesg *m;
|
|
@@ -532,13 +624,16 @@ void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp)
|
|
|
|
|
|
/* Handle old version of the protocol */
|
|
|
if (sysctl_sync_ver(ipvs) == 0) {
|
|
|
- ip_vs_sync_conn_v0(net, cp);
|
|
|
+ ip_vs_sync_conn_v0(net, cp, pkts);
|
|
|
return;
|
|
|
}
|
|
|
/* Do not sync ONE PACKET */
|
|
|
if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
|
|
|
goto control;
|
|
|
sloop:
|
|
|
+ if (!ip_vs_sync_conn_needed(ipvs, cp, pkts))
|
|
|
+ goto control;
|
|
|
+
|
|
|
/* Sanity checks */
|
|
|
pe_name_len = 0;
|
|
|
if (cp->pe_data_len) {
|
|
@@ -653,16 +748,10 @@ control:
|
|
|
cp = cp->control;
|
|
|
if (!cp)
|
|
|
return;
|
|
|
- /*
|
|
|
- * Reduce sync rate for templates
|
|
|
- * i.e only increment in_pkts for Templates.
|
|
|
- */
|
|
|
- if (cp->flags & IP_VS_CONN_F_TEMPLATE) {
|
|
|
- int pkts = atomic_add_return(1, &cp->in_pkts);
|
|
|
-
|
|
|
- if (pkts % sysctl_sync_period(ipvs) != 1)
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (cp->flags & IP_VS_CONN_F_TEMPLATE)
|
|
|
+ pkts = atomic_add_return(1, &cp->in_pkts);
|
|
|
+ else
|
|
|
+ pkts = sysctl_sync_threshold(ipvs);
|
|
|
goto sloop;
|
|
|
}
|
|
|
|
|
@@ -1494,7 +1583,7 @@ next_sync_buff(struct netns_ipvs *ipvs)
|
|
|
if (sb)
|
|
|
return sb;
|
|
|
/* Do not delay entries in buffer for more than 2 seconds */
|
|
|
- return get_curr_sync_buff(ipvs, 2 * HZ);
|
|
|
+ return get_curr_sync_buff(ipvs, IPVS_SYNC_FLUSH_TIME);
|
|
|
}
|
|
|
|
|
|
static int sync_thread_master(void *data)
|