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: Petr Machata <petrm@mellanox.com> Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
cda880de93
commit
c520bc6986
@ -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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
|||||||
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)
|
|||||||
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,
|
|||||||
|
|
||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user