diff options
| author | Jiri Pirko <jpirko@redhat.com> | 2011-12-07 23:11:18 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-12-08 19:52:42 -0500 |
| commit | 5b9ea6e022e9ba0fe39cb349ac40361f78d5da5b (patch) | |
| tree | 11f0de492ee799fd4174f79ac6aae4c3533beb25 /net/8021q | |
| parent | 87002b03baabd2b8f6281ab6411ed88d24958de1 (diff) | |
vlan: introduce vid list with reference counting
This allows to keep track of vids needed to be in rx vlan filters of
devices even if they are used in bond/team etc.
vlan_info as well as vlan_group previously was, is allocated when first
vid is added and dealocated whan last vid is deleted.
vlan_group definition is moved to private header.
Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/8021q')
| -rw-r--r-- | net/8021q/vlan.c | 90 | ||||
| -rw-r--r-- | net/8021q/vlan.h | 30 | ||||
| -rw-r--r-- | net/8021q/vlan_core.c | 168 |
3 files changed, 217 insertions, 71 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index dd9aa400888b..efea35b02e7f 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
| @@ -51,27 +51,6 @@ const char vlan_version[] = DRV_VERSION; | |||
| 51 | 51 | ||
| 52 | /* End of global variables definitions. */ | 52 | /* End of global variables definitions. */ |
| 53 | 53 | ||
| 54 | static void vlan_group_free(struct vlan_group *grp) | ||
| 55 | { | ||
| 56 | int i; | ||
| 57 | |||
| 58 | for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) | ||
| 59 | kfree(grp->vlan_devices_arrays[i]); | ||
| 60 | kfree(grp); | ||
| 61 | } | ||
| 62 | |||
| 63 | static struct vlan_group *vlan_group_alloc(struct net_device *real_dev) | ||
| 64 | { | ||
| 65 | struct vlan_group *grp; | ||
| 66 | |||
| 67 | grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL); | ||
| 68 | if (!grp) | ||
| 69 | return NULL; | ||
| 70 | |||
| 71 | grp->real_dev = real_dev; | ||
| 72 | return grp; | ||
| 73 | } | ||
| 74 | |||
| 75 | static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) | 54 | static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) |
| 76 | { | 55 | { |
| 77 | struct net_device **array; | 56 | struct net_device **array; |
| @@ -92,22 +71,20 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) | |||
| 92 | return 0; | 71 | return 0; |
| 93 | } | 72 | } |
| 94 | 73 | ||
| 95 | static void vlan_rcu_free(struct rcu_head *rcu) | ||
| 96 | { | ||
| 97 | vlan_group_free(container_of(rcu, struct vlan_group, rcu)); | ||
| 98 | } | ||
| 99 | |||
| 100 | void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | 74 | void unregister_vlan_dev(struct net_device *dev, struct list_head *head) |
| 101 | { | 75 | { |
| 102 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 76 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
| 103 | struct net_device *real_dev = vlan->real_dev; | 77 | struct net_device *real_dev = vlan->real_dev; |
| 78 | struct vlan_info *vlan_info; | ||
| 104 | struct vlan_group *grp; | 79 | struct vlan_group *grp; |
| 105 | u16 vlan_id = vlan->vlan_id; | 80 | u16 vlan_id = vlan->vlan_id; |
| 106 | 81 | ||
| 107 | ASSERT_RTNL(); | 82 | ASSERT_RTNL(); |
| 108 | 83 | ||
| 109 | grp = rtnl_dereference(real_dev->vlgrp); | 84 | vlan_info = rtnl_dereference(real_dev->vlan_info); |
| 110 | BUG_ON(!grp); | 85 | BUG_ON(!vlan_info); |
| 86 | |||
| 87 | grp = &vlan_info->grp; | ||
| 111 | 88 | ||
| 112 | /* Take it out of our own structures, but be sure to interlock with | 89 | /* Take it out of our own structures, but be sure to interlock with |
| 113 | * HW accelerating devices or SW vlan input packet processing if | 90 | * HW accelerating devices or SW vlan input packet processing if |
| @@ -116,7 +93,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
| 116 | if (vlan_id) | 93 | if (vlan_id) |
| 117 | vlan_vid_del(real_dev, vlan_id); | 94 | vlan_vid_del(real_dev, vlan_id); |
| 118 | 95 | ||
| 119 | grp->nr_vlans--; | 96 | grp->nr_vlan_devs--; |
| 120 | 97 | ||
| 121 | if (vlan->flags & VLAN_FLAG_GVRP) | 98 | if (vlan->flags & VLAN_FLAG_GVRP) |
| 122 | vlan_gvrp_request_leave(dev); | 99 | vlan_gvrp_request_leave(dev); |
| @@ -128,16 +105,9 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) | |||
| 128 | */ | 105 | */ |
| 129 | unregister_netdevice_queue(dev, head); | 106 | unregister_netdevice_queue(dev, head); |
| 130 | 107 | ||
| 131 | /* If the group is now empty, kill off the group. */ | 108 | if (grp->nr_vlan_devs == 0) |
| 132 | if (grp->nr_vlans == 0) { | ||
| 133 | vlan_gvrp_uninit_applicant(real_dev); | 109 | vlan_gvrp_uninit_applicant(real_dev); |
| 134 | 110 | ||
| 135 | RCU_INIT_POINTER(real_dev->vlgrp, NULL); | ||
| 136 | |||
| 137 | /* Free the group, after all cpu's are done. */ | ||
| 138 | call_rcu(&grp->rcu, vlan_rcu_free); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Get rid of the vlan's reference to real_dev */ | 111 | /* Get rid of the vlan's reference to real_dev */ |
| 142 | dev_put(real_dev); | 112 | dev_put(real_dev); |
| 143 | } | 113 | } |
| @@ -169,17 +139,23 @@ int register_vlan_dev(struct net_device *dev) | |||
| 169 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 139 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
| 170 | struct net_device *real_dev = vlan->real_dev; | 140 | struct net_device *real_dev = vlan->real_dev; |
| 171 | u16 vlan_id = vlan->vlan_id; | 141 | u16 vlan_id = vlan->vlan_id; |
| 172 | struct vlan_group *grp, *ngrp = NULL; | 142 | struct vlan_info *vlan_info; |
| 143 | struct vlan_group *grp; | ||
| 173 | int err; | 144 | int err; |
| 174 | 145 | ||
| 175 | grp = rtnl_dereference(real_dev->vlgrp); | 146 | err = vlan_vid_add(real_dev, vlan_id); |
| 176 | if (!grp) { | 147 | if (err) |
| 177 | ngrp = grp = vlan_group_alloc(real_dev); | 148 | return err; |
| 178 | if (!grp) | 149 | |
| 179 | return -ENOBUFS; | 150 | vlan_info = rtnl_dereference(real_dev->vlan_info); |
| 151 | /* vlan_info should be there now. vlan_vid_add took care of it */ | ||
| 152 | BUG_ON(!vlan_info); | ||
| 153 | |||
| 154 | grp = &vlan_info->grp; | ||
| 155 | if (grp->nr_vlan_devs == 0) { | ||
| 180 | err = vlan_gvrp_init_applicant(real_dev); | 156 | err = vlan_gvrp_init_applicant(real_dev); |
| 181 | if (err < 0) | 157 | if (err < 0) |
| 182 | goto out_free_group; | 158 | goto out_vid_del; |
| 183 | } | 159 | } |
| 184 | 160 | ||
| 185 | err = vlan_group_prealloc_vid(grp, vlan_id); | 161 | err = vlan_group_prealloc_vid(grp, vlan_id); |
| @@ -200,23 +176,15 @@ int register_vlan_dev(struct net_device *dev) | |||
| 200 | * it into our local structure. | 176 | * it into our local structure. |
| 201 | */ | 177 | */ |
| 202 | vlan_group_set_device(grp, vlan_id, dev); | 178 | vlan_group_set_device(grp, vlan_id, dev); |
| 203 | grp->nr_vlans++; | 179 | grp->nr_vlan_devs++; |
| 204 | |||
| 205 | if (ngrp) { | ||
| 206 | rcu_assign_pointer(real_dev->vlgrp, ngrp); | ||
| 207 | } | ||
| 208 | vlan_vid_add(real_dev, vlan_id); | ||
| 209 | 180 | ||
| 210 | return 0; | 181 | return 0; |
| 211 | 182 | ||
| 212 | out_uninit_applicant: | 183 | out_uninit_applicant: |
| 213 | if (ngrp) | 184 | if (grp->nr_vlan_devs == 0) |
| 214 | vlan_gvrp_uninit_applicant(real_dev); | 185 | vlan_gvrp_uninit_applicant(real_dev); |
| 215 | out_free_group: | 186 | out_vid_del: |
| 216 | if (ngrp) { | 187 | vlan_vid_del(real_dev, vlan_id); |
| 217 | /* Free the group, after all cpu's are done. */ | ||
| 218 | call_rcu(&ngrp->rcu, vlan_rcu_free); | ||
| 219 | } | ||
| 220 | return err; | 188 | return err; |
| 221 | } | 189 | } |
| 222 | 190 | ||
| @@ -357,6 +325,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
| 357 | { | 325 | { |
| 358 | struct net_device *dev = ptr; | 326 | struct net_device *dev = ptr; |
| 359 | struct vlan_group *grp; | 327 | struct vlan_group *grp; |
| 328 | struct vlan_info *vlan_info; | ||
| 360 | int i, flgs; | 329 | int i, flgs; |
| 361 | struct net_device *vlandev; | 330 | struct net_device *vlandev; |
| 362 | struct vlan_dev_priv *vlan; | 331 | struct vlan_dev_priv *vlan; |
| @@ -372,9 +341,10 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
| 372 | vlan_vid_add(dev, 0); | 341 | vlan_vid_add(dev, 0); |
| 373 | } | 342 | } |
| 374 | 343 | ||
| 375 | grp = rtnl_dereference(dev->vlgrp); | 344 | vlan_info = rtnl_dereference(dev->vlan_info); |
| 376 | if (!grp) | 345 | if (!vlan_info) |
| 377 | goto out; | 346 | goto out; |
| 347 | grp = &vlan_info->grp; | ||
| 378 | 348 | ||
| 379 | /* It is OK that we do not hold the group lock right now, | 349 | /* It is OK that we do not hold the group lock right now, |
| 380 | * as we run under the RTNL lock. | 350 | * as we run under the RTNL lock. |
| @@ -478,9 +448,9 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
| 478 | if (!vlandev) | 448 | if (!vlandev) |
| 479 | continue; | 449 | continue; |
| 480 | 450 | ||
| 481 | /* unregistration of last vlan destroys group, abort | 451 | /* removal of last vid destroys vlan_info, abort |
| 482 | * afterwards */ | 452 | * afterwards */ |
| 483 | if (grp->nr_vlans == 1) | 453 | if (vlan_info->nr_vids == 1) |
| 484 | i = VLAN_N_VID; | 454 | i = VLAN_N_VID; |
| 485 | 455 | ||
| 486 | unregister_vlan_dev(vlandev, &list); | 456 | unregister_vlan_dev(vlandev, &list); |
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index d3c4ea4a3836..28d8dc20cb6d 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <linux/if_vlan.h> | 4 | #include <linux/if_vlan.h> |
| 5 | #include <linux/u64_stats_sync.h> | 5 | #include <linux/u64_stats_sync.h> |
| 6 | #include <linux/list.h> | ||
| 6 | 7 | ||
| 7 | 8 | ||
| 8 | /** | 9 | /** |
| @@ -74,6 +75,29 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) | |||
| 74 | return netdev_priv(dev); | 75 | return netdev_priv(dev); |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 78 | /* if this changes, algorithm will have to be reworked because this | ||
| 79 | * depends on completely exhausting the VLAN identifier space. Thus | ||
| 80 | * it gives constant time look-up, but in many cases it wastes memory. | ||
| 81 | */ | ||
| 82 | #define VLAN_GROUP_ARRAY_SPLIT_PARTS 8 | ||
| 83 | #define VLAN_GROUP_ARRAY_PART_LEN (VLAN_N_VID/VLAN_GROUP_ARRAY_SPLIT_PARTS) | ||
| 84 | |||
| 85 | struct vlan_group { | ||
| 86 | unsigned int nr_vlan_devs; | ||
| 87 | struct hlist_node hlist; /* linked list */ | ||
| 88 | struct net_device **vlan_devices_arrays[VLAN_GROUP_ARRAY_SPLIT_PARTS]; | ||
| 89 | }; | ||
| 90 | |||
| 91 | struct vlan_info { | ||
| 92 | struct net_device *real_dev; /* The ethernet(like) device | ||
| 93 | * the vlan is attached to. | ||
| 94 | */ | ||
| 95 | struct vlan_group grp; | ||
| 96 | struct list_head vid_list; | ||
| 97 | unsigned int nr_vids; | ||
| 98 | struct rcu_head rcu; | ||
| 99 | }; | ||
| 100 | |||
| 77 | static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, | 101 | static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, |
| 78 | u16 vlan_id) | 102 | u16 vlan_id) |
| 79 | { | 103 | { |
| @@ -97,10 +121,10 @@ static inline void vlan_group_set_device(struct vlan_group *vg, | |||
| 97 | static inline struct net_device *vlan_find_dev(struct net_device *real_dev, | 121 | static inline struct net_device *vlan_find_dev(struct net_device *real_dev, |
| 98 | u16 vlan_id) | 122 | u16 vlan_id) |
| 99 | { | 123 | { |
| 100 | struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp); | 124 | struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); |
| 101 | 125 | ||
| 102 | if (grp) | 126 | if (vlan_info) |
| 103 | return vlan_group_get_device(grp, vlan_id); | 127 | return vlan_group_get_device(&vlan_info->grp, vlan_id); |
| 104 | 128 | ||
| 105 | return NULL; | 129 | return NULL; |
| 106 | } | 130 | } |
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 544f9cb9678c..329e0313e01f 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c | |||
| @@ -71,10 +71,10 @@ bool vlan_do_receive(struct sk_buff **skbp, bool last_handler) | |||
| 71 | struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, | 71 | struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, |
| 72 | u16 vlan_id) | 72 | u16 vlan_id) |
| 73 | { | 73 | { |
| 74 | struct vlan_group *grp = rcu_dereference_rtnl(real_dev->vlgrp); | 74 | struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); |
| 75 | 75 | ||
| 76 | if (grp) { | 76 | if (vlan_info) { |
| 77 | return vlan_group_get_device(grp, vlan_id); | 77 | return vlan_group_get_device(&vlan_info->grp, vlan_id); |
| 78 | } else { | 78 | } else { |
| 79 | /* | 79 | /* |
| 80 | * Bonding slaves do not have grp assigned to themselves. | 80 | * Bonding slaves do not have grp assigned to themselves. |
| @@ -147,25 +147,177 @@ err_free: | |||
| 147 | return NULL; | 147 | return NULL; |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | int vlan_vid_add(struct net_device *dev, unsigned short vid) | 150 | |
| 151 | /* | ||
| 152 | * vlan info and vid list | ||
| 153 | */ | ||
| 154 | |||
| 155 | static void vlan_group_free(struct vlan_group *grp) | ||
| 156 | { | ||
| 157 | int i; | ||
| 158 | |||
| 159 | for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) | ||
| 160 | kfree(grp->vlan_devices_arrays[i]); | ||
| 161 | } | ||
| 162 | |||
| 163 | static void vlan_info_free(struct vlan_info *vlan_info) | ||
| 164 | { | ||
| 165 | vlan_group_free(&vlan_info->grp); | ||
| 166 | kfree(vlan_info); | ||
| 167 | } | ||
| 168 | |||
| 169 | static void vlan_info_rcu_free(struct rcu_head *rcu) | ||
| 170 | { | ||
| 171 | vlan_info_free(container_of(rcu, struct vlan_info, rcu)); | ||
| 172 | } | ||
| 173 | |||
| 174 | static struct vlan_info *vlan_info_alloc(struct net_device *dev) | ||
| 175 | { | ||
| 176 | struct vlan_info *vlan_info; | ||
| 177 | |||
| 178 | vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL); | ||
| 179 | if (!vlan_info) | ||
| 180 | return NULL; | ||
| 181 | |||
| 182 | vlan_info->real_dev = dev; | ||
| 183 | INIT_LIST_HEAD(&vlan_info->vid_list); | ||
| 184 | return vlan_info; | ||
| 185 | } | ||
| 186 | |||
| 187 | struct vlan_vid_info { | ||
| 188 | struct list_head list; | ||
| 189 | unsigned short vid; | ||
| 190 | int refcount; | ||
| 191 | }; | ||
| 192 | |||
| 193 | static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, | ||
| 194 | unsigned short vid) | ||
| 195 | { | ||
| 196 | struct vlan_vid_info *vid_info; | ||
| 197 | |||
| 198 | list_for_each_entry(vid_info, &vlan_info->vid_list, list) { | ||
| 199 | if (vid_info->vid == vid) | ||
| 200 | return vid_info; | ||
| 201 | } | ||
| 202 | return NULL; | ||
| 203 | } | ||
| 204 | |||
| 205 | static struct vlan_vid_info *vlan_vid_info_alloc(unsigned short vid) | ||
| 206 | { | ||
| 207 | struct vlan_vid_info *vid_info; | ||
| 208 | |||
| 209 | vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL); | ||
| 210 | if (!vid_info) | ||
| 211 | return NULL; | ||
| 212 | vid_info->vid = vid; | ||
| 213 | |||
| 214 | return vid_info; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int __vlan_vid_add(struct vlan_info *vlan_info, unsigned short vid, | ||
| 218 | struct vlan_vid_info **pvid_info) | ||
| 151 | { | 219 | { |
| 220 | struct net_device *dev = vlan_info->real_dev; | ||
| 152 | const struct net_device_ops *ops = dev->netdev_ops; | 221 | const struct net_device_ops *ops = dev->netdev_ops; |
| 222 | struct vlan_vid_info *vid_info; | ||
| 223 | int err; | ||
| 224 | |||
| 225 | vid_info = vlan_vid_info_alloc(vid); | ||
| 226 | if (!vid_info) | ||
| 227 | return -ENOMEM; | ||
| 153 | 228 | ||
| 154 | if ((dev->features & NETIF_F_HW_VLAN_FILTER) && | 229 | if ((dev->features & NETIF_F_HW_VLAN_FILTER) && |
| 155 | ops->ndo_vlan_rx_add_vid) { | 230 | ops->ndo_vlan_rx_add_vid) { |
| 156 | return ops->ndo_vlan_rx_add_vid(dev, vid); | 231 | err = ops->ndo_vlan_rx_add_vid(dev, vid); |
| 232 | if (err) { | ||
| 233 | kfree(vid_info); | ||
| 234 | return err; | ||
| 235 | } | ||
| 157 | } | 236 | } |
| 237 | list_add(&vid_info->list, &vlan_info->vid_list); | ||
| 238 | vlan_info->nr_vids++; | ||
| 239 | *pvid_info = vid_info; | ||
| 158 | return 0; | 240 | return 0; |
| 159 | } | 241 | } |
| 242 | |||
| 243 | int vlan_vid_add(struct net_device *dev, unsigned short vid) | ||
| 244 | { | ||
| 245 | struct vlan_info *vlan_info; | ||
| 246 | struct vlan_vid_info *vid_info; | ||
| 247 | bool vlan_info_created = false; | ||
| 248 | int err; | ||
| 249 | |||
| 250 | ASSERT_RTNL(); | ||
| 251 | |||
| 252 | vlan_info = rtnl_dereference(dev->vlan_info); | ||
| 253 | if (!vlan_info) { | ||
| 254 | vlan_info = vlan_info_alloc(dev); | ||
| 255 | if (!vlan_info) | ||
| 256 | return -ENOMEM; | ||
| 257 | vlan_info_created = true; | ||
| 258 | } | ||
| 259 | vid_info = vlan_vid_info_get(vlan_info, vid); | ||
| 260 | if (!vid_info) { | ||
| 261 | err = __vlan_vid_add(vlan_info, vid, &vid_info); | ||
| 262 | if (err) | ||
| 263 | goto out_free_vlan_info; | ||
| 264 | } | ||
| 265 | vid_info->refcount++; | ||
| 266 | |||
| 267 | if (vlan_info_created) | ||
| 268 | rcu_assign_pointer(dev->vlan_info, vlan_info); | ||
| 269 | |||
| 270 | return 0; | ||
| 271 | |||
| 272 | out_free_vlan_info: | ||
| 273 | if (vlan_info_created) | ||
| 274 | kfree(vlan_info); | ||
| 275 | return err; | ||
| 276 | } | ||
| 160 | EXPORT_SYMBOL(vlan_vid_add); | 277 | EXPORT_SYMBOL(vlan_vid_add); |
| 161 | 278 | ||
| 162 | void vlan_vid_del(struct net_device *dev, unsigned short vid) | 279 | static void __vlan_vid_del(struct vlan_info *vlan_info, |
| 280 | struct vlan_vid_info *vid_info) | ||
| 163 | { | 281 | { |
| 282 | struct net_device *dev = vlan_info->real_dev; | ||
| 164 | const struct net_device_ops *ops = dev->netdev_ops; | 283 | const struct net_device_ops *ops = dev->netdev_ops; |
| 284 | unsigned short vid = vid_info->vid; | ||
| 285 | int err; | ||
| 165 | 286 | ||
| 166 | if ((dev->features & NETIF_F_HW_VLAN_FILTER) && | 287 | if ((dev->features & NETIF_F_HW_VLAN_FILTER) && |
| 167 | ops->ndo_vlan_rx_kill_vid) { | 288 | ops->ndo_vlan_rx_kill_vid) { |
| 168 | ops->ndo_vlan_rx_kill_vid(dev, vid); | 289 | err = ops->ndo_vlan_rx_kill_vid(dev, vid); |
| 290 | if (err) { | ||
| 291 | pr_warn("failed to kill vid %d for device %s\n", | ||
| 292 | vid, dev->name); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | list_del(&vid_info->list); | ||
| 296 | kfree(vid_info); | ||
| 297 | vlan_info->nr_vids--; | ||
| 298 | } | ||
| 299 | |||
| 300 | void vlan_vid_del(struct net_device *dev, unsigned short vid) | ||
| 301 | { | ||
| 302 | struct vlan_info *vlan_info; | ||
| 303 | struct vlan_vid_info *vid_info; | ||
| 304 | |||
| 305 | ASSERT_RTNL(); | ||
| 306 | |||
| 307 | vlan_info = rtnl_dereference(dev->vlan_info); | ||
| 308 | if (!vlan_info) | ||
| 309 | return; | ||
| 310 | |||
| 311 | vid_info = vlan_vid_info_get(vlan_info, vid); | ||
| 312 | if (!vid_info) | ||
| 313 | return; | ||
| 314 | vid_info->refcount--; | ||
| 315 | if (vid_info->refcount == 0) { | ||
| 316 | __vlan_vid_del(vlan_info, vid_info); | ||
| 317 | if (vlan_info->nr_vids == 0) { | ||
| 318 | RCU_INIT_POINTER(dev->vlan_info, NULL); | ||
| 319 | call_rcu(&vlan_info->rcu, vlan_info_rcu_free); | ||
| 320 | } | ||
| 169 | } | 321 | } |
| 170 | } | 322 | } |
| 171 | EXPORT_SYMBOL(vlan_vid_del); | 323 | EXPORT_SYMBOL(vlan_vid_del); |
