Skip to content

Commit 8e8dfe9

Browse files
idoschdavem330
authored andcommitted
mlxsw: spectrum: Add IEEE 802.1Qaz ETS support
Implement the appropriate DCB ops and allow a user to configure: * Priority to traffic class (TC) mapping with a total of 8 supported TCs * Transmission selection algorithm (TSA) for each TC and the corresponding weights in case of weighted round robin (WRR) As previously explained, we treat the priority group (PG) buffer in the port's headroom as the ingress counterpart of the egress TC. Therefore, when a certain priority to TC mapping is configured, we also configure the port's headroom buffer. 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>
1 parent f00817d commit 8e8dfe9

File tree

3 files changed

+283
-10
lines changed

3 files changed

+283
-10
lines changed

drivers/net/ethernet/mellanox/mlxsw/spectrum.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -450,22 +450,55 @@ static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
450450
return 0;
451451
}
452452

453-
static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
454-
int mtu)
453+
static void mlxsw_sp_pg_buf_pack(char *pbmc_pl, int pg_index, int mtu)
455454
{
456-
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
457455
u16 pg_size = 2 * MLXSW_SP_BYTES_TO_CELLS(mtu);
456+
457+
mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg_index, pg_size);
458+
}
459+
460+
int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
461+
u8 *prio_tc)
462+
{
463+
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
458464
char pbmc_pl[MLXSW_REG_PBMC_LEN];
459-
int err;
465+
int i, j, err;
460466

461467
mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0, 0);
462468
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
463469
if (err)
464470
return err;
465-
mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, 0, pg_size);
471+
472+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
473+
bool configure = false;
474+
475+
for (j = 0; j < IEEE_8021QAZ_MAX_TCS; j++) {
476+
if (prio_tc[j] == i) {
477+
configure = true;
478+
break;
479+
}
480+
}
481+
482+
if (!configure)
483+
continue;
484+
mlxsw_sp_pg_buf_pack(pbmc_pl, i, mtu);
485+
}
486+
466487
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
467488
}
468489

490+
static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
491+
int mtu)
492+
{
493+
u8 def_prio_tc[IEEE_8021QAZ_MAX_TCS] = {0};
494+
bool dcb_en = !!mlxsw_sp_port->dcb.ets;
495+
u8 *prio_tc;
496+
497+
prio_tc = dcb_en ? mlxsw_sp_port->dcb.ets->prio_tc : def_prio_tc;
498+
499+
return __mlxsw_sp_port_headroom_set(mlxsw_sp_port, mtu, prio_tc);
500+
}
501+
469502
static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu)
470503
{
471504
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
@@ -1465,9 +1498,9 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
14651498
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
14661499
}
14671500

1468-
static int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
1469-
enum mlxsw_reg_qeec_hr hr, u8 index,
1470-
u8 next_index, bool dwrr, u8 dwrr_weight)
1501+
int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
1502+
enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
1503+
bool dwrr, u8 dwrr_weight)
14711504
{
14721505
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
14731506
char qeec_pl[MLXSW_REG_QEEC_LEN];
@@ -1494,8 +1527,8 @@ static int mlxsw_sp_port_ets_maxrate_set(struct mlxsw_sp_port *mlxsw_sp_port,
14941527
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
14951528
}
14961529

1497-
static int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
1498-
u8 switch_prio, u8 tclass)
1530+
int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
1531+
u8 switch_prio, u8 tclass)
14991532
{
15001533
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
15011534
char qtct_pl[MLXSW_REG_QTCT_LEN];

drivers/net/ethernet/mellanox/mlxsw/spectrum.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/bitops.h>
4343
#include <linux/if_vlan.h>
4444
#include <linux/list.h>
45+
#include <linux/dcbnl.h>
4546
#include <net/switchdev.h>
4647
#include <net/devlink.h>
4748

@@ -170,6 +171,9 @@ struct mlxsw_sp_port {
170171
struct mlxsw_sp_vfid *vfid;
171172
u16 vid;
172173
} vport;
174+
struct {
175+
struct ieee_ets *ets;
176+
} dcb;
173177
/* 802.1Q bridge VLANs */
174178
unsigned long *active_vlans;
175179
unsigned long *untagged_vlans;
@@ -269,6 +273,13 @@ int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
269273
bool set, bool only_uc);
270274
void mlxsw_sp_port_active_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port);
271275
int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
276+
int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
277+
enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
278+
bool dwrr, u8 dwrr_weight);
279+
int mlxsw_sp_port_prio_tc_set(struct mlxsw_sp_port *mlxsw_sp_port,
280+
u8 switch_prio, u8 tclass);
281+
int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu,
282+
u8 *prio_tc);
272283

273284
#ifdef CONFIG_MLXSW_SPECTRUM_DCB
274285

drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@
3333
*/
3434

3535
#include <linux/netdevice.h>
36+
#include <linux/string.h>
3637
#include <net/dcbnl.h>
3738

3839
#include "spectrum.h"
40+
#include "reg.h"
3941

4042
static u8 mlxsw_sp_dcbnl_getdcbx(struct net_device __always_unused *dev)
4143
{
@@ -48,18 +50,245 @@ static u8 mlxsw_sp_dcbnl_setdcbx(struct net_device __always_unused *dev,
4850
return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0;
4951
}
5052

53+
static int mlxsw_sp_dcbnl_ieee_getets(struct net_device *dev,
54+
struct ieee_ets *ets)
55+
{
56+
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
57+
58+
memcpy(ets, mlxsw_sp_port->dcb.ets, sizeof(*ets));
59+
60+
return 0;
61+
}
62+
63+
static int mlxsw_sp_port_ets_validate(struct mlxsw_sp_port *mlxsw_sp_port,
64+
struct ieee_ets *ets)
65+
{
66+
struct net_device *dev = mlxsw_sp_port->dev;
67+
bool has_ets_tc = false;
68+
int i, tx_bw_sum = 0;
69+
70+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
71+
switch (ets->tc_tsa[i]) {
72+
case IEEE_8021QAZ_TSA_STRICT:
73+
break;
74+
case IEEE_8021QAZ_TSA_ETS:
75+
has_ets_tc = true;
76+
tx_bw_sum += ets->tc_tx_bw[i];
77+
break;
78+
default:
79+
netdev_err(dev, "Only strict priority and ETS are supported\n");
80+
return -EINVAL;
81+
}
82+
83+
if (ets->prio_tc[i] >= IEEE_8021QAZ_MAX_TCS) {
84+
netdev_err(dev, "Invalid TC\n");
85+
return -EINVAL;
86+
}
87+
}
88+
89+
if (has_ets_tc && tx_bw_sum != 100) {
90+
netdev_err(dev, "Total ETS bandwidth should equal 100\n");
91+
return -EINVAL;
92+
}
93+
94+
return 0;
95+
}
96+
97+
static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port *mlxsw_sp_port,
98+
u8 *prio_tc)
99+
{
100+
char pptb_pl[MLXSW_REG_PPTB_LEN];
101+
int i;
102+
103+
mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
104+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
105+
mlxsw_reg_pptb_prio_to_buff_set(pptb_pl, i, prio_tc[i]);
106+
return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
107+
pptb_pl);
108+
}
109+
110+
static bool mlxsw_sp_ets_has_pg(u8 *prio_tc, u8 pg)
111+
{
112+
int i;
113+
114+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
115+
if (prio_tc[i] == pg)
116+
return true;
117+
return false;
118+
}
119+
120+
static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
121+
u8 *old_prio_tc, u8 *new_prio_tc)
122+
{
123+
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
124+
char pbmc_pl[MLXSW_REG_PBMC_LEN];
125+
int err, i;
126+
127+
mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0, 0);
128+
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
129+
if (err)
130+
return err;
131+
132+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
133+
u8 pg = old_prio_tc[i];
134+
135+
if (!mlxsw_sp_ets_has_pg(new_prio_tc, pg))
136+
mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg, 0);
137+
}
138+
139+
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
140+
}
141+
142+
static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
143+
struct ieee_ets *ets)
144+
{
145+
struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
146+
struct net_device *dev = mlxsw_sp_port->dev;
147+
int err;
148+
149+
/* Create the required PGs, but don't destroy existing ones, as
150+
* traffic is still directed to them.
151+
*/
152+
err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
153+
ets->prio_tc);
154+
if (err) {
155+
netdev_err(dev, "Failed to configure port's headroom\n");
156+
return err;
157+
}
158+
159+
err = mlxsw_sp_port_pg_prio_map(mlxsw_sp_port, ets->prio_tc);
160+
if (err) {
161+
netdev_err(dev, "Failed to set PG-priority mapping\n");
162+
goto err_port_prio_pg_map;
163+
}
164+
165+
err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc,
166+
ets->prio_tc);
167+
if (err)
168+
netdev_warn(dev, "Failed to remove ununsed PGs\n");
169+
170+
return 0;
171+
172+
err_port_prio_pg_map:
173+
mlxsw_sp_port_pg_destroy(mlxsw_sp_port, ets->prio_tc, my_ets->prio_tc);
174+
return err;
175+
}
176+
177+
static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port,
178+
struct ieee_ets *ets)
179+
{
180+
struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
181+
struct net_device *dev = mlxsw_sp_port->dev;
182+
int i, err;
183+
184+
/* Egress configuration. */
185+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
186+
bool dwrr = ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS;
187+
u8 weight = ets->tc_tx_bw[i];
188+
189+
err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
190+
MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i,
191+
0, dwrr, weight);
192+
if (err) {
193+
netdev_err(dev, "Failed to link subgroup ETS element %d to group\n",
194+
i);
195+
goto err_port_ets_set;
196+
}
197+
}
198+
199+
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
200+
err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
201+
ets->prio_tc[i]);
202+
if (err) {
203+
netdev_err(dev, "Failed to map prio %d to TC %d\n", i,
204+
ets->prio_tc[i]);
205+
goto err_port_prio_tc_set;
206+
}
207+
}
208+
209+
/* Ingress configuration. */
210+
err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets);
211+
if (err)
212+
goto err_port_headroom_set;
213+
214+
return 0;
215+
216+
err_port_headroom_set:
217+
i = IEEE_8021QAZ_MAX_TCS;
218+
err_port_prio_tc_set:
219+
for (i--; i >= 0; i--)
220+
mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, my_ets->prio_tc[i]);
221+
i = IEEE_8021QAZ_MAX_TCS;
222+
err_port_ets_set:
223+
for (i--; i >= 0; i--) {
224+
bool dwrr = my_ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS;
225+
u8 weight = my_ets->tc_tx_bw[i];
226+
227+
err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
228+
MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i,
229+
0, dwrr, weight);
230+
}
231+
return err;
232+
}
233+
234+
static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev,
235+
struct ieee_ets *ets)
236+
{
237+
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
238+
int err;
239+
240+
err = mlxsw_sp_port_ets_validate(mlxsw_sp_port, ets);
241+
if (err)
242+
return err;
243+
244+
err = __mlxsw_sp_dcbnl_ieee_setets(mlxsw_sp_port, ets);
245+
if (err)
246+
return err;
247+
248+
memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets));
249+
250+
return 0;
251+
}
252+
51253
static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = {
254+
.ieee_getets = mlxsw_sp_dcbnl_ieee_getets,
255+
.ieee_setets = mlxsw_sp_dcbnl_ieee_setets,
256+
52257
.getdcbx = mlxsw_sp_dcbnl_getdcbx,
53258
.setdcbx = mlxsw_sp_dcbnl_setdcbx,
54259
};
55260

261+
static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
262+
{
263+
mlxsw_sp_port->dcb.ets = kzalloc(sizeof(*mlxsw_sp_port->dcb.ets),
264+
GFP_KERNEL);
265+
if (!mlxsw_sp_port->dcb.ets)
266+
return -ENOMEM;
267+
268+
mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
269+
270+
return 0;
271+
}
272+
273+
static void mlxsw_sp_port_ets_fini(struct mlxsw_sp_port *mlxsw_sp_port)
274+
{
275+
kfree(mlxsw_sp_port->dcb.ets);
276+
}
277+
56278
int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port)
57279
{
280+
int err;
281+
282+
err = mlxsw_sp_port_ets_init(mlxsw_sp_port);
283+
if (err)
284+
return err;
285+
58286
mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops;
59287

60288
return 0;
61289
}
62290

63291
void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
64292
{
293+
mlxsw_sp_port_ets_fini(mlxsw_sp_port);
65294
}

0 commit comments

Comments
 (0)