bridge: vlan: use br_vlan_(get|put)_master to deal with refcounts
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Fri, 2 Oct 2015 13:05:11 +0000 (15:05 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sun, 4 Oct 2015 23:43:48 +0000 (16:43 -0700)
Introduce br_vlan_(get|put)_master which take a reference (or create the
master vlan first if it didn't exist) and drop a reference respectively.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_vlan.c

index d97a55e..6e41fba 100644 (file)
@@ -146,6 +146,40 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
        return err;
 }
 
+/* Returns a master vlan, if it didn't exist it gets created. In all cases a
+ * a reference is taken to the master vlan before returning.
+ */
+static struct net_bridge_vlan *br_vlan_get_master(struct net_bridge *br, u16 vid)
+{
+       struct net_bridge_vlan *masterv;
+
+       masterv = br_vlan_find(br->vlgrp, vid);
+       if (!masterv) {
+               /* missing global ctx, create it now */
+               if (br_vlan_add(br, vid, 0))
+                       return NULL;
+               masterv = br_vlan_find(br->vlgrp, vid);
+               if (WARN_ON(!masterv))
+                       return NULL;
+       }
+       atomic_inc(&masterv->refcnt);
+
+       return masterv;
+}
+
+static void br_vlan_put_master(struct net_bridge_vlan *masterv)
+{
+       if (!br_vlan_is_master(masterv))
+               return;
+
+       if (atomic_dec_and_test(&masterv->refcnt)) {
+               rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash,
+                                      &masterv->vnode, br_vlan_rht_params);
+               __vlan_del_list(masterv);
+               kfree_rcu(masterv, rcu);
+       }
+}
+
 /* This is the shared VLAN add function which works for both ports and bridge
  * devices. There are four possible calls to this function in terms of the
  * vlan entry type:
@@ -196,16 +230,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
                                goto out_filt;
                }
 
-               masterv = br_vlan_find(br->vlgrp, v->vid);
-               if (!masterv) {
-                       /* missing global ctx, create it now */
-                       err = br_vlan_add(br, v->vid, 0);
-                       if (err)
-                               goto out_filt;
-                       masterv = br_vlan_find(br->vlgrp, v->vid);
-                       WARN_ON(!masterv);
-               }
-               atomic_inc(&masterv->refcnt);
+               masterv = br_vlan_get_master(br, v->vid);
+               if (!masterv)
+                       goto out_filt;
                v->brvlan = masterv;
        }
 
@@ -240,7 +267,7 @@ out_filt:
        if (p) {
                __vlan_vid_del(dev, br, v->vid);
                if (masterv) {
-                       atomic_dec(&masterv->refcnt);
+                       br_vlan_put_master(masterv);
                        v->brvlan = NULL;
                }
        }
@@ -289,12 +316,7 @@ static int __vlan_del(struct net_bridge_vlan *v)
                kfree_rcu(v, rcu);
        }
 
-       if (atomic_dec_and_test(&masterv->refcnt)) {
-               rhashtable_remove_fast(&masterv->br->vlgrp->vlan_hash,
-                                      &masterv->vnode, br_vlan_rht_params);
-               __vlan_del_list(masterv);
-               kfree_rcu(masterv, rcu);
-       }
+       br_vlan_put_master(masterv);
 out:
        return err;
 }