Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 278530
b: refs/heads/master
c: 5b9ea6e
h: refs/heads/master
v: v3
  • Loading branch information
Jiri Pirko authored and David S. Miller committed Dec 9, 2011
1 parent 8dd7690 commit 362409b
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 90 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 87002b03baabd2b8f6281ab6411ed88d24958de1
refs/heads/master: 5b9ea6e022e9ba0fe39cb349ac40361f78d5da5b
17 changes: 1 addition & 16 deletions trunk/include/linux/if_vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
/* found in socket.c */
extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *));

/* if this changes, algorithm will have to be reworked because this
* depends on completely exhausting the VLAN identifier space. Thus
* it gives constant time look-up, but in many cases it wastes memory.
*/
#define VLAN_GROUP_ARRAY_SPLIT_PARTS 8
#define VLAN_GROUP_ARRAY_PART_LEN (VLAN_N_VID/VLAN_GROUP_ARRAY_SPLIT_PARTS)

struct vlan_group {
struct net_device *real_dev; /* The ethernet(like) device
* the vlan is attached to.
*/
unsigned int nr_vlans;
struct hlist_node hlist; /* linked list */
struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS];
struct rcu_head rcu;
};
struct vlan_info;

static inline int is_vlan_dev(struct net_device *dev)
{
Expand Down
3 changes: 1 addition & 2 deletions trunk/include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@

#include <linux/netdev_features.h>

struct vlan_group;
struct netpoll_info;
struct phy_device;
/* 802.11 specific */
Expand Down Expand Up @@ -1096,7 +1095,7 @@ struct net_device {
/* Protocol specific pointers */

#if IS_ENABLED(CONFIG_VLAN_8021Q)
struct vlan_group __rcu *vlgrp; /* VLAN group */
struct vlan_info __rcu *vlan_info; /* VLAN info */
#endif
#if IS_ENABLED(CONFIG_NET_DSA)
struct dsa_switch_tree *dsa_ptr; /* dsa specific data */
Expand Down
90 changes: 30 additions & 60 deletions trunk/net/8021q/vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
30 changes: 27 additions & 3 deletions trunk/net/8021q/vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <linux/if_vlan.h>
#include <linux/u64_stats_sync.h>
#include <linux/list.h>


/**
Expand Down Expand Up @@ -74,6 +75,29 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
return netdev_priv(dev);
}

/* if this changes, algorithm will have to be reworked because this
* depends on completely exhausting the VLAN identifier space. Thus
* it gives constant time look-up, but in many cases it wastes memory.
*/
#define VLAN_GROUP_ARRAY_SPLIT_PARTS 8
#define VLAN_GROUP_ARRAY_PART_LEN (VLAN_N_VID/VLAN_GROUP_ARRAY_SPLIT_PARTS)

struct vlan_group {
unsigned int nr_vlan_devs;
struct hlist_node hlist; /* linked list */
struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS];
};

struct vlan_info {
struct net_device *real_dev; /* The ethernet(like) device
* the vlan is attached to.
*/
struct vlan_group grp;
struct list_head vid_list;
unsigned int nr_vids;
struct rcu_head rcu;
};

static inline struct net_device *vlan_group_get_device(struct vlan_group *vg,
u16 vlan_id)
{
Expand All @@ -97,10 +121,10 @@ static inline void vlan_group_set_device(struct vlan_group *vg,
static inline struct net_device *vlan_find_dev(struct net_device *real_dev,
u16 vlan_id)
{
struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp);
struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info);

if (grp)
return vlan_group_get_device(grp, vlan_id);
if (vlan_info)
return vlan_group_get_device(&vlan_info->grp, vlan_id);

return NULL;
}
Expand Down
Loading

0 comments on commit 362409b

Please sign in to comment.