Split removal of a VLAN from its lookup and flood structures from object
destruction. Pass the VLAN group explicitly to __vlan_del() and call both
operations consecutively for now. This prepares deletion paths to quiesce
readers after making an entry unreachable and before freeing it.

Reviewed-by: Ido Schimmel <[email protected]>
Signed-off-by: Nikolay Aleksandrov <[email protected]>
---
 net/bridge/br_vlan.c | 45 +++++++++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 7ba6e57b18fa..8a914e249767 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -429,22 +429,34 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 
flags,
        goto out;
 }
 
-static void __vlan_del(struct net_bridge_vlan *v)
+static void __vlan_unpublish(struct net_bridge_vlan_group *vg,
+                            struct net_bridge_vlan *v)
+{
+       __vlan_delete_pvid(vg, v);
+       if (!br_vlan_is_master(v)) {
+               struct net_bridge_vlan *masterv = v->brvlan;
+
+               rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
+                                      br_vlan_rht_params);
+               __vlan_del_list(v);
+               /* -1 because br_vlan_put_master() is called later */
+               br_vlan_rebuild_port_array(masterv,
+                                          br_vlan_num_ports(masterv) - 1);
+       }
+}
+
+static void __vlan_del(struct net_bridge_vlan_group *vg,
+                      struct net_bridge_vlan *v)
 {
        struct net_bridge_vlan *masterv = v;
-       struct net_bridge_vlan_group *vg;
        struct net_bridge_port *p = NULL;
        int err;
 
-       if (br_vlan_is_master(v)) {
-               vg = br_vlan_group(v->br);
-       } else {
+       if (!br_vlan_is_master(v)) {
                p = v->port;
-               vg = nbp_vlan_group(v->port);
                masterv = v->brvlan;
        }
 
-       __vlan_delete_pvid(vg, v);
        if (p) {
                err = __vlan_vid_del(p->dev, p->br, v);
                if (err)
@@ -467,12 +479,6 @@ static void __vlan_del(struct net_bridge_vlan *v)
 
        if (masterv != v) {
                vlan_tunnel_info_del(vg, v);
-               rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
-                                      br_vlan_rht_params);
-               __vlan_del_list(v);
-               /* -1 because br_vlan_put_master() is called later */
-               br_vlan_rebuild_port_array(masterv,
-                                          br_vlan_num_ports(masterv) - 1);
                nbp_vlan_set_vlan_dev_state(p, v->vid);
                br_multicast_toggle_one_vlan(v, false);
                br_multicast_port_ctx_deinit(&v->port_mcast_ctx);
@@ -509,7 +515,8 @@ static void __vlan_flush(const struct net_bridge *br,
                }
                v_end = vlan->vid;
 
-               __vlan_del(vlan);
+               __vlan_unpublish(vg, vlan);
+               __vlan_del(vg, vlan);
        }
 
        /* notify about the last/whole vlan range */
@@ -873,7 +880,8 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
        br_fdb_cleanup_by_dst(br, br_port_to_dst(NULL), vid, 0);
 
        vlan_tunnel_info_del(vg, v);
-       __vlan_del(v);
+       __vlan_unpublish(vg, v);
+       __vlan_del(vg, v);
 
        return 0;
 }
@@ -1389,15 +1397,18 @@ int nbp_vlan_add(struct net_bridge_port *port, u16 vid, 
u16 flags,
  */
 int nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
 {
+       struct net_bridge_vlan_group *vg;
        struct net_bridge_vlan *v;
 
        ASSERT_RTNL();
 
-       v = br_vlan_find(nbp_vlan_group(port), vid);
+       vg = nbp_vlan_group(port);
+       v = br_vlan_find(vg, vid);
        if (!v)
                return -ENOENT;
        br_fdb_cleanup_by_dst(port->br, br_vlan_to_dst(v), vid, 0);
-       __vlan_del(v);
+       __vlan_unpublish(vg, v);
+       __vlan_del(vg, v);
 
        return 0;
 }
-- 
2.47.3


Reply via email to