diff options
Diffstat (limited to 'net/8021q/vlan.c')
-rw-r--r-- | net/8021q/vlan.c | 97 |
1 files changed, 35 insertions, 62 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 85addcd9372b..9424f3718ea7 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -51,14 +51,18 @@ const char vlan_version[] = DRV_VERSION; | |||
51 | 51 | ||
52 | /* End of global variables definitions. */ | 52 | /* End of global variables definitions. */ |
53 | 53 | ||
54 | static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) | 54 | static int vlan_group_prealloc_vid(struct vlan_group *vg, |
55 | __be16 vlan_proto, u16 vlan_id) | ||
55 | { | 56 | { |
56 | struct net_device **array; | 57 | struct net_device **array; |
58 | unsigned int pidx, vidx; | ||
57 | unsigned int size; | 59 | unsigned int size; |
58 | 60 | ||
59 | ASSERT_RTNL(); | 61 | ASSERT_RTNL(); |
60 | 62 | ||
61 | array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; | 63 | pidx = vlan_proto_idx(vlan_proto); |
64 | vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN; | ||
65 | array = vg->vlan_devices_arrays[pidx][vidx]; | ||
62 | if (array != NULL) | 66 | if (array != NULL) |
63 | return 0; | 67 | return 0; |
64 | 68 | ||
@@ -67,7 +71,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) | |||
67 | if (array == NULL) | 71 | if (array == NULL) |
68 | return -ENOBUFS; | 72 | return -ENOBUFS; |
69 | 73 | ||
70 | vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array; | 74 | vg->vlan_devices_arrays[pidx][vidx] = array; |
71 | return 0; | 75 | return 0; |
72 | } | 76 | } |
73 | 77 | ||
@@ -93,7 +97,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
93 | if (vlan->flags & VLAN_FLAG_GVRP) | 97 | if (vlan->flags & VLAN_FLAG_GVRP) |
94 | vlan_gvrp_request_leave(dev); | 98 | vlan_gvrp_request_leave(dev); |
95 | 99 | ||
96 | vlan_group_set_device(grp, vlan_id, NULL); | 100 | vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL); |
97 | /* Because unregister_netdevice_queue() makes sure at least one rcu | 101 | /* Because unregister_netdevice_queue() makes sure at least one rcu |
98 | * grace period is respected before device freeing, | 102 | * grace period is respected before device freeing, |
99 | * we dont need to call synchronize_net() here. | 103 | * we dont need to call synchronize_net() here. |
@@ -112,13 +116,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
112 | * VLAN is not 0 (leave it there for 802.1p). | 116 | * VLAN is not 0 (leave it there for 802.1p). |
113 | */ | 117 | */ |
114 | if (vlan_id) | 118 | if (vlan_id) |
115 | vlan_vid_del(real_dev, vlan_id); | 119 | vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); |
116 | 120 | ||
117 | /* Get rid of the vlan's reference to real_dev */ | 121 | /* Get rid of the vlan's reference to real_dev */ |
118 | dev_put(real_dev); | 122 | dev_put(real_dev); |
119 | } | 123 | } |
120 | 124 | ||
121 | int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) | 125 | int vlan_check_real_dev(struct net_device *real_dev, |
126 | __be16 protocol, u16 vlan_id) | ||
122 | { | 127 | { |
123 | const char *name = real_dev->name; | 128 | const char *name = real_dev->name; |
124 | 129 | ||
@@ -127,7 +132,7 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) | |||
127 | return -EOPNOTSUPP; | 132 | return -EOPNOTSUPP; |
128 | } | 133 | } |
129 | 134 | ||
130 | if (vlan_find_dev(real_dev, vlan_id) != NULL) | 135 | if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) |
131 | return -EEXIST; | 136 | return -EEXIST; |
132 | 137 | ||
133 | return 0; | 138 | return 0; |
@@ -142,7 +147,7 @@ int register_vlan_dev(struct net_device *dev) | |||
142 | struct vlan_group *grp; | 147 | struct vlan_group *grp; |
143 | int err; | 148 | int err; |
144 | 149 | ||
145 | err = vlan_vid_add(real_dev, vlan_id); | 150 | err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id); |
146 | if (err) | 151 | if (err) |
147 | return err; | 152 | return err; |
148 | 153 | ||
@@ -160,7 +165,7 @@ int register_vlan_dev(struct net_device *dev) | |||
160 | goto out_uninit_gvrp; | 165 | goto out_uninit_gvrp; |
161 | } | 166 | } |
162 | 167 | ||
163 | err = vlan_group_prealloc_vid(grp, vlan_id); | 168 | err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id); |
164 | if (err < 0) | 169 | if (err < 0) |
165 | goto out_uninit_mvrp; | 170 | goto out_uninit_mvrp; |
166 | 171 | ||
@@ -181,7 +186,7 @@ int register_vlan_dev(struct net_device *dev) | |||
181 | /* So, got the sucker initialized, now lets place | 186 | /* So, got the sucker initialized, now lets place |
182 | * it into our local structure. | 187 | * it into our local structure. |
183 | */ | 188 | */ |
184 | vlan_group_set_device(grp, vlan_id, dev); | 189 | vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev); |
185 | grp->nr_vlan_devs++; | 190 | grp->nr_vlan_devs++; |
186 | 191 | ||
187 | return 0; | 192 | return 0; |
@@ -195,7 +200,7 @@ out_uninit_gvrp: | |||
195 | if (grp->nr_vlan_devs == 0) | 200 | if (grp->nr_vlan_devs == 0) |
196 | vlan_gvrp_uninit_applicant(real_dev); | 201 | vlan_gvrp_uninit_applicant(real_dev); |
197 | out_vid_del: | 202 | out_vid_del: |
198 | vlan_vid_del(real_dev, vlan_id); | 203 | vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); |
199 | return err; | 204 | return err; |
200 | } | 205 | } |
201 | 206 | ||
@@ -213,7 +218,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) | |||
213 | if (vlan_id >= VLAN_VID_MASK) | 218 | if (vlan_id >= VLAN_VID_MASK) |
214 | return -ERANGE; | 219 | return -ERANGE; |
215 | 220 | ||
216 | err = vlan_check_real_dev(real_dev, vlan_id); | 221 | err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id); |
217 | if (err < 0) | 222 | if (err < 0) |
218 | return err; | 223 | return err; |
219 | 224 | ||
@@ -255,6 +260,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) | |||
255 | new_dev->mtu = real_dev->mtu; | 260 | new_dev->mtu = real_dev->mtu; |
256 | new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT); | 261 | new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT); |
257 | 262 | ||
263 | vlan_dev_priv(new_dev)->vlan_proto = htons(ETH_P_8021Q); | ||
258 | vlan_dev_priv(new_dev)->vlan_id = vlan_id; | 264 | vlan_dev_priv(new_dev)->vlan_id = vlan_id; |
259 | vlan_dev_priv(new_dev)->real_dev = real_dev; | 265 | vlan_dev_priv(new_dev)->real_dev = real_dev; |
260 | vlan_dev_priv(new_dev)->dent = NULL; | 266 | vlan_dev_priv(new_dev)->dent = NULL; |
@@ -301,7 +307,7 @@ static void vlan_transfer_features(struct net_device *dev, | |||
301 | { | 307 | { |
302 | vlandev->gso_max_size = dev->gso_max_size; | 308 | vlandev->gso_max_size = dev->gso_max_size; |
303 | 309 | ||
304 | if (dev->features & NETIF_F_HW_VLAN_TX) | 310 | if (dev->features & NETIF_F_HW_VLAN_CTAG_TX) |
305 | vlandev->hard_header_len = dev->hard_header_len; | 311 | vlandev->hard_header_len = dev->hard_header_len; |
306 | else | 312 | else |
307 | vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; | 313 | vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; |
@@ -341,16 +347,17 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
341 | int i, flgs; | 347 | int i, flgs; |
342 | struct net_device *vlandev; | 348 | struct net_device *vlandev; |
343 | struct vlan_dev_priv *vlan; | 349 | struct vlan_dev_priv *vlan; |
350 | bool last = false; | ||
344 | LIST_HEAD(list); | 351 | LIST_HEAD(list); |
345 | 352 | ||
346 | if (is_vlan_dev(dev)) | 353 | if (is_vlan_dev(dev)) |
347 | __vlan_device_event(dev, event); | 354 | __vlan_device_event(dev, event); |
348 | 355 | ||
349 | if ((event == NETDEV_UP) && | 356 | if ((event == NETDEV_UP) && |
350 | (dev->features & NETIF_F_HW_VLAN_FILTER)) { | 357 | (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { |
351 | pr_info("adding VLAN 0 to HW filter on device %s\n", | 358 | pr_info("adding VLAN 0 to HW filter on device %s\n", |
352 | dev->name); | 359 | dev->name); |
353 | vlan_vid_add(dev, 0); | 360 | vlan_vid_add(dev, htons(ETH_P_8021Q), 0); |
354 | } | 361 | } |
355 | 362 | ||
356 | vlan_info = rtnl_dereference(dev->vlan_info); | 363 | vlan_info = rtnl_dereference(dev->vlan_info); |
@@ -365,22 +372,13 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
365 | switch (event) { | 372 | switch (event) { |
366 | case NETDEV_CHANGE: | 373 | case NETDEV_CHANGE: |
367 | /* Propagate real device state to vlan devices */ | 374 | /* Propagate real device state to vlan devices */ |
368 | for (i = 0; i < VLAN_N_VID; i++) { | 375 | vlan_group_for_each_dev(grp, i, vlandev) |
369 | vlandev = vlan_group_get_device(grp, i); | ||
370 | if (!vlandev) | ||
371 | continue; | ||
372 | |||
373 | netif_stacked_transfer_operstate(dev, vlandev); | 376 | netif_stacked_transfer_operstate(dev, vlandev); |
374 | } | ||
375 | break; | 377 | break; |
376 | 378 | ||
377 | case NETDEV_CHANGEADDR: | 379 | case NETDEV_CHANGEADDR: |
378 | /* Adjust unicast filters on underlying device */ | 380 | /* Adjust unicast filters on underlying device */ |
379 | for (i = 0; i < VLAN_N_VID; i++) { | 381 | vlan_group_for_each_dev(grp, i, vlandev) { |
380 | vlandev = vlan_group_get_device(grp, i); | ||
381 | if (!vlandev) | ||
382 | continue; | ||
383 | |||
384 | flgs = vlandev->flags; | 382 | flgs = vlandev->flags; |
385 | if (!(flgs & IFF_UP)) | 383 | if (!(flgs & IFF_UP)) |
386 | continue; | 384 | continue; |
@@ -390,11 +388,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
390 | break; | 388 | break; |
391 | 389 | ||
392 | case NETDEV_CHANGEMTU: | 390 | case NETDEV_CHANGEMTU: |
393 | for (i = 0; i < VLAN_N_VID; i++) { | 391 | vlan_group_for_each_dev(grp, i, vlandev) { |
394 | vlandev = vlan_group_get_device(grp, i); | ||
395 | if (!vlandev) | ||
396 | continue; | ||
397 | |||
398 | if (vlandev->mtu <= dev->mtu) | 392 | if (vlandev->mtu <= dev->mtu) |
399 | continue; | 393 | continue; |
400 | 394 | ||
@@ -404,26 +398,16 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
404 | 398 | ||
405 | case NETDEV_FEAT_CHANGE: | 399 | case NETDEV_FEAT_CHANGE: |
406 | /* Propagate device features to underlying device */ | 400 | /* Propagate device features to underlying device */ |
407 | for (i = 0; i < VLAN_N_VID; i++) { | 401 | vlan_group_for_each_dev(grp, i, vlandev) |
408 | vlandev = vlan_group_get_device(grp, i); | ||
409 | if (!vlandev) | ||
410 | continue; | ||
411 | |||
412 | vlan_transfer_features(dev, vlandev); | 402 | vlan_transfer_features(dev, vlandev); |
413 | } | ||
414 | |||
415 | break; | 403 | break; |
416 | 404 | ||
417 | case NETDEV_DOWN: | 405 | case NETDEV_DOWN: |
418 | if (dev->features & NETIF_F_HW_VLAN_FILTER) | 406 | if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) |
419 | vlan_vid_del(dev, 0); | 407 | vlan_vid_del(dev, htons(ETH_P_8021Q), 0); |
420 | 408 | ||
421 | /* Put all VLANs for this dev in the down state too. */ | 409 | /* Put all VLANs for this dev in the down state too. */ |
422 | for (i = 0; i < VLAN_N_VID; i++) { | 410 | vlan_group_for_each_dev(grp, i, vlandev) { |
423 | vlandev = vlan_group_get_device(grp, i); | ||
424 | if (!vlandev) | ||
425 | continue; | ||
426 | |||
427 | flgs = vlandev->flags; | 411 | flgs = vlandev->flags; |
428 | if (!(flgs & IFF_UP)) | 412 | if (!(flgs & IFF_UP)) |
429 | continue; | 413 | continue; |
@@ -437,11 +421,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
437 | 421 | ||
438 | case NETDEV_UP: | 422 | case NETDEV_UP: |
439 | /* Put all VLANs for this dev in the up state too. */ | 423 | /* Put all VLANs for this dev in the up state too. */ |
440 | for (i = 0; i < VLAN_N_VID; i++) { | 424 | vlan_group_for_each_dev(grp, i, vlandev) { |
441 | vlandev = vlan_group_get_device(grp, i); | ||
442 | if (!vlandev) | ||
443 | continue; | ||
444 | |||
445 | flgs = vlandev->flags; | 425 | flgs = vlandev->flags; |
446 | if (flgs & IFF_UP) | 426 | if (flgs & IFF_UP) |
447 | continue; | 427 | continue; |
@@ -458,17 +438,15 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
458 | if (dev->reg_state != NETREG_UNREGISTERING) | 438 | if (dev->reg_state != NETREG_UNREGISTERING) |
459 | break; | 439 | break; |
460 | 440 | ||
461 | for (i = 0; i < VLAN_N_VID; i++) { | 441 | vlan_group_for_each_dev(grp, i, vlandev) { |
462 | vlandev = vlan_group_get_device(grp, i); | ||
463 | if (!vlandev) | ||
464 | continue; | ||
465 | |||
466 | /* removal of last vid destroys vlan_info, abort | 442 | /* removal of last vid destroys vlan_info, abort |
467 | * afterwards */ | 443 | * afterwards */ |
468 | if (vlan_info->nr_vids == 1) | 444 | if (vlan_info->nr_vids == 1) |
469 | i = VLAN_N_VID; | 445 | last = true; |
470 | 446 | ||
471 | unregister_vlan_dev(vlandev, &list); | 447 | unregister_vlan_dev(vlandev, &list); |
448 | if (last) | ||
449 | break; | ||
472 | } | 450 | } |
473 | unregister_netdevice_many(&list); | 451 | unregister_netdevice_many(&list); |
474 | break; | 452 | break; |
@@ -482,13 +460,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
482 | case NETDEV_NOTIFY_PEERS: | 460 | case NETDEV_NOTIFY_PEERS: |
483 | case NETDEV_BONDING_FAILOVER: | 461 | case NETDEV_BONDING_FAILOVER: |
484 | /* Propagate to vlan devices */ | 462 | /* Propagate to vlan devices */ |
485 | for (i = 0; i < VLAN_N_VID; i++) { | 463 | vlan_group_for_each_dev(grp, i, vlandev) |
486 | vlandev = vlan_group_get_device(grp, i); | ||
487 | if (!vlandev) | ||
488 | continue; | ||
489 | |||
490 | call_netdevice_notifiers(event, vlandev); | 464 | call_netdevice_notifiers(event, vlandev); |
491 | } | ||
492 | break; | 465 | break; |
493 | } | 466 | } |
494 | 467 | ||