diff mbox series

[net-next,v2,3/7] net: dsa: Register devlink ports before calling DSA driver setup()

Message ID 20200926210632.3888886-4-andrew@lunn.ch
State Changes Requested
Delegated to: David Miller
Headers show
Series mv88e6xxx: Add per port devlink regions | expand

Commit Message

Andrew Lunn Sept. 26, 2020, 9:06 p.m. UTC
DSA drivers want to create regions on devlink ports as well as the
devlink device instance, in order to export registers and other tables
per port. To keep all this code together in the drivers, have the
devlink ports registered early, so the setup() method can setup both
device and port devlink regions.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 include/net/dsa.h |   1 +
 net/dsa/dsa2.c    | 133 ++++++++++++++++++++++++++++------------------
 2 files changed, 83 insertions(+), 51 deletions(-)

Comments

Vladimir Oltean Sept. 26, 2020, 11:37 p.m. UTC | #1
On Sat, Sep 26, 2020 at 11:06:28PM +0200, Andrew Lunn wrote:
> DSA drivers want to create regions on devlink ports as well as the
> devlink device instance, in order to export registers and other tables
> per port. To keep all this code together in the drivers, have the
> devlink ports registered early, so the setup() method can setup both
> device and port devlink regions.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
>  include/net/dsa.h |   1 +
>  net/dsa/dsa2.c    | 133 ++++++++++++++++++++++++++++------------------
>  2 files changed, 83 insertions(+), 51 deletions(-)
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index d16057c5987a..9aa44dc8ecdb 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -208,6 +208,7 @@ struct dsa_port {
>  	u8			stp_state;
>  	struct net_device	*bridge_dev;
>  	struct devlink_port	devlink_port;
> +	bool			devlink_port_setup;
>  	struct phylink		*pl;
>  	struct phylink_config	pl_config;
>  
> diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
> index 2c149fb36928..90cc70bd7c22 100644
> --- a/net/dsa/dsa2.c
> +++ b/net/dsa/dsa2.c
> @@ -251,47 +251,19 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
>  
>  static int dsa_port_setup(struct dsa_port *dp)
>  {
> -	struct dsa_switch *ds = dp->ds;
> -	struct dsa_switch_tree *dst = ds->dst;
> -	const unsigned char *id = (const unsigned char *)&dst->index;
> -	const unsigned char len = sizeof(dst->index);
>  	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;
>  
>  	switch (dp->type) {
>  	case DSA_PORT_TYPE_UNUSED:
> -		memset(dlp, 0, sizeof(*dlp));
> -		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
> -		devlink_port_attrs_set(dlp, &attrs);
> -		err = devlink_port_register(dl, dlp, dp->index);
> -		if (err)
> -			break;
> -
> -		devlink_port_registered = true;
> -
>  		dsa_port_disable(dp);
>  		break;
>  	case DSA_PORT_TYPE_CPU:
> -		memset(dlp, 0, sizeof(*dlp));
> -		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
> -		devlink_port_attrs_set(dlp, &attrs);
> -		err = devlink_port_register(dl, dlp, dp->index);
> -		if (err)
> -			break;
> -		devlink_port_registered = true;
> -
>  		err = dsa_port_link_register_of(dp);
>  		if (err)
>  			break;
> @@ -304,14 +276,6 @@ static int dsa_port_setup(struct dsa_port *dp)
>  
>  		break;
>  	case DSA_PORT_TYPE_DSA:
> -		memset(dlp, 0, sizeof(*dlp));
> -		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
> -		devlink_port_attrs_set(dlp, &attrs);
> -		err = devlink_port_register(dl, dlp, dp->index);
> -		if (err)
> -			break;
> -		devlink_port_registered = true;
> -
>  		err = dsa_port_link_register_of(dp);
>  		if (err)
>  			break;
> @@ -324,14 +288,6 @@ static int dsa_port_setup(struct dsa_port *dp)
>  
>  		break;
>  	case DSA_PORT_TYPE_USER:
> -		memset(dlp, 0, sizeof(*dlp));
> -		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
> -		devlink_port_attrs_set(dlp, &attrs);
> -		err = devlink_port_register(dl, dlp, dp->index);
> -		if (err)
> -			break;
> -		devlink_port_registered = true;
> -
>  		dp->mac = of_get_mac_address(dp->dn);
>  		err = dsa_slave_create(dp);
>  		if (err)
> @@ -345,8 +301,6 @@ static int dsa_port_setup(struct dsa_port *dp)
>  		dsa_port_disable(dp);
>  	if (err && dsa_port_link_registered)
>  		dsa_port_link_unregister_of(dp);
> -	if (err && devlink_port_registered)
> -		devlink_port_unregister(dlp);
>  	if (err)
>  		return err;
>  
> @@ -355,30 +309,77 @@ static int dsa_port_setup(struct dsa_port *dp)
>  	return 0;
>  }
>  
> -static void dsa_port_teardown(struct dsa_port *dp)
> +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;
> +
> +	if (dp->setup)
> +		return 0;
>  

I wonder what this is protecting against? I ran on a multi-switch tree
without these 2 lines and I didn't get anything like multiple
registration or things like that. What is the call path that would call
dsa_port_devlink_setup twice?

> +	switch (dp->type) {
> +	case DSA_PORT_TYPE_UNUSED:
> +		memset(dlp, 0, sizeof(*dlp));
> +		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;

> +		devlink_port_attrs_set(dlp, &attrs);
> +		err = devlink_port_register(dl, dlp, dp->index);

These 2 lines are common everywhere. Could you move them out of the
switch-case statement?

> +		break;
> +	case DSA_PORT_TYPE_CPU:
> +		memset(dlp, 0, sizeof(*dlp));
> +		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
> +		devlink_port_attrs_set(dlp, &attrs);
> +		err = devlink_port_register(dl, dlp, dp->index);
> +		break;
> +	case DSA_PORT_TYPE_DSA:
> +		memset(dlp, 0, sizeof(*dlp));
> +		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
> +		devlink_port_attrs_set(dlp, &attrs);
> +		err = devlink_port_register(dl, dlp, dp->index);
> +		break;
> +	case DSA_PORT_TYPE_USER:
> +		memset(dlp, 0, sizeof(*dlp));
> +		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
> +		devlink_port_attrs_set(dlp, &attrs);
> +		err = devlink_port_register(dl, dlp, dp->index);
> +		break;
> +	}
> +
> +	if (!err)
> +		dp->devlink_port_setup = true;
> +
> +	return err;
> +}
> +
> +static void dsa_port_teardown(struct dsa_port *dp)
> +{
>  	if (!dp->setup)
>  		return;
>  
>  	switch (dp->type) {
>  	case DSA_PORT_TYPE_UNUSED:
> -		devlink_port_unregister(dlp);
>  		break;
>  	case DSA_PORT_TYPE_CPU:
>  		dsa_port_disable(dp);
>  		dsa_tag_driver_put(dp->tag_ops);
> -		devlink_port_unregister(dlp);
>  		dsa_port_link_unregister_of(dp);
>  		break;
>  	case DSA_PORT_TYPE_DSA:
>  		dsa_port_disable(dp);
> -		devlink_port_unregister(dlp);
>  		dsa_port_link_unregister_of(dp);
>  		break;
>  	case DSA_PORT_TYPE_USER:
> -		devlink_port_unregister(dlp);
>  		if (dp->slave) {
>  			dsa_slave_destroy(dp->slave);
>  			dp->slave = NULL;
> @@ -389,6 +390,15 @@ static void dsa_port_teardown(struct dsa_port *dp)
>  	dp->setup = false;
>  }
>  
> +static void dsa_port_devlink_teardown(struct dsa_port *dp)
> +{
> +	struct devlink_port *dlp = &dp->devlink_port;
> +
> +	if (dp->devlink_port_setup)
> +		devlink_port_unregister(dlp);
> +	dp->devlink_port_setup = false;
> +}
> +
>  static int dsa_devlink_info_get(struct devlink *dl,
>  				struct devlink_info_req *req,
>  				struct netlink_ext_ack *extack)
> @@ -408,6 +418,7 @@ static const struct devlink_ops dsa_devlink_ops = {
>  static int dsa_switch_setup(struct dsa_switch *ds)
>  {
>  	struct dsa_devlink_priv *dl_priv;
> +	struct dsa_port *dp;
>  	int err;
>  
>  	if (ds->setup)
> @@ -433,6 +444,17 @@ static int dsa_switch_setup(struct dsa_switch *ds)
>  	if (err)
>  		goto free_devlink;
>  
> +	/* Setup devlink port instances now, so that the switch
> +	 * setup() can register regions etc, against the ports
> +	 */
> +	list_for_each_entry(dp, &ds->dst->ports, list) {
> +		if (dp->ds == ds) {
> +			err = dsa_port_devlink_setup(dp);
> +			if (err)
> +				goto unregister_devlink_ports;
> +		}
> +	}
> +
>  	err = dsa_switch_register_notifier(ds);
>  	if (err)
>  		goto unregister_devlink;

Need to use goto unregister_devlink_ports here.

> @@ -463,6 +485,10 @@ static int dsa_switch_setup(struct dsa_switch *ds)
>  
>  unregister_notifier:
>  	dsa_switch_unregister_notifier(ds);
> +unregister_devlink_ports:
> +	list_for_each_entry(dp, &ds->dst->ports, list)
> +		if (dp->ds == ds)
> +			dsa_port_devlink_teardown(dp);
>  unregister_devlink:
>  	devlink_unregister(ds->devlink);
>  free_devlink:
> @@ -474,6 +500,8 @@ 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;
>  
> @@ -486,6 +514,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
>  		ds->ops->teardown(ds);
>  
>  	if (ds->devlink) {
> +		list_for_each_entry(dp, &ds->dst->ports, list)
> +			if (dp->ds == ds)
> +				dsa_port_devlink_teardown(dp);
>  		devlink_unregister(ds->devlink);
>  		devlink_free(ds->devlink);
>  		ds->devlink = NULL;
> -- 
> 2.28.0
>
Andrew Lunn Sept. 27, 2020, 12:45 a.m. UTC | #2
> > +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;
> > +
> > +	if (dp->setup)
> > +		return 0;
> >  
> 
> I wonder what this is protecting against? I ran on a multi-switch tree
> without these 2 lines and I didn't get anything like multiple
> registration or things like that. What is the call path that would call
> dsa_port_devlink_setup twice?

I made a duplicate copy of dsa_port_setup() and trimmed out what was
not needed to give the new dsa_port_setup() and
dsa_port_devlink_setup(). I did not trim enough...

> 
> > +	switch (dp->type) {
> > +	case DSA_PORT_TYPE_UNUSED:
> > +		memset(dlp, 0, sizeof(*dlp));
> > +		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
> 
> > +		devlink_port_attrs_set(dlp, &attrs);
> > +		err = devlink_port_register(dl, dlp, dp->index);
> 
> These 2 lines are common everywhere. Could you move them out of the
> switch-case statement?

Yes, that makes sense. Too much blind copy/paste without actually
reviewing the code afterwards.

	  Andrew
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index d16057c5987a..9aa44dc8ecdb 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -208,6 +208,7 @@  struct dsa_port {
 	u8			stp_state;
 	struct net_device	*bridge_dev;
 	struct devlink_port	devlink_port;
+	bool			devlink_port_setup;
 	struct phylink		*pl;
 	struct phylink_config	pl_config;
 
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 2c149fb36928..90cc70bd7c22 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -251,47 +251,19 @@  static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
 
 static int dsa_port_setup(struct dsa_port *dp)
 {
-	struct dsa_switch *ds = dp->ds;
-	struct dsa_switch_tree *dst = ds->dst;
-	const unsigned char *id = (const unsigned char *)&dst->index;
-	const unsigned char len = sizeof(dst->index);
 	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;
 
 	switch (dp->type) {
 	case DSA_PORT_TYPE_UNUSED:
-		memset(dlp, 0, sizeof(*dlp));
-		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
-		devlink_port_attrs_set(dlp, &attrs);
-		err = devlink_port_register(dl, dlp, dp->index);
-		if (err)
-			break;
-
-		devlink_port_registered = true;
-
 		dsa_port_disable(dp);
 		break;
 	case DSA_PORT_TYPE_CPU:
-		memset(dlp, 0, sizeof(*dlp));
-		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
-		devlink_port_attrs_set(dlp, &attrs);
-		err = devlink_port_register(dl, dlp, dp->index);
-		if (err)
-			break;
-		devlink_port_registered = true;
-
 		err = dsa_port_link_register_of(dp);
 		if (err)
 			break;
@@ -304,14 +276,6 @@  static int dsa_port_setup(struct dsa_port *dp)
 
 		break;
 	case DSA_PORT_TYPE_DSA:
-		memset(dlp, 0, sizeof(*dlp));
-		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
-		devlink_port_attrs_set(dlp, &attrs);
-		err = devlink_port_register(dl, dlp, dp->index);
-		if (err)
-			break;
-		devlink_port_registered = true;
-
 		err = dsa_port_link_register_of(dp);
 		if (err)
 			break;
@@ -324,14 +288,6 @@  static int dsa_port_setup(struct dsa_port *dp)
 
 		break;
 	case DSA_PORT_TYPE_USER:
-		memset(dlp, 0, sizeof(*dlp));
-		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
-		devlink_port_attrs_set(dlp, &attrs);
-		err = devlink_port_register(dl, dlp, dp->index);
-		if (err)
-			break;
-		devlink_port_registered = true;
-
 		dp->mac = of_get_mac_address(dp->dn);
 		err = dsa_slave_create(dp);
 		if (err)
@@ -345,8 +301,6 @@  static int dsa_port_setup(struct dsa_port *dp)
 		dsa_port_disable(dp);
 	if (err && dsa_port_link_registered)
 		dsa_port_link_unregister_of(dp);
-	if (err && devlink_port_registered)
-		devlink_port_unregister(dlp);
 	if (err)
 		return err;
 
@@ -355,30 +309,77 @@  static int dsa_port_setup(struct dsa_port *dp)
 	return 0;
 }
 
-static void dsa_port_teardown(struct dsa_port *dp)
+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;
+
+	if (dp->setup)
+		return 0;
 
+	switch (dp->type) {
+	case DSA_PORT_TYPE_UNUSED:
+		memset(dlp, 0, sizeof(*dlp));
+		attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
+		devlink_port_attrs_set(dlp, &attrs);
+		err = devlink_port_register(dl, dlp, dp->index);
+		break;
+	case DSA_PORT_TYPE_CPU:
+		memset(dlp, 0, sizeof(*dlp));
+		attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
+		devlink_port_attrs_set(dlp, &attrs);
+		err = devlink_port_register(dl, dlp, dp->index);
+		break;
+	case DSA_PORT_TYPE_DSA:
+		memset(dlp, 0, sizeof(*dlp));
+		attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
+		devlink_port_attrs_set(dlp, &attrs);
+		err = devlink_port_register(dl, dlp, dp->index);
+		break;
+	case DSA_PORT_TYPE_USER:
+		memset(dlp, 0, sizeof(*dlp));
+		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
+		devlink_port_attrs_set(dlp, &attrs);
+		err = devlink_port_register(dl, dlp, dp->index);
+		break;
+	}
+
+	if (!err)
+		dp->devlink_port_setup = true;
+
+	return err;
+}
+
+static void dsa_port_teardown(struct dsa_port *dp)
+{
 	if (!dp->setup)
 		return;
 
 	switch (dp->type) {
 	case DSA_PORT_TYPE_UNUSED:
-		devlink_port_unregister(dlp);
 		break;
 	case DSA_PORT_TYPE_CPU:
 		dsa_port_disable(dp);
 		dsa_tag_driver_put(dp->tag_ops);
-		devlink_port_unregister(dlp);
 		dsa_port_link_unregister_of(dp);
 		break;
 	case DSA_PORT_TYPE_DSA:
 		dsa_port_disable(dp);
-		devlink_port_unregister(dlp);
 		dsa_port_link_unregister_of(dp);
 		break;
 	case DSA_PORT_TYPE_USER:
-		devlink_port_unregister(dlp);
 		if (dp->slave) {
 			dsa_slave_destroy(dp->slave);
 			dp->slave = NULL;
@@ -389,6 +390,15 @@  static void dsa_port_teardown(struct dsa_port *dp)
 	dp->setup = false;
 }
 
+static void dsa_port_devlink_teardown(struct dsa_port *dp)
+{
+	struct devlink_port *dlp = &dp->devlink_port;
+
+	if (dp->devlink_port_setup)
+		devlink_port_unregister(dlp);
+	dp->devlink_port_setup = false;
+}
+
 static int dsa_devlink_info_get(struct devlink *dl,
 				struct devlink_info_req *req,
 				struct netlink_ext_ack *extack)
@@ -408,6 +418,7 @@  static const struct devlink_ops dsa_devlink_ops = {
 static int dsa_switch_setup(struct dsa_switch *ds)
 {
 	struct dsa_devlink_priv *dl_priv;
+	struct dsa_port *dp;
 	int err;
 
 	if (ds->setup)
@@ -433,6 +444,17 @@  static int dsa_switch_setup(struct dsa_switch *ds)
 	if (err)
 		goto free_devlink;
 
+	/* Setup devlink port instances now, so that the switch
+	 * setup() can register regions etc, against the ports
+	 */
+	list_for_each_entry(dp, &ds->dst->ports, list) {
+		if (dp->ds == ds) {
+			err = dsa_port_devlink_setup(dp);
+			if (err)
+				goto unregister_devlink_ports;
+		}
+	}
+
 	err = dsa_switch_register_notifier(ds);
 	if (err)
 		goto unregister_devlink;
@@ -463,6 +485,10 @@  static int dsa_switch_setup(struct dsa_switch *ds)
 
 unregister_notifier:
 	dsa_switch_unregister_notifier(ds);
+unregister_devlink_ports:
+	list_for_each_entry(dp, &ds->dst->ports, list)
+		if (dp->ds == ds)
+			dsa_port_devlink_teardown(dp);
 unregister_devlink:
 	devlink_unregister(ds->devlink);
 free_devlink:
@@ -474,6 +500,8 @@  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;
 
@@ -486,6 +514,9 @@  static void dsa_switch_teardown(struct dsa_switch *ds)
 		ds->ops->teardown(ds);
 
 	if (ds->devlink) {
+		list_for_each_entry(dp, &ds->dst->ports, list)
+			if (dp->ds == ds)
+				dsa_port_devlink_teardown(dp);
 		devlink_unregister(ds->devlink);
 		devlink_free(ds->devlink);
 		ds->devlink = NULL;