mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 11:03:07 -04:00
ipvs: properly update the overload flag on dest edit
The upper/lower connection thresholds for dest can be changed,
so use ip_vs_dest_update_overload() to properly update the
dest overload flag.
The thresholds were not limited, fit them in the 0 .. INT_MAX
range as already done in ipvsadm.
As the thresholds are also read when connections are created
and expired, use WRITE_ONCE/READ_ONCE to access them.
As the lower threshold is optional, use (u - (u >> 2)) to
calculate the 75% default value based on the upper threshold
by preserving the integer rounding, as suggested by Yizhou Zhao.
Trigger flag update when totalconns reaches one of the
thresholds and use dst_lock to serialize the updating.
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
committed by
Pablo Neira Ayuso
parent
04d2feaed8
commit
8f843441c4
@@ -991,6 +991,7 @@ struct ip_vs_dest {
|
||||
atomic_t persistconns; /* persistent connections */
|
||||
__u32 u_threshold; /* upper threshold */
|
||||
__u32 l_threshold; /* lower threshold */
|
||||
__u32 l_threshold_val;/* used lower threshold */
|
||||
|
||||
/* for destination cache */
|
||||
spinlock_t dst_lock; /* lock of dst_cache */
|
||||
@@ -1907,6 +1908,8 @@ static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest)
|
||||
kfree(dest);
|
||||
}
|
||||
|
||||
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode);
|
||||
|
||||
/* IPVS sync daemon data and function prototypes
|
||||
* (from ip_vs_sync.c)
|
||||
*/
|
||||
|
||||
@@ -1141,22 +1141,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
|
||||
|
||||
/* Update the connection counters */
|
||||
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
|
||||
int tc;
|
||||
|
||||
/* It is a normal connection, so modify the counters
|
||||
* according to the flags, later the protocol can
|
||||
* update them on state change
|
||||
*/
|
||||
if (!(flags & IP_VS_CONN_F_INACTIVE))
|
||||
atomic_inc(&dest->activeconns);
|
||||
atomic_inc(&dest->totalconns);
|
||||
tc = atomic_inc_return(&dest->totalconns);
|
||||
if (tc == READ_ONCE(dest->u_threshold))
|
||||
ip_vs_dest_update_overload(dest, 1);
|
||||
} else {
|
||||
/* It is a persistent connection/template, so increase
|
||||
the persistent connection counter */
|
||||
atomic_inc(&dest->persistconns);
|
||||
}
|
||||
|
||||
if (dest->u_threshold != 0 &&
|
||||
atomic_read(&dest->totalconns) >= dest->u_threshold)
|
||||
dest->flags |= IP_VS_DEST_F_OVERLOAD;
|
||||
}
|
||||
|
||||
|
||||
@@ -1237,27 +1237,20 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
|
||||
|
||||
/* Update the connection counters */
|
||||
if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
|
||||
int tc;
|
||||
|
||||
/* It is a normal connection, so decrease the counters */
|
||||
if (!(cp->flags & IP_VS_CONN_F_INACTIVE))
|
||||
atomic_dec(&dest->activeconns);
|
||||
atomic_dec(&dest->totalconns);
|
||||
tc = atomic_fetch_dec(&dest->totalconns);
|
||||
if (tc == READ_ONCE(dest->l_threshold_val))
|
||||
ip_vs_dest_update_overload(dest, -1);
|
||||
} else {
|
||||
/* It is a persistent connection/template, so decrease
|
||||
the persistent connection counter */
|
||||
atomic_dec(&dest->persistconns);
|
||||
}
|
||||
|
||||
if (dest->l_threshold != 0) {
|
||||
if (atomic_read(&dest->totalconns) < dest->l_threshold)
|
||||
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
|
||||
} else if (dest->u_threshold != 0) {
|
||||
if (atomic_read(&dest->totalconns) * 4 < dest->u_threshold * 3)
|
||||
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
|
||||
} else {
|
||||
if (dest->flags & IP_VS_DEST_F_OVERLOAD)
|
||||
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
|
||||
}
|
||||
|
||||
ip_vs_dest_put(dest);
|
||||
}
|
||||
|
||||
|
||||
@@ -1304,6 +1304,40 @@ void ip_vs_stats_free(struct ip_vs_stats *stats)
|
||||
}
|
||||
}
|
||||
|
||||
/* Update overload flag based on number of dest conns and lower/upper
|
||||
* connection thresholds:
|
||||
* - conns reach u_threshold and exceed it: set the flag
|
||||
* - conns go below l_threshold (or 75% of u_threshold): clear the flag
|
||||
*/
|
||||
static void __ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
|
||||
{
|
||||
int conns;
|
||||
u32 l, u;
|
||||
|
||||
lockdep_assert_held(&dest->dst_lock);
|
||||
u = READ_ONCE(dest->u_threshold);
|
||||
if (!u)
|
||||
goto unset;
|
||||
l = READ_ONCE(dest->l_threshold_val);
|
||||
conns = atomic_read(&dest->totalconns);
|
||||
if (conns >= (mode > 0 ? l : u)) {
|
||||
dest->flags |= IP_VS_DEST_F_OVERLOAD;
|
||||
return;
|
||||
}
|
||||
if (conns >= (mode < 0 ? u : l))
|
||||
return;
|
||||
|
||||
unset:
|
||||
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
|
||||
}
|
||||
|
||||
void ip_vs_dest_update_overload(struct ip_vs_dest *dest, int mode)
|
||||
{
|
||||
spin_lock_bh(&dest->dst_lock);
|
||||
__ip_vs_dest_update_overload(dest, mode);
|
||||
spin_unlock_bh(&dest->dst_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a destination in the given service
|
||||
*/
|
||||
@@ -1370,10 +1404,19 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
|
||||
/* set the dest status flags */
|
||||
dest->flags |= IP_VS_DEST_F_AVAILABLE;
|
||||
|
||||
if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
|
||||
dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
|
||||
dest->u_threshold = udest->u_threshold;
|
||||
dest->l_threshold = udest->l_threshold;
|
||||
if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
|
||||
READ_ONCE(dest->l_threshold) != udest->l_threshold) {
|
||||
spin_lock_bh(&dest->dst_lock);
|
||||
WRITE_ONCE(dest->u_threshold, udest->u_threshold);
|
||||
WRITE_ONCE(dest->l_threshold, udest->l_threshold);
|
||||
/* Low threshold defaults to 75% of upper threshold */
|
||||
WRITE_ONCE(dest->l_threshold_val,
|
||||
udest->l_threshold ? :
|
||||
(udest->u_threshold -
|
||||
(udest->u_threshold >> 2)));
|
||||
__ip_vs_dest_update_overload(dest, 0);
|
||||
spin_unlock_bh(&dest->dst_lock);
|
||||
}
|
||||
|
||||
dest->af = udest->af;
|
||||
|
||||
@@ -1486,6 +1529,9 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (udest->u_threshold > INT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
|
||||
if (udest->tun_port == 0) {
|
||||
pr_err("%s(): tunnel port is zero\n", __func__);
|
||||
@@ -1559,6 +1605,9 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (udest->u_threshold > INT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (udest->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
|
||||
if (udest->tun_port == 0) {
|
||||
pr_err("%s(): tunnel port is zero\n", __func__);
|
||||
@@ -3667,8 +3716,8 @@ __ip_vs_get_dest_entries(struct netns_ipvs *ipvs, const struct ip_vs_get_dests *
|
||||
entry.port = dest->port;
|
||||
entry.conn_flags = atomic_read(&dest->conn_flags);
|
||||
entry.weight = atomic_read(&dest->weight);
|
||||
entry.u_threshold = dest->u_threshold;
|
||||
entry.l_threshold = dest->l_threshold;
|
||||
entry.u_threshold = READ_ONCE(dest->u_threshold);
|
||||
entry.l_threshold = READ_ONCE(dest->l_threshold);
|
||||
entry.activeconns = atomic_read(&dest->activeconns);
|
||||
entry.inactconns = ip_vs_dest_inactconns(dest);
|
||||
entry.persistconns = atomic_read(&dest->persistconns);
|
||||
@@ -4277,8 +4326,10 @@ static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest)
|
||||
dest->tun_port) ||
|
||||
nla_put_u16(skb, IPVS_DEST_ATTR_TUN_FLAGS,
|
||||
dest->tun_flags) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_U_THRESH,
|
||||
READ_ONCE(dest->u_threshold)) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_L_THRESH,
|
||||
READ_ONCE(dest->l_threshold)) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS,
|
||||
atomic_read(&dest->activeconns)) ||
|
||||
nla_put_u32(skb, IPVS_DEST_ATTR_INACT_CONNS,
|
||||
|
||||
Reference in New Issue
Block a user