Skip to content

Commit 18f1e70

Browse files
idoschdavem330
authored andcommitted
mlxsw: spectrum: Introduce port splitting
Allow a user to split or unsplit a port using the newly introduced devlink ops. Once split, the original netdev is destroyed and 2 or 4 others are created, according to user configuration. The new ports are like any other port, with the sole difference of supporting a lower maximum speed. When unsplit, the reverse process takes place. 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 a133318 commit 18f1e70

File tree

3 files changed

+219
-3
lines changed

3 files changed

+219
-3
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959

6060
#define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS)
6161

62+
#define MLXSW_PORT_MODULE_MAX_WIDTH 4
63+
6264
enum mlxsw_port_admin_status {
6365
MLXSW_PORT_ADMIN_STATUS_UP = 1,
6466
MLXSW_PORT_ADMIN_STATUS_DOWN = 2,

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

Lines changed: 211 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,22 @@ static int mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp,
321321
return 0;
322322
}
323323

324+
static int mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port,
325+
u8 module, u8 width, u8 lane)
326+
{
327+
char pmlp_pl[MLXSW_REG_PMLP_LEN];
328+
int i;
329+
330+
mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
331+
mlxsw_reg_pmlp_width_set(pmlp_pl, width);
332+
for (i = 0; i < width; i++) {
333+
mlxsw_reg_pmlp_module_set(pmlp_pl, i, module);
334+
mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, lane + i); /* Rx & Tx */
335+
}
336+
337+
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
338+
}
339+
324340
static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port)
325341
{
326342
char pmlp_pl[MLXSW_REG_PMLP_LEN];
@@ -1284,6 +1300,18 @@ static u32 mlxsw_sp_to_ptys_speed(u32 speed)
12841300
return ptys_proto;
12851301
}
12861302

1303+
static u32 mlxsw_sp_to_ptys_upper_speed(u32 upper_speed)
1304+
{
1305+
u32 ptys_proto = 0;
1306+
int i;
1307+
1308+
for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
1309+
if (mlxsw_sp_port_link_mode[i].speed <= upper_speed)
1310+
ptys_proto |= mlxsw_sp_port_link_mode[i].mask;
1311+
}
1312+
return ptys_proto;
1313+
}
1314+
12871315
static int mlxsw_sp_port_set_settings(struct net_device *dev,
12881316
struct ethtool_cmd *cmd)
12891317
{
@@ -1360,7 +1388,22 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
13601388
.set_settings = mlxsw_sp_port_set_settings,
13611389
};
13621390

1363-
static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
1391+
static int
1392+
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
1393+
{
1394+
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1395+
u32 upper_speed = MLXSW_SP_PORT_BASE_SPEED * width;
1396+
char ptys_pl[MLXSW_REG_PTYS_LEN];
1397+
u32 eth_proto_admin;
1398+
1399+
eth_proto_admin = mlxsw_sp_to_ptys_upper_speed(upper_speed);
1400+
mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port,
1401+
eth_proto_admin);
1402+
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
1403+
}
1404+
1405+
static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
1406+
bool split, u8 module, u8 width)
13641407
{
13651408
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
13661409
struct mlxsw_sp_port *mlxsw_sp_port;
@@ -1376,6 +1419,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
13761419
mlxsw_sp_port->dev = dev;
13771420
mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
13781421
mlxsw_sp_port->local_port = local_port;
1422+
mlxsw_sp_port->split = split;
13791423
bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
13801424
mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
13811425
if (!mlxsw_sp_port->active_vlans) {
@@ -1417,6 +1461,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
14171461
dev->hard_header_len += MLXSW_TXHDR_LEN;
14181462

14191463
devlink_port = &mlxsw_sp_port->devlink_port;
1464+
if (mlxsw_sp_port->split)
1465+
devlink_port_split_set(devlink_port, module);
14201466
err = devlink_port_register(devlink, devlink_port, local_port);
14211467
if (err) {
14221468
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register devlink port\n",
@@ -1438,6 +1484,13 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
14381484
goto err_port_swid_set;
14391485
}
14401486

1487+
err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port, width);
1488+
if (err) {
1489+
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
1490+
mlxsw_sp_port->local_port);
1491+
goto err_port_speed_by_width_set;
1492+
}
1493+
14411494
err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
14421495
if (err) {
14431496
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
@@ -1479,6 +1532,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
14791532
err_port_buffers_init:
14801533
err_port_admin_status_set:
14811534
err_port_mtu_set:
1535+
err_port_speed_by_width_set:
14821536
err_port_swid_set:
14831537
err_port_system_port_mapping_set:
14841538
devlink_port_unregister(&mlxsw_sp_port->devlink_port);
@@ -1494,6 +1548,28 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
14941548
return err;
14951549
}
14961550

1551+
static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
1552+
bool split, u8 module, u8 width, u8 lane)
1553+
{
1554+
int err;
1555+
1556+
err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, module, width,
1557+
lane);
1558+
if (err)
1559+
return err;
1560+
1561+
err = __mlxsw_sp_port_create(mlxsw_sp, local_port, split, module,
1562+
width);
1563+
if (err)
1564+
goto err_port_create;
1565+
1566+
return 0;
1567+
1568+
err_port_create:
1569+
mlxsw_sp_port_module_unmap(mlxsw_sp, local_port);
1570+
return err;
1571+
}
1572+
14971573
static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
14981574
{
14991575
struct net_device *dev = mlxsw_sp_port->dev;
@@ -1562,7 +1638,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
15621638
if (!width)
15631639
continue;
15641640
mlxsw_sp->port_to_module[i] = module;
1565-
err = mlxsw_sp_port_create(mlxsw_sp, i);
1641+
err = __mlxsw_sp_port_create(mlxsw_sp, i, false, module, width);
15661642
if (err)
15671643
goto err_port_create;
15681644
}
@@ -1576,6 +1652,137 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
15761652
return err;
15771653
}
15781654

1655+
static u8 mlxsw_sp_cluster_base_port_get(u8 local_port)
1656+
{
1657+
u8 offset = (local_port - 1) % MLXSW_SP_PORTS_PER_CLUSTER_MAX;
1658+
1659+
return local_port - offset;
1660+
}
1661+
1662+
static int mlxsw_sp_port_split(void *priv, u8 local_port, unsigned int count)
1663+
{
1664+
struct mlxsw_sp *mlxsw_sp = priv;
1665+
struct mlxsw_sp_port *mlxsw_sp_port;
1666+
u8 width = MLXSW_PORT_MODULE_MAX_WIDTH / count;
1667+
u8 module, cur_width, base_port;
1668+
int i;
1669+
int err;
1670+
1671+
mlxsw_sp_port = mlxsw_sp->ports[local_port];
1672+
if (!mlxsw_sp_port) {
1673+
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
1674+
local_port);
1675+
return -EINVAL;
1676+
}
1677+
1678+
if (count != 2 && count != 4) {
1679+
netdev_err(mlxsw_sp_port->dev, "Port can only be split into 2 or 4 ports\n");
1680+
return -EINVAL;
1681+
}
1682+
1683+
err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
1684+
&cur_width);
1685+
if (err) {
1686+
netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
1687+
return err;
1688+
}
1689+
1690+
if (cur_width != MLXSW_PORT_MODULE_MAX_WIDTH) {
1691+
netdev_err(mlxsw_sp_port->dev, "Port cannot be split further\n");
1692+
return -EINVAL;
1693+
}
1694+
1695+
/* Make sure we have enough slave (even) ports for the split. */
1696+
if (count == 2) {
1697+
base_port = local_port;
1698+
if (mlxsw_sp->ports[base_port + 1]) {
1699+
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
1700+
return -EINVAL;
1701+
}
1702+
} else {
1703+
base_port = mlxsw_sp_cluster_base_port_get(local_port);
1704+
if (mlxsw_sp->ports[base_port + 1] ||
1705+
mlxsw_sp->ports[base_port + 3]) {
1706+
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
1707+
return -EINVAL;
1708+
}
1709+
}
1710+
1711+
for (i = 0; i < count; i++)
1712+
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
1713+
1714+
for (i = 0; i < count; i++) {
1715+
err = mlxsw_sp_port_create(mlxsw_sp, base_port + i, true,
1716+
module, width, i * width);
1717+
if (err) {
1718+
dev_err(mlxsw_sp->bus_info->dev, "Failed to create split port\n");
1719+
goto err_port_create;
1720+
}
1721+
}
1722+
1723+
return 0;
1724+
1725+
err_port_create:
1726+
for (i--; i >= 0; i--)
1727+
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
1728+
for (i = 0; i < count / 2; i++) {
1729+
module = mlxsw_sp->port_to_module[base_port + i * 2];
1730+
mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
1731+
module, MLXSW_PORT_MODULE_MAX_WIDTH, 0);
1732+
}
1733+
return err;
1734+
}
1735+
1736+
static int mlxsw_sp_port_unsplit(void *priv, u8 local_port)
1737+
{
1738+
struct mlxsw_sp *mlxsw_sp = priv;
1739+
struct mlxsw_sp_port *mlxsw_sp_port;
1740+
u8 module, cur_width, base_port;
1741+
unsigned int count;
1742+
int i;
1743+
int err;
1744+
1745+
mlxsw_sp_port = mlxsw_sp->ports[local_port];
1746+
if (!mlxsw_sp_port) {
1747+
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
1748+
local_port);
1749+
return -EINVAL;
1750+
}
1751+
1752+
if (!mlxsw_sp_port->split) {
1753+
netdev_err(mlxsw_sp_port->dev, "Port wasn't split\n");
1754+
return -EINVAL;
1755+
}
1756+
1757+
err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
1758+
&cur_width);
1759+
if (err) {
1760+
netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
1761+
return err;
1762+
}
1763+
count = cur_width == 1 ? 4 : 2;
1764+
1765+
base_port = mlxsw_sp_cluster_base_port_get(local_port);
1766+
1767+
/* Determine which ports to remove. */
1768+
if (count == 2 && local_port >= base_port + 2)
1769+
base_port = base_port + 2;
1770+
1771+
for (i = 0; i < count; i++)
1772+
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
1773+
1774+
for (i = 0; i < count / 2; i++) {
1775+
module = mlxsw_sp->port_to_module[base_port + i * 2];
1776+
err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
1777+
module, MLXSW_PORT_MODULE_MAX_WIDTH,
1778+
0);
1779+
if (err)
1780+
dev_err(mlxsw_sp->bus_info->dev, "Failed to reinstantiate port\n");
1781+
}
1782+
1783+
return 0;
1784+
}
1785+
15791786
static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
15801787
char *pude_pl, void *priv)
15811788
{
@@ -1999,6 +2206,8 @@ static struct mlxsw_driver mlxsw_sp_driver = {
19992206
.priv_size = sizeof(struct mlxsw_sp),
20002207
.init = mlxsw_sp_init,
20012208
.fini = mlxsw_sp_fini,
2209+
.port_split = mlxsw_sp_port_split,
2210+
.port_unsplit = mlxsw_sp_port_unsplit,
20022211
.txhdr_construct = mlxsw_sp_txhdr_construct,
20032212
.txhdr_len = MLXSW_TXHDR_LEN,
20042213
.profile = &mlxsw_sp_config_profile,

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@
5858

5959
#define MLXSW_SP_MID_MAX 7000
6060

61+
#define MLXSW_SP_PORTS_PER_CLUSTER_MAX 4
62+
63+
#define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */
64+
6165
struct mlxsw_sp_port;
6266

6367
struct mlxsw_sp_upper {
@@ -151,7 +155,8 @@ struct mlxsw_sp_port {
151155
learning_sync:1,
152156
uc_flood:1,
153157
bridged:1,
154-
lagged:1;
158+
lagged:1,
159+
split:1;
155160
u16 pvid;
156161
u16 lag_id;
157162
struct {

0 commit comments

Comments
 (0)