diff options
-rw-r--r-- | include/linux/if_vlan.h | 1 | ||||
-rw-r--r-- | net/8021q/vlan.c | 76 | ||||
-rw-r--r-- | net/8021q/vlan.h | 2 | ||||
-rw-r--r-- | net/8021q/vlan_netlink.c | 7 |
4 files changed, 23 insertions, 63 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 07db4169463e..129fa876dbe4 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -82,6 +82,7 @@ extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *)); | |||
82 | 82 | ||
83 | struct vlan_group { | 83 | struct vlan_group { |
84 | int real_dev_ifindex; /* The ifindex of the ethernet(like) device the vlan is attached to. */ | 84 | int real_dev_ifindex; /* The ifindex of the ethernet(like) device the vlan is attached to. */ |
85 | unsigned int nr_vlans; | ||
85 | struct hlist_node hlist; /* linked list */ | 86 | struct hlist_node hlist; /* linked list */ |
86 | struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; | 87 | struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; |
87 | struct rcu_head rcu; | 88 | struct rcu_head rcu; |
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index ad34e4a0326a..ac7963854103 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -132,33 +132,17 @@ static void vlan_rcu_free(struct rcu_head *rcu) | |||
132 | vlan_group_free(container_of(rcu, struct vlan_group, rcu)); | 132 | vlan_group_free(container_of(rcu, struct vlan_group, rcu)); |
133 | } | 133 | } |
134 | 134 | ||
135 | 135 | void unregister_vlan_dev(struct net_device *dev) | |
136 | /* This returns 0 if everything went fine. | ||
137 | * It will return 1 if the group was killed as a result. | ||
138 | * A negative return indicates failure. | ||
139 | * | ||
140 | * The RTNL lock must be held. | ||
141 | */ | ||
142 | static int unregister_vlan_dev(struct net_device *real_dev, | ||
143 | unsigned short vlan_id) | ||
144 | { | 136 | { |
145 | struct net_device *dev; | 137 | struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev); |
146 | int real_dev_ifindex = real_dev->ifindex; | 138 | struct net_device *real_dev = vlan->real_dev; |
147 | struct vlan_group *grp; | 139 | struct vlan_group *grp; |
148 | unsigned int i; | 140 | unsigned short vlan_id = vlan->vlan_id; |
149 | int ret; | ||
150 | |||
151 | if (vlan_id >= VLAN_VID_MASK) | ||
152 | return -EINVAL; | ||
153 | 141 | ||
154 | ASSERT_RTNL(); | 142 | ASSERT_RTNL(); |
155 | grp = __vlan_find_group(real_dev_ifindex); | ||
156 | if (!grp) | ||
157 | return -ENOENT; | ||
158 | 143 | ||
159 | dev = vlan_group_get_device(grp, vlan_id); | 144 | grp = __vlan_find_group(real_dev->ifindex); |
160 | if (!dev) | 145 | BUG_ON(!grp); |
161 | return -ENOENT; | ||
162 | 146 | ||
163 | vlan_proc_rem_dev(dev); | 147 | vlan_proc_rem_dev(dev); |
164 | 148 | ||
@@ -169,20 +153,12 @@ static int unregister_vlan_dev(struct net_device *real_dev, | |||
169 | real_dev->vlan_rx_kill_vid(real_dev, vlan_id); | 153 | real_dev->vlan_rx_kill_vid(real_dev, vlan_id); |
170 | 154 | ||
171 | vlan_group_set_device(grp, vlan_id, NULL); | 155 | vlan_group_set_device(grp, vlan_id, NULL); |
172 | synchronize_net(); | 156 | grp->nr_vlans--; |
173 | 157 | ||
174 | /* Caller unregisters (and if necessary, puts) VLAN device, but we | 158 | synchronize_net(); |
175 | * get rid of the reference to real_dev here. | ||
176 | */ | ||
177 | dev_put(real_dev); | ||
178 | 159 | ||
179 | /* If the group is now empty, kill off the group. */ | 160 | /* If the group is now empty, kill off the group. */ |
180 | ret = 0; | 161 | if (grp->nr_vlans == 0) { |
181 | for (i = 0; i < VLAN_VID_MASK; i++) | ||
182 | if (vlan_group_get_device(grp, i)) | ||
183 | break; | ||
184 | |||
185 | if (i == VLAN_VID_MASK) { | ||
186 | if (real_dev->features & NETIF_F_HW_VLAN_RX) | 162 | if (real_dev->features & NETIF_F_HW_VLAN_RX) |
187 | real_dev->vlan_rx_register(real_dev, NULL); | 163 | real_dev->vlan_rx_register(real_dev, NULL); |
188 | 164 | ||
@@ -190,23 +166,12 @@ static int unregister_vlan_dev(struct net_device *real_dev, | |||
190 | 166 | ||
191 | /* Free the group, after all cpu's are done. */ | 167 | /* Free the group, after all cpu's are done. */ |
192 | call_rcu(&grp->rcu, vlan_rcu_free); | 168 | call_rcu(&grp->rcu, vlan_rcu_free); |
193 | ret = 1; | ||
194 | } | 169 | } |
195 | 170 | ||
196 | return ret; | 171 | /* Get rid of the vlan's reference to real_dev */ |
197 | } | 172 | dev_put(real_dev); |
198 | |||
199 | int unregister_vlan_device(struct net_device *dev) | ||
200 | { | ||
201 | int ret; | ||
202 | 173 | ||
203 | ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev, | ||
204 | VLAN_DEV_INFO(dev)->vlan_id); | ||
205 | unregister_netdevice(dev); | 174 | unregister_netdevice(dev); |
206 | |||
207 | if (ret == 1) | ||
208 | ret = 0; | ||
209 | return ret; | ||
210 | } | 175 | } |
211 | 176 | ||
212 | static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) | 177 | static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) |
@@ -291,6 +256,8 @@ int register_vlan_dev(struct net_device *dev) | |||
291 | * it into our local structure. | 256 | * it into our local structure. |
292 | */ | 257 | */ |
293 | vlan_group_set_device(grp, vlan_id, dev); | 258 | vlan_group_set_device(grp, vlan_id, dev); |
259 | grp->nr_vlans++; | ||
260 | |||
294 | if (ngrp && real_dev->features & NETIF_F_HW_VLAN_RX) | 261 | if (ngrp && real_dev->features & NETIF_F_HW_VLAN_RX) |
295 | real_dev->vlan_rx_register(real_dev, ngrp); | 262 | real_dev->vlan_rx_register(real_dev, ngrp); |
296 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) | 263 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) |
@@ -479,20 +446,16 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
479 | case NETDEV_UNREGISTER: | 446 | case NETDEV_UNREGISTER: |
480 | /* Delete all VLANs for this dev. */ | 447 | /* Delete all VLANs for this dev. */ |
481 | for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { | 448 | for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { |
482 | int ret; | ||
483 | |||
484 | vlandev = vlan_group_get_device(grp, i); | 449 | vlandev = vlan_group_get_device(grp, i); |
485 | if (!vlandev) | 450 | if (!vlandev) |
486 | continue; | 451 | continue; |
487 | 452 | ||
488 | ret = unregister_vlan_dev(dev, | 453 | /* unregistration of last vlan destroys group, abort |
489 | VLAN_DEV_INFO(vlandev)->vlan_id); | 454 | * afterwards */ |
490 | 455 | if (grp->nr_vlans == 1) | |
491 | unregister_netdevice(vlandev); | 456 | i = VLAN_GROUP_ARRAY_LEN; |
492 | 457 | ||
493 | /* Group was destroyed? */ | 458 | unregister_vlan_dev(vlandev); |
494 | if (ret == 1) | ||
495 | break; | ||
496 | } | 459 | } |
497 | break; | 460 | break; |
498 | } | 461 | } |
@@ -598,7 +561,8 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) | |||
598 | err = -EPERM; | 561 | err = -EPERM; |
599 | if (!capable(CAP_NET_ADMIN)) | 562 | if (!capable(CAP_NET_ADMIN)) |
600 | break; | 563 | break; |
601 | err = unregister_vlan_device(dev); | 564 | unregister_vlan_dev(dev); |
565 | err = 0; | ||
602 | break; | 566 | break; |
603 | 567 | ||
604 | case GET_VLAN_REALDEV_NAME_CMD: | 568 | case GET_VLAN_REALDEV_NAME_CMD: |
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 56378651cc46..0cfdf77b497c 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h | |||
@@ -38,7 +38,7 @@ void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result); | |||
38 | int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); | 38 | int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); |
39 | void vlan_setup(struct net_device *dev); | 39 | void vlan_setup(struct net_device *dev); |
40 | int register_vlan_dev(struct net_device *dev); | 40 | int register_vlan_dev(struct net_device *dev); |
41 | int unregister_vlan_device(struct net_device *dev); | 41 | void unregister_vlan_dev(struct net_device *dev); |
42 | 42 | ||
43 | int vlan_netlink_init(void); | 43 | int vlan_netlink_init(void); |
44 | void vlan_netlink_fini(void); | 44 | void vlan_netlink_fini(void); |
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index 0996185e2ed5..9ee63583ed27 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c | |||
@@ -137,11 +137,6 @@ static int vlan_newlink(struct net_device *dev, | |||
137 | return register_vlan_dev(dev); | 137 | return register_vlan_dev(dev); |
138 | } | 138 | } |
139 | 139 | ||
140 | static void vlan_dellink(struct net_device *dev) | ||
141 | { | ||
142 | unregister_vlan_device(dev); | ||
143 | } | ||
144 | |||
145 | static inline size_t vlan_qos_map_size(unsigned int n) | 140 | static inline size_t vlan_qos_map_size(unsigned int n) |
146 | { | 141 | { |
147 | if (n == 0) | 142 | if (n == 0) |
@@ -226,7 +221,7 @@ struct rtnl_link_ops vlan_link_ops __read_mostly = { | |||
226 | .validate = vlan_validate, | 221 | .validate = vlan_validate, |
227 | .newlink = vlan_newlink, | 222 | .newlink = vlan_newlink, |
228 | .changelink = vlan_changelink, | 223 | .changelink = vlan_changelink, |
229 | .dellink = vlan_dellink, | 224 | .dellink = unregister_vlan_dev, |
230 | .get_size = vlan_get_size, | 225 | .get_size = vlan_get_size, |
231 | .fill_info = vlan_fill_info, | 226 | .fill_info = vlan_fill_info, |
232 | }; | 227 | }; |