1 /* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ 2 /* Copyright (c) 2019 Mellanox Technologies. */ 3 4 #include <linux/module.h> 5 #include <linux/mlx5/driver.h> 6 #include <linux/mlx5/port.h> 7 #include <linux/mlx5/cmd.h> 8 #include "mlx5_core.h" 9 #include "lib/port_tun.h" 10 11 struct mlx5_port_tun_entropy_flags { 12 bool force_supported, force_enabled; 13 bool calc_supported, calc_enabled; 14 bool gre_calc_supported, gre_calc_enabled; 15 }; 16 17 static void mlx5_query_port_tun_entropy(struct mlx5_core_dev *mdev, 18 struct mlx5_port_tun_entropy_flags *entropy_flags) 19 { 20 u32 out[MLX5_ST_SZ_DW(pcmr_reg)]; 21 /* Default values for FW which do not support MLX5_REG_PCMR */ 22 entropy_flags->force_supported = false; 23 entropy_flags->calc_supported = false; 24 entropy_flags->gre_calc_supported = false; 25 entropy_flags->force_enabled = false; 26 entropy_flags->calc_enabled = true; 27 entropy_flags->gre_calc_enabled = true; 28 29 if (!MLX5_CAP_GEN(mdev, ports_check)) 30 return; 31 32 if (mlx5_query_ports_check(mdev, out, sizeof(out))) 33 return; 34 35 entropy_flags->force_supported = !!(MLX5_GET(pcmr_reg, out, entropy_force_cap)); 36 entropy_flags->calc_supported = !!(MLX5_GET(pcmr_reg, out, entropy_calc_cap)); 37 entropy_flags->gre_calc_supported = !!(MLX5_GET(pcmr_reg, out, entropy_gre_calc_cap)); 38 entropy_flags->force_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_force)); 39 entropy_flags->calc_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_calc)); 40 entropy_flags->gre_calc_enabled = !!(MLX5_GET(pcmr_reg, out, entropy_gre_calc)); 41 } 42 43 static int mlx5_set_port_tun_entropy_calc(struct mlx5_core_dev *mdev, u8 enable, 44 u8 force) 45 { 46 u32 in[MLX5_ST_SZ_DW(pcmr_reg)] = {0}; 47 int err; 48 49 err = mlx5_query_ports_check(mdev, in, sizeof(in)); 50 if (err) 51 return err; 52 MLX5_SET(pcmr_reg, in, local_port, 1); 53 MLX5_SET(pcmr_reg, in, entropy_force, force); 54 MLX5_SET(pcmr_reg, in, entropy_calc, enable); 55 return mlx5_set_ports_check(mdev, in, sizeof(in)); 56 } 57 58 static int mlx5_set_port_gre_tun_entropy_calc(struct mlx5_core_dev *mdev, 59 u8 enable, u8 force) 60 { 61 u32 in[MLX5_ST_SZ_DW(pcmr_reg)] = {0}; 62 int err; 63 64 err = mlx5_query_ports_check(mdev, in, sizeof(in)); 65 if (err) 66 return err; 67 MLX5_SET(pcmr_reg, in, local_port, 1); 68 MLX5_SET(pcmr_reg, in, entropy_force, force); 69 MLX5_SET(pcmr_reg, in, entropy_gre_calc, enable); 70 return mlx5_set_ports_check(mdev, in, sizeof(in)); 71 } 72 73 void mlx5_init_port_tun_entropy(struct mlx5_tun_entropy *tun_entropy, 74 struct mlx5_core_dev *mdev) 75 { 76 struct mlx5_port_tun_entropy_flags entropy_flags; 77 78 tun_entropy->mdev = mdev; 79 mutex_init(&tun_entropy->lock); 80 mlx5_query_port_tun_entropy(mdev, &entropy_flags); 81 tun_entropy->num_enabling_entries = 0; 82 tun_entropy->num_disabling_entries = 0; 83 tun_entropy->enabled = entropy_flags.calc_supported ? 84 entropy_flags.calc_enabled : true; 85 } 86 87 static int mlx5_set_entropy(struct mlx5_tun_entropy *tun_entropy, 88 int reformat_type, bool enable) 89 { 90 struct mlx5_port_tun_entropy_flags entropy_flags; 91 int err; 92 93 mlx5_query_port_tun_entropy(tun_entropy->mdev, &entropy_flags); 94 /* Tunnel entropy calculation may be controlled either on port basis 95 * for all tunneling protocols or specifically for GRE protocol. 96 * Prioritize GRE protocol control (if capable) over global port 97 * configuration. 98 */ 99 if (entropy_flags.gre_calc_supported && 100 reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE) { 101 if (!entropy_flags.force_supported) 102 return 0; 103 err = mlx5_set_port_gre_tun_entropy_calc(tun_entropy->mdev, 104 enable, !enable); 105 if (err) 106 return err; 107 } else if (entropy_flags.calc_supported) { 108 /* Other applications may change the global FW entropy 109 * calculations settings. Check that the current entropy value 110 * is the negative of the updated value. 111 */ 112 if (entropy_flags.force_enabled && 113 enable == entropy_flags.calc_enabled) { 114 mlx5_core_warn(tun_entropy->mdev, 115 "Unexpected entropy calc setting - expected %d", 116 !entropy_flags.calc_enabled); 117 return -EOPNOTSUPP; 118 } 119 /* GRE requires disabling entropy calculation. if there are 120 * enabling entries (i.e VXLAN) we cannot turn it off for them, 121 * thus fail. 122 */ 123 if (tun_entropy->num_enabling_entries) 124 return -EOPNOTSUPP; 125 err = mlx5_set_port_tun_entropy_calc(tun_entropy->mdev, enable, 126 entropy_flags.force_supported); 127 if (err) 128 return err; 129 tun_entropy->enabled = enable; 130 /* if we turn on the entropy we don't need to force it anymore */ 131 if (entropy_flags.force_supported && enable) { 132 err = mlx5_set_port_tun_entropy_calc(tun_entropy->mdev, 1, 0); 133 if (err) 134 return err; 135 } 136 } 137 138 return 0; 139 } 140 141 /* the function manages the refcount for enabling/disabling tunnel types. 142 * the return value indicates if the inc is successful or not, depending on 143 * entropy capabilities and configuration. 144 */ 145 int mlx5_tun_entropy_refcount_inc(struct mlx5_tun_entropy *tun_entropy, 146 int reformat_type) 147 { 148 /* the default is error for unknown (non VXLAN/GRE tunnel types) */ 149 int err = -EOPNOTSUPP; 150 151 mutex_lock(&tun_entropy->lock); 152 if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN && 153 tun_entropy->enabled) { 154 /* in case entropy calculation is enabled for all tunneling 155 * types, it is ok for VXLAN, so approve. 156 * otherwise keep the error default. 157 */ 158 tun_entropy->num_enabling_entries++; 159 err = 0; 160 } else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE) { 161 /* turn off the entropy only for the first GRE rule. 162 * for the next rules the entropy was already disabled 163 * successfully. 164 */ 165 if (tun_entropy->num_disabling_entries == 0) 166 err = mlx5_set_entropy(tun_entropy, reformat_type, 0); 167 else 168 err = 0; 169 if (!err) 170 tun_entropy->num_disabling_entries++; 171 } 172 mutex_unlock(&tun_entropy->lock); 173 174 return err; 175 } 176 177 void mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy *tun_entropy, 178 int reformat_type) 179 { 180 mutex_lock(&tun_entropy->lock); 181 if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_VXLAN) 182 tun_entropy->num_enabling_entries--; 183 else if (reformat_type == MLX5_REFORMAT_TYPE_L2_TO_NVGRE && 184 --tun_entropy->num_disabling_entries == 0) 185 mlx5_set_entropy(tun_entropy, reformat_type, 1); 186 mutex_unlock(&tun_entropy->lock); 187 } 188 189