Merge branch 'net-Remove-support-from-bridge-bypass-for-mlxsw-rocker-drivers'
Jiri Pirko says: =================== net: Remove support from bridge bypass for mlxsw/rocker drivers Currently setting bridge port attributes and adding FDBs are done via setting the SELF flag which implies unconsistent offloading model. This patch-set fixes this behavior by making the bridge and drivers which are using it to be totally in sync. This implies several changes: - Offloading bridge flags from the bridge code. - Sending notification about FDB add/del to the software bridge in a similiar way it is done for the hardware externally learned FDBs. By making the offloading model more consistent a cleanup is done in the drivers supporting it. This is done in order to remove un-needed logic related to dump operation which is redundant. First add missing functionality to bridge, then clean up the mlxsw/rocker drivers. v1->v2 - Move bridge-switchdev related stuff to br_switchdev.c as suggested by Nik =================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4cd31af775
@ -4175,12 +4175,6 @@ static const struct net_device_ops bond_netdev_ops = {
|
||||
.ndo_add_slave = bond_enslave,
|
||||
.ndo_del_slave = bond_release,
|
||||
.ndo_fix_features = bond_fix_features,
|
||||
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
|
||||
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
|
||||
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
|
||||
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||
.ndo_features_check = passthru_features_check,
|
||||
};
|
||||
|
||||
|
@ -1756,12 +1756,6 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
|
||||
.ndo_get_offload_stats = mlxsw_sp_port_get_offload_stats,
|
||||
.ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid,
|
||||
.ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid,
|
||||
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
|
||||
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
|
||||
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
|
||||
.ndo_get_phys_port_name = mlxsw_sp_port_get_phys_port_name,
|
||||
};
|
||||
|
||||
@ -3709,7 +3703,7 @@ struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev)
|
||||
return mlxsw_sp_port ? mlxsw_sp_port->mlxsw_sp : NULL;
|
||||
}
|
||||
|
||||
static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev)
|
||||
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev)
|
||||
{
|
||||
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||
|
||||
|
@ -361,6 +361,7 @@ struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev);
|
||||
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev);
|
||||
struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev);
|
||||
void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port);
|
||||
struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev);
|
||||
|
||||
/* spectrum_dcb.c */
|
||||
#ifdef CONFIG_MLXSW_SPECTRUM_DCB
|
||||
|
@ -102,8 +102,6 @@ struct mlxsw_sp_bridge_vlan {
|
||||
struct list_head list;
|
||||
struct list_head port_vlan_list;
|
||||
u16 vid;
|
||||
u8 egress_untagged:1,
|
||||
pvid:1;
|
||||
};
|
||||
|
||||
struct mlxsw_sp_bridge_ops {
|
||||
@ -456,6 +454,9 @@ static int mlxsw_sp_port_attr_get(struct net_device *dev,
|
||||
mlxsw_sp_port_bridge_flags_get(mlxsw_sp->bridge, attr->orig_dev,
|
||||
&attr->u.brport_flags);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
|
||||
attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -1000,8 +1001,6 @@ mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
goto err_port_vlan_bridge_join;
|
||||
|
||||
bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid);
|
||||
bridge_vlan->egress_untagged = is_untagged;
|
||||
bridge_vlan->pvid = is_pvid;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1142,44 +1141,40 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
const struct switchdev_obj_port_fdb *fdb,
|
||||
struct switchdev_trans *trans)
|
||||
mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct switchdev_notifier_fdb_info *fdb_info, bool adding)
|
||||
{
|
||||
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||
struct net_device *orig_dev = fdb->obj.orig_dev;
|
||||
struct net_device *orig_dev = fdb_info->info.dev;
|
||||
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
|
||||
struct mlxsw_sp_bridge_device *bridge_device;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
u16 fid_index, vid;
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
|
||||
if (WARN_ON(!bridge_port))
|
||||
if (!bridge_port)
|
||||
return -EINVAL;
|
||||
|
||||
bridge_device = bridge_port->bridge_device;
|
||||
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
|
||||
bridge_device,
|
||||
fdb->vid);
|
||||
fdb_info->vid);
|
||||
if (!mlxsw_sp_port_vlan)
|
||||
return 0;
|
||||
|
||||
fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
|
||||
vid = mlxsw_sp_port_vlan->vid;
|
||||
|
||||
if (!mlxsw_sp_port->lagged)
|
||||
if (!bridge_port->lagged)
|
||||
return mlxsw_sp_port_fdb_uc_op(mlxsw_sp,
|
||||
mlxsw_sp_port->local_port,
|
||||
fdb->addr, fid_index, true,
|
||||
false);
|
||||
bridge_port->system_port,
|
||||
fdb_info->addr, fid_index,
|
||||
adding, false);
|
||||
else
|
||||
return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp,
|
||||
mlxsw_sp_port->lag_id,
|
||||
fdb->addr, fid_index, vid,
|
||||
true, false);
|
||||
bridge_port->lag_id,
|
||||
fdb_info->addr, fid_index,
|
||||
vid, adding, false);
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
|
||||
@ -1349,11 +1344,6 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj),
|
||||
trans);
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj),
|
||||
trans);
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
||||
err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_MDB(obj),
|
||||
@ -1405,43 +1395,6 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
const struct switchdev_obj_port_fdb *fdb)
|
||||
{
|
||||
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||
struct net_device *orig_dev = fdb->obj.orig_dev;
|
||||
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
|
||||
struct mlxsw_sp_bridge_device *bridge_device;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
u16 fid_index, vid;
|
||||
|
||||
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
|
||||
if (WARN_ON(!bridge_port))
|
||||
return -EINVAL;
|
||||
|
||||
bridge_device = bridge_port->bridge_device;
|
||||
mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port,
|
||||
bridge_device,
|
||||
fdb->vid);
|
||||
if (!mlxsw_sp_port_vlan)
|
||||
return 0;
|
||||
|
||||
fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid);
|
||||
vid = mlxsw_sp_port_vlan->vid;
|
||||
|
||||
if (!mlxsw_sp_port->lagged)
|
||||
return mlxsw_sp_port_fdb_uc_op(mlxsw_sp,
|
||||
mlxsw_sp_port->local_port,
|
||||
fdb->addr, fid_index, false,
|
||||
false);
|
||||
else
|
||||
return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp,
|
||||
mlxsw_sp_port->lag_id,
|
||||
fdb->addr, fid_index, vid,
|
||||
false, false);
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
const struct switchdev_obj_port_mdb *mdb)
|
||||
{
|
||||
@ -1501,10 +1454,6 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
|
||||
err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj));
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj));
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
||||
err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_MDB(obj));
|
||||
@ -1534,161 +1483,11 @@ static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct switchdev_obj_port_fdb *fdb,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||
struct net_device *orig_dev = fdb->obj.orig_dev;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
u16 lag_id, fid_index;
|
||||
char mac[ETH_ALEN];
|
||||
int stored_err = 0;
|
||||
char *sfd_pl;
|
||||
u8 num_rec;
|
||||
int err;
|
||||
|
||||
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
|
||||
if (!bridge_port)
|
||||
return 0;
|
||||
|
||||
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
|
||||
if (!sfd_pl)
|
||||
return -ENOMEM;
|
||||
|
||||
mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
|
||||
do {
|
||||
struct mlxsw_sp_port *tmp;
|
||||
u8 local_port;
|
||||
int i;
|
||||
|
||||
mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
|
||||
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
|
||||
|
||||
/* Even in case of error, we have to run the dump to the end
|
||||
* so the session in firmware is finished.
|
||||
*/
|
||||
if (stored_err)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < num_rec; i++) {
|
||||
switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
|
||||
case MLXSW_REG_SFD_REC_TYPE_UNICAST:
|
||||
mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac,
|
||||
&fid_index,
|
||||
&local_port);
|
||||
if (bridge_port->lagged)
|
||||
continue;
|
||||
if (bridge_port->system_port != local_port)
|
||||
continue;
|
||||
if (bridge_port->bridge_device->vlan_enabled)
|
||||
fdb->vid = fid_index;
|
||||
else
|
||||
fdb->vid = 0;
|
||||
ether_addr_copy(fdb->addr, mac);
|
||||
fdb->ndm_state = NUD_REACHABLE;
|
||||
err = cb(&fdb->obj);
|
||||
if (err)
|
||||
stored_err = err;
|
||||
break;
|
||||
case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
|
||||
mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
|
||||
mac, &fid_index,
|
||||
&lag_id);
|
||||
if (!bridge_port->lagged)
|
||||
continue;
|
||||
if (bridge_port->lag_id != lag_id)
|
||||
continue;
|
||||
tmp = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
|
||||
if (tmp->local_port !=
|
||||
mlxsw_sp_port->local_port)
|
||||
continue;
|
||||
if (bridge_port->bridge_device->vlan_enabled)
|
||||
fdb->vid = fid_index;
|
||||
else
|
||||
fdb->vid = 0;
|
||||
ether_addr_copy(fdb->addr, mac);
|
||||
fdb->ndm_state = NUD_REACHABLE;
|
||||
err = cb(&fdb->obj);
|
||||
if (err)
|
||||
stored_err = err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
|
||||
|
||||
out:
|
||||
kfree(sfd_pl);
|
||||
return stored_err ? stored_err : err;
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
struct switchdev_obj_port_vlan *vlan,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||
struct net_device *orig_dev = vlan->obj.orig_dev;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
struct mlxsw_sp_bridge_vlan *bridge_vlan;
|
||||
int err = 0;
|
||||
|
||||
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
|
||||
if (WARN_ON(!bridge_port))
|
||||
return -EINVAL;
|
||||
|
||||
if (!bridge_port->bridge_device->vlan_enabled)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(bridge_vlan, &bridge_port->vlans_list, list) {
|
||||
vlan->flags = 0;
|
||||
if (bridge_vlan->pvid)
|
||||
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
|
||||
if (bridge_vlan->egress_untagged)
|
||||
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
|
||||
vlan->vid_begin = bridge_vlan->vid;
|
||||
vlan->vid_end = bridge_vlan->vid;
|
||||
err = cb(&vlan->obj);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mlxsw_sp_port_obj_dump(struct net_device *dev,
|
||||
struct switchdev_obj *obj,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
|
||||
int err = 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj), cb);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
|
||||
.switchdev_port_attr_get = mlxsw_sp_port_attr_get,
|
||||
.switchdev_port_attr_set = mlxsw_sp_port_attr_set,
|
||||
.switchdev_port_obj_add = mlxsw_sp_port_obj_add,
|
||||
.switchdev_port_obj_del = mlxsw_sp_port_obj_del,
|
||||
.switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
|
||||
};
|
||||
|
||||
static int
|
||||
@ -1857,19 +1656,16 @@ void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||
mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port);
|
||||
}
|
||||
|
||||
static void mlxsw_sp_fdb_call_notifiers(bool learning_sync, bool adding,
|
||||
char *mac, u16 vid,
|
||||
struct net_device *dev)
|
||||
static void
|
||||
mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type,
|
||||
const char *mac, u16 vid,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
unsigned long notifier_type;
|
||||
|
||||
if (learning_sync) {
|
||||
info.addr = mac;
|
||||
info.vid = vid;
|
||||
notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
|
||||
call_switchdev_notifiers(notifier_type, dev, &info.info);
|
||||
}
|
||||
info.addr = mac;
|
||||
info.vid = vid;
|
||||
call_switchdev_notifiers(type, dev, &info.info);
|
||||
}
|
||||
|
||||
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
||||
@ -1880,6 +1676,7 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
||||
struct mlxsw_sp_bridge_device *bridge_device;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||
enum switchdev_notifier_type type;
|
||||
char mac[ETH_ALEN];
|
||||
u8 local_port;
|
||||
u16 vid, fid;
|
||||
@ -1918,8 +1715,9 @@ do_fdb_op:
|
||||
|
||||
if (!do_notification)
|
||||
return;
|
||||
mlxsw_sp_fdb_call_notifiers(bridge_port->flags & BR_LEARNING_SYNC,
|
||||
adding, mac, vid, bridge_port->dev);
|
||||
type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
|
||||
mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev);
|
||||
|
||||
return;
|
||||
|
||||
just_remove:
|
||||
@ -1936,6 +1734,7 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
|
||||
struct mlxsw_sp_bridge_device *bridge_device;
|
||||
struct mlxsw_sp_bridge_port *bridge_port;
|
||||
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||
enum switchdev_notifier_type type;
|
||||
char mac[ETH_ALEN];
|
||||
u16 lag_vid = 0;
|
||||
u16 lag_id;
|
||||
@ -1976,8 +1775,9 @@ do_fdb_op:
|
||||
|
||||
if (!do_notification)
|
||||
return;
|
||||
mlxsw_sp_fdb_call_notifiers(bridge_port->flags & BR_LEARNING_SYNC,
|
||||
adding, mac, vid, bridge_port->dev);
|
||||
type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE;
|
||||
mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev);
|
||||
|
||||
return;
|
||||
|
||||
just_remove:
|
||||
@ -2050,6 +1850,97 @@ out:
|
||||
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
|
||||
}
|
||||
|
||||
struct mlxsw_sp_switchdev_event_work {
|
||||
struct work_struct work;
|
||||
struct switchdev_notifier_fdb_info fdb_info;
|
||||
struct net_device *dev;
|
||||
unsigned long event;
|
||||
};
|
||||
|
||||
static void mlxsw_sp_switchdev_event_work(struct work_struct *work)
|
||||
{
|
||||
struct mlxsw_sp_switchdev_event_work *switchdev_work =
|
||||
container_of(work, struct mlxsw_sp_switchdev_event_work, work);
|
||||
struct net_device *dev = switchdev_work->dev;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||
int err;
|
||||
|
||||
rtnl_lock();
|
||||
mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev);
|
||||
if (!mlxsw_sp_port)
|
||||
goto out;
|
||||
|
||||
switch (switchdev_work->event) {
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE:
|
||||
fdb_info = &switchdev_work->fdb_info;
|
||||
err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true);
|
||||
if (err)
|
||||
break;
|
||||
mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED,
|
||||
fdb_info->addr,
|
||||
fdb_info->vid, dev);
|
||||
break;
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
fdb_info = &switchdev_work->fdb_info;
|
||||
mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
rtnl_unlock();
|
||||
kfree(switchdev_work->fdb_info.addr);
|
||||
kfree(switchdev_work);
|
||||
dev_put(dev);
|
||||
}
|
||||
|
||||
/* Called under rcu_read_lock() */
|
||||
static int mlxsw_sp_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
||||
struct mlxsw_sp_switchdev_event_work *switchdev_work;
|
||||
struct switchdev_notifier_fdb_info *fdb_info = ptr;
|
||||
|
||||
if (!mlxsw_sp_port_dev_lower_find_rcu(dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
|
||||
if (!switchdev_work)
|
||||
return NOTIFY_BAD;
|
||||
|
||||
INIT_WORK(&switchdev_work->work, mlxsw_sp_switchdev_event_work);
|
||||
switchdev_work->dev = dev;
|
||||
switchdev_work->event = event;
|
||||
|
||||
switch (event) {
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
memcpy(&switchdev_work->fdb_info, ptr,
|
||||
sizeof(switchdev_work->fdb_info));
|
||||
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
|
||||
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
|
||||
fdb_info->addr);
|
||||
/* Take a reference on the device. This can be either
|
||||
* upper device containig mlxsw_sp_port or just a
|
||||
* mlxsw_sp_port
|
||||
*/
|
||||
dev_hold(dev);
|
||||
break;
|
||||
default:
|
||||
kfree(switchdev_work);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
mlxsw_core_schedule_work(&switchdev_work->work);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block mlxsw_sp_switchdev_notifier = {
|
||||
.notifier_call = mlxsw_sp_switchdev_event,
|
||||
};
|
||||
|
||||
static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
|
||||
{
|
||||
struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
|
||||
@ -2060,6 +1951,13 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
|
||||
dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = register_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
|
||||
if (err) {
|
||||
dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev notifier\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
|
||||
bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
|
||||
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
|
||||
@ -2069,6 +1967,8 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
|
||||
static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
|
||||
{
|
||||
cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
|
||||
unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier);
|
||||
|
||||
}
|
||||
|
||||
int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
|
||||
|
@ -105,32 +105,27 @@ struct rocker_world_ops {
|
||||
int (*port_open)(struct rocker_port *rocker_port);
|
||||
void (*port_stop)(struct rocker_port *rocker_port);
|
||||
int (*port_attr_stp_state_set)(struct rocker_port *rocker_port,
|
||||
u8 state,
|
||||
struct switchdev_trans *trans);
|
||||
u8 state);
|
||||
int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port,
|
||||
unsigned long brport_flags,
|
||||
struct switchdev_trans *trans);
|
||||
int (*port_attr_bridge_flags_get)(const struct rocker_port *rocker_port,
|
||||
unsigned long *p_brport_flags);
|
||||
int (*port_attr_bridge_flags_support_get)(const struct rocker_port *
|
||||
rocker_port,
|
||||
unsigned long *
|
||||
p_brport_flags);
|
||||
int (*port_attr_bridge_ageing_time_set)(struct rocker_port *rocker_port,
|
||||
u32 ageing_time,
|
||||
struct switchdev_trans *trans);
|
||||
int (*port_obj_vlan_add)(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_vlan *vlan,
|
||||
struct switchdev_trans *trans);
|
||||
const struct switchdev_obj_port_vlan *vlan);
|
||||
int (*port_obj_vlan_del)(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_vlan *vlan);
|
||||
int (*port_obj_vlan_dump)(const struct rocker_port *rocker_port,
|
||||
struct switchdev_obj_port_vlan *vlan,
|
||||
switchdev_obj_dump_cb_t *cb);
|
||||
int (*port_obj_fdb_add)(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_fdb *fdb,
|
||||
struct switchdev_trans *trans);
|
||||
u16 vid, const unsigned char *addr);
|
||||
int (*port_obj_fdb_del)(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_fdb *fdb);
|
||||
int (*port_obj_fdb_dump)(const struct rocker_port *rocker_port,
|
||||
struct switchdev_obj_port_fdb *fdb,
|
||||
switchdev_obj_dump_cb_t *cb);
|
||||
u16 vid, const unsigned char *addr);
|
||||
int (*port_master_linked)(struct rocker_port *rocker_port,
|
||||
struct net_device *master);
|
||||
int (*port_master_unlinked)(struct rocker_port *rocker_port,
|
||||
|
@ -1557,7 +1557,11 @@ static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port,
|
||||
|
||||
if (!wops->port_attr_stp_state_set)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_attr_stp_state_set(rocker_port, state, trans);
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
return wops->port_attr_stp_state_set(rocker_port, state);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1569,6 +1573,10 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
|
||||
|
||||
if (!wops->port_attr_bridge_flags_set)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
return wops->port_attr_bridge_flags_set(rocker_port, brport_flags,
|
||||
trans);
|
||||
}
|
||||
@ -1584,6 +1592,20 @@ rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port,
|
||||
return wops->port_attr_bridge_flags_get(rocker_port, p_brport_flags);
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port *
|
||||
rocker_port,
|
||||
unsigned long *
|
||||
p_brport_flags_support)
|
||||
{
|
||||
struct rocker_world_ops *wops = rocker_port->rocker->wops;
|
||||
|
||||
if (!wops->port_attr_bridge_flags_support_get)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_attr_bridge_flags_support_get(rocker_port,
|
||||
p_brport_flags_support);
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
|
||||
u32 ageing_time,
|
||||
@ -1594,6 +1616,10 @@ rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
|
||||
|
||||
if (!wops->port_attr_bridge_ageing_time_set)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time,
|
||||
trans);
|
||||
}
|
||||
@ -1607,7 +1633,11 @@ rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
|
||||
|
||||
if (!wops->port_obj_vlan_add)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_obj_vlan_add(rocker_port, vlan, trans);
|
||||
|
||||
if (switchdev_trans_ph_prepare(trans))
|
||||
return 0;
|
||||
|
||||
return wops->port_obj_vlan_add(rocker_port, vlan);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1622,50 +1652,26 @@ rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port,
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_obj_vlan_dump(const struct rocker_port *rocker_port,
|
||||
struct switchdev_obj_port_vlan *vlan,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
struct rocker_world_ops *wops = rocker_port->rocker->wops;
|
||||
|
||||
if (!wops->port_obj_vlan_dump)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_obj_vlan_dump(rocker_port, vlan, cb);
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_fdb *fdb,
|
||||
struct switchdev_trans *trans)
|
||||
rocker_world_port_fdb_add(struct rocker_port *rocker_port,
|
||||
struct switchdev_notifier_fdb_info *info)
|
||||
{
|
||||
struct rocker_world_ops *wops = rocker_port->rocker->wops;
|
||||
|
||||
if (!wops->port_obj_fdb_add)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_obj_fdb_add(rocker_port, fdb, trans);
|
||||
|
||||
return wops->port_obj_fdb_add(rocker_port, info->vid, info->addr);
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port,
|
||||
const struct switchdev_obj_port_fdb *fdb)
|
||||
rocker_world_port_fdb_del(struct rocker_port *rocker_port,
|
||||
struct switchdev_notifier_fdb_info *info)
|
||||
{
|
||||
struct rocker_world_ops *wops = rocker_port->rocker->wops;
|
||||
|
||||
if (!wops->port_obj_fdb_del)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_obj_fdb_del(rocker_port, fdb);
|
||||
}
|
||||
|
||||
static int
|
||||
rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port,
|
||||
struct switchdev_obj_port_fdb *fdb,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
struct rocker_world_ops *wops = rocker_port->rocker->wops;
|
||||
|
||||
if (!wops->port_obj_fdb_dump)
|
||||
return -EOPNOTSUPP;
|
||||
return wops->port_obj_fdb_dump(rocker_port, fdb, cb);
|
||||
return wops->port_obj_fdb_del(rocker_port, info->vid, info->addr);
|
||||
}
|
||||
|
||||
static int rocker_world_port_master_linked(struct rocker_port *rocker_port,
|
||||
@ -2022,12 +2028,6 @@ static const struct net_device_ops rocker_port_netdev_ops = {
|
||||
.ndo_start_xmit = rocker_port_xmit,
|
||||
.ndo_set_mac_address = rocker_port_set_mac_address,
|
||||
.ndo_change_mtu = rocker_port_change_mtu,
|
||||
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
|
||||
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
|
||||
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
|
||||
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||
.ndo_get_phys_port_name = rocker_port_get_phys_port_name,
|
||||
.ndo_change_proto_down = rocker_port_change_proto_down,
|
||||
.ndo_neigh_destroy = rocker_port_neigh_destroy,
|
||||
@ -2053,6 +2053,10 @@ static int rocker_port_attr_get(struct net_device *dev,
|
||||
err = rocker_world_port_attr_bridge_flags_get(rocker_port,
|
||||
&attr->u.brport_flags);
|
||||
break;
|
||||
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
|
||||
err = rocker_world_port_attr_bridge_flags_support_get(rocker_port,
|
||||
&attr->u.brport_flags_support);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -2104,11 +2108,6 @@ static int rocker_port_obj_add(struct net_device *dev,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj),
|
||||
trans);
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = rocker_world_port_obj_fdb_add(rocker_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj),
|
||||
trans);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
@ -2128,36 +2127,6 @@ static int rocker_port_obj_del(struct net_device *dev,
|
||||
err = rocker_world_port_obj_vlan_del(rocker_port,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj));
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = rocker_world_port_obj_fdb_del(rocker_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj));
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rocker_port_obj_dump(struct net_device *dev,
|
||||
struct switchdev_obj *obj,
|
||||
switchdev_obj_dump_cb_t *cb)
|
||||
{
|
||||
const struct rocker_port *rocker_port = netdev_priv(dev);
|
||||
int err = 0;
|
||||
|
||||
switch (obj->id) {
|
||||
case SWITCHDEV_OBJ_ID_PORT_FDB:
|
||||
err = rocker_world_port_obj_fdb_dump(rocker_port,
|
||||
SWITCHDEV_OBJ_PORT_FDB(obj),
|
||||
cb);
|
||||
break;
|
||||
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
||||
err = rocker_world_port_obj_vlan_dump(rocker_port,
|
||||
SWITCHDEV_OBJ_PORT_VLAN(obj),
|
||||
cb);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
@ -2171,7 +2140,6 @@ static const struct switchdev_ops rocker_port_switchdev_ops = {
|
||||
.switchdev_port_attr_set = rocker_port_attr_set,
|
||||
.switchdev_port_obj_add = rocker_port_obj_add,
|
||||
.switchdev_port_obj_del = rocker_port_obj_del,
|
||||
.switchdev_port_obj_dump = rocker_port_obj_dump,
|
||||
};
|
||||
|
||||
struct rocker_fib_event_work {
|
||||
@ -2729,6 +2697,109 @@ static void rocker_msix_fini(const struct rocker *rocker)
|
||||
kfree(rocker->msix_entries);
|
||||
}
|
||||
|
||||
static bool rocker_port_dev_check(const struct net_device *dev)
|
||||
{
|
||||
return dev->netdev_ops == &rocker_port_netdev_ops;
|
||||
}
|
||||
|
||||
struct rocker_switchdev_event_work {
|
||||
struct work_struct work;
|
||||
struct switchdev_notifier_fdb_info fdb_info;
|
||||
struct rocker_port *rocker_port;
|
||||
unsigned long event;
|
||||
};
|
||||
|
||||
static void
|
||||
rocker_fdb_offload_notify(struct rocker_port *rocker_port,
|
||||
struct switchdev_notifier_fdb_info *recv_info)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
|
||||
info.addr = recv_info->addr;
|
||||
info.vid = recv_info->vid;
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
|
||||
rocker_port->dev, &info.info);
|
||||
}
|
||||
|
||||
static void rocker_switchdev_event_work(struct work_struct *work)
|
||||
{
|
||||
struct rocker_switchdev_event_work *switchdev_work =
|
||||
container_of(work, struct rocker_switchdev_event_work, work);
|
||||
struct rocker_port *rocker_port = switchdev_work->rocker_port;
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
int err;
|
||||
|
||||
rtnl_lock();
|
||||
switch (switchdev_work->event) {
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE:
|
||||
fdb_info = &switchdev_work->fdb_info;
|
||||
err = rocker_world_port_fdb_add(rocker_port, fdb_info);
|
||||
if (err) {
|
||||
netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err);
|
||||
break;
|
||||
}
|
||||
rocker_fdb_offload_notify(rocker_port, fdb_info);
|
||||
break;
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
fdb_info = &switchdev_work->fdb_info;
|
||||
err = rocker_world_port_fdb_del(rocker_port, fdb_info);
|
||||
if (err)
|
||||
netdev_dbg(rocker_port->dev, "fdb add failed err=%d\n", err);
|
||||
break;
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
kfree(switchdev_work->fdb_info.addr);
|
||||
kfree(switchdev_work);
|
||||
dev_put(rocker_port->dev);
|
||||
}
|
||||
|
||||
/* called under rcu_read_lock() */
|
||||
static int rocker_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
||||
struct rocker_switchdev_event_work *switchdev_work;
|
||||
struct switchdev_notifier_fdb_info *fdb_info = ptr;
|
||||
struct rocker_port *rocker_port;
|
||||
|
||||
if (!rocker_port_dev_check(dev))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
rocker_port = netdev_priv(dev);
|
||||
switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
|
||||
if (WARN_ON(!switchdev_work))
|
||||
return NOTIFY_BAD;
|
||||
|
||||
INIT_WORK(&switchdev_work->work, rocker_switchdev_event_work);
|
||||
switchdev_work->rocker_port = rocker_port;
|
||||
switchdev_work->event = event;
|
||||
|
||||
switch (event) {
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
memcpy(&switchdev_work->fdb_info, ptr,
|
||||
sizeof(switchdev_work->fdb_info));
|
||||
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
|
||||
ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
|
||||
fdb_info->addr);
|
||||
/* Take a reference on the rocker device */
|
||||
dev_hold(dev);
|
||||
break;
|
||||
default:
|
||||
kfree(switchdev_work);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
queue_work(rocker_port->rocker->rocker_owq,
|
||||
&switchdev_work->work);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block rocker_switchdev_notifier = {
|
||||
.notifier_call = rocker_switchdev_event,
|
||||
};
|
||||
|
||||
static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct rocker *rocker;
|
||||
@ -2834,6 +2905,12 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (err)
|
||||
goto err_register_fib_notifier;
|
||||
|
||||
err = register_switchdev_notifier(&rocker_switchdev_notifier);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to register switchdev notifier\n");
|
||||
goto err_register_switchdev_notifier;
|
||||
}
|
||||
|
||||
rocker->hw.id = rocker_read64(rocker, SWITCH_ID);
|
||||
|
||||
err = rocker_probe_ports(rocker);
|
||||
@ -2848,6 +2925,8 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
return 0;
|
||||
|
||||
err_probe_ports:
|
||||
unregister_switchdev_notifier(&rocker_switchdev_notifier);
|
||||
err_register_switchdev_notifier:
|
||||
unregister_fib_notifier(&rocker->fib_nb);
|
||||
err_register_fib_notifier:
|
||||
destroy_workqueue(rocker->rocker_owq);
|
||||
@ -2878,6 +2957,7 @@ static void rocker_remove(struct pci_dev *pdev)
|
||||
struct rocker *rocker = pci_get_drvdata(pdev);
|
||||
|
||||
rocker_remove_ports(rocker);
|
||||
unregister_switchdev_notifier(&rocker_switchdev_notifier);
|
||||
unregister_fib_notifier(&rocker->fib_nb);
|
||||
rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET);
|
||||
destroy_workqueue(rocker->rocker_owq);
|
||||
@ -2902,11 +2982,6 @@ static struct pci_driver rocker_pci_driver = {
|
||||
* Net device notifier event handler
|
||||
************************************/
|
||||
|
||||
static bool rocker_port_dev_check(const struct net_device *dev)
|
||||
{
|
||||
return dev->netdev_ops == &rocker_port_netdev_ops;
|
||||
}
|
||||
|
||||
static bool rocker_port_dev_check_under(const struct net_device *dev,
|
||||
struct rocker *rocker)
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2005,12 +2005,6 @@ static const struct net_device_ops team_netdev_ops = {
|
||||
.ndo_del_slave = team_del_slave,
|
||||
.ndo_fix_features = team_fix_features,
|
||||
.ndo_change_carrier = team_change_carrier,
|
||||
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
|
||||
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
|
||||
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
|
||||
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||
.ndo_features_check = passthru_features_check,
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,7 @@ enum switchdev_attr_id {
|
||||
SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
|
||||
SWITCHDEV_ATTR_ID_PORT_STP_STATE,
|
||||
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
|
||||
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT,
|
||||
SWITCHDEV_ATTR_ID_PORT_MROUTER,
|
||||
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
|
||||
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
|
||||
@ -62,6 +63,7 @@ struct switchdev_attr {
|
||||
struct netdev_phys_item_id ppid; /* PORT_PARENT_ID */
|
||||
u8 stp_state; /* PORT_STP_STATE */
|
||||
unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */
|
||||
unsigned long brport_flags_support; /* PORT_BRIDGE_FLAGS_SUPPORT */
|
||||
bool mrouter; /* PORT_MROUTER */
|
||||
clock_t ageing_time; /* BRIDGE_AGEING_TIME */
|
||||
bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
|
||||
@ -153,8 +155,11 @@ struct switchdev_ops {
|
||||
};
|
||||
|
||||
enum switchdev_notifier_type {
|
||||
SWITCHDEV_FDB_ADD = 1,
|
||||
SWITCHDEV_FDB_DEL,
|
||||
SWITCHDEV_FDB_ADD_TO_BRIDGE = 1,
|
||||
SWITCHDEV_FDB_DEL_TO_BRIDGE,
|
||||
SWITCHDEV_FDB_ADD_TO_DEVICE,
|
||||
SWITCHDEV_FDB_DEL_TO_DEVICE,
|
||||
SWITCHDEV_FDB_OFFLOADED,
|
||||
};
|
||||
|
||||
struct switchdev_notifier_info {
|
||||
|
@ -41,6 +41,7 @@ enum {
|
||||
#define NTF_MASTER 0x04
|
||||
#define NTF_PROXY 0x08 /* == ATF_PUBL */
|
||||
#define NTF_EXT_LEARNED 0x10
|
||||
#define NTF_OFFLOADED 0x20
|
||||
#define NTF_ROUTER 0x80
|
||||
|
||||
/*
|
||||
|
@ -797,12 +797,6 @@ static const struct net_device_ops vlan_netdev_ops = {
|
||||
.ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup,
|
||||
#endif
|
||||
.ndo_fix_features = vlan_dev_fix_features,
|
||||
.ndo_fdb_add = switchdev_port_fdb_add,
|
||||
.ndo_fdb_del = switchdev_port_fdb_del,
|
||||
.ndo_fdb_dump = switchdev_port_fdb_dump,
|
||||
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
|
||||
.ndo_bridge_getlink = switchdev_port_bridge_getlink,
|
||||
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
|
||||
.ndo_get_lock_subclass = vlan_dev_get_lock_subclass,
|
||||
.ndo_get_iflink = vlan_dev_get_iflink,
|
||||
};
|
||||
|
@ -121,7 +121,7 @@ static struct notifier_block br_device_notifier = {
|
||||
.notifier_call = br_device_event
|
||||
};
|
||||
|
||||
/* called with RTNL */
|
||||
/* called with RTNL or RCU */
|
||||
static int br_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
@ -131,27 +131,36 @@ static int br_switchdev_event(struct notifier_block *unused,
|
||||
struct switchdev_notifier_fdb_info *fdb_info;
|
||||
int err = NOTIFY_DONE;
|
||||
|
||||
p = br_port_get_rtnl(dev);
|
||||
p = br_port_get_rtnl_rcu(dev);
|
||||
if (!p)
|
||||
goto out;
|
||||
|
||||
br = p->br;
|
||||
|
||||
switch (event) {
|
||||
case SWITCHDEV_FDB_ADD:
|
||||
case SWITCHDEV_FDB_ADD_TO_BRIDGE:
|
||||
fdb_info = ptr;
|
||||
err = br_fdb_external_learn_add(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
if (err)
|
||||
if (err) {
|
||||
err = notifier_from_errno(err);
|
||||
break;
|
||||
}
|
||||
br_fdb_offloaded_set(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
break;
|
||||
case SWITCHDEV_FDB_DEL:
|
||||
case SWITCHDEV_FDB_DEL_TO_BRIDGE:
|
||||
fdb_info = ptr;
|
||||
err = br_fdb_external_learn_del(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
if (err)
|
||||
err = notifier_from_errno(err);
|
||||
break;
|
||||
case SWITCHDEV_FDB_OFFLOADED:
|
||||
fdb_info = ptr;
|
||||
br_fdb_offloaded_set(br, p, fdb_info->addr,
|
||||
fdb_info->vid);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -511,6 +511,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
|
||||
fdb->is_static = is_static;
|
||||
fdb->added_by_user = 0;
|
||||
fdb->added_by_external_learn = 0;
|
||||
fdb->offloaded = 0;
|
||||
fdb->updated = fdb->used = jiffies;
|
||||
hlist_add_head_rcu(&fdb->hlist, head);
|
||||
}
|
||||
@ -647,11 +648,16 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
|
||||
ndm->ndm_family = AF_BRIDGE;
|
||||
ndm->ndm_pad1 = 0;
|
||||
ndm->ndm_pad2 = 0;
|
||||
ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0;
|
||||
ndm->ndm_flags = 0;
|
||||
ndm->ndm_type = 0;
|
||||
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
|
||||
ndm->ndm_state = fdb_to_nud(br, fdb);
|
||||
|
||||
if (fdb->offloaded)
|
||||
ndm->ndm_flags |= NTF_OFFLOADED;
|
||||
if (fdb->added_by_external_learn)
|
||||
ndm->ndm_flags |= NTF_EXT_LEARNED;
|
||||
|
||||
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->addr))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex))
|
||||
@ -690,6 +696,8 @@ static void fdb_notify(struct net_bridge *br,
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
br_switchdev_fdb_notify(fdb, type);
|
||||
|
||||
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
goto errout;
|
||||
@ -1075,7 +1083,6 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
int err = 0;
|
||||
|
||||
ASSERT_RTNL();
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
|
||||
head = &br->hash[br_mac_hash(addr, vid)];
|
||||
@ -1110,7 +1117,6 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
int err = 0;
|
||||
|
||||
ASSERT_RTNL();
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
|
||||
fdb = br_fdb_find(br, addr, vid);
|
||||
@ -1123,3 +1129,17 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
|
||||
fdb = br_fdb_find(br, addr, vid);
|
||||
if (fdb)
|
||||
fdb->offloaded = 1;
|
||||
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
}
|
||||
|
@ -662,16 +662,26 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
|
||||
}
|
||||
|
||||
/* Set/clear or port flags based on attribute */
|
||||
static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
|
||||
int attrtype, unsigned long mask)
|
||||
static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
|
||||
int attrtype, unsigned long mask)
|
||||
{
|
||||
if (tb[attrtype]) {
|
||||
u8 flag = nla_get_u8(tb[attrtype]);
|
||||
if (flag)
|
||||
p->flags |= mask;
|
||||
else
|
||||
p->flags &= ~mask;
|
||||
}
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
if (!tb[attrtype])
|
||||
return 0;
|
||||
|
||||
if (nla_get_u8(tb[attrtype]))
|
||||
flags = p->flags | mask;
|
||||
else
|
||||
flags = p->flags & ~mask;
|
||||
|
||||
err = br_switchdev_set_port_flag(p, flags, mask);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
p->flags = flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process bridge protocol info on port */
|
||||
@ -681,20 +691,55 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
|
||||
bool br_vlan_tunnel_old = false;
|
||||
int err;
|
||||
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false;
|
||||
br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
|
||||
err = br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL))
|
||||
nbp_vlan_tunnel_info_flush(p);
|
||||
|
||||
|
@ -169,7 +169,8 @@ struct net_bridge_fdb_entry {
|
||||
unsigned char is_local:1,
|
||||
is_static:1,
|
||||
added_by_user:1,
|
||||
added_by_external_learn:1;
|
||||
added_by_external_learn:1,
|
||||
offloaded:1;
|
||||
|
||||
/* write-heavy members should not affect lookups */
|
||||
unsigned long updated ____cacheline_aligned_in_smp;
|
||||
@ -284,6 +285,12 @@ static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device *
|
||||
rtnl_dereference(dev->rx_handler_data) : NULL;
|
||||
}
|
||||
|
||||
static inline struct net_bridge_port *br_port_get_rtnl_rcu(const struct net_device *dev)
|
||||
{
|
||||
return br_port_exists(dev) ?
|
||||
rcu_dereference_rtnl(dev->rx_handler_data) : NULL;
|
||||
}
|
||||
|
||||
struct net_bridge {
|
||||
spinlock_t lock;
|
||||
spinlock_t hash_lock;
|
||||
@ -530,6 +537,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
|
||||
const unsigned char *addr, u16 vid);
|
||||
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
||||
const unsigned char *addr, u16 vid);
|
||||
void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
|
||||
const unsigned char *addr, u16 vid);
|
||||
|
||||
/* br_forward.c */
|
||||
enum br_pkt_type {
|
||||
@ -1076,6 +1085,11 @@ void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
|
||||
struct sk_buff *skb);
|
||||
bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
const struct sk_buff *skb);
|
||||
int br_switchdev_set_port_flag(struct net_bridge_port *p,
|
||||
unsigned long flags,
|
||||
unsigned long mask);
|
||||
void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
|
||||
int type);
|
||||
#else
|
||||
static inline int nbp_switchdev_mark_set(struct net_bridge_port *p)
|
||||
{
|
||||
@ -1092,6 +1106,18 @@ static inline bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int br_switchdev_set_port_flag(struct net_bridge_port *p,
|
||||
unsigned long flags,
|
||||
unsigned long mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_NET_SWITCHDEV */
|
||||
|
||||
#endif
|
||||
|
@ -55,3 +55,79 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
return !skb->offload_fwd_mark ||
|
||||
BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
|
||||
}
|
||||
|
||||
/* Flags that can be offloaded to hardware */
|
||||
#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
|
||||
BR_MCAST_FLOOD | BR_BCAST_FLOOD)
|
||||
|
||||
int br_switchdev_set_port_flag(struct net_bridge_port *p,
|
||||
unsigned long flags,
|
||||
unsigned long mask)
|
||||
{
|
||||
struct switchdev_attr attr = {
|
||||
.orig_dev = p->dev,
|
||||
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
|
||||
return 0;
|
||||
|
||||
err = switchdev_port_attr_get(p->dev, &attr);
|
||||
if (err == -EOPNOTSUPP)
|
||||
return 0;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Check if specific bridge flag attribute offload is supported */
|
||||
if (!(attr.u.brport_flags_support & mask)) {
|
||||
br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
|
||||
(unsigned int)p->port_no, p->dev->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
|
||||
attr.flags = SWITCHDEV_F_DEFER;
|
||||
attr.u.brport_flags = flags;
|
||||
err = switchdev_port_attr_set(p->dev, &attr);
|
||||
if (err) {
|
||||
br_warn(p->br, "error setting offload flag on port %u(%s)\n",
|
||||
(unsigned int)p->port_no, p->dev->name);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
|
||||
u16 vid, struct net_device *dev)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info info;
|
||||
unsigned long notifier_type;
|
||||
|
||||
info.addr = mac;
|
||||
info.vid = vid;
|
||||
notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
|
||||
call_switchdev_notifiers(notifier_type, dev, &info.info);
|
||||
}
|
||||
|
||||
void
|
||||
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
|
||||
{
|
||||
if (!fdb->added_by_user)
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case RTM_DELNEIGH:
|
||||
br_switchdev_fdb_call_notifiers(false, fdb->addr.addr,
|
||||
fdb->vlan_id,
|
||||
fdb->dst->dev);
|
||||
break;
|
||||
case RTM_NEWNEIGH:
|
||||
br_switchdev_fdb_call_notifiers(true, fdb->addr.addr,
|
||||
fdb->vlan_id,
|
||||
fdb->dst->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -571,24 +571,17 @@ int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_port_obj_dump);
|
||||
|
||||
static RAW_NOTIFIER_HEAD(switchdev_notif_chain);
|
||||
static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain);
|
||||
|
||||
/**
|
||||
* register_switchdev_notifier - Register notifier
|
||||
* @nb: notifier_block
|
||||
*
|
||||
* Register switch device notifier. This should be used by code
|
||||
* which needs to monitor events happening in particular device.
|
||||
* Return values are same as for atomic_notifier_chain_register().
|
||||
* Register switch device notifier.
|
||||
*/
|
||||
int register_switchdev_notifier(struct notifier_block *nb)
|
||||
{
|
||||
int err;
|
||||
|
||||
rtnl_lock();
|
||||
err = raw_notifier_chain_register(&switchdev_notif_chain, nb);
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
return atomic_notifier_chain_register(&switchdev_notif_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_switchdev_notifier);
|
||||
|
||||
@ -597,16 +590,10 @@ EXPORT_SYMBOL_GPL(register_switchdev_notifier);
|
||||
* @nb: notifier_block
|
||||
*
|
||||
* Unregister switch device notifier.
|
||||
* Return values are same as for atomic_notifier_chain_unregister().
|
||||
*/
|
||||
int unregister_switchdev_notifier(struct notifier_block *nb)
|
||||
{
|
||||
int err;
|
||||
|
||||
rtnl_lock();
|
||||
err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb);
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
return atomic_notifier_chain_unregister(&switchdev_notif_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
|
||||
|
||||
@ -616,18 +603,13 @@ EXPORT_SYMBOL_GPL(unregister_switchdev_notifier);
|
||||
* @dev: port device
|
||||
* @info: notifier information data
|
||||
*
|
||||
* Call all network notifier blocks. This should be called by driver
|
||||
* when it needs to propagate hardware event.
|
||||
* Return values are same as for atomic_notifier_call_chain().
|
||||
* rtnl_lock must be held.
|
||||
* Call all network notifier blocks.
|
||||
*/
|
||||
int call_switchdev_notifiers(unsigned long val, struct net_device *dev,
|
||||
struct switchdev_notifier_info *info)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
info->dev = dev;
|
||||
return raw_notifier_call_chain(&switchdev_notif_chain, val, info);
|
||||
return atomic_notifier_call_chain(&switchdev_notif_chain, val, info);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_switchdev_notifiers);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user