bonding: no longer rely on RTNL in bond_fill_info()

Add READ_ONCE()/WRITE_ONCE() annotations on port->is_enabled.
While this field is written under bond->mode_lock protection,
is is read without this lock being held.

Change bond_fill_info() to acquire RCU and use READ_ONCE()
to read bond->params fields that can be updated concurrently
from sysfs/procfs/rtnetlink.

Add const qualifiers to bond_uses_primary(), __agg_active_ports(),
bond_option_active_slave_get_rcu(), bond_3ad_get_active_agg_info(),
__bond_3ad_get_active_agg_info() helpers.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jay Vosburgh <jv@jvosburgh.net>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Link: https://patch.msgid.link/20260629173200.469953-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Eric Dumazet
2026-06-30 17:00:03 -07:00
committed by Jakub Kicinski
parent 333289d169
commit 317cefdcaa
5 changed files with 85 additions and 68 deletions
+14 -10
View File
@@ -760,14 +760,14 @@ static int __agg_usable_ports(struct aggregator *agg)
return valid;
}
static int __agg_active_ports(struct aggregator *agg)
static int __agg_active_ports(const struct aggregator *agg)
{
struct port *port;
const struct port *port;
int active = 0;
for (port = agg->lag_ports; port;
port = port->next_port_in_aggregator) {
if (port->is_enabled)
if (READ_ONCE(port->is_enabled))
active++;
}
@@ -2801,11 +2801,11 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
* some of he adaptors(ce1000.lan) report.
*/
if (link == BOND_LINK_UP) {
port->is_enabled = true;
WRITE_ONCE(port->is_enabled, true);
ad_update_actor_keys(port, false);
} else {
/* link has failed */
port->is_enabled = false;
WRITE_ONCE(port->is_enabled, false);
ad_update_actor_keys(port, true);
}
agg = __get_first_agg(port);
@@ -2878,16 +2878,20 @@ out:
* Returns: 0 on success
* < 0 on error
*/
int __bond_3ad_get_active_agg_info(struct bonding *bond,
int __bond_3ad_get_active_agg_info(const struct bonding *bond,
struct ad_info *ad_info)
{
struct aggregator *aggregator = NULL, *tmp;
const struct aggregator *aggregator = NULL, *tmp;
struct ad_slave_info *ad_slave_info;
const struct port *port;
struct list_head *iter;
struct slave *slave;
struct port *port;
bond_for_each_slave_rcu(bond, slave, iter) {
port = &(SLAVE_AD_INFO(slave)->port);
ad_slave_info = SLAVE_AD_INFO(slave);
if (!ad_slave_info)
continue;
port = &ad_slave_info->port;
tmp = rcu_dereference(port->aggregator);
if (tmp && tmp->is_active) {
aggregator = tmp;
@@ -2907,7 +2911,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
return 0;
}
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info)
{
int ret;
+61 -48
View File
@@ -686,53 +686,58 @@ static size_t bond_get_size(const struct net_device *bond_dev)
0;
}
static int bond_option_active_slave_get_ifindex(struct bonding *bond)
static int bond_option_active_slave_get_ifindex_rcu(const struct bonding *bond)
{
const struct net_device *slave;
int ifindex;
const struct net_device *dev = NULL;
const struct slave *slave;
rcu_read_lock();
slave = bond_option_active_slave_get_rcu(bond);
ifindex = slave ? slave->ifindex : 0;
rcu_read_unlock();
return ifindex;
slave = rcu_dereference(bond->curr_active_slave);
if (slave)
dev = slave->dev;
return dev ? dev->ifindex : 0;
}
static int bond_fill_info(struct sk_buff *skb,
const struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
unsigned int packets_per_slave;
int ifindex, i, targets_added;
const struct bonding *bond = netdev_priv(bond_dev);
int i, targets_added, miimon, mode;
const struct slave *primary;
struct nlattr *targets;
struct slave *primary;
if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
rcu_read_lock();
mode = READ_ONCE(bond->params.mode);
if (nla_put_u8(skb, IFLA_BOND_MODE, mode))
goto nla_put_failure;
ifindex = bond_option_active_slave_get_ifindex(bond);
if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex))
goto nla_put_failure;
if (bond_mode_uses_primary(mode)) {
int ifindex = bond_option_active_slave_get_ifindex_rcu(bond);
if (nla_put_u32(skb, IFLA_BOND_MIIMON, bond->params.miimon))
if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex))
goto nla_put_failure;
}
miimon = READ_ONCE(bond->params.miimon);
if (nla_put_u32(skb, IFLA_BOND_MIIMON, miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_UPDELAY,
bond->params.updelay * bond->params.miimon))
READ_ONCE(bond->params.updelay) * miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_DOWNDELAY,
bond->params.downdelay * bond->params.miimon))
READ_ONCE(bond->params.downdelay) * miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_PEER_NOTIF_DELAY,
bond->params.peer_notif_delay * bond->params.miimon))
READ_ONCE(bond->params.peer_notif_delay) * miimon))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_USE_CARRIER, 1))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval))
if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL,
READ_ONCE(bond->params.arp_interval)))
goto nla_put_failure;
targets = nla_nest_start_noflag(skb, IFLA_BOND_ARP_IP_TARGET);
@@ -741,8 +746,10 @@ static int bond_fill_info(struct sk_buff *skb,
targets_added = 0;
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
if (bond->params.arp_targets[i]) {
if (nla_put_be32(skb, i, bond->params.arp_targets[i]))
__be32 t = READ_ONCE(bond->params.arp_targets[i]);
if (t) {
if (nla_put_be32(skb, i, t))
goto nla_put_failure;
targets_added = 1;
}
@@ -753,11 +760,12 @@ static int bond_fill_info(struct sk_buff *skb,
else
nla_nest_cancel(skb, targets);
if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, bond->params.arp_validate))
if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE,
READ_ONCE(bond->params.arp_validate)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_ARP_ALL_TARGETS,
bond->params.arp_all_targets))
READ_ONCE(bond->params.arp_all_targets)))
goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
@@ -767,6 +775,9 @@ static int bond_fill_info(struct sk_buff *skb,
targets_added = 0;
for (i = 0; i < BOND_MAX_NS_TARGETS; i++) {
/* Note: IPv6 addresses can not be read in an atomic READ_ONCE() yet.
* We accept this minor race for the moment.
*/
if (!ipv6_addr_any(&bond->params.ns_targets[i])) {
if (nla_put_in6_addr(skb, i, &bond->params.ns_targets[i]))
goto nla_put_failure;
@@ -780,97 +791,97 @@ static int bond_fill_info(struct sk_buff *skb,
nla_nest_cancel(skb, targets);
#endif
primary = rtnl_dereference(bond->primary_slave);
primary = rcu_dereference(bond->primary_slave);
if (primary &&
nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT,
bond->params.primary_reselect))
READ_ONCE(bond->params.primary_reselect)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_FAIL_OVER_MAC,
bond->params.fail_over_mac))
READ_ONCE(bond->params.fail_over_mac)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_XMIT_HASH_POLICY,
bond->params.xmit_policy))
READ_ONCE(bond->params.xmit_policy)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_RESEND_IGMP,
bond->params.resend_igmp))
READ_ONCE(bond->params.resend_igmp)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_NUM_PEER_NOTIF,
bond->params.num_peer_notif))
READ_ONCE(bond->params.num_peer_notif)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_ALL_SLAVES_ACTIVE,
bond->params.all_slaves_active))
READ_ONCE(bond->params.all_slaves_active)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_MIN_LINKS,
bond->params.min_links))
READ_ONCE(bond->params.min_links)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_LP_INTERVAL,
bond->params.lp_interval))
READ_ONCE(bond->params.lp_interval)))
goto nla_put_failure;
packets_per_slave = bond->params.packets_per_slave;
if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE,
packets_per_slave))
READ_ONCE(bond->params.packets_per_slave)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_LACP_ACTIVE,
bond->params.lacp_active))
READ_ONCE(bond->params.lacp_active)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_LACP_RATE,
bond->params.lacp_fast))
READ_ONCE(bond->params.lacp_fast)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_SELECT,
bond->params.ad_select))
READ_ONCE(bond->params.ad_select)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_TLB_DYNAMIC_LB,
bond->params.tlb_dynamic_lb))
READ_ONCE(bond->params.tlb_dynamic_lb)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_MISSED_MAX,
bond->params.missed_max))
READ_ONCE(bond->params.missed_max)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_COUPLED_CONTROL,
bond->params.coupled_control))
READ_ONCE(bond->params.coupled_control)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_BROADCAST_NEIGH,
bond->params.broadcast_neighbor))
READ_ONCE(bond->params.broadcast_neighbor)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT,
bond->params.lacp_strict))
READ_ONCE(bond->params.lacp_strict)))
goto nla_put_failure;
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
if (mode == BOND_MODE_8023AD) {
struct ad_info info;
if (capable(CAP_NET_ADMIN)) {
if (nla_put_u16(skb, IFLA_BOND_AD_ACTOR_SYS_PRIO,
bond->params.ad_actor_sys_prio))
READ_ONCE(bond->params.ad_actor_sys_prio)))
goto nla_put_failure;
if (nla_put_u16(skb, IFLA_BOND_AD_USER_PORT_KEY,
bond->params.ad_user_port_key))
READ_ONCE(bond->params.ad_user_port_key)))
goto nla_put_failure;
/* Small race here, this is a minor trade off. */
if (nla_put(skb, IFLA_BOND_AD_ACTOR_SYSTEM,
ETH_ALEN, &bond->params.ad_actor_system))
goto nla_put_failure;
}
if (!bond_3ad_get_active_agg_info(bond, &info)) {
if (!__bond_3ad_get_active_agg_info(bond, &info)) {
struct nlattr *nest;
nest = nla_nest_start_noflag(skb, IFLA_BOND_AD_INFO);
@@ -898,9 +909,11 @@ static int bond_fill_info(struct sk_buff *skb,
}
}
rcu_read_unlock();
return 0;
nla_put_failure:
rcu_read_unlock();
return -EMSGSIZE;
}
+4 -4
View File
@@ -934,14 +934,14 @@ static int bond_option_mode_set(struct bonding *bond,
/* don't cache arp_validate between modes */
WRITE_ONCE(bond->params.arp_validate, BOND_ARP_VALIDATE_NONE);
bond->params.mode = newval->value;
WRITE_ONCE(bond->params.mode, newval->value);
/* When changing mode, the bond device is down, we may reduce
* the bond_bcast_neigh_enabled in bond_close() if broadcast_neighbor
* enabled in 8023ad mode. Therefore, only clear broadcast_neighbor
* to 0.
*/
bond->params.broadcast_neighbor = 0;
WRITE_ONCE(bond->params.broadcast_neighbor, 0);
if (bond->dev->reg_state == NETREG_REGISTERED) {
bool update = false;
@@ -1706,7 +1706,7 @@ static int bond_option_lacp_strict_set(struct bonding *bond,
{
netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n",
newval->string, newval->value);
bond->params.lacp_strict = newval->value;
WRITE_ONCE(bond->params.lacp_strict, newval->value);
bond_3ad_set_carrier(bond);
return 0;
@@ -1927,7 +1927,7 @@ static int bond_option_broadcast_neigh_set(struct bonding *bond,
if (bond->params.broadcast_neighbor == newval->value)
return 0;
bond->params.broadcast_neighbor = newval->value;
WRITE_ONCE(bond->params.broadcast_neighbor, newval->value);
if (bond->dev->flags & IFF_UP) {
if (bond->params.broadcast_neighbor)
static_branch_inc(&bond_bcast_neigh_enabled);
+2 -2
View File
@@ -302,8 +302,8 @@ void bond_3ad_state_machine_handler(struct work_struct *);
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
void bond_3ad_adapter_speed_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
int __bond_3ad_get_active_agg_info(struct bonding *bond,
int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info);
int __bond_3ad_get_active_agg_info(const struct bonding *bond,
struct ad_info *ad_info);
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave);
+4 -4
View File
@@ -345,14 +345,14 @@ static inline bool bond_mode_uses_primary(int mode)
mode == BOND_MODE_ALB;
}
static inline bool bond_uses_primary(struct bonding *bond)
static inline bool bond_uses_primary(const struct bonding *bond)
{
return bond_mode_uses_primary(BOND_MODE(bond));
}
static inline struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
static inline struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond)
{
struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave);
const struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave);
return bond_uses_primary(bond) && slave ? slave->dev : NULL;
}
@@ -703,7 +703,7 @@ void bond_setup(struct net_device *bond_dev);
unsigned int bond_get_num_tx_queues(void);
int bond_netlink_init(void);
void bond_netlink_fini(void);
struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond);
const char *bond_slave_link_status(s8 link);
struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev,