summaryrefslogtreecommitdiff
path: root/net/8021q/vlan.c
diff options
context:
space:
mode:
authorJiri Pirko <jpirko@redhat.com>2011-12-08 04:11:18 +0000
committerDavid S. Miller <davem@davemloft.net>2011-12-08 19:52:42 -0500
commit5b9ea6e022e9ba0fe39cb349ac40361f78d5da5b (patch)
tree11f0de492ee799fd4174f79ac6aae4c3533beb25 /net/8021q/vlan.c
parent87002b03baabd2b8f6281ab6411ed88d24958de1 (diff)
vlan: introduce vid list with reference counting
This allows to keep track of vids needed to be in rx vlan filters of devices even if they are used in bond/team etc. vlan_info as well as vlan_group previously was, is allocated when first vid is added and dealocated whan last vid is deleted. vlan_group definition is moved to private header. Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/8021q/vlan.c')
-rw-r--r--net/8021q/vlan.c90
1 files changed, 30 insertions, 60 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index dd9aa400888b..efea35b02e7f 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -51,27 +51,6 @@ const char vlan_version[] = DRV_VERSION;
/* End of global variables definitions. */
-static void vlan_group_free(struct vlan_group *grp)
-{
- int i;
-
- for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
- kfree(grp->vlan_devices_arrays[i]);
- kfree(grp);
-}
-
-static struct vlan_group *vlan_group_alloc(struct net_device *real_dev)
-{
- struct vlan_group *grp;
-
- grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL);
- if (!grp)
- return NULL;
-
- grp->real_dev = real_dev;
- return grp;
-}
-
static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
{
struct net_device **array;
@@ -92,22 +71,20 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
return 0;
}
-static void vlan_rcu_free(struct rcu_head *rcu)
-{
- vlan_group_free(container_of(rcu, struct vlan_group, rcu));
-}
-
void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct net_device *real_dev = vlan->real_dev;
+ struct vlan_info *vlan_info;
struct vlan_group *grp;
u16 vlan_id = vlan->vlan_id;
ASSERT_RTNL();
- grp = rtnl_dereference(real_dev->vlgrp);
- BUG_ON(!grp);
+ vlan_info = rtnl_dereference(real_dev->vlan_info);
+ BUG_ON(!vlan_info);
+
+ grp = &vlan_info->grp;
/* Take it out of our own structures, but be sure to interlock with
* HW accelerating devices or SW vlan input packet processing if
@@ -116,7 +93,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
if (vlan_id)
vlan_vid_del(real_dev, vlan_id);
- grp->nr_vlans--;
+ grp->nr_vlan_devs--;
if (vlan->flags & VLAN_FLAG_GVRP)
vlan_gvrp_request_leave(dev);
@@ -128,16 +105,9 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
*/
unregister_netdevice_queue(dev, head);
- /* If the group is now empty, kill off the group. */
- if (grp->nr_vlans == 0) {
+ if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev);
- RCU_INIT_POINTER(real_dev->vlgrp, NULL);
-
- /* Free the group, after all cpu's are done. */
- call_rcu(&grp->rcu, vlan_rcu_free);
- }
-
/* Get rid of the vlan's reference to real_dev */
dev_put(real_dev);
}
@@ -169,17 +139,23 @@ int register_vlan_dev(struct net_device *dev)
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct net_device *real_dev = vlan->real_dev;
u16 vlan_id = vlan->vlan_id;
- struct vlan_group *grp, *ngrp = NULL;
+ struct vlan_info *vlan_info;
+ struct vlan_group *grp;
int err;
- grp = rtnl_dereference(real_dev->vlgrp);
- if (!grp) {
- ngrp = grp = vlan_group_alloc(real_dev);
- if (!grp)
- return -ENOBUFS;
+ err = vlan_vid_add(real_dev, vlan_id);
+ if (err)
+ return err;
+
+ vlan_info = rtnl_dereference(real_dev->vlan_info);
+ /* vlan_info should be there now. vlan_vid_add took care of it */
+ BUG_ON(!vlan_info);
+
+ grp = &vlan_info->grp;
+ if (grp->nr_vlan_devs == 0) {
err = vlan_gvrp_init_applicant(real_dev);
if (err < 0)
- goto out_free_group;
+ goto out_vid_del;
}
err = vlan_group_prealloc_vid(grp, vlan_id);
@@ -200,23 +176,15 @@ int register_vlan_dev(struct net_device *dev)
* it into our local structure.
*/
vlan_group_set_device(grp, vlan_id, dev);
- grp->nr_vlans++;
-
- if (ngrp) {
- rcu_assign_pointer(real_dev->vlgrp, ngrp);
- }
- vlan_vid_add(real_dev, vlan_id);
+ grp->nr_vlan_devs++;
return 0;
out_uninit_applicant:
- if (ngrp)
+ if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev);
-out_free_group:
- if (ngrp) {
- /* Free the group, after all cpu's are done. */
- call_rcu(&ngrp->rcu, vlan_rcu_free);
- }
+out_vid_del:
+ vlan_vid_del(real_dev, vlan_id);
return err;
}
@@ -357,6 +325,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
{
struct net_device *dev = ptr;
struct vlan_group *grp;
+ struct vlan_info *vlan_info;
int i, flgs;
struct net_device *vlandev;
struct vlan_dev_priv *vlan;
@@ -372,9 +341,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
vlan_vid_add(dev, 0);
}
- grp = rtnl_dereference(dev->vlgrp);
- if (!grp)
+ vlan_info = rtnl_dereference(dev->vlan_info);
+ if (!vlan_info)
goto out;
+ grp = &vlan_info->grp;
/* It is OK that we do not hold the group lock right now,
* as we run under the RTNL lock.
@@ -478,9 +448,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
if (!vlandev)
continue;
- /* unregistration of last vlan destroys group, abort
+ /* removal of last vid destroys vlan_info, abort
* afterwards */
- if (grp->nr_vlans == 1)
+ if (vlan_info->nr_vids == 1)
i = VLAN_N_VID;
unregister_vlan_dev(vlandev, &list);