mlxsw: spectrum: pass local_port to mlxsw_sp_port_fdb_uc_op
[deliverable/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_switchdev.c
index 49d5318735365393d0be7593c470b6eaeca437eb..5150e9e2b71831ed69e2f36ff1e135306ff460bf 100644 (file)
@@ -151,6 +151,11 @@ 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 idx_begin, u16 idx_end, bool set,
                                     bool only_uc)
@@ -164,7 +169,10 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
 
        if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
                table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
-               local_port = MLXSW_PORT_CPU_PORT;
+               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;
        }
@@ -244,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;
 
@@ -288,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)
@@ -312,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;
@@ -494,8 +526,13 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
        }
 
        /* 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,
@@ -534,15 +571,15 @@ 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)
@@ -557,11 +594,10 @@ static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
                        MLXSW_REG_SFD_OP_WRITE_REMOVE;
 }
 
-static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port,
+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)
 {
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        char *sfd_pl;
        int err;
 
@@ -572,7 +608,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, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
-                             mlxsw_sp_port->local_port);
+                             local_port);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
        kfree(sfd_pl);
 
@@ -580,8 +616,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 fid, bool adding,
-                                      bool dynamic)
+                                      const char *mac, u16 fid, u16 lag_vid,
+                                      bool adding, bool dynamic)
 {
        char *sfd_pl;
        int err;
@@ -593,7 +629,7 @@ 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, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
-                                 lag_id);
+                                 lag_vid, lag_id);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
        kfree(sfd_pl);
 
@@ -606,6 +642,7 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
                             struct switchdev_trans *trans)
 {
        u16 fid = fdb->vid;
+       u16 lag_vid = 0;
 
        if (switchdev_trans_ph_prepare(trans))
                return 0;
@@ -614,18 +651,21 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *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,
+               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, true, false);
+                                                  fdb->addr, fid, lag_vid,
+                                                  true, false);
 }
 
 static int mlxsw_sp_port_obj_add(struct net_device *dev,
@@ -748,21 +788,24 @@ 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,
+               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,
+                                                  fdb->addr, fid, lag_vid,
                                                   false, false);
 }
 
@@ -917,6 +960,8 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
                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);
@@ -994,7 +1039,25 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
                return;
        }
 
-       err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, fid,
+       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, local_port, mac, fid,
                                      adding && mlxsw_sp_port->learning, true);
        if (err) {
                if (net_ratelimit())
@@ -1002,8 +1065,6 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
                return;
        }
 
-       vid = fid;
-
        mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
                                    mlxsw_sp_port->learning_sync,
                                    adding, mac, vid, mlxsw_sp_port->dev);
@@ -1015,6 +1076,7 @@ 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, fid;
        int err;
@@ -1026,7 +1088,26 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
                return;
        }
 
-       err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid,
+       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) {
@@ -1035,8 +1116,6 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
                return;
        }
 
-       vid = fid;
-
        mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
                                    mlxsw_sp_port->learning_sync,
                                    adding, mac, vid,
@@ -1151,7 +1230,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;
This page took 0.034292 seconds and 5 git commands to generate.