|
IPVS destination schedulers read dest->flags from packet processing paths
while holding only the RCU read lock. The same word is updated by plain
read-modify-write operations from connection accounting and destination
update paths, for example ip_vs_bind_dest(), ip_vs_unbind_dest(), and
__ip_vs_update_dest().
The RCU read lock protects the destination lifetime, but it does not
serialize accesses to dest->flags. A plain load can therefore race with a
plain write, and concurrent plain read-modify-write updates can lose an
AVAILABLE or OVERLOAD bit update.
KCSAN reports the race with a standard IPVS configuration using the SH
scheduler and a destination with u_threshold set:
BUG: KCSAN: data-race in __ip_vs_update_dest / ip_vs_sh_schedule
write to ... of 4 bytes by task ipvs_cfg:
__ip_vs_update_dest
ip_vs_edit_dest
do_ip_vs_set_ctl
__x64_sys_setsockopt
read to ... of 4 bytes by task ipvs_churn:
ip_vs_sh_schedule
ip_vs_schedule
tcp_conn_schedule
ip_vs_in_hook
tcp_connect
__x64_sys_connect
value changed: 0x00000003 -> 0x00000001
Convert dest->flags to atomic_t and use atomic_read(), atomic_or(), and
atomic_and() for all destination flag tests and updates. This preserves
the existing 32-bit field size while making the flag updates atomic RMW
operations and making readers use atomic accesses. Valid minimum-sized
IPVS configuration and scheduling paths are unchanged; only the
synchronization of the destination status flags changes.
This is limited to synchronizing the flags word itself. It does not add
ordering for readers, and it does not make scheduler decisions operate on a
fresh snapshot of all destination state; readers may still observe stale
state in the usual IPVS fast path. This keeps the packet fast path free
of additional barriers or locks.
Fixes: eba3b5a78799d ("ipvs: SH fallback and L4 hashing")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
Reported-by: Yuxiang Yang <yangyx22@xxxxxxxxxxxxxxxxxxxxx>
Reported-by: Ao Wang <wangao@xxxxxxxxxx>
Reported-by: Xuewei Feng <fengxw06@xxxxxxx>
Reported-by: Qi Li <qli01@xxxxxxxxxxxxxxx>
Reported-by: Ke Xu <xuke@xxxxxxxxxxxxxxx>
Assisted-by: Claude-Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@xxxxxxxxxxxxxxxxxxxxx>
---
Changes in v2:
- Clarify that the patch fixes the flags data race and RMW lost updates,
but does not prevent readers from observing stale scheduling state.
- Fix checkpatch logical-continuation warnings.
- Suggested by Julian Anastasov.
- Link to v1:
https://lore.kernel.org/netfilter-devel/20260707085706.96322-1-zhaoyz24@xxxxxxxxxxxxxxxxxxxxx/
---
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 49297fec448a..bb969738ed73 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -972,7 +972,7 @@ struct ip_vs_dest {
u16 af; /* address family */
__be16 port; /* port number of the server */
union nf_inet_addr addr; /* IP address of the server */
- volatile unsigned int flags; /* dest status flags */
+ atomic_t flags; /* dest status flags */
atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
atomic_t last_weight; /* server latest weight */
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index cb36641f8d1c..539f603f38b7 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -1055,7 +1055,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest
*dest)
if (dest->u_threshold != 0 &&
ip_vs_dest_totalconns(dest) >= dest->u_threshold)
- dest->flags |= IP_VS_DEST_F_OVERLOAD;
+ atomic_or(IP_VS_DEST_F_OVERLOAD, &dest->flags);
}
@@ -1151,13 +1151,13 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn
*cp)
if (dest->l_threshold != 0) {
if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
} else if (dest->u_threshold != 0) {
if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
} else {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
}
ip_vs_dest_put(dest);
@@ -1188,7 +1188,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) ||
+ !(atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) ||
expire_quiescent_template(ipvs, dest) ||
(cdest && (dest != cdest))) {
IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -1929,7 +1929,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs
*ipvs)
cp = ip_vs_hn0_to_conn(hn);
resched_score++;
dest = cp->dest;
- if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
+ if (!dest || (atomic_read(&dest->flags) &
IP_VS_DEST_F_AVAILABLE))
continue;
if (atomic_read(&cp->n_control))
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index d40b404c1bf6..ca778937facf 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -302,7 +302,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 && (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -338,7 +338,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 && (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -2204,7 +2204,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 && !(atomic_read(&cp->dest->flags) &
IP_VS_DEST_F_AVAILABLE)) {
/* the destination server is not available */
if (sysctl_expire_nodest_conn(ipvs)) {
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index bcf40b8c41cf..685b2675b6e0 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1368,10 +1368,10 @@ __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;
+ atomic_or(IP_VS_DEST_F_AVAILABLE, &dest->flags);
if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
dest->u_threshold = udest->u_threshold;
dest->l_threshold = udest->l_threshold;
@@ -1613,7 +1613,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;
+ atomic_and(~IP_VS_DEST_F_AVAILABLE, &dest->flags);
spin_lock_bh(&dest->dst_lock);
__ip_vs_dst_cache_reset(dest);
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index e1f62f6b25e2..82492e824f02 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -201,7 +201,7 @@ static int ip_vs_dh_dest_changed(struct ip_vs_service *svc,
*/
static inline int is_overloaded(struct ip_vs_dest *dest)
{
- return dest->flags & IP_VS_DEST_F_OVERLOAD;
+ return atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
@@ -219,10 +219,10 @@ 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)
- || atomic_read(&dest->weight) <= 0
- || is_overloaded(dest)) {
+ if (!dest ||
+ !(atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) ||
+ atomic_read(&dest->weight) <= 0 ||
+ is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
return NULL;
}
diff --git a/net/netfilter/ipvs/ip_vs_fo.c b/net/netfilter/ipvs/ip_vs_fo.c
index d657b47c6511..5231e518c07c 100644
--- a/net/netfilter/ipvs/ip_vs_fo.c
+++ b/net/netfilter/ipvs/ip_vs_fo.c
@@ -29,7 +29,7 @@ ip_vs_fo_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
* Find virtual server with highest weight and send it traffic
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > hw) {
hweight = dest;
hw = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 15ccb2b2fa1f..d2eb5dda5b68 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -414,7 +414,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
if (atomic_read(&dest->weight) > 0) {
least = dest;
@@ -429,7 +429,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
@@ -502,7 +502,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 ((atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) &&
atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
goto out;
}
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index c90ea897c3f7..48f02453a5be 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -166,11 +166,11 @@ static inline struct ip_vs_dest
*ip_vs_dest_set_min(struct ip_vs_dest_set *set)
/* select the first destination server, whose weight > 0 */
list_for_each_entry_rcu(e, &set->list, list) {
least = e->dest;
- if (least->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&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) &&
+ (atomic_read(&least->flags) & IP_VS_DEST_F_AVAILABLE)) {
loh = ip_vs_dest_conn_overhead(least);
goto nextstage;
}
@@ -181,13 +181,13 @@ static inline struct ip_vs_dest
*ip_vs_dest_set_min(struct ip_vs_dest_set *set)
nextstage:
list_for_each_entry_continue_rcu(e, &set->list, list) {
dest = e->dest;
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
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)) &&
+ (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
least = dest;
loh = doh;
}
@@ -577,7 +577,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
if (atomic_read(&dest->weight) > 0) {
@@ -593,7 +593,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index 38cc38c5d8bb..6acb3c904af5 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -38,7 +38,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if ((atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) ||
atomic_read(&dest->weight) == 0)
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_mh.c b/net/netfilter/ipvs/ip_vs_mh.c
index 020863047562..c322ed1754b7 100644
--- a/net/netfilter/ipvs/ip_vs_mh.c
+++ b/net/netfilter/ipvs/ip_vs_mh.c
@@ -80,7 +80,7 @@ static inline void generate_hash_secret(hsiphash_key_t *hash1,
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
/* Returns hash value for IPVS MH entry */
diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c
index ada158c610ce..ffa4bfeb21d9 100644
--- a/net/netfilter/ipvs/ip_vs_nq.c
+++ b/net/netfilter/ipvs/ip_vs_nq.c
@@ -72,7 +72,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD ||
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD ||
!atomic_read(&dest->weight))
continue;
diff --git a/net/netfilter/ipvs/ip_vs_ovf.c b/net/netfilter/ipvs/ip_vs_ovf.c
index c5c67df80a0b..f7f17dddbb05 100644
--- a/net/netfilter/ipvs/ip_vs_ovf.c
+++ b/net/netfilter/ipvs/ip_vs_ovf.c
@@ -33,7 +33,7 @@ ip_vs_ovf_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
w = atomic_read(&dest->weight);
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if ((atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) ||
atomic_read(&dest->activeconns) > w ||
w == 0)
continue;
diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c
index 4125ee561cdc..98453d205d6f 100644
--- a/net/netfilter/ipvs/ip_vs_rr.c
+++ b/net/netfilter/ipvs/ip_vs_rr.c
@@ -66,7 +66,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) &
IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0)
/* HIT */
goto out;
diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c
index 245a323c84cd..0249062d1360 100644
--- a/net/netfilter/ipvs/ip_vs_sed.c
+++ b/net/netfilter/ipvs/ip_vs_sed.c
@@ -75,7 +75,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_sed_dest_overhead(least);
@@ -90,7 +90,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_sed_dest_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index cd67066e3b26..343780b82c95 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -73,7 +73,7 @@ struct ip_vs_sh_state {
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
/*
diff --git a/net/netfilter/ipvs/ip_vs_twos.c b/net/netfilter/ipvs/ip_vs_twos.c
index dbb7f5fd4688..35fa4c6dc5cf 100644
--- a/net/netfilter/ipvs/ip_vs_twos.c
+++ b/net/netfilter/ipvs/ip_vs_twos.c
@@ -52,7 +52,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct
ip_vs_service *svc,
/* Generate a random weight between [0,sum of all weights) */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD)) {
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)) {
weight = atomic_read(&dest->weight);
if (weight > 0) {
total_weight += weight;
@@ -75,7 +75,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct
ip_vs_service *svc,
/* Pick two weighted servers */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
weight = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c
index 9da445ca09a1..c2d09ac96fe8 100644
--- a/net/netfilter/ipvs/ip_vs_wlc.c
+++ b/net/netfilter/ipvs/ip_vs_wlc.c
@@ -47,7 +47,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_dest_conn_overhead(least);
@@ -62,7 +62,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c
index 2dcff1040da5..f21a75284971 100644
--- a/net/netfilter/ipvs/ip_vs_wrr.c
+++ b/net/netfilter/ipvs/ip_vs_wrr.c
@@ -176,7 +176,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct
sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) &
IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) >= mark->cw)
goto found;
if (dest == stop)
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index ce542ed4b013..37b9671e4960 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af,
struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
else
noref = 0;
@@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af,
struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst,
cookie);
else
noref = 0;
|