Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+15 −7
Original line number Diff line number Diff line
@@ -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);
}

+659 −115

File changed.

Preview size limit exceeded, changes collapsed.

+98 −4
Original line number Diff line number Diff line
@@ -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
+199 −35
Original line number Diff line number Diff line
@@ -51,12 +51,33 @@
#include "core.h"
#include "reg.h"

static struct mlxsw_sp_port *
mlxsw_sp_port_orig_get(struct net_device *dev,
		       struct mlxsw_sp_port *mlxsw_sp_port)
{
	struct mlxsw_sp_port *mlxsw_sp_vport;
	u16 vid;

	if (!is_vlan_dev(dev))
		return mlxsw_sp_port;

	vid = vlan_dev_vlan_id(dev);
	mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
	WARN_ON(!mlxsw_sp_vport);

	return mlxsw_sp_vport;
}

static int mlxsw_sp_port_attr_get(struct net_device *dev,
				  struct switchdev_attr *attr)
{
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;

	mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
	if (!mlxsw_sp_port)
		return -EINVAL;

	switch (attr->id) {
	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
		attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
@@ -105,8 +126,14 @@ static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
	if (!spms_pl)
		return -ENOMEM;
	mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
		mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
	} else {
		for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
			mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
	}

	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
	kfree(spms_pl);
@@ -124,22 +151,38 @@ 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 bool mlxsw_sp_vfid_is_vport_br(u16 vfid)
{
	return vfid >= MLXSW_SP_VFID_PORT_MAX;
}

static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
				     u16 fid_begin, u16 fid_end, bool set,
				     u16 idx_begin, u16 idx_end, bool set,
				     bool only_uc)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u16 range = fid_end - fid_begin + 1;
	u16 local_port = mlxsw_sp_port->local_port;
	enum mlxsw_flood_table_type table_type;
	u16 range = idx_end - idx_begin + 1;
	char *sftr_pl;
	int err;

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
		if (mlxsw_sp_vfid_is_vport_br(idx_begin))
			local_port = mlxsw_sp_port->local_port;
		else
			local_port = MLXSW_PORT_CPU_PORT;
	} else {
		table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
	}

	sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
	if (!sftr_pl)
		return -ENOMEM;

	mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
			    MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
			    mlxsw_sp_port->local_port, set);
	mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin,
			    table_type, range, local_port, set);
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
	if (err)
		goto buffer_out;
@@ -150,9 +193,8 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
	if (only_uc)
		goto buffer_out;

	mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
			    MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
			    mlxsw_sp_port->local_port, set);
	mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin,
			    table_type, range, local_port, set);
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);

buffer_out:
@@ -167,6 +209,13 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
	u16 vid, last_visited_vid;
	int err;

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);

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

	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);
@@ -185,6 +234,16 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
	return err;
}

int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
			     bool set, bool only_uc)
{
	/* In case of vFIDs, index into the flooding table is relative to
	 * the start of the vFIDs range.
	 */
	return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set,
					 only_uc);
}

static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
					   struct switchdev_trans *trans,
					   unsigned long brport_flags)
@@ -244,6 +303,10 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	int err = 0;

	mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
	if (!mlxsw_sp_port)
		return -EINVAL;

	switch (attr->id) {
	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
		err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
@@ -304,7 +367,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
	enum mlxsw_reg_svfa_mt mt;

	if (mlxsw_sp_port->nr_vfids)
	if (!list_empty(&mlxsw_sp_port->vports_list))
		mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
	else
		mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
@@ -316,7 +379,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
	enum mlxsw_reg_svfa_mt mt;

	if (!mlxsw_sp_port->nr_vfids)
	if (list_empty(&mlxsw_sp_port->vports_list))
		return 0;

	mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
@@ -503,7 +566,7 @@ static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
}

static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port,
				   const char *mac, u16 vid, bool adding,
				   const char *mac, u16 fid, bool adding,
				   bool dynamic)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
@@ -516,7 +579,7 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port,

	mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
	mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
			      mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
			      mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
			      mlxsw_sp_port->local_port);
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
	kfree(sfd_pl);
@@ -525,8 +588,8 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port,
}

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)
				       const char *mac, u16 fid, u16 lag_vid,
				       bool adding, bool dynamic)
{
	char *sfd_pl;
	int err;
@@ -537,8 +600,8 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,

	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);
				  mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
				  lag_vid, lag_id);
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
	kfree(sfd_pl);

@@ -550,21 +613,30 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
			     const struct switchdev_obj_port_fdb *fdb,
			     struct switchdev_trans *trans)
{
	u16 vid = fdb->vid;
	u16 fid = fdb->vid;
	u16 lag_vid = 0;

	if (switchdev_trans_ph_prepare(trans))
		return 0;

	if (!vid)
		vid = mlxsw_sp_port->pvid;
	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);

		fid = mlxsw_sp_vfid_to_fid(vfid);
		lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
	}

	if (!fid)
		fid = mlxsw_sp_port->pvid;

	if (!mlxsw_sp_port->lagged)
		return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port,
					       fdb->addr, vid, true, false);
					       fdb->addr, fid, 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);
						   fdb->addr, fid, lag_vid,
						   true, false);
}

static int mlxsw_sp_port_obj_add(struct net_device *dev,
@@ -574,8 +646,15 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	int err = 0;

	mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
	if (!mlxsw_sp_port)
		return -EINVAL;

	switch (obj->id) {
	case SWITCHDEV_OBJ_ID_PORT_VLAN:
		if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
			return 0;

		err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
					      SWITCHDEV_OBJ_PORT_VLAN(obj),
					      trans);
@@ -679,14 +758,24 @@ static int
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
			     const struct switchdev_obj_port_fdb *fdb)
{
	u16 fid = fdb->vid;
	u16 lag_vid = 0;

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);

		fid = mlxsw_sp_vfid_to_fid(vfid);
		lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
	}

	if (!mlxsw_sp_port->lagged)
		return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port,
					       fdb->addr, fdb->vid,
					       fdb->addr, fid,
					       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,
						   fdb->addr, fid, lag_vid,
						   false, false);
}

@@ -696,8 +785,15 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	int err = 0;

	mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
	if (!mlxsw_sp_port)
		return -EINVAL;

	switch (obj->id) {
	case SWITCHDEV_OBJ_ID_PORT_VLAN:
		if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
			return 0;

		err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
					      SWITCHDEV_OBJ_PORT_VLAN(obj));
		break;
@@ -732,9 +828,10 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
				  switchdev_obj_dump_cb_t *cb)
{
	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
	u16 vport_vid = 0, vport_fid = 0;
	char *sfd_pl;
	char mac[ETH_ALEN];
	u16 vid;
	u16 fid;
	u8 local_port;
	u16 lag_id;
	u8 num_rec;
@@ -746,6 +843,14 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
	if (!sfd_pl)
		return -ENOMEM;

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		u16 tmp;

		tmp = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
		vport_fid = mlxsw_sp_vfid_to_fid(tmp);
		vport_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
	}

	mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
	do {
		mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
@@ -764,12 +869,17 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
		for (i = 0; i < num_rec; i++) {
			switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
			case MLXSW_REG_SFD_REC_TYPE_UNICAST:
				mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid,
				mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid,
							&local_port);
				if (local_port == mlxsw_sp_port->local_port) {
					if (vport_fid && vport_fid != fid)
						continue;
					else if (vport_fid)
						fdb->vid = vport_vid;
					else
						fdb->vid = fid;
					ether_addr_copy(fdb->addr, mac);
					fdb->ndm_state = NUD_REACHABLE;
					fdb->vid = vid;
					err = cb(&fdb->obj);
					if (err)
						stored_err = err;
@@ -777,12 +887,17 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
				break;
			case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
				mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
							    mac, &vid, &lag_id);
							    mac, &fid, &lag_id);
				if (mlxsw_sp_port ==
				    mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) {
					if (vport_fid && vport_fid != fid)
						continue;
					else if (vport_fid)
						fdb->vid = vport_vid;
					else
						fdb->vid = fid;
					ether_addr_copy(fdb->addr, mac);
					fdb->ndm_state = NUD_REACHABLE;
					fdb->vid = vid;
					err = cb(&fdb->obj);
					if (err)
						stored_err = err;
@@ -804,6 +919,13 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
	u16 vid;
	int err = 0;

	if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
		vlan->flags = 0;
		vlan->vid_begin = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
		vlan->vid_end = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
		return cb(&vlan->obj);
	}

	for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
		vlan->flags = 0;
		if (vid == mlxsw_sp_port->pvid)
@@ -824,6 +946,10 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev,
	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
	int err = 0;

	mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
	if (!mlxsw_sp_port)
		return -EINVAL;

	switch (obj->id) {
	case SWITCHDEV_OBJ_ID_PORT_VLAN:
		err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
@@ -871,17 +997,35 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
	struct mlxsw_sp_port *mlxsw_sp_port;
	char mac[ETH_ALEN];
	u8 local_port;
	u16 vid;
	u16 vid, fid;
	int err;

	mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port);
	mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
	mlxsw_sp_port = mlxsw_sp->ports[local_port];
	if (!mlxsw_sp_port) {
		dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
		return;
	}

	err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, vid,
	if (mlxsw_sp_fid_is_vfid(fid)) {
		u16 vfid = mlxsw_sp_fid_to_vfid(fid);
		struct mlxsw_sp_port *mlxsw_sp_vport;

		mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
								  vfid);
		if (!mlxsw_sp_vport) {
			netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
			return;
		}

		vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
		/* Override the physical port with the vPort. */
		mlxsw_sp_port = mlxsw_sp_vport;
	} else {
		vid = fid;
	}

	err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, fid,
				      adding && mlxsw_sp_port->learning, true);
	if (err) {
		if (net_ratelimit())
@@ -900,18 +1044,38 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
{
	struct mlxsw_sp_port *mlxsw_sp_port;
	char mac[ETH_ALEN];
	u16 lag_vid = 0;
	u16 lag_id;
	u16 vid;
	u16 vid, fid;
	int err;

	mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &vid, &lag_id);
	mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &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,
	if (mlxsw_sp_fid_is_vfid(fid)) {
		u16 vfid = mlxsw_sp_fid_to_vfid(fid);
		struct mlxsw_sp_port *mlxsw_sp_vport;

		mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
								  vfid);
		if (!mlxsw_sp_vport) {
			netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
			return;
		}

		vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
		lag_vid = vid;
		/* Override the physical port with the vPort. */
		mlxsw_sp_port = mlxsw_sp_vport;
	} else {
		vid = fid;
	}

	err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
					  adding && mlxsw_sp_port->learning,
					  true);
	if (err) {
Loading