summaryrefslogtreecommitdiff
path: root/net/dsa/port.c
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2021-12-08 14:31:18 -0800
committerJakub Kicinski <kuba@kernel.org>2021-12-08 14:31:19 -0800
commit3a262c71d3e8734fc426c42d5315e259e8f47bdd (patch)
tree006a5609fe64795e5c6e693c300c3e6206be8e68 /net/dsa/port.c
parent1fe5b01262844be03de98afdd56d1d393df04d7e (diff)
parent857fdd74fb38dad4d0333401fb1826cd8cf0df2c (diff)
Merge branch 'rework-dsa-bridge-tx-forwarding-offload-api'
Vladimir Oltean says: ==================== Rework DSA bridge TX forwarding offload API This change set is preparation work for DSA support of bridge FDB isolation. It replaces struct net_device *dp->bridge_dev with a struct dsa_bridge *dp->bridge that contains some extra information about that bridge, like a unique number kept by DSA. Up until now we computed that number only with the bridge TX forwarding offload feature, but it will be needed for other features too, like for isolation of FDB entries belonging to different bridges. Hardware implementations vary, but one common pattern seems to be the presence of a FID field which can be associated with that bridge number kept by DSA. The idea was outlined here: https://patchwork.kernel.org/project/netdevbpf/patch/20210818120150.892647-16-vladimir.oltean@nxp.com/ (the difference being that with this new proposal, drivers would not need to call dsa_bridge_num_find, instead the bridge_num would be part of the struct dsa_bridge :: num passed as argument). No functional change is intended for drivers that don't already make use of the bridge TX forwarding offload. I've tested the changes on the felix, sja1105 and mv88e6xxx drivers, but nonetheless I'm copying all DSA driver maintainers due to API changes that are taking place. Compared to v1 and v2, the amount of patches is larger, but the contents is mostly the same, just split up hopefully a bit better for review. ==================== Link: https://lore.kernel.org/r/20211206165758.1553882-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/dsa/port.c')
-rw-r--r--net/dsa/port.c124
1 files changed, 64 insertions, 60 deletions
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 6d5ebe61280b2..20f183213cbcb 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -130,7 +130,7 @@ int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
return err;
}
- if (!dp->bridge_dev)
+ if (!dp->bridge)
dsa_port_set_state_now(dp, BR_STATE_FORWARDING, false);
if (dp->pl)
@@ -158,7 +158,7 @@ void dsa_port_disable_rt(struct dsa_port *dp)
if (dp->pl)
phylink_stop(dp->pl);
- if (!dp->bridge_dev)
+ if (!dp->bridge)
dsa_port_set_state_now(dp, BR_STATE_DISABLED, false);
if (ds->ops->port_disable)
@@ -221,7 +221,7 @@ static int dsa_port_switchdev_sync_attrs(struct dsa_port *dp,
struct netlink_ext_ack *extack)
{
struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
- struct net_device *br = dp->bridge_dev;
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
int err;
err = dsa_port_inherit_brport_flags(dp, extack);
@@ -270,52 +270,55 @@ static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
*/
}
-static void dsa_port_bridge_tx_fwd_unoffload(struct dsa_port *dp,
- struct net_device *bridge_dev)
+static int dsa_port_bridge_create(struct dsa_port *dp,
+ struct net_device *br,
+ struct netlink_ext_ack *extack)
{
- int bridge_num = dp->bridge_num;
struct dsa_switch *ds = dp->ds;
+ struct dsa_bridge *bridge;
- /* No bridge TX forwarding offload => do nothing */
- if (!ds->ops->port_bridge_tx_fwd_unoffload || dp->bridge_num == -1)
- return;
+ bridge = dsa_tree_bridge_find(ds->dst, br);
+ if (bridge) {
+ refcount_inc(&bridge->refcount);
+ dp->bridge = bridge;
+ return 0;
+ }
- dp->bridge_num = -1;
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
- dsa_bridge_num_put(bridge_dev, bridge_num);
+ refcount_set(&bridge->refcount, 1);
- /* Notify the chips only once the offload has been deactivated, so
- * that they can update their configuration accordingly.
- */
- ds->ops->port_bridge_tx_fwd_unoffload(ds, dp->index, bridge_dev,
- bridge_num);
+ bridge->dev = br;
+
+ bridge->num = dsa_bridge_num_get(br, ds->max_num_bridges);
+ if (ds->max_num_bridges && !bridge->num) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Range of offloadable bridges exceeded");
+ kfree(bridge);
+ return -EOPNOTSUPP;
+ }
+
+ dp->bridge = bridge;
+
+ return 0;
}
-static bool dsa_port_bridge_tx_fwd_offload(struct dsa_port *dp,
- struct net_device *bridge_dev)
+static void dsa_port_bridge_destroy(struct dsa_port *dp,
+ const struct net_device *br)
{
- struct dsa_switch *ds = dp->ds;
- int bridge_num, err;
+ struct dsa_bridge *bridge = dp->bridge;
- if (!ds->ops->port_bridge_tx_fwd_offload)
- return false;
+ dp->bridge = NULL;
- bridge_num = dsa_bridge_num_get(bridge_dev,
- ds->num_fwd_offloading_bridges);
- if (bridge_num < 0)
- return false;
-
- dp->bridge_num = bridge_num;
+ if (!refcount_dec_and_test(&bridge->refcount))
+ return;
- /* Notify the driver */
- err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge_dev,
- bridge_num);
- if (err) {
- dsa_port_bridge_tx_fwd_unoffload(dp, bridge_dev);
- return false;
- }
+ if (bridge->num)
+ dsa_bridge_num_put(br, bridge->num);
- return true;
+ kfree(bridge);
}
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
@@ -325,30 +328,32 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
.tree_index = dp->ds->dst->index,
.sw_index = dp->ds->index,
.port = dp->index,
- .br = br,
};
struct net_device *dev = dp->slave;
struct net_device *brport_dev;
- bool tx_fwd_offload;
int err;
/* Here the interface is already bridged. Reflect the current
* configuration so that drivers can program their chips accordingly.
*/
- dp->bridge_dev = br;
+ err = dsa_port_bridge_create(dp, br, extack);
+ if (err)
+ return err;
brport_dev = dsa_port_to_bridge_port(dp);
+ info.bridge = *dp->bridge;
err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
if (err)
goto out_rollback;
- tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br);
+ /* Drivers which support bridge TX forwarding should set this */
+ dp->bridge->tx_fwd_offload = info.tx_fwd_offload;
err = switchdev_bridge_port_offload(brport_dev, dev, dp,
&dsa_slave_switchdev_notifier,
&dsa_slave_switchdev_blocking_notifier,
- tx_fwd_offload, extack);
+ dp->bridge->tx_fwd_offload, extack);
if (err)
goto out_rollback_unbridge;
@@ -365,7 +370,7 @@ out_rollback_unoffload:
out_rollback_unbridge:
dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
out_rollback:
- dp->bridge_dev = NULL;
+ dsa_port_bridge_destroy(dp, br);
return err;
}
@@ -390,16 +395,14 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
.tree_index = dp->ds->dst->index,
.sw_index = dp->ds->index,
.port = dp->index,
- .br = br,
+ .bridge = *dp->bridge,
};
int err;
/* Here the port is already unbridged. Reflect the current configuration
* so that drivers can program their chips accordingly.
*/
- dp->bridge_dev = NULL;
-
- dsa_port_bridge_tx_fwd_unoffload(dp, br);
+ dsa_port_bridge_destroy(dp, br);
err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
if (err)
@@ -477,12 +480,15 @@ err_lag_join:
void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag)
{
- if (dp->bridge_dev)
- dsa_port_pre_bridge_leave(dp, dp->bridge_dev);
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
+
+ if (br)
+ dsa_port_pre_bridge_leave(dp, br);
}
void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
{
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
struct dsa_notifier_lag_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
@@ -496,8 +502,8 @@ void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
/* Port might have been part of a LAG that in turn was
* attached to a bridge.
*/
- if (dp->bridge_dev)
- dsa_port_bridge_leave(dp, dp->bridge_dev);
+ if (br)
+ dsa_port_bridge_leave(dp, br);
dp->lag_tx_enabled = false;
dp->lag_dev = NULL;
@@ -526,8 +532,8 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
* as long as we have 8021q uppers.
*/
if (vlan_filtering && dsa_port_is_user(dp)) {
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
struct net_device *upper_dev, *slave = dp->slave;
- struct net_device *br = dp->bridge_dev;
struct list_head *iter;
netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
@@ -561,17 +567,15 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
* different setting than what is being requested.
*/
dsa_switch_for_each_port(other_dp, ds) {
- struct net_device *other_bridge;
+ struct net_device *other_br = dsa_port_bridge_dev_get(other_dp);
- other_bridge = other_dp->bridge_dev;
- if (!other_bridge)
- continue;
/* If it's the same bridge, it also has same
* vlan_filtering setting => no need to check
*/
- if (other_bridge == dp->bridge_dev)
+ if (!other_br || other_br == dsa_port_bridge_dev_get(dp))
continue;
- if (br_vlan_enabled(other_bridge) != vlan_filtering) {
+
+ if (br_vlan_enabled(other_br) != vlan_filtering) {
NL_SET_ERR_MSG_MOD(extack,
"VLAN filtering is a global setting");
return false;
@@ -655,13 +659,13 @@ restore:
*/
bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
{
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
struct dsa_switch *ds = dp->ds;
- if (!dp->bridge_dev)
+ if (!br)
return false;
- return (!ds->configure_vlan_while_not_filtering &&
- !br_vlan_enabled(dp->bridge_dev));
+ return !ds->configure_vlan_while_not_filtering && !br_vlan_enabled(br);
}
int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)