aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/dsa/switch.c')
-rw-r--r--net/dsa/switch.c322
1 files changed, 255 insertions, 67 deletions
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 3fb362b6874e..4b5da89dc27a 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -33,15 +33,12 @@ static int dsa_switch_ageing_time(struct dsa_switch *ds,
struct dsa_notifier_ageing_time_info *info)
{
unsigned int ageing_time = info->ageing_time;
- struct switchdev_trans *trans = info->trans;
-
- if (switchdev_trans_ph_prepare(trans)) {
- if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
- return -ERANGE;
- if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
- return -ERANGE;
- return 0;
- }
+
+ if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
+ return -ERANGE;
+
+ if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
+ return -ERANGE;
/* Program the fastest ageing time in case of multiple bridges */
ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
@@ -109,6 +106,7 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
{
bool unset_vlan_filtering = br_vlan_enabled(info->br);
struct dsa_switch_tree *dst = ds->dst;
+ struct netlink_ext_ack extack = {0};
int err, i;
if (dst->index == info->tree_index && ds->index == info->sw_index &&
@@ -139,17 +137,11 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
}
}
if (unset_vlan_filtering) {
- struct switchdev_trans trans;
-
- trans.ph_prepare = true;
err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
- false, &trans);
- if (err && err != EOPNOTSUPP)
- return err;
-
- trans.ph_prepare = false;
- err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
- false, &trans);
+ false, &extack);
+ if (extack._msg)
+ dev_err(ds->dev, "port %d: %s\n", info->port,
+ extack._msg);
if (err && err != EOPNOTSUPP)
return err;
}
@@ -178,6 +170,65 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
}
+static int dsa_switch_hsr_join(struct dsa_switch *ds,
+ struct dsa_notifier_hsr_info *info)
+{
+ if (ds->index == info->sw_index && ds->ops->port_hsr_join)
+ return ds->ops->port_hsr_join(ds, info->port, info->hsr);
+
+ return -EOPNOTSUPP;
+}
+
+static int dsa_switch_hsr_leave(struct dsa_switch *ds,
+ struct dsa_notifier_hsr_info *info)
+{
+ if (ds->index == info->sw_index && ds->ops->port_hsr_leave)
+ return ds->ops->port_hsr_leave(ds, info->port, info->hsr);
+
+ return -EOPNOTSUPP;
+}
+
+static int dsa_switch_lag_change(struct dsa_switch *ds,
+ struct dsa_notifier_lag_info *info)
+{
+ if (ds->index == info->sw_index && ds->ops->port_lag_change)
+ return ds->ops->port_lag_change(ds, info->port);
+
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
+ return ds->ops->crosschip_lag_change(ds, info->sw_index,
+ info->port);
+
+ return 0;
+}
+
+static int dsa_switch_lag_join(struct dsa_switch *ds,
+ struct dsa_notifier_lag_info *info)
+{
+ if (ds->index == info->sw_index && ds->ops->port_lag_join)
+ return ds->ops->port_lag_join(ds, info->port, info->lag,
+ info->info);
+
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
+ return ds->ops->crosschip_lag_join(ds, info->sw_index,
+ info->port, info->lag,
+ info->info);
+
+ return 0;
+}
+
+static int dsa_switch_lag_leave(struct dsa_switch *ds,
+ struct dsa_notifier_lag_info *info)
+{
+ if (ds->index == info->sw_index && ds->ops->port_lag_leave)
+ return ds->ops->port_lag_leave(ds, info->port, info->lag);
+
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
+ return ds->ops->crosschip_lag_leave(ds, info->sw_index,
+ info->port, info->lag);
+
+ return 0;
+}
+
static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
struct dsa_notifier_mdb_info *info)
{
@@ -190,41 +241,24 @@ static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
return false;
}
-static int dsa_switch_mdb_prepare(struct dsa_switch *ds,
- struct dsa_notifier_mdb_info *info)
+static int dsa_switch_mdb_add(struct dsa_switch *ds,
+ struct dsa_notifier_mdb_info *info)
{
- int port, err;
+ int err = 0;
+ int port;
- if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
+ if (!ds->ops->port_mdb_add)
return -EOPNOTSUPP;
for (port = 0; port < ds->num_ports; port++) {
if (dsa_switch_mdb_match(ds, port, info)) {
- err = ds->ops->port_mdb_prepare(ds, port, info->mdb);
+ err = ds->ops->port_mdb_add(ds, port, info->mdb);
if (err)
- return err;
+ break;
}
}
- return 0;
-}
-
-static int dsa_switch_mdb_add(struct dsa_switch *ds,
- struct dsa_notifier_mdb_info *info)
-{
- int port;
-
- if (switchdev_trans_ph_prepare(info->trans))
- return dsa_switch_mdb_prepare(ds, info);
-
- if (!ds->ops->port_mdb_add)
- return 0;
-
- for (port = 0; port < ds->num_ports; port++)
- if (dsa_switch_mdb_match(ds, port, info))
- ds->ops->port_mdb_add(ds, port, info->mdb);
-
- return 0;
+ return err;
}
static int dsa_switch_mdb_del(struct dsa_switch *ds,
@@ -251,17 +285,18 @@ static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
return false;
}
-static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
- struct dsa_notifier_vlan_info *info)
+static int dsa_switch_vlan_add(struct dsa_switch *ds,
+ struct dsa_notifier_vlan_info *info)
{
int port, err;
- if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
+ if (!ds->ops->port_vlan_add)
return -EOPNOTSUPP;
for (port = 0; port < ds->num_ports; port++) {
if (dsa_switch_vlan_match(ds, port, info)) {
- err = ds->ops->port_vlan_prepare(ds, port, info->vlan);
+ err = ds->ops->port_vlan_add(ds, port, info->vlan,
+ info->extack);
if (err)
return err;
}
@@ -270,36 +305,163 @@ static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
return 0;
}
-static int dsa_switch_vlan_add(struct dsa_switch *ds,
+static int dsa_switch_vlan_del(struct dsa_switch *ds,
struct dsa_notifier_vlan_info *info)
{
+ if (!ds->ops->port_vlan_del)
+ return -EOPNOTSUPP;
+
+ if (ds->index == info->sw_index)
+ return ds->ops->port_vlan_del(ds, info->port, info->vlan);
+
+ /* Do not deprogram the DSA links as they may be used as conduit
+ * for other VLAN members in the fabric.
+ */
+ return 0;
+}
+
+static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port,
+ struct dsa_notifier_tag_proto_info *info)
+{
+ if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
+ return true;
+
+ return false;
+}
+
+static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
+ struct dsa_notifier_tag_proto_info *info)
+{
+ const struct dsa_device_ops *tag_ops = info->tag_ops;
+ int port, err;
+
+ if (!ds->ops->change_tag_protocol)
+ return -EOPNOTSUPP;
+
+ ASSERT_RTNL();
+
+ for (port = 0; port < ds->num_ports; port++) {
+ if (dsa_switch_tag_proto_match(ds, port, info)) {
+ err = ds->ops->change_tag_protocol(ds, port,
+ tag_ops->proto);
+ if (err)
+ return err;
+
+ if (dsa_is_cpu_port(ds, port))
+ dsa_port_set_tag_protocol(dsa_to_port(ds, port),
+ tag_ops);
+ }
+ }
+
+ /* Now that changing the tag protocol can no longer fail, let's update
+ * the remaining bits which are "duplicated for faster access", and the
+ * bits that depend on the tagger, such as the MTU.
+ */
+ for (port = 0; port < ds->num_ports; port++) {
+ if (dsa_is_user_port(ds, port)) {
+ struct net_device *slave;
+
+ slave = dsa_to_port(ds, port)->slave;
+ dsa_slave_setup_tagger(slave);
+
+ /* rtnl_mutex is held in dsa_tree_change_tag_proto */
+ dsa_slave_change_mtu(slave, slave->mtu);
+ }
+ }
+
+ return 0;
+}
+
+static bool dsa_switch_mrp_match(struct dsa_switch *ds, int port,
+ struct dsa_notifier_mrp_info *info)
+{
+ if (ds->index == info->sw_index && port == info->port)
+ return true;
+
+ if (dsa_is_dsa_port(ds, port))
+ return true;
+
+ return false;
+}
+
+static int dsa_switch_mrp_add(struct dsa_switch *ds,
+ struct dsa_notifier_mrp_info *info)
+{
+ int err = 0;
int port;
- if (switchdev_trans_ph_prepare(info->trans))
- return dsa_switch_vlan_prepare(ds, info);
+ if (!ds->ops->port_mrp_add)
+ return -EOPNOTSUPP;
- if (!ds->ops->port_vlan_add)
- return 0;
+ for (port = 0; port < ds->num_ports; port++) {
+ if (dsa_switch_mrp_match(ds, port, info)) {
+ err = ds->ops->port_mrp_add(ds, port, info->mrp);
+ if (err)
+ break;
+ }
+ }
+
+ return err;
+}
- for (port = 0; port < ds->num_ports; port++)
- if (dsa_switch_vlan_match(ds, port, info))
- ds->ops->port_vlan_add(ds, port, info->vlan);
+static int dsa_switch_mrp_del(struct dsa_switch *ds,
+ struct dsa_notifier_mrp_info *info)
+{
+ if (!ds->ops->port_mrp_del)
+ return -EOPNOTSUPP;
+
+ if (ds->index == info->sw_index)
+ return ds->ops->port_mrp_del(ds, info->port, info->mrp);
return 0;
}
-static int dsa_switch_vlan_del(struct dsa_switch *ds,
- struct dsa_notifier_vlan_info *info)
+static bool
+dsa_switch_mrp_ring_role_match(struct dsa_switch *ds, int port,
+ struct dsa_notifier_mrp_ring_role_info *info)
{
- if (!ds->ops->port_vlan_del)
+ if (ds->index == info->sw_index && port == info->port)
+ return true;
+
+ if (dsa_is_dsa_port(ds, port))
+ return true;
+
+ return false;
+}
+
+static int
+dsa_switch_mrp_add_ring_role(struct dsa_switch *ds,
+ struct dsa_notifier_mrp_ring_role_info *info)
+{
+ int err = 0;
+ int port;
+
+ if (!ds->ops->port_mrp_add)
+ return -EOPNOTSUPP;
+
+ for (port = 0; port < ds->num_ports; port++) {
+ if (dsa_switch_mrp_ring_role_match(ds, port, info)) {
+ err = ds->ops->port_mrp_add_ring_role(ds, port,
+ info->mrp);
+ if (err)
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int
+dsa_switch_mrp_del_ring_role(struct dsa_switch *ds,
+ struct dsa_notifier_mrp_ring_role_info *info)
+{
+ if (!ds->ops->port_mrp_del)
return -EOPNOTSUPP;
if (ds->index == info->sw_index)
- return ds->ops->port_vlan_del(ds, info->port, info->vlan);
+ return ds->ops->port_mrp_del_ring_role(ds, info->port,
+ info->mrp);
- /* Do not deprogram the DSA links as they may be used as conduit
- * for other VLAN members in the fabric.
- */
return 0;
}
@@ -325,6 +487,21 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_FDB_DEL:
err = dsa_switch_fdb_del(ds, info);
break;
+ case DSA_NOTIFIER_HSR_JOIN:
+ err = dsa_switch_hsr_join(ds, info);
+ break;
+ case DSA_NOTIFIER_HSR_LEAVE:
+ err = dsa_switch_hsr_leave(ds, info);
+ break;
+ case DSA_NOTIFIER_LAG_CHANGE:
+ err = dsa_switch_lag_change(ds, info);
+ break;
+ case DSA_NOTIFIER_LAG_JOIN:
+ err = dsa_switch_lag_join(ds, info);
+ break;
+ case DSA_NOTIFIER_LAG_LEAVE:
+ err = dsa_switch_lag_leave(ds, info);
+ break;
case DSA_NOTIFIER_MDB_ADD:
err = dsa_switch_mdb_add(ds, info);
break;
@@ -340,15 +517,26 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_MTU:
err = dsa_switch_mtu(ds, info);
break;
+ case DSA_NOTIFIER_TAG_PROTO:
+ err = dsa_switch_change_tag_proto(ds, info);
+ break;
+ case DSA_NOTIFIER_MRP_ADD:
+ err = dsa_switch_mrp_add(ds, info);
+ break;
+ case DSA_NOTIFIER_MRP_DEL:
+ err = dsa_switch_mrp_del(ds, info);
+ break;
+ case DSA_NOTIFIER_MRP_ADD_RING_ROLE:
+ err = dsa_switch_mrp_add_ring_role(ds, info);
+ break;
+ case DSA_NOTIFIER_MRP_DEL_RING_ROLE:
+ err = dsa_switch_mrp_del_ring_role(ds, info);
+ break;
default:
err = -EOPNOTSUPP;
break;
}
- /* Non-switchdev operations cannot be rolled back. If a DSA driver
- * returns an error during the chained call, switch chips may be in an
- * inconsistent state.
- */
if (err)
dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
event, err);