diff options
author | Ido Schimmel <idosch@mellanox.com> | 2016-02-26 11:32:31 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-03-01 16:07:31 -0500 |
commit | 18f1e70c413713f28629ffe6863a2c43248ff7a3 (patch) | |
tree | 459917c7636f20843eddce7956b4949bfa28b0c6 | |
parent | a133318cde2000a3264032ea3b561c9054613486 (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.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 213 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 7 |
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 | |||
62 | enum mlxsw_port_admin_status { | 64 | enum 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 | ||
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 | |||
324 | static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) | 340 | static 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 | ||
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 | |||
1287 | static int mlxsw_sp_port_set_settings(struct net_device *dev, | 1315 | static 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 | ||
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) | ||
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: | |||
1479 | err_port_buffers_init: | 1532 | err_port_buffers_init: |
1480 | err_port_admin_status_set: | 1533 | err_port_admin_status_set: |
1481 | err_port_mtu_set: | 1534 | err_port_mtu_set: |
1535 | err_port_speed_by_width_set: | ||
1482 | err_port_swid_set: | 1536 | err_port_swid_set: |
1483 | err_port_system_port_mapping_set: | 1537 | err_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 | ||
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 | |||
1497 | static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port) | 1573 | static 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 | ||
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 | |||
1579 | static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, | 1786 | static 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 | |||
61 | struct mlxsw_sp_port; | 65 | struct mlxsw_sp_port; |
62 | 66 | ||
63 | struct mlxsw_sp_upper { | 67 | struct 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 { |