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