[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20220210125201.2859463-7-vladimir.oltean@nxp.com>
Date: Thu, 10 Feb 2022 14:51:55 +0200
From: Vladimir Oltean <vladimir.oltean@....com>
To: netdev@...r.kernel.org
Cc: "David S. Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>, Andrew Lunn <andrew@...n.ch>,
Vivien Didelot <vivien.didelot@...il.com>,
Florian Fainelli <f.fainelli@...il.com>,
Ansuel Smith <ansuelsmth@...il.com>,
Tobias Waldekranz <tobias@...dekranz.com>,
DENG Qingfang <dqfext@...il.com>,
Claudiu Manoil <claudiu.manoil@....com>,
Alexandre Belloni <alexandre.belloni@...tlin.com>,
UNGLinuxDriver@...rochip.com, Jiri Pirko <jiri@...nulli.us>,
Ivan Vecera <ivecera@...hat.com>
Subject: [PATCH v2 net-next 06/12] net: dsa: create a dsa_lag structure
The main purpose of this change is to create a data structure for a LAG
as seen by DSA. This is similar to what we have for bridging - we pass a
copy of this structure by value to ->port_lag_join and ->port_lag_leave.
For now we keep the lag_dev, id and a reference count in it. Future
patches will add a list of FDB entries for the LAG (these also need to
be refcounted to work properly).
The LAG structure is created using dsa_port_lag_create() and destroyed
using dsa_port_lag_destroy(), just like we have for bridging.
Because now, the dsa_lag itself is refcounted, we can simplify
dsa_lag_map() and dsa_lag_unmap(). These functions need to keep a LAG in
the dst->lags array only as long as at least one port uses it. The
refcounting logic inside those functions can be removed now - they are
called only when we should perform the operation.
dsa_lag_dev() is renamed to dsa_lag_by_id() and now returns the dsa_lag
structure instead of the lag_dev net_device.
dsa_lag_foreach_port() now takes the dsa_lag structure as argument.
dst->lags holds an array of dsa_lag structures.
dsa_lag_map() now also saves the dsa_lag->id value, so that linear
walking of dst->lags in drivers using dsa_lag_id() is no longer
necessary. They can just look at lag.id.
dsa_port_lag_id_get() is a helper, similar to dsa_port_bridge_num_get(),
which can be used by drivers to get the LAG ID assigned by DSA to a
given port.
Signed-off-by: Vladimir Oltean <vladimir.oltean@....com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 60 +++++++++++++++----------------
drivers/net/dsa/ocelot/felix.c | 8 ++---
drivers/net/dsa/qca8k.c | 37 +++++++++----------
include/net/dsa.h | 50 +++++++++++++++++++-------
net/dsa/dsa2.c | 41 +++++++++++----------
net/dsa/dsa_priv.h | 8 +++--
net/dsa/port.c | 61 +++++++++++++++++++++++++-------
net/dsa/slave.c | 4 +--
net/dsa/switch.c | 8 ++---
net/dsa/tag_dsa.c | 4 ++-
10 files changed, 172 insertions(+), 109 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index be4f185442bd..c1067352b695 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1606,7 +1606,7 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
ds = dsa_switch_find(dst->index, dev);
dp = ds ? dsa_to_port(ds, port) : NULL;
- if (dp && dp->lag_dev) {
+ if (dp && dp->lag) {
/* As the PVT is used to limit flooding of
* FORWARD frames, which use the LAG ID as the
* source port, we must translate dev/port to
@@ -1615,7 +1615,7 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
* (zero-based).
*/
dev = MV88E6XXX_G2_PVT_ADDR_DEV_TRUNK;
- port = dsa_lag_id(dst, dp->lag_dev) - 1;
+ port = dsa_port_lag_id_get(dp) - 1;
}
}
@@ -1653,7 +1653,7 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
struct mv88e6xxx_chip *chip = ds->priv;
int err;
- if (dsa_to_port(ds, port)->lag_dev)
+ if (dsa_to_port(ds, port)->lag)
/* Hardware is incapable of fast-aging a LAG through a
* regular ATU move operation. Until we have something
* more fancy in place this is a no-op.
@@ -6115,21 +6115,20 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
}
static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
- struct net_device *lag_dev,
+ struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct dsa_port *dp;
- int id, members = 0;
+ int members = 0;
if (!mv88e6xxx_has_lag(chip))
return false;
- id = dsa_lag_id(ds->dst, lag_dev);
- if (id <= 0 || id > ds->num_lag_ids)
+ if (!lag.id)
return false;
- dsa_lag_foreach_port(dp, ds->dst, lag_dev)
+ dsa_lag_foreach_port(dp, ds->dst, &lag)
/* Includes the port joining the LAG */
members++;
@@ -6149,8 +6148,7 @@ static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
return true;
}
-static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds,
- struct net_device *lag_dev)
+static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct dsa_lag lag)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct dsa_port *dp;
@@ -6158,13 +6156,13 @@ static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds,
int id;
/* DSA LAG IDs are one-based, hardware is zero-based */
- id = dsa_lag_id(ds->dst, lag_dev) - 1;
+ id = lag.id - 1;
/* Build the map of all ports to distribute flows destined for
* this LAG. This can be either a local user port, or a DSA
* port if the LAG port is on a remote chip.
*/
- dsa_lag_foreach_port(dp, ds->dst, lag_dev)
+ dsa_lag_foreach_port(dp, ds->dst, &lag)
map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index));
return mv88e6xxx_g2_trunk_mapping_write(chip, id, map);
@@ -6208,9 +6206,9 @@ static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port,
static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
{
struct mv88e6xxx_chip *chip = ds->priv;
- struct net_device *lag_dev;
unsigned int id, num_tx;
struct dsa_port *dp;
+ struct dsa_lag *lag;
int i, err, nth;
u16 mask[8];
u16 ivec;
@@ -6220,7 +6218,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
/* Disable all masks for ports that _are_ members of a LAG. */
dsa_switch_for_each_port(dp, ds) {
- if (!dp->lag_dev)
+ if (!dp->lag)
continue;
ivec &= ~BIT(dp->index);
@@ -6233,12 +6231,12 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
* are in the Tx set.
*/
dsa_lags_foreach_id(id, ds->dst) {
- lag_dev = dsa_lag_dev(ds->dst, id);
- if (!lag_dev)
+ lag = dsa_lag_by_id(ds->dst, id);
+ if (!lag)
continue;
num_tx = 0;
- dsa_lag_foreach_port(dp, ds->dst, lag_dev) {
+ dsa_lag_foreach_port(dp, ds->dst, lag) {
if (dp->lag_tx_enabled)
num_tx++;
}
@@ -6247,7 +6245,7 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
continue;
nth = 0;
- dsa_lag_foreach_port(dp, ds->dst, lag_dev) {
+ dsa_lag_foreach_port(dp, ds->dst, lag) {
if (!dp->lag_tx_enabled)
continue;
@@ -6269,14 +6267,14 @@ static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
}
static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds,
- struct net_device *lag_dev)
+ struct dsa_lag lag)
{
int err;
err = mv88e6xxx_lag_sync_masks(ds);
if (!err)
- err = mv88e6xxx_lag_sync_map(ds, lag_dev);
+ err = mv88e6xxx_lag_sync_map(ds, lag);
return err;
}
@@ -6293,17 +6291,17 @@ static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port)
}
static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
- struct net_device *lag_dev,
+ struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err, id;
- if (!mv88e6xxx_lag_can_offload(ds, lag_dev, info))
+ if (!mv88e6xxx_lag_can_offload(ds, lag, info))
return -EOPNOTSUPP;
/* DSA LAG IDs are one-based */
- id = dsa_lag_id(ds->dst, lag_dev) - 1;
+ id = lag.id - 1;
mv88e6xxx_reg_lock(chip);
@@ -6311,7 +6309,7 @@ static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
if (err)
goto err_unlock;
- err = mv88e6xxx_lag_sync_masks_map(ds, lag_dev);
+ err = mv88e6xxx_lag_sync_masks_map(ds, lag);
if (err)
goto err_clear_trunk;
@@ -6326,13 +6324,13 @@ static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
}
static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port,
- struct net_device *lag_dev)
+ struct dsa_lag lag)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err_sync, err_trunk;
mv88e6xxx_reg_lock(chip);
- err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag_dev);
+ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0);
mv88e6xxx_reg_unlock(chip);
return err_sync ? : err_trunk;
@@ -6351,18 +6349,18 @@ static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index,
}
static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag_dev,
+ int port, struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
- if (!mv88e6xxx_lag_can_offload(ds, lag_dev, info))
+ if (!mv88e6xxx_lag_can_offload(ds, lag, info))
return -EOPNOTSUPP;
mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_lag_sync_masks_map(ds, lag_dev);
+ err = mv88e6xxx_lag_sync_masks_map(ds, lag);
if (err)
goto unlock;
@@ -6374,13 +6372,13 @@ static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index,
}
static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag_dev)
+ int port, struct dsa_lag lag)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err_sync, err_pvt;
mv88e6xxx_reg_lock(chip);
- err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag_dev);
+ err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port);
mv88e6xxx_reg_unlock(chip);
return err_sync ? : err_pvt;
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 9957772201d5..4624d51a9b0a 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -737,20 +737,20 @@ static void felix_bridge_leave(struct dsa_switch *ds, int port,
}
static int felix_lag_join(struct dsa_switch *ds, int port,
- struct net_device *bond,
+ struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
struct ocelot *ocelot = ds->priv;
- return ocelot_port_lag_join(ocelot, port, bond, info);
+ return ocelot_port_lag_join(ocelot, port, lag.dev, info);
}
static int felix_lag_leave(struct dsa_switch *ds, int port,
- struct net_device *bond)
+ struct dsa_lag lag)
{
struct ocelot *ocelot = ds->priv;
- ocelot_port_lag_leave(ocelot, port, bond);
+ ocelot_port_lag_leave(ocelot, port, lag.dev);
return 0;
}
diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c
index e1e045ceec63..58a49e625c46 100644
--- a/drivers/net/dsa/qca8k.c
+++ b/drivers/net/dsa/qca8k.c
@@ -2779,18 +2779,16 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,
}
static bool
-qca8k_lag_can_offload(struct dsa_switch *ds,
- struct net_device *lag_dev,
+qca8k_lag_can_offload(struct dsa_switch *ds, struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
struct dsa_port *dp;
- int id, members = 0;
+ int members = 0;
- id = dsa_lag_id(ds->dst, lag_dev);
- if (id <= 0 || id > ds->num_lag_ids)
+ if (!lag.id)
return false;
- dsa_lag_foreach_port(dp, ds->dst, lag_dev)
+ dsa_lag_foreach_port(dp, ds->dst, &lag)
/* Includes the port joining the LAG */
members++;
@@ -2808,16 +2806,14 @@ qca8k_lag_can_offload(struct dsa_switch *ds,
}
static int
-qca8k_lag_setup_hash(struct dsa_switch *ds,
- struct net_device *lag_dev,
+qca8k_lag_setup_hash(struct dsa_switch *ds, struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
+ struct net_device *lag_dev = lag.dev;
struct qca8k_priv *priv = ds->priv;
bool unique_lag = true;
+ unsigned int i;
u32 hash = 0;
- int i, id;
-
- id = dsa_lag_id(ds->dst, lag_dev);
switch (info->hash_type) {
case NETDEV_LAG_HASH_L23:
@@ -2834,7 +2830,7 @@ qca8k_lag_setup_hash(struct dsa_switch *ds,
/* Check if we are the unique configured LAG */
dsa_lags_foreach_id(i, ds->dst)
- if (i != id && dsa_lag_dev(ds->dst, i)) {
+ if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
unique_lag = false;
break;
}
@@ -2859,14 +2855,14 @@ qca8k_lag_setup_hash(struct dsa_switch *ds,
static int
qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
- struct net_device *lag_dev, bool delete)
+ struct dsa_lag lag, bool delete)
{
struct qca8k_priv *priv = ds->priv;
int ret, id, i;
u32 val;
/* DSA LAG IDs are one-based, hardware is zero-based */
- id = dsa_lag_id(ds->dst, lag_dev) - 1;
+ id = lag.id - 1;
/* Read current port member */
ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
@@ -2928,27 +2924,26 @@ qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
}
static int
-qca8k_port_lag_join(struct dsa_switch *ds, int port,
- struct net_device *lag_dev,
+qca8k_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
struct netdev_lag_upper_info *info)
{
int ret;
- if (!qca8k_lag_can_offload(ds, lag_dev, info))
+ if (!qca8k_lag_can_offload(ds, lag, info))
return -EOPNOTSUPP;
- ret = qca8k_lag_setup_hash(ds, lag_dev, info);
+ ret = qca8k_lag_setup_hash(ds, lag, info);
if (ret)
return ret;
- return qca8k_lag_refresh_portmap(ds, port, lag_dev, false);
+ return qca8k_lag_refresh_portmap(ds, port, lag, false);
}
static int
qca8k_port_lag_leave(struct dsa_switch *ds, int port,
- struct net_device *lag_dev)
+ struct dsa_lag lag)
{
- return qca8k_lag_refresh_portmap(ds, port, lag_dev, true);
+ return qca8k_lag_refresh_portmap(ds, port, lag, true);
}
static void
diff --git a/include/net/dsa.h b/include/net/dsa.h
index d0224f648777..1f3a5ac3fb96 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -116,6 +116,12 @@ struct dsa_netdevice_ops {
#define MODULE_ALIAS_DSA_TAG_DRIVER(__proto) \
MODULE_ALIAS(DSA_TAG_DRIVER_ALIAS __stringify(__proto##_VALUE))
+struct dsa_lag {
+ struct net_device *dev;
+ unsigned int id;
+ refcount_t refcount;
+};
+
struct dsa_switch_tree {
struct list_head list;
@@ -134,7 +140,7 @@ struct dsa_switch_tree {
/* Maps offloaded LAG netdevs to a zero-based linear ID for
* drivers that need it.
*/
- struct net_device **lags;
+ struct dsa_lag **lags;
/* Tagging protocol operations */
const struct dsa_device_ops *tag_ops;
@@ -170,14 +176,14 @@ struct dsa_switch_tree {
#define dsa_lag_foreach_port(_dp, _dst, _lag) \
list_for_each_entry((_dp), &(_dst)->ports, list) \
- if ((_dp)->lag_dev == (_lag))
+ if (dsa_port_offloads_lag((_dp), (_lag)))
#define dsa_hsr_foreach_port(_dp, _ds, _hsr) \
list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
if ((_dp)->ds == (_ds) && (_dp)->hsr_dev == (_hsr))
-static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
- unsigned int id)
+static inline struct dsa_lag *dsa_lag_by_id(struct dsa_switch_tree *dst,
+ unsigned int id)
{
/* DSA LAG IDs are one-based, dst->lags is zero-based */
return dst->lags[id - 1];
@@ -189,8 +195,10 @@ static inline int dsa_lag_id(struct dsa_switch_tree *dst,
unsigned int id;
dsa_lags_foreach_id(id, dst) {
- if (dsa_lag_dev(dst, id) == lag_dev)
- return id;
+ struct dsa_lag *lag = dsa_lag_by_id(dst, id);
+
+ if (lag->dev == lag_dev)
+ return lag->id;
}
return -ENODEV;
@@ -293,7 +301,7 @@ struct dsa_port {
struct devlink_port devlink_port;
struct phylink *pl;
struct phylink_config pl_config;
- struct net_device *lag_dev;
+ struct dsa_lag *lag;
struct net_device *hsr_dev;
struct list_head list;
@@ -644,8 +652,8 @@ struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp)
if (!dp->bridge)
return NULL;
- if (dp->lag_dev)
- return dp->lag_dev;
+ if (dp->lag)
+ return dp->lag->dev;
else if (dp->hsr_dev)
return dp->hsr_dev;
@@ -722,6 +730,22 @@ dsa_tree_offloads_bridge_dev(struct dsa_switch_tree *dst,
return false;
}
+static inline unsigned int dsa_port_lag_id_get(struct dsa_port *dp)
+{
+ return dp->lag ? dp->lag->id : 0;
+}
+
+static inline struct net_device *dsa_port_lag_dev_get(struct dsa_port *dp)
+{
+ return dp->lag ? dp->lag->dev : NULL;
+}
+
+static inline bool dsa_port_offloads_lag(struct dsa_port *dp,
+ const struct dsa_lag *lag)
+{
+ return dsa_port_lag_dev_get(dp) == lag->dev;
+}
+
typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
bool is_static, void *data);
struct dsa_switch_ops {
@@ -960,10 +984,10 @@ struct dsa_switch_ops {
int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index,
int port);
int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag_dev,
+ int port, struct dsa_lag lag,
struct netdev_lag_upper_info *info);
int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index,
- int port, struct net_device *lag_dev);
+ int port, struct dsa_lag lag);
/*
* PTP functionality
@@ -1035,10 +1059,10 @@ struct dsa_switch_ops {
*/
int (*port_lag_change)(struct dsa_switch *ds, int port);
int (*port_lag_join)(struct dsa_switch *ds, int port,
- struct net_device *lag_dev,
+ struct dsa_lag lag,
struct netdev_lag_upper_info *info);
int (*port_lag_leave)(struct dsa_switch *ds, int port,
- struct net_device *lag_dev);
+ struct dsa_lag lag);
/*
* HSR integration
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index 5670b441419f..74a2ad1a4047 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -72,27 +72,24 @@ int dsa_broadcast(unsigned long e, void *v)
}
/**
- * dsa_lag_map() - Map LAG netdev to a linear LAG ID
+ * dsa_lag_map() - Map LAG structure to a linear LAG array
* @dst: Tree in which to record the mapping.
- * @lag_dev: Netdev that is to be mapped to an ID.
+ * @lag: LAG structure that is to be mapped to the tree's array.
*
- * dsa_lag_id/dsa_lag_dev can then be used to translate between the
+ * dsa_lag_id/dsa_lag_by_id can then be used to translate between the
* two spaces. The size of the mapping space is determined by the
* driver by setting ds->num_lag_ids. It is perfectly legal to leave
* it unset if it is not needed, in which case these functions become
* no-ops.
*/
-void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag_dev)
+void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag)
{
unsigned int id;
- if (dsa_lag_id(dst, lag_dev) > 0)
- /* Already mapped */
- return;
-
for (id = 1; id <= dst->lags_len; id++) {
- if (!dsa_lag_dev(dst, id)) {
- dst->lags[id - 1] = lag_dev;
+ if (!dsa_lag_by_id(dst, id)) {
+ dst->lags[id - 1] = lag;
+ lag->id = id;
return;
}
}
@@ -108,28 +105,36 @@ void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag_dev)
/**
* dsa_lag_unmap() - Remove a LAG ID mapping
* @dst: Tree in which the mapping is recorded.
- * @lag_dev: Netdev that was mapped.
+ * @lag: LAG structure that was mapped.
*
* As there may be multiple users of the mapping, it is only removed
* if there are no other references to it.
*/
-void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag_dev)
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag)
{
- struct dsa_port *dp;
unsigned int id;
- dsa_lag_foreach_port(dp, dst, lag_dev)
- /* There are remaining users of this mapping */
- return;
-
dsa_lags_foreach_id(id, dst) {
- if (dsa_lag_dev(dst, id) == lag_dev) {
+ if (dsa_lag_by_id(dst, id) == lag) {
dst->lags[id - 1] = NULL;
+ lag->id = 0;
break;
}
}
}
+struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst,
+ const struct net_device *lag_dev)
+{
+ struct dsa_port *dp;
+
+ list_for_each_entry(dp, &dst->ports, list)
+ if (dsa_port_lag_dev_get(dp) == lag_dev)
+ return dp->lag;
+
+ return NULL;
+}
+
struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
const struct net_device *br)
{
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 9bc6dd4a5855..0853eed44fc9 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -74,7 +74,7 @@ struct dsa_notifier_mdb_info {
/* DSA_NOTIFIER_LAG_* */
struct dsa_notifier_lag_info {
- struct net_device *lag_dev;
+ struct dsa_lag lag;
int sw_index;
int port;
@@ -481,8 +481,10 @@ int dsa_switch_register_notifier(struct dsa_switch *ds);
void dsa_switch_unregister_notifier(struct dsa_switch *ds);
/* dsa2.c */
-void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag_dev);
-void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag_dev);
+void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag);
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag);
+struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst,
+ const struct net_device *lag_dev);
int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v);
int dsa_broadcast(unsigned long e, void *v);
int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
diff --git a/net/dsa/port.c b/net/dsa/port.c
index bb42ac7ed53f..e3e5f6de11c8 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -422,7 +422,7 @@ int dsa_port_lag_change(struct dsa_port *dp,
};
bool tx_enabled;
- if (!dp->lag_dev)
+ if (!dp->lag)
return 0;
/* On statically configured aggregates (e.g. loadbalance
@@ -440,6 +440,45 @@ int dsa_port_lag_change(struct dsa_port *dp,
return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
}
+static int dsa_port_lag_create(struct dsa_port *dp,
+ struct net_device *lag_dev)
+{
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_lag *lag;
+
+ lag = dsa_tree_lag_find(ds->dst, lag_dev);
+ if (lag) {
+ refcount_inc(&lag->refcount);
+ dp->lag = lag;
+ return 0;
+ }
+
+ lag = kzalloc(sizeof(*lag), GFP_KERNEL);
+ if (!lag)
+ return -ENOMEM;
+
+ refcount_set(&lag->refcount, 1);
+ lag->dev = lag_dev;
+ dsa_lag_map(ds->dst, lag);
+ dp->lag = lag;
+
+ return 0;
+}
+
+static void dsa_port_lag_destroy(struct dsa_port *dp)
+{
+ struct dsa_lag *lag = dp->lag;
+
+ dp->lag = NULL;
+ dp->lag_tx_enabled = false;
+
+ if (!refcount_dec_and_test(&lag->refcount))
+ return;
+
+ dsa_lag_unmap(dp->ds->dst, lag);
+ kfree(lag);
+}
+
int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
struct netdev_lag_upper_info *uinfo,
struct netlink_ext_ack *extack)
@@ -447,15 +486,16 @@ int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
struct dsa_notifier_lag_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
- .lag_dev = lag_dev,
.info = uinfo,
};
struct net_device *bridge_dev;
int err;
- dsa_lag_map(dp->ds->dst, lag_dev);
- dp->lag_dev = lag_dev;
+ err = dsa_port_lag_create(dp, lag_dev);
+ if (err)
+ goto err_lag_create;
+ info.lag = *dp->lag;
err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
if (err)
goto err_lag_join;
@@ -473,8 +513,8 @@ int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
err_bridge_join:
dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
err_lag_join:
- dp->lag_dev = NULL;
- dsa_lag_unmap(dp->ds->dst, lag_dev);
+ dsa_port_lag_destroy(dp);
+err_lag_create:
return err;
}
@@ -492,11 +532,11 @@ void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev)
struct dsa_notifier_lag_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
- .lag_dev = lag_dev,
+ .lag = *dp->lag,
};
int err;
- if (!dp->lag_dev)
+ if (!dp->lag)
return;
/* Port might have been part of a LAG that in turn was
@@ -505,16 +545,13 @@ void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev)
if (br)
dsa_port_bridge_leave(dp, br);
- dp->lag_tx_enabled = false;
- dp->lag_dev = NULL;
+ dsa_port_lag_destroy(dp);
err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
if (err)
dev_err(dp->ds->dev,
"port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n",
dp->index, ERR_PTR(err));
-
- dsa_lag_unmap(dp->ds->dst, lag_dev);
}
/* Must be called under rcu_read_lock() */
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 2b5b0f294233..974dc9f025e1 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -2172,7 +2172,7 @@ dsa_slave_lag_changeupper(struct net_device *dev,
continue;
dp = dsa_slave_to_port(lower);
- if (!dp->lag_dev)
+ if (!dp->lag)
/* Software LAG */
continue;
@@ -2201,7 +2201,7 @@ dsa_slave_lag_prechangeupper(struct net_device *dev,
continue;
dp = dsa_slave_to_port(lower);
- if (!dp->lag_dev)
+ if (!dp->lag)
/* Software LAG */
continue;
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index d0d59f2fd445..4866b58649e4 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -468,12 +468,12 @@ 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_dev,
+ 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_dev,
+ info->port, info->lag,
info->info);
return -EOPNOTSUPP;
@@ -483,11 +483,11 @@ 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_dev);
+ 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_dev);
+ info->port, info->lag);
return -EOPNOTSUPP;
}
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
index 26435bc4a098..c8b4bbd46191 100644
--- a/net/dsa/tag_dsa.c
+++ b/net/dsa/tag_dsa.c
@@ -246,12 +246,14 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
if (trunk) {
struct dsa_port *cpu_dp = dev->dsa_ptr;
+ struct dsa_lag *lag;
/* The exact source port is not available in the tag,
* so we inject the frame directly on the upper
* team/bond.
*/
- skb->dev = dsa_lag_dev(cpu_dp->dst, source_port + 1);
+ lag = dsa_lag_by_id(cpu_dp->dst, source_port + 1);
+ skb->dev = lag ? lag->dev : NULL;
} else {
skb->dev = dsa_master_find_slave(dev, source_device,
source_port);
--
2.25.1
Powered by blists - more mailing lists