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