aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2016-02-26 11:32:31 -0500
committerDavid S. Miller <davem@davemloft.net>2016-03-01 16:07:31 -0500
commit18f1e70c413713f28629ffe6863a2c43248ff7a3 (patch)
tree459917c7636f20843eddce7956b4949bfa28b0c6
parenta133318cde2000a3264032ea3b561c9054613486 (diff)
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>
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/port.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c213
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h7
3 files changed, 219 insertions, 3 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/port.h b/drivers/net/ethernet/mellanox/mlxsw/port.h
index ae65b9940aed..f33b997f2b61 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/port.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/port.h
@@ -59,6 +59,8 @@
59 59
60#define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS) 60#define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS)
61 61
62#define MLXSW_PORT_MODULE_MAX_WIDTH 4
63
62enum mlxsw_port_admin_status { 64enum mlxsw_port_admin_status {
63 MLXSW_PORT_ADMIN_STATUS_UP = 1, 65 MLXSW_PORT_ADMIN_STATUS_UP = 1,
64 MLXSW_PORT_ADMIN_STATUS_DOWN = 2, 66 MLXSW_PORT_ADMIN_STATUS_DOWN = 2,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 926019e86c36..53487d3eb9f6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -321,6 +321,22 @@ static int mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp,
321 return 0; 321 return 0;
322} 322}
323 323
324static 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
324static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) 340static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port)
325{ 341{
326 char pmlp_pl[MLXSW_REG_PMLP_LEN]; 342 char pmlp_pl[MLXSW_REG_PMLP_LEN];
@@ -1284,6 +1300,18 @@ static u32 mlxsw_sp_to_ptys_speed(u32 speed)
1284 return ptys_proto; 1300 return ptys_proto;
1285} 1301}
1286 1302
1303static 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
1287static int mlxsw_sp_port_set_settings(struct net_device *dev, 1315static int mlxsw_sp_port_set_settings(struct net_device *dev,
1288 struct ethtool_cmd *cmd) 1316 struct ethtool_cmd *cmd)
1289{ 1317{
@@ -1360,7 +1388,22 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
1360 .set_settings = mlxsw_sp_port_set_settings, 1388 .set_settings = mlxsw_sp_port_set_settings,
1361}; 1389};
1362 1390
1363static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) 1391static int
1392mlxsw_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
1405static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
1406 bool split, u8 module, u8 width)
1364{ 1407{
1365 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); 1408 struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
1366 struct mlxsw_sp_port *mlxsw_sp_port; 1409 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)
1376 mlxsw_sp_port->dev = dev; 1419 mlxsw_sp_port->dev = dev;
1377 mlxsw_sp_port->mlxsw_sp = mlxsw_sp; 1420 mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
1378 mlxsw_sp_port->local_port = local_port; 1421 mlxsw_sp_port->local_port = local_port;
1422 mlxsw_sp_port->split = split;
1379 bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE); 1423 bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
1380 mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL); 1424 mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
1381 if (!mlxsw_sp_port->active_vlans) { 1425 if (!mlxsw_sp_port->active_vlans) {
@@ -1417,6 +1461,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
1417 dev->hard_header_len += MLXSW_TXHDR_LEN; 1461 dev->hard_header_len += MLXSW_TXHDR_LEN;
1418 1462
1419 devlink_port = &mlxsw_sp_port->devlink_port; 1463 devlink_port = &mlxsw_sp_port->devlink_port;
1464 if (mlxsw_sp_port->split)
1465 devlink_port_split_set(devlink_port, module);
1420 err = devlink_port_register(devlink, devlink_port, local_port); 1466 err = devlink_port_register(devlink, devlink_port, local_port);
1421 if (err) { 1467 if (err) {
1422 dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register devlink port\n", 1468 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)
1438 goto err_port_swid_set; 1484 goto err_port_swid_set;
1439 } 1485 }
1440 1486
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
1441 err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN); 1494 err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
1442 if (err) { 1495 if (err) {
1443 dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n", 1496 dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n",
@@ -1479,6 +1532,7 @@ err_register_netdev:
1479err_port_buffers_init: 1532err_port_buffers_init:
1480err_port_admin_status_set: 1533err_port_admin_status_set:
1481err_port_mtu_set: 1534err_port_mtu_set:
1535err_port_speed_by_width_set:
1482err_port_swid_set: 1536err_port_swid_set:
1483err_port_system_port_mapping_set: 1537err_port_system_port_mapping_set:
1484 devlink_port_unregister(&mlxsw_sp_port->devlink_port); 1538 devlink_port_unregister(&mlxsw_sp_port->devlink_port);
@@ -1494,6 +1548,28 @@ err_port_active_vlans_alloc:
1494 return err; 1548 return err;
1495} 1549}
1496 1550
1551static 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
1568err_port_create:
1569 mlxsw_sp_port_module_unmap(mlxsw_sp, local_port);
1570 return err;
1571}
1572
1497static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port) 1573static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1498{ 1574{
1499 struct net_device *dev = mlxsw_sp_port->dev; 1575 struct net_device *dev = mlxsw_sp_port->dev;
@@ -1562,7 +1638,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
1562 if (!width) 1638 if (!width)
1563 continue; 1639 continue;
1564 mlxsw_sp->port_to_module[i] = module; 1640 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);
1566 if (err) 1642 if (err)
1567 goto err_port_create; 1643 goto err_port_create;
1568 } 1644 }
@@ -1576,6 +1652,137 @@ err_port_module_info_get:
1576 return err; 1652 return err;
1577} 1653}
1578 1654
1655static 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
1662static 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
1725err_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
1736static 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
1579static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, 1786static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
1580 char *pude_pl, void *priv) 1787 char *pude_pl, void *priv)
1581{ 1788{
@@ -1999,6 +2206,8 @@ static struct mlxsw_driver mlxsw_sp_driver = {
1999 .priv_size = sizeof(struct mlxsw_sp), 2206 .priv_size = sizeof(struct mlxsw_sp),
2000 .init = mlxsw_sp_init, 2207 .init = mlxsw_sp_init,
2001 .fini = mlxsw_sp_fini, 2208 .fini = mlxsw_sp_fini,
2209 .port_split = mlxsw_sp_port_split,
2210 .port_unsplit = mlxsw_sp_port_unsplit,
2002 .txhdr_construct = mlxsw_sp_txhdr_construct, 2211 .txhdr_construct = mlxsw_sp_txhdr_construct,
2003 .txhdr_len = MLXSW_TXHDR_LEN, 2212 .txhdr_len = MLXSW_TXHDR_LEN,
2004 .profile = &mlxsw_sp_config_profile, 2213 .profile = &mlxsw_sp_config_profile,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index a7d86ac033f8..1b691d7e4a2a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -58,6 +58,10 @@
58 58
59#define MLXSW_SP_MID_MAX 7000 59#define MLXSW_SP_MID_MAX 7000
60 60
61#define MLXSW_SP_PORTS_PER_CLUSTER_MAX 4
62
63#define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */
64
61struct mlxsw_sp_port; 65struct mlxsw_sp_port;
62 66
63struct mlxsw_sp_upper { 67struct mlxsw_sp_upper {
@@ -151,7 +155,8 @@ struct mlxsw_sp_port {
151 learning_sync:1, 155 learning_sync:1,
152 uc_flood:1, 156 uc_flood:1,
153 bridged:1, 157 bridged:1,
154 lagged:1; 158 lagged:1,
159 split:1;
155 u16 pvid; 160 u16 pvid;
156 u16 lag_id; 161 u16 lag_id;
157 struct { 162 struct {