From: Petr Machata <pe...@mellanox.com>

Changes to switchdev artifact can make a SPAN entry offloadable or
unoffloadable. To that end:

- Listen to SWITCHDEV_FDB_*_TO_BRIDGE notifications in addition to
  the *_TO_DEVICE ones, to catch whatever activity is sent to the
  bridge (likely by mlxsw itself).

  On each FDB notification, respin SPAN to reconcile it with the FDB
  changes.

- Also respin on switchdev port attribute changes (which currently
  covers changes to STP state of ports) and port object additions and
  removals.

Signed-off-by: Petr Machata <pe...@mellanox.com>
Signed-off-by: Ido Schimmel <ido...@mellanox.com>
---
 .../ethernet/mellanox/mlxsw/spectrum_switchdev.c   | 63 ++++++++++++++++++++--
 1 file changed, 59 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c 
b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index db4aea0f8996..1af99fe5fd32 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -49,6 +49,7 @@
 #include <linux/netlink.h>
 #include <net/switchdev.h>
 
+#include "spectrum_span.h"
 #include "spectrum_router.h"
 #include "spectrum_switchdev.h"
 #include "spectrum.h"
@@ -923,6 +924,9 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
                break;
        }
 
+       if (switchdev_trans_ph_commit(trans))
+               mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
+
        return err;
 }
 
@@ -1647,18 +1651,57 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port 
*mlxsw_sp_port,
        }
 }
 
+struct mlxsw_sp_span_respin_work {
+       struct work_struct work;
+       struct mlxsw_sp *mlxsw_sp;
+};
+
+static void mlxsw_sp_span_respin_work(struct work_struct *work)
+{
+       struct mlxsw_sp_span_respin_work *respin_work =
+               container_of(work, struct mlxsw_sp_span_respin_work, work);
+
+       rtnl_lock();
+       mlxsw_sp_span_respin(respin_work->mlxsw_sp);
+       rtnl_unlock();
+       kfree(respin_work);
+}
+
+static void mlxsw_sp_span_respin_schedule(struct mlxsw_sp *mlxsw_sp)
+{
+       struct mlxsw_sp_span_respin_work *respin_work;
+
+       respin_work = kzalloc(sizeof(*respin_work), GFP_ATOMIC);
+       if (!respin_work)
+               return;
+
+       INIT_WORK(&respin_work->work, mlxsw_sp_span_respin_work);
+       respin_work->mlxsw_sp = mlxsw_sp;
+
+       mlxsw_core_schedule_work(&respin_work->work);
+}
+
 static int mlxsw_sp_port_obj_add(struct net_device *dev,
                                 const struct switchdev_obj *obj,
                                 struct switchdev_trans *trans)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+       const struct switchdev_obj_port_vlan *vlan;
        int err = 0;
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
-               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
-                                             SWITCHDEV_OBJ_PORT_VLAN(obj),
-                                             trans);
+               vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
+               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans);
+
+               if (switchdev_trans_ph_commit(trans)) {
+                       /* The event is emitted before the changes are actually
+                        * applied to the bridge. Therefore schedule the respin
+                        * call for later, so that the respin logic sees the
+                        * updated bridge state.
+                        */
+                       mlxsw_sp_span_respin_schedule(mlxsw_sp_port->mlxsw_sp);
+               }
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
                err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
@@ -1809,6 +1852,8 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
                break;
        }
 
+       mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
+
        return err;
 }
 
@@ -2236,8 +2281,16 @@ static void mlxsw_sp_switchdev_event_work(struct 
work_struct *work)
                fdb_info = &switchdev_work->fdb_info;
                mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
                break;
+       case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */
+       case SWITCHDEV_FDB_DEL_TO_BRIDGE:
+               /* These events are only used to potentially update an existing
+                * SPAN mirror.
+                */
+               break;
        }
 
+       mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
+
 out:
        rtnl_unlock();
        kfree(switchdev_work->fdb_info.addr);
@@ -2266,7 +2319,9 @@ static int mlxsw_sp_switchdev_event(struct notifier_block 
*unused,
 
        switch (event) {
        case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
-       case SWITCHDEV_FDB_DEL_TO_DEVICE:
+       case SWITCHDEV_FDB_DEL_TO_DEVICE: /* fall through */
+       case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */
+       case SWITCHDEV_FDB_DEL_TO_BRIDGE:
                memcpy(&switchdev_work->fdb_info, ptr,
                       sizeof(switchdev_work->fdb_info));
                switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
-- 
2.14.3

Reply via email to