FAILED: patch "[PATCH] ipvs: separate destination availability state" failed to apply to 6.12-stable tree
Subsystems (5)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x cdcc4e46180df8161f4d2f3c6fd6beaf6990133d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026081700-require-division-36c1@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From cdcc4e46180df8161f4d2f3c6fd6beaf6990133d Mon Sep 17 00:00:00 2001
From: Yizhou Zhao <[email protected]>
Date: Fri, 31 Jul 2026 22:27:43 +0800
Subject: [PATCH] ipvs: separate destination availability state
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.
include/net/ip_vs.h +7 -0
@@ -36,6 +36,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 */ +/* conn_tab limits (as per Kconfig) */ #define IP_VS_CONN_TAB_MIN_BITS 8#if BITS_PER_LONG >32
@@ -976,6 +982,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 */
include/uapi/linux/ip_vs.h +0 -6
@@ -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 */
net/netfilter/ipvs/ip_vs_conn.c +2 -2
@@ -1279,7 +1279,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 "
@@ -2020,7 +2020,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 || (dest->cflags &IP_VS_DEST_CF_AVAILABLE ))continue ;if (atomic_read (&cp->n_control))
net/netfilter/ipvs/ip_vs_core.c +3 -3
@@ -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 && (dest->cflags &IP_VS_DEST_CF_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 && (dest->cflags &IP_VS_DEST_CF_AVAILABLE )) {struct ip_vs_cpu_stats *s;struct ip_vs_service *svc;
@@ -2210,7 +2210,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);
net/netfilter/ipvs/ip_vs_ctl.c +2 -2
@@ -1402,7 +1402,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) {
@@ -1662,7 +1662,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 ;spin_lock_bh (&dest->dst_lock);__ip_vs_dst_cache_reset (dest);
net/netfilter/ipvs/ip_vs_dh.c +2 -2
@@ -219,8 +219,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" );
net/netfilter/ipvs/ip_vs_lblc.c +1 -1
@@ -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 ((dest->cflags &IP_VS_DEST_CF_AVAILABLE ) &&atomic_read (&dest->weight) >0 && !is_overloaded (dest, svc)) goto out; }
net/netfilter/ipvs/ip_vs_lblcr.c +4 -4
@@ -169,8 +169,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; }
@@ -186,8 +186,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; }
net/netfilter/ipvs/ip_vs_xmit.c +2 -2
@@ -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 ) + dest->cflags &IP_VS_DEST_CF_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 ) + dest->cflags &IP_VS_DEST_CF_AVAILABLE )__ip_vs_dst_set (dest, dest_dst, &rt->dst, cookie);else noref =0 ;
Sasha Levin
maintainerPATCH 1/2 rculist_bl: add hlist_bl_for_each_entry_continue_rcu
From: Julian Anastasov <[email protected]>
[ Upstream commit 1ac252ad036cdb18f5fb7f76bb6061adfed9cedf ]
Change the old hlist_bl_first_rcu to hlist_bl_first_rcu_dereference to indicate that it is a RCU dereference.
Add hlist_bl_next_rcu and hlist_bl_first_rcu to use RCU pointers and use them to fix sparse warnings.
Add hlist_bl_for_each_entry_continue_rcu.
Stable-dep-of: cdcc4e46180d ("ipvs: separate destination availability state")
--- include/linux/rculist_bl.h | 49 +++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-)
include/linux/rculist_bl.h +40 -9
@@ -8,21 +8,31 @@#include <linux/list_bl.h> #include <linux/rcupdate.h> +/* return the first ptr or next element in an RCU protected list */ +#define hlist_bl_first_rcu (head) \ + (*((struct hlist_bl_node __rcu **)(&(head)->first))) +#define hlist_bl_next_rcu (node) \ + (*((struct hlist_bl_node __rcu **)(&(node)->next))) +static inline void hlist_bl_set_first_rcu (struct hlist_bl_head *h,struct hlist_bl_node *n) {LIST_BL_BUG_ON ((unsigned long )n &LIST_BL_LOCKMASK );LIST_BL_BUG_ON (((unsigned long )h->first &LIST_BL_LOCKMASK ) !=LIST_BL_LOCKMASK ); -rcu_assign_pointer (h->first, +rcu_assign_pointer (hlist_bl_first_rcu (h), (struct hlist_bl_node *)((unsigned long )n |LIST_BL_LOCKMASK )); } -static inline struct hlist_bl_node *hlist_bl_first_rcu (struct hlist_bl_head *h) -{ -return (struct hlist_bl_node *) - ((unsigned long )rcu_dereference_check (h->first,hlist_bl_is_locked (h)) & ~LIST_BL_LOCKMASK ); -} +#define hlist_bl_first_rcu_dereference (head) \ +({ \ + struct hlist_bl_head *__head = (head); \ + \ + (struct hlist_bl_node *) \ + ((unsigned long)rcu_dereference_check(hlist_bl_first_rcu(__head), \ + hlist_bl_is_locked(__head)) & \ + ~LIST_BL_LOCKMASK); \ +})/** * hlist_bl_del_rcu - deletes entry from hash list without re-initialization
@@ -73,7 +83,7 @@ static inline void hlist_bl_add_head_rcu(struct hlist_bl_node *n, {struct hlist_bl_node *first; -/* don't need hlist_bl_first_rcu because we're under lock */ +/* don't need hlist_bl_first_rcu* because we're under lock */ first =hlist_bl_first (h); n->next = first;
@@ -93,9 +103,30 @@ static inline void hlist_bl_add_head_rcu(struct hlist_bl_node *n,* */ #define hlist_bl_for_each_entry_rcu (tpos, pos, head, member) \ - for (pos = hlist_bl_first_rcu(head); \ + for (pos = hlist_bl_first_rcu_dereference(head); \ pos && \ ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1; }); \ - pos = rcu_dereference_raw(pos->next)) + pos = rcu_dereference_raw(hlist_bl_next_rcu(pos))) + +/** +* hlist_bl_for_each_entry_continue_rcu - continue iteration over list of given +* type +* @tpos: the type * to use as a loop cursor. +* @pos: the &struct hlist_bl_node to use as a loop cursor. +* @member: the name of the hlist_bl_node within the struct. +* +* Continue to iterate over list of given type, continuing after +* the current position which must have been in the list when the RCU read +* lock was taken. +* This would typically require either that you obtained the node from a +* previous walk of the list in the same RCU read-side critical section, or +* that you held some sort of non-RCU reference (such as a reference count) +* to keep the node alive *and* in the list. +*/ +#define hlist_bl_for_each_entry_continue_rcu (tpos, pos, member) \ + for (pos = rcu_dereference_raw(hlist_bl_next_rcu(&(tpos)->member)); \ + pos && \ + ({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1; }); \ + pos = rcu_dereference_raw(hlist_bl_next_rcu(pos)))#endif
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 ipvs: separate destination availability state
From: Yizhou Zhao <[email protected]>
[ Upstream commit cdcc4e46180df8161f4d2f3c6fd6beaf6990133d ]
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.
--- include/net/ip_vs.h | 7 +++++++ include/uapi/linux/ip_vs.h | 6 ------ net/netfilter/ipvs/ip_vs_conn.c | 4 ++-- net/netfilter/ipvs/ip_vs_core.c | 6 +++--- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/ipvs/ip_vs_dh.c | 4 ++-- net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 8 ++++---- 8 files changed, 21 insertions(+), 20 deletions(-)
include/net/ip_vs.h +7 -0
@@ -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 */
include/uapi/linux/ip_vs.h +0 -6
@@ -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 */
net/netfilter/ipvs/ip_vs_conn.c +2 -2
@@ -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 "
@@ -1402,7 +1402,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))
net/netfilter/ipvs/ip_vs_core.c +3 -3
@@ -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);
net/netfilter/ipvs/ip_vs_ctl.c +2 -2
@@ -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.
net/netfilter/ipvs/ip_vs_dh.c +2 -2
@@ -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" );
net/netfilter/ipvs/ip_vs_lblc.c +1 -1
@@ -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 ((dest->cflags &IP_VS_DEST_CF_AVAILABLE ) &&atomic_read (&dest->weight) >0 && !is_overloaded (dest, svc)) goto out; }
net/netfilter/ipvs/ip_vs_lblcr.c +4 -4
@@ -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; }
Signature · 1 line
2.53.0