mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
ipvs: separate destination availability state
commitcdcc4e4618upstream. IPVS configuration paths update destination availability while connection accounting updates destination overload state. The two independent states share dest->flags, so their read-modify-write updates can race and lose one another. Keep OVERLOAD in flags, where the preceding patch serializes its updates with dst_lock, and move AVAILABLE to cflags. This keeps configuration- controlled availability out of the scheduler hot cacheline until a scheduler needs to check it. It also prevents availability updates from clobbering overload state. The destination status bits are not exposed through the IPVS sockopt or netlink interfaces, so keep their definitions in the internal IPVS header. Readers can still observe stale destination state; this does not provide a cross-field snapshot. Fixes:1da177e4c3("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn> Reported-by: Ao Wang <wangao@seu.edu.cn> Reported-by: Xuewei Feng <fengxw06@126.com> Reported-by: Qi Li <qli01@tsinghua.edu.cn> Reported-by: Ke Xu <xuke@tsinghua.edu.cn> Link: https://lore.kernel.org/all/8913381c-1e02-35c7-0ec4-61de5a12fd35@ssi.bg/ Assisted-by: Claude-Code:GLM-5.2 Suggested-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> Acked-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (cherry picked from commitcdcc4e4618) [ Julian: Backport by removing the hunks from ip_vs_xmit.c ] Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
9ff46bf75b
commit
1a4c3ffe2a
@@ -34,6 +34,12 @@
|
||||
#define IP_VS_HDR_INVERSE 1
|
||||
#define IP_VS_HDR_ICMP 2
|
||||
|
||||
/* Destination Server Flags */
|
||||
#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
|
||||
|
||||
/* Destination Server Config Flags */
|
||||
#define IP_VS_DEST_CF_AVAILABLE 0x0001 /* server is available */
|
||||
|
||||
/* Generic access of ipvs struct */
|
||||
static inline struct netns_ipvs *net_ipvs(struct net* net)
|
||||
{
|
||||
@@ -725,6 +731,7 @@ struct ip_vs_dest {
|
||||
volatile unsigned int flags; /* dest status flags */
|
||||
atomic_t conn_flags; /* flags to copy to conn */
|
||||
atomic_t weight; /* server weight */
|
||||
unsigned long cflags; /* config flags */
|
||||
atomic_t last_weight; /* server latest weight */
|
||||
__u16 tun_type; /* tunnel type */
|
||||
__be16 tun_port; /* tunnel port */
|
||||
|
||||
@@ -28,12 +28,6 @@
|
||||
#define IP_VS_SVC_F_SCHED_SH_FALLBACK IP_VS_SVC_F_SCHED1 /* SH fallback */
|
||||
#define IP_VS_SVC_F_SCHED_SH_PORT IP_VS_SVC_F_SCHED2 /* SH use port */
|
||||
|
||||
/*
|
||||
* Destination Server Flags
|
||||
*/
|
||||
#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */
|
||||
#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
|
||||
|
||||
/*
|
||||
* IPVS sync daemon states
|
||||
*/
|
||||
|
||||
@@ -759,7 +759,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest)
|
||||
* Checking the dest server status.
|
||||
*/
|
||||
if ((dest == NULL) ||
|
||||
!(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
|
||||
!(dest->cflags & IP_VS_DEST_CF_AVAILABLE) ||
|
||||
expire_quiescent_template(ipvs, dest) ||
|
||||
(cdest && (dest != cdest))) {
|
||||
IP_VS_DBG_BUF(9, "check_template: dest not available for "
|
||||
@@ -1408,7 +1408,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs)
|
||||
continue;
|
||||
|
||||
dest = cp->dest;
|
||||
if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
|
||||
if (!dest || (dest->cflags & IP_VS_DEST_CF_AVAILABLE))
|
||||
continue;
|
||||
|
||||
if (atomic_read(&cp->n_control))
|
||||
|
||||
@@ -124,7 +124,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
|
||||
struct ip_vs_dest *dest = cp->dest;
|
||||
struct netns_ipvs *ipvs = cp->ipvs;
|
||||
|
||||
if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
|
||||
if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
|
||||
struct ip_vs_cpu_stats *s;
|
||||
struct ip_vs_service *svc;
|
||||
|
||||
@@ -160,7 +160,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
|
||||
struct ip_vs_dest *dest = cp->dest;
|
||||
struct netns_ipvs *ipvs = cp->ipvs;
|
||||
|
||||
if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
|
||||
if (dest && (dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
|
||||
struct ip_vs_cpu_stats *s;
|
||||
struct ip_vs_service *svc;
|
||||
|
||||
@@ -2030,7 +2030,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
|
||||
}
|
||||
|
||||
/* Check the server status */
|
||||
if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
|
||||
if (cp && cp->dest && !(cp->dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
|
||||
/* the destination server is not available */
|
||||
if (sysctl_expire_nodest_conn(ipvs)) {
|
||||
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
|
||||
|
||||
@@ -1058,7 +1058,7 @@ __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;
|
||||
dest->cflags |= IP_VS_DEST_CF_AVAILABLE;
|
||||
|
||||
if (READ_ONCE(dest->u_threshold) != udest->u_threshold ||
|
||||
READ_ONCE(dest->l_threshold) != udest->l_threshold) {
|
||||
@@ -1325,7 +1325,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
|
||||
struct ip_vs_dest *dest,
|
||||
int svcupd)
|
||||
{
|
||||
dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
|
||||
dest->cflags &= ~IP_VS_DEST_CF_AVAILABLE;
|
||||
|
||||
/*
|
||||
* Remove it from the d-linked destination list.
|
||||
|
||||
@@ -220,8 +220,8 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
|
||||
|
||||
s = (struct ip_vs_dh_state *) svc->sched_data;
|
||||
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
|
||||
if (!dest
|
||||
|| !(dest->flags & IP_VS_DEST_F_AVAILABLE)
|
||||
if (!dest ||
|
||||
!(dest->cflags & IP_VS_DEST_CF_AVAILABLE)
|
||||
|| atomic_read(&dest->weight) <= 0
|
||||
|| is_overloaded(dest)) {
|
||||
ip_vs_scheduler_err(svc, "no destination available");
|
||||
|
||||
@@ -503,7 +503,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
|
||||
*/
|
||||
|
||||
dest = en->dest;
|
||||
if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
|
||||
if ((dest->cflags & IP_VS_DEST_CF_AVAILABLE) &&
|
||||
atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -170,8 +170,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
|
||||
if (least->flags & IP_VS_DEST_F_OVERLOAD)
|
||||
continue;
|
||||
|
||||
if ((atomic_read(&least->weight) > 0)
|
||||
&& (least->flags & IP_VS_DEST_F_AVAILABLE)) {
|
||||
if ((atomic_read(&least->weight) > 0) &&
|
||||
(least->cflags & IP_VS_DEST_CF_AVAILABLE)) {
|
||||
loh = ip_vs_dest_conn_overhead(least);
|
||||
goto nextstage;
|
||||
}
|
||||
@@ -187,8 +187,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
|
||||
|
||||
doh = ip_vs_dest_conn_overhead(dest);
|
||||
if (((__s64)loh * atomic_read(&dest->weight) >
|
||||
(__s64)doh * atomic_read(&least->weight))
|
||||
&& (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
|
||||
(__s64)doh * atomic_read(&least->weight)) &&
|
||||
(dest->cflags & IP_VS_DEST_CF_AVAILABLE)) {
|
||||
least = dest;
|
||||
loh = doh;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user