2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the names of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * Alternatively, this software may be distributed under the terms of the
19 * GNU General Public License ("GPL") version 2 as published by the Free
20 * Software Foundation.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
35 #include <linux/netdevice.h>
36 #include <linux/string.h>
37 #include <linux/bitops.h>
38 #include <net/dcbnl.h>
43 static u8
mlxsw_sp_dcbnl_getdcbx(struct net_device __always_unused
*dev
)
45 return DCB_CAP_DCBX_HOST
| DCB_CAP_DCBX_VER_IEEE
;
48 static u8
mlxsw_sp_dcbnl_setdcbx(struct net_device __always_unused
*dev
,
51 return (mode
!= (DCB_CAP_DCBX_HOST
| DCB_CAP_DCBX_VER_IEEE
)) ? 1 : 0;
54 static int mlxsw_sp_dcbnl_ieee_getets(struct net_device
*dev
,
57 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
59 memcpy(ets
, mlxsw_sp_port
->dcb
.ets
, sizeof(*ets
));
64 static int mlxsw_sp_port_ets_validate(struct mlxsw_sp_port
*mlxsw_sp_port
,
67 struct net_device
*dev
= mlxsw_sp_port
->dev
;
68 bool has_ets_tc
= false;
71 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
72 switch (ets
->tc_tsa
[i
]) {
73 case IEEE_8021QAZ_TSA_STRICT
:
75 case IEEE_8021QAZ_TSA_ETS
:
77 tx_bw_sum
+= ets
->tc_tx_bw
[i
];
80 netdev_err(dev
, "Only strict priority and ETS are supported\n");
84 if (ets
->prio_tc
[i
] >= IEEE_8021QAZ_MAX_TCS
) {
85 netdev_err(dev
, "Invalid TC\n");
90 if (has_ets_tc
&& tx_bw_sum
!= 100) {
91 netdev_err(dev
, "Total ETS bandwidth should equal 100\n");
98 static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port
*mlxsw_sp_port
,
101 char pptb_pl
[MLXSW_REG_PPTB_LEN
];
104 mlxsw_reg_pptb_pack(pptb_pl
, mlxsw_sp_port
->local_port
);
105 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++)
106 mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl
, i
, prio_tc
[i
]);
108 return mlxsw_reg_write(mlxsw_sp_port
->mlxsw_sp
->core
, MLXSW_REG(pptb
),
112 static bool mlxsw_sp_ets_has_pg(u8
*prio_tc
, u8 pg
)
116 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++)
117 if (prio_tc
[i
] == pg
)
122 static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port
*mlxsw_sp_port
,
123 u8
*old_prio_tc
, u8
*new_prio_tc
)
125 struct mlxsw_sp
*mlxsw_sp
= mlxsw_sp_port
->mlxsw_sp
;
126 char pbmc_pl
[MLXSW_REG_PBMC_LEN
];
129 mlxsw_reg_pbmc_pack(pbmc_pl
, mlxsw_sp_port
->local_port
, 0, 0);
130 err
= mlxsw_reg_query(mlxsw_sp
->core
, MLXSW_REG(pbmc
), pbmc_pl
);
134 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
135 u8 pg
= old_prio_tc
[i
];
137 if (!mlxsw_sp_ets_has_pg(new_prio_tc
, pg
))
138 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl
, pg
, 0);
141 return mlxsw_reg_write(mlxsw_sp
->core
, MLXSW_REG(pbmc
), pbmc_pl
);
144 static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port
*mlxsw_sp_port
,
145 struct ieee_ets
*ets
)
147 bool pause_en
= mlxsw_sp_port_is_pause_en(mlxsw_sp_port
);
148 struct ieee_ets
*my_ets
= mlxsw_sp_port
->dcb
.ets
;
149 struct net_device
*dev
= mlxsw_sp_port
->dev
;
152 /* Create the required PGs, but don't destroy existing ones, as
153 * traffic is still directed to them.
155 err
= __mlxsw_sp_port_headroom_set(mlxsw_sp_port
, dev
->mtu
,
156 ets
->prio_tc
, pause_en
,
157 mlxsw_sp_port
->dcb
.pfc
);
159 netdev_err(dev
, "Failed to configure port's headroom\n");
163 err
= mlxsw_sp_port_pg_prio_map(mlxsw_sp_port
, ets
->prio_tc
);
165 netdev_err(dev
, "Failed to set PG-priority mapping\n");
166 goto err_port_prio_pg_map
;
169 err
= mlxsw_sp_port_pg_destroy(mlxsw_sp_port
, my_ets
->prio_tc
,
172 netdev_warn(dev
, "Failed to remove ununsed PGs\n");
176 err_port_prio_pg_map
:
177 mlxsw_sp_port_pg_destroy(mlxsw_sp_port
, ets
->prio_tc
, my_ets
->prio_tc
);
181 static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port
*mlxsw_sp_port
,
182 struct ieee_ets
*ets
)
184 struct ieee_ets
*my_ets
= mlxsw_sp_port
->dcb
.ets
;
185 struct net_device
*dev
= mlxsw_sp_port
->dev
;
188 /* Egress configuration. */
189 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
190 bool dwrr
= ets
->tc_tsa
[i
] == IEEE_8021QAZ_TSA_ETS
;
191 u8 weight
= ets
->tc_tx_bw
[i
];
193 err
= mlxsw_sp_port_ets_set(mlxsw_sp_port
,
194 MLXSW_REG_QEEC_HIERARCY_SUBGROUP
, i
,
197 netdev_err(dev
, "Failed to link subgroup ETS element %d to group\n",
199 goto err_port_ets_set
;
203 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
204 err
= mlxsw_sp_port_prio_tc_set(mlxsw_sp_port
, i
,
207 netdev_err(dev
, "Failed to map prio %d to TC %d\n", i
,
209 goto err_port_prio_tc_set
;
213 /* Ingress configuration. */
214 err
= mlxsw_sp_port_headroom_set(mlxsw_sp_port
, ets
);
216 goto err_port_headroom_set
;
220 err_port_headroom_set
:
221 i
= IEEE_8021QAZ_MAX_TCS
;
222 err_port_prio_tc_set
:
223 for (i
--; i
>= 0; i
--)
224 mlxsw_sp_port_prio_tc_set(mlxsw_sp_port
, i
, my_ets
->prio_tc
[i
]);
225 i
= IEEE_8021QAZ_MAX_TCS
;
227 for (i
--; i
>= 0; i
--) {
228 bool dwrr
= my_ets
->tc_tsa
[i
] == IEEE_8021QAZ_TSA_ETS
;
229 u8 weight
= my_ets
->tc_tx_bw
[i
];
231 err
= mlxsw_sp_port_ets_set(mlxsw_sp_port
,
232 MLXSW_REG_QEEC_HIERARCY_SUBGROUP
, i
,
238 static int mlxsw_sp_dcbnl_ieee_setets(struct net_device
*dev
,
239 struct ieee_ets
*ets
)
241 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
244 err
= mlxsw_sp_port_ets_validate(mlxsw_sp_port
, ets
);
248 err
= __mlxsw_sp_dcbnl_ieee_setets(mlxsw_sp_port
, ets
);
252 memcpy(mlxsw_sp_port
->dcb
.ets
, ets
, sizeof(*ets
));
253 mlxsw_sp_port
->dcb
.ets
->ets_cap
= IEEE_8021QAZ_MAX_TCS
;
258 static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device
*dev
,
259 struct ieee_maxrate
*maxrate
)
261 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
263 memcpy(maxrate
, mlxsw_sp_port
->dcb
.maxrate
, sizeof(*maxrate
));
268 static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device
*dev
,
269 struct ieee_maxrate
*maxrate
)
271 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
272 struct ieee_maxrate
*my_maxrate
= mlxsw_sp_port
->dcb
.maxrate
;
275 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
276 err
= mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port
,
277 MLXSW_REG_QEEC_HIERARCY_SUBGROUP
,
279 maxrate
->tc_maxrate
[i
]);
281 netdev_err(dev
, "Failed to set maxrate for TC %d\n", i
);
282 goto err_port_ets_maxrate_set
;
286 memcpy(mlxsw_sp_port
->dcb
.maxrate
, maxrate
, sizeof(*maxrate
));
290 err_port_ets_maxrate_set
:
291 for (i
--; i
>= 0; i
--)
292 mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port
,
293 MLXSW_REG_QEEC_HIERARCY_SUBGROUP
,
294 i
, 0, my_maxrate
->tc_maxrate
[i
]);
298 static int mlxsw_sp_port_pfc_cnt_get(struct mlxsw_sp_port
*mlxsw_sp_port
,
301 struct mlxsw_sp
*mlxsw_sp
= mlxsw_sp_port
->mlxsw_sp
;
302 struct ieee_pfc
*my_pfc
= mlxsw_sp_port
->dcb
.pfc
;
303 char ppcnt_pl
[MLXSW_REG_PPCNT_LEN
];
306 mlxsw_reg_ppcnt_pack(ppcnt_pl
, mlxsw_sp_port
->local_port
,
307 MLXSW_REG_PPCNT_PRIO_CNT
, prio
);
308 err
= mlxsw_reg_query(mlxsw_sp
->core
, MLXSW_REG(ppcnt
), ppcnt_pl
);
312 my_pfc
->requests
[prio
] = mlxsw_reg_ppcnt_tx_pause_get(ppcnt_pl
);
313 my_pfc
->indications
[prio
] = mlxsw_reg_ppcnt_rx_pause_get(ppcnt_pl
);
318 static int mlxsw_sp_dcbnl_ieee_getpfc(struct net_device
*dev
,
319 struct ieee_pfc
*pfc
)
321 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
324 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++) {
325 err
= mlxsw_sp_port_pfc_cnt_get(mlxsw_sp_port
, i
);
327 netdev_err(dev
, "Failed to get PFC count for priority %d\n",
333 memcpy(pfc
, mlxsw_sp_port
->dcb
.pfc
, sizeof(*pfc
));
338 static int mlxsw_sp_port_pfc_set(struct mlxsw_sp_port
*mlxsw_sp_port
,
339 struct ieee_pfc
*pfc
)
341 char pfcc_pl
[MLXSW_REG_PFCC_LEN
];
343 mlxsw_reg_pfcc_pack(pfcc_pl
, mlxsw_sp_port
->local_port
);
344 mlxsw_reg_pfcc_pprx_set(pfcc_pl
, mlxsw_sp_port
->link
.rx_pause
);
345 mlxsw_reg_pfcc_pptx_set(pfcc_pl
, mlxsw_sp_port
->link
.tx_pause
);
346 mlxsw_reg_pfcc_prio_pack(pfcc_pl
, pfc
->pfc_en
);
348 return mlxsw_reg_write(mlxsw_sp_port
->mlxsw_sp
->core
, MLXSW_REG(pfcc
),
352 static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device
*dev
,
353 struct ieee_pfc
*pfc
)
355 struct mlxsw_sp_port
*mlxsw_sp_port
= netdev_priv(dev
);
356 bool pause_en
= mlxsw_sp_port_is_pause_en(mlxsw_sp_port
);
359 if (pause_en
&& pfc
->pfc_en
) {
360 netdev_err(dev
, "PAUSE frames already enabled on port\n");
364 err
= __mlxsw_sp_port_headroom_set(mlxsw_sp_port
, dev
->mtu
,
365 mlxsw_sp_port
->dcb
.ets
->prio_tc
,
368 netdev_err(dev
, "Failed to configure port's headroom for PFC\n");
372 err
= mlxsw_sp_port_pfc_set(mlxsw_sp_port
, pfc
);
374 netdev_err(dev
, "Failed to configure PFC\n");
375 goto err_port_pfc_set
;
378 memcpy(mlxsw_sp_port
->dcb
.pfc
, pfc
, sizeof(*pfc
));
379 mlxsw_sp_port
->dcb
.pfc
->pfc_cap
= IEEE_8021QAZ_MAX_TCS
;
384 __mlxsw_sp_port_headroom_set(mlxsw_sp_port
, dev
->mtu
,
385 mlxsw_sp_port
->dcb
.ets
->prio_tc
, pause_en
,
386 mlxsw_sp_port
->dcb
.pfc
);
390 static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops
= {
391 .ieee_getets
= mlxsw_sp_dcbnl_ieee_getets
,
392 .ieee_setets
= mlxsw_sp_dcbnl_ieee_setets
,
393 .ieee_getmaxrate
= mlxsw_sp_dcbnl_ieee_getmaxrate
,
394 .ieee_setmaxrate
= mlxsw_sp_dcbnl_ieee_setmaxrate
,
395 .ieee_getpfc
= mlxsw_sp_dcbnl_ieee_getpfc
,
396 .ieee_setpfc
= mlxsw_sp_dcbnl_ieee_setpfc
,
398 .getdcbx
= mlxsw_sp_dcbnl_getdcbx
,
399 .setdcbx
= mlxsw_sp_dcbnl_setdcbx
,
402 static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port
*mlxsw_sp_port
)
404 mlxsw_sp_port
->dcb
.ets
= kzalloc(sizeof(*mlxsw_sp_port
->dcb
.ets
),
406 if (!mlxsw_sp_port
->dcb
.ets
)
409 mlxsw_sp_port
->dcb
.ets
->ets_cap
= IEEE_8021QAZ_MAX_TCS
;
414 static void mlxsw_sp_port_ets_fini(struct mlxsw_sp_port
*mlxsw_sp_port
)
416 kfree(mlxsw_sp_port
->dcb
.ets
);
419 static int mlxsw_sp_port_maxrate_init(struct mlxsw_sp_port
*mlxsw_sp_port
)
423 mlxsw_sp_port
->dcb
.maxrate
= kmalloc(sizeof(*mlxsw_sp_port
->dcb
.maxrate
),
425 if (!mlxsw_sp_port
->dcb
.maxrate
)
428 for (i
= 0; i
< IEEE_8021QAZ_MAX_TCS
; i
++)
429 mlxsw_sp_port
->dcb
.maxrate
->tc_maxrate
[i
] = MLXSW_REG_QEEC_MAS_DIS
;
434 static void mlxsw_sp_port_maxrate_fini(struct mlxsw_sp_port
*mlxsw_sp_port
)
436 kfree(mlxsw_sp_port
->dcb
.maxrate
);
439 static int mlxsw_sp_port_pfc_init(struct mlxsw_sp_port
*mlxsw_sp_port
)
441 mlxsw_sp_port
->dcb
.pfc
= kzalloc(sizeof(*mlxsw_sp_port
->dcb
.pfc
),
443 if (!mlxsw_sp_port
->dcb
.pfc
)
446 mlxsw_sp_port
->dcb
.pfc
->pfc_cap
= IEEE_8021QAZ_MAX_TCS
;
451 static void mlxsw_sp_port_pfc_fini(struct mlxsw_sp_port
*mlxsw_sp_port
)
453 kfree(mlxsw_sp_port
->dcb
.pfc
);
456 int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port
*mlxsw_sp_port
)
460 err
= mlxsw_sp_port_ets_init(mlxsw_sp_port
);
463 err
= mlxsw_sp_port_maxrate_init(mlxsw_sp_port
);
465 goto err_port_maxrate_init
;
466 err
= mlxsw_sp_port_pfc_init(mlxsw_sp_port
);
468 goto err_port_pfc_init
;
470 mlxsw_sp_port
->dev
->dcbnl_ops
= &mlxsw_sp_dcbnl_ops
;
475 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port
);
476 err_port_maxrate_init
:
477 mlxsw_sp_port_ets_fini(mlxsw_sp_port
);
481 void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port
*mlxsw_sp_port
)
483 mlxsw_sp_port_pfc_fini(mlxsw_sp_port
);
484 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port
);
485 mlxsw_sp_port_ets_fini(mlxsw_sp_port
);