Skip to content

Commit

Permalink
Merge branch 'mlxsw-offload-mc-flood'
Browse files Browse the repository at this point in the history
Jiri Pirko says:

====================
mlxsw: Offload MC flood for unregister MC

Nogah says:

When multicast is enabled, the Linux bridge floods unregistered multicast
packets only to ports connected to a multicast router. Devices capable of
offloading the Linux bridge need to be made aware of such ports, for
proper flooding behavior.
On the other hand, when multicast is disabled, such packets should be
flooded to all ports. This patchset aims to fix that, by offloading
the multicast state and the list of multicast router ports.

The first 3 patches adds switchdev attributes to offload this data.
The rest of the patchset add implementation for handling this data in the
mlxsw driver.

The effects this data has on the MDB (namely, when the multicast is
disabled the MDB should be considered as invalid, and when it is enabled, a
packet that is flooded by it should also be flooded to the multicast
routers ports) is subject of future work.

Testing of this patchset included:
Sending 3 mc packets streams, LL, register and unregistered, and checking
that they reached only to the ports that should have received them.
The configs were:
mc disabled, mc without mc router ports and mc with fixed router port.
It was checked for vlan aware bridge, vlan unaware bridge and vlan unaware
bridge with another vlan unaware bridge on the same machine
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
davem330 committed Feb 10, 2017
2 parents 94134bf + 90e0f0c commit 58be242
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 54 deletions.
29 changes: 23 additions & 6 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.c
Original file line number Diff line number Diff line change
Expand Up @@ -3073,10 +3073,17 @@ static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core,
else
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;

if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
switch (type) {
case MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST:
flood_table = MLXSW_SP_FLOOD_TABLE_UC;
else
flood_table = MLXSW_SP_FLOOD_TABLE_BM;
break;
case MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4:
case MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6:
flood_table = MLXSW_SP_FLOOD_TABLE_MC;
break;
default:
flood_table = MLXSW_SP_FLOOD_TABLE_BC;
}

mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type,
flood_table);
Expand Down Expand Up @@ -3270,9 +3277,9 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = {
.used_flood_tables = 1,
.used_flood_mode = 1,
.flood_mode = 3,
.max_fid_offset_flood_tables = 2,
.max_fid_offset_flood_tables = 3,
.fid_offset_flood_table_size = VLAN_N_VID - 1,
.max_fid_flood_tables = 2,
.max_fid_flood_tables = 3,
.fid_flood_table_size = MLXSW_SP_VFID_MAX,
.used_max_ib_mc = 1,
.max_ib_mc = 0,
Expand Down Expand Up @@ -3689,7 +3696,7 @@ static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid,

table_type = mlxsw_sp_flood_table_type_get(fid);
index = mlxsw_sp_flood_table_index_get(fid);
mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, index, table_type,
mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type,
1, MLXSW_PORT_ROUTER_PORT, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);

Expand Down Expand Up @@ -4065,6 +4072,9 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port->learning = 1;
mlxsw_sp_port->learning_sync = 1;
mlxsw_sp_port->uc_flood = 1;
mlxsw_sp_port->mc_flood = 1;
mlxsw_sp_port->mc_router = 0;
mlxsw_sp_port->mc_disabled = 1;
mlxsw_sp_port->bridged = 1;

return 0;
Expand All @@ -4081,6 +4091,8 @@ static void mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port)
mlxsw_sp_port->learning = 0;
mlxsw_sp_port->learning_sync = 0;
mlxsw_sp_port->uc_flood = 0;
mlxsw_sp_port->mc_flood = 0;
mlxsw_sp_port->mc_router = 0;
mlxsw_sp_port->bridged = 0;

/* Add implicit VLAN interface in the device, so that untagged
Expand Down Expand Up @@ -4743,6 +4755,9 @@ static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport,
mlxsw_sp_vport->learning = 1;
mlxsw_sp_vport->learning_sync = 1;
mlxsw_sp_vport->uc_flood = 1;
mlxsw_sp_vport->mc_flood = 1;
mlxsw_sp_vport->mc_router = 0;
mlxsw_sp_vport->mc_disabled = 1;
mlxsw_sp_vport->bridged = 1;

return 0;
Expand All @@ -4763,6 +4778,8 @@ static void mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport)
mlxsw_sp_vport->learning = 0;
mlxsw_sp_vport->learning_sync = 0;
mlxsw_sp_vport->uc_flood = 0;
mlxsw_sp_vport->mc_flood = 0;
mlxsw_sp_vport->mc_router = 0;
mlxsw_sp_vport->bridged = 0;
}

Expand Down
10 changes: 7 additions & 3 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
#include "core_acl_flex_actions.h"

#define MLXSW_SP_VFID_BASE VLAN_N_VID
#define MLXSW_SP_VFID_MAX 6656 /* Bridged VLAN interfaces */
#define MLXSW_SP_VFID_MAX 1024 /* Bridged VLAN interfaces */

#define MLXSW_SP_RFID_BASE 15360
#define MLXSW_SP_INVALID_RIF 0xffff
Expand Down Expand Up @@ -339,9 +339,12 @@ struct mlxsw_sp_port {
struct mlxsw_sp *mlxsw_sp;
u8 local_port;
u8 stp_state;
u8 learning:1,
u16 learning:1,
learning_sync:1,
uc_flood:1,
mc_flood:1,
mc_router:1,
mc_disabled:1,
bridged:1,
lagged:1,
split:1;
Expand Down Expand Up @@ -509,7 +512,8 @@ mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,

enum mlxsw_sp_flood_table {
MLXSW_SP_FLOOD_TABLE_UC,
MLXSW_SP_FLOOD_TABLE_BM,
MLXSW_SP_FLOOD_TABLE_BC,
MLXSW_SP_FLOOD_TABLE_MC,
};

int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp);
Expand Down
154 changes: 124 additions & 30 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,21 @@ mlxsw_sp_port_orig_get(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port)
{
struct mlxsw_sp_port *mlxsw_sp_vport;
struct mlxsw_sp_fid *fid;
u16 vid;

if (netif_is_bridge_master(dev)) {
fid = mlxsw_sp_vfid_find(mlxsw_sp_port->mlxsw_sp,
dev);
if (fid) {
mlxsw_sp_vport =
mlxsw_sp_port_vport_find_by_fid(mlxsw_sp_port,
fid->fid);
WARN_ON(!mlxsw_sp_vport);
return mlxsw_sp_vport;
}
}

if (!is_vlan_dev(dev))
return mlxsw_sp_port;

Expand Down Expand Up @@ -166,9 +179,10 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state);
}

static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 idx_begin, u16 idx_end, bool uc_set,
bool bm_set)
static int __mlxsw_sp_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 idx_begin, u16 idx_end,
enum mlxsw_sp_flood_table table,
bool set)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
u16 local_port = mlxsw_sp_port->local_port;
Expand All @@ -186,31 +200,48 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
if (!sftr_pl)
return -ENOMEM;

mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin,
table_type, range, local_port, uc_set);
mlxsw_reg_sftr_pack(sftr_pl, table, idx_begin,
table_type, range, local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);

kfree(sftr_pl);
return err;
}

static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 idx_begin, u16 idx_end, bool uc_set,
bool bc_set, bool mc_set)
{
int err;

err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
MLXSW_SP_FLOOD_TABLE_UC, uc_set);
if (err)
goto buffer_out;
return err;

mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin,
table_type, range, local_port, bm_set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
MLXSW_SP_FLOOD_TABLE_BC, bc_set);
if (err)
goto err_flood_bm_set;

goto buffer_out;
err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
MLXSW_SP_FLOOD_TABLE_MC, mc_set);
if (err)
goto err_flood_mc_set;
return 0;

err_flood_mc_set:
__mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
MLXSW_SP_FLOOD_TABLE_BC, !bc_set);
err_flood_bm_set:
mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin,
table_type, range, local_port, !uc_set);
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
buffer_out:
kfree(sftr_pl);
__mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
MLXSW_SP_FLOOD_TABLE_UC, !uc_set);
return err;
}

static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool set)
static int mlxsw_sp_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
enum mlxsw_sp_flood_table table,
bool set)
{
struct net_device *dev = mlxsw_sp_port->dev;
u16 vid, last_visited_vid;
Expand All @@ -220,13 +251,13 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 fid = mlxsw_sp_vport_fid_get(mlxsw_sp_port)->fid;
u16 vfid = mlxsw_sp_fid_to_vfid(fid);

return __mlxsw_sp_port_flood_set(mlxsw_sp_port, vfid, vfid,
set, true);
return __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vfid,
vfid, table, set);
}

for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set,
true);
err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vid, vid,
table, set);
if (err) {
last_visited_vid = vid;
goto err_port_flood_set;
Expand All @@ -237,21 +268,53 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,

err_port_flood_set:
for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid)
__mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, !set, true);
__mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vid, vid, table,
!set);
netdev_err(dev, "Failed to configure unicast flooding\n");
return err;
}

static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans,
bool mc_disabled)
{
int set;
int err = 0;

if (switchdev_trans_ph_prepare(trans))
return 0;

if (mlxsw_sp_port->mc_router != mlxsw_sp_port->mc_flood) {
set = mc_disabled ?
mlxsw_sp_port->mc_flood : mlxsw_sp_port->mc_router;
err = mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
MLXSW_SP_FLOOD_TABLE_MC,
set);
}

if (!err)
mlxsw_sp_port->mc_disabled = mc_disabled;

return err;
}

int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
bool set)
{
bool mc_set = set;
u16 vfid;

/* In case of vFIDs, index into the flooding table is relative to
* the start of the vFIDs range.
*/
vfid = mlxsw_sp_fid_to_vfid(fid);
return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, set);

if (set)
mc_set = mlxsw_sp_vport->mc_disabled ?
mlxsw_sp_vport->mc_flood : mlxsw_sp_vport->mc_router;

return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, set,
mc_set);
}

static int mlxsw_sp_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
Expand Down Expand Up @@ -297,8 +360,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
return 0;

if ((uc_flood ^ brport_flags) & BR_FLOOD) {
err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
!mlxsw_sp_port->uc_flood);
err = mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
MLXSW_SP_FLOOD_TABLE_UC,
!mlxsw_sp_port->uc_flood);
if (err)
return err;
}
Expand All @@ -318,8 +382,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,

err_port_learning_set:
if ((uc_flood ^ brport_flags) & BR_FLOOD)
mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
mlxsw_sp_port->uc_flood);
mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
MLXSW_SP_FLOOD_TABLE_UC,
mlxsw_sp_port->uc_flood);
return err;
}

Expand Down Expand Up @@ -371,6 +436,22 @@ static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
return 0;
}

static int mlxsw_sp_port_attr_mc_router_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans,
bool is_port_mc_router)
{
if (switchdev_trans_ph_prepare(trans))
return 0;

mlxsw_sp_port->mc_router = is_port_mc_router;
if (!mlxsw_sp_port->mc_disabled)
return mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
MLXSW_SP_FLOOD_TABLE_MC,
is_port_mc_router);

return 0;
}

static int mlxsw_sp_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans)
Expand Down Expand Up @@ -400,6 +481,14 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
attr->orig_dev,
attr->u.vlan_filtering);
break;
case SWITCHDEV_ATTR_ID_PORT_MROUTER:
err = mlxsw_sp_port_attr_mc_router_set(mlxsw_sp_port, trans,
attr->u.mrouter);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans,
attr->u.mc_disabled);
break;
default:
err = -EOPNOTSUPP;
break;
Expand Down Expand Up @@ -545,6 +634,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid,
static int mlxsw_sp_port_fid_join(struct mlxsw_sp_port *mlxsw_sp_port,
u16 fid_begin, u16 fid_end)
{
bool mc_flood;
int fid, err;

for (fid = fid_begin; fid <= fid_end; fid++) {
Expand All @@ -553,8 +643,12 @@ static int mlxsw_sp_port_fid_join(struct mlxsw_sp_port *mlxsw_sp_port,
goto err_port_fid_join;
}

mc_flood = mlxsw_sp_port->mc_disabled ?
mlxsw_sp_port->mc_flood : mlxsw_sp_port->mc_router;

err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, fid_begin, fid_end,
mlxsw_sp_port->uc_flood, true);
mlxsw_sp_port->uc_flood, true,
mc_flood);
if (err)
goto err_port_flood_set;

Expand All @@ -570,7 +664,7 @@ static int mlxsw_sp_port_fid_join(struct mlxsw_sp_port *mlxsw_sp_port,
for (fid--; fid >= fid_begin; fid--)
mlxsw_sp_port_fid_map(mlxsw_sp_port, fid, false);
__mlxsw_sp_port_flood_set(mlxsw_sp_port, fid_begin, fid_end, false,
false);
false, false);
err_port_flood_set:
fid = fid_end;
err_port_fid_join:
Expand All @@ -588,7 +682,7 @@ static void mlxsw_sp_port_fid_leave(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port_fid_map(mlxsw_sp_port, fid, false);

__mlxsw_sp_port_flood_set(mlxsw_sp_port, fid_begin, fid_end, false,
false);
false, false);

for (fid = fid_begin; fid <= fid_end; fid++)
__mlxsw_sp_port_fid_leave(mlxsw_sp_port, fid);
Expand Down
Loading

0 comments on commit 58be242

Please sign in to comment.