Commit d7b1fd52 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller
Browse files

net: dsa: let the core manage the tag_8021q context



The basic problem description is as follows:

Be there 3 switches in a daisy chain topology:

                                             |
    sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
 [  user ] [  user ] [  user ] [  dsa  ] [  cpu  ]
                                   |
                                   +---------+
                                             |
    sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
 [  user ] [  user ] [  user ] [  dsa  ] [  dsa  ]
                                   |
                                   +---------+
                                             |
    sw2p0     sw2p1     sw2p2     sw2p3     sw2p4
 [  user ] [  user ] [  user ] [  user ] [  dsa  ]

The CPU will not be able to ping through the user ports of the
bottom-most switch (like for example sw2p0), simply because tag_8021q
was not coded up for this scenario - it has always assumed DSA switch
trees with a single switch.

To add support for the topology above, we must admit that the RX VLAN of
sw2p0 must be added on some ports of switches 0 and 1 as well. This is
in fact a textbook example of thing that can use the cross-chip notifier
framework that DSA has set up in switch.c.

There is only one problem: core DSA (switch.c) is not able right now to
make the connection between a struct dsa_switch *ds and a struct
dsa_8021q_context *ctx. Right now, it is drivers who call into
tag_8021q.c and always provide a struct dsa_8021q_context *ctx pointer,
and tag_8021q.c calls them back with the .tag_8021q_vlan_{add,del}
methods.

But with cross-chip notifiers, it is possible for tag_8021q to call
drivers without drivers having ever asked for anything. A good example
is right above: when sw2p0 wants to set itself up for tag_8021q,
the .tag_8021q_vlan_add method needs to be called for switches 1 and 0,
so that they transport sw2p0's VLANs towards the CPU without dropping
them.

So instead of letting drivers manage the tag_8021q context, add a
tag_8021q_ctx pointer inside of struct dsa_switch, which will be
populated when dsa_tag_8021q_register() returns success.

The patch is fairly long-winded because we are partly reverting commit
5899ee36 ("net: dsa: tag_8021q: add a context structure") which made
the driver-facing tag_8021q API use "ctx" instead of "ds". Now that we
can access "ctx" directly from "ds", this is no longer needed.

Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8b6e638b
Loading
Loading
Loading
Loading
+11 −11
Original line number Diff line number Diff line
@@ -425,14 +425,14 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
	ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC);
	ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_BC);

	felix->dsa_8021q_ctx = dsa_tag_8021q_register(ds, &felix_tag_8021q_ops,
	err = dsa_tag_8021q_register(ds, &felix_tag_8021q_ops,
				     htons(ETH_P_8021AD));
	if (!felix->dsa_8021q_ctx)
		return -ENOMEM;
	if (err)
		return err;

	err = dsa_8021q_setup(felix->dsa_8021q_ctx, true);
	err = dsa_8021q_setup(ds, true);
	if (err)
		goto out_free_dsa_8021_ctx;
		goto out_tag_8021q_unregister;

	err = felix_setup_mmio_filtering(felix);
	if (err)
@@ -441,9 +441,9 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
	return 0;

out_teardown_dsa_8021q:
	dsa_8021q_setup(felix->dsa_8021q_ctx, false);
out_free_dsa_8021_ctx:
	dsa_tag_8021q_unregister(felix->dsa_8021q_ctx);
	dsa_8021q_setup(ds, false);
out_tag_8021q_unregister:
	dsa_tag_8021q_unregister(ds);
	return err;
}

@@ -458,11 +458,11 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu)
		dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d",
			err);

	err = dsa_8021q_setup(felix->dsa_8021q_ctx, false);
	err = dsa_8021q_setup(ds, false);
	if (err)
		dev_err(ds->dev, "dsa_8021q_setup returned %d", err);

	dsa_tag_8021q_unregister(felix->dsa_8021q_ctx);
	dsa_tag_8021q_unregister(ds);

	for (port = 0; port < ds->num_ports; port++) {
		if (dsa_is_unused_port(ds, port))
+0 −1
Original line number Diff line number Diff line
@@ -60,7 +60,6 @@ struct felix {
	struct lynx_pcs			**pcs;
	resource_size_t			switch_base;
	resource_size_t			imdio_base;
	struct dsa_8021q_context	*dsa_8021q_ctx;
	enum dsa_tag_protocol		tag_proto;
};

+0 −1
Original line number Diff line number Diff line
@@ -257,7 +257,6 @@ struct sja1105_private {
	 * the switch doesn't confuse them with one another.
	 */
	struct mutex mgmt_lock;
	struct dsa_8021q_context *dsa_8021q_ctx;
	struct devlink_region **regions;
	struct sja1105_cbs_entry *cbs;
	struct mii_bus *mdio_base_t1;
+15 −25
Original line number Diff line number Diff line
@@ -1995,8 +1995,6 @@ static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
					 int other_port, struct net_device *br)
{
	struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index);
	struct sja1105_private *other_priv = other_ds->priv;
	struct sja1105_private *priv = ds->priv;
	int port, rc;

	if (other_ds->ops != &sja1105_switch_ops)
@@ -2008,17 +2006,13 @@ static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		rc = dsa_8021q_crosschip_bridge_join(priv->dsa_8021q_ctx,
						     port,
						     other_priv->dsa_8021q_ctx,
		rc = dsa_8021q_crosschip_bridge_join(ds, port, other_ds,
						     other_port);
		if (rc)
			return rc;

		rc = dsa_8021q_crosschip_bridge_join(other_priv->dsa_8021q_ctx,
						     other_port,
						     priv->dsa_8021q_ctx,
						     port);
		rc = dsa_8021q_crosschip_bridge_join(other_ds, other_port,
						     ds, port);
		if (rc)
			return rc;
	}
@@ -2032,8 +2026,6 @@ static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,
					   struct net_device *br)
{
	struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index);
	struct sja1105_private *other_priv = other_ds->priv;
	struct sja1105_private *priv = ds->priv;
	int port;

	if (other_ds->ops != &sja1105_switch_ops)
@@ -2045,22 +2037,19 @@ static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		dsa_8021q_crosschip_bridge_leave(priv->dsa_8021q_ctx, port,
						 other_priv->dsa_8021q_ctx,
		dsa_8021q_crosschip_bridge_leave(ds, port, other_ds,
						 other_port);

		dsa_8021q_crosschip_bridge_leave(other_priv->dsa_8021q_ctx,
						 other_port,
						 priv->dsa_8021q_ctx, port);
		dsa_8021q_crosschip_bridge_leave(other_ds, other_port,
						 ds, port);
	}
}

static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	rc = dsa_8021q_setup(priv->dsa_8021q_ctx, enabled);
	rc = dsa_8021q_setup(ds, enabled);
	if (rc)
		return rc;

@@ -2233,6 +2222,7 @@ static int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify);

static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)
{
	struct dsa_8021q_context *ctx = priv->ds->tag_8021q_ctx;
	struct sja1105_crosschip_switch *s, *pos;
	struct list_head crosschip_switches;
	struct dsa_8021q_crosschip_link *c;
@@ -2240,7 +2230,7 @@ static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)

	INIT_LIST_HEAD(&crosschip_switches);

	list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) {
	list_for_each_entry(c, &ctx->crosschip_links, list) {
		bool already_added = false;

		list_for_each_entry(s, &crosschip_switches, list) {
@@ -3306,10 +3296,10 @@ static int sja1105_probe(struct spi_device *spi)
	mutex_init(&priv->ptp_data.lock);
	mutex_init(&priv->mgmt_lock);

	priv->dsa_8021q_ctx = dsa_tag_8021q_register(ds, &sja1105_dsa_8021q_ops,
	rc = dsa_tag_8021q_register(ds, &sja1105_dsa_8021q_ops,
				    htons(ETH_P_8021Q));
	if (!priv->dsa_8021q_ctx)
		return -ENOMEM;
	if (rc)
		return rc;

	INIT_LIST_HEAD(&priv->bridge_vlans);
	INIT_LIST_HEAD(&priv->dsa_8021q_vlans);
@@ -3373,7 +3363,7 @@ static int sja1105_probe(struct spi_device *spi)
out_unregister_switch:
	dsa_unregister_switch(ds);
out_tag_8021q_unregister:
	dsa_tag_8021q_unregister(priv->dsa_8021q_ctx);
	dsa_tag_8021q_unregister(ds);

	return rc;
}
@@ -3384,7 +3374,7 @@ static int sja1105_remove(struct spi_device *spi)
	struct dsa_switch *ds = priv->ds;

	dsa_unregister_switch(ds);
	dsa_tag_8021q_unregister(priv->dsa_8021q_ctx);
	dsa_tag_8021q_unregister(ds);

	return 0;
}
+9 −9
Original line number Diff line number Diff line
@@ -34,20 +34,20 @@ struct dsa_8021q_context {
	__be16 proto;
};

struct dsa_8021q_context *dsa_tag_8021q_register(struct dsa_switch *ds,
int dsa_tag_8021q_register(struct dsa_switch *ds,
			   const struct dsa_8021q_ops *ops,
			   __be16 proto);

void dsa_tag_8021q_unregister(struct dsa_8021q_context *ctx);
void dsa_tag_8021q_unregister(struct dsa_switch *ds);

int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled);
int dsa_8021q_setup(struct dsa_switch *ds, bool enabled);

int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port,
				    struct dsa_8021q_context *other_ctx,
int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port,
				    struct dsa_switch *other_ds,
				    int other_port);

int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port,
				     struct dsa_8021q_context *other_ctx,
int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port,
				     struct dsa_switch *other_ds,
				     int other_port);

struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
Loading