[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1461796217-18893-15-git-send-email-vivien.didelot@savoirfairelinux.com>
Date: Wed, 27 Apr 2016 18:30:11 -0400
From: Vivien Didelot <vivien.didelot@...oirfairelinux.com>
To: netdev@...r.kernel.org
Cc: linux-kernel@...r.kernel.org, kernel@...oirfairelinux.com,
"David S. Miller" <davem@...emloft.net>,
Florian Fainelli <f.fainelli@...il.com>,
Andrew Lunn <andrew@...n.ch>, Jiri Pirko <jiri@...nulli.us>,
Vivien Didelot <vivien.didelot@...oirfairelinux.com>
Subject: [RFC 14/20] net: dsa: add tree-wide bridge ops
In order to support cross-chip operations, we need to inform each switch
driver when a port operation occurs in a DSA tree.
This allows drivers to configure cross-chip port-based VLAN table, VTU
or FDB entries on DSA links, in order to implement a correct hardware
switching of frames.
Add a new tree.c file to implement tree-wide operations, propagating a
port-based operation on each switch of a tree.
Implement tree-wide bridge operations.
Signed-off-by: Vivien Didelot <vivien.didelot@...oirfairelinux.com>
---
drivers/net/dsa/bcm_sf2.c | 6 +++++
drivers/net/dsa/mv88e6xxx.c | 6 +++++
include/net/dsa.h | 6 +++++
net/dsa/Makefile | 2 +-
net/dsa/dsa_priv.h | 6 +++++
net/dsa/slave.c | 46 ++++---------------------------
net/dsa/tree.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 96 insertions(+), 42 deletions(-)
create mode 100644 net/dsa/tree.c
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 6e3b844..0a91ea9 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -498,6 +498,9 @@ static int bcm_sf2_sw_br_join(struct dsa_switch *ds, struct dsa_port *dp,
struct dsa_port *intp;
u32 reg, p_ctl;
+ if (dsa_port_is_external(dp, ds))
+ return -EOPNOTSUPP;
+
p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(dp->port));
dsa_switch_for_each_port(ds, intp, priv->hw_params.num_ports) {
@@ -531,6 +534,9 @@ static void bcm_sf2_sw_br_leave(struct dsa_switch *ds, struct dsa_port *dp,
struct dsa_port *intp;
u32 reg, p_ctl;
+ if (dsa_port_is_external(dp, ds))
+ return;
+
p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(dp->port));
dsa_switch_for_each_port(ds, intp, priv->hw_params.num_ports) {
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 89d0206..6fef29b 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -2212,6 +2212,9 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, struct dsa_port *dp,
struct dsa_port *intp;
int err;
+ if (dsa_port_is_external(dp, ds))
+ return -EOPNOTSUPP;
+
mutex_lock(&ps->smi_mutex);
/* Remap each port's VLANTable */
@@ -2234,6 +2237,9 @@ void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, struct dsa_port *dp,
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
struct dsa_port *intp;
+ if (dsa_port_is_external(dp, ds))
+ return;
+
mutex_lock(&ps->smi_mutex);
/* Remap each port's VLANTable */
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 85fac8a..33172c9 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -193,6 +193,12 @@ struct dsa_switch {
struct list_head dp;
};
+static inline bool dsa_port_is_external(struct dsa_port *dp,
+ struct dsa_switch *ds)
+{
+ return dp->ds != ds;
+}
+
static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p)
{
return !!(ds->index == ds->dst->cpu_switch && p == ds->dst->cpu_port);
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index da06ed1..bf8d12c 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -1,6 +1,6 @@
# the core
obj-$(CONFIG_NET_DSA) += dsa_core.o
-dsa_core-y += dsa.o slave.o
+dsa_core-y += dsa.o tree.o slave.o
# tagging formats
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index c5afddd..6e08b3d 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -46,6 +46,12 @@ struct dsa_slave_priv {
/* dsa.c */
extern char dsa_driver_version[];
+/* tree.c */
+int dsa_tree_bridge_port_join(struct dsa_switch_tree *dst, struct dsa_port *dp,
+ struct net_device *br);
+void dsa_tree_bridge_port_leave(struct dsa_switch_tree *dst,
+ struct dsa_port *dp, struct net_device *br);
+
/* slave.c */
extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index b90caf8..7123ae2 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -425,45 +425,6 @@ static int dsa_slave_port_obj_dump(struct net_device *dev,
return err;
}
-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->dp->ds;
- int ret = -EOPNOTSUPP;
-
- p->dp->br = br;
-
- if (ds->drv->port_bridge_join)
- ret = ds->drv->port_bridge_join(ds, p->dp, br);
-
- if (ret && ret != -EOPNOTSUPP) {
- p->dp->br = NULL;
- return ret;
- }
-
- return 0;
-}
-
-static void dsa_slave_bridge_port_leave(struct net_device *dev)
-{
- struct dsa_slave_priv *p = netdev_priv(dev);
- struct dsa_switch *ds = p->dp->ds;
- struct net_device *br = p->dp->br;
-
- p->dp->br = NULL;
-
- if (ds->drv->port_bridge_leave)
- ds->drv->port_bridge_leave(ds, p->dp, br);
-
- /* 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
- */
- if (ds->drv->port_stp_state_set)
- ds->drv->port_stp_state_set(ds, p->dp->port,
- BR_STATE_FORWARDING);
-}
-
static int dsa_slave_port_attr_get(struct net_device *dev,
struct switchdev_attr *attr)
{
@@ -1140,6 +1101,9 @@ static bool dsa_slave_dev_check(struct net_device *dev)
static int dsa_slave_port_upper_event(struct net_device *dev,
unsigned long event, void *ptr)
{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_port *dp = p->dp;
+ struct dsa_switch_tree *dst = dp->ds->dst;
struct netdev_notifier_changeupper_info *info = ptr;
struct net_device *upper = info->upper_dev;
int err = 0;
@@ -1148,9 +1112,9 @@ static int dsa_slave_port_upper_event(struct net_device *dev,
case NETDEV_CHANGEUPPER:
if (netif_is_bridge_master(upper)) {
if (info->linking)
- err = dsa_slave_bridge_port_join(dev, upper);
+ err = dsa_tree_bridge_port_join(dst, dp, upper);
else
- dsa_slave_bridge_port_leave(dev);
+ dsa_tree_bridge_port_leave(dst, dp, upper);
}
break;
diff --git a/net/dsa/tree.c b/net/dsa/tree.c
new file mode 100644
index 0000000..d3f5aea
--- /dev/null
+++ b/net/dsa/tree.c
@@ -0,0 +1,66 @@
+/*
+ * net/dsa/tree.c - DSA switch tree handling
+ * Copyright (c) 2016 Vivien Didelot <vivien.didelot@...oirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/if_bridge.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+
+#include "dsa_priv.h"
+
+int dsa_tree_bridge_port_join(struct dsa_switch_tree *dst, struct dsa_port *dp,
+ struct net_device *br)
+{
+ struct dsa_switch *ds;
+ int err = 0;
+
+ /* on NETDEV_CHANGEUPPER, the port is already bridged */
+ dp->br = br;
+
+ dsa_tree_for_each_switch(dst, ds) {
+ if (ds->drv->port_bridge_join) {
+ err = ds->drv->port_bridge_join(ds, dp, br);
+ if (err) {
+ if (err != -EOPNOTSUPP)
+ break;
+ err = 0;
+ }
+ }
+ }
+
+ /* if an error is reported, bridge rolls back the operation */
+ if (err)
+ dp->br = NULL;
+
+ return err;
+}
+
+void dsa_tree_bridge_port_leave(struct dsa_switch_tree *dst,
+ struct dsa_port *dp, struct net_device *br)
+{
+ struct dsa_switch *ds;
+
+ /* on NETDEV_CHANGEUPPER, the port is already unbridged */
+ dp->br = NULL;
+
+ dsa_tree_for_each_switch(dst, ds) {
+ if (ds->drv->port_bridge_leave)
+ ds->drv->port_bridge_leave(ds, dp, br);
+
+ if (dsa_port_is_external(dp, ds))
+ continue;
+
+ /* The bridge layer put the port in BR_STATE_DISABLED,
+ * restore BR_STATE_FORWARDING to keep it functional.
+ */
+ if (ds->drv->port_stp_state_set)
+ ds->drv->port_stp_state_set(ds, dp->port,
+ BR_STATE_FORWARDING);
+ }
+}
--
2.8.0
Powered by blists - more mailing lists