Merge remote-tracking branch 'lsk/v3.10/topic/arm64-misc' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / net / 8021q / vlan_dev.c
index cf35f383db4ca3e554ca4a115723a1c1663e78d1..698e922f41ea154fff497150da46b4b2543d7aa3 100644 (file)
@@ -512,10 +512,48 @@ static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
        }
 }
 
+static int vlan_calculate_locking_subclass(struct net_device *real_dev)
+{
+       int subclass = 0;
+
+       while (is_vlan_dev(real_dev)) {
+               subclass++;
+               real_dev = vlan_dev_priv(real_dev)->real_dev;
+       }
+
+       return subclass;
+}
+
+static void vlan_dev_mc_sync(struct net_device *to, struct net_device *from)
+{
+       int err = 0, subclass;
+
+       subclass = vlan_calculate_locking_subclass(to);
+
+       spin_lock_nested(&to->addr_list_lock, subclass);
+       err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
+       if (!err)
+               __dev_set_rx_mode(to);
+       spin_unlock(&to->addr_list_lock);
+}
+
+static void vlan_dev_uc_sync(struct net_device *to, struct net_device *from)
+{
+       int err = 0, subclass;
+
+       subclass = vlan_calculate_locking_subclass(to);
+
+       spin_lock_nested(&to->addr_list_lock, subclass);
+       err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
+       if (!err)
+               __dev_set_rx_mode(to);
+       spin_unlock(&to->addr_list_lock);
+}
+
 static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
 {
-       dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
-       dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
+       vlan_dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
+       vlan_dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
 }
 
 /*
@@ -624,9 +662,7 @@ static int vlan_dev_init(struct net_device *dev)
 
        SET_NETDEV_DEVTYPE(dev, &vlan_type);
 
-       if (is_vlan_dev(real_dev))
-               subclass = 1;
-
+       subclass = vlan_calculate_locking_subclass(dev);
        vlan_dev_set_lockdep_class(dev, subclass);
 
        vlan_dev_priv(dev)->vlan_pcpu_stats = alloc_percpu(struct vlan_pcpu_stats);