Commit 90561843 authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-Offload-bridge-device-mrouter'

Jiri Pirko says:

====================
mlxsw: Offload bridge device mrouter

Yotam says:

Similarly to a bridged port, the bridge device itself can be configured by
the user to be an mrouter port. In this case, all multicast traffic should
be forwarded to it. Make the mlxsw Spectrum driver offload these directives
to the Spectrum hardware.

Patches 1 and 2 add a new switchdev notification for bridge device mrouter
port status and make the bridge module notify about it.

Patches 3-5 change the mlxsw Spectrum driver to handle these notifications
by adding the Spectrum router port to the bridge MDB entries.

v1->v2:
 - patch1:
   - Don't add the MDB_RTR_TYPE_TEMP state and use the timer_pending to
     distinguish between learning-on and learning-off states
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 31070e7f 593bc28a
...@@ -5957,7 +5957,7 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif, ...@@ -5957,7 +5957,7 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
} }
static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp) u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp)
{ {
return mlxsw_core_max_ports(mlxsw_sp->core) + 1; return mlxsw_core_max_ports(mlxsw_sp->core) + 1;
} }
......
...@@ -70,6 +70,7 @@ u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif); ...@@ -70,6 +70,7 @@ u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif);
u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *rif); u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *rif);
u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *rif); u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *rif);
int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif); int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif);
u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp);
const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif); const struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif);
int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_rif *rif, struct mlxsw_sp_rif *rif,
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/netlink.h> #include <linux/netlink.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include "spectrum_router.h"
#include "spectrum.h" #include "spectrum.h"
#include "core.h" #include "core.h"
#include "reg.h" #include "reg.h"
...@@ -79,7 +80,8 @@ struct mlxsw_sp_bridge_device { ...@@ -79,7 +80,8 @@ struct mlxsw_sp_bridge_device {
struct list_head ports_list; struct list_head ports_list;
struct list_head mids_list; struct list_head mids_list;
u8 vlan_enabled:1, u8 vlan_enabled:1,
multicast_enabled:1; multicast_enabled:1,
mrouter:1;
const struct mlxsw_sp_bridge_ops *ops; const struct mlxsw_sp_bridge_ops *ops;
}; };
...@@ -170,6 +172,7 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, ...@@ -170,6 +172,7 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge,
bridge_device->dev = br_dev; bridge_device->dev = br_dev;
bridge_device->vlan_enabled = vlan_enabled; bridge_device->vlan_enabled = vlan_enabled;
bridge_device->multicast_enabled = br_multicast_enabled(br_dev); bridge_device->multicast_enabled = br_multicast_enabled(br_dev);
bridge_device->mrouter = br_multicast_router(br_dev);
INIT_LIST_HEAD(&bridge_device->ports_list); INIT_LIST_HEAD(&bridge_device->ports_list);
if (vlan_enabled) { if (vlan_enabled) {
bridge->vlan_enabled_exists = true; bridge->vlan_enabled_exists = true;
...@@ -812,6 +815,60 @@ static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, ...@@ -812,6 +815,60 @@ static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
return 0; return 0;
} }
static int mlxsw_sp_smid_router_port_set(struct mlxsw_sp *mlxsw_sp,
u16 mid_idx, bool add)
{
char *smid_pl;
int err;
smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
if (!smid_pl)
return -ENOMEM;
mlxsw_reg_smid_pack(smid_pl, mid_idx,
mlxsw_sp_router_port(mlxsw_sp), add);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
kfree(smid_pl);
return err;
}
static void
mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_bridge_device *bridge_device,
bool add)
{
struct mlxsw_sp_mid *mid;
list_for_each_entry(mid, &bridge_device->mids_list, list)
mlxsw_sp_smid_router_port_set(mlxsw_sp, mid->mid, add);
}
static int
mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans,
struct net_device *orig_dev,
bool is_mrouter)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct mlxsw_sp_bridge_device *bridge_device;
if (switchdev_trans_ph_prepare(trans))
return 0;
/* It's possible we failed to enslave the port, yet this
* operation is executed due to it being deferred.
*/
bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
if (!bridge_device)
return 0;
if (bridge_device->mrouter != is_mrouter)
mlxsw_sp_bridge_mrouter_update_mdb(mlxsw_sp, bridge_device,
is_mrouter);
bridge_device->mrouter = is_mrouter;
return 0;
}
static int mlxsw_sp_port_attr_set(struct net_device *dev, static int mlxsw_sp_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr, const struct switchdev_attr *attr,
struct switchdev_trans *trans) struct switchdev_trans *trans)
...@@ -849,6 +906,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, ...@@ -849,6 +906,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
attr->orig_dev, attr->orig_dev,
attr->u.mc_disabled); attr->u.mc_disabled);
break; break;
case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, trans,
attr->orig_dev,
attr->u.mrouter);
break;
default: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
break; break;
...@@ -1243,7 +1305,8 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, ...@@ -1243,7 +1305,8 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
} }
static int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx, static int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx,
long *ports_bitmap) long *ports_bitmap,
bool set_router_port)
{ {
char *smid_pl; char *smid_pl;
int err, i; int err, i;
...@@ -1258,9 +1321,15 @@ static int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx, ...@@ -1258,9 +1321,15 @@ static int mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx,
mlxsw_reg_smid_port_mask_set(smid_pl, i, 1); mlxsw_reg_smid_port_mask_set(smid_pl, i, 1);
} }
mlxsw_reg_smid_port_mask_set(smid_pl,
mlxsw_sp_router_port(mlxsw_sp), 1);
for_each_set_bit(i, ports_bitmap, mlxsw_core_max_ports(mlxsw_sp->core)) for_each_set_bit(i, ports_bitmap, mlxsw_core_max_ports(mlxsw_sp->core))
mlxsw_reg_smid_port_set(smid_pl, i, 1); mlxsw_reg_smid_port_set(smid_pl, i, 1);
mlxsw_reg_smid_port_set(smid_pl, mlxsw_sp_router_port(mlxsw_sp),
set_router_port);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
kfree(smid_pl); kfree(smid_pl);
return err; return err;
...@@ -1364,7 +1433,8 @@ mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, ...@@ -1364,7 +1433,8 @@ mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_mc_get_mrouters_bitmap(flood_bitmap, bridge_device, mlxsw_sp); mlxsw_sp_mc_get_mrouters_bitmap(flood_bitmap, bridge_device, mlxsw_sp);
mid->mid = mid_idx; mid->mid = mid_idx;
err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, flood_bitmap); err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, flood_bitmap,
bridge_device->mrouter);
kfree(flood_bitmap); kfree(flood_bitmap);
if (err) if (err)
return false; return false;
......
...@@ -64,6 +64,7 @@ int br_multicast_list_adjacent(struct net_device *dev, ...@@ -64,6 +64,7 @@ int br_multicast_list_adjacent(struct net_device *dev,
bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto); bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto);
bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto); bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto);
bool br_multicast_enabled(const struct net_device *dev); bool br_multicast_enabled(const struct net_device *dev);
bool br_multicast_router(const struct net_device *dev);
#else #else
static inline int br_multicast_list_adjacent(struct net_device *dev, static inline int br_multicast_list_adjacent(struct net_device *dev,
struct list_head *br_ip_list) struct list_head *br_ip_list)
...@@ -84,6 +85,10 @@ static inline bool br_multicast_enabled(const struct net_device *dev) ...@@ -84,6 +85,10 @@ static inline bool br_multicast_enabled(const struct net_device *dev)
{ {
return false; return false;
} }
static inline bool br_multicast_router(const struct net_device *dev)
{
return false;
}
#endif #endif
#if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
......
...@@ -51,6 +51,7 @@ enum switchdev_attr_id { ...@@ -51,6 +51,7 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
}; };
struct switchdev_attr { struct switchdev_attr {
......
...@@ -859,8 +859,32 @@ static void br_multicast_router_expired(unsigned long data) ...@@ -859,8 +859,32 @@ static void br_multicast_router_expired(unsigned long data)
spin_unlock(&br->multicast_lock); spin_unlock(&br->multicast_lock);
} }
static void br_mc_router_state_change(struct net_bridge *p,
bool is_mc_router)
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
.flags = SWITCHDEV_F_DEFER,
.u.mrouter = is_mc_router,
};
switchdev_port_attr_set(p->dev, &attr);
}
static void br_multicast_local_router_expired(unsigned long data) static void br_multicast_local_router_expired(unsigned long data)
{ {
struct net_bridge *br = (struct net_bridge *)data;
spin_lock(&br->multicast_lock);
if (br->multicast_router == MDB_RTR_TYPE_DISABLED ||
br->multicast_router == MDB_RTR_TYPE_PERM ||
timer_pending(&br->multicast_router_timer))
goto out;
br_mc_router_state_change(br, false);
out:
spin_unlock(&br->multicast_lock);
} }
static void br_multicast_querier_expired(struct net_bridge *br, static void br_multicast_querier_expired(struct net_bridge *br,
...@@ -1364,9 +1388,12 @@ static void br_multicast_mark_router(struct net_bridge *br, ...@@ -1364,9 +1388,12 @@ static void br_multicast_mark_router(struct net_bridge *br,
unsigned long now = jiffies; unsigned long now = jiffies;
if (!port) { if (!port) {
if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) {
if (!timer_pending(&br->multicast_router_timer))
br_mc_router_state_change(br, true);
mod_timer(&br->multicast_router_timer, mod_timer(&br->multicast_router_timer,
now + br->multicast_querier_interval); now + br->multicast_querier_interval);
}
return; return;
} }
...@@ -1952,7 +1979,7 @@ void br_multicast_init(struct net_bridge *br) ...@@ -1952,7 +1979,7 @@ void br_multicast_init(struct net_bridge *br)
spin_lock_init(&br->multicast_lock); spin_lock_init(&br->multicast_lock);
setup_timer(&br->multicast_router_timer, setup_timer(&br->multicast_router_timer,
br_multicast_local_router_expired, 0); br_multicast_local_router_expired, (unsigned long)br);
setup_timer(&br->ip4_other_query.timer, setup_timer(&br->ip4_other_query.timer,
br_ip4_multicast_querier_expired, (unsigned long)br); br_ip4_multicast_querier_expired, (unsigned long)br);
setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired, setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired,
...@@ -2042,9 +2069,14 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val) ...@@ -2042,9 +2069,14 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
switch (val) { switch (val) {
case MDB_RTR_TYPE_DISABLED: case MDB_RTR_TYPE_DISABLED:
case MDB_RTR_TYPE_PERM: case MDB_RTR_TYPE_PERM:
br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM);
del_timer(&br->multicast_router_timer); del_timer(&br->multicast_router_timer);
/* fall through */ br->multicast_router = val;
err = 0;
break;
case MDB_RTR_TYPE_TEMP_QUERY: case MDB_RTR_TYPE_TEMP_QUERY:
if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY)
br_mc_router_state_change(br, false);
br->multicast_router = val; br->multicast_router = val;
err = 0; err = 0;
break; break;
...@@ -2184,6 +2216,18 @@ bool br_multicast_enabled(const struct net_device *dev) ...@@ -2184,6 +2216,18 @@ bool br_multicast_enabled(const struct net_device *dev)
} }
EXPORT_SYMBOL_GPL(br_multicast_enabled); EXPORT_SYMBOL_GPL(br_multicast_enabled);
bool br_multicast_router(const struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
bool is_router;
spin_lock_bh(&br->multicast_lock);
is_router = br_multicast_is_router(br);
spin_unlock_bh(&br->multicast_lock);
return is_router;
}
EXPORT_SYMBOL_GPL(br_multicast_router);
int br_multicast_set_querier(struct net_bridge *br, unsigned long val) int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
{ {
unsigned long max_delay; unsigned long max_delay;
......
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