From 23289a37e2b127dfc4de1313fba15bb4c9f0cd5b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 27 Oct 2009 07:06:36 +0000 Subject: net: add a list_head parameter to dellink() method Adding a list_head parameter to rtnl_link_ops->dellink() methods allow us to queue devices on a list, in order to dismantle them all at once. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/8021q/vlan.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8836575f9d7..6b5c9dddaa7 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -140,7 +140,7 @@ 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) +void unregister_vlan_dev(struct net_device *dev, struct list_head *head) { struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; @@ -164,7 +164,7 @@ void unregister_vlan_dev(struct net_device *dev) synchronize_net(); - unregister_netdevice(dev); + unregister_netdevice_queue(dev, head); /* If the group is now empty, kill off the group. */ if (grp->nr_vlans == 0) { @@ -535,7 +535,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (grp->nr_vlans == 1) i = VLAN_GROUP_ARRAY_LEN; - unregister_vlan_dev(vlandev); + unregister_vlan_dev(vlandev, NULL); } break; } @@ -642,7 +642,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; - unregister_vlan_dev(dev); + unregister_vlan_dev(dev, NULL); err = 0; break; -- cgit v1.2.3-70-g09d2 From 63c8099d90096db56ee1c66c31f05d4fcfbc1c69 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 27 Oct 2009 07:06:49 +0000 Subject: vlan: Optimize multiple unregistration Use unregister_netdevice_many() to speedup master device unregister. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 1 + net/8021q/vlan.c | 49 +++++++++++++++++++++++++++++++++---------------- net/core/dev.c | 1 + 3 files changed, 35 insertions(+), 16 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8898cbebcf3..71a4870c09a 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -85,6 +85,7 @@ struct vlan_group { * the vlan is attached to. */ unsigned int nr_vlans; + int killall; struct hlist_node hlist; /* linked list */ struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; struct rcu_head rcu; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6b5c9dddaa7..511afe72af3 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -159,11 +159,12 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) if (real_dev->features & NETIF_F_HW_VLAN_FILTER) ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id); - vlan_group_set_device(grp, vlan_id, NULL); grp->nr_vlans--; - synchronize_net(); - + if (!grp->killall) { + vlan_group_set_device(grp, vlan_id, NULL); + synchronize_net(); + } unregister_netdevice_queue(dev, head); /* If the group is now empty, kill off the group. */ @@ -183,6 +184,34 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) dev_put(real_dev); } +void unregister_vlan_dev_alls(struct vlan_group *grp) +{ + LIST_HEAD(list); + int i; + struct net_device *vlandev; + struct vlan_group save; + + memcpy(&save, grp, sizeof(save)); + memset(&grp->vlan_devices_arrays, 0, sizeof(grp->vlan_devices_arrays)); + grp->killall = 1; + + synchronize_net(); + + /* Delete all VLANs for this dev. */ + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + vlandev = vlan_group_get_device(&save, i); + if (!vlandev) + continue; + + unregister_vlan_dev(vlandev, &list); + if (grp->nr_vlans == 0) + break; + } + unregister_netdevice_many(&list); + for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) + kfree(save.vlan_devices_arrays[i]); +} + static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) { @@ -524,19 +553,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, break; case NETDEV_UNREGISTER: - /* Delete all VLANs for this dev. */ - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - vlandev = vlan_group_get_device(grp, i); - if (!vlandev) - continue; - - /* unregistration of last vlan destroys group, abort - * afterwards */ - if (grp->nr_vlans == 1) - i = VLAN_GROUP_ARRAY_LEN; - - unregister_vlan_dev(vlandev, NULL); - } + unregister_vlan_dev_alls(grp); break; } diff --git a/net/core/dev.c b/net/core/dev.c index 4513dfd5718..09551cc143a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5303,6 +5303,7 @@ void unregister_netdevice_many(struct list_head *head) net_set_todo(dev); } } +EXPORT_SYMBOL(unregister_netdevice_many); /** * unregister_netdev - remove device from the kernel -- cgit v1.2.3-70-g09d2 From 29906f6a427d2004a515ebbcdc7b28bae8f6c19c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Thu, 29 Oct 2009 23:43:00 -0700 Subject: vlan: cleanup multiple unregistrations The temporary copy of the VLAN group is not neccessary since the lower device is already in the process of being unregistered, if it was neccessary the memset of the global group would introduce a race condition. With this removed, the changes to the original code are only a few lines, so remove the new function and move the code back into vlan_device_event(). Signed-off-by: Patrick McHardy Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/8021q/vlan.c | 52 ++++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 511afe72af3..39f8d012010 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -161,10 +161,10 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) grp->nr_vlans--; - if (!grp->killall) { - vlan_group_set_device(grp, vlan_id, NULL); + vlan_group_set_device(grp, vlan_id, NULL); + if (!grp->killall) synchronize_net(); - } + unregister_netdevice_queue(dev, head); /* If the group is now empty, kill off the group. */ @@ -184,34 +184,6 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) dev_put(real_dev); } -void unregister_vlan_dev_alls(struct vlan_group *grp) -{ - LIST_HEAD(list); - int i; - struct net_device *vlandev; - struct vlan_group save; - - memcpy(&save, grp, sizeof(save)); - memset(&grp->vlan_devices_arrays, 0, sizeof(grp->vlan_devices_arrays)); - grp->killall = 1; - - synchronize_net(); - - /* Delete all VLANs for this dev. */ - for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - vlandev = vlan_group_get_device(&save, i); - if (!vlandev) - continue; - - unregister_vlan_dev(vlandev, &list); - if (grp->nr_vlans == 0) - break; - } - unregister_netdevice_many(&list); - for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) - kfree(save.vlan_devices_arrays[i]); -} - static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) { @@ -456,6 +428,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, struct vlan_group *grp; int i, flgs; struct net_device *vlandev; + LIST_HEAD(list); if (is_vlan_dev(dev)) __vlan_device_event(dev, event); @@ -553,7 +526,22 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, break; case NETDEV_UNREGISTER: - unregister_vlan_dev_alls(grp); + /* Delete all VLANs for this dev. */ + grp->killall = 1; + + for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { + vlandev = vlan_group_get_device(grp, i); + if (!vlandev) + continue; + + /* unregistration of last vlan destroys group, abort + * afterwards */ + if (grp->nr_vlans == 1) + i = VLAN_GROUP_ARRAY_LEN; + + unregister_vlan_dev(vlandev, &list); + } + unregister_netdevice_many(&list); break; } -- cgit v1.2.3-70-g09d2 From f99189b186f3922ede4fa33c02f6edc735b8c981 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 Nov 2009 10:42:49 +0000 Subject: netns: net_identifiers should be read_mostly Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- drivers/net/ppp_generic.c | 2 +- drivers/net/pppoe.c | 2 +- drivers/net/pppol2tp.c | 2 +- net/8021q/vlan.c | 2 +- net/ipv4/ip_gre.c | 2 +- net/ipv4/ipip.c | 2 +- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/sit.c | 2 +- net/key/af_key.c | 2 +- net/netfilter/nf_conntrack_proto_dccp.c | 2 +- net/netfilter/nf_conntrack_proto_gre.c | 2 +- net/phonet/pn_dev.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ecea6c29413..726bd755338 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -158,7 +158,7 @@ MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the static const char * const version = DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n"; -int bond_net_id; +int bond_net_id __read_mostly; static __be32 arp_target[BOND_MAX_ARP_TARGETS]; static int arp_ip_count; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 965adb6174c..0a56a778af0 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -184,7 +184,7 @@ static atomic_t ppp_unit_count = ATOMIC_INIT(0); static atomic_t channel_count = ATOMIC_INIT(0); /* per-net private data for this module */ -static int ppp_net_id; +static int ppp_net_id __read_mostly; struct ppp_net { /* units to ppp mapping */ struct idr units_idr; diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 60c8d233209..a1dcba255b0 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -97,7 +97,7 @@ static const struct proto_ops pppoe_ops; static struct ppp_channel_ops pppoe_chan_ops; /* per-net private data for this module */ -static int pppoe_net_id; +static int pppoe_net_id __read_mostly; struct pppoe_net { /* * we could use _single_ hash table for all diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 849cc9c62c2..442c382c2c8 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -232,7 +232,7 @@ static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL }; static const struct proto_ops pppol2tp_ops; /* per-net private data for this module */ -static int pppol2tp_net_id; +static int pppol2tp_net_id __read_mostly; struct pppol2tp_net { struct list_head pppol2tp_tunnel_list; rwlock_t pppol2tp_tunnel_list_lock; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 39f8d012010..d9cb020029b 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -41,7 +41,7 @@ /* Global VLAN variables */ -int vlan_net_id; +int vlan_net_id __read_mostly; /* Our listing of VLAN group(s) */ static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE]; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index a7de9e3a8f1..c5f6af5d0f3 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -125,7 +125,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev); #define HASH_SIZE 16 -static int ipgre_net_id; +static int ipgre_net_id __read_mostly; struct ipgre_net { struct ip_tunnel *tunnels[4][HASH_SIZE]; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index c5b1f71c3cd..7242ffcc44e 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -119,7 +119,7 @@ #define HASH_SIZE 16 #define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) -static int ipip_net_id; +static int ipip_net_id __read_mostly; struct ipip_net { struct ip_tunnel *tunnels_r_l[HASH_SIZE]; struct ip_tunnel *tunnels_r[HASH_SIZE]; diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 1d614113a4b..e5c0f6bb831 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -78,7 +78,7 @@ static void ip6_fb_tnl_dev_init(struct net_device *dev); static void ip6_tnl_dev_init(struct net_device *dev); static void ip6_tnl_dev_setup(struct net_device *dev); -static int ip6_tnl_net_id; +static int ip6_tnl_net_id __read_mostly; struct ip6_tnl_net { /* the IPv6 tunnel fallback device */ struct net_device *fb_tnl_dev; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b6e145a673a..d9deaa7753e 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -66,7 +66,7 @@ static void ipip6_fb_tunnel_init(struct net_device *dev); static void ipip6_tunnel_init(struct net_device *dev); static void ipip6_tunnel_setup(struct net_device *dev); -static int sit_net_id; +static int sit_net_id __read_mostly; struct sit_net { struct ip_tunnel *tunnels_r_l[HASH_SIZE]; struct ip_tunnel *tunnels_r[HASH_SIZE]; diff --git a/net/key/af_key.c b/net/key/af_key.c index 86b2c22d091..478c8b32a5f 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -35,7 +35,7 @@ #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) -static int pfkey_net_id; +static int pfkey_net_id __read_mostly; struct netns_pfkey { /* List of all pfkey sockets. */ struct hlist_head table; diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 1b816a2ea81..80abdf297b3 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -384,7 +384,7 @@ dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = }; /* this module per-net specifics */ -static int dccp_net_id; +static int dccp_net_id __read_mostly; struct dccp_net { int dccp_loose; unsigned int dccp_timeout[CT_DCCP_MAX + 1]; diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index a54a0af0edb..91d0e719d67 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -43,7 +43,7 @@ #define GRE_TIMEOUT (30 * HZ) #define GRE_STREAM_TIMEOUT (180 * HZ) -static int proto_gre_net_id; +static int proto_gre_net_id __read_mostly; struct netns_proto_gre { rwlock_t keymap_lock; struct list_head keymap_list; diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 3287f8f0b5c..d5ad7947d77 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -43,7 +43,7 @@ struct phonet_net { struct phonet_routes routes; }; -int phonet_net_id; +int phonet_net_id __read_mostly; struct phonet_device_list *phonet_device_list(struct net *net) { -- cgit v1.2.3-70-g09d2 From 5e7565930524410f097f5b04f8aba663089a6ffc Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 25 Nov 2009 07:54:54 +0000 Subject: vlan: support "loose binding" to the underlying network device Currently the UP/DOWN state of VLANs is synchronized to the state of the underlying device, meaning all VLANs are set down once the underlying device is set down. This causes all routes to the VLAN devices to vanish. Add a flag to specify a "loose binding" mode, in which only the operstate is transfered, but the VLAN device state is independant. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 1 + net/8021q/vlan.c | 9 +++++++-- net/8021q/vlan_dev.c | 6 ++++-- net/8021q/vlan_netlink.c | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 153f6b9e722..3d870fda8c4 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -339,6 +339,7 @@ enum vlan_ioctl_cmds { enum vlan_flags { VLAN_FLAG_REORDER_HDR = 0x1, VLAN_FLAG_GVRP = 0x2, + VLAN_FLAG_LOOSE_BINDING = 0x4, }; enum vlan_name_types { diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 1483243edf1..225aa2fac0e 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -431,6 +431,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, struct vlan_group *grp; int i, flgs; struct net_device *vlandev; + struct vlan_dev_info *vlan; LIST_HEAD(list); if (is_vlan_dev(dev)) @@ -507,7 +508,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!(flgs & IFF_UP)) continue; - dev_change_flags(vlandev, flgs & ~IFF_UP); + vlan = vlan_dev_info(vlandev); + if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) + dev_change_flags(vlandev, flgs & ~IFF_UP); vlan_transfer_operstate(dev, vlandev); } break; @@ -523,7 +526,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (flgs & IFF_UP) continue; - dev_change_flags(vlandev, flgs | IFF_UP); + vlan = vlan_dev_info(vlandev); + if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) + dev_change_flags(vlandev, flgs | IFF_UP); vlan_transfer_operstate(dev, vlandev); } break; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index de0dc6bacbe..b7889782047 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -431,7 +431,8 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) struct vlan_dev_info *vlan = vlan_dev_info(dev); u32 old_flags = vlan->flags; - if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) + if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | + VLAN_FLAG_LOOSE_BINDING)) return -EINVAL; vlan->flags = (old_flags & ~mask) | (flags & mask); @@ -456,7 +457,8 @@ static int vlan_dev_open(struct net_device *dev) struct net_device *real_dev = vlan->real_dev; int err; - if (!(real_dev->flags & IFF_UP)) + if (!(real_dev->flags & IFF_UP) && + !(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) return -ENETDOWN; if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) { diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index 3c9cf6a8e7f..ddc105734af 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -60,7 +60,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) if (data[IFLA_VLAN_FLAGS]) { flags = nla_data(data[IFLA_VLAN_FLAGS]); if ((flags->flags & flags->mask) & - ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) + ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP | + VLAN_FLAG_LOOSE_BINDING)) return -EINVAL; } -- cgit v1.2.3-70-g09d2 From 946d1a9298c9e592b189a168326603c92d782b5f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 29 Nov 2009 15:46:05 +0000 Subject: net: Simplify vlan pernet operations. Take advantage of the new pernet automatic storage management, and stop using compatibility network namespace functions. Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller --- net/8021q/vlan.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 225aa2fac0e..91e9073e199 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -686,47 +686,28 @@ out: static int vlan_init_net(struct net *net) { + struct vlan_net *vn = net_generic(net, vlan_net_id); int err; - struct vlan_net *vn; - - err = -ENOMEM; - vn = kzalloc(sizeof(struct vlan_net), GFP_KERNEL); - if (vn == NULL) - goto err_alloc; - - err = net_assign_generic(net, vlan_net_id, vn); - if (err < 0) - goto err_assign; vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; err = vlan_proc_init(net); - if (err < 0) - goto err_proc; - return 0; - -err_proc: - /* nothing */ -err_assign: - kfree(vn); -err_alloc: return err; } static void vlan_exit_net(struct net *net) { - struct vlan_net *vn; - - vn = net_generic(net, vlan_net_id); rtnl_kill_links(net, &vlan_link_ops); + vlan_proc_cleanup(net); - kfree(vn); } static struct pernet_operations vlan_net_ops = { .init = vlan_init_net, .exit = vlan_exit_net, + .id = &vlan_net_id, + .size = sizeof(struct vlan_net), }; static int __init vlan_proto_init(void) @@ -736,7 +717,7 @@ static int __init vlan_proto_init(void) pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); pr_info("All bugs added by %s\n", vlan_buggyright); - err = register_pernet_gen_device(&vlan_net_id, &vlan_net_ops); + err = register_pernet_device(&vlan_net_ops); if (err < 0) goto err0; @@ -761,7 +742,7 @@ err4: err3: unregister_netdevice_notifier(&vlan_notifier_block); err2: - unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); + unregister_pernet_device(&vlan_net_ops); err0: return err; } @@ -781,7 +762,7 @@ static void __exit vlan_cleanup_module(void) for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) BUG_ON(!hlist_empty(&vlan_group_hash[i])); - unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); + unregister_pernet_device(&vlan_net_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ vlan_gvrp_uninit(); -- cgit v1.2.3-70-g09d2 From 91e2ff3528ac90b9dbcb04b76488e8ad74e3d3d9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 2 Dec 2009 13:19:08 +0000 Subject: net: Teach vlans to cleanup as a pernet subsystem Take advantage of the fact that an explicit rtnl_kill_links is unnecessary (and skipping it improves batching), as network namespace exit calls dellink on all remaining virtual devices, and rtnl_link_unregister calls dellink on all outstanding devices in that network namespace. To do this we need to leave the vlan proc directories in place until after network device exit time, which is done by using register_pernet_subsys instead of register_pernet_device. Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller --- net/8021q/vlan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 91e9073e199..ec3769295da 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -698,8 +698,6 @@ static int vlan_init_net(struct net *net) static void vlan_exit_net(struct net *net) { - rtnl_kill_links(net, &vlan_link_ops); - vlan_proc_cleanup(net); } @@ -717,7 +715,7 @@ static int __init vlan_proto_init(void) pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); pr_info("All bugs added by %s\n", vlan_buggyright); - err = register_pernet_device(&vlan_net_ops); + err = register_pernet_subsys(&vlan_net_ops); if (err < 0) goto err0; @@ -742,7 +740,7 @@ err4: err3: unregister_netdevice_notifier(&vlan_notifier_block); err2: - unregister_pernet_device(&vlan_net_ops); + unregister_pernet_subsys(&vlan_net_ops); err0: return err; } @@ -762,7 +760,7 @@ static void __exit vlan_cleanup_module(void) for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) BUG_ON(!hlist_empty(&vlan_group_hash[i])); - unregister_pernet_device(&vlan_net_ops); + unregister_pernet_subsys(&vlan_net_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ vlan_gvrp_uninit(); -- cgit v1.2.3-70-g09d2 From fc4a7489663250360cd40d5adf06a08d1c5d54df Mon Sep 17 00:00:00 2001 From: Patrick Mullaney Date: Thu, 3 Dec 2009 15:59:22 -0800 Subject: netdevice: provide common routine for macvlan and vlan operstate management Provide common routine for the transition of operational state for a leaf device during a root device transition. Signed-off-by: Patrick Mullaney Acked-by: Arnd Bergmann Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- drivers/net/macvlan.c | 24 +++--------------------- include/linux/netdevice.h | 3 +++ net/8021q/vlan.c | 29 ++++------------------------- net/core/dev.c | 27 +++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 46 deletions(-) (limited to 'net/8021q/vlan.c') diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 93c3e6edf70..21a9c9ab4b3 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -582,25 +582,6 @@ static void macvlan_port_destroy(struct net_device *dev) kfree(port); } -static void macvlan_transfer_operstate(struct net_device *dev) -{ - struct macvlan_dev *vlan = netdev_priv(dev); - const struct net_device *lowerdev = vlan->lowerdev; - - if (lowerdev->operstate == IF_OPER_DORMANT) - netif_dormant_on(dev); - else - netif_dormant_off(dev); - - if (netif_carrier_ok(lowerdev)) { - if (!netif_carrier_ok(dev)) - netif_carrier_on(dev); - } else { - if (netif_carrier_ok(dev)) - netif_carrier_off(dev); - } -} - static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { @@ -693,7 +674,7 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev, return err; list_add_tail(&vlan->list, &port->vlans); - macvlan_transfer_operstate(dev); + netif_stacked_transfer_operstate(lowerdev, dev); return 0; } @@ -768,7 +749,8 @@ static int macvlan_device_event(struct notifier_block *unused, switch (event) { case NETDEV_CHANGE: list_for_each_entry(vlan, &port->vlans, list) - macvlan_transfer_operstate(vlan->dev); + netif_stacked_transfer_operstate(vlan->lowerdev, + vlan->dev); break; case NETDEV_FEAT_CHANGE: list_for_each_entry(vlan, &port->vlans, list) { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index daf13d36749..a3fccc85b1a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1981,6 +1981,9 @@ unsigned long netdev_increment_features(unsigned long all, unsigned long one, unsigned long mask); unsigned long netdev_fix_features(unsigned long features, const char *name); +void netif_stacked_transfer_operstate(const struct net_device *rootdev, + struct net_device *dev); + static inline int net_gso_ok(int features, int gso_type) { int feature = gso_type << NETIF_F_GSO_SHIFT; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index ec3769295da..33f90e7362c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -184,27 +184,6 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) dev_put(real_dev); } -static void vlan_transfer_operstate(const struct net_device *dev, - struct net_device *vlandev) -{ - /* Have to respect userspace enforced dormant state - * of real device, also must allow supplicant running - * on VLAN device - */ - if (dev->operstate == IF_OPER_DORMANT) - netif_dormant_on(vlandev); - else - netif_dormant_off(vlandev); - - if (netif_carrier_ok(dev)) { - if (!netif_carrier_ok(vlandev)) - netif_carrier_on(vlandev); - } else { - if (netif_carrier_ok(vlandev)) - netif_carrier_off(vlandev); - } -} - int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) { const char *name = real_dev->name; @@ -262,7 +241,7 @@ int register_vlan_dev(struct net_device *dev) /* Account for reference in struct vlan_dev_info */ dev_hold(real_dev); - vlan_transfer_operstate(real_dev, dev); + netif_stacked_transfer_operstate(real_dev, dev); linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ /* So, got the sucker initialized, now lets place @@ -453,7 +432,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, if (!vlandev) continue; - vlan_transfer_operstate(dev, vlandev); + netif_stacked_transfer_operstate(dev, vlandev); } break; @@ -511,7 +490,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, vlan = vlan_dev_info(vlandev); if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) dev_change_flags(vlandev, flgs & ~IFF_UP); - vlan_transfer_operstate(dev, vlandev); + netif_stacked_transfer_operstate(dev, vlandev); } break; @@ -529,7 +508,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, vlan = vlan_dev_info(vlandev); if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) dev_change_flags(vlandev, flgs | IFF_UP); - vlan_transfer_operstate(dev, vlandev); + netif_stacked_transfer_operstate(dev, vlandev); } break; diff --git a/net/core/dev.c b/net/core/dev.c index 0913a08a87d..c36a17aafcf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4900,6 +4900,33 @@ unsigned long netdev_fix_features(unsigned long features, const char *name) } EXPORT_SYMBOL(netdev_fix_features); +/** + * netif_stacked_transfer_operstate - transfer operstate + * @rootdev: the root or lower level device to transfer state from + * @dev: the device to transfer operstate to + * + * Transfer operational state from root to device. This is normally + * called when a stacking relationship exists between the root + * device and the device(a leaf device). + */ +void netif_stacked_transfer_operstate(const struct net_device *rootdev, + struct net_device *dev) +{ + if (rootdev->operstate == IF_OPER_DORMANT) + netif_dormant_on(dev); + else + netif_dormant_off(dev); + + if (netif_carrier_ok(rootdev)) { + if (!netif_carrier_ok(dev)) + netif_carrier_on(dev); + } else { + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); + } +} +EXPORT_SYMBOL(netif_stacked_transfer_operstate); + /** * register_netdevice - register a network device * @dev: device to register -- cgit v1.2.3-70-g09d2