mlxsw: spectrum: remove FDB entry in case we get unknown object notification
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_switchdev.c
index 617fb22..d64559e 100644 (file)
 #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);
-       for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+
+       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 @@ err_port_flood_set:
        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)
@@ -193,6 +252,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
        bool set;
        int err;
 
+       if (!mlxsw_sp_port->bridged)
+               return -EINVAL;
+
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
@@ -237,6 +299,22 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
        return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
 }
 
+static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                         struct switchdev_trans *trans,
+                                         struct net_device *orig_dev,
+                                         bool vlan_enabled)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+       /* SWITCHDEV_TRANS_PREPARE phase */
+       if ((!vlan_enabled) && (mlxsw_sp->master_bridge.dev == orig_dev)) {
+               netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int mlxsw_sp_port_attr_set(struct net_device *dev,
                                  const struct switchdev_attr *attr,
                                  struct switchdev_trans *trans)
@@ -244,6 +322,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,
@@ -257,6 +339,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
                err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
                                                       attr->u.ageing_time);
                break;
+       case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+               err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
+                                                    attr->orig_dev,
+                                                    attr->u.vlan_filtering);
+               break;
        default:
                err = -EOPNOTSUPP;
                break;
@@ -304,7 +391,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 +403,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;
@@ -342,14 +429,35 @@ err_port_add_vid:
        return err;
 }
 
+static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    u16 vid_begin, u16 vid_end, bool is_member,
+                                    bool untagged)
+{
+       u16 vid, vid_e;
+       int err;
+
+       for (vid = vid_begin; vid <= vid_end;
+            vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
+               vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
+                           vid_end);
+
+               err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e,
+                                            is_member, untagged);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                     u16 vid_begin, u16 vid_end,
                                     bool flag_untagged, bool flag_pvid)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct net_device *dev = mlxsw_sp_port->dev;
+       u16 vid, last_visited_vid, old_pvid;
        enum mlxsw_reg_svfa_mt mt;
-       u16 vid, vid_e;
        int err;
 
        /* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -377,15 +485,18 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                        if (err) {
                                netdev_err(dev, "Failed to create FID=VID=%d mapping\n",
                                           vid);
-                               return err;
+                               goto err_port_vid_to_fid_set;
                        }
                }
+       }
 
-               /* Set FID mapping according to port's mode */
+       /* Set FID mapping according to port's mode */
+       for (vid = vid_begin; vid <= vid_end; vid++) {
                err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid);
                if (err) {
                        netdev_err(dev, "Failed to map FID=%d", vid);
-                       return err;
+                       last_visited_vid = --vid;
+                       goto err_port_fid_map;
                }
        }
 
@@ -393,83 +504,133 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                        true, false);
        if (err) {
                netdev_err(dev, "Failed to configure flooding\n");
-               return err;
+               goto err_port_flood_set;
        }
 
-       for (vid = vid_begin; vid <= vid_end;
-            vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
-               vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
-                           vid_end);
-
-               err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true,
-                                            flag_untagged);
-               if (err) {
-                       netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n",
-                                  vid, vid_e);
-                       return err;
-               }
+       err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+                                       true, flag_untagged);
+       if (err) {
+               netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin,
+                          vid_end);
+               goto err_port_vlans_set;
        }
 
-       vid = vid_begin;
-       if (flag_pvid && mlxsw_sp_port->pvid != vid) {
-               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid);
+       old_pvid = mlxsw_sp_port->pvid;
+       if (flag_pvid && old_pvid != vid_begin) {
+               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid_begin);
                if (err) {
-                       netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n",
-                                  vid);
-                       return err;
+                       netdev_err(dev, "Unable to add PVID %d\n", vid_begin);
+                       goto err_port_pvid_set;
                }
-               mlxsw_sp_port->pvid = vid;
+               mlxsw_sp_port->pvid = vid_begin;
        }
 
        /* Changing activity bits only if HW operation succeded */
-       for (vid = vid_begin; vid <= vid_end; vid++)
+       for (vid = vid_begin; vid <= vid_end; vid++) {
                set_bit(vid, mlxsw_sp_port->active_vlans);
+               if (flag_untagged)
+                       set_bit(vid, mlxsw_sp_port->untagged_vlans);
+               else
+                       clear_bit(vid, mlxsw_sp_port->untagged_vlans);
+       }
+
+       /* STP state change must be done after we set active VLANs */
+       err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
+                                         mlxsw_sp_port->stp_state);
+       if (err) {
+               netdev_err(dev, "Failed to set STP state\n");
+               goto err_port_stp_state_set;
+       }
 
-       return mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
-                                          mlxsw_sp_port->stp_state);
+       return 0;
+
+err_port_vid_to_fid_set:
+       mlxsw_sp_fid_destroy(mlxsw_sp, vid);
+       return err;
+
+err_port_stp_state_set:
+       for (vid = vid_begin; vid <= vid_end; vid++)
+               clear_bit(vid, mlxsw_sp_port->active_vlans);
+       if (old_pvid != mlxsw_sp_port->pvid)
+               mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
+err_port_pvid_set:
+       __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false,
+                                 false);
+err_port_vlans_set:
+       __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end, false,
+                                 false);
+err_port_flood_set:
+       last_visited_vid = vid_end;
+err_port_fid_map:
+       for (vid = last_visited_vid; vid >= vid_begin; vid--)
+               mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid);
+       return err;
 }
 
 static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                   const struct switchdev_obj_port_vlan *vlan,
                                   struct switchdev_trans *trans)
 {
-       bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-       bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
 
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
        return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
                                         vlan->vid_begin, vlan->vid_end,
-                                        untagged_flag, pvid_flag);
+                                        flag_untagged, flag_pvid);
+}
+
+static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
+{
+       return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
+                        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_op(struct mlxsw_sp_port *mlxsw_sp_port,
-                               const char *mac, u16 vid, bool adding,
-                               bool dynamic)
+static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+                                  const char *mac, u16 fid, bool adding,
+                                  bool dynamic)
 {
-       enum mlxsw_reg_sfd_rec_policy policy;
-       enum mlxsw_reg_sfd_op op;
        char *sfd_pl;
        int err;
 
-       if (!vid)
-               vid = mlxsw_sp_port->pvid;
+       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_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
+                             mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+                             local_port);
+       err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), 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 fid, u16 lag_vid,
+                                      bool adding, bool dynamic)
+{
+       char *sfd_pl;
+       int err;
 
        sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
        if (!sfd_pl)
                return -ENOMEM;
 
-       policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
-                          MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
-       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,
-                             mlxsw_sp_port->local_port);
-       err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd),
-                             sfd_pl);
+       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, 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);
 
        return err;
@@ -480,11 +641,31 @@ 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 fid = fdb->vid;
+       u16 lag_vid = 0;
+
        if (switchdev_trans_ph_prepare(trans))
                return 0;
 
-       return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
-                                   true, false);
+       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->mlxsw_sp,
+                                              mlxsw_sp_port->local_port,
+                                              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, fid, lag_vid,
+                                                  true, false);
 }
 
 static int mlxsw_sp_port_obj_add(struct net_device *dev,
@@ -494,8 +675,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);
@@ -532,7 +720,7 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
                                     u16 vid_begin, u16 vid_end, bool init)
 {
        struct net_device *dev = mlxsw_sp_port->dev;
-       u16 vid, vid_e;
+       u16 vid, pvid;
        int err;
 
        /* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -542,30 +730,23 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
        if (!init && !mlxsw_sp_port->bridged)
                return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end);
 
-       for (vid = vid_begin; vid <= vid_end;
-            vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
-               vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
-                           vid_end);
-               err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false,
-                                            false);
-               if (err) {
-                       netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n",
-                                  vid, vid_e);
-                       return err;
-               }
+       err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+                                       false, false);
+       if (err) {
+               netdev_err(dev, "Unable to del VIDs %d-%d\n", vid_begin,
+                          vid_end);
+               return err;
        }
 
-       if ((mlxsw_sp_port->pvid >= vid_begin) &&
-           (mlxsw_sp_port->pvid <= vid_end)) {
+       pvid = mlxsw_sp_port->pvid;
+       if (pvid >= vid_begin && pvid <= vid_end && pvid != 1) {
                /* Default VLAN is always 1 */
-               mlxsw_sp_port->pvid = 1;
-               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port,
-                                            mlxsw_sp_port->pvid);
+               err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
                if (err) {
-                       netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n",
-                                  vid);
+                       netdev_err(dev, "Unable to del PVID %d\n", pvid);
                        return err;
                }
+               mlxsw_sp_port->pvid = 1;
        }
 
        if (init)
@@ -606,8 +787,26 @@ static int
 mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
                             const struct switchdev_obj_port_fdb *fdb)
 {
-       return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
-                                   false, false);
+       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->mlxsw_sp,
+                                              mlxsw_sp_port->local_port,
+                                              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, fid, lag_vid,
+                                                  false, false);
 }
 
 static int mlxsw_sp_port_obj_del(struct net_device *dev,
@@ -616,8 +815,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;
@@ -633,14 +839,31 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
        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,
                                  struct switchdev_obj_port_fdb *fdb,
                                  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;
        int stored_err = 0;
        int i;
@@ -650,11 +873,18 @@ 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);
-               err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core,
-                                     MLXSW_REG(sfd), sfd_pl);
+               err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
                if (err)
                        goto out;
 
@@ -669,16 +899,40 @@ 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;
                                }
+                               break;
+                       case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
+                               mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
+                                                           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;
+                                       err = cb(&fdb->obj);
+                                       if (err)
+                                               stored_err = err;
+                               }
+                               break;
                        }
                }
        } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
@@ -695,10 +949,19 @@ 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)
                        vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+               if (test_bit(vid, mlxsw_sp_port->untagged_vlans))
+                       vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
                vlan->vid_begin = vid;
                vlan->vid_end = vid;
                err = cb(&vlan->obj);
@@ -715,6 +978,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,
@@ -740,6 +1007,21 @@ static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
        .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,
                                            char *sfn_pl, int rec_index,
                                            bool adding)
@@ -747,34 +1029,119 @@ 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;
+       bool do_notification = true;
        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;
+               goto just_remove;
+       }
+
+       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");
+                       goto just_remove;
+               }
+               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_op(mlxsw_sp_port, mac, vid,
-                                  adding && mlxsw_sp_port->learning, true);
+       adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+       err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
+                                     adding, true);
        if (err) {
                if (net_ratelimit())
                        netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
                return;
        }
 
-       if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) {
-               struct switchdev_notifier_fdb_info info;
-               unsigned long notifier_type;
+       if (!do_notification)
+               return;
+       mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
+                                   mlxsw_sp_port->learning_sync,
+                                   adding, mac, vid, mlxsw_sp_port->dev);
+       return;
+
+just_remove:
+       adding = false;
+       do_notification = false;
+       goto do_fdb_op;
+}
 
-               info.addr = mac;
-               info.vid = vid;
-               notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
-               call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev,
-                                        &info.info);
+static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
+                                               char *sfn_pl, int rec_index,
+                                               bool adding)
+{
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       char mac[ETH_ALEN];
+       u16 lag_vid = 0;
+       u16 lag_id;
+       u16 vid, fid;
+       bool do_notification = true;
+       int err;
+
+       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");
+               goto just_remove;
+       }
+
+       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");
+                       goto just_remove;
+               }
+
+               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;
        }
+
+       adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+       err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
+                                         adding, true);
+       if (err) {
+               if (net_ratelimit())
+                       netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+               return;
+       }
+
+       if (!do_notification)
+               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);
+       return;
+
+just_remove:
+       adding = false;
+       do_notification = false;
+       goto do_fdb_op;
 }
 
 static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
@@ -789,6 +1156,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
                                                rec_index, false);
                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;
        }
 }
 
@@ -877,7 +1252,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
         * with VID 1.
         */
        mlxsw_sp_port->pvid = 1;
-       err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true);
+       err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID - 1,
+                                       true);
        if (err) {
                netdev_err(dev, "Unable to init VLANs\n");
                return err;