Commit | Line | Data |
---|---|---|
08fb1dac SM |
1 | /* |
2 | * Copyright (c) 2016, Mellanox Technologies. All rights reserved. | |
3 | * | |
4 | * This software is available to you under a choice of one of two | |
5 | * licenses. You may choose to be licensed under the terms of the GNU | |
6 | * General Public License (GPL) Version 2, available from the file | |
7 | * COPYING in the main directory of this source tree, or the | |
8 | * OpenIB.org BSD license below: | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or | |
11 | * without modification, are permitted provided that the following | |
12 | * conditions are met: | |
13 | * | |
14 | * - Redistributions of source code must retain the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer. | |
17 | * | |
18 | * - Redistributions in binary form must reproduce the above | |
19 | * copyright notice, this list of conditions and the following | |
20 | * disclaimer in the documentation and/or other materials | |
21 | * provided with the distribution. | |
22 | * | |
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
30 | * SOFTWARE. | |
31 | */ | |
32 | #include <linux/device.h> | |
33 | #include <linux/netdevice.h> | |
34 | #include "en.h" | |
35 | ||
36 | #define MLX5E_MAX_PRIORITY 8 | |
37 | ||
d8880795 TT |
38 | #define MLX5E_100MB (100000) |
39 | #define MLX5E_1GB (1000000) | |
40 | ||
3a6a931d HN |
41 | #define MLX5E_CEE_STATE_UP 1 |
42 | #define MLX5E_CEE_STATE_DOWN 0 | |
43 | ||
be0f161e HN |
44 | enum { |
45 | MLX5E_VENDOR_TC_GROUP_NUM = 7, | |
46 | MLX5E_LOWEST_PRIO_GROUP = 0, | |
47 | }; | |
48 | ||
e207b7e9 HN |
49 | /* If dcbx mode is non-host set the dcbx mode to host. |
50 | */ | |
51 | static int mlx5e_dcbnl_set_dcbx_mode(struct mlx5e_priv *priv, | |
52 | enum mlx5_dcbx_oper_mode mode) | |
53 | { | |
54 | struct mlx5_core_dev *mdev = priv->mdev; | |
55 | u32 param[MLX5_ST_SZ_DW(dcbx_param)]; | |
56 | int err; | |
57 | ||
58 | err = mlx5_query_port_dcbx_param(mdev, param); | |
59 | if (err) | |
60 | return err; | |
61 | ||
62 | MLX5_SET(dcbx_param, param, version_admin, mode); | |
63 | if (mode != MLX5E_DCBX_PARAM_VER_OPER_HOST) | |
64 | MLX5_SET(dcbx_param, param, willing_admin, 1); | |
65 | ||
66 | return mlx5_set_port_dcbx_param(mdev, param); | |
67 | } | |
68 | ||
69 | static int mlx5e_dcbnl_switch_to_host_mode(struct mlx5e_priv *priv) | |
70 | { | |
71 | struct mlx5e_dcbx *dcbx = &priv->dcbx; | |
72 | int err; | |
73 | ||
74 | if (!MLX5_CAP_GEN(priv->mdev, dcbx)) | |
75 | return 0; | |
76 | ||
77 | if (dcbx->mode == MLX5E_DCBX_PARAM_VER_OPER_HOST) | |
78 | return 0; | |
79 | ||
80 | err = mlx5e_dcbnl_set_dcbx_mode(priv, MLX5E_DCBX_PARAM_VER_OPER_HOST); | |
81 | if (err) | |
82 | return err; | |
83 | ||
84 | dcbx->mode = MLX5E_DCBX_PARAM_VER_OPER_HOST; | |
85 | return 0; | |
86 | } | |
87 | ||
08fb1dac SM |
88 | static int mlx5e_dcbnl_ieee_getets(struct net_device *netdev, |
89 | struct ieee_ets *ets) | |
90 | { | |
91 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
820c2c5e | 92 | struct mlx5_core_dev *mdev = priv->mdev; |
be0f161e HN |
93 | u8 tc_group[IEEE_8021QAZ_MAX_TCS]; |
94 | bool is_tc_group_6_exist = false; | |
95 | bool is_zero_bw_ets_tc = false; | |
820c2c5e HN |
96 | int err = 0; |
97 | int i; | |
08fb1dac SM |
98 | |
99 | if (!MLX5_CAP_GEN(priv->mdev, ets)) | |
9eb78923 | 100 | return -EOPNOTSUPP; |
08fb1dac | 101 | |
820c2c5e HN |
102 | ets->ets_cap = mlx5_max_tc(priv->mdev) + 1; |
103 | for (i = 0; i < ets->ets_cap; i++) { | |
104 | err = mlx5_query_port_prio_tc(mdev, i, &ets->prio_tc[i]); | |
105 | if (err) | |
106 | return err; | |
820c2c5e | 107 | |
be0f161e HN |
108 | err = mlx5_query_port_tc_group(mdev, i, &tc_group[i]); |
109 | if (err) | |
110 | return err; | |
111 | ||
820c2c5e HN |
112 | err = mlx5_query_port_tc_bw_alloc(mdev, i, &ets->tc_tx_bw[i]); |
113 | if (err) | |
114 | return err; | |
be0f161e HN |
115 | |
116 | if (ets->tc_tx_bw[i] < MLX5E_MAX_BW_ALLOC && | |
117 | tc_group[i] == (MLX5E_LOWEST_PRIO_GROUP + 1)) | |
118 | is_zero_bw_ets_tc = true; | |
119 | ||
120 | if (tc_group[i] == (MLX5E_VENDOR_TC_GROUP_NUM - 1)) | |
121 | is_tc_group_6_exist = true; | |
122 | } | |
123 | ||
124 | /* Report 0% ets tc if exits*/ | |
125 | if (is_zero_bw_ets_tc) { | |
126 | for (i = 0; i < ets->ets_cap; i++) | |
127 | if (tc_group[i] == MLX5E_LOWEST_PRIO_GROUP) | |
128 | ets->tc_tx_bw[i] = 0; | |
129 | } | |
130 | ||
131 | /* Update tc_tsa based on fw setting*/ | |
132 | for (i = 0; i < ets->ets_cap; i++) { | |
820c2c5e HN |
133 | if (ets->tc_tx_bw[i] < MLX5E_MAX_BW_ALLOC) |
134 | priv->dcbx.tc_tsa[i] = IEEE_8021QAZ_TSA_ETS; | |
be0f161e HN |
135 | else if (tc_group[i] == MLX5E_VENDOR_TC_GROUP_NUM && |
136 | !is_tc_group_6_exist) | |
137 | priv->dcbx.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; | |
820c2c5e | 138 | } |
820c2c5e HN |
139 | memcpy(ets->tc_tsa, priv->dcbx.tc_tsa, sizeof(ets->tc_tsa)); |
140 | ||
141 | return err; | |
08fb1dac SM |
142 | } |
143 | ||
08fb1dac SM |
144 | static void mlx5e_build_tc_group(struct ieee_ets *ets, u8 *tc_group, int max_tc) |
145 | { | |
146 | bool any_tc_mapped_to_ets = false; | |
be0f161e | 147 | bool ets_zero_bw = false; |
08fb1dac SM |
148 | int strict_group; |
149 | int i; | |
150 | ||
be0f161e HN |
151 | for (i = 0; i <= max_tc; i++) { |
152 | if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { | |
08fb1dac | 153 | any_tc_mapped_to_ets = true; |
be0f161e HN |
154 | if (!ets->tc_tx_bw[i]) |
155 | ets_zero_bw = true; | |
156 | } | |
157 | } | |
08fb1dac | 158 | |
be0f161e HN |
159 | /* strict group has higher priority than ets group */ |
160 | strict_group = MLX5E_LOWEST_PRIO_GROUP; | |
161 | if (any_tc_mapped_to_ets) | |
162 | strict_group++; | |
163 | if (ets_zero_bw) | |
164 | strict_group++; | |
08fb1dac SM |
165 | |
166 | for (i = 0; i <= max_tc; i++) { | |
167 | switch (ets->tc_tsa[i]) { | |
168 | case IEEE_8021QAZ_TSA_VENDOR: | |
169 | tc_group[i] = MLX5E_VENDOR_TC_GROUP_NUM; | |
170 | break; | |
171 | case IEEE_8021QAZ_TSA_STRICT: | |
172 | tc_group[i] = strict_group++; | |
173 | break; | |
174 | case IEEE_8021QAZ_TSA_ETS: | |
be0f161e HN |
175 | tc_group[i] = MLX5E_LOWEST_PRIO_GROUP; |
176 | if (ets->tc_tx_bw[i] && ets_zero_bw) | |
177 | tc_group[i] = MLX5E_LOWEST_PRIO_GROUP + 1; | |
08fb1dac SM |
178 | break; |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | static void mlx5e_build_tc_tx_bw(struct ieee_ets *ets, u8 *tc_tx_bw, | |
184 | u8 *tc_group, int max_tc) | |
185 | { | |
be0f161e HN |
186 | int bw_for_ets_zero_bw_tc = 0; |
187 | int last_ets_zero_bw_tc = -1; | |
188 | int num_ets_zero_bw = 0; | |
08fb1dac SM |
189 | int i; |
190 | ||
be0f161e HN |
191 | for (i = 0; i <= max_tc; i++) { |
192 | if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS && | |
193 | !ets->tc_tx_bw[i]) { | |
194 | num_ets_zero_bw++; | |
195 | last_ets_zero_bw_tc = i; | |
196 | } | |
197 | } | |
198 | ||
199 | if (num_ets_zero_bw) | |
200 | bw_for_ets_zero_bw_tc = MLX5E_MAX_BW_ALLOC / num_ets_zero_bw; | |
201 | ||
08fb1dac SM |
202 | for (i = 0; i <= max_tc; i++) { |
203 | switch (ets->tc_tsa[i]) { | |
204 | case IEEE_8021QAZ_TSA_VENDOR: | |
205 | tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; | |
206 | break; | |
207 | case IEEE_8021QAZ_TSA_STRICT: | |
208 | tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; | |
209 | break; | |
210 | case IEEE_8021QAZ_TSA_ETS: | |
be0f161e HN |
211 | tc_tx_bw[i] = ets->tc_tx_bw[i] ? |
212 | ets->tc_tx_bw[i] : | |
213 | bw_for_ets_zero_bw_tc; | |
08fb1dac SM |
214 | break; |
215 | } | |
216 | } | |
be0f161e HN |
217 | |
218 | /* Make sure the total bw for ets zero bw group is 100% */ | |
219 | if (last_ets_zero_bw_tc != -1) | |
220 | tc_tx_bw[last_ets_zero_bw_tc] += | |
221 | MLX5E_MAX_BW_ALLOC % num_ets_zero_bw; | |
08fb1dac SM |
222 | } |
223 | ||
be0f161e HN |
224 | /* If there are ETS BW 0, |
225 | * Set ETS group # to 1 for all ETS non zero BW tcs. Their sum must be 100%. | |
226 | * Set group #0 to all the ETS BW 0 tcs and | |
227 | * equally splits the 100% BW between them | |
228 | * Report both group #0 and #1 as ETS type. | |
229 | * All the tcs in group #0 will be reported with 0% BW. | |
230 | */ | |
08fb1dac SM |
231 | int mlx5e_dcbnl_ieee_setets_core(struct mlx5e_priv *priv, struct ieee_ets *ets) |
232 | { | |
233 | struct mlx5_core_dev *mdev = priv->mdev; | |
234 | u8 tc_tx_bw[IEEE_8021QAZ_MAX_TCS]; | |
235 | u8 tc_group[IEEE_8021QAZ_MAX_TCS]; | |
236 | int max_tc = mlx5_max_tc(mdev); | |
237 | int err; | |
238 | ||
08fb1dac SM |
239 | mlx5e_build_tc_group(ets, tc_group, max_tc); |
240 | mlx5e_build_tc_tx_bw(ets, tc_tx_bw, tc_group, max_tc); | |
241 | ||
242 | err = mlx5_set_port_prio_tc(mdev, ets->prio_tc); | |
243 | if (err) | |
244 | return err; | |
245 | ||
246 | err = mlx5_set_port_tc_group(mdev, tc_group); | |
247 | if (err) | |
248 | return err; | |
249 | ||
820c2c5e HN |
250 | err = mlx5_set_port_tc_bw_alloc(mdev, tc_tx_bw); |
251 | ||
252 | if (err) | |
253 | return err; | |
254 | ||
255 | memcpy(priv->dcbx.tc_tsa, ets->tc_tsa, sizeof(ets->tc_tsa)); | |
820c2c5e | 256 | return err; |
08fb1dac SM |
257 | } |
258 | ||
1722b969 EBE |
259 | static int mlx5e_dbcnl_validate_ets(struct net_device *netdev, |
260 | struct ieee_ets *ets) | |
08fb1dac | 261 | { |
9a472c21 | 262 | bool have_ets_tc = false; |
08fb1dac SM |
263 | int bw_sum = 0; |
264 | int i; | |
265 | ||
266 | /* Validate Priority */ | |
267 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { | |
1722b969 EBE |
268 | if (ets->prio_tc[i] >= MLX5E_MAX_PRIORITY) { |
269 | netdev_err(netdev, | |
270 | "Failed to validate ETS: priority value greater than max(%d)\n", | |
271 | MLX5E_MAX_PRIORITY); | |
08fb1dac | 272 | return -EINVAL; |
1722b969 | 273 | } |
08fb1dac SM |
274 | } |
275 | ||
276 | /* Validate Bandwidth Sum */ | |
9a472c21 HN |
277 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
278 | if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) { | |
279 | have_ets_tc = true; | |
08fb1dac | 280 | bw_sum += ets->tc_tx_bw[i]; |
9a472c21 HN |
281 | } |
282 | } | |
08fb1dac | 283 | |
9a472c21 | 284 | if (have_ets_tc && bw_sum != 100) { |
1722b969 EBE |
285 | netdev_err(netdev, |
286 | "Failed to validate ETS: BW sum is illegal\n"); | |
08fb1dac | 287 | return -EINVAL; |
1722b969 | 288 | } |
08fb1dac SM |
289 | return 0; |
290 | } | |
291 | ||
292 | static int mlx5e_dcbnl_ieee_setets(struct net_device *netdev, | |
293 | struct ieee_ets *ets) | |
294 | { | |
295 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
296 | int err; | |
297 | ||
820c2c5e | 298 | if (!MLX5_CAP_GEN(priv->mdev, ets)) |
9eb78923 | 299 | return -EOPNOTSUPP; |
820c2c5e | 300 | |
1722b969 | 301 | err = mlx5e_dbcnl_validate_ets(netdev, ets); |
08fb1dac SM |
302 | if (err) |
303 | return err; | |
304 | ||
305 | err = mlx5e_dcbnl_ieee_setets_core(priv, ets); | |
306 | if (err) | |
307 | return err; | |
308 | ||
08fb1dac SM |
309 | return 0; |
310 | } | |
311 | ||
ef918433 AS |
312 | static int mlx5e_dcbnl_ieee_getpfc(struct net_device *dev, |
313 | struct ieee_pfc *pfc) | |
314 | { | |
315 | struct mlx5e_priv *priv = netdev_priv(dev); | |
316 | struct mlx5_core_dev *mdev = priv->mdev; | |
cf678570 GP |
317 | struct mlx5e_pport_stats *pstats = &priv->stats.pport; |
318 | int i; | |
ef918433 AS |
319 | |
320 | pfc->pfc_cap = mlx5_max_tc(mdev) + 1; | |
cf678570 GP |
321 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
322 | pfc->requests[i] = PPORT_PER_PRIO_GET(pstats, i, tx_pause); | |
323 | pfc->indications[i] = PPORT_PER_PRIO_GET(pstats, i, rx_pause); | |
324 | } | |
ef918433 AS |
325 | |
326 | return mlx5_query_port_pfc(mdev, &pfc->pfc_en, NULL); | |
327 | } | |
328 | ||
329 | static int mlx5e_dcbnl_ieee_setpfc(struct net_device *dev, | |
330 | struct ieee_pfc *pfc) | |
331 | { | |
332 | struct mlx5e_priv *priv = netdev_priv(dev); | |
333 | struct mlx5_core_dev *mdev = priv->mdev; | |
ef918433 AS |
334 | u8 curr_pfc_en; |
335 | int ret; | |
336 | ||
337 | mlx5_query_port_pfc(mdev, &curr_pfc_en, NULL); | |
338 | ||
339 | if (pfc->pfc_en == curr_pfc_en) | |
340 | return 0; | |
341 | ||
ef918433 | 342 | ret = mlx5_set_port_pfc(mdev, pfc->pfc_en, pfc->pfc_en); |
667daeda | 343 | mlx5_toggle_port_link(mdev); |
ef918433 AS |
344 | |
345 | return ret; | |
346 | } | |
347 | ||
08fb1dac SM |
348 | static u8 mlx5e_dcbnl_getdcbx(struct net_device *dev) |
349 | { | |
0eca995f | 350 | struct mlx5e_priv *priv = netdev_priv(dev); |
0eca995f | 351 | |
9e10bf1d | 352 | return priv->dcbx.cap; |
08fb1dac SM |
353 | } |
354 | ||
355 | static u8 mlx5e_dcbnl_setdcbx(struct net_device *dev, u8 mode) | |
356 | { | |
0eca995f HN |
357 | struct mlx5e_priv *priv = netdev_priv(dev); |
358 | struct mlx5e_dcbx *dcbx = &priv->dcbx; | |
359 | ||
33e21c59 HN |
360 | if (mode & DCB_CAP_DCBX_LLD_MANAGED) |
361 | return 1; | |
362 | ||
0eca995f HN |
363 | if ((!mode) && MLX5_CAP_GEN(priv->mdev, dcbx)) { |
364 | if (dcbx->mode == MLX5E_DCBX_PARAM_VER_OPER_AUTO) | |
365 | return 0; | |
366 | ||
367 | /* set dcbx to fw controlled */ | |
368 | if (!mlx5e_dcbnl_set_dcbx_mode(priv, MLX5E_DCBX_PARAM_VER_OPER_AUTO)) { | |
369 | dcbx->mode = MLX5E_DCBX_PARAM_VER_OPER_AUTO; | |
9e10bf1d | 370 | dcbx->cap &= ~DCB_CAP_DCBX_HOST; |
0eca995f HN |
371 | return 0; |
372 | } | |
373 | ||
374 | return 1; | |
375 | } | |
376 | ||
33e21c59 | 377 | if (!(mode & DCB_CAP_DCBX_HOST)) |
e207b7e9 HN |
378 | return 1; |
379 | ||
33e21c59 | 380 | if (mlx5e_dcbnl_switch_to_host_mode(netdev_priv(dev))) |
08fb1dac SM |
381 | return 1; |
382 | ||
9e10bf1d HN |
383 | dcbx->cap = mode; |
384 | ||
08fb1dac SM |
385 | return 0; |
386 | } | |
387 | ||
d8880795 TT |
388 | static int mlx5e_dcbnl_ieee_getmaxrate(struct net_device *netdev, |
389 | struct ieee_maxrate *maxrate) | |
390 | { | |
391 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
392 | struct mlx5_core_dev *mdev = priv->mdev; | |
393 | u8 max_bw_value[IEEE_8021QAZ_MAX_TCS]; | |
394 | u8 max_bw_unit[IEEE_8021QAZ_MAX_TCS]; | |
395 | int err; | |
396 | int i; | |
397 | ||
398 | err = mlx5_query_port_ets_rate_limit(mdev, max_bw_value, max_bw_unit); | |
399 | if (err) | |
400 | return err; | |
401 | ||
402 | memset(maxrate->tc_maxrate, 0, sizeof(maxrate->tc_maxrate)); | |
403 | ||
404 | for (i = 0; i <= mlx5_max_tc(mdev); i++) { | |
405 | switch (max_bw_unit[i]) { | |
406 | case MLX5_100_MBPS_UNIT: | |
407 | maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_100MB; | |
408 | break; | |
409 | case MLX5_GBPS_UNIT: | |
410 | maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_1GB; | |
411 | break; | |
412 | case MLX5_BW_NO_LIMIT: | |
413 | break; | |
414 | default: | |
415 | WARN(true, "non-supported BW unit"); | |
416 | break; | |
417 | } | |
418 | } | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
423 | static int mlx5e_dcbnl_ieee_setmaxrate(struct net_device *netdev, | |
424 | struct ieee_maxrate *maxrate) | |
425 | { | |
426 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
427 | struct mlx5_core_dev *mdev = priv->mdev; | |
428 | u8 max_bw_value[IEEE_8021QAZ_MAX_TCS]; | |
429 | u8 max_bw_unit[IEEE_8021QAZ_MAX_TCS]; | |
430 | __u64 upper_limit_mbps = roundup(255 * MLX5E_100MB, MLX5E_1GB); | |
431 | int i; | |
432 | ||
433 | memset(max_bw_value, 0, sizeof(max_bw_value)); | |
434 | memset(max_bw_unit, 0, sizeof(max_bw_unit)); | |
435 | ||
436 | for (i = 0; i <= mlx5_max_tc(mdev); i++) { | |
437 | if (!maxrate->tc_maxrate[i]) { | |
438 | max_bw_unit[i] = MLX5_BW_NO_LIMIT; | |
439 | continue; | |
440 | } | |
441 | if (maxrate->tc_maxrate[i] < upper_limit_mbps) { | |
442 | max_bw_value[i] = div_u64(maxrate->tc_maxrate[i], | |
443 | MLX5E_100MB); | |
444 | max_bw_value[i] = max_bw_value[i] ? max_bw_value[i] : 1; | |
445 | max_bw_unit[i] = MLX5_100_MBPS_UNIT; | |
446 | } else { | |
447 | max_bw_value[i] = div_u64(maxrate->tc_maxrate[i], | |
448 | MLX5E_1GB); | |
449 | max_bw_unit[i] = MLX5_GBPS_UNIT; | |
450 | } | |
451 | } | |
452 | ||
453 | return mlx5_modify_port_ets_rate_limit(mdev, max_bw_value, max_bw_unit); | |
454 | } | |
455 | ||
3a6a931d HN |
456 | static u8 mlx5e_dcbnl_setall(struct net_device *netdev) |
457 | { | |
458 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
459 | struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; | |
460 | struct mlx5_core_dev *mdev = priv->mdev; | |
461 | struct ieee_ets ets; | |
462 | struct ieee_pfc pfc; | |
9eb78923 | 463 | int err = -EOPNOTSUPP; |
3a6a931d HN |
464 | int i; |
465 | ||
820c2c5e HN |
466 | if (!MLX5_CAP_GEN(mdev, ets)) |
467 | goto out; | |
468 | ||
3a6a931d HN |
469 | memset(&ets, 0, sizeof(ets)); |
470 | memset(&pfc, 0, sizeof(pfc)); | |
471 | ||
472 | ets.ets_cap = IEEE_8021QAZ_MAX_TCS; | |
473 | for (i = 0; i < CEE_DCBX_MAX_PGS; i++) { | |
474 | ets.tc_tx_bw[i] = cee_cfg->pg_bw_pct[i]; | |
475 | ets.tc_rx_bw[i] = cee_cfg->pg_bw_pct[i]; | |
476 | ets.tc_tsa[i] = IEEE_8021QAZ_TSA_ETS; | |
477 | ets.prio_tc[i] = cee_cfg->prio_to_pg_map[i]; | |
478 | } | |
479 | ||
480 | err = mlx5e_dbcnl_validate_ets(netdev, &ets); | |
481 | if (err) { | |
482 | netdev_err(netdev, | |
483 | "%s, Failed to validate ETS: %d\n", __func__, err); | |
484 | goto out; | |
485 | } | |
486 | ||
487 | err = mlx5e_dcbnl_ieee_setets_core(priv, &ets); | |
488 | if (err) { | |
489 | netdev_err(netdev, | |
490 | "%s, Failed to set ETS: %d\n", __func__, err); | |
491 | goto out; | |
492 | } | |
493 | ||
494 | /* Set PFC */ | |
495 | pfc.pfc_cap = mlx5_max_tc(mdev) + 1; | |
496 | if (!cee_cfg->pfc_enable) | |
497 | pfc.pfc_en = 0; | |
498 | else | |
499 | for (i = 0; i < CEE_DCBX_MAX_PRIO; i++) | |
500 | pfc.pfc_en |= cee_cfg->pfc_setting[i] << i; | |
501 | ||
502 | err = mlx5e_dcbnl_ieee_setpfc(netdev, &pfc); | |
503 | if (err) { | |
504 | netdev_err(netdev, | |
505 | "%s, Failed to set PFC: %d\n", __func__, err); | |
506 | goto out; | |
507 | } | |
508 | out: | |
509 | return err ? MLX5_DCB_NO_CHG : MLX5_DCB_CHG_RESET; | |
510 | } | |
511 | ||
512 | static u8 mlx5e_dcbnl_getstate(struct net_device *netdev) | |
513 | { | |
514 | return MLX5E_CEE_STATE_UP; | |
515 | } | |
516 | ||
517 | static void mlx5e_dcbnl_getpermhwaddr(struct net_device *netdev, | |
518 | u8 *perm_addr) | |
519 | { | |
520 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
521 | ||
522 | if (!perm_addr) | |
523 | return; | |
524 | ||
d968f0f2 HN |
525 | memset(perm_addr, 0xff, MAX_ADDR_LEN); |
526 | ||
3a6a931d HN |
527 | mlx5_query_nic_vport_mac_address(priv->mdev, 0, perm_addr); |
528 | } | |
529 | ||
530 | static void mlx5e_dcbnl_setpgtccfgtx(struct net_device *netdev, | |
531 | int priority, u8 prio_type, | |
532 | u8 pgid, u8 bw_pct, u8 up_map) | |
533 | { | |
534 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
535 | struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; | |
536 | ||
537 | if (priority >= CEE_DCBX_MAX_PRIO) { | |
538 | netdev_err(netdev, | |
539 | "%s, priority is out of range\n", __func__); | |
540 | return; | |
541 | } | |
542 | ||
543 | if (pgid >= CEE_DCBX_MAX_PGS) { | |
544 | netdev_err(netdev, | |
545 | "%s, priority group is out of range\n", __func__); | |
546 | return; | |
547 | } | |
548 | ||
549 | cee_cfg->prio_to_pg_map[priority] = pgid; | |
550 | } | |
551 | ||
552 | static void mlx5e_dcbnl_setpgbwgcfgtx(struct net_device *netdev, | |
553 | int pgid, u8 bw_pct) | |
554 | { | |
555 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
556 | struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; | |
557 | ||
558 | if (pgid >= CEE_DCBX_MAX_PGS) { | |
559 | netdev_err(netdev, | |
560 | "%s, priority group is out of range\n", __func__); | |
561 | return; | |
562 | } | |
563 | ||
564 | cee_cfg->pg_bw_pct[pgid] = bw_pct; | |
565 | } | |
566 | ||
567 | static void mlx5e_dcbnl_getpgtccfgtx(struct net_device *netdev, | |
568 | int priority, u8 *prio_type, | |
569 | u8 *pgid, u8 *bw_pct, u8 *up_map) | |
570 | { | |
571 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
572 | struct mlx5_core_dev *mdev = priv->mdev; | |
573 | ||
d15118af MS |
574 | if (!MLX5_CAP_GEN(priv->mdev, ets)) { |
575 | netdev_err(netdev, "%s, ets is not supported\n", __func__); | |
576 | return; | |
577 | } | |
578 | ||
3a6a931d HN |
579 | if (priority >= CEE_DCBX_MAX_PRIO) { |
580 | netdev_err(netdev, | |
581 | "%s, priority is out of range\n", __func__); | |
582 | return; | |
583 | } | |
584 | ||
585 | *prio_type = 0; | |
586 | *bw_pct = 0; | |
587 | *up_map = 0; | |
588 | ||
589 | if (mlx5_query_port_prio_tc(mdev, priority, pgid)) | |
590 | *pgid = 0; | |
591 | } | |
592 | ||
593 | static void mlx5e_dcbnl_getpgbwgcfgtx(struct net_device *netdev, | |
594 | int pgid, u8 *bw_pct) | |
595 | { | |
be0f161e | 596 | struct ieee_ets ets; |
3a6a931d HN |
597 | |
598 | if (pgid >= CEE_DCBX_MAX_PGS) { | |
599 | netdev_err(netdev, | |
600 | "%s, priority group is out of range\n", __func__); | |
601 | return; | |
602 | } | |
603 | ||
be0f161e HN |
604 | mlx5e_dcbnl_ieee_getets(netdev, &ets); |
605 | *bw_pct = ets.tc_tx_bw[pgid]; | |
3a6a931d HN |
606 | } |
607 | ||
608 | static void mlx5e_dcbnl_setpfccfg(struct net_device *netdev, | |
609 | int priority, u8 setting) | |
610 | { | |
611 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
612 | struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; | |
613 | ||
614 | if (priority >= CEE_DCBX_MAX_PRIO) { | |
615 | netdev_err(netdev, | |
616 | "%s, priority is out of range\n", __func__); | |
617 | return; | |
618 | } | |
619 | ||
620 | if (setting > 1) | |
621 | return; | |
622 | ||
623 | cee_cfg->pfc_setting[priority] = setting; | |
624 | } | |
625 | ||
626 | static int | |
627 | mlx5e_dcbnl_get_priority_pfc(struct net_device *netdev, | |
628 | int priority, u8 *setting) | |
629 | { | |
630 | struct ieee_pfc pfc; | |
631 | int err; | |
632 | ||
633 | err = mlx5e_dcbnl_ieee_getpfc(netdev, &pfc); | |
634 | ||
635 | if (err) | |
636 | *setting = 0; | |
637 | else | |
638 | *setting = (pfc.pfc_en >> priority) & 0x01; | |
639 | ||
640 | return err; | |
641 | } | |
642 | ||
643 | static void mlx5e_dcbnl_getpfccfg(struct net_device *netdev, | |
644 | int priority, u8 *setting) | |
645 | { | |
646 | if (priority >= CEE_DCBX_MAX_PRIO) { | |
647 | netdev_err(netdev, | |
648 | "%s, priority is out of range\n", __func__); | |
649 | return; | |
650 | } | |
651 | ||
652 | if (!setting) | |
653 | return; | |
654 | ||
655 | mlx5e_dcbnl_get_priority_pfc(netdev, priority, setting); | |
656 | } | |
657 | ||
658 | static u8 mlx5e_dcbnl_getcap(struct net_device *netdev, | |
659 | int capid, u8 *cap) | |
660 | { | |
661 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
662 | struct mlx5_core_dev *mdev = priv->mdev; | |
663 | u8 rval = 0; | |
664 | ||
665 | switch (capid) { | |
666 | case DCB_CAP_ATTR_PG: | |
667 | *cap = true; | |
668 | break; | |
669 | case DCB_CAP_ATTR_PFC: | |
670 | *cap = true; | |
671 | break; | |
672 | case DCB_CAP_ATTR_UP2TC: | |
673 | *cap = false; | |
674 | break; | |
675 | case DCB_CAP_ATTR_PG_TCS: | |
676 | *cap = 1 << mlx5_max_tc(mdev); | |
677 | break; | |
678 | case DCB_CAP_ATTR_PFC_TCS: | |
679 | *cap = 1 << mlx5_max_tc(mdev); | |
680 | break; | |
681 | case DCB_CAP_ATTR_GSP: | |
682 | *cap = false; | |
683 | break; | |
684 | case DCB_CAP_ATTR_BCN: | |
685 | *cap = false; | |
686 | break; | |
687 | case DCB_CAP_ATTR_DCBX: | |
9e10bf1d HN |
688 | *cap = priv->dcbx.cap | |
689 | DCB_CAP_DCBX_VER_CEE | | |
690 | DCB_CAP_DCBX_VER_IEEE; | |
3a6a931d HN |
691 | break; |
692 | default: | |
693 | *cap = 0; | |
694 | rval = 1; | |
695 | break; | |
696 | } | |
697 | ||
698 | return rval; | |
699 | } | |
700 | ||
701 | static int mlx5e_dcbnl_getnumtcs(struct net_device *netdev, | |
702 | int tcs_id, u8 *num) | |
703 | { | |
704 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
705 | struct mlx5_core_dev *mdev = priv->mdev; | |
706 | ||
707 | switch (tcs_id) { | |
708 | case DCB_NUMTCS_ATTR_PG: | |
709 | case DCB_NUMTCS_ATTR_PFC: | |
710 | *num = mlx5_max_tc(mdev) + 1; | |
711 | break; | |
712 | default: | |
713 | return -EINVAL; | |
714 | } | |
715 | ||
716 | return 0; | |
717 | } | |
718 | ||
719 | static u8 mlx5e_dcbnl_getpfcstate(struct net_device *netdev) | |
720 | { | |
721 | struct ieee_pfc pfc; | |
722 | ||
723 | if (mlx5e_dcbnl_ieee_getpfc(netdev, &pfc)) | |
724 | return MLX5E_CEE_STATE_DOWN; | |
725 | ||
726 | return pfc.pfc_en ? MLX5E_CEE_STATE_UP : MLX5E_CEE_STATE_DOWN; | |
727 | } | |
728 | ||
729 | static void mlx5e_dcbnl_setpfcstate(struct net_device *netdev, u8 state) | |
730 | { | |
731 | struct mlx5e_priv *priv = netdev_priv(netdev); | |
732 | struct mlx5e_cee_config *cee_cfg = &priv->dcbx.cee_cfg; | |
733 | ||
734 | if ((state != MLX5E_CEE_STATE_UP) && (state != MLX5E_CEE_STATE_DOWN)) | |
735 | return; | |
736 | ||
737 | cee_cfg->pfc_enable = state; | |
738 | } | |
739 | ||
08fb1dac SM |
740 | const struct dcbnl_rtnl_ops mlx5e_dcbnl_ops = { |
741 | .ieee_getets = mlx5e_dcbnl_ieee_getets, | |
742 | .ieee_setets = mlx5e_dcbnl_ieee_setets, | |
d8880795 TT |
743 | .ieee_getmaxrate = mlx5e_dcbnl_ieee_getmaxrate, |
744 | .ieee_setmaxrate = mlx5e_dcbnl_ieee_setmaxrate, | |
ef918433 AS |
745 | .ieee_getpfc = mlx5e_dcbnl_ieee_getpfc, |
746 | .ieee_setpfc = mlx5e_dcbnl_ieee_setpfc, | |
08fb1dac SM |
747 | .getdcbx = mlx5e_dcbnl_getdcbx, |
748 | .setdcbx = mlx5e_dcbnl_setdcbx, | |
3a6a931d HN |
749 | |
750 | /* CEE interfaces */ | |
751 | .setall = mlx5e_dcbnl_setall, | |
752 | .getstate = mlx5e_dcbnl_getstate, | |
753 | .getpermhwaddr = mlx5e_dcbnl_getpermhwaddr, | |
754 | ||
755 | .setpgtccfgtx = mlx5e_dcbnl_setpgtccfgtx, | |
756 | .setpgbwgcfgtx = mlx5e_dcbnl_setpgbwgcfgtx, | |
757 | .getpgtccfgtx = mlx5e_dcbnl_getpgtccfgtx, | |
758 | .getpgbwgcfgtx = mlx5e_dcbnl_getpgbwgcfgtx, | |
759 | ||
760 | .setpfccfg = mlx5e_dcbnl_setpfccfg, | |
761 | .getpfccfg = mlx5e_dcbnl_getpfccfg, | |
762 | .getcap = mlx5e_dcbnl_getcap, | |
763 | .getnumtcs = mlx5e_dcbnl_getnumtcs, | |
764 | .getpfcstate = mlx5e_dcbnl_getpfcstate, | |
765 | .setpfcstate = mlx5e_dcbnl_setpfcstate, | |
08fb1dac | 766 | }; |
e207b7e9 HN |
767 | |
768 | static void mlx5e_dcbnl_query_dcbx_mode(struct mlx5e_priv *priv, | |
769 | enum mlx5_dcbx_oper_mode *mode) | |
770 | { | |
771 | u32 out[MLX5_ST_SZ_DW(dcbx_param)]; | |
772 | ||
773 | *mode = MLX5E_DCBX_PARAM_VER_OPER_HOST; | |
774 | ||
775 | if (!mlx5_query_port_dcbx_param(priv->mdev, out)) | |
776 | *mode = MLX5_GET(dcbx_param, out, version_oper); | |
777 | ||
778 | /* From driver's point of view, we only care if the mode | |
779 | * is host (HOST) or non-host (AUTO) | |
780 | */ | |
781 | if (*mode != MLX5E_DCBX_PARAM_VER_OPER_HOST) | |
782 | *mode = MLX5E_DCBX_PARAM_VER_OPER_AUTO; | |
783 | } | |
784 | ||
785 | static void mlx5e_ets_init(struct mlx5e_priv *priv) | |
786 | { | |
787 | int i; | |
788 | struct ieee_ets ets; | |
789 | ||
4525a45b HN |
790 | if (!MLX5_CAP_GEN(priv->mdev, ets)) |
791 | return; | |
792 | ||
e207b7e9 HN |
793 | memset(&ets, 0, sizeof(ets)); |
794 | ets.ets_cap = mlx5_max_tc(priv->mdev) + 1; | |
795 | for (i = 0; i < ets.ets_cap; i++) { | |
796 | ets.tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC; | |
797 | ets.tc_tsa[i] = IEEE_8021QAZ_TSA_VENDOR; | |
798 | ets.prio_tc[i] = i; | |
799 | } | |
800 | ||
e207b7e9 HN |
801 | /* tclass[prio=0]=1, tclass[prio=1]=0, tclass[prio=i]=i (for i>1) */ |
802 | ets.prio_tc[0] = 1; | |
803 | ets.prio_tc[1] = 0; | |
804 | ||
805 | mlx5e_dcbnl_ieee_setets_core(priv, &ets); | |
806 | } | |
807 | ||
808 | void mlx5e_dcbnl_initialize(struct mlx5e_priv *priv) | |
809 | { | |
810 | struct mlx5e_dcbx *dcbx = &priv->dcbx; | |
811 | ||
33c52b67 HN |
812 | if (!MLX5_CAP_GEN(priv->mdev, qos)) |
813 | return; | |
814 | ||
e207b7e9 HN |
815 | if (MLX5_CAP_GEN(priv->mdev, dcbx)) |
816 | mlx5e_dcbnl_query_dcbx_mode(priv, &dcbx->mode); | |
817 | ||
9e10bf1d HN |
818 | priv->dcbx.cap = DCB_CAP_DCBX_VER_CEE | |
819 | DCB_CAP_DCBX_VER_IEEE; | |
820 | if (priv->dcbx.mode == MLX5E_DCBX_PARAM_VER_OPER_HOST) | |
821 | priv->dcbx.cap |= DCB_CAP_DCBX_HOST; | |
822 | ||
e207b7e9 HN |
823 | mlx5e_ets_init(priv); |
824 | } |