Commit 18f1e70c authored by Ido Schimmel's avatar Ido Schimmel Committed by David S. Miller

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: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a133318c
...@@ -59,6 +59,8 @@ ...@@ -59,6 +59,8 @@
#define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS) #define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS)
#define MLXSW_PORT_MODULE_MAX_WIDTH 4
enum mlxsw_port_admin_status { enum mlxsw_port_admin_status {
MLXSW_PORT_ADMIN_STATUS_UP = 1, MLXSW_PORT_ADMIN_STATUS_UP = 1,
MLXSW_PORT_ADMIN_STATUS_DOWN = 2, MLXSW_PORT_ADMIN_STATUS_DOWN = 2,
......
...@@ -321,6 +321,22 @@ static int mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp, ...@@ -321,6 +321,22 @@ static int mlxsw_sp_port_module_info_get(struct mlxsw_sp *mlxsw_sp,
return 0; return 0;
} }
static int mlxsw_sp_port_module_map(struct mlxsw_sp *mlxsw_sp, u8 local_port,
u8 module, u8 width, u8 lane)
{
char pmlp_pl[MLXSW_REG_PMLP_LEN];
int i;
mlxsw_reg_pmlp_pack(pmlp_pl, local_port);
mlxsw_reg_pmlp_width_set(pmlp_pl, width);
for (i = 0; i < width; i++) {
mlxsw_reg_pmlp_module_set(pmlp_pl, i, module);
mlxsw_reg_pmlp_tx_lane_set(pmlp_pl, i, lane + i); /* Rx & Tx */
}
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl);
}
static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port) static int mlxsw_sp_port_module_unmap(struct mlxsw_sp *mlxsw_sp, u8 local_port)
{ {
char pmlp_pl[MLXSW_REG_PMLP_LEN]; char pmlp_pl[MLXSW_REG_PMLP_LEN];
...@@ -1284,6 +1300,18 @@ static u32 mlxsw_sp_to_ptys_speed(u32 speed) ...@@ -1284,6 +1300,18 @@ static u32 mlxsw_sp_to_ptys_speed(u32 speed)
return ptys_proto; return ptys_proto;
} }
static u32 mlxsw_sp_to_ptys_upper_speed(u32 upper_speed)
{
u32 ptys_proto = 0;
int i;
for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) {
if (mlxsw_sp_port_link_mode[i].speed <= upper_speed)
ptys_proto |= mlxsw_sp_port_link_mode[i].mask;
}
return ptys_proto;
}
static int mlxsw_sp_port_set_settings(struct net_device *dev, static int mlxsw_sp_port_set_settings(struct net_device *dev,
struct ethtool_cmd *cmd) struct ethtool_cmd *cmd)
{ {
...@@ -1360,7 +1388,22 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { ...@@ -1360,7 +1388,22 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
.set_settings = mlxsw_sp_port_set_settings, .set_settings = mlxsw_sp_port_set_settings,
}; };
static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) static int
mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 width)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
u32 upper_speed = MLXSW_SP_PORT_BASE_SPEED * width;
char ptys_pl[MLXSW_REG_PTYS_LEN];
u32 eth_proto_admin;
eth_proto_admin = mlxsw_sp_to_ptys_upper_speed(upper_speed);
mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port,
eth_proto_admin);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
}
static int __mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
bool split, u8 module, u8 width)
{ {
struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
struct mlxsw_sp_port *mlxsw_sp_port; 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,6 +1419,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
mlxsw_sp_port->dev = dev; mlxsw_sp_port->dev = dev;
mlxsw_sp_port->mlxsw_sp = mlxsw_sp; mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
mlxsw_sp_port->local_port = local_port; mlxsw_sp_port->local_port = local_port;
mlxsw_sp_port->split = split;
bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE); bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL); mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
if (!mlxsw_sp_port->active_vlans) { 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,6 +1461,8 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
dev->hard_header_len += MLXSW_TXHDR_LEN; dev->hard_header_len += MLXSW_TXHDR_LEN;
devlink_port = &mlxsw_sp_port->devlink_port; devlink_port = &mlxsw_sp_port->devlink_port;
if (mlxsw_sp_port->split)
devlink_port_split_set(devlink_port, module);
err = devlink_port_register(devlink, devlink_port, local_port); err = devlink_port_register(devlink, devlink_port, local_port);
if (err) { if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register devlink port\n", 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,6 +1484,13 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
goto err_port_swid_set; goto err_port_swid_set;
} }
err = mlxsw_sp_port_speed_by_width_set(mlxsw_sp_port, width);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to enable speeds\n",
mlxsw_sp_port->local_port);
goto err_port_speed_by_width_set;
}
err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN); err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN);
if (err) { if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n", 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) ...@@ -1479,6 +1532,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
err_port_buffers_init: err_port_buffers_init:
err_port_admin_status_set: err_port_admin_status_set:
err_port_mtu_set: err_port_mtu_set:
err_port_speed_by_width_set:
err_port_swid_set: err_port_swid_set:
err_port_system_port_mapping_set: err_port_system_port_mapping_set:
devlink_port_unregister(&mlxsw_sp_port->devlink_port); 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) ...@@ -1494,6 +1548,28 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
return err; return err;
} }
static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
bool split, u8 module, u8 width, u8 lane)
{
int err;
err = mlxsw_sp_port_module_map(mlxsw_sp, local_port, module, width,
lane);
if (err)
return err;
err = __mlxsw_sp_port_create(mlxsw_sp, local_port, split, module,
width);
if (err)
goto err_port_create;
return 0;
err_port_create:
mlxsw_sp_port_module_unmap(mlxsw_sp, local_port);
return err;
}
static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port) static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{ {
struct net_device *dev = mlxsw_sp_port->dev; struct net_device *dev = mlxsw_sp_port->dev;
...@@ -1562,7 +1638,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) ...@@ -1562,7 +1638,7 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
if (!width) if (!width)
continue; continue;
mlxsw_sp->port_to_module[i] = module; mlxsw_sp->port_to_module[i] = module;
err = mlxsw_sp_port_create(mlxsw_sp, i); err = __mlxsw_sp_port_create(mlxsw_sp, i, false, module, width);
if (err) if (err)
goto err_port_create; goto err_port_create;
} }
...@@ -1576,6 +1652,137 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) ...@@ -1576,6 +1652,137 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
return err; return err;
} }
static u8 mlxsw_sp_cluster_base_port_get(u8 local_port)
{
u8 offset = (local_port - 1) % MLXSW_SP_PORTS_PER_CLUSTER_MAX;
return local_port - offset;
}
static int mlxsw_sp_port_split(void *priv, u8 local_port, unsigned int count)
{
struct mlxsw_sp *mlxsw_sp = priv;
struct mlxsw_sp_port *mlxsw_sp_port;
u8 width = MLXSW_PORT_MODULE_MAX_WIDTH / count;
u8 module, cur_width, base_port;
int i;
int err;
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
local_port);
return -EINVAL;
}
if (count != 2 && count != 4) {
netdev_err(mlxsw_sp_port->dev, "Port can only be split into 2 or 4 ports\n");
return -EINVAL;
}
err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
&cur_width);
if (err) {
netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
return err;
}
if (cur_width != MLXSW_PORT_MODULE_MAX_WIDTH) {
netdev_err(mlxsw_sp_port->dev, "Port cannot be split further\n");
return -EINVAL;
}
/* Make sure we have enough slave (even) ports for the split. */
if (count == 2) {
base_port = local_port;
if (mlxsw_sp->ports[base_port + 1]) {
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
return -EINVAL;
}
} else {
base_port = mlxsw_sp_cluster_base_port_get(local_port);
if (mlxsw_sp->ports[base_port + 1] ||
mlxsw_sp->ports[base_port + 3]) {
netdev_err(mlxsw_sp_port->dev, "Invalid split configuration\n");
return -EINVAL;
}
}
for (i = 0; i < count; i++)
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
for (i = 0; i < count; i++) {
err = mlxsw_sp_port_create(mlxsw_sp, base_port + i, true,
module, width, i * width);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create split port\n");
goto err_port_create;
}
}
return 0;
err_port_create:
for (i--; i >= 0; i--)
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
for (i = 0; i < count / 2; i++) {
module = mlxsw_sp->port_to_module[base_port + i * 2];
mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
module, MLXSW_PORT_MODULE_MAX_WIDTH, 0);
}
return err;
}
static int mlxsw_sp_port_unsplit(void *priv, u8 local_port)
{
struct mlxsw_sp *mlxsw_sp = priv;
struct mlxsw_sp_port *mlxsw_sp_port;
u8 module, cur_width, base_port;
unsigned int count;
int i;
int err;
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err(mlxsw_sp->bus_info->dev, "Port number \"%d\" does not exist\n",
local_port);
return -EINVAL;
}
if (!mlxsw_sp_port->split) {
netdev_err(mlxsw_sp_port->dev, "Port wasn't split\n");
return -EINVAL;
}
err = mlxsw_sp_port_module_info_get(mlxsw_sp, local_port, &module,
&cur_width);
if (err) {
netdev_err(mlxsw_sp_port->dev, "Failed to get port's width\n");
return err;
}
count = cur_width == 1 ? 4 : 2;
base_port = mlxsw_sp_cluster_base_port_get(local_port);
/* Determine which ports to remove. */
if (count == 2 && local_port >= base_port + 2)
base_port = base_port + 2;
for (i = 0; i < count; i++)
mlxsw_sp_port_remove(mlxsw_sp, base_port + i);
for (i = 0; i < count / 2; i++) {
module = mlxsw_sp->port_to_module[base_port + i * 2];
err = mlxsw_sp_port_create(mlxsw_sp, base_port + i * 2, false,
module, MLXSW_PORT_MODULE_MAX_WIDTH,
0);
if (err)
dev_err(mlxsw_sp->bus_info->dev, "Failed to reinstantiate port\n");
}
return 0;
}
static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
char *pude_pl, void *priv) char *pude_pl, void *priv)
{ {
...@@ -1999,6 +2206,8 @@ static struct mlxsw_driver mlxsw_sp_driver = { ...@@ -1999,6 +2206,8 @@ static struct mlxsw_driver mlxsw_sp_driver = {
.priv_size = sizeof(struct mlxsw_sp), .priv_size = sizeof(struct mlxsw_sp),
.init = mlxsw_sp_init, .init = mlxsw_sp_init,
.fini = mlxsw_sp_fini, .fini = mlxsw_sp_fini,
.port_split = mlxsw_sp_port_split,
.port_unsplit = mlxsw_sp_port_unsplit,
.txhdr_construct = mlxsw_sp_txhdr_construct, .txhdr_construct = mlxsw_sp_txhdr_construct,
.txhdr_len = MLXSW_TXHDR_LEN, .txhdr_len = MLXSW_TXHDR_LEN,
.profile = &mlxsw_sp_config_profile, .profile = &mlxsw_sp_config_profile,
......
...@@ -58,6 +58,10 @@ ...@@ -58,6 +58,10 @@
#define MLXSW_SP_MID_MAX 7000 #define MLXSW_SP_MID_MAX 7000
#define MLXSW_SP_PORTS_PER_CLUSTER_MAX 4
#define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */
struct mlxsw_sp_port; struct mlxsw_sp_port;
struct mlxsw_sp_upper { struct mlxsw_sp_upper {
...@@ -151,7 +155,8 @@ struct mlxsw_sp_port { ...@@ -151,7 +155,8 @@ struct mlxsw_sp_port {
learning_sync:1, learning_sync:1,
uc_flood:1, uc_flood:1,
bridged:1, bridged:1,
lagged:1; lagged:1,
split:1;
u16 pvid; u16 pvid;
u16 lag_id; u16 lag_id;
struct { struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment