Commit a6f0b26d authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'cross-chip-bridging-for-disjoint-dsa-trees'

Vladimir Oltean says:

====================
This series adds support for boards where DSA switches of multiple types
are cascaded together. Actually this type of setup was brought up before
on netdev, and it looks like utilizing disjoint trees is the way to go:

https://lkml.org/lkml/2019/7/7/225

The trouble with disjoint trees (prior to this patch series) is that only
bridging of ports within the same hardware switch can be offloaded.
After scratching my head for a while, it looks like the easiest way to
support hardware bridging between different DSA trees is to bridge their
DSA masters and extend the crosschip bridging operations.

I have given some thought to bridging the DSA masters with the slaves
themselves, but given the hardware topology described in the commit
message of patch 4/4, virtually any number (and combination) of bridges
(forwarding domains) can be created on top of those 3x4-port front-panel
switches. So it becomes a lot less obvious, when the front-panel ports
are enslaved to more than 1 bridge, which bridge should the DSA masters
be enslaved to.

So the least awkward approach was to just create a completely separate
bridge for the DSA masters, whose entire purpose is to permit hardware
forwarding between the discrete switches beneath it.

This is a direct resend of v3, which was deferred due to lack of review.
In the meantime Florian has reviewed and tested some of them.

v1 was submitted here:
https://patchwork.ozlabs.org/project/netdev/cover/20200429161952.17769-1-olteanv@gmail.com/

v2 was submitted here:
https://patchwork.ozlabs.org/project/netdev/cover/20200430202542.11797-1-olteanv@gmail.com/

v3 was submitted here:
https://patchwork.ozlabs.org/project/netdev/cover/20200503221228.10928-1-olteanv@gmail.com/


====================

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 90d9834e ac02a451
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -2233,26 +2233,34 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
	mv88e6xxx_reg_unlock(chip);
}

static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev,
static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,
					   int tree_index, int sw_index,
					   int port, struct net_device *br)
{
	struct mv88e6xxx_chip *chip = ds->priv;
	int err;

	if (tree_index != ds->dst->index)
		return 0;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_pvt_map(chip, dev, port);
	err = mv88e6xxx_pvt_map(chip, sw_index, port);
	mv88e6xxx_reg_unlock(chip);

	return err;
}

static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev,
static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds,
					     int tree_index, int sw_index,
					     int port, struct net_device *br)
{
	struct mv88e6xxx_chip *chip = ds->priv;

	if (tree_index != ds->dst->index)
		return;

	mv88e6xxx_reg_lock(chip);
	if (mv88e6xxx_pvt_map(chip, dev, port))
	if (mv88e6xxx_pvt_map(chip, sw_index, port))
		dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");
	mv88e6xxx_reg_unlock(chip);
}
+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <linux/dsa/sja1105.h>
#include <linux/dsa/8021q.h>
#include <net/dsa.h>
#include <linux/mutex.h>
#include "sja1105_static_config.h"
@@ -185,6 +186,7 @@ struct sja1105_private {
	struct gpio_desc *reset_gpio;
	struct spi_device *spidev;
	struct dsa_switch *ds;
	struct list_head crosschip_links;
	struct sja1105_flow_block flow_block;
	struct sja1105_port ports[SJA1105_NUM_PORTS];
	/* Serializes transmission of management frames so that
+90 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include "sja1105_sgmii.h"
#include "sja1105_tas.h"

static const struct dsa_switch_ops sja1105_switch_ops;

static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
			     unsigned int startup_delay)
{
@@ -1791,6 +1793,84 @@ static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid,
	return 0;
}

static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
					 int tree_index, int sw_index,
					 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)
		return 0;

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_user_port(ds, port))
			continue;
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		rc = dsa_8021q_crosschip_bridge_join(ds, port, other_ds,
						     other_port, br,
						     &priv->crosschip_links);
		if (rc)
			return rc;

		rc = dsa_8021q_crosschip_bridge_join(other_ds, other_port, ds,
						     port, br,
						     &other_priv->crosschip_links);
		if (rc)
			return rc;
	}

	return 0;
}

static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,
					   int tree_index, int sw_index,
					   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;

	if (other_ds->ops != &sja1105_switch_ops)
		return;

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_user_port(ds, port))
			continue;
		if (dsa_to_port(ds, port)->bridge_dev != br)
			continue;

		dsa_8021q_crosschip_bridge_leave(ds, port, other_ds, other_port,
						 br, &priv->crosschip_links);

		dsa_8021q_crosschip_bridge_leave(other_ds, other_port, ds,
						 port, br,
						 &other_priv->crosschip_links);
	}
}

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

	list_for_each_entry(c, &priv->crosschip_links, list) {
		rc = dsa_8021q_crosschip_link_apply(ds, c->port, c->other_ds,
						    c->other_port, enabled);
		if (rc)
			break;
	}

	return rc;
}

static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
{
	int rc, i;
@@ -1803,6 +1883,12 @@ static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
			return rc;
		}
	}
	rc = sja1105_replay_crosschip_vlans(ds, enabled);
	if (rc) {
		dev_err(ds->dev, "Failed to replay crosschip VLANs: %d\n", rc);
		return rc;
	}

	dev_info(ds->dev, "%s switch tagging\n",
		 enabled ? "Enabled" : "Disabled");
	return 0;
@@ -2370,6 +2456,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
	.cls_flower_add		= sja1105_cls_flower_add,
	.cls_flower_del		= sja1105_cls_flower_del,
	.cls_flower_stats	= sja1105_cls_flower_stats,
	.crosschip_bridge_join	= sja1105_crosschip_bridge_join,
	.crosschip_bridge_leave	= sja1105_crosschip_bridge_leave,
};

static int sja1105_check_device_id(struct sja1105_private *priv)
@@ -2472,6 +2560,8 @@ static int sja1105_probe(struct spi_device *spi)
	mutex_init(&priv->ptp_data.lock);
	mutex_init(&priv->mgmt_lock);

	INIT_LIST_HEAD(&priv->crosschip_links);

	sja1105_tas_setup(ds);
	sja1105_flower_setup(ds);

+45 −0
Original line number Diff line number Diff line
@@ -12,11 +12,33 @@ struct sk_buff;
struct net_device;
struct packet_type;

struct dsa_8021q_crosschip_link {
	struct list_head list;
	int port;
	struct dsa_switch *other_ds;
	int other_port;
	refcount_t refcount;
};

#if IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q)

int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
				 bool enabled);

int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
				   struct dsa_switch *other_ds,
				   int other_port, bool enabled);

int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port,
				    struct dsa_switch *other_ds,
				    int other_port, struct net_device *br,
				    struct list_head *crosschip_links);

int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port,
				     struct dsa_switch *other_ds,
				     int other_port, struct net_device *br,
				     struct list_head *crosschip_links);

struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
			       u16 tpid, u16 tci);

@@ -36,6 +58,29 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
	return 0;
}

int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
				   struct dsa_switch *other_ds,
				   int other_port, bool enabled)
{
	return 0;
}

int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port,
				    struct dsa_switch *other_ds,
				    int other_port, struct net_device *br,
				    struct list_head *crosschip_links)
{
	return 0;
}

int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port,
				     struct dsa_switch *other_ds,
				     int other_port, struct net_device *br,
				     struct list_head *crosschip_links)
{
	return 0;
}

struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
			       u16 tpid, u16 tci)
{
+8 −5
Original line number Diff line number Diff line
@@ -574,10 +574,12 @@ struct dsa_switch_ops {
	/*
	 * Cross-chip operations
	 */
	int	(*crosschip_bridge_join)(struct dsa_switch *ds, int sw_index,
					 int port, struct net_device *br);
	void	(*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index,
					  int port, struct net_device *br);
	int	(*crosschip_bridge_join)(struct dsa_switch *ds, int tree_index,
					 int sw_index, int port,
					 struct net_device *br);
	void	(*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index,
					  int sw_index, int port,
					  struct net_device *br);

	/*
	 * PTP functionality
@@ -651,7 +653,7 @@ struct dsa_switch_driver {
struct net_device *dsa_dev_to_net_device(struct device *dev);

/* Keep inline for faster access in hot path */
static inline bool netdev_uses_dsa(struct net_device *dev)
static inline bool netdev_uses_dsa(const struct net_device *dev)
{
#if IS_ENABLED(CONFIG_NET_DSA)
	return dev->dsa_ptr && dev->dsa_ptr->rcv;
@@ -670,6 +672,7 @@ static inline bool dsa_can_decode(const struct sk_buff *skb,

void dsa_unregister_switch(struct dsa_switch *ds);
int dsa_register_switch(struct dsa_switch *ds);
struct dsa_switch *dsa_switch_find(int tree_index, int sw_index);
#ifdef CONFIG_PM_SLEEP
int dsa_switch_suspend(struct dsa_switch *ds);
int dsa_switch_resume(struct dsa_switch *ds);
Loading