[PATCH net] bonding: annotate data-races around slave->last_rx

4 views
Skip to first unread message

Eric Dumazet

unread,
Jan 20, 2026, 10:29:01 AMJan 20
to David S . Miller, Jakub Kicinski, Paolo Abeni, Jay Vosburgh, Andy Gospodarek, net...@vger.kernel.org, eric.d...@gmail.com, Eric Dumazet, syzbot
slave->last_rx and slave->target_last_arp_rx[...] can be read and written
locklessly. Add READ_ONCE() and WRITE_ONCE() annotations.

syzbot reported:

BUG: KCSAN: data-race in bond_rcv_validate / bond_rcv_validate

write to 0xffff888149f0d428 of 8 bytes by interrupt on cpu 1:
bond_rcv_validate+0x202/0x7a0 drivers/net/bonding/bond_main.c:3335
bond_handle_frame+0xde/0x5e0 drivers/net/bonding/bond_main.c:1533
__netif_receive_skb_core+0x5b1/0x1950 net/core/dev.c:6039
__netif_receive_skb_one_core net/core/dev.c:6150 [inline]
__netif_receive_skb+0x59/0x270 net/core/dev.c:6265
netif_receive_skb_internal net/core/dev.c:6351 [inline]
netif_receive_skb+0x4b/0x2d0 net/core/dev.c:6410
...

write to 0xffff888149f0d428 of 8 bytes by interrupt on cpu 0:
bond_rcv_validate+0x202/0x7a0 drivers/net/bonding/bond_main.c:3335
bond_handle_frame+0xde/0x5e0 drivers/net/bonding/bond_main.c:1533
__netif_receive_skb_core+0x5b1/0x1950 net/core/dev.c:6039
__netif_receive_skb_one_core net/core/dev.c:6150 [inline]
__netif_receive_skb+0x59/0x270 net/core/dev.c:6265
netif_receive_skb_internal net/core/dev.c:6351 [inline]
netif_receive_skb+0x4b/0x2d0 net/core/dev.c:6410
br_netif_receive_skb net/bridge/br_input.c:30 [inline]
NF_HOOK include/linux/netfilter.h:318 [inline]
...

value changed: 0x0000000100005365 -> 0x0000000100005366

Fixes: f5b2b966f032 ("[PATCH] bonding: Validate probe replies in ARP monitor")
Signed-off-by: Eric Dumazet <edum...@google.com>
Reported-by: syzbot <syzk...@googlegroups.com>
---
drivers/net/bonding/bond_main.c | 18 ++++++++++--------
drivers/net/bonding/bond_options.c | 2 +-
include/net/bonding.h | 13 +++++++------
3 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 0aca6c937297def91d5740dfd456800432b5e343..0ab400b870b4247cc64a698c2ad7961406cec82e 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3047,8 +3047,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3267,8 +3267,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3338,7 +3338,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3406,7 +3406,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)

if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {

bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3430,8 +3430,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {

bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 384499c869b8da9f036c43eb081095f1bf2141af..74708bd2570ef1d09e8faa596c6064ad11ffb3a8 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1152,7 +1152,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 49edc7da05867f355f880329817bdc2a371f226b..46207840355709ee28d771c803b8ee6ef8920538 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -521,13 +521,14 @@ static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
struct slave *slave)
{
+ unsigned long tmp, ret = READ_ONCE(slave->target_last_arp_rx[0]);
int i = 1;
- unsigned long ret = slave->target_last_arp_rx[0];
-
- for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
- if (time_before(slave->target_last_arp_rx[i], ret))
- ret = slave->target_last_arp_rx[i];

+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) {
+ tmp = READ_ONCE(slave->target_last_arp_rx[i]);
+ if (time_before(tmp, ret))
+ ret = tmp;
+ }
return ret;
}

@@ -537,7 +538,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,
if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
return slave_oldest_target_arp_rx(bond, slave);

- return slave->last_rx;
+ return READ_ONCE(slave->last_rx);
}

static inline void slave_update_last_tx(struct slave *slave)
--
2.52.0.457.g6b5491de43-goog

Jakub Kicinski

unread,
Jan 21, 2026, 9:22:42 PMJan 21
to edum...@google.com, Jakub Kicinski, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.

For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
bonding: annotate data-races around slave->last_rx

This patch adds READ_ONCE()/WRITE_ONCE() annotations to slave->last_rx
and slave->target_last_arp_rx[] to fix KCSAN-reported data races in the
bonding driver's ARP monitoring code.

> diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
> index 384499c869b8..74708bd2570e 100644
> --- a/drivers/net/bonding/bond_options.c
> +++ b/drivers/net/bonding/bond_options.c
> @@ -1152,7 +1152,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
>
> if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
> bond_for_each_slave(bond, slave, iter)
> - slave->target_last_arp_rx[slot] = last_rx;
> + WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
> targets[slot] = target;
> }
> }

Should the IPv6 NS version of this function also get WRITE_ONCE?
_bond_options_ns_ip6_target_set() writes to the same target_last_arp_rx[]
array:

static void _bond_options_ns_ip6_target_set(...)
{
...
if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
slave->target_last_arp_rx[slot] = last_rx; <-- no WRITE_ONCE
...
}
}
}

This seems like the same concurrent access pattern that the ARP IPv4 path
needed fixing for.

[ ... ]
--
pw-bot: cr

Eric Dumazet

unread,
Jan 21, 2026, 11:43:09 PMJan 21
to Jakub Kicinski, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
I saw this spot, but determined this was used at setup time, not in
packet processing.

I was maybe wrong.

Jakub Kicinski

unread,
Jan 22, 2026, 10:29:04 AMJan 22
to Eric Dumazet, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
On Thu, 22 Jan 2026 05:42:55 +0100 Eric Dumazet wrote:
> > This seems like the same concurrent access pattern that the ARP IPv4 path
> > needed fixing for.
>
> I saw this spot, but determined this was used at setup time, not in
> packet processing.
>
> I was maybe wrong.

I guess if nothing else we should do it for consistency?

Eric Dumazet

unread,
Jan 22, 2026, 10:50:59 AMJan 22
to Jakub Kicinski, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
To be clear, I will cook a V2 with this part.

However, I will not add WRITE_ONCE() in bond_enslave() :

new_slave->last_rx = jiffies -

(msecs_to_jiffies(bond->params.arp_interval) + 1);
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
new_slave->target_last_arp_rx[i] = new_slave->last_rx;

new_slave->last_tx = new_slave->last_rx;

Eric Dumazet

unread,
Jan 22, 2026, 10:56:49 AMJan 22
to Jakub Kicinski, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
On Thu, Jan 22, 2026 at 4:50 PM Eric Dumazet <edum...@google.com> wrote:
>
> On Thu, Jan 22, 2026 at 4:29 PM Jakub Kicinski <ku...@kernel.org> wrote:
> >
> > On Thu, 22 Jan 2026 05:42:55 +0100 Eric Dumazet wrote:
> > > > This seems like the same concurrent access pattern that the ARP IPv4 path
> > > > needed fixing for.
> > >
> > > I saw this spot, but determined this was used at setup time, not in
> > > packet processing.
> > >
> > > I was maybe wrong.
> >
> > I guess if nothing else we should do it for consistency?
>
> To be clear, I will cook a V2 with this part.
>

_bond_options_ns_ip6_target_set() probably needs a fix as well.

Is AI review stopping at the first error ?

Eric Dumazet

unread,
Jan 22, 2026, 10:57:18 AMJan 22
to Jakub Kicinski, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
Sorry, copy paste error : I meant : bond_option_arp_ip_target_rem()

Eric Dumazet

unread,
Jan 22, 2026, 11:29:17 AMJan 22
to David S . Miller, Jakub Kicinski, Paolo Abeni, Jay Vosburgh, Andy Gospodarek, net...@vger.kernel.org, eric.d...@gmail.com, Eric Dumazet, syzbot
v2: Addressed AI feedback about _bond_options_ns_ip6_target_set()
Added {READ,WRITEE_ONCE() in bond_option_arp_ip_target_rem()
v1: https://lore.kernel.org/netdev/20260120152856.1...@google.com/

drivers/net/bonding/bond_main.c | 18 ++++++++++--------
drivers/net/bonding/bond_options.c | 8 ++++----
include/net/bonding.h | 13 +++++++------
3 files changed, 21 insertions(+), 18 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index e7caf400a59cbd9680adea3d1b8ab7a22c78f7e6..a909ebcf1102d46beadc216a1b1a8a10e227ecb4 100644
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 384499c869b8da9f036c43eb081095f1bf2141af..f1c6e9d8f61671e817bb417af39271dc14dd7bd9 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1152,7 +1152,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
@@ -1221,8 +1221,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
bond_for_each_slave(bond, slave, iter) {
targets_rx = slave->target_last_arp_rx;
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
- targets_rx[i] = targets_rx[i+1];
- targets_rx[i] = 0;
+ WRITE_ONCE(targets_rx[i], READ_ONCE(targets_rx[i+1]));
+ WRITE_ONCE(targets_rx[i], 0);
}
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
targets[i] = targets[i+1];
@@ -1377,7 +1377,7 @@ static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
slave_set_ns_maddr(bond, slave, target, &targets[slot]);
}
targets[slot] = *target;

Jakub Kicinski

unread,
Jan 22, 2026, 11:58:18 AMJan 22
to Eric Dumazet, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net, c...@meta.com
I don't think it explicitly stops after the first occurrence but it
seems to have limited ability to dig around. Simplifying, it only digs
around for 5-10min, and it spends a lot of time proving that the
issue is real. So once it's spend time proving one issue it's often out
of time, and may not search for another one :(

Ccing Chris, hopefully he'll chime in later if I'm wrong.

Chris Mason

unread,
Jan 22, 2026, 4:23:19 PMJan 22
to Jakub Kicinski, Eric Dumazet, net...@vger.kernel.org, syzk...@googlegroups.com, an...@greyhouse.net, eric.d...@gmail.com, pab...@redhat.com, j.vos...@gmail.com, da...@davemloft.net
It's supposed to keep hunting for bugs until it has reviewed the entire
thing, and then run all the bugs it found through the false positive pass.

But, sometimes it gets really excited and jumps out early, or as Jakub
mentions sometimes the context window fills and it starts skipping
steps. I've tried different ways to force things, but unfortunately
none of them have worked 100% of the time.

-chris

patchwork-b...@kernel.org

unread,
Jan 25, 2026, 4:20:18 PM (13 days ago) Jan 25
to Eric Dumazet, da...@davemloft.net, ku...@kernel.org, pab...@redhat.com, j.vos...@gmail.com, an...@greyhouse.net, net...@vger.kernel.org, eric.d...@gmail.com, syzk...@googlegroups.com
Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <ku...@kernel.org>:

On Thu, 22 Jan 2026 16:29:14 +0000 you wrote:
> slave->last_rx and slave->target_last_arp_rx[...] can be read and written
> locklessly. Add READ_ONCE() and WRITE_ONCE() annotations.
>
> syzbot reported:
>
> BUG: KCSAN: data-race in bond_rcv_validate / bond_rcv_validate
>
> [...]

Here is the summary with links:
- [v2,net] bonding: annotate data-races around slave->last_rx
https://git.kernel.org/netdev/net/c/f6c3665b6dc5

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html


Greg Kroah-Hartman

unread,
Feb 4, 2026, 10:13:57 AM (3 days ago) Feb 4
to sta...@vger.kernel.org, Greg Kroah-Hartman, pat...@lists.linux.dev, Eric Dumazet, syzbot, Jakub Kicinski, Sasha Levin
6.1-stable review patch. If anyone has any objections, please let me know.

------------------

From: Eric Dumazet <edum...@google.com>

[ Upstream commit f6c3665b6dc53c3ab7d31b585446a953a74340ef ]

slave->last_rx and slave->target_last_arp_rx[...] can be read and written
locklessly. Add READ_ONCE() and WRITE_ONCE() annotations.

syzbot reported:

BUG: KCSAN: data-race in bond_rcv_validate / bond_rcv_validate

write to 0xffff888149f0d428 of 8 bytes by interrupt on cpu 1:
bond_rcv_validate+0x202/0x7a0 drivers/net/bonding/bond_main.c:3335
bond_handle_frame+0xde/0x5e0 drivers/net/bonding/bond_main.c:1533
__netif_receive_skb_core+0x5b1/0x1950 net/core/dev.c:6039
__netif_receive_skb_one_core net/core/dev.c:6150 [inline]
__netif_receive_skb+0x59/0x270 net/core/dev.c:6265
netif_receive_skb_internal net/core/dev.c:6351 [inline]
netif_receive_skb+0x4b/0x2d0 net/core/dev.c:6410
...

write to 0xffff888149f0d428 of 8 bytes by interrupt on cpu 0:
bond_rcv_validate+0x202/0x7a0 drivers/net/bonding/bond_main.c:3335
bond_handle_frame+0xde/0x5e0 drivers/net/bonding/bond_main.c:1533
__netif_receive_skb_core+0x5b1/0x1950 net/core/dev.c:6039
__netif_receive_skb_one_core net/core/dev.c:6150 [inline]
__netif_receive_skb+0x59/0x270 net/core/dev.c:6265
netif_receive_skb_internal net/core/dev.c:6351 [inline]
netif_receive_skb+0x4b/0x2d0 net/core/dev.c:6410
br_netif_receive_skb net/bridge/br_input.c:30 [inline]
NF_HOOK include/linux/netfilter.h:318 [inline]
...

value changed: 0x0000000100005365 -> 0x0000000100005366

Fixes: f5b2b966f032 ("[PATCH] bonding: Validate probe replies in ARP monitor")
Signed-off-by: Eric Dumazet <edum...@google.com>
Reported-by: syzbot <syzk...@googlegroups.com>
Link: https://patch.msgid.link/20260122162914.2...@google.com
Signed-off-by: Jakub Kicinski <ku...@kernel.org>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
drivers/net/bonding/bond_main.c | 18 ++++++++++--------
drivers/net/bonding/bond_options.c | 8 ++++----
include/net/bonding.h | 13 +++++++------
3 files changed, 21 insertions(+), 18 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b0bc811aaab91..71912ddfa7149 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3082,8 +3082,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3302,8 +3302,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3373,7 +3373,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3441,7 +3441,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)

if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {

bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3465,8 +3465,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {

bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 1235878d87159..9473e76c6dc9d 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1133,7 +1133,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
@@ -1202,8 +1202,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
bond_for_each_slave(bond, slave, iter) {
targets_rx = slave->target_last_arp_rx;
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
- targets_rx[i] = targets_rx[i+1];
- targets_rx[i] = 0;
+ WRITE_ONCE(targets_rx[i], READ_ONCE(targets_rx[i+1]));
+ WRITE_ONCE(targets_rx[i], 0);
}
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
targets[i] = targets[i+1];
@@ -1358,7 +1358,7 @@ static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
slave_set_ns_maddr(bond, slave, target, &targets[slot]);
}
targets[slot] = *target;
diff --git a/include/net/bonding.h b/include/net/bonding.h
index bfd3e4e58f861..bdfbe77c18420 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -525,13 +525,14 @@ static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
struct slave *slave)
{
+ unsigned long tmp, ret = READ_ONCE(slave->target_last_arp_rx[0]);
int i = 1;
- unsigned long ret = slave->target_last_arp_rx[0];
-
- for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
- if (time_before(slave->target_last_arp_rx[i], ret))
- ret = slave->target_last_arp_rx[i];

+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) {
+ tmp = READ_ONCE(slave->target_last_arp_rx[i]);
+ if (time_before(tmp, ret))
+ ret = tmp;
+ }
return ret;
}

@@ -541,7 +542,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,
if (bond->params.arp_all_targets == BOND_ARP_TARGETS_ALL)
return slave_oldest_target_arp_rx(bond, slave);

- return slave->last_rx;
+ return READ_ONCE(slave->last_rx);
}

static inline void slave_update_last_tx(struct slave *slave)
--
2.51.0



Greg Kroah-Hartman

unread,
Feb 4, 2026, 10:20:52 AM (3 days ago) Feb 4
to sta...@vger.kernel.org, Greg Kroah-Hartman, pat...@lists.linux.dev, Eric Dumazet, syzbot, Jakub Kicinski, Sasha Levin
6.6-stable review patch. If anyone has any objections, please let me know.
index 9385c3ac0c83c..4373e300879d9 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3137,8 +3137,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3357,8 +3357,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3428,7 +3428,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3496,7 +3496,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)

if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {

bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3520,8 +3520,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {

bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index a2fa068193e3b..5a2a935945c4c 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1124,7 +1124,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
@@ -1193,8 +1193,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
bond_for_each_slave(bond, slave, iter) {
targets_rx = slave->target_last_arp_rx;
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
- targets_rx[i] = targets_rx[i+1];
- targets_rx[i] = 0;
+ WRITE_ONCE(targets_rx[i], READ_ONCE(targets_rx[i+1]));
+ WRITE_ONCE(targets_rx[i], 0);
}
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
targets[i] = targets[i+1];
@@ -1349,7 +1349,7 @@ static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
slave_set_ns_maddr(bond, slave, target, &targets[slot]);
}
targets[slot] = *target;
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 95f67b308c19a..9fb40a5920209 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -519,13 +519,14 @@ static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
struct slave *slave)
{
+ unsigned long tmp, ret = READ_ONCE(slave->target_last_arp_rx[0]);
int i = 1;
- unsigned long ret = slave->target_last_arp_rx[0];
-
- for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
- if (time_before(slave->target_last_arp_rx[i], ret))
- ret = slave->target_last_arp_rx[i];

+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) {
+ tmp = READ_ONCE(slave->target_last_arp_rx[i]);
+ if (time_before(tmp, ret))
+ ret = tmp;
+ }
return ret;
}

@@ -535,7 +536,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,

Greg Kroah-Hartman

unread,
Feb 4, 2026, 10:24:06 AM (3 days ago) Feb 4
to sta...@vger.kernel.org, Greg Kroah-Hartman, pat...@lists.linux.dev, Eric Dumazet, syzbot, Jakub Kicinski, Sasha Levin
6.12-stable review patch. If anyone has any objections, please let me know.
index b52f5f64e3abb..209cab75ac0a5 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3214,8 +3214,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3434,8 +3434,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3505,7 +3505,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3573,7 +3573,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)

if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {

bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3597,8 +3597,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {

bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 28c53f1b13826..a37b47b8ea8ed 100644

Greg Kroah-Hartman

unread,
Feb 4, 2026, 10:28:32 AM (3 days ago) Feb 4
to sta...@vger.kernel.org, Greg Kroah-Hartman, pat...@lists.linux.dev, Eric Dumazet, syzbot, Jakub Kicinski, Sasha Levin
6.18-stable review patch. If anyone has any objections, please let me know.
index 595fda2444b1f..99adfffcca044 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3138,8 +3138,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
__func__, &sip);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3358,8 +3358,8 @@ static void bond_validate_na(struct bonding *bond, struct slave *slave,
__func__, saddr);
return;
}
- slave->last_rx = jiffies;
- slave->target_last_arp_rx[i] = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
+ WRITE_ONCE(slave->target_last_arp_rx[i], jiffies);
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
@@ -3429,7 +3429,7 @@ int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
(slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
!slave_do_arp_validate_only(bond))
- slave->last_rx = jiffies;
+ WRITE_ONCE(slave->last_rx, jiffies);
return RX_HANDLER_ANOTHER;
} else if (is_arp) {
return bond_arp_rcv(skb, bond, slave);
@@ -3497,7 +3497,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)

if (slave->link != BOND_LINK_UP) {
if (bond_time_in_interval(bond, last_tx, 1) &&
- bond_time_in_interval(bond, slave->last_rx, 1)) {
+ bond_time_in_interval(bond, READ_ONCE(slave->last_rx), 1)) {

bond_propose_link_state(slave, BOND_LINK_UP);
slave_state_changed = 1;
@@ -3521,8 +3521,10 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
- if (!bond_time_in_interval(bond, last_tx, bond->params.missed_max) ||
- !bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
+ if (!bond_time_in_interval(bond, last_tx,
+ bond->params.missed_max) ||
+ !bond_time_in_interval(bond, READ_ONCE(slave->last_rx),
+ bond->params.missed_max)) {

bond_propose_link_state(slave, BOND_LINK_DOWN);
slave_state_changed = 1;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 384499c869b8d..f1c6e9d8f6167 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1152,7 +1152,7 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
bond_for_each_slave(bond, slave, iter)
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
targets[slot] = target;
}
}
@@ -1221,8 +1221,8 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
bond_for_each_slave(bond, slave, iter) {
targets_rx = slave->target_last_arp_rx;
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
- targets_rx[i] = targets_rx[i+1];
- targets_rx[i] = 0;
+ WRITE_ONCE(targets_rx[i], READ_ONCE(targets_rx[i+1]));
+ WRITE_ONCE(targets_rx[i], 0);
}
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
targets[i] = targets[i+1];
@@ -1377,7 +1377,7 @@ static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,

if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter) {
- slave->target_last_arp_rx[slot] = last_rx;
+ WRITE_ONCE(slave->target_last_arp_rx[slot], last_rx);
slave_set_ns_maddr(bond, slave, target, &targets[slot]);
}
targets[slot] = *target;
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 49edc7da05867..4620784035570 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -521,13 +521,14 @@ static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
struct slave *slave)
{
+ unsigned long tmp, ret = READ_ONCE(slave->target_last_arp_rx[0]);
int i = 1;
- unsigned long ret = slave->target_last_arp_rx[0];
-
- for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
- if (time_before(slave->target_last_arp_rx[i], ret))
- ret = slave->target_last_arp_rx[i];

+ for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++) {
+ tmp = READ_ONCE(slave->target_last_arp_rx[i]);
+ if (time_before(tmp, ret))
+ ret = tmp;
+ }
return ret;
}

@@ -537,7 +538,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,
Reply all
Reply to author
Forward
0 new messages