Commit 8fb49c01 authored by David S. Miller's avatar David S. Miller

Merge branch 'Expose-port-split-attributes'

Ido Schimmel says:

====================
Expose port split attributes

Danielle says:

Currently, user space has no way of knowing if a port can be split and
into how many ports. Among other things, this makes it impossible to
write generic tests for port split functionality.

Therefore, this set exposes two new devlink port attributes to user
space: Number of lanes and whether the port can be split or not.

Patch set overview:

Patches #1-#4 cleanup 'struct devlink_port_attrs' and reduce the number
of parameters passed between drivers and devlink via
devlink_port_attrs_set()

Patch #5 adds devlink port lanes attributes

Patches #6-#7 add devlink port splittable attribute

Patch #8 exploits the fact that devlink is now aware of port's number of
lanes and whether the port can be split or not and moves some checks
from drivers to devlink

Patch #9 adds a port split test

Changes since v2:
* Remove some local variables from patch #3
* Reword function description in patch #5
* Fix a bug in patch #8
* Add a test for the splittable attribute in patch #9

Changes since v1:
* Rename 'width' attribute to 'lanes'
* Add 'splittable' attribute
* Move checks from drivers to devlink
====================
Reviewed-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents faea30ed f3348a82
......@@ -691,6 +691,7 @@ static void bnxt_dl_params_unregister(struct bnxt *bp)
int bnxt_dl_register(struct bnxt *bp)
{
struct devlink_port_attrs attrs = {};
struct devlink *dl;
int rc;
......@@ -719,9 +720,11 @@ int bnxt_dl_register(struct bnxt *bp)
if (!BNXT_PF(bp))
return 0;
devlink_port_attrs_set(&bp->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
bp->pf.port_id, false, 0, bp->dsn,
sizeof(bp->dsn));
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = bp->pf.port_id;
memcpy(attrs.switch_id.id, bp->dsn, sizeof(bp->dsn));
attrs.switch_id.id_len = sizeof(bp->dsn);
devlink_port_attrs_set(&bp->dl_port, &attrs);
rc = devlink_port_register(dl, &bp->dl_port, bp->pf.port_id);
if (rc) {
netdev_err(bp->dev, "devlink_port_register failed\n");
......
......@@ -312,6 +312,7 @@ int ice_devlink_create_port(struct ice_pf *pf)
struct devlink *devlink = priv_to_devlink(pf);
struct ice_vsi *vsi = ice_get_main_vsi(pf);
struct device *dev = ice_pf_to_dev(pf);
struct devlink_port_attrs attrs = {};
int err;
if (!vsi) {
......@@ -319,8 +320,9 @@ int ice_devlink_create_port(struct ice_pf *pf)
return -EIO;
}
devlink_port_attrs_set(&pf->devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
pf->hw.pf_id, false, 0, NULL, 0);
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = pf->hw.pf_id;
devlink_port_attrs_set(&pf->devlink_port, &attrs);
err = devlink_port_register(devlink, &pf->devlink_port, pf->hw.pf_id);
if (err) {
dev_err(dev, "devlink_port_register failed: %d\n", err);
......
......@@ -6,17 +6,16 @@
int mlx5e_devlink_port_register(struct mlx5e_priv *priv)
{
struct devlink *devlink = priv_to_devlink(priv->mdev);
struct devlink_port_attrs attrs = {};
if (mlx5_core_is_pf(priv->mdev))
devlink_port_attrs_set(&priv->dl_port,
DEVLINK_PORT_FLAVOUR_PHYSICAL,
PCI_FUNC(priv->mdev->pdev->devfn),
false, 0,
NULL, 0);
else
devlink_port_attrs_set(&priv->dl_port,
DEVLINK_PORT_FLAVOUR_VIRTUAL,
0, false, 0, NULL, 0);
if (mlx5_core_is_pf(priv->mdev)) {
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = PCI_FUNC(priv->mdev->pdev->devfn);
} else {
attrs.flavour = DEVLINK_PORT_FLAVOUR_VIRTUAL;
}
devlink_port_attrs_set(&priv->dl_port, &attrs);
return devlink_port_register(devlink, &priv->dl_port, 1);
}
......
......@@ -1185,6 +1185,7 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
{
struct devlink *devlink = priv_to_devlink(dev);
struct mlx5_eswitch_rep *rep = rpriv->rep;
struct devlink_port_attrs attrs = {};
struct netdev_phys_item_id ppid = {};
unsigned int dl_port_index = 0;
u16 pfnum;
......@@ -1195,19 +1196,16 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid);
dl_port_index = mlx5_esw_vport_to_devlink_port_index(dev, rep->vport);
pfnum = PCI_FUNC(dev->pdev->devfn);
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = pfnum;
memcpy(attrs.switch_id.id, &ppid.id[0], ppid.id_len);
attrs.switch_id.id_len = ppid.id_len;
if (rep->vport == MLX5_VPORT_UPLINK)
devlink_port_attrs_set(&rpriv->dl_port,
DEVLINK_PORT_FLAVOUR_PHYSICAL,
pfnum, false, 0,
&ppid.id[0], ppid.id_len);
devlink_port_attrs_set(&rpriv->dl_port, &attrs);
else if (rep->vport == MLX5_VPORT_PF)
devlink_port_attrs_pci_pf_set(&rpriv->dl_port,
&ppid.id[0], ppid.id_len,
pfnum);
devlink_port_attrs_pci_pf_set(&rpriv->dl_port, pfnum);
else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport))
devlink_port_attrs_pci_vf_set(&rpriv->dl_port,
&ppid.id[0], ppid.id_len,
pfnum, rep->vport - 1);
return devlink_port_register(devlink, &rpriv->dl_port, dl_port_index);
......
......@@ -2122,6 +2122,7 @@ static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
enum devlink_port_flavour flavour,
u32 port_number, bool split,
u32 split_port_subnumber,
bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len)
{
......@@ -2129,12 +2130,19 @@ static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
struct mlxsw_core_port *mlxsw_core_port =
&mlxsw_core->ports[local_port];
struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
struct devlink_port_attrs attrs = {};
int err;
attrs.split = split;
attrs.lanes = lanes;
attrs.splittable = splittable;
attrs.flavour = flavour;
attrs.phys.port_number = port_number;
attrs.phys.split_subport_number = split_port_subnumber;
memcpy(attrs.switch_id.id, switch_id, switch_id_len);
attrs.switch_id.id_len = switch_id_len;
mlxsw_core_port->local_port = local_port;
devlink_port_attrs_set(devlink_port, flavour, port_number,
split, split_port_subnumber,
switch_id, switch_id_len);
devlink_port_attrs_set(devlink_port, &attrs);
err = devlink_port_register(devlink, devlink_port, local_port);
if (err)
memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port));
......@@ -2154,12 +2162,14 @@ static void __mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port)
int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
u32 port_number, bool split,
u32 split_port_subnumber,
bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len)
{
return __mlxsw_core_port_init(mlxsw_core, local_port,
DEVLINK_PORT_FLAVOUR_PHYSICAL,
port_number, split, split_port_subnumber,
splittable, lanes,
switch_id, switch_id_len);
}
EXPORT_SYMBOL(mlxsw_core_port_init);
......@@ -2181,7 +2191,7 @@ int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core,
err = __mlxsw_core_port_init(mlxsw_core, MLXSW_PORT_CPU_PORT,
DEVLINK_PORT_FLAVOUR_CPU,
0, false, 0,
0, false, 0, false, 0,
switch_id, switch_id_len);
if (err)
return err;
......
......@@ -191,8 +191,8 @@ void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port);
int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
u32 port_number, bool split,
u32 split_port_subnumber,
u32 port_number, bool split, u32 split_port_subnumber,
bool splittable, u32 lanes,
const unsigned char *switch_id,
unsigned char switch_id_len);
void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port);
......
......@@ -164,8 +164,8 @@ mlxsw_m_port_create(struct mlxsw_m *mlxsw_m, u8 local_port, u8 module)
int err;
err = mlxsw_core_port_init(mlxsw_m->core, local_port,
module + 1, false, 0,
mlxsw_m->base_mac,
module + 1, false, 0, false,
0, mlxsw_m->base_mac,
sizeof(mlxsw_m->base_mac));
if (err) {
dev_err(mlxsw_m->bus_info->dev, "Port %d: Failed to init core port\n",
......
......@@ -1733,12 +1733,16 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
bool split = !!split_base_local_port;
struct mlxsw_sp_port *mlxsw_sp_port;
u32 lanes = port_mapping->width;
struct net_device *dev;
bool splittable;
int err;
splittable = lanes > 1 && !split;
err = mlxsw_core_port_init(mlxsw_sp->core, local_port,
port_mapping->module + 1, split,
port_mapping->lane / port_mapping->width,
port_mapping->lane / lanes,
splittable, lanes,
mlxsw_sp->base_mac,
sizeof(mlxsw_sp->base_mac));
if (err) {
......@@ -2232,13 +2236,6 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
return -EINVAL;
}
/* Split ports cannot be split. */
if (mlxsw_sp_port->split) {
netdev_err(mlxsw_sp_port->dev, "Port cannot be split further\n");
NL_SET_ERR_MSG_MOD(extack, "Port cannot be split further");
return -EINVAL;
}
max_width = mlxsw_core_module_max_width(mlxsw_core,
mlxsw_sp_port->mapping.module);
if (max_width < 0) {
......@@ -2247,19 +2244,13 @@ static int mlxsw_sp_port_split(struct mlxsw_core *mlxsw_core, u8 local_port,
return max_width;
}
/* Split port with non-max and 1 module width cannot be split. */
if (mlxsw_sp_port->mapping.width != max_width || max_width == 1) {
/* Split port with non-max cannot be split. */
if (mlxsw_sp_port->mapping.width != max_width) {
netdev_err(mlxsw_sp_port->dev, "Port cannot be split\n");
NL_SET_ERR_MSG_MOD(extack, "Port cannot be split");
return -EINVAL;
}
if (count == 1 || !is_power_of_2(count) || count > max_width) {
netdev_err(mlxsw_sp_port->dev, "Invalid split count\n");
NL_SET_ERR_MSG_MOD(extack, "Invalid split count");
return -EINVAL;
}
offset = mlxsw_sp_local_ports_offset(mlxsw_core, count, max_width);
if (offset < 0) {
netdev_err(mlxsw_sp_port->dev, "Cannot obtain local port offset\n");
......
......@@ -281,7 +281,7 @@ static int mlxsw_sib_port_create(struct mlxsw_sib *mlxsw_sib, u8 local_port,
int err;
err = mlxsw_core_port_init(mlxsw_sib->core, local_port,
module + 1, false, 0,
module + 1, false, 0, false, 0,
mlxsw_sib->hw_id, sizeof(mlxsw_sib->hw_id));
if (err) {
dev_err(mlxsw_sib->bus_info->dev, "Port %d: Failed to init core port\n",
......
......@@ -1107,7 +1107,7 @@ static int mlxsw_sx_port_eth_create(struct mlxsw_sx *mlxsw_sx, u8 local_port,
int err;
err = mlxsw_core_port_init(mlxsw_sx->core, local_port,
module + 1, false, 0,
module + 1, false, 0, false, 0,
mlxsw_sx->hw_id, sizeof(mlxsw_sx->hw_id));
if (err) {
dev_err(mlxsw_sx->bus_info->dev, "Port %d: Failed to init core port\n",
......
......@@ -70,9 +70,6 @@ nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index,
unsigned int lanes;
int ret;
if (count < 2)
return -EINVAL;
mutex_lock(&pf->lock);
rtnl_lock();
......@@ -81,7 +78,7 @@ nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index,
if (ret)
goto out;
if (eth_port.is_split || eth_port.port_lanes % count) {
if (eth_port.port_lanes % count) {
ret = -EINVAL;
goto out;
}
......@@ -353,6 +350,7 @@ const struct devlink_ops nfp_devlink_ops = {
int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
{
struct devlink_port_attrs attrs = {};
struct nfp_eth_table_port eth_port;
struct devlink *devlink;
const u8 *serial;
......@@ -365,10 +363,15 @@ int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
if (ret)
return ret;
attrs.split = eth_port.is_split;
attrs.splittable = !attrs.split;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = eth_port.label_port;
attrs.phys.split_subport_number = eth_port.label_subport;
serial_len = nfp_cpp_serial(port->app->cpp, &serial);
devlink_port_attrs_set(&port->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
eth_port.label_port, eth_port.is_split,
eth_port.label_subport, serial, serial_len);
memcpy(attrs.switch_id.id, serial, serial_len);
attrs.switch_id.id_len = serial_len;
devlink_port_attrs_set(&port->dl_port, &attrs);
devlink = priv_to_devlink(app->pf);
......
......@@ -69,6 +69,7 @@ void ionic_devlink_free(struct ionic *ionic)
int ionic_devlink_register(struct ionic *ionic)
{
struct devlink *dl = priv_to_devlink(ionic);
struct devlink_port_attrs attrs = {};
int err;
err = devlink_register(dl, ionic->dev);
......@@ -77,8 +78,8 @@ int ionic_devlink_register(struct ionic *ionic)
return err;
}
devlink_port_attrs_set(&ionic->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
0, false, 0, NULL, 0);
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
devlink_port_attrs_set(&ionic->dl_port, &attrs);
err = devlink_port_register(dl, &ionic->dl_port, 0);
if (err)
dev_err(ionic->dev, "devlink_port_register failed: %d\n", err);
......
......@@ -889,6 +889,7 @@ static const struct devlink_ops nsim_dev_devlink_ops = {
static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
unsigned int port_index)
{
struct devlink_port_attrs attrs = {};
struct nsim_dev_port *nsim_dev_port;
struct devlink_port *devlink_port;
int err;
......@@ -899,10 +900,11 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
nsim_dev_port->port_index = port_index;
devlink_port = &nsim_dev_port->devlink_port;
devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
port_index + 1, 0, 0,
nsim_dev->switch_id.id,
nsim_dev->switch_id.id_len);
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = port_index + 1;
memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
attrs.switch_id.id_len = nsim_dev->switch_id.id_len;
devlink_port_attrs_set(devlink_port, &attrs);
err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
port_index);
if (err)
......
......@@ -52,7 +52,7 @@ struct devlink_port_phys_attrs {
* A physical port which is visible to the user
* for a given port flavour.
*/
u32 split_subport_number;
u32 split_subport_number; /* If the port is split, this is the number of subport. */
};
struct devlink_port_pci_pf_attrs {
......@@ -64,10 +64,18 @@ struct devlink_port_pci_vf_attrs {
u16 vf; /* Associated PCI VF for of the PCI PF for this port. */
};
/**
* struct devlink_port_attrs - devlink port object
* @flavour: flavour of the port
* @split: indicates if this is split port
* @splittable: indicates if the port can be split.
* @lanes: maximum number of lanes the port supports. 0 value is not passed to netlink.
* @switch_id: if the port is part of switch, this is buffer with ID, otherwise this is NULL
*/
struct devlink_port_attrs {
u8 set:1,
split:1,
switch_port:1;
u8 split:1,
splittable:1;
u32 lanes;
enum devlink_port_flavour flavour;
struct netdev_phys_item_id switch_id;
union {
......@@ -90,6 +98,8 @@ struct devlink_port {
enum devlink_port_type desired_type;
void *type_dev;
struct devlink_port_attrs attrs;
u8 attrs_set:1,
switch_port:1;
struct delayed_work type_warn_dw;
};
......@@ -1180,17 +1190,9 @@ void devlink_port_type_ib_set(struct devlink_port *devlink_port,
struct ib_device *ibdev);
void devlink_port_type_clear(struct devlink_port *devlink_port);
void devlink_port_attrs_set(struct devlink_port *devlink_port,
enum devlink_port_flavour flavour,
u32 port_number, bool split,
u32 split_subport_number,
const unsigned char *switch_id,
unsigned char switch_id_len);
void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port,
const unsigned char *switch_id,
unsigned char switch_id_len, u16 pf);
struct devlink_port_attrs *devlink_port_attrs);
void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u16 pf);
void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port,
const unsigned char *switch_id,
unsigned char switch_id_len,
u16 pf, u16 vf);
int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
u32 size, u16 ingress_pools_count,
......
......@@ -455,6 +455,9 @@ enum devlink_attr {
DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER, /* string */
DEVLINK_ATTR_PORT_LANES, /* u32 */
DEVLINK_ATTR_PORT_SPLITTABLE, /* u8 */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
......@@ -528,8 +528,14 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
{
struct devlink_port_attrs *attrs = &devlink_port->attrs;
if (!attrs->set)
if (!devlink_port->attrs_set)
return 0;
if (attrs->lanes) {
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_LANES, attrs->lanes))
return -EMSGSIZE;
}
if (nla_put_u8(msg, DEVLINK_ATTR_PORT_SPLITTABLE, attrs->splittable))
return -EMSGSIZE;
if (nla_put_u16(msg, DEVLINK_ATTR_PORT_FLAVOUR, attrs->flavour))
return -EMSGSIZE;
switch (devlink_port->attrs.flavour) {
......@@ -934,6 +940,7 @@ static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_port *devlink_port;
u32 port_index;
u32 count;
......@@ -941,8 +948,27 @@ static int devlink_nl_cmd_port_split_doit(struct sk_buff *skb,
!info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT])
return -EINVAL;
devlink_port = devlink_port_get_from_info(devlink, info);
port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
count = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_SPLIT_COUNT]);
if (IS_ERR(devlink_port))
return -EINVAL;
if (!devlink_port->attrs.splittable) {
/* Split ports cannot be split. */
if (devlink_port->attrs.split)
NL_SET_ERR_MSG_MOD(info->extack, "Port cannot be split further");
else
NL_SET_ERR_MSG_MOD(info->extack, "Port cannot be split");
return -EINVAL;
}
if (count < 2 || !is_power_of_2(count) || count > devlink_port->attrs.lanes) {
NL_SET_ERR_MSG_MOD(info->extack, "Invalid split count");
return -EINVAL;
}
return devlink_port_split(devlink, port_index, count, info->extack);
}
......@@ -7510,24 +7536,20 @@ void devlink_port_type_clear(struct devlink_port *devlink_port)
EXPORT_SYMBOL_GPL(devlink_port_type_clear);
static int __devlink_port_attrs_set(struct devlink_port *devlink_port,
enum devlink_port_flavour flavour,
const unsigned char *switch_id,
unsigned char switch_id_len)
enum devlink_port_flavour flavour)
{
struct devlink_port_attrs *attrs = &devlink_port->attrs;
if (WARN_ON(devlink_port->registered))
return -EEXIST;
attrs->set = true;
devlink_port->attrs_set = true;
attrs->flavour = flavour;
if (switch_id) {
attrs->switch_port = true;
if (WARN_ON(switch_id_len > MAX_PHYS_ITEM_ID_LEN))
switch_id_len = MAX_PHYS_ITEM_ID_LEN;
memcpy(attrs->switch_id.id, switch_id, switch_id_len);
attrs->switch_id.id_len = switch_id_len;
if (attrs->switch_id.id_len) {
devlink_port->switch_port = true;
if (WARN_ON(attrs->switch_id.id_len > MAX_PHYS_ITEM_ID_LEN))
attrs->switch_id.id_len = MAX_PHYS_ITEM_ID_LEN;
} else {
attrs->switch_port = false;
devlink_port->switch_port = false;
}
return 0;
}
......@@ -7536,33 +7558,18 @@ static int __devlink_port_attrs_set(struct devlink_port *devlink_port,
* devlink_port_attrs_set - Set port attributes
*
* @devlink_port: devlink port
* @flavour: flavour of the port
* @port_number: number of the port that is facing user, for example
* the front panel port number
* @split: indicates if this is split port
* @split_subport_number: if the port is split, this is the number
* of subport.
* @switch_id: if the port is part of switch, this is buffer with ID,
* otwerwise this is NULL
* @switch_id_len: length of the switch_id buffer
* @attrs: devlink port attrs
*/
void devlink_port_attrs_set(struct devlink_port *devlink_port,
enum devlink_port_flavour flavour,
u32 port_number, bool split,
u32 split_subport_number,
const unsigned char *switch_id,
unsigned char switch_id_len)
struct devlink_port_attrs *attrs)
{
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
ret = __devlink_port_attrs_set(devlink_port, flavour,
switch_id, switch_id_len);
devlink_port->attrs = *attrs;
ret = __devlink_port_attrs_set(devlink_port, attrs->flavour);
if (ret)
return;
attrs->split = split;
attrs->phys.port_number = port_number;
attrs->phys.split_subport_number = split_subport_number;
WARN_ON(attrs->splittable && attrs->split);
}
EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
......@@ -7571,20 +7578,14 @@ EXPORT_SYMBOL_GPL(devlink_port_attrs_set);
*
* @devlink_port: devlink port
* @pf: associated PF for the devlink port instance
* @switch_id: if the port is part of switch, this is buffer with ID,
* otherwise this is NULL
* @switch_id_len: length of the switch_id buffer
*/
void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port,
const unsigned char *switch_id,
unsigned char switch_id_len, u16 pf)
void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u16 pf)
{
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_PF,
switch_id, switch_id_len);
DEVLINK_PORT_FLAVOUR_PCI_PF);
if (ret)
return;
......@@ -7598,21 +7599,15 @@ EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_pf_set);
* @devlink_port: devlink port
* @pf: associated PF for the devlink port instance
* @vf: associated VF of a PF for the devlink port instance
* @switch_id: if the port is part of switch, this is buffer with ID,
* otherwise this is NULL
* @switch_id_len: length of the switch_id buffer
*/
void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port,
const unsigned char *switch_id,
unsigned char switch_id_len,
u16 pf, u16 vf)
{
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_VF,
switch_id, switch_id_len);
DEVLINK_PORT_FLAVOUR_PCI_VF);
if (ret)
return;
attrs->pci_vf.pf = pf;
......@@ -7626,7 +7621,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int n = 0;
if (!attrs->set)
if (!devlink_port->attrs_set)
return -EOPNOTSUPP;
switch (attrs->flavour) {
......@@ -9461,7 +9456,7 @@ int devlink_compat_switch_id_get(struct net_device *dev,
* any devlink lock as only permanent values are accessed.
*/
devlink_port = netdev_to_devlink_port(dev);
if (!devlink_port || !devlink_port->attrs.switch_port)
if (!devlink_port || !devlink_port->switch_port)
return -EOPNOTSUPP;
memcpy(ppid, &devlink_port->attrs.switch_id, sizeof(*ppid));
......
......@@ -261,10 +261,15 @@ static int dsa_port_setup(struct dsa_port *dp)
struct devlink_port *dlp = &dp->devlink_port;
bool dsa_port_link_registered = false;
bool devlink_port_registered = false;
struct devlink_port_attrs attrs = {};
struct devlink *dl = ds->devlink;
bool dsa_port_enabled = false;
int err = 0;
attrs.phys.port_number = dp->index;
memcpy(attrs.switch_id.id, id, len);
attrs.switch_id.id_len = len;
if (dp->setup)
return 0;
......@@ -274,8 +279,8 @@ static int dsa_port_setup(struct dsa_port *dp)
break;
case DSA_PORT_TYPE_CPU:
memset(dlp, 0, sizeof(*dlp));
devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_CPU,
dp->index, false, 0, id, len);
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
......@@ -294,8 +299,8 @@ static int dsa_port_setup(struct dsa_port *dp)
break;
case DSA_PORT_TYPE_DSA:
memset(dlp, 0, sizeof(*dlp));
devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_DSA,
dp->index, false, 0, id, len);
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
......@@ -314,8 +319,8 @@ static int dsa_port_setup(struct dsa_port *dp)
break;
case DSA_PORT_TYPE_USER:
memset(dlp, 0, sizeof(*dlp));
devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_PHYSICAL,
dp->index, false, 0, id, len);
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
......
......@@ -18,6 +18,7 @@ TEST_PROGS += reuseaddr_ports_exhausted.sh
TEST_PROGS += txtimestamp.sh
TEST_PROGS += vrf-xfrm-tests.sh
TEST_PROGS += rxtimestamp.sh
TEST_PROGS += devlink_port_split.py
TEST_PROGS_EXTENDED := in_netns.sh
TEST_GEN_FILES = socket nettest
TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
......
#!/usr/bin/python3
# SPDX-License-Identifier: GPL-2.0
from subprocess import PIPE, Popen
import json
import time
import argparse
import collections
import sys
#
# Test port split configuration using devlink-port lanes attribute.
# The test is skipped in case the attribute is not available.
#
# First, check that all the ports with 1 lane fail to split.
# Second, check that all the ports with more than 1 lane can be split
# to all valid configurations (e.g., split to 2, split to 4 etc.)
#
Port = collections.namedtuple('Port', 'bus_info name')
def run_command(cmd, should_fail=False):
"""
Run a command in subprocess.
Return: Tuple of (stdout, stderr).
"""
p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
stdout, stderr = p.communicate()
stdout, stderr = stdout.decode(), stderr.decode()
if stderr != "" and not should_fail:
print("Error sending command: %s" % cmd)
print(stdout)
print(stderr)
return stdout, stderr
class devlink_ports(object):
"""
Class that holds information on the devlink ports, required to the tests;
if_names: A list of interfaces in the devlink ports.
"""
def get_if_names(dev):
"""
Get a list of physical devlink ports.
Return: Array of tuples (bus_info/port, if_name).
"""
arr = []
cmd = "devlink -j port show"
stdout, stderr = run_command(cmd)
assert stderr == ""
ports = json.loads(stdout)['port']
for port in ports:
if dev in port:
if ports[port]['flavour'] == 'physical':
arr.append(Port(bus_info=port, name=ports[port]['netdev']))
return arr
def __init__(self, dev):
self.if_names = devlink_ports.get_if_names(dev)
def get_max_lanes(port):
"""
Get the $port's maximum number of lanes.
Return: number of lanes, e.g. 1, 2, 4 and 8.
"""
cmd = "devlink -j port show %s" % port
stdout, stderr = run_command(cmd)
assert stderr == ""
values = list(json.loads(stdout)['port'].values())[0]
if 'lanes' in values:
lanes = values['lanes']
else:
lanes = 0
return lanes
def get_split_ability(port):
"""
Get the $port split ability.
Return: split ability, true or false.
"""
cmd = "devlink -j port show %s" % port.name
stdout, stderr = run_command(cmd)
assert stderr == ""
values = list(json.loads(stdout)['port'].values())[0]
return values['splittable']
def split(k, port, should_fail=False):
"""
Split $port into $k ports.
If should_fail == True, the split should fail. Otherwise, should pass.
Return: Array of sub ports after splitting.
If the $port wasn't split, the array will be empty.
"""
cmd = "devlink port split %s count %s" % (port.bus_info, k)
stdout, stderr = run_command(cmd, should_fail=should_fail)
if should_fail:
if not test(stderr != "", "%s is unsplittable" % port.name):
print("split an unsplittable port %s" % port.name)
return create_split_group(port, k)
else:
if stderr == "":
return create_split_group(port, k)
print("didn't split a splittable port %s" % port.name)
return []
def unsplit(port):
"""
Unsplit $port.
"""
cmd = "devlink port unsplit %s" % port
stdout, stderr = run_command(cmd)
test(stderr == "", "Unsplit port %s" % port)
def exists(port, dev):
"""
Check if $port exists in the devlink ports.
Return: True is so, False otherwise.
"""
return any(dev_port.name == port
for dev_port in devlink_ports.get_if_names(dev))
def exists_and_lanes(ports, lanes, dev):
"""
Check if every port in the list $ports exists in the devlink ports and has
$lanes number of lanes after splitting.
Return: True if both are True, False otherwise.
"""
for port in ports:
max_lanes = get_max_lanes(port)
if not exists(port, dev):
print("port %s doesn't exist in devlink ports" % port)
return False
if max_lanes != lanes:
print("port %s has %d lanes, but %s were expected"
% (port, lanes, max_lanes))
return False
return True
def test(cond, msg):
"""
Check $cond and print a message accordingly.
Return: True is pass, False otherwise.
"""
if cond:
print("TEST: %-60s [ OK ]" % msg)
else:
print("TEST: %-60s [FAIL]" % msg)
return cond
def create_split_group(port, k):
"""
Create the split group for $port.
Return: Array with $k elements, which are the split port group.
"""
return list(port.name + "s" + str(i) for i in range(k))
def split_unsplittable_port(port, k):
"""
Test that splitting of unsplittable port fails.
"""
# split to max
new_split_group = split(k, port, should_fail=True)
if new_split_group != []:
unsplit(port.bus_info)
def split_splittable_port(port, k, lanes, dev):
"""
Test that splitting of splittable port passes correctly.
"""
new_split_group = split(k, port)
# Once the split command ends, it takes some time to the sub ifaces'
# to get their names. Use udevadm to continue only when all current udev
# events are handled.
cmd = "udevadm settle"
stdout, stderr = run_command(cmd)
assert stderr == ""
if new_split_group != []:
test(exists_and_lanes(new_split_group, lanes/k, dev),
"split port %s into %s" % (port.name, k))
unsplit(port.bus_info)
def make_parser():
parser = argparse.ArgumentParser(description='A test for port splitting.')
parser.add_argument('--dev',
help='The devlink handle of the device under test. ' +
'The default is the first registered devlink ' +
'handle.')
return parser
def main(cmdline=None):
parser = make_parser()
args = parser.parse_args(cmdline)
dev = args.dev
if not dev:
cmd = "devlink -j dev show"
stdout, stderr = run_command(cmd)
assert stderr == ""
devs = json.loads(stdout)['dev']
dev = list(devs.keys())[0]
cmd = "devlink dev show %s" % dev
stdout, stderr = run_command(cmd)
if stderr != "":
print("devlink device %s can not be found" % dev)
sys.exit(1)
ports = devlink_ports(dev)
for port in ports.if_names:
max_lanes = get_max_lanes(port.name)
# If max lanes is 0, do not test port splitting at all
if max_lanes == 0:
continue
# If 1 lane, shouldn't be able to split
elif max_lanes == 1:
test(not get_split_ability(port),
"%s should not be able to split" % port.name)
split_unsplittable_port(port, max_lanes)
# Else, splitting should pass and all the split ports should exist.
else:
lane = max_lanes
test(get_split_ability(port),
"%s should be able to split" % port.name)
while lane > 1:
split_splittable_port(port, lane, max_lanes, dev)
lane //= 2
if __name__ == "__main__":
main()
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