aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2016-04-06 11:10:16 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-06 17:24:20 -0400
commitd81a6bdb87ce75337b453169ee39cdccb3286ddf (patch)
treee899fa5292a894d18859a7db35bb0cf6b8647b98
parent34dba0a59d072201171be1aeb9e52d1148d7c365 (diff)
mlxsw: spectrum: Add IEEE 802.1Qbb PFC support
Implement the appropriate DCB ops and allow a user to configure certain traffic classes as lossless. The operation configures PFC for both the egress (respecting PFC frames) and ingress (sending PFC frames) parts of the port. At egress, when a PFC frame is received for a PFC enabled priority, then all the priorities mapped to the same TC are stopped. At ingress, the priority group (PG) buffers to which the enabled PFC priorities are mapped are configured to be lossless. PFC frames will be transmitted when the Xoff threshold is crossed. The user-supplied delay parameter is used to determine the PG's size according to the following formula: PG_SIZE = PG_SIZE_LOSSY + delay * CELL_FACTOR + MTU In the worst case scenario the delay will be made up of packets that are all of size CELL_SIZE + 1, which means each packet will require almost twice its true size when buffered in the switch. We therefore multiply this value by the "cell factor", which is close to 2. Another MTU is added in case the transmitting host already started transmitting a maximum length frame when the PFC packet was received. As with PAUSE enabled ports, when the port's MTU is changed both the PGs' size and threshold are adjusted accordingly. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h12
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c117
4 files changed, 158 insertions, 11 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 84aacb36c12a..28f5b99e585a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -2442,6 +2442,16 @@ MLXSW_ITEM32(reg, pfcc, aprx, 0x0C, 30, 1);
2442 */ 2442 */
2443MLXSW_ITEM32(reg, pfcc, pfcrx, 0x0C, 16, 8); 2443MLXSW_ITEM32(reg, pfcc, pfcrx, 0x0C, 16, 8);
2444 2444
2445#define MLXSW_REG_PFCC_ALL_PRIO 0xFF
2446
2447static inline void mlxsw_reg_pfcc_prio_pack(char *payload, u8 pfc_en)
2448{
2449 mlxsw_reg_pfcc_prio_mask_tx_set(payload, MLXSW_REG_PFCC_ALL_PRIO);
2450 mlxsw_reg_pfcc_prio_mask_rx_set(payload, MLXSW_REG_PFCC_ALL_PRIO);
2451 mlxsw_reg_pfcc_pfctx_set(payload, pfc_en);
2452 mlxsw_reg_pfcc_pfcrx_set(payload, pfc_en);
2453}
2454
2445static inline void mlxsw_reg_pfcc_pack(char *payload, u8 local_port) 2455static inline void mlxsw_reg_pfcc_pack(char *payload, u8 local_port)
2446{ 2456{
2447 MLXSW_REG_ZERO(pfcc, payload); 2457 MLXSW_REG_ZERO(pfcc, payload);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 36a94a94a420..507263a2d226 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -451,24 +451,27 @@ static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
451} 451}
452 452
453static void mlxsw_sp_pg_buf_pack(char *pbmc_pl, int pg_index, int mtu, 453static void mlxsw_sp_pg_buf_pack(char *pbmc_pl, int pg_index, int mtu,
454 bool pause_en) 454 bool pause_en, bool pfc_en, u16 delay)
455{ 455{
456 u16 pg_size = 2 * MLXSW_SP_BYTES_TO_CELLS(mtu); 456 u16 pg_size = 2 * MLXSW_SP_BYTES_TO_CELLS(mtu);
457 457
458 if (pause_en) { 458 delay = pfc_en ? mlxsw_sp_pfc_delay_get(mtu, delay) :
459 u16 pg_pause_size = pg_size + MLXSW_SP_PAUSE_DELAY; 459 MLXSW_SP_PAUSE_DELAY;
460 460
461 if (pause_en || pfc_en)
461 mlxsw_reg_pbmc_lossless_buffer_pack(pbmc_pl, pg_index, 462 mlxsw_reg_pbmc_lossless_buffer_pack(pbmc_pl, pg_index,
462 pg_pause_size, pg_size); 463 pg_size + delay, pg_size);
463 } else { 464 else
464 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg_index, pg_size); 465 mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg_index, pg_size);
465 }
466} 466}
467 467
468int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, 468int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
469 u8 *prio_tc, bool pause_en) 469 u8 *prio_tc, bool pause_en,
470 struct ieee_pfc *my_pfc)
470{ 471{
471 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 472 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
473 u8 pfc_en = !!my_pfc ? my_pfc->pfc_en : 0;
474 u16 delay = !!my_pfc ? my_pfc->delay : 0;
472 char pbmc_pl[MLXSW_REG_PBMC_LEN]; 475 char pbmc_pl[MLXSW_REG_PBMC_LEN];
473 int i, j, err; 476 int i, j, err;
474 477
@@ -479,9 +482,11 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
479 482
480 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { 483 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
481 bool configure = false; 484 bool configure = false;
485 bool pfc = false;
482 486
483 for (j = 0; j < IEEE_8021QAZ_MAX_TCS; j++) { 487 for (j = 0; j < IEEE_8021QAZ_MAX_TCS; j++) {
484 if (prio_tc[j] == i) { 488 if (prio_tc[j] == i) {
489 pfc = pfc_en & BIT(j);
485 configure = true; 490 configure = true;
486 break; 491 break;
487 } 492 }
@@ -489,7 +494,7 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
489 494
490 if (!configure) 495 if (!configure)
491 continue; 496 continue;
492 mlxsw_sp_pg_buf_pack(pbmc_pl, i, mtu, pause_en); 497 mlxsw_sp_pg_buf_pack(pbmc_pl, i, mtu, pause_en, pfc, delay);
493 } 498 }
494 499
495 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); 500 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
@@ -500,12 +505,14 @@ static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
500{ 505{
501 u8 def_prio_tc[IEEE_8021QAZ_MAX_TCS] = {0}; 506 u8 def_prio_tc[IEEE_8021QAZ_MAX_TCS] = {0};
502 bool dcb_en = !!mlxsw_sp_port->dcb.ets; 507 bool dcb_en = !!mlxsw_sp_port->dcb.ets;
508 struct ieee_pfc *my_pfc;
503 u8 *prio_tc; 509 u8 *prio_tc;
504 510
505 prio_tc = dcb_en ? mlxsw_sp_port->dcb.ets->prio_tc : def_prio_tc; 511 prio_tc = dcb_en ? mlxsw_sp_port->dcb.ets->prio_tc : def_prio_tc;
512 my_pfc = dcb_en ? mlxsw_sp_port->dcb.pfc : NULL;
506 513
507 return __mlxsw_sp_port_headroom_set(mlxsw_sp_port, mtu, prio_tc, 514 return __mlxsw_sp_port_headroom_set(mlxsw_sp_port, mtu, prio_tc,
508 pause_en); 515 pause_en, my_pfc);
509} 516}
510 517
511static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu) 518static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu)
@@ -1032,6 +1039,11 @@ static int mlxsw_sp_port_set_pauseparam(struct net_device *dev,
1032 bool pause_en = pause->tx_pause || pause->rx_pause; 1039 bool pause_en = pause->tx_pause || pause->rx_pause;
1033 int err; 1040 int err;
1034 1041
1042 if (mlxsw_sp_port->dcb.pfc && mlxsw_sp_port->dcb.pfc->pfc_en) {
1043 netdev_err(dev, "PFC already enabled on port\n");
1044 return -EINVAL;
1045 }
1046
1035 if (pause->autoneg) { 1047 if (pause->autoneg) {
1036 netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n"); 1048 netdev_err(dev, "PAUSE frames autonegotiation isn't supported\n");
1037 return -EINVAL; 1049 return -EINVAL;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index f4b53dd34f22..47610a5ccd78 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -72,6 +72,14 @@
72 */ 72 */
73#define MLXSW_SP_PAUSE_DELAY 612 73#define MLXSW_SP_PAUSE_DELAY 612
74 74
75#define MLXSW_SP_CELL_FACTOR 2 /* 2 * cell_size / (IPG + cell_size + 1) */
76
77static inline u16 mlxsw_sp_pfc_delay_get(int mtu, u16 delay)
78{
79 delay = MLXSW_SP_BYTES_TO_CELLS(DIV_ROUND_UP(delay, BITS_PER_BYTE));
80 return MLXSW_SP_CELL_FACTOR * delay + MLXSW_SP_BYTES_TO_CELLS(mtu);
81}
82
75struct mlxsw_sp_port; 83struct mlxsw_sp_port;
76 84
77struct mlxsw_sp_upper { 85struct mlxsw_sp_upper {
@@ -183,6 +191,7 @@ struct mlxsw_sp_port {
183 struct { 191 struct {
184 struct ieee_ets *ets; 192 struct ieee_ets *ets;
185 struct ieee_maxrate *maxrate; 193 struct ieee_maxrate *maxrate;
194 struct ieee_pfc *pfc;
186 } dcb; 195 } dcb;
187 /* 802.1Q bridge VLANs */ 196 /* 802.1Q bridge VLANs */
188 unsigned long *active_vlans; 197 unsigned long *active_vlans;
@@ -295,7 +304,8 @@ int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
295int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port, 304int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
296 u8 switch_prio, u8 tclass); 305 u8 switch_prio, u8 tclass);
297int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, 306int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
298 u8 *prio_tc, bool pause_en); 307 u8 *prio_tc, bool pause_en,
308 struct ieee_pfc *my_pfc);
299int mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port, 309int mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port,
300 enum mlxsw_reg_qeec_hr hr, u8 index, 310 enum mlxsw_reg_qeec_hr hr, u8 index,
301 u8 next_index, u32 maxrate); 311 u8 next_index, u32 maxrate);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
index 8786424f6191..0b323661c0b6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
@@ -34,6 +34,7 @@
34 34
35#include <linux/netdevice.h> 35#include <linux/netdevice.h>
36#include <linux/string.h> 36#include <linux/string.h>
37#include <linux/bitops.h>
37#include <net/dcbnl.h> 38#include <net/dcbnl.h>
38 39
39#include "spectrum.h" 40#include "spectrum.h"
@@ -151,7 +152,8 @@ static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
151 * traffic is still directed to them. 152 * traffic is still directed to them.
152 */ 153 */
153 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu, 154 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
154 ets->prio_tc, pause_en); 155 ets->prio_tc, pause_en,
156 mlxsw_sp_port->dcb.pfc);
155 if (err) { 157 if (err) {
156 netdev_err(dev, "Failed to configure port's headroom\n"); 158 netdev_err(dev, "Failed to configure port's headroom\n");
157 return err; 159 return err;
@@ -291,11 +293,101 @@ err_port_ets_maxrate_set:
291 return err; 293 return err;
292} 294}
293 295
296static int mlxsw_sp_port_pfc_cnt_get(struct mlxsw_sp_port *mlxsw_sp_port,
297 u8 prio)
298{
299 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
300 struct ieee_pfc *my_pfc = mlxsw_sp_port->dcb.pfc;
301 char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
302 int err;
303
304 mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port,
305 MLXSW_REG_PPCNT_PRIO_CNT, prio);
306 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
307 if (err)
308 return err;
309
310 my_pfc->requests[prio] = mlxsw_reg_ppcnt_tx_pause_get(ppcnt_pl);
311 my_pfc->indications[prio] = mlxsw_reg_ppcnt_rx_pause_get(ppcnt_pl);
312
313 return 0;
314}
315
316static int mlxsw_sp_dcbnl_ieee_getpfc(struct net_device *dev,
317 struct ieee_pfc *pfc)
318{
319 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
320 int err, i;
321
322 for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
323 err = mlxsw_sp_port_pfc_cnt_get(mlxsw_sp_port, i);
324 if (err) {
325 netdev_err(dev, "Failed to get PFC count for priority %d\n",
326 i);
327 return err;
328 }
329 }
330
331 memcpy(pfc, mlxsw_sp_port->dcb.pfc, sizeof(*pfc));
332
333 return 0;
334}
335
336static int mlxsw_sp_port_pfc_set(struct mlxsw_sp_port *mlxsw_sp_port,
337 struct ieee_pfc *pfc)
338{
339 char pfcc_pl[MLXSW_REG_PFCC_LEN];
340
341 mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port);
342 mlxsw_reg_pfcc_prio_pack(pfcc_pl, pfc->pfc_en);
343
344 return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc),
345 pfcc_pl);
346}
347
348static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
349 struct ieee_pfc *pfc)
350{
351 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
352 int err;
353
354 if (mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) {
355 netdev_err(dev, "PAUSE frames already enabled on port\n");
356 return -EINVAL;
357 }
358
359 err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
360 mlxsw_sp_port->dcb.ets->prio_tc,
361 false, pfc);
362 if (err) {
363 netdev_err(dev, "Failed to configure port's headroom for PFC\n");
364 return err;
365 }
366
367 err = mlxsw_sp_port_pfc_set(mlxsw_sp_port, pfc);
368 if (err) {
369 netdev_err(dev, "Failed to configure PFC\n");
370 goto err_port_pfc_set;
371 }
372
373 memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc));
374
375 return 0;
376
377err_port_pfc_set:
378 __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
379 mlxsw_sp_port->dcb.ets->prio_tc, false,
380 mlxsw_sp_port->dcb.pfc);
381 return err;
382}
383
294static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = { 384static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = {
295 .ieee_getets = mlxsw_sp_dcbnl_ieee_getets, 385 .ieee_getets = mlxsw_sp_dcbnl_ieee_getets,
296 .ieee_setets = mlxsw_sp_dcbnl_ieee_setets, 386 .ieee_setets = mlxsw_sp_dcbnl_ieee_setets,
297 .ieee_getmaxrate = mlxsw_sp_dcbnl_ieee_getmaxrate, 387 .ieee_getmaxrate = mlxsw_sp_dcbnl_ieee_getmaxrate,
298 .ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate, 388 .ieee_setmaxrate = mlxsw_sp_dcbnl_ieee_setmaxrate,
389 .ieee_getpfc = mlxsw_sp_dcbnl_ieee_getpfc,
390 .ieee_setpfc = mlxsw_sp_dcbnl_ieee_setpfc,
299 391
300 .getdcbx = mlxsw_sp_dcbnl_getdcbx, 392 .getdcbx = mlxsw_sp_dcbnl_getdcbx,
301 .setdcbx = mlxsw_sp_dcbnl_setdcbx, 393 .setdcbx = mlxsw_sp_dcbnl_setdcbx,
@@ -338,6 +430,23 @@ static void mlxsw_sp_port_maxrate_fini(struct mlxsw_sp_port *mlxsw_sp_port)
338 kfree(mlxsw_sp_port->dcb.maxrate); 430 kfree(mlxsw_sp_port->dcb.maxrate);
339} 431}
340 432
433static int mlxsw_sp_port_pfc_init(struct mlxsw_sp_port *mlxsw_sp_port)
434{
435 mlxsw_sp_port->dcb.pfc = kzalloc(sizeof(*mlxsw_sp_port->dcb.pfc),
436 GFP_KERNEL);
437 if (!mlxsw_sp_port->dcb.pfc)
438 return -ENOMEM;
439
440 mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
441
442 return 0;
443}
444
445static void mlxsw_sp_port_pfc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
446{
447 kfree(mlxsw_sp_port->dcb.pfc);
448}
449
341int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port) 450int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port)
342{ 451{
343 int err; 452 int err;
@@ -348,11 +457,16 @@ int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port)
348 err = mlxsw_sp_port_maxrate_init(mlxsw_sp_port); 457 err = mlxsw_sp_port_maxrate_init(mlxsw_sp_port);
349 if (err) 458 if (err)
350 goto err_port_maxrate_init; 459 goto err_port_maxrate_init;
460 err = mlxsw_sp_port_pfc_init(mlxsw_sp_port);
461 if (err)
462 goto err_port_pfc_init;
351 463
352 mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops; 464 mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops;
353 465
354 return 0; 466 return 0;
355 467
468err_port_pfc_init:
469 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port);
356err_port_maxrate_init: 470err_port_maxrate_init:
357 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 471 mlxsw_sp_port_ets_fini(mlxsw_sp_port);
358 return err; 472 return err;
@@ -360,6 +474,7 @@ err_port_maxrate_init:
360 474
361void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port) 475void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
362{ 476{
477 mlxsw_sp_port_pfc_fini(mlxsw_sp_port);
363 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port); 478 mlxsw_sp_port_maxrate_fini(mlxsw_sp_port);
364 mlxsw_sp_port_ets_fini(mlxsw_sp_port); 479 mlxsw_sp_port_ets_fini(mlxsw_sp_port);
365} 480}