Commit 8a1ab5d7 authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

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: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0d65fc13
...@@ -490,32 +490,56 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -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, ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 = { ...@@ -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, ...@@ -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, ...@@ -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;
} }
} }
......
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