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