netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging
@ 2015-02-24 21:15 Florian Fainelli
  2015-02-24 21:15 ` [PATCH net-next v3 1/3] net: dsa: Ensure that port array elements are initialized before being used Florian Fainelli
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Florian Fainelli @ 2015-02-24 21:15 UTC (permalink / raw)
  To: netdev
  Cc: davem, Florian Fainelli, vivien.didelot, jerome.oufella, linux,
	andrew, cphealy

Hi all,

This patch set provides the DSA and SWITCHDEV integration bits together and
modifies the bcm_sf2 driver accordingly such that it works properly with HW
bridging.

Changes in v3:

- add back the null pointer check in dsa_slave_br_port_mask from Guenter
- slightly rework patch 1 commit message not to mention the function name
  we add in patch 2

Changes in v2:

- avoid a race condition in how DSA network devices are created, patch from
  Guenter Roeck
- provide a consistent and work STP state once a port leaves the bridge
- retain a bridge device pointer to properly flag port/bridge membership
- properly flush the ARL (Address Resolution Logic) in bcm_sf2.c
- properly retain port membership when individually bringing devices up/down
  while they are members of a bridge

We discussed on the mailing-list the possibility of standardizing a "fdb_flush"
operation for DSA switch drivers, looking at the Marvell and Broadcom switches,
I am not convinced this is practical or diserable as the terminologies vary
here, but there is nothing preventing us from doing it later.

Many thanks to Guenter and Andrew for both testing and providing feedback.

Thanks!

Florian Fainelli (2):
  net: dsa: integrate with SWITCHDEV for HW bridging
  net: dsa: bcm_sf2: add HW bridging support

Guenter Roeck (1):
  net: dsa: Ensure that port array elements are initialized before being
    used

 drivers/net/dsa/bcm_sf2.c      | 155 ++++++++++++++++++++++++++++++++++++++-
 drivers/net/dsa/bcm_sf2.h      |   2 +
 drivers/net/dsa/bcm_sf2_regs.h |  15 ++++
 include/net/dsa.h              |  10 +++
 net/dsa/Kconfig                |   1 +
 net/dsa/dsa.c                  |  17 +++--
 net/dsa/dsa_priv.h             |   9 ++-
 net/dsa/slave.c                | 161 +++++++++++++++++++++++++++++++++++++++--
 8 files changed, 352 insertions(+), 18 deletions(-)

-- 
2.1.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 1/3] net: dsa: Ensure that port array elements are initialized before being used
  2015-02-24 21:15 [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging Florian Fainelli
@ 2015-02-24 21:15 ` Florian Fainelli
  2015-02-24 21:15 ` [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging Florian Fainelli
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Florian Fainelli @ 2015-02-24 21:15 UTC (permalink / raw)
  To: netdev
  Cc: davem, Guenter Roeck, vivien.didelot, jerome.oufella, andrew,
	cphealy

From: Guenter Roeck <linux@roeck-us.net>

A network device notifier can be called for one or more of the created
slave devices before all slave devices have been registered. This can
result in a mismatch between ds->phys_port_mask and the registered devices
by the time the call is made, and it can result in a slave device being
added to a bridge before its entry in ds->ports[] has been initialized.

Rework the initialization code to initialize entries in ds->ports[] in
dsa_slave_create. With this change, dsa_slave_create no longer needs
to return slave_dev but can return an error code instead.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
Changes in v2:
- slightly rework the commit message so it does not mention the
  netdevice_notifier that we are adding in the next patch

 net/dsa/dsa.c      | 10 +++-------
 net/dsa/dsa_priv.h |  5 ++---
 net/dsa/slave.c    | 15 ++++++++-------
 3 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 2173402d87e0..fc1813140be6 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -314,19 +314,15 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
 	 * Create network devices for physical switch ports.
 	 */
 	for (i = 0; i < DSA_MAX_PORTS; i++) {
-		struct net_device *slave_dev;
-
 		if (!(ds->phys_port_mask & (1 << i)))
 			continue;
 
-		slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
-		if (slave_dev == NULL) {
+		ret = dsa_slave_create(ds, parent, i, pd->port_names[i]);
+		if (ret < 0) {
 			netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n",
 				   index, i, pd->port_names[i]);
-			continue;
+			ret = 0;
 		}
-
-		ds->ports[i] = slave_dev;
 	}
 
 #ifdef CONFIG_NET_DSA_HWMON
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index dc9756d3154c..7eb1a6acd46c 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -53,9 +53,8 @@ extern char dsa_driver_version[];
 /* slave.c */
 extern const struct dsa_device_ops notag_netdev_ops;
 void dsa_slave_mii_bus_init(struct dsa_switch *ds);
-struct net_device *dsa_slave_create(struct dsa_switch *ds,
-				    struct device *parent,
-				    int port, char *name);
+int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
+		     int port, char *name);
 int dsa_slave_suspend(struct net_device *slave_dev);
 int dsa_slave_resume(struct net_device *slave_dev);
 
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index f23deadf42a0..5be4c928c9c9 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -605,9 +605,8 @@ int dsa_slave_resume(struct net_device *slave_dev)
 	return 0;
 }
 
-struct net_device *
-dsa_slave_create(struct dsa_switch *ds, struct device *parent,
-		 int port, char *name)
+int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
+		     int port, char *name)
 {
 	struct net_device *master = ds->dst->master_netdev;
 	struct net_device *slave_dev;
@@ -617,7 +616,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 	slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name,
 				 NET_NAME_UNKNOWN, ether_setup);
 	if (slave_dev == NULL)
-		return slave_dev;
+		return -ENOMEM;
 
 	slave_dev->features = master->vlan_features;
 	slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
@@ -667,19 +666,21 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 	ret = dsa_slave_phy_setup(p, slave_dev);
 	if (ret) {
 		free_netdev(slave_dev);
-		return NULL;
+		return ret;
 	}
 
+	ds->ports[port] = slave_dev;
 	ret = register_netdev(slave_dev);
 	if (ret) {
 		netdev_err(master, "error %d registering interface %s\n",
 			   ret, slave_dev->name);
 		phy_disconnect(p->phy);
+		ds->ports[port] = NULL;
 		free_netdev(slave_dev);
-		return NULL;
+		return ret;
 	}
 
 	netif_carrier_off(slave_dev);
 
-	return slave_dev;
+	return 0;
 }
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging
  2015-02-24 21:15 [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging Florian Fainelli
  2015-02-24 21:15 ` [PATCH net-next v3 1/3] net: dsa: Ensure that port array elements are initialized before being used Florian Fainelli
@ 2015-02-24 21:15 ` Florian Fainelli
  2015-02-25  5:44   ` Guenter Roeck
  2015-02-24 21:15 ` [PATCH net-next v3 3/3] net: dsa: bcm_sf2: add HW bridging support Florian Fainelli
  2015-02-25 22:04 ` [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging David Miller
  3 siblings, 1 reply; 6+ messages in thread
From: Florian Fainelli @ 2015-02-24 21:15 UTC (permalink / raw)
  To: netdev
  Cc: davem, Florian Fainelli, vivien.didelot, jerome.oufella, linux,
	andrew, cphealy

In order to support bridging offloads in DSA switch drivers, select
NET_SWITCHDEV to get access to the port_stp_update and parent_get_id
NDOs that we are required to implement.

To facilitate the integratation at the DSA driver level, we implement 3
types of operations:

- port_join_bridge
- port_leave_bridge
- port_stp_update

DSA will resolve which switch ports that are currently bridge port
members as some Switch hardware/drivers need to know about that to limit
the register programming to just the relevant registers (especially for
slow MDIO buses).

We also take care of setting the correct STP state when slave network
devices are brought up/down while being bridge members.

Finally, when a port is leaving the bridge, we make sure we set in
BR_STATE_FORWARDING state, otherwise the bridge layer would leave it
disabled as a result of having left the bridge.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
Changes in v3:
- add a NULL pointer check against ds->ports[port]

 include/net/dsa.h  |  10 ++++
 net/dsa/Kconfig    |   1 +
 net/dsa/dsa.c      |   7 +++
 net/dsa/dsa_priv.h |   4 ++
 net/dsa/slave.c    | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 171 insertions(+)

diff --git a/include/net/dsa.h b/include/net/dsa.h
index ed3c34bbb67a..92be34791963 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -275,6 +275,16 @@ struct dsa_switch_driver {
 	int	(*get_regs_len)(struct dsa_switch *ds, int port);
 	void	(*get_regs)(struct dsa_switch *ds, int port,
 			    struct ethtool_regs *regs, void *p);
+
+	/*
+	 * Bridge integration
+	 */
+	int	(*port_join_bridge)(struct dsa_switch *ds, int port,
+				    u32 br_port_mask);
+	int	(*port_leave_bridge)(struct dsa_switch *ds, int port,
+				     u32 br_port_mask);
+	int	(*port_stp_update)(struct dsa_switch *ds, int port,
+				   u8 state);
 };
 
 void register_switch_driver(struct dsa_switch_driver *type);
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 5f8ac404535b..b45206e8dd3e 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -8,6 +8,7 @@ config NET_DSA
 	tristate
 	depends on HAVE_NET_DSA
 	select PHYLIB
+	select NET_SWITCHDEV
 
 if NET_DSA
 
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index fc1813140be6..9c208f0dab08 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -826,6 +826,10 @@ static struct packet_type dsa_pack_type __read_mostly = {
 	.func	= dsa_switch_rcv,
 };
 
+static struct notifier_block dsa_netdevice_nb __read_mostly = {
+	.notifier_call	= dsa_slave_netdevice_event,
+};
+
 #ifdef CONFIG_PM_SLEEP
 static int dsa_suspend(struct device *d)
 {
@@ -884,6 +888,8 @@ static int __init dsa_init_module(void)
 {
 	int rc;
 
+	register_netdevice_notifier(&dsa_netdevice_nb);
+
 	rc = platform_driver_register(&dsa_driver);
 	if (rc)
 		return rc;
@@ -896,6 +902,7 @@ module_init(dsa_init_module);
 
 static void __exit dsa_cleanup_module(void)
 {
+	unregister_netdevice_notifier(&dsa_netdevice_nb);
 	dev_remove_pack(&dsa_pack_type);
 	platform_driver_unregister(&dsa_driver);
 }
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 7eb1a6acd46c..d5f1f9b862ea 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -45,6 +45,8 @@ struct dsa_slave_priv {
 	int			old_link;
 	int			old_pause;
 	int			old_duplex;
+
+	struct net_device	*bridge_dev;
 };
 
 /* dsa.c */
@@ -57,6 +59,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 		     int port, char *name);
 int dsa_slave_suspend(struct net_device *slave_dev);
 int dsa_slave_resume(struct net_device *slave_dev);
+int dsa_slave_netdevice_event(struct notifier_block *unused,
+			      unsigned long event, void *ptr);
 
 /* tag_dsa.c */
 extern const struct dsa_device_ops dsa_netdev_ops;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 5be4c928c9c9..b5a4d8974b76 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -10,10 +10,13 @@
 
 #include <linux/list.h>
 #include <linux/etherdevice.h>
+#include <linux/netdevice.h>
 #include <linux/phy.h>
 #include <linux/phy_fixed.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
+#include <net/rtnetlink.h>
+#include <linux/if_bridge.h>
 #include "dsa_priv.h"
 
 /* slave mii_bus handling ***************************************************/
@@ -60,11 +63,18 @@ static int dsa_slave_init(struct net_device *dev)
 	return 0;
 }
 
+static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
+{
+	return !!p->bridge_dev;
+}
+
 static int dsa_slave_open(struct net_device *dev)
 {
 	struct dsa_slave_priv *p = netdev_priv(dev);
 	struct net_device *master = p->parent->dst->master_netdev;
 	struct dsa_switch *ds = p->parent;
+	u8 stp_state = dsa_port_is_bridged(p) ?
+			BR_STATE_BLOCKING : BR_STATE_FORWARDING;
 	int err;
 
 	if (!(master->flags & IFF_UP))
@@ -93,6 +103,9 @@ static int dsa_slave_open(struct net_device *dev)
 			goto clear_promisc;
 	}
 
+	if (ds->drv->port_stp_update)
+		ds->drv->port_stp_update(ds, p->port, stp_state);
+
 	if (p->phy)
 		phy_start(p->phy);
 
@@ -133,6 +146,9 @@ static int dsa_slave_close(struct net_device *dev)
 	if (ds->drv->port_disable)
 		ds->drv->port_disable(ds, p->port, p->phy);
 
+	if (ds->drv->port_stp_update)
+		ds->drv->port_stp_update(ds, p->port, BR_STATE_DISABLED);
+
 	return 0;
 }
 
@@ -194,6 +210,95 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 	return -EOPNOTSUPP;
 }
 
+/* Return a bitmask of all ports being currently bridged within a given bridge
+ * device. Note that on leave, the mask will still return the bitmask of ports
+ * currently bridged, prior to port removal, and this is exactly what we want.
+ */
+static u32 dsa_slave_br_port_mask(struct dsa_switch *ds,
+				  struct net_device *bridge)
+{
+	struct dsa_slave_priv *p;
+	unsigned int port;
+	u32 mask = 0;
+
+	for (port = 0; port < DSA_MAX_PORTS; port++) {
+		if (!((1 << port) & ds->phys_port_mask))
+			continue;
+
+		if (!ds->ports[port])
+			continue;
+
+		p = netdev_priv(ds->ports[port]);
+
+		if (ds->ports[port]->priv_flags & IFF_BRIDGE_PORT &&
+		    p->bridge_dev == bridge)
+			mask |= 1 << port;
+	}
+
+	return mask;
+}
+
+static int dsa_slave_stp_update(struct net_device *dev, u8 state)
+{
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	int ret = -EOPNOTSUPP;
+
+	if (ds->drv->port_stp_update)
+		ret = ds->drv->port_stp_update(ds, p->port, state);
+
+	return ret;
+}
+
+static int dsa_slave_bridge_port_join(struct net_device *dev,
+				      struct net_device *br)
+{
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	int ret = -EOPNOTSUPP;
+
+	p->bridge_dev = br;
+
+	if (ds->drv->port_join_bridge)
+		ret = ds->drv->port_join_bridge(ds, p->port,
+						dsa_slave_br_port_mask(ds, br));
+
+	return ret;
+}
+
+static int dsa_slave_bridge_port_leave(struct net_device *dev)
+{
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+	int ret = -EOPNOTSUPP;
+
+
+	if (ds->drv->port_leave_bridge)
+		ret = ds->drv->port_leave_bridge(ds, p->port,
+						 dsa_slave_br_port_mask(ds, p->bridge_dev));
+
+	p->bridge_dev = NULL;
+
+	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
+	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
+	 */
+	dsa_slave_stp_update(dev, BR_STATE_FORWARDING);
+
+	return ret;
+}
+
+static int dsa_slave_parent_id_get(struct net_device *dev,
+				   struct netdev_phys_item_id *psid)
+{
+	struct dsa_slave_priv *p = netdev_priv(dev);
+	struct dsa_switch *ds = p->parent;
+
+	psid->id_len = sizeof(ds->index);
+	memcpy(&psid->id, &ds->index, psid->id_len);
+
+	return 0;
+}
+
 static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct dsa_slave_priv *p = netdev_priv(dev);
@@ -470,6 +575,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
 	.ndo_set_rx_mode	= dsa_slave_set_rx_mode,
 	.ndo_set_mac_address	= dsa_slave_set_mac_address,
 	.ndo_do_ioctl		= dsa_slave_ioctl,
+	.ndo_switch_parent_id_get = dsa_slave_parent_id_get,
+	.ndo_switch_port_stp_update = dsa_slave_stp_update,
 };
 
 static void dsa_slave_adjust_link(struct net_device *dev)
@@ -684,3 +791,45 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
 
 	return 0;
 }
+
+static bool dsa_slave_dev_check(struct net_device *dev)
+{
+	return dev->netdev_ops == &dsa_slave_netdev_ops;
+}
+
+static int dsa_slave_master_changed(struct net_device *dev)
+{
+	struct net_device *master = netdev_master_upper_dev_get(dev);
+	int err = 0;
+
+	if (master && master->rtnl_link_ops &&
+	    !strcmp(master->rtnl_link_ops->kind, "bridge"))
+		err = dsa_slave_bridge_port_join(dev, master);
+	else
+		err = dsa_slave_bridge_port_leave(dev);
+
+	return err;
+}
+
+int dsa_slave_netdevice_event(struct notifier_block *unused,
+			      unsigned long event, void *ptr)
+{
+	struct net_device *dev;
+	int err = 0;
+
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		dev = netdev_notifier_info_to_dev(ptr);
+		if (!dsa_slave_dev_check(dev))
+			goto out;
+
+		err = dsa_slave_master_changed(dev);
+		if (err)
+			netdev_warn(dev, "failed to reflect master change\n");
+
+		break;
+	}
+
+out:
+	return NOTIFY_DONE;
+}
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net-next v3 3/3] net: dsa: bcm_sf2: add HW bridging support
  2015-02-24 21:15 [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging Florian Fainelli
  2015-02-24 21:15 ` [PATCH net-next v3 1/3] net: dsa: Ensure that port array elements are initialized before being used Florian Fainelli
  2015-02-24 21:15 ` [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging Florian Fainelli
@ 2015-02-24 21:15 ` Florian Fainelli
  2015-02-25 22:04 ` [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging David Miller
  3 siblings, 0 replies; 6+ messages in thread
From: Florian Fainelli @ 2015-02-24 21:15 UTC (permalink / raw)
  To: netdev
  Cc: davem, Florian Fainelli, vivien.didelot, jerome.oufella, linux,
	andrew, cphealy

Implement the bridge join, leave and set_stp callbacks by making that
we do the following:

- when a port joins the bridge, all existing ports in the bridge get
  their VLAN control register updated with that joining port
- the joining port is including all existing bridge ports in its own
  VLAN control register

The leave operation is fairly similar, special care must be taken to
make sure that port leaving the bridging is not removing itself from its
own VLAN control register.

Since the various BR_* states apply directly to our HW semantics, we
just need to translate these constants into their corresponding HW
settings, and voila!

We make sure to trigger a fast-ageing process for ports that are
joining/leaving the bridge and transition from incompatible states, this
is equivalent to triggering an ARL flush for that port.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/dsa/bcm_sf2.c      | 155 ++++++++++++++++++++++++++++++++++++++++-
 drivers/net/dsa/bcm_sf2.h      |   2 +
 drivers/net/dsa/bcm_sf2_regs.h |  15 ++++
 3 files changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 4daffb284931..cedb572bf25a 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -23,6 +23,7 @@
 #include <linux/of_address.h>
 #include <net/dsa.h>
 #include <linux/ethtool.h>
+#include <linux/if_bridge.h>
 
 #include "bcm_sf2.h"
 #include "bcm_sf2_regs.h"
@@ -299,10 +300,14 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,
 	if (port == 7)
 		intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF));
 
-	/* Set this port, and only this one to be in the default VLAN */
+	/* Set this port, and only this one to be in the default VLAN,
+	 * if member of a bridge, restore its membership prior to
+	 * bringing down this port.
+	 */
 	reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port));
 	reg &= ~PORT_VLAN_CTRL_MASK;
 	reg |= (1 << port);
+	reg |= priv->port_sts[port].vlan_ctl_mask;
 	core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port));
 
 	bcm_sf2_imp_vlan_setup(ds, cpu_port);
@@ -400,6 +405,151 @@ static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+/* Fast-ageing of ARL entries for a given port, equivalent to an ARL
+ * flush for that port.
+ */
+static int bcm_sf2_sw_fast_age_port(struct dsa_switch  *ds, int port)
+{
+	struct bcm_sf2_priv *priv = ds_to_priv(ds);
+	unsigned int timeout = 1000;
+	u32 reg;
+
+	core_writel(priv, port, CORE_FAST_AGE_PORT);
+
+	reg = core_readl(priv, CORE_FAST_AGE_CTRL);
+	reg |= EN_AGE_PORT | FAST_AGE_STR_DONE;
+	core_writel(priv, reg, CORE_FAST_AGE_CTRL);
+
+	do {
+		reg = core_readl(priv, CORE_FAST_AGE_CTRL);
+		if (!(reg & FAST_AGE_STR_DONE))
+			break;
+
+		cpu_relax();
+	} while (timeout--);
+
+	if (!timeout)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port,
+			      u32 br_port_mask)
+{
+	struct bcm_sf2_priv *priv = ds_to_priv(ds);
+	unsigned int i;
+	u32 reg, p_ctl;
+
+	p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port));
+
+	for (i = 0; i < priv->hw_params.num_ports; i++) {
+		if (!((1 << i) & br_port_mask))
+			continue;
+
+		/* Add this local port to the remote port VLAN control
+		 * membership and update the remote port bitmask
+		 */
+		reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i));
+		reg |= 1 << port;
+		core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i));
+		priv->port_sts[i].vlan_ctl_mask = reg;
+
+		p_ctl |= 1 << i;
+	}
+
+	/* Configure the local port VLAN control membership to include
+	 * remote ports and update the local port bitmask
+	 */
+	core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port));
+	priv->port_sts[port].vlan_ctl_mask = p_ctl;
+
+	return 0;
+}
+
+static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port,
+			       u32 br_port_mask)
+{
+	struct bcm_sf2_priv *priv = ds_to_priv(ds);
+	unsigned int i;
+	u32 reg, p_ctl;
+
+	p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port));
+
+	for (i = 0; i < priv->hw_params.num_ports; i++) {
+		/* Don't touch the remaining ports */
+		if (!((1 << i) & br_port_mask))
+			continue;
+
+		reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i));
+		reg &= ~(1 << port);
+		core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i));
+		priv->port_sts[port].vlan_ctl_mask = reg;
+
+		/* Prevent self removal to preserve isolation */
+		if (port != i)
+			p_ctl &= ~(1 << i);
+	}
+
+	core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port));
+	priv->port_sts[port].vlan_ctl_mask = p_ctl;
+
+	return 0;
+}
+
+static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port,
+				       u8 state)
+{
+	struct bcm_sf2_priv *priv = ds_to_priv(ds);
+	u8 hw_state, cur_hw_state;
+	int ret = 0;
+	u32 reg;
+
+	reg = core_readl(priv, CORE_G_PCTL_PORT(port));
+	cur_hw_state = reg >> G_MISTP_STATE_SHIFT;
+
+	switch (state) {
+	case BR_STATE_DISABLED:
+		hw_state = G_MISTP_DIS_STATE;
+		break;
+	case BR_STATE_LISTENING:
+		hw_state = G_MISTP_LISTEN_STATE;
+		break;
+	case BR_STATE_LEARNING:
+		hw_state = G_MISTP_LEARN_STATE;
+		break;
+	case BR_STATE_FORWARDING:
+		hw_state = G_MISTP_FWD_STATE;
+		break;
+	case BR_STATE_BLOCKING:
+		hw_state = G_MISTP_BLOCK_STATE;
+		break;
+	default:
+		pr_err("%s: invalid STP state: %d\n", __func__, state);
+		return -EINVAL;
+	}
+
+	/* Fast-age ARL entries if we are moving a port from Learning or
+	 * Forwarding state to Disabled, Blocking or Listening state
+	 */
+	if (cur_hw_state != hw_state) {
+		if (cur_hw_state & 4 && !(hw_state & 4)) {
+			ret = bcm_sf2_sw_fast_age_port(ds, port);
+			if (ret) {
+				pr_err("%s: fast-ageing failed\n", __func__);
+				return ret;
+			}
+		}
+	}
+
+	reg = core_readl(priv, CORE_G_PCTL_PORT(port));
+	reg &= ~(G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT);
+	reg |= hw_state;
+	core_writel(priv, reg, CORE_G_PCTL_PORT(port));
+
+	return 0;
+}
+
 static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
 {
 	struct bcm_sf2_priv *priv = dev_id;
@@ -916,6 +1066,9 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
 	.port_disable		= bcm_sf2_port_disable,
 	.get_eee		= bcm_sf2_sw_get_eee,
 	.set_eee		= bcm_sf2_sw_set_eee,
+	.port_join_bridge	= bcm_sf2_sw_br_join,
+	.port_leave_bridge	= bcm_sf2_sw_br_leave,
+	.port_stp_update	= bcm_sf2_sw_br_set_stp_state,
 };
 
 static int __init bcm_sf2_init(void)
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
index ee9f650d5026..0f217e99904f 100644
--- a/drivers/net/dsa/bcm_sf2.h
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -46,6 +46,8 @@ struct bcm_sf2_port_status {
 	unsigned int link;
 
 	struct ethtool_eee eee;
+
+	u32 vlan_ctl_mask;
 };
 
 struct bcm_sf2_priv {
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
index cabdfa5e217a..fa4e6e78c9ea 100644
--- a/drivers/net/dsa/bcm_sf2_regs.h
+++ b/drivers/net/dsa/bcm_sf2_regs.h
@@ -163,6 +163,21 @@
 #define  EN_CHIP_RST			(1 << 6)
 #define  EN_SW_RESET			(1 << 4)
 
+#define CORE_FAST_AGE_CTRL		0x00220
+#define  EN_FAST_AGE_STATIC		(1 << 0)
+#define  EN_AGE_DYNAMIC			(1 << 1)
+#define  EN_AGE_PORT			(1 << 2)
+#define  EN_AGE_VLAN			(1 << 3)
+#define  EN_AGE_SPT			(1 << 4)
+#define  EN_AGE_MCAST			(1 << 5)
+#define  FAST_AGE_STR_DONE		(1 << 7)
+
+#define CORE_FAST_AGE_PORT		0x00224
+#define  AGE_PORT_MASK			0xf
+
+#define CORE_FAST_AGE_VID		0x00228
+#define  AGE_VID_MASK			0x3fff
+
 #define CORE_LNKSTS			0x00400
 #define  LNK_STS_MASK			0x1ff
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging
  2015-02-24 21:15 ` [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging Florian Fainelli
@ 2015-02-25  5:44   ` Guenter Roeck
  0 siblings, 0 replies; 6+ messages in thread
From: Guenter Roeck @ 2015-02-25  5:44 UTC (permalink / raw)
  To: Florian Fainelli, netdev
  Cc: davem, vivien.didelot, jerome.oufella, andrew, cphealy

On 02/24/2015 01:15 PM, Florian Fainelli wrote:
> In order to support bridging offloads in DSA switch drivers, select
> NET_SWITCHDEV to get access to the port_stp_update and parent_get_id
> NDOs that we are required to implement.
>
> To facilitate the integratation at the DSA driver level, we implement 3
> types of operations:
>
> - port_join_bridge
> - port_leave_bridge
> - port_stp_update
>
> DSA will resolve which switch ports that are currently bridge port
> members as some Switch hardware/drivers need to know about that to limit
> the register programming to just the relevant registers (especially for
> slow MDIO buses).
>
> We also take care of setting the correct STP state when slave network
> devices are brought up/down while being bridge members.
>
> Finally, when a port is leaving the bridge, we make sure we set in
> BR_STATE_FORWARDING state, otherwise the bridge layer would leave it
> disabled as a result of having left the bridge.
>
> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

And with my code adding support for HW bridging to the mv88e6352 driver:

Tested-by: Guenter Roeck <linux@roeck-us.net>

Guenter

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging
  2015-02-24 21:15 [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging Florian Fainelli
                   ` (2 preceding siblings ...)
  2015-02-24 21:15 ` [PATCH net-next v3 3/3] net: dsa: bcm_sf2: add HW bridging support Florian Fainelli
@ 2015-02-25 22:04 ` David Miller
  3 siblings, 0 replies; 6+ messages in thread
From: David Miller @ 2015-02-25 22:04 UTC (permalink / raw)
  To: f.fainelli; +Cc: netdev, vivien.didelot, jerome.oufella, linux, andrew, cphealy

From: Florian Fainelli <f.fainelli@gmail.com>
Date: Tue, 24 Feb 2015 13:15:31 -0800

> This patch set provides the DSA and SWITCHDEV integration bits together and
> modifies the bcm_sf2 driver accordingly such that it works properly with HW
> bridging.

Series applied, thanks Florian.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-02-25 22:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-24 21:15 [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging Florian Fainelli
2015-02-24 21:15 ` [PATCH net-next v3 1/3] net: dsa: Ensure that port array elements are initialized before being used Florian Fainelli
2015-02-24 21:15 ` [PATCH net-next v3 2/3] net: dsa: integrate with SWITCHDEV for HW bridging Florian Fainelli
2015-02-25  5:44   ` Guenter Roeck
2015-02-24 21:15 ` [PATCH net-next v3 3/3] net: dsa: bcm_sf2: add HW bridging support Florian Fainelli
2015-02-25 22:04 ` [PATCH net-next v3 0/3] net: dsa: integration with SWITCHDEV for HW bridging David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).