All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christian Marangi <ansuelsmth@gmail.com>
To: Vladimir Oltean <olteanv@gmail.com>
Cc: Andrew Lunn <andrew@lunn.ch>,
	Florian Fainelli <f.fainelli@gmail.com>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [net-next PATCH] net: dsa: qca8k: add support for port_change_master
Date: Tue, 20 Jun 2023 15:04:28 +0200	[thread overview]
Message-ID: <64921dee.df0a0220.f64e1.72c7@mx.google.com> (raw)
In-Reply-To: <20230620201227.7sdb3zmwutwtmt2e@skbuf>

On Tue, Jun 20, 2023 at 11:12:27PM +0300, Vladimir Oltean wrote:
> Hi Christian,
> 
> On Tue, Jun 20, 2023 at 08:37:47AM +0200, Christian Marangi wrote:
> > Add support for port_change_master to permit assigning an alternative
> > CPU port if the switch have both CPU port connected or create a LAG on
> > both CPU port and assign the LAG as DSA master.
> > 
> > On port change master request, we check if the master is a LAG.
> > With LAG we compose the cpu_port_mask with the CPU port in the LAG, if
> > master is a simple dsa_port, we derive the index.
> > 
> > Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit
> > the port to receive packet by the new CPU port setup for the port and
> > we reenable the target port previously disabled.
> > 
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> >  drivers/net/dsa/qca/qca8k-8xxx.c | 54 ++++++++++++++++++++++++++++++++
> >  drivers/net/dsa/qca/qca8k.h      |  1 +
> >  2 files changed, 55 insertions(+)
> > 
> > diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
> > index dee7b6579916..435b69c1c552 100644
> > --- a/drivers/net/dsa/qca/qca8k-8xxx.c
> > +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
> > @@ -1713,6 +1713,59 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
> >  	return DSA_TAG_PROTO_QCA;
> >  }
> >  
> > +static int qca8k_port_change_master(struct dsa_switch *ds, int port,
> > +				    struct net_device *master,
> > +				    struct netlink_ext_ack *extack)
> > +{
> > +	struct qca8k_priv *priv = ds->priv;
> > +	u32 val, cpu_port_mask = 0;
> > +	struct dsa_port *dp;
> > +	int ret;
> > +
> > +	/* With LAG of CPU port, compose the mask for LOOKUP MEMBER */
> > +	if (netif_is_lag_master(master)) {
> > +		struct dsa_lag *lag;
> > +		int id;
> > +
> > +		id = dsa_lag_id(ds->dst, master);
> > +		lag = dsa_lag_by_id(ds->dst, id);
> > +
> > +		dsa_lag_foreach_port(dp, ds->dst, lag)
> 
> I think you use ds->dst often enough that you could assign it to its own
> local variable.
>

Will do thanks!

> > +			if (dsa_port_is_cpu(dp))
> > +				cpu_port_mask |= BIT(dp->index);
> > +	} else {
> > +		dp = dsa_port_from_netdev(master);
> 
> dsa_port_from_netdev() is implemented by calling:
> 
> static inline struct dsa_port *dsa_slave_to_port(const struct net_device *dev)
> {
> 	struct dsa_slave_priv *p = netdev_priv(dev);
> 
> 	return p->dp;
> }
> 
> The "struct net_device *master" does not have a netdev_priv() of the
> type "struct dsa_slave_priv *". So, this function does not do what you
> want, but instead it messes through the guts of an unrelated private
> structure, treating whatever it finds at offset 16 as a pointer, and
> dereferincing that as a struct dsa_port *. I'm surprised it didn't
> crash, to be frank.
> 
> To find the cpu_dp behind the master, you need to dereference
> master->dsa_ptr (for which we don't have a helper).
> 

I was searching for an helper but no luck. Is it safe to access
master->dsa_ptr? In theory the caller of port_change_master should
already check that the passed master is a dsa port?

I see in other context that master->dsa_ptr is checked if not NULL.
Should I do the same check here?

> > +		cpu_port_mask |= BIT(dp->index);
> > +	}
> > +
> > +	/* Disable port */
> > +	qca8k_port_set_status(priv, port, 0);
> > +
> > +	/* Connect it to new cpu port */
> > +	ret = qca8k_read(priv, QCA8K_PORT_LOOKUP_CTRL(port), &val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Reset connected CPU port in LOOKUP MEMBER */
> > +	val &= QCA8K_PORT_LOOKUP_USER_MEMBER;
> 
> val &= GENMASK(5, 1) practically has the effect of unsetting BIT(0) and BIT(6).
> I suppose those are the 2 possible CPU ports? If so, then use ~dsa_cpu_ports(ds),
> it's more readable at least for me as a fallback maintainer.
> 

Yes they are and yes I love this so I can also drop the stupid define.

> > +	/* Assign the new CPU port in LOOKUP MEMBER */
> > +	val |= cpu_port_mask;
> > +
> > +	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
> > +			QCA8K_PORT_LOOKUP_MEMBER,
> > +			val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Fast Age the port to flush FDB table */
> > +	qca8k_port_fast_age(ds, port);
> 
> Why do you have to fast age the (user) port?
> 

The 2 CPU port have a different mac address, is it a problem?

> > +
> > +	/* Reenable port */
> > +	qca8k_port_set_status(priv, port, 1);
> 
> or disable/enable it, for that matter?
> 

The idea is sto stop any traffic flowing to one CPU to another before
doing the change.

> > +
> > +	return 0;
> > +}
> > +
> >  static void
> >  qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
> >  		    bool operational)
> > @@ -1996,6 +2049,7 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
> >  	.get_phy_flags		= qca8k_get_phy_flags,
> >  	.port_lag_join		= qca8k_port_lag_join,
> >  	.port_lag_leave		= qca8k_port_lag_leave,
> > +	.port_change_master	= qca8k_port_change_master,
> 
> From my notes in commit eca70102cfb1 ("net: dsa: felix: add support for
> changing DSA master"), I recall this:
> 
>     When we change the DSA master to a LAG device, DSA guarantees us that
>     the LAG has at least one lower interface as a physical DSA master.
>     But DSA masters can come and go as lowers of that LAG, and
>     ds->ops->port_change_master() will not get called, because the DSA
>     master is still the same (the LAG). So we need to hook into the
>     ds->ops->port_lag_{join,leave} calls on the CPU ports and update the
>     logical port ID of the LAG that user ports are assigned to.
> 
> Otherwise said:
> 
> $ ip link add bond0 type bond mode balance-xor && ip link set bond0 up
> $ ip link set eth0 down && ip link set eth0 master bond0 # .port_change_master() gets called
> $ ip link set eth1 down && ip link set eth1 master bond0 # .port_change_master() does not get called
> $ ip link set eth0 nomaster # .port_change_master() does not get called
> 
> Unless something has changed, I believe that you need to handle these as well,
> and update the QCA8K_PORT_LOOKUP_MEMBER field. In the case above, your
> CPU port association would remain towards eth0, but the bond's lower interface
> is eth1.
> 

Can you better describe this case?

In theory from the switch view, with a LAG we just set that an user port
can receive packet from both CPU port.

Or you are saying that when an additional memeber is added to the LAG,
port_change_master is not called and we could face a scenario where:

- dsa master is LAG
- LAG have the 2 CPU port
- user port have LAG as master but QCA8K_PORT_LOOKUP_MEMBER with only
  one CPU?

If I got this right, then I get what you mean with the fact that I
should update the lag_join/leave definition and refresh each
configuration.

> >  	.master_state_change	= qca8k_master_change,
> >  	.connect_tag_protocol	= qca8k_connect_tag_protocol,
> >  };
> > diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h
> > index c5cc8a172d65..424f851db881 100644
> > --- a/drivers/net/dsa/qca/qca8k.h
> > +++ b/drivers/net/dsa/qca/qca8k.h
> > @@ -250,6 +250,7 @@
> >  #define   QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK		GENMASK(14, 8)
> >  #define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK		GENMASK(6, 0)
> >  #define QCA8K_PORT_LOOKUP_CTRL(_i)			(0x660 + (_i) * 0xc)
> > +#define   QCA8K_PORT_LOOKUP_USER_MEMBER			GENMASK(5, 1)
> >  #define   QCA8K_PORT_LOOKUP_MEMBER			GENMASK(6, 0)
> >  #define   QCA8K_PORT_LOOKUP_VLAN_MODE_MASK		GENMASK(9, 8)
> >  #define   QCA8K_PORT_LOOKUP_VLAN_MODE(x)		FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x)
> > -- 
> > 2.40.1
> > 
> 

-- 
	Ansuel

  reply	other threads:[~2023-06-20 21:45 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-20  6:37 [net-next PATCH] net: dsa: qca8k: add support for port_change_master Christian Marangi
2023-06-20 11:22 ` Christian Marangi
2023-06-20 20:12 ` Vladimir Oltean
2023-06-20 13:04   ` Christian Marangi [this message]
2023-06-21 10:25     ` Vladimir Oltean
2023-06-20 18:52       ` Christian Marangi
2023-06-21 12:17         ` Vladimir Oltean
2023-06-21  4:08           ` Christian Marangi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=64921dee.df0a0220.f64e1.72c7@mx.google.com \
    --to=ansuelsmth@gmail.com \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=f.fainelli@gmail.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=olteanv@gmail.com \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.