OSDN Git Service

Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma
[android-x86/kernel.git] / drivers / net / ethernet / mellanox / mlx4 / port.c
index 021fad6..787b7bb 100644 (file)
@@ -61,6 +61,7 @@ void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table)
        for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
                table->entries[i] = 0;
                table->refs[i]   = 0;
+               table->is_dup[i] = false;
        }
        table->max   = 1 << dev->caps.log_num_macs;
        table->total = 0;
@@ -74,6 +75,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table)
        for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
                table->entries[i] = 0;
                table->refs[i]   = 0;
+               table->is_dup[i] = false;
        }
        table->max   = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR;
        table->total = 0;
@@ -159,21 +161,94 @@ int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx)
 }
 EXPORT_SYMBOL_GPL(mlx4_find_cached_mac);
 
+static bool mlx4_need_mf_bond(struct mlx4_dev *dev)
+{
+       int i, num_eth_ports = 0;
+
+       if (!mlx4_is_mfunc(dev))
+               return false;
+       mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH)
+               ++num_eth_ports;
+
+       return (num_eth_ports ==  2) ? true : false;
+}
+
 int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
 {
        struct mlx4_port_info *info = &mlx4_priv(dev)->port[port];
        struct mlx4_mac_table *table = &info->mac_table;
        int i, err = 0;
        int free = -1;
+       int free_for_dup = -1;
+       bool dup = mlx4_is_mf_bonded(dev);
+       u8 dup_port = (port == 1) ? 2 : 1;
+       struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
+       bool need_mf_bond = mlx4_need_mf_bond(dev);
+       bool can_mf_bond = true;
+
+       mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d %s duplicate\n",
+                (unsigned long long)mac, port,
+                dup ? "with" : "without");
+
+       if (need_mf_bond) {
+               if (port == 1) {
+                       mutex_lock(&table->mutex);
+                       mutex_lock(&dup_table->mutex);
+               } else {
+                       mutex_lock(&dup_table->mutex);
+                       mutex_lock(&table->mutex);
+               }
+       } else {
+               mutex_lock(&table->mutex);
+       }
+
+       if (need_mf_bond) {
+               int index_at_port = -1;
+               int index_at_dup_port = -1;
 
-       mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d\n",
-                (unsigned long long) mac, port);
+               for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+                       if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))))
+                               index_at_port = i;
+                       if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]))))
+                               index_at_dup_port = i;
+               }
+
+               /* check that same mac is not in the tables at different indices */
+               if ((index_at_port != index_at_dup_port) &&
+                   (index_at_port >= 0) &&
+                   (index_at_dup_port >= 0))
+                       can_mf_bond = false;
+
+               /* If the mac is already in the primary table, the slot must be
+                * available in the duplicate table as well.
+                */
+               if (index_at_port >= 0 && index_at_dup_port < 0 &&
+                   dup_table->refs[index_at_port]) {
+                       can_mf_bond = false;
+               }
+               /* If the mac is already in the duplicate table, check that the
+                * corresponding index is not occupied in the primary table, or
+                * the primary table already contains the mac at the same index.
+                * Otherwise, you cannot bond (primary contains a different mac
+                * at that index).
+                */
+               if (index_at_dup_port >= 0) {
+                       if (!table->refs[index_at_dup_port] ||
+                           ((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[index_at_dup_port]))))
+                               free_for_dup = index_at_dup_port;
+                       else
+                               can_mf_bond = false;
+               }
+       }
 
-       mutex_lock(&table->mutex);
        for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
                if (!table->refs[i]) {
                        if (free < 0)
                                free = i;
+                       if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+                               if (!dup_table->refs[i])
+                                       free_for_dup = i;
+                       }
                        continue;
                }
 
@@ -182,10 +257,30 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
                        /* MAC already registered, increment ref count */
                        err = i;
                        ++table->refs[i];
+                       if (dup) {
+                               u64 dup_mac = MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]);
+
+                               if (dup_mac != mac || !dup_table->is_dup[i]) {
+                                       mlx4_warn(dev, "register mac: expect duplicate mac 0x%llx on port %d index %d\n",
+                                                 mac, dup_port, i);
+                               }
+                       }
                        goto out;
                }
        }
 
+       if (need_mf_bond && (free_for_dup < 0)) {
+               if (dup) {
+                       mlx4_warn(dev, "Fail to allocate duplicate MAC table entry\n");
+                       mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+                       dup = false;
+               }
+               can_mf_bond = false;
+       }
+
+       if (need_mf_bond && can_mf_bond)
+               free = free_for_dup;
+
        mlx4_dbg(dev, "Free MAC index is %d\n", free);
 
        if (table->total == table->max) {
@@ -205,10 +300,35 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
                goto out;
        }
        table->refs[free] = 1;
-       err = free;
+       table->is_dup[free] = false;
        ++table->total;
+       if (dup) {
+               dup_table->refs[free] = 0;
+               dup_table->is_dup[free] = true;
+               dup_table->entries[free] = cpu_to_be64(mac | MLX4_MAC_VALID);
+
+               err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+               if (unlikely(err)) {
+                       mlx4_warn(dev, "Failed adding duplicate mac: 0x%llx\n", mac);
+                       dup_table->is_dup[free] = false;
+                       dup_table->entries[free] = 0;
+                       goto out;
+               }
+               ++dup_table->total;
+       }
+       err = free;
 out:
-       mutex_unlock(&table->mutex);
+       if (need_mf_bond) {
+               if (port == 2) {
+                       mutex_unlock(&table->mutex);
+                       mutex_unlock(&dup_table->mutex);
+               } else {
+                       mutex_unlock(&dup_table->mutex);
+                       mutex_unlock(&table->mutex);
+               }
+       } else {
+               mutex_unlock(&table->mutex);
+       }
        return err;
 }
 EXPORT_SYMBOL_GPL(__mlx4_register_mac);
@@ -255,6 +375,9 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
        struct mlx4_port_info *info;
        struct mlx4_mac_table *table;
        int index;
+       bool dup = mlx4_is_mf_bonded(dev);
+       u8 dup_port = (port == 1) ? 2 : 1;
+       struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
 
        if (port < 1 || port > dev->caps.num_ports) {
                mlx4_warn(dev, "invalid port number (%d), aborting...\n", port);
@@ -262,22 +385,59 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
        }
        info = &mlx4_priv(dev)->port[port];
        table = &info->mac_table;
-       mutex_lock(&table->mutex);
+
+       if (dup) {
+               if (port == 1) {
+                       mutex_lock(&table->mutex);
+                       mutex_lock(&dup_table->mutex);
+               } else {
+                       mutex_lock(&dup_table->mutex);
+                       mutex_lock(&table->mutex);
+               }
+       } else {
+               mutex_lock(&table->mutex);
+       }
+
        index = find_index(dev, table, mac);
 
        if (validate_index(dev, table, index))
                goto out;
-       if (--table->refs[index]) {
+
+       if (--table->refs[index] || table->is_dup[index]) {
                mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n",
                         index);
+               if (!table->refs[index])
+                       dup_table->is_dup[index] = false;
                goto out;
        }
 
        table->entries[index] = 0;
-       mlx4_set_port_mac_table(dev, port, table->entries);
+       if (mlx4_set_port_mac_table(dev, port, table->entries))
+               mlx4_warn(dev, "Fail to set mac in port %d during unregister\n", port);
        --table->total;
+
+       if (dup) {
+               dup_table->is_dup[index] = false;
+               if (dup_table->refs[index])
+                       goto out;
+               dup_table->entries[index] = 0;
+               if (mlx4_set_port_mac_table(dev, dup_port, dup_table->entries))
+                       mlx4_warn(dev, "Fail to set mac in duplicate port %d during unregister\n", dup_port);
+
+               --table->total;
+       }
 out:
-       mutex_unlock(&table->mutex);
+       if (dup) {
+               if (port == 2) {
+                       mutex_unlock(&table->mutex);
+                       mutex_unlock(&dup_table->mutex);
+               } else {
+                       mutex_unlock(&dup_table->mutex);
+                       mutex_unlock(&table->mutex);
+               }
+       } else {
+               mutex_unlock(&table->mutex);
+       }
 }
 EXPORT_SYMBOL_GPL(__mlx4_unregister_mac);
 
@@ -311,9 +471,22 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
        struct mlx4_mac_table *table = &info->mac_table;
        int index = qpn - info->base_qpn;
        int err = 0;
+       bool dup = mlx4_is_mf_bonded(dev);
+       u8 dup_port = (port == 1) ? 2 : 1;
+       struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
 
        /* CX1 doesn't support multi-functions */
-       mutex_lock(&table->mutex);
+       if (dup) {
+               if (port == 1) {
+                       mutex_lock(&table->mutex);
+                       mutex_lock(&dup_table->mutex);
+               } else {
+                       mutex_lock(&dup_table->mutex);
+                       mutex_lock(&table->mutex);
+               }
+       } else {
+               mutex_lock(&table->mutex);
+       }
 
        err = validate_index(dev, table, index);
        if (err)
@@ -326,9 +499,30 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
                mlx4_err(dev, "Failed adding MAC: 0x%llx\n",
                         (unsigned long long) new_mac);
                table->entries[index] = 0;
+       } else {
+               if (dup) {
+                       dup_table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID);
+
+                       err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+                       if (unlikely(err)) {
+                               mlx4_err(dev, "Failed adding duplicate MAC: 0x%llx\n",
+                                        (unsigned long long)new_mac);
+                               dup_table->entries[index] = 0;
+                       }
+               }
        }
 out:
-       mutex_unlock(&table->mutex);
+       if (dup) {
+               if (port == 2) {
+                       mutex_unlock(&table->mutex);
+                       mutex_unlock(&dup_table->mutex);
+               } else {
+                       mutex_unlock(&dup_table->mutex);
+                       mutex_unlock(&table->mutex);
+               }
+       } else {
+               mutex_unlock(&table->mutex);
+       }
        return err;
 }
 EXPORT_SYMBOL_GPL(__mlx4_replace_mac);
@@ -380,8 +574,28 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
        struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
        int i, err = 0;
        int free = -1;
-
-       mutex_lock(&table->mutex);
+       int free_for_dup = -1;
+       bool dup = mlx4_is_mf_bonded(dev);
+       u8 dup_port = (port == 1) ? 2 : 1;
+       struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+       bool need_mf_bond = mlx4_need_mf_bond(dev);
+       bool can_mf_bond = true;
+
+       mlx4_dbg(dev, "Registering VLAN: %d for port %d %s duplicate\n",
+                vlan, port,
+                dup ? "with" : "without");
+
+       if (need_mf_bond) {
+               if (port == 1) {
+                       mutex_lock(&table->mutex);
+                       mutex_lock(&dup_table->mutex);
+               } else {
+                       mutex_lock(&dup_table->mutex);
+                       mutex_lock(&table->mutex);
+               }
+       } else {
+               mutex_lock(&table->mutex);
+       }
 
        if (table->total == table->max) {
                /* No free vlan entries */
@@ -389,22 +603,85 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
                goto out;
        }
 
+       if (need_mf_bond) {
+               int index_at_port = -1;
+               int index_at_dup_port = -1;
+
+               for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
+                       if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(table->entries[i]))))
+                               index_at_port = i;
+                       if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]))))
+                               index_at_dup_port = i;
+               }
+               /* check that same vlan is not in the tables at different indices */
+               if ((index_at_port != index_at_dup_port) &&
+                   (index_at_port >= 0) &&
+                   (index_at_dup_port >= 0))
+                       can_mf_bond = false;
+
+               /* If the vlan is already in the primary table, the slot must be
+                * available in the duplicate table as well.
+                */
+               if (index_at_port >= 0 && index_at_dup_port < 0 &&
+                   dup_table->refs[index_at_port]) {
+                       can_mf_bond = false;
+               }
+               /* If the vlan is already in the duplicate table, check that the
+                * corresponding index is not occupied in the primary table, or
+                * the primary table already contains the vlan at the same index.
+                * Otherwise, you cannot bond (primary contains a different vlan
+                * at that index).
+                */
+               if (index_at_dup_port >= 0) {
+                       if (!table->refs[index_at_dup_port] ||
+                           (vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[index_at_dup_port]))))
+                               free_for_dup = index_at_dup_port;
+                       else
+                               can_mf_bond = false;
+               }
+       }
+
        for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
-               if (free < 0 && (table->refs[i] == 0)) {
-                       free = i;
-                       continue;
+               if (!table->refs[i]) {
+                       if (free < 0)
+                               free = i;
+                       if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+                               if (!dup_table->refs[i])
+                                       free_for_dup = i;
+                       }
                }
 
-               if (table->refs[i] &&
+               if ((table->refs[i] || table->is_dup[i]) &&
                    (vlan == (MLX4_VLAN_MASK &
                              be32_to_cpu(table->entries[i])))) {
                        /* Vlan already registered, increase references count */
+                       mlx4_dbg(dev, "vlan %u is already registered.\n", vlan);
                        *index = i;
                        ++table->refs[i];
+                       if (dup) {
+                               u16 dup_vlan = MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]);
+
+                               if (dup_vlan != vlan || !dup_table->is_dup[i]) {
+                                       mlx4_warn(dev, "register vlan: expected duplicate vlan %u on port %d index %d\n",
+                                                 vlan, dup_port, i);
+                               }
+                       }
                        goto out;
                }
        }
 
+       if (need_mf_bond && (free_for_dup < 0)) {
+               if (dup) {
+                       mlx4_warn(dev, "Fail to allocate duplicate VLAN table entry\n");
+                       mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+                       dup = false;
+               }
+               can_mf_bond = false;
+       }
+
+       if (need_mf_bond && can_mf_bond)
+               free = free_for_dup;
+
        if (free < 0) {
                err = -ENOMEM;
                goto out;
@@ -412,6 +689,7 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
 
        /* Register new VLAN */
        table->refs[free] = 1;
+       table->is_dup[free] = false;
        table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
 
        err = mlx4_set_port_vlan_table(dev, port, table->entries);
@@ -421,11 +699,35 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
                table->entries[free] = 0;
                goto out;
        }
+       ++table->total;
+       if (dup) {
+               dup_table->refs[free] = 0;
+               dup_table->is_dup[free] = true;
+               dup_table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
+
+               err = mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries);
+               if (unlikely(err)) {
+                       mlx4_warn(dev, "Failed adding duplicate vlan: %u\n", vlan);
+                       dup_table->is_dup[free] = false;
+                       dup_table->entries[free] = 0;
+                       goto out;
+               }
+               ++dup_table->total;
+       }
 
        *index = free;
-       ++table->total;
 out:
-       mutex_unlock(&table->mutex);
+       if (need_mf_bond) {
+               if (port == 2) {
+                       mutex_unlock(&table->mutex);
+                       mutex_unlock(&dup_table->mutex);
+               } else {
+                       mutex_unlock(&dup_table->mutex);
+                       mutex_unlock(&table->mutex);
+               }
+       } else {
+               mutex_unlock(&table->mutex);
+       }
        return err;
 }
 
@@ -455,8 +757,22 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
 {
        struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
        int index;
+       bool dup = mlx4_is_mf_bonded(dev);
+       u8 dup_port = (port == 1) ? 2 : 1;
+       struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+
+       if (dup) {
+               if (port == 1) {
+                       mutex_lock(&table->mutex);
+                       mutex_lock(&dup_table->mutex);
+               } else {
+                       mutex_lock(&dup_table->mutex);
+                       mutex_lock(&table->mutex);
+               }
+       } else {
+               mutex_lock(&table->mutex);
+       }
 
-       mutex_lock(&table->mutex);
        if (mlx4_find_cached_vlan(dev, port, vlan, &index)) {
                mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan);
                goto out;
@@ -467,16 +783,38 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
                goto out;
        }
 
-       if (--table->refs[index]) {
+       if (--table->refs[index] || table->is_dup[index]) {
                mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n",
                         table->refs[index], index);
+               if (!table->refs[index])
+                       dup_table->is_dup[index] = false;
                goto out;
        }
        table->entries[index] = 0;
-       mlx4_set_port_vlan_table(dev, port, table->entries);
+       if (mlx4_set_port_vlan_table(dev, port, table->entries))
+               mlx4_warn(dev, "Fail to set vlan in port %d during unregister\n", port);
        --table->total;
+       if (dup) {
+               dup_table->is_dup[index] = false;
+               if (dup_table->refs[index])
+                       goto out;
+               dup_table->entries[index] = 0;
+               if (mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries))
+                       mlx4_warn(dev, "Fail to set vlan in duplicate port %d during unregister\n", dup_port);
+               --dup_table->total;
+       }
 out:
-       mutex_unlock(&table->mutex);
+       if (dup) {
+               if (port == 2) {
+                       mutex_unlock(&table->mutex);
+                       mutex_unlock(&dup_table->mutex);
+               } else {
+                       mutex_unlock(&dup_table->mutex);
+                       mutex_unlock(&table->mutex);
+               }
+       } else {
+               mutex_unlock(&table->mutex);
+       }
 }
 
 void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
@@ -495,6 +833,220 @@ void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
 }
 EXPORT_SYMBOL_GPL(mlx4_unregister_vlan);
 
+int mlx4_bond_mac_table(struct mlx4_dev *dev)
+{
+       struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+       struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+       int ret = 0;
+       int i;
+       bool update1 = false;
+       bool update2 = false;
+
+       mutex_lock(&t1->mutex);
+       mutex_lock(&t2->mutex);
+       for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+               if ((t1->entries[i] != t2->entries[i]) &&
+                   t1->entries[i] && t2->entries[i]) {
+                       mlx4_warn(dev, "can't duplicate entry %d in mac table\n", i);
+                       ret = -EINVAL;
+                       goto unlock;
+               }
+       }
+
+       for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+               if (t1->entries[i] && !t2->entries[i]) {
+                       t2->entries[i] = t1->entries[i];
+                       t2->is_dup[i] = true;
+                       update2 = true;
+               } else if (!t1->entries[i] && t2->entries[i]) {
+                       t1->entries[i] = t2->entries[i];
+                       t1->is_dup[i] = true;
+                       update1 = true;
+               } else if (t1->entries[i] && t2->entries[i]) {
+                       t1->is_dup[i] = true;
+                       t2->is_dup[i] = true;
+               }
+       }
+
+       if (update1) {
+               ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to set MAC table for port 1 (%d)\n", ret);
+       }
+       if (!ret && update2) {
+               ret = mlx4_set_port_mac_table(dev, 2, t2->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to set MAC table for port 2 (%d)\n", ret);
+       }
+
+       if (ret)
+               mlx4_warn(dev, "failed to create mirror MAC tables\n");
+unlock:
+       mutex_unlock(&t2->mutex);
+       mutex_unlock(&t1->mutex);
+       return ret;
+}
+
+int mlx4_unbond_mac_table(struct mlx4_dev *dev)
+{
+       struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+       struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+       int ret = 0;
+       int ret1;
+       int i;
+       bool update1 = false;
+       bool update2 = false;
+
+       mutex_lock(&t1->mutex);
+       mutex_lock(&t2->mutex);
+       for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+               if (t1->entries[i] != t2->entries[i]) {
+                       mlx4_warn(dev, "mac table is in an unexpected state when trying to unbond\n");
+                       ret = -EINVAL;
+                       goto unlock;
+               }
+       }
+
+       for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+               if (!t1->entries[i])
+                       continue;
+               t1->is_dup[i] = false;
+               if (!t1->refs[i]) {
+                       t1->entries[i] = 0;
+                       update1 = true;
+               }
+               t2->is_dup[i] = false;
+               if (!t2->refs[i]) {
+                       t2->entries[i] = 0;
+                       update2 = true;
+               }
+       }
+
+       if (update1) {
+               ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to unmirror MAC tables for port 1(%d)\n", ret);
+       }
+       if (update2) {
+               ret1 = mlx4_set_port_mac_table(dev, 2, t2->entries);
+               if (ret1) {
+                       mlx4_warn(dev, "failed to unmirror MAC tables for port 2(%d)\n", ret1);
+                       ret = ret1;
+               }
+       }
+unlock:
+       mutex_unlock(&t2->mutex);
+       mutex_unlock(&t1->mutex);
+       return ret;
+}
+
+int mlx4_bond_vlan_table(struct mlx4_dev *dev)
+{
+       struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+       struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+       int ret = 0;
+       int i;
+       bool update1 = false;
+       bool update2 = false;
+
+       mutex_lock(&t1->mutex);
+       mutex_lock(&t2->mutex);
+       for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+               if ((t1->entries[i] != t2->entries[i]) &&
+                   t1->entries[i] && t2->entries[i]) {
+                       mlx4_warn(dev, "can't duplicate entry %d in vlan table\n", i);
+                       ret = -EINVAL;
+                       goto unlock;
+               }
+       }
+
+       for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+               if (t1->entries[i] && !t2->entries[i]) {
+                       t2->entries[i] = t1->entries[i];
+                       t2->is_dup[i] = true;
+                       update2 = true;
+               } else if (!t1->entries[i] && t2->entries[i]) {
+                       t1->entries[i] = t2->entries[i];
+                       t1->is_dup[i] = true;
+                       update1 = true;
+               } else if (t1->entries[i] && t2->entries[i]) {
+                       t1->is_dup[i] = true;
+                       t2->is_dup[i] = true;
+               }
+       }
+
+       if (update1) {
+               ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to set VLAN table for port 1 (%d)\n", ret);
+       }
+       if (!ret && update2) {
+               ret = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to set VLAN table for port 2 (%d)\n", ret);
+       }
+
+       if (ret)
+               mlx4_warn(dev, "failed to create mirror VLAN tables\n");
+unlock:
+       mutex_unlock(&t2->mutex);
+       mutex_unlock(&t1->mutex);
+       return ret;
+}
+
+int mlx4_unbond_vlan_table(struct mlx4_dev *dev)
+{
+       struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+       struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+       int ret = 0;
+       int ret1;
+       int i;
+       bool update1 = false;
+       bool update2 = false;
+
+       mutex_lock(&t1->mutex);
+       mutex_lock(&t2->mutex);
+       for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+               if (t1->entries[i] != t2->entries[i]) {
+                       mlx4_warn(dev, "vlan table is in an unexpected state when trying to unbond\n");
+                       ret = -EINVAL;
+                       goto unlock;
+               }
+       }
+
+       for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+               if (!t1->entries[i])
+                       continue;
+               t1->is_dup[i] = false;
+               if (!t1->refs[i]) {
+                       t1->entries[i] = 0;
+                       update1 = true;
+               }
+               t2->is_dup[i] = false;
+               if (!t2->refs[i]) {
+                       t2->entries[i] = 0;
+                       update2 = true;
+               }
+       }
+
+       if (update1) {
+               ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+               if (ret)
+                       mlx4_warn(dev, "failed to unmirror VLAN tables for port 1(%d)\n", ret);
+       }
+       if (update2) {
+               ret1 = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+               if (ret1) {
+                       mlx4_warn(dev, "failed to unmirror VLAN tables for port 2(%d)\n", ret1);
+                       ret = ret1;
+               }
+       }
+unlock:
+       mutex_unlock(&t2->mutex);
+       mutex_unlock(&t1->mutex);
+       return ret;
+}
+
 int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
 {
        struct mlx4_cmd_mailbox *inmailbox, *outmailbox;