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
mlx5_query_port_tun_entropy(struct mlx5_core_dev * mdev,struct mlx5_port_tun_entropy_flags * entropy_flags)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
mlx5_set_port_tun_entropy_calc(struct mlx5_core_dev * mdev,u8 enable,u8 force)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
mlx5_set_port_gre_tun_entropy_calc(struct mlx5_core_dev * mdev,u8 enable,u8 force)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
mlx5_init_port_tun_entropy(struct mlx5_tun_entropy * tun_entropy,struct mlx5_core_dev * mdev)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
mlx5_set_entropy(struct mlx5_tun_entropy * tun_entropy,int reformat_type,bool enable)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 */
mlx5_tun_entropy_refcount_inc(struct mlx5_tun_entropy * tun_entropy,int reformat_type)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
mlx5_tun_entropy_refcount_dec(struct mlx5_tun_entropy * tun_entropy,int reformat_type)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