Commit 2483223e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'devlink-sanitize-per-port-region-creation-destruction'

Jiri Pirko says:

====================
devlink: sanitize per-port region creation/destruction

Currently the only user of per-port devlink regions is DSA. All drivers
that use regions register them before devlink registration. For DSA,
this was not possible as the internals of struct devlink_port needed for
region creation are initialized during port registration.

This introduced a mismatch in creation flow of devlink and devlink port
regions. As you can see, it causes the DSA driver to make the port
init/exit flow a bit cumbersome.

Fix this by introducing port_init/fini() which could be optionally
called by drivers like DSA, to prepare struct devlink_port to be used
for region creation purposes before devlink port register is called.

Tested by Vladimir on his setup.
====================

Link: https://lore.kernel.org/r/20220929072902.2986539-1-jiri@resnulli.usSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3406079b 61e4a516
......@@ -129,7 +129,9 @@ struct devlink_port {
void *type_dev;
struct devlink_port_attrs attrs;
u8 attrs_set:1,
switch_port:1;
switch_port:1,
registered:1,
initialized:1;
struct delayed_work type_warn_dw;
struct list_head reporter_list;
struct mutex reporters_lock; /* Protects reporter_list */
......@@ -1562,6 +1564,9 @@ void devlink_set_features(struct devlink *devlink, u64 features);
void devlink_register(struct devlink *devlink);
void devlink_unregister(struct devlink *devlink);
void devlink_free(struct devlink *devlink);
void devlink_port_init(struct devlink *devlink,
struct devlink_port *devlink_port);
void devlink_port_fini(struct devlink_port *devlink_port);
int devl_port_register(struct devlink *devlink,
struct devlink_port *devlink_port,
unsigned int port_index);
......
......@@ -294,8 +294,6 @@ struct dsa_port {
u8 lag_tx_enabled:1;
u8 devlink_port_setup:1;
/* Master state bits, valid only on CPU ports */
u8 master_admin_up:1;
u8 master_oper_up:1;
......
......@@ -371,6 +371,13 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
return ERR_PTR(-ENODEV);
}
#define ASSERT_DEVLINK_PORT_REGISTERED(devlink_port) \
WARN_ON_ONCE(!(devlink_port)->registered)
#define ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port) \
WARN_ON_ONCE((devlink_port)->registered)
#define ASSERT_DEVLINK_PORT_INITIALIZED(devlink_port) \
WARN_ON_ONCE(!(devlink_port)->initialized)
static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
unsigned int port_index)
{
......@@ -9847,6 +9854,44 @@ static void devlink_port_type_warn_cancel(struct devlink_port *devlink_port)
cancel_delayed_work_sync(&devlink_port->type_warn_dw);
}
/**
* devlink_port_init() - Init devlink port
*
* @devlink: devlink
* @devlink_port: devlink port
*
* Initialize essencial stuff that is needed for functions
* that may be called before devlink port registration.
* Call to this function is optional and not needed
* in case the driver does not use such functions.
*/
void devlink_port_init(struct devlink *devlink,
struct devlink_port *devlink_port)
{
if (devlink_port->initialized)
return;
devlink_port->devlink = devlink;
INIT_LIST_HEAD(&devlink_port->region_list);
devlink_port->initialized = true;
}
EXPORT_SYMBOL_GPL(devlink_port_init);
/**
* devlink_port_fini() - Deinitialize devlink port
*
* @devlink_port: devlink port
*
* Deinitialize essencial stuff that is in use for functions
* that may be called after devlink port unregistration.
* Call to this function is optional and not needed
* in case the driver does not use such functions.
*/
void devlink_port_fini(struct devlink_port *devlink_port)
{
WARN_ON(!list_empty(&devlink_port->region_list));
}
EXPORT_SYMBOL_GPL(devlink_port_fini);
/**
* devl_port_register() - Register devlink port
*
......@@ -9869,14 +9914,15 @@ int devl_port_register(struct devlink *devlink,
if (devlink_port_index_exists(devlink, port_index))
return -EEXIST;
WARN_ON(devlink_port->devlink);
devlink_port->devlink = devlink;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
devlink_port_init(devlink, devlink_port);
devlink_port->registered = true;
devlink_port->index = port_index;
spin_lock_init(&devlink_port->type_lock);
INIT_LIST_HEAD(&devlink_port->reporter_list);
mutex_init(&devlink_port->reporters_lock);
list_add_tail(&devlink_port->list, &devlink->port_list);
INIT_LIST_HEAD(&devlink_port->region_list);
INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
devlink_port_type_warn_schedule(devlink_port);
......@@ -9926,8 +9972,8 @@ void devl_port_unregister(struct devlink_port *devlink_port)
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
list_del(&devlink_port->list);
WARN_ON(!list_empty(&devlink_port->reporter_list));
WARN_ON(!list_empty(&devlink_port->region_list));
mutex_destroy(&devlink_port->reporters_lock);
devlink_port->registered = false;
}
EXPORT_SYMBOL_GPL(devl_port_unregister);
......@@ -9952,8 +9998,8 @@ static void __devlink_port_type_set(struct devlink_port *devlink_port,
enum devlink_port_type type,
void *type_dev)
{
if (WARN_ON(!devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_REGISTERED(devlink_port);
devlink_port_type_warn_cancel(devlink_port);
spin_lock_bh(&devlink_port->type_lock);
devlink_port->type = type;
......@@ -10072,8 +10118,8 @@ void devlink_port_attrs_set(struct devlink_port *devlink_port,
{
int ret;
if (WARN_ON(devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
devlink_port->attrs = *attrs;
ret = __devlink_port_attrs_set(devlink_port, attrs->flavour);
if (ret)
......@@ -10096,8 +10142,8 @@ void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u32 contro
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
if (WARN_ON(devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_PF);
if (ret)
......@@ -10123,8 +10169,8 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
if (WARN_ON(devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_VF);
if (ret)
......@@ -10151,8 +10197,8 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro
struct devlink_port_attrs *attrs = &devlink_port->attrs;
int ret;
if (WARN_ON(devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
ret = __devlink_port_attrs_set(devlink_port,
DEVLINK_PORT_FLAVOUR_PCI_SF);
if (ret)
......@@ -10267,8 +10313,8 @@ EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
void devlink_port_linecard_set(struct devlink_port *devlink_port,
struct devlink_linecard *linecard)
{
if (WARN_ON(devlink_port->devlink))
return;
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
devlink_port->linecard = linecard;
}
EXPORT_SYMBOL_GPL(devlink_port_linecard_set);
......@@ -11339,6 +11385,8 @@ devlink_port_region_create(struct devlink_port *port,
struct devlink_region *region;
int err = 0;
ASSERT_DEVLINK_PORT_INITIALIZED(port);
if (WARN_ON(!ops) || WARN_ON(!ops->destructor))
return ERR_PTR(-EINVAL);
......
......@@ -461,6 +461,72 @@ static void dsa_tree_teardown_cpu_ports(struct dsa_switch_tree *dst)
dp->cpu_dp = NULL;
}
static int dsa_port_devlink_setup(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch_tree *dst = dp->ds->dst;
struct devlink_port_attrs attrs = {};
struct devlink *dl = dp->ds->devlink;
struct dsa_switch *ds = dp->ds;
const unsigned char *id;
unsigned char len;
int err;
memset(dlp, 0, sizeof(*dlp));
devlink_port_init(dl, dlp);
if (ds->ops->port_setup) {
err = ds->ops->port_setup(ds, dp->index);
if (err)
return err;
}
id = (const unsigned char *)&dst->index;
len = sizeof(dst->index);
attrs.phys.port_number = dp->index;
memcpy(attrs.switch_id.id, id, len);
attrs.switch_id.id_len = len;
switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
break;
case DSA_PORT_TYPE_CPU:
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
break;
case DSA_PORT_TYPE_DSA:
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
break;
case DSA_PORT_TYPE_USER:
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
break;
}
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err) {
if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);
return err;
}
return 0;
}
static void dsa_port_devlink_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch *ds = dp->ds;
devlink_port_unregister(dlp);
if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);
devlink_port_fini(dlp);
}
static int dsa_port_setup(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
......@@ -472,11 +538,9 @@ static int dsa_port_setup(struct dsa_port *dp)
if (dp->setup)
return 0;
if (ds->ops->port_setup) {
err = ds->ops->port_setup(ds, dp->index);
if (err)
return err;
}
err = dsa_port_devlink_setup(dp);
if (err)
return err;
switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
......@@ -533,8 +597,7 @@ static int dsa_port_setup(struct dsa_port *dp)
if (err && dsa_port_link_registered)
dsa_shared_port_link_unregister_of(dp);
if (err) {
if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);
dsa_port_devlink_teardown(dp);
return err;
}
......@@ -543,59 +606,13 @@ static int dsa_port_setup(struct dsa_port *dp)
return 0;
}
static int dsa_port_devlink_setup(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch_tree *dst = dp->ds->dst;
struct devlink_port_attrs attrs = {};
struct devlink *dl = dp->ds->devlink;
const unsigned char *id;
unsigned char len;
int err;
id = (const unsigned char *)&dst->index;
len = sizeof(dst->index);
attrs.phys.port_number = dp->index;
memcpy(attrs.switch_id.id, id, len);
attrs.switch_id.id_len = len;
memset(dlp, 0, sizeof(*dlp));
switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
break;
case DSA_PORT_TYPE_CPU:
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
break;
case DSA_PORT_TYPE_DSA:
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
break;
case DSA_PORT_TYPE_USER:
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
break;
}
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (!err)
dp->devlink_port_setup = true;
return err;
}
static void dsa_port_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch *ds = dp->ds;
if (!dp->setup)
return;
if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);
devlink_port_type_clear(dlp);
switch (dp->type) {
......@@ -619,46 +636,15 @@ static void dsa_port_teardown(struct dsa_port *dp)
break;
}
dp->setup = false;
}
static void dsa_port_devlink_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
dsa_port_devlink_teardown(dp);
if (dp->devlink_port_setup)
devlink_port_unregister(dlp);
dp->devlink_port_setup = false;
dp->setup = false;
}
/* Destroy the current devlink port, and create a new one which has the UNUSED
* flavour. At this point, any call to ds->ops->port_setup has been already
* balanced out by a call to ds->ops->port_teardown, so we know that any
* devlink port regions the driver had are now unregistered. We then call its
* ds->ops->port_setup again, in order for the driver to re-create them on the
* new devlink port.
*/
static int dsa_port_reinit_as_unused(struct dsa_port *dp)
static int dsa_port_setup_as_unused(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
int err;
dsa_port_devlink_teardown(dp);
dp->type = DSA_PORT_TYPE_UNUSED;
err = dsa_port_devlink_setup(dp);
if (err)
return err;
if (ds->ops->port_setup) {
/* On error, leave the devlink port registered,
* dsa_switch_teardown will clean it up later.
*/
err = ds->ops->port_setup(ds, dp->index);
if (err)
return err;
}
return 0;
return dsa_port_setup(dp);
}
static int dsa_devlink_info_get(struct devlink *dl,
......@@ -882,7 +868,6 @@ static int dsa_switch_setup(struct dsa_switch *ds)
{
struct dsa_devlink_priv *dl_priv;
struct device_node *dn;
struct dsa_port *dp;
int err;
if (ds->setup)
......@@ -905,18 +890,9 @@ static int dsa_switch_setup(struct dsa_switch *ds)
dl_priv = devlink_priv(ds->devlink);
dl_priv->ds = ds;
/* Setup devlink port instances now, so that the switch
* setup() can register regions etc, against the ports
*/
dsa_switch_for_each_port(dp, ds) {
err = dsa_port_devlink_setup(dp);
if (err)
goto unregister_devlink_ports;
}
err = dsa_switch_register_notifier(ds);
if (err)
goto unregister_devlink_ports;
goto devlink_free;
ds->configure_vlan_while_not_filtering = true;
......@@ -957,9 +933,7 @@ static int dsa_switch_setup(struct dsa_switch *ds)
ds->ops->teardown(ds);
unregister_notifier:
dsa_switch_unregister_notifier(ds);
unregister_devlink_ports:
dsa_switch_for_each_port(dp, ds)
dsa_port_devlink_teardown(dp);
devlink_free:
devlink_free(ds->devlink);
ds->devlink = NULL;
return err;
......@@ -967,8 +941,6 @@ static int dsa_switch_setup(struct dsa_switch *ds)
static void dsa_switch_teardown(struct dsa_switch *ds)
{
struct dsa_port *dp;
if (!ds->setup)
return;
......@@ -987,8 +959,6 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
dsa_switch_unregister_notifier(ds);
if (ds->devlink) {
dsa_switch_for_each_port(dp, ds)
dsa_port_devlink_teardown(dp);
devlink_free(ds->devlink);
ds->devlink = NULL;
}
......@@ -1041,7 +1011,7 @@ static int dsa_tree_setup_ports(struct dsa_switch_tree *dst)
if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) {
err = dsa_port_setup(dp);
if (err) {
err = dsa_port_reinit_as_unused(dp);
err = dsa_port_setup_as_unused(dp);
if (err)
goto teardown;
}
......
......@@ -294,6 +294,7 @@ int dsa_port_mrp_add_ring_role(const struct dsa_port *dp,
int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
const struct switchdev_obj_ring_role_mrp *mrp);
int dsa_port_phylink_create(struct dsa_port *dp);
void dsa_port_phylink_destroy(struct dsa_port *dp);
int dsa_shared_port_link_register_of(struct dsa_port *dp);
void dsa_shared_port_link_unregister_of(struct dsa_port *dp);
int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
......
......@@ -1661,6 +1661,7 @@ int dsa_port_phylink_create(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
phy_interface_t mode;
struct phylink *pl;
int err;
err = of_get_phy_mode(dp->dn, &mode);
......@@ -1677,16 +1678,24 @@ int dsa_port_phylink_create(struct dsa_port *dp)
if (ds->ops->phylink_get_caps)
ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
mode, &dsa_port_phylink_mac_ops);
if (IS_ERR(dp->pl)) {
pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
mode, &dsa_port_phylink_mac_ops);
if (IS_ERR(pl)) {
pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
return PTR_ERR(dp->pl);
return PTR_ERR(pl);
}
dp->pl = pl;
return 0;
}
void dsa_port_phylink_destroy(struct dsa_port *dp)
{
phylink_destroy(dp->pl);
dp->pl = NULL;
}
static int dsa_shared_port_setup_phy_of(struct dsa_port *dp, bool enable)
{
struct dsa_switch *ds = dp->ds;
......@@ -1781,7 +1790,7 @@ static int dsa_shared_port_phylink_register(struct dsa_port *dp)
return 0;
err_phy_connect:
phylink_destroy(dp->pl);
dsa_port_phylink_destroy(dp);
return err;
}
......@@ -1983,8 +1992,7 @@ void dsa_shared_port_link_unregister_of(struct dsa_port *dp)
rtnl_lock();
phylink_disconnect_phy(dp->pl);
rtnl_unlock();
phylink_destroy(dp->pl);
dp->pl = NULL;
dsa_port_phylink_destroy(dp);
return;
}
......
......@@ -2304,7 +2304,7 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
if (ret) {
netdev_err(slave_dev, "failed to connect to PHY: %pe\n",
ERR_PTR(ret));
phylink_destroy(dp->pl);
dsa_port_phylink_destroy(dp);
}
return ret;
......@@ -2476,7 +2476,7 @@ int dsa_slave_create(struct dsa_port *port)
rtnl_lock();
phylink_disconnect_phy(p->dp->pl);
rtnl_unlock();
phylink_destroy(p->dp->pl);
dsa_port_phylink_destroy(p->dp);
out_gcells:
gro_cells_destroy(&p->gcells);
out_free:
......@@ -2499,7 +2499,7 @@ void dsa_slave_destroy(struct net_device *slave_dev)
phylink_disconnect_phy(dp->pl);
rtnl_unlock();
phylink_destroy(dp->pl);
dsa_port_phylink_destroy(dp);
gro_cells_destroy(&p->gcells);
free_percpu(slave_dev->tstats);
free_netdev(slave_dev);
......
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