OSDN Git Service

Merge branch 'mlxsw-Various-fixes'
authorDavid S. Miller <davem@davemloft.net>
Fri, 7 Feb 2020 17:47:01 +0000 (18:47 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 7 Feb 2020 17:47:01 +0000 (18:47 +0100)
Ido Schimmel says:

====================
mlxsw: Various fixes

This patch set contains various fixes for the mlxsw driver.

Patch #1 fixes an issue introduced in 5.6 in which a route in the main
table can replace an identical route in the local table despite the
local table having an higher precedence.

Patch #2 contains a test case for the bug fixed in patch #1.

Patch #3 also fixes an issue introduced in 5.6 in which the driver
failed to clear the offload indication from IPv6 nexthops upon abort.

Patch #4 fixes an issue that prevents the driver from loading on
Spectrum-3 systems. The problem and solution are explained in detail in
the commit message.

Patch #5 adds a missing error path. Discovered using smatch.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
tools/testing/selftests/drivers/net/mlxsw/fib.sh

index 9bf8da5..3fe878d 100644 (file)
@@ -573,6 +573,7 @@ static int mlxsw_hwmon_module_init(struct mlxsw_hwmon *mlxsw_hwmon)
 
 static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon *mlxsw_hwmon)
 {
+       enum mlxsw_reg_mgpir_device_type device_type;
        int index, max_index, sensor_index;
        char mgpir_pl[MLXSW_REG_MGPIR_LEN];
        char mtmp_pl[MLXSW_REG_MTMP_LEN];
@@ -584,8 +585,9 @@ static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon *mlxsw_hwmon)
        if (err)
                return err;
 
-       mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, NULL, NULL, NULL);
-       if (!gbox_num)
+       mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, &device_type, NULL, NULL);
+       if (device_type != MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE ||
+           !gbox_num)
                return 0;
 
        index = mlxsw_hwmon->module_sensor_max;
index c721b17..ce0a683 100644 (file)
@@ -895,8 +895,10 @@ static int
 mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
                             struct mlxsw_thermal *thermal)
 {
+       enum mlxsw_reg_mgpir_device_type device_type;
        struct mlxsw_thermal_module *gearbox_tz;
        char mgpir_pl[MLXSW_REG_MGPIR_LEN];
+       u8 gbox_num;
        int i;
        int err;
 
@@ -908,11 +910,13 @@ mlxsw_thermal_gearboxes_init(struct device *dev, struct mlxsw_core *core,
        if (err)
                return err;
 
-       mlxsw_reg_mgpir_unpack(mgpir_pl, &thermal->tz_gearbox_num, NULL, NULL,
+       mlxsw_reg_mgpir_unpack(mgpir_pl, &gbox_num, &device_type, NULL,
                               NULL);
-       if (!thermal->tz_gearbox_num)
+       if (device_type != MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE ||
+           !gbox_num)
                return 0;
 
+       thermal->tz_gearbox_num = gbox_num;
        thermal->tz_gearbox_arr = kcalloc(thermal->tz_gearbox_num,
                                          sizeof(*thermal->tz_gearbox_arr),
                                          GFP_KERNEL);
index 4993381..2dc0978 100644 (file)
@@ -215,7 +215,7 @@ mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled,
 start_again:
        err = devlink_dpipe_entry_ctx_prepare(dump_ctx);
        if (err)
-               return err;
+               goto err_ctx_prepare;
        j = 0;
        for (; i < rif_count; i++) {
                struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i);
@@ -247,6 +247,7 @@ start_again:
        return 0;
 err_entry_append:
 err_entry_get:
+err_ctx_prepare:
        rtnl_unlock();
        devlink_dpipe_entry_clear(&entry);
        return err;
index ce70772..4a77b51 100644 (file)
@@ -4844,6 +4844,23 @@ mlxsw_sp_fib_node_entry_unlink(struct mlxsw_sp *mlxsw_sp,
        fib_node->fib_entry = NULL;
 }
 
+static bool mlxsw_sp_fib4_allow_replace(struct mlxsw_sp_fib4_entry *fib4_entry)
+{
+       struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node;
+       struct mlxsw_sp_fib4_entry *fib4_replaced;
+
+       if (!fib_node->fib_entry)
+               return true;
+
+       fib4_replaced = container_of(fib_node->fib_entry,
+                                    struct mlxsw_sp_fib4_entry, common);
+       if (fib4_entry->tb_id == RT_TABLE_MAIN &&
+           fib4_replaced->tb_id == RT_TABLE_LOCAL)
+               return false;
+
+       return true;
+}
+
 static int
 mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,
                             const struct fib_entry_notifier_info *fen_info)
@@ -4872,6 +4889,12 @@ mlxsw_sp_router_fib4_replace(struct mlxsw_sp *mlxsw_sp,
                goto err_fib4_entry_create;
        }
 
+       if (!mlxsw_sp_fib4_allow_replace(fib4_entry)) {
+               mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry);
+               mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
+               return 0;
+       }
+
        replaced = fib_node->fib_entry;
        err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib4_entry->common);
        if (err) {
@@ -4908,7 +4931,7 @@ static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp,
                return;
 
        fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info);
-       if (WARN_ON(!fib4_entry))
+       if (!fib4_entry)
                return;
        fib_node = fib4_entry->common.fib_node;
 
@@ -4970,6 +4993,9 @@ static void mlxsw_sp_rt6_release(struct fib6_info *rt)
 
 static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6)
 {
+       struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
+
+       fib6_nh->fib_nh_flags &= ~RTNH_F_OFFLOAD;
        mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt);
        kfree(mlxsw_sp_rt6);
 }
@@ -5408,6 +5434,27 @@ mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp,
        return NULL;
 }
 
+static bool mlxsw_sp_fib6_allow_replace(struct mlxsw_sp_fib6_entry *fib6_entry)
+{
+       struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node;
+       struct mlxsw_sp_fib6_entry *fib6_replaced;
+       struct fib6_info *rt, *rt_replaced;
+
+       if (!fib_node->fib_entry)
+               return true;
+
+       fib6_replaced = container_of(fib_node->fib_entry,
+                                    struct mlxsw_sp_fib6_entry,
+                                    common);
+       rt = mlxsw_sp_fib6_entry_rt(fib6_entry);
+       rt_replaced = mlxsw_sp_fib6_entry_rt(fib6_replaced);
+       if (rt->fib6_table->tb6_id == RT_TABLE_MAIN &&
+           rt_replaced->fib6_table->tb6_id == RT_TABLE_LOCAL)
+               return false;
+
+       return true;
+}
+
 static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,
                                        struct fib6_info **rt_arr,
                                        unsigned int nrt6)
@@ -5442,6 +5489,12 @@ static int mlxsw_sp_router_fib6_replace(struct mlxsw_sp *mlxsw_sp,
                goto err_fib6_entry_create;
        }
 
+       if (!mlxsw_sp_fib6_allow_replace(fib6_entry)) {
+               mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry);
+               mlxsw_sp_fib_node_put(mlxsw_sp, fib_node);
+               return 0;
+       }
+
        replaced = fib_node->fib_entry;
        err = mlxsw_sp_fib_node_entry_link(mlxsw_sp, &fib6_entry->common);
        if (err)
index 45115f8..eab79b9 100755 (executable)
@@ -14,6 +14,7 @@ ALL_TESTS="
        ipv4_plen
        ipv4_replay
        ipv4_flush
+       ipv4_local_replace
        ipv6_add
        ipv6_metric
        ipv6_append_single
@@ -26,6 +27,7 @@ ALL_TESTS="
        ipv6_delete_multipath
        ipv6_replay_single
        ipv6_replay_multipath
+       ipv6_local_replace
 "
 NUM_NETIFS=0
 source $lib_dir/lib.sh
@@ -89,6 +91,43 @@ ipv4_flush()
        fib_ipv4_flush_test "testns1"
 }
 
+ipv4_local_replace()
+{
+       local ns="testns1"
+
+       RET=0
+
+       ip -n $ns link add name dummy1 type dummy
+       ip -n $ns link set dev dummy1 up
+
+       ip -n $ns route add table local 192.0.2.1/32 dev dummy1
+       fib4_trap_check $ns "table local 192.0.2.1/32 dev dummy1" false
+       check_err $? "Local table route not in hardware when should"
+
+       ip -n $ns route add table main 192.0.2.1/32 dev dummy1
+       fib4_trap_check $ns "table main 192.0.2.1/32 dev dummy1" true
+       check_err $? "Main table route in hardware when should not"
+
+       fib4_trap_check $ns "table local 192.0.2.1/32 dev dummy1" false
+       check_err $? "Local table route was replaced when should not"
+
+       # Test that local routes can replace routes in main table.
+       ip -n $ns route add table main 192.0.2.2/32 dev dummy1
+       fib4_trap_check $ns "table main 192.0.2.2/32 dev dummy1" false
+       check_err $? "Main table route not in hardware when should"
+
+       ip -n $ns route add table local 192.0.2.2/32 dev dummy1
+       fib4_trap_check $ns "table local 192.0.2.2/32 dev dummy1" false
+       check_err $? "Local table route did not replace route in main table when should"
+
+       fib4_trap_check $ns "table main 192.0.2.2/32 dev dummy1" true
+       check_err $? "Main table route was not replaced when should"
+
+       log_test "IPv4 local table route replacement"
+
+       ip -n $ns link del dev dummy1
+}
+
 ipv6_add()
 {
        fib_ipv6_add_test "testns1"
@@ -149,6 +188,43 @@ ipv6_replay_multipath()
        fib_ipv6_replay_multipath_test "testns1" "$DEVLINK_DEV"
 }
 
+ipv6_local_replace()
+{
+       local ns="testns1"
+
+       RET=0
+
+       ip -n $ns link add name dummy1 type dummy
+       ip -n $ns link set dev dummy1 up
+
+       ip -n $ns route add table local 2001:db8:1::1/128 dev dummy1
+       fib6_trap_check $ns "table local 2001:db8:1::1/128 dev dummy1" false
+       check_err $? "Local table route not in hardware when should"
+
+       ip -n $ns route add table main 2001:db8:1::1/128 dev dummy1
+       fib6_trap_check $ns "table main 2001:db8:1::1/128 dev dummy1" true
+       check_err $? "Main table route in hardware when should not"
+
+       fib6_trap_check $ns "table local 2001:db8:1::1/128 dev dummy1" false
+       check_err $? "Local table route was replaced when should not"
+
+       # Test that local routes can replace routes in main table.
+       ip -n $ns route add table main 2001:db8:1::2/128 dev dummy1
+       fib6_trap_check $ns "table main 2001:db8:1::2/128 dev dummy1" false
+       check_err $? "Main table route not in hardware when should"
+
+       ip -n $ns route add table local 2001:db8:1::2/128 dev dummy1
+       fib6_trap_check $ns "table local 2001:db8:1::2/128 dev dummy1" false
+       check_err $? "Local route route did not replace route in main table when should"
+
+       fib6_trap_check $ns "table main 2001:db8:1::2/128 dev dummy1" true
+       check_err $? "Main table route was not replaced when should"
+
+       log_test "IPv6 local table route replacement"
+
+       ip -n $ns link del dev dummy1
+}
+
 setup_prepare()
 {
        ip netns add testns1