net: dsa: create a dsa_lag structure
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 23 Feb 2022 14:00:49 +0000 (16:00 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 25 Feb 2022 05:31:43 +0000 (21:31 -0800)
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@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/qca8k.c
include/net/dsa.h
net/dsa/dsa2.c
net/dsa/dsa_priv.h
net/dsa/port.c
net/dsa/slave.c
net/dsa/switch.c
net/dsa/tag_dsa.c

index 8868110..1b9a20b 100644 (file)
@@ -1625,7 +1625,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
@@ -1634,7 +1634,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;
                }
        }
 
@@ -1672,7 +1672,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.
@@ -6176,21 +6176,20 @@ out:
 }
 
 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++;
 
@@ -6210,8 +6209,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;
@@ -6219,13 +6217,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);
@@ -6269,9 +6267,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;
@@ -6281,7 +6279,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);
@@ -6294,12 +6292,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++;
                }
@@ -6308,7 +6306,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;
 
@@ -6330,14 +6328,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;
 }
@@ -6354,17 +6352,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);
 
@@ -6372,7 +6370,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;
 
@@ -6387,13 +6385,13 @@ err_unlock:
 }
 
 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;
@@ -6412,18 +6410,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;
 
@@ -6435,13 +6433,13 @@ unlock:
 }
 
 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;
index 9ffd549..6d48388 100644 (file)
@@ -677,20 +677,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;
 }
index ed55e93..6844106 100644 (file)
@@ -2646,18 +2646,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++;
 
@@ -2675,16 +2673,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:
@@ -2701,7 +2697,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;
                }
@@ -2726,14 +2722,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);
@@ -2795,27 +2791,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
index 3ae93ad..81ed349 100644 (file)
@@ -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;
@@ -643,14 +651,30 @@ static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp)
                return dp->vlan_filtering;
 }
 
+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;
+}
+
 static inline
 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;
 
@@ -968,10 +992,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
@@ -1043,10 +1067,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
index 4915abe..030d5f2 100644 (file)
@@ -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)
 {
index 0293a74..8612ff8 100644 (file)
@@ -76,7 +76,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;
 
@@ -487,8 +487,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,
index 0b42b36..338467c 100644 (file)
@@ -429,7 +429,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
@@ -447,6 +447,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)
@@ -454,15 +493,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;
@@ -480,8 +520,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;
 }
 
@@ -499,11 +539,10 @@ 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,
        };
        int err;
 
-       if (!dp->lag_dev)
+       if (!dp->lag)
                return;
 
        /* Port might have been part of a LAG that in turn was
@@ -512,16 +551,15 @@ 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;
+       info.lag = *dp->lag;
+
+       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() */
index f61e6b7..e31c771 100644 (file)
@@ -2134,7 +2134,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;
 
@@ -2163,7 +2163,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;
 
index c71bade..0bb3987 100644 (file)
@@ -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;
 }
index 26435bc..c8b4bbd 100644 (file)
@@ -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);