summaryrefslogtreecommitdiff
path: root/net/dsa
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2022-09-30 18:17:22 -0700
committerJakub Kicinski <kuba@kernel.org>2022-09-30 18:17:23 -0700
commit2483223e19b16b423b71e775be2359c2c0c8949c (patch)
tree03eeb395f256a2b127980740d47c74ef6448ff95 /net/dsa
parent3406079bbb279e33ab4b8d9a30e4dd68ce7bcafe (diff)
parent61e4a51621587c939672d6a9354f6d0aa3d4e131 (diff)
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.us Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/dsa')
-rw-r--r--net/dsa/dsa2.c184
-rw-r--r--net/dsa/dsa_priv.h1
-rw-r--r--net/dsa/port.c22
-rw-r--r--net/dsa/slave.c6
4 files changed, 96 insertions, 117 deletions
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 7024e2120de1..af0e2c0394ac 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -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 @@ teardown:
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 @@ unregister_devlink_ports:
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;
}
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 129e4a649c7e..6e65c7ffd6f3 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -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);
diff --git a/net/dsa/port.c b/net/dsa/port.c
index e6289a1db0a0..e4a0513816bb 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -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;
}
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index aa47ddc19fdf..1a59918d3b30 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -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 @@ out_phy:
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);