| /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ |
| /* Copyright (c) 2019 Mellanox Technologies. */ |
| |
| #include <linux/module.h> |
| #include <linux/mlx5/driver.h> |
| #include <linux/mlx5/port.h> |
| #include "mlx5_core.h" |
| #include "lib/port_tun.h" |
| |
| struct mlx5_port_tun_entropy_flags { |
| bool force_supported, force_enabled; |
| bool calc_supported, calc_enabled; |
| bool gre_calc_supported, gre_calc_enabled; |
| }; |
| |
| static void mlx5_query_port_tun_entropy(struct mlx5_core_dev *mdev, |
| struct mlx5_port_tun_entropy_flags *entropy_flags) |
| { |
| u32 out[MLX5_ST_SZ_DW(pcmr_reg)]; |
| /* Default values for FW which do not support MLX5_REG_PCMR */ |
| entropy_flags->force_supported = false; |
| entropy_flags->calc_supported = false; |
| entropy_flags->gre_calc_supported = false; |
| entropy_flags->force_enabled = false; |
| entropy_flags->calc_enabled = true; |
| entropy_flags->gre_calc_enabled = true; |
| |
| if (!MLX5_CAP_GEN(mdev, ports_check)) |
| return; |
| |
| if (mlx5_query_ports_check(mdev, out, sizeof(out))) |
| return; |
| |
| entropy_flags->force_supported = !!(MLX5_GET(pcmr_reg, out, entropy_force_cap)); |
| entropy_flags->calc_supported = !!(MLX5_GET(pcmr_reg, out, entropy_calc_cap)); |
| entropy_flags->gre_calc_supported = !!(MLX5_GET(pcmr_reg, out, entropy_gre_calc_cap)); |
| entropy_flags->force_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_force)); |
| entropy_flags->calc_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_calc)); |
| entropy_flags->gre_calc_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_gre_calc)); |
| } |
| |
| static int mlx5_set_port_tun_entropy_calc(struct mlx5_core_dev *mdev, u8 enable, |
| u8 force) |
| { |
| u32 in[MLX5_ST_SZ_DW(pcmr_reg)] = {0}; |
| int err; |
| |
| err = mlx5_query_ports_check(mdev, in, sizeof(in)); |
| if (err) |
| return err; |
| MLX5_SET(pcmr_reg, in, local_port, 1); |
| MLX5_SET(pcmr_reg, in, entropy_force, force); |
| MLX5_SET(pcmr_reg, in, entropy_calc, enable); |
| return mlx5_set_ports_check(mdev, in, sizeof(in)); |
| } |
| |
| static int mlx5_set_port_gre_tun_entropy_calc(struct mlx5_core_dev *mdev, |
| u8 enable, u8 force) |
| { |
| u32 in[MLX5_ST_SZ_DW(pcmr_reg)] = {0}; |
| int err; |
| |
| err = mlx5_query_ports_check(mdev, in, sizeof(in)); |
| if (err) |
| return err; |
| MLX5_SET(pcmr_reg, in, local_port, 1); |
| MLX5_SET(pcmr_reg, in, entropy_force, force); |
| MLX5_SET(pcmr_reg, in, entropy_gre_calc, enable); |
| return mlx5_set_ports_check(mdev, in, sizeof(in)); |
| } |
| |
| void mlx5_init_port_tun_entropy(struct mlx5_tun_entropy *tun_entropy, |
| struct mlx5_core_dev *mdev) |
| { |
| struct mlx5_port_tun_entropy_flags entropy_flags; |
| |
| tun_entropy->mdev = mdev; |
| mutex_init(&tun_entropy->lock); |
| mlx5_query_port_tun_entropy(mdev, &entropy_flags); |
| tun_entropy->num_enabling_entries = 0; |
| tun_entropy->num_disabling_entries = 0; |
| tun_entropy->enabled = entropy_flags.calc_supported ? |
| entropy_flags.calc_enabled : true; |
| } |
| |
| static int mlx5_set_entropy(struct mlx5_tun_entropy *tun_entropy, |
| int reformat_type, bool enable) |
| { |
| struct mlx5_port_tun_entropy_flags entropy_flags; |
| int err; |
| |
| mlx5_query_port_tun_entropy(tun_entropy->mdev, &entropy_flags); |
| /* Tunnel entropy calculation may be controlled either on port basis |
| * for all tunneling protocols or specifically for GRE protocol. |
| * Prioritize GRE protocol control (if capable) over global port |
| * configuration. |
| */ |
| if (entropy_flags.gre_calc_supported && |
| reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE) { |
| if (!entropy_flags.force_supported) |
| return 0; |
| err = mlx5_set_port_gre_tun_entropy_calc(tun_entropy->mdev, |
| enable, !enable); |
| if (err) |
| return err; |
| } else if (entropy_flags.calc_supported) { |
| /* Other applications may change the global FW entropy |
| * calculations settings. Check that the current entropy value |
| * is the negative of the updated value. |
| */ |
| if (entropy_flags.force_enabled && |
| enable == entropy_flags.calc_enabled) { |
| mlx5_core_warn(tun_entropy->mdev, |
| "Unexpected entropy calc setting - expected %d", |
| !entropy_flags.calc_enabled); |
| return -EOPNOTSUPP; |
| } |
| /* GRE requires disabling entropy calculation. if there are |
| * enabling entries (i.e VXLAN) we cannot turn it off for them, |
| * thus fail. |
| */ |
| if (tun_entropy->num_enabling_entries) |
| return -EOPNOTSUPP; |
| err = mlx5_set_port_tun_entropy_calc(tun_entropy->mdev, enable, |
| entropy_flags.force_supported); |
| if (err) |
| return err; |
| tun_entropy->enabled = enable; |
| /* if we turn on the entropy we don't need to force it anymore */ |
| if (entropy_flags.force_supported && enable) { |
| err = mlx5_set_port_tun_entropy_calc(tun_entropy->mdev, 1, 0); |
| if (err) |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* the function manages the refcount for enabling/disabling tunnel types. |
| * the return value indicates if the inc is successful or not, depending on |
| * entropy capabilities and configuration. |
| */ |
| int mlx5_tun_entropy_refcount_inc(struct mlx5_tun_entropy *tun_entropy, |
| int reformat_type) |
| { |
| int err = -EOPNOTSUPP; |
| |
| mutex_lock(&tun_entropy->lock); |
| if ((reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN || |
| reformat_type == MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL) && |
| tun_entropy->enabled) { |
| /* in case entropy calculation is enabled for all tunneling |
| * types, it is ok for VXLAN, so approve. |
| * otherwise keep the error default. |
| */ |
| tun_entropy->num_enabling_entries++; |
| err = 0; |
| } else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE) { |
| /* turn off the entropy only for the first GRE rule. |
| * for the next rules the entropy was already disabled |
| * successfully. |
| */ |
| if (tun_entropy->num_disabling_entries == 0) |
| err = mlx5_set_entropy(tun_entropy, reformat_type, 0); |
| else |
| err = 0; |
| if (!err) |
| tun_entropy->num_disabling_entries++; |
| } |
| mutex_unlock(&tun_entropy->lock); |
| |
| return err; |
| } |
| |
| void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, |
| int reformat_type) |
| { |
| mutex_lock(&tun_entropy->lock); |
| if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) |
| tun_entropy->num_enabling_entries--; |
| else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && |
| --tun_entropy->num_disabling_entries == 0) |
| mlx5_set_entropy(tun_entropy, reformat_type, 1); |
| mutex_unlock(&tun_entropy->lock); |
| } |
| |