aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/if_vlan.h1
-rw-r--r--net/8021q/vlan.c76
-rw-r--r--net/8021q/vlan.h2
-rw-r--r--net/8021q/vlan_netlink.c7
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
83struct vlan_group { 83struct 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 135void 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 */
142static 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
199int 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
212static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev) 177static 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);
38int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); 38int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id);
39void vlan_setup(struct net_device *dev); 39void vlan_setup(struct net_device *dev);
40int register_vlan_dev(struct net_device *dev); 40int register_vlan_dev(struct net_device *dev);
41int unregister_vlan_device(struct net_device *dev); 41void unregister_vlan_dev(struct net_device *dev);
42 42
43int vlan_netlink_init(void); 43int vlan_netlink_init(void);
44void vlan_netlink_fini(void); 44void 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
140static void vlan_dellink(struct net_device *dev)
141{
142 unregister_vlan_device(dev);
143}
144
145static inline size_t vlan_qos_map_size(unsigned int n) 140static 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};