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

Merge branch 'mlxsw-bridge-vlan-offloading'

Ido Schimmel says:

====================
This patchset introduces support for the offloading of 802.1D bridges
between VLAN devices. These can either be VLAN devices configured on top
of the physical ports or on top of LAG devices.

Patches 1-2 deal with the necessary infrastructure changes needed in order
to enable the above. The main change is that switchdev drivers can now know
the device from which the switchdev op originated from.

Patches 3-10 lay the groundwork for 802.1D bridges support in the mlxsw
driver, with patch 4 doing most of the heavy lifting.

Patch 11 finally offloads these bridges to hardware by listening to the
notifications sent when the VLAN device joins or leaves a bridge. It is
very similar to the already existing 802.1Q bridge we support.

Patches 12-14 add minor modifications to allow one to bridge a VLAN device
configured on top of LAG.
====================
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 335b3209 272c4470
......@@ -41,7 +41,7 @@ config MLXSW_SWITCHX2
config MLXSW_SPECTRUM
tristate "Mellanox Technologies Spectrum support"
depends on MLXSW_CORE && NET_SWITCHDEV
depends on MLXSW_CORE && NET_SWITCHDEV && VLAN_8021Q
default m
---help---
This driver supports Mellanox Technologies Spectrum Ethernet
......
......@@ -396,7 +396,7 @@ static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index,
static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
enum mlxsw_reg_sfd_rec_policy policy,
const char *mac, u16 vid,
const char *mac, u16 fid_vid,
enum mlxsw_reg_sfd_rec_action action,
u8 local_port)
{
......@@ -404,16 +404,16 @@ static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
MLXSW_REG_SFD_REC_TYPE_UNICAST,
policy, mac, action);
mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid);
mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, fid_vid);
mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port);
}
static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index,
char *mac, u16 *p_vid,
char *mac, u16 *p_fid_vid,
u8 *p_local_port)
{
mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
*p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
*p_fid_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
*p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index);
}
......@@ -438,6 +438,13 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8,
MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
MLXSW_REG_SFD_REC_LEN, 0x08, false);
/* reg_sfd_uc_lag_lag_vid
* Indicates VID in case of vFIDs. Reserved for FIDs.
* Access: RW
*/
MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_vid, MLXSW_REG_SFD_BASE_LEN, 16, 12,
MLXSW_REG_SFD_REC_LEN, 0x0C, false);
/* reg_sfd_uc_lag_lag_id
* LAG Identifier - pointer into the LAG descriptor table.
* Access: RW
......@@ -448,15 +455,16 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_id, MLXSW_REG_SFD_BASE_LEN, 0, 10,
static inline void
mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index,
enum mlxsw_reg_sfd_rec_policy policy,
const char *mac, u16 vid,
enum mlxsw_reg_sfd_rec_action action,
const char *mac, u16 fid_vid,
enum mlxsw_reg_sfd_rec_action action, u16 lag_vid,
u16 lag_id)
{
mlxsw_reg_sfd_rec_pack(payload, rec_index,
MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG,
policy, mac, action);
mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0);
mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, vid);
mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid);
mlxsw_reg_sfd_uc_lag_lag_vid_set(payload, rec_index, lag_vid);
mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id);
}
......
......@@ -41,11 +41,16 @@
#include <linux/netdevice.h>
#include <linux/bitops.h>
#include <linux/if_vlan.h>
#include <linux/list.h>
#include <net/switchdev.h>
#include "core.h"
#define MLXSW_SP_VFID_BASE VLAN_N_VID
#define MLXSW_SP_VFID_PORT_MAX 512 /* Non-bridged VLAN interfaces */
#define MLXSW_SP_VFID_BR_MAX 8192 /* Bridged VLAN interfaces */
#define MLXSW_SP_VFID_MAX (MLXSW_SP_VFID_PORT_MAX + MLXSW_SP_VFID_BR_MAX)
#define MLXSW_SP_LAG_MAX 64
#define MLXSW_SP_PORT_PER_LAG_MAX 16
......@@ -56,8 +61,38 @@ struct mlxsw_sp_upper {
unsigned int ref_count;
};
struct mlxsw_sp_vfid {
struct list_head list;
u16 nr_vports;
u16 vfid; /* Starting at 0 */
struct net_device *br_dev;
u16 vid;
};
static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid)
{
return MLXSW_SP_VFID_BASE + vfid;
}
static inline u16 mlxsw_sp_fid_to_vfid(u16 fid)
{
return fid - MLXSW_SP_VFID_BASE;
}
static inline bool mlxsw_sp_fid_is_vfid(u16 fid)
{
return fid >= MLXSW_SP_VFID_BASE;
}
struct mlxsw_sp {
unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
struct {
struct list_head list;
unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)];
} port_vfids;
struct {
struct list_head list;
unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_BR_MAX)];
} br_vfids;
unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
struct mlxsw_sp_port **ports;
struct mlxsw_core *core;
......@@ -102,11 +137,15 @@ struct mlxsw_sp_port {
lagged:1;
u16 pvid;
u16 lag_id;
struct {
struct list_head list;
struct mlxsw_sp_vfid *vfid;
u16 vid;
} vport;
/* 802.1Q bridge VLANs */
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
unsigned long *active_vlans;
/* VLAN interfaces */
unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
u16 nr_vfids;
struct list_head vports_list;
};
static inline struct mlxsw_sp_port *
......@@ -121,6 +160,59 @@ mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index)
return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL;
}
static inline bool
mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port)
{
return mlxsw_sp_port->vport.vfid;
}
static inline struct net_device *
mlxsw_sp_vport_br_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
{
return mlxsw_sp_vport->vport.vfid->br_dev;
}
static inline u16
mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
{
return mlxsw_sp_vport->vport.vid;
}
static inline u16
mlxsw_sp_vport_vfid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
{
return mlxsw_sp_vport->vport.vfid->vfid;
}
static inline struct mlxsw_sp_port *
mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_vport;
list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
vport.list) {
if (mlxsw_sp_vport_vid_get(mlxsw_sp_vport) == vid)
return mlxsw_sp_vport;
}
return NULL;
}
static inline struct mlxsw_sp_port *
mlxsw_sp_port_vport_find_by_vfid(const struct mlxsw_sp_port *mlxsw_sp_port,
u16 vfid)
{
struct mlxsw_sp_port *mlxsw_sp_vport;
list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
vport.list) {
if (mlxsw_sp_vport_vfid_get(mlxsw_sp_vport) == vfid)
return mlxsw_sp_vport;
}
return NULL;
}
enum mlxsw_sp_flood_table {
MLXSW_SP_FLOOD_TABLE_UC,
MLXSW_SP_FLOOD_TABLE_BM,
......@@ -143,5 +235,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid);
int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid);
int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
bool set, bool only_uc);
#endif
......@@ -50,6 +50,7 @@ enum switchdev_attr_id {
};
struct switchdev_attr {
struct net_device *orig_dev;
enum switchdev_attr_id id;
u32 flags;
union {
......@@ -68,6 +69,7 @@ enum switchdev_obj_id {
};
struct switchdev_obj {
struct net_device *orig_dev;
enum switchdev_obj_id id;
u32 flags;
};
......
......@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <net/arp.h>
#include <net/switchdev.h>
#include "vlan.h"
#include "vlanproc.h"
......@@ -774,6 +775,12 @@ 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,
};
......
......@@ -135,6 +135,7 @@ static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
{
struct switchdev_obj_port_fdb fdb = {
.obj = {
.orig_dev = f->dst->dev,
.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.flags = SWITCHDEV_F_DEFER,
},
......
......@@ -40,6 +40,7 @@ void br_log_state(const struct net_bridge_port *p)
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
.flags = SWITCHDEV_F_DEFER,
.u.stp_state = state,
......@@ -570,6 +571,7 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
.orig_dev = br->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
.u.ageing_time = ageing_time,
......
......@@ -37,6 +37,7 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
void br_init_port(struct net_bridge_port *p)
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER,
.u.ageing_time = p->br->ageing_time,
......
......@@ -73,6 +73,7 @@ static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
......@@ -120,6 +121,7 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
u16 vid)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
......
......@@ -471,6 +471,7 @@ static ssize_t phys_switch_id_show(struct device *dev,
if (dev_isalive(netdev)) {
struct switchdev_attr attr = {
.orig_dev = netdev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
......
......@@ -1027,6 +1027,7 @@ static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
struct switchdev_attr attr = {
.orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
......
......@@ -723,6 +723,7 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
u32 filter_mask)
{
struct switchdev_vlan_dump dump = {
.vlan.obj.orig_dev = dev,
.vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.skb = skb,
.filter_mask = filter_mask,
......@@ -757,6 +758,7 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
int nlflags)
{
struct switchdev_attr attr = {
.orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u16 mode = BRIDGE_MODE_UNDEF;
......@@ -778,6 +780,7 @@ static int switchdev_port_br_setflag(struct net_device *dev,
unsigned long brport_flag)
{
struct switchdev_attr attr = {
.orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u8 flag = nla_get_u8(nlattr);
......@@ -853,6 +856,7 @@ static int switchdev_port_br_afspec(struct net_device *dev,
struct nlattr *attr;
struct bridge_vlan_info *vinfo;
struct switchdev_obj_port_vlan vlan = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
};
int rem;
......@@ -975,6 +979,7 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid, u16 nlm_flags)
{
struct switchdev_obj_port_fdb fdb = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
......@@ -1000,6 +1005,7 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid)
{
struct switchdev_obj_port_fdb fdb = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
......@@ -1077,6 +1083,7 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *filter_dev, int idx)
{
struct switchdev_fdb_dump dump = {
.fdb.obj.orig_dev = dev,
.fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.dev = dev,
.skb = skb,
......@@ -1135,6 +1142,7 @@ static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
if (!dev)
return NULL;
attr.orig_dev = dev;
if (switchdev_port_attr_get(dev, &attr))
return NULL;
......@@ -1194,6 +1202,7 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags |= RTNH_F_OFFLOAD;
......@@ -1238,6 +1247,7 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags &= ~RTNH_F_OFFLOAD;
......@@ -1270,10 +1280,12 @@ static bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b)
{
struct switchdev_attr a_attr = {
.orig_dev = a,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
struct switchdev_attr b_attr = {
.orig_dev = b,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
......
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