Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8ed289f authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller
Browse files

bridge: vlan: use br_vlan_(get|put)_master to deal with refcounts



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: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 586c2b57
Loading
Loading
Loading
Loading
+39 −17
Original line number Diff line number Diff line
@@ -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)
		masterv = br_vlan_get_master(br, v->vid);
		if (!masterv)
			goto out_filt;
			masterv = br_vlan_find(br->vlgrp, v->vid);
			WARN_ON(!masterv);
		}
		atomic_inc(&masterv->refcnt);
		v->brvlan = masterv;
	}

@@ -240,7 +267,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags)
	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;
}