Commit c520bc69 authored by Petr Machata's avatar Petr Machata Committed by David S. Miller

mlxsw: Respin SPAN on switchdev events

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: default avatarPetr Machata <petrm@mellanox.com>
Signed-off-by: default avatarIdo Schimmel <idosch@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cda880de
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/netlink.h> #include <linux/netlink.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include "spectrum_span.h"
#include "spectrum_router.h" #include "spectrum_router.h"
#include "spectrum_switchdev.h" #include "spectrum_switchdev.h"
#include "spectrum.h" #include "spectrum.h"
...@@ -923,6 +924,9 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, ...@@ -923,6 +924,9 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
break; break;
} }
if (switchdev_trans_ph_commit(trans))
mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
return err; return err;
} }
...@@ -1647,18 +1651,57 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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, static int mlxsw_sp_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj, const struct switchdev_obj *obj,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
const struct switchdev_obj_port_vlan *vlan;
int err = 0; int err = 0;
switch (obj->id) { switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN: case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
SWITCHDEV_OBJ_PORT_VLAN(obj), err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans);
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; break;
case SWITCHDEV_OBJ_ID_PORT_MDB: case SWITCHDEV_OBJ_ID_PORT_MDB:
err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
...@@ -1809,6 +1852,8 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev, ...@@ -1809,6 +1852,8 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
break; break;
} }
mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
return err; return err;
} }
...@@ -2236,8 +2281,16 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work) ...@@ -2236,8 +2281,16 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work)
fdb_info = &switchdev_work->fdb_info; fdb_info = &switchdev_work->fdb_info;
mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
break; 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: out:
rtnl_unlock(); rtnl_unlock();
kfree(switchdev_work->fdb_info.addr); kfree(switchdev_work->fdb_info.addr);
...@@ -2266,7 +2319,9 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused, ...@@ -2266,7 +2319,9 @@ static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
switch (event) { switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ 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, memcpy(&switchdev_work->fdb_info, ptr,
sizeof(switchdev_work->fdb_info)); sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment