mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-04-15 18:27:41 +07:00
mlxsw: spectrum: Implement FDB add/remove/dump for LAG
Implement FDB offloading for lagged ports, including learning LAG FDB entries, adding/removing static FDB entries and dumping existing LAG FDB entries. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0d65fc1304
commit
8a1ab5d766
@ -490,32 +490,56 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|||||||
untagged_flag, pvid_flag);
|
untagged_flag, pvid_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port,
|
static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
|
||||||
const char *mac, u16 vid, bool adding,
|
|
||||||
bool dynamic)
|
|
||||||
{
|
{
|
||||||
enum mlxsw_reg_sfd_rec_policy policy;
|
return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
|
||||||
enum mlxsw_reg_sfd_op op;
|
MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
|
||||||
|
{
|
||||||
|
return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
|
||||||
|
MLXSW_REG_SFD_OP_WRITE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||||
|
const char *mac, u16 vid, bool adding,
|
||||||
|
bool dynamic)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||||
char *sfd_pl;
|
char *sfd_pl;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!vid)
|
|
||||||
vid = mlxsw_sp_port->pvid;
|
|
||||||
|
|
||||||
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
|
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
|
||||||
if (!sfd_pl)
|
if (!sfd_pl)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
|
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
|
||||||
MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
|
mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
|
||||||
op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
|
|
||||||
MLXSW_REG_SFD_OP_WRITE_REMOVE;
|
|
||||||
mlxsw_reg_sfd_pack(sfd_pl, op, 0);
|
|
||||||
mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy,
|
|
||||||
mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
|
mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
|
||||||
mlxsw_sp_port->local_port);
|
mlxsw_sp_port->local_port);
|
||||||
err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd),
|
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
|
||||||
sfd_pl);
|
kfree(sfd_pl);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
|
||||||
|
const char *mac, u16 vid, bool adding,
|
||||||
|
bool dynamic)
|
||||||
|
{
|
||||||
|
char *sfd_pl;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
|
||||||
|
if (!sfd_pl)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
|
||||||
|
mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
|
||||||
|
mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
|
||||||
|
lag_id);
|
||||||
|
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
|
||||||
kfree(sfd_pl);
|
kfree(sfd_pl);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -526,11 +550,21 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|||||||
const struct switchdev_obj_port_fdb *fdb,
|
const struct switchdev_obj_port_fdb *fdb,
|
||||||
struct switchdev_trans *trans)
|
struct switchdev_trans *trans)
|
||||||
{
|
{
|
||||||
|
u16 vid = fdb->vid;
|
||||||
|
|
||||||
if (switchdev_trans_ph_prepare(trans))
|
if (switchdev_trans_ph_prepare(trans))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
|
if (!vid)
|
||||||
true, false);
|
vid = mlxsw_sp_port->pvid;
|
||||||
|
|
||||||
|
if (!mlxsw_sp_port->lagged)
|
||||||
|
return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port,
|
||||||
|
fdb->addr, vid, true, false);
|
||||||
|
else
|
||||||
|
return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
|
||||||
|
mlxsw_sp_port->lag_id,
|
||||||
|
fdb->addr, vid, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlxsw_sp_port_obj_add(struct net_device *dev,
|
static int mlxsw_sp_port_obj_add(struct net_device *dev,
|
||||||
@ -645,8 +679,15 @@ static int
|
|||||||
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||||
const struct switchdev_obj_port_fdb *fdb)
|
const struct switchdev_obj_port_fdb *fdb)
|
||||||
{
|
{
|
||||||
return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
|
if (!mlxsw_sp_port->lagged)
|
||||||
false, false);
|
return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port,
|
||||||
|
fdb->addr, fdb->vid,
|
||||||
|
false, false);
|
||||||
|
else
|
||||||
|
return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
|
||||||
|
mlxsw_sp_port->lag_id,
|
||||||
|
fdb->addr, fdb->vid,
|
||||||
|
false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlxsw_sp_port_obj_del(struct net_device *dev,
|
static int mlxsw_sp_port_obj_del(struct net_device *dev,
|
||||||
@ -672,14 +713,30 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
|
||||||
|
u16 lag_id)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
|
||||||
|
mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
|
||||||
|
if (mlxsw_sp_port)
|
||||||
|
return mlxsw_sp_port;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||||
struct switchdev_obj_port_fdb *fdb,
|
struct switchdev_obj_port_fdb *fdb,
|
||||||
switchdev_obj_dump_cb_t *cb)
|
switchdev_obj_dump_cb_t *cb)
|
||||||
{
|
{
|
||||||
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
||||||
char *sfd_pl;
|
char *sfd_pl;
|
||||||
char mac[ETH_ALEN];
|
char mac[ETH_ALEN];
|
||||||
u16 vid;
|
u16 vid;
|
||||||
u8 local_port;
|
u8 local_port;
|
||||||
|
u16 lag_id;
|
||||||
u8 num_rec;
|
u8 num_rec;
|
||||||
int stored_err = 0;
|
int stored_err = 0;
|
||||||
int i;
|
int i;
|
||||||
@ -692,8 +749,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
|||||||
mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
|
mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
|
||||||
do {
|
do {
|
||||||
mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
|
mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
|
||||||
err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core,
|
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
|
||||||
MLXSW_REG(sfd), sfd_pl);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -718,6 +774,20 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
|
|||||||
if (err)
|
if (err)
|
||||||
stored_err = err;
|
stored_err = err;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
|
||||||
|
mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
|
||||||
|
mac, &vid, &lag_id);
|
||||||
|
if (mlxsw_sp_port ==
|
||||||
|
mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) {
|
||||||
|
ether_addr_copy(fdb->addr, mac);
|
||||||
|
fdb->ndm_state = NUD_REACHABLE;
|
||||||
|
fdb->vid = vid;
|
||||||
|
err = cb(&fdb->obj);
|
||||||
|
if (err)
|
||||||
|
stored_err = err;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
|
} while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
|
||||||
@ -779,6 +849,21 @@ static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
|
|||||||
.switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
|
.switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void mlxsw_sp_fdb_call_notifiers(bool learning, bool learning_sync,
|
||||||
|
bool adding, char *mac, u16 vid,
|
||||||
|
struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct switchdev_notifier_fdb_info info;
|
||||||
|
unsigned long notifier_type;
|
||||||
|
|
||||||
|
if (learning && 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
||||||
char *sfn_pl, int rec_index,
|
char *sfn_pl, int rec_index,
|
||||||
bool adding)
|
bool adding)
|
||||||
@ -796,24 +881,49 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid,
|
err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, vid,
|
||||||
adding && mlxsw_sp_port->learning, true);
|
adding && mlxsw_sp_port->learning, true);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (net_ratelimit())
|
if (net_ratelimit())
|
||||||
netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
|
netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) {
|
mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
|
||||||
struct switchdev_notifier_fdb_info info;
|
mlxsw_sp_port->learning_sync,
|
||||||
unsigned long notifier_type;
|
adding, mac, vid, mlxsw_sp_port->dev);
|
||||||
|
}
|
||||||
|
|
||||||
info.addr = mac;
|
static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
|
||||||
info.vid = vid;
|
char *sfn_pl, int rec_index,
|
||||||
notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
|
bool adding)
|
||||||
call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev,
|
{
|
||||||
&info.info);
|
struct mlxsw_sp_port *mlxsw_sp_port;
|
||||||
|
char mac[ETH_ALEN];
|
||||||
|
u16 lag_id;
|
||||||
|
u16 vid;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &vid, &lag_id);
|
||||||
|
mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
|
||||||
|
if (!mlxsw_sp_port) {
|
||||||
|
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, vid,
|
||||||
|
adding && mlxsw_sp_port->learning,
|
||||||
|
true);
|
||||||
|
if (err) {
|
||||||
|
if (net_ratelimit())
|
||||||
|
netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
|
||||||
|
mlxsw_sp_port->learning_sync,
|
||||||
|
adding, mac, vid,
|
||||||
|
mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
|
static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
|
||||||
@ -828,6 +938,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
|
|||||||
mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
|
mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
|
||||||
rec_index, false);
|
rec_index, false);
|
||||||
break;
|
break;
|
||||||
|
case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
|
||||||
|
mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
|
||||||
|
rec_index, true);
|
||||||
|
break;
|
||||||
|
case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
|
||||||
|
mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
|
||||||
|
rec_index, false);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user