Change net/bridge/br_netlink.c to use atomic operations to read/change bits in p->flags. Signed-off-by: Eric Dumazet --- net/bridge/br_netlink.c | 97 ++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 7cb24de9c77d3d15892723f77288c27a15a6a0ad..12850f60bfcb418918508612070a6b0266ffbe6b 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -113,7 +113,7 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev, num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask); rcu_read_unlock(); - if (p && (p->flags & BR_VLAN_TUNNEL)) + if (p && test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) vinfo_sz += br_get_vlan_tunnel_info_size(vg); /* Each VLAN is returned in bridge_vlan_info along with flags */ @@ -823,7 +823,7 @@ static int br_afspec(struct net_bridge *br, err = 0; switch (nla_type(attr)) { case IFLA_BRIDGE_VLAN_TUNNEL_INFO: - if (!p || !(p->flags & BR_VLAN_TUNNEL)) + if (!p || !test_bit(BR_VLAN_TUNNEL_BIT, &p->flags)) return -EINVAL; err = br_parse_vlan_tunnel_info(attr, &tinfo_curr); if (err) @@ -934,58 +934,64 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state) } /* Set/clear or port flags based on attribute */ -static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], +static unsigned long br_set_port_flag(unsigned long flags, struct nlattr *tb[], int attrtype, unsigned long mask) { - if (!tb[attrtype]) - return; - - if (nla_get_u8(tb[attrtype])) - p->flags |= mask; - else - p->flags &= ~mask; + if (tb[attrtype]) { + if (nla_get_u8(tb[attrtype])) + flags |= mask; + else + flags &= ~mask; + } + return flags; } /* Process bridge protocol info on port */ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], struct netlink_ext_ack *extack) { - unsigned long old_flags, changed_mask; + unsigned long old_flags, flags, changed_mask; bool br_vlan_tunnel_old; int err; - old_flags = p->flags; +retry: + flags = old_flags = READ_ONCE(p->flags); br_vlan_tunnel_old = (old_flags & BR_VLAN_TUNNEL) ? true : false; - br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); - br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); - br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, - BR_MULTICAST_FAST_LEAVE); - br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); - br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); - br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, - BR_MULTICAST_TO_UNICAST); - br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); - br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); - br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); - br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, BR_NEIGH_SUPPRESS); - br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); - br_set_port_flag(p, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); - br_set_port_flag(p, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); - br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, - BR_NEIGH_VLAN_SUPPRESS); - br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_FORWARD_GRAT, - BR_NEIGH_FORWARD_GRAT); - - if ((p->flags & BR_PORT_MAB) && - (!(p->flags & BR_PORT_LOCKED) || !(p->flags & BR_LEARNING))) { + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_FAST_LEAVE, + BR_MULTICAST_FAST_LEAVE); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_MCAST_FLOOD, + BR_MCAST_FLOOD); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_MCAST_TO_UCAST, + BR_MULTICAST_TO_UNICAST); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_BCAST_FLOOD, + BR_BCAST_FLOOD); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_PROXYARP_WIFI, + BR_PROXYARP_WIFI); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_VLAN_TUNNEL, + BR_VLAN_TUNNEL); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_NEIGH_SUPPRESS, + BR_NEIGH_SUPPRESS); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_LOCKED, BR_PORT_LOCKED); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_MAB, BR_PORT_MAB); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_NEIGH_VLAN_SUPPRESS, + BR_NEIGH_VLAN_SUPPRESS); + flags = br_set_port_flag(flags, tb, IFLA_BRPORT_NEIGH_FORWARD_GRAT, + BR_NEIGH_FORWARD_GRAT); + + if ((flags & BR_PORT_MAB) && + (!(flags & BR_PORT_LOCKED) || !(flags & BR_LEARNING))) { NL_SET_ERR_MSG(extack, "Bridge port must be locked and have learning enabled when MAB is enabled"); - p->flags = old_flags; return -EINVAL; - } else if (!(p->flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) { + } + if (!(flags & BR_PORT_MAB) && (old_flags & BR_PORT_MAB)) { struct net_bridge_fdb_flush_desc desc = { .flags = BIT(BR_FDB_LOCKED), .flags_mask = BIT(BR_FDB_LOCKED), @@ -995,15 +1001,16 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_fdb_flush(p->br, &desc); } - changed_mask = old_flags ^ p->flags; + changed_mask = old_flags ^ flags; - err = br_switchdev_set_port_flag(p, p->flags, changed_mask, extack); - if (err) { - p->flags = old_flags; + err = br_switchdev_set_port_flag(p, flags, changed_mask, extack); + if (err) return err; - } - if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL)) + if (cmpxchg(&p->flags, old_flags, flags) != old_flags) + goto retry; + + if (br_vlan_tunnel_old && !(flags & BR_VLAN_TUNNEL)) nbp_vlan_tunnel_info_flush(p); br_port_flags_change(p, changed_mask); -- 2.54.0.1064.gd145956f57-goog