net: vlan: prepare for 802.1ad support
[deliverable/linux.git] / net / 8021q / vlan.c
index 447c5c93434fb04aebf1892834cecc682f96bf9f..9424f3718ea703adc1019193a30352ad95981e6a 100644 (file)
@@ -51,14 +51,18 @@ const char vlan_version[] = DRV_VERSION;
 
 /* End of global variables definitions. */
 
-static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
+static int vlan_group_prealloc_vid(struct vlan_group *vg,
+                                  __be16 vlan_proto, u16 vlan_id)
 {
        struct net_device **array;
+       unsigned int pidx, vidx;
        unsigned int size;
 
        ASSERT_RTNL();
 
-       array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
+       pidx  = vlan_proto_idx(vlan_proto);
+       vidx  = vlan_id / VLAN_GROUP_ARRAY_PART_LEN;
+       array = vg->vlan_devices_arrays[pidx][vidx];
        if (array != NULL)
                return 0;
 
@@ -67,7 +71,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id)
        if (array == NULL)
                return -ENOBUFS;
 
-       vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array;
+       vg->vlan_devices_arrays[pidx][vidx] = array;
        return 0;
 }
 
@@ -93,7 +97,7 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
        if (vlan->flags & VLAN_FLAG_GVRP)
                vlan_gvrp_request_leave(dev);
 
-       vlan_group_set_device(grp, vlan_id, NULL);
+       vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL);
        /* Because unregister_netdevice_queue() makes sure at least one rcu
         * grace period is respected before device freeing,
         * we dont need to call synchronize_net() here.
@@ -112,13 +116,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
         * VLAN is not 0 (leave it there for 802.1p).
         */
        if (vlan_id)
-               vlan_vid_del(real_dev, htons(ETH_P_8021Q), vlan_id);
+               vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
 
        /* Get rid of the vlan's reference to real_dev */
        dev_put(real_dev);
 }
 
-int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
+int vlan_check_real_dev(struct net_device *real_dev,
+                       __be16 protocol, u16 vlan_id)
 {
        const char *name = real_dev->name;
 
@@ -127,7 +132,7 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
                return -EOPNOTSUPP;
        }
 
-       if (vlan_find_dev(real_dev, vlan_id) != NULL)
+       if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL)
                return -EEXIST;
 
        return 0;
@@ -142,7 +147,7 @@ int register_vlan_dev(struct net_device *dev)
        struct vlan_group *grp;
        int err;
 
-       err = vlan_vid_add(real_dev, htons(ETH_P_8021Q), vlan_id);
+       err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id);
        if (err)
                return err;
 
@@ -160,7 +165,7 @@ int register_vlan_dev(struct net_device *dev)
                        goto out_uninit_gvrp;
        }
 
-       err = vlan_group_prealloc_vid(grp, vlan_id);
+       err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id);
        if (err < 0)
                goto out_uninit_mvrp;
 
@@ -181,7 +186,7 @@ int register_vlan_dev(struct net_device *dev)
        /* So, got the sucker initialized, now lets place
         * it into our local structure.
         */
-       vlan_group_set_device(grp, vlan_id, dev);
+       vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev);
        grp->nr_vlan_devs++;
 
        return 0;
@@ -195,7 +200,7 @@ out_uninit_gvrp:
        if (grp->nr_vlan_devs == 0)
                vlan_gvrp_uninit_applicant(real_dev);
 out_vid_del:
-       vlan_vid_del(real_dev, htons(ETH_P_8021Q), vlan_id);
+       vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
        return err;
 }
 
@@ -213,7 +218,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        if (vlan_id >= VLAN_VID_MASK)
                return -ERANGE;
 
-       err = vlan_check_real_dev(real_dev, vlan_id);
+       err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id);
        if (err < 0)
                return err;
 
@@ -255,6 +260,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        new_dev->mtu = real_dev->mtu;
        new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT);
 
+       vlan_dev_priv(new_dev)->vlan_proto = htons(ETH_P_8021Q);
        vlan_dev_priv(new_dev)->vlan_id = vlan_id;
        vlan_dev_priv(new_dev)->real_dev = real_dev;
        vlan_dev_priv(new_dev)->dent = NULL;
@@ -341,6 +347,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        int i, flgs;
        struct net_device *vlandev;
        struct vlan_dev_priv *vlan;
+       bool last = false;
        LIST_HEAD(list);
 
        if (is_vlan_dev(dev))
@@ -365,22 +372,13 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        switch (event) {
        case NETDEV_CHANGE:
                /* Propagate real device state to vlan devices */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        netif_stacked_transfer_operstate(dev, vlandev);
-               }
                break;
 
        case NETDEV_CHANGEADDR:
                /* Adjust unicast filters on underlying device */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (!(flgs & IFF_UP))
                                continue;
@@ -390,11 +388,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                break;
 
        case NETDEV_CHANGEMTU:
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        if (vlandev->mtu <= dev->mtu)
                                continue;
 
@@ -404,14 +398,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
        case NETDEV_FEAT_CHANGE:
                /* Propagate device features to underlying device */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        vlan_transfer_features(dev, vlandev);
-               }
-
                break;
 
        case NETDEV_DOWN:
@@ -419,11 +407,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                        vlan_vid_del(dev, htons(ETH_P_8021Q), 0);
 
                /* Put all VLANs for this dev in the down state too.  */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (!(flgs & IFF_UP))
                                continue;
@@ -437,11 +421,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 
        case NETDEV_UP:
                /* Put all VLANs for this dev in the up state too.  */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        flgs = vlandev->flags;
                        if (flgs & IFF_UP)
                                continue;
@@ -458,17 +438,15 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
                if (dev->reg_state != NETREG_UNREGISTERING)
                        break;
 
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev) {
                        /* removal of last vid destroys vlan_info, abort
                         * afterwards */
                        if (vlan_info->nr_vids == 1)
-                               i = VLAN_N_VID;
+                               last = true;
 
                        unregister_vlan_dev(vlandev, &list);
+                       if (last)
+                               break;
                }
                unregister_netdevice_many(&list);
                break;
@@ -482,13 +460,8 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
        case NETDEV_NOTIFY_PEERS:
        case NETDEV_BONDING_FAILOVER:
                /* Propagate to vlan devices */
-               for (i = 0; i < VLAN_N_VID; i++) {
-                       vlandev = vlan_group_get_device(grp, i);
-                       if (!vlandev)
-                               continue;
-
+               vlan_group_for_each_dev(grp, i, vlandev)
                        call_netdevice_notifiers(event, vlandev);
-               }
                break;
        }
 
This page took 0.042479 seconds and 5 git commands to generate.